Teknik Makale

Delphi'de COLRv1 Tekrar/Yansıma Gradyanları ve PDF Shading

HotPDF Component, COLRv1 tekrar ve yansıma gradyanlarını glifi rasterleştirmek yerine vektör PDF shading'leri olarak boyar. Geometri, renk çizgisinin tam sayıda çevrimini kapsayacak biçimde gerilir; her çevrim, Extend kapalı olacak şekilde kendi axial ya da radial shading'ini alır; renkler lineer ışıklı CalRGB içinde aradeğerlenir ve Porter-Duff in/out composite modları luminosity soft mask ile ifade edilir

HotPDF'in Delphi'de COLR v1 ve SVG renkli emoji fontlarını nasıl çizdiği yazısını ele aldığımızda tekrar ve yansıma extend modları, yerel yolun üretmediği şeyler listesindeydi; bu glifler raster yedeğine gidiyordu. Daha yeni HotPDF sürümleri onları vektör olarak çiziyor. Oraya varmak birkaç yanlış başlangıca mal oldu; çoğuna, en az bir yaygın kullanılan görüntüleyicide yanlış çıkan, makul görünümlü bir PDF varsayımı neden oldu. Bu yazı o varsayımları tek tek geziyor; çünkü CSS, SVG ya da OpenType gradyanlarını PDF üzerine eşleyen herkes aynılarıyla karşılaşacak

Bir PDF görüntüleyicisi gradyanı sizin için neden tekrarlayamaz?

Bir PDF shading'inin tekrar ya da yansıma modu yoktur; her çevrimi üreten tarafın kendisi dizer. ISO 32000-1 §8.7.4.5.3 ve §8.7.4.5.4, axial ve radial shading'ler için /Extend tanımlar ve yaptığı tek şey, eksene ucundaki rengi t0 ya da t1'de sürdürmektir. Bu COLRv1 EXTEND_PAD'dir, başka hiçbir şey değildir. OpenType COLR v1 ayrıca EXTEND_REPEAT — parametre bir tam sayıyı her geçtiğinde renk çizgisinin baştan başladığı — ile EXTEND_REFLECT — her öbür çevrimin geriye doğru aktığı — modlarını tanımlar

Delphi'de HotPDF COLRv1 PDF shading eşlemesi: EXTEND_PAD, axial ve radial shading'lerin /Extend sürdürme bayrağıyla eşleşir; EXTEND_REPEAT ile EXTEND_REFLECT'in PDF karşılığı yoktur ve çevrim başına shading geometrisinden kurulmaları gerekir
PDF shading'leri renkleri yalnızca t0 ve t1'de doldurur; COLRv1 tekrar ya da yansıma gradyanı bu yüzden görüntüleyicide bir anahtarla açılmaz, üretici tarafından çevrim çevrim dizilir

HotPDF'in ilk denemesi periyodik işi fonksiyona yaptırmayı denedi. Geniş bir domainle — 25 çevrimlik bir aralık için kabaca [-12.73, 12.73] — bir Type 0 örneklenmiş fonksiyon kaydetti (§7.10.2), içini katlanmış örneklerle doldurdu ve Extend'i açtı. İlk çevrimin içinde minimal bir test dosyası doğru render ediliyordu; dışında MuPDF tek bir ortalama renk boyuyordu ve Poppler daha iyiydi. Her iki görüntüleyici de fonksiyonu shading parametresi boyunca değerlendirir ve eksenin ötesinde doldurur; hiçbiri genişletilmiş bölgede periyodik bir fonksiyona geri dönüp sormaz. Geniş domain ayrıca spesifikasyonun yanlış okunmasıydı: shading parametresi t doğrudan fonksiyona verilir ve §7.10.1 uyarınca fonksiyon domaini dışındaki bir girdi kırpılır; domaini shading'in /Domain'iyle (varsayılan [0 1]) uyuşmayan bir fonksiyonun örneklerinin yalnızca bir kısmı hiç okunur

Çalışan tasarımın üç parçası vardır ve hepsi MuPDF ile Poppler render'larına karşı doğrulandı. Shading geometrisi fiziksel olarak tam sayı N çevrimi kapsar; hiçbir piksel Extend'e bağlı kalmaz. Örneklenmiş fonksiyon varsayılan [0 1] domainini korur; görüntüleyicinin gerçekten verdiğine uyar. Ve örnek dizisi kendisi çevrim başına katlanır — çevrim başına 256 aralıkla; fonksiyon böylece herhangi bir görüntüleyici özelliğine yaslanmak yerine tekrar ya da yansıma renk verisini kendi içinde taşır

