Free, open source, independently developed. If ODT Template Engine is useful to you, you can help continued development.
Ways to support the project →
Install with Composer

Or explore the canonical live samples below first — no local installation required.

composer require waltdietzney/odt-template-engine
Three complementary working models

Choose who owns each piece of structure: Writer or PHP.

The engine does not force one rendering model. A document can combine simple visible template logic, PHP-owned native ODT content, and named Writer-owned structures while remaining a normal editable ODT file.

1

Simple Template Processing

Keep surrounding structure in Writer and supply variables, conditions, filters, and lightweight repeating data from PHP.

Hello {{customer_name}}

{{#foreach:items}}
{{name}} — {{price}}
{{#endforeach}}
2

Structured ODT Construction

When PHP owns a dynamic subtree, build native RichText, paragraphs, lists, tables, images, frames, and text boxes.

$richText = new RichText();
$richText->addParagraph('Dynamic content');

$template->setElement('content', $richText);
3

Writer-native Document Model

Keep named Sections, Bookmarks, tables, frames, and User Fields owned by Writer and address them through bounded semantic APIs.

$template->bookmark('FullName')
  ->replaceText('Jane Smith');
Why OpenDocument?

When the generated document must stay editable.

HTML-to-PDF workflows are useful when the final result should be fixed. ODT Template Engine targets a different workflow: generate a native office document from PHP that can still be reviewed, changed and reused after generation.

ODT-aware PHP API

From simple placeholders to Writer-native automation.

Template logic

Variables, filters, repeating blocks, if/elseif/else and ifnot conditions.

Rich document content

Paragraphs, formatted text, numbered and bullet lists, images and native ODT tables.

Writer-native workflows

Named native structures, source-template inspection, Writer User Fields, mapping, concrete preflight, and bounded automation.

Live Sample Explorer

Don't take the feature list on trust. Generate an ODT.

Inspect the semantic template contract and the real PHP source behind each example, then generate and download the actual ODT file. The examples use the same engine code and templates that ship with the repository.

22 canonical samples shown
Learn L01

Variables & Filters

Teaches scalar assignment and template-side formatting with filters and ODT line breaks.

Canonical sample ✓ Template available 5 dependencies · 1 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => name
            [1] => email
            [2] => birthday
            [3] => amount
            [4] => note
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => table
                    [name] => Table1
                )

        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L01_variables_filters.odt');
$template->assign([
    'name' => 'Anna Beispiel',
    'email' => 'ANNA@EXAMPLE.COM',
    'birthday' => '1995-08-15',
    'amount' => '1345.5',
    'note' => "Thank you for your order.\nYour receipt is attached.",
]);

$template->render();
$template->save(__DIR__ . '/output/output_L01_variables_filters.odt');
Learn L02

Conditions

Teaches template-authored if, elseif, else, and ifnot branches.

Canonical sample ✓ Template available 4 dependencies · 1 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => account
            [1] => balance
            [2] => priority_account
            [3] => review_required
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => table
                    [name] => Table1
                )

        )

    [controls] => Array
        (
            [0] => Array
                (
                    [kind] => IF
                    [representation] => CLASSIC
                )

            [1] => Array
                (
                    [kind] => IF
                    [representation] => CLASSIC
                )

            [2] => Array
                (
                    [kind] => IFNOT
                    [representation] => CLASSIC
                )

        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L02_conditions.odt');
$template->assign([
    'account' => 'AC-2048',
    'balance' => 0,
    'priority_account' => true,
    'review_required' => false,
]);

$template->render();
$template->save(__DIR__ . '/output/output_L02_conditions.odt');
Learn L03

Repeating Content

Teaches assignRepeating() over LibreOffice-authored repeatable paragraphs.

Canonical sample ✓ Template available 7 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => order_number
            [1] => customer
            [2] => items[]
            [3] => items[].name
            [4] => items[].sku
            [5] => items[].quantity
            [6] => items[].price
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
            [0] => Array
                (
                    [kind] => FOREACH
                    [representation] => CLASSIC
                )

        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L03_repeating_content.odt');
$template->assign([
    'order_number' => 'SO-1042',
    'customer' => 'Anna Beispiel',
]);
$template->assignRepeating('items', [
    ['name' => 'Notebook', 'sku' => 'PAP-01', 'quantity' => '2', 'price' => '8.50'],
    ['name' => 'Fountain pen', 'sku' => 'WR-14', 'quantity' => '1', 'price' => '19.90'],
    ['name' => 'Ink bottle', 'sku' => 'INK-03', 'quantity' => '3', 'price' => '4.20'],
]);

$template->render();
$template->save(__DIR__ . '/output/output_L03_repeating_content.odt');
Learn L04

Rich Content

Teaches PHP-generated native RichText and Paragraph content inserted into a LibreOffice-authored shell.

Canonical sample ✓ Template available 1 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => project_brief
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\Elements\RichText;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L04_rich_content.odt');

$projectBrief = new RichText();
$projectBrief->addParagraph(
    (new Paragraph(null, ['margin-bottom' => '0.14cm']))
        ->addText('Project: ', ['bold' => true, 'color' => '#27616d'])
        ->addText('Aurora')
);
$projectBrief->addParagraph(
    (new Paragraph(null, ['margin-bottom' => '0.14cm']))
        ->addText('Status: ', ['bold' => true, 'color' => '#27616d'])
        ->addText('On track')
);
$summary = (new Paragraph(null, ['margin-bottom' => '0.14cm']))
    ->addText('Summary: ', ['bold' => true, 'color' => '#27616d'])
    ->addText('The team is preparing a clear, editable project brief.')
    ->addLineBreak()
    ->addText('Each paragraph and text run remains native Writer content.');
$projectBrief->addParagraph($summary);
$projectBrief->addParagraph(
    (new Paragraph(null, ['margin-bottom' => '0.14cm']))
        ->addText('More information: ')
        ->addHyperlink('Project notes', 'https://example.com/aurora', [
            'color' => '#1a5fb4',
            'underline' => true,
        ])
);

$template->setElement('project_brief', $projectBrief);
$template->save(__DIR__ . '/output/output_L04_rich_content.odt');
Learn L05

Lists

Teaches native bullet, numbered, and nested ODT lists built from Paragraph items.

Canonical sample ✓ Template available 1 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => project_plan
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\ListElement;
use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\Elements\RichText;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L05_lists.odt');

$plan = new RichText();
$numberedSteps = new ListElement('numbered');
$numberedSteps
    ->addItem((new Paragraph())->addText('Prepare the project'))
    ->addItem((new Paragraph())->addText('Build and review'))
    ->addItem((new Paragraph())->addText('Deliver the document'));
$plan->addElement($numberedSteps);

$plan->addParagraphBreak();
$plan->addParagraph('Workstreams and nested tasks', null, ['bold' => true, 'color' => '#27616d']);
$workstreams = new ListElement('bullet');
$workstreams->addItem((new Paragraph())->addText('Preparation'));
$preparation = new ListElement('bullet');
$preparation
    ->addItem((new Paragraph())->addText('Confirm requirements'))
    ->addItem((new Paragraph())->addText('Gather the resources'));
$workstreams->addSubList($preparation);

$workstreams->addItem((new Paragraph())->addText('Implementation'));
$implementation = new ListElement('bullet');
$implementation->addItem((new Paragraph())->addText('Develop the document workflow'));
$nestedTasks = new ListElement('bullet');
$nestedTasks
    ->addItem((new Paragraph())->addText('Create the template'))
    ->addItem((new Paragraph())->addText('Generate an editable ODT'));
$implementation->addSubList($nestedTasks);
$implementation->addItem((new Paragraph())->addText('Test the result'));
$workstreams->addSubList($implementation);

$workstreams->addItem((new Paragraph())->addText('Delivery'));
$plan->addElement($workstreams);

$plan->addParagraphBreak();
$plan->addParagraph('Project principles', null, ['bold' => true, 'color' => '#27616d']);
$principles = new ListElement('bullet');
$principles
    ->addItem((new Paragraph())->addText('Keep the source data separate'))
    ->addItem((new Paragraph())->addText('Keep the generated document editable'));
$plan->addElement($principles);

$template->setElement('project_plan', $plan);
$template->save(__DIR__ . '/output/output_L05_lists.odt');
Learn L06

Images

Compares a LibreOffice-authored named image position with a PHP-generated ImageElement frame.

Canonical sample ✓ Template available 1 dependencies · 2 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => generated_image
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => frame
                    [name] => LearnTemplatePosition
                )

            [1] => Array
                (
                    [kind] => bookmark
                    [name] => LearnTemplateImage
                )

        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\ImageElement;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L06_images.odt');

// LibreOffice authored this named frame; PHP replaces its image resource.
$template->replaceImageByName(
    'LearnTemplatePosition',
    __DIR__ . '/../assets/Logo.png',
    ['width' => '4.5cm', 'height' => '2.4cm']
);

// PHP creates a separate native frame as part of this generated ODT element.
$generatedImage = new ImageElement(__DIR__ . '/../assets/banner.png', [
    'width' => '4.5cm',
    'anchor' => 'as-char',
]);
$template->setElement('generated_image', $generatedImage);

$template->save(__DIR__ . '/output/output_L06_images.odt');
Learn L07

Tables

Teaches native editable RichTable structure, header rows, cell content, and basic cell styling.

Canonical sample ✓ Template available 1 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => project_status
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\Elements\RichTable;
use OdtTemplateEngine\Elements\RichTableCell;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L07_tables.odt');

$headerStyle = [
    'background' => '#27616d',
    'border' => '0.5pt solid #27616d',
    'padding' => '0.18cm',
    'bold' => true,
    'color' => '#ffffff',
];
$bodyStyle = [
    'border' => '0.5pt solid #cbd7dc',
    'padding' => '0.16cm',
];

$table = (new RichTable())->setHeaderRowCount(1);
$table->addRow([
    new RichTableCell('Task', $headerStyle),
    new RichTableCell('Owner', $headerStyle),
    new RichTableCell('Status', $headerStyle),
]);

$requirements = (new Paragraph())
    ->addText('Requirements', ['bold' => true])
    ->addLineBreak()
    ->addText('Scope and stakeholders');

$table->addRow([
    new RichTableCell($requirements, $bodyStyle),
    new RichTableCell('Anna', $bodyStyle),
    new RichTableCell('Complete', array_merge($bodyStyle, ['background' => '#e4f2eb', 'color' => '#245d3c'])),
]);
$table->addRow([
    new RichTableCell('Template design', $bodyStyle),
    new RichTableCell('Ben', $bodyStyle),
    new RichTableCell('In progress', array_merge($bodyStyle, ['background' => '#fff2d6', 'color' => '#815b14'])),
]);
$table->addRow([
    new RichTableCell('Review', $bodyStyle),
    new RichTableCell('Carla', $bodyStyle),
    new RichTableCell('Planned', $bodyStyle),
]);

$template->setElement('project_status', $table);
$template->save(__DIR__ . '/output/output_L07_tables.odt');
Learn L08

HTML Import

Shows substantial controlled HTML translated into editable native ODT content.

Canonical sample ✓ Template available 1 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => imported_report
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Import\HtmlImporter;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L08_html_import.odt');
$localImage = __DIR__ . '/../assets/banner.png';
$embeddedImage = 'data:image/png;base64,' . base64_encode((string) file_get_contents(__DIR__ . '/../assets/Logo.png'));

$html = <<<HTML
<h1 style="color: #27616d;">Project Report: Aurora</h1>
<p style="margin-bottom: 0.3cm; text-align: justify;">Aurora is a compact example of a <strong>substantial HTML fragment</strong> translated into <em>editable Writer content</em>. The importer creates native ODT structures rather than storing a screenshot or opaque HTML.</p>
<h2>Visual references</h2>
<p>The first image is loaded from a local readable project asset:</p>
<p><img src="{$localImage}" width="5.2cm" height="3.5cm"></p>
<p>The second image is embedded as a data URL:</p>
<p><img src="{$embeddedImage}" width="2.4cm" height="2.4cm"></p>
<h2>Executive summary</h2>
<p>The team is <strong>on track</strong>, with <u>reviewable milestones</u> and a <mark>clear delivery plan</mark>. One early assumption was <del>to preserve HTML as markup</del>; the document now contains native paragraphs and tables. Water is H<sub>2</sub>O and the release is scheduled for Q<sup>3</sup>.</p>
<p>Implementation notes:<br><code>HtmlImporter::fromHtml(\$html)</code> turns controlled markup into ODT elements.</p>
<div style="background-color: #e9f2f3; border: 0.03cm solid #91b7bd; padding: 0.25cm; margin-top: 0.2cm; margin-bottom: 0.2cm;">
  <strong>Project principle</strong><br>Keep content semantic, editable, and easy to review.
</div>
<blockquote>“A useful document is structured content first, and presentation second.”</blockquote>
<h2>Work plan</h2>
<ul>
  <li><strong>Prepare</strong> — confirm requirements and gather source material</li>
  <li><strong>Build</strong> — create the Writer shell and import the report</li>
  <li><strong>Review</strong> — approve the editable result</li>
</ul>
<p>Approval sequence:</p>
<ol><li>Approve the content</li><li>Deliver the editable ODT</li></ol>
<pre>composer require waltdietzney/odt-template-engine</pre>
<h2>Team overview</h2>
<table style="border: 0.03cm solid #cbd7dc;">
  <thead>
    <tr>
      <th style="background-color: #27616d; color: #ffffff; padding: 0.16cm; text-align: left;">Contributor</th>
      <th style="background-color: #27616d; color: #ffffff; padding: 0.16cm; text-align: left;">Focus</th>
      <th style="background-color: #27616d; color: #ffffff; padding: 0.16cm; text-align: left;">Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="background-color: #f1f6f7; padding: 0.14cm;"><strong>Anna</strong></td>
      <td style="padding: 0.14cm;">Content and review</td>
      <td style="padding: 0.14cm;"><span style="color: #245d3c;">Ready</span></td>
    </tr>
    <tr>
      <td style="background-color: #f1f6f7; padding: 0.14cm;">Ben</td>
      <td style="padding: 0.14cm;">Template and import</td>
      <td style="padding: 0.14cm;">In progress</td>
    </tr>
  </tbody>
</table>
<h2>Delivery notes</h2>
<p>The table is an editable Writer table, while paragraphs and list items remain native document content. This supports ordinary review and follow-up editing after generation.</p>
<ul><li>Keep the source fragment under application control</li><li>Validate image sources before enabling remote access</li></ul>
<h2>Further information</h2>
<p>Read the <a href="https://example.com/aurora">project notes</a> or contact the delivery team.</p>
HTML;

// Remote images are disabled by default. Trusted callers may opt in explicitly:
// HtmlImporter::fromHtml($html, ['allow_remote_images' => true]);
// This public sample intentionally performs no network request.
$content = HtmlImporter::fromHtml($html);
$template->setElement('imported_report', $content);
$template->save(__DIR__ . '/output/output_L08_html_import.odt');
Learn L09

Native Objects

Shows bounded bookmark and Section mutation plus typed native table and frame addressing.

Canonical sample ✓ Template available 0 dependencies · 4 native objects
Template contract
Array
(
    [dependencies] => Array
        (
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => bookmark
                    [name] => ClientName
                )

            [1] => Array
                (
                    [kind] => section
                    [name] => ProjectSummary
                )

            [2] => Array
                (
                    [kind] => table
                    [name] => ProjectMilestones
                )

            [3] => Array
                (
                    [kind] => frame
                    [name] => ProjectNote
                )

        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\Elements\RichText;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L09_native_objects.odt');

// Resolve and use only the bounded mutation supported by each native target.
$template->bookmark('ClientName')->replaceText('Aurora Studio');
$template->section('ProjectSummary')->replaceContent(
    (new RichText())->addParagraph(
        (new Paragraph())->addText('Project summary supplied by PHP.', ['bold' => true])
    )
);

// Tables and frames are addressable and inspectable here. L12 demonstrates
// the separate bounded operation for populating an existing named table.
$table = $template->table('ProjectMilestones')->descriptor();
$frame = $template->frame('ProjectNote')->descriptor();

echo sprintf(
    "Table %s: %d rows, %s columns\nFrame %s: %s (%s × %s)\n",
    $table->name(),
    $table->rowCount(),
    $table->columnCount() ?? 'unknown',
    $frame->name(),
    $frame->payloadType(),
    $frame->width() ?? 'unspecified',
    $frame->height() ?? 'unspecified'
);

$template->save(__DIR__ . '/output/output_L09_native_objects.odt');
Learn L10

Writer User Fields

Binds a template-owned document-global Writer string User Field through setUserField().

Canonical sample ✓ Template available 1 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => customer
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L10_writer_user_fields.odt');

// The template owns one document-global Writer string User Field.
$template->setUserField('customer', 'Aurora Studio');
$template->save(__DIR__ . '/output/output_L10_writer_user_fields.odt');

echo "Saved Writer User Field example. Open it in LibreOffice Writer to reevaluate the native field.\n";
Learn L11

Template Inspection

Projects source-oriented bindings, controls, native objects, dependencies, capabilities, and diagnostics without rendering or mutation.

Canonical sample ✓ Template available 5 dependencies · 4 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => project_name
            [1] => show_note
            [2] => milestones[]
            [3] => milestones[].title
            [4] => customer
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => bookmark
                    [name] => ClientName
                )

            [1] => Array
                (
                    [kind] => section
                    [name] => ProjectSummary
                )

            [2] => Array
                (
                    [kind] => table
                    [name] => ProjectMilestones
                )

            [3] => Array
                (
                    [kind] => frame
                    [name] => ProjectNote
                )

        )

    [controls] => Array
        (
            [0] => Array
                (
                    [kind] => IF
                    [representation] => CLASSIC
                )

            [1] => Array
                (
                    [kind] => FOREACH
                    [representation] => CLASSIC
                )

        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L11_template_inspection.odt');
$contract = $template->inspectTemplate();

$summary = [
    'bindings' => array_map(static fn ($binding): array => [
        'kind' => $binding->kind(),
        'name' => $binding->variableName(),
        'source' => $binding->provenance()->sourcePart(),
    ], $contract->bindings()),
    'controls' => array_map(static fn ($control): array => [
        'kind' => $control->kind(),
        'representation' => $control->representation(),
        'support' => $control->supportState(),
    ], $contract->controls()),
    'native_objects' => array_map(static fn ($object): array => [
        'kind' => $object->kind(),
        'name' => $object->name(),
        'source' => $object->provenance()->sourcePart(),
    ], $contract->nativeObjects()),
    'dependencies' => array_map(static fn ($dependency): array => [
        'path' => $dependency->path(),
        'kind' => $dependency->kind(),
        'scope' => $dependency->scope()->kind(),
    ], $contract->dependencies()),
    'capabilities' => $contract->capabilities()->toArray(),
    'diagnostics' => array_map(static fn ($diagnostic): array => [
        'code' => $diagnostic->code(),
        'severity' => $diagnostic->severity(),
    ], $contract->diagnostics()),
];

// Use $contract->toArray() when the complete machine-readable contract is needed.
echo json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n";

Inspection only · no generated ODT.

Learn L12

Writer Table Population

Populates an existing Writer-authored named table while preserving its structure, formatting, headers, and kept source rows.

Canonical sample ✓ Template available 0 dependencies · 1 native objects
Template contract
Array
(
    [dependencies] => Array
        (
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => table
                    [name] => L12_ResultTable
                )

        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

/**
 * L12 — Writer Table Population
 *
 * This sample teaches how to populate an existing named table authored and
 * formatted in LibreOffice Writer. PHP supplies scalar row data; it does not
 * rebuild the table, its columns, or its formatting.
 *
 * keepRows uses zero-based indices from the ordinary Writer source-row
 * sequence. Here, source row 0 is kept while the remaining source rows are
 * populated or cloned as needed.
 */

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_L12_writer_table_population.odt');

// Writer owns the named table, header, columns, and formatting. PHP supplies
// only the mutable scalar data and protects the first ordinary source row.
$template
    ->table('L12_ResultTable')
    ->populate(
        [
            ['Participant outcomes', '248', 'UPDATED'],
            ['Completion rate', '87%', 'UPDATED'],
            ['Employer partners', '42', 'UPDATED'],
            ['Further training', '23%', 'UPDATED'],
        ],
        [
            'keepRows' => [0],
        ]
    );

$template->save(__DIR__ . '/output/output_L12_writer_table_population.odt');
Capabilities C01

Page & Flow Layout

Shows generated paragraph-flow intent alongside Writer-owned page structure and Writer-computed pagination.

Canonical sample ✓ Template available 8 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => report_period
            [1] => opening
            [2] => lead
            [3] => body_block
            [4] => chapter_break
            [5] => chapter_body
            [6] => final_heading
            [7] => final_body
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_C01_page_flow_layout.odt');

$template->setElement(
    'opening',
    (new Paragraph(null, ['keep-with-next' => 'always']))
        ->addText('Executive overview', ['bold' => true])
);
$template->setElement(
    'lead',
    (new Paragraph(null, ['keep-together' => 'always']))
        ->addText('This short introduction is kept as one paragraph block. ')
        ->addText('Writer still decides where the page ends.')
);

$longText = 'The quarterly review describes the delivery outlook, operational risks, and decisions needed by the project team. '
    . 'Its paragraph-flow policy asks Writer to retain a minimum number of lines at the top and bottom of a page. '
    . 'That policy describes reading flow; it does not calculate physical pagination or select a page style. ';
$template->setElement(
    'body_block',
    (new Paragraph(null, ['widows' => 2, 'orphans' => 2]))
        ->addText(str_repeat($longText, 5))
);

$template->setElement(
    'chapter_break',
    (new Paragraph(null, ['break-before' => 'page', 'keep-with-next' => 'always']))
        ->addText('Delivery outlook', ['bold' => true])
);
$template->setElement(
    'chapter_body',
    (new Paragraph(null, ['keep-together' => 'always', 'break-after' => 'page']))
        ->addText('The delivery outlook is stable. The team has completed the discovery work, confirmed the next review date, and assigned owners for each open decision. ')
        ->addText('This short chapter intentionally ends before the next authored page region.')
);

$template->setElement(
    'final_heading',
    (new Paragraph(null, ['keep-with-next' => 'always']))
        ->addText('Decisions and next steps', ['bold' => true])
);
$template->setElement(
    'final_body',
    (new Paragraph(null, ['widows' => 2, 'orphans' => 2]))
        ->addText('The report keeps page design in the Writer-authored template. Generated paragraph properties express keep and break intent; Writer computes the actual pagination when the document is opened or exported.')
);

$template->assign(['report_period' => 'Q3 2026']);
$template->render();
$template->save(__DIR__ . '/output/output_C01_page_flow_layout.odt');
Capabilities C02

Advanced Table Layout

Demonstrates whole-table geometry, relative column proportions, row geometry, and cell/paragraph alignment.

Canonical sample ✓ Template available 2 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => absolute_budget
            [1] => relative_capacity
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\RichTable;
use OdtTemplateEngine\Elements\RichTableCell;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_C02_advanced_table_layout.odt');

$absolute = (new RichTable())
    ->setTableName('BudgetByWorkstream')
    ->setTableStyle(['width' => '16cm', 'alignment' => 'center']);
$absolute->setColumnWidthRatios([3, 2, 2]);
$absolute->setHeaderRowCount(1);
$absolute->addRow([
    new RichTableCell('Workstream', ['background' => '#173e4a', 'color' => '#ffffff', 'weight' => 'bold', 'padding' => '0.2cm', 'vertical-align' => 'middle']),
    new RichTableCell('Budget', ['background' => '#173e4a', 'color' => '#ffffff', 'weight' => 'bold', 'text-align' => 'right', 'padding' => '0.2cm', 'vertical-align' => 'middle']),
    new RichTableCell('Owner', ['background' => '#173e4a', 'color' => '#ffffff', 'weight' => 'bold', 'padding' => '0.2cm', 'vertical-align' => 'middle']),
], ['row-height' => '0.95cm']);
$absolute->addRow([
    new RichTableCell('Research and discovery', ['padding' => '0.18cm', 'vertical-align' => 'middle']),
    new RichTableCell('€ 18,000', ['padding' => '0.18cm', 'text-align' => 'right', 'vertical-align' => 'middle']),
    new RichTableCell('Anna', ['padding' => '0.18cm', 'vertical-align' => 'middle']),
], ['row-height' => '1.15cm']);
$absolute->addRow([
    new RichTableCell('Template production', ['padding' => '0.18cm', 'vertical-align' => 'middle']),
    new RichTableCell('€ 24,500', ['padding' => '0.18cm', 'text-align' => 'right', 'vertical-align' => 'middle']),
    new RichTableCell('Ben', ['padding' => '0.18cm', 'vertical-align' => 'middle']),
], ['min-row-height' => '1.0cm']);
$absolute->addRow([
    new RichTableCell('Review and delivery', ['padding' => '0.18cm', 'vertical-align' => 'middle']),
    new RichTableCell('€ 9,500', ['padding' => '0.18cm', 'text-align' => 'right', 'vertical-align' => 'middle']),
    new RichTableCell('Carla', ['padding' => '0.18cm', 'vertical-align' => 'middle']),
], ['min-row-height' => '1.0cm']);

$relative = (new RichTable())
    ->setTableName('QuarterlyCapacity')
    ->setTableStyle(['relative-width' => '82%', 'alignment' => 'right']);
$relative->setColumnWidthRatios([3, 2, 2]);
$relative->setHeaderRowCount(1);
$relative->addRow([
    new RichTableCell('Quarterly capacity', ['background' => '#e6f0f0', 'weight' => 'bold', 'padding' => '0.16cm']),
    new RichTableCell('Plan', ['background' => '#e6f0f0', 'weight' => 'bold', 'text-align' => 'center', 'padding' => '0.16cm']),
    new RichTableCell('Unit', ['background' => '#e6f0f0', 'weight' => 'bold', 'text-align' => 'center', 'padding' => '0.16cm']),
], ['min-row-height' => '0.8cm']);
$relative->addRow([
    new RichTableCell('Available delivery days', ['padding' => '0.16cm']),
    new RichTableCell('240', ['padding' => '0.16cm', 'text-align' => 'center']),
    new RichTableCell('days', ['padding' => '0.16cm', 'text-align' => 'center']),
], ['min-row-height' => '0.8cm']);

$template->setElement('absolute_budget', $absolute);
$template->setElement('relative_capacity', $relative);
$template->save(__DIR__ . '/output/output_C02_advanced_table_layout.odt');
Capabilities C03

Frame Layout

Shows shared drawing-layout semantics for floating and as-character DrawTextBox and ImageElement content.

Canonical sample ✓ Template available 4 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => floating_callout
            [1] => right_image
            [2] => inline_mark
            [3] => offset_callout
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\DrawTextBox;
use OdtTemplateEngine\Elements\ImageElement;
use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_C03_frame_layout.odt');
$imagePath = dirname(__DIR__) . '/assets/Logo.png';

$callout = (new DrawTextBox('DeliveryCallout', [
    'background-color' => '#e8f2fb',
    'border' => '0.04cm solid #3978a8',
    'padding' => '0.2cm',
]))->setFrameLayout([
    'anchor' => 'paragraph',
    'width' => '6.2cm',
    'height' => '2.4cm',
    'horizontal' => ['alignment' => 'right', 'relative-to' => 'paragraph'],
    'vertical' => ['alignment' => 'top', 'relative-to' => 'paragraph'],
    'wrap' => 'left',
])->addElement(
    (new Paragraph())
        ->addText('DELIVERY NOTE', ['bold' => true])
        ->addLineBreak()
        ->addText('A floating text box keeps this short decision visible beside the report narrative.')
);

$inline = (new ImageElement($imagePath, ['width' => '1.25cm']))
    ->setFrameLayout([
        'anchor' => 'as-char',
        'vertical' => ['alignment' => 'top', 'relative-to' => 'baseline'],
    ]);
$inlineParagraph = (new Paragraph())->addText('An inline brand mark ')->addElement($inline)->addText(' participates in the text line.');

$rightImage = (new ImageElement($imagePath, ['width' => '3.4cm']))
    ->setFrameLayout([
        'anchor' => 'paragraph',
        'horizontal' => ['alignment' => 'right', 'relative-to' => 'paragraph'],
        'vertical' => ['alignment' => 'top', 'relative-to' => 'paragraph'],
        'wrap' => 'left',
    ]);

$offsetBox = (new DrawTextBox('ReviewCallout', [
    'background-color' => '#f4efe2',
    'border' => '0.04cm solid #a88b22',
    'padding' => '0.16cm',
]))->setFrameLayout([
    'anchor' => 'paragraph',
    'width' => '5.2cm',
    'height' => '1.8cm',
    'horizontal' => ['offset' => '1cm', 'relative-to' => 'paragraph'],
    'vertical' => ['offset' => '0.2cm', 'relative-to' => 'paragraph'],
    'wrap' => 'left',
])->addElement((new Paragraph())->addText('Review point: alignment is not an offset.', ['bold' => true]));

$template->setElement('floating_callout', $callout);
$template->setElement('inline_mark', $inlineParagraph);
$template->setElement('right_image', $rightImage);
$template->setElement('offset_callout', $offsetBox);
$template->save(__DIR__ . '/output/output_C03_frame_layout.odt');
Capabilities C04

Declarative Structured Collections

Executes Writer-authored nested Section controls directly with template-shaped data.

Canonical sample ✓ Template available 7 dependencies · 4 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => projects[].title
            [1] => projects[].status
            [2] => projects[].milestones[].milestone
            [3] => projects[]
            [4] => projects[].featured
            [5] => projects[].archived
            [6] => projects[].milestones[]
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => section
                    [name] => #foreach:projects
                )

            [1] => Array
                (
                    [kind] => section
                    [name] => #if:featured
                )

            [2] => Array
                (
                    [kind] => section
                    [name] => #ifnot:archived
                )

            [3] => Array
                (
                    [kind] => section
                    [name] => #foreach:milestones
                )

        )

    [controls] => Array
        (
            [0] => Array
                (
                    [kind] => FOREACH
                    [representation] => NATIVE_SECTION_DECLARATION
                )

            [1] => Array
                (
                    [kind] => IF
                    [representation] => NATIVE_SECTION_DECLARATION
                )

            [2] => Array
                (
                    [kind] => IFNOT
                    [representation] => NATIVE_SECTION_DECLARATION
                )

            [3] => Array
                (
                    [kind] => FOREACH
                    [representation] => NATIVE_SECTION_DECLARATION
                )

        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_C04_declarative_structured_collections.odt');
$contract = $template->inspectTemplate();

// The records already use the template's dependency names: no mapping is involved.
$template->executeDeclarative($contract, [
    'projects' => [
        [
            'title' => 'Aurora',
            'status' => 'on track',
            'featured' => true,
            'archived' => false,
            'milestones' => [
                ['milestone' => 'Discovery complete'],
                ['milestone' => 'Template review'],
            ],
        ],
        [
            'title' => 'Beacon',
            'status' => 'planned',
            'featured' => false,
            'archived' => true,
            'milestones' => [
                ['milestone' => 'Kickoff'],
            ],
        ],
    ],
]);

$template->save(__DIR__ . '/output/output_C04_declarative_structured_collections.odt');
Capabilities C05

Mapping & Automation

Connects application data and template dependencies through inspection, preflight, mapping, and atomic automation.

Canonical sample ✓ Template available 5 dependencies · 3 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => project_title
            [1] => client
            [2] => members[].name
            [3] => members[].role
            [4] => members[]
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => section
                    [name] => ExecutiveSummary
                )

            [1] => Array
                (
                    [kind] => section
                    [name] => #foreach:members
                )

            [2] => Array
                (
                    [kind] => frame
                    [name] => CompanyLogo
                )

        )

    [controls] => Array
        (
            [0] => Array
                (
                    [kind] => FOREACH
                    [representation] => NATIVE_SECTION_DECLARATION
                )

        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\Mapping\ApplicationPath;
use OdtTemplateEngine\Mapping\ConcreteMappingPreflight;
use OdtTemplateEngine\Mapping\DependencyMapping;
use OdtTemplateEngine\Mapping\DocumentCapabilityMapping;
use OdtTemplateEngine\Mapping\MappingDefinition;
use OdtTemplateEngine\Mapping\NativeObjectActionMapping;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_C05_mapping_automation.odt');
$contract = $template->inspectTemplate();

$mappings = new MappingDefinition(
    [
        new DependencyMapping(ApplicationPath::parse('customer.display_name'), 'client'),
        new DependencyMapping(ApplicationPath::parse('project.title'), 'project_title'),
        new DependencyMapping(ApplicationPath::parse('project.team[]'), 'members[]'),
        new DependencyMapping(ApplicationPath::parse('project.team[].full_name'), 'members[].name'),
        new DependencyMapping(ApplicationPath::parse('project.team[].responsibility'), 'members[].role'),
    ],
    [
        new NativeObjectActionMapping(ApplicationPath::parse('project.summary'), 'section', 'ExecutiveSummary', 'replace-content'),
        new NativeObjectActionMapping(ApplicationPath::parse('branding.logo'), 'frame', 'CompanyLogo', 'replace-image'),
    ],
    [new DocumentCapabilityMapping(ApplicationPath::parse('document.author'), 'metadata', 'creator')]
);

$application = [
    'customer' => ['display_name' => 'Northstar Studio'],
    'project' => [
        'title' => 'Template modernization',
        'summary' => (new Paragraph())->addText('The modernization keeps authored report structure in Writer while mapping application values explicitly.', ['bold' => true]),
        'team' => [
            ['full_name' => 'Anna Example', 'responsibility' => 'Template design'],
            ['full_name' => 'Ben Example', 'responsibility' => 'Integration'],
        ],
    ],
    'branding' => ['logo' => [
        'source' => dirname(__DIR__) . '/assets/Logo.png',
        'options' => [],
    ]],
    'document' => ['author' => 'Northstar Delivery Team'],
];

$preflight = (new ConcreteMappingPreflight())->preflight(
    $mappings,
    $contract,
    $application,
    $template->inspect()
);

if (!$preflight->ready()) {
    foreach ($preflight->diagnostics() as $diagnostic) {
        fwrite(STDERR, sprintf("%s: %s\n", $diagnostic->code(), $diagnostic->message()));
    }
    exit(1);
}

// The shared Phase-E invocation starts only after complete concrete preflight succeeds.
$template->automate($contract, $preflight);
$template->save(__DIR__ . '/output/output_C05_mapping_automation.odt');
Builders B01

Invoice Template Builder

Builds a complete editable invoice template candidate with structured PHP elements, semantic styles, native tabs, and visible template syntax.

Canonical sample ✓ Template available 3 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => invoice_body
            [1] => invoice_header
            [2] => invoice_footer
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\ImageElement;
use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\Elements\RichTable;
use OdtTemplateEngine\Elements\RichTableCell;
use OdtTemplateEngine\Elements\RichText;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/invoice-richtext-prototype.odt');

$orange = '#e17a19';
$ink = '#1f252b';
$muted = '#6f777d';
$lightGray = '#f1f1ef';

$left = new RichText();

$logoParagraph = new Paragraph('InvoiceBrand', [
    'margin-top' => '0cm',
    'margin-bottom' => '0cm',
]);
$logoParagraph->addElement(new ImageElement(__DIR__ . '/assets/northstar-mark.png', [
    'width' => '5.0cm',
    'anchor' => 'as-char',
]));
$left->addParagraph($logoParagraph);

$left->addParagraph(
    (new Paragraph('InvoiceTagline', [
        'margin-top' => '0cm',
        'margin-bottom' => '0cm',
    ]))->addText('Creative & Digital Services', [
        'color' => $muted,
        'font-size' => '9pt',
    ])
);

$left->addParagraphBreak(2);
$left->addParagraph(
    (new Paragraph('InvoiceBillToHeading', [
        'margin-top' => '0cm',
        'margin-bottom' => '0cm',
    ]))->addText('BILL TO', [
        'bold' => true,
        'color' => $orange,
        'font-size' => '8pt',
    ])
);
$left->addParagraph(
    (new Paragraph('InvoiceBillToName', [
        'margin-top' => '0cm',
        'margin-bottom' => '0cm',
    ]))->addText('Hamid Group', [
        'bold' => true,
        'color' => $ink,
        'font-size' => '10pt',
    ])
);
$left->addParagraph(
    (new Paragraph('InvoiceBillToAddress', [
        'margin-top' => '0cm',
        'margin-bottom' => '0cm',
    ]))->addText('123 Short Name Town/City', [
        'color' => $muted,
        'font-size' => '8pt',
    ])->addLineBreak()->addText('State, County 556', [
        'color' => $muted,
        'font-size' => '8pt',
    ])->addLineBreak()->addText('P: 00 999 123 456 789', [
        'color' => $muted,
        'font-size' => '8pt',
    ])->addLineBreak()->addText('M: info@hamidgroup.example', [
        'color' => $muted,
        'font-size' => '8pt',
    ])
);

$contactLine = static function (string $label, string $value) use ($orange, $muted): Paragraph {
    return (new Paragraph('InvoiceContact', [
        'text-align' => 'right',
        'margin-top' => '0cm',
        'margin-bottom' => '0cm',
    ]))
        ->addText($label . '  ', [
            'bold' => true,
            'color' => $orange,
            'font-size' => '7pt',
        ])
        ->addText($value, [
            'color' => $muted,
            'font-size' => '8pt',
        ]);
};

$right = new RichText();
$right->addParagraph($contactLine('LOC', '555 Market Street · Sydney NSW 2000'));
$right->addParagraph($contactLine('TEL', '+61 2 9999 4567'));
$right->addParagraph($contactLine('MAIL', 'info@northstar.example'));
$right->addParagraph($contactLine('WEB', 'www.northstar.example'));

$right->addParagraphBreak(2);
$right->addParagraph(
    (new Paragraph('InvoiceTitle', [
        'text-align' => 'right',
        'margin-top' => '0cm',
        'margin-bottom' => '0cm',
    ]))->addText('INVOICE', [
        'bold' => true,
        'color' => $ink,
        'font-size' => '25pt',
    ])
);

$metadata = new Paragraph('InvoiceMeta', [
    'background-color' => $lightGray,
    'border-left' => '0.12cm solid ' . $orange,
    'padding-left' => '0.28cm',
    'padding-right' => '0.28cm',
    'padding-top' => '0.16cm',
    'padding-bottom' => '0.16cm',
    'tab-stops' => [
        ['position' => 2.6, 'alignment' => 'left'],
    ],
]);
$metadata
    ->addText('Invoice No.', [
        'bold' => true,
        'color' => $ink,
        'font-size' => '8pt',
    ])
    ->addTab()
    ->addText('INV-2026-0142', [
        'color' => $ink,
        'font-size' => '8pt',
    ])
    ->addLineBreak()
    ->addText('Date', [
        'bold' => true,
        'color' => $ink,
        'font-size' => '8pt',
    ])
    ->addTab()
    ->addText('23 September 2026', [
        'color' => $ink,
        'font-size' => '8pt',
    ]);
$right->addParagraph($metadata);

$table = (new RichTable())->setTableStyle(['relative-width' => '100%']);
$table->setColumnWidthRatios([55, 45]);
$table->addRow([
    new RichTableCell($left, [
        'border' => 'none',
        'padding' => '0cm',
    ]),
    new RichTableCell($right, [
        'border' => 'none',
        'padding' => '0cm',
    ]),
]);

$tabStops = [
    ['position' => 10.2, 'alignment' => 'right'],
    ['position' => 13.1, 'alignment' => 'center'],
    ['position' => 17.0, 'alignment' => 'right'],
];

$body = new RichText();

// This salutation is the prototype for the later Writer-authored conditional
// salutation Sections in the final S02 template.
$body->addParagraph(
    (new Paragraph('InvoiceSalutation', [
        'margin-top' => '0.35cm',
        'margin-bottom' => '0.12cm',
    ]))->addText('Dear Mr Hamid,', [
        'bold' => true,
        'color' => $ink,
        'font-size' => '10pt',
    ])
);

$body->addParagraph(
    (new Paragraph('InvoiceBodyText', [
        'margin-top' => '0cm',
        'margin-bottom' => '0.35cm',
        'line-height' => '115%',
    ]))->addText(
        'Thank you for choosing Northstar Studio. Please find below the services provided for the current project.',
        [
            'color' => $muted,
            'font-size' => '9pt',
        ]
    )
);

$body->addParagraph(
    (new Paragraph('InvoiceItemsHeader', [
        'background-color' => $orange,
        'padding-left' => '0.18cm',
        'padding-right' => '0.18cm',
        'padding-top' => '0.14cm',
        'padding-bottom' => '0.14cm',
        'margin-top' => '0cm',
        'margin-bottom' => '0.16cm',
        'tab-stops' => $tabStops,
    ]))
        ->addText('ITEM DESCRIPTION', [
            'bold' => true,
            'color' => '#ffffff',
            'font-size' => '8pt',
        ])
        ->addTab()
        ->addText('PRICE', [
            'bold' => true,
            'color' => '#ffffff',
            'font-size' => '8pt',
        ])
        ->addTab()
        ->addText('QTY', [
            'bold' => true,
            'color' => '#ffffff',
            'font-size' => '8pt',
        ])
        ->addTab()
        ->addText('TOTAL', [
            'bold' => true,
            'color' => '#ffffff',
            'font-size' => '8pt',
        ])
);

$markerStyle = [
    'margin-top' => '0.02cm',
    'margin-bottom' => '0.02cm',
    'font-size' => '6pt',
    'color' => '#a3a3a3',
];

$body->addParagraph(
    (new Paragraph('InvoiceTemplateMarker', $markerStyle))->addText('{{#foreach:items}}')
);

$body->addParagraph(
    (new Paragraph('InvoiceItem', [
        'margin-top' => '0.12cm',
        'margin-bottom' => '0.04cm',
        'tab-stops' => $tabStops,
    ]))
        ->addText('{{name}}', [
            'bold' => true,
            'color' => $ink,
            'font-size' => '9pt',
        ])
        ->addTab()
        ->addText('{{price}}', [
            'color' => $ink,
            'font-size' => '9pt',
        ])
        ->addTab()
        ->addText('{{quantity}}', [
            'color' => $ink,
            'font-size' => '9pt',
        ])
        ->addTab()
        ->addText('{{total}}', [
            'bold' => true,
            'color' => $ink,
            'font-size' => '9pt',
        ])
);

$body->addParagraph(
    (new Paragraph('InvoiceItemDescription', [
        'margin-left' => '0.18cm',
        'margin-top' => '0cm',
        'margin-bottom' => '0.08cm',
        'line-height' => '110%',
    ]))->addText('{{description}}', [
        'color' => $muted,
        'font-size' => '8pt',
    ])
);

$body->addParagraph(
    (new Paragraph('InvoiceTemplateMarker', $markerStyle))->addText('{{#endforeach}}')
);

$lowerLeft = new RichText();
$lowerLeft->addParagraph(
    (new Paragraph('InvoicePaymentHeading', [
        'margin-top' => '0.35cm',
        'margin-bottom' => '0.12cm',
    ]))->addText('PAYMENT METHOD', [
        'bold' => true,
        'color' => $orange,
        'font-size' => '8pt',
    ])
);
$lowerLeft->addParagraph(
    (new Paragraph('InvoicePaymentText', [
        'margin-top' => '0cm',
        'margin-bottom' => '0.24cm',
        'line-height' => '112%',
    ]))->addText('Bank Account', [
        'bold' => true,
        'color' => $ink,
        'font-size' => '8pt',
    ])->addLineBreak()->addText('Bank Full Name', [
        'color' => $muted,
        'font-size' => '8pt',
    ])->addLineBreak()->addText('Bank Code: 110-245', [
        'color' => $muted,
        'font-size' => '8pt',
    ])->addLineBreak(2)->addText('PayPal', [
        'bold' => true,
        'color' => $ink,
        'font-size' => '8pt',
    ])->addLineBreak()->addText('info@northstar.example', [
        'color' => $muted,
        'font-size' => '8pt',
    ])
);
$lowerRight = new RichText();
$footerTabStops = [
    ['position' => 5.7, 'alignment' => 'right'],
];
$lowerRight->addParagraph(
    (new Paragraph('InvoiceSummary', [
        'margin-top' => '0.35cm',
        'margin-bottom' => '0.08cm',
        'tab-stops' => $footerTabStops,
    ]))
        ->addText('Sub Total', [
            'color' => $muted,
            'font-size' => '8pt',
        ])
        ->addTab()
        ->addText('{{subtotal}}', [
            'color' => $ink,
            'font-size' => '8pt',
        ])
        ->addLineBreak()
        ->addText('Tax Vat 18%', [
            'color' => $muted,
            'font-size' => '8pt',
        ])
        ->addTab()
        ->addText('{{tax}}', [
            'color' => $ink,
            'font-size' => '8pt',
        ])
);
$lowerRight->addParagraph(
    (new Paragraph('InvoiceGrandTotal', [
        'background-color' => $orange,
        'padding-left' => '0.18cm',
        'padding-right' => '0.18cm',
        'padding-top' => '0.14cm',
        'padding-bottom' => '0.14cm',
        'margin-top' => '0.08cm',
        'margin-bottom' => '0.28cm',
        'tab-stops' => $footerTabStops,
    ]))
        ->addText('GRAND TOTAL', [
            'bold' => true,
            'color' => '#ffffff',
            'font-size' => '8pt',
        ])
        ->addTab()
        ->addText('{{total}}', [
            'bold' => true,
            'color' => '#ffffff',
            'font-size' => '9pt',
        ])
);
$lowerRight->addParagraph(
    (new Paragraph('InvoiceSignature', [
        'text-align' => 'right',
        'margin-top' => '0.08cm',
        'margin-bottom' => '0.05cm',
    ]))->addText('/ / / / /', [
        'italic' => true,
        'color' => $ink,
        'font-size' => '11pt',
    ])
);
$lowerRight->addParagraph(
    (new Paragraph('InvoiceSignatureRole', [
        'text-align' => 'right',
        'margin-top' => '0cm',
        'margin-bottom' => '0.03cm',
    ]))->addText('Account Manager', [
        'color' => $muted,
        'font-size' => '7pt',
    ])
);
$lowerRight->addParagraph(
    (new Paragraph('InvoiceSignatureName', [
        'text-align' => 'right',
        'margin-top' => '0cm',
        'margin-bottom' => '0cm',
    ]))->addText('JONATHON DEO', [
        'bold' => true,
        'color' => $ink,
        'font-size' => '8pt',
    ])
);

$footer = new RichText();
$footer->addParagraph(
    (new Paragraph('InvoiceFooter', [
        'margin-top' => '0.3cm',
        'margin-bottom' => '0.08cm',
    ]))->addText('THANK YOU FOR YOUR BUSINESS', [
        'bold' => true,
        'color' => $ink,
        'font-size' => '8pt',
    ])
);
$footer->addParagraph(
    (new Paragraph('InvoiceFooterTerms', [
        'margin-top' => '0cm',
        'margin-bottom' => '0cm',
        'line-height' => '110%',
    ]))->addText('Terms: Payment is due within 14 days. Thank you for your continued partnership.', [
        'color' => $muted,
        'font-size' => '7pt',
    ])
);

$lowerTable = (new RichTable())->setTableStyle(['relative-width' => '100%']);
$lowerTable->setColumnWidthRatios([55, 45]);
$lowerTable->addRow([
    new RichTableCell($lowerLeft, [
        'border' => 'none',
        'padding' => '0cm',
    ]),
    new RichTableCell($lowerRight, [
        'border' => 'none',
        'padding' => '0cm',
    ]),
]);
$body->addTable($lowerTable);

$template->setElement('invoice_body', $body);
$template->setElement('invoice_header', $table);
$template->setElement('invoice_footer', $footer);
$template->save(__DIR__ . '/output/output_B01_invoice_template_builder.odt');

echo "Invoice RichText prototype generated successfully.\n";
Builders B02

Professional Report Builder

Constructs a fictional multi-page annual report from existing structured PHP authoring APIs.

Canonical sample ✓ Template available 3 dependencies · 0 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => report_body
            [1] => report_header
            [2] => report_footer
        )

    [native_objects] => Array
        (
        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

/**
 * B02 is the programmatic-builder counterpart to the Writer-owned showcase path.
 *
 * PHP intentionally owns the report's generated content structure here:
 * paragraphs, tables, images, page-flow hints and frame callouts are composed
 * through structured ODT elements. The surrounding ODT remains an editable
 * document shell, but B02 is not a template-first/native-object automation
 * example. Compare S03 when LibreOffice should own stable report structure.
 */

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\DrawTextBox;
use OdtTemplateEngine\Elements\ImageElement;
use OdtTemplateEngine\Elements\ListElement;
use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\Elements\RichTable;
use OdtTemplateEngine\Elements\RichTableCell;
use OdtTemplateEngine\Elements\RichText;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_B02_report_builder.odt');
$template->setDocumentDefaults([
    'text' => ['font-family' => 'Liberation Sans', 'color' => '#263746'],
    'paragraph' => ['margin-top' => '0cm', 'margin-bottom' => '0.18cm'],
]);
$template->setMeta([
    'title' => 'Annual Performance Report 2026',
    'subject' => 'B02 Professional Report Builder demonstration',
    'description' => 'A fictional annual performance report constructed with the ODT Template Engine.',
    'creator' => 'Asteria Demo Foundation',
    'keywords' => ['ODT Template Engine', 'B02', 'report builder', 'fictional demonstration'],
    'language' => 'en',
    'date' => '2027-01-31T00:00:00',
]);

$navy = '#0d3157';
$ink = '#263746';
$muted = '#536574';
$accent = '#2f89a3';
$paleBlue = '#e9f3f7';
$line = '#c9d9e1';
$light = '#f5f8fa';

$heading = static function (string $number, string $title, bool $breakBefore = false) use ($navy, $line): Paragraph {
    $options = [
        'keep-with-next' => 'always',
        'margin-top' => '0.2cm',
        'margin-bottom' => '0.28cm',
        'border-bottom' => '0.02cm solid ' . $line,
        'padding-bottom' => '0.16cm',
    ];
    if ($breakBefore) {
        $options['break-before'] = 'page';
    }

    return (new Paragraph($breakBefore ? 'B02PageHeading' : 'B02Heading', $options))
        ->addText($number . '  ', ['color' => '#a8c2d2', 'font-size' => '22pt', 'bold' => true])
        ->addText($title, ['color' => $navy, 'font-size' => '18pt', 'bold' => true]);
};

$bodyParagraph = static function (string $text, string $marginBottom = '0.22cm') use ($ink): Paragraph {
    $style = $marginBottom === '0.22cm'
        ? 'B02Body'
        : 'B02Body' . str_replace(['.', 'cm'], '', $marginBottom);

    return (new Paragraph($style, [
        'widows' => 2,
        'orphans' => 2,
        'line-height' => '112%',
        'margin-bottom' => $marginBottom,
    ]))->addText($text, ['color' => $ink, 'font-size' => '10pt']);
};

$subheading = static function (string $text, string $marginTop = '0.12cm', string $marginBottom = '0.08cm') use ($navy): Paragraph {
    $style = ($marginTop === '0.12cm' && $marginBottom === '0.08cm')
        ? 'B02Subheading'
        : 'B02Subheading' . str_replace(['.', 'cm'], '', $marginTop . $marginBottom);

    return (new Paragraph($style, [
        'keep-with-next' => 'always',
        'margin-top' => $marginTop,
        'margin-bottom' => $marginBottom,
    ]))->addText($text, ['color' => $navy, 'font-size' => '12pt', 'bold' => true]);
};

$plain = static function (string $text) use ($ink): Paragraph {
    return (new Paragraph('B02Body'))->addText($text, ['color' => $ink, 'font-size' => '10pt']);
};

$cell = static function (string|Paragraph|RichText $content, array $style = []): RichTableCell {
    return new RichTableCell($content, array_merge([
        'padding' => '0.16cm',
        'vertical-align' => 'middle',
    ], $style));
};

$header = (new RichText())->addParagraph(
    (new Paragraph('B02Header', ['margin-top' => '0cm', 'margin-bottom' => '0cm', 'tab-stops' => [['position' => 16.5, 'alignment' => 'right']]]))
        ->addText('ASTERIA', ['bold' => true, 'color' => $navy, 'font-size' => '8pt'])
        ->addText('  /  ANNUAL PERFORMANCE REPORT 2026', ['color' => $muted, 'font-size' => '8pt'])
);

$footer = (new RichText())->addParagraph(
    (new Paragraph('B02Footer', ['margin-top' => '0cm', 'margin-bottom' => '0cm']))
        ->addText('Asteria Demo Foundation · Pathways 360 Demo Program', ['color' => $muted, 'font-size' => '8pt'])
);

$body = new RichText();

// Cover: an editable editorial composition with the title above a larger visual.
$coverImage = new ImageElement(__DIR__ . '/assets/asteria-b02-cover.png', [
    'width' => '9.4cm',
    'anchor' => 'as-char',
]);
$cover = (new RichTable())->setTableStyle(['relative-width' => '100%', 'alignment' => 'center']);
$cover->setColumnWidthRatios([3, 2, 2, 2]);
$cover->addRow([
    $cell((new Paragraph('B02CoverImage', ['text-align' => 'center']))->addElement($coverImage), ['padding' => '0.12cm', 'background' => $light, 'border' => 'none'])->setColspan(4),
]);
$body->addParagraph((new Paragraph('B02CoverBrand', ['margin-left' => '0.28cm', 'margin-right' => '0.28cm', 'margin-bottom' => '0.08cm']))->addText('ASTERIA DEMO FOUNDATION', ['color' => $navy, 'font-size' => '11pt', 'bold' => true]));
$body->addParagraph((new Paragraph('B02CoverKicker', ['margin-left' => '0.28cm', 'margin-right' => '0.28cm', 'margin-bottom' => '0.12cm']))->addText('SAMPLE DOCUMENT · FICTIONAL ORGANIZATION AND DATA', ['color' => $accent, 'font-size' => '9pt', 'bold' => true]));
$body->addParagraph((new Paragraph('B02CoverTitle', ['keep-with-next' => 'always', 'margin-left' => '0.28cm', 'margin-right' => '0.28cm', 'margin-bottom' => '0.12cm']))->addText('Annual Performance Report 2026', ['color' => $navy, 'font-size' => '30pt', 'bold' => true]));
$body->addParagraph((new Paragraph('B02CoverProgram', ['margin-left' => '0.28cm', 'margin-right' => '0.28cm', 'margin-bottom' => '0.14cm']))->addText('Pathways 360 Demo Program', ['color' => $navy, 'font-size' => '14pt', 'bold' => true]));
$body->addParagraph((new Paragraph('B02CoverMeta', ['margin-left' => '0.28cm', 'margin-right' => '0.28cm', 'margin-bottom' => '0.38cm']))->addText('January–December 2026  ·  Final Report · 31 January 2027', ['color' => $ink, 'font-size' => '10pt']));
$body->addTable($cover);
$body->addParagraph((new Paragraph('B02CoverClosing', ['break-after' => 'page', 'margin-top' => '0.18cm', 'border-top' => '0.02cm solid ' . $line, 'padding-top' => '0.14cm']))->addText('This document is a demonstration sample created for the ODT Template Engine. All organizations, programs, persons, figures and report data shown in this document are fictional and are used solely for demonstration purposes.', ['color' => $muted, 'font-size' => '8.5pt', 'italic' => true]));

// Executive Summary.
$body->addParagraph($heading('01', 'Executive Summary'));
$body->addParagraph($bodyParagraph('The Pathways 360 Demo Program supports people to build skills, confidence and connections for sustainable employment and further training. During 2026, the program reached 248 participants across three delivery locations, combining individual coaching, practical workshops, digital skills development and direct engagement with local employers.', '0.34cm'));
$body->addParagraph($bodyParagraph('The delivery model is designed around progression rather than a single intervention. Participants can move between coaching, skills practice and employer-facing activity as their needs develop. This integrated approach helped the program maintain strong completion and outcome rates while building a broader base of employer partnerships.', '0.34cm'));
$body->addParagraph($bodyParagraph('The results indicate that coordinated support is most valuable when it is connected to realistic next steps. The strongest progression was observed among participants who received both individual coaching and employer-facing support. The year also highlighted the importance of differentiated digital support and more consistent outcome tracking as the program grows.', '0.38cm'));

$kpi = static function (string $value, string $label) use ($navy, $muted): RichText {
    return (new RichText())
        ->addParagraph((new Paragraph('B02KpiValue', ['text-align' => 'center', 'margin-bottom' => '0.04cm']))->addText($value, ['color' => $navy, 'font-size' => '20pt', 'bold' => true]))
        ->addParagraph((new Paragraph('B02KpiLabel', ['text-align' => 'center', 'margin-bottom' => '0cm']))->addText($label, ['color' => $muted, 'font-size' => '8.5pt']));
};
$kpis = (new RichTable())->setTableStyle(['relative-width' => '100%', 'alignment' => 'center']);
$kpis->setColumnWidthRatios([3, 2, 2, 2]);
$kpis->addRow([
    $cell($kpi('248', 'Participants'), ['background' => $paleBlue, 'border' => 'none']),
    $cell($kpi('87%', 'Completion rate'), ['background' => $paleBlue, 'border' => 'none']),
    $cell($kpi('64%', 'Positive outcomes'), ['background' => $paleBlue, 'border' => 'none']),
    $cell($kpi('42', 'Employer partners'), ['background' => $paleBlue, 'border' => 'none']),
], ['min-row-height' => '1.7cm']);
$body->addTable($kpis);
$body->addParagraphBreak(1);

$finding = (new DrawTextBox('B02KeyFinding', [
    'background-color' => $paleBlue,
    'border-left' => '0.08cm solid ' . $accent,
    'padding' => '0.24cm',
]))->setFrameLayout([
    'anchor' => 'paragraph',
    'width' => '16.2cm',
    'height' => '1.95cm',
    'horizontal' => ['alignment' => 'center', 'relative-to' => 'paragraph'],
    'vertical' => ['alignment' => 'top', 'relative-to' => 'paragraph'],
    'wrap' => 'none',
])->addElement(
    (new Paragraph('B02KeyFindingTitle', ['margin-bottom' => '0.05cm']))->addText('KEY FINDING', ['color' => $navy, 'font-size' => '10pt', 'bold' => true])
)->addElement(
    (new Paragraph('B02KeyFindingBody', ['margin-bottom' => '0cm']))->addText('Participants receiving combined individual coaching and employer-facing support showed the strongest progression.', ['color' => $ink, 'font-size' => '9.5pt'])
);
$body->addElement($finding);
$body->addParagraph($subheading('Management reading', '0.28cm', '0.12cm'));
$body->addParagraph($bodyParagraph('Management reading: the headline results are strongest when reach, completion and employer connection are considered together. The next planning cycle should protect this integrated model while targeting digital confidence and more consistent outcome tracking.', '0.28cm'));

// Program Overview & Delivery.
$body->addParagraph($heading('02', 'Program Overview & Delivery', true));
$body->addParagraph($bodyParagraph('Pathways 360 is a fictional demonstration program that combines coaching, practical skills development and employer engagement. Its 2026 delivery model focused on improving readiness for employment or further training while maintaining a supportive route for participants with different starting points.', '0.34cm'));
$facts = (new RichTable())->setTableStyle(['relative-width' => '100%', 'alignment' => 'center']);
$facts->setColumnWidthRatios([3, 2, 2, 2]);
$facts->addRow([
    $cell((new RichText())->addParagraph((new Paragraph('B02Fact'))->addText('REPORTING PERIOD', ['color' => $muted, 'font-size' => '8pt', 'bold' => true]))->addParagraph((new Paragraph('B02FactValue'))->addText('January–December 2026', ['color' => $navy, 'font-size' => '9pt'])), ['background' => $light, 'border' => 'none']),
    $cell((new RichText())->addParagraph((new Paragraph('B02Fact'))->addText('LOCATIONS', ['color' => $muted, 'font-size' => '8pt', 'bold' => true]))->addParagraph((new Paragraph('B02FactValue'))->addText('3 program locations', ['color' => $navy, 'font-size' => '9pt'])), ['background' => $light, 'border' => 'none']),
    $cell((new RichText())->addParagraph((new Paragraph('B02Fact'))->addText('DELIVERY TEAM', ['color' => $muted, 'font-size' => '8pt', 'bold' => true]))->addParagraph((new Paragraph('B02FactValue'))->addText('12 staff', ['color' => $navy, 'font-size' => '9pt'])), ['background' => $light, 'border' => 'none']),
    $cell((new RichText())->addParagraph((new Paragraph('B02Fact'))->addText('EMPLOYER PARTNERS', ['color' => $muted, 'font-size' => '8pt', 'bold' => true]))->addParagraph((new Paragraph('B02FactValue'))->addText('42 partners', ['color' => $navy, 'font-size' => '9pt'])), ['background' => $light, 'border' => 'none']),
]);
$body->addTable($facts);
$body->addParagraph($subheading('Program Objectives', '0.28cm', '0.12cm'));
$objectives = new ListElement('bullet');
foreach ([
    'Improve employment readiness and training opportunities.',
    'Strengthen vocational and digital skills.',
    'Connect participants with employers.',
    'Support progression into employment or further training.',
] as $objective) {
    $objectives->addItem((new Paragraph('B02ListItem'))->addText($objective, ['color' => $ink, 'font-size' => '9.5pt']));
}
$body->addElement($objectives);
$body->addParagraph($subheading('Activities Delivered', '0.30cm', '0.12cm'));
$activities = (new RichTable())->setTableStyle(['relative-width' => '100%', 'alignment' => 'center']);
$activities->setColumnWidthRatios([3, 2, 2, 2]);
$activities->setHeaderRowCount(1);
$activities->addRow([
    $cell('Activity', ['background' => $navy, 'color' => '#ffffff', 'weight' => 'bold', 'border' => 'none'])->setColspan(2),
    $cell((new Paragraph('B02TableHeader', ['text-align' => 'right']))->addText('Delivered', ['color' => '#ffffff', 'font-size' => '8.5pt', 'bold' => true]), ['background' => $navy, 'border' => 'none']),
    $cell((new Paragraph('B02TableHeader', ['text-align' => 'right']))->addText('Participants', ['color' => '#ffffff', 'font-size' => '8.5pt', 'bold' => true]), ['background' => $navy, 'border' => 'none']),
]);
foreach ([
    ['Individual coaching sessions', '1,146', '231'],
    ['Skills workshops', '84', '218'],
    ['Employer events', '18', '156'],
    ['Digital skills sessions', '46', '174'],
] as $row) {
    $activities->addRow([
        $cell($row[0], ['border-bottom' => '0.02cm solid ' . $line])->setColspan(2),
        $cell((new Paragraph('B02TableNumber', ['text-align' => 'right']))->addText($row[1], ['color' => $ink, 'font-size' => '9pt']), ['border-bottom' => '0.02cm solid ' . $line]),
        $cell((new Paragraph('B02TableNumber', ['text-align' => 'right']))->addText($row[2], ['color' => $ink, 'font-size' => '9pt']), ['border-bottom' => '0.02cm solid ' . $line]),
    ]);
}
$body->addTable($activities);
$body->addParagraph($subheading('Delivery Observation', '0.30cm', '0.12cm'));
$body->addParagraph($bodyParagraph('The activity mix combines a high volume of individual coaching with shared skills practice and employer-facing opportunities. This balance gives the program a practical route to respond to different starting points while still connecting participants to common progression goals.', '0.30cm'));
$body->addParagraph($bodyParagraph('The scale of coaching, alongside 84 workshops and 18 employer events, also shows why coordination matters: individual support carries the largest contact load, while group and employer activity creates the shared practice and external connection needed to turn preparation into progression.', '0.28cm'));

// Performance & Outcomes.
$body->addParagraph($heading('03', 'Performance & Outcomes', true));
$body->addParagraph($subheading('Performance against Targets'));
$performance = (new RichTable())->setTableStyle(['relative-width' => '100%', 'alignment' => 'center']);
$performance->setColumnWidthRatios([3, 2, 2, 2]);
$performance->setHeaderRowCount(1);
$performance->addRow([
    $cell('Indicator', ['background' => $navy, 'color' => '#ffffff', 'weight' => 'bold', 'border' => 'none']),
    $cell('Target', ['background' => $navy, 'color' => '#ffffff', 'weight' => 'bold', 'text-align' => 'right', 'border' => 'none']),
    $cell('Actual', ['background' => $navy, 'color' => '#ffffff', 'weight' => 'bold', 'text-align' => 'right', 'border' => 'none']),
    $cell('Status', ['background' => $navy, 'color' => '#ffffff', 'weight' => 'bold', 'border' => 'none']),
]);
foreach ([
    ['Participants enrolled', '240', '248', 'Achieved'],
    ['Program completion', '80%', '87%', 'Above target'],
    ['Employment / training outcome', '60%', '64%', 'Above target'],
    ['Employer partners', '35', '42', 'Above target'],
    ['Digital skills completion', '75%', '78%', 'Achieved'],
] as $row) {
    $performance->addRow([
        $cell($row[0], ['border-bottom' => '0.02cm solid ' . $line]),
        $cell((new Paragraph('B02TableNumber', ['text-align' => 'right']))->addText($row[1], ['color' => $ink, 'font-size' => '9pt']), ['border-bottom' => '0.02cm solid ' . $line]),
        $cell((new Paragraph('B02TableNumber', ['text-align' => 'right']))->addText($row[2], ['color' => $ink, 'font-size' => '9pt']), ['border-bottom' => '0.02cm solid ' . $line]),
        $cell((new Paragraph('B02Status'))->addText($row[3], ['color' => $accent, 'font-size' => '9pt', 'bold' => true]), ['border-bottom' => '0.02cm solid ' . $line]),
    ]);
}
$body->addTable($performance);
$body->addParagraphBreak(1);
$outcomes = (new RichText())
    ->addParagraph($subheading('Participant Outcomes'))
    ->addParagraph((new Paragraph('B02Chart'))->addElement(new ImageElement(__DIR__ . '/assets/asteria-b02-outcomes.png', ['width' => '15.8cm', 'anchor' => 'as-char'])))
    ->addParagraph((new Paragraph('B02Caption', ['margin-top' => '0.06cm']))->addText('Prepared visualization of fictional 2026 participant outcomes.', ['color' => $muted, 'font-size' => '8.5pt', 'italic' => true]))
    ->addParagraph($bodyParagraph('Employment represented the largest outcome group at 41%, while a further 23% moved into training and 21% remained in active progression. Together, these results indicate that the program supported several meaningful routes forward rather than a single definition of success.'));
$body->addElement($outcomes);

// Findings & Analysis.
$body->addParagraph($heading('04', 'Findings & Analysis', true));
$body->addParagraph($subheading('Integrated support produced stronger outcomes'));
$body->addParagraph($bodyParagraph('Participants who combined individual coaching with workshops and employer-facing activity showed the strongest progression. The pattern suggests that practical support is most effective when it is connected to a clear next step and reinforced by more than one type of engagement.', '0.30cm'));
$body->addParagraph($bodyParagraph('For management, the implication is to preserve the sequence between preparation and opportunity: coaching builds readiness, practice makes capability visible, and employer contact gives progression a concrete direction.', '0.34cm'));
$body->addParagraph($subheading('Digital confidence remains uneven', '0.28cm', '0.12cm'));
$body->addParagraph($bodyParagraph('Digital confidence improved across the program, but starting points varied considerably. Participants with less prior experience benefited from additional practice, more patient guidance and opportunities to apply digital skills in realistic vocational contexts.', '0.30cm'));
$body->addParagraph($bodyParagraph('This variation matters because digital tasks increasingly sit inside otherwise practical routes into work and training. A differentiated support offer can therefore strengthen both participation in the program and the quality of later progression.', '0.34cm'));

$insight = (new DrawTextBox('B02ProgramInsight', [
    'background-color' => '#dceef3',
    'border-left' => '0.08cm solid ' . $accent,
    'padding' => '0.24cm',
]))->setFrameLayout([
    'anchor' => 'paragraph',
    'width' => '9.2cm',
    'height' => '2.75cm',
    'horizontal' => ['alignment' => 'right', 'relative-to' => 'paragraph'],
    'vertical' => ['alignment' => 'top', 'relative-to' => 'paragraph'],
    'wrap' => 'left',
])->addElement(
    (new Paragraph('B02ProgramInsightTitle', ['margin-bottom' => '0.06cm']))->addText('PROGRAM INSIGHT', ['color' => $navy, 'font-size' => '10pt', 'bold' => true])
)->addElement(
    (new Paragraph('B02ProgramInsightBody', ['margin-bottom' => '0cm']))->addText('Employer engagement proved most effective when employers became involved before the formal recruitment stage rather than only receiving completed candidate profiles. Early contact made expectations clearer and gave participants a more realistic context for preparation.', ['color' => $ink, 'font-size' => '9pt'])
);
$body->addElement($insight);
$body->addParagraph($subheading('Challenges', '0.30cm', '0.12cm'));
$challenges = new ListElement('bullet');
foreach ([
    'Irregular attendance among a small participant group.',
    'Varying levels of digital literacy.',
    'Limited placement opportunities in some occupational areas.',
] as $challenge) {
    $challenges->addItem((new Paragraph('B02ListItem'))->addText($challenge, ['color' => $ink, 'font-size' => '9.5pt']));
}
$body->addElement($challenges);
$body->addParagraph($subheading('Management implication', '0.34cm', '0.12cm'));
$body->addParagraph($bodyParagraph('The response should be selective rather than broad: protect flexible support for attendance, build digital practice into ordinary delivery, and focus employer development where placement opportunities are most constrained.', '0.30cm'));

// Recommendations & Outlook.
$body->addParagraph($heading('05', 'Recommendations & Outlook', true));
$body->addParagraph($subheading('Recommendations', '0.28cm', '0.12cm'));
$recommendations = new ListElement('numbered');
foreach ([
    'Expand employer involvement by bringing partners into program design and preparation earlier. This should align practice opportunities with realistic recruitment expectations and make employer-facing activity more purposeful.',
    'Strengthen targeted digital support for participants with lower initial confidence. Additional guided practice should help participants apply digital skills with greater confidence in vocational contexts.',
    'Improve outcome tracking so longer-term progression can be understood more consistently. A clearer view after completion would help distinguish immediate results from sustained movement into work or training.',
    'Create more peer-learning opportunities around sector-specific practice and confidence building. Shared practice can reinforce capability and reduce isolation for participants working toward similar goals.',
] as $recommendation) {
    $recommendations->addItem((new Paragraph('B02Recommendation', ['margin-bottom' => '0.16cm']))->addText($recommendation, ['color' => $ink, 'font-size' => '9.5pt']));
}
$body->addElement($recommendations);
$body->addParagraph($subheading('Outlook 2027', '0.34cm', '0.12cm'));
$body->addParagraph($bodyParagraph('In 2027, Pathways 360 will build on the progress achieved in 2026 by deepening employer partnerships, strengthening targeted digital support and improving the continuity of outcome tracking. The focus will remain on high-quality, person-centred support that connects confidence, capability and realistic progression. Delivery will continue to balance individual attention with shared practice, so that growth in scale does not weaken the relationships that make progression possible.', '0.30cm'));
$body->addParagraph($bodyParagraph('The intended measure of progress is not only more activity, but clearer movement from preparation to opportunity. The program will therefore use the 2026 learning to refine support, strengthen the quality of employer connection and make outcomes easier to follow over time.', '0.30cm'));
$body->addParagraph((new Paragraph('B02Disclaimer', ['break-after' => 'page', 'margin-top' => '0.50cm', 'border-top' => '0.02cm solid ' . $line, 'padding-top' => '0.18cm']))->addText('This document is a demonstration sample created for the ODT Template Engine. All organizations, programs, persons, figures and report data shown in this document are fictional and are used solely for demonstration purposes.', ['color' => $muted, 'font-size' => '8.5pt', 'italic' => true]));

$template->setElement('report_header', $header);
$template->setElement('report_body', $body);
$template->setElement('report_footer', $footer);
$template->save(__DIR__ . '/output/output_B02_report_builder.odt');

echo "B02 Professional Report Builder generated successfully.\n";
Showcases S01b

Professional CV · Structured Template

Shows a professional CV where LibreOffice owns native Sections, page design, and image geometry while PHP supplies bounded content.

Canonical sample ✓ Template available 15 dependencies · 17 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => VName
            [1] => Name
            [2] => BDate
            [3] => BTown
            [4] => JobDuration
            [5] => JobName
            [6] => JobCompany
            [7] => Activity
            [8] => EducationDuration
            [9] => EducationName
            [10] => EducationInstitution
            [11] => QualificationName
            [12] => QualificationDescription
            [13] => CVSidebarPage1
            [14] => CVSidebarPage2
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => section
                    [name] => TimelineWrapper
                )

            [1] => Array
                (
                    [kind] => frame
                    [name] => Rahmen1
                )

            [2] => Array
                (
                    [kind] => frame
                    [name] => CVImage
                )

            [3] => Array
                (
                    [kind] => section
                    [name] => Introduction
                )

            [4] => Array
                (
                    [kind] => bookmark
                    [name] => Extract
                )

            [5] => Array
                (
                    [kind] => bookmark
                    [name] => ExtractJobHeadline
                )

            [6] => Array
                (
                    [kind] => bookmark
                    [name] => ExtractJobDescription
                )

            [7] => Array
                (
                    [kind] => section
                    [name] => Experience
                )

            [8] => Array
                (
                    [kind] => bookmark
                    [name] => JobSectionName
                )

            [9] => Array
                (
                    [kind] => section
                    [name] => JobSection
                )

            [10] => Array
                (
                    [kind] => section
                    [name] => ActivitySection
                )

            [11] => Array
                (
                    [kind] => section
                    [name] => Education
                )

            [12] => Array
                (
                    [kind] => section
                    [name] => EducationSection
                )

            [13] => Array
                (
                    [kind] => section
                    [name] => AdditionalQualifications
                )

            [14] => Array
                (
                    [kind] => section
                    [name] => QualificationSection
                )

            [15] => Array
                (
                    [kind] => frame
                    [name] => sidebar
                )

            [16] => Array
                (
                    [kind] => frame
                    [name] => Rahmen2
                )

        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\ImageElement;
