Настройка фигур презентации в PHP
Обзор
В этой статье объясняется, как настраивать фигуры в презентациях Aspose.Slides, редактируя геометрию фигур с помощью точек редактирования и путей геометрии. Показано, как работать с GeometryPath для изменения существующих фигур, выполнения базовых операций редактирования пути, добавления или удаления точек и применения обновлённой геометрии обратно к фигуре.
Также демонстрируется, как создавать пользовательские и составные фигуры, строить фигуры со скруглёнными углами, определять, закрыта ли геометрия фигуры, исследовать элементы фигуры и преобразовывать контуры текста Java AWT в пути геометрии.
Изменение фигуры с помощью точек редактирования
В PowerPoint вы можете использовать Edit Points для изменения квадрата:
- Переместить угол внутрь или наружу.
- Отрегулировать кривизну в точке.
- Добавить новые точки.
Редактирование точек позволяет изменять контур фигуры или использовать существующую фигуру в качестве основы для новой.
Советы по редактированию фигур

При редактировании фигур в PowerPoint учитывайте следующие концепции:
- Фигура (или её путь) может быть закрытой или открытой.
- Путь фигуры состоит из опорных точек, соединённых сегментами.
- Сегмент может быть прямым или изогнутым. Опорные точки и управляющие рычаги определяют его форму.
- Опорные точки бывают угловыми, прямыми или гладкими:
- В угловой точке соединяющие сегменты могут встречаться под углом.
- В гладкой точке два управляющих рычага выровнены и находятся на одинаковом расстоянии от опорной точки.
- В прямой точке два управляющих рычага выровнены, но могут находиться на разном расстоянии от опорной точки.
- Перемещение опорных точек или корректировка их рычагов изменяет контур фигуры.
Для редактирования фигур PowerPoint через точки редактирования Aspose.Slides предоставляет класс GeometryPath.
- Экземпляр GeometryPath представляет путь геометрии объекта GeometryShape.
- Чтобы получить GeometryPath из экземпляра GeometryShape, можно использовать метод GeometryShape::getGeometryPaths.
- Чтобы задать GeometryPath для фигуры, можно использовать следующие методы: GeometryShape::setGeometryPath для фигур с одним путём и GeometryShape::setGeometryPaths для фигур с несколькими путями.
- Чтобы добавить сегменты, можно использовать методы из GeometryPath.
- С помощью методов GeometryPath::setStroke и GeometryPath::setFillMode можно задать внешний вид пути геометрии.
- С помощью метода GeometryPath::getPathData можно получить путь геометрии GeometryShape в виде массива сегментов пути.
- Для доступа к дополнительным параметрам настройки геометрии фигуры можно прочитать java.awt.Shape через её PathIterator и воспроизвести сегменты на GeometryPath.
Простые операции редактирования
Используйте следующие методы для редактирования пути геометрии. Перегрузки, принимающие индекс, вставляют сегмент в указанную позицию (нумерация с нуля) массива сегментов пути.
| Operation | Method |
|---|---|
| Добавить или вставить прямую линию. | GeometryPath::lineTo |
| Добавить или вставить кубическую кривую Безье с двумя управляющими точками и конечной точкой. | GeometryPath::cubicBezierTo |
| Добавить или вставить квадратичную кривую Безье с одной управляющей точкой и конечной точкой. | GeometryPath::quadraticBezierTo |
| Добавить эллиптическую дугу с указанными размерами и углами в градусах. | GeometryPath::arcTo |
| Замкнуть текущую фигуру, соединив её конечную точку с начальной точкой. | GeometryPath::closeFigure |
| Задать начальную точку фигуры. | GeometryPath::moveTo |
| Удалить сегмент по индексу, начиная с нуля. | GeometryPath::removeAt |
Добавление пользовательских точек к фигуре
В этом примере в прямоугольную фигуру вставляются две точки с помощью GeometryPath::lineTo и применяется отредактированный путь через GeometryShape::setGeometryPath. Координаты локальны относительно фигуры: обе новые точки находятся в её центре, на расстоянии 100 пунктов от левого края и 50 пунктов от верхнего. Индексы сегментов 1 и 4 размещают их на верхнем и нижнем краях, притягивая эти края внутрь.
use aspose\slides\Presentation;
use aspose\slides\ShapeType;
$presentation = new Presentation();
try {
$slide = $presentation->getSlides()->get_Item(0);
$shape = $slide->getShapes()->addAutoShape(ShapeType::Rectangle, 100, 100, 200, 100);
$geometryPath = $shape->getGeometryPaths()[0];
$geometryPath->lineTo(100, 50, 1);
$geometryPath->lineTo(100, 50, 4);
$shape->setGeometryPath($geometryPath);
} finally {
$presentation->dispose();
}

