Skip to main content

Operators

Table of contents


Basic operators

Arithmetic operators

The Expression Language supports the basic arithmetic operators for integer and double values. Integer values are automatically converted to doubles, so integer and double values can be mixed in calculations and comparisons. For example, 2 = 2.0 is true.

  • Plus: +
  • Minus: -
  • Multiply: *
  • Divide: /
  • Modulo: mod

The mod operator returns the remainder of a division. It works with integer and double values and is commonly used to check divisibility, alternating values (e.g. even/odd), or repeating patterns. It is evaluated with the same precedence as multiplication and division.

Examples:

shape.right - shape.left    // equals shape.width

17 mod 3          // equals 2 (or 2.0)
17.5 mod 3 // equals 2.5 7.5 mod 2.0 // equals 1.5 // Even / odd check 10 mod 2 // equals 0 11 mod 2 // equals 1 index mod 2 = 0 // true for even index values

Comparison operators

Comparison operators compare values and return a boolean. String comparisons are case-sensitive.

  • Equals: =
  • Not equals: !=
  • Greater than: >
  • Greater than or equal to: >=
  • Less than: <
  • Less than or equal to: <=

Example:

shape.bottom >= 200.0
"arial" = "Arial" // false (case-sensitive)

Logical operators

Logical operators work on boolean expressions. They combine conditions and always return true or false.

Parentheses ( ) are used to group expressions and control evaluation order. Expressions inside parentheses are evaluated first, which is especially useful when combining multiple logical operators such as and and or.

and

The and operator returns true if both operands evaluate to true.

Example:

shape.left > 30.0 and shape.top < 500.0
// true only if both conditions are fulfilled

or

The or operator returns true if at least one operand evaluates to true.

Example:

shape.right >= 200.0 or shape.bottom < 100.0

You can combine multiple logical operators in a single expression. Parentheses can be used to control evaluation order.

Example:

(shape.left > 30.0 and shape.top < 500.0) or shape.right >= 200.0

not

The not operator inverts the result of the operators in, inside, intersects and match. It is placed directly before the operator it inverts, for example not in or not inside.

The not operator cannot be used on its own to negate other expressions. For example, not (shape.left > 30.0) is not supported. To invert a comparison, use the opposite comparison operator instead, such as shape.left <= 30.0.

Examples:

10 not in [1,2,3]  // true
Rectangle(0.0,0.0,10.0,10.0) not intersects Rectangle(20.0,20.0,30.0,30.0)  // true
5 not inside 4..6   // false

Rectangle(5.0, 5.0, 15.0, 15.0) not inside Rectangle(0.0, 0.0, 10.0, 10.0) 
// true (partially overlaps, not fully contained)

shape.text not match "•"
// Find all text shapes in the document that don't contain a bullet •

Ternary operator

The ternary operator uses the signs ? and :.

It acts as a logical if .. then .. else. The expression before ? is the condition. If it is true, the value after ? is used; otherwise, the value after : is used.

Example:

reference.text = "Datum:" ? "de" : "en"  // If reference.text equals "Datum:"
                                         // then use "de", else use "en" 

You can chain ternary operators. 

Example:

reference.text = "Datum" ? "de" : reference.text = "Fecha" ? "es" : "en"
// If reference.text = "Datum", use "de"
// Else if reference.text = "Fecha", use "es"
// Else use "en"

null-coalescing operator

The coalescence operator deals with missing values and uses the ?? sign.

It checks whether the left operand is null and acts as a shortcut for if a = null then b else a.

Examples:

prev.left ?? 0.0        // If there is no previous item, prev.left is null.
                        // In this case, use 0.0 instead of null.
                        // If there is a previous item, prev.left is not null.
                        // In this case, use prev.left.
                  
shape.font.size ?? 9.0  // If shape is a path or image shape, it has no font; shape.font.size 
                        // is therefore null. In this case, use 9.0 as the font size.

Note

The Expression Language has built-in support for null values and handles such cases automatically. Most likely you will not need the coalescence operator in your templates.


Numeric and interval operators

The Expression Language supports several operators that work on numbers and numeric intervals.

Range operator

The range operator uses the .. sign and works with integer or double values.

It creates an inclusive range from the first value to the second value. The range covers all values between the two bounds, including the bounds themselves. This also applies to ranges with integer bounds, so decimal values within the bounds are included as well.