use OdtTemplateEngine\Elements\ListElement;
use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\Elements\RichText;
use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_S01b_cv_structured.odt');

$template->setDocumentDefaults([
    'text' => [
        'font-family' => 'Arial',
    ],
]);

$cv = [
    'personal' => [
        'first_name' => 'Andrew',
        'last_name' => 'Thompson',
        'birth' => '12.04.1984',
        'city' => 'Sydney, Australia',
        'email' => 'andrew.t@example.com',
        'phone' => '+61 412 555 018',
    ],
    'experience' => [
        [
            'duration' => '2021 – present',
            'name' => 'Senior Project Manager',
            'company' => 'Harbour Digital, Sydney',
            'activities' => [
                'Leading international delivery teams across a portfolio of digital transformation projects.',
                'Introduced agile planning practices that improved delivery predictability and stakeholder visibility.',
                'Managing budgets of up to AUD 5 million across parallel programmes.',
            ],
        ],
        [
            'duration' => '2017 – 2021',
            'name' => 'Project Manager',
            'company' => 'Southern Cross Consulting, Melbourne',
            'activities' => [
                'Delivered customer-platform programmes for public and private-sector organisations.',
                'Coordinated product, engineering, design, and external delivery partners.',
            ],
        ],
        [
            'duration' => '2014 – 2017',
            'name' => 'Junior Project Manager',
            'company' => 'Pacific Systems, Brisbane',
            'activities' => [
                'Supported planning, risk management, and communication for software projects.',
                'Built reporting routines that made milestones and dependencies visible to project teams.',
            ],
        ],
        [
            'duration' => '2012 – 2014',
            'name' => 'Assistant Project Manager',
            'company' => 'Northshore Technology, Sydney',
            'activities' => [
                'Maintained project documentation and supported sprint and release coordination.',
                'Prepared stakeholder updates and captured decisions for distributed teams.',
            ],
        ],
    ],
    'education' => [
        [
            'duration' => '2010 – 2012',
            'name' => 'Master of Business Administration',
            'institution' => 'University of Sydney',
        ],
        [
            'duration' => '2006 – 2010',
            'name' => 'Bachelor of Business Administration',
            'institution' => 'University of Technology Sydney',
        ],
    ],
    'qualifications' => [
        ['name' => 'Volunteer work', 'description' => 'Mentor for early-career project managers.'],
        ['name' => 'Awards', 'description' => 'Delivery Excellence Award, Harbour Digital, 2023.'],
        ['name' => 'Publications', 'description' => 'Contributor to the Australian Project Leadership Review.'],
    ],
];

