Dostosuj kształty prezentacji w Javie
Przegląd
Ten artykuł wyjaśnia, jak dostosować kształty prezentacji w Aspose.Slides poprzez edycję geometrii kształtu przy użyciu punktów edycji i ścieżek geometrycznych. Pokazuje, jak pracować z GeometryPath i IGeometryPath 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.
Dodatkowo demonstruje, jak tworzyć niestandardowe i złożone kształty, budować kształty z zaokrąglonymi rogami, określać, czy geometria kształtu jest zamknięta, przeglądać elementy kształtu i konwertować kontury tekstu Java AWT na ścieżki geometryczne.
Zmiana kształtu za pomocą punktów edycji
W PowerPoint możesz używać Edit Points, aby modyfikować kwadrat:
- Przesuń róg do wewnątrz lub na zewnątrz.
- Dostosuj krzywiznę w punkcie.
- Dodaj nowe punkty.
Edycja punktów pozwala zmienić obrys kształtu lub użyć istniejącego kształtu jako podstawy dla nowego.
Wskazówki dotyczące edycji kształtów

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ęty lub otwarty.
- Ścieżka kształtu składa się z punktów zakotwienia połączonych odcinkami.
- Odcinek może być prosty lub krzywy. Punkty zakotwienia i uchwyty kontrolne określają jego kształt.
- Punkty zakotwienia występują jako punkty narożne, proste lub płynne:
- W punkcie narożnym sąsiadujące odcinki mogą łączyć się pod kątem.
- W punkcie płynnym dwa uchwyty kontrolne są wyrównane i znajdują się w równej odległości od punktu zakotwienia.
- W punkcie prostym dwa uchwyty kontrolne są wyrównane, ale mogą mieć różne odległości od punktu zakotwienia.
- Przesuwanie punktów zakotwienia lub dostosowywanie ich uchwytów zmienia kontur kształtu.
Aby edytować kształty PowerPoint za pomocą punktów edycji, Aspose.Slides udostępnia klasę GeometryPath i interfejs IGeometryPath.
- Instancja GeometryPath reprezentuje ścieżkę geometryczną obiektu IGeometryShape.
- Aby pobrać GeometryPath z instancji IGeometryShape, możesz użyć metody IGeometryShape.getGeometryPaths.
- Aby ustawić GeometryPath dla kształtu, możesz użyć następujących metod: IGeometryShape.setGeometryPath dla kształtów z pojedynczą ścieżką oraz IGeometryShape.setGeometryPaths dla kształtów z wieloma ścieżkami.
- Aby dodać odcinki, możesz używać metod z interfejsu IGeometryPath.
- Używając metod IGeometryPath.setStroke i IGeometryPath.setFillMode, możesz ustawić wygląd ścieżki geometrycznej.
- Używając metody IGeometryPath.getPathData, możesz pobrać ścieżkę geometryczną GeometryShape jako tablicę odcinków ścieżki.
- Aby uzyskać dodatkowe opcje dostosowywania geometrii kształtu, możesz odczytać java.awt.Shape za pomocą jego PathIterator i odtworzyć odcinki na GeometryPath.
Proste operacje edycji
Użyj poniższych metod, aby edytować ścieżkę geometryczną. Przeciążenia przyjmujące indeks wstawiają odcinek w podanej pozycji zerowo‑indeksowanej w tablicy odcinków ścieżki.
| Operacja | Metoda |
|---|---|
| Dodaj lub wstaw prostą linię. | GeometryPath.lineTo |
| Dodaj lub wstaw krzywą Béziera trzeciego stopnia z dwoma punktami kontrolnymi i punktem końcowym. | GeometryPath.cubicBezierTo |
| Dodaj lub wstaw krzywą Béziera drugiego stopnia z jednym punktem kontrolnym i punktem końcowym. | GeometryPath.quadraticBezierTo |
| Dodaj eliptyczny łuk 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 pod zerowo‑indeksowanym indeksem. | GeometryPath.removeAt |
Dodaj niestandardowe punkty do kształtu
Ten przykład wstawia dwa punkty do prostokątnego kształtu przy użyciu GeometryPath.lineTo i stosuje zmodyfikowaną ścieżkę za pomocą IGeometryShape.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, wciągając te krawędzie do środka.
import com.aspose.slides.*;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);
IGeometryPath geometryPath = shape.getGeometryPaths()[0];
geometryPath.lineTo(100, 50, 1);
geometryPath.lineTo(100, 50, 4);
shape.setGeometryPath(geometryPath);
} finally {
presentation.dispose();
}

