Aspose.PSD for Java 23.7 - Release Notes

Key Summary Category
PSDJAVA-502 Add ability to Export each layer of PSD File to the Animated Gif File Feature
PSDJAVA-503 Assign Fill property of Shape layer form vscg resource Feature
PSDJAVA-504 Add new types of warp (arc & arch) Feature
PSDJAVA-505 Change the application that saves the PSD File to Aspose.PSD if property UpdateMetadata is set to true Feature
PSDJAVA-510 Increase the calculation area of the warp image Feature
PSDJAVA-511 Black and white adjustment Layer processes semi-transparency wrong Bug
PSDJAVA-512 SmartObject ReplaceContents (when the AllowWarpRepaint options is active) falls after 2 minutes of computing Bug
PSDJAVA-513 Add ability to get the real LayerGroup Left and Top position Bug
PSDJAVA-514 Resize of the layer works wrong when psd file has VogkResource with structures in points Bug
PSDJAVA-515 TextBound is not working as expected Bug
PSDJAVA-516 Adding a layer created with default constructor to PSD image does not add default resources to it Bug
PSDJAVA-517 Timeline.LoopesCount is ignored when exporting to animated GIF Bug

Public API Changes

Added APIs:

  • F:com.aspose.psd.fileformats.ai.AiFormatVersion.Pdf17
  • F:com.aspose.psd.fileformats.ai.AiFormatVersion.Pdf16
  • M:com.aspose.psd.imageoptions.PsdOptions.getUpdateMetadata
  • M:com.aspose.psd.imageoptions.PsdOptions.setUpdateMetadata(boolean)
  • M:com.aspose.psd.xmp.schemas.xmpbaseschema.XmpBasicPackage.containsKey(java.lang.String)
  • M:com.aspose.psd.xmp.schemas.xmpbaseschema.XmpBasicPackage.get_Item(java.lang.String)
  • M:com.aspose.psd.xmp.schemas.xmpbaseschema.XmpBasicPackage.set_Item(java.lang.String,java.lang.Object)
  • M:com.aspose.psd.xmp.schemas.xmpbaseschema.XmpBasicPackage.setValue(java.lang.String,com.aspose.psd.xmp.IXmlValue)

Removed APIs:

  • M:com.aspose.psd.xmp.schemas.xmpbaseschema.XmpBasicPackage.setCreatedDate(java.util.Date)
  • M:com.aspose.psd.xmp.schemas.xmpbaseschema.XmpBasicPackage.setMetadataDate(java.util.Date)
  • M:com.aspose.psd.xmp.schemas.xmpbaseschema.XmpBasicPackage.setModifyDate(java.util.Date)
  • M:com.aspose.psd.fileformats.psd.layers.layerresources.vectorpaths.VectorPath.getFillColor
  • M:com.aspose.psd.fileformats.psd.layers.layerresources.vectorpaths.VectorPath.setFillColor(com.aspose.psd.Color)

Usage examples:

PSDJAVA-502. Add ability to Export each layer of PSD File to the Animated Gif File

    String src = "src/main/resources/EachLayerIsFrame.psd";
    String outputGif = "src/main/resources/out_EachLayerIsFrame.gif";
    String outputPsd = "src/main/resources/out_EachLayerIsFrame.psd";

    try (PsdImage psdImage = (PsdImage) Image.load(src)) {
        // Create frames for each layer.
        int framesCount = psdImage.getLayers().length;
        Timeline timeline = psdImage.getTimeline();

        Frame[] frames = new Frame[framesCount];
        for (int i = 0; i < framesCount; i++) {
            frames[i] = new Frame();
            LayerState[] layerStates = new LayerState[framesCount];

            for (int j = 0; j < framesCount; j++) {
                layerStates[j] = new LayerState();
                layerStates[j].setEnabled(i == j);
            }

            frames[i].setLayerStates(layerStates);
        }

        timeline.setFrames(frames);

        // Update current states
        Layer[] layers = psdImage.getLayers();
        LayerState[] states = timeline.getFrames()[timeline.getActiveFrameIndex()].getLayerStates();
        for (int i = 0; i < framesCount; i++) {
            layers[i].setVisible(states[i].getEnabled());
        }

        timeline.save(outputGif, new GifOptions());
        psdImage.save(outputPsd);
    }

