Cách làm việc với đồ họa PDF trong C++

Cách làm việc với đồ họa PDF trong C++

Aspose.PDF FOSS cho C++ bao gồm một API đồ họa vector nhỏ để vẽ các hình dạng tự do trực tiếp trên trang PDF — tách biệt khỏi các thiết bị raster-rendering của thư viện (BmpDevice, JpegDevice, TiffDevice), những thiết bị này chuyển nội dung trang hiện có thành hình ảnh. Hướng dẫn này mô tả cách tạo một container Graph, thêm các hình Circle, Ellipse, và Line vào đó, tạo kiểu cho các hình bằng GraphInfo, và đặt Graph đã hoàn thiện lên một trang. Thư viện được thêm vào dự án qua CMake.

Hướng dẫn từng bước

Bước 1: Cài đặt gói

Thêm thư viện như một thư mục con CMake và liên kết với target aspose_pdf_foss:

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

Xác minh chuỗi công cụ và các header được giải quyết bằng cách biên dịch một chương trình tối thiểu:

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

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

Một bản dựng thành công in ra pages: 0 xác nhận thư viện đã được liên kết đúng cách.


Bước 2: Nhập các lớp cần thiết

Bao gồm các tiêu đề cho tài liệu, bộ chứa hình dạng, ba hình dạng cụ thể, và các kiểu định dạng của chúng:

#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, và Line nằm trong không gian tên Aspose::Pdf::Drawing; GraphInfo, BorderInfo, và Color nằm trực tiếp dưới Aspose::Pdf.


Bước 3: Tạo một Đồ thị và Thêm Nó vào Trang

Graph là một bộ chứa định vị bản thân trên trang thông qua Left(), Top(), Width(), và Height(), và chứa các hình dạng được vẽ bên trong nó thông qua Shapes(). Khi đã được cấu hình, nó được đặt trên trang thông qua Page.Paragraphs().Add(), cùng một bộ sưu tập được sử dụng cho nội dung trang khác:

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);

Bước 4: Vẽ một Hình tròn và một Hình bầu dục

Circle mô tả hình học của nó bằng PosX(), PosY(), và Radius(). Ellipse sử dụng một hộp bao thay vì — Left(), Bottom(), Width(), và Height(). Cả hai đều được thêm vào Graph bằng cách nối chúng, dưới dạng con trỏ Shape, vào vector được trả về bởi 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));

Cả hai hình dạng đều kế thừa CheckBounds(containerWidth, containerHeight) từ Shape, cái mà báo cáo liệu hình học hiện tại của hình dạng có vừa trong một bộ chứa có kích thước cho trước hay không:

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

Bước 5: Vẽ một Đường

Line lưu trữ hình học của nó theo cách khác: PositionArray() chứa một std::vector<float> phẳng của các tọa độ — một cặp x, y cho mỗi điểm — thay vì các thuộc tính có tên. Một đoạn có hai điểm cần bốn giá trị: x bắt đầu, y bắt đầu, x kết thúc, y kết thúc.

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

Bước 6: Định dạng Các Hình dạng bằng GraphInfo và Viền Bộ chứa

Màu viền và màu nền của mỗi hình được lấy từ một GraphInfo, đọc và đặt thông qua Shape.GraphInfo() / GraphInfo(value). Nó cung cấp độ rộng viền (LineWidth(), mặc định 1.0f), màu viền và màu nền (Color(), FillColor()), và mẫu gạch ngang (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));

Bản thân container Graph mang một BorderInfo, được đặt thông qua Border(), mà nhóm một GraphInfo cho mỗi cạnh (Left(), Right(), Top(), Bottom()) cộng với một RoundedBorderRadius() cho khung của chính container:

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

Bước 7: Lưu Tài liệu

doc.Save("graphics.pdf");

Các Vấn đề Thông thường và Cách Khắc phục

Một hình tôi tạo ra không bao giờ xuất hiện trên trang

Việc tạo một Circle, Ellipse, hoặc Line riêng lẻ không có hiệu lực cho đến khi nó được nối vào vector được trả về bởi Shapes() của Graph sở hữu, và Graph đó được thêm vào một trang thông qua Page.Paragraphs().Add(). Ba bước đều cần thiết — tạo, nối, thêm Graph vào trang —.

Các hình được hiển thị ở vị trí sai

Graph.Left() và Graph.Top() định vị container trên trang; các thuộc tính hình (Circle.PosX()/PosY(), Ellipse.Left()/Bottom(), tọa độ Line.PositionArray()) mô tả hình học bên trong container đó. Kiểm tra cả vị trí container và tọa độ riêng của hình khi đầu ra trông bị lệch.

CheckBounds returns false unexpectedly

CheckBounds(containerWidth, containerHeight) kiểm tra hình học riêng của hình so với các kích thước được truyền vào — thường là Width()/Height() của Graph sở hữu, không phải toàn bộ media box của trang. Việc truyền sai kích thước tạo ra kết quả âm tính sai lệch ngay cả khi hình thực tế có thể vừa trong Graph.

A Line không xuất hiện, hoặc xuất hiện như một điểm duy nhất

PositionArray() cần ít nhất hai cặp (x, y) — bốn giá trị float — để mô tả một đoạn hiển thị. Một vector ngắn hơn sẽ không tạo thành đường thẳng.

Confusing Circle with AnnotationType::Circle or CircleAnnotation

Thư viện cũng có một API chú thích PDF riêng biệt, trong đó AnnotationType::Circle và CircleAnnotation đại diện cho các chú thích dạng vòng tròn. Lớp Circle được đề cập trong hướng dẫn này không liên quan — nó nằm trong Aspose::Pdf::Drawing và vẽ trực tiếp vào nội dung trang thay vì thêm một đối tượng chú thích.

Câu hỏi thường gặp

Sự khác biệt giữa GraphInfo và BorderInfo là gì?

GraphInfo định dạng một nét vẽ đơn — độ rộng đường, màu sắc, màu nền và mẫu gạch — và được sử dụng cả trên các hình dạng riêng lẻ (qua Shape.GraphInfo()) và trên mỗi cạnh bên trong một BorderInfo. BorderInfo nhóm bốn giá trị GraphInfo (Left(), Right(), Top(), Bottom()) cộng thêm một RoundedBorderRadius() để mô tả khung viền riêng của một container Graph.

Tôi có thể tô một hình dạng bằng màu đồng nhất không?

Có. GraphInfo.FillColor() đặt màu nền được sử dụng khi hình dạng được vẽ thông qua Graph sở hữu của nó.

Circle có lưu trữ hộp bao giống như Ellipse không?

Không. Circle cung cấp PosX(), PosY() và Radius(); Ellipse cung cấp Left(), Bottom(), Width() và Height(). Chúng không chia sẻ một biểu diễn hình học chung ngoài cơ sở trừu tượng Shape và kiểu dáng GraphInfo.

Một Graph có thể chứa hơn một hình dạng không?

Có. Graph.Shapes() trả về toàn bộ bộ sưu tập các đối tượng Shape được đặt trong Graph đó — các vòng tròn, hình bầu dục và đường thẳng đều có thể được thêm vào cùng một container.

Có phải đây giống như việc render một trang thành hình ảnh không?

Không. BmpDevice, JpegDevice và TiffDevice rasterize existing page content to image formats. Graph, Shape, Circle, Ellipse và Line tạo ra hình học vector mới trực tiếp trong nội dung PDF trong khi tài liệu đang được xây dựng hoặc chỉnh sửa.

Xem thêm

 Tiếng Việt