Dibujando gráficos vectoriales en una página PDF
Dibujar gráficos vectoriales en una página PDF
Aspose.PDF FOSS for C++ incluye un pequeño API de gráficos vectoriales para dibujar formas directamente sobre una página PDF mientras se construye un documento. El punto de entrada es Graph, un contenedor colocado en una página que alberga una colección de objetos Shape; tres formas concretas — Circle, Ellipse y Line — derivan de Shape y cada una expresa su geometría de manera diferente. El estilo de trazo y relleno por forma proviene de GraphInfo, y el propio contenedor Graph lleva un BorderInfo para encuadrar. Esto no está relacionado con la rasterización de página (BmpDevice, JpegDevice, TiffDevice), que convierte el contenido existente de la página a formatos de imagen raster en lugar de dibujar nueva geometría.
El contenedor de gráficos y la adición de formas a una página
Graph se posiciona y dimensiona en la página mediante Left(), Top(), Width() y Height(), y su bandera IsChangePosition() controla si esa posición puede desplazarse respecto al resto del contenido de la página. Las formas que dibuja residen en el vector devuelto por Shapes() — std::vector<std::unique_ptr<Shape>> — que se llena al crear una forma, configurarla y moverla a ese vector. Una vez construida, el propio Graph se coloca en la página mediante Page.Paragraphs().Add(), la misma colección utilizada para otro contenido a nivel de página.
#include <aspose/pdf/document.hpp>
#include <aspose/pdf/drawing/graph.hpp>
#include <aspose/pdf/drawing/circle.hpp>
using namespace Aspose::Pdf;
using namespace Aspose::Pdf::Drawing;
Document doc;
Page page = doc.Pages().Add();
auto graph = std::make_shared<Graph>();
graph->Left(36.0);
graph->Top(36.0);
graph->Width(500.0);
graph->Height(300.0);
auto marker = std::make_unique<Circle>();
marker->PosX(60.0);
marker->PosY(60.0);
marker->Radius(30.0);
graph->Shapes().push_back(std::move(marker));
page.Paragraphs().Add(graph);
doc.Save("shapes.pdf");Dibujar círculos y elipses
Circle y Ellipse expresan la geometría como propiedades numéricas simples en lugar de un tipo compartido de punto o rectángulo. Circle representa un punto central y un radio — PosX(), PosY(), Radius() — mientras que Ellipse es un cuadro delimitador — Left(), Bottom(), Width(), Height(). Ambos heredan CheckBounds(containerWidth, containerHeight) de Shape, que indica si la geometría actual de la forma cabe dentro de un contenedor del tamaño especificado sin modificar la propia forma.
#include <aspose/pdf/drawing/circle.hpp>
#include <aspose/pdf/drawing/ellipse.hpp>
using namespace Aspose::Pdf::Drawing;
Circle circle;
circle.PosX(120.0);
circle.PosY(120.0);
circle.Radius(60.0);
bool circleFits = circle.CheckBounds(500.0, 300.0);
Ellipse ellipse;
ellipse.Left(220.0);
ellipse.Bottom(20.0);
ellipse.Width(250.0);
ellipse.Height(100.0);
bool ellipseFits = ellipse.CheckBounds(500.0, 300.0);Dibujar líneas con PositionArray
Line almacena su geometría de forma diferente a Circle y Ellipse: PositionArray() devuelve un std::vector<float> plano que contiene un par x, y para cada punto por el que la línea pasa, en lugar de un conjunto fijo de propiedades con nombre. Dos puntos (cuatro valores) dibujan un segmento recto; pares adicionales dibujan una ruta de varios segmentos a través de esos puntos en orden. Al igual que las otras formas, Line sobrescribe CheckBounds(containerWidth, containerHeight) de Shape.
#include <aspose/pdf/drawing/line.hpp>
using namespace Aspose::Pdf::Drawing;
Line path;
path.PositionArray({0.0f, 0.0f, 250.0f, 0.0f, 125.0f, 200.0f});
bool pathFits = path.CheckBounds(500.0, 300.0);Estilizando formas con GraphInfo y BorderInfo
Cada forma lee y escribe su estilo de trazo y relleno a través de Shape.GraphInfo() / GraphInfo(value). GraphInfo por defecto es un LineWidth() de 1.0f con un DashArray() vacío, y también expone Color(), FillColor(), DashArray()/DashPhase() para trazos discontinuos, IsDoubled() para una línea duplicada, y SkewAngleX()/SkewAngleY(), ScalingRateX()/ScalingRateY(), y RotationAngle() para transformar el trazo. Graph lleva un BorderInfo, establecido mediante Border(), que agrupa un GraphInfo por borde — Left(), Right(), Top(), Bottom() — más un RoundedBorderRadius(), y puede construirse indicando qué bordes estilizar con una bandera BorderSide (BorderSide::Left | BorderSide::Right, o BorderSide::All para cada borde).
#include <aspose/pdf/graph_info.hpp>
#include <aspose/pdf/border_info.hpp>
#include <aspose/pdf/border_side.hpp>
#include <aspose/pdf/color.hpp>
using namespace Aspose::Pdf;
GraphInfo stroke;
stroke.LineWidth(2.5f);
stroke.Color(Color::FromRgb(1.0, 0.0, 0.0));
stroke.DashArray({3, 2});
stroke.DashPhase(1);
stroke.IsDoubled(true);
// Style only the left and right edges of the container border.
BorderInfo border{BorderSide::Left | BorderSide::Right, 3.0f};
border.RoundedBorderRadius(4.0);
graph->GraphInfo(stroke);
graph->Border(border);Consejos y buenas prácticas
- Llama a
CheckBoundscontra elWidth()/Height()propio delGraph, no contra el cuadro de medios completo de la página — la comprobación valida la geometría de una forma contra el tamaño del contenedor que proporciones, no mira la página automáticamente. - Agrega las formas a
Shapes()antes de añadir elGraphaPage.Paragraphs()— crear unCircle,EllipseoLineno tiene efecto visible hasta que se añada tanto a suGraphpropietario como eseGraphse agregue a una página. - Agrupa formas relacionadas bajo un único
Graphen lugar de unGraphpor forma;Left()/Top()/Width()/Height()posicionan todo el contenedor una sola vez, y cada forma enShapes()se dibuja en relación con él. - Establece
Shape.GraphInfo()explícitamente para las formas que requieran un relleno visible o un trazo no predeterminado — elGraphInfopredeterminado tiene un ancho de línea1.0f, sin relleno y unDashArray()vacío. - Construye
Line.PositionArray()como paresx0, y0, x1, y1, …en el orden en que se debe dibujar la trayectoria — un vector de longitud impar describe un punto incompleto.
Problemas comunes
| Problema | Causa | Solución |
|---|---|---|
Un Circle, Ellipse o Line nunca aparece en la página guardada | La forma se construyó pero nunca se añadió a su Graph’s Shapes(), o el Graph nunca se agregó a la página | Empuje la forma a graph->Shapes() y llame a page.Paragraphs().Add(graph) |
| La forma se dibuja con un trazo negro delgado predeterminado | Shape.GraphInfo() nunca se estableció | Llame a shape->GraphInfo(customGraphInfo) antes de añadir la forma a Shapes() |
Line falta o parece un punto único | PositionArray() tiene menos de dos pares (x, y) o un número impar de valores | Proporcione al menos cuatro valores como x0, y0, x1, y1 |
| Solo algunos bordes del contorno del contenedor están estilizados | BorderInfo se construyó con una máscara BorderSide que excluye algunos bordes | Utilice BorderSide::All, o combine con OR todos los indicadores BorderSide necesarios |
CheckBounds devuelve inesperadamente false | Las dimensiones pasadas a CheckBounds no coinciden con el contenedor en el que realmente se dibuja la forma | Pasa el Width()/Height() actual del Graph propietario |
FAQ
¿Cuál es la diferencia entre Circle y AnnotationType::Circle?
Circle (esta guía) es una forma de dibujo que se inserta directamente en el contenido de la página a través de un Graph. AnnotationType::Circle y CircleAnnotation pertenecen a un API de anotaciones independiente y representan una anotación de marcado con forma circular — ambos no están relacionados a pesar del nombre compartido.
¿Puede un único Graph contener más de una forma?
Sí. Graph.Shapes() devuelve el std::vector<std::unique_ptr<Shape>> completo para ese contenedor, y cualquier combinación de instancias Circle, Ellipse y Line puede añadirse antes de que se inserte el Graph en la página.
¿Comparten Circle y Ellipse un tipo de geometría?
No. Circle es un punto central y radio (PosX, PosY, Radius); Ellipse es una caja delimitadora (Left, Bottom, Width, Height). Ambos se derivan de Shape, pero ninguno comparte un punto o tipo de rectángulo con el otro.
¿Dibujar formas con Graph es lo mismo que representar una página como imagen?
No. Graph, Shape, Circle, Ellipse y Line incorporan nueva geometría vectorial al contenido de una página durante la generación del documento. Convertir una página existente a una imagen raster es una capacidad distinta, gestionada por BmpDevice, JpegDevice y TiffDevice.
¿Qué hace GraphInfo.IsDoubled()?
Marca el trazo como una línea doble sin modificar ninguna otra propiedad del trazo. Es una bandera booleana que se lee y establece a través de IsDoubled() / IsDoubled(value) junto con las demás propiedades de trazo GraphInfo.
Resumen de API Reference
| Clase/Método | Descripción |
|---|---|
Graph | Contenedor de forma colocado en una página; se posiciona con Left(), Top(), Width(), Height() |
Graph.Shapes() | Devuelve el std::vector<std::unique_ptr<Shape>> dibujado por este contenedor |
Graph.GraphInfo() / GraphInfo(value) | Obtiene/establece el estilo de trazo y relleno propio del contenedor |
Graph.Border() / Border(value) | Obtiene/establece el BorderInfo que enmarca el contenedor |
Shape | Base abstracta para Circle, Ellipse y Line; expone GraphInfo() y CheckBounds() |
Shape.CheckBounds(w, h) | Informa si la geometría de la figura cabe dentro de un contenedor con el ancho/alto especificado |
Circle | Forma de círculo: PosX(), PosY(), Radius() |
Ellipse | Forma de elipse: caja delimitadora mediante Left(), Bottom(), Width(), Height() |
Line | Forma de línea/ruta: PositionArray(), un plano std::vector<float> de coordenadas x, y |
GraphInfo | Estilo de trazo/relleno: LineWidth(), Color(), FillColor(), DashArray(), DashPhase(), IsDoubled(), RotationAngle(), SkewAngleX()/SkewAngleY(), ScalingRateX()/ScalingRateY() |
BorderInfo | Borde del contenedor por borde: Left(), Right(), Top(), Bottom() (cada uno es un GraphInfo), más RoundedBorderRadius() |
BorderSide | Enumeración que selecciona los bordes al crear un BorderInfo: None, Left, Top, Right, Bottom, All, Box |