Teknik Makale

PDFium ile Delphi'de Axial ve Radyal PDF Gradyanları

PDFium Component, Delphi'de axial ve radyal PDF gradyanlarını TPdf.SaveAsShadingCreated üzerinden oluşturur. Her gradyanı bir TPdfShadingCreate kaydında tanımlarsınız (tür, renk uzayı, koordinatlar, renkler ya da duraklar, hedef sayfa); metot, her dolgunun gerçek bir ISO 32000-1 shading sözlüğü olduğu, sayfa /Resources /Shading sözlüğüne kayıtlı ve sh operatörüyle boyanan doğrulanmış bir kopya yazar; PDFium böylece onu yerli olarak render eder

İhtiyaç rapor ve sertifika üreticilerinde ortaya çıkar. Tasarımcı, yumuşak bir maviden beyaza başlık bandı ya da bir kaşenin arkasında radyal bir parıltı içeren bir taslak uzatır; metni, yolları ve görselleri kapsayan sayfa-nesnesi API'sinin bunun için sunduğu hiçbir şey yoktur. Gradyanı, renkleri hafifçe farklı yüz ince dikdörtgenle taklit edebilirsiniz ama sonuç yakınlaştırıldığında görünür biçimde bantlanır, içerik akışını şişirir ve kötü basılır. PDF, 1.3'ten beri yumuşak shading'lere sahip; iş, bir renderer'ın onları gerçekten çizmesi için yazmaktır

PDFium Component gradyanları neden ayrı bir kaydetme olarak yazar?

TPdf.SaveAsShadingCreated, bir sayfa-nesnesi çağrısı değil bir kaydetme işlemidir; çünkü PDFium'un genel ABI'sinde bir shading sözlüğü oluşturan ya da sh operatörü yayan bir fonksiyon yoktur. PDFium Component ile sıfırdan PDF oluştururken kullanılan sayfa-nesnesi katmanı yol, metin ve görsel kurar ve orada durur. Shading'ler bir düzey aşağıda, nesne grafiğinde ve içerik akışı söz diziminde yaşar

Bu yüzden metot evrelerle çalışır. PDFium önce açık belgeyi yerli biçimde kaydeder; normal API üzerinden yaptığınız her düzenleme böylece korunur. PDFium Component sonra o sahnelenmiş çıktıyı yeniden yazar: shading başına bir dolaylı nesne ayırır, her hedef sayfaya kaynak girdisini ekler, sayfa /Contents'ine yeni bir içerik akışı ekler ve taze bir çapraz referans tablosu yazar. Herhangi bir şey akışınıza ya da dosyanıza ulaşmadan önce sonuç, katı bir çapraz referans kontrolüyle yeniden yüklenir; o yükleme başarısız olursa metot raporda bir iletiyle False döndürür. Dosya aşırı yüklemesi önce geçici bir dosyaya yazar; böylece başarısız bir kaydetme var olan hedefi ezmez

İki renkli bir axial gradyan kurmak

Bir axial shading (ISO 32000-1 §8.7.4.5.3, Type 2), renkleri x0 y0'dan x1 y1'e bir doğru boyunca harmanlar; Coords bu yüzden dört değer alır. Bir radyal shading (§8.7.4.5.4, Type 3) iki çember arasında harmanlar ve altı değer alır: x0 y0 r0 x1 y1 r1. Düz StartColor ile EndColor kullanıldığında PDFium Component, düz doğrusal bir aradeğerleme olan /N 1'li bir Type 2 üstel fonksiyon yazar (§7.10.3) ve eksenin her iki ucunun ötesinde son renklerin sürmesi için /Extend [true true] ayarlar

