Műszaki cikk

Rowspan rácsok és ismételt táblafejlécek a HotPDF-ben

A HotPDF a HTML táblákat a HTML5 paged-media profilján keresztül rendereli valódi elfoglaltsági ráccsal a rowspan és colspan kezelésére, mért sormagasságokkal karakterbecslések helyett, és minden folytatólagos oldalon megismételt fejlécsorokkal. Két helyzetben tagadja meg a fejléc ismétlését, és ezeket előre ismerni olcsóbb, mint később egy duplikált cellát debuggolni

A dokumentumosztály, amely ezt kikényszeríti, az, amelyet minden jelentéskészítő csapat előbb-utóbb szállít: egy számla vagy megfelelőségi jelentés, ahol az igazságforrás HTML, a tábla négy oldalon fut át, és a fejlécnek mindegyikén olvashatónak kell lennie. Bármi, ami kevesebb egy valódi táblaelrendezésnél, előállítja a két hibát, amelyet az olvasók azonnal észrevesznek: egy fejlécet, amely csak az első oldalon jelenik meg, és sorokat, amelyeknek a magasságát karakterből találták ki

Miért költözött a táblaképesség a HTML renderelőbe?

Mert az alternatíva elveszíti a rich textet, a rich text pedig az az ok, amiért a tartalom egyáltalán HTML. A kézenfekvő terv újrahasznosításnak tűnik: a HotPDFnek már van elrendezési DOM táblaobjektuma korrekt rácsgyal, bridzselje rá a HTML elemzőt, és a spanning ingyen jön. A gond az, hogy min rajzol az a táblaobjektum. Cellái szöveget és stílust hordoznak, a rajzolási útja sima szövegkimenetet ad, tehát bármi, amit a HTML ténylegesen tartalmazott a fonton és a színen túl, linkek, felső indexek, inline méretváltások, futásonkénti szín, odaveszik, mire az oldalra ér

A valós dokumentumokkal való találkozást túlélt irány a fordított. Vigye át a táblamotor képességeit, az elfoglaltsági rácsot, a valódi mérést, a fejlécismétlést és az oszlopsúlyozást a HTML renderelőbe, és hagyja a rich text renderelést ott, ahol már működik. Ez nagyobb változtatás, mint a híd, és ez az, amely azt biztosítja, hogy egy táblacellán belüli hyperlink hyperlink maradjon

Rowspan union-find nélkül

A spanning cellák atomi sorcsoportokat hoznak létre, de a csoportok feletti lezárás nem igényel általános disjoint-set struktúrát, mert az elfoglaltság mindig összefüggő intervallum. Egy K sorban kezdődő rowspan="3"-os cella a K-tól K+2-ig terjedő sorokat foglalja el, semmi mást, tehát a csoportinformáció soronkénti végejelre redukálódik

Az algoritmus két szándéksor. Amikor elhelyez egy K-ban kezdődő és E-ben végződő spanning cellát, jegyezze fel: GroupEnd[K] := Max(GroupEnd[K], E). Aztán járja be egyszer a sorokat visszafelé, és alkalmazza a G[R] := G[G[R]] lépést, amely az egyes sorvégeket visszafelé terjeszti az átfedő spaneken át, és egy menetben adja meg a tranzitív lezárást. Amit kap, minden sorhoz az utolsó sort, amelynek vele azonos oldalon kell maradnia, és pontosan ezt kell a lapozási lépésnek ahhoz, hogy eldöntse, hová eshet törés

A magasság elosztása a másik fele. Amikor egy spanning cellának több függőleges hely kell, mint amennyit az általa lefedett sorok jelenleg adnak, a többlet a span utolsó sorára megy, nem oszlik el egyenletesen köztük. Kezelje a spanning cellákat azután, hogy a közönséges sormagasságok kialakultak, majd töltse fel minden span utolsó sorát. A többlet egyenletes szétosztása méltányosabbnak tűnik, és láthatóan rossz kimenetet ad: azok a sorok, amelyek csak rövid, egysoros cellákat tartalmaznak, felfúvódnak, mert három sorral odébb véletlenül egy magas cella volt

