HotPDF terkompilasi dan berjalan di bawah Free Pascal 3.2.2 dengan Lazarus, dan ringkasan jujur atas porting itu adalah dua kalimat. Pembuatan dokumen, pemuatan, penyimpanan, kompresi, dekompresi, enkripsi, dan dekripsi semuanya bekerja pada backend khusus Pascal, sehingga aplikasi Lazarus dapat memproduksi dan mengonsumsi PDF sungguhan tanpa dependensi C. Codec gambar native opsional tidak, karena object Win64 yang sudah jadi memakai varian COFF yang tidak bisa dikonsumsi linker Free Pascal mana pun, sehingga pada toolchain itu entry point-nya terselesaikan ke stub yang gagal secara tertutup
Menempuh jalan dari "terkompilasi" ke "berfungsi" membutuhkan sekumpulan perbaikan tertentu, dan setiap satunya adalah jebakan yang akan menemukan codebase Delphi lain mana pun yang berpindah ke Free Pascal. Semuanya layak dicatat dalam urutan kapan mereka menyakitkan
Mengapa unit yang terkompilasi tidak membuktikan apa pun?
Karena unit Pascal bisa mereferensikan simbol yang tidak akan pernah melakukan apa pun yang berguna dan tetap memuaskan kompiler. Pada titik ketika seluruh 113 unit pustaka terbangun bersih di Free Pascal, handler kontainer arsip benar-benar bekerja, diverifikasi oleh smoke test yang membuka CBZ dan mengonversinya ke PDF. Perataan form XFA sama sekali tidak bekerja, karena perataan harus meng-inflate stream paket /XFA yang terkompresi dan entry point deflate masih berupa stub. Tidak ada apa pun di output build yang membedakan kedua kasus itu
Aturan yang lahir dari sana pendek. Sebelum menulis di release note bahwa sebuah fitur bekerja pada toolchain baru, tulis probe runtime yang menjalankan fitur itu end to end pada toolchain tersebut. Cakupan kompilasi adalah prasyarat, bukan bukti. Gambaran lebih luas tentang apa yang dicakup porting ini ada di catatan dukungan Win64 Free Pascal dan Lazarus
Raise di dalam stub cdecl tidak sampai ke caller
Yang ini layak mendapat bagian sendiri karena gejalanya begitu menyesatkan. Unit stub memaparkan entry point C sebagaimana pustaka statis melakukannya, sehingga sebuah stub tampak seperti ini
// Tampak masuk akal. Tidak.
function inflate(Strm: Pointer; Flush: Integer): PtrUInt; cdecl;
public name 'inflate';
begin
raise ENotSupportedException.Create('codec unavailable');
end;
Di Free Pascal untuk Win64 exception itu tidak merambat ke caller. Tidak ada handler try..except yang melihatnya, karena unwinding melintasi batas cdecl yang dideklarasikan dengan cara ini tidak membawa frame exception Pascal; proses berakhir dengan exit code 217. Dari sisi aplikasi tidak ada error, tidak ada pesan, tidak ada baris log, hanya program yang lenyap. Itu secara mutlak lebih buruk daripada jawaban yang salah, karena jawaban yang salah bisa ditangani
Perbaikan yang menggoda adalah membuat stub mengembalikan kode kegagalan, dan untuk inflate itu benar karena zlib punya return error yang terdefinisi baik. Secara umum itu keliru: stub untuk jpeg_read_header yang mengembalikan nol menyuruh caller melanjutkan dengan struktur yang tidak diinisialisasi siapa pun. Perbaikan yang tahan lama adalah melakukan gating di entry point Pascal alih-alih di dalam stub berbentuk C, memakai konvensi kegagalan apa pun yang sudah dimiliki API itu
function TryDecodeJPEG(const Data: TBytes; out Bitmap: TBitmap): Boolean;
begin
{$IFDEF FPC}
// Menolak sebelum stub tersentuh, dengan konvensi kegagalan milik
// API ini sendiri alih-alih exception melintasi cdecl
Bitmap := nil;
Result := False;
Exit;
{$ENDIF}
Result := DecodeJPEGNative(Data, Bitmap);
end;
paszlib bukan zlib, dan perbedaannya adalah dua kelas dokumen
Implementasi deflate Pascal yang tersedia di Free Pascal menangani dua framing: wrapper zlib dan deflate mentah. Ia tidak menangani framing gzip, yang dipilih zlib melalui nilai windowBits 16 sampai 31, dan tidak menangani mode deteksi otomatis yang dipilih nilai 32 sampai 47. HotPDF membutuhkan keduanya. Jalur impor SVG yang aman meminta 31, dan loader punya tangga fallback yang meminta 47 ketika framing sebuah stream ambigu. Melewatkan salah satunya dan satu keluarga dokumen penuh berhenti terbuka, dengan decode error yang menunjuk ke stream alih-alih ke framing yang hilang
Ada ketidakcocokan kedua yang lebih tajam. Record z_stream yang dideklarasikan paszlib tidak punya layout memori yang sama dengan versi C: field msg-nya short string alih-alih pointer, dan total_in serta total_out berukuran 64-bit di mana ABI C memakai machine word. Record caller karenanya tidak bisa diteruskan begitu saja. Susunan yang bekerja adalah menyimpan status paszlib di belakang pointer state yang sudah dicadangkan record publik, dan menyalin field publik masuk dan keluar di sekitar setiap panggilan. CRC gzip dan trailer panjang delapan byte diperhitungkan di lapisan shim yang sama, yang merupakan tempat alaminya karena lapisan itu sudah memiliki keputusan framing
Meneruskan dynamic array ke parameter var tanpa tipe
Inilah bug yang paling mungkin sedang duduk di kode Anda saat ini. Ketika Anda meneruskan dynamic array ke parameter var tanpa tipe, yang diterima callee adalah alamat variabel array, yaitu alamat sebuah pointer, bukan alamat payload-nya. Jadi pembacaan ke dalamnya menimpa variabel itu sendiri dan apa pun yang berada di sebelahnya
var
FBuffer: TBytes;
begin
SetLength(FBuffer, 65536);
// Keliru: menyerahkan alamat variabel FBuffer
FStream.Read(FBuffer, Length(FBuffer));
// Benar: menyerahkan alamat byte payload pertama
FStream.Read(FBuffer[0], Length(FBuffer));
end;
Di Delphi bentuk yang keliru sering tampak bekerja, karena yang dikorupsinya adalah slot stack tetangga yang tidak dibaca siapa pun setelahnya. Di Free Pascal baris yang sama segmentation fault pada pemakaian pertama. Yang membuatnya sulit ditangkap mata adalah static array tidak punya masalah semacam itu, karena variabel static array adalah payload-nya sendiri, sehingga kedua penulisan benar di berkas yang sama tergantung deklarasi beberapa ratus baris jauhnya
Kontainer ZIP tanpa System.Zip
Free Pascal tidak punya padanan unit zip RTL, dan alternatif yang tersedia punya permukaan API yang berbeda sekaligus tidak mendukung enkripsi legacy yang masih dipakai format kontainer lama, sehingga reader kecil di dalam pustaka ternyata lebih pendek daripada menyesuaikan diri kepadanya. Dua detail format memakan waktu dan mudah keliru
Yang pertama adalah byte pemeriksa header enkripsi. Byte keduabelas biasanya byte tinggi CRC, tetapi ketika bit 3 flag general-purpose di-set, yang berarti ukuran-ukuran berada di trailing data descriptor dan CRC belum diketahui, byte pemeriksa datang dari byte tinggi waktu modifikasi. Mengimplementasikan hanya bentuk CRC membuat setiap arsip yang ditulis dalam mode streaming menolak password yang benar. Yang kedua adalah extra field ZIP64: ketiga field 64-bit-nya muncul dalam urutan tetap tetapi hanya ditulis ketika field 32-bit yang bersangkutan jenuh, sehingga membacanya pada offset tetap bekerja pada arsip yang Anda uji dan gagal pada arsip berikutnya. Parse posisi mereka terhadap field 32-bit mana yang jenuh
Satu kemudahan yang layak diketahui: stream dekompresi Free Pascal menerima argumen constructor kedua yang melewati header zlib, yang persis dibutuhkan entri ZIP karena entri itu menyimpan deflate mentah. Jalur itu sama sekali tidak menyentuh shim zlib pustaka, sehingga tidak terpengaruh oleh backend C yang hilang
Transparansi glyph berwarna di bawah LCL
Membaca channel alpha dari glyph berwarna yang dirasterisasi adalah satu detail grafik tanpa terjemahan langsung. Kelas PNG LCL tidak punya accessor scanline yang memaparkan alpha, dan meng-assign PNG ke bitmap membuangnya, sehingga emoji berwarna datang sepenuhnya opak dan terkomposit dengan kotak hitam di belakangnya. Jalur yang bekerja adalah gambar interface: buat dari PNG, lalu baca piksel melalui accessor warna, dengan mengingat bahwa komponennya 16-bit dan perlu digeser turun delapan untuk menjadi byte. Permukaan itu juga memakai urutan baris top-down yang natural, sehingga inversi Height - 1 - Y yang dibutuhkan kode scanline VCL harus dihapus, bukan di-porting
Dua catatan build-system sebelum Anda melaporkan bug
Rebuild penuh kadang gagal dengan simbol tak terdefinisi yang namanya berakhiran suffix $crc dan nilai hex. Suffix itu dihitung dari tipe parameter, dan gagal cocok ketika satu build mengompilasi sebuah unit terhadap dua versi interface berbeda dalam lintasan yang sama. Menjalankan ulang build menghapusnya; signature-nya tidak salah
Kedua, Free Pascal 3.2.2 tidak punya anonymous methods, sehingga di mana pun pustaka memakai closure untuk menghubungkan pipeline paralel, build Free Pascal mengambil fallback serial yang deterministik sebagai gantinya. Output identik, throughput tidak; jika Anda bergantung pada rendering halaman paralel, itu alasan untuk tetap di Delphi untuk saat ini, dan desain pipeline-nya dijelaskan dalam artikel pipeline render paralel. Situasi codec gambar adalah tempat lain di mana pilihan toolchain mengubah kemampuan, bukan hanya kecepatan, sehingga deployment Lazarus sebaiknya merencanakan format gambarnya sesuai itu; matriks per toolchain saat ini ada di halaman produk HotPDF Delphi PDF component