Teknik Makale

Delphi'de PDF Tip 2/3/4 Fonksiyonları: Üstel, PostScript

Delphi ve C++Builder için yerel VCL PDF bileşeni olan HotPDF, örnek ızgaraları yerine formüllerden inşa edilen üç PDF fonksiyon türünü değerlendirir: Tip 2 üstel enterpolasyon, Tip 3 dikiş ve Tip 4 PostScript hesap makinesi fonksiyonları; sırasıyla ISO 32000-1 §7.10.3, §7.10.4 ve §7.10.5'e karşılık gelir. Tip 2, bir eğri boyunca iki çıkış vektörü arasında harmanlar, Tip 3 tek bir girdi alanı boyunca birkaç alt fonksiyonu zincirler ve Tip 4, dallanabilen, karşılaştırabilen ve bir içerik akışının girdilerinden neredeyse her şeyi hesaplayabilen kısıtlanmış bir PostScript programı çalıştırır. Üçünden herhangi birini biraz yanlış yapın ve hata kendini asla bir hata olarak duyurmaz — ölü düz bir bant içeren bir gradyan, saf siyah oluşturan bir spot renk veya bir test paketinin tesadüfen denemediği girdilerde tam olarak bir birim kayan bir hesap makinesi fonksiyonu olarak ortaya çıkar

Bu üçü, bir formül yerine örneklenmiş bir ızgara saklayan ve Tip 0 renk arama tabloları üzerine tamamlayıcı makalede ayrıca ele alınan dördüncü bir tür olan Tip 0'ın yanında yer alır. Her iki aile de aynı sorunu çözer, bir girdiyi bir çıktıya eşlemek, ancak Tip 0 bir kez hesaplanıp dosyaya pişirilmiş veridir; Tip 2, 3 ve 4 ise okuyucunun her çağrıda değerlendirdiği koddur. Dördü de HotPDF'in oluşturucusunda, fonksiyon sözlüğünün /FunctionType girdisiyle anahtarlanan tek bir gönderim noktasını paylaşır; bu yüzden bir gölgelendirme, bir renk tonu dönüşümü veya bir yarı ton spot fonksiyonu, bir renk isteyebilmeden önce dördünden hangisini aldığını hiç bilmek zorunda kalmaz

Bir PDF Tip 2 üstel fonksiyonu nasıl çalışır?

Bir PDF Tip 2 fonksiyonu tek bir formül hesaplar — y = C0 + x^N × (C1 − C0), bileşen bileşen uygulanır — burada x, fonksiyonun tek girdisidir; formül çalışmadan önce /Domain'ine karşı normalize edilir (ISO 32000-1 §7.10.3). /C0 ve /C1, o aralığın iki ucundaki çıkış vektörleridir, çıkış bileşeni başına bir sayı, ve /N, aralarındaki eğriyi şekillendiren üstür: N = 1 çoğu gradyan durağının ve düotone dönüşümünün arkasındaki düz doğrusal rampayı verir, N 1'in üzerinde eğriyi C0'a doğru çeker ve N 0 ile 1 arasında onu C1'e doğru iter. RegisterExponentialFunction, bu sözlüğü beş argümandan inşa eder ve bir gölgelendirmeye, bir yarı ton spot fonksiyonuna veya spesifikasyonun bir /Function anahtarını kabul ettiği başka herhangi bir yere takılmaya hazır bir fonksiyon nesnesi geri verir

C0 ve C1 arasındaki bileşen sayısı ilişkisi iki kez önem taşır: bir kez bir Tip 2 fonksiyonu yazarken, bir kez de HotPDF oluşturmadığı bir fonksiyonu oluşturmak zorunda kaldığında. Yazma tarafında, RegisterExponentialFunction C0 ve C1'i birbirine karşı kontrol eder ve uyuşmazlarsa hata fırlatır; bu yüzden BeginDoc'a ulaşan bir çağrı zaten kendi içinde tutarlı bir fonksiyon nesnesidir. Ancak oluşturma tarafında, değerlendiriciye bir kaynak dosyanın gerçekte bildirdiği herhangi bir /C0 ve /C1 dizisine — önizleme için açılan bir matbaa dosyası veya bir kullanıcıya geri gösterilen imzalı bir belge diyelim — güvenmesi gerekir ve 2.376.0'dan önceki sürümler bu dizileri dört bileşen için boyutlandırılmış bir tampona okuyordu, CMYK durumu. Bir DeviceGray veya DeviceRGB üstel tonu, tek veya üç elemanlı bir /C0 ve /C1 ile, bu okumayı sessizce başarısız kılıyordu ve her iki diziyi de sıfırda bırakıyordu; bu yüzden ton, amaçlanan rengi yerine düz siyah boyuyordu. Sürüm 2.376.0, okuyucuyu sabit bir tampon yerine fonksiyonun gerçek bildirilen çıkış sayısına yeniden boyutlandırdı — tam olarak yalnızca CMYK olmayan bir test durumunun ortaya çıkardığı türden bir hata, çünkü mevcut paket her yerde CMYK çalıştırıyordu; burada dört-içine-dört her zaman uyuyordu

