String functions are available throughout the Articles report script editor and can be used in band event handlers, script procedures, and inline expressions enclosed in square brackets within text objects.
Chr / Ord
Chr converts an integer ASCII code to its corresponding character. Ord does the reverse โ converting a character to its ASCII code. Useful for inserting special characters such as tabs and line breaks, or for checking whether a character falls within a particular range.
Chr(I: Integer): Char
Ord(Ch: Char): Integer
| Parameter | Type | Description |
|---|---|---|
| I | Integer | ASCII code to convert to a character (Chr) |
| Ch | Char | Character to convert to an ASCII code (Ord) |
// Insert a tab character between two values (useful in TSV export scripts)
Result := [Customer.Name] + Chr(9) + [Customer.Phone];
// Insert a Windows line break into a multi-line value
Result := 'Line one' + Chr(13) + Chr(10) + 'Line two';
// Check whether the first character of a code is a letter A-Z
var firstChar := [Item.Code][1];
if (Ord(firstChar) >= Ord('A')) and (Ord(firstChar) <= Ord('Z')) then
Result := 'Alpha code'
else
Result := 'Numeric code';
CompareText
Compares two strings without regard to case. Returns 0 if equal, a negative number if the first string is less, and a positive number if it is greater. Use this for case-insensitive comparisons without needing to call Lowercase on both sides first.
CompareText(S1: String; S2: String): Integer
| Parameter | Type | Description |
|---|---|---|
| S1 | String | The first string |
| S2 | String | The second string to compare against |
Returns 0 if equal, negative if S1 < S2, positive if S1 > S2.
// 'ACTIVE', 'Active', and 'active' all match
if CompareText([Customer.Status], 'active') = 0 then
Result := 'Current Customer'
else
Result := 'Inactive';
// Check for one of several status values
var status := [Orders.Status];
if (CompareText(status, 'shipped') = 0) or
(CompareText(status, 'delivered') = 0) then
Result := 'Fulfilled'
else
Result := 'Pending';
// Alphabetical range check
if CompareText([Customer.Name], 'M') < 0 then
Result := 'A - L'
else
Result := 'M - Z';
Copy
Extracts a substring starting at a given position for a given number of characters. Positions are 1-based โ the first character is at position 1. Copy and MidStr do the same thing; use whichever name you prefer.
Copy(S: String; From: Integer; Count: Integer): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The source string |
| From | Integer | Starting position (1 = first character) |
| Count | Integer | Number of characters to extract |
Result := Copy('Hello World', 7, 5);
// Returns: 'World'
// Extract the year from a stored date code 'YYYYMMDD'
Result := Copy([Period.Code], 1, 4);
// '20240315' returns '2024'
// Extract the month portion
Result := Copy([Period.Code], 5, 2);
// '20240315' returns '03'
// Extract everything after a known 3-character prefix 'GL-'
var s := [Account.Code];
Result := Copy(s, 4, Length(s) - 3);
// 'GL-1200' returns '1200'
Delete
Removes a specified number of characters from a string in place, starting at a given position. Because Delete modifies the variable directly, you must assign the field value to a local variable first โ you cannot pass a field expression directly.
procedure Delete(var S: String; From: Integer; Count: Integer)
| Parameter | Type | Description |
|---|---|---|
| S | String | The string variable to modify (must be a variable, not a field expression) |
| From | Integer | Starting position of the characters to remove (1-based) |
| Count | Integer | Number of characters to remove |
// Remove the '+1' country code from a stored phone number
var phone := [Customer.Phone];
if Copy(phone, 1, 2) = '+1' then
Delete(phone, 1, 2);
Result := phone;
// '+15551234567' becomes '5551234567'
// Strip the 'INV-' prefix from a reference number
var ref := [Invoice.Reference];
if Copy(ref, 1, 4) = 'INV-' then
Delete(ref, 1, 4);
Result := ref;
// 'INV-001234' becomes '001234'
// Remove a trailing asterisk flag character
var code := [Item.Code];
if RightStr(code, 1) = '*' then
Delete(code, Length(code), 1);
Result := code;
Insert
Inserts one string into another at a specified position, modifying the target variable in place. As with Delete, assign the field value to a local variable before calling Insert. Positions are 1-based.
procedure Insert(S: String; var S2: String; Pos: Integer)
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to insert |
| S2 | String | The target string variable to insert into |
| Pos | Integer | Position at which to insert (1-based) |
// Format a 9-digit EIN '123456789' as '12-3456789'
var ein := [Company.EIN];
Insert('-', ein, 3);
Result := ein;
// Format a 10-digit phone '5551234567' as '555-1234567'
var phone := [Customer.Phone];
Insert('-', phone, 4);
Result := phone;
// Insert a space between a numeric prefix and the rest of a code
// '12ABCD' becomes '12 ABCD'
var s := [Item.Code];
Insert(' ', s, 3);
Result := s;
LeftStr
Returns the leftmost N characters of a string. If the string is shorter than N, the full string is returned unchanged.
LeftStr(S: String; N: Integer): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The source string |
| N | Integer | Number of characters to return from the left |
// Truncate a description to 30 characters for a narrow column
Result := LeftStr([Item.Description], 30);
// Build a 3-letter code from the start of a customer name
Result := LeftStr(Uppercase([Customer.Name]), 3);
// 'Acme Corp' becomes 'ACM'
// Truncate with ellipsis only when the text exceeds the limit
var s := [Orders.Notes];
if Length(s) > 40 then
Result := LeftStr(s, 40) + '...'
else
Result := s;
Length
Returns the number of characters in a string. Returns 0 for an empty string.
Length(S: String): Integer
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to measure |
// Check whether a notes field has any content
if Length(TrimSafe([Orders.Notes])) > 0 then
Result := [Orders.Notes]
else
Result := '(No notes)';
// Truncate with ellipsis only when needed
var s := [Item.Description];
if Length(s) > 50 then
Result := LeftStr(s, 50) + '...'
else
Result := s;
// Validate that a phone number has enough digits
if Length(CleanNumber([Customer.Phone])) < 10 then
Result := 'Invalid phone number'
else
Result := FormatPhone([Customer.Phone]);
Lowercase
Converts all characters in a string to lowercase. See also Uppercase.
Lowercase(S: String): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to convert |
// Case-insensitive comparison
if Lowercase([Customer.Status]) = 'active' then
Result := 'Current Customer';
// Normalise mixed-case data before applying NameCase
Result := NameCase(Lowercase([Customer.Name]));
// Works correctly whether input is 'JOHN SMITH' or 'john smith'
// Use before UpperFirst to normalise then sentence-capitalise
Result := UpperFirst(Lowercase([Orders.Notes]));
// 'APPROVED BY MANAGER' becomes 'Approved by manager'
MidStr
Returns a substring starting at a specified position for a given number of characters. Positions are 1-based. MidStr and Copy do the same thing; use whichever name you prefer.
MidStr(S: String; Start: Integer; Count: Integer): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The source string |
| Start | Integer | Starting position (1 = first character) |
| Count | Integer | Number of characters to extract |
// Extract area code from a stored 10-digit number '5551234567'
Result := MidStr([Customer.Phone], 1, 3);
// Returns: 555
// Extract the exchange portion (digits 4-6)
Result := MidStr([Customer.Phone], 4, 3);
// Returns: 123
// Extract a date portion from a reference number 'INV-20240315-001'
Result := MidStr('INV-20240315-001', 5, 8);
// Returns: 20240315
// Extract a 2-character state code from a combined field 'FL-Miami'
Result := MidStr([Location.Code], 1, 2);
// Returns: FL
NameCase
Converts a string to proper name casing โ the first letter of each word is uppercased and the remainder are lowercased. Use this to clean up names stored in all-caps in older databases. See also UpperFirst for single-word sentence capitalisation.
NameCase(S: String): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to convert |
Result := NameCase('JOHN SMITH');
// Returns: 'John Smith'
Result := NameCase('acme manufacturing co');
// Returns: 'Acme Manufacturing Co'
// For robust normalisation of mixed-case data, lowercase first
Result := NameCase(Lowercase([Customer.Name]));
// Handles 'JOHN SMITH', 'john smith', and 'jOhN sMiTh' all correctly
PadLeft
Pads a string on the left with a specified character until it reaches the desired total length. If the string is already at or beyond that length, it is returned unchanged. Most commonly used to zero-pad numeric codes for sorting or fixed-width export.
PadLeft(S: String; Len: Integer; Ch: Char): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to pad |
| Len | Integer | The total desired length of the result |
| Ch | Char | The character to pad with (e.g. '0' or ' ') |
// Zero-pad an invoice number to 6 digits
Result := PadLeft(IntToStr([Invoice.InvoiceNumber]), 6, '0');
// 1234 becomes '001234'
// 99 becomes '000099'
// Right-align a code in a 15-character space-padded field
Result := PadLeft([Item.Code], 15, ' ');
// Pad a check number to 8 digits for a fixed-width bank export
Result := PadLeft(IntToStr([Check.Number]), 8, '0');
// 5 becomes '00000005'
PadRight
Pads a string on the right with a specified character until it reaches the desired total length. Use this to left-align text in fixed-width columns or to create visual leader lines and separators.
PadRight(S: String; Len: Integer; Ch: Char): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to pad |
| Len | Integer | The total desired length of the result |
| Ch | Char | The character to pad with |
// Left-align a product name in a 30-character export field
Result := PadRight([Item.Name], 30, ' ');
// Create a dotted leader line between a label and a value
Result := PadRight('Subtotal', 25, '.');
// Returns: 'Subtotal.................'
// Build a fixed-width text line combining padded columns
Result := PadRight([Customer.Name], 30, ' ') +
PadLeft(FormatCurrencySafe([Orders.Total]), 12, ' ');
// Create a full divider line
Result := PadRight('', 40, '-');
// Returns: '----------------------------------------'
Pos
Returns the character position of the first occurrence of a substring within a string. Returns 0 if not found. Positions are 1-based. Commonly used to detect whether a string contains a keyword, or to locate a delimiter so the string can be split.
Pos(SubStr: String; S: String): Integer
| Parameter | Type | Description |
|---|---|---|
| SubStr | String | The substring to search for |
| S | String | The string to search within |
// Check if a note contains the word 'URGENT'
if Pos('URGENT', Uppercase([Orders.Notes])) > 0 then
Result := 'Priority Order'
else
Result := 'Normal';
// Split a combined 'FL-Miami' field on the hyphen
var code := [Location.Code];
var p := Pos('-', code);
if p > 0 then
begin
var stateCode := Copy(code, 1, p - 1);
var cityName := Copy(code, p + 1, Length(code));
Result := cityName + ' (' + stateCode + ')';
// Returns: 'Miami (FL)'
end;
// Basic email format check
var email := [Customer.Email];
if (Pos('@', email) > 0) and (Pos('.', email) > 0) then
Result := email
else
Result := '(invalid email)';
RightStr
Returns the rightmost N characters of a string. Commonly used to extract the last digits of account or reference numbers, or the extension portion of a ZIP+4 code.
RightStr(S: String; N: Integer): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The source string |
| N | Integer | Number of characters to return from the right |
// Mask an account number, showing only the last 4 digits
Result := '****' + RightStr([Account.Number], 4);
// Returns: ****7890
// Extract the 4-digit extension from a ZIP+4 field '12345-6789'
Result := RightStr([Customer.Zip], 4);
// Returns: 6789
// Remove a trailing flag character safely
var code := [Item.Code];
if RightStr(code, 1) = '*' then
Result := LeftStr(code, Length(code) - 1)
else
Result := code;
SetLength
Resizes a string variable to a specified number of characters. If the new length is shorter, the string is truncated from the right. If longer, it is extended with null characters (#0). Most commonly used to pre-allocate a string buffer in procedures that build strings character by character.
Note: For simple truncation, LeftStr is the better choice. SetLength pads with null characters when extending, which can cause unexpected output in some export formats.
procedure SetLength(var S: String; L: Integer)
| Parameter | Type | Description |
|---|---|---|
| S | String | The string variable to resize |
| L | Integer | The new length in characters |
// Pre-allocate a fixed-length buffer before filling it character by character
var buffer: String;
SetLength(buffer, 80);
// buffer is now exactly 80 characters wide, filled with #0
// Truncate a local variable to 20 characters
var s := [Item.Description];
SetLength(s, 20);
Result := s;
// If s is shorter than 20 it will be padded with #0 โ use LeftStr to avoid this
Trim
Removes leading and trailing whitespace (spaces and tabs) from a string. Use TrimSafe instead if the value might be Null.
Trim(S: String): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to trim |
Result := Trim(' Hello World ');
// Returns: 'Hello World'
// Clean a field before concatenating
Result := Trim([Customer.City]) + ', ' + Trim([Customer.State]);
// Trim before a length check
if Length(Trim([Orders.Notes])) = 0 then
Result := '(No notes)'
else
Result := Trim([Orders.Notes]);
TrimSafe
Removes leading and trailing whitespace from a string. Unlike Trim, TrimSafe handles Null and empty variant values without raising an error, making it the safer choice when working with database fields that may be null.
TrimSafe(S: String): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to trim. Null and empty values are handled safely. |
// Safe to call on a field that might be NULL โ no error raised
Result := TrimSafe([Vendor.Address2]);
// Trim before comparing
if TrimSafe([Item.Code]) = 'DISC' then
Result := 'Discontinued'
else
Result := TrimSafe([Item.Description]);
// Use in an inline text object expression where nulls are possible
// [TrimSafe([Orders.Notes])]
Uppercase
Converts all characters in a string to uppercase. See also Lowercase.
Uppercase(S: String): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to convert |
// Force an account code to uppercase for consistent display
Result := Uppercase([Account.Code]);
// 'sales-west' becomes 'SALES-WEST'
// Use before Pos for a case-insensitive keyword search
if Pos('HOLD', Uppercase([Orders.Notes])) > 0 then
Result := 'On Hold';
// Build a short code from the first 3 letters of a name
Result := LeftStr(Uppercase([Customer.Name]), 3);
// 'Acme Corp' becomes 'ACM'
UpperFirst
Converts only the first character of a string to uppercase, leaving all other characters unchanged. This gives sentence-style capitalisation. It differs from NameCase, which capitalises the first letter of every word.
UpperFirst(S: String): String
| Parameter | Type | Description |
|---|---|---|
| S | String | The string to process |
// Capitalise the first letter of a note or comment field
Result := UpperFirst([Orders.Notes]);
// 'approved by manager' becomes 'Approved by manager'
// Normalise before applying โ lowercase first, then capitalise
Result := UpperFirst(Lowercase([Customer.Name]));
// 'JOHN SMITH' becomes 'John SMITH'
// Use NameCase if you want 'John Smith'
// Use in a greeting line
Result := 'Dear ' + UpperFirst(Lowercase([Contact.FirstName])) + ',';
// Capitalise a status value stored in lowercase
Result := UpperFirst([Orders.Status]);
// 'pending' becomes 'Pending'