Di HotPDF Component, THotPDF.Resolution mendefinisikan unit gambar: setiap koordinat X dan Y, setiap margin, ukuran yang diberikan ke SetFont, dan hasil TextWidth serta GetWideTextWidth diukur dalam 1/Resolution inci. THPDFPage.Width dan Height tidak mengikutinya dan tetap dalam poin, jadi batas layout harus diambil dari UserWidth dan UserHeight yang read-only. Alasan lazim menyentuh Resolution adalah porting: report engine yang sudah berpikir dalam 1/96 atau 1/144 inci lebih mudah dipindahkan ketika sisi PDF berbicara dalam satuan yang sama daripada ketika setiap call site diberi faktor konversi. Itu bekerja dengan baik, selama Anda tahu angka mana yang pindah ke satuan baru dan mana yang tertinggal
Apa yang sebenarnya diubah THotPDF.Resolution?
THotPDF.Resolution hanya mengubah cara HotPDF membaca angka yang Anda berikan; PDF yang ditulisnya sama. Setter-nya dua baris: SetResolution menyimpan nilai dan menyetel DocScale := Value / 72. Sejak saat itu, XProjection dan YProjection membagi setiap koordinat dengan DocScale pada jalur masuk ke content stream, dan SetFont membagi ukuran dengan cara yang sama sebelum mencatatnya. User space PDF defaultnya 1/72 inci (ISO 32000-1 §8.3.2.3), jadi pada Resolution default 72 proyeksinya identitas dan pada 144 satu unit gambar setengah poin. Tak ada entri /UserUnit yang ditulis. Atribut halaman itu, ditambahkan di PDF 1.6, adalah hal terpisah yang HotPDF ekspos sebagai THPDFPage.SetUserUnit. Satu detail yang menjebak orang yang datang dari tutorial TextOut: koordinat halaman berjalan dari sudut kiri atas dengan Y bertambah ke bawah, karena YProjection menghitung atas MediaBox dikurangi Y yang diskalakan, dan itu tetap berlaku di setiap Resolution
var
Pdf: THotPDF;
Page: THPDFPage;
Margin: Single;
Title: WideString;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice.pdf';
Pdf.Resolution := 144; // 1 unit gambar = 1/144 inci
Pdf.BeginDoc;
Page := Pdf.CurrentPage; // A4: Width = 595, UserWidth = 1190
Margin := 144; // satu inci dalam unit gambar
Page.SetFont('Arial', [fsBold], 28); // 28/144 inci, font 14 pt
Title := 'INVOICE 2026-0417';
// Rata kanan terhadap tepi halaman yang diukur dalam satuan yang sama
Page.TextOut(Page.UserWidth - Margin - Page.GetWideTextWidth(Title),
Margin, 0, Title);
Page.SetLineWidth(2); // garis 1 pt
Page.MoveTo(Margin, Margin + 48);
Page.LineTo(Page.UserWidth - Margin, Margin + 48);
Page.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Kenapa Page.Width tak sepaham dengan koordinat saya di Resolution 144?
THPDFPage.Width dan Height melaporkan halaman dalam poin apa pun Resolution dokumennya, sementara koordinat Anda dalam 1/Resolution inci, jadi di 144 halaman tampak selebar setengah dari ukuran sebenarnya. Halaman A4 terbaca Width = 595 dan Height = 842 di Resolution 72 dan tetap terbaca 595 dan 842 di 144, padahal tepi kanannya sebenarnya di X = 1190. UserWidth dan UserHeight, ditambahkan di v2.766.0, mengembalikan Width * DocScale, yaitu ukuran halaman dalam satuan yang Anda pakai menggambar. Sebelum keduanya ada, library mencampur keduanya secara internal, dan gejalanya di Resolution 144 dramatis: paragraf wrap setelah setiap karakter, THPDFTable.Render mendorong setiap baris ke halaman baru, dan HTML importer maupun XFA flattener sama-sama menggambar kontennya setengah ukuran, form hasil flattening berdesakan di sudut kiri atas. Layout paragraf, rendering tabel, import HTML, pemusatan EMF, clip halaman WMF, dan diagnostik layout kini semuanya membaca ukuran user-unit. Kode layout Anda sendiri harus begitu juga: apa pun yang membandingkan dengan koordinat gambar (margin kanan, tes pindah halaman, perhitungan pemusatan) sebaiknya di UserWidth dan UserHeight, tidak pernah di Width dan Height
Jebakan satu: mengisi Width atau Height memindahkan halaman ke poin
Menyetel Page.Width atau Page.Height diam-diam mengubah halaman menjadi UserDefined, dan halaman UserDefined mengabaikan DocScale sepenuhnya, jadi semua yang Anda gambar di atasnya setelahnya dalam poin, bukan 1/Resolution inci. Setter-nya tua dan menerima poin memang by design, itulah kenapa maknanya dibiarkan. Proyeksi untuk halaman UserDefined polos saja, X + MinX, dan SetFont menyimpan ukuran tanpa diubah. Di Resolution 144 hasilnya halaman yang kontennya mendadak keluar dua kali lebih besar dari halaman sebelumnya. Library sendiri pernah membuat kesalahan persis ini: halaman lanjutan paragraf dulu menyalin ukuran halaman sebelumnya lewat Width, dan setiap halaman overflow pindah ke poin. Halaman-halaman itu kini menyalin Size, Orientation, dan Resolution halaman sebagai gantinya, dan hanya jatuh ke Width dan Height ketika halaman aslinya memang sudah UserDefined
Dua jalan keluar, tergantung kebutuhan Anda. Kalau lembar standar sudah cukup, setel Page.Size dan Page.Orientation dan terus menggambar dalam satuan Resolution Anda. Kalau Anda benar-benar butuh ukuran halaman kustom, terimalah bahwa ia halaman poin dan gambar dalam poin; UserWidth sama dengan Width di sana, jadi kode layout yang selalu membaca UserWidth tetap bekerja di kedua jenis halaman. Unit test menguncinya: di Resolution 144 halaman A4 melaporkan UserWidth 1190, tapi setelah Width := 500 dan Height := 400 ia melaporkan 500 dan 400. Halaman termuat berperilaku sama, karena halaman yang dibangun ulang dari PDF yang sudah ada hanya tahu MediaBox-nya dalam poin dan menggambar dalam poin. Halaman yang dibuat dokumen ini mempertahankan satuannya sendiri ketika Anda pindah lalu kembali lewat CurrentPageNumber, yang sudah berlaku sejak v2.766.26
Jebakan dua: kenapa ukuran font keluar setengah ukuran?
Ukuran font yang awalnya poin keluar setengah ukuran di Resolution 144 karena SetFont memperlakukan argumen ukurannya sebagai unit gambar dan mengonversinya ke poin sebelum menyimpannya. Secara internal, SetFont menyimpan ASize / DocScale * DPI di objek font saat ini, jadi nilai tersimpannya selalu poin. Library tersandung ini dua kali: font fallback di WideTextOutBoxEx dan halaman lanjutan paragraf sama-sama menyerahkan nilai poin tersimpan itu kembali ke SetFont, yang menskalakannya untuk kedua kalinya dan memangkas teks jadi separuh. Kode Anda tak bisa membaca ukuran tersimpannya, tapi bug yang sama muncul setiap kali nilai poin dari tempat lain sampai ke SetFont: sebuah TFont.Size dari form VCL, ukuran di definisi report, panjang CSS pt. Konversikan lebih dulu, dan masukkan Resolution halaman sendiri beserta kasus UserDefined ke dalam faktornya, seperti yang dilakukan playback metafile saat memutar ulang Canvas halaman (lihat cara HotPDF mengimpor grafik vektor EMF dan WMF untuk jalur itu):
// Unit gambar per poin di halaman saat ini. Mencerminkan proyeksi
// yang dipakai HotPDF: 1 di halaman yang diukur lewat Width/Height, selain itu
// (Resolution dokumen / 72) * (Resolution halaman / 72)
function UnitsPerPoint(Pdf: THotPDF): Single;
begin
if Pdf.CurrentPage.Size = UserDefined then
Result := 1
else
Result := (Pdf.Resolution / 72) * (Pdf.CurrentPage.Resolution / 72);
end;
procedure SetFontFromVcl(Pdf: THotPDF; Font: TFont);
begin
// TFont.Size dalam poin; SetFont mengharapkan unit gambar
Pdf.CurrentPage.SetFont(AnsiString(Font.Name), Font.Style,
Font.Size * UnitsPerPoint(Pdf));
end;
Library menerapkan aturan yang sama pada konstanta poin miliknya sendiri. Font 12 poin yang menjadi awal setiap halaman baru kini dikalikan faktor internal unit-per-poin, jadi ia 12 poin di Resolution mana pun. DrawChart, yang margin, ukuran label, dan lebar garisnya semuanya poin hard-coded, kini berjalan dengan skala yang sementara disetel ke 1. Yang tetap dalam unit gambar, memang disengaja, adalah default parameter publik seperti ukuran modul DrawQRCode dan ukuran font tabel default: mereka bagian dari kontrak API, jadi di Resolution 144 maknanya setengah dari makna di 72. Kalau Anda mengukur report dari template, panduan output report dengan font dan gambar di HotPDF membahas dari mana nilai-nilai itu biasanya berasal
Bagaimana memverifikasi bahwa sebuah layout bebas Resolution?
Pemeriksaan yang paling bisa diandalkan adalah perbandingan byte: render halaman yang sama di Resolution 72 lalu lagi di 144 dengan setiap koordinat dan ukuran digandakan, dan content stream tak terkompresinya harus identik. Kedua run mendarat pada nilai poin yang sama setelah proyeksi, jadi perbedaan apa pun adalah nilai yang melewatkan konversinya. Beginilah test suite HotPDF memeriksa paragraf, tabel, import HTML, XFA flattening, arc, metafile, dan gambar. Teknik yang sama bekerja untuk kode report Anda sendiri dengan harness nyaris nol:
procedure RenderPage(const FileName: string; Res: Integer; K: Single);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.AutoLaunch := False;
Pdf.Compression := cmNone; // content stream yang terbaca
Pdf.FileName := FileName;
Pdf.Resolution := Res;
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 10 * K);
Pdf.CurrentPage.TextOut(36 * K, 36 * K, 0, 'Line 1');
Pdf.CurrentPage.Rectangle(36 * K, 60 * K, 200 * K, 40 * K);
Pdf.CurrentPage.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
// RenderPage('r72.pdf', 72, 1) dan RenderPage('r144.pdf', 144, 2)
// harus menghasilkan content stream halaman yang identik byte-nya
Periksa operator yang membawa angka: Td, Tm, Tf, re, w, dan array TJ. Byte level file tetap akan berbeda di tanggal pembuatan dan /ID, jadi bandingkan stream-nya, bukan seluruh file. Ketidakcocokan hampir selalu menunjuk salah satu dari dua jebakan di atas: halaman yang diubah ukurannya lewat Width, atau nilai poin yang diberikan langsung ke SetFont. Kalau Anda baru pada panggilan menggambarnya sendiri, mulai dari walkthrough TextOut HotPDF untuk ukuran, style, dan rotasi, lalu kembali dan ganti Resolution begitu layout Anda membaca UserWidth. Detail API lengkap dan unduhan trial ada di halaman HotPDF Delphi PDF component