var
  EaseIn: THPDFDictionaryObject;
begin
  // Type 2: one input, N > 1 biases the ramp toward C0 (an ease-in curve)
  EaseIn := Pdf.RegisterExponentialFunction(
    [0,1],           // Domain: single input, clamped to [0,1]
    [0, 0, 0],       // C0: output at x = 0
    [0.8, 0, 0],     // C1: output at x = 1
    3,               // N: exponent, 1 = linear, > 1 eases toward C0
    []);             // Range omitted: defaults to a [0,1] clamp per output
end;

Tip 3 dikiş: bir Bounds dizisi boyunca alt fonksiyonları zincirleme

Bir PDF Tip 3 fonksiyonu, k alt fonksiyonu tek bir girdinin /Domain'i üzerinden tek bir parçalı eşlemeye diker ve bunu çalıştıran iki dizi /Bounds ve /Encode'dur (ISO 32000-1 §7.10.4). /Bounds, /Domain'i k ardışık aralığa oyan k − 1 iç bölme noktası tutar; değerlendirici, üst sınırı girdiyi aştığı ilk aralığı, veya girdi son sınıra ulaştığında son aralığı seçer ve o aralığın alt fonksiyonuna devreder. /Encode daha sonra girdiyi o aralık içindeki konumundan, değerlendirme bir çağrı daha derine, o alt fonksiyonun kendi /Domain ve /Range'ine devam etmeden önce, seçilen alt fonksiyonun kendisinin beklediği herhangi bir girdi aralığına — genellikle alt fonksiyon bir üstel segment daha ise [0, 1] — yeniden eşler

HotPDF'in dikiş değerlendiricisi eskiden yalnızca tam olarak iki alt fonksiyonu ele alıyordu ve /Bounds okuyucusu tam sekiz elemanlı bir dizi gerektiriyordu; bu yüzden iki segmentli bir gradyanın gerçekte ihtiyaç duyduğu tek bölme noktası — /Bounds'ta tek bir sayı — her zaman ayrıştırılamıyor ve fonksiyon hiçbir şey döndürmüyordu. /Encode hiç uygulanmıyordu. Sürüm 2.376.0, seçimi spesifikasyonun tanımladığı genel k-alt-fonksiyon aramasına yeniden yazdı ve /Bounds'u gerçek bildirilen uzunluğuna karşı okumaya başladı; bu yüzden bu kadar üstel segmentten dikilmiş üç, dört veya beş duraklı bir gradyan artık iki segmentli birinin her zaman iddia ettiği gibi çözülüyor. Aşağıdaki örnek, tek bir üstel eğrinin bir tasarımın gerektirdiği her renk durağını taşıyamadığı her durumda bir eksenel veya radyal gölgelendirmenin başvurduğu şekil olan siyahtan kırmızıya beyaza iki segmentli bir rampa inşa eder

var
  ToRed, ToWhite, Ramp: THPDFDictionaryObject;
begin
  // Two linear segments: black->red over [0, 0.5], red->white over [0.5, 1]
  ToRed   := Pdf.RegisterExponentialFunction([0,1], [0, 0, 0],   [0.8, 0, 0], 1, []);
  ToWhite := Pdf.RegisterExponentialFunction([0,1], [0.8, 0, 0], [1, 1, 1],   1, []);

  Ramp := Pdf.RegisterStitchingFunction(
    [0,1],                  // Domain: the stitched function's own input range
    [ToRed, ToWhite],       // Functions: k = 2 sub-functions
    [0.5],                  // Bounds: k - 1 = 1 split point
    [0,1, 0,1],             // Encode: 2 numbers per sub-function
    []);                    // Range omitted: inherited from each sub-function
end;

