Teknik Makale

SVG'yi PDF'e Yeniden Kullanılabilir Form XObject Olarak İçe Aktarma

HotPDF, bir SVG dosyasını yüklenmiş veya oluşturulmuş bir PDF'e, oluşturulan formun kaynak adını döndüren ImportSVGFormXObject aracılığıyla yeniden kullanılabilir bir Form XObject olarak aktarır. Bu formu yerleştirmek ayrı bir çağrı olan DrawSVGFormXObject ile yapılır; böylece tek bir içe aktarma, görselin tek bir baytını bile çoğaltmadan farklı boyutlarda birçok sayfaya basılabilir

Bu ayrım göründüğünden daha önemlidir. 300 DPI'de rasterize edilmiş ve 400 sayfaya yerleştirilmiş bir logo, bir bit eşlemin 400 kopyasıdır. Aynı logo Form XObject olarak kullanıldığında ise 400 kez referans verilen tek bir nesnedir; her yakınlaştırma seviyesinde keskin kalır ve derleme zamanında yeterli görünen DPI yerine cihaz çözünürlüğünde yazdırılır

Bir SVG içe aktarmanın tek bir PDF Form XObject oluşturduğunu ve DrawSVGFormXObject'in bunu farklı boyutlarda birçok sayfaya yerleştirdiğini, yüzlerce raster bit eşlem kopyasıyla karşılaştırmalı şekilde gösteren diyagram
Tek bir içe aktarma, görseli kendi kaynaklarına sahip bir Form XObject olarak yalnızca bir kez yazar ve her yerleştirme, hiçbir yinelenen bayt eklemeyen küçük bir çağrıdır

Her Seferinde SVG Çizmek Yerine Neden Form XObject?

Form XObject, PDF'nin yeniden kullanılabilir içerik için kendi mekanizmasıdır: kendi sınırlayıcı kutusuna ve kaynak sözlüğüne sahip, ada göre çağrılan, kendi kendine yeterli bir içerik akışı. SVG'yi bir Form XObject'e aktarmak, doğrudan çizimin sağlamadığı üç özellik kazandırır. Görsel, dosyada yalnızca bir kez bulunur. Her yerleştirme küçük bir matris ve bir çağrıdan ibarettir. Şekiller de vektör olarak kalır, böylece yüzde 800 yakınlaştırmadaki bir görüntüleyici piksel değil eğri çizer

İnceliği kaynaklarda yatar. Bir formun yazı tipleri ve grafik durumları, içe aktarma sırasında hangi sayfa geçerliyse onun kaynaklarında değil, formun kendi /Resources sözlüğünde bulunmalıdır. HotPDF, bir formun ihtiyaç duyduğu Base 14 yazı tiplerini ve ExtGState girdilerini doğrudan formun içine yazar; çünkü ilk barındırıcı sayfasından bir ad ödünç alan bir form, orada doğru şekilde işlenir ama daha sonra yerleştirildiği diğer her sayfada çözümlenemeyen bir kaynak adı üretir

İki Ters Çevirme: Biri Geometri, Biri Glif İçin

SVG'nin koordinat sistemi sol üst köşeden aşağıya doğru büyür; bir PDF Form XObject ise sol alttan yukarıya doğru büyüyen PDF kullanıcı alanını kullanır. Kök viewBox dönüşümü, geometrinin doğru şekilde yerleşmesi için tek bir Y ekseni ters çevirmesi uygular. Metin ise ikinci, yerel bir ters çevirmeye ihtiyaç duyar; çünkü zaten ters çevrilmiş bir çerçevede çizilen bir glif baş aşağı görünürdü

SVG ve PDF koordinatları arasındaki iki Y ekseni ters çevirmesini gösteren diyagram: kök viewBox dönüşümü geometriyi düzeltir ve metin glifleri dik kalmak için ikinci bir yerel ters çevirmeye ihtiyaç duyar
Kök viewBox dönüşümü, geometrinin PDF alanına doğru yerleşmesi için Y eksenini bir kez ters çevirir; metin ise ikinci, yerel bir ters çevirmeye ihtiyaç duyar, aksi halde her glif baş aşağı gelir