Egy HotPDF HTML táblarács, amelyben egy 2. sorban kezdődő, rowspan 3 cella egyetlen atomi téglalapként foglalja el a 2-től 4-ig terjedő sorokat, mellette a soronkénti csoportvég értékekkel, a G(R)-rel, amelyeket egyetlen visszafelé tartó bejárás ad, és amelyek azt mutatják, hogy a 2, 3 és 4-es sorok ugyanahhoz az oldalhoz kötődnek
A spanning elfoglaltság mindig összefüggő intervallum, ezért a soronkénti végejelek és egyetlen visszafelé tartó bejárás kiváltja a union-findet, és pontosan megmondja a lapozásnak, hová eshet törés
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;

A RenderHTML5 opcionális szerzői stíluslapot fogad második argumentumként, és a nyomtatási szabályok ide tartoznak. Tartsa távol tőle a képernyős stíluslapot. A profil verziózott, és a HTML5ProfileMilestones jelenti, hogy az aktuális build mely képességcsoportokat valósítja meg: himParserCascade, himPagedLayout, himTablesForms és himBoundedResources, tehát az alkalmazás tudatosan foszlhat le, ahelyett hogy egy hézagot gyártás közben fedezne fel

A méretnek pontosan egyet kell értenie a rajzolással

A sormagasság csak akkor helyes, ha a tördelt sorokat mérő kód ugyanazzal a szabállyal töri őket, mint amelyik rajzolja őket. Ez nyilvánvalónak hangzik, és ez a leggyakoribb forrása azoknak a tábláknak, amelyeknek a keretei nem állnak egybe a tartalmukkal. A HotPDF mohó sorszámlálóval mér, és annak a számlálónak három konkrét pontban kell egyeznie a rich text kimeneti út tördelési szemantikájával: csak szóközön törik, sosem darabol szót, és az oszlopnál szélesebb szó saját sort kap

A második követelmény a font. A mérésnek a cella saját fontjával kell futnia, amelyet a SetFont-tal állítanak be a tényleges névvel, stílussal és mérettel a szélességfüggvény hívása előtt, nem azzal, amelyik épp aktív volt. A félkövér szöveg azonos méretben rutinszerűen több mint tíz százalékkal szélesebb a szokásosnál, ami elég ahhoz, hogy egy háromsoros cella négysoros cellává váljon. Egy tábla, amelynek fejléccellái félkövérek, a többi cellája nem, és egyetlen fonttal mérték, pontosan azokban a sorokban lesz rossz, amelyeket az olvasók először megnéznek

Ennek eltalálása megváltoztatja, mit állíthat egy tesztben. A pontos mérés megfigyelhető hatása a sorköz, nem a glifaszám: egy egysoros sor nagyjából 20 pont magas, míg ugyanennek a tartalomnak a karakterből számolt becslése két sort és nagyjából 35-öt jelez. Állítson a sorok közti függőleges távolságról. És ne feledje, hogy a PDF felhasználói terében az Y felfelé nő, tehát az a fejléc, amely egy tartalmi sor felett ül, nagyobb Y értéket visel, ami ellenkezője annak, amit a képernyőkoordináta-ösztön ír

Mikor tagadja meg a HotPDF a fejléc ismétlését?

Két esetben, amelyek mindegyike láthatóan rossz kimenetet adna, ha továbbmenne. Az első egy fejlécblokk, amely olyan spanning cellát tartalmaz, amely a fejlécen túl a tartalmi sorokba nyúlik. A fejléc ismétlése másodszor is megrajzolná azt a cellatartalmat egy olyan pozícióban, ahová már nem tartozik, tehát a fejléc egyszer rajzolódik, és a tábla nélküle folytatódik. A második egy 90 százaléknál magasabb fejléc a használható oldalmagasságból, ahol az ismétlés szinte sehol sem hagyna helyet az adatnak, és a tábla nem haladna előre