Examples:

1..5              // range from 1 to 5, including 1 and 5 
1.0..5.0 // range from 1.0 to 5.0, including 1.0 and 5.0
4.893 inside 4..6 // true, because 4.893 lies between 4 and 6

Use the inside operator to check whether a value is within a range.

Tolerance operator

The tolerance operator uses the ~ sign and works with double values.

It creates a numeric tolerance range around a base value. The result is an inclusive range that extends the given value by a specified amount, either on both sides or on one side only:

  • ~ spreads the value both below and above 
  • ~+ spreads the value only upwards
  • ~- spreads the value only downwards

This operator is especially useful for handling rounding differences, measurement inaccuracies, or layout tolerances.

Examples:

16.0 ~ 0.5    // equal to 15.5..16.5
16.0 ~+ 0.5   // equal to 16.0..16.5
16.0 ~- 0.5   // equal to 15.5..16.0

Use the inside operator to check whether a value falls within a tolerance range.

Distance operator

The distance operator uses the _ sign and works with integer and double values.

It returns the absolute difference between two values, which is how far apart they are, regardless of which value is larger. The expression a _ b is equivalent to |a − b|. The result is therefore never negative, and the order of the operands doesn't matter.

This makes the distance operator useful for measuring gaps between shapes when you don't know in advance which value is larger.

Examples:

3 _ 5      // returns 2 
5 _ 3 // returns 2
2.5 _ 4.0 // returns 1.5
3 _ 5 = 2 // true
this.bottom _ next.top > 2.0 // In a Find sequence node, // finds paragraphs with a spacing of more than 2 points

Containment and intersection operators

in

The in operator checks whether a value exists within a sequence or a string. Its behavior varies slightly depending on the types of the operands. String checks are case-sensitive.

1. String in a sequence of strings

If the first operand is a string and the second operand is a sequence of strings, the in operator checks whether the string exists as a complete element in the sequence.

Examples:

"Verdana" in ["Arial", "Verdana", "Helvetica"]  // true
"Bold" in ["Verdana", "Verdana Bold"]           // false

shape.font.name in ["Verdana", "Verdana Bold"]

In the second example, the result is false because the operator does not match substrings within sequence elements. It only matches complete elements.

2. Sequence in another sequence (element-wise check)

If the first operand is a sequence, its elements are effectively “unpacked,” and the operator checks whether they appear as a contiguous subsequence in the second sequence. This means that order matters and the elements must occur next to each other.

Examples:

[1, 2] in [0, 1, 2, 3] // true
[2, 1] in [0, 1, 2, 3] // false (order does not match)
[1, 3] in [0, 1, 2, 3] // false (elements are not consecutive)
[1, 4] in [0, 1, 2, 3] // false (4 is not in the sequence)

In summary, the first sequence must appear exactly, in the same order, and without gaps, within the second sequence.

3. Value in a sequence or substring in a string

In all other cases, the operator checks whether the first operand is directly contained in the second operand.

Examples:

1 in [1, 2, 3]  // true
4 in [1, 2, 3]  // false

"hello" in "hello world"  // true
"world" in "hello world"  // true
"test" in "hello world"   // false
"Hello" in "hello world" // false (case-sensitive)

When used with strings, the operator performs a substring check.

inside

The inside operator checks whether a value or geometric entity lies completely within an inclusive range or area. Inclusive means that the lower and upper bounds of the range are included as well.

For geometric entities, inside returns true only if the entire entity is contained within the specified area. If it merely overlaps the area, the result is false. Consequently, not inside returns true for overlapping entities because they are not fully contained within the area.

Examples:

5 inside 4..6             // true
4 inside 4..6             // true
6 inside 4..6             // true
4.893 inside 4..6         // true
4.893 inside 5.0 ~ 1.0    // true
Point(5.0, 5.0) inside Rectangle(0.0, 0.0, 10.0, 10.0)                 // true
Rectangle(2.0, 2.0, 5.0, 5.0) inside Rectangle(0.0, 0.0, 10.0, 10.0)   // true

Rectangle(5.0, 5.0, 15.0, 15.0) inside Rectangle(0.0, 0.0, 10.0, 10.0) 
// false (partially overlaps but is not fully contained)

