Anpassa presentationsformer i Java

Översikt

Den här artikeln förklarar hur man anpassar presentationsformer i Aspose.Slides genom att redigera formgeometri via redigeringspunkter och geometrivägar. Den visar hur man arbetar med GeometryPath och IGeometryPath för att modifiera befintliga former, utföra grundläggande redigeringsoperationer, lägga till eller ta bort punkter och tillämpa uppdaterad geometri tillbaka på en form.

Den visar också hur man skapar anpassade och sammansatta former, bygger former med rundade hörn, avgör om en formgeometri är sluten, inspekterar formelement och konverterar Java AWT-textkonturer till geometrivägar.

Ändra en form med redigeringspunkter

I PowerPoint kan du använda Redigeringspunkter för att ändra en fyrkant:

  • Flytta ett hörn inåt eller utåt.
  • Justera krökningen på en punkt.
  • Lägg till nya punkter.

Att redigera punkter låter dig ändra en formens kontur eller använda en befintlig form som grund för en ny.

Tips för att redigera former

overview_image

När du redigerar PowerPoint‑former, ha dessa koncept i åtanke:

  • En form (eller dess bana) kan antingen vara sluten eller öppen.
  • En forms bana består av ankarnpunkter som är sammankopplade av segment.
  • Ett segment kan vara rakt eller böjt. Ankarnpunkter och kontrollhandtag bestämmer dess form.
  • Ankarnpunkter finns som hörnpunkter, raka punkter eller jämna punkter:
    • Vid en hörnpunkt kan intilliggande segment mötas i en vinkel.
    • Vid en jämn punkt är de två kontrollhandtagen i linje och lika långt från ankarnpunkten.
    • Vid en rak punkt är de två kontrollhandtagen i linje men kan ha olika avstånd från ankarnpunkten.
  • Att flytta ankarnpunkter eller justera deras handtag förändrar formens kontur.

För att redigera PowerPoint‑former via redigeringspunkter tillhandahåller Aspose.Slides klassen GeometryPath och gränssnittet IGeometryPath.

Enkla redigeringsoperationer

Använd följande metoder för att redigera en geometribana. Överlagringar som tar ett index infogar ett segment på den nollbaserade positionen i banans segmentmatris.

Operation Method
Lägg till eller infoga en rak linje. GeometryPath.lineTo
Lägg till eller infoga en kubisk Bézier‑kurva med två kontrollpunkter och en slutpunkt. GeometryPath.cubicBezierTo
Lägg till eller infoga en kvadratisk Bézier‑kurva med en kontrollpunkt och en slutpunkt. GeometryPath.quadraticBezierTo
Lägg till en elliptisk båg med angivna dimensioner och vinklar i grader. GeometryPath.arcTo
Stäng den aktuella figuren genom att koppla dess slutpunkt till startpunkten. GeometryPath.closeFigure
Ange startpunkten för en figur. GeometryPath.moveTo
Ta bort ett segment på ett nollbaserat index. GeometryPath.removeAt

Lägg till anpassade punkter till en form

Detta exempel infogar två punkter i en rektangulär form med GeometryPath.lineTo och tillämpar den redigerade banan med IGeometryShape.setGeometryPath. Koordinaterna är lokala för formen: båda nya punkterna ligger i dess centrum, 100 punkter från vänster och 50 punkter från toppen. Segmentindex 1 och 4 placerar dem på övre och nedre kanten och drar dessa kanter inåt.

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

example1_image

Ta bort punkter från en form

Detta exempel ändrar en hjärtformad kontur genom att ta bort segmentet på nollbaserat index 2 med GeometryPath.removeAt. Att tillämpa den modifierade banan med IGeometryShape.setGeometryPath uppdaterar formens kontur.

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

example2_image

Skapa en anpassad form

Detta exempel bygger en femspetsig stjärna med GeometryPath. Växlande yttre och inre radier på 100 respektive 50 punkter definierar spetsarna och inbuktningarna; ett steg på 72 grader placerar de fem spetsarna jämnt. Banen stängs med GeometryPath.closeFigure och tillämpas på en rektangulär form med IGeometryShape.setGeometryPath, vilket ersätter dess kontur.

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

example3_image

Skapa en sammansatt anpassad form

Detta exempel definierar två stängda GeometryPath‑objekt för övre och nedre tredjedelen av en form. Att tillämpa båda banorna med IGeometryShape.setGeometryPaths skapar en enda sammansatt form med ett glapp över dess mellersta tredjedel.

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

example4_image

Skapa en anpassad form med rundade hörn

Detta exempel kombinerar raka segment med GeometryPath.arcTo för att skapa en form med inåtböjda hörn. Varje hörnstorlek sätter bågens bredd och höjd i punkter, och -90‑graders svepningen ritar en moturs kvartsellips. Den färdiga banan tillämpas med IGeometryShape.setGeometryPath, och presentationen sparas i PPTX‑format.

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

Ta reda på om en formgeometri är sluten

Följande hjälpfunktion kontrollerar om varje icke‑tom geometribana slutar med ett avslutande segment. Den läser IGeometryPath.getPathData och jämför det sista segmentets IPathSegment.getPathCommand med PathCommandType.Close. Den returnerar false när det inte finns några icke‑tomma banor. Detta kontrollerar det sista kommandot för varje bana; den inspekterar inte varje underfigur inom en bana. Exemplet skriver ut true för en rektangel.

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