Bir renk çizgisini Type 0 örneklerine katlamak

Katlamak, renk çizgisini çevrim başına bir kez örneklemek ve reflect modu için diziyi tek çevrimlerde aynalamak demektir. Aşağıdaki yardımcı sade Delphi'dir, HotPDF API'si değil; ve naif sürümün yanlış yaptığı iki şeyi doğru yapar. Saklanan sRGB baytları üzerinde değil lineer ışıkta aradeğerleme yapar ve sert durakları (aynı ofsette iki durak) uzaktaki tarafın rengine çözer; OpenType ve CSS anlambilimi budur

uses
  System.SysUtils, System.Math;

type
  TColorStop = record
    Offset: Single;  // bir çevrim içinde 0..1, artan sırada
    R, G, B: Byte;   // CPAL tarzı sRGB baytları
  end;

function SRGBToLinear(V: Byte): Double;
var
  C: Double;
begin
  C := V / 255;
  if C <= 0.04045 then
    Result := C / 12.92
  else
    Result := Power((C + 0.055) / 1.055, 2.4);
end;

function LinearToSRGB(L: Double): Byte;
var
  C: Double;
begin
  L := EnsureRange(L, 0, 1);
  if L <= 0.0031308 then
    C := L * 12.92
  else
    C := 1.055 * Power(L, 1 / 2.4) - 0.055;
  Result := Round(C * 255);
end;

// Bir renk çizgisi çevrimi, 8 bitlik RGB üçlüleri olarak. Reverse = True
// çevrimi aynalar; reflect modunun tek çevrimlerde yaptığı da budur
function SampleCycle(const Stops: array of TColorStop; Count: Integer;
  Reverse: Boolean): TBytes;
var
  I, S: Integer;
  T, F, Span: Double;
begin
  SetLength(Result, Count * 3);
  for I := 0 to Count - 1 do
  begin
    T := I / (Count - 1);
    if Reverse then
      T := 1 - T;
    S := 0;
    // >= sert durağın ardındaki tarafı seçer
    while (S < High(Stops) - 1) and (T >= Stops[S + 1].Offset) do
      Inc(S);
    Span := Stops[S + 1].Offset - Stops[S].Offset;
    if Span > 0 then
      F := EnsureRange((T - Stops[S].Offset) / Span, 0, 1)
    else
      F := 1;
    Result[I * 3] := LinearToSRGB(SRGBToLinear(Stops[S].R) +
      (SRGBToLinear(Stops[S + 1].R) - SRGBToLinear(Stops[S].R)) * F);
    Result[I * 3 + 1] := LinearToSRGB(SRGBToLinear(Stops[S].G) +
      (SRGBToLinear(Stops[S + 1].G) - SRGBToLinear(Stops[S].G)) * F);
    Result[I * 3 + 2] := LinearToSRGB(SRGBToLinear(Stops[S].B) +
      (SRGBToLinear(Stops[S + 1].B) - SRGBToLinear(Stops[S].B)) * F);
  end;
end;

HotPDF içindeki COLRv1 renderer bu taslaktan daha ileri gider. Big-endian 16 bitlik lineer örnekleri bir CalRGB fonksiyonuna yazar, alpha'yı ayrı bir 16 bitlik gri örnek olarak saklar; sırada gösterilen genel yardımcılar ise 8 bitlik DeviceRGB üçlüleri alır — taslağın sRGB baytlarına geri dönmesinin nedeni bu. Okuyucunun o örneklerle ne yaptığının kuramı, 3D renk LUT'ları olarak Type 0 örneklenmiş fonksiyonlar makalesinde aradeğerleme modeliyle ayrıntılı işlenir

Her çevrim neden kendi shading'ine ihtiyaç duyar?