Rectangle(5.0, 5.0, 15.0, 15.0) not inside Rectangle(0.0, 0.0, 10.0, 10.0) 
// true (not fully contained)

intersects

The intersects operator checks whether two rectangles or two ranges overlap. It returns true if there is at least one shared value or area between the operands. Intersection checks are inclusive; shared boundaries count as intersection.

Examples:

Rectangle(10.0, 10.0, 100.0, 40.0) intersects Rectangle(80.0, 30.0, 150.0, 70.0)
// true (overlapping area)

Rectangle(0.0, 0.0, 50.0, 50.0) intersects Rectangle(50.0, 0.0, 100.0, 50.0)
// true (shared vertical border)

Rectangle(0.0, 0.0, 50.0, 50.0) intersects Rectangle(0.0, 60.0, 50.0, 100.0)
// false (gap between rectangles)

10..20 intersects 15..25      // true (common values: 15–20)
1..5 intersects 5..10         // true (shared boundary value: 5)
1..4 intersects 5..10         // false
9.5..10.5 intersects 10..12   // true (10–10.5 overlaps)

Use the inside operator to check whether a point is inside a rectangle.


Sequence and string operators

Sequence indexer

The sequence indexer uses square brackets [] and returns the element at a specific position within a sequence. The position is zero-based: the first element has the index 0, the second element has the index 1, and so on.

If the index is out of range, the sequence indexer returns null.

Examples:

item.pages[0]                       // returns the first page object of the shape tree
item.pages[item.pages.count - 1]    // returns the last page object of the shape tree
item.pages[1].index                 // returns the index of the second page containing shapes from the shape tree

item.pages[item.pages.count].index  // returns null (index out of range)

To retrieve several elements at once, use the slice operator. To retrieve a property from all elements of a sequence, use the collect operator.

collect

The collect operator uses the :: sign and is applied to a sequence of objects.

It returns a sequence of all unique values of a single property across all objects in the sequence. Each property is collected independently, so duplicate values are included only once, even if they belong to different objects.

If the sequence is empty, the collect operator returns an empty sequence.

Examples:

item.fonts::name                  // returns a sequence of all unique font names within the shape tree
item.fonts::size                  // returns a sequence of all unique font sizes within the shape tree

item.pages::index                 // returns a sequence of the indexes of all pages
                                  // containing shapes from the shape tree

item.pageBBoxes::width            // returns a sequence of all unique widths of the
                                  // bounding boxes of the shape tree

"Arial" in item.fonts::name       // true if the font "Arial" is used within the shape tree
"Arial" not in item.fonts::name   // true if the font "Arial" is not used within the shape tree

Use the in operator to check whether a specific value exists in the collected sequence.

find

The find operator returns the position of the first occurrence of a subsequence in a sequence as an integer value (zero-based). Since strings are considered sequences of characters, find can also find the first occurrence of one string within another string.

If a subsequence is not found in the sequence, find returns null. When searching strings, find is case-sensitive.

Examples:

[1,2,3,4,5] find [3,4]             // returns 2
"hello world" find "world"         // returns 6
"hello world" find ""              // returns 0
"hello world" find "space"         // returns null
"hello world" find "World" // returns null (case-sensitive)

concat

The concat operator concatenates two strings.

Examples:

"hello " concat "world"              // returns "hello world"
"Bill No. " concat reference.text    // Use "Bill No. " as static text and add a dynamic
                                     // text from the document (reference.text)

slice

The slice operator extracts a substring or subsequence from a string or sequence, based on a range (zero-based).

Examples:

[1, 2, 3, 4, 5, 6] slice 2..4    // returns [3, 4, 5]
"hello world" slice 6..10        // returns "world"

match

The match operator checks whether a string matches a regular expression pattern.

Example:

shape.text match "^Invoice|^Reminder"  // Find all text shapes in the document 
                                       // that start with "Invoice" or "Reminder"

Warning

The match operator can significantly slow down processing. Only use it if there are no alternative selection options.

We recommend using the in or find operators instead:

"Invoice" in shape.text               // shape.text contains "Invoice" anywhere
shape.text find "Invoice" = 0         // shape.text starts with "Invoice"

// Replace the slow regex match operator of the previous example with this expression:
(shape.text find "Invoice" = 0) or (shape.text find "Reminder" = 0)