function cvParagraph(string $text, ?string $paragraphStyle = null): Paragraph
{
    $paragraph = new Paragraph($paragraphStyle);
    $paragraph->addText($text);

    return $paragraph;
}

function addSidebarHeading(RichText $sidebar, string $heading): void
{
    $sidebar->addParagraph(cvParagraph($heading, 'S01bSidebarHeading'));
}

/** @param list<string> $items */
function addSidebarList(RichText $sidebar, string $heading, array $items): void
{
    addSidebarHeading($sidebar, $heading);
    $list = new ListElement('bullet');
    foreach ($items as $item) {
        $list->addItem(cvParagraph($item, 'S01bSidebarListItem'));
    }
    $sidebar->addElement($list);
}

// The sidebar is PHP-owned content inside template-authored Writer Frames.
// Typography and spacing stay in the template's S01bSidebar* paragraph styles.
$sidebarPage1 = new RichText();
addSidebarHeading($sidebarPage1, 'CONTACT');
$sidebarPage1->addParagraph(cvParagraph($cv['personal']['email'], 'S01bSidebarLine'));
$sidebarPage1->addParagraph(cvParagraph($cv['personal']['phone'], 'S01bSidebarLine'));
$sidebarPage1->addParagraph(cvParagraph($cv['personal']['city'], 'S01bSidebarLine'));
addSidebarList($sidebarPage1, 'PLUS POINTS', [
    'Strategic project leadership',
    'International team coordination',
    'Agile transformation',
]);
addSidebarList($sidebarPage1, 'SOFT SKILLS', [
    'Clear communication',
    'Calm decision-making',
    'Collaborative leadership',
]);
addSidebarHeading($sidebarPage1, 'PROFESSIONAL SKILLS');
foreach ([
    ['name' => 'Portfolio management', 'level' => 5],
    ['name' => 'Risk planning', 'level' => 4],
    ['name' => 'Stakeholder engagement', 'level' => 4],
] as $skill) {
    $rating = str_repeat('★', $skill['level']) . str_repeat('☆', 5 - $skill['level']);
    // Alignment is template-owned: S01bSidebarSkill defines the 5.2cm tab stop.
    $paragraph = new Paragraph('S01bSidebarSkill');
    $paragraph->addText($skill['name'])->addTab()->addText($rating);
    $sidebarPage1->addParagraph($paragraph);
}