Bu, bir PDF sayfası SVG olarak dışa aktarıldığında tersine görünen aynı düzeltmedir; konu PDF sayfalarını SVG'ye aktarma başlığında ele alınmıştır. Bu hatayı yanlış yapmak, metin içeren görsellerde fark edilmesi kolayken metin içermeyen görsellerde neredeyse görünmezdir; bu yüzden sloganlı bir logo, tek başına bir logodan daha iyi bir duman testidir

uses
  HPDFDoc;

var
  Pdf: THotPDF;
  SvgBytes: TBytes;
  FormName: AnsiString;
  Stats: THPDFSVGImportStatistics;
  I: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'catalogue.pdf';
    Pdf.BeginDoc;

    SvgBytes := LoadFileBytes('brand-logo.svg');
    FormName := Pdf.ImportSVGFormXObject(SvgBytes, Stats);
    if FormName = '' then
      raise Exception.Create('SVG rejected - see the import statistics');

    // Tek içe aktarma, çok yerleştirme: görsel baytları yalnızca bir kez var
    for I := 1 to 40 do
    begin
      if I > 1 then
        Pdf.AddPage;
      Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 780, 120, 40);
    end;

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Köprüler Bu Yolculuğu Nasıl Atlatır?

Formun içinde hayatta kalamazlar, çünkü bir Form XObject içeriktir, PDF bağlantı hedefleri ise bir sayfaya ait olan ek açıklamalardır (annotation). Bu nedenle kaynak SVG'deki bir <a> öğesi, formun içerik akışına doğrudan kopyalanamaz

HotPDF bunu, ayrıştırma sırasında bağlantı dikdörtgenlerini form alanı meta verisi olarak tutup her yerleştirmede bunları sayfa düzeyinde bağlantı ek açıklamalarına dönüştürerek çözer. DrawSVGFormXObject, bu dikdörtgenleri sizin geçirdiğiniz hedef dikdörtgen üzerinden eşler; böylece farklı boyutlarda iki kez yerleştirilen bir form, doğru boyutlandırılmış iki tıklama alanı kümesi elde eder. Bağlantı dikdörtgenleri yalnızca kök boyut üzerinden eşlenir ve hiçbir zaman içerik akışına girmez, bu da çizilen görselin taşıdığı bağlantı sayısından bağımsız olarak aynı kalmasını sağlar. Örneğin yalnızca yazdırma amaçlı bir sayfada olduğu gibi, etkileşim olmadan görseli istediğinizde AddLinks = False geçirin

İçe Aktarıcının Reddettikleri ve Bunun Neden Önemli Olduğu

SVG, betik çalıştırma, dış referanslar ve varlık (entity) genişletmesi içeren bir XML lehçesidir; bu da rastgele bir SVG dosyasını, rastgele bir HTML dosyası kadar düşmanca bir girdi haline getirir. İçe aktarma profili, belgenin dışına uzanan her şeye karşı hataya kapalı (fail-closed) çalışır: dış kaynak referansları, betikler, olay işleyici öznitelikleri, DTD'ler ve yerel bir parça referansı olmayan her url()

Hataya kapalı olmak, sorunlu yapıyı sessizce atmak yerine içe aktarmanın başarısız olması demektir. Bu ayrım toplu işlem hatları için önemlidir, çünkü sessiz bir atlama, kimsenin bilmediği bir eksiği olan bir belge üretirken, bir reddetme karantinaya alıp inceleyebileceğiniz bir dosya üretir. Kullanıcılardan SVG yüklemesi kabul ediyorsanız istediğiniz davranış budur ve bu, güvenilmeyen görüntüler için codec süreç izolasyonu başlığında anlatılan süreç düzeyindeki izolasyonla birlikte çalışır

Yerel vektör özelliklerini kabul eden, betikleri, dış referansları ve DTD'leri sessizce atmak yerine içe aktarmayı başarısız kılarak reddeden hataya kapalı bir SVG içe aktarma profilini gösteren diyagram
İçe aktarma profili, betikleri, dış referansları ve DTD'leri doğrudan başarısız olarak reddeder; bu da sessizce eksik kalan bir belge yerine karantinaya alabileceğiniz bir dosya sağlar