A HotPDF döntési folyamata a HTML táblafejlécek oldaltöréseken átmenő ismétlésére: az a fejléc, amelynek a rowspanja átnyúlik a tartalmi sorokba, egyszer rajzolódik, a használható oldalmagasság 90 százalékánál magasabb fejléc egyszer rajzolódik, és minden más fejléc megismétlődik minden folytatólagos oldalon
A két elutasítás szándékos: az a fejléc, amely tartalmi spanning cellát birtokol vagy kitölti az oldal nagy részét, ismételve olyan tartalmat rajzolna, amely már nem oda tartozik, vagy sehol sem hagyna helyet az adatnak

Mindkét elutasítás szándékos és tervezésből csendes, mert az alternatíva rosszabb. Ha a fejléce nem ismétlődik, pedig várta, ellenőrizze a markupban, van-e a thead határát átlépő rowspan, mielőtt a motort gyanúsítaná. Ez az egyetlen markupminta adja a meglepetések nagy részét

// Az oszlopsúlyok a markupból jönnek, tehát a nyomtatási stíluslap az a hely,
// ahol szabályozhatja őket. A szélességek súlyként kezelődnek, nem pixeleként
const
  PrintStyleSheet =
    'table { width: 100%; }' +
    'thead th { font-weight: bold; background: #eee; }' +
    'td.amount { text-align: right; }';

// Az a fejlécsor, amely a tartalomba nyúló rowspant visel, elnyomja a
// fejlécismétlést. Tartsa a spaneket egy szakaszon belül:
//   <thead><tr><th rowspan="2">Item</th>...</tr></thead>  rendben
//   <tr><th rowspan="3">Item</th>...  átnyúlik a tbodyba, nincs ismétlés

Az oszlopszélességek súlyként viselkednek, nem abszolút mérésként, és ez a viselkedés tartja használhatónak a táblát, amikor a tartalom nem egyezik a szerzői becsléssel. A 30 százalékra deklarált oszlop nagyjából a rendelkezésre álló szélesség 30 százalékát kapja, de az elosztás tiszteletben tartja az egyes oszlopok tényleges minimális szélességét, tehát egy hosszú, tördelhetetlen tokent viselő keskeny oszlop csendben nem túlfolyik a tábladobozon

Hol ül ez egy dokumentumfolyamatban

A táblamunka a tágabb paged-media profilon belül ül, és a HTML5 paged-media importút cikkben leírt lapozási szabályok, erőforrás-keretek és CSS kezelés változatlanul érvényesek a táblákat tartalmazó dokumentumokra. Ha az adata nem HTML-ként indul, a táblák egyenesen PDF-be építése cikk közvetlen építési útja teljesen kihagyja az elemzőréteget, és ugyanazt a rácsviselkedést adja API-n keresztül. És mivel a sormagasság végső soron azon múlik, hol törnek a sorok, a szövegigazítás és sortörés cikk mérésről szóló része a kísérő olvasmány mindenkinek, aki sűrű táblázatos kimenetet hangol

A hasznosítható tanulság itt egyáltalán nem a táblákról szól. Amikor egy új alrendszernek olyan képességre van szüksége, amelyet egy régi alrendszer már birtokol, kérdezze meg, melyikük birtokolja azt a dolgot, amelyet a legnehezebb újraimplementálni. A rácsaritmetika néhány tucat sor, és könnyen költözik. A rich text renderelés inline linkekkel, felső indexekkel és futásonkénti stílussal nem az, tehát a rács költözött, és a szöveg maradt. A HotPDF mindkét utat szállítja a HotPDF Delphi PDF komponens részeként, tehát a HTML bemenet és a közvetlen építés közti választás projekt döntés, nem könyvtári döntés