addSidebarList($sidebarPage1, 'LICENSES', [
    'PMP · 2018',
    'PRINCE2 Practitioner · 2016',
]);
addSidebarList($sidebarPage1, 'LANGUAGES', [
    'English · native',
    'German · professional',
]);

$sidebarPage2 = new RichText();
$sidebarPage2->addParagraph(cvParagraph('ANDREW THOMPSON', 'S01bSidebarName'));
$sidebarPage2->addParagraph(cvParagraph('Senior Project Manager', 'S01bSidebarLine'));
$sidebarPage2->addParagraph(cvParagraph($cv['personal']['email'], 'S01bSidebarLine'));
$sidebarPage2->addParagraph(cvParagraph($cv['personal']['phone'], 'S01bSidebarLine'));

// The authored CVImage frame owns its geometry and placement. The current
// compatibility facade requires explicit authored dimensions to avoid its
// legacy 5cm x 3cm defaults while replacing only the image resource.
$template->replaceImageByName('CVImage', __DIR__ . '/../assets/BFoto.png', [
    'width' => '4.001cm',
    'height' => '3.799cm',
]);

// {{Image}} is a separate authored insertion point in the sidebar frame.
$template->setElement('Image', new ImageElement(__DIR__ . '/../assets/WaltDietzney.png', [
    'width' => '2.4cm',
    'height' => '2.4cm',
    'anchor' => 'as-char',
]));
$template->setElement('CVSidebarPage1', $sidebarPage1);
$template->setElement('CVSidebarPage2', $sidebarPage2);

