วิธีทำงานกับกราฟิก PDF ใน C++

วิธีทำงานกับกราฟิก PDF ใน C++

Aspose.PDF FOSS สำหรับ C++ มี API เวกเตอร์กราฟิกขนาดเล็กสำหรับวาดรูปทรงอิสระโดยตรงบนหน้า PDF — แยกจากอุปกรณ์เรสเตอร์เรนเดอร์ของไลบรารี (BmpDevice, JpegDevice, TiffDevice) ซึ่งแปลงเนื้อหาหน้าที่มีอยู่เป็นภาพ คู่มือฉบับนี้จะอธิบายขั้นตอนการสร้างคอนเทนเนอร์ Graph, เพิ่มรูปทรง Circle, Ellipse, และ Line เข้าไป, ปรับสไตล์ของรูปทรงเหล่านั้นด้วย GraphInfo, และวาง Graph ที่เสร็จแล้วลงบนหน้า ไลบรารีจะถูกเพิ่มเข้าโครงการผ่าน CMake.

คู่มือแบบขั้นตอนต่อขั้นตอน

ขั้นตอนที่ 1: ติดตั้งแพ็กเกจ

เพิ่มไลบรารีเป็นไดเรกทอรีย่อยของ CMake และลิงก์กับเป้าหมาย aspose_pdf_foss:

add_subdirectory(aspose.pdf-foss-for-cpp)
target_link_libraries(your_app PRIVATE aspose_pdf_foss)

ตรวจสอบว่า toolchain และ header ถูก resolve โดยการคอมไพล์โปรแกรมขนาดเล็ก:

#include <aspose/pdf/document.hpp>
#include <iostream>

int main() {
    Aspose::Pdf::Document doc;
    std::cout << "Linked OK — pages: " << doc.Pages().Count() << "\n";
}

การสร้างที่สำเร็จและพิมพ์ pages: 0 ยืนยันว่าไลบรารีถูกลิงก์อย่างถูกต้อง.


ขั้นตอนที่ 2: นำเข้าคลาสที่จำเป็น

รวมไฟล์หัวเรื่องสำหรับเอกสาร, ตัวคอนเทนเนอร์รูปทรง, รูปทรงเชิงรูปธรรมสามแบบ, และประเภทการจัดรูปแบบของพวกมัน:

#include <aspose/pdf/document.hpp>
#include <aspose/pdf/drawing/graph.hpp>
#include <aspose/pdf/drawing/circle.hpp>
#include <aspose/pdf/drawing/ellipse.hpp>
#include <aspose/pdf/drawing/line.hpp>
#include <aspose/pdf/graph_info.hpp>
#include <aspose/pdf/border_info.hpp>
#include <aspose/pdf/color.hpp>

using namespace Aspose::Pdf;
using namespace Aspose::Pdf::Drawing;

Graph, Shape, Circle, Ellipse และ Line อยู่ในเนมสเปซ Aspose::Pdf::Drawing; GraphInfo, BorderInfo และ Color อยู่โดยตรงภายใต้ Aspose::Pdf.


ขั้นตอนที่ 3: สร้างกราฟและเพิ่มลงในหน้า

Graph เป็นคอนเทนเนอร์ที่วางตำแหน่งตัวเองบนหน้าโดยผ่าน Left(), Top(), Width() และ Height(), และเก็บรูปทรงที่วาดภายในผ่าน Shapes(). เมื่อกำหนดค่าแล้ว, มันจะถูกวางบนหน้าโดยผ่าน Page.Paragraphs().Add(), ซึ่งเป็นคอลเลกชันเดียวกันที่ใช้สำหรับเนื้อหาอื่นบนหน้า:

Document doc;
Page page = doc.Pages().Add();

auto graph = std::make_shared<Graph>();
graph->Left(50.0);
graph->Top(50.0);
graph->Width(400.0);
graph->Height(400.0);

page.Paragraphs().Add(graph);

ขั้นตอนที่ 4: วาดวงกลมและวงรี

