HotPDF mengekspor satu halaman dari dokumen PDF apa pun yang dimuat menjadi markup SVG mandiri dengan satu panggilan, BuildLoadedPageSVG, yang mengembalikan seluruh dokumen SVG sebagai string. Markup yang diekspor membawa geometri halaman, teks sebagai elemen text SVG sungguhan, gambar raster yang disematkan, dan status goresan yang telah ditetapkan operator PDF pada setiap operasi menggambar
Bagian terakhir itulah yang diam-diam membuat sebagian besar konverter buatan sendiri gagal. Mengubah halaman PDF menjadi SVG tampak seperti masalah koordinat, tetapi ternyata merupakan masalah status. PDF adalah mesin stack yang status grafisnya berubah seiring interpretasi content stream; SVG adalah pohon deklaratif yang setiap elemennya membawa atribut presentasi sendiri. Apa pun yang gagal diambil snapshot-nya oleh interpreter pada saat elemen diterbitkan akan hilang begitu saja dari output, dan kegagalan ini tidak terlihat: Anda mendapatkan SVG yang valid tetapi merender halaman yang sedikit salah
Mengapa halaman PDF tidak bisa langsung dikonversi ke SVG?
Tiga ketidaksesuaian membuat konversi ini tidak sederhana, dan ketiganya menghasilkan output yang tampak masuk akal sampai Anda membandingkannya berdampingan dengan aslinya. Yang pertama adalah sumbu y. User space PDF tumbuh ke atas dari sudut kiri bawah halaman; SVG tumbuh ke bawah dari sudut kiri atas. Satu kali pembalikan pada level halaman memperbaiki koordinat gambar tetapi kemudian merusak setiap glyph, karena membalik seluruh kanvas juga mencerminkan bentuk huruf
Ketidaksesuaian kedua adalah pewarisan. Dalam PDF, q dan Q mendorong dan mengeluarkan status grafis yang mencakup lebar garis, line cap, line join, miter limit, dash array, dash phase, dan alpha. Dalam SVG, elemen yang tidak menyebutkan suatu atribut mewarisinya dari grup leluhur, yang merupakan aturan scoping yang sama sekali berbeda. Exporter yang hanya melacak matriks transformasi saat ini dan melupakan status goresan akan membiarkan status yang dipulihkan setelah Q bocor ke elemen-elemen berikutnya
Ketiga, PDF menyatakan beberapa hal melalui konvensi, bukan melalui nilai. Line cap dan line join berupa integer, lebar garis nol berarti goresan rambut (hairline) dalam device-space, bukan garis yang tidak terlihat, dan varian bintang pada operator painting mengubah aturan winding, bukan warna. Masing-masing hal ini memerlukan penerjemahan, bukan sekadar penyalinan
Satu panggilan untuk kasus umum
Untuk pekerjaan biasa mengekspor halaman bagi penampil web, alat diffing, atau serah terima desain, permukaan API-nya hanya satu fungsi. BuildLoadedPageSVG menerima indeks halaman berbasis nol terhadap dokumen yang sedang dimuat dan mengembalikan dokumen SVG sebagai AnsiString:
var
Pdf: THotPDF;
I: Integer;
Svg: AnsiString;
Output: TFileStream;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('statements.pdf', '') <= 0 then
Exit; // LoadFromFile mengembalikan jumlah halaman
for I := 0 to Pdf.LoadedPageCount - 1 do
begin
Svg := Pdf.BuildLoadedPageSVG(I);
if Length(Svg) = 0 then
Continue;
Output := TFileStream.Create(Format('page-%d.svg', [I + 1]), fmCreate);
try
Output.WriteBuffer(Svg[1], Length(Svg));
finally
Output.Free;
end;
end;
finally
Pdf.Free;
end;
end;
Ekspor yang sama juga tersedia lewat command line tool HotPDF sebagai perintah export-svg, yang berguna dalam build pipeline dan skrip regresi ketika Anda menginginkan representasi halaman yang bisa di-diff sebagai teks tanpa menulis kode Pascal sama sekali. Karena SVG berupa teks, ia menjadi pendamping alami bagi jalur raster yang dijelaskan dalam merender halaman PDF ke bitmap: bitmap menunjukkan tampilan halaman, sementara SVG menunjukkan unsur pembentuknya
Bagaimana teks PDF dipetakan ke elemen teks SVG?
HotPDF menyusun rantai matriks teks sebagai prefix dikalikan CTM dikalikan matriks teks dikalikan pembalikan glyph, di mana pembalikan glyph adalah perkalian kanan dengan matrix(1,0,0,-1,0,0). Faktor sisi kanan ini semata-mata ada untuk membatalkan pembalikan vertikal level halaman pada bentuk glyph, karena teks SVG yang digambar dalam frame lokal yang terbalik jika tidak akan tampak terbalik. Menempatkan koreksi ini di dalam matriks, bukan dalam kode kasus khusus, berarti teks yang dirotasi, dicerminkan, dan di-shear semuanya tampil benar tanpa cabang kode tambahan
Penentuan posisi horizontal menggunakan sintaks multi-nilai x pada elemen text SVG, satu koordinat per karakter, diakumulasi dari setiap advance glyph ditambah character spacing Tc dan word spacing Tw yang berlaku saat itu. Penskalaan horizontal Tz dilipat ke dalam kolom a dan c pada matriks teks, bukan diterbitkan secara terpisah, sehingga penampil yang mengabaikan atribut teks eksotis tetap menempatkan setiap glyph di posisi yang ditetapkan PDF. Teks yang dihasilkan melalui shaping kompleks, dibahas dalam shaping teks skrip kompleks, melalui jalur yang sama, karena shaper telah menyelesaikan cluster menjadi glyph yang diposisikan pada saat content stream diinterpretasikan
Rotasi dan gambar: dua pembalikan yang mudah terbalik
Halaman dengan entri /Rotate bukan nol memerlukan pra-transformasi yang terdiri dari pembalikan terhadap tinggi kanvas yang dirotasi dan rotasi yang dinyatakan dalam ruang tampilan y-up. Ketiga matriks rotasinya adalah (0,-1,1,0,0,W) untuk 90 derajat, (-1,0,0,-1,W,H) untuk 180 derajat, dan (0,1,-1,0,H,0) untuk 270 derajat, dengan W dan H adalah dimensi halaman sebelum rotasi. Menurunkan matriks ini secara manual mengundang kesalahan tanda tepat di tiga tempat, sehingga exporter menyusunnya melalui rutin perkalian matriks yang sama yang menangani setiap transformasi lainnya
Gambar yang disematkan memerlukan pembalikannya sendiri, karena ruang gambar PDF menempatkan baris sampel pertama di tepi atas unit square, sementara elemen image SVG membawa frame lokal y-down. Transformasi yang diterbitkan karena itu adalah CTM dikalikan kanan dengan matrix(1,0,0,-1,0,1). Kesalahan di sini menghasilkan foto yang tercermin secara vertikal pada halaman yang seharusnya sempurna, jenis cacat yang langsung terlihat oleh reviewer manusia tetapi sering terlewat oleh pengujian otomatis
Apa sebenarnya yang dipertahankan oleh graphics state device?
HotPDF mengirimkan operator status goresan w, J, j, M, dan d melalui interface device opsional terpisah, sehingga fidelitas goresan ditambahkan tanpa mengubah vtable dari content device yang sudah ada dan tanpa merusak kompatibilitas biner untuk kode yang dibangun terhadap versi-versi sebelumnya. Secara konkret, SVG yang diekspor menerima kata kunci yang telah diterjemahkan, bukan integer PDF mentah:
// Enumerasi integer PDF menjadi atribut kata kunci SVG
// line cap 0, 1, 2 -> butt, round, square
// line join 0, 1, 2 -> miter, round, bevel
//
// Lebar garis nol berarti hairline device-space dalam PDF, sehingga
// exporter menerbitkan vector-effect="non-scaling-stroke" agar
// goresan tetap terlihat dan mendekati satu piksel device setelah CTM
//
// f* B* b* memilih aturan even-odd dan menerbitkan fill-rule="evenodd",
// sedangkan f B b mempertahankan default SVG yaitu nonzero winding
Pemulihan status pada Q mencakup opacity, lebar garis, cap, join, miter limit, dash array, dan dash phase sekaligus. Form XObject bersarang mengambil snapshot dan memulihkan kumpulan lengkap yang sama pada batasnya, sehingga border putus-putus yang didefinisikan di dalam stempel tidak bisa bocor polanya ke konten halaman berikutnya. Jika Anda sudah melacak perilaku clipping dan CTM untuk keperluan lain, ini adalah model status yang sama yang muncul dalam impor vektor EMF dan WMF, berjalan dalam arah sebaliknya
Batasan yang perlu diketahui sebelum Anda merilisnya
Exporter ini jujur soal cakupannya, dan mengetahui batasannya lebih awal lebih murah daripada menemukannya di produksi. Warna mencapai SVG device melalui operator rg, RG, g, dan G. Fill yang dibentuk melalui color space ditambah scn, yaitu cara warna Separation, DeviceN, dan ICCBased dilukis, tidak sampai ke device sebagai triple RGB yang sudah diresolusi, sehingga halaman yang menggunakan spot color dengan cara ini mengekspor geometrinya tetapi tidak warnanya. Untuk sumber yang berorientasi cetak, lakukan rasterisasi sebagai gantinya atau flatten spot color-nya terlebih dahulu; model pelukisan itu sendiri dibahas dalam merender spot color Separation dan DeviceN
Dua catatan kecil dapat menghemat waktu debugging. Literal warna heksadesimal diterbitkan dalam huruf besar, sehingga pengujian yang mengasersi #ff0000 akan gagal terhadap #FF0000 yang sebenarnya benar. Dan SVG device menggunakan reference counting melalui interface-nya, artinya melepaskannya adalah soal membiarkan interface keluar dari scope, bukan memanggil Free pada objeknya, perbedaan yang penting jika Anda memperluas device ini untuk menerbitkan markup Anda sendiri bersama konten halaman
Ekspor SVG berpasangan secara alami dengan perbandingan struktural ketika Anda perlu tahu apakah dokumen yang dihasilkan benar-benar berubah antara dua build. Toolkit yang lebih luas seputar dokumen yang dimuat, mulai dari rendering hingga editing hingga ekspor, didokumentasikan di halaman komponen PDF Delphi HotPDF