Renkli yazı tipleri de işliyorsanız bilmeye değer bir iç sınır vardır. OpenType SVG renkli glifleri için kullanılan katı SVG alt kümesi, ayrı bir giriş noktasından geçer ve kendi özgün, daha dar başarısızlık davranışını korur. Genel görsel içe aktarma ayrı bir yoldur, bu yüzden içe aktarılan görsellere yay, saydamlık ve CSS desteği eklemek, bir yazı tipi glifinin içerebileceği şeyleri genişletmedi

Gradyanlar, Maskeler ve Rasterleştirme Yok Kuralı

Gradyanlar, rasterleştirilmiş bir görüntü olarak değil, özgün geometrinin kırpma yoluyla sınırlandırılmış, sınırlı vektör renk bantları olarak içe aktarılır. Maskeler de aynı yaklaşımı kullanır: maske dış hattı bir vektör kırpma yoluna dönüşür. Sonuç olarak gradyan ağırlıklı bir illüstrasyon çözünürlükten bağımsız ve küçük kalırken, rasterleştiren bir içe aktarıcı, kalitesi içe aktarma anında sabitlenen büyük bir görüntü nesnesi üretirdi

Buradaki ödünleşim, aşırı durumlarda sadakattir. Pürüzsüz bir gradyanın bantlı bir yaklaşıklığı, SVG gradyanıyla matematiksel olarak özdeş değildir ve onlarca üst üste binen yumuşak maskeye sahip bir görsel, bunun yerine yüksek çözünürlüklü bir görüntüye render edilirse özgününe daha yakın görünür. PDF'nin doğrudan desteklediği gölgelendirme modelleri ve bunlara ne zaman doğrudan başvurulacağı için bkz. eksenel ve radyal gölgelendirmelerin render edilmesi

// İçe aktarma istatistiklerini okumak gerçekte neyin geldiğini gösterir
Writeln(Format('nodes=%d css rules=%d gradients=%d masks=%d',
  [Stats.NodeCount, Stats.CSSRuleCount, Stats.GradientCount,
   Stats.MaskCount]));
Writeln(Format('text runs=%d links=%d form bytes=%d',
  [Stats.TextRunCount, Stats.LinkCount, Stats.OutputBytes]));

// Aynı formu iki farklı boyutta yerleştirmek iki doğru bağlantı kümesi üretir
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 700, 240, 80);
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 320, 700, 120, 40);

// Yalnızca yazdırma için yerleştirme: tıklama alanları olmadan görsel
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 60, 120, 40, False);

SVG İçe Aktarma Diğer Vektör Yollarının Yanında Nereye Oturur

Üç vektör içe aktarma yolu vardır ve bunlar birbirinin yerine geçmez. SVG içe aktarma, tasarım araçlarıyla üretilen görseller ve bir web hattından gelen her şey için uygundur. EMF ve WMF vektör içe aktarma başlığında ele alınan metafile içe aktarma, Windows uygulamaları ve bir aygıt bağlamına çizim yapan grafik bileşenleri tarafından üretilen içerik için uygundur. Sayfa API'si üzerinden doğrudan çizim ise, ara bir formatın hiç bulunmadığı, kendi kodunuzun hesapladığı görseller için uygundur

Yeniden kullanılabilirlik argümanı, özellikle tekrarlanan öğeler için SVG içe aktarmayı öne çıkarır. Antetli kağıtlar, logolar, filigranlar, sertifikasyon işaretleri ve dekoratif çizgiler, birçok sayfada aynı şekilde çizilir; her biri tek bir form nesnesi artı sayfa başına tek bir çağrıdır. Önemli olmadığına karar vermeden önce gerçek bir belgede dosya boyutu farkını ölçün: sayfa başına bir vektör logosu içeren 500 sayfalık bir katalogda, tek bir form ile 500 satır içi kopya arasındaki fark genellikle birkaç megabayttır

SVG içe aktarma, metafile içe aktarma ve doğrudan vektör çizimi, Delphi ve C++Builder için aynı içerik akışı yazıcısını paylaşır; eksiksiz özellik listesi HotPDF Delphi PDF bileşeni sayfasında bulunur