HotPDF izravnava rotaciju PDF stranice metodom THotPDF.FlattenLoadedPageRotation: metoda umota sadržaj svake rotirane stranice u cm transformaciju u smeru kazaljke, prepravlja svaki page box koji stranica zaista ima, okrene geometriju anotacija, matrice izgleda, eksplicitne destinacije i geometriju strukturiranog označavanja za isti ugao, a zatim postavi /Rotate na 0. Stranica u pregledaču izgleda identično, ali njen koordinatni sistem je sada uspravan. To postaje bitno u trenutku kada neki nizvodni alat, print RIP ili vaš kod za žigove ignorišu /Rotate i stvari smeštaju u sirovi user space
Tipičan okidač je skener ili mobilna aplikacija za uhvatanje koja pejzažne stranice upisuje kao portret medij sa /Rotate 90. Svaki pregledač ih prikazuje ispravno, pa niko ne primećuje dok neko ne utisne broj stranice na „donju desnu“ stranu i on sleti bočno uz levu ivicu, ili korak impozicije koji čita samo /MediaBox za pejzažnu stranicu ne sledi portret ležaj. Izravnavanje zvuči kao posao od jedne matrice. U praksi dodiruje pet page boxova, tri vrste geometrije anotacija, linkove mete dokumenta i strukturno stablo, i svako od toga ima svoje pravilo u ISO 32000-1
Na koju stranu /Rotate okreće PDF stranicu?
/Rotate okreće stranicu u smeru kazaljke za prikaz i štampu, u umnošcima od 90 stepeni (ISO 32000-1 §7.7.3.3, tabela 30). Na 90 stepeni leva ivica medija postaje gornja, a gornja ivica postaje desna strana, pa u y-naniže device space-u mapiranje je X = (y - Bottom) * Scale i Y = (x - Left) * Scale. Na 270 stepeni desna ivica postaje gornja. /Rotate je takođe jedan od samo četiri naslediva page atributa, zajedno sa /Resources, /MediaBox i /CropBox (§7.7.3.4), pa rečnik stranice bez sopstvenog /Rotate-a može da okrene /Pages predak. THotPDF.GetLoadedPageRotation šeta /Parent lanac i rezultat normalizuje u 0–359, a to je vrednost koju želite, ne sirovi ključ na stranici
Smer je lako pogrešiti na način koji preživi testiranje, i raniji HotPDF buildovi su baš to i uradili. Stara page-to-device matrica zamenila je y komponente za 90 i 270, što proizvodi refleksiju preko dijagonale umesto rotacije: orijentacija matrice se prevrne u odnosu na neokrenuti slučaj. Oba ugla i dalje „izgledaju okrenuto“, bitmapa ima zamenjenu širinu i visinu, i put od stranice do prikaza i nazad vraća početnu tačku, pa su sve provere dimenzija i round-trip testovi prolazili. Jedina pouzdana provera je gde završi marker u uglu, poređen piksel po piksel sa referentnim rendererom. Pošto su model pregledača, SIMD render backend i mapiranje isticanja kopirali istu matricu, svi su ispravljeni zajedno, a kod izravnavanja sada koristi istu konvenciju kazaljke kao renderer
Kako FlattenLoadedPageRotation prepravlja stranicu
FlattenLoadedPageRotation(PageRange, Info) obrađuje svaku stranicu iz PageRange-a čija je efektivna rotacija 90, 180 ili 270, i vraća broj izravnanih stranica. Prazan PageRange znači sve stranice; inače string koristi uobičajenu sintaksu od jedinice, '1-3,7', a broj stranice van opsega podiže izuzetak umesto da se preskoči. Originalni content streamovi nikada se ponovo ne koduju. Metoda na /Contents stranice ispred dodaje nov stream sa q 0 -1 1 0 -Bottom Width+Left cm (za 90 stepeni), na kraj dodaje stream sa Q, i na kraju upisuje eksplicitan /Rotate 0 u rečnik stranice da nasleđena vrednost na /Pages čvoru ne može da okrene stranicu drugi put
var
Pdf: THotPDF;
Info: THPDFRotationFlattenInfo;
Flattened: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('scanned-batch.pdf') > 0 then
begin
// '' = sve stranice; stranice na 0 stepeni se pretražuju ali se ostavljaju na miru
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 zapis vredi logovati umesto da se baci. ScannedPageCount je veličina opsega, FlattenedPageCount jednaka je povratnoj vrednosti, a tri Transformed... brojača kažu da li je dokument imao linkove, bookmarkove ili tagovanu geometriju uperenu u okrenute stranice. Serija u kojoj svaki fajl javi nula destinacija je u redu; tagovani PDF/UA fajl koji javi nultu strukturnu geometriju dok ste očekivali ograđene kvadrate slika signal je da ga pregledate ručno
Koje page boxove izravnavanje prepravlja i kojim redom?
Izravnavanje prepravlja samo boxove koje stranica već ima, i pročita svaki box pre nego što ijedan upiše. Redosled je bitan zbog lanca podrazumevanih vrednosti: GetLoadedPageBox(PageIndex, pbCropBox, ...) vraća /MediaBox kad stranica nema /CropBox, a /BleedBox, /TrimBox i /ArtBox podrazumevano nasleđuju CropBox (§14.11.2). Ranija verzija je čitala, transformisala i upisivala po jedan box. Prvo je prepravila MediaBox, pa pročitala „CropBox“, dobila nazad već okrenuti MediaBox, okrenula ga drugi put i upisala CropBox koji stranica nikada nije imala, čime je pejzažnu stranicu odsekla na kvadrat. Pravila nasleđivanja seku se na istom mestu: MediaBox i CropBox traže se duž /Parent lanca, dok se Bleed, Trim i ArtBox računaju samo ako sede na samom rečniku stranice, pa zalutali /TrimBox na /Pages čvoru tretira se kao da ne postoji i nikada se ne kopira na stranicu
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]));
// True i bez /TrimBox ključa: vrednost pada na CropBox, pa na MediaBox
if Pdf.GetLoadedPageBox(PageIndex, pbTrimBox, L, B, R, T) then
Writeln(Format('TrimBox [%g %g %g %g]', [L, B, R, T]));
// Podesi Letter; GetLoadedPageVisibleBox izlaze ostavlja netaknutim pri neuspehu
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;
Pustite taj helper pre i posle izravnavanja i brojevi objasne sami sebe. Za stranicu od 90 stepeni sa MediaBox-om [0 0 612 792], izravnani MediaBox postaje [0 0 792 612]; svaki prepravljeni box mapira se kroz isto okretanje u smeru kazaljke, relativno na koordinatni početak originalnog MediaBox-a, pa novi MediaBox uvek počinje na početku a ostali boxovi čuvaju svoj položaj unutar njega. GetLoadedPageVisibleBox vraća ono što pregledači prikazuju a štampači štampaju, CropBox odsečen na MediaBox i normalizovan tako da je Left manje od Right, i HotPDF-ov renderer, SVG izvoz, pregledač i putanja štampe koriste taj isti box. Kad vam treba veličina stranice kakvu vidi čovek, zovite GetLoadedPageVisibleBox umesto da čitate /MediaBox
Zašto se anotacije lome kad okrenete samo /Rect?
Anotacije se lome jer se appearance stream ne crta pravo u /Rect. Po §12.5.5 pregledač prvo transformiše /BBox forme njegovim /Matrix-om, pa onda skalira i pomera ograđeni kvadrat tog rezultata u /Rect. Okrenete li samo /Rect, žig od 200 × 40 biva uguran u slot od 40 × 200, nečitljiv i na boku. FlattenLoadedPageRotation zato desnim množenjem uvršta okretanje stranice u smeru kazaljke u svaki appearance /Matrix (za 90 stepeni, [0 -1 1 0 0 0] u konvenciji red-vektora), kroz appearance-e /N, /R i /D i svako stanje unutar njih. Jedan appearance stream može biti deljen između više anotacija ili stanja, pa se svaki stream okreće tačno jednom po pozivu. Jedini slučaj bez čistog odgovora je stream deljen između stranica sa različitim rotacijama; on prati prvu stranicu koja do njega stigne
Još dva pravila drže form polja i lepljive beleške na mestu. /MK /R unos vidžeta (§12.5.6.19) je ugao suprotan od kazaljke, pa se ugao stranice u smeru kazaljke od njega oduzima, modul 360; preskočite li to, sledeća regeneracija izgleda crtaće tekst polja pogrešnim smerom. Anotacije sa NoRotate zastavicom (bit pozicija 5, vrednost 16, §12.5.3) ostaju uspravne na okrenutoj stranici i obrću se oko gornjeg levog ugla svog /Rect-a, pa izravnavanje čuva njihovu širinu, visinu i uspravan izgled i samo taj ugao pomeri tamo gde ga okretanje smesti. Van anotacija, metoda takođe okreće /QuadPoints, /Vertices, /L i /InkList, prepravlja eksplicitne destinacije koje imenuju stranicu (/XYZ tačke, /FitR pravougaonici, i /FitH / /FitV zamenjeni na 90 i 270 stepeni, §12.3.2.2), i transformiše tagovanu geometriju poput /BBox unosa atributa za strukturne elemente čiji je /Pg ta stranica
Šta izravnavanje ne pokriva?
Izravnavanje je geometrijsko prepravljanje sopstvenih objekata jedne stranice, i nekoliko situacija tiho umesto glasno ispada van njega
- Stranice čija je efektivna rotacija već 0, ili čiji MediaBox nedostaje ili ima nultu širinu ili visinu, preskaču se bez greške; uporedite povratnu vrednost sa brojem stranica za koji ste očekivali da se promene
- Form XObjecti referencirani iz resursa stranice zadržavaju sopstveni
/BBoxu form prostoru, jer ih spoljašnjicmveć okreće; sken strukturnog stabla prati samo/Ki/Apa nikada ne ulazi u resurse stranice ili anotacije drugi put - Destinacije se nalaze skeniranjem svakog indirektnog objekta jednom po izravnanoj stranici, pa veliki dokument sa stotinama okrenutih stranica plaća taj obilazak na svakoj
- HotPDF-ov page renderer ne crta anotacije, pa vizuelna provera okrenutih žigova prvo traži
FlattenLoadedAnnotations
// Ispeci appearance-e u sadržaj da ih renderer ume da prikaže,
// pa renderuj stranicu 1 pre i posle skidanja njenog /Rotate
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));
// Ovde uporedite piksele markera u uglu, ne samo dimenzije
finally
After.Free;
end;
finally
Before.Free;
end;
Za dublju pozadinu, anotacijska strana ove priče nastavlja se u tekstu o sintetizovanju appearance-a anotacija pre izravnavanja, renderer iza poređenja pre-posle pokriven je u tekstu o renderovanju učitane PDF stranice u bitmapu, a redakcija i N-up slaganje na učitanim PDF-ovima pokazuju istu tehniku dodavanja na content stream na koju se oslanjaju prefiks i sufiks rotacije. HotPDF, uključujući FlattenLoadedPageRotation i čitače page boxova, dostupan je za Delphi i C++Builder na stranici HotPDF Delphi PDF komponente