---
title: Print Layouts (PL)
aliases: [PL, print layout, printlayoutv2, thermal printer, PDF]
sources: [sources/sessions/2026-04-20-print-layouts-modules-sync.md]
last_updated: 2026-04-20
status: draft
---

# Print Layouts (PL)

Print Layouts define document templates for generating PDFs (standard printers) or thermal receipts (3-inch ESC/POS printers).

## Overview

-   All layouts use `.printlayoutv2.xml` extension (V1 fully deprecated)
-   Located in `src/{Module}/PL/{LayoutName}/` directories
-   Two printer targets: Standard (A4 PDF) and Thermal (3-inch continuous)
-   Data binding via macro syntax similar to UI but with different elements
-   Invoked from Process via `actionType="PRINTV2"`

## XML Structure

```xml
<PrintLayout xmlns="https://www.salesforce.com/cgcloud/xsds" name="OrderConfirmationPDF">
  <Declarations>
    <DataDeclaration name="order" type="BoOrder" />
    <DataDeclaration name="salesOrg" type="LuSalesOrg" />
    <DataDeclaration name="myLogo" type="Image" mimeType="image/png" imageId="CompanyLogo" />
    <DataDeclaration name="signature1" type="Signature" />
  </Declarations>
  <ReportLayout pageMargins="[40,40,40,40]" pageSize="[595, 842]">
    <!-- Visual content -->
  </ReportLayout>
</PrintLayout>
```

## Data Declarations

| Type        | Purpose                   | Example                               |
| ----------- | ------------------------- | ------------------------------------- |
| BO type     | Business Object data      | `type="BoOrder"`                      |
| LU type     | Lookup reference data     | `type="LuSalesOrg"`                   |
| `Image`     | Embedded image            | `mimeType="image/png" imageId="Logo"` |
| `Signature` | Digital/scanned signature | `type="Signature"`                    |

## Data Binding (Macro Syntax)

```xml
<!-- Direct property access -->
{{Declarations::order.fieldName}}

<!-- Nested object property -->
{{Declarations::order.luDeliveryRecipient.name}}

<!-- Current item in iteration -->
{{.fieldName}}

<!-- Labels with fallback -->
{{Labels::AddressId; defaultLabel=Address:}}

<!-- Number formatting -->
{{.price; numberFormat=8.2}}

<!-- Date formatting -->
{{Declarations::order.deliveryDate; dateTimeFormat=date}}

<!-- Domain value toggle -->
{{.status; toggleId=DomStatus; toggleField=shortText}}
```

## Visual Elements

| Element         | Purpose                                 |
| --------------- | --------------------------------------- |
| `<p>`           | Paragraph (bold, italics, alignment)    |
| `<h1>`, `<h2>`  | Headings                                |
| `<img>`         | Image (src via macro, width, alignment) |
| `<lineFeed/>`   | Vertical spacing                        |
| `<table>`       | Data table                              |
| `<header>`      | Page header (standard only)             |
| `<footer>`      | Page footer (standard only)             |
| `<pageNumber/>` | Current page number (standard only)     |

## Table Structure

```xml
<table name="ItemsTable" tableLayout="lightHorizontalLinesMainItemsOnly">
  <thead>
    <tr>
      <th width="40%">{{Labels::ProductId; defaultLabel=Product}}</th>
      <th width="20%">{{Labels::QtyId; defaultLabel=Qty}}</th>
      <th width="20%" alignment="right">{{Labels::PriceId; defaultLabel=Price}}</th>
      <th width="20%" alignment="right">{{Labels::AmountId; defaultLabel=Amount}}</th>
    </tr>
  </thead>
  <tbody>
    <each name="itemsEach" value="{{Declarations::order.loPrintItems}}">
      <filters>
        <filter fieldName="quantity" value="0" operator="GT" compareMode="NUMBER" />
      </filters>
      <orderCriteria>
        <orderCriterion fieldName="prdId" direction="ASC" compareMode="NUMBER" />
      </orderCriteria>
      <tr>
        <td>{{.text1}}</td>
        <td>{{.quantity}}</td>
        <td>{{.basePriceReceipt; numberFormat=8.2}}</td>
        <td>{{.valueReceipt; numberFormat=8.2}}</td>
      </tr>
      <!-- Related list via correlation -->
      <correlation name="cond1" value="{{Declarations::order.loSdoConditions}}"
                   key="pKey" correlationKey="sdoItemPKey">
        <filters>
          <filter fieldName="cpIsPrintRelevant" value="1" operator="EQ" />
        </filters>
        <tr>
          <td/>
          <td>{{.text1}} {{.conditionValue; numberFormat=8.2}}</td>
        </tr>
      </correlation>
    </each>
    <tr>
      <td>{{Labels::TotalId; defaultLabel=Total}}</td>
      <td><sum table="ItemsTable" col="1" /></td>
      <td/>
      <td><sum table="ItemsTable" col="3" numberFormat="8.2" /></td>
    </tr>
  </tbody>
</table>
```