Bir Tip 4 PostScript hesap makinesi fonksiyonu, Tip 2 ve 3'ün yapamadığı neyi yapabilir?

Bir PDF Tip 4 fonksiyonu, kasıtlı olarak kısıtlanmış olsa da gerçek bir program çalıştırır: girdilerini bir işlenen yığınına iten, aritmetik, karşılaştırma, yığın manipülasyonu ve boolean operatörlerini artı if/ifelse koşullarını yürüten ve bittiğinde çıktılarını yığında bırakan bir PostScript hesap makinesi (ISO 32000-1 §7.10.5, Tablo 42). Bir döngü yapısı ve adlandırılmış değişken depolama yoktur, yalnızca yığın vardır; bu da uyumlu bir programı akıl yürütmesi kolay tutar — ancak bu kısıtlı operatör kümesi içinde Tip 4, Tip 2 ve Tip 3'ün ifade edemediği şeyleri ifade edebilir, örneğin bir DeviceN ayrımı için gerçek bir çok mürekkepli karıştırma formülü veya koşullu bir eşiğe sahip bir yarı ton spot fonksiyonu. HotPDF'in değerlendiricisi HPDFEvalPostScriptCalculator, programı bir kez belirtir — sayılar, operatörler ve { } prosedür blokları — ardından ISO 32000-1 §7.10.5'in gerektirdiği derinlik olan 100 girdilik bir işlenen yığınını gezer; patolojik veya elle yazılmış programlara karşı savunmacı bir dayanak olarak 50.000 değerlendirilen operatörlük sert bir tavanın arkasında

roll operatörü: yön ilk denemede ters gelmesi kolay

roll, argüman sırası ve döndürme yönü her ikisi de İngilizcenin onları tanımlama şeklinin tersi çalıştığı için ilk denemede ters çıkması en olası operatördür. n j roll, bir sayı n ve bir döndürme miktarı j açar, ardından en üstteki n yığın girdisini j konum döngüsel olarak kaydırır, bir uçtan düşen öğeleri diğerine sarar; spesifikasyondan doğrudan gelen kanonik örnek, a b c 3 1 roll'un c a b üretmesidir — en üstteki öğe grubun altına gider, tersi değil, ve diğer her öğe yer açmak için bir yukarı kayar. HotPDF'in değerlendiricisi, yığın girdisi i'nin yeni konumunu (i + j) mod n olarak hesaplar; bu, o örnekle tam olarak eşleşir, ancak bu, döndürme ters çevrilerek de yazılması aynı derecede kolay olan iki satırlık bir döngüdür ve aynalanmış bir roll yine de makul görünen bir renk üretir — sadece dosya yazarının istediği renk değildir

const
  Prog = '{ 3 1 roll }';   // (a b c) -> (c a b): the third input moves to the front
var
  Reorder: THPDFStreamObject;
begin
  // Type 4: 3 inputs, 3 outputs, no extra clamping beyond Domain/Range
  Reorder := Pdf.RegisterPostScriptFunction(
    [0,1, 0,1, 0,1],   // Domain: 2 numbers per input
    [0,1, 0,1, 0,1],   // Range: 2 numbers per output (required for Type 4)
    Prog);
end;

round, Delphi'nin Round'u değildir: yarımı-yukarı ile bankacı yuvarlaması

PostScript'in round operatörü, her seferinde bir .5 beraberliğini daha büyük tam sayıya doğru çözer ve Delphi'nin yerleşik Round fonksiyonu bunu yapmaz: yarımı-çifte-yuvarlar, tekrarlanan yuvarlamanın önyargı biriktirmemesi için bir .5 beraberliğinin hangi yöne düştüğünü değiştiren bankacı yuvarlaması kuralı. İkisi neredeyse her yerde uyuşur ve burada önem taşıyan sınırda tam olarak uyuşmazlar — Delphi'nin Round(0.5)'i 0 döndürür ve Round(2.5) 2 döndürür, oysa PDF spesifikasyonunun round'u bu aynı girdiler için 1 ve 3 ister — bu yüzden uyuşmazlık gündelik testlerde saklanır ve ardından bir hesap makinesi programının ara matematiği tam olarak bir yarım tam sayıya indiği her yerde tutarlı bir birim-kayması olarak yeniden ortaya çıkar. ISO 32000-1 §7.10.5 Tablo 42, round'un bir kesirli .5'i daha büyük tam sayıya doğru ittiği konusunda açıktır; bu yüzden HotPDF operatörü Delphi'nin Round'unu çağırmak yerine Floor(x + 0.5) olarak uygular ve bir Tip 4 programının aritmetiğini elle yeniden uygulayan veya nokta kontrolü yapan herhangi bir kodun aynı ikameye ihtiyacı vardır