$template->assign([
    'VName' => $cv['personal']['first_name'],
    'Name' => $cv['personal']['last_name'],
    'BDate' => $cv['personal']['birth'],
    'BTown' => $cv['personal']['city'],
]);

$template->bookmark('Extract')->replaceText('PROFILE');
$template->bookmark('ExtractJobHeadline')->replaceText('Senior Project Manager with 10+ years of delivery leadership');
$template->bookmark('ExtractJobDescription')->replaceText(
    'Experienced project manager specialising in agile transformation, multi-project delivery, and international teams.'
);

if ($cv['experience'] === []) {
    $template->section('Experience')->instantiateMany([]);
} else {
    $experienceInstances = $template->section('Experience')->section('JobSection')->instantiateMany(
        array_map(
            static fn (array $job): array => [
                'JobDuration' => $job['duration'],
                'JobName' => $job['name'],
                'JobCompany' => $job['company'],
            ],
            $cv['experience']
        )
    );

    foreach ($experienceInstances as $index => $experience) {
        $activities = $cv['experience'][$index]['activities'];
        $experience->section('ActivitySection')->instantiateMany(
            array_map(static fn (string $activity): array => ['Activity' => $activity], $activities)
        );
    }
}

if ($cv['education'] === []) {
    $template->section('Education')->instantiateMany([]);
} else {
    $template->section('Education')->section('EducationSection')->instantiateMany(
        array_map(
            static fn (array $entry): array => [
                'EducationDuration' => $entry['duration'],
                'EducationName' => $entry['name'],
                'EducationInstitution' => $entry['institution'],
            ],
            $cv['education']
        )
    );
}

