Dostosuj kształty prezentacji w PHP

Przegląd

Ten artykuł wyjaśnia, jak dostosować kształty prezentacji w Aspose.Slides poprzez edycję geometrii kształtu za pomocą punktów edycji i ścieżek geometrycznych. Pokazuje, jak pracować z GeometryPath aby modyfikować istniejące kształty, wykonywać podstawowe operacje edycji ścieżek, dodawać lub usuwać punkty oraz zastosować zaktualizowaną geometrię z powrotem do kształtu. Pokazuje również, jak tworzyć własne i złożone kształty, budować kształty z zaokrąglonymi narożnikami, określić, czy geometria kształtu jest zamknięta, przeanalizować elementy kształtu oraz przekonwertować obrysy tekstu Java AWT na ścieżki geometryczne.

Zmienianie kształtu za pomocą punktów edycji

  • Przesuń róg do środka lub na zewnątrz.
  • Dostosuj krzywiznę w punkcie.
  • Dodaj nowe punkty.

Edycja punktów pozwala modyfikować kontur kształtu lub użyć istniejącego kształtu jako podstawy dla nowego.

Wskazówki dotyczące edycji kształtów

overview_image

Podczas edycji kształtów w PowerPoint, pamiętaj o następujących pojęciach:

  • Kształt (lub jego ścieżka) może być zamknięta lub otwarta.
  • Ścieżka kształtu składa się z punktów kotwiczących połączonych odcinkami.
  • Odcinek może być prosty lub zakrzywiony. Punkty kotwiczące i uchwyty kontrolne określają jego kształt.
  • Punkty kotwiczące występują jako punkty narożne, proste lub gładkie:
    • W punkcie narożnym łączące się odcinki mogą spotykać się pod kątem.
    • W punkcie gładkim oba uchwyty kontrolne są ustawione w jednej linii i znajdują się w równych odległościach od punktu kotwiczącego.
    • W punkcie prostym oba uchwyty kontrolne są ustawione w jednej linii, ale mogą mieć różne odległości od punktu kotwiczącego.
  • Przesuwanie punktów kotwiczących lub regulacja ich uchwytów zmienia kontur kształtu.

Aby edytować kształty PowerPoint przy użyciu punktów edycji, Aspose.Slides udostępnia klasę GeometryPath.

Proste operacje edycji

Użyj poniższych metod, aby edytować ścieżkę geometryczną. Przeciążenia przyjmujące indeks wstawiają odcinek na tej zero‑indeksowanej pozycji w tablicy odcinków ścieżki.

Operacja Metoda
Dodaj lub wstaw prostą linię. GeometryPath::lineTo
Dodaj lub wstaw krzywą Bézier o stopniu trzecim z dwoma punktami kontrolnymi i punktem końcowym. GeometryPath::cubicBezierTo
Dodaj lub wstaw kwadratową krzywą Bézier z jednym punktem kontrolnym i punktem końcowym. GeometryPath::quadraticBezierTo
Dodaj łuk eliptyczny o podanych wymiarach i kątach w stopniach. GeometryPath::arcTo
Zamknij bieżącą figurę, łącząc jej punkt końcowy z punktem początkowym. GeometryPath::closeFigure
Ustaw punkt początkowy figury. GeometryPath::moveTo
Usuń odcinek o indeksie zerowym. GeometryPath::removeAt

Dodaj własne punkty do kształtu

Ten przykład wstawia dwa punkty do prostokątnego kształtu za pomocą GeometryPath::lineTo i zastosowuje edytowaną ścieżkę przy użyciu GeometryShape::setGeometryPath. Współrzędne są lokalne względem kształtu: oba nowe punkty znajdują się w jego centrum, 100 punktów od lewej krawędzi i 50 punktów od górnej. Indeksy odcinków 1 i 4 umieszczają je na górnej i dolnej krawędzi, ściągając te krawędzie do środka.

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();
}

example1_image

Usuń punkty z kształtu

Ten przykład modyfikuje obrys w kształcie serca, usuwając odcinek o indeksie zerowym 2 za pomocą GeometryPath::removeAt. Zastosowanie zmodyfikowanej ścieżki przy użyciu GeometryShape::setGeometryPath aktualizuje kontur kształtu.

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();
}

