Teknik Makale

HotPDF ile Delphi'de PDF Sayfalarını SVG'ye Aktarma

HotPDF, tek bir çağrıyla — BuildLoadedPageSVG — yüklü herhangi bir PDF belgesinin bir sayfasını bağımsız SVG işaretlemesine aktarır; bu çağrı, eksiksiz SVG belgesini bir dize olarak döndürür. Dışa aktarılan işaretleme; sayfa geometrisini, gerçek SVG text öğeleri olarak metni, gömülü raster görüntüleri ve PDF operatörlerinin her çizim işleminde oluşturduğu vuruş (stroke) durumunu taşır

İşte tam bu son kısımda çoğu ev yapımı dönüştürücü sessizce çöker. Bir PDF sayfasını SVG'ye çevirmek bir koordinat sorunu gibi görünür, ama aslında bir durum (state) sorunudur. PDF, içerik akışı yorumlanırken grafik durumu değişen bir yığın makinesidir; SVG ise her öğesinin kendi sunum özniteliklerini taşıdığı bildirimsel (declarative) bir ağaçtır. Yorumlayıcının bir öğe üretilirken anlık görüntüsünü alamadığı her şey çıktıdan basitçe kaybolur ve bu hata sessizdir: geçerli ama ince bir biçimde yanlış bir sayfa çizen bir SVG elde edersiniz

Bir PDF sayfası neden SVG'ye doğrudan dönüştürülemiyor?

Dönüşümü zorlaştıran üç uyumsuzluk vardır ve üçü de, orijinaliyle yan yana karşılaştırana kadar makul görünen çıktılar üretir. Birincisi y eksenidir. PDF kullanıcı uzayı, sayfanın sol alt köşesinden yukarı doğru büyür; SVG ise sol üstten aşağı doğru büyür. Sayfa düzeyinde tek bir çevirme (flip) çizim koordinatlarını düzeltir, ama bu kez her glifi bozar, çünkü tüm tuvali çevirmek harf biçimlerini de aynalar

İkinci uyumsuzluk kalıtımdır (inheritance). PDF'de q ve Q, çizgi genişliği, çizgi başlığı (line cap), çizgi birleşimi (line join), gehre sınırı (miter limit), kesik çizgi dizisi, kesik çizgi fazı ve alfayı içeren bir grafik durumunu yığına iter ve yığından çıkarır. SVG'de ise bir öznitelik adlandırmayan bir öğe, onu bir üst (ancestor) gruptan devralır; bu tamamen farklı bir kapsam (scoping) kuralıdır. Yalnızca geçerli dönüşüm matrisini izleyip vuruş durumunu unutan bir dışa aktarıcı, bir Q sonrası geri yüklenen durumun sonraki öğelere sızmasına izin verir

Üçüncüsü, PDF birçok şeyi değer yerine kurala (convention) göre ifade eder. Çizgi başlıkları ve birleşimleri tam sayılardır, sıfır çizgi genişliği görünmez bir çizgi değil aygıt uzayında bir ince çizgi (hairline) anlamına gelir ve boyama operatörlerinin yıldızlı varyantları rengi değil sarma kuralını (winding rule) değiştirir. Bunların her biri bir kopya değil, bir çeviri gerektirir

Sıradan durum için tek bir çağrı

Bir web görüntüleyici, bir fark (diff) aracı veya bir tasarım teslimi için sayfaları dışa aktarma gibi sıradan bir iş için API yüzeyi tek bir fonksiyondur. BuildLoadedPageSVG, o an yüklü olan belgeye göre sıfır tabanlı bir sayfa dizini alır ve SVG belgesini bir AnsiString olarak döndürür:

var
  Pdf: THotPDF;
  I: Integer;
  Svg: AnsiString;
  Output: TFileStream;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('statements.pdf', '') <= 0 then
      Exit;                     // LoadFromFile sayfa sayısını döndürür
    for I := 0 to Pdf.LoadedPageCount - 1 do
    begin
      Svg := Pdf.BuildLoadedPageSVG(I);
      if Length(Svg) = 0 then
        Continue;
      Output := TFileStream.Create(Format('page-%d.svg', [I + 1]), fmCreate);
      try
        Output.WriteBuffer(Svg[1], Length(Svg));
      finally
        Output.Free;
      end;
    end;
  finally
    Pdf.Free;
  end;