if ($cv['qualifications'] === []) {
    $template->section('AdditionalQualifications')->instantiateMany([]);
} else {
    $template->section('AdditionalQualifications')->section('QualificationSection')->instantiateMany(
        array_map(
            static fn (array $qualification): array => [
                'QualificationName' => $qualification['name'],
                'QualificationDescription' => $qualification['description'],
            ],
            $cv['qualifications']
        )
    );
}

$template->render();
$template->save(__DIR__ . '/output/output_S01b_cv_structured.odt');

echo "Document generated successfully: samples/output/output_S01b_cv_structured.odt\n";
Showcases S03

Structured Professional Report

Demonstrates native User Fields, bookmarks, named tables, image frames, and bounded Section composition from one Writer-authored report template.

Canonical sample ✓ Template available 8 dependencies · 86 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => organization_name
            [1] => organization_short_name
            [2] => report_title
            [3] => report_year
            [4] => program_name
            [5] => report_period
            [6] => report_status
            [7] => report_date
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => section
                    [name] => ReportHeadline
                )

            [1] => Array
                (
                    [kind] => table
                    [name] => Table_1
                )

            [2] => Array
                (
                    [kind] => frame
                    [name] => ReportTitleImage
                )

            [3] => Array
                (
                    [kind] => section
                    [name] => ReportCoverClosing
                )

            [4] => Array
                (
                    [kind] => section
                    [name] => ExecutiveSummary
                )

            [5] => Array
                (
                    [kind] => section
                    [name] => ExecutiveSummaryContent
                )

            [6] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryTitle
                )

            [7] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryIntro
                )

            [8] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryApproach
                )

            [9] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryResults
                )

            [10] => Array
                (
                    [kind] => table
                    [name] => ExecutiveSummaryKpis
                )

            [11] => Array
                (
                    [kind] => bookmark
                    [name] => KpiParticipantsValue
                )

            [12] => Array
                (
                    [kind] => bookmark
                    [name] => KpiParticipantsLabel
                )

            [13] => Array
                (
                    [kind] => bookmark
                    [name] => KpiCompletionRateValue
                )

            [14] => Array
                (
                    [kind] => bookmark
                    [name] => KpiCompletionRateLabel
                )

            [15] => Array
                (
                    [kind] => bookmark
                    [name] => KpiPositiveOutcomesValue
                )

            [16] => Array
                (
                    [kind] => bookmark
                    [name] => KpiPositiveOutcomesLabel
                )

            [17] => Array
                (
                    [kind] => bookmark
                    [name] => KpiEmployerPartnersValue
                )

            [18] => Array
                (
                    [kind] => bookmark
                    [name] => KpiEmployerPartnersLabel
                )

            [19] => Array
                (
                    [kind] => frame
                    [name] => ExecutiveSummaryKeyFinding
                )

            [20] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryKeyFinding
                )

            [21] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryKeyFindingText
                )

            [22] => Array
                (
                    [kind] => section
                    [name] => ReportManagementReading
                )

            [23] => Array
                (
                    [kind] => bookmark
                    [name] => ManagementReadingTitle
                )

            [24] => Array
                (
                    [kind] => bookmark
                    [name] => ManagementReadingText
                )

            [25] => Array
                (
                    [kind] => section
                    [name] => ProgramOverview
                )

            [26] => Array
                (
                    [kind] => section
                    [name] => ProgramOverviewIntroduction
                )

            [27] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramOverviewTitle
                )

            [28] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramOverviewIntro
                )

            [29] => Array
                (
                    [kind] => table
                    [name] => Table_3
                )

            [30] => Array
                (
                    [kind] => bookmark
                    [name] => ReportingPeriodLabel
                )

            [31] => Array
                (
                    [kind] => bookmark
                    [name] => ReportingPeriodValue
                )

            [32] => Array
                (
                    [kind] => bookmark
                    [name] => LocationsLabel
                )

            [33] => Array
                (
                    [kind] => bookmark
                    [name] => LocationsValue
                )

            [34] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryTeamLabel
                )

            [35] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryTeamValue
                )

            [36] => Array
                (
                    [kind] => bookmark
                    [name] => EmployerPartnersLabel
                )

            [37] => Array
                (
                    [kind] => bookmark
                    [name] => EmployerPartnersValue
                )

            [38] => Array
                (
                    [kind] => section
                    [name] => ProgramObjectives
                )

            [39] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramObjectivesTitle
                )

            [40] => Array
                (
                    [kind] => section
                    [name] => ProgramObjectivesContent
                )

            [41] => Array
                (
                    [kind] => bookmark
                    [name] => ActivitiesDeliveredTitle
                )

            [42] => Array
                (
                    [kind] => table
                    [name] => ActivitiesDeliveredTable
                )

            [43] => Array
                (
                    [kind] => section
                    [name] => DeliveryObservation
                )

            [44] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryObservationTitle
                )

            [45] => Array
                (
                    [kind] => section
                    [name] => DeliveryObservationText
                )

            [46] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryObservationText1
                )

            [47] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryObservationText2
                )

            [48] => Array
                (
                    [kind] => section
                    [name] => PerformanceOutcomes
                )

            [49] => Array
                (
                    [kind] => bookmark
                    [name] => PerformanceOutcomesTitle
                )

            [50] => Array
                (
                    [kind] => bookmark
                    [name] => PerformanceAgainstTargetsTitle
                )

            [51] => Array
                (
                    [kind] => table
                    [name] => PerformanceAgainstTargetsTable
                )

            [52] => Array
                (
                    [kind] => section
                    [name] => ParticipantOutcomes
                )

            [53] => Array
                (
                    [kind] => bookmark
                    [name] => ParticipantOutcomesTitle
                )

            [54] => Array
                (
                    [kind] => frame
                    [name] => ParticipantOutcomesImage
                )

            [55] => Array
                (
                    [kind] => bookmark
                    [name] => ParticipantOutcomesCaption
                )

            [56] => Array
                (
                    [kind] => bookmark
                    [name] => ParticipantOutcomesText
                )

            [57] => Array
                (
                    [kind] => section
                    [name] => FindingsAnalysis
                )

            [58] => Array
                (
                    [kind] => bookmark
                    [name] => FindingsAnalysisTitle
                )

            [59] => Array
                (
                    [kind] => section
                    [name] => FindingIntegratedSupport
                )

            [60] => Array
                (
                    [kind] => bookmark
                    [name] => FindingIntegratedSupportTitle
                )

            [61] => Array
                (
                    [kind] => bookmark
                    [name] => FindingIntegratedSupportText1
                )

            [62] => Array
                (
                    [kind] => bookmark
                    [name] => FindingIntegratedSupportText2
                )

            [63] => Array
                (
                    [kind] => section
                    [name] => FindingDigitalConfidence
                )

            [64] => Array
                (
                    [kind] => bookmark
                    [name] => FindingDigitalConfidenceTitle
                )

            [65] => Array
                (
                    [kind] => bookmark
                    [name] => FindingDigitalConfidenceText1
                )

            [66] => Array
                (
                    [kind] => bookmark
                    [name] => FindingDigitalConfidenceText2
                )

            [67] => Array
                (
                    [kind] => frame
                    [name] => ProgramInsight
                )

            [68] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramInsightTitle
                )

            [69] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramInsightText
                )

            [70] => Array
                (
                    [kind] => section
                    [name] => Challenges
                )

            [71] => Array
                (
                    [kind] => bookmark
                    [name] => ChallengesTitle
                )

            [72] => Array
                (
                    [kind] => section
                    [name] => ChallengesContent
                )

            [73] => Array
                (
                    [kind] => section
                    [name] => ManagementImplication
                )

            [74] => Array
                (
                    [kind] => bookmark
                    [name] => ManagementImplicationTitle
                )

            [75] => Array
                (
                    [kind] => bookmark
                    [name] => ManagementImplicationText
                )

            [76] => Array
                (
                    [kind] => section
                    [name] => RecommendationsOutlook
                )

            [77] => Array
                (
                    [kind] => bookmark
                    [name] => RecommendationsOutlookTitle
                )

            [78] => Array
                (
                    [kind] => section
                    [name] => Recommendations
                )

            [79] => Array
                (
                    [kind] => bookmark
                    [name] => RecommendationsTitle
                )

            [80] => Array
                (
                    [kind] => section
                    [name] => RecommendationsContent
                )

            [81] => Array
                (
                    [kind] => section
                    [name] => ReportOutlook
                )

            [82] => Array
                (
                    [kind] => bookmark
                    [name] => ReportOutlookTitle
                )

            [83] => Array
                (
                    [kind] => bookmark
                    [name] => ReportOutlookText1
                )

            [84] => Array
                (
                    [kind] => bookmark
                    [name] => ReportOutlookText2
                )

            [85] => Array
                (
                    [kind] => bookmark
                    [name] => ReportClosing
                )

        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