Circle อธิบายเรขาคณิตของมันด้วย PosX(), PosY() และ Radius(). Ellipse ใช้กล่องขอบเขตแทน — Left(), Bottom(), Width() และ Height(). ทั้งสองจะถูกเพิ่มเข้าไปใน Graph โดยการต่อท้ายพวกมันเป็นตัวชี้ Shape ไปยังเวกเตอร์ที่ Shapes() คืนค่า:

auto circle = std::make_unique<Circle>();
circle->PosX(150.0);
circle->PosY(150.0);
circle->Radius(75.0);
graph->Shapes().push_back(std::move(circle));

auto ellipse = std::make_unique<Ellipse>();
ellipse->Left(20.0);
ellipse->Bottom(20.0);
ellipse->Width(120.0);
ellipse->Height(60.0);
graph->Shapes().push_back(std::move(ellipse));

รูปทรงทั้งสองสืบทอด CheckBounds(containerWidth, containerHeight) จาก Shape, ซึ่งรายงานว่ารูปทรงปัจจุบันของมันมีเรขาคณิตพอดีภายในคอนเทนเนอร์ที่มีขนาดกำหนดหรือไม่:

bool circleFits = circle->CheckBounds(graph->Width(), graph->Height());

ขั้นตอน 5: วาดเส้น

Line จัดเก็บเรขาคณิตของมันแตกต่างกัน: PositionArray() มี std::vector<float> แบนของพิกัด — คู่ x, y ต่อจุด — แทนคุณสมบัติที่ตั้งชื่อไว้. ส่วนเส้นที่มีสองจุดต้องการค่าสี่ค่า: x เริ่มต้น, y เริ่มต้น, x สิ้นสุด, y สิ้นสุด.

auto line = std::make_unique<Line>();
line->PositionArray({0.0f, 0.0f, 400.0f, 400.0f});
graph->Shapes().push_back(std::move(line));

ขั้นตอน 6: สไตล์รูปร่างด้วย GraphInfo และขอบของคอนเทนเนอร์

เส้นขอบและการเติมสีของแต่ละรูปมาจาก GraphInfo ซึ่งอ่านและตั้งค่าผ่าน Shape.GraphInfo() / GraphInfo(value). มันเปิดเผยความกว้างของเส้นขอบ (LineWidth(), ค่าเริ่มต้น 1.0f), สีเส้นขอบและสีเติม (Color(), FillColor()), และรูปแบบเส้นประ (DashArray(), DashPhase()):

GraphInfo stroke;
stroke.LineWidth(2.0f);
stroke.Color(Color::FromRgb(1.0, 0.0, 0.0));
stroke.FillColor(Color::FromRgb(1.0, 0.9, 0.9));

คอนเทนเนอร์ Graph เองมี BorderInfo ที่ตั้งค่าผ่าน Border(), ซึ่งจัดกลุ่ม GraphInfo หนึ่งรายการต่อขอบ (Left(), Right(), Top(), Bottom()) พร้อมกับ RoundedBorderRadius() สำหรับกรอบของคอนเทนเนอร์เอง:

BorderInfo border{BorderSide::All, 1.5f, Color::Black()};
border.RoundedBorderRadius(6.0);
graph->Border(border);

ขั้นตอน 7: บันทึกเอกสาร

doc.Save("graphics.pdf");

ปัญหาทั่วไปและวิธีแก้ไข

รูปร่างที่ฉันสร้างขึ้นไม่เคยปรากฏบนหน้า

การสร้าง Circle, Ellipse หรือ Line เพียงอย่างเดียวไม่มีผลจนกว่าจะถูกต่อเข้ากับเวกเตอร์ที่ส่งกลับโดย Graph ของเจ้าของที่มี Shapes(), และ Graph นั้นถูกเพิ่มลงในหน้าโดยผ่าน Page.Paragraphs().Add(). ทั้งสามขั้นตอน — สร้าง, ต่อ, เพิ่ม Graph ลงในหน้า — จำเป็นต้องทำครบ.

รูปร่างแสดงผลในตำแหน่งที่ผิด

Graph.Left() และ Graph.Top() กำหนดตำแหน่งของคอนเทนเนอร์บนหน้า; คุณสมบัติของรูปร่าง (Circle.PosX()/PosY(), Ellipse.Left()/Bottom(), พิกัด Line.PositionArray()) บรรยายรูปทรงภายในคอนเทนเนอร์นั้น. ตรวจสอบทั้งการวางคอนเทนเนอร์และพิกัดของรูปร่างเองเมื่อผลลัพธ์ดูเบี่ยงเบน.

