HotPDF flatter PDF-siderotasjon med THotPDF.FlattenLoadedPageRotation: metoden pakker hver roterte sides innhold inn i en cm-transform som vender med klokken, omskriver hver sideboks siden faktisk har, vrir annotasjonsgeometri, utseendesmatriser, eksplisitte destinasjoner og tagget strukturgeometri med samme vinkel, og setter så /Rotate til 0. Siden ser identisk ut i en visning, men koordinatsystemet dens er nå opprett. Det betyr noe i det øyeblikket et verktøy lenger ned, en print RIP eller din egen stemplingskode ignorerer /Rotate og plasserer ting i rå user space
Den typiske utløseren er en skanner eller en mobil capture-app som skriver liggende sider som stående media med /Rotate 90. Hver viser viser dem riktig, så ingen legger merke til det før noen stampler et sidetall ved «nederst til høyre» og det lander på skrå langs venstre kant, eller et utleggingstrinn som bare leser /MediaBox legger opp en stående plass til en liggende side. Flattering høres ut som en eine-linjes matrisejobb. I praksis berører den fem sidebokser, tre slags annotasjonsgeometri, dokumentets lenkemål og strukturstreet, og hver av dem har sin egen regel i ISO 32000-1
Hvilken vei vrir /Rotate en PDF-side?
/Rotate vrir siden med klokken for visning og utskrift, i multipler av 90 grader (ISO 32000-1 §7.7.3.3, tabell 30). Ved 90 grader blir venstre kant av mediet toppen, og toppkanten blir høyresiden, så i et y-ned enhetsrom er mappingen X = (y - Bottom) * Scale og Y = (x - Left) * Scale. Ved 270 grader blir høyrekanten toppen. /Rotate er også én av bare fire arvelige sideattributter, sammen med /Resources, /MediaBox og /CropBox (§7.7.3.4), så en sideordbok uten egen /Rotate kan fortsatt vris av en /Pages-forgjenger. THotPDF.GetLoadedPageRotation går /Parent-kjeden og normaliserer resultatet til 0–359, som er verdien du vil ha, ikke den rå nøkkelen på siden
Retningen er lett å få feil på en måte som overlever testing, og tidligere HotPDF-bygg gjorde nøyaktig det. Den gamle side-til-enhet-matrisen byttet om y-komponentene for 90 og 270, noe som gir en speiling over diagonalen i stedet for en rotasjon: matrisens orientering flipper relativt til den uroterte tilfellet. Begge vinkler «ser rotert ut», bitmapen har byttet bredde og høyde, og en rundtur fra side til visning og tilbake returnerer startpunktet, så dimensjonssjekker og rundturstester passerer alle. Den eneste pålitelige sjekken er hvor et hjørnemerke ender, sammenlignet piksel for piksel mot en referanserenderer. Fordi visermodellen, SIMD-render-backend-en og highlight-mappingen hadde kopiert samme matrise, ble alle korrigert sammen, og flatte-koden bruker nå samme med-klokken-konvensjon som rendereren
Hvordan FlattenLoadedPageRotation omskriver en side
FlattenLoadedPageRotation(PageRange, Info) behandler hver side i PageRange hvis effektive rotasjon er 90, 180 eller 270, og returnerer antallet sider den flattet. En tom PageRange betyr alle sider; ellers bruker strengen den vanlige enebaserte '1-3,7'-syntaksen, og et sidetall utenfor området reiser et unntak i stedet for å hoppes over. De opprinnelige content streams re-enkodes aldri. Metoden prepender en ny stream som inneholder q 0 -1 1 0 -Bottom Width+Left cm (for 90 grader) på sidens /Contents, appender en stream som inneholder Q og skriver til slutt en eksplisitt /Rotate 0 inn i sideordboken slik at en arvet verdi på en /Pages-node ikke kan vride siden en gang til
var
Pdf: THotPDF;
Info: THPDFRotationFlattenInfo;
Flattened: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('scanned-batch.pdf') > 0 then
begin
// '' = alle sider; sider på 0 grader skannes men får stå i fred
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-recorden er verdt å logge i stedet for å forkaste. ScannedPageCount er størrelsen på området, FlattenedPageCount er lik returverdien, og de tre Transformed...-tellerne forteller deg om dokumentet hadde lenker, bokmerker eller tagget geometri som pekte på de vridde sidene. En bunke der hver fil rapporterer null destinasjoner er grei; en tagget PDF/UA-fil som rapporterer null strukturgeometri når du ventet figuravgrensingsbokser, er et signal om å inspisere den for hånd
Hvilke sidebokser omskriver flatting, og i hvilken rekkefølge?
Flatting omskriver bare boksene siden allerede har, og den leser hver boks før den skriver noen av dem. Rekkefølgen betyr noe på grunn av standardkjeden: GetLoadedPageBox(PageIndex, pbCropBox, ...) returnerer /MediaBox når siden ikke har /CropBox, og /BleedBox, /TrimBox og /ArtBox faller tilbake til CropBox (§14.11.2). En tidligere versjon leste, transformerte og skrev én boks om gangen. Den omskrev MediaBox først, leste så «CropBox», fikk den allerede vridde MediaBox-en tilbake, vridde den en gang til og skrev en CropBox siden aldri hadde hatt, noe som beskar en liggende side ned til et kvadrat. Arvereglene deler seg på samme måte: MediaBox og CropBox slås opp langs /Parent-kjeden, mens Bleed, Trim og ArtBox bare teller hvis de sitter på sideordboken selv, så en vill /TrimBox på en /Pages-node behandles som fraværende og kopieres aldri over på siden
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 selv uten en /TrimBox-nøkkel: verdien faller tilbake til CropBox, så MediaBox
if Pdf.GetLoadedPageBox(PageIndex, pbTrimBox, L, B, R, T) then
Writeln(Format('TrimBox [%g %g %g %g]', [L, B, R, T]));
// Forhåndsatt Letter; GetLoadedPageVisibleBox lar outputene stå urørt ved feil
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;
Kjør den hjelperen før og etter flatting, så forklarer tallene seg selv. For en 90-graders side med MediaBox [0 0 612 792] blir den flattede MediaBox-en [0 0 792 612]; hver omskrevne boks mappes gjennom samme med-klokken-vending, relativt til den opprinnelige MediaBox-origoen, så den nye MediaBox-en starter alltid i origo og de andre boksene beholder posisjonen sin inni den. GetLoadedPageVisibleBox returnerer det visere viser og skrivere skriver ut, CropBox-en beskåret til MediaBox-en og normalisert slik at Left er mindre enn Right, og HotPDFs renderer, SVG-eksport, viser og utskriftssti bruker alle samme boks. Når du trenger sidestørrelsen et menneske ser, kall GetLoadedPageVisibleBox i stedet for å lese /MediaBox
Hvorfor knekker annotasjoner når du bare vrir /Rect?
Annotasjoner knekker fordi en appearance stream ikke tegnes rett inn i /Rect. Under §12.5.5 transformerer viseren først formens /BBox med dens /Matrix, skalerer og oversetter så avgrensingsboksen til det resultatet inn i /Rect. Vrir du bare /Rect, blir et 200 × 40-stempel klemt inn i en 40 × 200-plass, uleselig og på høykant. FlattenLoadedPageRotation høyremultipliserer derfor sidens med-klokken-vending på hver appearance-/Matrix (for 90 grader, [0 -1 1 0 0 0] i radvektor-konvensjonen), på tvers av /N-, /R- og /D-utseendene og hver tilstand inni dem. Én appearance stream kan deles av flere annotasjoner eller tilstander, så hver stream vrir nøyaktig én gang per kall. Tilfellet uten rent svar er en stream delt på tvers av sider med ulike rotasjoner; den følger den første siden som når den
To flere regler holder skjemafelt og lapper på plass. En widgets /MK /R-oppføring (§12.5.6.19) er en mot-klokken-vinkel, så sidens med-klokken-vinkel trekkes fra den, modulo 360; hopp over det, og neste appearance-regenerering tegner feltteksten i feil retning. Annotasjoner med NoRotate-flagget (bitposisjon 5, verdi 16, §12.5.3) forblir opprette på en rotert side og dreier rundt øvre venstre hjørne av sin /Rect, så flatting beholder bredde, høyde og opprett utseende og flytter bare det hjørnet til der vendingen plasserer det. Utover annotasjoner vrir metoden også /QuadPoints, /Vertices, /L og /InkList, omskriver eksplisitte destinasjoner som navngir siden (/XYZ-punkter, /FitR-rektangler, og /FitH / /FitV byttet om ved 90 og 270 grader, §12.3.2.2), og transformerer tagget geometri som attributt-/BBox-oppføringer for struktorelementer hvis /Pg er siden
Hva dekker ikke flatting?
Flatting er en geometrisk omskriving av ett sides egne objekter, og flere situasjoner faller utenfor den stille i stedet for høylytt
- Sider hvis effektive rotasjon allerede er 0, eller hvis MediaBox mangler eller har null bredde eller høyde, hoppes over uten en feil; sammenlign returverdien med antallet sider du ventet skulle endres
- Form XObjects referert fra sideressursene beholder sin egen
/BBoxi formrommet, for den ytrecm-en vrir dem allerede; strukturtre-skanningen følger bare/Kog/A, så den går aldri inn i sidens ressurser eller annotasjoner en gang til - Destinasjoner finnes ved å skanne hvert indirekte objekt én gang per flatte side, så et stort dokument med hundrevis av roterte sider betaler for den vandringen på hver av dem
- HotPDFs siderenderer tegner ikke annotasjoner, så en visuell sjekk av vridde stempler trenger
FlattenLoadedAnnotationsførst
// Bake utseender inn i innholdet slik at rendereren kan vise dem,
// render så side 1 før og etter å ha fjernet dens /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));
// Sammenlign hjørnemerkes-piksler her, ikke bare dimensjonene
finally
After.Free;
end;
finally
Before.Free;
end;
For dypere bakgrunn fortsetter annotasjonssiden av denne historien i å syntetisere annotasjonsutseender før de flatter, rendereren bak før-og-etter-sammenligningen er dekket i å rendere en lastet PDF-side til en bitmap, og redaksjon og N-up-sying på lastede PDF-er viser samme content stream-append-teknikk som rotasjonsprefikset og -suffikset støtter seg på. HotPDF, inkludert FlattenLoadedPageRotation og sideboksleserne, er tilgjengelig for Delphi og C++Builder på HotPDF Delphi PDF-komponentsiden