Alan değerleri olan ama görünüm akışı olmayan bir PDF formu, bir Linux çizim işinden boş kutular, yanlış yazı tipi ya da hizalama ya da "Widget field requires a name field type" okuyan bir EHPDFHeadlessRenderError olarak çıkabilir. HotPDF'in başlıksız (headless) çizicisi metin, düğme ve seçim widget'ları için geçici görünümler çizer; /FT, /V, /DA ve /Q'yu alan ağacı üzerinden devralır
Bir widget alan ayarlarını nerede tutar
Asıl bela, alan ile widget'ındaki bölünmede başlar. Alan sözlüğü, form denetiminin mantıksal kısmını taşır: türü, değeri, bayrakları ve varsayılan görünümü. Widget ek açıklaması, bir sayfanın ihtiyaç duyduğunu taşır: /Rect, kenarlık ve kendi sayfasına bir işaretçi. Tek widget'lı bir alan genellikle tek sözlüğe birleşir. Aynı değerin birkaç sayfada görünmesi gerektiğinde widget'lar alanın çocukları olur ve çoğu zaman geometri ile bir /Parent başvurusundan başka bir şey taşımaz. ISO 32000-1 §12.7.3.1, /FT, /Ff, /V ve /DV'yi o zincir boyunca devralınabilir kılar (Tablo 220); §12.7.3.3 ise Tablo 222'deki devralınabilir kümeye yalnızca iki değişken metin özniteliği ekler: zorunlu olan ama bir atadan gelebilen /DA ile /Q. Etkileşimli form sözlüğü (Tablo 218), /DA ile /Q için belge geneli varsayılanlar ve /DA yazı tipi adlarının arandığı varsayılan kaynak sözlüğü /DR sağlar. /DR Tablo 222'de yoktur; o, formun bütününe aittir. Yalnızca widget sözlüğünü okuyan bir çizici, aşağıdaki dosyada tür, değer ya da /DA bulamaz ve kendi varsayılan yazı tipine düşer
1 0 obj % katalog
<< /Type /Catalog /Pages 2 0 R
/AcroForm << /Fields [10 0 R] /DA (/Helv 0 Tf 0 g) /Q 0
/DR << /Font << /Helv 20 0 R /Cour 21 0 R >> >> >> >>
endobj
10 0 obj % uç alan: tür, değer ve görünüm varsayılanları burada
<< /FT /Tx /T (invoice_no) /V (INV-2026-0417)
/DA (/Cour 10 Tf 0 0 0.6 rg) /Q 2
/Kids [11 0 R 12 0 R] >>
endobj
11 0 obj % 1. sayfadaki widget: yalnızca geometri; köşeler sağ üstten başlar
<< /Type /Annot /Subtype /Widget /Parent 10 0 R
/Rect [300 720 160 700] /F 4 /P 3 0 R >>
endobj
12 0 obj % 2. sayfadaki widget: yalnızca geometri
<< /Type /Annot /Subtype /Widget /Parent 10 0 R
/Rect [160 60 300 80] /F 4 /P 4 0 R >>
endobj
HotPDF her alan girdisini widget'ta arar, bulamazsa /Parent'ı izleyerek bir sözlük birden yukarı tırmanır; en yakın değer kazanır ve zincir tükendiğinde yalnızca /DA ile /Q, AcroForm sözlüğüne düşer. Yukarıdaki dosya için beklenen: iki widget da INV-2026-0417'yi 10 punto Courier, koyu maviyle sağa yaslı gösterir; nesne 10'un kendi /DA'sı olduğundan belge geneli /Helv 0 Tf hiç devreye girmez. Bir alan 12 punto siyah Helvetica ile çıkıyorsa, bariz şüpheli Linux kutusunda eksik bir Courier yazı tipidir. Boyut ele verir: bu, HotPDF'in widget'ta, hiçbir atada ya da AcroForm sözlüğünde /DA bulamadığında çizdiği şeyin ta kendisidir. O yüzden widget'ı dökün, /Parent'ını elle izleyin ve üreticinin /DA'yı nereye koyduğunu bulun; örneğin widget'ın hiç geri bağlanmadığı bir alan sözlüğüne
/DA'dan gelen yazı tipi adları, sırayla widget'ta ya da bir ebeveyn alanda bir /DR, AcroForm /DR'si ve sayfanın yazı tipi kaynakları karşısında çözülür. Alan düzeyi bir /DR Tablo 222'nin dışındadır ama var olduğunda belirsiz değildir; HotPDF onu kabul eder. Tırmanış sınırlıdır: ağaçtaki bir döngü "Widget field inheritance contains a cycle" ile durur, sözlük olmayan bir /Parent "Widget Parent requires a dictionary" ile, çizim seçeneklerindeki (öntanımlı 32) MaxDepth'ten derin bir zincir ise "Widget field inheritance depth budget exceeded" ile. VCL bileşeni, devralınan AcroForm değerlerini okuyup alanları sıfırlarken aynı devralımı uygular
Başlıksız çizici ne çizeceğine nasıl karar verir?
HPDFRenderHeadlessPagePNG ile dosya ikizi, yedek yolu yalnızca /AP'si eksik ya da null olan görünür bir widget için çalıştırır; ISO 32000-1 §7.3.7, null bir sözlük değerinin yok bir girdiyle aynı olduğunu söyler ve çizici /AP null'a öyle davranır. Var olan normal bir görünüm her zaman kazanır, /NeedAppearances ne derse desin; böylece önceki bir doldurmadan kalma bayat bir görünüm, onu bir şey yeniden üretene kadar gösterilmeye devam eder. Yüklenen belge hiç değiştirilmez. Ne çizileceği, çözümlenen alan türüne ve bayraklarına bağlıdır:
| Alan | Sentezlenen görünüm |
|---|---|
Tek satır metin, /FT /Tx | /Rect içinde çözümlenen /DA ve /Q ile dizilen /V dizgisi |
Onay kutusu, /FT /Btn | /Border'dan köşe yarıçaplı dikdörtgen kenarlık, /MK /BG dolgusu ve açıkken konturlu bir vektör tik işareti |
| Radyo düğmesi, 16. bitli Btn | Açıkken dolu bir noktayla dairesel kenarlık |
| İtiş düğmesi (pushbutton), 17. bitli Btn | /MK /CA altyazısı ortalanmış dikdörtgen kenarlık |
Açılır liste (combo box), 18. bitli /FT /Ch | Seçili /Opt girdisinin görüntü metni, ortalanmış bir altyazı olarak |
| Liste kutusu, 18. biti temiz Ch | /TI'den itibaren seçenek başına bir satır; her seçili satırın arkasında bir vurgu bandı |
Tik işareti ile radyo noktası, pek çok üreticinin çizdiği gibi ZapfDingbats glifleri değil vektör yollardır; ince bir Linux kapsayıcısında ZapfDingbats kurulu olmayabilir. İtiş düğmesi ile açılır liste yüzleri, aynı alan için HotPDF'in VCL görünüm üreticisinin çizdiğiyle eşleşir; böylece bir form, imajı hangi taraf ürettiyse benzer görünür. Altyazı yazı tipini, boyutunu ve rengini /DA'dan alır; altyazı düzeni otomatik sığmadığından 0 boyutlu bir /DA, kutu yüksekliğinin yüzde 70'ine dönüşür ve 12 puanda tavan yapar. Onay kutusu ile radyo durumu widget'ın kendi /AS'inden gelir, devralınan /V'ye düşer ve Off dışındaki her durum işareti çizer. Her radyo çocuğuna kendi /AS'ini verin. Ebeveynin /V'si bütün grup için seçili durumu adlandırır ve bir çocuğu ötekinden ayıramaz
uses
SysUtils, HPDFHeadlessDocument, HPDFHeadlessCairoDevice;
function RenderFormPage(const FileName, PngName: string; PageIndex: Integer;
out Diagnostic: string): Boolean;
var
Doc: THPDFHeadlessDocument;
Options: THPDFHeadlessRenderOptions;
Info: THPDFHeadlessRenderResult;
begin
Result := False;
Diagnostic := '';
Doc := THPDFHeadlessDocument.Create;
try
Doc.LoadFromFile(FileName);
Options := THPDFHeadlessRenderOptions.Default; // RenderAnnotations = True, MaxDepth = 32
Options.Usage := hruPrint; // bir görüntüleyicinin baskısı gibi: Print bayrağı olmayan widget'lar atlanır
try
// PageIndex sıfır tabanlıdır
Info := HPDFRenderHeadlessPagePNGFile(Doc.GetRenderAccess(PageIndex),
PngName, Options);
Diagnostic := Format('%d appearances drawn', [Info.Appearances]);
Result := True;
except
on E: EHPDFHeadlessRenderError do
// örn. 'Widget field requires a name field type'; dolu bir form için,
// RenderAnnotations := False ile yeniden denemeyin; her widget'ı değeriyle düşürür
Diagnostic := E.Message;
end;
finally
Doc.Free;
end;
end;
Metin alanı yedeği, dizgi değeri tutan tek satır alanları çizer. Daha zengin her metin düzeni için görünümü, doldurma anında Windows tarafında üretin; EnsureLoadedFieldAppearanceStream orada eksik /AP akışını, dosya düzleştirilmeden ya da yola çıkmadan önce kurar; Linux işi böylece gerçek görünüm akışları alır ve başlıksız çizici, hâlâ birine ihtiyacı olan her alanla karşılaştığında onu adlandırır. Yine de Linux çıktısını Linux çizicisiyle yargılayın. RenderLoadedPageToBitmap, Windows GDI arka ucuyla çizer ve bu yedeği hiç çalıştırmaz; orada doğru görünen bir sayfa, Cairo aygıtının aynı dosyadan ne üreteceği hakkında hiçbir şey söylemez
Alan bayrağı bitleri 1'den itibaren numaralanır
ISO 32000-1 §12.7.3.1, /Ff sözcüğündeki bit konumlarının düşük sırada 1'den başlayıp 32'ye kadar koştuğunu söyler ve Tablo 226, 228 ile 230 hep o numaralamayı kullanır. Delphi'de N. bit bu yüzden 1 shl (N - 1)'dir. Bit numarasının kendisiyle kaydırın; her test bir konum yukarı kayar: "radio, bit 16" için yazılan 1 shl 16 aslında pushbutton bayrağını sınar ve "combo, bit 18" için yazılan 1 shl 18, Edit'i sınar. İki yanlış maske de gerçek bayraktır. Kod derler, çalışır ve alanları yanlış türe ayırır
| Bayrak | Tablo | Bit | Delphi maskesi |
|---|---|---|---|
| Multiline | 228 (metin) | 13 | 1 shl 12 = 4096 |
| Password | 228 (metin) | 14 | 1 shl 13 = 8192 |
| Comb | 228 (metin) | 25 | 1 shl 24 = 16777216 |
| RichText | 228 (metin) | 26 | 1 shl 25 = 33554432 |
| Radio | 226 (düğme) | 16 | 1 shl 15 = 32768 |
| Pushbutton | 226 (düğme) | 17 | 1 shl 16 = 65536 |
| Combo | 230 (seçim) | 18 | 1 shl 17 = 131072 |
uses
Classes, SysUtils, HPDFHeadlessDocument;
const
// ISO 32000-1, Ff bitlerini 1'den (düşük sıra) sayar; N. bit 1 shl (N - 1)
TxMultiline = 1 shl 12; // bit 13
TxPassword = 1 shl 13; // bit 14
TxFileSelect = 1 shl 20; // bit 21
TxDoNotScroll = 1 shl 23; // bit 24
TxComb = 1 shl 24; // bit 25
TxRichText = 1 shl 25; // bit 26
TxProducerLayout = TxMultiline or TxPassword or TxFileSelect or
TxDoNotScroll or TxComb or TxRichText;
procedure ListTextFieldsForProducerAppearance(const FileName: string;
Report: TStrings);
var
Doc: THPDFHeadlessDocument;
Field: THPDFHeadlessFormField;
I: Integer;
begin
Doc := THPDFHeadlessDocument.Create;
try
Doc.LoadFromFile(FileName);
for I := 0 to Doc.FormFieldCount - 1 do
begin
// FieldType, Flags ve MaxLength atalardan devralınmış olarak gelir
Field := Doc.GetFormField(I);
if Field.FieldType <> 'Tx' then
Continue;
if (Field.Flags and TxProducerLayout) <> 0 then
Report.Add(Format('%s: flags $%.8x, needs a producer /AP',
[Field.Name, Field.Flags]))
else if Field.MaxLength > 0 then
Report.Add(Format('%s: /MaxLen %d, needs a producer /AP',
[Field.Name, Field.MaxLength]))
else if Field.Value = '' then
Report.Add(Format('%s: blank, fill it or give it an /AP', [Field.Name]));
end;
finally
Doc.Free;
end;
end;
Tarama, alan düzeyi bayrakları okur; üreticiler onları oraya koyar. Çizici ayrı bir widget üzerindeki bir /Ff'yi de dikkate alır; listeyi bir planlama yardımcısı, çizim tanısını son söz olarak görün. Boş denetimi önemli olabilir; çünkü /V'si ve /AP'si olmayan bir metin alanının sentezleyecek hiçbir şeyi yoktur: çizim "A text-field widget without a value has no appearance to synthesize" ile durur ve çare, dosya çiziciye varmadan bir değer ya da bir görünüm koymaktır
"Degenerate appearance geometry" ne demek?
Dikdörtgenin ya da görünüm kutusunun alanı yok demektir; ters bir /Rect sebep değildir. ISO 32000-1 §7.9.5, bir dikdörtgenin çapraz karşıt herhangi iki köşeyi adlandırabilmesine izin verir ve notu, işleme yazılımının bunları normalleştirmeye hazır olması gerektiğini söyler. Genişliği x2 - x1 olarak hesaplayan naif bir çizici, [300 720 160 700]'den negatif bir sayı alır ve ya ek açıklamayı reddeder ya da görünümünü aynalar. HotPDF, sentezlenen yüzler için de var olan /AP akışları için de herhangi bir boyut denetiminden önce koordinatları sol alt ve sağ üst sırasına çevirir; dolayısıyla yukarıdaki 11 numaralı widget, köşeleri konvansiyonelmiş gibi çizilir. Hata yalnızca, normalleştirmeden sonra /Rect'in genişliği ya da yüksekliği sıfırsa ya da akışın /BBox'ı kendi /Matrix'i altında bir çizgiye ya da noktaya çökerse, görünüm akışı olan bir ek açıklama için fırlatılır. Göründüğünde widget'ın /Rect'ini akışın /BBox'ı ve /Matrix'i ile birlikte dökün; üçünden birinin sıfır boyutu vardır
/AP'si olmayan ve sıfır alanlı bir /Rect'i olan widget başka bir yol izler: yedek yol onu atlar ve sayfayla devam eder. ISO 32000-2 §12.5.2, görünüm zorunluluğundan yalnızca Popup, Projection ve Link ek açıklamalarını ile tek bir noktaya çöken bir dikdörtgeni muaf tutar. Yalnızca bir çizgi olan bir dikdörtgen PDF 2.0 altında yine görünüm borçludur; çizici onu da atlar, sadece boyanacak bir alan yoktur
Alan türü bulunamadığında
"Widget field requires a name field type", çizicinin widget'tan /Parent bağlarının bittiği yere kadar tırmandığını ve bir /FT bulamadığını demektir. Çizici, sayfanın /Annots dizisinden başlar; yukarı doğru /Parent bağı, alana giden tek yoludur. Yükleme öbür yönde yürür; AcroForm /Fields dizisinden aşağı, /Kids üzerinden. Bu yüzden LoadFromFile, sayfası sonra çizilemeyen bir dosyada başarılı olabilir. Tipik sebep, bir alanın /Kids'inde listelenip geri işaret etmeyen bir widget'tır. ISO 32000-1 Tablo 188 bunun için biraz örtü sağlar: /Parent'ı bir widget üzerinde yalnızca widget birkaç çocuktan biri olduğunda ister ve "absent otherwise" der; dolayısıyla onu harfi harfine okuyan bir yazıcı, tek ayrı widget'lı bir alan için bağı düşürür. ISO 32000-2 aynı girdiyi "optional otherwise"e gevşetmiştir. Biz zaten, alanıyla birleşmemiş her widget'a /Parent yazardık. ISO 32000-2 buna izin verir ve sayfa güdümlü bir okuyucunun — bizim ya da bir başkasının — alana ulaşmasının tek yolu budur. Üreticiyi düzeltemiyorsanız, yalnız widget'ı alan sözlüğüyle birleştirmek aynı sonucu verir
Ebeveynine bağlanan ve yukarı doğru bir yerde bir /DA bulan bir widget, HotPDF Delphi component'ün başlıksız çizicisinde hiçbir /AP olmadan doğru çizilir; bu da bir Linux form hattında önce doğru kurulacak şeyin, görünüm akışlarından çok, alan ağacı olduğunu gösterir