Teknik Makale

HotPDF ile EMF ve WMF Vektörlerini Delphi PDF'lerine Aktarma

Yerel Delphi ve C++Builder PDF bileşeni olan HotPDF, Windows EMF ve WMF meta dosyalarını, dosyayı bir bit eşleme düzleştirmek yerine her GDI kaydını doğrudan PDF operatörlerine yorumlayarak içe aktarır: gradyan dolgular PDF eksenel gölgelendirme desenleri haline gelir, tarama fırçaları PDF döşeme desenleri haline gelir ve merkezi bir yol durumu kapısı, bozuk kayıtların çıktıyı bozmasını engeller. Bir TChart'ın, bir GDI+ yüzeyinin veya sıradan bir TCanvas'ın gelişmiş bir meta dosya olarak dışa aktarabildiği her grafik bu yol için bir adaydır ve fark, birisi sayfayı yakınlaştırdığı veya yüksek çözünürlüklü bir yazıcıya gönderdiği anda ortaya çıkar

Çoğu Delphi geliştiricisinin varsayılan olarak başvurduğu alternatif, meta dosyayı sayfaya bırakmadan önce bir bit eşlemeye dönüştürmektir ve bedeli yalnızca daha sonra ortaya çıkar: ekranda keskin görünen bir çubuk grafik, PDF 600 DPI'de yazdırıldığı veya bir toplantı odası ekranına yansıtıldığı anda görünür şekilde bloklu hale gelir; dolgu stili taşınmadıysa tarama dolgulu bir CAD bölgesi tek düz gri bir dikdörtgene çöker. Meta dosyayı bir resim yerine bir program olarak okumak her iki sorunu da önler ve doğru şekilde uygulamak daha zor bir yoldur — bu yüzden bir rapor gönderilmeden önce aşağıdaki tuzakları bilmeye değer

Meta dosyayı bir bit eşlemeye düzleştirmek yerine neden yorumlamalı?

HotPDF, EMF ve WMF içe aktarımını vektör yolunda tutar çünkü bir Windows meta dosyası kaydedilmiş bir GDI çizim çağrıları dizisidir, bir resim değildir; bu çağrıları PDF yol, metin ve gölgelendirme operatörleri olarak yeniden oynatmak, sonucun sayfanın geri kalanı gibi ölçeklenmesini sağlayan şeydir. THPDFPage.ShowMetafile ve karşılığı ShowMetafileEx, bir uygulamanın çağırdığı giriş noktalarıdır ve her ikisi de meta dosyayı, her GDI kaydını gezen ve çeviren sınıf olan THPDFWmf'ye verir. Bu ayrım mutlak değildir ve HotPDF de öyleymiş gibi davranmaz: gerçekten raster veri olan bir meta dosya kaydı — örneğin bir StretchDIBits bit eşleme blit'i — bir fotoğrafı ifade edemeyecek yol operatörlerine zorlanmak yerine, sayfadaki diğer her resmin geçtiği aynı çağrı çifti olan AddImage ve ShowImage aracılığıyla gerçek bir PDF Image XObject olarak gömülür. Çizgiler, dolgular ve metin vektör kalır; kaynakta zaten piksel olan pikseller çıktıda da piksel kalır. En basit çağrının, yüklenmiş meta dosyadan başka bir şeye ihtiyacı yoktur:

var
  Pdf: THotPDF;
  Chart: TMetafile;
begin
  Pdf := THotPDF.Create(nil);
  Chart := TMetafile.Create;
  try
    Chart.LoadFromFile('quarterly-revenue.emf');  // exported from TChart or GDI+
    Pdf.FileName := 'quarterly-report.pdf';
    Pdf.BeginDoc;
    Pdf.CurrentPage.ShowMetafile(Chart);
    Pdf.EndDoc;
  finally
    Chart.Free;
    Pdf.Free;
  end;
end;

Yorumlayıcı GDI koordinatlarını PDF sayfa alanına nasıl dönüştürür?

HotPDF buna, GDI'nin ikinci bir uygulamasını yazmak yerine meta dosyanın kendi kayıt akışı üzerinde tek bir geçişle yanıt verir. THPDFWmf.Analyse, meta dosya başlığını Win32 GetEnhMetaFileHeader çağrısı üzerinden okur, dahili çizim durumunu sıfırlar ve bir meta dosya görüntüleyicisinin kullanacağı aynı numaralandırma API'si olan EnumEnhMetafile'ı çağırır; böylece her EMR_* kaydı orijinal olarak kaydedildiği sırayla THPDFWmf.ExecuteRecord'a ulaşır. GDI, koordinatları yukarıdan aşağıya, meta dosyanın kendi eşleme moduyla seçilen aygıt veya mantıksal birimlerle ifade eder; bir PDF sayfası ise HotPDF'in yollar ve dolgular için tuval çizim modelinde ele alınan koordinat sistemi olan aşağıdan yukarıya kullanıcı alanı noktalarındadır. Her kayıt işleyicisi bu uyuşmazlığı, anizotropik ve izotropik eşleme modları için GDI'nin kendi pencere-görünüm alanı formülünü yeniden oynatmak üzere ProjectX ve ProjectY'yi çağıran ScaleX ve ScaleY aracılığıyla çözer; böylece beş mantıksal birim genişliğinde kaydedilen bir şekil, kaynak uygulamanın hangi pencere ve görünüm alanı boyutlarını ayarladığından bağımsız olarak PDF noktalarında doğru genişliğe iner

