Table of contents
-
Basic operators
-
Arithmetic operators (
+,-,*,/,mod) -
Comparison operators (
=,!=,>,>=,<,<=) -
Logical operators (
and,or,not)
-
Arithmetic operators (
-
Ternary operator (
? :) -
null-coalescing operator (
??) -
Numeric and interval operators
-
Range operator (
..) -
Tolerance operator (
~,~+,~-) -
Distance operator (
_)
-
Range operator (
- Containment and intersection operators
- Sequence and string operators
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 treeUse 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)