diff --git a/gatsby-config.js b/gatsby-config.js index 9d090ce0a..917e6a79a 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -246,6 +246,10 @@ module.exports = { title: "ReadOnlyItemList", path: "references/document-sandbox/document-apis/classes/ReadOnlyItemList.md", }, + { + title: "ReadOnlyMask", + path: "references/document-sandbox/document-apis/classes/ReadOnlyMask.md", + }, { title: "RectangleNode", path: "references/document-sandbox/document-apis/classes/RectangleNode.md", @@ -332,10 +336,10 @@ module.exports = { title: "BaseParagraphStyles", path: "references/document-sandbox/document-apis/interfaces/BaseParagraphStyles.md", }, - { - title: "BitmapImage", - path: "references/document-sandbox/document-apis/interfaces/BitmapImage.md", - }, + // { + // title: "BitmapImage", + // path: "references/document-sandbox/document-apis/interfaces/BitmapImage.md", + // }, { title: "CharacterStyles", path: "references/document-sandbox/document-apis/interfaces/CharacterStyles.md", @@ -360,6 +364,10 @@ module.exports = { title: "ColorFill", path: "references/document-sandbox/document-apis/interfaces/ColorFill.md", }, + { + title: "CommonResizeOptions", + path: "references/document-sandbox/document-apis/interfaces/CommonResizeOptions.md", + }, { title: "ContainerNode", path: "references/document-sandbox/document-apis/interfaces/ContainerNode.md", @@ -376,6 +384,14 @@ module.exports = { title: "IFillableNode", path: "references/document-sandbox/document-apis/interfaces/IFillableNode.md", }, + { + title: "IMediaContainerNode", + path: "references/document-sandbox/document-apis/interfaces/IMediaContainerNode.md", + }, + { + title: "INodeBounds", + path: "references/document-sandbox/document-apis/interfaces/INodeBounds.md", + }, { title: "IRectangularNode", path: "references/document-sandbox/document-apis/interfaces/IRectangularNode.md", @@ -384,6 +400,10 @@ module.exports = { title: "IStrokableNode", path: "references/document-sandbox/document-apis/interfaces/IStrokableNode.md", }, + { + title: "IVisualNodeBounds", + path: "references/document-sandbox/document-apis/interfaces/IVisualNodeBounds.md", + }, { title: "ListItem", path: "references/document-sandbox/document-apis/interfaces/ListItem.md", @@ -420,6 +440,30 @@ module.exports = { title: "RectangleGeometry", path: "references/document-sandbox/document-apis/interfaces/RectangleGeometry.md", }, + { + title: "RemoveListStyleInput", + path: "references/document-sandbox/document-apis/interfaces/RemoveListStyleInput.md", + }, + { + title: "RescaleProportionalToHeightOptions", + path: "references/document-sandbox/document-apis/interfaces/RescaleProportionalToHeightOptions.md", + }, + { + title: "RescaleProportionalToWidthOptions", + path: "references/document-sandbox/document-apis/interfaces/RescaleProportionalToWidthOptions.md", + }, + { + title: "ResizeUsingHeightOptions", + path: "references/document-sandbox/document-apis/interfaces/ResizeUsingHeightOptions.md", + }, + { + title: "ResizeUsingWidthOptions", + path: "references/document-sandbox/document-apis/interfaces/ResizeUsingWidthOptions.md", + }, + { + title: "SolidColorStroke", + path: "references/document-sandbox/document-apis/interfaces/SolidColorStroke.md", + }, { title: "Stroke", path: "references/document-sandbox/document-apis/interfaces/Stroke.md", @@ -466,6 +510,10 @@ module.exports = { title: "FillType", path: "references/document-sandbox/document-apis/enumerations/FillType.md", }, + { + title: "ResizeBehavior", + path: "references/document-sandbox/document-apis/enumerations/ResizeBehavior.md", + }, { title: "OrderedListNumbering", path: "references/document-sandbox/document-apis/namespaces/Constants/enumerations/OrderedListNumbering.md", @@ -498,6 +546,10 @@ module.exports = { title: "TextScriptStyle", path: "references/document-sandbox/document-apis/enumerations/TextScriptStyle.md", }, + { + title: "TextStyleSource", + path: "references/document-sandbox/document-apis/namespaces/Constants/enumerations/TextStyleSource.md", + }, { title: "VisualEffectType", path: "references/document-sandbox/document-apis/enumerations/VisualEffectType.md", @@ -520,10 +572,18 @@ module.exports = { title: "Font", path: "references/document-sandbox/document-apis/type-aliases/Font.md", }, + { + title: "ListStyleInput", + path: "references/document-sandbox/document-apis/type-aliases/ListStyleInput.md", + }, { title: "OrderedListStyle", path: "references/document-sandbox/document-apis/type-aliases/OrderedListStyle.md", }, + { + title: "ResizeOptions", + path: "references/document-sandbox/document-apis/type-aliases/ResizeOptions.md", + }, { title: "SolidColorStrokeWithOptionalType", path: "references/document-sandbox/document-apis/type-aliases/SolidColorStrokeWithOptionalType.md", @@ -738,10 +798,10 @@ module.exports = { title: "Group Elements", path: "guides/learn/how_to/group_elements.md", }, - // { - // title: "Resize & Rescale Elements", - // path: "guides/learn/how_to/resize_rescale_elements.md", - // }, + { + title: "Resize Elements", + path: "guides/learn/how_to/resize_elements.md", + }, { title: "Position Elements", path: "guides/learn/how_to/position_elements.md", diff --git a/src/pages/guides/getting_started/changelog.md b/src/pages/guides/getting_started/changelog.md index 6bc617736..821a3e54a 100644 --- a/src/pages/guides/getting_started/changelog.md +++ b/src/pages/guides/getting_started/changelog.md @@ -45,6 +45,7 @@ contributors: - **Major restructure** of the [Add-on UI SDK Constants Reference](../../references/addonsdk/addonsdk-constants.md). - Improved content and metadata for SEO and AI assistant optimization. +- **Breaking change:** Removed experimental resize/rescale methods (`rescaleProportionalToWidth()`, `rescaleProportionalToHeight()`, `resizeToFitWithin()`, `resizeToCover()`) and replaced with unified **experimental** [`resize()`](../../references/document-sandbox/document-apis/classes/Node.md#resize) method offering more flexible behavior control via [`ResizeBehavior`](../../references/document-sandbox/document-apis/enumerations/ResizeBehavior.md) options. The [`experimentalApis`](../../references/manifest/index.md#requirements) flag is required. ## 2025-12-28 @@ -98,15 +99,6 @@ The Adobe Express Code Playground has received a major update with the release o ### Added -- **Experimental** [`rescaleProportionalToHeight()`](../../references/document-sandbox/document-apis/classes/Node.md#rescaleproportionaltoheight) method - Proportional height scaling for all node types -- **Experimental** [`rescaleProportionalToWidth()`](../../references/document-sandbox/document-apis/classes/Node.md#rescaleproportionaltowidth) method - Proportional width scaling for all node types -- **Experimental** [`resizeToCover()`](../../references/document-sandbox/document-apis/classes/Node.md#resizetocover) method - Resize nodes to cover specified dimensions -- **Experimental** [`resizeToFitWithin()`](../../references/document-sandbox/document-apis/classes/Node.md#resizetofitwithin) method - Resize nodes to fit within specified dimensions -- **Experimental** [`appendText()`](../../references/document-sandbox/document-apis/classes/TextContentModel.md#appendtext) method - Append text to existing content -- **Experimental** [`deleteText()`](../../references/document-sandbox/document-apis/classes/TextContentModel.md#deletetext) method - Delete text ranges from content -- **Experimental** [`insertText()`](../../references/document-sandbox/document-apis/classes/TextContentModel.md#inserttext) method - Insert text at specific positions -- **Experimental** [`replaceText()`](../../references/document-sandbox/document-apis/classes/TextContentModel.md#replacetext) method - Replace text ranges with new content -- **Experimental** [`hasUnavailableFonts()`](../../references/document-sandbox/document-apis/classes/TextContentModel.md#hasunavailablefonts) method - Check for unavailable fonts in text content - **Experimental** [`id`](../../references/document-sandbox/document-apis/classes/TextContentModel.md#id) property - Unique identifier for text content models - **Experimental** [`TextStyleSource`](../../references/document-sandbox/document-apis/namespaces/Constants/enumerations/TextStyleSource.md) enumeration - Options for text style source matching @@ -240,7 +232,7 @@ With MCP-enabled IDEs (Cursor, Claude Desktop, VS Code etc.), developers can [co ### Added - New [Markdown Parser add-on tutorial](../learn/how_to/tutorials/markdown-parser-text-api.md) covering the Text API, while building from scratch an add-on capable of parsing Markdown files and converting them into rich text directly within an Adobe Express document. -- New [Resize and Rescale Elements](../learn/how_to/resize_rescale_elements.md) how-to guide, which covers the new Resize/Rescale APIs, and provides examples and code snippets. +- New [Resize and Rescale Elements](../learn/how_to/resize_elements.md) how-to guide, which covers the new Resize/Rescale APIs, and provides examples and code snippets. ## 2025-06-19 @@ -259,10 +251,10 @@ The [Page Metadata API](../../references/addonsdk/app-document.md#pagemetadata) ### Added - Four new Resize/Rescale APIs have been added to the [Node](../../references/document-sandbox/document-apis/classes/Node.md) class as experimental features: - - [`rescaleProportionalToHeight()`](../../references/document-sandbox/document-apis/classes/Node.md#rescaleproportionaltoheight) - - [`rescaleProportionalToWidth()`](../../references/document-sandbox/document-apis/classes/Node.md#rescaleproportionaltowidth) - - [`resizeToCover()`](../../references/document-sandbox/document-apis/classes/Node.md#resizetocover) - - [`resizeToFitWithin()`](../../references/document-sandbox/document-apis/classes/Node.md#resizetofitwithin) + - `rescaleProportionalToHeight()` + - `rescaleProportionalToWidth()` + - `resizeToCover()` + - `resizeToFitWithin()` - [`Editor.createText()`](../../references/document-sandbox/document-apis/classes/Editor.md#createtext) now accepts a String parameter, which sets the text content of the new node. The use without a parameter is deprecated. - [`TextNode`](../../references/document-sandbox/document-apis/classes/TextNode.md) is now an abstract base class with two specialized subclasses: - [StandaloneTextNode](../../references/document-sandbox/document-apis/classes/StandaloneTextNode.md): displays text in a single frame. diff --git a/src/pages/guides/learn/how_to/images/rescale--rescale-proportional-height.png b/src/pages/guides/learn/how_to/images/rescale--rescale-proportional-height.png index 9a7ab13b5..b774af165 100644 Binary files a/src/pages/guides/learn/how_to/images/rescale--rescale-proportional-height.png and b/src/pages/guides/learn/how_to/images/rescale--rescale-proportional-height.png differ diff --git a/src/pages/guides/learn/how_to/images/resize--contain-media.png b/src/pages/guides/learn/how_to/images/resize--contain-media.png new file mode 100644 index 000000000..8647443de Binary files /dev/null and b/src/pages/guides/learn/how_to/images/resize--contain-media.png differ diff --git a/src/pages/guides/learn/how_to/images/resize--cover-one-dimension.png b/src/pages/guides/learn/how_to/images/resize--cover-one-dimension.png new file mode 100644 index 000000000..b9ae5e473 Binary files /dev/null and b/src/pages/guides/learn/how_to/images/resize--cover-one-dimension.png differ diff --git a/src/pages/guides/learn/how_to/images/resize--proportional-styles.png b/src/pages/guides/learn/how_to/images/resize--proportional-styles.png new file mode 100644 index 000000000..af69c61a7 Binary files /dev/null and b/src/pages/guides/learn/how_to/images/resize--proportional-styles.png differ diff --git a/src/pages/guides/learn/how_to/images/resize--within-media-crop.png b/src/pages/guides/learn/how_to/images/resize--within-media-crop.png new file mode 100644 index 000000000..d4f66e526 Binary files /dev/null and b/src/pages/guides/learn/how_to/images/resize--within-media-crop.png differ diff --git a/src/pages/guides/learn/how_to/images/resize-to-cover.png b/src/pages/guides/learn/how_to/images/resize-to-cover.png new file mode 100644 index 000000000..83a66a783 Binary files /dev/null and b/src/pages/guides/learn/how_to/images/resize-to-cover.png differ diff --git a/src/pages/guides/learn/how_to/resize_elements.md b/src/pages/guides/learn/how_to/resize_elements.md new file mode 100644 index 000000000..17417ec8d --- /dev/null +++ b/src/pages/guides/learn/how_to/resize_elements.md @@ -0,0 +1,583 @@ +--- +keywords: + - Adobe Express + - Express Add-on SDK + - Express Editor + - Adobe Express + - Add-on SDK + - SDK + - JavaScript + - Extend + - Extensibility + - API + - Resize + - ResizeBehavior + - Proportional + - Contain + - Cover + - Dimensions + - Aspect Ratio + - Visual Details + - Stroke Width +title: Resize Elements +description: Resize Elements with the unified Resize API. +contributors: + - https://github.com/undavide + - https://github.com/hollyschinsky +faq: + questions: + - question: "How do I resize an element?" + answer: 'Use `node.resize()` with options specifying width/height, behavior, and visual detail preferences.' + + - question: "How do I resize proportionally by width?" + answer: 'Call `node.resize({ width, behavior: ResizeBehavior.proportional, avoidScalingVisualDetailsIfPossible })` to maintain aspect ratio.' + + - question: "What's the difference between contain and cover behavior?" + answer: 'Contain fits the element within bounds; cover ensures the element completely covers the specified area.' + + - question: "What does avoidScalingVisualDetailsIfPossible do?" + answer: 'When true, tries to preserve stroke widths, corner radii, and font sizes at their original values.' + + - question: "Can I resize with only one dimension?" + answer: 'Yes, use `proportional` behavior (which requires only one dimension) or `contain` behavior with just width or height specified.' + + - question: "How do I resize pages?" + answer: 'Set `page.width` and `page.height` properties directly on the PageNode.' +--- + +# Resize Elements + +Adobe Express provides a powerful unified API for resizing elements. The [`resize()`](../../../references/document-sandbox/document-apis/classes/Node.md#resize) method gives you fine-grained control over how elements change size, whether they maintain aspect ratios, and how visual details like strokes and corners are affected. + + + +**IMPORTANT:** This API is currently **_experimental only_** and should not be used in any add-ons you will be distributing until it has been declared stable. To use it, you will first need to set the `experimentalApis` flag to `true` in the [`requirements`](../../../references/manifest/index.md#requirements) section of the `manifest.json`. + +## Understanding Resize Behavior + +The `resize()` method accepts a [`ResizeOptions`](../../../references/document-sandbox/document-apis/type-aliases/ResizeOptions.md) object with three mandatory components: + +1. **Dimensions**: Specify `width`, `height`, or both (depending on the behavior). +2. **Behavior**: Controls aspect ratio handling via [`ResizeBehavior`](../../../references/document-sandbox/document-apis/enumerations/ResizeBehavior.md) enumeration. +3. **Visual Details**: Choose whether to scale strokes, corners detailing, etc. + +### Resize Behaviors + +Defined in the [`ResizeBehavior`](../../../references/document-sandbox/document-apis/enumerations/ResizeBehavior.md) enumeration. + +- **`ResizeBehavior.contain`**: Fits the element entirely within the specified dimensions. +- **`ResizeBehavior.cover`**: Ensures the element completely covers the specified area. +- **`ResizeBehavior.proportional`**: Maintains the element's aspect ratio. + + + +**Dimension requirements:** `ResizeBehavior.proportional` requires only one dimension (never both); `ResizeBehavior.contain` can use one or both dimensions; `ResizeBehavior.cover` typically uses both dimensions. + +### Visual Details Flag + +The `avoidScalingVisualDetailsIfPossible` flag determines how visual styling is handled: + +- **`true`**: Preserves stroke widths, corner radii, etc. +- **`false`**: Scales all visual properties proportionally with the element. + +## Proportional Resizing + +Proportional resizing maintains the element's aspect ratio while changing its size. Specify either `width` or `height` (never both) and the other dimension will adjust automatically. + +### Example: Resize by Width + +```js +// sandbox/code.js +import { editor, colorUtils, constants } from "express-document-sdk"; + +// Create a rectangle +const rect = editor.createRectangle(); +rect.width = 200; +rect.height = 100; +rect.translation = { x: 100, y: 100 }; +rect.fill = editor.makeColorFill(colorUtils.fromHex("#3498db")); + +editor.context.insertionParent.children.append(rect); + +// Resize proportionally by width, scaling all visual details +rect.resize({ + width: 300, + behavior: constants.ResizeBehavior.proportional, + avoidScalingVisualDetailsIfPossible: false +}); + +console.log(`New dimensions: ${rect.width} x ${rect.height}`); +// New dimensions: 300 x 150 +``` + +![Resize by Width](./images/rescale--rescale-proportional-width.png) + +### Example: Resize by Height + +```js +// sandbox/code.js +import { editor, colorUtils, constants } from "express-document-sdk"; + +const ellipse = editor.createEllipse(); +ellipse.rx = 100; // radius x = 100 (width = 200) +ellipse.ry = 50; // radius y = 50 (height = 100) +ellipse.translation = { x: 150, y: 100 }; +ellipse.fill = editor.makeColorFill(colorUtils.fromHex("#F0B76C")); + +editor.context.insertionParent.children.append(ellipse); + +// Resize proportionally by height, scaling all visual details +ellipse.resize({ + height: 150, + behavior: constants.ResizeBehavior.proportional, + avoidScalingVisualDetailsIfPossible: false +}); + +console.log(`New bounds: ${ellipse.boundsLocal.width} x ${ellipse.boundsLocal.height}`); +// New bounds: 300 x 150 +``` + +![Resize by Height](./images/rescale--rescale-proportional-height.png) + +### Example: Preserve Visual Details + +When `avoidScalingVisualDetailsIfPossible` is set to `true`, strokes and other visual details maintain their original size: + +```js +// sandbox/code.js +import { editor, colorUtils, constants } from "express-document-sdk"; + +const rect = editor.createRectangle(); +rect.width = 100; +rect.height = 100; +rect.translation = { x: 100, y: 100 }; +rect.fill = editor.makeColorFill(colorUtils.fromHex("#3498db")); + +// Add a 5px stroke +const stroke = editor.makeStroke({ + color: colorUtils.fromHex("#2c3e50"), + width: 5, + position: constants.StrokePosition.inside +}); +rect.stroke = stroke; + +editor.context.insertionParent.children.append(rect); + +// Resize proportionally while preserving stroke width +rect.resize({ + width: 200, + behavior: constants.ResizeBehavior.proportional, + avoidScalingVisualDetailsIfPossible: true // preserves stroke width + // avoidScalingVisualDetailsIfPossible: false // scales stroke width +}); + +console.log(`Stroke width after resize: ${rect.stroke.width}px`); +// Stroke width after resize: 5px (unchanged) +``` + +![Preserve Visual Details](./images/resize--proportional-styles.png) + +## Contain Behavior + +The `contain` behavior fits an element entirely within specified dimensions, similar to CSS `object-fit: contain`. Elements with fixed aspect ratios may leave unused space on one axis. + +### Example: Contain Within Bounds (Shapes) + +```js +// sandbox/code.js +import { editor, colorUtils, constants } from "express-document-sdk"; + +const ellipse = editor.createEllipse(); +ellipse.rx = 100; // radius x = 100 (width = 200) +ellipse.ry = 50; // radius y = 50 (height = 100) +ellipse.translation = { x: 150, y: 100 }; +ellipse.fill = editor.makeColorFill(colorUtils.fromHex("#F0B76C")); + +editor.context.insertionParent.children.append(ellipse); +// Fit within a 150x150 box +ellipse.resize({ + width: 150, + height: 150, + behavior: constants.ResizeBehavior.contain, + avoidScalingVisualDetailsIfPossible: true +}); + +console.log(`Resized dimensions: ${ellipse.width} x ${ellipse.height}`); +// Resized dimensions: 150 x 150 +``` + +![Resize to Fit Within Bounds (Shapes)](./images/rescale--resize-within-ellipse.png) + + + +Shapes and Aspect Ratio + +Please note that shapes are resized to fit within a bounding box **disregarding their aspect ratio**; they are free to extend or shrink on both axes. + +### Example: Contain with One Dimension (Shapes) + +You can specify only one dimension with `contain` behavior to adjust that dimension while ideally keeping the other fixed: + +```js +// sandbox/code.js +import { editor, colorUtils, constants } from "express-document-sdk"; + +const ellipse = editor.createEllipse(); +ellipse.rx = 100; +ellipse.ry = 75; +ellipse.translation = { x: 150, y: 150 }; +ellipse.fill = editor.makeColorFill(colorUtils.fromHex("#e74c3c")); + +editor.context.insertionParent.children.append(ellipse); + +// Adjust width while attempting to preserve height +ellipse.resize({ + width: 300, + behavior: constants.ResizeBehavior.contain, + avoidScalingVisualDetailsIfPossible: true +}); + +console.log(`New bounds: ${ellipse.boundsLocal.width} x ${ellipse.boundsLocal.height}`); +``` + +![Resize to Fit Within Bounds (Shapes) with One Dimension](./images/resize--cover-one-dimension.png) + +### Example: Contain Within Bounds (Media) + +Media elements (images, videos) maintain their aspect ratio; cropping may happen when the aspect ratio of the media does not match the aspect ratio of the container. + +```js +// sandbox/code.js +import { editor, constants } from "express-document-sdk"; + +// Assuming the user has selected an Image node +const imageNode = editor.context.selection[0]; + +console.log( + "Initial dimensions: " + + imageNode.boundsLocal.width + + " x " + + imageNode.boundsLocal.height +); +// Initial dimensions: 300 x 200 + +imageNode.resize({ + width: 150, + height: 150, + behavior: constants.ResizeBehavior.contain, + avoidScalingVisualDetailsIfPossible: true +}); +console.log( + "Resized dimensions: " + + imageNode.boundsLocal.width + + " x " + + imageNode.boundsLocal.height +); +// Resized dimensions: 150 x 150 +``` + +![Resize to Fit Within Bounds (Media)](./images/resize--contain-media.png) + + + +Note that the resize behavior for **media elements** can differ significantly from shapes, especially when **cropping** is involved. + +This is because the **container** is resized to fit within the bounding box, while the **media** inside the container also needs to be adjusted to avoid blank space between the two. + +For instance, in the next example we’ll start with the 150x150 image we resized earlier, trying to fit it within a 300x150 box. The results can be counterintuitive, since resize, under the hood, uses a combination of steps to achieve the final dimensions — but it lacks the contextual understanding that Adobe Express has when users interact with the UI. This means that API-based resize calls should always be validated afterward to ensure they produce the expected outcome. + +```js +// sandbox/code.js +import { editor, constants } from "express-document-sdk"; + +// Assuming the user has selected an Image node +const imageNode = editor.context.selection[0]; + +console.log( + "Initial dimensions: " + + imageNode.boundsLocal.width + + " x " + + imageNode.boundsLocal.height +); +// Initial dimensions: 150 x 150 + +// Resize to fit within a 300x150 box +imageNode.resize({ + width: 300, + height: 150, + behavior: constants.ResizeBehavior.contain, + avoidScalingVisualDetailsIfPossible: true +}); + +console.log( + "Resized dimensions: " + + imageNode.boundsLocal.width + + " x " + + imageNode.boundsLocal.height +); +// Resized dimensions: 300 x 150 +``` + +![Resize to Fit Within Bounds (Media) reverse transformation](./images/resize--within-media-crop.png) + +## Cover Behavior + +The `cover` behavior ensures an element completely covers the specified area, similar to CSS `object-fit: cover`. Elements with fixed aspect ratios may extend beyond the target bounds on one axis. + +### Example: Cover an Area + +```js +// sandbox/code.js +import { editor, colorUtils, constants } from "express-document-sdk"; + +// Assuming the user has selected an Image node +const imageNode = editor.context.selection[0]; + +console.log( + "Initial dimensions: " + + imageNode.boundsLocal.width + + " x " + + imageNode.boundsLocal.height +); +// Initial dimensions: 150 x 100 + +// Resize to cover a 120x120 area +imageNode.resize({ + width: 120, + height: 120, + behavior: constants.ResizeBehavior.cover, + avoidScalingVisualDetailsIfPossible: true +}); + +console.log( + "Covered dimensions: " + + imageNode.boundsLocal.width + + " x " + + imageNode.boundsLocal.height +); +// Covered dimensions: 120 x 120 +``` + +![Resize to Cover](./images/resize-to-cover.png) + +In this example, the image is resized to cover a 120x120 area; the image is enlarged maintaining its aspect ratio, while the crop is adjusted to fit within the bounding box. + + + +With nodes that can resize irrespective of their aspect ratio, like shapes, the `ResizeBehavior.cover` and `ResizeBehavior.contain` methods yield the same result. + +## Working with Text Elements + +Text elements adjust their font size when resized. Both proportional and contain/cover behaviors produce similar results: + +### Example: Resize Text Proportionally + +```js +// sandbox/code.js +import { editor, constants } from "express-document-sdk"; + +const textNode = editor.createText("Hello, World!"); + +const insertionParent = editor.context.insertionParent; +textNode.setPositionInParent( + { x: insertionParent.width / 2, y: insertionParent.height / 2 }, + { x: 0, y: 0 } +); + +insertionParent.children.append(textNode); + +// Resize text proportionally - font size scales accordingly +textNode.resize({ + width: 300, + behavior: constants.ResizeBehavior.proportional, + avoidScalingVisualDetailsIfPossible: false +}); +``` + +![Rescale Text Proportionally](./images/rescale--rescale-proportional-text.png) + +### Example: Equivalent Resize Behaviors for Text + +As we've seen, when resizing text the font size is adjusted to fit within the specified bounds. Different combinations of `ResizeBehavior` parameters can produce equivalent results, as shown below. + +```js +// sandbox/code.js +// import { editor } from "express-document-sdk"; + +// Assuming the user has selected a text frame +const textNode = editor.context.selection[0]; + +// Both will result in a 200px wide text +textNode.resize({ + width: 200, height: 100, + behavior: constants.ResizeBehavior.contain, + avoidScalingVisualDetailsIfPossible: true +}) +textNode.resize({ + width: 200, + behavior: constants.ResizeBehavior.proportional, + avoidScalingVisualDetailsIfPossible: true +}) + +// Both will result in a 100px tall text +textNode.resize({ + width: 200, height: 100, + behavior: constants.ResizeBehavior.cover, + avoidScalingVisualDetailsIfPossible: true +}) +textNode.resize({ + height: 100, + behavior: constants.ResizeBehavior.proportional, + avoidScalingVisualDetailsIfPossible: true +}) +``` + +![Resizing and Rescaling Text](./images/rescale--rescale-resize-text.png) + +In the screenshot above, the original text is top-left; top-right, the result of both `resize()` with `contain` and `proportional` behavior (width-only); bottom, the result of both `resize()` with `cover` and `proportional` behavior (height-only). + +## Working with Pages + +Page resizing uses a simpler approach; simply set the `width` and `height` properties: + +```js +// sandbox/code.js +import { editor } from "express-document-sdk"; + +const page = editor.documentRoot.pages.first; +console.log(`Page dimensions before: ${page.width} x ${page.height}`); + +page.width = 300; +page.height = 300; + +console.log(`Page dimensions after: ${page.width} x ${page.height}`); +// Page dimensions before: 400 x 600 +// Page dimensions after: 300 x 300 +``` + +## Common Patterns + +### Resize to Exact Dimensions (Shapes) + +For shapes without aspect ratio constraints: + +```js +rect.resize({ + width: 300, + height: 200, + behavior: constants.ResizeBehavior.contain, + avoidScalingVisualDetailsIfPossible: true +}); +``` + +### Scale Everything Proportionally + +To scale an element like a zoom operation: + +```js +element.resize({ + width: newWidth, + behavior: constants.ResizeBehavior.proportional, + avoidScalingVisualDetailsIfPossible: false +}); +``` + +### Maintain Stroke Appearance + +To resize while keeping strokes crisp: + +```js +element.resize({ + width: newWidth, + height: newHeight, + behavior: constants.ResizeBehavior.contain, + avoidScalingVisualDetailsIfPossible: true +}); +``` + +### Thumbnail Generation + +To create thumbnails that fit within bounds: + +```js +image.resize({ + width: 150, + height: 150, + behavior: constants.ResizeBehavior.contain, + avoidScalingVisualDetailsIfPossible: true +}); +``` + +## Migration from Deprecated APIs + +If you're updating code that uses the old (experimental) resize/rescale methods, here are the conversions: + +```js +// OLD: +element.rescaleProportionalToWidth(300); + +// NEW: +element.resize({ + width: 300, + behavior: constants.ResizeBehavior.proportional, + avoidScalingVisualDetailsIfPossible: false +}); + +// OLD: +element.rescaleProportionalToHeight(200); + +// NEW: +element.resize({ + height: 200, + behavior: constants.ResizeBehavior.proportional, + avoidScalingVisualDetailsIfPossible: false +}); + +// OLD: +element.resizeToFitWithin(300, 200); + +// NEW: +element.resize({ + width: 300, + height: 200, + behavior: constants.ResizeBehavior.contain, + avoidScalingVisualDetailsIfPossible: true +}); + +// OLD: +element.resizeToCover(300, 200); + +// NEW: +element.resize({ + width: 300, + height: 200, + behavior: constants.ResizeBehavior.cover, + avoidScalingVisualDetailsIfPossible: true +}); +``` + +## FAQs + +#### Q: How do I resize an element? + +**A:** Use `node.resize()` with options specifying width/height, behavior, and visual detail preferences. + +#### Q: How do I resize proportionally by width? + +**A:** Call `node.resize({ width, behavior: ResizeBehavior.proportional, avoidScalingVisualDetailsIfPossible })` to maintain aspect ratio. + +#### Q: What's the difference between contain and cover behavior? + +**A:** Contain fits the element within bounds; cover ensures the element completely covers the specified area. + +#### Q: What does avoidScalingVisualDetailsIfPossible do? + +**A:** When true, tries to preserve stroke widths, corner radii, and font sizes at their original values. + +#### Q: Can I resize with only one dimension? + +**A:** Yes, use `proportional` behavior (which requires only one dimension) or `contain` behavior with just width or height specified. + +#### Q: How do I resize pages? + +**A:** Set `page.width` and `page.height` properties directly on the PageNode. diff --git a/src/pages/guides/learn/how_to/resize_rescale_elements.md b/src/pages/guides/learn/how_to/resize_rescale_elements.md deleted file mode 100644 index 746512904..000000000 --- a/src/pages/guides/learn/how_to/resize_rescale_elements.md +++ /dev/null @@ -1,390 +0,0 @@ ---- -keywords: - - Adobe Express - - Express Add-on SDK - - Express Editor - - Adobe Express - - Add-on SDK - - SDK - - JavaScript - - Extend - - Extensibility - - API - - Resize - - Rescale - - Proportional - - Fit Within - - Cover - - Dimensions - - Aspect Ratio -title: Resize and Rescale Elements -description: Resize and Rescale Elements. -contributors: - - https://github.com/undavide - - https://github.com/hollyschinsky -faq: - questions: - - question: "What's the difference between resize and rescale?" - answer: "Resize adjusts bounding box preserving stroke/font sizes; rescale proportionally changes all visual elements." - - - question: "How do I rescale proportionally by width?" - answer: 'Call `element.rescaleProportionalToWidth(newWidth)` to maintain aspect ratio.' - - - question: "How do I resize to fit within bounds?" - answer: 'Call `element.resizeToFitWithin(width, height)` to fit within specified dimensions.' - - - question: "How do I resize to cover an area?" - answer: 'Call `element.resizeToCover(width, height)` to completely cover the specified area.' - - - question: "Do shapes and media resize differently?" - answer: "Yes, shapes can resize freely; media maintains aspect ratio and may involve cropping." - - - question: "How does text resizing work?" - answer: "Text resize and rescale methods produce similar results by adjusting font size proportionally." - - - question: "How do I resize pages?" - answer: 'Set `page.width` and `page.height` properties directly on the PageNode.' ---- - -# Resize and Rescale Elements - -Adobe Express offers robust APIs for resizing and rescaling elements, ensuring different behaviors for aspect ratios and visual styling. It's essential to understand the difference between _resizing_ and _rescaling_ to achieve the desired visual results. - -## Resize vs. Rescale - -**Resizing** adjusts the bounding box of an element while trying to preserve the existing size of visual detailing such as strokes, corners, and fonts. - -**Rescaling** visually scales the entire content larger or smaller, which changes the size of visual styling elements such as stroke width, corner detailing, and font size proportionally. - - - -**IMPORTANT:** These APIs are currently **_experimental only_** and should not be used in any add-ons you will be distributing until they have been declared stable. To use them, you will first need to set the `experimentalApis` flag to `true` in the [`requirements`](../../../references/manifest/index.md#requirements) section of the `manifest.json`. - -## Rescale Elements Proportionally - -Rescaling operations maintain the aspect ratio of elements while changing their overall size. The visual styling elements (strokes, fonts, etc.) scale proportionally with the content. - -### Example: Rescale by Width - -Use `rescaleProportionalToWidth()` to adjust an element's width while maintaining its aspect ratio. The height will automatically adjust proportionally. - -```js{try id=rescaleByWidth} -// sandbox/code.js -import { editor, colorUtils } from "express-document-sdk"; - -// Create a rectangle with specific dimensions -const rect = editor.createRectangle(); -rect.width = 200; -rect.height = 100; -rect.translation = { x: 100, y: 100 }; -rect.fill = editor.makeColorFill(colorUtils.fromHex("#3498db")); - -// Add it to the page -editor.context.insertionParent.children.append(rect); - -// Rescale to 300px width - height becomes 150px automatically -rect.rescaleProportionalToWidth(300); - -console.log(`New dimensions: ${rect.width} x ${rect.height}`); -// New dimensions: 300 x 150 -``` - -![Rescale by Width](./images/rescale--rescale-proportional-width.png) - -### Example: Rescale by Height - -Similarly, use `rescaleProportionalToHeight()` to adjust an element's height while maintaining its aspect ratio. The width will automatically adjust proportionally. - -```js{try id=rescaleByHeight} -// sandbox/code.js -import { editor, colorUtils } from "express-document-sdk"; - -const ellipse = editor.createEllipse(); -ellipse.rx = 100; // radius x = 100 (width = 200) -ellipse.ry = 50; // radius y = 50 (height = 100) -ellipse.translation = { x: 150, y: 100 }; -ellipse.fill = editor.makeColorFill(colorUtils.fromHex("#F0B76C")); - -editor.context.insertionParent.children.append(ellipse); - -// Rescale to 150px height - width becomes 300px automatically -ellipse.rescaleProportionalToHeight(150); - -console.log(`New bounds: ${ellipse.boundsLocal.width} x ${ellipse.boundsLocal.height}`); -// New bounds: 300 x 150 -``` - -![Rescale by Height](./images/rescale--rescale-proportional-height.png) - -### Example: Rescaling with Styled Elements - -When rescaling elements with strokes and fills, all visual properties adjust proportionally: - -```js -// sandbox/code.js -// import { editor, colorUtils, constants } from "express-document-sdk"; - -const rect = editor.createRectangle(); -rect.width = 100; -rect.height = 100; - -// Apply styling -rect.fill = editor.makeColorFill(colorUtils.fromHex("#3498db")); -const stroke = editor.makeStroke({ - color: colorUtils.fromHex("#2c3e50"), - width: 5, - position: constants.StrokePosition.inside, -}); -rect.stroke = stroke; - -editor.context.insertionParent.children.append(rect); - -// Rescale proportionally - stroke width scales from 5px to 10px -rect.rescaleProportionalToWidth(200); -``` - -![Rescale with Styled Elements](./images/rescale--rescale-proportional-styles.png) - -## Resize Elements to Fit Constraints - -Resizing operations focus on fitting elements within specific dimensional constraints while trying to preserve visual styling elements at their original sizes. - -### Example: Resize to Fit Within Bounds (Shapes) - -Use `resizeToFitWithin()` to ensure an element fits entirely within specified dimensions. Elements with fixed aspect ratios may leave unused space on one axis. - -```js -// sandbox/code.js -import { editor } from "express-document-sdk"; - -const rect = editor.createRectangle(); -rect.width = 300; -rect.height = 200; - -editor.context.insertionParent.children.append(rect); - -// Resize to fit within a 150x150 box -rect.resizeToFitWithin(150, 150); - -console.log(`Resized dimensions: ${rect.width} x ${rect.height}`); -// Resized dimensions: 150 x 100 -``` - -![Resize to Fit Within Shapes](./images/rescale--resize-within-ellipse.png) - - - -Please note that shapes are resized to fit within a bounding box **disregarding their aspect ratio**; they are free to extend or shrink on both axes. - -### Example: Resize to Fit Within Bounds (Media) - -Media elements, such as images and videos follow the same rules as shapes. - -```js -// sandbox/code.js -import { editor } from "express-document-sdk"; - -// Assuming the user has selected an Image node -const imageNode = editor.context.selection[0]; - -console.log( - "Initial dimensions: " + - imageNode.boundsLocal.width + - " x " + - imageNode.boundsLocal.height -); -// Initial dimensions: 300 x 200 - -// Resize to fit within a 150x150 box -imageNode.resizeToFitWithin(150, 150); - -console.log( - "Resized dimensions: " + - imageNode.boundsLocal.width + - " x " + - imageNode.boundsLocal.height -); -// Resized dimensions: 150 x 150 -``` - -![Resize to Fit Within Media](./images/rescale--resize-within-media.png) - - - -Note that the resize behavior for media elements can differ significantly from shapes, especially when cropping is involved. - -This is because the container is resized to fit within the bounding box, while the media inside the container also needs to be adjusted to avoid blank space between the two. - -For instance, in the next example we’ll start with the 150x150 image we resized earlier, trying to fit it within a 300x150 box. The results can be counterintuitive, since resize, under the hood, uses a combination of steps to achieve the final dimensions — but it lacks the contextual understanding that Adobe Express has when users interact with the UI. This means that API-based resize calls should always be validated afterward to ensure they produce the expected outcome. - -```js -// sandbox/code.js -import { editor } from "express-document-sdk"; - -// Assuming the user has selected an Image node -const imageNode = editor.context.selection[0]; - -console.log( - "Initial dimensions: " + - imageNode.boundsLocal.width + - " x " + - imageNode.boundsLocal.height -); -// Initial dimensions: 150 x 150 - -// Resize to fit within a 300x150 box -imageNode.resizeToFitWithin(300, 150); - -console.log( - "Resized dimensions: " + - imageNode.boundsLocal.width + - " x " + - imageNode.boundsLocal.height -); -// Resized dimensions: 300 x 150 -``` - -![Resize to Fit Within Media](./images/rescale--resize-within-media-crop.png) - -### Example: Resize to Cover Area - -Use `resizeToCover()` to ensure an element completely covers the specified dimensions. Elements with fixed aspect ratios may extend outside the target bounds on one axis. - -```js -// sandbox/code.js -import { editor } from "express-document-sdk"; - -// Assuming the user has selected an Image node -const imageNode = editor.context.selection[0]; - -console.log( - "Initial dimensions: " + - imageNode.boundsLocal.width + - " x " + - imageNode.boundsLocal.height -); -// Initial dimensions: 150 x 100 - -// Resize to cover a 120x120 area -imageNode.resizeToCover(120, 120); - -console.log( - "Covered dimensions: " + - imageNode.boundsLocal.width + - " x " + - imageNode.boundsLocal.height -); -// Covered dimensions: 120 x 120 -``` - -![Resize to Cover](./images/rescale--resize-to-cover.png) - -In this example, the image is resized to cover a 120x120 area; the image is enlarged maintaining its aspect ratio, while the crop is adjusted to fit within the bounding box. - - - -With nodes that can resize irrespective of their aspect ratio, like shapes, the `resizeToCover()` and `resizeToFitWithin()` methods yield the same result. - -## Working with Text Elements - -Text elements require special considerations when resizing and rescaling, as font sizes and text flow can be affected differently. - -### Example: Rescaling Text Proportionally - -```js -// sandbox/code.js -import { editor } from "express-document-sdk"; - -const textNode = editor.createText("Hello, World!"); - -// Center the text -const insertionParent = editor.context.insertionParent; -textNode.setPositionInParent( - { x: insertionParent.width, y: insertionParent.height / 2 }, - { x: 0, y: 0 } -); - -insertionParent.children.append(textNode); - -// Rescale the text proportionally: the font size scales accordingly! -textNode.rescaleProportionalToWidth(300); -``` - -![Rescale Text Proportionally](./images/rescale--rescale-proportional-text.png) - -### Example: Similarities between Resizing and Rescaling Text - -As we’ve seen, when rescaling text, the font size is adjusted to fit within the specified bounds. The same applies when resizing, which means the four available methods produce pairs of equivalent results then the parameters are set appropriately. - -```js -// sandbox/code.js -// import { editor } from "express-document-sdk"; - -// Assuming the user has selected a text frame -const textNode = editor.context.selection[0]; - -// Both will result in a 200px wide text -textNode.resizeToFitWithin(200, 100); -textNode.rescaleProportionalToWidth(200); - -// Both will result in a 100px tall text -textNode.resizeToCover(200, 100); -textNode.rescaleProportionalToHeight(100); -``` - -![Resizing and Rescaling Text](./images/rescale--rescale-resize-text.png) - -In the screenshot above, the original text is top-left; top-right, the result of both `resizeToFitWithin()` and `rescaleProportionalToWidth()`; bottom, `resizeToCover()` -`rescaleProportionalToHeight()`. - -## Working with Page Elements - -Adjusting the page size is a simpler case of resizing and rescaling. Pages are resized by changing the `width` and `height` properties of the [`PageNode`](../../../references/document-sandbox/document-apis/classes/PageNode.md). - -### Example: Resizing a Page - -```js -// sandbox/code.js -import { editor } from "express-document-sdk"; - -const doc = editor.documentRoot.pages.first; -console.log(`Doc's dimensions before: ${doc.width}x${doc.height}`); - -doc.width = 300; -doc.height = 300; - -console.log(`Doc's dimensions after: ${doc.width}x${doc.height}`); - -// Doc's dimensions before: 400x600 -// Doc's dimensions after: 300x300 -``` - -## FAQs - -#### Q: What's the difference between resize and rescale? - -**A:** Resize adjusts bounding box preserving stroke/font sizes; rescale proportionally changes all visual elements. - -#### Q: How do I rescale proportionally by width? - -**A:** Call `element.rescaleProportionalToWidth(newWidth)` to maintain aspect ratio. - -#### Q: How do I resize to fit within bounds? - -**A:** Call `element.resizeToFitWithin(width, height)` to fit within specified dimensions. - -#### Q: How do I resize to cover an area? - -**A:** Call `element.resizeToCover(width, height)` to completely cover the specified area. - -#### Q: Do shapes and media resize differently? - -**A:** Yes, shapes can resize freely; media maintains aspect ratio and may involve cropping. - -#### Q: How does text resizing work? - -**A:** Text resize and rescale methods produce similar results by adjusting font size proportionally. - -#### Q: How do I resize pages? - -**A:** Set `page.width` and `page.height` properties directly on the PageNode. diff --git a/src/pages/references/changelog.md b/src/pages/references/changelog.md index 2e4cf48fa..809f06fd4 100644 --- a/src/pages/references/changelog.md +++ b/src/pages/references/changelog.md @@ -45,6 +45,8 @@ contributors: - **Major restructure** of the [Add-on UI SDK Constants Reference](./addonsdk/addonsdk-constants.md). - Improved content and metadata for SEO and AI assistant optimization. +- **Breaking change:** Removed experimental resize/rescale methods (`rescaleProportionalToWidth()`, `rescaleProportionalToHeight()`, `resizeToFitWithin()`, `resizeToCover()`) and replaced with unified **experimental** [`resize()`](../references/document-sandbox/document-apis/classes/Node.md#resize) method offering more flexible behavior control via [`ResizeBehavior`](../references/document-sandbox/document-apis/enumerations/ResizeBehavior.md) options. The [`experimentalApis`](../references/manifest/index.md#requirements) flag is required. +- ## 2025-12-28 @@ -98,15 +100,6 @@ The Adobe Express Code Playground has received a major update with the release o ### Added -- **Experimental** [`rescaleProportionalToHeight()`](../references/document-sandbox/document-apis/classes/Node.md#rescaleproportionaltoheight) method - Proportional height scaling for all node types -- **Experimental** [`rescaleProportionalToWidth()`](../references/document-sandbox/document-apis/classes/Node.md#rescaleproportionaltowidth) method - Proportional width scaling for all node types -- **Experimental** [`resizeToCover()`](../references/document-sandbox/document-apis/classes/Node.md#resizetocover) method - Resize nodes to cover specified dimensions -- **Experimental** [`resizeToFitWithin()`](../references/document-sandbox/document-apis/classes/Node.md#resizetofitwithin) method - Resize nodes to fit within specified dimensions -- **Experimental** [`appendText()`](../references/document-sandbox/document-apis/classes/TextContentModel.md#appendtext) method - Append text to existing content -- **Experimental** [`deleteText()`](../references/document-sandbox/document-apis/classes/TextContentModel.md#deletetext) method - Delete text ranges from content -- **Experimental** [`insertText()`](../references/document-sandbox/document-apis/classes/TextContentModel.md#inserttext) method - Insert text at specific positions -- **Experimental** [`replaceText()`](../references/document-sandbox/document-apis/classes/TextContentModel.md#replacetext) method - Replace text ranges with new content -- **Experimental** [`hasUnavailableFonts()`](../references/document-sandbox/document-apis/classes/TextContentModel.md#hasunavailablefonts) method - Check for unavailable fonts in text content - **Experimental** [`id`](../references/document-sandbox/document-apis/classes/TextContentModel.md#id) property - Unique identifier for text content models - **Experimental** [`TextStyleSource`](../references/document-sandbox/document-apis/namespaces/Constants/enumerations/TextStyleSource.md) enumeration - Options for text style source matching @@ -234,7 +227,7 @@ With MCP-enabled IDEs (Cursor, Claude Desktop, VS Code etc.), developers can [co ### Added - New [Markdown Parser add-on tutorial](../guides/learn/how_to/tutorials/markdown-parser-text-api.md) covering the Text API, while building from scratch an add-on capable of parsing Markdown files and converting them into rich text directly within an Adobe Express document. -- New [Resize and Rescale Elements](../guides/learn/how_to/resize_rescale_elements.md) how-to guide, which covers the new Resize/Rescale APIs, and provides examples and code snippets. +- New [Resize and Rescale Elements](../guides/learn/how_to/resize_elements.md) how-to guide, which covers the new Resize/Rescale APIs, and provides examples and code snippets. ## 2025-06-19 @@ -253,10 +246,10 @@ The [Page Metadata API](./addonsdk/app-document.md#pagemetadata) now includes ne ### Added - Four new Resize/Rescale APIs have been added to the [Node](./document-sandbox/document-apis/classes/Node.md) class as experimental features: - - [`rescaleProportionalToHeight()`](./document-sandbox/document-apis/classes/Node.md#rescaleproportionaltoheight) - - [`rescaleProportionalToWidth()`](./document-sandbox/document-apis/classes/Node.md#rescaleproportionaltowidth) - - [`resizeToCover()`](./document-sandbox/document-apis/classes/Node.md#resizetocover) - - [`resizeToFitWithin()`](./document-sandbox/document-apis/classes/Node.md#resizetofitwithin) + - `rescaleProportionalToHeight()` + - `rescaleProportionalToWidth()` + - `resizeToCover()` + - `resizeToFitWithin()` - [`Editor.createText()`](./document-sandbox/document-apis/classes/Editor.md#createtext) now accepts a String parameter, which sets the text content of the new node. The use without a parameter is deprecated. - [`TextNode()`](./document-sandbox/document-apis/classes/TextNode.md) is now an abstract base class with two specialized subclasses: - [StandaloneTextNode](./document-sandbox/document-apis/classes/StandaloneTextNode.md): displays text in a single frame.