Usuń punkty z kształtu
Ten przykład modyfikuje obrys serca, usuwając odcinek pod zerowo‑indeksowanym indeksem 2 przy użyciu GeometryPath.removeAt. Zastosowanie zmodyfikowanej ścieżki za pomocą IGeometryShape.setGeometryPath aktualizuje kontur kształtu.
import com.aspose.slides.*;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Heart, 100, 100, 300, 300);
IGeometryPath path = shape.getGeometryPaths()[0];
path.removeAt(2);
shape.setGeometryPath(path);
} finally {
presentation.dispose();
}

Utwórz niestandardowy kształt
Ten przykład buduje pięciopunktową gwiazdę przy użyciu GeometryPath. Naprzemienne promienie zewnętrzne i wewnętrzne o wartościach 100 i 50 punktów definiują wierzchołki i wcięcia; krok 72 stopni równomiernie rozmieszcza pięć wierzchołków. Ścieżka jest zamykana metodą GeometryPath.closeFigure i stosowana do prostokątnego kształtu przy pomocy IGeometryShape.setGeometryPath, zastępując jego obrys.
import com.aspose.slides.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
List<Point2D.Float> points = new ArrayList<>();
float outerRadius = 100f;
float innerRadius = 50f;
int angleStep = 72;
for (int angle = -90; angle < 270; angle += angleStep)
{
double radians = angle * (Math.PI / 180f);
double x = outerRadius * Math.cos(radians);
double y = outerRadius * Math.sin(radians);
Point2D.Float point = new Point2D.Float((float)x + outerRadius, (float)y + outerRadius);
points.add(point);
radians = Math.PI * (angle + angleStep / 2) / 180.0;
x = innerRadius * Math.cos(radians);
y = innerRadius * Math.sin(radians);
point = new Point2D.Float((float)x + outerRadius, (float)y + outerRadius);
points.add(point);
}
GeometryPath starPath = new GeometryPath();
starPath.moveTo(points.get(0));
for (int i = 1; i < points.size(); i++)
{
starPath.lineTo(points.get(i));
}
starPath.closeFigure();
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, outerRadius * 2, outerRadius * 2);
shape.setGeometryPath(starPath);
} finally {
presentation.dispose();
}

Utwórz złożony niestandardowy kształt
Ten przykład definiuje dwa zamknięte obiekty GeometryPath dla górnej i dolnej trzeciej części kształtu. Zastosowanie obu ścieżek przy pomocy IGeometryShape.setGeometryPaths tworzy jeden złożony kształt z przerwą w środkowej trzeciej części.
import com.aspose.slides.*;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);
GeometryPath topPath = new GeometryPath();
topPath.moveTo(0, 0);
topPath.lineTo(shape.getWidth(), 0);
topPath.lineTo(shape.getWidth(), shape.getHeight() / 3);
topPath.lineTo(0, shape.getHeight() / 3);
topPath.closeFigure();
GeometryPath bottomPath = new GeometryPath();
bottomPath.moveTo(0, shape.getHeight() / 3 * 2);
bottomPath.lineTo(shape.getWidth(), shape.getHeight() / 3 * 2);
bottomPath.lineTo(shape.getWidth(), shape.getHeight());
bottomPath.lineTo(0, shape.getHeight());
bottomPath.closeFigure();
shape.setGeometryPaths(new GeometryPath[] { topPath, bottomPath });
} finally {
presentation.dispose();
}

