Come lavorare con la grafica PDF in C++
Aspose.PDF FOSS per C++ include un piccolo API di grafica vettoriale per disegnare forme libere direttamente su una pagina PDF — separato dai dispositivi di rendering raster della libreria (BmpDevice, JpegDevice, TiffDevice), che convertono il contenuto della pagina esistente in immagini. Questa guida descrive come creare un contenitore Graph, aggiungere forme Circle, Ellipse e Line, stilizzare quelle forme con GraphInfo, e posizionare il Graph finito su una pagina. La libreria viene aggiunta a un progetto tramite CMake.
Guida passo-passo
Passo 1: Installa il pacchetto
Aggiungi la libreria come sottodirectory CMake e collega contro il target aspose_pdf_foss:
add_subdirectory(aspose.pdf-foss-for-cpp)
target_link_libraries(your_app PRIVATE aspose_pdf_foss)Verifica che la toolchain e le intestazioni siano risolte compilando un programma minimale:
#include <aspose/pdf/document.hpp>
#include <iostream>
int main() {
Aspose::Pdf::Document doc;
std::cout << "Linked OK — pages: " << doc.Pages().Count() << "\n";
}Una compilazione riuscita che stampa pages: 0 conferma che la libreria è collegata correttamente.
Passo 2: Importa le classi richieste
Includi gli header per il documento, il contenitore di forme, le tre forme concrete e i loro tipi di stile:
#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 e Line si trovano nello spazio dei nomi Aspose::Pdf::Drawing; GraphInfo, BorderInfo e Color si trovano direttamente sotto Aspose::Pdf.
Passo 3: Crea un grafico e aggiungilo a una pagina
Graph è un contenitore che si posiziona nella pagina tramite Left(), Top(), Width() e Height(), e contiene le forme disegnate al suo interno tramite Shapes(). Una volta configurato, viene inserito nella pagina tramite Page.Paragraphs().Add(), la stessa collezione usata per gli altri contenuti della pagina:
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);Passo 4: Disegna un cerchio e un’ellisse
Circle descrive la sua geometria con PosX(), PosY() e Radius(). Ellipse utilizza invece una bounding box — Left(), Bottom(), Width() e Height(). Entrambi vengono aggiunti al Graph aggiungendoli, come puntatori Shape, al vettore restituito da 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));Entrambe le forme ereditano CheckBounds(containerWidth, containerHeight) da Shape, che segnala se la geometria attuale della forma rientra in un contenitore delle dimensioni specificate:
bool circleFits = circle->CheckBounds(graph->Width(), graph->Height());Passo 5: Disegna una linea
Line memorizza la sua geometria in modo diverso: PositionArray() contiene un std::vector<float> piatto di coordinate — una coppia x, y per punto — invece di proprietà nominate. Un segmento a due punti richiede quattro valori: x iniziale, y iniziale, x finale, y finale.
auto line = std::make_unique<Line>();
line->PositionArray({0.0f, 0.0f, 400.0f, 400.0f});
graph->Shapes().push_back(std::move(line));Passo 6: Stila le forme con GraphInfo e il bordo del contenitore
Il tratto e il riempimento di ogni forma provengono da un GraphInfo, letti e impostati tramite Shape.GraphInfo() / GraphInfo(value). Esso espone lo spessore del tratto (LineWidth(), valore predefinito 1.0f), il colore del tratto e del riempimento (Color(), FillColor()), e un modello di tratteggio (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));Il contenitore Graph stesso contiene un BorderInfo, impostato tramite Border(), che raggruppa un GraphInfo per bordo (Left(), Right(), Top(), Bottom()) più un RoundedBorderRadius() per il telaio del contenitore:
BorderInfo border{BorderSide::All, 1.5f, Color::Black()};
border.RoundedBorderRadius(6.0);
graph->Border(border);Passo 7: Salva il documento
doc.Save("graphics.pdf");Problemi comuni e soluzioni
Una forma che ho costruito non appare mai nella pagina
Costruire un Circle, Ellipse o Line da solo non ha effetto finché non viene aggiunto al vettore restituito dal Shapes() del Graph proprietario, e quel Graph viene aggiunto a una pagina tramite Page.Paragraphs().Add(). Tutti e tre i passaggi — costruire, aggiungere, inserire il Graph nella pagina — sono necessari.
Le forme vengono renderizzate nella posizione sbagliata
Graph.Left() e Graph.Top() posizionano il contenitore nella pagina; le proprietà della forma (Circle.PosX()/PosY(), Ellipse.Left()/Bottom(), coordinate Line.PositionArray()) descrivono la geometria all’interno di quel contenitore. Verifica sia il posizionamento del contenitore sia le coordinate della forma stessa quando l’output sembra spostato.
CheckBounds returns false unexpectedly
CheckBounds(containerWidth, containerHeight) verifica la geometria della forma rispetto alle dimensioni passate — tipicamente le Width()/Height() del Graph proprietario, non l’intero media box della pagina. Passare dimensioni errate genera un falso negativo anche quando la forma sarebbe effettivamente contenuta nel Graph.
A Line non appare, o appare come un punto singolo
PositionArray() richiede almeno due coppie (x, y) — quattro valori float — per descrivere un segmento visibile. Un vettore più corto non forma una linea.
Confusing Circle with AnnotationType::Circle or CircleAnnotation
La libreria dispone anche di un separato API per le annotazioni PDF, dove AnnotationType::Circle e CircleAnnotation rappresentano annotazioni di markup a forma di cerchio. La classe Circle trattata in questa guida non è correlata — si trova in Aspose::Pdf::Drawing e disegna direttamente nel contenuto della pagina anziché aggiungere un oggetto di annotazione.
Domande Frequenti
Qual è la differenza tra GraphInfo e BorderInfo?
GraphInfo definisce lo stile di un singolo tratto — spessore della linea, colore, colore di riempimento e modello di tratteggio — ed è usato sia sulle forme individuali (tramite Shape.GraphInfo()) sia per ogni bordo all’interno di un BorderInfo. BorderInfo raggruppa quattro valori GraphInfo (Left(), Right(), Top(), Bottom()) più un RoundedBorderRadius() per descrivere il frame del bordo di un contenitore Graph.
Posso riempire una forma con un colore solido?
Sì. GraphInfo.FillColor() imposta il colore di riempimento usato quando la forma viene disegnata tramite il suo Graph di appartenenza.
Circle memorizza un bounding box come fa Ellipse?
No. Circle espone PosX(), PosY() e Radius(); Ellipse espone Left(), Bottom(), Width() e Height(). Non condividono una rappresentazione geometrica comune oltre alla base astratta Shape e allo stile GraphInfo.
Un Graph può contenere più di una forma?
Sì. Graph.Shapes() restituisce l’intera collezione di oggetti Shape inseriti in quel Graph — cerchi, ellissi e linee possono tutti essere aggiunti allo stesso contenitore.
È questo lo stesso di renderizzare una pagina in un’immagine?
No. BmpDevice, JpegDevice e TiffDevice rasterizzano il contenuto della pagina esistente in formati immagine. Graph, Shape, Circle, Ellipse e Line costruiscono nuova geometria vettoriale direttamente nel contenuto PDF mentre un documento viene creato o modificato.