Her çevrimin kendi shading'i olması gerekir, çünkü okuyucular örneklenmiş bir fonksiyonu tam olarak değerlendirmez; bir renk tablosu önceden hesaplarlar ve tablo, bir çevrimin son renginden ötekinin ilk rengine sarmada aradeğerleme yapar. MuPDF bir shading'in tek boyutlu fonksiyonunu 256 girdilik bir renk tablosu olarak saklar. 11 çevrimi tek bir 2816 örnekli fonksiyona paketleyin; kaydedilen her örnek tam olabilir ama MuPDF yine de 12-14 düzey kanal hatası gösterir, çünkü 256 tablo girdisi bütün aralığa yayılır ve sert tekrar dikişi bir rampaya yumuşar. Poppler ile aynı dosyanın analitik aradeğerlemesi ikisi de doğruydu; yani bu bir okuyucu hassasiyet sınırıdır, dosya hatası değildir ve örnek sayısını artırmak yaramaz

Delphi için HotPDF COLRv1 tekrar gradyanı dikiş şeması: 11 çevrime yayılan tek bir örneklenmiş fonksiyon, MuPDF'in 256 girdilik renk tablosunun her tekrar dikişini bir rampaya yumuşatmasına izin verir; çevrim başına bir axial shading ise her tabloyu tek bir çevrimin içinde tutar ve sert renk sıçramasını korur
Okuyucular shading başına sabit bir renk tablosu önceden hesaplar; düzeltme bu yüzden geometriktir: Extend kapalıyken çevrim başına bir shading, tekrar dikişini hiçbir tablonun üzerinden aradeğerleme yapmayacağı bir sınıra taşır

Düzeltme, her okuyucu tablosunu tek bir çevrimin içinde tutmaktır. HotPDF periyodik doğrusal gradyanları, her ikisi de false olan Extend bayraklarıyla, özgün çevrim başına bir bağımsız axial shading olarak yazar; böylece sondan başa renk sıçraması tek bir tablonun içinde değil iki shading arasındaki sınıra düşer. Reflect için teklik testi, renk çizgisinin başlangıcından önceki negatif çevrimler dahil, özgün işaretli çevrim numarasını kullanır ve örnekler yeniden hesaplanmak yerine shading yönü çevrilir. Tüm çevrimler tek bir lineer ışıklı izole yüzeye çizilir ve boya alpha'sı sonda bir kez uygulanır; yarı saydam bir gradyan dikişlerde opaklığını ikiye katmaz

Genel THotPDF.RegisterSampledAxialGradient ve THotPDF.RegisterSampledRadialGradient metotları, örneklenmiş shading yapı taşını kendi çizimleriniz için açar. İkisi de geometriden sonra (..., DomainT0, DomainT1: Single; const Samples: TBytes; SampleCount, Cycles: Integer) alır, PDF 1.3 ister, Samples tam olarak SampleCount * 3 bayt tutmadığında istisna fırlatır ve THPDFPage.SetFillPattern için bir pattern adı döndürür. İki uçta da Extend açar ve Cycles'ı görüntüleyicilerin yok saydığı özel bir /Cycles pattern anahtarı olarak kaydeder. Çevrim başına bir shading kuralını bunlarla izlemek için çevrim başına bir pattern kaydedin ve kırpmayı bir bant dikdörtgenine bırakın

// Çevrim başına bir shading'den oluşan yatay tekrar ya da yansıma gradyanı.
// Pattern koordinatları varsayılan PDF uzayındadır; yatay eksen bu örneği
// sayfanın yukarıdan aşağıya Y kuralından bağımsız tutar
procedure PaintPeriodicBands(Pdf: THotPDF; const Stops: array of TColorStop;
  X, Y, Width, Height, CycleLength: Single; Reflect: Boolean);
const
  SamplesPerCycle = 257;  // çevrim başına 256 aralık
var
  Cycle, CycleCount: Integer;
  X0, X1: Single;
  Samples: TBytes;
  PatternName: AnsiString;
begin
  CycleCount := Ceil(Width / CycleLength);
  for Cycle := 0 to CycleCount - 1 do
  begin
    X0 := X + Cycle * CycleLength;
    X1 := X0 + CycleLength;
    Samples := SampleCycle(Stops, SamplesPerCycle, Reflect and Odd(Cycle));
    // Domain [0, 1]: shading parametresi fonksiyona değişmeden ulaşır
    PatternName := Pdf.RegisterSampledAxialGradient(X0, 0, X1, 0, 0, 1,
      Samples, SamplesPerCycle, 1);
    if PatternName = '' then
      Exit;  // belge sürümü PDF 1.3 altına kilitli
    Pdf.CurrentPage.SetFillPattern(PatternName);
    // Bant, shading'i tam bir çevrime kırpar; Extend asla görünmez
    Pdf.CurrentPage.Rectangle(X0, Y, Math.Min(X1, X + Width) - X0, Height);
    Pdf.CurrentPage.Fill;
  end;