/**
 * S03-A — Structured Professional Report
 *
 * This showcase updates a substantial report authored and designed in
 * LibreOffice Writer. PHP addresses the template's semantic targets; it does
 * not rebuild the report layout, lists, tables, or image geometry.
 *
 * Writer owns the document structure and formatting. The application supplies
 * User Field values, bounded bookmark text, table data, and one prepared image
 * resource. Table population uses the existing native Writer tables, while
 * the report's authored list Sections remain Writer-owned content.
 */

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\OdtTemplate;

$template = new OdtTemplate(__DIR__ . '/templates/template_S03_structured_professional_report.odt');

$userFields = [
    'organization_name' => 'Meridian Learning Foundation',
    'organization_short_name' => 'MERIDIAN',
    'report_title' => 'Community Pathways Performance Report',
    'report_year' => '2027',
    'program_name' => 'Bridgeway Skills Programme',
    'report_period' => 'January–December 2027',
    'report_status' => 'Final Report',
    'report_date' => '31 January 2028',
];
foreach ($userFields as $name => $value) {
    $template->setUserField($name, $value);
}

// These bounded text values are exposed by Writer-authored bookmark ranges.
$bookmarks = [
    'ExecutiveSummaryTitle' => 'Executive Summary',
    'ExecutiveSummaryIntro' => 'Bridgeway Skills supported 272 participants across three delivery locations during 2027, combining coaching, practical learning, and employer contact.',
    'ExecutiveSummaryApproach' => 'The updated report retains the authored professional structure while changing the report identity, results, activities, and management reading through native targets.',
    'ExecutiveSummaryResults' => 'Completion reached 89%, and 71% of participants progressed into employment or further training.',
    'KpiParticipantsValue' => '272',
    'KpiParticipantsLabel' => 'Participants',
    'KpiCompletionRateValue' => '89%',
    'KpiCompletionRateLabel' => 'Completion rate',
    'KpiPositiveOutcomesValue' => '71%',
    'KpiPositiveOutcomesLabel' => 'Positive outcomes',
    'KpiEmployerPartnersValue' => '47',
    'KpiEmployerPartnersLabel' => 'Employer partners',
    'ExecutiveSummaryKeyFinding' => 'KEY FINDING',
    'ExecutiveSummaryKeyFindingText' => 'Participants receiving coordinated coaching and employer-facing support showed the strongest progression.',
    'ManagementReadingTitle' => 'Management reading',
    'ManagementReadingText' => 'The 2027 results point to the value of linking practical learning with earlier employer contact and targeted confidence-building support.',
    'ProgramOverviewTitle' => 'Program Overview & Delivery',
    'ProgramOverviewIntro' => 'Bridgeway Skills combined individual guidance, practical workshops, and employer-facing activity to support progression across three fictional delivery locations.',
    'ReportingPeriodLabel' => 'Reporting period',
    'ReportingPeriodValue' => 'January–December 2027',
    'LocationsLabel' => 'Locations',
    'LocationsValue' => '3 locations',
    'DeliveryTeamLabel' => 'Delivery team',
    'DeliveryTeamValue' => '14 people',
    'EmployerPartnersLabel' => 'Employer partners',
    'EmployerPartnersValue' => '47 partners',
    'ProgramObjectivesTitle' => 'Program Objectives',
    'ActivitiesDeliveredTitle' => 'Activities Delivered',
    'DeliveryObservationTitle' => 'Delivery observation',
    'DeliveryObservationText1' => 'The expanded activity mix increased opportunities for participants to practise skills in different settings.',
    'DeliveryObservationText2' => 'Employer-facing activity remained concentrated around practical preparation and progression conversations.',
    'PerformanceOutcomesTitle' => 'Performance & Outcomes',
    'PerformanceAgainstTargetsTitle' => 'Performance against targets',
    'ParticipantOutcomesTitle' => 'Participant outcomes',
    'ParticipantOutcomesCaption' => 'Participant outcomes · 2027',
    'ParticipantOutcomesText' => 'Employment and further-training outcomes together accounted for 71% of recorded progression, with active progression representing a further 17%.',
    'FindingsAnalysisTitle' => 'Findings & Analysis',
    'FindingIntegratedSupportTitle' => '1. Integrated support produced stronger outcomes',
    'FindingIntegratedSupportText1' => 'Participants who received both individual coaching and employer-facing support showed the clearest progression through the program.',
    'FindingIntegratedSupportText2' => 'This pattern supports maintaining a connected delivery journey rather than treating activities as separate interventions.',
    'FindingDigitalConfidenceTitle' => '2. Digital confidence remains uneven',
    'FindingDigitalConfidenceText1' => 'Digital practice improved confidence for many participants, but starting points and preferred learning routes remained varied.',
    'FindingDigitalConfidenceText2' => 'Targeted practice should therefore remain part of ordinary delivery instead of being reserved for a single specialist session.',
    'ProgramInsightTitle' => 'PROGRAM INSIGHT',
    'ProgramInsightText' => 'employers became involved before the formal recruitment stage rather than only receiving completed candidate profiles.',
    'ChallengesTitle' => 'Challenges',
    'ManagementImplicationTitle' => 'Management implication',
    'ManagementImplicationText' => 'The response should remain selective: protect flexible attendance support, build digital practice into ordinary delivery, and focus employer development where progression opportunities are most constrained.',
    'RecommendationsOutlookTitle' => 'Recommendations & Outlook',
    'RecommendationsTitle' => 'Recommendations',
    'ReportOutlookTitle' => 'Outlook 2028',
    'ReportOutlookText1' => 'The next reporting period should consolidate the stronger activity model while extending purposeful employer contact and practical digital support.',
    'ReportOutlookText2' => 'A clearer follow-up view will help Meridian distinguish immediate completion from sustained progression into employment or further training.',
    'ReportClosing' => 'Demonstration report · fictional organization and data',
];
foreach ($bookmarks as $name => $value) {
    $template->bookmark($name)->replaceText($value);
}

// Writer owns the header, columns, native header row, and row formatting.
$template->table('ActivitiesDeliveredTable')->populate([
    ['Mentoring clinics', '980', '210'],
    ['Digital practice labs', '72', '198'],
    ['Employer roundtables', '12', '110'],
    ['Sector visits', '9', '96'],
    ['Peer learning circles', '28', '144'],
    ['Progression workshops', '34', '173'],
]);

// Four results replace the five authored ordinary source rows, preserving the
// native header and the Writer-authored table/cell/paragraph formatting.
$template->table('PerformanceAgainstTargetsTable')->populate([
    ['Participants enrolled', '260', '272', 'Achieved'],
    ['Program completion', '82%', '89%', 'Above target'],
    ['Employment/training outcome', '65%', '71%', 'Above target'],
    ['Employer partners', '40', '47', 'Above target'],
]);

$template->replaceImageByName(
    'ParticipantOutcomesImage',
    __DIR__ . '/assets/s03-participant-outcomes-2027.png',
    [
        'width' => '15.799cm',
        'height' => '10.007cm',
    ]
);

$template->setMeta([
    'title' => 'Community Pathways Performance Report 2027',
    'subject' => 'S03 Writer-authored structured professional report',
    'description' => 'Fictional S03 showcase updated through native document-semantic targets.',
    'language' => 'en-US',
]);

$outputPath = __DIR__ . '/output/output_S03_structured_professional_report.odt';
$template->save($outputPath);

echo "Saved S03 structured professional report to {$outputPath}.\n";
Showcases S03-B

Structured Professional Report · Alternative Composition

Demonstrates a substantially different report composition from the same Writer-authored S03 template through native targets, table population, and Section cloning.

Canonical sample ✓ Template available 8 dependencies · 86 native objects
Template contract
Array
(
    [dependencies] => Array
        (
            [0] => organization_name
            [1] => organization_short_name
            [2] => report_title
            [3] => report_year
            [4] => program_name
            [5] => report_period
            [6] => report_status
            [7] => report_date
        )

    [native_objects] => Array
        (
            [0] => Array
                (
                    [kind] => section
                    [name] => ReportHeadline
                )

            [1] => Array
                (
                    [kind] => table
                    [name] => Table_1
                )

            [2] => Array
                (
                    [kind] => frame
                    [name] => ReportTitleImage
                )

            [3] => Array
                (
                    [kind] => section
                    [name] => ReportCoverClosing
                )

            [4] => Array
                (
                    [kind] => section
                    [name] => ExecutiveSummary
                )

            [5] => Array
                (
                    [kind] => section
                    [name] => ExecutiveSummaryContent
                )

            [6] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryTitle
                )

            [7] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryIntro
                )

            [8] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryApproach
                )

            [9] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryResults
                )

            [10] => Array
                (
                    [kind] => table
                    [name] => ExecutiveSummaryKpis
                )

            [11] => Array
                (
                    [kind] => bookmark
                    [name] => KpiParticipantsValue
                )

            [12] => Array
                (
                    [kind] => bookmark
                    [name] => KpiParticipantsLabel
                )

            [13] => Array
                (
                    [kind] => bookmark
                    [name] => KpiCompletionRateValue
                )

            [14] => Array
                (
                    [kind] => bookmark
                    [name] => KpiCompletionRateLabel
                )

            [15] => Array
                (
                    [kind] => bookmark
                    [name] => KpiPositiveOutcomesValue
                )

            [16] => Array
                (
                    [kind] => bookmark
                    [name] => KpiPositiveOutcomesLabel
                )

            [17] => Array
                (
                    [kind] => bookmark
                    [name] => KpiEmployerPartnersValue
                )

            [18] => Array
                (
                    [kind] => bookmark
                    [name] => KpiEmployerPartnersLabel
                )

            [19] => Array
                (
                    [kind] => frame
                    [name] => ExecutiveSummaryKeyFinding
                )

            [20] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryKeyFinding
                )

            [21] => Array
                (
                    [kind] => bookmark
                    [name] => ExecutiveSummaryKeyFindingText
                )

            [22] => Array
                (
                    [kind] => section
                    [name] => ReportManagementReading
                )

            [23] => Array
                (
                    [kind] => bookmark
                    [name] => ManagementReadingTitle
                )

            [24] => Array
                (
                    [kind] => bookmark
                    [name] => ManagementReadingText
                )

            [25] => Array
                (
                    [kind] => section
                    [name] => ProgramOverview
                )

            [26] => Array
                (
                    [kind] => section
                    [name] => ProgramOverviewIntroduction
                )

            [27] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramOverviewTitle
                )

            [28] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramOverviewIntro
                )

            [29] => Array
                (
                    [kind] => table
                    [name] => Table_3
                )

            [30] => Array
                (
                    [kind] => bookmark
                    [name] => ReportingPeriodLabel
                )

            [31] => Array
                (
                    [kind] => bookmark
                    [name] => ReportingPeriodValue
                )

            [32] => Array
                (
                    [kind] => bookmark
                    [name] => LocationsLabel
                )

            [33] => Array
                (
                    [kind] => bookmark
                    [name] => LocationsValue
                )

            [34] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryTeamLabel
                )

            [35] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryTeamValue
                )

            [36] => Array
                (
                    [kind] => bookmark
                    [name] => EmployerPartnersLabel
                )

            [37] => Array
                (
                    [kind] => bookmark
                    [name] => EmployerPartnersValue
                )

            [38] => Array
                (
                    [kind] => section
                    [name] => ProgramObjectives
                )

            [39] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramObjectivesTitle
                )

            [40] => Array
                (
                    [kind] => section
                    [name] => ProgramObjectivesContent
                )

            [41] => Array
                (
                    [kind] => bookmark
                    [name] => ActivitiesDeliveredTitle
                )

            [42] => Array
                (
                    [kind] => table
                    [name] => ActivitiesDeliveredTable
                )

            [43] => Array
                (
                    [kind] => section
                    [name] => DeliveryObservation
                )

            [44] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryObservationTitle
                )

            [45] => Array
                (
                    [kind] => section
                    [name] => DeliveryObservationText
                )

            [46] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryObservationText1
                )

            [47] => Array
                (
                    [kind] => bookmark
                    [name] => DeliveryObservationText2
                )

            [48] => Array
                (
                    [kind] => section
                    [name] => PerformanceOutcomes
                )

            [49] => Array
                (
                    [kind] => bookmark
                    [name] => PerformanceOutcomesTitle
                )

            [50] => Array
                (
                    [kind] => bookmark
                    [name] => PerformanceAgainstTargetsTitle
                )

            [51] => Array
                (
                    [kind] => table
                    [name] => PerformanceAgainstTargetsTable
                )

            [52] => Array
                (
                    [kind] => section
                    [name] => ParticipantOutcomes
                )

            [53] => Array
                (
                    [kind] => bookmark
                    [name] => ParticipantOutcomesTitle
                )

            [54] => Array
                (
                    [kind] => frame
                    [name] => ParticipantOutcomesImage
                )

            [55] => Array
                (
                    [kind] => bookmark
                    [name] => ParticipantOutcomesCaption
                )

            [56] => Array
                (
                    [kind] => bookmark
                    [name] => ParticipantOutcomesText
                )

            [57] => Array
                (
                    [kind] => section
                    [name] => FindingsAnalysis
                )

            [58] => Array
                (
                    [kind] => bookmark
                    [name] => FindingsAnalysisTitle
                )

            [59] => Array
                (
                    [kind] => section
                    [name] => FindingIntegratedSupport
                )

            [60] => Array
                (
                    [kind] => bookmark
                    [name] => FindingIntegratedSupportTitle
                )

            [61] => Array
                (
                    [kind] => bookmark
                    [name] => FindingIntegratedSupportText1
                )

            [62] => Array
                (
                    [kind] => bookmark
                    [name] => FindingIntegratedSupportText2
                )

            [63] => Array
                (
                    [kind] => section
                    [name] => FindingDigitalConfidence
                )

            [64] => Array
                (
                    [kind] => bookmark
                    [name] => FindingDigitalConfidenceTitle
                )

            [65] => Array
                (
                    [kind] => bookmark
                    [name] => FindingDigitalConfidenceText1
                )

            [66] => Array
                (
                    [kind] => bookmark
                    [name] => FindingDigitalConfidenceText2
                )

            [67] => Array
                (
                    [kind] => frame
                    [name] => ProgramInsight
                )

            [68] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramInsightTitle
                )

            [69] => Array
                (
                    [kind] => bookmark
                    [name] => ProgramInsightText
                )

            [70] => Array
                (
                    [kind] => section
                    [name] => Challenges
                )

            [71] => Array
                (
                    [kind] => bookmark
                    [name] => ChallengesTitle
                )

            [72] => Array
                (
                    [kind] => section
                    [name] => ChallengesContent
                )

            [73] => Array
                (
                    [kind] => section
                    [name] => ManagementImplication
                )

            [74] => Array
                (
                    [kind] => bookmark
                    [name] => ManagementImplicationTitle
                )

            [75] => Array
                (
                    [kind] => bookmark
                    [name] => ManagementImplicationText
                )

            [76] => Array
                (
                    [kind] => section
                    [name] => RecommendationsOutlook
                )

            [77] => Array
                (
                    [kind] => bookmark
                    [name] => RecommendationsOutlookTitle
                )

            [78] => Array
                (
                    [kind] => section
                    [name] => Recommendations
                )

            [79] => Array
                (
                    [kind] => bookmark
                    [name] => RecommendationsTitle
                )

            [80] => Array
                (
                    [kind] => section
                    [name] => RecommendationsContent
                )

            [81] => Array
                (
                    [kind] => section
                    [name] => ReportOutlook
                )

            [82] => Array
                (
                    [kind] => bookmark
                    [name] => ReportOutlookTitle
                )

            [83] => Array
                (
                    [kind] => bookmark
                    [name] => ReportOutlookText1
                )

            [84] => Array
                (
                    [kind] => bookmark
                    [name] => ReportOutlookText2
                )

            [85] => Array
                (
                    [kind] => bookmark
                    [name] => ReportClosing
                )

        )

    [controls] => Array
        (
        )

)
View PHP source
<?php

