Рисование векторной графики на странице PDF

Рисование векторной графики на странице 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(). Оба наследуют CheckBounds(containerWidth, containerHeight) от Shape, который сообщает, помещается ли текущая геометрия фигуры в контейнер заданного размера без изменения самой фигуры.

#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 переопределяет CheckBounds(containerWidth, containerHeight) из 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);

Стилизация фигур с помощью GraphInfo и BorderInfo

Каждая фигура читает и записывает стили обводки и заливки через Shape.GraphInfo() / GraphInfo(value). GraphInfo по умолчанию имеет LineWidth() 1.0f с пустым DashArray(), а также предоставляет Color(), FillColor(), DashArray()/DashPhase() для пунктирных обводок, IsDoubled() для двойной линии и SkewAngleX()/SkewAngleY(), ScalingRateX()/ScalingRateY() и RotationAngle() для преобразования обводки. Сам Graph содержит BorderInfo, задаваемый через Border(), который группирует один 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);

Советы и лучшие практики

  • Вызовите CheckBounds относительно собственного Width()/Height() принадлежащего Graph, а не полной медиабокса страницы — проверка валидирует геометрию фигуры относительно размера контейнера, который вы передаёте, и не смотрит автоматически на страницу.
  • Добавляйте фигуры в Shapes() перед тем, как добавить Graph в Page.Paragraphs() — создание 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’s Shapes(), либо Graph никогда не был добавлен на страницуПоместите фигуру в graph->Shapes() и вызовите page.Paragraphs().Add(graph)
Фигура рисуется с тонкой чёрной обводкой по умолчаниюShape.GraphInfo() никогда не был установленВызовите shape->GraphInfo(customGraphInfo) перед добавлением фигуры к Shapes()
Line отсутствует или выглядит как одиночная точкаУ PositionArray() менее двух пар (x, y) либо нечётное количество значенийПредоставьте как минимум четыре значения в виде x0, y0, x1, y1
Только некоторые края границы контейнера стилизованыBorderInfo был построен с BorderSide маской, которая исключает некоторые краяИспользуйте BorderSide::All, или объедините при помощи OR каждый нужный флаг BorderSide
CheckBounds неожиданно возвращает falseПереданные в CheckBounds размеры не соответствуют контейнеру, в котором фактически отрисовывается фигураПередайте текущие Width()/Height() владельца Graph

FAQ

В чём разница между Circle и AnnotationType::Circle?

Circle (это руководство) — это форма рисования, которая рисует непосредственно в содержимое страницы через Graph. AnnotationType::Circle и CircleAnnotation относятся к отдельному набору аннотаций API и представляют собой аннотацию разметки в виде круга — эти два элемента не связаны, несмотря на одинаковое название.

Может ли один Graph содержать более одной формы?

Да. Graph.Shapes() возвращает полный std::vector<std::unique_ptr<Shape>> для этого контейнера, и любой набор экземпляров Circle, Ellipse и Line может быть добавлен к нему до того, как Graph будет добавлен на страницу.

Имеют ли 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

См. также:

 Русский