Техническа статия

Експорт на диапазон от клетки като изображение с HotXLS

Понякога резултатът не е документ, а картина на таблица; Резюме блок в статус имейл, изобразено KPI табло в dashboard, миниатюра до резултат от търсене: всички искат клетките и никой не иска хартия; TXLSCellImageExporter в HotXLS взема класически или XLSX правоъгълник от клетки и произвежда едно компактно PNG или JPEG без размер на страница, без полета, без горни или долни колонтитули, без print заглавия и без прекъсвания на страници; Резолюция, мащаб, формат и JPEG качество са конфигурируеми, обектите, мрежовите линии и границите на клетки имат независими ключове, фонът може да бъде цвят или прозрачен, а записът на файла минава през атомна замяна в същата папка, която оставя съществуваща цел недокосната, ако нещо се провали

Причината това да се нуждае от собствен exporter, а не от ключ върху пътя за печат, е, че пагинацията не е опционален слой, който можете да изключите; Тя е нещото, заради което page конвейерът съществува

Защо да не изобразите диапазона през печатния конвейер?

Защото печатният конвейер поставя страница между вас и клетките; Размерът на хартията решава колко се побира, полетата бутат съдържанието навътре, горните и долни колонтитули заемат ленти, които не сте искали, print заглавията повтарят редове, които вече имате, а прекъсванията на страници разделят диапазона; Резюме блок, който случайно застъпва прекъсване, излиза като две изображения с интересния ред отрязан наполовина; Можете да компенсирате всичко това, като настроите собствен размер на страница, който съвпада точно с диапазона, и хората го правят, но това означава преизчисляване на хартиената геометрия всеки път, когато диапазонът се промени и все още оставя лентата на колонтитула и print-title логиката в пътя

Cell exporter-ът измерва правоъгълника, разпределя bitmap с точно този размер, рисува клетките в него и кодира; Няма страница, така че няма какво да се конфигурира прочерк; За случаите, в които наистина искате хартия, PDF export пътят е правилният инструмент и е разгледан в статията за PDF export на работен лист

TXLSCellImageExporter измерва, рисува и кодира едно изображение на диапазон от клетки, докато печатният конвейер разделя диапазона при прекъсвания на страници
Page конвейерът поставя хартиена геометрия между вас и клетките; cell exporter-ът няма страница никъде в пътя

Измерете, преди да изобразите

Measure връща пикселните размери, които текущите настройки биха произвели, без да кодира нищо; Това има значение по две причини; HTML или имейл шаблон обикновено се нуждае от размерите на изображението, преди изображението да съществува, така че да резервира кутията и да избегне разместване на оформлението; И услуга, която изобразява избрани от потребителя диапазони, се нуждае от начин да отхвърли абсурдна заявка, преди да разпредели за нея

uses
  lxHandleX, lxPagination;

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Exporter: TXLSCellImageExporter;
  Summary: TXLSXRange;
  W, H, Bytes: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarter.xlsx');
    Sheet := Book.Sheets.ByPos[0];
    Summary := Sheet.Range['A1:F20'];

    Exporter := TXLSCellImageExporter.Create;
    try
      Exporter.ImageFormat := xpifPng;   // PNG държи тънките щрихи остри
      Exporter.DPI := 96;
      Exporter.Scale := 2.0;             // изход с retina плътност
      Exporter.IncludeGridlines := False;
      Exporter.IncludeCellBorders := True;
      Exporter.TransparentBackground := True;
      Exporter.MaxPixels := 40 * 1000 * 1000;
      Exporter.MaxBytes := 8 * 1024 * 1024;

      if not Exporter.Measure(Summary, W, H) then
        raise Exception.Create('range exceeds the configured budget');
      // W и H вече са известни; резервирайте layout кутията преди кодиране
      Bytes := Exporter.Save(Summary, 'summary.png');
      if Bytes <= 0 then
        raise Exception.Create('image export failed, previous file kept');
    finally
      Exporter.Free;
    end;
  finally
    Book.Free;
  end;
end;

Бюджети, защото мащабът умножава

MaxPixels и MaxBytes не са защитна украса; Броят пиксели расте с квадрата на коефициента на мащаб и с квадрата на съотношението на резолюцията, така че диапазон, който е разумен 1200 на 800 при 96 DPI, става приблизително 47 мегапиксела при 600 DPI, а потребител, който избере целия използван диапазон вместо резюме блок, добавя друга порядък величина върху това; Без таван режимът на отказ е разпределение, което процесът не може да задоволи, което събаря каквото и да е друго, което процесът правеше

