ในกระบวนการ Fill Stage ตัวประมวลผล JRVerticalFiller จะแบ่งพื้นที่เอกสาร A4 ออกเป็นระบบพิกัดเฉพาะส่วน (Band Coordinate System) โดยพิกัด $Y=0$ จะเริ่มต้นใหม่เสมอที่ขอบบนของแต่ละ Band ไม่ใช่พิกัดสะสมจากหัวกระดาษแผ่นใหญ่
สูตรคำนวณพื้นที่พิมพ์จริง (Printable Area):
Printable Height = pageHeight - (topMargin + bottomMargin)
Printable Width = pageWidth - (leftMargin + rightMargin)
// ตัวอย่างมาตรฐาน A4:
// 842pt - (20pt + 20pt) = 802pt (ความสูงสุทธิ)
// 595pt - (20pt + 20pt) = 555pt (ความกว้างสุทธิ)
ลักษณะการกระตุ้นทำงาน (Trigger Timing) ของ Bands หลัก:
<background>: วาดลงบนเลเยอร์ล่างสุดของทุกหน้า เหมาะสำหรับ Watermark / ลายเซ็นต์ดิจิทัล<title>: ประมวลผลครั้งเดียวที่จุดเริ่มต้นของเอกสาร (ควบคุมการขึ้นหน้าใหม่ด้วย isTitleNewPage)<pageHeader> / <pageFooter>: วาดที่หัวและท้ายกระดาษของทุกๆ หน้า<lastPageFooter>: หากประกาศไว้ จะถูกนำมาวาดแทนที่ pageFooter ปกติบนหน้าสุดท้าย<summary>: พิมพ์ปิดท้ายเอกสารเพียงครั้งเดียว (ควบคุมให้เกาะต่อท้ายแถวสุดท้ายด้วย isSummaryNewPage="false")whenNoDataType="AllSectionsNoDetail": บังคับให้โครงสร้าง Header/Footer วาดออกมาแม้ไม่มีแถวข้อมูลจาก DataSourceheight หรือยืดขยายด้วย StretchHeight จนมีความสูงรวมมากกว่าพื้นที่ Printable Height ($> 802\text{ pt}$) เครื่องยนต์จะไม่สามารถวาด Band นั้นลงในหน้าเดียวได้สำเร็จ ส่งผลให้เกิดการ Trigger ตัดขึ้นหน้าใหม่ไปเรื่อยๆ ซ้ำซากไม่รู้จบ จนกระทั่งหน่วยความจำ JVM พังทลายด้วย OutOfMemoryError: Java heap space
<!-- JRXML 7.0.x Schema: Tax Invoice Architecture -->
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
name="TaxInvoice" pageWidth="595" pageHeight="842"
columnWidth="555" leftMargin="20" rightMargin="20"
topMargin="20" bottomMargin="20"
whenNoDataType="AllSectionsNoDetail">
<background>
<band height="802">
<element kind="staticText" x="100" y="350" width="355" height="60">
<text><![CDATA[ORIGINAL]]></text>
</element>
</band>
</background>
<title>
<band height="60">
<element kind="staticText" x="0" y="10" width="250" height="30">
<text><![CDATA[TAX INVOICE / RECEIPT]]></text>
</element>
</band>
</title>
<detail>
<band height="25" splitType="Prevent">
<element kind="textField" x="0" y="2" width="400" height="20">
<expression><![CDATA[$F{item_description}]]></expression>
</element>
<element kind="textField" x="410" y="2" width="145" height="20">
<expression><![CDATA[$F{unit_price}]]></expression>
</element>
</band>
</detail>
<summary>
<band height="40">
<element kind="staticText" x="300" y="10" width="100" height="20">
<text><![CDATA[Grand Total:]></text>
</element>
</band>
</summary>
</jasperReport>
ข้อกำหนดสถาปัตยกรรม:
• splitType="Prevent": ป้องกันการฉีกขาดของแถวข้อมูลระหว่างรอยต่อหน้ากระดาษ
• whenNoDataType="AllSectionsNoDetail": ประคองโครงสร้างเอกสารให้ออกมาแม้ไร้ข้อมูลจาก Record
เงื่อนไขทางเทคนิค (Full-Coverage 100%):
1. กำหนด Root <jasperReport> เป็น A4 (595x842pt, Margins 20pt) พร้อม whenNoDataType="AllSectionsNoDetail"
2. Detail Band ต้องมี splitType="Prevent" และความสูงต้อง < 802pt เพื่อป้องกัน Infinite Loop บน JVM
3. ใช้ Unified Element Model 7.0.x: <element kind="textField" ...> วาง $F{service_node_name}
เงื่อนไขทางเทคนิค (Full-Coverage 100%):
1. <background> ความสูงเต็ม Printable Area (802pt) วาง Watermark staticText "INTERNAL AUDIT ONLY"
2. <title> ความสูง 60pt พร้อมใส่ isTitleNewPage="false" (อยู่ร่วมหน้าแรก)
3. <pageFooter> ความสูง 20pt แสดงตัวแปร "Page " + $V{PAGE_NUMBER}
4. <lastPageFooter> ความสูง 20pt วางข้อความ "-- END OF AUDIT REPORT --" แทนที่ pageFooter ปกติในหน้าสุดท้าย
5. <summary> ความสูง 40pt พร้อมใส่ isSummaryNewPage="false" ล็อกยอดรวมให้เกาะต่อท้าย Detail
แบบทดสอบ 15 ข้อเชิงลึก อ้างอิงจากหลักการ 11 Bands, Coordinate Calculation และจุดตายบน Production ที่สอนไว้ทั้งหมด: