HotPDF tasottaa PDF-sivun kierron metodilla THotPDF.FlattenLoadedPageRotation: metodi kietoo jokaisen kierretyn sivun sisällön myötäpäiväiseen cm-muunnokseen, kirjoittaa uudelleen jokaisen sivulaatikon, jonka sivu oikeasti omistaa, kiertää huomautusgeometrian, ulkoasumatriisit, eksplisiittiset kohteet ja tagitetun rakennegeometrian samalla kulmalla ja asettaa lopuksi /Rotaten arvoon 0. Sivu näyttää katselimissa identtiseltä, mutta sen koordinaatisto on nyt pystyssä. Se alkaa merkitä sillä hetkellä, kun myöhempi työkalu, tulostin-RIP tai oma leimauskoodisi ohittaa /Rotaten ja sijoittaa asioita raakaan käyttäjätilaan
Tyypillinen laukaiseja on skanneri tai mobiilikaappaussovellus, joka kirjoittaa vaakasivut pystymedianä, johon on merkitty /Rotate 90. Jokainen katselin näyttää ne oikein, joten kukaan ei huomaa mitään, ennen kuin joku leimaa sivunumeron "oikeaan alakulmaan" ja se laskeutuu kyljelleen vasemmalla reunaa pitkin, tai kun asetteluaskel, joka lukee vain /MediaBoxin, asettelee pystyslotin vaakasivulle. Tasottaminen kuulostaa yhden rivin matriisityöltä. Käytännössä se koskettaa viittä sivulaatikkoa, kolmea lajia huomautusgeometriaa, asiakirjan linkkikohteita ja rakennepuuta, ja jokaisella niistä on oma sääntönsä standardissa ISO 32000-1
Kumpaan suuntaan /Rotate kiertää PDF-sivun?
/Rotate kiertää sivun myötäpäivään näyttöä ja tulostusta varten 90 asteen kertomissa (ISO 32000-1 §7.7.3.3, taulukko 30). 90 asteessa median vasemmasta reunasta tulee yläreuna ja yläreunasta oikea sivu, joten alaspäin kasvavassa y-laitetilassa kuvaus on X = (y - Bottom) * Scale ja Y = (x - Left) * Scale. 270 asteessa oikeasta reunasta tulee yläreuna. /Rotate on lisäksi yksi vain neljästä periytyvästä sivuattribuutista yhdessä /Resourcesin, /MediaBoxin ja /CropBoxin kanssa (§7.7.3.4), joten sivusanakirja ilman omaa /Rotatea voi yhä kiertyä /Pages-esivanhemman voimalla. THotPDF.GetLoadedPageRotation kävelee /Parent-ketjun läpi ja normalisoi tuloksen välille 0–359, ja tuo on arvo, jota haluat, ei sivun raaka avain
Suunnan saa helposti väärin tavalla, joka selviää testauksesta hengissä, ja aiemmat HotPDF-buildit tekivät juuri niin. Vanha sivusta laitteeseen -matriisi vaihtoi y-komponentit keskenään kulmilla 90 ja 270, mikä tuottaa heijastuksen diagonaalin yli kierron sijaan: matriisin orientaatio kääntyy verrattuna kierrättämättömään tapaukseen. Molemmat kulmat "näyttävät kierretyiltä" silti, bittikartta on vaihdetun levyinen ja korkea, ja kiekura sivusta näkymään ja takaisin palauttaa lähtöpisteen, joten dimensioiden tarkistukset ja kiekuratestit menevät kaikki läpi. Ainoa luotettava tarkistus on se, mihin kulmamerkki päätyy, verrattuna pikseli pikseliltä referenssirenderoijaan. Koska katselinmalli, SIMD-renderbackend ja korostusmappaus olivat kopioineet saman matriisin, kaikki ne korjattiin yhdessä, ja tasottamiskoodi käyttää nyt samaa myötäpäiväkonventiota kuin renderoija
Miten FlattenLoadedPageRotation kirjoittaa sivun uudelleen
FlattenLoadedPageRotation(PageRange, Info) käsittelee jokaisen PageRangein sivun, jonka efektiivinen kierto on 90, 180 tai 270, ja palauttaa tasottamiensa sivujen määrän. Tyhjä PageRange tarkoittaa kaikkia sivuja; muuten merkkijono noudattaa tavallista yhdestä alkavaa '1-3,7'-syntaksia, ja alueen ulkopuolinen sivunumero nostaa poikkeuksen ohittamisen sijaan. Alkuperäisiä sisältöstreameja ei koskaan koodata uudelleen. Metodi liittää uuden streamin, joka sisältää rivin q 0 -1 1 0 -Bottom Width+Left cm (90 astetta), sivun /Contentsin alkuun, lisää loppuun streamin, joka sisältää Q:n, ja kirjoittaa lopuksi eksplisiittisen /Rotate 0:n sivusanakirjaan, jotta /Pages-solmulta periytyvä arvo ei voi kiertää sivua toista kertaa
var
Pdf: THotPDF;
Info: THPDFRotationFlattenInfo;
Flattened: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('scanned-batch.pdf') > 0 then
begin
// '' = jokainen sivu; 0 asteen sivut skannataan mutta jätetään rauhaan
Flattened := Pdf.FlattenLoadedPageRotation('', Info);
Writeln(Format('Scanned %d, flattened %d pages', [Info.ScannedPageCount, Info.FlattenedPageCount]));
Writeln(Format('Turned %d annotations, %d destinations, %d tagged geometry entries',
[Info.TransformedAnnotationCount, Info.TransformedDestinationCount,
Info.TransformedStructureGeometryCount]));
if Flattened > 0 then
Pdf.SaveLoadedDocument('scanned-batch-upright.pdf');
end;
finally
Pdf.Free;
end;
end;
THPDFRotationFlattenInfo-tietue on syytä kirjata lokiin sen sijaan, että heittäisi sen menemään. ScannedPageCount on alueen koko, FlattenedPageCount on sama kuin paluuarvo, ja kolme Transformed...-laskuria kertovat, oliko asiakirjassa linkkejä, kirjanmerkkejä tai tagitettua geometriaa, jotka osoittivat kierrettyihin sivuihin. Erä, jossa jokainen tiedosto raportoi nollaa kohdetta, on kunnossa; tagitettu PDF/UA-tiedosto, joka raportoi nollaa rakennegeometriaa, kun odotit kuvioiden rajauslaatikoita, on merkki siitä, että se kannattaa tarkistaa käsin
Mitkä sivulaatikot tasottaminen kirjoittaa uudelleen, ja missä järjestyksessä?
Tasottaminen kirjoittaa uudelleen vain laatikot, jotka sivulla jo on, ja se lukee jokaisen laatikon ennen kuin kirjoittaa yhtäkään. Järjestyksellä on väliä oletusketjun vuoksi: GetLoadedPageBox(PageIndex, pbCropBox, ...) palauttaa /MediaBoxin, kun sivulla ei ole /CropBoxia, ja /BleedBox, /TrimBox ja /ArtBox olettuvat CropBoxiin (§14.11.2). Aiempi versio luki, muunsi ja kirjoitti yhden laatikon kerrallaan. Se kirjoitti MediaBoxin ensin, luki sitten "CropBoxin", sai takaisin jo kierretyyn MediaBoxin, kiersi sen toista kertaa ja kirjoitti CropBoxin, jota sivulla ei koskaan ollut, mikä rajasi vaakasivun neliöksi. Periytymissäännöt jakautuvat samalla tavalla: MediaBox ja CropBox haetaan /Parent-ketjua pitkin, kun taas Bleed, Trim ja ArtBox lasketaan vain, jos ne istuvat sivusanakirjassa itsessään, joten harhainen /TrimBox /Pages-solmulla käsitellään puuttuvana, eikä sitä koskaan kopioida sivulle
procedure DumpPageGeometry(Pdf: THotPDF; PageIndex: Integer);
var
L, B, R, T: Single;
begin
Writeln('Effective /Rotate: ', Pdf.GetLoadedPageRotation(PageIndex));
if Pdf.GetLoadedPageBox(PageIndex, pbMediaBox, L, B, R, T) then
Writeln(Format('MediaBox [%g %g %g %g]', [L, B, R, T]));
// Tosi ilmankin /TrimBox-avainta: arvo putoaa CropBoxiin ja sitten MediaBoxiin
if Pdf.GetLoadedPageBox(PageIndex, pbTrimBox, L, B, R, T) then
Writeln(Format('TrimBox [%g %g %g %g]', [L, B, R, T]));
// Esiasetettu Letter; GetLoadedPageVisibleBox jättää tulosteet koskemattomiksi epäonnistuessaan
L := 0; B := 0; R := 612; T := 792;
Pdf.GetLoadedPageVisibleBox(PageIndex, L, B, R, T);
Writeln(Format('Visible [%g %g %g %g]', [L, B, R, T]));
end;
Aja tuo apufunktio ennen tasottamista ja sen jälkeen, ja luvut selittävät itse itsensä. 90-asteiselle sivulle, jonka MediaBox on [0 0 612 792], tasotettu MediaBox muuttuu muodoksi [0 0 792 612]; jokainen uudelleenkirjoitettu laatikko kuvataan saman myötäpäiväkierroksen läpi alkuperäisen MediaBoxin origoon nähden, joten uusi MediaBox alkaa aina origosta ja muut laatikot säilyttävät asemansa sen sisällä. GetLoadedPageVisibleBox palauttaa sen, mitä katselimet näyttävät ja tulostimet tulostavat, CropBoxin rajattuna MediaBoxiin ja normalisoituna niin, että Left on pienempi kuin Right, ja HotPDF:n renderoija, SVG-vienti, katselin ja tulostuspolku käyttävät kaikkia sitä samaa laatikkoa. Kun tarvitset sen sivukoon, jonka ihminen näkee, kutsu funktiota GetLoadedPageVisibleBox /MediaBoxin lukemisen sijaan
Miksi huomautukset rikkoutuvat, jos kiertää vain /Rectin?
Huomautukset rikkoutuvat, koska ulkoasustreamia ei piirretä suoraan /Rectiin. Kohdan §12.5.5 mukaan katselin muuntaa ensin formia /BBoxin sen /Matrixilla ja skaalaa ja siirtää sitten kyseisen tuloksen rajauslaatikon /Rectiin. Kierrä vain /Rect, ja 200 × 40:n leima puristuu 40 × 200:n slottiin, lukukelvottomana ja kyljellään. FlattenLoadedPageRotation kertoo siksi sivun myötäpäiväkierroksen oikealta jokaisen ulkoasun /Matrixin perään (90 astetta: [0 -1 1 0 0 0] rivivektorikonventiossa) kaikissa /N-, /R- ja /D-ulkoasuissa ja jokaisessa niiden sisällä olevassa tilassa. Yhden ulkoasustreamin voi jakaa useampi huomautus tai tila, joten jokainen stream kierrätetään täsmälleen kerran per kutsu. Ainoa tapaus ilman siistiä vastausta on stream, joka on jaettu sivujen välillä eri kieroilla; se seuraa ensimmäistä sivua, joka siihen ulottuu
Kaksi sääntöä lisää pitävät lomakekentät ja muistilaput paikoillaan. Widgetin /MK /R-merkintä (§12.5.6.19) on vastapäiväkulma, joten sivun myötäpäiväkulma vähennetään siitä modulo 360; jos tuon ohittaa, seuraava ulkoasun uudelleengenerointi piirtää kenttätekstin väärään suuntaan. NoRotate-lipun (bittipaikka 5, arvo 16, §12.5.3) kantavat huomautukset pysyvät pystyssä kierretyllä sivulla ja kääntyvät /Rectinsä vasemman yläkulman ympäri, joten tasottaminen säilyttää niiden leveyden, korkeuden ja pystyasun ja siirtää vain kyseisen kulman sinne, mihin kierto sen laittaa. Huomautusten lisäksi metodi kiertää myös /QuadPointsit, /Verticesit, /Lin ja /InkListin, kirjoittaa uudelleen sivun nimeävät eksplisiittiset kohteet (/XYZ-pisteet, /FitR-suorakulmiot sekä /FitH / /FitV vaihdettuina kulmilla 90 ja 270, §12.3.2.2) ja muuntaa tagitettua geometriaa, kuten attribuutin /BBox-merkintöjä rakennealkioille, joiden /Pg on kyseinen sivu
Mitä tasottaminen ei kata?
Tasottaminen on yhden sivun omien objektien geometrinen uudelleenkirjoitus, ja useat tilanteet jäävät sen ulkopuolelle hiljaa, eivät äänekkäästi
- Sivut, joiden efektiivinen kierto on jo 0 tai joiden MediaBox puuttuu tai on nollan levyinen tai korkea, ohitetaan ilman virhettä; vertaa paluuarvoa siihen sivumäärään, jonka odotit muuttuvan
- Sivuresursseista viitatut form-XObjectit pitävät oman
/BBoxinsa formin tilassa, koska uloincmkiertää niitä jo; rakennepuun skannaus seuraa vain/Kta ja/Ata, joten se ei koskaan kävele sivun resursseihin tai huomautuksiin toista kertaa - Kohteet löydetään skannaamalla jokainen epäsuora objekti kerran tasotettua sivua kohti, joten suuri asiakirja, jossa on satoja kierrettyjä sivuja, maksaa sen kävelyn jokaisesta
- HotPDF:n sivurenderoija ei piirrä huomautuksia, joten kierrettyjen leimojen visuaalinen tarkistus tarvitsee ensin funktion
FlattenLoadedAnnotations
// Paista ulkoasut sisältöön, jotta renderoija voi näyttää ne,
// ja renderöi sivu 1 ennen ja jälkeen sen /Rotaten poistamisen
Pdf.FlattenLoadedAnnotations('1');
Before := Pdf.RenderLoadedPageToBitmap(0, 96);
try
Pdf.FlattenLoadedPageRotation('1', Info);
After := Pdf.RenderLoadedPageToBitmap(0, 96);
try
Assert((Before.Width = After.Width) and (Before.Height = After.Height));
// Vertaa kulmamerkin pikseleitä tässä, ei vain dimensioita
finally
After.Free;
end;
finally
Before.Free;
end;
Syvempää taustaa varten tämän tarinan huomautuspuoli jatkuu kirjoituksessa huomautusten ulkoasujen syntetisointi ennen niiden tasottamista, vertailun taustalla oleva renderoija on käsitelty kirjoituksessa ladatun PDF-sivun renderöinti bittikartaksi, ja mustaus ja N-up-ompeleminen ladatuille PDF:ille näyttää saman sisältöstreamiin liittämisen tekniikan, johon kiertoetuliite ja -loppuliite nojaavat. HotPDF, mukaan lukien FlattenLoadedPageRotation ja sivulaatikoiden lukijat, on saatavilla Delphille ja C++Builderille HotPDF Delphi PDF component -sivulla