Удаление точек из фигуры
В этом примере контур в виде сердца изменяется удалением сегмента с нулевым индексом 2 с помощью GeometryPath::removeAt. Применение изменённого пути через GeometryShape::setGeometryPath обновляет контур фигуры.
use aspose\slides\Presentation;
use aspose\slides\ShapeType;
$presentation = new Presentation();
try {
$slide = $presentation->getSlides()->get_Item(0);
$shape = $slide->getShapes()->addAutoShape(ShapeType::Heart, 100, 100, 300, 300);
$path = $shape->getGeometryPaths()[0];
$path->removeAt(2);
$shape->setGeometryPath($path);
} finally {
$presentation->dispose();
}

Создание пользовательской фигуры
В этом примере создаётся пятиконечная звезда с помощью GeometryPath. Чередующиеся внешние и внутренние радиусы 100 и 50 пунктов определяют вершины и выемки; шаг в 72 градуса равномерно размещает пять вершин. Путь закрывается методом GeometryPath::closeFigure и применяется к прямоугольной фигуре через GeometryShape::setGeometryPath, заменяя её контур.
use aspose\slides\GeometryPath;
use aspose\slides\Presentation;
use aspose\slides\ShapeType;
$points = [];
$outerRadius = 100;
$innerRadius = 50;
$angleStep = 72;
for ($angle = -90; $angle < 270; $angle += $angleStep) {
$radians = deg2rad($angle);
$points[] = [$outerRadius * cos($radians) + $outerRadius, $outerRadius * sin($radians) + $outerRadius];
$radians = deg2rad($angle + $angleStep / 2);
$points[] = [$innerRadius * cos($radians) + $outerRadius, $innerRadius * sin($radians) + $outerRadius];
}
$starPath = new GeometryPath();
$starPath->moveTo($points[0][0], $points[0][1]);
for ($i = 1; $i < count($points); $i++) {
$starPath->lineTo($points[$i][0], $points[$i][1]);
}
$starPath->closeFigure();
$presentation = new Presentation();
try {
$slide = $presentation->getSlides()->get_Item(0);
$shape = $slide->getShapes()->addAutoShape(ShapeType::Rectangle, 100, 100, $outerRadius * 2, $outerRadius * 2);
$shape->setGeometryPath($starPath);
} finally {
$presentation->dispose();
}

Создание составной пользовательской фигуры
В этом примере определяются два замкнутых объекта GeometryPath для верхней и нижней трети фигуры. Применение обоих путей через GeometryShape::setGeometryPaths создаёт одну составную фигуру с зазором в средней трети.
use aspose\slides\GeometryPath;
use aspose\slides\Presentation;
use aspose\slides\ShapeType;
$presentation = new Presentation();
try {
$slide = $presentation->getSlides()->get_Item(0);
$shape = $slide->getShapes()->addAutoShape(ShapeType::Rectangle, 100, 100, 200, 100);
$topPath = new GeometryPath();
$topPath->moveTo(0, 0);
$topPath->lineTo(java_values($shape->getWidth()), 0);
$topPath->lineTo(java_values($shape->getWidth()), java_values($shape->getHeight()) / 3);
$topPath->lineTo(0, java_values($shape->getHeight()) / 3);
$topPath->closeFigure();
$bottomPath = new GeometryPath();
$bottomPath->moveTo(0, java_values($shape->getHeight()) / 3 * 2);
$bottomPath->lineTo(java_values($shape->getWidth()), java_values($shape->getHeight()) / 3 * 2);
$bottomPath->lineTo(java_values($shape->getWidth()), java_values($shape->getHeight()));
$bottomPath->lineTo(0, java_values($shape->getHeight()));
$bottomPath->closeFigure();
$shape->setGeometryPaths([$topPath, $bottomPath]);
} finally {
$presentation->dispose();
}