example2_image

Utwórz własny kształt

Ten przykład tworzy pięcioramienną gwiazdę przy pomocy GeometryPath. Naprzemienne promienie zewnętrzne i wewnętrzne wynoszące odpowiednio 100 i 50 punktów określają szczyty i wcięcia; krok 72‑stopni równomiernie rozmieszcza pięć szczytów. Ścieżka jest zamykana metodą GeometryPath::closeFigure i zastosowana do prostokątnego kształtu przy użyciu GeometryShape::setGeometryPath, zastępując jego obrys.

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();
}

example3_image

Utwórz złożony własny kształt

Ten przykład definiuje dwa zamknięte obiekty GeometryPath dla górnej i dolnej jednej trzeciej kształtu. Zastosowanie obu ścieżek przy użyciu GeometryShape::setGeometryPaths tworzy pojedynczy złożony kształt z przerwą w środkowej jednej trzeciej.

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();
}

example4_image

Utwórz własny kształt z zaokrąglonymi narożnikami

Ten przykład łączy proste odcinki z GeometryPath::arcTo, aby stworzyć kształt z wklęsłymi narożnikami. Rozmiar każdego narożnika określa szerokość i wysokość łuku w punktach, a przemieszczenie -90 stopni rysuje elipsę ćwiartkową w kierunku przeciwnym do ruchu wskazówek zegara. Gotowa ścieżka jest zastosowana przy użyciu GeometryShape::setGeometryPath, a prezentacja jest zapisywana w formacie 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();
}

Sprawdź, czy geometria kształtu jest zamknięta

Poniższy pomocnik sprawdza, czy każda niepusta ścieżka geometryczna kończy się odcinkiem zamykającym. Odczytuje GeometryPath::getPathData i porównuje polecenie ostatniego odcinka PathSegment::getPathCommand z PathCommandType::Close. Zwraca false, gdy nie ma niepustych ścieżek. Sprawdza on polecenie końcowe każdej ścieżki; nie analizuje wszystkich podfigur w ścieżce. Przykład wypisuje true dla prostokąta.

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();
}

Wyodrębnij i przeanalizuj elementy kształtu

Użyj GeometryShape::createShapeElements, aby przeanalizować istniejący kształt jako elementy zawierające punkty ścieżki i typy punktów. Skorzystaj z każdego zwróconego ShapeElement , aby pobrać tablice punktów i typów za pomocą metod getPathPoints i getPathTypes. Ta reprezentacja jest przydatna do analizy konturów lub przekazywania geometrii do własnych narzędzi rysujących.

Przykład otwiera plik input.pptx, znajduje pierwszy kształt geometryczny bezpośrednio na pierwszym slajdzie i wypisuje jego odcinki ścieżki. Użyj prezentacji zawierającej kształt z odcinkami prostymi i zakrzywionymi, aby zobaczyć oba rodzaje wyników. Nie przeszukuje on grup.

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; // Zachowaj typ odcinka, pomijając flagi.
                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; // Zużyj oba punkty kontrolne i punkt końcowy.
                        break;
                }
                if (($types[$i] & 0x80) !== 0) {
                    echo "Close subpath back to its starting point." . PHP_EOL;
                }
            }
        }
    }
} finally {
    $presentation->dispose();
}

Tablice odpowiadają sobie po indeksie: $points[$i] jest punktem, a $types[$i] opisuje jego rolę. Typ 0 rozpoczyna figurę, a typ 1 rysuje linię od poprzedniego punktu do bieżącego. Odcinek krzywej Bézier trzeciego stopnia zaczyna się od poprzedniego punktu końcowego i używa trzech kolejnych wpisów typu 3: dwa punkty kontrolne, a następnie nowy punkt końcowy. Pętla zużywa te trzy wpisy razem i sprawdza, czy punkt końcowy zamyka figurę.

