HotPDF วาดกราฟิกเวกเตอร์ด้วยการสร้างเส้นทางบนหน้าปัจจุบัน แล้วจึงสั่งให้ระบายมันออกมา ไม่มีขั้นตอนบิตแมปคั่นกลาง เส้นที่คุณวาดด้วย MoveTo และ LineTo จบลงเป็นตัวดำเนินการเส้นทางของ PDF ในสตรีมเนื้อหา มันจึงยังเป็นเวกเตอร์แท้: คมที่ซูม 50% คมที่ซูม 1600% และกินพื้นที่เพียงเศษเสี้ยวของเวอร์ชันที่แปลงเป็นภาพแรสเตอร์ สำหรับแผนภาพ เส้นตาราง แกนกราฟ และของประดับในฟอร์ม นั่นคือสิ่งที่คุณต้องการพอดี และ API เบื้องหลังก็เล็กพอจะเรียนจบในการนั่งครั้งเดียว
พื้นผิวการวาดทั้งหมดอยู่บน THotPDF.CurrentPage ระหว่าง BeginDoc กับ EndDoc คุณตั้งสีและความหนาเส้นบนออบเจกต์หน้านั้น วางรูปทรงเรขาคณิตลงไป แล้วเรียกตัวดำเนินการระบายเพื่อยืนยันผล พื้นฐานสี่ตัวที่คุณจะใช้บ่อยที่สุดคือ MoveTo และ LineTo สำหรับเส้นทางอิสระ Rectangle สำหรับกล่อง Circle สำหรับวงกลมทึบ และตัวดำเนินการระบายสองตัวคือ Stroke กับ Fill
ระบบพิกัดวัดจากมุมซ้ายล่าง
นี่คือเรื่องเดียวที่ทำให้ทุกคนที่มาจากฝั่ง VCL สะดุด TCanvas ที่คุณใช้วาดคอนโทรลวางจุดกำเนิดไว้ที่มุมซ้ายบน โดยค่า Y เพิ่มขึ้นเมื่อลงล่าง PDF ทำตรงกันข้าม HotPDF วัดจากมุมซ้ายล่างของหน้าเป็นหน่วยพอยต์ (1/72 นิ้ว) โดยค่า Y เพิ่มขึ้นเมื่อขยับขึ้นบน จุดที่ Y := 720 อยู่ใกล้ขอบบนของหน้าขนาด US Letter ซึ่งสูง 792 พอยต์ ส่วน Y := 50 อยู่ใกล้ขอบล่าง ถ้าภาพวาดชิ้นแรกของคุณออกมากลับหัวในแนวตั้ง นี่คือสาเหตุ: โค้ดที่ย้ายมาจากกราฟิกบนจอสมมติทิศทางผิด แล้ววิ่งเลยขอบล่างออกไป
ธรรมเนียมเดียวกันนี้ควบคุม TextOut ด้วย ข้อความกับรูปทรงจึงใช้แบบจำลองความคิดชุดเดียวกันเมื่อคุณซึมซับมันแล้ว วางแผนเลย์เอาต์โดยตัดสินว่าขอบล่างของแต่ละองค์ประกอบอยู่ตรงไหน ไม่ใช่ขอบบน แล้วที่เหลือจะตามมาเอง
เส้นทาง: MoveTo, LineTo, Stroke
เส้นทางที่ถูก stroke คือปากกาที่ยกขึ้น วางลง แล้วลาก MoveTo ยกปากกาขึ้นและกำหนดจุดเริ่มโดยไม่ทิ้งรอยอะไรไว้ LineTo แต่ละครั้งต่อเส้นทางปัจจุบันไปยังจุดใหม่ ไม่มีอะไรปรากฏบนหน้าจนกว่าคุณจะเรียก Stroke ซึ่งวาดเส้นทางที่สะสมไว้ด้วยสี stroke และความหนาเส้นปัจจุบัน แล้วล้างเส้นทางทิ้ง เพื่อให้ MoveTo ครั้งถัดไปเริ่มใหม่หมด
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'DrawPaths.pdf';
Pdf.BeginDoc;
// ความหนาเส้นมีหน่วยเป็นพอยต์ และมีผลจนกว่าคุณจะเปลี่ยนมัน
Pdf.CurrentPage.SetLineWidth(1.5);
Pdf.CurrentPage.SetRGBStrokeColor(clBlack);
// เส้นคั่นแนวนอนใกล้ขอบบนของหน้า (Y วัดจากด้านล่าง)
Pdf.CurrentPage.MoveTo(72, 720);
Pdf.CurrentPage.LineTo(523, 720);
Pdf.CurrentPage.Stroke; // ยืนยันเส้นทาง ก่อนหน้านี้ยังไม่มีอะไรถูกวาด
// เส้นหักหนากว่าที่ต่อกัน: สามท่อนในเส้นทางเดียว
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;
รายละเอียดสองข้อช่วยประหยัดเวลาไล่บั๊กจริง ๆ ความหนาเส้นเป็นสถานะ ไม่ใช่อาร์กิวเมนต์: SetLineWidth ตั้งค่าครั้งเดียว แล้วทุก Stroke ถัดไปใช้ค่านั้นจนกว่าคุณจะเปลี่ยนอีกครั้ง นั่นคือเหตุผลที่เส้นหักด้านบนหนากว่าเส้นคั่น อีกข้อคือเส้นทางถูกรีเซ็ตหลัง Stroke ทุกครั้ง การลืม Stroke จึงแปลว่ารูปทรงที่คุณอุตส่าห์วางไว้อย่างประณีตจะไม่ถูกเรนเดอร์เลย ถ้ารูปทรงหายไปจากผลลัพธ์ ให้ดูที่การเรียกระบายเป็นที่แรก
พิกัดมีหน่วยเป็นพอยต์ และพอยต์เป็นค่าทศนิยมได้ MoveTo และ LineTo รับค่าชนิด Single เส้นบางระดับ 0.5 พอยต์หรือตำแหน่งที่ 72.25 จึงถูกต้องและมีความหมาย ไม่ถูกปัดทิ้งไปที่จำนวนเต็มที่ใกล้ที่สุด ความละเอียดนั้นสำคัญในสองทิศทางที่ตรงข้ามกัน ความหนาเส้นที่ต่ำกว่าราว 0.5 อาจถูกเรนเดอร์เป็นเส้นบางที่สุดเท่าที่อุปกรณ์ทำได้ ซึ่งหายไปบนจอแล้วกลับมาโผล่ตอนพิมพ์ เส้นคั่นที่ต้องมองเห็นจึงควรมีความหนาที่คุณตั้งอย่างตั้งใจ ไม่ใช่ค่าเริ่มต้น ในอีกด้านหนึ่ง การดึงเส้นตารางและเส้นกริดให้ตรงกับพิกัดจำนวนเต็มพอยต์ ช่วยไม่ให้กริดที่หนาแน่นดูเหลื่อมกันเล็กน้อยตรงที่เส้นข้างเคียงปัดค่าไปคนละทาง ตัดสินระยะกริดเป็นพอยต์ตั้งแต่ต้น แล้วเลย์เอาต์ที่เหลือจะสืบทอดมันไปเอง
รูปทรงที่ระบายสีและเรื่องสี
รูปทรงพื้นฐานแบบปิดสามารถระบายทึบแทนการวาดเส้นขอบได้ Rectangle รับตำแหน่งกับขนาด Circle รับจุดศูนย์กลางกับรัศมี และทั้งคู่ยืนยันผลด้วย Fill ซึ่งระบายพื้นที่ภายในด้วยสี fill ปัจจุบัน หรือด้วย Stroke เพื่อเอาเฉพาะเส้นขอบ สี fill กับสี stroke เป็นสถานะคนละชิ้น ตั้งด้วย SetRGBFillColor และ SetRGBStrokeColor ซึ่งทั้งคู่รับค่า TColor ตัวเดียว นั่นแปลว่าคุณใช้ค่าคงที่สีของ Delphi และตัวช่วย RGB ได้โดยตรง
// Rectangle(X, Y, Width, Height): X และ Y คือมุมซ้ายล่าง
Pdf.CurrentPage.SetRGBFillColor(RGB(220, 60, 60));
Pdf.CurrentPage.Rectangle(72, 500, 160, 90);
Pdf.CurrentPage.Fill;
// Circle(X, Y, Radius): X และ Y คือจุดศูนย์กลาง
Pdf.CurrentPage.SetRGBFillColor(clNavy);
Pdf.CurrentPage.Circle(420, 545, 45);
Pdf.CurrentPage.Fill;
// เอาเฉพาะเส้นขอบ: ตั้งสี stroke กับความหนา แล้วเรียก 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 ที่คุณสร้างจากโค้ดมักเป็นสิ่งที่คุณอยากทดสอบด้วย และสี fill แบบสุ่มทำให้ผลลัพธ์เทียบข้ามการรันไม่ได้เลย: การ diff แบบไบต์ต่อไบต์กับไฟล์อ้างอิงที่รู้ว่าถูกต้องจะล้มเหลวทุกครั้งโดยไม่มีเหตุผลจริง ให้เลือกสีอย่างชัดเจน เมื่อคุณต้องการความหลากหลายข้ามชุดรูปทรง ให้ขับมันจากข้อมูลของคุณเองหรือจากอาร์เรย์จานสีที่ตายตัว เพื่อให้อินพุตเดิมผลิตไฟล์เดิมเสมอ ความเป็นระบบที่คาดเดาผลได้มีค่ามากกว่าความแปลกใหม่ เมื่อไฟล์ผลลัพธ์ต้องเดินผ่านไปป์ไลน์การปล่อยรุ่น
ประกอบพื้นฐานเข้าด้วยกัน: กล่องคำอธิบายชี้จุด
พื้นฐานแต่ละตัวเรียบง่ายในตัวเอง ผลตอบแทนจะเห็นชัดเมื่อหยิบมาสองสามตัวประกอบกันเป็นสิ่งที่รายงานต้องใช้จริง กล่องคำอธิบายชี้จุด ซึ่งเป็นกล่องพร้อมคำอธิบายที่ชี้ไปยังภาพประกอบ ใช้ทุกอย่างที่กล่าวมาแล้ว: สี่เหลี่ยมที่ระบายทึบพร้อมเส้นขอบ เส้นชี้ที่ถูก stroke จุดกลมที่ยึดปลายเส้นชี้ และข้อความที่จัดวางอยู่ในกล่องด้วยพิกัดมุมซ้ายล่างชุดเดียวกับที่รูปทรงใช้ FillAndStroke พิสูจน์คุณค่าของมันตรงนี้ ด้วยการระบายทั้งพื้นที่ภายในและเส้นขอบของเส้นทางเดียวในการยืนยันครั้งเดียว แทนที่จะสร้างสี่เหลี่ยมนั้นสองรอบ
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'Callout.pdf';
Pdf.BeginDoc;
// 1. กล่อง: พื้นสีอ่อนบวกเส้นขอบที่มองเห็น หนึ่งเส้นทาง หนึ่งการยืนยัน
// Rectangle คือมุมซ้ายล่างบวกขนาด โดย Y วัดจากด้านล่าง
Pdf.CurrentPage.SetRGBFillColor(RGB(255, 244, 214)); // แผงสีอำพันอ่อน
Pdf.CurrentPage.SetRGBStrokeColor(RGB(180, 130, 40)); // ขอบสีเข้มกว่า
Pdf.CurrentPage.SetLineWidth(1);
Pdf.CurrentPage.Rectangle(90, 600, 240, 70);
Pdf.CurrentPage.FillAndStroke;
// 2. เส้นชี้: หนึ่งท่อนที่ stroke จากขอบกล่องลงล่าง
// ไปยังสิ่งที่กำลังถูกอธิบาย
Pdf.CurrentPage.SetLineWidth(1.5);
Pdf.CurrentPage.MoveTo(90, 615); // ขอบซ้ายของกล่อง
Pdf.CurrentPage.LineTo(66, 546);
Pdf.CurrentPage.Stroke;
// 3. จุดกลมที่ระบายทึบยึดเส้นชี้ไว้กับเป้าหมาย
Pdf.CurrentPage.SetRGBFillColor(RGB(180, 130, 40));
Pdf.CurrentPage.Circle(64, 542, 3);
Pdf.CurrentPage.Fill;
// 4. ป้ายกำกับ วางตำแหน่งเทียบกับมุมซ้ายล่างของกล่อง
// ข้อความกับรูปทรงใช้ระบบพิกัดเดียวกัน ระยะเยื้องจึงเป็น
// เลขคณิตธรรมดาเทียบกับ (90, 600)
Pdf.CurrentPage.SetFont('Arial', [fsBold], 10);
Pdf.CurrentPage.TextOut(102, 645, 0, 'Check this total');
Pdf.CurrentPage.SetFont('Arial', [], 9);
Pdf.CurrentPage.TextOut(102, 628, 0, 'The rounding rule changed in the');
Pdf.CurrentPage.TextOut(102, 616, 0, 'June release; verify against v2.1');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
สังเกตว่าภาพประกอบนี้ต้องจัดการสถานะน้อยเพียงใด สี fill สี stroke และความหนาเส้นถูกตั้งทันทีก่อนรูปทรงที่ใช้มัน แต่ละบล็อกของภาพวาดจึงอ่านได้เป็นหน่วยที่เบ็ดเสร็จในตัว และสลับลำดับหรือแยกออกไปเป็นตัวช่วยได้โดยไม่ลากสถานะแอบแฝงติดไปด้วย ห่อโค้ดนี้ไว้ในโพรซีเจอร์ที่รับจุดยึดกับสตริงเข้าไป แล้วคุณก็ได้คำอธิบายประกอบแผนภาพที่ใช้ซ้ำได้ในราคาสี่สิบบรรทัด
จุดที่การวาดเวกเตอร์คุ้มค่า และจุดที่ไม่คุ้ม
หยิบการเรียกเส้นทางและรูปทรงเหล่านี้มาใช้เมื่อรูปทรงเรขาคณิตถูกสร้างขึ้นด้วยโปรแกรม: เส้นกริดและแท่งของแผนภูมิ เส้นตารางของใบแจ้งหนี้ กล่องคำอธิบายบนแผนภาพ หรือเครื่องหมายโลโก้ที่แสดงได้ด้วยเส้นทางไม่กี่เส้น ทั้งหมดนี้ขยายขนาดได้โดยไม่เบลอ และแทบไม่เพิ่มขนาดไฟล์ เพราะสี่เหลี่ยมหนึ่งรูปคือตัวเลขไม่กี่ตัว ไม่ใช่พิกเซลหลายพันจุด อีกด้านหนึ่งก็ต้องพูดตามตรง ถ้าสิ่งที่คุณมีจริง ๆ คือภาพถ่ายหรือภาพหน้าจอ ให้วาดมันเป็นภาพด้วย AddImage และ ShowImage แทน การลอกลายบิตแมปด้วยการเรียกเวกเตอร์ไม่ได้อะไรกลับมาเลย ท่อนเส้นตรง สี่เหลี่ยม และวงกลมข้างต้นรับงานรายงานจริงไว้ได้เป็นส่วนใหญ่ ส่วนของเสริมสามอย่างที่นักพัฒนามักถามถึงต่อ ได้แก่ เส้นโค้ง ลายเส้นประ และความโปร่งใส ก็อยู่บนออบเจกต์หน้าตัวเดียวกัน
เส้นโค้ง เส้นประ และความโปร่งใสอย่างย่อ
เส้นโค้งอิสระต่อยอดจากกลไกเส้นทางเดิมที่คุณมีอยู่แล้ว CurveToC(X1, Y1, X2, Y2, X3, Y3) ต่อท่อนเบซิเยร์กำลังสามจากจุดปัจจุบันไปยัง (X3, Y3) โดยโค้งเข้าหาจุดควบคุมสองจุด ส่วนตัวย่อ CurveToV และ CurveToY ครอบคลุมกรณีที่จุดควบคุมจุดหนึ่งทับกับจุดปลาย เส้นทางหนึ่งเส้นผสมท่อน LineTo กับ CurveToC ได้อย่างอิสระก่อนที่ Stroke หรือ Fill ครั้งเดียวจะยืนยันมัน ซึ่งเป็นวิธีสร้างมุมโค้งมนและเส้นกราฟที่ลื่นไหล
เส้นประก็เป็นสถานะ เหมือนความหนาเส้นเป๊ะ ๆ SetDash([3, 3], 0) เปลี่ยนทุกการ stroke ถัดไปให้เป็นลายสามพอยต์ติดสามพอยต์เว้น โดยอาร์เรย์ระบุความยาวช่วงติดและช่วงเว้นเป็นพอยต์ ส่วนอาร์กิวเมนต์ตัวที่สองเลื่อนเฟสว่ารอบเริ่มตรงไหน ส่วน NoDash คืนปากกากลับเป็นเส้นทึบ ตั้งค่ามัน stroke เส้นกริดที่ต้องการ แล้วรีเซ็ตก่อนเส้นทึบเส้นถัดไป ไม่อย่างนั้นเส้นประจะแพร่ไปติดทุกอย่างที่ตามมาแบบเงียบ ๆ
ความโปร่งใสทำงานผ่านสถานะกราฟิกที่มีชื่อ ไม่ใช่ผ่านอาร์กิวเมนต์สี เพราะค่าอัลฟาใน PDF เป็นคุณสมบัติของพจนานุกรมสถานะกราฟิก ลงทะเบียนสถานะหนึ่งชุดกับเอกสารด้วย RegisterExtGState โดยส่งค่าอัลฟาของ fill และอัลฟาของ stroke ระหว่าง 0 ถึง 1 แล้วนำชื่อที่มันคืนมาไปใช้กับ CurrentPage.SetGraphicsState ตั้งแต่จุดนั้นไป การ fill และ stroke จะระบายที่ค่าความทึบที่ลงทะเบียนไว้ พิธีกรรมนี้หนักกว่าตัวตั้งค่าสี และคุ้มค่าตั้งแต่ครั้งแรกที่แถบไฮไลต์ต้องวางทับข้อความโดยไม่บังมัน
นิสัยที่เหลือซึ่งควรรักษาไว้คือการตรวจสอบผล รูปทรงที่สร้างด้วยโปรแกรมอาจผ่านบนเครื่องคุณแต่พังบนเครื่องลูกค้า มักเป็นเพราะการแทนที่ฟอนต์ในข้อความที่คุณผสมเข้าไป หรือเพราะข้อสมมติเรื่องขนาดหน้าที่ไม่เป็นจริง เปิดไฟล์ที่เสร็จแล้วดูที่ระดับซูมหลายระดับเพื่อยืนยันว่าขอบยังคม และตรวจว่าทุกรูปทรงตกอยู่ในกรอบระยะขอบที่คุณตั้งใจ เมื่อใช้ชุดสีที่คาดเดาผลได้ การตรวจนั้นทำอัตโนมัติเทียบกับ PDF อ้างอิงได้ แทนการนั่งดูด้วยตา
การเรียก MoveTo, LineTo, Stroke, Fill และการตั้งสีที่แสดงไว้ที่นี่เป็นส่วนหนึ่งของ HotPDF Delphi Component สำหรับ Delphi และ C++Builder