end;

Aynı dışa aktarma işlevi, HotPDF komut satırı aracı tarafından export-svg komutu olarak da sunulur; bu, hiç Pascal kodu yazmadan bir sayfanın metin olarak karşılaştırılabilir (diffable) bir temsilini istediğiniz derleme (build) hatları ve regresyon betiklerinde işe yarar. SVG metin olduğundan, PDF sayfasını bitmap'e render etme yazısında anlatılan raster yoluna doğal bir eşlik eder: bitmap size sayfanın neye benzediğini söyler, SVG ise neyden oluştuğunu söyler

PDF metni SVG text öğelerine nasıl eşlenir?

HotPDF, metin matrisi zincirini önek çarpı CTM çarpı metin matrisi çarpı glif çevirmesi (glyph flip) olarak oluşturur; burada glif çevirmesi matrix(1,0,0,-1,0,0) ile yapılan bir sağdan çarpımdır. Bu sağ taraftaki çarpan, yalnızca glif şekilleri için sayfa düzeyindeki dikey çevirmeyi iptal etmek amacıyla vardır, çünkü aksi halde çevrilmiş bir yerel çerçevede çizilen SVG metni baş aşağı görünürdü. Düzeltmeyi özel durumlu koda değil matrise koymak, döndürülmüş, aynalanmış ve eğilmiş (sheared) metnin ekstra dallanma olmadan doğru çıkması anlamına gelir

Yatay konumlandırma, SVG text öğesinin çok değerli x söz dizimini kullanır; karakter başına bir koordinat olacak şekilde, her glif ilerlemesine (advance) o anda geçerli olan karakter aralığı Tc ve kelime aralığı Tw eklenerek biriktirilir. Yatay ölçekleme Tz, ayrı olarak yayınlanmak yerine metin matrisinin a ve c sütunlarına katlanır, böylece egzotik metin özniteliklerini yok sayan bir görüntüleyici bile her glifi PDF'nin koyduğu yere yerleştirir. Karmaşık betik metin şekillendirme yazısında ele alınan, karmaşık şekillendirme yoluyla üretilen metin de aynı yoldan geçer, çünkü şekillendirici (shaper), içerik akışı yorumlanana kadar kümeleri (clusters) konumlandırılmış glif haline zaten çözmüştür

Döndürme ve görüntüler: tersine çevrilmesi kolay iki çevirme

Sıfır olmayan bir /Rotate girdisine sahip bir sayfa, döndürülmüş tuval yüksekliğine göre bir çevirme ile y-yukarı görüntüleme uzayında ifade edilen bir döndürmeden oluşan bir ön dönüşüm gerektirir. Üç döndürme matrisi şunlardır: 90 derece için (0,-1,1,0,0,W), 180 derece için (-1,0,0,-1,W,H) ve 270 derece için (0,1,-1,0,H,0); W ve H döndürme öncesi sayfa boyutlarıdır. Bunları elle türetmek tam olarak üç noktada işaret hatasına davetiye çıkarır, bu yüzden dışa aktarıcı bunları diğer her dönüşümü işleyen aynı matris çarpım yordamı üzerinden oluşturur

Gömülü görüntülerin kendine ait bir çevirmeye ihtiyacı vardır, çünkü PDF görüntü uzayı ilk örnek satırını birim karenin üst kenarına yerleştirirken SVG image öğesi y-aşağı bir yerel çerçeve taşır. Bu nedenle yayınlanan dönüşüm, CTM'nin matrix(1,0,0,-1,0,1) ile sağdan çarpılmasıdır. Bunu yanlış yapmak, her yönden kusursuz bir sayfada dikey olarak aynalanmış fotoğraflar üretir; bu, bir incelemecinin anında fark edeceği ama otomatik bir testin çoğu zaman fark edemeyeceği türden bir kusurdur

Grafik durum aygıtı gerçekte neyi korur?

