Jak pracować z grafiką PDF w C++

Jak pracować z grafiką PDF w C++

FOSS for C++ zawiera mały API wektorowej grafiki do rysowania kształtów dowolnych bezpośrednio na stronie PDF — oddzielny od rasteryzujących urządzeń biblioteki (BmpDevice, JpegDevice, TiffDevice), które konwertują istniejącą zawartość strony na obrazy. Ten przewodnik prowadzi przez tworzenie kontenera Graph, dodawanie kształtów Circle, Ellipse i Line, stylowanie tych kształtów za pomocą GraphInfo oraz umieszczanie gotowego Graph na stronie. Biblioteka jest dodawana do projektu za pomocą CMake.

Przewodnik krok po kroku

Krok 1: Zainstaluj pakiet

Dodaj bibliotekę jako podkatalog CMake i połącz się z celem aspose_pdf_foss:

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

Sprawdź, czy toolchain i nagłówki są poprawnie rozwiązywane, kompilując minimalny program:

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

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

Udane zbudowanie, które wypisuje pages: 0, potwierdza, że biblioteka została poprawnie połączona.


Krok 2: Importuj wymagane klasy

Dołącz nagłówki dla dokumentu, kontenera kształtów, trzech konkretnych kształtów oraz ich typów stylizacji:

#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 i Line znajdują się w przestrzeni nazw Aspose::Pdf::Drawing; GraphInfo, BorderInfo i Color znajdują się bezpośrednio pod Aspose::Pdf.


Krok 3: Utwórz graf i dodaj go do strony

Graph jest kontenerem, który pozycjonuje się na stronie za pomocą Left(), Top(), Width() i Height(), oraz przechowuje rysowane w nim kształty za pomocą Shapes(). Po skonfigurowaniu jest umieszczany na stronie poprzez Page.Paragraphs().Add(), tę samą kolekcję używaną dla innych elementów strony:

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

Krok 4: Narysuj koło i elipsę

Circle opisuje swoją geometrię przy użyciu PosX(), PosY() i Radius(). Ellipse używa zamiast tego obramowania — Left(), Bottom(), Width() i Height(). Oba są dodawane do Graph poprzez dołączanie ich, jako wskaźników Shape, do wektora zwróconego przez 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));

Oba kształty dziedziczą CheckBounds(containerWidth, containerHeight) z Shape, które zgłasza, czy bieżąca geometria kształtu mieści się w kontenerze o podanym rozmiarze:

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

Krok 5: Narysuj linię

Line przechowuje swoją geometrię inaczej: PositionArray() zawiera płaską std::vector<float> współrzędnych — parę x, y dla każdego punktu — zamiast nazwanych właściwości. Segment składający się z dwóch punktów wymaga czterech wartości: start x, start y, end x, end y.

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

Krok 6: Stylizuj kształty przy użyciu GraphInfo i obramowania kontenera

Obrys i wypełnienie każdego kształtu pochodzą z GraphInfo, odczytywane i ustawiane przez Shape.GraphInfo() / GraphInfo(value). Udostępnia szerokość obrysu (LineWidth(), domyślnie 1.0f), kolor obrysu i wypełnienia (Color(), FillColor()) oraz wzór przerywania (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));

Sam kontener Graph zawiera BorderInfo, ustawiany przez Border(), który grupuje po jednym GraphInfo na krawędź (Left(), Right(), Top(), Bottom()) oraz RoundedBorderRadius() dla własnej ramki kontenera:

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

Krok 7: Zapisz dokument

doc.Save("graphics.pdf");

Typowe problemy i rozwiązania

Kształt, który stworzyłem, nigdy nie pojawia się na stronie

Tworzenie Circle, Ellipse lub Line samodzielnie nie ma efektu, dopóki nie zostanie dołączone do wektora zwróconego przez własnego Graph’s Shapes(), a ten Graph zostanie dodany do strony za pomocą Page.Paragraphs().Add(). Wymagane są wszystkie trzy kroki — utworzenie, dołączenie, dodanie Graph do strony.

Kształty renderują się w nieprawidłowej pozycji

Graph.Left() i Graph.Top() pozycjonują kontener na stronie; właściwości kształtu (Circle.PosX()/PosY(), Ellipse.Left()/Bottom(), współrzędne Line.PositionArray()) opisują geometrię wewnątrz tego kontenera. Sprawdź zarówno położenie kontenera, jak i własne współrzędne kształtu, gdy wynik wydaje się przesunięty.

CheckBounds returns false unexpectedly

CheckBounds(containerWidth, containerHeight) sprawdza własną geometrię kształtu względem przekazanych wymiarów — zazwyczaj Width()/Height() własnego Graph, a nie pełnego pudełka mediów strony. Przekazanie niewłaściwych wymiarów powoduje fałszywy negatyw, nawet gdy kształt faktycznie mieściłby się w Graph.

A Line nie pojawia się, albo pojawia się jako pojedynczy punkt

PositionArray() wymaga co najmniej dwóch par (x, y) — czterech wartości float — aby opisać widoczny odcinek. Krótszy wektor nie tworzy linii.

Confusing Circle with AnnotationType::Circle or CircleAnnotation

Biblioteka ma także osobny API adnotacji PDF, w którym AnnotationType::Circle i CircleAnnotation oznaczają adnotacje w kształcie koła. Klasa Circle omówiona w tym przewodniku nie jest powiązana — znajduje się w Aspose::Pdf::Drawing i rysuje bezpośrednio w treści strony zamiast dodawać obiekt adnotacji.

Najczęściej zadawane pytania

Jaka jest różnica między GraphInfo a BorderInfo?

GraphInfo styluje pojedynczy pociąg — szerokość linii, kolor, kolor wypełnienia i wzór kreski — i jest używany zarówno na pojedynczych kształtach (przez Shape.GraphInfo()), jak i na krawędzi wewnątrz BorderInfo. BorderInfo grupuje cztery wartości GraphInfo (Left(), Right(), Top(), Bottom()) plus RoundedBorderRadius(), aby opisać własną ramkę graniczną kontenera Graph.

Czy mogę wypełnić kształt jednolitym kolorem?

Tak. GraphInfo.FillColor() ustawia kolor wypełnienia używany, gdy kształt jest rysowany przez jego Graph.

Czy Circle przechowuje ramkę ograniczającą tak jak Ellipse?

Nie. Circle udostępnia PosX(), PosY() i Radius(); Ellipse udostępnia Left(), Bottom(), Width() i Height(). Nie mają wspólnej reprezentacji geometrii poza abstrakcyjną bazą Shape i stylizacją GraphInfo.

Czy jeden Graph może pomieścić więcej niż jeden kształt?

Tak. Graph.Shapes() zwraca pełną kolekcję obiektów Shape umieszczonych w tym Graph — koła, elipsy i linie mogą być wszystkie dołączane do tego samego kontenera.

Czy to to samo, co renderowanie strony do obrazu?

Nie. BmpDevice, JpegDevice i TiffDevice rasteryzują istniejącą treść strony do formatów obrazów. Graph, Shape, Circle, Ellipse i Line tworzą nową wektorową geometrię bezpośrednio w treści PDF, gdy dokument jest tworzony lub edytowany.

Zobacz także

 Polski