Delphi için PDFium Component axial ve radyal shading geometrisi: Type 2 gradyan, /Extend true true her iki ucun ötesinde sürerken StartColor'dan EndColor'a x0 y0'dan x1 y1'e doğru boyunca harmanlar; Type 3 gradyan ise r0 yarıçaplı başlangıç çemberi ile r1 yarıçaplı bitiş çemberi arasında harmanlar
PDFium, shading'i kırpmanın izin verdiğine boyar: Type 2 Coords harman eksenini x0 y0'dan x1 y1'e yerleştirir, Type 3 Coords iki çemberi yerleştirir ve /Extend [true true] her iki uç rengi geometrinin ötesine taşır
uses
  System.SysUtils, PDFium, FPdfCompress;

procedure AddHeaderBand(const InFile, OutFile: string);
var
  Pdf: TPdf;
  Fills: TPdfShadingCreateShadings;
  Report: TPdfShadingCreateReport;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := InFile;
    Pdf.Active := True;

    SetLength(Fills, 1);
    Fills[0] := Default(TPdfShadingCreate);
    Fills[0].ShadingType := pstAxial;
    Fills[0].ColorSpace := pscDeviceRGB;
    Fills[0].Coords := [0, 792, 0, 640];        // x0 y0 x1 y1, üstten alta
    Fills[0].StartColor := [0.12, 0.35, 0.75];  // üst kenarda koyu mavi
    Fills[0].EndColor := [1, 1, 1];             // aşağıda beyaz
    Fills[0].PageNumber := 1;                   // 1 tabanlı
    Fills[0].ResourceName := 'HeaderBand';      // isteğe bağlı; varsayılan Sh1, Sh2...

    if not Pdf.SaveAsShadingCreated(OutFile, Fills, Report) then
      raise Exception.Create('Shading creation failed: ' + Report.ErrorMessage);
  finally
    Pdf.Free;
  end;
end;

Her rengin bileşen sayısı renk uzayıyla eşleşmelidir: pscDeviceGray ile pscCalGray için bir, pscDeviceRGB, pscCalRGB ile pscLab için üç, pscDeviceCMYK için dört. pscICCBased için sayı, IccProfile'da verdiğiniz profil baytlarından gelir; 1, 3 ya da 4 bileşen açmalıdır. Profil, eşleşen /N ile kendi ICC akışı olarak gömülür. Kalibre edilmiş uzaylar isteğe bağlı CalWhitePoint (üç değer, pozitif Y), CalGamma (CalGray için bir değer, CalRGB için üç) ve Lab için altı değerli bir LabRange kabul eder. Bir bayt yazılmadan önce her koordinat ve bileşen NaN ile sonsuzluk açısından kontrol edilir

Koordinatların varsayılan user space'te, başlangıcın sayfanın sol altında olduğunu unutmayın. Eksenin yalnızca harmanın nerede olacağını tanımladığını akılda tutun: boyanan alan, kırpmanın izin verdiğidir; bu, sonraki bölümlerin göstereceği gibi göründüğünden daha önemlidir

Çok duraklı gradyanlar bir Type 3 stitching fonksiyonuna derlenir

PDFium Component, bir Stops dizisini bir PDF okuyucusunun beklediği fonksiyona çevirir: iki durak tek bir Type 2 fonksiyon olur; 3 ile 256 durak ise komşu her çift için bir lineer Type 2 segmentinden kurulan bir Type 3 stitching fonksiyonu olur (ISO 32000-1 §7.10.4). Her TPdfShadingCreateStop bir Position ile bir Color taşır. Stops boş olmadığında StartColor ile EndColor'un yerine tümüyle geçer

Stitching fonksiyonu elle yazıldığında ince ince yanılınması kolay üç dizi ister. /Functions segmentleri tutar; /Bounds yalnızca iç durak konumlarını tutar (k segment k-1 sınır ister); /Encode her alt alanı [0 1]'e geri eşler, böylece her segment tam giriş aralığını görür. Bileşen üçünü de duraklarınızdan türetir; girdiye katı olmasının nedeni budur: konumlar sonlu, kesinlikle artan, 0'da başlayıp 1'de bitmelidir (1e-6 içinde). 0.1'de başlayan bir durak listesi alanın bir kısmını tanımsız fonksiyonla bırakırdı; metot tahmin etmek yerine onu reddeder

