Desenhando Gráficos Vetoriais em uma Página PDF
Desenhando Gráficos Vetoriais em uma Página PDF
Aspose.PDF FOSS para C++ inclui um pequeno API de gráficos vetoriais para desenhar formas diretamente em uma página PDF enquanto o documento está sendo construído. O ponto de entrada é Graph, um contêiner colocado em uma página que contém uma coleção de objetos Shape; três formas concretas — Circle, Ellipse e Line — derivam de Shape e cada uma expressa sua geometria de maneira diferente. O estilo de traçado e preenchimento por forma vem de GraphInfo, e o próprio contêiner Graph possui um BorderInfo para enquadramento. Isso não tem relação com a rasterização de página (BmpDevice, JpegDevice, TiffDevice), que converte o conteúdo existente da página em formatos de imagem raster ao invés de desenhar nova geometria.
O Contêiner de Gráficos e Adicionando Formas a uma Página
Graph posiciona e dimensiona a si mesmo na página por meio de Left(), Top(), Width() e Height(), e sua bandeira IsChangePosition() controla se essa posição pode mudar em relação a outros conteúdos da página. As formas que ele desenha vivem no vetor retornado por Shapes() — std::vector<std::unique_ptr<Shape>> — preenchido ao construir uma forma, configurá-la e movê-la para esse vetor. Uma vez construído, o próprio Graph é colocado na página através de Page.Paragraphs().Add(), a mesma coleção usada para outros conteúdos ao nível da 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");Desenhando Círculos e Elipses
Circle e Ellipse expressam a geometria como propriedades numéricas simples, em vez de um tipo compartilhado de ponto ou retângulo. Circle é um ponto central e raio — PosX(), PosY(), Radius() — enquanto Ellipse é uma caixa delimitadora — Left(), Bottom(), Width(), Height(). Ambos herdam CheckBounds(containerWidth, containerHeight) de Shape, que relata se a geometria atual da forma cabe dentro de um contêiner do tamanho especificado sem alterar a própria 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);Desenhando Linhas com PositionArray
Line armazena sua geometria de forma diferente de Circle e Ellipse: PositionArray() devolve um std::vector<float> plano contendo um par x, y para cada ponto que a linha atravessa, em vez de um conjunto fixo de propriedades nomeadas. Dois pontos (quatro valores) desenham um segmento reto; pares adicionais desenham um caminho de múltiplos segmentos através desses pontos na ordem. Como as outras formas, Line sobrescreve 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 com GraphInfo e BorderInfo
Cada forma lê e grava sua estilização de traço e preenchimento através de Shape.GraphInfo() / GraphInfo(value). GraphInfo tem como padrão um LineWidth() de 1.0f com um DashArray() vazio, e também expõe Color(), FillColor(), DashArray()/DashPhase() para traços tracejados, IsDoubled() para uma linha dupla, e SkewAngleX()/SkewAngleY(), ScalingRateX()/ScalingRateY() e RotationAngle() para transformar o traço. Graph por si só contém um BorderInfo, definido através de Border(), que agrupa um GraphInfo por aresta — Left(), Right(), Top(), Bottom() — mais um RoundedBorderRadius(), e pode ser construído nomeando quais arestas estilizar com uma flag BorderSide (BorderSide::Left | BorderSide::Right, ou BorderSide::All para cada aresta).
#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);Dicas e Melhores Práticas
- Chame
CheckBoundscontra o próprioWidth()/Height()doGraph, não contra a caixa de mídia completa da página — a verificação valida a geometria de uma forma em relação ao tamanho do contêiner que você fornece, não analisa a página automaticamente. - Adicione formas a
Shapes()antes de adicionar oGraphaPage.Paragraphs()— a construção de umCircle,EllipseouLinenão tem efeito visível até que seja anexado ao seuGraphproprietário e que esseGraphseja adicionado a uma página. - Agrupe formas relacionadas sob um único
Graphem vez de umGraphpor forma;Left()/Top()/Width()/Height()posicionam todo o contêiner de uma vez, e cada forma emShapes()é desenhada em relação a ele. - Defina
Shape.GraphInfo()explicitamente para formas que precisam de preenchimento visível ou de um traço não padrão — oGraphInfopadrão tem uma largura de linha1.0f, sem preenchimento e umDashArray()vazio. - Construa
Line.PositionArray()como paresx0, y0, x1, y1, …na ordem em que o caminho deve ser desenhado — um vetor de comprimento ímpar descreve um ponto incompleto.
Problemas comuns
| Problema | Causa | Correção |
|---|---|---|
Um Circle, Ellipse ou Line nunca aparece na página salva | A forma foi construída, mas nunca foi anexada ao Graph’s Shapes(), ou o Graph nunca foi adicionado à página | Empurre a forma para graph->Shapes() e chame page.Paragraphs().Add(graph) |
| A forma é desenhada com um traço fino preto padrão | Shape.GraphInfo() nunca foi definido | Chame shape->GraphInfo(customGraphInfo) antes de anexar a forma a Shapes() |
Line está ausente ou parece um ponto único | PositionArray() tem menos de dois pares (x, y) ou um número ímpar de valores | Forneça pelo menos quatro valores como x0, y0, x1, y1 |
| Apenas algumas arestas da borda do contêiner estão estilizadas | BorderInfo foi construído com uma máscara BorderSide que exclui algumas arestas | Use BorderSide::All, ou faça OR de todas as bandeiras BorderSide necessárias |
CheckBounds inesperadamente retorna false | As dimensões passadas para CheckBounds não correspondem ao contêiner em que a forma realmente é desenhada | Passe o Width()/Height() atual do Graph proprietário |
FAQ
Qual é a diferença entre Circle e AnnotationType::Circle?
Circle (este guia) é uma forma de desenho que desenha diretamente no conteúdo da página através de um Graph. AnnotationType::Circle e CircleAnnotation pertencem a um API de anotações separado e representam, em vez disso, uma anotação de marcação em forma de círculo — os dois não estão relacionados apesar do nome compartilhado.
Um único Graph pode conter mais de uma forma?
Sim. Graph.Shapes() devolve o std::vector<std::unique_ptr<Shape>> completo para esse contêiner, e qualquer combinação de instâncias de Circle, Ellipse e Line pode ser acrescentada a ele antes que o Graph seja adicionado à página.
Circle e Ellipse compartilham um tipo de geometria?
Não. Circle é um ponto central e raio (PosX, PosY, Radius); Ellipse é uma caixa delimitadora (Left, Bottom, Width, Height). Ambos derivam de Shape, mas nenhum compartilha um ponto ou tipo de retângulo comum com o outro.
Desenhar formas com Graph é o mesmo que renderizar uma página para uma imagem?
Não. Graph, Shape, Circle, Ellipse e Line constroem nova geometria vetorial no conteúdo de uma página enquanto o documento é gerado. Converter uma página existente em uma imagem raster é uma capacidade separada, tratada por BmpDevice, JpegDevice e TiffDevice.
O que GraphInfo.IsDoubled() faz?
Ele marca o traço como uma linha dupla ao invés de mudar qualquer outra propriedade do traço. É uma flag booleana lida e definida através de IsDoubled() / IsDoubled(value) juntamente com as outras propriedades de traço GraphInfo.
API Reference Resumo
| Classe/Método | Descrição |
|---|---|
Graph | Contêiner de forma colocado em uma página; posiciona-se com Left(), Top(), Width(), Height() |
Graph.Shapes() | Retorna o std::vector<std::unique_ptr<Shape>> desenhado por este contêiner |
Graph.GraphInfo() / GraphInfo(value) | Obtém/define o estilo de traço e preenchimento próprio do contêiner |
Graph.Border() / Border(value) | Obtém/define o BorderInfo que enquadra o contêiner |
Shape | Base abstrata para Circle, Ellipse e Line; expõe GraphInfo() e CheckBounds() |
Shape.CheckBounds(w, h) | Informa se a geometria da forma cabe dentro de um contêiner de largura/altura dada |
Circle | Forma de círculo: PosX(), PosY(), Radius() |
Ellipse | Forma de elipse: caixa delimitadora via Left(), Bottom(), Width(), Height() |
Line | Forma de linha/caminho: PositionArray(), um std::vector<float> plano de coordenadas x, y |
GraphInfo | Estilização de traço/preenchimento: LineWidth(), Color(), FillColor(), DashArray(), DashPhase(), IsDoubled(), RotationAngle(), SkewAngleX()/SkewAngleY(), ScalingRateX()/ScalingRateY() |
BorderInfo | Borda de contêiner por borda: Left(), Right(), Top(), Bottom() (cada um um GraphInfo), mais RoundedBorderRadius() |
BorderSide | Enum que seleciona bordas ao construir um BorderInfo: None, Left, Top, Right, Bottom, All, Box |