Создание пользовательской фигуры со скруглёнными углами
В этом примере комбинируются прямые сегменты с GeometryPath::arcTo для создания фигуры со скруглёнными внутрь углами. Размер каждого угла задаёт ширину и высоту дуги в пунктах, а проход -90 градусов рисует дугу против часовой стрелки, представляющую четверть эллипса. Завершённый путь применяется через GeometryShape::setGeometryPath, и презентация сохраняется в формате PPTX.
use aspose\slides\GeometryPath;
use aspose\slides\Point2DFloat;
use aspose\slides\Presentation;
use aspose\slides\SaveFormat;
use aspose\slides\ShapeType;
$shapeX = 20;
$shapeY = 20;
$shapeWidth = 300;
$shapeHeight = 200;
$leftTopSize = 50;
$rightTopSize = 20;
$rightBottomSize = 40;
$leftBottomSize = 10;
$presentation = new Presentation();
try {
$slide = $presentation->getSlides()->get_Item(0);
$shape = $slide->getShapes()->addAutoShape(ShapeType::Custom, $shapeX, $shapeY, $shapeWidth, $shapeHeight);
$topLeft = new Point2DFloat($leftTopSize, 0);
$topRight = new Point2DFloat($shapeWidth - $rightTopSize, 0);
$rightBottom = new Point2DFloat($shapeWidth, $shapeHeight - $rightBottomSize);
$bottomLeft = new Point2DFloat($leftBottomSize, $shapeHeight);
$leftTop = new Point2DFloat(0, $leftTopSize);
$geometryPath = new GeometryPath();
$geometryPath->moveTo($topLeft);
$geometryPath->lineTo($topRight);
$geometryPath->arcTo($rightTopSize, $rightTopSize, 180, -90);
$geometryPath->lineTo($rightBottom);
$geometryPath->arcTo($rightBottomSize, $rightBottomSize, -90, -90);
$geometryPath->lineTo($bottomLeft);
$geometryPath->arcTo($leftBottomSize, $leftBottomSize, 0, -90);
$geometryPath->lineTo($leftTop);
$geometryPath->arcTo($leftTopSize, $leftTopSize, 90, -90);
$geometryPath->closeFigure();
$shape->setGeometryPath($geometryPath);
$presentation->save("output.pptx", SaveFormat::Pptx);
} finally {
$presentation->dispose();
}
Определение, закрыта ли геометрия фигуры
Следующая вспомогательная функция проверяет, заканчивается ли каждый непустой путь геометрии закрывающим сегментом. Она считывает GeometryPath::getPathData и сравнивает последнюю команду сегмента с помощью PathSegment::getPathCommand с PathCommandType::Close. Возвращает false, если нет непустых путей. Проверяется окончательная команда каждого пути; не анализируются все подфигуры внутри пути. Пример выводит true для прямоугольника.
use aspose\slides\PathCommandType;
use aspose\slides\Presentation;
use aspose\slides\ShapeType;
function isGeometryClosed($geometryShape)
{
$hasNonemptyPath = false;
foreach ($geometryShape->getGeometryPaths() as $geometryPath) {
$segments = java_values($geometryPath->getPathData());
if (count($segments) === 0) {
continue;
}
$hasNonemptyPath = true;
$lastSegment = $segments[count($segments) - 1];
if (java_values($lastSegment->getPathCommand()) !== PathCommandType::Close) {
return false;
}
}
return $hasNonemptyPath;
}
$presentation = new Presentation();
try {
$slide = $presentation->getSlides()->get_Item(0);
$shape = $slide->getShapes()->addAutoShape(ShapeType::Rectangle, 100, 100, 200, 100);
$closed = isGeometryClosed($shape);
echo ($closed ? "true" : "false") . PHP_EOL;
} finally {
$presentation->dispose();
}
Извлечение и проверка элементов фигуры
Используйте GeometryShape::createShapeElements для просмотра существующей фигуры в виде элементов, содержащих точки пути и типы точек. Каждый возвращённый ShapeElement используйте для получения массивов точек и типов через методы getPathPoints и getPathTypes. Такое представление полезно для анализа контура или передачи геометрии в пользовательские инструменты рисования.
В примере открывается файл input.pptx, находится первая геометрическая фигура на первом слайде и выводятся её сегменты пути. Используйте презентацию, содержащую фигуру с прямыми и изогнутыми сегментами, чтобы увидеть оба типа вывода. Поиск внутри групп не выполняется.
use aspose\slides\Presentation;
$presentation = new Presentation("input.pptx");
try {
$slide = $presentation->getSlides()->get_Item(0);
$shape = null;
$shapes = $slide->getShapes();
for ($shapeIndex = 0; $shapeIndex < java_values($shapes->size()); $shapeIndex++) {
$candidate = $shapes->get_Item($shapeIndex);
if (java_instanceof($candidate, java("com.aspose.slides.GeometryShape"))) {
$shape = $candidate;
break;
}
}
if ($shape === null) {
echo "No geometry shape found on the first slide." . PHP_EOL;
} else {
foreach ($shape->createShapeElements() as $element) {
$points = java_values($element->getPathPoints());
$types = array_values(unpack("C*", java_values($element->getPathTypes())));
echo "Shape element:" . PHP_EOL;
for ($i = 0; $i < count($points); $i++) {
$pointType = $types[$i] & 0x07; // Сохранить тип сегмента, исключая флаги.
switch ($pointType) {
case 0:
echo "Start figure at " . $points[$i] . PHP_EOL;
break;
case 1:
echo "Line to " . $points[$i] . PHP_EOL;
break;
case 3:
echo "Cubic Bezier: controls " . $points[$i] . ", " . $points[$i + 1] . "; end " . $points[$i + 2] . PHP_EOL;
$i += 2; // Consume both control points and the endpoint.
break;
}
if (($types[$i] & 0x80) !== 0) {
echo "Close subpath back to its starting point." . PHP_EOL;
}
}
}
}
} finally {
$presentation->dispose();
}
Массивы соответствуют по индексу: $points[$i] — точка, а $types[$i] описывает её роль. Тип 0 начинается фигуру, тип 1 рисует линию от предыдущей точки к текущей. Кубический сегмент Безье начинается в предыдущей конечной точке и использует три последовательных элемента типа 3: две управляющие точки и новую конечную точку. Цикл обрабатывает эти три записи вместе и проверяет конечную точку на замыкание.
Типы точек также содержат флаги. Маска 0x07 используется для определения типа сегмента, а проверка 0x80 отдельно — для обнаружения закрытого подпути. Например, 129 (0x81) означает конечную точку линии, закрывающую подпуть, а 131 (0x83) — конечную точку Безье, закрывающую подпуть. PHP/Java Bridge возвращает массив байтов как бинарную строку; распаковка её с помощью “C*” дает беззнаковые байтовые значения. Маскирование сохраняет требуемые битовые значения. Сравнение сырого значения только с 1 или 3 упустит эти случаи. Другие флаги, такие как черта (0x10) и маркер (0x20), не меняют базовый тип сегмента.
Координаты и преобразования. Точки измеряются в пунктах (1/72 дюйма), при этом X увеличивается вправо, а Y — вниз. Для не сгруппированной фигуры извлечённые координаты уже включают позицию, полученную через getX и getY, и отражают её текущую ширину и высоту. Например, не повернутый прямоугольник с координатами (100, 200) и размерами 120 × 80 имеет границы контура (100, 200)–(220, 280). Не добавляйте позицию фигуры повторно. Вычтите её X и Y, чтобы получить координаты относительно её ненаправленного кадра.
Извлечённые точки не учитывают вращение фигуры, возвращаемое через getRotation, а также горизонтальные и вертикальные отражения. Чтобы воспроизвести отображаемый контур, примените эти преобразования относительно центра фигуры. Для сгруппированных фигур также учитывайте сопоставление координат группы и её преобразования; не предполагайте, что извлечённые точки являются конечными координатами на слайде, и не применяйте трансляцию или масштаб дважды, если они уже учтены в кадре дочернего элемента.
Проверка против редактирования. Извлечённые массивы описывают контур; их изменение не обновляет исходную фигуру. Выбирайте этот подход, когда ваш код анализа или рисования требует массивов точек/типов. Используйте getGeometryPaths, когда нужны команды геометрии в локальной системе координат фигуры или требуется редактировать её геометрию. Применяйте отредактированные пути через setGeometryPath или setGeometryPaths.
Преобразование Java AWT Shape в GeometryPath
В этом примере комбинируется прямоугольный контур с текстом, преобразованным в геометрию. Метод setFillMode контура отключает его заливку. GlyphVector создаёт контур текста в виде java.awt.Shape. Его PathIterator предоставляет операции перемещения, линии, квадратичной, кубической и закрывающей сегментов, которые пример воспроизводит на GeometryPath.
В примере используется массив Java float, чтобы PHP/Java Bridge сохранял координаты, записанные итератором. Текст использует шрифт Arial размером 40 пунктов. Контур смещён на 20 пунктов по горизонтали и расположен так, что его визуальная верхняя часть находится на 10 пунктов ниже верхнего края фигуры. Путь текста заполнен, в то время как исходный прямоугольник остаётся контуром. Применение обоих путей через GeometryShape::setGeometryPaths создаёт одну фигуру, содержащую прямоугольник и текст.
use aspose\slides\GeometryPath;
use aspose\slides\PathFillModeType;
use aspose\slides\Presentation;
use aspose\slides\ShapeType;
$presentation = new Presentation();
try {
$slide = $presentation->getSlides()->get_Item(0);
$shape = $slide->getShapes()->addAutoShape(ShapeType::Rectangle, 100, 100, 300, 100);
$originalPath = $shape->getGeometryPaths()[0];
$originalPath->setFillMode(PathFillModeType::None);
$fontClass = java("java.awt.Font");
$imageClass = java("java.awt.image.BufferedImage");
$font = new Java("java.awt.Font", "Arial", $fontClass->PLAIN, 40);
$image = new Java("java.awt.image.BufferedImage", 100, 100, $imageClass->TYPE_INT_ARGB);
$graphics = $image->createGraphics();
try {
$fontContext = $graphics->getFontRenderContext();
$glyphVector = $font->createGlyphVector($fontContext, "Text in shape");
$textBounds = $glyphVector->getVisualBounds();
$baseline = -java_values($textBounds->getY()) + 10;
$graphicsPath = $glyphVector->getOutline(20.0, $baseline);
} finally {
$graphics->dispose();
}
$textPath = new GeometryPath();
$pathIterator = $graphicsPath->getPathIterator(null);
$iteratorClass = java("java.awt.geom.PathIterator");
$arrayClass = java("java.lang.reflect.Array");
$floatClass = java("java.lang.Float");
$coordinates = $arrayClass->newInstance($floatClass->TYPE, 6);
while (!java_values($pathIterator->isDone())) {
$segmentType = java_values($pathIterator->currentSegment($coordinates));
$points = java_values($coordinates);
switch ($segmentType) {
case java_values($iteratorClass->SEG_MOVETO):
$textPath->moveTo($points[0], $points[1]);
break;
case java_values($iteratorClass->SEG_LINETO):
$textPath->lineTo($points[0], $points[1]);
break;
case java_values($iteratorClass->SEG_QUADTO):
$textPath->quadraticBezierTo($points[0], $points[1], $points[2], $points[3]);
break;
case java_values($iteratorClass->SEG_CUBICTO):
$textPath->cubicBezierTo($points[0], $points[1], $points[2], $points[3], $points[4], $points[5]);
break;
case java_values($iteratorClass->SEG_CLOSE):
$textPath->closeFigure();
break;
}
$pathIterator->next();
}
$textPath->setFillMode(PathFillModeType::Normal);
$shape->setGeometryPaths([$originalPath, $textPath]);
} finally {
$presentation->dispose();
}

FAQ
Что произойдёт с заливкой и контуром после замены геометрии?
Стиль остаётся привязанным к фигуре; меняется только контур. Заливка и контур автоматически применяются к новой геометрии.
Как правильно повернуть пользовательскую фигуру вместе с её геометрией?
Используйте метод setRotation фигуры; геометрия вращается вместе с фигурой, потому что она привязана к её собственной системе координат.
Можно ли преобразовать пользовательскую фигуру в изображение, чтобы “зафиксировать” результат?
Да. Экспортируйте нужную slide область или саму shape в растровый формат; это упрощает дальнейшую работу со сложными геометриями.