Typy punktów zawierają również flagi. Maskuj przy użyciu 0x07, aby zidentyfikować typ odcinka, oraz testuj osobno 0x80, aby wykryć zamkniętą podścieżkę. Na przykład 129 (0x81) oznacza końcowy punkt linii, który zamyka podścieżkę, a 131 (0x83) oznacza końcowy punkt Bézier, który również zamyka podścieżkę. PHP/Java Bridge zwraca tablicę bajtów jako łańcuch binarny; rozwinięcie go przy pomocy “C*” daje nieujemne wartości bajtów. Maskowanie zachowuje zamierzone wartości bitów. Porównywanie surowej wartości jedynie z 1 lub 3 pominęłoby te przypadki. Inne flagi, takie jak kreska (0x10) i znacznik (0x20), nie zmieniają podstawowego typu odcinka.

Współrzędne i przekształcenia.
Punkty są mierzone w punktach (1/72 cala), przy czym X rośnie w prawo, a Y w dół. Dla niepogrupowanego kształtu wyodrębnione współrzędne już zawierają pozycję zwróconą przez getX i getY oraz odzwierciedlają jego aktualną szerokość i wysokość. Na przykład prostokąt nieobrócony o współrzędnych (100, 200) i rozmiarze 120 × 80 ma granice konturu (100, 200)–(220, 280). Nie dodawaj ponownie pozycji kształtu. Odejmij jego X i Y, aby uzyskać współrzędne względem nieobróconej ramki.

Wyodrębnione punkty nie uwzględniają obrotu kształtu, zwróconego przez getRotation, ani odbić poziomych i pionowych. Aby odtworzyć wyświetlany kontur, zastosuj te przekształcenia wokół środka kształtu. Dla grupowanych kształtów uwzględnij także mapowanie współrzędnych grupy i przekształcenia; nie zakładaj, że wyodrębnione punkty są ostatecznymi współrzędnymi slajdu ani nie stosuj podwójnego przesunięcia lub skalowania, jeśli już jest ono odzwierciedlone w ramce dziecka.

Inspekcja vs. edycja. Wyodrębnione tablice opisują kontur; ich zmiana nie aktualizuje oryginalnego kształtu. Wybierz to podejście, gdy Twoja analiza lub kod rysujący potrzebuje tablic punktów/typów. Użyj getGeometryPaths, gdy potrzebujesz poleceń geometrii w lokalnym układzie współrzędnych kształtu lub chcesz edytować jego geometrię. Zastosuj edytowane ścieżki przy użyciu setGeometryPath lub setGeometryPaths.

Konwertuj kształt Java AWT na GeometryPath

Ten przykład łączy prostokątny obrys z tekstem skonwertowanym na geometrię. Metoda setFillMode obrysu wyłącza jego wypełnienie. GlyphVector tworzy obrys tekstu w java.awt.Shape. Jego PathIterator dostarcza segmenty move, line, quadratic, cubic i zamykające, które przykład odtwarza na GeometryPath.

Przykład używa tablicy floatów Java, aby PHP/Java Bridge zachował współrzędne zapisane przez iterator. Tekst używa czcionki Arial o rozmiarze 40 punktów. Obrys jest przesunięty o 20 punktów w poziomie i umieszczony tak, aby jego wizualna górna krawędź znajdowała się 10 punktów poniżej górnej krawędzi kształtu. Ścieżka tekstowa jest wypełniona, podczas gdy pierwotny prostokąt pozostaje obrysem. Zastosowanie obu ścieżek przy użyciu GeometryShape::setGeometryPaths tworzy pojedynczy kształt zawierający prostokąt i tekst.

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();
}

example5_image

FAQ

Co się stanie z wypełnieniem i obrysem po zastąpieniu geometrii?
Styl pozostaje przypisany do kształtu; zmienia się jedynie kontur. Wypełnienie i obrys są automatycznie stosowane do nowej geometrii.

Jak prawidłowo obrócić własny kształt wraz z jego geometrią?
Użyj metody setRotation kształtu; geometria obraca się wraz z kształtem, ponieważ jest powiązana z jego własnym układem współrzędnych.

Czy mogę skonwertować własny kształt na obraz, aby „zablokować” wynik?
Tak. Wyeksportuj wymaganą slide lub sam shape do formatu rastrowego; ułatwia to dalszą pracę z złożonymi geometriami.