С таван заявката се проваля и извикващият може да избере: отказ, намаляване на мащаба или стесняване на диапазона; Това е много по-добра позиция за сървър за отчети и е същото разсъждение зад изричните бюджети в metafile декодера, описани в статията за оградения EMF и WMF декодер

Бюджетен поток за TXLSCellImageExporter в HotXLS: Measure връща пикселния размер първо, после MaxPixels и MaxBytes ограничават разпределението и размера на изхода
Отказът се случва преди разпределение и провал на байтов бюджет оставя предишното изображение недокоснато за извикващия

Атомна замяна и защо папката има значение

Save към име на файл не записва в целта; Той записва временен файл в същата папка, кодира в него и чак тогава заменя целта; Ако кодирането се провали, ако бюджетът е надхвърлен по средата или ако процесът е убит, предишното изображение все още е там и все още е валидно; Dashboard, който регенерира своите плочки по график, следователно никога не показва отрязано PNG, което е обичайният симптом на наивен запис, който отваря дестинацията и започва да стриймва

Детайлът същата папка не е случаен; Атомна замяна е атомна само вътре в един том, защото между томове операционната система трябва да копира и после да изтрие, което въвежда наново прозореца, който се опитвахте да затворите; Всяка имплементация на този образец, която слага временния си файл в системната temp директория, не е атомна на машина, където изходът живее на различно устройство

TXLSCellImageExporter Save кодира във временен файл в същата папка, после заменя целта атомно; провалите оставят предишното изображение валидно
Временният файл трябва да живее до целта, защото атомна замяна работи само вътре в един том

Paint събития рисуват върху истинския canvas

И range exporter-ът, и page exporter-ът излагат водещи и завършващи paint събития и те получават пълен read-only контекст, а не просто canvas handle; TXLSPagePaintContext носи живия canvas, пикселните граници, размера на страницата в точки, резолюцията и мащаба действително използвани, номера на страницата на документа, номера на страницата в листа, общия брой страници, името на листа и изходния работен лист в класически и XLSX варианти; Това е достатъчно, за да нарисувате воден знак, който се мащабира правилно, или страница печат, който знае къде е в изпълнението

procedure TReportJob.StampDraft(Sender: TObject;
  const AContext: TXLSPagePaintContext);
begin
  // Съзнаващ мащаба, така че печатът изглежда същият при 1x и 3x
  AContext.Canvas.Font.Height := Round(-48 * AContext.Scale);
  AContext.Canvas.Font.Color := clSilver;
  AContext.Canvas.Brush.Style := bsClear;
  AContext.Canvas.TextOut(AContext.Bounds.Left + Round(24 * AContext.Scale),
    AContext.Bounds.Top + Round(24 * AContext.Scale), 'DRAFT');
end;

Exporter.AfterPaint := Job.StampDraft;

Три поведения си заслужават да се разчита на тях; Събитията изстрелват точно веднъж на изобразена рамка, включително всяка рамка на многостраничен TIFF, така че брояч, увеличаван в handler-а, е доверен; Те остават мълчаливи по време на измерване, така че handler със страничен ефект не тича два пъти за един изход; И ако водещото събитие хвърли, завършващото събитие не изстрелва и никакви частични байтове на изображение не се записват, така че изключение във вашия собствен код за рисуване не може да произведе наполовина печатен файл

Избор на формат

PNG за всичко, натежало с текст; JPEG прилага блокова трансформация, която произвежда видимо звънене около тънки висококонтрастни щрихи, което е точно това, което са границите на клетки и малкият текст, а артефактите оцеляват при настройки за качество, при които фотография изглежда перфектна; JPEG заслужава мястото си, когато диапазонът е доминиран от вградени фотографии и размерът на файла има повече значение от точността на ръбовете; Прозрачни фонове изискват PNG, тъй като JPEG няма alpha канал, така че плочка, предназначена да седи върху цветна повърхност, е взела избора вместо вас

Ако вашият диапазон съдържа слети клетки, проверете изхода спрямо листа: слети региони взаимодействат с ширините на колони по начини, които изненадват хората, а правилата за оформление са разгледани в статията за слети клетки и шаблони за отчети; HotXLS чете и записва XLS, XLSX, ODS и CSV от Delphi и C++Builder без Excel зависимост и пълната повърхност на exporter-а е документирана на продуктовата страница HotXLS Delphi spreadsheet component