Utwórz niestandardowy kształt z zaokrąglonymi rogami
Ten przykład łączy odcinki proste z metodą GeometryPath.arcTo w celu utworzenia kształtu z wewnętrznie zaokrąglonymi rogami. Rozmiar każdego rogu określa szerokość i wysokość łuku w punktach, a obrót –90 stopni rysuje przeciwzegarowy ćwiartkowy elipsę. Gotowa ścieżka jest stosowana metodą IGeometryShape.setGeometryPath, a prezentacja zapisywana jest w formacie PPTX.
import com.aspose.slides.*;
import java.awt.geom.Point2D;
float shapeX = 20f;
float shapeY = 20f;
float shapeWidth = 300f;
float shapeHeight = 200f;
float leftTopSize = 50f;
float rightTopSize = 20f;
float rightBottomSize = 40f;
float leftBottomSize = 10f;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Custom, shapeX, shapeY, shapeWidth, shapeHeight);
Point2D.Float topLeft = new Point2D.Float(leftTopSize, 0);
Point2D.Float topRight = new Point2D.Float(shapeWidth - rightTopSize, 0);
Point2D.Float rightBottom = new Point2D.Float(shapeWidth, shapeHeight - rightBottomSize);
Point2D.Float bottomLeft = new Point2D.Float(leftBottomSize, shapeHeight);
Point2D.Float leftTop = new Point2D.Float(0, leftTopSize);
GeometryPath 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();
}
Dowiedz się, czy geometria kształtu jest zamknięta
Poniższy pomocniczy kod sprawdza, czy każda niepusta ścieżka geometryczna kończy się odcinkiem zamykającym. Odczytuje IGeometryPath.getPathData i porównuje polecenie ostatniego odcinka IPathSegment.getPathCommand z PathCommandType.Close. Zwraca false, gdy nie ma niepustych ścieżek. Sprawdza polecenie końcowe każdej ścieżki; nie przegląda wszystkich podfigur w obrębie jednej ścieżki. Przykład wypisuje true dla prostokąta.
import com.aspose.slides.*;
class GeometryInspector {
boolean isGeometryClosed(IGeometryShape geometryShape) {
boolean hasNonemptyPath = false;
for (IGeometryPath geometryPath : geometryShape.getGeometryPaths()) {
IPathSegment[] segments = geometryPath.getPathData();
if (segments.length == 0) {
continue;
}
hasNonemptyPath = true;
IPathSegment lastSegment = segments[segments.length - 1];
if (lastSegment.getPathCommand() != PathCommandType.Close) {
return false;
}
}
return hasNonemptyPath;
}
}
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);
GeometryInspector inspector = new GeometryInspector();
boolean closed = inspector.isGeometryClosed(shape);
System.out.println(closed);
} finally {
presentation.dispose();
}
Wyodrębnij i sprawdź elementy kształtu
Użyj IGeometryShape.createShapeElements aby przeanalizować istniejący kształt jako elementy zawierające punkty ścieżki i typy punktów. Rzutuj każdy zwrócony IShapeElement na ShapeElement aby uzyskać tablice punktów i typów przez metody getPathPoints i getPathTypes. Taka reprezentacja jest przydatna przy analizie konturu lub przekazywaniu geometrii do własnych narzędzi rysunkowych.
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 krzywymi, aby zobaczyć oba rodzaje wyjścia. Nie przeszukuje grup.
import com.aspose.slides.*;
import java.awt.geom.Point2D;
Presentation presentation = new Presentation("input.pptx");
try {
ISlide slide = presentation.getSlides().get_Item(0);
IGeometryShape shape = null;
for (IShape candidate : slide.getShapes()) {
if (candidate instanceof IGeometryShape) {
shape = (IGeometryShape) candidate;
break;
}
}
if (shape == null) {
System.out.println("No geometry shape found on the first slide.");
} else {
for (IShapeElement element : shape.createShapeElements()) {
ShapeElement shapeElement = (ShapeElement) element;
Point2D.Float[] points = shapeElement.getPathPoints();
byte[] types = shapeElement.getPathTypes();
System.out.println("Shape element:");
for (int i = 0; i < points.length; i++) {
int pointType = types[i] & 0x07; // Zachowaj typ odcinka, wyłączając flagi.
switch (pointType) {
case 0:
System.out.println("Start figure at " + points[i]);
break;
case 1:
System.out.println("Line to " + points[i]);
break;
case 3:
System.out.println("Cubic Bezier: controls " + points[i] + ", " + points[i + 1] + "; end " + points[i + 2]);
i += 2; // Consume both control points and the endpoint.
break;
}
if ((types[i] & 0x80) != 0) {
System.out.println("Close subpath back to its starting point.");
}
}
}
}
} finally {
presentation.dispose();
}
Tablice odpowiadają sobie indeksami: 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éziera trzeciego stopnia zaczyna się w poprzednim punkcie końcowym i używa trzech kolejnych wpisów typu 3: dwóch punktów kontrolnych i nowego punktu końcowego. Pętla zużywa te trzy wpisy razem i sprawdza punkt końcowy pod kątem zamknięcia.
Typy punktów zawierają również flagi. Maskuj przy pomocy 0x07, aby zidentyfikować typ odcinka, a testuj 0x80 osobno, aby wykryć zamkniętą podścieżkę. Na przykład 129 (0x81) oznacza punkt końcowy linii zamykający podścieżkę, a 131 (0x83) oznacza punkt końcowy Béziera zamykający podścieżkę. Java przechowuje te flagi w bajtach ze znakiem; maskowanie zachowuje zamierzone wartości bitowe. Porównywanie surowej wartości tylko z 1 lub 3 pominęłoby te przypadki. Inne flagi, takie jak przerywanie (0x10) i marker (0x20), nie zmieniają podstawowego typu odcinka.
Współrzędne i transformacje. Punkty są mierzone w punktach (1/72 cala), przy czym X rośnie w prawo, a Y w dół. Dla niezgroupowanego 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 nieobrócony prostokąt w (100, 200) o 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 poziomych i pionowych odbić. Aby odtworzyć wyświetlany kontur, zastosuj te transformacje wokół środka kształtu. Dla grupowanych kształtów uwzględnij także mapowanie współrzędnych grupy i jej transformacje; 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ż zostały odzwierciedlone w ramce dziecka.
Inspekcja kontra 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żywaj getGeometryPaths gdy potrzebujesz poleceń geometrii w lokalnym układzie współrzędnych kształtu lub chcesz edytować jego geometrię. Zastosuj edytowane ścieżki za pomocą setGeometryPath lub setGeometryPaths.
Konwertuj kształt Java AWT do GeometryPath
Ten przykład łączy prostokątny obrys z tekstem przekonwertowanym na geometrię. Metoda setFillMode wyłącza wypełnienie obrysu. GlyphVector tworzy kontur tekstu w java.awt.Shape. Jego PathIterator dostarcza odcinki move, line, quadratic, cubic i closing, które przykład odtwarza na GeometryPath.
Tekst używa czcionki Arial 40‑punktowej. 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 oryginalny prostokąt pozostaje obrysem. Zastosowanie obu ścieżek przy pomocy IGeometryShape.setGeometryPaths tworzy pojedynczy kształt zawierający prostokąt i tekst.
import com.aspose.slides.*;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.GlyphVector;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.geom.PathIterator;
import java.awt.image.BufferedImage;
Presentation presentation = new Presentation();
try {
// Utwórz nowy kształt
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 300, 100);
// Pobierz ścieżkę geometryczną kształtu
IGeometryPath originalPath = shape.getGeometryPaths()[0];
originalPath.setFillMode(PathFillModeType.None);
// Utwórz nową ścieżkę graficzną z tekstem
Shape graphicsPath;
Font font = new Font("Arial", Font.PLAIN, 40);
String text = "Text in shape";
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
try
{
FontRenderContext fontContext = graphics.getFontRenderContext();
GlyphVector glyphVector = font.createGlyphVector(fontContext, text);
Rectangle2D textBounds = glyphVector.getVisualBounds();
float baseline = (float) -textBounds.getY() + 10;
graphicsPath = glyphVector.getOutline(20f, baseline);
}
finally {
graphics.dispose();
}
// Konwertuj ścieżkę graficzną na ścieżkę geometryczną
IGeometryPath textPath = new GeometryPath();
PathIterator pathIterator = graphicsPath.getPathIterator(null);
float[] points = new float[6];
while (!pathIterator.isDone())
{
int segmentType = pathIterator.currentSegment(points);
switch (segmentType)
{
case PathIterator.SEG_MOVETO:
textPath.moveTo(points[0], points[1]);
break;
case PathIterator.SEG_LINETO:
textPath.lineTo(points[0], points[1]);
break;
case PathIterator.SEG_QUADTO:
textPath.quadraticBezierTo(points[0], points[1], points[2], points[3]);
break;
case PathIterator.SEG_CUBICTO:
textPath.cubicBezierTo(points[0], points[1], points[2], points[3], points[4], points[5]);
break;
case PathIterator.SEG_CLOSE:
textPath.closeFigure();
break;
}
pathIterator.next();
}
textPath.setFillMode(PathFillModeType.Normal);
// Ustaw kombinację nowej ścieżki geometrycznej i pierwotnej ścieżki geometrycznej na kształt
shape.setGeometryPaths(new IGeometryPath[] { originalPath, textPath });
} finally {
presentation.dispose();
}

FAQ
Co stanie się z wypełnieniem i konturem po zastąpieniu geometrii?
Styl pozostaje przypisany do kształtu; zmienia się tylko kontur. Wypełnienie i kontur są automatycznie stosowane do nowej geometrii.
Jak poprawnie obrócić niestandardowy kształt wraz z jego geometrią?
Użyj metody kształtu setRotation; geometria obraca się razem z kształtem, ponieważ jest związana z jego własnym układem współrzędnych.
Czy mogę przekonwertować niestandardowy kształt na obraz, aby „zablokować” wynik?
Tak. Wyeksportuj wymagany slajd lub sam kształt do formatu rastrowego; to upraszcza dalszą pracę z złożonymi geometriami.