Artikel Teknis

Impor Vektor EMF di Free Pascal dengan PDFlibPas

PDFlibPas mengonversi enhanced metafile menjadi konten halaman PDF sungguhan rekaman demi rekaman, alih-alih merasterisasinya, dan itulah yang membuat grafik atau gambar CAD yang diimpor tetap tajam pada zoom berapa pun. Konverter itu berukuran sekitar 6500 baris dan ditulis terhadap VCL, sehingga ketika pustaka mendapatkan target Free Pascal ia diklasifikasikan sebagai tidak portabel dan diganti stub. Klasifikasi itu keliru, dan cara ia keliru adalah pelajaran berguna tentang cara mengaudit sebuah dependensi sebelum memutuskan menulis ulang di sekitarnya

Permukaan VCL yang benar-benar dipakai dari 6500 baris itu ternyata kecil: satu kelas bitmap yang digunakan untuk pixel format, penyimpanan stream, handle, canvas, dan scanlines-nya; satu kelas metafile yang digunakan untuk width, height, dan handle-nya; serta tipe colour dengan dua konstanta. Semuanya sudah disediakan oleh unit grafik milik pustaka sendiri, yang justru hadir agar build non-VCL punya padanannya. Konverter tidak pernah tersendat pada VCL sama sekali. Ia tersendat pada unit Windows milik Free Pascal

Pisahkan pada sumbu yang benar-benar menjadi tumpuan kode

Jadi perubahannya bukan reimplementasi. Hanya satu conditional: dari "kompilasi stub saat dibangun tanpa VCL" menjadi "kompilasi stub saat tidak membangun untuk Windows". Itulah sumbu yang benar, dan menyebut alasannya membuat perbedaannya jelas. Enhanced metafile adalah kontainer Windows. Konverter adalah parser untuk rekaman GDI Windows dari ujung ke ujung. Apakah aplikasi host memakai VCL, widget set lain, atau tidak memakai widget set sama sekali tidak ada hubungannya dengan apakah rekaman-rekaman itu bisa ditafsirkan; apakah targetnya Windows sangat menentukan

Konsekuensi memilih sumbu yang benar mengalir begitu saja. Build C++Builder, yang me-undefine simbol platform Windows di pustaka ini, mempertahankan stub yang melempar exception dan berperilaku persis seperti sebelumnya. macOS mempertahankan stub, dengan benar, karena tidak ada rekaman GDI untuk di-parse di sana. Build Delphi VCL tidak tersentuh. Dan build Windows dengan widget set non-VCL mendapatkan impor EMF vektor sebagai efek samping, tanpa ada yang perlu mengimplementasikannya. Conditional yang selaras dengan dependensi sesungguhnya mengubah pekerjaan platform menjadi perubahan satu baris; conditional yang selaras dengan yang keliru mengubahnya menjadi penulisan ulang yang tidak pernah masuk jadwal

Conditional impor EMF diubah sumbunya dari keanggotaan VCL ke platform Windows, mempertahankan stub di tempat lain dan memberi build Windows non-VCL impor vektor
Mengubah sumbu kondisi stub ke platform Windows mempertahankan perilaku semua build yang ada dan memberi target Windows non-VCL impor vektor EMF secara gratis

Celah Free Pascal ada di deklarasi, bukan logika

Yang benar-benar hilang adalah deklarasi Win32 yang disediakan unit Windows milik Delphi tetapi tidak oleh milik Free Pascal. Mengumpulkannya ke dalam satu unit kompatibilitas alih-alih menebar conditional di seluruh konverter menjaga parser tetap terbaca. Daftarnya bersifat informatif karena menunjukkan betapa tidak meratanya cakupan header antara dua RTL tersebut: 113 konstanta tipe rekaman metafile, dua flag extended text-output, tiga konstanta mode gradient fill, satu tipe pointer tabel handle, alias untuk rekaman gradient vertex dan primitive, serta tiga tipe rekaman yang sama sekali tidak dideklarasikan Free Pascal, yang mencakup alpha blending, transparent blitting, dan mode colour-management

Tidak ada satu pun yang menarik secara individual. Semuanya harus benar sebelum parser terkompilasi, dan unit kompatibilitas adalah rumah alaminya karena bisa di-diff terhadap dokumentasi header sebagai satu kesatuan

Deklarasi Win32 yang tidak ada di unit Windows Free Pascal, dikumpulkan menjadi satu unit kompatibilitas untuk konverter vektor EMF ke PDF
Konstanta rekaman, flag, alias, dan tiga tipe rekaman yang absen semuanya berada di satu unit kompatibilitas yang bisa di-diff terhadap dokumentasi header

Deklarasi yang diam-diam menggambar gambar yang keliru

Dua dari deklarasi tersebut bukan sekadar hilang, keduanya hadir dan keliru untuk keperluan ini, dan inilah bagian yang layak diingat bahkan jika Anda tidak pernah menyentuh metafile

Free Pascal mendeklarasikan rekaman pembuatan brush dengan struktur brush run-time tertanam di dalamnya, dan rekaman extended-pen dengan struktur pen run-time tertanam di dalamnya. Kedua struktur run-time itu mendeklarasikan member hatch-nya sebagai integer berukuran pointer, karena dalam panggilan GDI langsung member tersebut bisa membawa handle. Metafile, bagaimanapun, selalu menyimpan bentuk 32-bit, karena layout rekaman adalah bagian dari format berkas terserialisasi dan tidak berubah dengan bitness proses

