在 PDF 页面上绘制矢量图形
在 PDF 页面上绘制矢量图形
Aspose.PDF FOSS for C++ 包含一个小型矢量图形 API,用于在构建文档的过程中直接在 PDF 页面上绘制形状。入口点是 Graph,它是放置在页面上的容器,保存一组 Shape 对象;三种具体形状——Circle、Ellipse 和 Line——继承自 Shape,并各自以不同方式表达其几何形状。每个形状的描边和填充样式来自 GraphInfo,而 Graph 容器本身携带一个用于框架的 BorderInfo。这与页面栅格化(BmpDevice、JpegDevice、TiffDevice)无关,后者将现有页面内容转换为栅格图像格式,而不是绘制新几何体。
图形容器及向页面添加形状
Graph 通过 Left()、Top()、Width() 和 Height() 在页面上定位并设定大小,其 IsChangePosition() 标志控制该位置是否可以相对于其他页面内容移动。它绘制的形状存放在由 Shapes() 返回的向量中——std::vector<std::unique_ptr<Shape>>——该向量通过构造形状、配置形状并将其移动到该向量中来填充。构建完成后,Graph 本身通过 Page.Paragraphs().Add() 放置在页面上,这与用于其他页面级内容的同一集合相同。
#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");绘制圆形和椭圆
Circle 和 Ellipse 都以纯数值属性而非共享的点或矩形类型来表达几何形状。Circle 是中心点和半径——PosX()、PosY()、Radius()——而 Ellipse 是边界框——Left()、Bottom()、Width()、Height()。两者都从 Shape 继承 CheckBounds(containerWidth, containerHeight),该属性报告形状当前的几何是否能够在给定尺寸的容器内适配,而不改变形状本身。
#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);使用 PositionArray 绘制直线
Line 以不同于 Circle 和 Ellipse 的方式存储其几何信息:PositionArray() 返回一个扁平的 std::vector<float>,其中包含每个线段经过的点的 x、y 对,而不是一组固定的命名属性。两个点(四个值)绘制一条直线段;额外的点对按顺序绘制经过这些点的多段路径。和其他形状一样,Line 会覆盖来自 Shape 的 CheckBounds(containerWidth, containerHeight)。
#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);使用 GraphInfo 和 BorderInfo 对形状进行样式设置
每个形状通过 Shape.GraphInfo() / GraphInfo(value) 读取和写入其描边和填充样式。GraphInfo 默认使用 LineWidth() 为 1.0f,并且空的 DashArray(),同时还公开 Color()、FillColor()、DashArray()/DashPhase() 用于虚线描边,IsDoubled() 用于双线,以及 SkewAngleX()/SkewAngleY()、ScalingRateX()/ScalingRateY()、RotationAngle() 用于变换描边。Graph 本身携带一个通过 Border() 设置的 BorderInfo,它将每条边分组为一个 GraphInfo —— Left()、Right()、Top()、Bottom() —— 加上一个 RoundedBorderRadius(),并且可以通过使用 BorderSide 标志来指定要为哪些边设置样式(BorderSide::Left | BorderSide::Right,或对所有边使用 BorderSide::All)。
#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);技巧与最佳实践
- 对所属的
Graph的自身Width()/Height()调用CheckBounds,而不是页面的完整媒体框——此检查会根据你传入的容器尺寸验证形状的几何,而不会自动查看页面。 - 在将
Graph添加到Page.Paragraphs()之前先将形状加入Shapes()——构造Circle、Ellipse或Line在它们被追加到所属的Graph并且该Graph被添加到页面之前不会产生可见效果。 - 将相关形状归入单个
Graph而不是每个形状使用一个Graph;Left()/Top()/Width()/Height()只需要定位一次整个容器,而Shapes()中的每个形状均相对于该容器绘制。 - 为需要可见填充或非默认描边的形状显式设置
Shape.GraphInfo()——默认的GraphInfo具有1.0f的线宽、无填充且空的DashArray()。 - 按路径应绘制的顺序将
Line.PositionArray()构建为x0, y0, x1, y1, …对——奇数长度的向量描述了一个不完整的点。
常见问题
| 问题 | 原因 | 修复 |
|---|---|---|
一个 Circle、Ellipse 或 Line 从未出现在已保存的页面上 | 形状已构建,但从未追加到其 Graph 的 Shapes(),或者 Graph 从未添加到页面 | 将形状推入 graph->Shapes() 并调用 page.Paragraphs().Add(graph) |
| 形状使用默认的细黑色描边绘制 | Shape.GraphInfo() 从未设置 | 在将形状追加到 Shapes() 之前调用 shape->GraphInfo(customGraphInfo) |
Line 缺失或看起来像单个点 | PositionArray() 的 (x, y) 对少于两个,或值的数量为奇数 | 提供至少四个值作为 x0, y0, x1, y1 |
| 仅对容器边框的部分边缘设置了样式 | BorderInfo 是使用排除某些边缘的 BorderSide 掩码构建的 | 使用 BorderSide::All,或将所有所需的 BorderSide 标志进行按位或 |
CheckBounds 意外返回了 false | 传递给 CheckBounds 的尺寸与实际绘制形状的容器不匹配 | 传递拥有者 Graph 当前的 Width()/Height() |
FAQ
Circle 与 AnnotationType::Circle 有何区别?
Circle(本指南)是一种绘图形状,通过 Graph 直接绘制到页面内容中。AnnotationType::Circle 和 CircleAnnotation 属于另一个注释 API,并表示圆形标记注释——尽管名称相同,但两者并不相关。
单个 Graph 能包含多个形状吗?
是的。Graph.Shapes() 返回该容器的完整 std::vector<std::unique_ptr<Shape>>,并且在将 Graph 添加到页面之前,可以向其中追加任意组合的 Circle、Ellipse 和 Line 实例。
Circle 与 Ellipse 共享几何类型吗?
否。Circle 是中心点和半径(PosX, PosY, Radius);Ellipse 是边界框(Left, Bottom, Width, Height)。两者都源自 Shape,但彼此之间既不共享相同的点,也不共享相同的矩形类型。
使用 Graph 绘制形状与将页面渲染为图像是同一回事吗?
否。Graph、Shape、Circle、Ellipse 和 Line 在生成文档时向页面内容中构建新的矢量几何。将已有页面转换为光栅图像是另一项功能,由 BmpDevice、JpegDevice 和 TiffDevice 处理。
GraphInfo.IsDoubled() 是做什么的?
它将笔画标记为双线,而不改变其他任何笔画属性。它是一个布尔标志,通过 IsDoubled() / IsDoubled(value) 与其他 GraphInfo 笔画属性一起读取和设置。
API Reference 摘要
| 类/方法 | 描述 |
|---|---|
Graph | 放置在页面上的形状容器;使用 Left()、Top()、Width()、Height() 来定位自身 |
Graph.Shapes() | 返回此容器绘制的 std::vector<std::unique_ptr<Shape>> |
Graph.GraphInfo() / GraphInfo(value) | 获取/设置容器自身的描边和填充样式 |
Graph.Border() / Border(value) | 获取/设置围绕容器的 BorderInfo |
Shape | 用于 Circle、Ellipse 和 Line 的抽象基类;公开 GraphInfo() 和 CheckBounds() |
Shape.CheckBounds(w, h) | 报告形状的几何是否适合放入给定宽度/高度的容器中 |
Circle | 圆形: PosX(), PosY(), Radius() |
Ellipse | 椭圆形: 边界框通过 Left(), Bottom(), Width(), Height() |
Line | 线/路径形状:PositionArray(),一个平面std::vector<float>,由 x、y 坐标构成 |
GraphInfo | 描边/填充样式:LineWidth(),Color(),FillColor(),DashArray(),DashPhase(),IsDoubled(),RotationAngle(),SkewAngleX()/SkewAngleY(),ScalingRateX()/ScalingRateY() |
BorderInfo | 每条边的容器边框:Left(),Right(),Top(),Bottom()(每个都是GraphInfo),以及RoundedBorderRadius() |
BorderSide | 在构建BorderInfo时选择边的枚举:None,Left,Top,Right,Bottom,All,Box |