HotPDF рендира HTML таблици през своя HTML5 paged-media профил с реална occupancy решетка за rowspan и colspan, измерени височини на редовете вместо оценки по брой символи, и заглавни редове, повтаряни на всяка страница-продължение. Две ситуации го карат да откаже да повтори заглавието, а познаването им предварително е по-евтино от дебъгване на дублирана клетка после
Класът документи, който налага това, е този, който всеки reporting екип рано или късно ship-ва: фактура или compliance доклад, където източникът на истина е HTML, таблицата минава през четири страници, а заглавието трябва да е четимо на всяка от тях. Всичко по-малко от истинско таблично оформление ражда двата провала, които читателите забелязват мигновено: заглавие, появило се веднъж на първа страница, и редове, чиято височина е отгатната по брой символи
Защо табличната възможност се премести в HTML рендерера?
Защото алтернативата губи rich text, а rich text е причината съдържанието изобщо да е HTML. Очевидният план изглежда като преизползване: HotPDF вече има layout DOM table обект с истинска решетка, значи свържи HTML парсера с него и вземи spanning-а наготово. Проблемът е с какво този table обект чертае. Клетките му носят текст и стил, а пътят му за чертаене излъчва plain text изход, така че всичко, което HTML реално е съдържал отвъд шрифт и цвят – линкове, superscript, inline смени на размер, цвят на выполнение – е изчезнало, докато стигне страницата
Посоката, оцеляваща при контакт с истински документи, е обратната. Преместете възможностите на table engine-а – occupancy решетката, реалното измерване, повторението на заглавията и претеглянето на колони – в HTML рендерера, и оставете rich-text рендирането там, където вече работи. Това е по-голяма промяна от моста и е промяната, която пази хиперлинк в клетка на таблица хиперлинк
Rowspan без union-find
Разпънатите клетки създават атомарни групи от редове, но затварянето върху тези групи не се нуждае от обща disjoint-set структура, защото occupancy-то е винаги непрекъснат интервал. Клетка с rowspan="3", започваща на ред K, заема редове K до K+2 и нищо друго, така че груповата информация се свежда до маркер за край на ред
Алгоритъмът е два реда намерение. Когато поставите разпънка, започваща на K и свършваща на E, запишете GroupEnd[K] := Max(GroupEnd[K], E). После минете редовете веднъж в обратен ред и приложете G[R] := G[G[R]], което разпространява всеки край на ред назад през припокриващи се разпънки и дава транзитивното затваряне в един пас. Това, което получавате, е за всеки ред последният ред, който трябва да остане на същата страница с него – точно това, от което стъпката на пагинация се нуждае, за да реши къде може да падне разрез
Разпределянето на височината е другата половина. Когато разпънка се нуждае от повече вертикално пространство, отколкото редовете, които покрива, осигуряват в момента, излишъкът отива при последния ред на разпънката, а не се разпределя равномерно. Обработете разпънките, след като обикновените височини на редовете са уредени, после долейте на последния ред на всяка разпънка. Разпределянето на излишъка равномерно изглежда по-честно и ражда видимо грешен изход: редове, съдържащи само къси едноредови клетки, се надуват, защото някоя несвързана клетка три реда по-нагоре случайно е била висока
var
Pdf: THotPDF;
Importer: THPDFHTMLImporter;
Stats: THPDFHTMLImportStatistics;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'audit-report.pdf';
Pdf.BeginDoc;
Importer := THPDFHTMLImporter.Create(Pdf);
try
Importer.Margin := 48;
Importer.BaseFontName := 'Arial';
Importer.BaseFontSize := 10;
Importer.MaxDOMNodes := 200000;
Importer.MaxLayoutOperations := 2000000;
if Importer.RenderHTML5(SourceHtml, PrintStyleSheet) then
begin
Stats := Importer.Statistics;
Writeln('tables ', Stats.TableCount,
' page breaks ', Stats.PageBreakCount);
end;
finally
Importer.Free;
end;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
RenderHTML5 приема незадължителен author style sheet като втори аргумент, и там е мястото на print правилата. Дръжте screen style sheet-а далеч от него. Профилът е версиониран и HTML5ProfileMilestones докладва кои групи възможности текущият build имплементира – himParserCascade, himPagedLayout, himTablesForms и himBoundedResources – така че приложение може да деградира нарочно, вместо да открие дупка в продукция
Измерването трябва да съвпада с чертаенето, точно
Височината на ред е коректна само когато кодът, измерващ пренесените редове, ги пренася по същото правило като кодът, който ги чертае. Звучи очевидно и е най-честият източник на таблици, чиито граници не се изравняват със съдържанието им. HotPDF измерва с алчен брояч на редове, и този брояч трябва да пасва на wrapping семантиката на rich-text изходния път в три конкретни отношения: чупи само на интервали, никога не разделя дума, а дума, по-широка от колоната, получава собствен ред
Второто изискване е шрифтът. Измерването трябва да върви с собствения шрифт на клетката, зададен чрез SetFont с истинското име, стил и размер преди викането на функцията за широчина, а не с който и да е шрифт, случайно активен. Bold текстът обикновено е с над десет процента по-широк от обикновения при същия размер, което стига да превърне триредова клетка в четириредова. Таблица, където заглавните клетки са bold, а телесните не са, измерена с един шрифт, ще греши точно в редовете, които читателите гледат първи
Да го свършите право мени това, което можете да утверждавате в тест. Наблюдаемият ефект на точно измерване е редовото разстояние, не броят глифи: едноредов ред е висок около 20 пункта, докато оценка по брой символи на същото съдържание предсказва два реда и около 35. Утвърждавайте вертикалното разстояние между редовете. И помнете, че PDF user space има Y нарастващо нагоре, така че заглавие, седящо над телесен ред, значи Y стойността на заглавието е по-голямата – обратното на това, което екранно-координатният инстинкт пише
Кога HotPDF отказва да повтори заглавие?
В два случая, като и в двия по-нататък би произвел видимо грешен изход. Първият е заглавен блок, съдържащ разпънка, минаваща отвъд заглавието в телесните редове. Повторението би начертало съдържанието на тази клетка втори път на място, където вече не принадлежи, затова заглавието се чертае веднъж, а таблицата продължава без него. Вторият е заглавие, по-високо от 90 процента от използваемата височина на страницата, където повторението би оставило почти никакво място за данни и таблицата нямаше да прави напредък
И двата отказа са нарочни и тихи по замисъл, защото алтернативата е по-лоша. Ако заглавието ви не се повтаря, а очаквахте да се, проверете markup-а за rowspan, минаващ границата на thead, преди да подозирате двигателя. Този единствен markup модел обяснява по-голямата част от изненадата
// Теглата на колоните идват от markup-а, затова print style sheet-ът е
// мястото да ги контролирате. Широчините се третират като тегла, не като пиксели
const
PrintStyleSheet =
'table { width: 100%; }' +
'thead th { font-weight: bold; background: #eee; }' +
'td.amount { text-align: right; }';
// Заглавен ред, носещ rowspan, минаващ в тялото, потиска
// повторението на заглавието. Дръжте разпънките в една секция:
// <thead><tr><th rowspan="2">Item</th>...</tr></thead> добре
// <tr><th rowspan="3">Item</th>... минава в tbody, без повторение
Широчините на колоните се държат като тегла, а не като абсолютни измервания, което е поведението, пазещо таблицата годна, когато съдържанието не пасва на оценката на автора. Колона, обявена на 30 процента, получава грубо 30 процента от наличната широчина, но разпределението уважава минималната широчина, от която всяка колона реално се нуждае, така че тясна колона, държаща дълъг неразделим токен, не прелива тихо табличната кутия
Къде се намества това в документен pipeline
Табличната работа стои в по-широкия paged-media профил, а правилата за пагинация, ресурсните бюджети и обработката на CSS, описани в HTML5 paged-media импортния път, важат непроменени за документи, съдържащи таблици. Ако данните ви не започват като HTML, пътят на директно конструиране в граждане на таблици направо в PDF избягва парсерния слой изцяло и ви дава същото поведение на решетката през API. И тъй като височината на ред в крайна сметка зависи от това къде се чупят редовете, разсъждението за измерване в подравняване на текст и пренасяне на редове е спътникът за всеки, настройващ плътен табличен изход
Преносимият урок тук изобщо не е за таблици. Когато нова подсистема се нуждае от възможност, която стара подсистема вече има, запитайте се коя от двете притежава нещото, най-трудно за преимплементиране. Решетъчната аритметика е няколко десетки реда и се мести лесно. Rich-text рендирането с inline линкове, superscript и стил на выполнение – не е, затова решетката се премести, а текстът остана. HotPDF ship-ва и двата пътя като част от HotPDF Delphi PDF компонента, така че изборът между HTML вход и директно конструиране е решение на проекта, а не на библиотеката