function PostScriptRound(const X: Double): Double;
begin
  // ISO 32000-1 7.10.5 Table 42: round pushes .5 toward the greater
  // integer. Delphi's Round() is banker's rounding and disagrees here:
  // Round(0.5) = 0, Round(2.5) = 2 - both one short of the spec value.
  Result := Floor(X + 0.5);
end;

Kayıt zamanı doğrulama kötü bir hesap makinesi programını erken yakalar

Bozuk bir Tip 4 programını yazma anında yakalamak ucuzdur, başka herhangi bir yerde yakalamak pahalıdır; bu yüzden RegisterPostScriptFunction yalnızca kaynak metni saklamaz: fonksiyon nesnesi belgeye hiç yazılmadan önce, bildirilen /Domain'in orta noktasında programı bir kez deneme değerlendirmesinden geçirir. Dengesiz { } blokları, tanınmayan bir operatör, bir yığın alt taşması veya /Range ile eşleşmeyen bir çıkış sayısı, hepsi bu deneme çalışmasında başarısız olur ve zaten gönderilmiş bir dosyada QA sırasında keşfedilen bir oluşturma sanatı yerine çağrı yığınının RegisterPostScriptFunction çağrısını gösterdiği bir istisnayı hemen fırlatır. Orta nokta denemesi, programın tüm /Domain'i boyunca doğru olduğunu kanıtlamaz — girdi aralığının yalnızca bir kenarının yakınında yanlış davranan koşullu bir dal yine de tek bir örnek noktasını geçebilir — ancak yalnızca bir köşede yanlış olmak yerine yapısal olarak bozuk olan programların tüm sınıfını kapatır

Gradyanların ve spot renklerin bu fonksiyonları nerede işe koştuğu

Tip 2, 3 ve 4 gerçek bir PDF'de nadiren yalnız başına görünür; spesifikasyonun bir /Function anahtarını kabul ettiği her yerde ortaya çıkarlar ve en yaygın iki tüketici gölgelendirmeler ve spot renk tonu dönüşümleridir. Bir eksenel veya radyal gradyanın sh operatörü (ISO 32000-1 §8.7.4.5), gradyan ekseni boyunca konum başına /Function'ını bir kez değerlendirir; bu tam olarak Tip 3 dikişin var olduğu çok duraklı durumdur. Bir Separation veya DeviceN renk alanının tonu dönüşümü, bu üç türün diğer sık evidir ve Tip 4'ün kendini kanıtladığı yerdir: tek bir spot mürekkep genellikle bir Tip 2 veya Tip 0 eğrisine indirgenir, ancak gerçek tuzaklama ve baskı üstü davranışı olan birkaç mürekkebin bir DeviceN karışımı genellikle yalnızca bir PostScript hesap makinesinin ifade edebileceği koşullu mantığa ihtiyaç duyar; Separation ve DeviceN spot renklerini oluşturma üzerine makalede ele alınan durum. RegisterSeparationFunc, yazma tarafındaki eşleşen çağrıdır: bir kolorant adı, alternatif bir renk alanı ve Register*Function ailesinin döndürdüğü herhangi bir nesneyi alır ve o tonu dönüşümünü, sayfanın geri kalanının scn/SCN ile seçebileceği bir Separation renk alanı kaynağına bağlar

Birlikte, Tip 0'ın örneklenmiş ızgaraları ve bu üç formül odaklı tür, bir PDF'in bildirebileceği her /Function'ı kapsar ve doğru olanı seçmek çoğunlukla zaten neye sahip olduğunuzla ilgili bir sorudur: başka bir yerde hesaplanmış bir arama tablosu Tip 0 olur, iki uç noktalı bir harman Tip 2 olur, bir alan boyunca zincirlenmiş birkaç harman Tip 3 olur ve gerçek koşullu mantığa sahip herhangi bir şey Tip 4 olur. RegisterExponentialFunction, RegisterStitchingFunction ve RegisterPostScriptFunction, ISO 32000-1 fonksiyon ve gölgelendirme API'sinin geri kalanının yanı sıra, Delphi ve C++Builder için standart HotPDF Bileşeni'nin bir parçasıdır