function Stop(Position: Double; const Rgb: array of Double): TPdfShadingCreateStop;
var
  I: Integer;
begin
  Result.Position := Position;
  SetLength(Result.Color, Length(Rgb));
  for I := 0 to High(Rgb) do
    Result.Color[I] := Rgb[I];
end;

procedure BuildSealGlow(var Fill: TPdfShadingCreate);
begin
  Fill := Default(TPdfShadingCreate);
  Fill.ShadingType := pstRadial;
  Fill.ColorSpace := pscDeviceRGB;
  // x0 y0 r0 x1 y1 r1: kaşe merkezindeki bir noktanın 90 pt çembere büyümesi
  Fill.Coords := [450, 160, 0, 450, 160, 90];
  Fill.Stops := [Stop(0.0, [1.00, 0.95, 0.70]),
                 Stop(0.4, [0.95, 0.75, 0.30]),
                 Stop(1.0, [1.00, 1.00, 1.00])];
  Fill.PageNumber := 1;
end;

Üç duraklı bir gradyan için bu, iki Type 2 segmentli /Functions, /Bounds [0.4] ve /Encode [0 1 0 1] üretir. Segment sınırları sert renk birleşimleridir; durak listesi onların üzerinden yumuşamaz. Bir birleşim fazla keskin görünüyorsa okuyucudan eğrilik aradeğerlemesi beklemek yerine durak ekleyin

Delphi'de PDFium Component çok duraklı gradyan derlemesi: 0.0, 0.4 ve 1.0 konumlarındaki üç Stops, komşu her durak çifti için bir Type 2 segmenti bulunan bir Type 3 stitching fonksiyonuna, iç konum için /Bounds [0.4]'e ve her alt alanı tam aralığa geri eşleyen /Encode [0 1 0 1]'e dönüşür
Komşu her durak çifti, Type 3 fonksiyonun içinde bir Type 2 segmentine dönüşür; elle yazılan stitching fonksiyonlarının genellikle yanıldığı yer, türetilen /Bounds ile /Encode dizileridir

PDFium neden sessizce hiçbir şey çizmez?

PDFium, yeni oluşturulmuş bir shading'i yalnızca iki ayrıntı doğruyken render eder: /ShadingType bir tamsayı olmalı ve sh çağrısı bir kırpma yolu içinde durmalıdır. Hangisini yanlış yaparsanız dosya açılır, yapısal olarak doğrulanır ve dokunulmamış bir sayfa gösterir. Hata yok, uyarı yok, günlük satırı yok

Tuzak bir: /ShadingType bir sayıdır, ad değil

ISO 32000-1 Tablo 78, /ShadingType'ı bir tamsayı olarak tanımlar: axial için 2, radyal için 3. Bu yazıcının ilk sürümü /ShadingType /Axial yazıyordu; hex dökümünde daha iyi okunuyordu. PDFium anahtarı bir tamsayı getter'ıyla okur, bir ad için 0 alır, shading'i geçersiz sınıflandırır, yükleyemez ve sh operatörünü şikâysiz atlar. Sayfa boş render edildi ve PDFium içinde hiçbir şey nedenini söylemedi. Neden, minimal elle yazılmış bir PDF kurup ikinci bağımsız bir renderer'da açıldıktan sonra görünür oldu; o renderer, bilinmeyen shading türü 0 bildirdi. Ders her PDF yazıcısına genellenir: spesifikasyon bir numaralandırmayı tamsayı olarak tiplendirdiğinde, kendini tanımlayan bir ad ne kadar cazip görünse de tamsayıyı yazın

Tuzak iki: yalnızca sh içeren bir içerik akışının sınırlayıcı kutusu yoktur

