ในการส่งออกเวิร์กชีต Excel เป็น PDF จาก Delphi หรือ C++Builder โดยไม่ต้องติดตั้ง Office คุณสามารถเรียกใช้ SaveAsPDF บนเวิร์กชีตหรือเวิร์กบุ๊กของ HotXLS ได้เลย ตัว HotXLS เป็นส่วนประกอบ VCL Excel แบบดั้งเดิม และระบบส่งออกเวิร์กชีตเป็น PDF จะเรนเดอร์ขอบเขตข้อมูลที่ใช้งานจริง (UsedRange) ของชีตให้ออกมาเป็นไฟล์ PDF 1.4 แบบเบ็ดเสร็จในตัว โดยจะจับคู่ขนาดความกว้างคอลัมน์ของ Excel และข้อความแสดงผลในเซลล์ลงบนหน้ากระดาษขนาด A4 พร้อมระบบจัดแบ่งหน้าหลายหน้าอัตโนมัติ โดยไม่จำเป็นต้องใช้โปรแกรม Excel, ไดรเวอร์เครื่องพิมพ์ หรือไฟล์ DLL ภายนอกใดๆ ในขั้นตอนการเรนเดอร์
HotXLS ปฏิบัติต่อ PDF ในฐานะที่เป็นรูปแบบการนำเสนอข้อมูล ซึ่งเป็นส่วนการทำงานคู่ขนานกับ CSV, TSV, and HTML export paths มากกว่าที่จะเป็นเอนจินการเรนเดอร์ข้อมูลที่แสดงผลแบบแม่นยำทุกพิกเซล การส่งออกจะถอดแบบเนื้อหาเค้าโครงตารางข้อมูล: ทั้งค่าที่ปรากฏในตารางสเปรดชีต, สัดส่วนของคอลัมน์ และเส้นตารางรอบเซลล์ การกำหนดขอบเขตนี้มีความสำคัญเนื่องจากช่วยสร้างความเข้าใจที่ถูกต้องตั้งแต่แรกเริ่ม และเนื้อหาที่เหลือของบทความนี้จะอธิบายรายละเอียดของขอบเขตดังกล่าวอย่างตรงไปตรงมา
จะแปลงไฟล์ Excel เป็น PDF ใน Delphi โดยไม่ใช้ Office ได้อย่างไร?
แนวทางที่สั้นที่สุดคือการเรียกใช้งานเพียงคำสั่งเดียว โดย TXLSWorksheet จะแสดงเมธอด SaveAsPDF(FileName) และ SaveAsPDF(Stream) และ TXLSWorkbook ก็แสดงเวอร์ชันโหลดเกิน (overload) สองรูปแบบเดียวกันนี้เพื่อส่งต่อการทำงานไปยังชีตที่ใช้งานอยู่ แต่ละฟังก์ชันจะส่งคืนค่า Integer เป็น 1 เมื่อสำเร็จ และ -1 เมื่อล้มเหลว ทำให้คุณสามารถแยกเงื่อนไขการทำงานต่อได้ทันทีโดยไม่ต้องครอบคำสั่งไว้ในบล็อกจัดการข้อผิดพลาดของคุณเอง
uses
lxHandle;
var
Book: TXLSWorkbook;
begin
Book := TXLSWorkbook.Create;
try
Book.Open('sales-june.xlsx');
// Render the active sheet's used range straight to report.pdf.
if Book.SaveAsPDF('report.pdf') = 1 then
Writeln('PDF written');
finally
Book.Free;
end;
end;
เมื่อเรียกใช้ SaveAsPDF บนเวิร์กชีต HotXLS จะส่งออกส่วน UsedRange ของชีตนั้น ซึ่งก็คือกล่องขอบเขตของเซลล์ทั้งหมดที่มีข้อมูลอยู่ ส่วนโหลดเกินของเวิร์กบุ๊กเป็นวิธีอำนวยความสะดวกที่ช่วยส่งต่อคำสั่งไปยังชีตที่ใช้งานอยู่ ดังนั้นหากรายงานมีหลายชีต คุณจะต้องใช้ลูปวนซ้ำตามชีตที่ต้องการ หรือเลือกชีตที่ใช้งานอยู่ก่อนเรียกใช้คำสั่ง ระบบนี้ไม่มีโหมดประมวลผลทั้งเวิร์กบุ๊กแบบรวดเดียวที่จะรวมทุกชีตเข้าด้วยกันเป็นไฟล์เดียว: แต่ละคำสั่งการเรียกใช้จะทำหน้าที่เรนเดอร์ทีละชีตหรือทีละช่วงข้อมูลเท่านั้น
HotXLS แปลงช่วงข้อมูลที่ใช้งานเป็นหน้า PDF อย่างไร?
HotXLS จะใช้ช่วงข้อมูลที่ใช้งานจริงทีละเซลล์และจัดวางโครงร่างเป็นตารางกริดที่แบ่งหน้า ข้อมูลมิติของคอลัมน์ดึงมาจาก Excel โดยตรง: ขนาดความกว้างคอลัมน์จะถูกแปลงจากหน่วยความกว้าง Excel เป็นหน่วยพอยต์สำหรับงานพิมพ์ (points) พร้อมกำหนดขนาดขั้นต่ำเพื่อป้องกันไม่ให้คอลัมน์แคบเกินไป เมื่อผลรวมขนาดคอลัมน์กว้างกว่าหน้ากระดาษที่พิมพ์ได้ HotXLS จะย่อขนาดลงอย่างได้สัดส่วนเพื่อให้พอดีหน้ากระดาษ แต่หากขนาดพอดีอยู่แล้ว ระบบจะคงขนาดเดิมไว้โดยไม่มีการขยายขนาดออกไป
แถวข้อมูลจะไหลลงตามหน้ากระดาษจนกระทั่งแถวถัดไปจะล้นขอบล่าง ซึ่ง ณ จุดนั้น HotXLS จะเริ่มหน้าใหม่และดำเนินการต่อ ทำให้ชีตที่มีแถวข้อมูลเป็นพันๆ แถวสามารถแบ่งหน้าได้ด้วยตัวเองโดยที่คุณไม่ต้องจัดการเรื่องการแบ่งหน้าเลย ทุกเซลล์จะแสดงผลตามค่า FormattedText ซึ่งเป็นสตริงการแสดงผลหลังจากจัดรูปแบบตัวเลขแล้ว ดังนั้นวันที่จึงแสดงออกมาตามแบบที่เห็นบนชีตจริง และเซลล์สกุลเงินจะยังคงเครื่องหมายจุลภาคและสัญลักษณ์สกุลเงินไว้ ข้อความที่ยาวเกินขนาดความกว้างคอลัมน์จะถูกตัดให้แสดงผลพอดีขอบเขตเซลล์เท่านั้น แทนที่จะล้นไปทับเซลล์ข้างเคียง
การควบคุมแนวตั้งแนวนอน, ขอบกระดาษ และขนาดตัวอักษร
สำหรับการตั้งค่าเค้าโครงที่ซับซ้อนกว่าปกติ คุณสามารถสั่งงานตัวส่งออกข้อมูลได้โดยตรง คลาส TXLSPDFExport คือคลาสที่ทำงานอยู่เบื้องหลังเมธอดอำนวยความสะดวกบรรทัดเดียวดังกล่าว และคลาสนี้เผยแพร่คุณสมบัติสามประการ ได้แก่: Orientation (ค่า xlsPdfPortrait หรือ xlsPdfLandscape โดยค่าเริ่มต้นเป็นแนวตั้ง), Margin (หน่วยพอยต์ ค่าเริ่มต้นคือ 40) และ FontSize (หน่วยพอยต์ ค่าเริ่มต้นคือ 9) กำหนดค่าเหล่านี้ก่อนเรียกใช้ SaveAsPDF เพื่อให้มีผลทั่วทั้งเอกสาร
uses
lxHandle, lxPDF;
var
Book: TXLSWorkbook;
Exporter: TXLSPDFExport;
begin
Book := TXLSWorkbook.Create;
try
Book.Open('wide-ledger.xlsx');
Exporter := TXLSPDFExport.Create;
try
Exporter.Orientation := xlsPdfLandscape; // fit wide sheets on the page
Exporter.Margin := 28; // tighter page margin, in points
Exporter.FontSize := 8; // denser rows
Exporter.SaveAsPDF(Book.ActiveSheet, 'ledger.pdf');
finally
Exporter.Free;
end;
finally
Book.Free;
end;
end;
คลาส TXLSPDFExport ยังยอมรับช่วงข้อมูลแทนการส่งออกทั้งชีตผ่านเมธอด SaveAsPDF(Range: IXLSRange; ...) ซึ่งช่วยให้คุณสามารถส่งออกเฉพาะบล็อกข้อมูลสรุปจากชีตที่มีการเขียนสูตรคำนวณทดเลขอื่นๆ ปนอยู่ได้ ส่วนโหลดเกินของช่วงข้อมูลดังกล่าวคือคำสั่งเดียวกับที่ตัวช่วยของเวิร์กชีตเรียกใช้งานเป็นการภายใน ดังนั้นพฤติกรรมการจัดวาง, การปรับขนาด และการแบ่งหน้าจึงทำงานเหมือนกันทุกประการ ไม่ว่าคุณจะส่งช่วงข้อมูล UsedRange ทั้งหมดหรือเจาะจงเฉพาะพื้นที่เลือกก็ตาม
var
Book: TXLSWorkbook;
Exporter: TXLSPDFExport;
Summary: IXLSRange;
begin
Book := TXLSWorkbook.Create;
try
Book.Open('quarter.xlsx');
// Export only the summary block, not the scratch columns beside it.
Summary := Book.Sheets[1].Range['A1', 'F20'];
Exporter := TXLSPDFExport.Create;
try
Exporter.SaveAsPDF(Summary, 'summary.pdf');
finally
Exporter.Free;
end;
finally
Book.Free;
end;
end;
ทำไม HotXLS ไม่ฝังฟอนต์: ข้อพิจารณาระหว่าง Helvetica และ WinAnsi
HotXLS เขียนข้อความโดยใช้ฟอนต์ Helvetica ในตัวของ PDF และระบบการเข้ารหัส WinAnsiEncoding ซึ่งเป็นการตัดสินใจเลือกแบบไม่มีการพึ่งพาภายนอกและมีข้อจำกัดที่แท้จริงข้อหนึ่ง เนื่องจาก Helvetica เป็นหนึ่งในฟอนต์มาตรฐาน 14 ฟอนต์ที่โปรแกรมเปิดอ่าน PDF ทุกโปรแกรมมีอยู่แล้ว ตัวส่งออกข้อมูลจึงไม่ต้องฝังชุดคำสั่งฟอนต์ลงไปเลย: ทำให้ไฟล์มีขนาดเล็กและสามารถเปิดดูได้ทุกที่ โดยไม่ต้องมีใบอนุญาตใช้งานหรือต้องจัดส่งไฟล์ร่วมไปเพิ่ม สิ่งที่ต้องแลกมาคือประเภทตัวอักษรที่ระบบสามารถแสดงผลบนหน้ากระดาษได้
การเข้ารหัส WinAnsiEncoding จะครอบคลุมชุดอักขระ Windows-1252 ทำให้ข้อความภาษาละติน, เครื่องหมายวรรคตอนทั่วไป และการออกเสียงในยุโรปตะวันตกทั่วไปสามารถเรนเดอร์ได้อย่างถูกต้อง และ HotXLS ยังรวมอักขระสำหรับงานพิมพ์จำนวนหนึ่ง เช่น อัญประกาศแบบโค้ง, เครื่องหมายขีดกลาง en และ em รวมถึงสัญลักษณ์ยูโรลงใน CP1252 อักขระที่อยู่นอกช่วงดังกล่าว เช่น ตัวอักษร CJK (จีน, ญี่ปุ่น, เกาหลี), อักษรซีริลลิก, กรีก และอาหรับ จะแสดงผลออกมาเป็นเครื่องหมายคำถาม (?) บนหน้ากระดาษ ดังนั้นหากชีตของคุณมีข้อความที่ไม่ใช่ภาษาละติน ตัวส่งออกนี้จะไม่ใช่เครื่องมือที่เหมาะสม และควรเลือกใช้เอนจินการเรนเดอร์ PDF แบบเต็มสไตล์แทน
ข้อจำกัดของการส่งออกเวิร์กชีตเป็น PDF
HotXLS จะเรนเดอร์เฉพาะเนื้อหาและโครงสร้าง ไม่ใช่รูปแบบการแสดงผลที่สมบูรณ์ของชีต สีถมในเซลล์, สไตล์เส้นขอบแต่ละด้าน, ฟอนต์และสีของแต่ละเซลล์, แผนภูมิ และรูปภาพที่ฝังอยู่จะอยู่นอกเหนือการประมวลผลของตัวส่งออก สิ่งที่คุณจะได้รับคือตารางข้อความพร้อมเส้นตารางขนาดสม่ำเสมอ ซึ่งเป็นข้อตกลงการทำงานเดียวกับที่ตัวส่งออก HTML และ RTF ปฏิบัติตาม และเหมาะสำหรับไฟล์รายงานหรือการส่งมอบข้อมูลที่เน้นความสำคัญของตัวเลขมากกว่าเรื่องธีมการตกแต่ง
เมื่อเป้าหมายคือการสร้างเอกสารที่ตกแต่งสวยงามและสอดคล้องกับเอกลักษณ์ของแบรนด์ แนวทางปฏิบัติทั่วไปคือการรวบรวมตัวเลขลงในเทมเพลตสเปรดชีตก่อน ซึ่งหัวข้อ template-driven report generation workflow จะอธิบายรายละเอียดเรื่องนี้ และเนื่องจากการทำงานทั้งหมดนี้ไม่มีการเรียกใช้งาน COM หรือโปรแกรม Excel ที่รันอยู่ กระบวนการทำงานจึงสอดคล้องกับ Office-free automation model ที่ HotXLS ใช้เป็นหลัก: ซึ่งเป็นโค้ด Pascal แท้ๆ ที่นำไปปรับใช้งานบนเซิร์ฟเวอร์แบบ headless ได้โดยไม่ต้องกังวลเรื่องใบอนุญาตใช้งานระบบเชื่อมต่อ
ไฟล์ PDF แบบเบ็ดเสร็จในตัว แบบไบต์ต่อไบต์
HotXLS สร้างโครงสร้างไฟล์ PDF ขึ้นด้วยมือและคำนวณตารางอ้างอิงไขว้ (cross-reference table) จากพิกัดไบต์ที่แน่นอน (byte offsets) ซึ่งเป็นสาเหตุที่ทำให้ผลลัพธ์ของไฟล์สามารถเปิดอ่านได้ในโปรแกรมเปิดอ่านทุกโปรแกรม ในระหว่างการแปลงข้อมูล (serialize) ของแต่ละออบเจกต์ HotXLS จะบันทึกตำแหน่งออฟเซตเริ่มต้นของออบเจกต์นั้นก่อนที่จะเขียนข้อมูลไบต์ จากนั้นจะขยับตำแหน่งการทำงานตามความยาวที่เพิ่งเขียนลงไป เพื่อให้ตาราง xref ชี้ไปยังตำแหน่งแต่ละออบเจกต์ได้อย่างแม่นยำ และ startxref ระบุจุดเริ่มต้นของตารางนั้น หากโปรแกรมเปิดอ่านพบว่าตำแหน่งออฟเซตเหล่านี้คลาดเคลื่อนไปแม้เพียงไบต์เดียว จะปฏิเสธไฟล์ทั้งหมดทันที ทำให้ตำแหน่งออฟเซตที่คำนวณเองมีความสำคัญอย่างยิ่งต่อการทำงานของระบบส่งออกข้อมูลนี้
ข้อตกลงการทำงานดังกล่าวส่งผลกระทบที่สำคัญข้อหนึ่งคือ โหนด Pages จะถูกเขียนขึ้นก่อนเป็นออบเจกต์ลำดับที่ 1 แต่รายการหน้าย่อยภายในจะยังไม่ทราบแน่ชัดจนกว่าหน้ากระดาษทั้งหมดจะถูกส่งออก ดังนั้น HotXLS จึงจองพื้นที่ออบเจกต์นี้ไว้เป็นพื้นที่ตัวแทน (placeholder) และเขียนข้อมูลหน้าย่อยและจำนวนหน้าจริงลงไปก่อนที่ขั้นตอนการคำนวณตำแหน่งออฟเซตจะทำงาน และจะไม่มีการแก้ไขหลังจากนั้น เมื่อไบต์ของออบเจกต์ได้รับการวัดค่าสำหรับ xref แล้ว ข้อมูลจะถูกตรึงไว้ทันที: เนื่องจากการแก้ไขเพียงไบต์เดียวในภายหลังจะส่งผลให้ออฟเซตถัดไปคลาดเคลื่อนตามกันไปทั้งหมดและทำให้ตารางเสียหาย นี่คือกฎการทำงานส่วนย่อยเพื่อรับรองความถูกต้องของโครงสร้างไฟล์ PDF 1.4 ตามมาตรฐานโดยไม่ต้องใช้ไลบรารีภายนอกมาช่วย
เมธอด SaveAsPDF และคลาส TXLSPDFExport ที่แสดงในที่นี้ถูกจัดส่งมาพร้อมกับ HotXLS Delphi Excel Component ควบคู่ไปกับระบบส่งออกไฟล์ CSV, HTML และ RTF ตลอดจน API การทำงานกับเวิร์กชีตที่สมบูรณ์สำหรับ Delphi และ C++Builder