CheckBounds returns false unexpectedly

CheckBounds(containerWidth, containerHeight) ตรวจสอบรูปทรงของรูปร่างเองเทียบกับมิติที่ส่งเข้ามา — ปกติจะเป็น Width()/Height() ของ Graph ที่เป็นเจ้าของ, ไม่ใช่กล่องสื่อเต็มของหน้า. การส่งมิติผิดทำให้ได้ผลลัพธ์เป็นลบเท็จแม้ว่ารูปร่างจะพอดีภายใน Graph จริงๆ.

A Line ไม่ปรากฏ หรือปรากฏเป็นจุดเดียว

PositionArray() ต้องมีอย่างน้อยสองคู่ (x, y) — สี่ค่า float — เพื่ออธิบายส่วนที่มองเห็นได้. เวกเตอร์ที่สั้นกว่าจะไม่สร้างเส้น.

Confusing Circle with AnnotationType::Circle or CircleAnnotation

ไลบรารียังมี API คำอธิบายประกอบ PDF แยกต่างหาก โดยที่ AnnotationType::Circle และ CircleAnnotation แทนการทำเครื่องหมายรูปวงกลม Circle คลาสที่อธิบายไว้ในคู่มือนี้ไม่มีความเกี่ยวข้อง — มันอยู่ใน Aspose::Pdf::Drawing และวาดโดยตรงลงในเนื้อหาหน้าแทนการเพิ่มอ็อบเจกต์คำอธิบาย.

คำถามที่พบบ่อย

อะไรคือความแตกต่างระหว่าง GraphInfo และ BorderInfo?

GraphInfo กำหนดสไตล์ของเส้นเดี่ยว — ความกว้างของเส้น, สี, สีเติม, และรูปแบบเส้นประ — และใช้ได้ทั้งบนรูปร่างเดี่ยว (ผ่าน Shape.GraphInfo()) และบนขอบแต่ละด้านภายใน BorderInfo. BorderInfo จัดกลุ่มค่าทั้งสี่ของ GraphInfo (Left(), Right(), Top(), Bottom()) พร้อมกับ RoundedBorderRadius() เพื่ออธิบายกรอบขอบของคอนเทนเนอร์ Graph ของตนเอง.

ฉันสามารถเติมรูปร่างด้วยสีทึบได้ไหม?

ใช่. GraphInfo.FillColor() กำหนดสีเติมที่ใช้เมื่อรูปร่างถูกวาดผ่าน Graph ของมันเอง.

Circle เก็บ bounding box เช่นเดียวกับ Ellipse หรือไม่?

โหนด Circle เปิดเผย PosX(), PosY() และ Radius(); Ellipse เปิดเผย Left(), Bottom(), Width() และ Height(). พวกเขาไม่มีการแสดงผลเรขาคณิตร่วมกันนอกเหนือจากฐาน Shape แบบนามธรรมและสไตลิ่ง GraphInfo.

กราฟหนึ่งสามารถเก็บมากกว่าหนึ่งรูปทรงได้หรือไม่?

ใช่. Graph.Shapes() คืนค่าชุดทั้งหมดของออบเจ็กต์ Shape ที่วางอยู่ภายใน Graph นั้น — วงกลม, รูปรี, และเส้นสามารถเพิ่มลงในคอนเทนเนอร์เดียวกันได้ทั้งหมด.

นี่เหมือนกับการเรนเดอร์หน้าเป็นภาพหรือไม่?

ไม่. BmpDevice, JpegDevice และ TiffDevice ทำการแปลงข้อมูลหน้าเดิมเป็นรูปแบบภาพแบบแรสเตอร์. Graph, Shape, Circle, Ellipse, และ Line สร้างเรขาคณิตเวกเตอร์ใหม่โดยตรงในเนื้อหา PDF ขณะที่เอกสารถูกสร้างหรือแก้ไข.

ดูเพิ่มเติม

 ภาษาไทย