Como trabalhar com gráficos PDF em C++
Aspose.PDF FOSS for C++ inclui um pequeno API de vetor-gráficos para desenhar formas livres diretamente em uma página PDF — separado dos dispositivos de renderização raster da biblioteca (BmpDevice, JpegDevice, TiffDevice), que convertem o conteúdo da página existente em imagens. Este guia mostra como criar um contêiner Graph, adicionar formas Circle, Ellipse e Line a ele, estilizar essas formas com GraphInfo, e colocar o Graph finalizado em uma página. A biblioteca é adicionada a um projeto via CMake.
Guia Passo a Passo
Passo 1: Instalar o Pacote
Adicione a biblioteca como um subdiretório CMake e vincule ao alvo aspose_pdf_foss:
add_subdirectory(aspose.pdf-foss-for-cpp)
target_link_libraries(your_app PRIVATE aspose_pdf_foss)Verifique se a cadeia de ferramentas e os cabeçalhos são resolvidos compilando um 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";
}Uma compilação bem-sucedida que imprime pages: 0 confirma que a biblioteca está vinculada corretamente.
Etapa 2: Importar Classes Necessárias
Inclua os cabeçalhos para o documento, o contêiner de formas, as três formas concretas e seus 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 e Line estão no namespace Aspose::Pdf::Drawing; GraphInfo, BorderInfo e Color estão diretamente sob Aspose::Pdf.
Etapa 3: Criar um Gráfico e Adicioná-lo a uma Página
Graph é um contêiner que se posiciona na página por meio de Left(), Top(), Width() e Height(), e contém as formas desenhadas dentro dele por meio de Shapes(). Uma vez configurado, ele é colocado na página por meio de Page.Paragraphs().Add(), a mesma coleção usada para outros conteúdos da 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);Etapa 4: Desenhar um Círculo e uma Elipse
Circle descreve sua geometria com PosX(), PosY() e Radius(). Ellipse usa uma caixa delimitadora em vez disso — Left(), Bottom(), Width() e Height(). Ambos são adicionados ao Graph ao anexá-los, como ponteiros Shape, ao vetor retornado 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 as formas herdam CheckBounds(containerWidth, containerHeight) de Shape, que informa se a geometria atual da forma cabe dentro de um contêiner do tamanho especificado:
bool circleFits = circle->CheckBounds(graph->Width(), graph->Height());Passo 5: Desenhar uma Linha
Line armazena sua geometria de forma diferente: PositionArray() contém um std::vector<float> plano de coordenadas — um par x, y por ponto — em vez de propriedades nomeadas. Um segmento de dois pontos requer quatro 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));Passo 6: Estilizar Formas com GraphInfo e a Borda do Contêiner
O traço e o preenchimento de cada forma vêm de um GraphInfo, lidos e definidos através de Shape.GraphInfo() / GraphInfo(value). Ele expõe a largura do traço (LineWidth(), padrão 1.0f), a cor do traço e do preenchimento (Color(), FillColor()), e um padrão de traçado (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));O próprio contêiner Graph possui um BorderInfo, definido através de Border(), que agrupa um GraphInfo por aresta (Left(), Right(), Top(), Bottom()) além de um RoundedBorderRadius() para a moldura do próprio contêiner:
BorderInfo border{BorderSide::All, 1.5f, Color::Black()};
border.RoundedBorderRadius(6.0);
graph->Border(border);Passo 7: Salvar o Documento
doc.Save("graphics.pdf");Problemas Comuns e Soluções
Uma forma que eu construí nunca aparece na página
Construir um Circle, Ellipse ou Line por si só não tem efeito até que seja anexado ao vetor retornado pelo Graph proprietário Shapes(), e esse Graph seja adicionado a uma página por meio de Page.Paragraphs().Add(). Todas as três etapas — construir, anexar, adicionar o Graph à página — são necessárias.
Formas são renderizadas na posição errada
Graph.Left() e Graph.Top() posicionam o contêiner na página; as propriedades da forma (Circle.PosX()/PosY(), Ellipse.Left()/Bottom(), coordenadas Line.PositionArray()) descrevem a geometria dentro desse contêiner. Verifique tanto o posicionamento do contêiner quanto as próprias coordenadas da forma quando a saída parecer deslocada.
CheckBounds returns false unexpectedly
CheckBounds(containerWidth, containerHeight) verifica a própria geometria da forma em relação às dimensões fornecidas — tipicamente as Width()/Height() do Graph proprietário, não a caixa de mídia completa da página. Passar dimensões incorretas gera um falso negativo mesmo quando a forma realmente caberia dentro do Graph.
A Line não aparece, ou aparece como um único ponto
PositionArray() precisa de pelo menos dois pares (x, y) — quatro valores float — para descrever um segmento visível. Um vetor mais curto não forma uma linha.
Confusing Circle with AnnotationType::Circle or CircleAnnotation
A biblioteca também possui um API separado de anotações PDF, onde AnnotationType::Circle e CircleAnnotation representam anotações de marcação em forma de círculo. A classe Circle abordada neste guia não está relacionada — ela reside em Aspose::Pdf::Drawing e desenha diretamente no conteúdo da página ao invés de adicionar um objeto de anotação.
Perguntas Frequentes
Qual é a diferença entre GraphInfo e BorderInfo?
GraphInfo estiliza um único traço — largura da linha, cor, cor de preenchimento e padrão de traço — e é usado tanto em formas individuais (através de Shape.GraphInfo()) quanto por aresta dentro de um BorderInfo. BorderInfo agrupa quatro valores GraphInfo (Left(), Right(), Top(), Bottom()) mais um RoundedBorderRadius() para descrever a própria moldura de borda de um contêiner Graph.
Posso preencher uma forma com uma cor sólida?
Sim. GraphInfo.FillColor() define a cor de preenchimento usada quando a forma é desenhada através de seu Graph proprietário.
O Circle armazena uma caixa delimitadora como o Ellipse faz?
No. Circle expõe PosX(), PosY() e Radius(); Ellipse expõe Left(), Bottom(), Width() e Height(). Eles não compartilham uma representação geométrica comum além da base abstrata Shape e do estilo GraphInfo.
Um Graph pode conter mais de uma forma?
Sim. Graph.Shapes() retorna a coleção completa de objetos Shape colocados dentro desse Graph — círculos, elipses e linhas podem todos ser anexados ao mesmo contêiner.
Isso é o mesmo que renderizar uma página para uma imagem?
Não. BmpDevice, JpegDevice e TiffDevice rasterizam o conteúdo de página existente para formatos de imagem. Graph, Shape, Circle, Ellipse e Line constroem nova geometria vetorial diretamente no conteúdo PDF enquanto um documento está sendo criado ou editado.