Drawing Vector Graphics on a PDF Page

Drawing Vector Graphics on a PDF Page

Drawing Vector Graphics on a PDF Page

Aspose.PDF FOSS for C++ includes a small vector-graphics API for drawing shapes directly onto a PDF page while a document is being built. The entry point is Graph, a container placed on a page that holds a collection of Shape objects; three concrete shapes — Circle, Ellipse, and Line — derive from Shape and each express their geometry differently. Per-shape stroke and fill styling comes from GraphInfo, and the Graph container itself carries a BorderInfo for framing. This is unrelated to page rasterization (BmpDevice, JpegDevice, TiffDevice), which converts existing page content to raster image formats rather than drawing new geometry.


The Graph Container and Adding Shapes to a Page

Graph positions and sizes itself on the page through Left(), Top(), Width(), and Height(), and its IsChangePosition() flag controls whether that position can shift relative to other page content. The shapes it draws live in the vector returned by Shapes()std::vector<std::unique_ptr<Shape>> — populated by constructing a shape, configuring it, and moving it onto that vector. Once built, the Graph itself is placed on the page through Page.Paragraphs().Add(), the same collection used for other page-level content.

#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");

Drawing Circles and Ellipses

Circle and Ellipse each express geometry as plain numeric properties rather than a shared point or rectangle type. Circle is a center point and radius — PosX(), PosY(), Radius() — while Ellipse is a bounding box — Left(), Bottom(), Width(), Height(). Both inherit CheckBounds(containerWidth, containerHeight) from Shape, which reports whether the shape’s current geometry fits inside a container of the given size without altering the shape itself.

#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);

Drawing Lines with PositionArray

Line stores its geometry differently from Circle and Ellipse: PositionArray() returns a flat std::vector<float> holding an x, y pair for every point the line passes through, rather than a fixed set of named properties. Two points (four values) draw a straight segment; additional pairs draw a multi-segment path through those points in order. Like the other shapes, Line overrides CheckBounds(containerWidth, containerHeight) from 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);

Styling Shapes with GraphInfo and BorderInfo

Each shape reads and writes its stroke and fill styling through Shape.GraphInfo() / GraphInfo(value). GraphInfo defaults to a LineWidth() of 1.0f with an empty DashArray(), and also exposes Color(), FillColor(), DashArray()/DashPhase() for dashed strokes, IsDoubled() for a doubled line, and SkewAngleX()/SkewAngleY(), ScalingRateX()/ScalingRateY(), and RotationAngle() for transforming the stroke. Graph itself carries a BorderInfo, set through Border(), which groups one GraphInfo per edge — Left(), Right(), Top(), Bottom() — plus a RoundedBorderRadius(), and can be constructed by naming which edges to style with a BorderSide flag (BorderSide::Left | BorderSide::Right, or BorderSide::All for every edge).

#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);

Tips and Best Practices

  • Call CheckBounds against the owning Graph’s own Width()/Height(), not the page’s full media box — the check validates a shape’s geometry against whatever container size you pass in, it doesn’t look at the page automatically.
  • Add shapes to Shapes() before adding the Graph to Page.Paragraphs() — constructing a Circle, Ellipse, or Line has no visible effect until it is both appended to its owning Graph and that Graph is added to a page.
  • Group related shapes under a single Graph rather than one Graph per shape; Left()/Top()/Width()/Height() position the whole container once, and every shape in Shapes() is drawn relative to it.
  • Set Shape.GraphInfo() explicitly for shapes that need a visible fill or a non-default stroke — the default GraphInfo has a 1.0f line width, no fill, and an empty DashArray().
  • Build Line.PositionArray() as x0, y0, x1, y1, … pairs in the order the path should be drawn — an odd-length vector describes an incomplete point.

Common Issues

IssueCauseFix
A Circle, Ellipse, or Line never appears on the saved pageThe shape was constructed but never appended to its Graph’s Shapes(), or the Graph was never added to the pagePush the shape onto graph->Shapes() and call page.Paragraphs().Add(graph)
Shape draws with a default thin black strokeShape.GraphInfo() was never setCall shape->GraphInfo(customGraphInfo) before appending the shape to Shapes()
Line is missing or looks like a single pointPositionArray() has fewer than two (x, y) pairs, or an odd number of valuesSupply at least four values as x0, y0, x1, y1
Only some edges of the container border are styledBorderInfo was constructed with a BorderSide mask that excludes some edgesUse BorderSide::All, or OR together every BorderSide flag needed
CheckBounds unexpectedly returns falseDimensions passed to CheckBounds don’t match the container the shape is actually drawn inPass the owning Graph’s current Width()/Height()

FAQ

What is the difference between Circle and AnnotationType::Circle?

Circle (this guide) is a drawing shape that draws directly into page content through a Graph. AnnotationType::Circle and CircleAnnotation belong to a separate annotations API and represent a circle-shaped markup annotation instead — the two are unrelated despite the shared name.

Can a single Graph contain more than one shape?

Yes. Graph.Shapes() returns the full std::vector<std::unique_ptr<Shape>> for that container, and any mix of Circle, Ellipse, and Line instances can be appended to it before the Graph is added to the page.

Do Circle and Ellipse share a geometry type?

No. Circle is a center point and radius (PosX, PosY, Radius); Ellipse is a bounding box (Left, Bottom, Width, Height). Both derive from Shape, but neither shares a common point or rectangle type with the other.

Is drawing shapes with Graph the same as rendering a page to an image?

No. Graph, Shape, Circle, Ellipse, and Line build new vector geometry into a page’s content while the document is generated. Converting an existing page to a raster image is a separate capability, handled by BmpDevice, JpegDevice, and TiffDevice.

What does GraphInfo.IsDoubled() do?

It marks the stroke as a doubled line rather than changing any other stroke property. It is a boolean flag read and set through IsDoubled() / IsDoubled(value) alongside the other GraphInfo stroke properties.


API Reference Summary

Class/MethodDescription
GraphShape container placed on a page; positions itself with Left(), Top(), Width(), Height()
Graph.Shapes()Returns the std::vector<std::unique_ptr<Shape>> drawn by this container
Graph.GraphInfo() / GraphInfo(value)Gets/sets the container’s own stroke and fill styling
Graph.Border() / Border(value)Gets/sets the BorderInfo framing the container
ShapeAbstract base for Circle, Ellipse, and Line; exposes GraphInfo() and CheckBounds()
Shape.CheckBounds(w, h)Reports whether the shape’s geometry fits inside a container of the given width/height
CircleCircle shape: PosX(), PosY(), Radius()
EllipseEllipse shape: bounding box via Left(), Bottom(), Width(), Height()
LineLine/path shape: PositionArray(), a flat std::vector<float> of x, y coordinates
GraphInfoStroke/fill styling: LineWidth(), Color(), FillColor(), DashArray(), DashPhase(), IsDoubled(), RotationAngle(), SkewAngleX()/SkewAngleY(), ScalingRateX()/ScalingRateY()
BorderInfoPer-edge container border: Left(), Right(), Top(), Bottom() (each a GraphInfo), plus RoundedBorderRadius()
BorderSideEnum selecting edges when constructing a BorderInfo: None, Left, Top, Right, Bottom, All, Box

See Also