## Iteration Elements

### `<each>` — Loop over ListObject items

-   `value` — Macro reference to LO: `{{Declarations::order.loItems}}`
-   `<filters>` — Include only matching items
-   `<orderCriteria>` — Sort within iteration

### `<correlation>` — Related list for current item

-   `value` — Related LO
-   `key` — Field in parent item
-   `correlationKey` — Field in related LO that matches

### `<sum>` — Column aggregation

-   `table` — Table name to aggregate
-   `col` — Column index (0-based)
-   `numberFormat` — Output format

## Thermal vs Standard Printer

| Feature          | Standard (PDF)      | Thermal (3-inch)  |
| ---------------- | ------------------- | ----------------- |
| pageSize         | `[595, 842]` (A4)   | `[216, auto]`     |
| pageMargins      | `[40,40,40,40]`     | `[5,0,5,0]`       |
| Headers/footers  | Yes                 | No                |
| Page numbers     | Yes                 | No                |
| Images in tables | Yes (rowSpan)       | No                |
| Alignment        | left, center, right | left, right only  |
| Min column width | None                | 20%               |
| Protocol         | PDF rendering       | ESC/POS @ 203 DPI |

## Process Invocation (PRINTV2 Action)

```xml
<Action name="printDocument" actionType="PRINTV2"
        printId="ProcessContext::PrintId"
        locale="ApplicationContext::user.languageSpoken"
        showShareButton="true"
        watermark="ProcessContext::TextItems.documentStatus"
        generateAndSave="ProcessContext::TextItems.printV2GenerateAndSave">
  <Parameters>
    <Input name="order" value="ProcessContext::MainBO" />
    <Input name="salesOrg" value="ApplicationContext::SalesOrg" />
    <Input name="textItems" value="ProcessContext::BoOrderPrintTextItems" />
    <Input name="signature1" value="ProcessContext::TextItems.signature1MediaPath" />
  </Parameters>
  <Return name="ProcessContext::FinalPath" />
  <TransitionTo action="AfterPrint" />
</Action>
```

-   `printId` — Name of the PrintLayout to render (determined at runtime by BL)
-   `locale` — Language for label resolution
-   `watermark` — Text overlay (e.g., "DRAFT", "COPY")
-   `generateAndSave` — Boolean to save PDF as attachment
-   Parameters map ProcessContext variables to Declaration names

## Validation rules

PrintLayout is the only XSD in the modeler that declares a `targetNamespace` (`https://www.salesforce.com/cgcloud/xsds`). Authors don't need to touch the namespace — just keep the root `<PrintLayout>` element intact.

### Cross-cutting (every contract)

-   Contract names must be unique workspace-wide — two print layouts cannot share the same `@name`.
-   Files must be readable, well-formed XML.

### Must

-   Root element is `<PrintLayout>` with the CG Cloud target namespace intact.
-   File name ends with `.printlayoutv2.xml`. Custom print layouts start with the customizing indicator in both the file name and the root `@name`.
-   Every `<th>` width must be a valid percentage ≥ 8%, and the sum of `<th>` widths in a row must equal 100%. A sum under 100% is warned.
-   `<tr>` must contain at least one `<th>`, and every `<table>` must declare a `<thead>`.
-   An `<img>` element must declare `width` (except when nested inside a `<td>` — which itself is unsupported, see below).

### Must not

-   No more than 4 `<DataDeclaration>` entries of type `Signature` — 4 is the practical cap.
-   `<img>` cannot appear inside a `<td>`; `<img>` cannot declare `fit`.
-   `<thead>` cannot contain multiple `<tr>` rows — warned.
-   On `<th>` / `<td>`, italics / `colSpan` / `rowSpan` / `bold` / `width` attributes are unsupported — use the documented style alternatives.
-   `<header>`, `<footer>`, `<styles>`, and `<div>` are unsupported at the top level; `<lineFeed>` and `<rule>` apply only on thermal layouts; consecutive `<rule>` rows are rejected.
-   `pageMargins` is not supported on thermal layouts.
-   `dontBreakRows` is unsupported.
-   The `correlation` row's column count must match the host table's column count — warned when off.

### Coerced (silently rewritten)

-   `MODIFIED_CORE` print layouts are warned — the framework prefers you copy-and-customize rather than edit core layouts.
-   `xmlns="*.xsd"` on the root is stripped during pre-processing (the legitimate `targetNamespace` is preserved).

Internal schema reference: `rcg-mobile-dev-agent/wiki/contracts/print-layout.md`.

## Cross-References

-   [[processes]] — PRINTV2 action invokes print layouts
-   [[business-objects]] — Print data comes from BOs/LOs
-   [[business-logic]] — BL determines printId based on order type
