ใน PDFium Component สำหรับ Delphi การเปิด BrotliEnabled หรือ IsolatePerDocument ใน TPdfLibraryConfiguration เคยพาบิลด์ Skia ที่แถมมาสลับไปเป็นตัวเรนเดอร์ AGG โดยไม่มี error สักคำ เพราะทั้งสองตัวเลือกยก FPDF_LIBRARY_CONFIG ขึ้นไปถึงเวอร์ชันที่ PDFium อ่าน m_RendererType ตรงตามตัวหนังสือ ตั้งแต่ v3.123.0 ตัวเรนเดอร์ค่าเริ่มต้นคงเป็น default ของตัว DLL เอง และตั้งแต่ v3.125.0 การขอ Skia หรือ Fontations ที่ DLL หยิบยื่นไม่ได้จะขึ้น EPdfError ที่จับได้ แทนที่จะฆ่า process ทิ้ง
ทั้งสองบั๊กไม่มีตัวไหนประกาศตัวเอง อันแรกผลิตหน้าที่ดูปกติดี เพียงแต่ถูกเรนเดอร์โดย rasterizer ต่างคนละตัว ขอบลดรอยหยักกับขอบตัวอักษรเพี้ยนนิด ๆ จากบิลด์ที่คุณแถมไปให้ลูกค้าพร้อมเทสต์มาแล้ว อันที่สองประกาศตัวเสียอย่างดังก้อง ด้วยการพา process ของ host ลงทั้งตัวจากข้างใน initialization ระดับ native ทั้งคู่มาจากที่เดียวกัน: โครงสร้าง C ที่มีเวอร์ชันกำกับ ซึ่งฟิลด์จะมีค่าก็ต่อเมื่อเลขเวอร์ชันบอกว่ามีเท่านั้น และค่าศูนย์ในนั้นไม่ได้แปลว่า “ยังไม่ได้ตั้ง” แต่เป็นทางเลือกจริง ๆ หนึ่งทาง
FPDF_LIBRARY_CONFIG ตัดสินว่า PDFium จะใช้ตัวเรนเดอร์ตัวไหนอย่างไร
FPDF_InitLibraryWithConfig จะถามถึง m_RendererType ก็ต่อเมื่อฟิลด์ Version ของโครงสร้างเป็น 4 ขึ้นไป และตั้งแต่เวอร์ชันนั้นเป็นต้นไปมันใช้ค่าตามที่เขียนมาเป๊ะ ๆ ใต้เวอร์ชัน 4 PDFium เมินฟิลด์นี้แล้วหยิบ default ของบิลด์ ซึ่งคือ Skia ในบิลด์ที่คอมไพล์ด้วย PDF_USE_SKIA และเป็น AGG ในทุกกรณีอื่น
ฟิลด์ที่มาทีหลังทุกตัวเดินตามแพทเทิร์นเดียวกัน โครงสร้างโตขึ้นทีละความสามารถ และแต่ละความสามารถมาพร้อมเลขเวอร์ชันใหม่ PDFium Component ประกอบโครงสร้าง native ใน LoadLibrary จาก TPdfLibraryConfiguration ของคุณ แล้วยกเวอร์ชันขึ้นเท่าที่ตัวเลือกที่เซ็ตไว้บังคับเท่านั้น
| เวอร์ชันโครงสร้าง | ฟิลด์ที่เพิ่ม | เซ็ตเมื่อไร |
|---|---|---|
| 2 | m_pIsolate, m_v8EmbedderSlot | เขียนเสมอ; V8Isolate, V8EmbedderSlot |
| 3 | m_pPlatform | V8Platform ไม่ใช่ nil |
| 4 | m_RendererType | Renderer ที่ไม่ใช่ prpDefault |
| 5 | m_FontLibraryType | FontBackend ที่ไม่ใช่ pfbpDefault |
| 6 | m_BrotliEnabled | BrotliEnabled = True |
| 7 | m_IsolatePerDocument | IsolatePerDocument = True |
กับดักอยู่ในสองแถวสุดท้าย เวอร์ชันเป็นแบบสะสม: โครงสร้างเวอร์ชัน 6 ก็คือโครงสร้างเวอร์ชัน 4 พร้อมเวอร์ชัน 5 ไปด้วย PDFium จึงอ่าน m_RendererType กับ m_FontLibraryType ทั้งที่คุณขอแค่ Brotli สิ่งที่นั่งอยู่ในสองฟิลด์นั้นขณะนั้นจะกลายเป็นตัวเรนเดอร์กับ font backend ไปเต็ม ๆ ไม่ว่าคุณจะตั้งใจเลือกมันหรือไม่
ทำไมเปิด Brotli แล้วตัวเรนเดอร์ถึงสลับไป AGG
ก่อน v3.123.0 PDFium Component เขียน FPDF_RENDERERTYPE_AGG ลง m_RendererType สำหรับ prpDefault การตั้งค่าใดก็ตามที่ดันโครงสร้างขึ้นไปเวอร์ชัน 6 หรือ 7 จึงบังคับ AGG ลงบนบิลด์ Skia ทั้งที่ pdfium.dll กับ pdfium.v8.dll ที่แถมมากับ component เป็นบิลด์ Skia ปัญหาจึงโจมตี deployment ค่าเริ่มต้น ไม่ใช่ deployment แปลก ๆ
การแมปนี้ตอนเขียนดูไม่มีพิษภัย ที่เวอร์ชัน 2 หรือ 3 ฟิลด์นี้ไม่เคยถูกอ่าน prpDefault จึงแปลว่า “DLL จะทำอะไรก็ตามใจ” จริง ๆ แต่วินาทีที่ BrotliEnabled (เวอร์ชัน 6) หรือ IsolatePerDocument (เวอร์ชัน 7) เข้ามาในภาพ โค้ดชุดเดิมเปลี่ยน “ไม่มีความเห็น” ให้กลายเป็นคำขอ AGG อย่างชัดเจน ไม่มีอะไรพัง PDFium initialize ปกติ เรนเดอร์ทุกหน้า และคืน error code กลับมาศูนย์ เพราะจากมุมมองของมัน ผู้เรียกขอ AGG มาเองและก็ได้ AGG ไป
pixel hash ทำให้การสลับนี้มองเห็นในที่ที่ screenshot มองไม่เห็น เรนเดอร์หน้าแรกของเอกสารตัวอย่างเดียวกันใต้สามการตั้งค่าได้แบบนี้:
- ค่าเริ่มต้น: hash
502D77C3711B4ACF BrotliEnabled= True โดยปล่อยRendererเป็นprpDefault: hashF75B5EB4728ADE87- สั่ง
prpAggตรง ๆ: hashF75B5EB4728ADE87ตรงกับรอบที่เปิด Brotli เป๊ะ
การแก้ใน v3.123.0 คือฟังก์ชันสาธารณะ PdfNativeRendererType ซึ่งแปล TPdfRendererPreference เป็นค่าที่จะเขียนลง m_RendererType prpAgg กับ prpSkia แมปตรงตัวต่อตัว prpDefault ตอนนี้แมปไป Skia เมื่อ DLL ที่โหลดแล้ว export FPDF_RenderPageSkia และไป AGG ในกรณีอื่น export นี้ถูกคอมไพล์ภายใต้เงื่อนไข PDF_USE_SKIA เดียวกับค่า default Skia เอง จึงกลายเป็นคุณสมบัติบิลด์ตัวเดียวที่มองเห็นได้จากนอกตัว DLL หลังแก้แล้วการตั้งค่าแบบเปิด Brotli ให้ hash เดียวกับค่าเริ่มต้น
font backend ไม่เคยมีปัญหาแบบเดียวกัน m_FontLibraryType ถูกอ่านตั้งแต่เวอร์ชัน 5 และค่าศูนย์ของมัน คือ FPDF_FONTBACKENDTYPE_FREETYPE ก็เป็น default ของ PDFium ด้วยเมื่อฟิลด์ไม่ถูกอ่านเลย การเขียน FreeType สำหรับ pfbpDefault จึงทำซ้ำ default ของ native ได้เป๊ะ ค่าศูนย์ไม่ได้ผิดเสมอไป มันแค่ไม่ถูกโดยอัตโนมัติเสมอเท่านั้น
เมื่อมี v3.123.0 ขึ้นไป โค้ดตอนสตาร์ทที่คุณจะเขียนตามธรรมชาติก็ทำตามที่มันพูดจริง ๆ แล้ว:
uses
PDFium;
procedure ConfigurePdfiumAtStartup;
var
Config: TPdfLibraryConfiguration;
begin
// ต้องรันก่อนอะไรก็แล้วแต่จะไปโหลดไลบรารี native
Config := TPdfLibraryConfiguration.Default;
Config.BrotliEnabled := True; // ยก FPDF_LIBRARY_CONFIG ขึ้นเป็นเวอร์ชัน 6
// Renderer คง prpDefault: บนบิลด์ที่ export จะ resolve เป็น Skia
// เป็น FPDF_RenderPageSkia และเป็น AGG บนบิลด์ที่มีแต่ AGG
SetLength(Config.UserFontPaths, 1);
Config.UserFontPaths[0] := 'C:\ProgramData\MyApp\Fonts';
ConfigurePdfLibrary(Config);
end;
เตือนตัวเองไว้ว่า BrotliEnabled ทำให้ stream /BrotliDecode ของ PDF 2.0 ถอดรหัสได้ก็ต่อเมื่อตัว DLL เองถูกบิลด์ด้วย PDF_ENABLE_BROTLI flag นี้เป็นเพียงคำขอ บนบิลด์ที่ไม่มีรองรับ Brotli มันไม่มีผลอะไรเลย TPdfLibraryConfiguration.Hardened เหมือน Default ทุกอย่างยกเว้น AllowMachineTime ที่เป็น False ซึ่งกัน JavaScript ของเอกสารไว้ไม่ให้อ่านนาฬิกาจริง เป็นจุดเริ่มต้นที่สมเหตุผลสำหรับการประมวลผลไฟล์ไม่น่าเชื่อถือฝั่งเซิร์ฟเวอร์
เกิดอะไรขึ้นเมื่อคุณขอ backend ที่ DLL ไม่มีอยู่
PDFium ไม่คืน error ให้ตัวเรนเดอร์หรือ font backend ที่บิลด์ไม่มี FPDF_InitLibraryWithConfig จะโจมตี native CHECK ให้พัง ซึ่งบน Windows โผล่มาเป็น breakpoint exception และถ้าไม่มี structured exception handler ล้อม call ไว้ process ก็จบ ส่วนหัวของไฟล์ header ก็บอกไว้แบบนั้นจริง ๆ เตือนว่าค่าที่ไม่รองรับ “will similarly fail with an immediate crash”
สองกรณีที่จับต้องได้คือบิลด์ที่มีแต่ AGG ไปรับ FPDF_RENDERERTYPE_SKIA และบิลด์ที่ไม่มี Fontations ไปรับ FPDF_FONTBACKENDTYPE_FONTATIONS runtime Skia ที่แถมมาอยู่ในกลุ่มที่สอง เรนเดอร์ด้วย Skia แต่ใช้ FreeType กับฟอนต์ การขอ prpSkia คู่กับ pfbpFontations ใส่มันผลิต External exception 80000003 ฝั่ง Delphi มาให้ เมื่อ debugger หรือ exception handler บังเอิญจับมันได้ สถานการณ์ก็ยังฟื้นไม่ได้อยู่ดี:
- PDFium ถูกทิ้งค้างครึ่ง initialize
- configuration ระดับ process ถูกผนึกไปแล้ว
ConfigurePdfLibraryจึงปฏิเสธ configuration ที่แก้แล้ว - การลองใหม่ด้วย configuration อื่นใน process เดิมเป็นไปไม่ได้อีกต่อไป
นี่คือความล้มเหลวหัวตรงข้ามกับบั๊ก Brotli ที่นั่นฟิลด์ถือค่าที่ไม่มีใครเลือกและ PDFium ก็รับมันมาอย่างเงียบ ๆ ที่นี่ฟิลด์ถือค่าที่ผู้เรียกเลือกตั้งใจไว้และ PDFium ก็ไม่ยอมรับการต่อรองอะไรกับมันเลย ทั้งคู่เป็นปัญหาที่ wrapper ต้องเก็บให้จบก่อนคำสั่ง call ระดับ native เพราะพ้นจุดนั้นไปก็ไม่เหลืออะไรให้จับแล้ว
PDFium Component พรีเช็ก Skia กับ Fontations อย่างไร
ตั้งแต่ v3.125.0 LoadLibrary ตรวจ configuration หลังผูก export ของ DLL และก่อนเรียก FPDF_InitLibraryWithConfig แล้วเปลี่ยนตัวเรนเดอร์หรือ font backend ที่ไม่รองรับให้กลายเป็น EPdfError พร้อมข้อความที่เอ่ยชื่อการตั้งค่าที่มีปัญหากับทางเลือกอื่น DLL ถูกยูโหลดและ configuration ถูกเปิดผนึก ผู้เรียกจึงเลือกการตั้งค่าอื่นแล้วโหลดใหม่ได้
การตัดสินเองนั่งอยู่ในฟังก์ชันบริสุทธิ์ PdfLibraryConfigurationSupportError ที่รับ configuration บวก boolean สองตัวที่บรรยายบิลด์ แล้วคืนสตริงว่างเมื่อคุณสมบัติผสมนั้นปลอดภัย เพราะมันไม่แตะ state ระดับ native เลย คุณจึงเรียกมันจากเทสต์ของตัวเองกับคู่ความสามารถแบบไหนก็ได้ ข้างใน LoadLibrary boolean สองตัวนั้นมาจากหลักฐานคนละชนิด และควรได้รับความเชื่อต่างระดับกัน:
- Skiaตรวจพบจากการมีอยู่ของ export
FPDF_RenderPageSkiaซึ่งเป็นสัญญาณเดียวกับที่PdfNativeRendererTypeใช้ export กับตัวเรนเดอร์ Skia ถูกคอมไพล์ใต้เงื่อนไขเดียวกัน เช็กนี้จึงเป๊ะ - Fontationsไม่มี export ของตัวเอง ร่องรอยเดียวที่มันทิ้งไว้คือ font crates ภาษา Rust ที่มันดึงเข้าไฟล์ไบนารี PDFium Component จึงสแกนไฟล์ไลบรารีที่โหลดแล้วหาชื่อ crate
skrifaกับread-fonts(รวมread_fonts) การสแกนรันเฉพาะเมื่อถูกขอpfbpFontationsและไฟล์ที่อ่านไม่ได้นับเป็น “ไม่มี Fontations”
การเช็ก Fontations เป็น heuristic และมันผิดได้ฝั่งเดียว บิลด์ Fontations ที่ถูก strip สตริงพวกนั้นจนหมดจะถูกปฏิเสธ ทั้งที่ตัวมันใช้งานได้ การถ่วงน้ำหนักแบบนี้ตั้งใจไว้ การปฏิเสธที่ผิดต้องจ่ายด้วย exception หนึ่งตัวที่จับได้กับการ fallback ไป FreeType การยอมรับที่ผิดต้องจ่ายด้วย process ทั้งตัว
การเปิดผนึกสำคัญพอกับการเช็ก LoadLibrary ผนึก configuration ตั้งแต่ต้นการโหลด ไม่มีการรีเซ็ตก็เลยเกิดเหตุที่การปฏิเสธความสามารถทิ้งให้ ConfigurePdfLibrary ตอบทุกการลองใหม่ด้วย EPdfError “PDFium library configuration is already sealed” เส้นทางการปฏิเสธจึงเรียก UnloadLibrary ก่อน call FPDF_DestroyLibrary ของมันปลอดภัยในจังหวะนั้น เพราะ PDFium ยังไม่ถูก initialize และคืนค่าทันที ความล้มเหลวอื่นของการโหลด เช่น DLL หายไม่เจอหรือสถาปัตยกรรมไม่ตรง คงผนึกไว้ ลูปลองใหม่จึงต้องแยกสองกรณีนี้ออกจากกันให้ได้:
uses
SysUtils, PDFium;
function StartPdfiumPreferringSkia: TPdfRendererPreference;
var
Config: TPdfLibraryConfiguration;
begin
Config := TPdfLibraryConfiguration.Default;
Config.Renderer := prpSkia;
ConfigurePdfLibrary(Config);
try
PDFium.LoadLibrary; // ระบุยูนิต: Windows.LoadLibrary ใช้ชื่อเดียวกัน
Result := prpSkia;
except
on E: EPdfError do
begin
// การปฏิเสธความสามารถจะยูโหลด DLL แล้วเปิดผนึก configuration
// DLL ที่โหลดไม่ได้เลยยังผนึกอยู่: ลองซ้ำก็ไม่ช่วย
if PdfLibraryConfigurationSealed then
raise;
Config.Renderer := prpAgg;
ConfigurePdfLibrary(Config);
PDFium.LoadLibrary;
Result := prpAgg;
end;
end;
end;
สังเกต PDFium.LoadLibrary ที่ระบุยูนิตชัด ๆ ในยูนิตที่ใช้ Windows หรือ Winapi.Windows ด้วย LoadLibrary ที่ไม่ระบุยูนิตจะ resolve ไปยูนิตที่ปรากฏท้ายสุดใน uses clause ถ้าผู้ชนะคือฟังก์ชัน Win32 call แบบไร้พารามิเตอร์จะคอมไพล์ไม่ผ่านด้วย error เรื่องจำนวนอาร์กิวเมนต์ ซึ่งไม่บอกอะไรเกี่ยวกับ PDFium เลย
การตรวจที่เกิดเร็วกว่านั้นอีก
ConfigurePdfLibrary ปฏิเสธบางคู่คุณสมบัติก่อนมี DLL เข้ามาเกี่ยวข้องเลย ทั้งหมดขึ้น EPdfError การระบุ FontBackend ชัด ๆ รวมถึง pfbpFreeType บังคับให้ Renderer = prpSkia เพราะ PDFium ถามหา font backend เฉพาะกับตัวเรนเดอร์ Skia IsolatePerDocument บังคับให้ V8Isolate เป็น nil เพราะ PDFium สร้าง isolate ของตัวเองต่อเอกสาร แล้วก็โจมตี native CHECK ให้พังถ้าคุณยังส่งอันอื่นเข้าไปให้ด้วย สตริงว่างใน UserFontPaths ถูกปฏิเสธ และการ call หลังความพยายามโหลดครั้งแรกพังด้วย “PDFium library configuration is already sealed”
กฎสุดท้ายมีผลทางปฏิบัติ: คุณแอบพรีเช็ก DLL ก่อนแล้วค่อยตั้งค่ามันไม่ได้ GetSkiaRenderCapabilities, V8FeaturesAvailable การเปิดเอกสาร และ entry point อื่นส่วนใหญ่เรียก LoadLibrary ภายใน ซึ่งผนึก configuration ตรงนั้นทันที การ call UnloadLibrary ทีหลังก็ไม่ได้เปิดมันกลับมา ตั้งค่าก่อน โหลดต่อ แล้วค่อยถามคำถาม ซึ่งเป๊ะกับลำดับที่ routine วินิจฉัยควรเดิน:
uses
SysUtils, PDFium, FPdfView;
function DescribePdfiumState: string;
var
Config: TPdfLibraryConfiguration;
Renderer: string;
begin
Config := GetPdfLibraryConfiguration; // เป็นสำเนา ส่องดูได้ปลอดภัย
if not PDFium.Loaded then
begin
if PdfLibraryConfigurationSealed then
Exit('PDFium failed to load; configuration is sealed');
Exit('PDFium not loaded; configuration can still change');
end;
// resolution เดียวกับที่ LoadLibrary ใช้ตอนประกอบ FPDF_LIBRARY_CONFIG
if PdfNativeRendererType(Config.Renderer,
GetSkiaRenderCapabilities.PageRender) = FPDF_RENDERERTYPE_SKIA then
Renderer := 'Skia'
else
Renderer := 'AGG';
Result := Format('Renderer=%s Brotli=%s IsolatePerDocument=%s',
[Renderer, BoolToStr(Config.BrotliEnabled, True),
BoolToStr(Config.IsolatePerDocument, True)]);
end;
การ log บรรทัดนี้หนึ่งครั้งตอนสตาร์ทแสนถูก และเป็นสิ่งแรกที่คุณอยากเห็นในตั๋วแจ้งปัญหาที่พูดว่า “ข้อความบนเซิร์ฟเวอร์ดูต่างออกไป” PDFium.Loaded ระบุยูนิตด้วยเหตุผลเดียวกับ LoadLibrary: ข้างในเมธอดของฟอร์มหรือ component Loaded เปล่า ๆ จะผูกไปที่ TComponent.Loaded
สองวิธีที่โครงสร้าง C แบบมีเวอร์ชันพังได้
โครงสร้าง configuration แบบมีเวอร์ชันทุกตัว ไม่ว่าจะเป็น FPDF_LIBRARY_CONFIG, record cbSize ของ Win32 หรือ ABI ของ plugin ล้วนพังได้สองแบบสมมาตรกัน และ wrapper ต้องกันทั้งคู่ แบบแรกคือเติมฟิลด์ไปแต่คงเวอร์ชันต่ำเกินไป แบบที่สองคือยกเวอร์ชันไปแต่ทิ้งฟิลด์ไว้ที่ค่าศูนย์ซึ่งไลบรารีอ่านว่าเป็นทางเลือกที่ตั้งใจไว้
- เซ็ตฟิลด์แต่เวอร์ชันต่ำเกินไป เขียน
m_BrotliEnabled= 1 ลงโครงสร้างเวอร์ชัน 2 แล้ว PDFium ไม่เคยหันมามองเลย call สำเร็จและ stream Brotli ก็ยังถอดรหัสไม่ได้ การป้องกันคือ derive เวอร์ชันจากฟิลด์ที่ใช้จริง ซึ่งเป็นสิ่งที่LoadLibraryทำ ไม่ใช่ hard-code เวอร์ชันตายตัว - เวอร์ชันพอแล้วแต่ค่าศูนย์มีความหมาย ยกเวอร์ชันขึ้น 6 แล้วทุกฟิลด์จนถึงเวอร์ชัน 6 กลายเป็นมีชีวิต
FillCharล้างm_RendererTypeเป็นศูนย์ซึ่งก็คือFPDF_RENDERERTYPE_AGGตัวเรนเดอร์ตัวจริง ไม่ใช่ “ยังไม่ได้ตั้ง” การป้องกันคือเขียนทุกฟิลด์ที่เวอร์ชันที่เลือกคลุมด้วยค่าที่ตั้งใจไว้ และ resolve คำว่า “default” กับบิลด์จริงแทนการสมมติ
กฎข้อที่สามตามมาสำหรับค่าที่พาให้ตัวถูกเรียกพังได้: ตรวจมันกับสิ่งที่ไบนารีทำได้ก่อนคำสั่ง call ด้วยหลักฐานที่แข็งแรงที่สุดเท่าที่มี และพูดความจริงในโค้ดกับเอกสารเมื่อหลักฐานนั้นเป็นแค่ heuristic สัญลักษณ์ที่ export มาคือหลักฐาน ชื่อ crate ใน string table คือการเดาที่ดีเท่านั้น
สรุป: configuration ไลบรารีของ PDFium Component
- call
ConfigurePdfLibraryครั้งเดียว ก่อนอะไรก็แล้วแต่จะไปโหลด DLL การถามความสามารถใด ๆ หรือการโหลดเอกสารจะผนึกมันทันที - อัปเกรดเป็น v3.123.0 ขึ้นไปถ้าคุณเซ็ต
BrotliEnabledหรือIsolatePerDocumentแล้วคาดหวัง output Skia จาก runtime ที่แถมมา - ปล่อย
Rendererเป็นprpDefaultเว้นแต่คุณต้องการ rasterizer ตัวใดตัวหนึ่ง ตอนนี้มัน resolve เป็น default ของบิลด์ในทุกเวอร์ชันโครงสร้าง - ใช้
PdfNativeRendererTypeคู่กับGetSkiaRenderCapabilities.PageRenderเพื่อ log ว่าตัวเรนเดอร์ที่ทำงานจริงคือตัวไหน - คาดหวัง
EPdfErrorไม่ใช่ crash สำหรับprpSkiaบน DLL ที่มีแต่ AGG หรือpfbpFontationsบน DLL ที่ไม่มี Fontations ใน v3.125.0 ขึ้นไป - หลังการปฏิเสธความสามารถ
PdfLibraryConfigurationSealedจะเป็น False และคุณตั้งค่าใหม่ได้ หลังการโหลด DLL พังมันคงเป็น True - ถือการตรวจจับ Fontations เป็น heuristic และเตรียม fallback เป็น FreeType ไว้
- เขียน
PDFium.LoadLibraryกับPDFium.Loadedพร้อมชื่อยูนิต กันชนชื่อกับ Win32 และTComponent
ถ้า DLL พังก่อนที่ configuration จะมีโอกาสมีความหมายด้วยซ้ำ เริ่มจากการวินิจฉัยความล้มเหลวของการโหลด PDFium DLL ใน Delphi และวิธีที่ component หาไบนารีที่ถูกต้องบนแต่ละแพลตฟอร์ม ดูที่การโหลดไลบรารี native ของ PDFium บนทุกเป้าหมาย พอเรื่องตัวเรนเดอร์ลุล่งแล้ว render cache กับกลยุทธ์ซูมลื่น เล่าวิธีทำให้การเรนเดอร์หน้ายังไวใน viewer
PDFium Component ห่อเอนจิน PDFium ไว้ให้ Delphi กับ C++Builder พร้อมการเช็ก configuration อย่างที่เล่าไว้ในนี้ การ initialize ระดับ native จึงพังเป็น exception ของ Pascal ที่จับจัดการได้ ไม่ใช่การจบ process รายละเอียดผลิตภัณฑ์กับดาวน์โหลดอยู่ที่หน้าผลิตภัณฑ์ PDFium Component for Delphi