Prezentációs alakzatok testreszabása Java-ban

Áttekintés

Ez a cikk elmagyarázza, hogyan lehet testreszabni a prezentáció alakzatokat az Aspose.Slides‑ban úgy, hogy a forma geometriáját szerkesztő pontok és geometriai útvonalak segítségével módosítjuk. Bemutatja, hogyan kell használni a GeometryPath és a IGeometryPath elemeket a meglévő alakzatok módosításához, az alapvető útvonal‑szerkesztési műveletek végrehajtásához, pontok hozzáadásához vagy eltávolításához, és a frissített geometria alkalmazásához egy alakzatra.

Emellett azt is bemutatja, hogyan hozhatunk létre egyéni és összetett alakzatokat, hogyan építhetünk ívelt sarkú alakzatokat, hogyan állapítható meg, hogy egy forma geometria zárt‑e, hogyan vizsgálhatók meg az alakzat elemei, és hogyan konvertálhatók a Java AWT szöveg körvonalak geometriai útvonalakká.

Alakzat módosítása szerkesztő pontokkal

  • Mozgassa be vagy ki egy sarkot.
  • Állítsa be a görbületet egy pontnál.
  • Adjon hozzá új pontokat.

A szerkesztő pontok lehetővé teszik az alakzat körvonalának módosítását vagy egy meglévő alakzat használatát új alapként.

Alakzat szerkesztési tippek

overview_image

PowerPoint‑alakzatok szerkesztésekor tartsa szem előtt az alábbi fogalmakat:

  • Egy alakzat (vagy annak útvonala) lehet zárt vagy nyitott.
  • Az alakzat útvonala horgonypontokból áll, amelyeket szegmensek kötnek össze.
  • Egy szegmens lehet egyenes vagy görbe. A horgonypontok és a vezérlő fogantyúk határozzák meg alakját.
  • A horgonypontok lehetnek sarkok, egyenes vagy sima pontok:
    • Sarkpontnál a szomszédos szegmensek szöggel találkozhatnak.
    • Simapontnál a két vezérlő fogantyú igazított és egyenlő távolságra van a horgonyponttól.
    • Egyenespontnál a két vezérlő fogantyú igazított, de különböző távolságban lehet a horgonyponttól.
  • A horgonypontok mozgatása vagy a fogantyúk beállítása megváltoztatja az alakzat körvonalát.

A PowerPoint‑alakzatok szerkesztéséhez a Aspose.Slides a GeometryPath osztályt és a IGeometryPath interfészt biztosítja.

Egyszerű szerkesztési műveletek

Az alábbi metódusokkal szerkesztheti egy geometriai útvonalat. Az indexet elfogadó túlterhelések a szegmenst a szegmens tömb nulláról indexelt pozíciójába szúrják be.

Operation Method
Egyenes vonal hozzáadása vagy beszúrása. GeometryPath.lineTo
Köbös Bézier‑görbe hozzáadása vagy beszúrása két vezérlőponttal és végponttal. GeometryPath.cubicBezierTo
Másodfokú Bézier‑görbe hozzáadása vagy beszúrása egy vezérlőponttal és végponttal. GeometryPath.quadraticBezierTo
Ellipszis ív hozzáadása a megadott méretekkel és fokban megadott szögekkel. GeometryPath.arcTo
Az aktuális alakzat lezárása azzal, hogy a végpontját összeköti a kezdőponttal. GeometryPath.closeFigure
Az alakzat kezdőpontjának beállítása. GeometryPath.moveTo
Szegmens eltávolítása nulláról indexelt pozíción. GeometryPath.removeAt

Egyéni pontok hozzáadása egy alakzathoz

Ez a példa két pontot szúr be egy téglalap alakzatba a GeometryPath.lineTo használatával, és a szerkesztett útvonalat alkalmazza a IGeometryShape.setGeometryPath segítségével. A koordináták az alakzathoz relatívak: mindkét új pont az alakzat közepén helyezkedik el, 100 ponttól balra és 50 ponttól felül. Az 1‑es és 4‑es szegmens‑indexek a pontokat a felső és alsó élre helyezik, behúzva ezeket az éleket.

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

Pontok eltávolítása egy alakzatból

Ez a példa a szív alakú körvonalat módosítja azzal, hogy eltávolítja a nulláról indexelt 2‑es szegmenst a GeometryPath.removeAt használatával. A módosított útvonal alkalmazása a IGeometryShape.setGeometryPath segítségével frissíti az alakzat körvonalát.

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

Egyéni alakzat létrehozása

Ez a példa egy ötpontos csillagot épít fel a GeometryPath segítségével. A 100 és 50 pont közti váltakozó külső és belső sugár definiálja a csúcsokat és a bemetszéseket; egy 72‑fokos lépés egyenletesen helyezi el az öt csúcsot. Az útvonal a GeometryPath.closeFigure használatával záródik, és egy téglalap alakzatra alkalmazza a IGeometryShape.setGeometryPath segítségével, ezzel felülírva annak körvonalát.

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

Összetett egyéni alakzat létrehozása

Ez a példa két zárt GeometryPath objektumot definiál egy alakzat felső és alsó harmadjához. Mindkét útvonal alkalmazása a IGeometryShape.setGeometryPaths segítségével egyetlen összetett alakzatot hoz létre, amelynek középső harmadja egy részt tartalmaz.

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

Egyéni alakzat létrehozása ívelt sarkokkal

Ez a példa egyenes szegmenseket kombinál a GeometryPath.arcTo használatával, hogy egy befelé ívelt sarkokkal rendelkező alakzatot hozzon létre. Minden sarokméret beállítja az ív szélességét és magasságát pontban, a -90‑fokos ív pedig egy óramutatóval ellentétes negyed‑ellipszist rajzol. A befejezett útvonalat a IGeometryShape.setGeometryPath segítségével alkalmazzák, és a prezentációt PPTX formátumban mentik.

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

Kiderítés, hogy egy alakzat geometriája zárt-e

A következő segédprogram ellenőrzi, hogy minden nem üres geometriai útvonal lezáró szegmenssel végződik‑e. A IGeometryPath.getPathData beolvassa, és az utolsó szegmens IPathSegment.getPathCommand értékét összehasonlítja a PathCommandType.Close értékkel. Ha nincs nem üres útvonal, hamisat ad vissza. Ez minden útvonal utolsó parancsát ellenőrzi; nem vizsgálja meg az útvonal minden alalakzatát. A példa true értéket ír ki egy téglalapra.

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

Alakzat elemeinek kinyerése és vizsgálata

A IGeometryShape.createShapeElements használatával vizsgálhat egy meglévő alakzatot, amely elemeket tartalmaz, útpontokkal és ponttípusokkal. Az egyes visszaadott IShapeElement objektumot konvertálja ShapeElement‑re, hogy a getPathPoints és getPathTypes metódusok segítségével lekérje a pont- és típus‑tömböket. Ez a reprezentáció hasznos a körvonal‑analízishez vagy a geometria átadásához egyéni rajzoló eszközöknek.

A példa megnyitja az input.pptx fájlt, megtalálja az első geometriai alakzatot közvetlenül az első dián, és kiírja az útvonal szegmenseit. Használjon egy olyan prezentációt, amely alakzatot tartalmaz egyenes és görbe szegmensekkel, hogy mindkét kimenetet lássa. Nem keres a csoportok belsejében.

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; // Tartsa meg a szegmens típusát, a flag-ek nélkül.
                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; // Fogyasztja mindkét vezérlőpontot és a végpontot.
                        break;
                }

                if ((types[i] & 0x80) != 0) {
                    System.out.println("Close subpath back to its starting point.");
                }
            }
        }
    }
} finally {
    presentation.dispose();
}

A tömbök index szerint egyeznek: a points[i] egy pont, a types[i] leírja a szerepét. A 0 típus egy alakzatot indít, az 1 típus egy vonalat rajzol az előző pontból a jelenlegi pontba. Egy köbös Bézier szegmens az előző végpontnál kezdődik, és három egymást követő 3 típusú bejegyzést használ: két vezérlőpontot, majd az új végpontot. A ciklus ezeknek a három bejegyzésnek a fogyasztását végzi, és ellenőrzi a végpontot a zárás szempontjából.

A ponttípusok flag‑eket is tartalmaznak. A 0x07 maszkolással azonosítható a szegmens típusa, a 0x80 önálló tesztelése pedig a zárt alútvonal felismerésére szolgál. Például a 129 (0x81) egy vonalvégpontot jelent, amely zár egy alútvonalat, a 131 (0x83) pedig egy Bézier‑végpontot, amely szintén zár. A Java ezeket a flag‑eket előjeles bájtokban tárolja; a maszkolás megőrzi a kívánt bitértékeket. Ha csak a nyers értéket hasonlítjuk 1‑gyel vagy 3‑mal, ezek az esetek kimaradnak. Egyéb flag‑ek, mint a szaggatás (0x10) és a marker (0x20), nem változtatják a szegmens alap típusát.

Koordináták és transzformációk. A pontok pontban (1/72 hüvelyk) mérnek, az X jobbra, a Y pedig lefelé nő. Egy csoportosítatlan alakzat esetén a kinyert koordináták már tartalmazzák a getX és getY által visszaadott pozíciót, és tükrözik a jelenlegi szélességét és magasságát. Például egy nem elfordított téglalap a (100, 200) helyen, 120 × 80 mérettel, a körvonalhatárait (100, 200)–(220, 280) adja. Ne adja hozzá újra az alakzat pozícióját. Vonja ki az X és Y értékét, hogy a koordináták a nem elfordított kerethez viszonyítva legyenek.

A kinyert pontok nem alkalmazzák az alakzat getRotation által visszaadott forgását, vagy a vízszintes és függőleges tükrözéseket. A megjelenített körvonal reprodukálásához alkalmazza ezeket a transzformációkat az alakzat középpontja körül. Csoportosított alakzatok esetén vegye figyelembe a csoport koordináta leképezését és transzformációit; ne feltételezze, hogy a kinyert pontok a végleges megjelenített dia koordináták, és ne alkalmazzon kétszer fordítási vagy méretezési lépést, ha az már a gyermek keretben tükröződik.

Vizsgálat vs. szerkesztés. A kinyert tömbök leírják a körvonalat; azok módosítása nem frissíti az eredeti alakzatot. Ezt a megközelítést válassza, ha elemzése vagy rajzoló kódja pont‑típus tömböket igényel. Használja a getGeometryPaths metódust, ha geometriai parancsokra van szüksége az alakzat helyi koordináta‑rendszerében, vagy ha a geometriát szerkeszteni szeretné. Alkalmazza a szerkesztett útvonalakat a setGeometryPath vagy a setGeometryPaths segítségével.

Java AWT Shape konvertálása GeometryPath‑re

Ez a példa egy téglalap körvonalat kombinál a geometriává konvertált szöveggel. A körvonal setFillMode metódusa kikapcsolja a kitöltést. Egy GlyphVector hozza létre a szöveg körvonalát egy java.awt.Shape objektumban. A PathIterator mozgás, vonal, kvadratikus, köbös és záró szegmenseket szolgáltat, amelyeket a példa újrajátssza egy GeometryPath objektumban.

A szöveg 40‑pontos Arial betűtípussal kerül megjelenítésre. A körvonal 20 ponttal vízszintesen eltolódik, és a vizuális teteje 10 ponttal a alakzat felső szélétől helyezkedik el. A szöveg útvonal kitöltött, míg az eredeti téglalap csak körvonal marad. Mindkét útvonal alkalmazása a IGeometryShape.setGeometryPaths segítségével egyetlen alakzatot hoz létre, amely tartalmazza a téglalapot és a szöveget.

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 {
    // Új alakzat létrehozása
    ISlide slide = presentation.getSlides().get_Item(0);

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

    // Az alakzat geometriai útvonalának lekérése
    IGeometryPath originalPath = shape.getGeometryPaths()[0];
    originalPath.setFillMode(PathFillModeType.None);

    // Új grafikai útvonal létrehozása szöveggel
    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();
    }

    // Grafikai útvonal konvertálása geometriai útra
    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);

    // Az új geometriai útvonal és az eredeti geometriai útvonal kombinációjának beállítása az alakzatra
    shape.setGeometryPaths(new IGeometryPath[] { originalPath, textPath });
} finally {
    presentation.dispose();
}

example5_image

GYIK

Mi történik a kitöltéssel és a körvonallal a geometria cseréje után?

A stílus az alakzaton marad; csak a körvonal változik. A kitöltés és a körvonal automatikusan alkalmazásra kerül az új geometriára.

Hogyan lehet helyesen elforgatni egy egyéni alakzatot a geometriai adataival együtt?

Használja az alakzat setRotation metódusát; a geometria az alakzattal együtt forog, mivel az az alakzat saját koordináta‑rendszeréhez van kötve.

Átkonvertálhatom-e egy egyéni alakzatot képpé a végeredmény „lezárásához”?

Igen. Exportálja a szükséges dia területet vagy az alakzat maga raster formátumba; ez egyszerűsíti a komplex geometriák további kezelését.