declare(strict_types=1);

/**
 * S03-B1 — Different Report Composition
 *
 * This showcase reuses the canonical LibreOffice Writer-authored S03 report
 * template for a strategic review with a different identity and composition.
 * Writer continues to own the page layout, styles, tables, and image frames;
 * PHP changes only explicitly addressed native targets.
 *
 * The example also clones one authored Section and replaces the content of
 * selected semantic Sections. It deliberately does not address generated
 * bookmark names inside the clone; application-owned clone content is supplied
 * through replaceContent().
 */

require_once __DIR__ . '/bootstrap.php';

use OdtTemplateEngine\Elements\ListElement;
use OdtTemplateEngine\Elements\Paragraph;
use OdtTemplateEngine\Elements\RichText;
use OdtTemplateEngine\OdtTemplate;

function strategicParagraph(string $text, string $style): Paragraph
{
    return (new Paragraph($style))->addText($text);
}

/** @param list<string> $items */
function strategicList(array $items, string $listStyle, string $paragraphStyle): RichText
{
    $content = new RichText();
    $list = new ListElement('bullet', $listStyle);
    foreach ($items as $item) {
        $list->addItem(strategicParagraph($item, $paragraphStyle));
    }

    return $content->addElement($list);
}

$template = new OdtTemplate(__DIR__ . '/templates/template_S03_structured_professional_report.odt');

$userFields = [
    'organization_name' => 'Northbridge Community Trust',
    'organization_short_name' => 'NORTHBRIDGE',
    'report_title' => 'Strategic Review',
    'report_year' => '2028',
    'program_name' => 'ForwardWorks Initiative',
    'report_period' => 'January–December 2028',
    'report_status' => 'Strategic Review',
    'report_date' => '31 January 2029',
];
foreach ($userFields as $name => $value) {
    $template->setUserField($name, $value);
}

// These values address only bookmark ranges already authored in the template.
$bookmarks = [
    'ExecutiveSummaryTitle' => 'Strategic Summary',
    'ExecutiveSummaryIntro' => 'Northbridge Community Trust reviewed the first year of the ForwardWorks Initiative across three fictional delivery locations during 2028, with a focus on local capacity, practical progression, and durable partnerships.',
    'ExecutiveSummaryApproach' => 'The strategic review keeps the Writer-authored report shell while shifting emphasis from annual performance reporting toward the conditions needed for the next phase of delivery.',
    'ExecutiveSummaryResults' => 'Completion reached 91%, while 74% of participants moved into employment, training, or an active progression route.',
    'KpiParticipantsValue' => '286',
    'KpiParticipantsLabel' => 'Participants',
    'KpiCompletionRateValue' => '91%',
    'KpiCompletionRateLabel' => 'Completion rate',
    'KpiPositiveOutcomesValue' => '74%',
    'KpiPositiveOutcomesLabel' => 'Positive outcomes',
    'KpiEmployerPartnersValue' => '52',
    'KpiEmployerPartnersLabel' => 'Partner organisations',
    'ExecutiveSummaryKeyFinding' => 'STRATEGIC PRIORITY',
    'ExecutiveSummaryKeyFindingText' => 'The next phase should invest in local delivery capacity before expanding the program footprint.',
    'ManagementReadingTitle' => 'Management reading',
    'ManagementReadingText' => 'The review points to a transition from proving the model to strengthening the systems, partnerships, and specialist capacity that can sustain it.',
    'ProgramOverviewTitle' => 'Program Overview & Delivery',
    'ProgramOverviewIntro' => 'ForwardWorks combined local partnership development, practical skills activity, and targeted progression support to build a stronger route through the fictional program.',
    'ReportingPeriodLabel' => 'Review period',
    'ReportingPeriodValue' => 'January–December 2028',
    'LocationsLabel' => 'Delivery footprint',
    'LocationsValue' => '3 locations',
    'DeliveryTeamLabel' => 'Core delivery team',
    'DeliveryTeamValue' => '16 people',
    'EmployerPartnersLabel' => 'Partner organisations',
    'EmployerPartnersValue' => '52 partners',
    'ProgramObjectivesTitle' => 'Strategic Objectives',
    'ActivitiesDeliveredTitle' => 'Delivery Portfolio',
    'DeliveryObservationTitle' => 'Delivery observation',
    'DeliveryObservationText1' => 'The portfolio is broad enough to support different starting points, but the next phase will require clearer local ownership of specialist activity.',
    'DeliveryObservationText2' => 'Partnership activity is becoming a platform for coordinated planning rather than a series of isolated events.',
    'PerformanceOutcomesTitle' => 'Performance & Outcomes',
    'PerformanceAgainstTargetsTitle' => 'Progress against strategic measures',
    'ParticipantOutcomesTitle' => 'Progression routes',
    'ParticipantOutcomesCaption' => 'ForwardWorks progression routes · 2028',
    'ParticipantOutcomesText' => 'Employment and further training remained the strongest routes forward, while active progression indicates a substantial group still building readiness for their next step.',
    'FindingsAnalysisTitle' => 'Findings & Analysis',
    'FindingIntegratedSupportTitle' => '1. Local capacity is now the critical enabler',
    'FindingIntegratedSupportText1' => 'The first year established demand for a connected support model, but delivery quality increasingly depends on the strength of local coordination around it.',
    'FindingIntegratedSupportText2' => 'The strategic question is therefore not whether the model can attract participation, but how consistently each location can turn shared practice into durable progression.',
    'FindingDigitalConfidenceTitle' => '2. Partnership depth matters more than partner count',
    'FindingDigitalConfidenceText1' => 'The review found that the most useful relationships were those with a clear role in planning, preparation, or progression rather than those measured only by attendance at events.',
    'FindingDigitalConfidenceText2' => 'A smaller group of active partners can provide more strategic value when expectations, referral routes, and feedback loops are explicit.',
    'ProgramInsightTitle' => 'PROGRAM INSIGHT',
    'ProgramInsightText' => 'employers helped local delivery teams build confidence before the model extended into additional communities.',
    'ChallengesTitle' => 'Strategic constraints',
    'ManagementImplicationTitle' => 'Management implication',
    'ManagementImplicationText' => 'Protect the quality of local delivery first: clarify ownership, invest in specialist capacity, and use partnership evidence to guide any future expansion.',
    'RecommendationsOutlookTitle' => 'Recommendations & Outlook',
    'RecommendationsTitle' => 'Priority actions',
    'ReportOutlookTitle' => 'Outlook 2029',
    'ReportOutlookText1' => 'The next review period should consolidate the operating model in existing locations while making the conditions for careful expansion more explicit.',
    'ReportOutlookText2' => 'Northbridge should use the coming year to build a stronger evidence base for local capacity, partnership depth, and sustained progression.',
    'ReportClosing' => 'Strategic review · fictional organization and data',
];
foreach ($bookmarks as $name => $value) {
    $template->bookmark($name)->replaceText($value);
}

// A native Writer Section becomes a second, visible strategic finding. The
// replacement owns only this cloned Section's structured content.
$capacityFinding = $template->section('FindingDigitalConfidence')->clone();
$capacityFinding->replaceContent(
    (new RichText())
        ->addParagraph(
            (new Paragraph('B02Subheading028012'))
                ->addText('3. Local delivery capacity needs reinforcement', ['bold' => true])
        )
        ->addParagraph(strategicParagraph('Demand increased most strongly in selected delivery areas, creating pressure on specialist workshop capacity and local coordination.', 'B02Body030'))
        ->addParagraph(strategicParagraph('Priority for 2029: strengthen local delivery partnerships and create additional specialist workshop capacity before extending the geographic footprint.', 'B02Body034'))
);

// These existing native list Sections are deliberately transferred to
// application-owned structured content while their Section containers remain
// Writer-authored and addressable.
$template->section('ProgramObjectivesContent')->replaceContent(
    strategicList([
        'Build stronger local delivery capacity and ownership.',
        'Connect practical learning to credible progression routes.',
        'Develop deeper partnerships with community and employer networks.',
        'Use evidence to guide responsible future expansion.',
    ], 'L1', 'P2')
);

$template->section('ChallengesContent')->replaceContent(
    strategicList([
        'Specialist workshop capacity is uneven across locations.',
        'Partnership roles are not yet equally clear in every area.',
        'Expansion could dilute quality if local ownership is not strengthened first.',
    ], 'L2', 'P3')
);

$template->section('RecommendationsContent')->replaceContent(
    strategicList([
        'Strengthen local delivery partnerships before expanding the footprint.',
        'Create a specialist capacity plan for the areas under greatest pressure.',
        'Introduce a shared partnership review so roles and referral routes stay clear.',
        'Build a stronger evidence base for sustained progression and future investment.',
    ], 'L3', 'P1')
);

$template->table('ActivitiesDeliveredTable')->populate([
    ['Local planning sessions', '36', '284'],
    ['Specialist workshops', '68', '232'],
    ['Community partner forums', '21', '178'],
    ['Progression clinics', '54', '219'],
    ['Employer pathway meetings', '17', '96'],
]);

$template->table('PerformanceAgainstTargetsTable')->populate([
    ['Participants enrolled', '270', '286', 'Achieved'],
    ['Program completion', '85%', '91%', 'Above target'],
    ['Employment/training outcome', '68%', '74%', 'Above target'],
    ['Partner organisations', '45', '52', 'Above target'],
]);

$template->replaceImageByName(
    'ReportTitleImage',
    __DIR__ . '/assets/s03-strategic-review-cover-2028.png',
    [
        // replaceImageByName() keeps legacy defaults when one dimension is
        // omitted, so both dimensions are explicit for this cover asset.
        'width' => '15cm',
        'height' => '8.452cm',
    ]
);
$template->replaceImageByName(
    'ParticipantOutcomesImage',
    __DIR__ . '/assets/s03-strategic-outcomes-2028.png',
    [
        'width' => '15.799cm',
        'height' => '10.007cm',
    ]
);

$template->setMeta([
    'title' => 'Strategic Review 2028',
    'subject' => 'S03-B Writer-authored structured professional report',
    'description' => 'Fictional strategic review composed through native document-semantic targets.',
    'language' => 'en-US',
]);

$outputPath = __DIR__ . '/output/output_S03_structured_professional_report_b.odt';
$template->save($outputPath);

echo "Saved S03-B structured professional report to {$outputPath}.\n";
No matching samples.
Try another search term or select a different feature category.
Real-world usage

Used for real editable document generation.

ODT Template Engine is developed alongside production document workflows rather than only as a format experiment.

Open source · MIT licensed

Help keep ODT Template Engine moving.

ODT Template Engine is free, MIT licensed, and independently developed. If it saves you time or becomes useful in your own project, there are several voluntary ways to support continued development.

★ Star on GitHub
PayPal Support via PayPal →
⚡ Bitcoin Lightning Support via Lightning →