sh operatörü (ISO 32000-1 §8.7.4.2) shading'i geçerli kırpma bölgesine boyar; kağıt üzerinde onu sayfanın kendi kapsamı sınırlar. PDFium ise bir shading sayfa nesnesi kurar; dikdörtgeni ya etkin kırpma yolundan ya da aynı akışta önceki çizim operatörlerinin biriktirdiği sınırlayıcı kutudan gelir ve renderer, boş dikdörtgenli nesneleri eleyerek atar. Yeni iliştirilmiş ve /Sh1 sh'ten başka bir şey içermeyen bir akışta ne biri vardır ne öteki; gradyan rasterizasyondan önce düşer. TPdf.SaveAsShadingCreated bu yüzden her sayfanın crop kutusunu okur (önce media kutusuna, sonra sayfa boyutuna düşerek) ve her çağrıyı açık bir re W n kırpmasına sarar. Başlangıcı sıfırda bir US Letter sayfasında iliştirilen akış şöyle görünür:

q
0 0 612 792 re W n
/HeaderBand sh
Q

q/Q çifti kırpma ile her dönüşümü var olan sayfa içeriğinden izole eder; sayfaya sonra çizilen hiçbir şey onları miras almaz. Shading çağrılarını başka bir araçta kendiniz yazıyorsanız bu biçimi kopyalayın: aynı akıştaki açık bir kırpma ucuzdur ve önünde ne varsa ona bağımlılığı kaldırır

Geçerli görünen bir PDF shading'inin PDFium'da neden hiçbir şey çizmediği: Axial adı olarak yazılan /ShadingType bir tamsayı getter'ı tarafından 0 okunur ve sh operatörü sessizce atlanır; yalnızca sh içeren bir içerik akışının ise açık bir re W n kırpması ona bir dikdörtgen verene dek sınırlayıcı kutusu yoktur
Görünmez shading başarısızlıklarının ikisi de PDFium'da sessizdir; SaveAsShadingCreated bu yüzden tamsayı ShadingType'ı kendisi yazar ve her sh çağrısını crop kutulu bir re W n kırpmasına sarar

Özel bir gradyan matrisi kırpma ile nasıl etkileşir?

PDFium Component'te bir gradyan matrisi, kırpmanın ardından ve sh'ten önce konan bir cm operatörü olarak uygulanır; kırpma tüm sayfada kalır, yalnızca gradyan geometrisi dönüşür. HasMatrix'i True yapın ve MatrixA'dan MatrixF'ye kadar doldurun; herhangi bir PDF dönüşüm matrisindeki aynı altı sayı. Değerler sonlu ve determinant sıfırdan farklı olmalıdır; tekil bir matris shading'i bir doğruya çökertirdi. Bileşen altı değeri shading sözlüğünde /Matrix olarak da kaydeder ama doğrudan bir sh çağrısında PDFium'un boyadığı koordinat sistemini belirleyen, içerik akışındaki cm'dir

Burada sıra önemlidir ve üçüncü tuzak budur. re işlenenleri geçerli dönüşüm matrisinde yorumlanır. cm'yi öne koyarsanız sayfa boyutlu kırpma dikdörtgeni gradyanla birlikte ölçeklenir, döner ya da eğrilir; elipsi ezen bir matris görünür alanı da ezer ve sayfanın bazı bölümleri dolgusuz kalır. Önce dönüşümsüz user space'te kırpmak, sonra matrisi eklemek beklenen sonucu verir: bütün sayfayı kaplayan dönüşmüş bir gradyan. Bir tane kurmanız gerekirse Form XObject damgaları ve altı sayılı matrisleri rehberi, her matris bileşeninin ne yaptığını tek tek anlatır

