HotPDF, PDF sayfa döndürmesini THotPDF.FlattenLoadedPageRotation ile düzleştirir: metot, döndürülmüş her sayfanın içeriğini saat yönünde bir cm dönüşümüne sarar, sayfanın gerçekten taşıdığı her page boxı yeniden yazar, annotation geometrisini, appearance matrixlerini, açık destinationları ve etiketli yapı geometrisini aynı açıyla döndürür ve sonra /Rotatei 0a set eder. Sayfa bir görüntüleyicide birebir aynı görünür ama koordinat sistemi artık dik durur. Bu, bir aşağı akış aracının, bir print RIPin ya da sizin damga kodunuzun /Rotatei yok sayıp şeyleri ham user spacete yerleştirdiği anda önem kazanır
Tipik tetikleyici, yatay sayfaları /Rotate 90lı dikey media olarak yazan bir tarayıcı ya da mobil yakalama uygulamasıdır. Her görüntüleyici onları doğru gösterir; kimse birileri "sağ alt köşeye" bir sayfa numarası damgalayıp numaranın sol kenar boyunca yan yatmasını, ya da yalnızca /MediaBox okuyan bir imposition adımının yatay sayfaya dikey slot açmasını görene kadar fark etmez. Düzleştirme tek satırlık bir matrix işi gibi duyulur. Gerçekte beş page boxa, üç tür annotation geometrisine, belgenin link hedeflerine ve yapı ağacına dokunur; her birinin ISO 32000-1de kendi kuralı vardır
/Rotate bir PDF sayfasını hangi yöne döndürür?
/Rotate, sayfayı görüntüleme ve yazdırma için saat yönünde, 90 derecenin katları biçiminde döndürür (ISO 32000-1 §7.7.3.3, Tablo 30). 90 derecede medyanın sol kenarı üst olur, üst kenar sağ yan olur; yani y-down device spacete eşleme X = (y - Bottom) * Scale ve Y = (x - Left) * Scaledir. 270 derecede sağ kenar üst olur. /Rotate ayrıca, /Resources, /MediaBox ve /CropBoxla birlikte (§7.7.3.4) kalıtsal dört sayfa attributunden biridir; kendi /Rotatei bulunmayan bir sayfa sözlüğü yine de bir /Pages atası tarafından döndürülmüş olabilir. THotPDF.GetLoadedPageRotation, /Parent zincirinde yürür ve sonucu 0–359 aralığına normalize eder; istediğiniz değer budur, sayfadaki ham anahtar değil
Yön, testi atlatacak biçimde kolay yanlışlanır ve eski HotPDF derlemeleri tam olarak öyle yaptı. Eski page-to-device matrixi 90 ile 270 için y bileşenlerini takas ediyordu; bu, rotasyon yerine köşegen üzerinde bir yansıma üretir: matrixin yönelimi döndürülmemiş duruma göre ters döner. İki açı da "döndürülmüş gibi görünür", bitmap takas edilmiş genişlik ve yüksekliğe sahiptir ve sayfadan görünüme, görünimden sayfaya round-trip başlangıç noktasına döner; boyut kontrolleri ve round-trip testleri geçer. Güvenilir tek kontrol, köşe işaretinin nereye düştüğüdür; referans rendererla piksel piksel karşılaştırılır. Görüntüleyici modeli, SIMD render backendi ve highlight eşlemesi aynı matrixi kopyalamış olduğu için hepsi birlikte düzeltildi ve düzleştirme kodu artık rendererla aynı saat yönü kuralını kullanıyor
FlattenLoadedPageRotation bir sayfayı nasıl yeniden yazar?
FlattenLoadedPageRotation(PageRange, Info), PageRangein etkin döndürmesi 90, 180 ya da 270 olan her sayfasını işler ve düzleştirdiği sayfa sayısını döndürür. Boş PageRange bütün sayfalar demektir; aksi hâlde dize her zamanki 1 tabanlı '1-3,7' sözdizimini kullanır ve aralık dışı bir sayfa numarası atlanmak yerine istisna fırlatır. Özgün content streamler asla yeniden kodlanmaz. Metot, sayfanın /Contentsinin başına q 0 -1 1 0 -Bottom Width+Left cm içeren (90 derece için) yeni bir stream ekler, sonuna Q içeren bir stream yapıştırır ve son olarak bir /Pages düğümündeki kalıtsal değerin sayfayı ikinci kez döndürememesi için sayfa sözlüğüne açık bir /Rotate 0 yazar
var
Pdf: THotPDF;
Info: THPDFRotationFlattenInfo;
Flattened: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('scanned-batch.pdf') > 0 then
begin
// '' = bütün sayfalar; 0 derecedeki sayfalar taranır ama dokunulmaz
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 recordu çöpe atılmaya değil loglanmaya değerdir. ScannedPageCount aralığın boyutudur, FlattenedPageCount dönüş değerine eşittir ve üç Transformed... sayacı, belgenin döndürülmüş sayfaları gösteren linkleri, bookmarkları ya da etiketli geometrisi olup olmadığını söyler. Her dosyası sıfır destination bildiren bir parti sorunsuzdur; figure bounding boxları beklerken sıfır yapı geometrisi bildiren etiketli bir PDF/UA dosyası ise elle incelemeye işarettir
Düzleştirme hangi page boxları, hangi sırayla yeniden yazar?
Düzleştirme yalnızca sayfanın hâlihazırda taşıdığı kutuları yeniden yazar ve hiçbirini yazmadan önce hepsini okur. Sıra, varsayılan zincir yüzünden önemlidir: GetLoadedPageBox(PageIndex, pbCropBox, ...), sayfada /CropBox yoksa /MediaBoxı döndürür; /BleedBox, /TrimBox ve /ArtBox ise CropBoxa varsayılanlanır (§14.11.2). Daha eski bir sürüm tek seferde bir kutuyu okuyor, dönüştürüyor ve yazıyordu. Önce MediaBoxı yeniden yazıyor, sonra "CropBox"ı okuyup hâlihazırda döndürülmüş MediaBoxı geri alıyor, onu ikinci kez döndürüyor ve sayfanın hiç sahip olmadığı bir CropBox yazıyordu; bu da yatay sayfayı kareye kırpıyordu. Kalıtım kuralları da aynı şekilde ayrışır: MediaBox ile CropBox /Parent zinciri boyunca aranır; Bleed, Trim ve ArtBox ise yalnızca sayfa sözlüğünün üzerinde duruyorlarsa sayılır, dolayısıyla bir /Pages düğümündeki başıboş bir /TrimBox yok sayılır ve sayfaya asla kopyalanmaz
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]));
// /TrimBox anahtarı olmasa da True: değer önce CropBoxa, sonra MediaBoxa döner
if Pdf.GetLoadedPageBox(PageIndex, pbTrimBox, L, B, R, T) then
Writeln(Format('TrimBox [%g %g %g %g]', [L, B, R, T]));
// Letter presetli; GetLoadedPageVisibleBox başarısızlıkta çıktılara dokunmaz
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;
O yardımcıyı düzleştirme öncesi ve sonrası çalıştırın; sayılar kendini anlatır. MediaBoxı [0 0 612 792] olan 90 derecelik bir sayfada düzleştirilmiş MediaBox [0 0 792 612] olur; yeniden yazılan her kutu, özgün MediaBox başlangıcına göre aynı saat yönü dönüşüyle eşlenir, böylece yeni MediaBox her zaman başlangıçta durur ve öteki kutular konumlarını onun içinde korur. GetLoadedPageVisibleBox, görüntüleyicilerin gösterdiğini ve yazıcıların bastığını döndürür: MediaBoxa kırpılmış, Left Rightten küçük olacak biçimde normalize edilmiş CropBox; HotPDFin rendereri, SVG exportu, görüntüleyicisi ve yazdırma yolu hep aynı kutuyu kullanır. Bir insanın gördüğü sayfa boyutu gerektiğinde /MediaBoxı okumak yerine GetLoadedPageVisibleBoxu çağırın
Yalnızca /Recti döndürünce annotationlar neden bozulur?
Annotationlar bozulur, çünkü bir appearance stream doğrudan /Recte çizilmez. §12.5.5 uyarınca görüntüleyici önce formun /BBoxını /Matrixiyle dönüştürür, sonra o sonucun bounding boxını ölçekleyip /Recte taşır. Yalnızca /Recti döndürürseniz 200 x 40lık bir damga 40 x 200lük bir yuvaya sıkışır; okunaksız ve yan yatmış. FlattenLoadedPageRotation bu yüzden sayfanın saat yönü dönüşünü her appearance /Matrixine sağdan çarpar (90 derece için satır vektör kuralında [0 -1 1 0 0 0]), /N, /R ve /D görünümleri ve içindeki her durum boyunca. Bir appearance streami birden çok annotation ya da durum tarafından paylaşılabilir; bu yüzden her stream çağrı başına tam olarak bir kez döndürülür. Temiz cevabı olmayan tek durum, farklı döndürmelere sahip sayfalar arasında paylaşılan bir streamdir; ona ilk ulaşan sayfayı izler
İki kural daha form alanlarını ve yapışkan notları yerinde tutar. Bir widgetın /MK /R girdisi (§12.5.6.19) saat tersi bir açıdır; sayfanın saat yönü açısı ondan çıkarılır, 360la modu alınır; bunu atlarsanız bir sonraki appearance yenilemesi alan metnini yanlış yöne çizer. NoRotate bayraklı annotationlar (bit konumu 5, değer 16, §12.5.3) döndürülmüş sayfada dik kalır ve /Rectlerinin sol üst köşesi etrafında döner; düzleştirme genişliklerini, yüksekliklerini ve dik görünümlerini korur ve yalnızca o köşeyi dönüşün koyduğu yere taşır. Annotationların ötesinde metot ayrıca /QuadPointsi, /Verticesi, /Lyi ve /InkListi döndürür, sayfayı adlandıran açık destinationları yeniden yazar (/XYZ noktaları, /FitR dikdörtgenleri ve 90 ile 270 derecede takas edilen /FitH / /FitV, §12.3.2.2) ve /Pgsi o sayfa olan yapı elementlerinin attribute /BBox girdileri gibi etiketli geometriyi dönüştürür
Düzleştirme neleri kapsamaz?
Düzleştirme tek bir sayfanın kendi objelerinin geometrik yeniden yazımıdır ve birkaç durum gürültülü değil sessiz biçimde onun dışında kalır
- Etkin döndürmesi hâlihazırda 0 olan ya da MediaBoxı eksik ya da sıfır genişlik/yükseklikli sayfalar hata vermeden atlanır; dönüş değerini değişmesini beklediğiniz sayfa sayısıyla karşılaştırın
- Sayfa kaynaklarından referans verilen Form XObjectler form uzayındaki kendi
/BBoxlarını korur, çünkü dıştakicmonları çoktan döndürür; yapı ağacı taraması yalnızca/Kile/Ayı izler ve sayfanın kaynaklarına ya da annotationlarına ikinci kez asla girmez - Destinationlar, düzleştirilen her sayfa için bütün dolaylı objelerin bir kez taranmasıyla bulunur; yüzlerce döndürülmüş sayfalı büyük bir belge her biri için bu yürüyüşün bedelini öder
- HotPDFin sayfa rendererı annotation çizmez; döndürülmüş damgaların görsel kontrolü önce
FlattenLoadedAnnotationsister
// Appearanceları renderer görebilsin diye contente pişir,
// sonra /Rotateini kaldırmadan önce ve sonra 1. sayfayı render et
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));
// Burada yalnızca boyutları değil köşe işareti piksellerini karşılaştır
finally
After.Free;
end;
finally
Before.Free;
end;
Daha derin arka plan için bu hikâyenin annotation tarafı annotation appearancelarını düzleştirmeden önce sentezlemede sürer; öncesi-sonrası karşılaştırmasının arkasındaki renderer yüklenmiş bir PDF sayfasını bitmape render etmede kapsanır ve yüklenmiş PDFlerde redaction ve N-up dikişi, döndürme öneki ile sonunun dayandığı aynı content-stream ekleme tekniğini gösterir. FlattenLoadedPageRotation ve page-box okuyucuları dahil HotPDF, Delphi ve C++Builder için HotPDF Delphi PDF component sayfasındadır