Appearance
Groups
A group turns several slide objects into one PowerPoint object, with one Selection Pane entry, one drag target and one rotation handle.
ts
import TsPptx from "pptx-ts"
const pptx = new TsPptx()
const slide = pptx.addSlide()
slide.addGroup(
[
{ rect: { x: 1, y: 1, w: 2, h: 1, fill: { color: "CC0000" } } },
{ text: { text: "Label", options: { x: 1.2, y: 1.2, w: 1.6, h: 0.6, color: "FFFFFF" } } },
],
{ objectName: "Badge" },
)
await pptx.writeFile({ fileName: "group.pptx" })slide.addGroup(children, options?) builds a group from child descriptors. slide.groupObjects(objectNames, options?) groups objects that are already on the slide. Both take the same options and return the slide.
Options at a glance
| Option | Type | Default | Effect |
|---|---|---|---|
x, y, w, h | Coord | the children's bounding box | The group frame. Pass all four or none |
rotate | number | none | Rotates the whole group, in degrees |
flipH, flipV | boolean | false | Mirrors the whole group |
objectName | string | Group 1, Group 2, ... | Selection Pane name |
altText | string | none | Alt text for the group |
objectLock | ObjectLockProps | none | Locks. A group takes noGrp, noSelect, noRot, noChangeAspect, noMove and noResize |
The identity child space
A group never moves or scales its children. Each child stays at the slide position you gave it, and the group frame only sets the selection box and the centre the group rotates about.
So you place every child at its final slide position, and grouping leaves the slide looking exactly as it did. Text that fits measures text inside a group at its authored size, like any other text box.
Build a group from descriptors
ts
slide.addGroup(
[
{ roundRect: { x: 1, y: 1, w: 3, h: 1.5, fill: { color: "1F3A5F" } } },
{ shape: { type: "ellipse", options: { x: 1.2, y: 1.2, w: 1, h: 1, fill: { color: "FFFFFF" } } } },
{ image: { path: "logo.png", x: 2.5, y: 1.25, w: 1, h: 1 } },
{ line: { x: 1, y: 2.7, w: 3, h: 0, line: { color: "1F3A5F" } } },
],
{ objectName: "Logo lockup" },
)| Descriptor | Adds |
|---|---|
{ rect: ShapeProps }, { roundRect: ShapeProps }, { line: ShapeProps } | a shape of that preset |
{ shape: { type, options } } | a shape of any preset in SHAPE_NAME |
{ text: { text, options } } | a text box |
{ image: ImageProps } | an image |
{ group: { children, options } } | a nested group |
- Each child takes slide coordinates, the same options its
add*method takes. - The array order is the stacking order. The first child is at the bottom.
- Charts, tables, media and placeholders cannot be group children. The type rejects them, and at run time each one warns and is skipped.
Set the group frame
| You pass | Frame written |
|---|---|
none of x, y, w, h | the bounding box of the children as they are drawn, nested groups included |
| all four | that box, exactly |
| one, two or three of them | a group/partial-frame warning, then the bounding box |
ts
// The frame is larger than the rectangle, and the rectangle stays at x 1, y 1
slide.addGroup([{ rect: { x: 1, y: 1, w: 2, h: 1 } }], { x: 0.5, y: 0.5, w: 3, h: 2 })- A partial frame such as
{ x: 5 }reads like a move, and a group frame cannot move its children, so ts-pptx falls back to the bounding box rather than draw the frame away from its content. - The bounding box uses the size each child is drawn at. A text box with no
wcounts at its default width, and an image with asizingbox counts at that box. - A negative
worhon an explicit frame becomes a positive size plus a flip. See Positions and sizes.
Rotate, flip and lock a group
ts
slide.addGroup([{ rect: { x: 1, y: 1, w: 2, h: 1 } }], {
rotate: 45,
flipH: true,
objectName: "Badge",
altText: "Award badge",
objectLock: { noMove: true, noResize: true },
})rotate,flipHandflipVapply to the whole group, about the centre of its frame.- A lock flag that groups do not support, such as
noCrop, warns and is left out.
Nest groups
ts
slide.addGroup(
[
{ rect: { x: 1, y: 1, w: 1, h: 1 } },
{
group: {
children: [
{ rect: { x: 3, y: 1, w: 1, h: 1 } },
{ text: { text: "Nested", options: { x: 3, y: 1, w: 1, h: 1 } } },
],
options: { objectName: "Inner" },
},
},
],
{ objectName: "Outer" },
)- A
groupchild nests to any depth, and every level keeps its children where they were placed. - Default names count per slide, and a nested group takes its number before the group around it. An unnamed group holding an unnamed group, on a slide with no other groups, writes
Group 2outside andGroup 1inside. groupObjects()also accepts an existing group as a member.
Group objects already on a slide
groupObjects() takes the objectName of each top-level object to group. It suits a slide built by separate functions, where replaying each object's descriptor just to group it is not an option.
ts
slide.addShape("rect", { x: 1, y: 1, w: 2, h: 1, objectName: "Header" })
slide.addText("Caption", { x: 1.2, y: 2.2, w: 1.6, h: 0.6, objectName: "Caption" })
slide.addShape("rect", { x: 5, y: 1, w: 1, h: 1, objectName: "Loose" })
slide.groupObjects(["Header", "Caption"], { objectName: "Banner" })
// "Header" and "Caption" are now inside "Banner". "Loose" stays top-level.| Object | Groupable |
|---|---|
| shapes, text boxes, images, connectors and groups | yes |
| charts, tables, media, 3D models, OLE objects and zooms | no, the call throws |
| any object in a layout placeholder | no, the call throws |
The call keeps the slide looking the same. Here A and C are grouped with slide.groupObjects(["C", "A"]), and each column lists the stack from the top down:
- The members keep their stacking order among themselves. The order of the names in the array does not matter.
- The group takes the place of the topmost member.
- An object that sat between two members ends up above the group.
- Every name is checked before anything moves, so a call that throws leaves the slide unchanged.
The frame and naming rules are the same as for addGroup().
List what a slide holds
slide.objects returns the objects on the slide, bottom of the stack first, with the names groupObjects() accepts. It is how code that did not add the objects finds out what to group.
ts
slide.addShape("rect", { x: 1, y: 1, w: 3, h: 2, objectName: "card:frame" })
slide.addText("Revenue", { x: 1.2, y: 1.2, w: 2.6, h: 0.5, objectName: "card:title" })
slide.addText("Draft", { x: 5, y: 1, w: 1, h: 0.5 })
for (const o of slide.objects) {
console.log(o.type, o.objectName, o.canGroup)
}
// text card:frame true
// text card:title true
// text Text 3 true
const card = slide.objects.filter((o) => o.canGroup && o.objectName.startsWith("card:"))
slide.groupObjects(card.map((o) => o.objectName), { objectName: "Card" })Each entry is a SlideObjectInfo:
| Field | Meaning |
|---|---|
type | the kind the object was added as. Shapes and text boxes both report "text" |
objectName | the name you gave, spelled as you gave it, or the generated name PowerPoint shows, such as Shape 3 |
isPlaceholder | the object fills a layout placeholder, which groupObjects() refuses |
canGroup | groupObjects() accepts this kind of object |
children | a group's members, bottom first, nested to any depth. Empty for anything else |
- A name read from
slide.objectsresolves when you pass it back, including names that contain&,<or quotes. canGroupspeaks for the object alone. A call can still throw for a name that matches nothing, or more than one object.- Each read returns a fresh snapshot. Changing it does nothing to the slide.
- Speaker notes are not listed. They belong to the notes page rather than to the slide's stack, and nothing can group them.
Target a shape inside a group
Group children are named on the slide like top-level objects, so a connector end or an animation can point at one.
ts
slide.addGroup([{ rect: { x: 1, y: 1, w: 2, h: 1, objectName: "boxInGroup" } }], { objectName: "Grp" })
slide.addConnector({ type: "elbow", x1: 3, y1: 1.5, x2: 6, y2: 4.5, startShape: "boxInGroup", startShapeIdx: 3 })
slide.addAnimation({ preset: "fadeIn", objectName: "boxInGroup" })Bind a connector to a shape has the lookup order and what happens when a name does not resolve. An animation whose name resolves to nothing warns animation/target-not-found and is left out.
Invalid input
addGroup() warns and carries on. groupObjects() throws instead, because a failed lookup would otherwise leave an object outside the group with no sign of it. Rows marked "when written" are reported while the deck is exported.
| Condition | Warns or throws | Code |
|---|---|---|
an addGroup() child is a chart, table, media or placeholder | warns, child skipped | group/unsupported-child |
an addGroup() child has a key no descriptor uses | warns, child skipped | group/unrecognized-child |
an addGroup() child needs a construct family the presentation was composed without | warns, child skipped | family/child-not-composed |
addGroup() is left with no child to draw | warns, writes a zero-size group | group/no-children |
one to three of x, y, w, h are set (when written) | warns, uses the bounding box | group/partial-frame |
| a lock flag groups do not support (when written) | warns, flag left out | object-lock/unsupported-on-shape |
an objectName appears twice on the slide, group children included (when written) | warns | object-name/duplicate |
groupObjects() gets an empty list, or no list | throws InvalidOptionError | group/missing-object-names |
| a name is not a non-empty string | throws InvalidOptionError | group/invalid-object-name |
| a name appears twice in the list | throws InvalidOptionError | group/duplicate-object-name |
| no top-level object has the name. The message says when it is already inside a group | throws InvalidOptionError | group/unresolved-object-name |
| more than one top-level object has the name | throws InvalidOptionError | group/ambiguous-object-name |
| the object's kind cannot be grouped, or it is a placeholder | throws UnsupportedFeatureError | group/kind-not-groupable |
Composing a presentation from construct families is covered in Smaller bundles. Errors and warnings covers the error classes and how to route warnings.
Limits
addGroup()takes only the descriptors in the table above.groupObjects()takes shapes, text boxes, images, connectors and groups only.groupObjects()reaches top-level objects only, so an object belongs to one group at a time.- A written group never scales its children.
- There is no call to ungroup.
- A group takes no fill or outline of its own.
Reading it back
- Before export,
slide.objectsreports groups and theirchildren, as described above. - After loading a deck with
pptx-ts/read, a group is aGroupShapewhoseshapesare its children. Read object model covers each child's slide-absolute geometry through the groups around it. - Inspect a package reports groups as
kind: 'group'elements, and gives each child a slide-absolute box.
See also
- Connectors
- Positions and sizes
- Text that fits
- Demos, whose showcase deck groups its content
- Errors and warnings
- API reference:
Slide,GroupProps,GroupChildProps,SlideObjectInfo,ObjectLockProps