end;

Çok uzun aralıklar başka bir numara ister, çünkü çevrim başına bir shading, çevrim başına bir kaynak demektir. Periyodik doğrusal COLRv1 gradyanlarında HotPDF ikileme şemasına geçer: yaprak bir form, tek çevrimli tekrar ya da iki çevrimli yansıma ilkelini taşır ve her Form XObject düzeyi bir öncekini genişliği kadar kaydırarak iki kez çizer. Form sayısı aralıkla logaritmik büyür; görüntüleyici yine de her görünür çevrimi boyar, böylece faz hizalaması sonrası kapsam 65536 çevrim birimiyle sınırlıdır. İlkelini bir Type 1 tiling pattern'e koyan daha eski bir sürüm atıldı; çünkü okuyucular döşeme aralığını aygıt ızgarasına uyarlar ve Poppler, tam sayı piksel olmayan bir çevrimde faz dışına savruldu

Radyal çember aileleri için parametre aralığını çözmek

Bir radyal gradyan glifi yalnızca çevrim aralığı doğru çember ailesi için hesaplandığında kapsar ve COLRv1 radyal boyamalar farklı matematiğe sahip üç aileye ayrılır. D = c1 - c0 ve dr = r1 - r0 ile kesinlikle iç içe bir ailede |dr| > |D|, iç içe olmayan bir konide |D|² > dr², teğet bir ailede |D| = |dr| vardır. HotPDF parametreyi özgün ailenin sıfır yarıçaplı noktasından ölçer ve yaydığı her çemberin merkezini ile yarıçapını aynı özgün parametreye bağlar; yeniden eşlenen negatif yarıçaplar asla bağımsız olarak kırpılmaz

Delphi için HotPDF COLRv1 radyal gradyan karar ağacı: dr ile D karşılaştırması, ikili aramayla çözülen iç içe çember ailelerini; kısa çevrim başına radyal shading'lerle çözülen konileri ve q = |W|^2 / (2(W*V)) lineer köküne indirgenen teğet ailelerini ayırır
|dr| ile |D| arasındaki eşitsizlik çevrim matematiğini belirler: iç içe aileler ilk değen çember için ikili arama yapar, koniler çevrim başına bir kısa shading alır ve teğet doğrular tekillik şeridini saydam bırakır
  • İç içe aileler doldurma kutusunu monoton biçimde keser; parametre aralığı dört köşeden ve kutuya değen ilk çember için bir ikili aramadan gelir. 64 çevrime kadar, çevrim başına 256 örnek olacak biçimde tek bir shading'e örneklenir; 4096 örnekle sınırlıdır, daha derin aralıklar çevrim başına dilimlere geçer
  • Koniler ve eş yarıçaplı şeritler, özgün çevrim sınırlarında ayrı kısa radyal shading'ler kullanır; her biri 257 örnekli ve Extend kapalı, izole ve non-knockout bir grup içinde özgün parametre sırasında compositing edilir; böylece çemberlerin örtüştüğü yerde en büyük geçerli parametre kazanır
  • Teğet aileler çember denklemini lineer bir denkleme indirger. Paylaşılan teğet noktasına göre kök q = |W|² / (2·(W·V)) olur ve kapsanan bölge, o noktanın önündeki açık yarı-düzlemdir

Teğet durumun gerçek bir tekilliği vardır. Teğet doğru doldurma kutusunu kestiğinde o doğruya yakın piksellere ulaşmak için gereken çevrim sayısı sınırsızdır; sonlu sayıda shading onu kapsayamaz. HotPDF teğet noktasından 64 çevrimlik bir bütçe harcar ve kalan ince şeridi saydam bırakır. Kutunun dört köşesi de teğet noktasının ardındaysa boya bütünüyle saydamdır ve glifin geri kalanı yine çizilir. Sonlu koni yolu 4096 zorunlu çevrime kadar kabul eder; kapsam 1024 dilimle sınırlıdır ve sınırı aşan hiçbir şey boyanmaz; 1024 dilimli bir glif kabaca 1.8 MB tutar

