Personnaliser les formes de présentation en Java

Vue d’ensemble

Cet article explique comment personnaliser les formes de présentation dans Aspose.Slides en modifiant la géométrie des formes à l’aide des points d’édition et des chemins géométriques. Il montre comment travailler avec GeometryPath et IGeometryPath pour modifier les formes existantes, effectuer des opérations d’édition de base, ajouter ou supprimer des points, et appliquer la géométrie mise à jour à une forme.

Il démontre également comment créer des formes personnalisées et composites, construire des formes avec des coins arrondis, déterminer si la géométrie d’une forme est fermée, inspecter les éléments de forme, et convertir les contours de texte Java AWT en chemins géométriques.

Modifier une forme à l’aide des points d’édition

Dans PowerPoint, vous pouvez utiliser Edit Points pour modifier un carré :

  • Déplacer un coin vers l’intérieur ou vers l’extérieur.
  • Ajuster la courbure à un point.
  • Ajouter de nouveaux points.

La modification des points vous permet de modifier le contour d’une forme ou d’utiliser une forme existante comme base pour une nouvelle.

Conseils pour la modification des formes

overview_image

Lorsque vous modifiez des formes PowerPoint, gardez ces concepts à l’esprit :

  • Une forme (ou son chemin) peut être fermée ou ouverte.
  • Le chemin d’une forme est composé de points d’ancrage reliés par des segments.
  • Un segment peut être droit ou courbe. Les points d’ancrage et les poignées de contrôle déterminent sa forme.
  • Les points d’ancrage existent comme points de coin, points droits ou points lisses :
    • Au point de coin, les segments adjacents peuvent se rejoindre sous un angle.
    • Au point lisse, les deux poignées de contrôle sont alignées et à égale distance du point d’ancrage.
    • Au point droit, les deux poignées de contrôle sont alignées mais peuvent être à des distances différentes du point d’ancrage.
  • Déplacer les points d’ancrage ou ajuster leurs poignées modifie le contour de la forme.

Pour modifier les formes PowerPoint via des points d’édition, Aspose.Slides fournit la classe GeometryPath et l’interface IGeometryPath.

Opérations d’édition simples

Utilisez les méthodes suivantes pour modifier un chemin géométrique. Les surcharges qui acceptent un indice insèrent un segment à cette position basée sur zéro dans le tableau de segments du chemin.

Opération Méthode
Ajouter ou insérer une ligne droite. GeometryPath.lineTo
Ajouter ou insérer une courbe de Bézier cubique avec deux points de contrôle et un point final. GeometryPath.cubicBezierTo
Ajouter ou insérer une courbe de Bézier quadratique avec un point de contrôle et un point final. GeometryPath.quadraticBezierTo
Ajouter un arc elliptique avec les dimensions et les angles spécifiés en degrés. GeometryPath.arcTo
Fermer la figure courante en reliant son point final à son point de départ. GeometryPath.closeFigure
Définir le point de départ d’une figure. GeometryPath.moveTo
Supprimer un segment à un indice basé sur zéro. GeometryPath.removeAt

Ajouter des points personnalisés à une forme

Cet exemple insère deux points dans une forme rectangulaire à l’aide de GeometryPath.lineTo et applique le chemin modifié avec IGeometryShape.setGeometryPath . Les coordonnées sont locales à la forme : les deux nouveaux points sont au centre, à 100 points du bord gauche et à 50 points du bord supérieur. Les indices de segment 1 et 4 les placent respectivement sur les bords supérieur et inférieur, tirant ces bords vers l’intérieur.

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

Supprimer des points d’une forme

Cet exemple modifie le contour d’une forme en forme de cœur en supprimant le segment à l’indice 2 (indice basé sur zéro) avec GeometryPath.removeAt . L’application du chemin modifié avec IGeometryShape.setGeometryPath met à jour le contour de la forme.

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

Créer une forme personnalisée

Cet exemple construit une étoile à cinq branches avec GeometryPath . Des rayons extérieurs et intérieurs alternés de 100 et 50 points définissent les pointes et les enfoncements ; un pas de 72 degrés espace uniformément les cinq pointes. Le chemin est fermé avec GeometryPath.closeFigure et appliqué à une forme rectangulaire à l’aide de IGeometryShape.setGeometryPath , remplaçant son contour.

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

Créer une forme composite personnalisée

Cet exemple définit deux GeometryPath fermés pour le tiers supérieur et inférieur d’une forme. L’application des deux chemins avec IGeometryShape.setGeometryPaths crée une unique forme composite avec un espace au centre du tiers moyen.

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

Créer une forme personnalisée avec des coins arrondis

Cet exemple combine des segments droits avec GeometryPath.arcTo pour créer une forme aux coins incurvés vers l’intérieur. Chaque taille de coin définit la largeur et la hauteur de l’arc en points, et le balayage de -90 degrés trace un quart d’ellipse dans le sens antihoraire. Le chemin complet est appliqué avec IGeometryShape.setGeometryPath , et la présentation est enregistrée au format 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();
}

Déterminer si la géométrie d’une forme est fermée

L’assistant suivant vérifie si chaque chemin géométrique non vide se termine par un segment de fermeture. Il lit IGeometryPath.getPathData et compare la commande du segment final via IPathSegment.getPathCommand avec PathCommandType.Close . Il renvoie false lorsqu’il n’y a aucun chemin non vide. Cette vérification porte sur la commande finale de chaque chemin ; elle n’inspecte pas chaque sous-figure d’un chemin. L’exemple affiche true pour un rectangle.

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

Extraire et inspecter les éléments d’une forme

Utilisez IGeometryShape.createShapeElements pour inspecter une forme existante sous forme d’éléments contenant les points de chemin et leurs types. Convertissez chaque IShapeElement renvoyé en ShapeElement afin de récupérer les tableaux de points et de types via les méthodes getPathPoints et getPathTypes . Cette représentation est utile pour l’analyse de contours ou le passage de la géométrie à des outils de dessin personnalisés.

L’exemple ouvre input.pptx, trouve la première forme géométrique directement sur la première diapositive, et affiche ses segments de chemin. Utilisez une présentation contenant une forme avec des segments droits et courbes pour voir les deux types de sortie. Il ne recherche pas à l’intérieur des groupes.

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; // Conserver le type de segment, en excluant les indicateurs.
                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();
}

Les tableaux correspondent par index : points[i] est un point et types[i] décrit son rôle. Le type 0 démarre une figure, et le type 1 trace une ligne du point précédent au point actuel. Un segment de Bézier cubique commence à l’extrémité précédente et utilise trois entrées de type 3 consécutives : deux points de contrôle suivis du nouveau point final. La boucle consomme ces trois entrées ensemble et vérifie la fermeture du point final.

Les types de points contiennent également des indicateurs. Masquez avec 0x07 pour identifier le type de segment, et testez 0x80 séparément pour détecter un sous‑chemin fermé. Par exemple, 129 (0x81) signifie une extrémité de ligne qui ferme un sous‑chemin, et 131 (0x83) signifie une extrémité de Bézier qui ferme un sous‑chemin. Java stocke ces indicateurs dans des octets signés ; le masquage préserve les valeurs de bits prévues. Comparer la valeur brute uniquement avec 1 ou 3 manquerait ces cas. D’autres indicateurs, tels que le tiret (0x10) et le marqueur (0x20), ne modifient pas le type de segment de base.

Coordonnées et transformations. Les points sont mesurés en points (1/72 pouce), X augmentant vers la droite et Y augmentant vers le bas. Pour une forme non groupée, les coordonnées extraites incluent déjà la position renvoyée par getX et getY et reflètent sa largeur et sa hauteur actuelles. Par exemple, un rectangle non pivoté à (100, 200) avec une taille 120 × 80 a des limites de contour (100, 200)–(220, 280). N’ajoutez pas de nouveau la position de la forme. Soustrayez son X et Y pour obtenir des coordonnées relatives à son cadre non pivoté.

Les points extraits n’appliquent pas la rotation de la forme, renvoyée par getRotation , ni les retournements horizontaux et verticaux. Pour reproduire le contour affiché, appliquez ces transformations autour du centre de la forme. Pour les formes groupées, tenez également compte du mappage des coordonnées du groupe et des transformations ; ne supposez pas que les points extraits sont les coordonnées finales rendues sur la diapositive ou n’appliquez pas une translation ou une échelle deux fois si elle est déjà reflétée dans le cadre de l’enfant.

Inspection versus modification. Les tableaux extraits décrivent le contour ; les modifier ne met pas à jour la forme d’origine. Choisissez cette approche lorsque votre code d’analyse ou de dessin a besoin des tableaux point/type. Utilisez getGeometryPaths lorsque vous avez besoin des commandes géométriques dans le système de coordonnées local de la forme ou que vous souhaitez modifier sa géométrie. Appliquez les chemins modifiés via setGeometryPath ou setGeometryPaths .

Convertir une forme Java AWT en GeometryPath

Cet exemple combine un contour rectangulaire avec du texte converti en géométrie. La méthode setFillMode du contour désactive son remplissage. Un GlyphVector crée le contour du texte dans un java.awt.Shape . Son PathIterator fournit des segments de déplacement, ligne, quadratique, cubique et de fermeture, que l’exemple rejoue sur un GeometryPath .

Le texte utilise une police Arial de 40 points. Le contour est décalé de 20 points horizontalement et positionné avec son haut visuel 10 points sous le bord supérieur de la forme. Le chemin de texte est rempli, tandis que le rectangle d’origine reste un contour. L’application des deux chemins avec IGeometryShape.setGeometryPaths produit une forme unique contenant le rectangle et le texte.

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 {
    // Créer une nouvelle forme
    ISlide slide = presentation.getSlides().get_Item(0);

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

    // Obtenir le chemin de géométrie de la forme
    IGeometryPath originalPath = shape.getGeometryPaths()[0];
    originalPath.setFillMode(PathFillModeType.None);

    // Créer un nouveau chemin graphique avec du texte
    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();
    }

    // Convertir le chemin graphique en chemin de géométrie
    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);

    // Définir la combinaison du nouveau chemin de géométrie et du chemin de géométrie d'origine sur la forme
    shape.setGeometryPaths(new IGeometryPath[] { originalPath, textPath });
} finally {
    presentation.dispose();
}

example5_image

FAQ

What will happen to the fill and outline after replacing the geometry?

Le style reste associé à la forme ; seul le contour change. Le remplissage et le contour sont appliqués automatiquement à la nouvelle géométrie.

How do I correctly rotate a custom shape along with its geometry?

Utilisez la méthode setRotation de la forme ; la géométrie pivote avec la forme car elle est liée au système de coordonnées propre de la forme.

Can I convert a custom shape to an image to “lock in” the result?

Oui. Exportez la zone de slide requise ou la shape elle‑même vers un format raster ; cela simplifie le travail ultérieur avec des géométries complexes.