Pada build 32-bit keduanya sepakat dan tidak terjadi apa-apa. Di Win64 member berukuran pointer itu delapan byte di mana berkasnya empat, sehingga setiap field setelah member hatch dibaca dari offset yang keliru. Tidak ada exception, tidak ada parse error, dan tidak ada peringatan. Metafile hanya dirender dengan keliru: warna dari byte yang salah, lebar pen dari byte yang salah, dan gambar yang tampak seperti bug rendering alih-alih bug layout struct. Delphi mengirim varian eksplisit 32-bit dari kedua struktur justru karena alasan ini, dan unit kompatibilitas mendeklarasikannya kembali dengan cara yang sama

Layout byte rekaman brush EMF yang memperlihatkan field hatch berukuran pointer menggeser field-field berikutnya empat byte di Win64 dibanding layout 32-bit tetap
Rekaman terserialisasi selalu menyimpan hatch 4-byte, sehingga struktur run-time berukuran pointer diam-diam salah membaca setiap field berikutnya di Win64
// Keliru di Win64: Hatch berukuran pointer, berkas menyimpan 32 bit,
// dan setiap field berikutnya bergeser empat byte tanpa error
type
  TLogBrushRuntime = record
    lbStyle: UINT;
    lbColor: COLORREF;
    lbHatch: ULONG_PTR;      // 8 byte dalam proses 64-bit
  end;

// Benar: layout terserialisasi, lebar tetap apa pun bitness-nya
type
  TLogBrush32 = record
    lbStyle: UINT;
    lbColor: COLORREF;
    lbHatch: DWORD;          // selalu 4 byte, seperti tersimpan di metafile
  end;

Aturan umumnya: setiap struktur yang muncul baik sebagai argumen API run-time maupun sebagai layout field terserialisasi membutuhkan dua deklarasi, dan yang terserialisasi harus memakai tipe berlebar tetap di seluruhnya. Member berukuran pointer dalam format berkas selalu merupakan bug yang menunggu build 64-bit

Perbedaan signature ditempatkan di wrapper, bukan di setiap call site

Perbedaan yang tersisa adalah ketidakcocokan signature biasa, dan cara menyerapnya adalah wrapper penerus alih-alih conditional di masing-masing call site. Fungsi penggabung transform mengambil pointer di Free Pascal di mana Delphi mengambil parameter referensi, sehingga wrapper menerima referensi dan meneruskan alamat. Wrapper juga menyalin kedua argumen sumber ke lokal lebih dulu, karena konverter punya call site tempat matriks tujuan sekaligus menjadi salah satu sumber, dan meneruskan alamat yang sama dua kali ke fungsi yang menulis sambil membaca menghasilkan transform yang keliru secara halus, yang hanya muncul pada konten yang dirotasi

function CombineTransformCompat(var Dest: TXForm;
  const A, B: TXForm): BOOL;
var
  SrcA, SrcB: TXForm;
begin
  // Salin dulu: caller secara sah meneruskan Dest sebagai A atau B
  SrcA := A;
  SrcB := B;
{$IFDEF FPC}
  Result := Windows.CombineTransform(@Dest, @SrcA, @SrcB);
{$ELSE}
  Result := Windows.CombineTransform(Dest, SrcA, SrcB);
{$ENDIF}
end;

Tipe rectangle dan point adalah kasus lainnya. Free Pascal memperlakukan rekaman rectangle dan point metafile sebagai tipe yang berbeda dari tipe grafik umum, sehingga delapan lokasi assignment membutuhkan cast eksplisit antara rekaman dengan layout yang identik. Kedua kompiler menerima bentuk cast, sehingga lokasi-lokasi itu sama sekali tidak membawa conditional, yang layak dibayar dengan sedikit ketidakrapian

Apa yang berubah untuk deployment Free Pascal

Impor EMF vektor bekerja di Windows di bawah Free Pascal, menghasilkan konten halaman yang sama dengan build Delphi: path sebagai path, gradient sebagai konten pattern, teks sebagai teks. Di luar Windows jalur raster tetap menjadi jawabannya, dan itu keterbatasan formatnya, bukan port-nya. Status koordinat dan clipping yang diberi makan oleh konverter dijelaskan dalam artikel pelacak CTM dan clipping content stream, dan primitive vektor yang ia hasilkan dibahas dalam grafik vektor, shader, dan gradient

Jika Anda mengaudit codebase sendiri untuk peluang yang sama, latihan yang berguna adalah yang memulai semua ini: daftarkan member yang benar-benar Anda pakai dari framework yang Anda kira menjadi dependensi Anda. Jawabannya sering kali jauh lebih pendek daripada yang disarankan daftar impor, dan kendala sesungguhnya biasanya ada di tempat yang sama sekali berbeda. Jalur impor berbasis device context secara umum dijelaskan dalam artikel print preview dan device context, dan cakupan platform serta toolchain tercantum di halaman produk losLab PDF Developer Library