HotPDF, vuruş durumu (stroke-state) operatörleri w, J, j, M ve d'yi ayrı, isteğe bağlı bir aygıt arayüzü üzerinden dağıtır; böylece vuruş sadakati, mevcut içerik aygıtının vtable'ını değiştirmeden ve önceki sürümlere karşı derlenmiş kodun ikili (binary) uyumluluğunu bozmadan eklenmiştir. Somut olarak, dışa aktarılan SVG ham PDF tam sayıları yerine çevrilmiş anahtar sözcükler alır:

// PDF tam sayı numaralandırmaları SVG anahtar sözcük özniteliklerine dönüşür
//   line cap  0, 1, 2  ->  butt, round, square
//   line join 0, 1, 2  ->  miter, round, bevel
//
// PDF'de sıfır çizgi genişliği aygıt uzayında bir ince çizgi (hairline)
// anlamına gelir, bu yüzden dışa aktarıcı, vuruşu görünür ve CTM
// sonrası yaklaşık bir aygıt pikseli genişliğinde tutmak için
// vector-effect="non-scaling-stroke" yayınlar
//
// f* B* b* çift-tek (even-odd) kuralını seçer ve fill-rule="evenodd"
// yayınlar; f B b ise SVG'nin varsayılan sıfır-olmayan (nonzero)
// sarma kuralını korur

Bir Q'da durumu geri yüklemek; opaklık, çizgi genişliği, başlık (cap), birleşim (join), gehre sınırı, kesik çizgi dizisi ve kesik çizgi fazını birlikte kapsar. İç içe Form XObject'leri, sınırlarında aynı tam kümenin anlık görüntüsünü alır ve geri yükler, böylece bir damga (stamp) içinde tanımlanan kesikli bir kenarlık, deseninin sonrasındaki sayfa içeriğine sızmasına izin vermez. Başka nedenlerle zaten kırpma (clipping) ve CTM davranışını izliyorsanız, bu, EMF ve WMF vektör içe aktarma yazısında ters yönde çalışan haliyle görünen aynı durum modelidir

Yayına almadan önce bilinmesi gereken sınırlar

Dışa aktarıcı, kapsamı konusunda dürüsttür ve sınırları baştan öğrenmek, onları üretimde keşfetmekten daha ucuzdur. Renk, SVG aygıtına rg, RG, g ve G operatörleri üzerinden ulaşır. Bir renk uzayı artı scn ile kurulan dolgular — Separation, DeviceN ve ICCBased renklerinin boyandığı yöntem — aygıta çözümlenmiş bir RGB üçlüsü olarak ulaşmaz, bu yüzden düz (spot) renkleri bu şekilde kullanan sayfalar geometrilerini dışa aktarır ama o renkleri aktarmaz. Baskıya yönelik kaynaklar için önce rasterize edin veya düz renkleri düzleştirin (flatten); boyama modelinin kendisi Separation ve DeviceN düz renklerini render etme yazısında ele alınmıştır

İki küçük not hata ayıklama süresinden tasarruf sağlar. Onaltılık (hexadecimal) renk değişmezleri büyük harfle yayınlanır, bu yüzden #ff0000 doğrulayan bir test, tamamen doğru olan #FF0000'a karşı başarısız olur. Ayrıca SVG aygıtı, arayüzü üzerinden referans sayımlıdır (reference counted); bu, onu serbest bırakmanın nesne üzerinde Free çağırmak değil, arayüzün kapsam dışına çıkmasına izin vermek anlamına geldiği demektir — sayfa içeriğinin yanında kendi işaretlemenizi yayınlamak için aygıtı genişletirseniz önemli olan bir ayrım

SVG dışa aktarma, üretilen bir belgenin iki derleme (build) arasında gerçekten değişip değişmediğini bilmeniz gerektiğinde yapısal karşılaştırmayla doğal olarak eşleşir. Yüklü belgeler etrafındaki daha geniş araç seti — render etmeden düzenlemeye ve dışa aktarmaya kadar — HotPDF Delphi PDF bileşeni sayfasında belgelenmiştir