Bir GDI gradyan dolgusu nasıl bir PDF gölgelendirme desenine dönüşür?

Bir EMR_GRADIENTFILL kaydı, GDI onu iki dikdörtgen modundan birinde kaydettiğinde gerçek bir PDF Tip 2 eksenel gölgelendirme deseni (ISO 32000-1 §8.7.4.5) haline gelir. THPDFWmf.VEMRGradientFill, kaydın kendi düzenini doğrudan ham byte tamponundan okur ve MS-EMF §2.3.1.6 yapısını izler: 16 bit RGBA köşelerden oluşan bir köşe dizisi, ardından her biri bu köşelerden ikisine referans veren bir dikdörtgen listesi. GRADIENT_FILL_RECT_H için renkler dikdörtgenin yatay orta çizgisi boyunca soldan sağa süpürür; GRADIENT_FILL_RECT_V için dikey orta çizgi boyunca yukarıdan aşağıya süpürür. Her iki durumda da iki köşe rengi ve izdüşürülmüş dikdörtgen koordinatları, bir desen adı döndüren THotPDF.RegisterAxialGradient'e doğrudan gider ve sayfa dikdörtgeni çizip düz bir SetRGBFillColor çağrısı yerine bu desen (SetFillPattern) üzerinden doldurur; böylece bir elektronik tablo tarzı bantlı başlık veya bir grafiğin gradyanlı çizim alanı, tek bir ortalama renge çökmek yerine karışımını korur

Gouraud üçgen modu dürüstçe kabul edilen bir boşluktur. Kaydın ulMode alanı GRADIENT_FILL_TRIANGLE bildirdiğinde, VEMRGradientFill bunu tanır, üçgen modunun henüz uygulanmadığını kaydeder ve iki renkli bir yaklaşıklık tahmin etmek yerine dikdörtgeni atlar. Rastgele bir üçgen ağı boyunca köşe başına, piksel başına enterpolasyon, iki durak noktalı eksenel veya radyal bir gölgelendirmeye indirgenmez; bunu düzgün ifade etmek bir PDF Tip 4 veya Tip 5 ağ gölgelendirmesi yaymak anlamına gelir — HotPDF'in sayfa oluşturucusunun da bir PDF'i geri okurken boyanmamış bıraktığı aynı gölgelendirme ailesi. Birbiriyle ilgisiz iki kod yolu aynı sınıra iniyor: ağ gölgelendirmeleri hem yazma tarafında hem okuma tarafında boşluktur ve pürüzsüz bir radyal parıltı için Gouraud üçgenleri kullanan bir kaynak diyagram, oluşturulmuş bir yaklaşıklığa değil, son katı fırça neyse ona geri döner

Tarama fırçaları düzleştirilmiş gri değil döşeme desenlerine dönüşür

Bir GDI tarama fırçası PDF'de dokusunu korur, çünkü THPDFWmf.SetBrushColor, düz bir dolguya geri dönmeden önce CurrentBrush.lbStyleBS_HATCHED için kontrol eder ve bu durumu bunun yerine SetHatchBrushPattern'e yönlendirir. Bu yöntem, GDI tarama stiline göre seçilen 8x8 birimlik bir PDF içerik akışı yazar — m, l ve S çizgi çizme operatörleri: HS_HORIZONTAL ve HS_VERTICAL için tek bir yatay veya dikey çizgi, HS_FDIAGONAL ve HS_BDIAGONAL için üç paralel çapraz, HS_CROSS ve HS_DIAGCROSS için yatay-artı-dikey veya her iki çapraz kombinasyonları. THotPDF.RegisterTilingPattern bu içerik akışını 8 birimlik XStep ve YStep ile renkli bir döşeme deseni (PaintType 1, ISO 32000-1 §8.7.3.1) olarak kaydeder ve sayfa, bir eksenel gölgelendirmenin yaptığı gibi SetFillPattern üzerinden doldurur. Malzemeleri ayırt etmek için tarama dolgularına dayanan bir CAD kat planı veya mühendislik çizimi, tüm bölgeleri aynı griye kaybetmek yerine bu görsel dili PDF'de korur

Her fırça bu muameleyi hak etmez ve bu boşluk bir CAD içe aktarımı gönderilmeden önce bilinmeye değer. EMR_CREATEDIBPATTERNBRUSHPT, GDI'nin altı stok tarama stilinden biri yerine özel bir bit eşleme görüntü deseni fırçası için olan kayıt, yalnızca tanıtıcısını kaydeder ki sonraki SELECTOBJECT ve DELETEOBJECT kayıtları tutarlı kalsın; HotPDF henüz keyfi döşeme görüntüleri için bir PDF Pattern kaynak boru hattı sunmuyor, bu yüzden bu fırçayı seçmek kaynak dokusu yerine düz renkli bir yedeğe düşer. Bir dolgu, orijinalinin açıkça tekrarlayan bir görüntü dokusu kullandığı yerde düz görünüyorsa, kaynak fırça neredeyse kesinlikle standart bir tarama yerine özel bir DIB desenidir ve önce elle kontrol etmeye değer tek durum budur. Böyle bir çizim için içe aktarımı yapılandırmak yine de aynı seçenekler nesnesinden geçer:

var
  Pdf: THotPDF;
  Drawing: TMetafile;
  Options: THPDFEmfOptions;
begin
  Pdf := THotPDF.Create(nil);
  Drawing := TMetafile.Create;
  Options := THPDFEmfOptions.Create;
  try
    Drawing.LoadFromFile('floor-plan.emf');
    Options.Assign(Pdf.EmfOptions);   // start from the document-wide defaults
    Options.Redraw := False;          // interpret the original EMF bytes, no GDI re-record pass
    Options.ShowNullBrush := True;    // keep explicitly unfilled CAD regions visible
    Options.UseFrame := True;         // clip output to the frame the EMF header declares
    Pdf.FileName := 'floor-plan.pdf';
    Pdf.BeginDoc;
    Pdf.CurrentPage.ShowMetafileEx(Drawing, Options);
    Pdf.EndDoc;
  finally
    Options.Free;
    Drawing.Free;
    Pdf.Free;
  end;
end;

Bozuk bir meta dosyanın sayfayı bozmasını ne engelliyor?

HotPDF'in yanıtı, yaklaşık seksen kayıt işleyicisinin her birinde tekrarlanan savunmacı bir kontrol yerine, ExecuteRecord'un tepesinde tek bir kapıdır. EMR_BEGINPATH ile açılan ve EMR_ENDPATH veya EMR_ABORTPATH ile kapatılan bir GDI yol parantezi, FPathContinue alanı tarafından desteklenen özel bir PathContinue özelliği tarafından izlenir. Bu parantez açıkken, ExecuteRecord yalnızca yol oluşturma kayıtlarının geçmesine izin verir — taşıma, çizgi, çoklu çizgi, çokgen, çoklu bezier ve çoklu çizim varyantları, artı CLOSEFIGURE ve SETWORLDTRANSFORM, SAVEDC ve RESTOREDC gibi küçük bir dönüşüm ve DC durumu kayıtları kümesi. Parantez açıkken ExecuteRecord'a ulaşan diğer her kayıt türü — örneğin başıboş bir EXTTEXTOUT veya bir bit eşleme blit'i — geldiği anda tek bir Exit ile merkezi olarak bırakılır

Bu kapı, elle yazılmış, araçla üretilmiş veya basitçe bozulmuş bir meta dosyadaki bir yol parantezinin, iyi biçimlendirilmiş bir dosyanın açma ve kapama kayıtları arasına koyacağı şeyi yalnızca içermesinin garanti olmamasından dolayı var. Bir kapı olmadan EMR_BEGINPATH ile EMR_ENDPATH arasına inen bir metin çıkışı kaydı, ya oluşturulmakta olan yol geometrisini kirletir ya da saf yol oluşturma olması gereken bir dizinin ortasında bir PDF metin gösterme operatörü yayar ve her iki hata modu de normal bir test paketinin kapsadığı bir şeyde değil, üçüncü taraf bir araçtan gelen tek bir bozuk girdide ortaya çıkan türden. Kontrolü ExecuteRecord'da merkezileştirmek, tek tek VEMR* işleyicilerinin her birinin yanlış zamanda çağrılmaya karşı kendini savunması gerekmediği anlamına gelir; kapı bunu gönderimden önce bir kez karar verir, sonrasında seksen kez değil

Bir vektör grafiğini metin ve görüntülerin yanına aynı sayfaya yerleştirmek

Bir rapor sayfası nadiren yalnızca bir grafik içerir ve ShowMetafile, tam olarak diğer herhangi bir çizim çağrısı gibi HotPDF'in diğer sayfa operatörleriyle birleşir. TextOut ile çizilen bir başlık, EMF olarak içe aktarılan tarama dolgulu bir çubuk grafik ve ShowImage ile yerleştirilen bir logo, her biri yerel doğruluğunu koruyarak aynı sayfada aynı içerik akışına yerleşebilir; bu bileşim deseni HotPDF'in bir raporda metin, yazı tipi ve görüntü düzenleme rehberinde ele alınmıştır:

Pdf.CurrentPage.SetFont('Arial', [fsBold], 14);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Q2 Regional Sales');
Pdf.CurrentPage.ShowMetafile(RegionChart);   // hatch-filled bars, still vector
Pdf.CurrentPage.ShowImage(LogoIndex, 450, 760, 90, 30, 0);

Burada anlatılan EMF ve WMF yorumlayıcısı, gradyan dolgular için kaydettiği eksenel gölgelendirme desenleri ve tarama fırçaları için döşeme deseni eşlemesi, hepsi Delphi ve C++Builder için standart HotPDF Bileşeni'nin bir parçası olarak gönderilir; bunların hiçbiri için harici DLL bağımlılığı olmayan yerel bir VCL kütüphanesidir