Extrahera och inspektera formelement

Använd IGeometryShape.createShapeElements för att inspektera en befintlig form som element som innehåller banpunkter och puntktyper. Kasta varje returnerad IShapeElement till ShapeElement för att hämta punkt‑ och typ‑arrayerna via metoderna getPathPoints och getPathTypes . Denna representation är användbar för konturanalys eller för att skicka geometri till anpassade ritverktyg.

Exemplet öppnar input.pptx, hittar den första geometriformen direkt på den första bilden och skriver ut dess bansegment. Använd en presentation som innehåller en form med raka och böjda segment för att se båda typerna av output. Den söker inte i grupper.

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; // Behåll segmenttypen, exklusive flaggor.
                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();
}

Arrayerna motsvarar varandra enligt index: points[i] är en punkt och types[i] beskriver dess roll. Typ 0 startar en figur, och typ 1 ritar en linje från föregående punkt till den aktuella. Ett kubiskt Bézier‑segment startar vid föregående slutpunkt och använder tre på varandra följande typ‑3‑poster: två kontrollpunkter följda av den nya slutpunkten. Loopen konsumerar dessa tre poster tillsammans och kontrollerar om slutpunkten är sluten.

Punkt‑typer innehåller också flaggor. Maskera med 0x07 för att identifiera segmenttypen och testa 0x80 separat för att upptäcka en sluten underbana. Till exempel betyder 129 (0x81) en linjeslutpunkt som stänger en underbana, och 131 (0x83) en Bézier‑slutpunkt som stänger en sådan. Java lagrar dessa flaggor i signerade byte; maskering bevarar de avsedda bitvärdena. Att bara jämföra råvärdet med 1 eller 3 skulle missa dessa fall. Andra flaggor, som streck (0x10) och markör (0x20), ändrar inte bassegmenttypen.

Koordinater och transformationer. Punkter mäts i punkter (1/72 tum), med X som ökar åt höger och Y som ökar nedåt. För en icke‑grupperad form inkluderar de extraherade koordinaterna redan positionen som returneras av getX och getY och återspeglar dess aktuella bredd och höjd. Till exempel har en icke‑roterad rektangel vid (100, 200) med storleken 120 × 80 konturbounds (100, 200)–(220, 280). Lägg inte till formens position igen. Subtrahera dess X och Y för att få koordinater relativt dess icke‑roterade ram.

De extraherade punkterna använder inte formens rotation, som returneras av getRotation, eller horisontella och vertikala speglingar. För att reproducera den visade konturen, tillämpa dessa transformationer kring formens centrum. För grupperade former, ta även hänsyn till gruppens koordinatmappning och transformationer; anta inte att de extraherade punkterna är de slutliga renderade bildkoordinaterna eller applicera en translation eller skala två gånger om den redan återspeglas i barnets ram.

Inspektion kontra redigering. Extraherade arrayer beskriver konturen; att ändra dem uppdaterar inte originalformen. Välj detta tillvägagångssätt när din analys‑ eller ritkod behöver punkt‑/typ‑arrayer. Använd getGeometryPaths när du behöver geometrikommandon i formens lokala koordinatsystem eller vill redigera dess geometri. Tillämpa redigerade banor via setGeometryPath eller setGeometryPaths.

Konvertera en Java AWT-form till GeometryPath

Detta exempel kombinerar en rektangulär kontur med text som har konverterats till geometri. Konturens setFillMode‑metod inaktiverar dess fyllning. En GlyphVector skapar textkonturen i en java.awt.Shape. Dess PathIterator levererar flytt‑, linje‑, kvadratisk‑, kubisk‑ och avslutningssegment, vilka exemplet spelar upp på en GeometryPath.

Texten använder en 40‑punkt Arial‑font. Konturen är förskjuten 20 punkter horisontellt och placerad så att dess visuella topp är 10 punkter under formens överkant. Textbanan är fylld, medan den ursprungliga rektangeln förblir en kontur. Att tillämpa båda banorna med IGeometryShape.setGeometryPaths skapar en enda form som innehåller rektangeln och texten.

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 {
    // Skapa ny form
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 300, 100);

    // Hämta geometribana för formen
    IGeometryPath originalPath = shape.getGeometryPaths()[0];
    originalPath.setFillMode(PathFillModeType.None);

    // Skapa ny grafikbana med text
    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();
    }

    // Konvertera grafikbana till geometribana
    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);

    // Ställ in kombinationen av ny geometribana och ursprunglig geometribana på formen
    shape.setGeometryPaths(new IGeometryPath[] { originalPath, textPath });
} finally {
    presentation.dispose();
}

example5_image

FAQ

Vad händer med fyllningen och konturen efter att geometrin har ersatts?

Stilen förblir påformen; endast konturen ändras. Fyllning och kontur appliceras automatiskt på den nya geometrin.

Hur roterar jag en anpassad form korrekt tillsammans med dess geometri?

Använd formens setRotation‑metod; geometrin roterar med formen eftersom den är bunden till formens eget koordinatsystem.

Kan jag konvertera en anpassad form till en bild för att “låsa” resultatet?

Ja. Exportera det erforderliga bild‑området eller själva form till ett rasterformat; detta förenklar vidare arbete med komplex geometri.