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.