Lineer ışık rengi ve iki haneli tuzak

COLRv1 lineer ışıkta aradeğerler ve compositing yapar; HotPDF bu yüzden her CPAL baytını sRGB ters transfer fonksiyonuyla çözer ve düz renkleri, shading'leri ve iç grupları, D65 beyaz, sRGB primerleri ve /Gamma [1 1 1] ile (§8.6.5.3) tek bir CalRGB uzayında boyar. Yalnızca bitmiş kök yüzey, çağıranın blend uzayına dönüşür. Saklanan sRGB baytlarını aradeğerlemekse gözle görülür biçimde daha koyu ara noktalar üretir; her gradyan motorunun bir kez yaptığı klasik gama hatası

Lineer ışığa geçiş eski bir biçimlendirici hatasını açığa çıkardı. Koyu bir CPAL kanal değeri olan 12, lineer ışıkta kabaca 0.0036765 olur ve scn'in arkasındaki ortak sayı biçimlendirici yalnızca iki hane tutuyordu; o bileşen, herhangi bir görüntüleyici dosyaya dokunmadan 0 olarak yazılıyordu. Belirti tam olarak okuyucu nicemlemesine benziyordu ve ilk refleks MuPDF'in 8 bitlik grup arabelleklerini suçlamaktı. Dolgu ve kontur bileşenleri artık küçük değerleri koruyan ve süreç yerelini yok sayan üssüz bir ondalık biçimlendiriciden geçiyor. Ders genellenir: koyu bir renk kaybolduğunda görüntüleyiciyi araştırmadan önce içerik akışını dökün ve yazılan işlenene bakın

O düzeltmeden sonra görüntüleyici hâlâ önemlidir. MuPDF 1.28.2, açık bir blend renk uzayı taşıyan izole bir saydamlık grubunu compositing ederken sıfıra yakın lineer kanalları kaybeder; Poppler 26.07.0 Splash ise grup blend uzaylarını büsbütün yok sayar. Seçime bağlı THotPDF.COLRv1DeviceSpaceGroupBlend özelliği (varsayılan False), COLRv1 gruplarını blend renk uzayı olmadan yazar; böylece içerik renkleri, shading'ler ve fonksiyonlar lineer ışıklı CalRGB kalırken gruplar miras kalan device uzayında compositing edilir. Bedeli şu: grup renk uzaylarına riayet eden görüntüleyicilerde örtüşen yarı saydam katmanlar o zaman device uzayında karışır

PDF hangi COLRv1 composite modlarını tam olarak ifade edebilir?

HotPDF, COLRv1 composite modlarının 5-8 aralığını (SRC_IN, DEST_IN, SRC_OUT, DEST_OUT) yerli olarak render eder; 9-12 aralığı (SRC_ATOP, DEST_ATOP, XOR, PLUS) ise OnColorGlyphRasterize'a gider. In ve out modları, bir yüzeyin ötekinin alpha'sıyla çarpımıdır; soft mask'in hesapladığı da tam olarak budur (§11.6.5). Kalan işlenen sıradan bir form olarak yakalanır; maskelenen işlenen bir alpha geçişinde, tek bir izole DeviceGray grubunun içinde düz içerik olarak yakalanır ve /BC [0] ile bir /Luminosity maskesi olarak kullanılır. Out modları her alpha için 1 − α yazar ve maske içeriğinin altına opak beyaz bir örtü koyar; kalan taraf, öteki işlenenin hiç boyamadığı yerde görünür kalır

Öteki dördü iki yüzeyin çarpımlı toplamasını ister. PDF'in source-over modelinde arka plan terimi daima kaynağın (1 − α) değeriyle zayıflatılır ve iki yüzeyi toplayan ya da arka planı yalnızca kaynağın kapsamasıyla ölçekleyen bir ilkel yoktur; atop, xor ve plus bu yüzden vektör olarak yazılamaz. Bir kısıt da çıplak test PDF'lerinden geldi: içeriği bir form Do içine alan soft-mask gruplarını her iki görüntüleyici de yanlış hesaplıyor; maskelenen işlenen bu yüzden düz olmak zorunda. Düz renkler, pad gradyanları ve onları saran bir PaintGlyph sarmalayıcısı yeterlidir; iç formlar üreten katmanlar, dönüşümler ve periyodik gradyanlar glifi yedeğe yollarlar

