tl; dr
Хотя Spout
можно улучшить, Excel не предназначен для большого количества стилей , так что это на самом деле не недостаток библиотеки ( возможно, вы захотите отменить вопрос )
История
Хорошо, здесь есть несколько моментов, которые я использовал для тестирования ... код, который я использовал для тестирования, находится внизу мой пост - ключевые цветовые функции: jonEg
и jonEgQuant
(переменная $by
внутри, которую можно настроить)
Стилизация в Excel немного похожа на HTML + CSS. Стили определяются вначале в файле (в блоке заголовка), а затем на листах указывается имя (класс). По сути, это говорит о том, что формат хранения Excel не был разработан для множества стилей, а только несколько, которые повторно используются во многих ячейках
Spout
скрывает сложность определения стиля от пользователя - если стиль уже определен (ie. , точный стиль уже существует ), то он использует это определение, в противном случае определяется новый стиль
Spout
создает индекс всех стилей путем сериализации объекта стиля и сохранения его в массиве. Примерно 1749 байт на стиль. Этот индекс жует память (в дополнение к фактическим объектам стиля, которые есть в памяти для генерации файла) - это можно улучшить в Spout
С этим кодом я гарантирую, что каждая ячейка имеет свой стиль (используя положение строки / столбца для определения красного и зеленого компонентов цвета) - , что является более экстремальным, чем ваше изображение-цвет- выбор, но, возможно, не намного (я не могу сказать без образца изображения). Есть 16M цветов, доступных с триплетом RGB ... но наши глаза не всегда могут обнаружить разницу в несколько пунктов. Например, в красном градиенте будет ясно, что 255,254,253 ... 128 выглядит гладким, но один блок из 255,254,253 случайно распределенных будет, вероятно, выглядеть как один цвет. Для всего, кроме основного цвета, эта дисперсия теперь применяется в трех измерениях (r, g и b). Формат JPEG использует это (сочетание сжатия и шума при восстановлении) - поэтому при выборе цвета, который может выглядеть как единообразный блок, каждый раз будут возвращаться немного разные значения
Когда вы запускаете мой код с jonEg
, вы получаете (100 * 150 + 1) 15001 стилей, что занимает (15001 * 1749/1024/1024) ~ 25 МБ памяти для индекса только в Spout
. Опять же, этот индекс необходим для предотвращения того, чтобы каждая ячейка в Excel имела свой собственный стиль (но, конечно, в этом надуманном примере я убедился, что он бессмысленен - у каждой ячейки есть свой собственный стиль) - здесь используется ~ 100 МБ памяти
Когда вы запускаете мой код с jonEgQuant
(оставляя $by=16;
), требуется только 71 стиль (но это довольно экстремальное округление, позволяющее всего 4096 цветов ), - при этом используется ~ 2 МБ памяти
При установке $by=4
(внутри ColorBuilder::jonEgQuant
) - теперь у вас до четверти миллиона цветов Требуется 989 стилей с использованием ~ 7 МБ памяти (точнее, при открытии в Excel это похоже на jonEg
)
Spout
включает метод преобразования десятичных значений RGB в строку Color::rgb($r,$g,$b)
- но это не меняет результат (spoutDoc
метод)
Стиль имеет гораздо больше измерений, чем этот - Я использовал цвет фона, но есть передний план (ваш пример), подчеркивание, жирный, я tali c, начертание шрифта, размер шрифта - все это умножает количество стилей (сокращение цветового пространства здесь решает вашу задокументированную проблему, но может быть отменено путем изменения других компонентов стиля)
Take Aways
Проблема не в использовании шестнадцатеричных кодов, значений RGB или констант, а в количестве используемых вами стилей
Сократите количество используемых стилей - Excel не ожидает большого количества, Spout
также не оптимизировано для этого. Сжатие цвета (округляя значения, как с jonEgQuant
) - это одно направление
Улучшение механизма индекса стиля Spout
, но обратите внимание, что индекс использует только часть памяти - он не маленький, но это не единственный участник - каждый объект стиля необходим для генерации результата. Удаление этого кэша стилей (который для этого надуманного примера не помогает) увеличивает использование памяти до 40 МБ (и создает тот же файл Excel). Экономия на 60% - это не что иное, но больше стилей позволяет увеличить использование памяти, увеличить файлы Excel и медленнее открывать / отображать эти файлы
Тестирование заметок
Я пропустил чтение изображения / сбор цветов во время тестирования, поскольку это может увеличить использование памяти, но не суть проблемы
С $colCount=250;
(из вашего примера ) вы превышаете предел памяти по умолчанию 128 МБ в PHP, лучше установить его $colCount=100;
, и вы можете запустить все тесты, не изменяя этот параметр
Я переключился на настройку background-color легче увидеть разницу при открытии в Excel
Для каждого генератора цвета (jonEg
, spoutDoc
, fixedEg
, jonEgQuant
) генерируется другой XLSX ( помогает увидеть размер файла и различия при рендеринге)
Размеры файлов соответствуют сложности Excel - jonEg
- это файл размером 179 КБ (и Excel пытается открыть), тогда как jonEgQuant
- 43KB
Код
<?php
require_once 'Spout/Autoloader/autoload.php';
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use Box\Spout\Common\Entity\Style\Color;
use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
// -- -- Set this to one of the method names on ColorBuilder (that isn't a helper)
$choice='jonEg';
// -- --
class ColorBuilder {
static $defaultBlue=255;
static function jonEg($x,$y) {return sprintf("%02x%02x%02x", $x, $y, static::$defaultBlue);}
static function spoutDoc($x,$y) {return Color::rgb($x, $y, static::$defaultBlue);}
static function fixedEg($x,$y) {return Color::BLUE;}
static function jonEgQuant($x,$y) {$by=16;return sprintf("%02x%02x%02x", static::_quantize($x,$by),static::_quantize($y,$by), static::_quantize(static::$defaultBlue,$by));}
//Helpers - don't use these for choice
static function validate(string $name):bool {
if ($name==null) return false;//Null or empty
if (substr($name,0,1)=='_') return false;//Private by convention
if ($name==='validate') return false;//Not the function you seek
return method_exists('ColorBuilder',$name);
}
private static function _quantize(int $i,int $by=16):int {return round($i/$by)*$by;}
}
function createRow($y,$color) {
$colCount=100;
$row = [];
for($x=0; $x<$colCount; $x++) {
$row[] = WriterEntityFactory::createCell('*', (new StyleBuilder())->setBackgroundColor(ColorBuilder::$color($x,$y))->build());
}
return $row;
}
function buildSheet($name) {
if (!ColorBuilder::validate($name)) {throw new Error('Invalid color provider');}
$writer = WriterEntityFactory::createXLSXWriter();
$writer->openToFile('output/'.$name.'.xlsx');
for($y=0; $y<150; $y++) {
$writer->addRow(WriterEntityFactory::createRow(createRow($y,$name)));
}
$writer->close();
}
buildSheet($choice);
echo 'Peak memory usage: '.round(memory_get_peak_usage() / 1024).' KB';
* 1 140 * Технология: PHP 7.4.2 CLI, Spout: 3.1.0, Win: 7 x64 (I know), Coffee: Venti Dark