procedure MakeEllipticalGlow(var Fill: TPdfShadingCreate);
begin
  BuildSealGlow(Fill);       // 450,160 merkezli radyal dolgu
  Fill.Coords := [0, 0, 0, 0, 0, 90];  // çemberler başlangıçta
  Fill.HasMatrix := True;
  Fill.MatrixA := 1.6;       // yatayda ger
  Fill.MatrixB := 0;
  Fill.MatrixC := 0;
  Fill.MatrixD := 0.8;       // dikeyde sık
  Fill.MatrixE := 450;       // sonra kaşe merkezine taşı
  Fill.MatrixF := 160;
end;

Raporu okumak ve sınırları bilmek

TPdfShadingCreateReport ne yazıldığını söyler: SourceByteCount ile OutputByteCount, CreatedShadingObjectNumbers (girdi başına bir nesne numarası, sırayla), AttachedShadingCount, Succeeded ile ErrorMessage. Doğrulama hataları girdiyi ve alanı adlandırır; örneğin kesinlikle artmayan bir durak listesi. Bunlar olduğu gibi günlüğe yazmaya değerdir

if Pdf.SaveAsShadingCreated(OutFile, Fills, Report) then
  Writeln(Format('%d shading(s) attached, %d -> %d bytes',
    [Report.AttachedShadingCount, Report.SourceByteCount,
     Report.OutputByteCount]))
else
  Writeln('Rejected: ' + Report.ErrorMessage);

Profil bilinçli olarak sınırlıdır. Yalnızca axial ve radyal shading'ler oluşturur; fonksiyon tabanlı ve mesh shading'ler (Type 1 ile 4-7) ile pattern renk uzayları reddedilir. Tek çağrı 1 ile 4096 arasında dolgu kabul eder; sayfa numaraları var olmalıdır ve kaynak adları harf, rakam, alt çizgi, eksi ve nokta içeren 127 karakterle sınırlıdır. Şifreli girdi trailer aşamasında reddedilir. İmzalı bir belge AllowSignedDocument := True ister; çıktı artımlı güncelleme değil eksiksiz bir yeniden yazım olduğundan mevcut imzalar artık doğrulanmaz, o yüzden gradyanları ekledikten sonra imzalayın. Her dolgu sayfa içeriğinin sonunda, hâlihazırda olanın üzerine boyanır; arka plan etkisi için önce gradyanı oluşturup metin ile görselleri sonra ekleyin ya da dolgunun anahtarlanabilir olması gerekiyorsa Delphi'de PDF isteğe bağlı içerik katmanlarını kontrol etme makalesinde anlatıldığı gibi isteğe bağlı içeriği kullanın

SSS

SaveAsShadingCreated çok duraklı gradyanları destekler mi?

Evet. Stops içinde 0'dan 1'e kesinlikle artan konumlarla 2 ile 256 arasında girdi verin. İki durak bir Type 2 fonksiyon üretir; üç ya da daha fazlası, türetilmiş /Bounds ile /Encode dizilerine sahip bir Type 3 stitching fonksiyonu üretir

Dosya geçerli görünürken gradyanım PDFium'da neden görünmez?

/ShadingType'ın bir ad değil 2 ya da 3 tamsayısı olduğunu ve sh operatörünün aynı içerik akışında bir re W n kırpması içinde çalıştığını kontrol edin. PDFium geçersiz shading türlerini sessizce atlar ve boş dikdörtgenli shading nesnelerini eler

Oluşturulan bir shading hangi renk uzaylarını kullanabilir?

TPdfShadingCreate, 1, 3 ya da 4 bileşenli profille DeviceGray, DeviceRGB, DeviceCMYK, CalGray, CalRGB, Lab ve ICCBased kabul eder. Pattern ile Indexed renk uzayları oluşturma profilinin parçası değildir

Gradyan oluşturma, isteğe bağlı içerik ve sahneleyen yazıcıların geri kalanı Delphi ve C++Builder için PDFium Component ile gelir; bileşen Delphi, C++Builder ve Lazarus projeleri için PDFium'u sarmalar