HotPDF วาดกราฟิกแบบเวกเตอร์โดยการสร้างเส้นทาง (path) บนหน้าปัจจุบันแล้วสั่งให้วาด (paint) โดยไม่มีขั้นตอนของบิตแมปมาคั่นกลาง เส้นที่คุณวาดด้วย MoveTo และ LineTo จะถูกแปลงเป็นโอเปอเรเตอร์เส้นทางของ PDF ในสตรีมเนื้อหา ทำให้มันยังคงเป็นเวกเตอร์แท้: คมชัดที่การซูม 50%, คมชัดที่ 1600% และใช้ขนาดไฟล์เพียงเศษเสี้ยวเมื่อเทียบกับเวอร์ชันที่ถูกแรสเตอร์ไรซ์ สำหรับแผนภาพ, เส้นขอบตาราง, แกนแผนภูมิ และการตกแต่งฟอร์ม นี่คือสิ่งที่คุณต้องการอย่างแน่นอน และ API ที่อยู่เบื้องหลังก็เล็กพอที่จะเรียนรู้ได้ในคราวเดียว
พื้นที่วาดทั้งหมดอยู่บน THotPDF.CurrentPage ระหว่าง BeginDoc และ EndDoc คุณจะตั้งค่าสีและความกว้างเส้นบนออบเจ็กต์หน้าปัจจุบัน วางรูปทรงเรขาคณิต และเรียกใช้โอเปอเรเตอร์วาดเพื่อยืนยันการวาด (commit) องค์ประกอบพื้นฐานสี่อย่างที่คุณจะใช้มากที่สุดคือ MoveTo และ LineTo สำหรับเส้นทางอิสระ, Rectangle สำหรับกล่อง, Circle สำหรับวงกลม และโอเปอเรเตอร์วาดสองตัวคือ Stroke และ Fill
ระบบพิกัดอยู่ที่มุมล่างซ้าย (bottom-left)
นี่คือสิ่งเดียวที่มักจะทำให้ทุกคนที่ย้ายมาจาก VCL สับสน TCanvas ที่คุณใช้สำหรับวาดคอนโทรลจะตั้งจุดกำเนิด (origin) ไว้ที่มุมบนซ้าย (top-left) โดยที่ค่า Y จะเพิ่มขึ้นเมื่อเลื่อนลงด้านล่าง แต่ PDF ทำตรงกันข้าม HotPDF วัดจากมุมล่างซ้ายของหน้าเว็บในหน่วยพอยต์ (1/72 นิ้ว) โดยค่า Y จะเพิ่มขึ้นเมื่อคุณเลื่อนขึ้นบน จุดที่ Y := 720 จะอยู่ใกล้ด้านบนของหน้ากระดาษแบบ US Letter ซึ่งมีความสูง 792 พอยต์ และ Y := 50 จะอยู่ใกล้ด้านล่าง หากรูปวาดแรกของคุณกลับหัวกลับหาง นี่คือเหตุผล: โค้ดที่พอร์ตมาจากกราฟิกบนหน้าจอจะถือว่าทิศทางตรงกันข้ามและวาดยาวเลยขอบด้านล่าง
ข้อกำหนดเดียวกันนี้ยังใช้กับ TextOut ดังนั้นข้อความและรูปทรงจึงแชร์โมเดลความคิด (mental model) แบบเดียวกันเมื่อคุณทำความเข้าใจแล้ว วางแผนเลย์เอาต์โดยตัดสินใจว่าส่วนล่างสุดของแต่ละองค์ประกอบควรจะอยู่ที่ใด ไม่ใช่ส่วนบน แล้วส่วนที่เหลือจะตามมาเอง
เส้นทาง: MoveTo, LineTo, Stroke
เส้นขอบที่ลากผ่าน (stroked path) ก็เหมือนปากกาที่ยกขึ้น วางลง และลากเส้น MoveTo จะยกปากกาขึ้นและตั้งค่าจุดเริ่มต้นโดยไม่ทำเครื่องหมายใดๆ แต่ละ LineTo จะขยายเส้นทางปัจจุบันไปยังจุดใหม่ จะไม่มีอะไรปรากฏบนหน้ากระดาษจนกว่าคุณจะเรียกใช้ Stroke ซึ่งจะวาดเส้นทางที่สะสมไว้โดยใช้สีเส้นขอบและความกว้างเส้นปัจจุบัน จากนั้นจะล้างเส้นทางเพื่อให้ MoveTo ในครั้งถัดไปเริ่มต้นใหม่
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'DrawPaths.pdf';
Pdf.BeginDoc;
// Line width is in points and applies until you change it.
Pdf.CurrentPage.SetLineWidth(1.5);
Pdf.CurrentPage.SetRGBStrokeColor(clBlack);
// A horizontal rule near the top of the page (Y measured from bottom).
Pdf.CurrentPage.MoveTo(72, 720);
Pdf.CurrentPage.LineTo(523, 720);
Pdf.CurrentPage.Stroke; // commit the path; nothing drew before this
// A thicker connected polyline: three segments in one path.
Pdf.CurrentPage.SetLineWidth(3);
Pdf.CurrentPage.SetRGBStrokeColor(RGB(30, 90, 200));
Pdf.CurrentPage.MoveTo(72, 640);
Pdf.CurrentPage.LineTo(172, 690);
Pdf.CurrentPage.LineTo(272, 620);
Pdf.CurrentPage.LineTo(372, 680);
Pdf.CurrentPage.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
รายละเอียดสองประการนี้ช่วยประหยัดเวลาในการแก้จุดบกพร่อง (debug) ได้จริง ความกว้างของเส้นคือสถานะ ไม่ใช่อาร์กิวเมนต์: SetLineWidth ตั้งค่าเพียงครั้งเดียวและทุกๆ Stroke ที่ตามมาจะใช้ค่านั้นจนกว่าคุณจะเปลี่ยนมันอีกครั้ง ซึ่งเป็นเหตุผลที่เส้นพอลีไลน์ (polyline) ด้านบนหนากว่าเส้นตรง (rule) และเส้นทางจะถูกรีเซ็ตหลังจากแต่ละ Stroke ดังนั้น Stroke ที่ถูกลืมไปจะหมายความว่ารูปทรงเรขาคณิตที่คุณวางเอาไว้อย่างรอบคอบนั้นจะไม่ถูกวาดเลย หากรูปทรงใดหายไปจากผลลัพธ์ การเรียกใช้คำสั่งวาด (painting call) เป็นจุดแรกที่คุณควรมองหา
พิกัดใช้หน่วยเป็นพอยต์ และพอยต์เป็นจุดทศนิยม MoveTo และ LineTo ยอมรับค่า Single ดังนั้นเส้นบางๆ (hairline) ที่ 0.5 พอยต์ หรือตำแหน่งที่ 72.25 จึงถูกต้องและมีความหมาย โดยไม่ต้องปัดเศษทิ้งไปยังหน่วยเต็มที่ใกล้ที่สุด ความแม่นยำนี้มีความสำคัญในสองทิศทางที่ตรงข้ามกัน ความกว้างเส้นที่ต่ำกว่า 0.5 อาจวาดออกมาเป็นเส้นที่บางที่สุดเท่าที่เป็นไปได้ขึ้นอยู่กับอุปกรณ์ (device-dependent) ซึ่งหายไปบนหน้าจอและปรากฏขึ้นอีกครั้งเมื่อพิมพ์ ดังนั้นเส้นตรงที่ต้องการให้มองเห็นได้จึงควรใช้ความกว้างที่คุณตั้งใจตั้งค่าเองมากกว่าค่าเริ่มต้น ในอีกด้านหนึ่ง การสน็อป (snapping) กฎเกณฑ์ของตารางและเส้นกริด (gridlines) ไปยังพิกัดแบบเต็มพอยต์ จะช่วยป้องกันไม่ให้กริดหนาแน่นดูไม่สม่ำเสมอในจุดที่เส้นข้างเคียงปัดเศษต่างกัน ตัดสินใจระยะห่างของกริดในหน่วยพอยต์ล่วงหน้าแล้วเลย์เอาต์ส่วนที่เหลือจะได้รับค่าสืบทอดนี้ (inherits it)
รูปทรงที่เติมสี (Filled shapes) และสี (color)
รูปทรงพื้นฐานแบบปิดสามารถถูกเติมสีได้แทนที่จะวาดแค่เค้าโครง (outlined) Rectangle รับพารามิเตอร์เป็นตำแหน่งและขนาด ส่วน Circle รับจุดศูนย์กลางและรัศมี และทั้งคู่สามารถยืนยันการวาดด้วย Fill ซึ่งจะทาสีด้านในตามสีที่ถูกเติมปัจจุบัน หรือด้วย Stroke สำหรับเค้าโครงเท่านั้น สีพื้น (Fill color) และสีเส้นขอบ (stroke color) เป็นสถานะที่แยกจากกัน ตั้งค่าด้วย SetRGBFillColor และ SetRGBStrokeColor ซึ่งทั้งคู่รับค่าเป็น TColor เพียงค่าเดียว นั่นหมายความว่าคุณสามารถใช้ค่าคงที่ของสีของ Delphi และตัวช่วย RGB ซ้ำได้โดยตรง
// Rectangle(X, Y, Width, Height): X and Y are the lower-left corner.
Pdf.CurrentPage.SetRGBFillColor(RGB(220, 60, 60));
Pdf.CurrentPage.Rectangle(72, 500, 160, 90);
Pdf.CurrentPage.Fill;
// Circle(X, Y, Radius): X and Y are the center.
Pdf.CurrentPage.SetRGBFillColor(clNavy);
Pdf.CurrentPage.Circle(420, 545, 45);
Pdf.CurrentPage.Fill;
// Outline only: set a stroke color and a width, then Stroke.
Pdf.CurrentPage.SetLineWidth(2);
Pdf.CurrentPage.SetRGBStrokeColor(clBlack);
Pdf.CurrentPage.Rectangle(72, 400, 160, 60);
Pdf.CurrentPage.Stroke;
สังเกตรูปแบบอาร์กิวเมนต์ของ Rectangle ให้ดี มันเป็นตำแหน่งบวกขนาด X, Y, Width, Height ไม่ใช่มุมตรงข้ามสองมุม TCanvas.Rectangle ที่นักพัฒนา Delphi คุ้นเคยจะรับค่า (Left, Top, Right, Bottom) ดังนั้นความเคยชินอาจทำให้คุณป้อนมุมที่สองให้กับ HotPDF ในขณะที่มันคาดหวังความกว้างและความสูง ทำให้กล่องออกมาขนาดผิดเพี้ยน คู่ของ (X, Y) คือมุมล่างซ้าย สอดคล้องกับจุดเริ่มต้นของหน้ากระดาษ สำหรับวงกลม (X, Y) คือจุดศูนย์กลางและอาร์กิวเมนต์ที่สามคือรัศมีในหน่วยพอยต์
การเลือกสีที่ตัวอย่างดั้งเดิมทำผิดพลาด
ตัวอย่างเวอร์ชันเก่าใช้วิธีกำหนดสีแบบสุ่มด้วย Random($FFFFFF) ในทุก ๆ รูปทรง มันดูมีชีวิตชีวา แต่มันเป็นสัญชาตญาณที่ผิดสำหรับเอกสารที่สร้างจากโค้ด PDF ที่คุณสร้างจากโค้ดมักจะเป็นสิ่งที่คุณต้องการทดสอบด้วย และการสุ่มสีเติมแบบสุ่มทำให้ผลลัพธ์ที่ได้จากการรันแต่ละครั้งไม่สามารถเปรียบเทียบกันได้: การตรวจสอบส่วนต่างทีละไบต์ (byte-for-byte diff) กับไฟล์ที่รับรองว่าถูกต้อง (known-good file) จะล้มเหลวทุกครั้งโดยไม่มีเหตุผลอันควร เลือกสีที่ชัดเจน เมื่อคุณต้องการความหลากหลายในชุดรูปทรง ให้ขับเคลื่อนสีนั้นจากข้อมูลของคุณหรืออาเรย์พาเลตต์ (palette array) แบบคงที่ เพื่อให้ข้อมูลที่ป้อนเข้ามาเหมือนเดิมจะสร้างไฟล์ที่เหมือนเดิมเสมอ ความสามารถในการกำหนดได้แน่นอน (Determinism) มีค่ามากกว่าความแปลกใหม่เมื่อผลงานเคลื่อนเข้าสู่ไปป์ไลน์การปล่อยเวอร์ชัน (release pipeline)
การรวมโครงสร้างพื้นฐานเข้าด้วยกัน: กล่องคำอธิบายประกอบ
กล่องคำอธิบายประกอบที่ใช้จริงมักประกอบด้วยเส้นขอบ พื้นสี และข้อความ การเรียก Rectangle แล้วตามด้วย Stroke หรือ Fill จะสร้างรูปทรงก่อน จากนั้นจึงวางข้อความภายในโดยเผื่อระยะจากขอบให้คงที่ ลำดับนี้ทำให้พื้นหลังไม่ทับข้อความ และทำให้ตำแหน่งทั้งหมดตรวจสอบได้จากพิกัดมุมล่างซ้าย
เวกเตอร์ดรอว์อิ้งมีประโยชน์ที่ใดและไม่มีประโยชน์ที่ใด
หยิบจับเครื่องมือสร้างเส้นทางและรูปทรงเหล่านี้มาใช้เมื่อต้องวาดรูปทรงเรขาคณิต (generated geometry): เส้นกริดและแท่งแผนภูมิ, เส้นตรงของตารางใบแจ้งหนี้, กล่องคำอธิบายสัญลักษณ์ในแผนภาพ, สัญลักษณ์โลโก้ที่สร้างขึ้นจากเส้นทางหลาย ๆ เส้น สิ่งเหล่านี้สามารถปรับขนาดได้โดยไม่เบลอ และแทบไม่เพิ่มขนาดไฟล์เลย เพราะสี่เหลี่ยมผืนผ้ามีแค่ตัวเลขสองสามตัว แทนที่จะเป็นพิกเซลหลายพันพิกเซล ในอีกด้านหนึ่งก็เป็นความจริงเช่นกัน หากสิ่งที่คุณมีคือภาพถ่ายหรือภาพสกรีนช็อต (screenshot) ให้วาดมันออกมาเป็นภาพด้วย AddImage และ ShowImage แทน; การร่องรอยตามบิตแมปด้วยการเรียกเวกเตอร์จะไม่ช่วยให้อะไรดีขึ้น ส่วนเส้นโค้งที่ซับซ้อนก็อยู่นอกเหนือขอบเขตเช่นกัน องค์ประกอบพื้นฐานด้านบนคือส่วนที่เป็นเส้นตรง สี่เหลี่ยมผืนผ้า และวงกลม ซึ่งทำหน้าที่ส่วนใหญ่ของงานรายงานที่เกิดขึ้นจริง; สิ่งใดที่จำเป็นต้องใช้เส้นโค้งเบซิเยร์แบบอิสระ (freeform Bezier curves) ถือเป็นอีกส่วนหนึ่งของ API ที่แยกต่างหาก
นิสัยที่เหลือที่ควรเก็บไว้คือการตรวจสอบ (verification) เรขาคณิตที่ถูกสร้างขึ้นอาจทำงานผ่าน (pass) ในเครื่องของคุณแต่อาจล้มเหลว (fail) ในเครื่องของลูกค้าได้ ส่วนใหญ่เป็นผลมาจากการแทนที่แบบอักษร (font substitution) ในข้อความที่คุณผสมเข้าไป หรือข้อสันนิษฐานขนาดหน้ากระดาษที่ไม่เป็นความจริง เปิดไฟล์ที่เสร็จสมบูรณ์ในระดับการซูมสักสองสามระดับเพื่อยืนยันว่าขอบภาพยังคงคมชัด และตรวจสอบว่าแต่ละรูปทรงนั้นอยู่ในกล่องขอบ (margin box) ตามที่คุณตั้งใจไว้หรือไม่ ด้วยชุดสีที่กำหนดได้แน่นอน การตรวจสอบเหล่านั้นสามารถถูกทำให้เป็นอัตโนมัติ (automated) โดยเทียบกับ PDF อ้างอิงได้ แทนที่จะใช้แค่สายตาในการตรวจสอบ (eyeballed)
เส้นโค้ง เส้นประ และความโปร่งใสโดยสังเขป
งานวาดที่ซับซ้อนขึ้นสามารถต่อยอดจากเส้นทางเดิมด้วยเส้นโค้งเบซิเยร์ การกำหนดรูปแบบเส้นประ และการตั้งค่าความโปร่งใสของกราฟิกสเตต แต่ควรกำหนดค่าเหล่านี้อย่างชัดเจนก่อนวาดและคืนค่าหลังจบองค์ประกอบ เพื่อไม่ให้รูปทรงถัดไปได้รับความกว้างเส้นหรือ alpha ที่ไม่ตั้งใจ
คำสั่ง MoveTo, LineTo, Stroke, Fill และคำสั่งเกี่ยวกับสีที่แสดงไว้นี้เป็นส่วนหนึ่งของ HotPDF Component สำหรับ Delphi และ C++Builder