PSDJAVA-503. Assign Fill property of Shape layer form vscg resource

        // Solid fill example
        public static void main(String[] args) {
            String srcFile = "src/main/resources/ShapeInternalSolid.psd";
            String outFile = "src/main/resources/ShapeInternalSolid.psd.out.psd";

            PsdLoadOptions psdLoadOptions = new PsdLoadOptions();
            psdLoadOptions.setLoadEffectsResource(true);

            try (PsdImage image = (PsdImage) Image.load(srcFile, psdLoadOptions)) {
                 ShapeLayer shapeLayer = (ShapeLayer) image.getLayers()[1];
                  ColorFillSettings fillSettings = (ColorFillSettings) shapeLayer.getFill();
                 fillSettings.setColor(Color.getRed());

                 shapeLayer.update();

                 image.save(outFile);
            }

            // Check saved changes
            try (PsdImage image = (PsdImage) Image.load(outFile, psdLoadOptions)) {
            ShapeLayer shapeLayer = (ShapeLayer) image.getLayers()[1];
            ColorFillSettings fillSettings = (ColorFillSettings) shapeLayer.getFill();

            assertAreEqual(Color.getRed(), fillSettings.getColor());

            image.save(outFile);
        }

        static void assertAreEqual(Object expected, Object actual, String message) {
            if (!expected.equals(actual)) {
                throw new IllegalArgumentException(message);
            }
        }

        static void assertAreEqual(Object expected, Object actual) {
            assertAreEqual(expected, actual, "Objects are not equal.");
        }
		
		//Gradient fill example:

	public static void main(String[] args) {
    String srcFile = "src/main/resources/ShapeInternalGradient.psd";
    String outFile = "src/main/resources/ShapeInternalGradient.psd.out.psd";

    PsdLoadOptions psdLoadOptions = new PsdLoadOptions();
    psdLoadOptions.setLoadEffectsResource(true);

    try (PsdImage image = (PsdImage) Image.load(srcFile, psdLoadOptions)) {
        ShapeLayer shapeLayer = (ShapeLayer) image.getLayers()[1];
        GradientFillSettings fillSettings = (GradientFillSettings) shapeLayer.getFill();
        fillSettings.setDither(true);
        fillSettings.setReverse(true);
        fillSettings.setAlignWithLayer(false);
        fillSettings.setAngle(20);
        fillSettings.setScale(50);
        fillSettings.getColorPoints()[0].setLocation(100);
        fillSettings.getColorPoints()[1].setLocation(4000);
        fillSettings.getTransparencyPoints()[0].setLocation(200);
        fillSettings.getTransparencyPoints()[1].setLocation(3800);
        fillSettings.getTransparencyPoints()[0].setOpacity(90);
        fillSettings.getTransparencyPoints()[1].setOpacity(10);

        shapeLayer.update();

        image.save(outFile);
    }

    // Check saved changes
    try (PsdImage image = (PsdImage) Image.load(outFile, psdLoadOptions)) {
        ShapeLayer shapeLayer = (ShapeLayer) image.getLayers()[1];
        GradientFillSettings fillSettings = (GradientFillSettings) shapeLayer.getFill();

        assertAreEqual(true, fillSettings.getDither());
        assertAreEqual(true, fillSettings.getReverse());
        assertAreEqual(false, fillSettings.getAlignWithLayer());
        assertAreEqual((double) 20, fillSettings.getAngle());
        assertAreEqual(50, fillSettings.getScale());
        assertAreEqual(100, fillSettings.getColorPoints()[0].getLocation());
        assertAreEqual(4000, fillSettings.getColorPoints()[1].getLocation());
        assertAreEqual(200, fillSettings.getTransparencyPoints()[0].getLocation());
        assertAreEqual(3800, fillSettings.getTransparencyPoints()[1].getLocation());
        assertAreEqual((double) 90, fillSettings.getTransparencyPoints()[0].getOpacity());
        assertAreEqual((double) 10, fillSettings.getTransparencyPoints()[1].getOpacity());
    }
}

static void assertAreEqual(Object expected, Object actual) {
    assertAreEqual(expected, actual, "Objects are not equal.");
}

PSDJAVA-504. Add new types of warp (arc & arch)

    String sourceArcFile = "src/main/resources/arc_warp.psd";
    String outputArcFile = "src/main/resources/arc_export.png";

    String sourceArchFile = "src/main/resources/arch_warp.psd";
    String outputArchFile = "src/main/resources/arch_export.png";

    PsdLoadOptions psdLoadOptions = new PsdLoadOptions();
    psdLoadOptions.setAllowWarpRepaint(true);
    psdLoadOptions.setLoadEffectsResource(true);

    PngOptions pngOptions = new PngOptions();
    pngOptions.setColorType(PngColorType.TruecolorWithAlpha);

    try (PsdImage psdImage = (PsdImage) Image.load(sourceArcFile, psdLoadOptions)) {
        psdImage.save(outputArcFile, pngOptions);
    }


    try (PsdImage psdImage = (PsdImage) Image.load(sourceArchFile, psdLoadOptions)) {
        psdImage.save(outputArchFile, pngOptions);
    }

PSDJAVA-505. Change the application that saves the PSD File to Aspose.PSD if property UpdateMetadata is set to true

    String path = "src/main/resources/output.psd";
    try (PsdImage image = new PsdImage(100, 100)) {
        // If you want the creator tool to change, make sure that the "UpdateMetadata" property is set to true. It's set to true by default.
        PsdOptions psdOptions = new PsdOptions();
        psdOptions.setUpdateMetadata(true);

        // Saving the image.
        image.save(path, psdOptions);

        // Checking creator tool in code.
        XmpPacketWrapper xmpData = image.getXmpData();
        XmpPackage basicPackage = image.getXmpData().getPackage(Namespaces.XmpBasic);

        // Here will be updated creator tool info.
        String currentCreatorTool = (String) basicPackage.get_Item(":CreatorTool");

PSDJAVA-510. Increase the calculation area of the warp image

    String sourceFile = "src/main/resources/mug4_warp.psd";
    String outputFile = "src/main/resources/mug4_export.png";

    PsdLoadOptions psdLoadOptions = new PsdLoadOptions();
    psdLoadOptions.setAllowWarpRepaint(true);
    psdLoadOptions.setLoadEffectsResource(true);

    try (PsdImage psdImage = (PsdImage) Image.load(sourceFile, psdLoadOptions)) {
        PngOptions pngOptions = new PngOptions();
        pngOptions.setColorType(PngColorType.TruecolorWithAlpha);

        psdImage.save(outputFile, pngOptions);
    }

PSDJAVA-511. Black and white adjustment Layer processes semi-transparency wrong

public static void main(String[] args) {
    String srcFile = "src/main/resources/frog_nosymb.psd";
    String outFile = "src/main/resources/frog_nosymb.psd.out.psd";

    try (PsdImage psdImage = (PsdImage) Image.load(srcFile)) {
        psdImage.addBlackWhiteAdjustmentLayer();
        psdImage.save(outFile);
    }

    PsdLoadOptions psdLoadOptions = new PsdLoadOptions();
    psdLoadOptions.setLoadEffectsResource(true);

    // Check saved changes
    try (PsdImage image = (PsdImage) Image.load(outFile, psdLoadOptions)) {
        assertAreEqual(2, image.getLayers().length);

        BlackWhiteAdjustmentLayer blackWhiteAdjustmentLayer = (BlackWhiteAdjustmentLayer) image.getLayers()[1];

        if (blackWhiteAdjustmentLayer == null) {
            throw new Exception("Layer 2 should be BlackWhiteAdjustmentLayer");
        }

        image.save(outFile);
    }
}

static void assertAreEqual(Object expected, Object actual) {
    assertAreEqual(expected, actual, "Objects are not equal.");
}

static void assertAreEqual(Object expected, Object actual, String message) {
    if (!expected.equals(actual)) {
       throw new IllegalArgumentException(message);
    }
}

PSDJAVA-512. SmartObject ReplaceContents (when the AllowWarpRepaint options is active) falls after 2 minutes of computing

    String sourceFile = "src/main/resources/mug 4.psd";
    String changeFile = "src/main/resources/artwork for replace.png";
    String outputFile = "src/main/resources/export.png";

    int newHeight = 300;

    PsdLoadOptions psdLoadOptions = new PsdLoadOptions();
    psdLoadOptions.setAllowWarpRepaint(true);
    psdLoadOptions.setLoadEffectsResource(true);

    try (PsdImage psdImage = (PsdImage) Image.load(sourceFile, psdLoadOptions)) {
        SmartObjectLayer smartObjectLayer = (SmartObjectLayer) psdImage.getLayers()[3];

        double scale = (double) newHeight / smartObjectLayer.getHeight();
        int newWidth = (int) Math.round(smartObjectLayer.getWidth() * scale);

        PsdImage innerImage = new PsdImage(newWidth, newHeight);
        innerImage.setResolution(72, 72);

        Stream innerStream = new FileStream(changeFile, FileMode.Open);
        Layer layer = new Layer(innerStream.toInputStream());
        layer.setHorizontalResolution(72);
        layer.setVerticalResolution(72);

        try {
            innerImage.addLayer(layer);

            smartObjectLayer.replaceContents(innerImage);
            smartObjectLayer.updateModifiedContent();

            PngOptions pngOptions = new PngOptions();
            pngOptions.setColorType(PngColorType.TruecolorWithAlpha);

            psdImage.save(outputFile, pngOptions);
        } finally {
            innerImage.dispose();
            innerStream.dispose();
            layer.dispose();
        }
    }

PSDJAVA-513. Add ability to get the real LayerGroup Left and Top position

public static void main(String[] args) {
    String sourceFile = "src/main/resources/layerGroupFigures.psd";

    try (PsdImage image = (PsdImage) Image.load(sourceFile)) {
        Layer[] layers = image.getLayers();

        for (int i = 0; i < layers.length; i++) {
            Layer layer = layers[i];

            if (layer instanceof LayerGroup) {
                {
                    // Getting LayerGroup.
                    LayerGroup group = (LayerGroup) layer;

                    int expectedLeft = Integer.MAX_VALUE;
                    int expectedTop = Integer.MAX_VALUE;
                    int expectedRight = 0;
                    int expectedBottom = 0;

                    // Calculating real Left, Top, Right, and Bottom positions.
                    for (Layer innerLayer : group.getLayers()) {
                        if (innerLayer instanceof AdjustmentLayer || innerLayer.getBounds().isEmpty()) {
                            continue;
                        }

                        expectedLeft = Math.min(expectedLeft, innerLayer.getLeft());
                        expectedTop = Math.min(expectedTop, innerLayer.getTop());
                        expectedRight = Math.max((expectedLeft + group.getWidth()), (innerLayer.getLeft() + innerLayer.getWidth()));
                        expectedBottom = Math.max((expectedTop + group.getHeight()), (innerLayer.getTop() + innerLayer.getHeight()));
                    }

                    // LayerGroup Left, Top, Right, and Bottom positions are calculated properly now.
                    assertAreEqual(group.getLeft(), expectedLeft);
                    assertAreEqual(group.getTop(), expectedTop);
                    assertAreEqual(group.getRight(), expectedRight);
                    assertAreEqual(group.getBottom(), expectedBottom);
                }
            }
        }
    }
}

static void assertAreEqual(Object expected, Object actual) {
    assertAreEqual(expected, actual, "Objects are not equal.");
}

static void assertAreEqual(Object expected, Object actual, String message) {
    if (!expected.equals(actual)) {
        throw new IllegalArgumentException(message);
    }
}

PSDJAVA-514. Resize of the layer works wrong when psd file has VogkResource with structures in points

public static void main(String[] args) {
    String[] sourceFiles = new String[]
            {
                    "src/main/resources/PointsVectorOrigin.psd",
                    "src/main/resources/TopVogkResStruct.psd"
            };

    for (String sourceFile : sourceFiles) {
        try (PsdImage image = (PsdImage)Image.load(sourceFile))
        {
            Layer layer = image.getLayers()[0];

            layer.resize(50, 50);

            assertAreEqual(layer.getHeight(), 50);
            assertAreEqual(layer.getWidth(), 50);
        }
    }
}

static void assertAreEqual(Object expected, Object actual) {
    assertAreEqual(expected, actual, "Objects are not equal.");
}

static void assertAreEqual(Object expected, Object actual, String message) {
    if (!expected.equals(actual)) {
        throw new IllegalArgumentException(message);
    }
}

PSDJAVA-515. TextBound is not working as expected

public static void main(String[] args) {
    String sourceFile = "src/main/resources/input_Test_forTicket.psd";
    String outFile = "src/main/resources/out_1608.psd";

    Size newTextBox = new Size(-1, -1);
    try (PsdImage psdImage = (PsdImage) Image.load(sourceFile)) {
        //Step: Replace text
        TextLayer textLayer = (TextLayer) psdImage.getLayers()[3];
        textLayer.getTextData().getItems()[0].setText("Text test replaced");

        //Step: Update TextBoundBox
        textLayer.getTextData().updateLayerData();
        newTextBox = new Size((int) Math.ceil(textLayer.getTextBoundBox().getWidth()), textLayer.getHeight());

        textLayer.setTextBoundBox(new RectangleF(PointF.getEmpty(), Size.to_SizeF(newTextBox)));
        textLayer.getTextData().updateLayerData();

        psdImage.save(outFile);
    }

    // Check changes
    try (PsdImage psdImage = (PsdImage) Image.load(outFile)) {
        Txt2Resource txt2Resource = (Txt2Resource) psdImage.getGlobalLayerResources()[1];
        String textData = new String(txt2Resource.getData(), "Windows-1251");

        String search = "<< /0 << /1 << /0 ["; // specific character set to find textBox value in this file.

        int startIndex = textData.indexOf(search) + search.length();
        int endIndex = textData.indexOf("]", startIndex);
        String boxItems = textData.substring(startIndex, endIndex);

        String height = String.format("%.1f", (float)newTextBox.getHeight()).replace(",", ".");
        String width = String.format("%.1f", (float)newTextBox.getWidth()).replace(",", ".");

        if (!boxItems.contains(height) || !boxItems.contains(width)) {
            throw new Exception("TextBox not updated.");
        }
    }
}

static void assertAreEqual(Object expected, Object actual, String message) {
    if (!expected.equals(actual)) {
        throw new IllegalArgumentException(message);
    }
}

PSDJAVA-516. Adding a layer created with default constructor to PSD image does not add default resources to it

public static void main(String[] args) {
    String output = "src/main/resources/newLayer.psd";

    try (PsdImage psdImage = new PsdImage(500, 500)) {
        Layer layer = new Layer();
        psdImage.addLayer(layer);

        LyidResource lyidResource = (LyidResource) findResource(LyidResource.TypeToolKey, layer.getResources());

        int layerId = lyidResource.getValue(); // Was NullReferenceException

        psdImage.save(output);
    }
}

private static LayerResource findResource(int resType, LayerResource[] resources) {
    if (resources != null) {
        for(LayerResource layerResource : resources) {
            if (layerResource.getKey() == resType) {
                return layerResource;
            }
        }
    }

    return null;
}

PSDJAVA-517. Timeline.LoopesCount is ignored when exporting to animated GIF

    String sourceFile = "src/main/resources/4_animated.psd";
    String outputGif = "src/main/resources/out_4_animated.psd.gif";

    PsdLoadOptions psdLoadOptions = new PsdLoadOptions();
    psdLoadOptions.setLoadEffectsResource(true);

    try (PsdImage psdImage = (PsdImage) Image.load(sourceFile, psdLoadOptions)) {
        psdImage.getTimeline().save(outputGif, new GifOptions());
    }