What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Put business decisions in prepared data, use template conditions for optional content, bind loops to arrays, and treat pagination as a separate layout pass. That division works across Adobe Document Generation API, HTML/CSS renderers such as Prince or DocRaptor, and XSLT/XSL-FO. The sections below show the syntax patterns, data contracts, pagination controls, tests, and failure fixes needed for production PDFs.
Choose where conditions and loops run
There are three dependable architectures. Select one before writing template tags, because the expression language, debugging tools, and page-layout controls differ.
| Architecture | Conditions and loops | Pagination control | Best fit | Main risk |
|---|---|---|---|---|
| Adobe Document Generation API | JSONata-compliant tags; conditions are wrapped in expr(); arrays repeat rows and list sections. |
Document constructs plus renderer behavior. | JSON-driven business documents. | Template/data contract mistakes. |
| HTML/CSS with Prince or DocRaptor | Your application or a template engine evaluates conditions and loops before conversion. | Strong CSS paged-media support through Prince; DocRaptor exposes Prince options. | Teams already producing HTML/CSS. | Pagination and script interactions. |
| XSLT/XSL-FO | XPath/XSLT expressions and iteration over XML nodes. | Explicit page masters and page-position rules. | XML-heavy, tightly controlled publishing. | Steeper XML, XPath, and XSL-FO complexity. |
Keep business rules in application code when an expression is difficult to audit. The template should decide presentation—whether an optional paragraph appears or how one array item becomes one row—not calculate policy, tax, eligibility, or authorization.
Design the data contract before the template
A stable contract prevents most missing-section and empty-table defects. Shape data into explicit booleans, arrays, and display-ready values before rendering.
#1 Best Overall
- Required fields: identify values that make the document invalid when absent, such as an invoice number or recipient.
- Optional fields: provide a boolean or null-safe value for paragraphs, signatures, notes, and contact details.
- Collections: always send an array, even when it is empty; never alternate between an array and a single object.
- Display values: format dates, currency, and localized text before the template if the renderer has no trusted formatter.
- Escaping: escape user-provided text for the target template language and HTML/XML context.
- Empty behavior: decide whether an empty collection hides the table, shows a “No items” row, or produces a separate explanatory paragraph.
For example, an invoice contract can contain isPaid, notes, and subscriptions. The template then consumes those fields without reimplementing payment logic.
Adobe Document Generation API: conditions and repeated rows
Adobe’s Document Generation API replaces template tags with input data. Its tags are JSONata-compliant, and Adobe requires the condition to be wrapped in expr(). The documented comparison operators are =, !=, >=, >, <=, and <.
Write short condition expressions
Use one condition for each optional semantic unit. In the Adobe template editor, put expressions such as these in the condition associated with a paragraph or phrase:
expr(status = 'paid')
expr(total >= 100)
expr(status != 'cancelled')
A conditional paragraph removes the whole paragraph when false. A conditional phrase removes only text inside a paragraph, which is useful for wording such as a short “Paid” label. Keep nested conditions shallow; calculate compound booleans such as showDiscountNote in your input data when the rule becomes hard to read.
Rank #2
- Material: These templates are made of acrylic material, sturdy and durable, the products are packed in a carton box to avoid transportation damage.
- Size: There are 3 different sizes in a package, thickness is about 2.5mm, please refer to the pictures for detailed inside and outside dimensions, suitable for most common sticky notes.
- Crafting Tools: These guides are designed for easy placement of cardboard covers when making notebook covers, small planers, etc.
- Wide Usage: This tool guide will help you to make your own perfect note book or mini book with whole pieces of sticky notes, the fixed template is perfect for beginners.
- Specially Gift: You can use this template to make a unique note book for your loved ones, family members or friends that they will never forget.
Repeat an array as table rows
Adobe documents array-driven repetition for tables. Put a tag such as {{subscriptions.name}} in a table row and configure that row for repetition. The service creates one row for each object in the subscriptions array. The same approach applies to repeating numbered and bulleted list sections.
{
"status": "paid",
"total": 125,
"subscriptions": [
{"name": "Analytics", "period": "Monthly", "amount": "$25"},
{"name": "Support", "period": "Annual", "amount": "$100"}
]
}
Define the empty-array result explicitly in your document design. If an empty table is not meaningful, guard the entire table with a condition and show an alternative paragraph. If the table must remain, include a deliberate empty-state row rather than relying on renderer behavior.
HTML/CSS templates with Prince or DocRaptor
Prince converts HTML, Markdown, XHTML, XML, and SVG styled with CSS into printable and archivable documents. DocRaptor accepts HTML or XML and exposes Prince-specific options. In this architecture, the application-side template engine evaluates all conditions and loops first; Prince performs paged layout afterward.
A complete Handlebars pattern
DocRaptor’s tutorial index lists a Handlebars.js template example. Confirm the current tutorial syntax before adopting version-specific helpers. The following standard Handlebars pattern handles an optional note, repeated rows, and an empty collection:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchRank #3
<h1>{{title}}</h1>
{{#if notes}}
<p class='notes'>{{notes}}</p>
{{/if}}
<table>
<thead>
<tr><th>Plan</th><th>Period</th><th>Amount</th></tr>
</thead>
<tbody>
{{#each subscriptions}}
<tr>
<td>{{name}}</td>
<td>{{period}}</td>
<td>{{amount}}</td>
</tr>
{{else}}
<tr><td colspan='3'>No subscriptions</td></tr>
{{/each}}
</tbody>
</table>
Compile this template with your application, then send the resulting HTML to Prince or DocRaptor. Do not expect the PDF engine to understand Handlebars tags; unresolved tags are an application-stage failure.
Add paged-media CSS after content logic works
@page {
size: A4;
margin: 18mm 16mm 20mm;
}
thead { display: table-header-group; }
tfoot { display: table-footer-group; }
tr { break-inside: avoid; }
h2 { break-after: avoid; }
Use the renderer’s documented support for page breaks, running headers, counters, and widows/orphans. A CSS rule cannot repair a loop that produced malformed HTML, and a correct loop cannot guarantee that a long row fits on one page.
Control JavaScript deliberately
DocRaptor documents optional JavaScript and a max_passes setting for Prince post-layout scripts. Repeated layout passes can create unexpected processing loops and longer processing times. Avoid post-layout JavaScript unless it is necessary, make the pass count explicit, and ensure every script reaches a stable state.
XSLT/XSL-FO for XML and page-master logic
XSLT is a natural place for XML conditions and loops. XSL-FO is particularly useful when a condition changes page geometry rather than merely showing text. The W3C specification defines fo:conditional-page-master-reference; its page-position, odd-or-even, and blank-or-not-blank subconditions select among page masters, and every specified subcondition must be true.
Rank #4
Iterate items and choose optional content
<xsl:template match='invoice'>
<fo:page-sequence master-reference='invoice-pages'>
<fo:flow flow-name='xsl-region-body'>
<fo:block font-size='16pt'>
<xsl:value-of select='number'/>
</fo:block>
<xsl:if test='notes'>
<fo:block space-after='6pt'>
<xsl:value-of select='notes'/>
</fo:block>
</xsl:if>
<fo:table>
<fo:table-body>
<xsl:for-each select='items/item'>
<fo:table-row>
<fo:table-cell><fo:block>
<xsl:value-of select='name'/>
</fo:block></fo:table-cell>
<fo:table-cell><fo:block>
<xsl:value-of select='amount'/>
</fo:block></fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
</xsl:template>
Use xsl:choose for mutually exclusive states and an explicit empty-list branch when a zero-item result needs a message. Keep XPath tests simple enough that a reviewer can trace them to the input XML.
Treat pagination as a separate engineering problem
Variable-length loops can move headings, split tables, and create blank pages even when every condition is correct. Add layout rules only after the data and content logic is stable.
- Make table headers repeatable on continuation pages.
- Prevent rows from splitting when the renderer supports
break-inside: avoidor its XSL-FO equivalent, while accepting that an oversized row may still need to split. - Keep headings with the following block and use deliberate page-break controls for major sections.
- Use XSL-FO page masters when first, last, only, rest, odd, even, or blank-page geometry changes.
- Check for orphaned headings and nearly empty trailing pages after every material template change.
Test three dataset sizes
| Dataset | What it reveals |
|---|---|
| Minimum | Missing optional values, empty arrays, and whether required content still fits. |
| Typical | Normal wrapping, table continuation, headers, and ordinary page counts. |
| Maximum | Long names, many rows, multi-page sections, late page breaks, and timeout-sensitive scripts. |
Inspect both the rendered pages and extracted text. A visually plausible PDF can still omit rows, expose unresolved tags, or place content outside the visible page area.
Validation and release checklist
- Validate the input against the required/optional/array contract before rendering.
- Render condition branches where each boolean is true and false.
- Render one-item, several-item, and empty-array cases.
- Search extracted text for template delimiters such as
{{,}}, or unresolved XPath output. - Compare page count and file size with the previous approved fixture; investigate unexpected jumps rather than accepting them.
- Open every page at 100% and check clipping, overlap, font fallback, repeated headers, and blank pages.
- Store representative fixtures so a renderer upgrade can be regression-tested.
Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Conditional text never appears | The expression is not wrapped in Adobe’s required expr(), or the field is a string such as 'false' instead of a boolean. |
Check the expression wrapper and normalize data types before rendering. |
| Only one table row renders | The row was not configured as a repeating array row, or the collection is an object rather than an array. | Bind the row to the documented array pattern and validate the payload shape. |
| Empty table leaves a large gap | The loop emits a table shell even when the collection is empty. | Condition the whole table or provide an intentional empty-state row. |
| Handlebars tags appear in the PDF | HTML was sent to the PDF renderer before the application template was compiled. | Render HTML first, assert that no delimiters remain, then call Prince or DocRaptor. |
| Blank pages or very slow output | Unbounded post-layout JavaScript or excessive layout passes. | Remove the script where possible and set a bounded, explicit max_passes value. |
| Rows or headings split awkwardly | Pagination rules were added too late, or a block is larger than the available page area. | Repeat headers, prevent avoidable row breaks, keep headings with content, and test maximum data. |
| Wrong page master is selected | One XSL-FO conditional-page-master subcondition is false. | Evaluate page-position, parity, and blank-page conditions together; all specified conditions must match. |
Performance, reliability, and operating cost
Keep the rendering request deterministic: fetch and prepare data once, compile the template once per job, and avoid network calls from layout scripts. Large collections increase HTML/XML size and layout work, so paginate at the document level when a business process permits multiple files. Measure your own latency and memory on representative maximum fixtures; the available documentation does not establish a universal row limit or benchmark.
For hosted services, account for API request limits, retries, storage, and the provider’s licensing or per-document charges. For self-hosted Prince or an XSL-FO processor, budget for runtime operations, font installation, and upgrade testing. In every architecture, log the input contract version, template version, renderer version, page count, and validation result so a visual regression can be reproduced.
Or skip the browser setup
If your template is rendered in a web application and you need automated screenshots of the result, ScreenshotNeo can capture the page without you maintaining a headless-browser script. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the capture; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
One GET request returns an image or PDF. The API also supports full-page captures with lazy images loaded, CSS-selector element shots, device presets and custom viewports, dark mode, retina scale, PDF paper and margin controls, custom CSS/JavaScript, click and wait actions, request blocking, headers, cookies, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
curl -G 'https://api.screenshotneo.com/v1/shot' -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for all parameters. Python and Node.js equivalents are:
Free tools Windows power users keep installed
One-click scans. No signup required.
import requests
r = requests.get('https://api.screenshotneo.com/v1/shot', params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'}, timeout=90)
open('shot.webp', 'wb').write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
The Free plan includes 1,000 shots each month with no card. Paid plans start at $5 for 3,000 shots; Growth is $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to try the 1,000 monthly shots without a card.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

