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
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 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
// 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