Cómo trabajar con gráficos PDF en C++
Aspose.PDF FOSS para C++ incluye un pequeño API de gráficos vectoriales para dibujar formas libres directamente en una página PDF — separado de los dispositivos de renderizado raster de la biblioteca (BmpDevice, JpegDevice, TiffDevice), que convierten el contenido existente de la página en imágenes. Esta guía recorre la creación de un contenedor Graph, la adición de formas Circle, Ellipse y Line, el estilo de esas formas con GraphInfo, y la colocación del Graph terminado en una página. La biblioteca se agrega a un proyecto mediante CMake.
Guía paso a paso
Paso 1: Instalar el paquete
Agregue la biblioteca como un subdirectorio CMake y vincúlela contra el objetivo aspose_pdf_foss:
add_subdirectory(aspose.pdf-foss-for-cpp)
target_link_libraries(your_app PRIVATE aspose_pdf_foss)Verifique que la cadena de herramientas y los encabezados se resuelvan compilando un programa mínimo:
#include <aspose/pdf/document.hpp>
#include <iostream>
int main() {
Aspose::Pdf::Document doc;
std::cout << "Linked OK — pages: " << doc.Pages().Count() << "\n";
}Una compilación exitosa que imprime pages: 0 confirma que la biblioteca está vinculada correctamente.
Paso 2: Importar clases requeridas
Incluya los encabezados para el documento, el contenedor de formas, las tres formas concretas y sus tipos de estilo:
#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 y Line viven en el espacio de nombres Aspose::Pdf::Drawing; GraphInfo, BorderInfo y Color viven directamente bajo Aspose::Pdf.
Paso 3: Crear un gráfico y añadirlo a una página
Graph es un contenedor que se posiciona en la página mediante Left(), Top(), Width() y Height(), y contiene las formas dibujadas dentro de él mediante Shapes(). Una vez configurado, se coloca en la página mediante Page.Paragraphs().Add(), la misma colección utilizada para otro contenido de la página:
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);Paso 4: Dibujar un círculo y una elipse
Circle describe su geometría con PosX(), PosY() y Radius(). Ellipse utiliza una caja delimitadora en su lugar — Left(), Bottom(), Width() y Height(). Ambas se añaden al Graph añadiéndolas, como punteros Shape, al vector devuelto por 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));Ambas formas heredan CheckBounds(containerWidth, containerHeight) de Shape, que informa si la geometría actual de la forma cabe dentro de un contenedor del tamaño dado:
bool circleFits = circle->CheckBounds(graph->Width(), graph->Height());Paso 5: Dibujar una línea
Line almacena su geometría de manera diferente: PositionArray() contiene una std::vector<float> plana de coordenadas — un par x, y por punto — en lugar de propiedades con nombre. Un segmento de dos puntos necesita cuatro valores: x inicial, y inicial, x final, y final.
auto line = std::make_unique<Line>();
line->PositionArray({0.0f, 0.0f, 400.0f, 400.0f});
graph->Shapes().push_back(std::move(line));Paso 6: Estilizar formas con GraphInfo y el borde del contenedor
El trazo y el relleno de cada forma provienen de un GraphInfo, leídos y establecidos mediante Shape.GraphInfo() / GraphInfo(value). Expone el ancho del trazo (LineWidth(), por defecto 1.0f), el color del trazo y del relleno (Color(), FillColor()), y un patrón de guiones (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));El propio contenedor Graph lleva un BorderInfo, configurado mediante Border(), que agrupa un GraphInfo por borde (Left(), Right(), Top(), Bottom()) más un RoundedBorderRadius() para el marco del propio contenedor:
BorderInfo border{BorderSide::All, 1.5f, Color::Black()};
border.RoundedBorderRadius(6.0);
graph->Border(border);Paso 7: Guardar el documento
doc.Save("graphics.pdf");Problemas comunes y soluciones
Una forma que construí nunca aparece en la página
Construir un Circle, Ellipse o Line por sí solo no tiene efecto hasta que se añada al vector devuelto por el Shapes() del Graph propietario, y ese Graph se agregue a una página mediante Page.Paragraphs().Add(). Los tres pasos — construir, añadir, agregar el Graph a la página — son necesarios.
Las formas se renderizan en la posición incorrecta
Graph.Left() y Graph.Top() posicionan el contenedor en la página; las propiedades de la forma (Circle.PosX()/PosY(), Ellipse.Left()/Bottom(), coordenadas Line.PositionArray()) describen la geometría dentro de ese contenedor. Verifique tanto la ubicación del contenedor como las coordenadas propias de la forma cuando la salida parezca desplazada.
CheckBounds returns false unexpectedly
CheckBounds(containerWidth, containerHeight) verifica la geometría propia de la forma frente a las dimensiones proporcionadas — típicamente las Width()/Height() del Graph propietario, no la caja de medios completa de la página. Pasar dimensiones incorrectas produce un falso negativo incluso cuando la forma realmente cabría dentro del Graph.
A Line no aparece, o aparece como un solo punto
PositionArray() necesita al menos dos pares (x, y) — cuatro valores float — para describir un segmento visible. Un vector más corto no forma una línea.
Confusing Circle with AnnotationType::Circle or CircleAnnotation
La biblioteca también tiene un API separado de anotaciones PDF, donde AnnotationType::Circle y CircleAnnotation representan anotaciones de marcado con forma de círculo. La clase Circle cubierta en esta guía no está relacionada — se encuentra en Aspose::Pdf::Drawing y dibuja directamente en el contenido de la página en lugar de agregar un objeto de anotación.
Preguntas frecuentes
¿Cuál es la diferencia entre GraphInfo y BorderInfo?
GraphInfo da estilo a un solo trazo — ancho de línea, color, color de relleno y patrón de guiones — y se usa tanto en formas individuales (a través de Shape.GraphInfo()) como por borde dentro de un BorderInfo. BorderInfo agrupa cuatro valores GraphInfo (Left(), Right(), Top(), Bottom()) más un RoundedBorderRadius() para describir el propio marco del contenedor Graph.
¿Puedo rellenar una forma con un color sólido?
Sí. GraphInfo.FillColor() establece el color de relleno que se usa cuando la forma se dibuja a través de su Graph propietario.
¿Circle almacena una caja delimitadora como lo hace Ellipse?
No. Circle expone PosX(), PosY() y Radius(); Ellipse expone Left(), Bottom(), Width() y Height(). No comparten una representación geométrica común más allá de la base abstracta Shape y el estilo GraphInfo.
¿Puede un Graph contener más de una forma?
Sí. Graph.Shapes() devuelve la colección completa de objetos Shape ubicados dentro de ese Graph — los círculos, elipses y líneas pueden añadirse al mismo contenedor.
¿Es esto lo mismo que renderizar una página a una imagen?
No. BmpDevice, JpegDevice y TiffDevice rasterizan el contenido de página existente a formatos de imagen. Graph, Shape, Circle, Ellipse y Line construyen nueva geometría vectorial directamente en el contenido PDF mientras se construye o edita un documento.