var
  Pdf: THotPDF;
  Fallback: TEmojiFallback;  // aşağıdaki olay imzasına sahip işleyici sınıfı
begin
  Pdf := THotPDF.Create(nil);
  Fallback := TEmojiFallback.Create;
  try
    Pdf.AutoLaunch := False;
    Pdf.FileName := 'periodic-icons.pdf';
    // Seçime bağlı: MuPDF ve Poppler için device-space grup compositing
    Pdf.COLRv1DeviceSpaceGroupBlend := True;
    // atop/xor/plus gliflerini ve alt küme dışındaki her şeyi alır:
    // (Sender; Format; const FontBytes, AssetData: TBytes; GlyphID: Word;
    //  PaletteIndex, PixelSize: Integer; out Width, Height: Integer;
    //  out RGBA: TBytes; out Handled: Boolean)
    Pdf.OnColorGlyphRasterize := Fallback.Rasterize;
    Pdf.BeginDoc;
    Pdf.RegisterUnicodeTTF('Fonts\GradientIcons.ttf');
    if not Pdf.DrawRegisteredColorGlyph(Pdf.CurrentPage, $E001,
      72, 144, 'Gradient Icons', 48) then
      Writeln('U+E001 was neither drawn natively nor rasterized');
    Pdf.EndDoc;
  finally
    Fallback.Free;
    Pdf.Free;
  end;
end;

Vektör yolunun hâlâ geride kaldığı yerler

Vektör çıktı, her görüntüleyicide özdeş pikseller demek değildir ve HotPDF kabul kayıtları bunu açıkça yazar. Çevrim sınırına bir iki adım kalan örnekler okuyucu aradeğerlemesiyle yumuşatılır; piksel kontrolleri dikişlerden uzakta örnekler, fonksiyon akışları ise onları çözerek doğrulanır. Ayrı saklanan straight RGB ile alpha örneklerini aradeğerlemek, düşük alpha uçlarında ve sert duraklarda bir yaklaşımdır; ölçülen hata çevrim başına 256 örnekle ikinci mertebedir ama tam değildir. Poppler Splash, pikselin merkezinde değil sol kenarında örnekler; en dik rampalarda küçük farklar olarak ortaya çıkar

Bütçeler de gerçek sınırlardır. İç içe radyal aileler 4096 çevrimlik aralıkların ötesini reddeder; koniler ve teğet aileler 4096 zorunlu çevrimin ötesini reddeder ve tekillik doğrusuna yakın teğet şeridi boş kalır. Değişen durak alpha'sı PDF 1.4 saydamlığı ister; PDF/A-1, PDF/X-1a ya da PDF/X-3 altında etkin değildir. Bir glif bu sınırlardan herhangi birinin dışına düşerse DrawRegisteredColorGlyph, hiçbir operatör yazmadan bütün grafiği reddeder; eskiden olduğu gibi hep ya da hiç davranışı

SSS

HotPDF, COLRv1 tekrar ve yansıma gradyanlarını rasterleştirir mi?

Hayır. Daha yeni HotPDF sürümleri onları bütün çevrimleri kapsayan vektör axial ve radial shading'ler olarak yazar. Yalnızca atop, xor ve plus composite gibi desteklenen alt kümenin dışındaki grafikler OnColorGlyphRasterize'a gider

Neden tüm çevrimler için tek bir örneklenmiş fonksiyon kullanılmıyor?

Okuyucular fonksiyondan bir renk tablosu önceden hesaplar ve MuPDF'in tablosu 256 girdilidir. Birçok çevrime yayıldığında her tekrar dikişinin üzerinden aradeğerleme yapar. Çevrim başına bir shading, her tabloyu tek bir çevrimin içinde tutar

COLRv1DeviceSpaceGroupBlend ne zaman açılmalı?

COLRv1 gliflerindeki siyaha yakın kanallar MuPDF'te sıfıra düşerken açın. Grup renk uzaylarına riayet eden görüntüleyicilerde örtüşen yarı saydam katmanların lineer ışıkta karışması gerekiyorsa kapalı tutun

Yerli COLRv1 gradyanları, renkli emoji, örneklenmiş shading'ler ve saydamlık gruplarının tamamı Delphi ve C++Builder için HotPDF Component'in parçasıdır; bileşen PDF belgelerini doğrudan Delphi ve C++Builder kodundan üretir