Initial commit

This commit is contained in:
2026-08-30 22:02:02 +00:00
commit b6bd2277f5
2334 changed files with 646393 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
# Ref: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style end of lines and a blank line at the end of the file
[*]
indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.php]
indent_style = space
indent_size = 4
[*.{js,json,scss,css,yml,vue}]
indent_style = space
indent_size = 2
@@ -0,0 +1,22 @@
# Normalize text sources to LF in the repository and on checkout everywhere.
# Critical for a byte-level library: keeps source and fixtures byte-identical
# across Windows/macOS/Linux checkouts regardless of core.autocrlf.
*.php text eol=lf
*.md text eol=lf
*.xml text eol=lf
*.xml.dist text eol=lf
*.json text eol=lf
*.toml text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.txt text eol=lf
Makefile text eol=lf
# Byte-exact fixtures must never be transformed (CRLF/auto-detection off).
test/**/*.bin -text
*.png binary
*.jpg binary
*.gif binary
*.pdf binary
*.ttf binary
*.otf binary
@@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. ...
**Expected behavior**
A clear and concise description of what you expected to happen.
**Logs**
If applicable, copy the relevant logs to help explain your problem.
**Environment:**
- OS:
- PHP version:
- Version:
**Additional context**
Add any other context about the problem here.
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
@@ -0,0 +1,26 @@
# Description
Please include a summary of the change and include relevant motivation and context.
...
## Checklist:
- [ ] The `make buildall` command has been run successfully without any error or warning.
- [ ] Any new code line is covered by unit tests and the coverage has not dropped.
- [ ] Any new code follows the style guidelines of this project.
- [ ] The code changes have been self-reviewed.
- [ ] Corresponding changes to the documentation have been made.
> The maintainer bumps the `VERSION` file at release time. Do not change it in your PR.
## Type of change:
- [ ] Minor non-breaking change (e.g., bug fix, dependencies updates).
- [ ] New feature (non-breaking change which adds functionality).
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected).
- [ ] Automation.
- [ ] Documentation.
- [ ] Example.
- [ ] Testing.
@@ -0,0 +1,94 @@
name: check
permissions:
contents: read
concurrency:
group: check-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches:
- 'main'
pull_request:
types: [opened, synchronize, reopened]
branches:
- main
workflow_dispatch:
jobs:
test-php:
name: Test on php ${{ matrix.php-version }}
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
php-version: ["8.2", "8.3", "8.4", "8.5"]
steps:
- uses: actions/checkout@v7
#- name: Install pdfinfo
# run: sudo apt-get install -y poppler-utils
- name: Use php ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: pcov
extensions: bcmath, curl, date, gd, hash, imagick, json, mbstring, openssl, pcre, zlib
ini-values: display_errors=on, error_reporting=-1, zend.assertions=1
- name: List php modules
run: php -m
- name: List php modules using "no php ini" mode
run: php -m -n
- name: Cache module
uses: actions/cache@v6
with:
path: ~/.composer/cache/
key: ${{ runner.os }}-php-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-${{ matrix.php-version }}-composer-
- name: Install dependencies
env:
GH_TOKEN: ${{ github.token }}
run: make deps
- name: Install qpdf
if: matrix.php-version == '8.4'
run: sudo apt-get update && sudo apt-get install -y qpdf
- name: Run preflight
if: matrix.php-version == '8.4'
run: make preflight
- name: Run all tests
run: make qa
#- name: Generate CSS renderability reports
# if: matrix.php-version == '8.4'
# run: |
# php resources/css/renderability_score.php \
# --corpus=test/fixtures/html/real_pages/corpus.json \
# --json=target/report/renderability-score.json \
# --markdown=target/report/renderability-score.md
# php resources/css/renderability_trend.php \
# --score=target/report/renderability-score.json \
# --history=target/report/renderability-trend.json \
# --markdown=target/report/renderability-trend.md \
# --sha=${{ github.sha }} \
# --ref=${{ github.ref_name }} \
# --run-id=${{ github.run_id }}
# cat target/report/renderability-score.md >> "$GITHUB_STEP_SUMMARY"
# cat target/report/renderability-trend.md >> "$GITHUB_STEP_SUMMARY"
- name: Upload CSS renderability reports
if: matrix.php-version == '8.4'
uses: actions/upload-artifact@v4
with:
name: css-renderability-reports-${{ github.run_id }}
path: |
target/report/renderability-score.json
target/report/renderability-score.md
target/report/renderability-trend.json
target/report/renderability-trend.md
- name: Send coverage
if: matrix.php-version == '8.4'
uses: codecov/codecov-action@v7
with:
flags: php-${{ matrix.php-version }}-ubuntu-latest
name: php-${{ matrix.php-version }}-ubuntu-latest
+23
View File
@@ -0,0 +1,23 @@
.phpdoc
.phpunit.cache
.phpunit.result.cache
.vscode
**/._*
**/.#*
**/.DS_Store
**/.idea
**/.vagrant
**/*.bak
**/*.tmp
**/auth.json
**/nbproject
**/temp.php
**/test.php
composer.lock
ecs.php
phpunit.xml
PLAN_*
rector.php
target
TMP_*
vendor
+1
View File
@@ -0,0 +1 @@
* @nicolaasuni
+128
View File
@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[info@tecnick.com](mailto:info@tecnick.com).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+200
View File
@@ -0,0 +1,200 @@
# Contributing to tc-lib-pdf
Thank you for your interest in contributing to **tc-lib-pdf** — the modern evolution of [TCPDF](https://tcpdf.org). Contributions of all kinds are welcome: bug reports, bug fixes, documentation improvements, new features, and refactors.
Please take a moment to read this guide before opening an issue or pull request.
> **Pull requests are restricted to project collaborators.** If you are not a collaborator, please [open an issue](https://github.com/tecnickcom/tc-lib-pdf/issues) instead of a pull request, describing the bug or feature in detail. A maintainer will review it and take it from there.
---
## Table of Contents
- [Code of Conduct](#code-of-conduct)
- [Security Vulnerabilities](#security-vulnerabilities)
- [Getting Started](#getting-started)
- [Reporting a Bug](#reporting-a-bug)
- [Submitting a Bug Fix](#submitting-a-bug-fix)
- [Proposing a New Feature](#proposing-a-new-feature)
- [Development Workflow](#development-workflow)
- [Coding Standards](#coding-standards)
- [Testing](#testing)
- [Pull Request Guidelines](#pull-request-guidelines)
- [Commit Message Guidelines](#commit-message-guidelines)
---
## Code of Conduct
This project follows the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating you agree to abide by its terms. Please report unacceptable behaviour to [info@tecnick.com](mailto:info@tecnick.com).
---
## Security Vulnerabilities
**Do not open a public GitHub issue for security vulnerabilities.**
Please follow the [Security Policy](SECURITY.md) and report them privately.
---
## Getting Started
### Requirements
- PHP **≥ 8.2**
- [Composer](https://getcomposer.org/) v2
- `make`, `git`
- Optional: `rpmbuild` (RPM packaging), `dpkg-buildpackage` (DEB packaging)
### Local setup
```bash
git clone https://github.com/tecnickcom/tc-lib-pdf.git
cd tc-lib-pdf
make buildall
```
To verify everything is working after a change:
```bash
make qa
```
This runs linting, static analysis, and the full unit-test suite with coverage.
---
## Reporting a Bug
Before opening an issue:
1. **Check the [Security Policy](SECURITY.md)** — if the bug is a security vulnerability, do not file a public issue.
2. **Search [existing issues](https://github.com/tecnickcom/tc-lib-pdf/issues)** to avoid duplicates.
If no existing issue matches, [open a new one](https://github.com/tecnickcom/tc-lib-pdf/issues/new) and include:
- A **clear title and description** of the problem.
- The **library version** (`composer show tecnickcom/tc-lib-pdf`) and PHP version.
- A **minimal, self-contained reproduction** — a short PHP script or a failing PHPUnit test case is ideal.
- **Expected vs. actual behaviour** — what you expected to happen and what actually happened.
- Any relevant **stack trace or error output**.
The more precise and reproducible the report, the faster it can be triaged and fixed.
---
## Submitting a Bug Fix
> Only project collaborators can open pull requests. If you are not a collaborator, please [open an issue](https://github.com/tecnickcom/tc-lib-pdf/issues/new) describing the bug in detail (see [Reporting a Bug](#reporting-a-bug)). A maintainer will take it from there.
Collaborators preparing a fix:
1. Create a branch from `main`:
```bash
git checkout -b fix/short-description-of-bug
```
2. Make your changes, following the [Coding Standards](#coding-standards) below.
3. Add or update unit tests to cover the changes.
4. Run the full quality-assurance suite locally and ensure it passes:
```bash
make qa
```
5. Commit your changes (see [Commit Message Guidelines](#commit-message-guidelines)).
6. Open a pull request against `main` and fill in the PR template, describing the problem and your solution and referencing the related issue number (e.g. `Fixes #123`).
---
## Proposing a New Feature
Before writing any code:
1. **Open a Feature Request** on [GitHub Issues](https://github.com/tecnickcom/tc-lib-pdf/issues/new) describing the use case and proposed API.
2. Wait for feedback from the maintainer. This avoids investing time in a direction that may not be accepted.
Once the feature is agreed upon, a collaborator will implement it following the same branch → code → test → pull request workflow as for bug fixes, using a branch named `feature/short-description`.
---
## Development Workflow
The `Makefile` exposes all common development tasks:
| Command | Description |
|---------|-------------|
| `make qa` | Run linting, static analysis, tests, and reports |
| `make test` | Run PHPUnit with code coverage |
| `make lint` | Check coding standards |
| `make format` | Auto-format the code |
| `make buildall` | Install dependencies, fix style, run QA, and build packages |
| `make clean` | Remove `vendor/` and `target/` directories |
| `make server` | Start the built-in PHP development server for the examples |
Run `make help` to see the full list of available targets.
---
## Coding Standards
- The codebase follows **PSR-12** for formatting.
- Run `make format` to auto-format the code.
- Run `make lint` to catch remaining issues.
- All source files live under `src/`, all tests under `test/`.
- Use strict types and explicit visibility on all class members.
- Avoid introducing new external dependencies without prior discussion.
---
## Testing
Tests are written with [PHPUnit](https://phpunit.de/) and live in `test/`.
```bash
# Run the full test suite with coverage
make test
# Run a specific test file
XDEBUG_MODE=coverage ./vendor/bin/phpunit test/HTMLTest.php
```
Requirements for contributions:
- Every bug fix must be accompanied by a regression test that fails before the fix and passes after.
- Every new feature must be accompanied by tests that cover both the happy path and edge cases.
Coverage reports are generated in `target/coverage/`.
---
## Pull Request Guidelines
> Opening pull requests is restricted to project collaborators. If you are an external contributor, please [open an issue](https://github.com/tecnickcom/tc-lib-pdf/issues/new) describing the problem or feature in detail instead.
- **Sign the Contributor License Agreement (CLA).** On your first pull request the CLA Assistant bot will comment with a link to sign; the PR cannot be merged until the CLA is signed.
- Target the `main` branch.
- Keep PRs focused — one fix or feature per PR.
- Ensure `make qa` passes locally before opening the PR.
- Do not bump the version number in your PR; that is handled by the maintainer at release time.
- Be responsive to review feedback; stale PRs may be closed after an extended period of inactivity.
---
## Commit Message Guidelines
Use concise, imperative-mood commit messages:
```
fix: correct path traversal in font loader
feat: add support for CSS grid layout
test: add regression test for #123
docs: update CONTRIBUTING workflow
refactor: extract text measurement into helper
```
Prefix tags: `fix`, `feat`, `test`, `docs`, `refactor`, `chore`, `ci`.
Reference issues where relevant: `fix: correct X (closes #42)`.
---
## Questions?
If you have a question that is not covered here, feel free to open a [GitHub Discussion](https://github.com/tecnickcom/tc-lib-pdf/discussions) or contact the maintainer at [info@tecnick.com](mailto:info@tecnick.com).
+862
View File
@@ -0,0 +1,862 @@
**********************************************************************
* LICENSE
*
* SOFTWARE : tc-lib-pdf
* AUTHOR : Nicola Asuni <info@tecnick.com>
* COPYRIGHT : 2002-2026 Nicola Asuni - Tecnick.com LTD
**********************************************************************
This is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
**********************************************************************
**********************************************************************
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://www.fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
**********************************************************************
**********************************************************************
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://www.fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/philosophy/why-not-lgpl.html>.
**********************************************************************
**********************************************************************
+343
View File
@@ -0,0 +1,343 @@
# Makefile
#
# @since 2015-02-21
# @category Library
# @package Pdf
# @author Nicola Asuni <info@tecnick.com>
# @copyright 2015-2026 Nicola Asuni - Tecnick.com LTD
# @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
# @link https://github.com/tecnickcom/tc-lib-pdf
#
# This file is part of tc-lib-pdf software library.
# ----------------------------------------------------------------------------------------------------------------------
SHELL=/bin/bash
.SHELLFLAGS=-o pipefail -c
# Project owner
OWNER=tecnickcom
# Project vendor
VENDOR=${OWNER}
# Project name
PROJECT=tc-lib-pdf
# Project version
VERSION=$(shell cat VERSION)
# Project release number (packaging build number)
RELEASE=$(shell cat RELEASE)
# Debian revision cannot be zero; map 0 to 1 for Debian packaging only.
DEBRELEASE=$(if $(filter 0,$(RELEASE)),1,$(RELEASE))
# RPM release is conventionally >= 1; map 0 to 1 for RPM packaging only.
RPMRELEASE=$(if $(filter 0,$(RELEASE)),1,$(RELEASE))
# Name of RPM or DEB package
PKGNAME=php-${OWNER}-${PROJECT}
# Data dir
DATADIR=usr/share
# PHP home folder
PHPHOME=${DATADIR}/php/Com/Tecnick
# Default installation path for code
LIBPATH=${PHPHOME}/Pdf/
# Path for configuration files (etc/$(PKGNAME)/)
CONFIGPATH=etc/$(PKGNAME)/
# Default installation path for documentation
DOCPATH=${DATADIR}/doc/$(PKGNAME)/
# Installation path for the code
PATHINSTBIN=$(DESTDIR)/$(LIBPATH)
# Installation path for the configuration files
PATHINSTCFG=$(DESTDIR)/$(CONFIGPATH)
# Installation path for documentation
PATHINSTDOC=$(DESTDIR)/$(DOCPATH)
# Current directory
CURRENTDIR=$(CURDIR)/
# Target directory
TARGETDIR=target
# RPM Packaging path (where RPMs will be stored)
PATHRPMPKG=$(TARGETDIR)/RPM
# RPM local database path (avoid host rpmdb permission issues)
RPMDBPATH=$(PATHRPMPKG)/.rpmdb
# DEB Packaging path (where DEBs will be stored)
PATHDEBPKG=$(TARGETDIR)/DEB
# BZ2 Packaging path (where BZ2s will be stored)
PATHBZ2PKG=$(TARGETDIR)/BZ2
# sed argument for in-place substitutions
SEDINPLACE=-i
ifeq ($(shell uname -s),Darwin)
SEDINPLACE=-i ''
endif
# Default port number for the example server
PORT?=8971
# PHP binary
PHP=$(shell which php)
# Composer executable (disable APC to as a work-around of a bug)
COMPOSER=$(PHP) -d "apc.enable_cli=0" $(shell which composer)
# phpDocumentor executable file
PHPDOC=$(shell which phpDocumentor)
# veraPDF binary path (can be overridden, e.g. make verapdf-ua VERAPDF_BIN=/opt/verapdf/bin/verapdf)
VERAPDF_BIN?=$(shell command -v verapdf 2>/dev/null)
# Mago version
MAGOVERSION=1.43.0
# --- MAKE TARGETS ---
# Display general help about this command
.PHONY: help
help:
@echo ""
@echo "$(PROJECT) Makefile."
@echo "The following commands are available:"
@echo ""
@awk '/^## /{desc=substr($$0,4)} /^\.PHONY:/{if(NF>1) {target=$$2; if(desc) printf " make %-15s: %s\n",target,desc; desc=""}}' Makefile
@echo ""
@echo "To test and build everything from scratch, use the shortcut:"
@echo " make x"
@echo ""
# alias for help target
.PHONY: all
all: help
# Test and build everything from scratch
.PHONY: x
x: buildall
## Test and build everything from scratch
.PHONY: buildall
buildall:
$(MAKE) deps format qa bz2 rpm deb
## Package the library in a compressed bz2 archive
.PHONY: bz2
bz2:
rm -rf "$(PATHBZ2PKG)"
make install DESTDIR="$(PATHBZ2PKG)"
tar -jcvf "$(PATHBZ2PKG)/$(PKGNAME)-$(VERSION)-$(RELEASE).tbz2" -C "$(PATHBZ2PKG)" "$(DATADIR)"
## Delete the vendor and target directories
.PHONY: clean
clean:
rm -rf ./vendor "$(TARGETDIR)"
## Build a DEB package for Debian-like Linux distributions
.PHONY: deb
deb:
rm -rf "$(PATHDEBPKG)"
$(MAKE) install DESTDIR="$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)"
rm -f "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/$(DOCPATH)LICENSE"
tar -zcvf "$(PATHDEBPKG)/$(PKGNAME)_$(VERSION).orig.tar.gz" -C "$(PATHDEBPKG)/" "$(PKGNAME)-$(VERSION)"
cp -rf ./resources/debian "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian"
find "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/" -type f -name '*.bak' -delete
chmod 755 "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/rules"
find "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/" -type f -exec sed $(SEDINPLACE) "s/~#DATE#~/`date -R`/" {} \;
find "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/" -type f -exec sed $(SEDINPLACE) "s/~#VENDOR#~/$(VENDOR)/" {} \;
find "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/" -type f -exec sed $(SEDINPLACE) "s/~#PROJECT#~/$(PROJECT)/" {} \;
find "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/" -type f -exec sed $(SEDINPLACE) "s/~#PKGNAME#~/$(PKGNAME)/" {} \;
find "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/" -type f -exec sed $(SEDINPLACE) "s/~#VERSION#~/$(VERSION)/" {} \;
find "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/" -type f -exec sed $(SEDINPLACE) "s/~#RELEASE#~/$(DEBRELEASE)/" {} \;
echo "$(LIBPATH)" > "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/$(PKGNAME).dirs"
echo "$(LIBPATH)* $(LIBPATH)" > "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/install"
echo "$(DOCPATH)" >> "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/$(PKGNAME).dirs"
echo "$(DOCPATH)* $(DOCPATH)" >> "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/install"
ifneq ($(strip $(CONFIGPATH)),)
echo "$(CONFIGPATH)" >> "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/$(PKGNAME).dirs"
echo "$(CONFIGPATH)* $(CONFIGPATH)" >> "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)/debian/install"
endif
cd "$(PATHDEBPKG)/$(PKGNAME)-$(VERSION)" && debuild -us -uc
## Clean all artifacts and download all dependencies
.PHONY: deps
deps: ensuretarget
rm -rf ./vendor/*
($(COMPOSER) install -vvv --no-interaction)
curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location https://carthage.software/mago.sh | bash -s -- --install-dir=./vendor/bin --version=$(MAGOVERSION)
## Generate source code documentation
.PHONY: doc
doc: ensuretarget
rm -rf "$(TARGETDIR)/doc"
$(PHPDOC) -d ./src -t "$(TARGETDIR)/doc/"
## Create missing target directories for test and build artifacts
.PHONY: ensuretarget
ensuretarget:
mkdir -p "$(TARGETDIR)/test"
mkdir -p "$(TARGETDIR)/report"
mkdir -p "$(TARGETDIR)/doc"
## Build default tc-font-mirror fonts via tc-lib-pdf-font
.PHONY: fonts
fonts:
cd vendor/tecnickcom/tc-lib-pdf-font/ && make fonts
## Install this application
.PHONY: install
install: uninstall
mkdir -p "$(PATHINSTBIN)"
cp -rf ./src/* "$(PATHINSTBIN)"
cp -f ./resources/autoload.php "$(PATHINSTBIN)"
find "$(PATHINSTBIN)" -type d -exec chmod 755 {} \;
find "$(PATHINSTBIN)" -type f -exec chmod 644 {} \;
mkdir -p "$(PATHINSTDOC)"
cp -f ./LICENSE "$(PATHINSTDOC)"
cp -f ./README.md "$(PATHINSTDOC)"
cp -f ./VERSION "$(PATHINSTDOC)"
cp -f ./RELEASE "$(PATHINSTDOC)"
chmod -R 644 "$(PATHINSTDOC)"*
ifneq ($(strip $(CONFIGPATH)),)
mkdir -p "$(PATHINSTCFG)"
touch -c "$(PATHINSTCFG)"*
cp -r "./resources/${CONFIGPATH}"* "$(PATHINSTCFG)"
find "$(PATHINSTCFG)" -type d -exec chmod 755 {} \;
find "$(PATHINSTCFG)" -type f -exec chmod 644 {} \;
endif
## Format the source code
.PHONY: format
format:
./vendor/bin/mago fmt src test examples
## Analyze and Lint the source code
.PHONY: lint
lint:
./vendor/bin/mago --config ./mago.src.toml analyze src
./vendor/bin/mago --config ./mago.test.toml analyze test
./vendor/bin/mago --config ./mago.src.toml lint src
./vendor/bin/mago --config ./mago.test.toml lint test
## Run all tests and reports
.PHONY: qa
qa: version ensuretarget lint test report
## Generate various reports
.PHONY: report
report: ensuretarget
./vendor/bin/pdepend --jdepend-xml="$(TARGETDIR)/report/dependencies.xml" --summary-xml="$(TARGETDIR)/report/metrics.xml" --jdepend-chart="$(TARGETDIR)/report/dependecies.svg" --overview-pyramid="$(TARGETDIR)/report/overview-pyramid.svg" --ignore=vendor ./src
#./vendor/bartlett/php-compatinfo/bin/phpcompatinfo --no-ansi analyser:run src/ > $(TARGETDIR)/report/phpcompatinfo.txt
## Generate mode samples and run external preflight validators (if installed)
.PHONY: preflight
preflight: ensuretarget
bash ./resources/preflight/run_preflight_matrix.sh
## Generate renderability score and trend reports for the real-page corpus
.PHONY: renderability
renderability: ensuretarget
$(PHP) resources/css/renderability_score.php \
--corpus=test/fixtures/html/real_pages/corpus.json \
--json=$(TARGETDIR)/report/renderability-score.json \
--markdown=$(TARGETDIR)/report/renderability-score.md
$(PHP) resources/css/renderability_trend.php \
--score=$(TARGETDIR)/report/renderability-score.json \
--history=$(TARGETDIR)/report/renderability-trend.json \
--markdown=$(TARGETDIR)/report/renderability-trend.md
## Generate PDF/UA examples and validate with veraPDF
.PHONY: verapdf-ua
verapdf-ua: ensuretarget
@if [[ -z "$(VERAPDF_BIN)" ]]; then \
echo "veraPDF CLI not found. Set VERAPDF_BIN=/path/to/verapdf or add verapdf to PATH."; \
exit 2; \
fi
$(PHP) examples/E015_pdfua.php > "$(TARGETDIR)/report/E015_pdfua.pdf"
$(PHP) examples/E016_pdfua1.php > "$(TARGETDIR)/report/E016_pdfua1.pdf"
$(PHP) examples/E017_pdfua2.php > "$(TARGETDIR)/report/E017_pdfua2.pdf"
@fail=0; \
for spec in "E015_pdfua.pdf:ua1" "E016_pdfua1.pdf:ua1" "E017_pdfua2.pdf:ua2"; do \
file="$${spec%%:*}"; \
flavour="$${spec##*:}"; \
input="$(TARGETDIR)/report/$${file}"; \
report="$(TARGETDIR)/report/$${file%.pdf}.verapdf.txt"; \
if "$(VERAPDF_BIN)" --format text --verbose --flavour "$${flavour}" "$${input}" > "$${report}" 2>&1; then \
echo "[ok] $${file} ($${flavour})"; \
else \
echo "[fail] $${file} ($${flavour}) - see $${report}"; \
fail=1; \
fi; \
done; \
echo "veraPDF reports written to $(TARGETDIR)/report/*.verapdf.txt"; \
exit $$fail
## Build the RPM package for RedHat-like Linux distributions
.PHONY: rpm
rpm:
@test $(words $(CURDIR)) -eq 1 || { echo "ERROR: rpmbuild does not support spaces in the project path: $(CURDIR)"; exit 1; }
rm -rf "$(PATHRPMPKG)"
mkdir -p "$(RPMDBPATH)" "$(PATHRPMPKG)/tmp"
rpmbuild \
--define "_topdir $(CURRENTDIR)$(PATHRPMPKG)" \
--define "_dbpath $(CURRENTDIR)$(RPMDBPATH)" \
--define "_tmppath $(CURRENTDIR)$(PATHRPMPKG)/tmp" \
--define "_vendor $(VENDOR)" \
--define "_owner $(OWNER)" \
--define "_project $(PROJECT)" \
--define "_package $(PKGNAME)" \
--define "_version $(VERSION)" \
--define "_release $(RPMRELEASE)" \
--define "_current_directory $(CURRENTDIR)" \
--define "_libpath /$(LIBPATH)" \
--define "_docpath /$(DOCPATH)" \
--define "_configpath /$(CONFIGPATH)" \
-bb resources/rpm/rpm.spec
## Start the development server
.PHONY: server
server:
$(PHP) -t examples -S localhost:$(PORT)
## Tag this GIT version
.PHONY: tag
tag:
git checkout main && \
git tag -a ${VERSION} -m "Release ${VERSION}" && \
git push origin --tags && \
git pull
## Run unit tests
.PHONY: test
test:
cp phpunit.xml.dist phpunit.xml
#./vendor/bin/phpunit --migrate-configuration || true
XDEBUG_MODE=coverage ./vendor/bin/phpunit --stderr test
## Remove all installed files
.PHONY: uninstall
uninstall:
rm -rf "$(PATHINSTBIN)"
rm -rf "$(PATHINSTDOC)"
## Set the version from the VERSION file
.PHONY: version
version:
sed $(SEDINPLACE) -e "s/protected string \$$version = '.*';/protected string \$$version = '${VERSION}';/g" src/Base.php
## Increase the version patch number
.PHONY: versionup
versionup:
echo ${VERSION} | gawk -F. '{printf("%d.%d.%d\n",$$1,$$2,(($$3+1)));}' > VERSION
$(MAKE) version
+294
View File
@@ -0,0 +1,294 @@
# tc-lib-pdf (TCPDF)
<img src="resources/img/tcpdf_logo.svg" alt="TCPDF logo" width="150" />
> **The next generation of [TCPDF](https://tcpdf.org)** - a modern, modular PHP library for programmatically generating PDF documents.
[![Latest Stable Version](https://poser.pugx.org/tecnickcom/tc-lib-pdf/version)](https://packagist.org/packages/tecnickcom/tc-lib-pdf)
[![Build](https://github.com/tecnickcom/tc-lib-pdf/actions/workflows/check.yml/badge.svg)](https://github.com/tecnickcom/tc-lib-pdf/actions/workflows/check.yml)
[![Coverage](https://codecov.io/gh/tecnickcom/tc-lib-pdf/graph/badge.svg?token=rmAqNKVG1c)](https://codecov.io/gh/tecnickcom/tc-lib-pdf)
[![License](https://poser.pugx.org/tecnickcom/tc-lib-pdf/license)](https://packagist.org/packages/tecnickcom/tc-lib-pdf)
[![Downloads](https://poser.pugx.org/tecnickcom/tc-lib-pdf/downloads)](https://packagist.org/packages/tecnickcom/tc-lib-pdf)
[![Sponsor on GitHub](https://img.shields.io/badge/sponsor-github-EA4AAA.svg?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/tecnickcom)
> 💖 **Keep TCPDF maintained.** `tc-lib-pdf` is the actively-developed successor to **TCPDF**, which is installed 100M+ times across 500+ PHP packages and is now maintenance-only. If your company depends on it, [become a sponsor](https://github.com/sponsors/tecnickcom) to keep this shared infrastructure secure and maintained. See [Sponsors](#sponsors) for tiers.
---
## Contents
- [Overview](#overview)
- [Sponsors](#sponsors)
- [For TCPDF Users](#for-tcpdf-users)
- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Examples](#examples)
- [In-Depth Documentation](#in-depth-documentation)
- [Contributing](#contributing)
---
## Overview
`tc-lib-pdf` is a pure-PHP library for dynamically generating PDF documents.
It is the modern evolution of the widely used TCPDF library, redesigned around a modular package architecture, Composer-first workflow, and strict PHP type safety.
It coordinates specialized companion packages for fonts, images, graphics, pages, filtering, encryption, and digital signatures into a cohesive document-authoring API. The result is a production-ready toolkit for invoices, reports, labels, and other generated PDFs where predictable output and long-term maintainability matter.
| | |
|---|---|
| **Namespace** | `\Com\Tecnick\Pdf` |
| **Author** | Nicola Asuni \<info@tecnick.com\> |
| **License** | [GNU LGPL v3](https://www.gnu.org/copyleft/lesser.html) - see [LICENSE](LICENSE) |
| **Website** | <https://tcpdf.org> |
| **API docs** | <https://tcpdf.org/docs/srcdoc/tc-lib-pdf> |
| **Packagist** | <https://packagist.org/packages/tecnickcom/tc-lib-pdf> |
Releases follow [Semantic Versioning](https://semver.org):
- **PATCH**: backwards-compatible bug fixes
- **MINOR**: backwards-compatible new features
- **MAJOR**: breaking changes
---
## Sponsors
`tc-lib-pdf` is the actively-developed successor to **TCPDF**, which is installed **100M+ times across 500+ PHP packages** and is now maintenance-only. If your product relies on TCPDF or `tc-lib-pdf`, sponsoring is how that shared infrastructure stays secure and maintained, closer to insurance on a dependency you already ship than to a donation.
[![Sponsor on GitHub](https://img.shields.io/badge/sponsor-github-EA4AAA.svg?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/tecnickcom)
<!-- sponsors -->
**Your logo here.** Be the first company to back the project: [become a sponsor →](https://github.com/sponsors/tecnickcom)
<!-- sponsors -->
See **[SPONSORS.md](https://github.com/tecnickcom/.github/blob/main/SPONSORS.md)** for sponsorship tiers, how to add your logo, and the logo/content policy. Individual backers are listed in **[BACKERS.md](https://github.com/tecnickcom/.github/blob/main/BACKERS.md)**.
---
## For TCPDF Users
If you already know TCPDF, `tc-lib-pdf` will feel familiar in purpose but it is not positioned as a drop-in replacement.
- The codebase is split across focused Composer packages instead of a single monolithic distribution.
- The API surface is more strongly typed and organized around companion services such as fonts, pages, graphics, and images.
- Setup is Composer-first, which means asset preparation such as font generation is part of project bootstrap rather than an implicit bundled step.
The fastest way to evaluate the library is to follow the installation and quick-start steps below, then compare the runnable examples in [examples/index.md](examples/index.md) with the equivalent workflows you already maintain in TCPDF.
## Features
### Text & Fonts
- Full **UTF-8 Unicode** and **right-to-left** (RTL) language support
- **TrueTypeUnicode**, **OpenTypeUnicode v1**, TrueType, OpenType v1, Type1, and CID-0 fonts
- Font subsetting to keep file sizes small
- Text hyphenation, stretching, and letter-spacing (tracking)
- Language-aware TeX hyphenation patterns and optional zero-width breakpoints
- Text rendering modes: fill, stroke, and clipping
- Automatic line breaks, page breaks, and justification
### Layout & Content
- All standard page sizes, custom formats, custom margins, and configurable units of measure
- **HTML** and **CSS** rendering
- **SVG** rendering
- Multi-column layouts and no-write page regions
- Headers, footers, and common page content
- Bookmarks, named destinations, and table of contents
- Automatic page numbering and page groups
- Full page box control (Media/Crop/Bleed/Trim/Art), page reordering, and viewer preferences
- **Per-page transparency group control** via `setPageTransparencyGroup()`: by default (`'auto'`) the page transparency `/Group` is emitted only on pages that actually blend, flattening fully-opaque pages for faster, more predictable printing on conservative RIPs/firmware; `'always'` keeps the group on every page (legacy) and `'never'` drops it entirely
### Images & Graphics
- Native **JPEG**, **PNG**, and **SVG** support
- Extended image format handling via GD (`GD`, `GD2`, `GD2PART`, `GIF`, `JPEG`, `PNG`, `BMP`, `XBM`, `XPM`, `WBMP`, `TIFF`, `ICO`, `PSD`, `IFF`, `SWC`)
- Geometric graphics and 2D transformations
- Linear and radial gradients, Coons patch mesh gradients, crop marks, and registration bars
- **JPEG and PNG ICC profiles**, grayscale/RGB/CMYK/spot colors, transparencies, and overprint control
### Security & Standards
- Password and certificate-based document encryption (RC4 and AES, up to 256-bit)
- Remote resource controls via `fileOptions` with host allowlists plus separate internal and markup local-path allowlists for external assets
- **Digital signatures**: detached CMS (PKCS#7) and **PAdES baseline** signatures (ETSI EN 319 142-1) via the fluent `signature()` facade, with configurable appearance fields. Profiles: `legacy` (ISO 32000-1 `adbe.pkcs7.detached`), `pades-b-b`, `pades-b-t`, `pades-b-lt`, and `pades-b-lta` (`ETSI.CAdES.detached`), with RSA or ECDSA keys and `sha256`/`sha384`/`sha512` digests. Both local (private-key) and external/remote (HSM) signing are supported. The cryptography lives in the companion package [`tc-lib-pdf-sign`](https://github.com/tecnickcom/tc-lib-pdf-sign); see [doc/DIGITAL_SIGNATURES.md](doc/DIGITAL_SIGNATURES.md)
- **RFC 3161 TSA timestamps** (PAdES B-T): embed a trusted timestamp token from any RFC 3161-compliant Time Stamping Authority (TSA) into the CMS signature as the `id-aa-signatureTimeStampToken` attribute; configurable digest algorithm (`sha256`, `sha384`, `sha512`), policy OID, nonce, timeout, and TLS peer verification
- **LTV (Long-Term Validation)** (PAdES B-LT): embed revocation evidence in a post-signing incremental revision:
- collects the signing certificate chain and fetches OCSP responses and/or CRL payloads from AIA and CDP URLs
- deduplicates binary payloads by fingerprint
- emits a Document Security Store (`/DSS`) carrying `/VRI`, `/Certs`, `/OCSPs`, and `/CRLs`, referenced from the re-emitted document catalog
- each feature (OCSP, CRL, cert embedding, DSS, VRI) can be enabled independently via the `signature()` LTV options
- **Archive timestamps** (PAdES B-LTA): add a `/Type /DocTimeStamp` archive timestamp over the whole document in a further incremental revision via `signature()->upgradeToLta()`
- **PDF annotations**: links, text notes, file attachments, markup, shapes, media, and widgets
- **JavaScript** embedding
- **PDF/A** (1/2/3, including a/b/u conformance levels): see [doc/STANDARDS.md](doc/STANDARDS.md) and [E001_invoice.php](examples/E001_invoice.php) for a Factur-X / ZUGFeRD example
- **PDF/X** (generic alias, PDF/X-1a, PDF/X-3, PDF/X-4, PDF/X-5): print-exchange conformance covering per-variant OutputIntent identifiers, GTS_PDFXVersion in Info dict and XMP, PDF version enforcement, CMYK color forcing for restrictive profiles (X-1a, X-3), transparency restrictions, and suppression of encryption and JavaScript
- **PDF/UA** (generic alias, PDF/UA-1, PDF/UA-2): accessibility conformance covering tagged structure tree (`StructTreeRoot` / `ParentTree`), `MarkInfo /Marked true`, document language (`/Lang`), `DisplayDocTitle true`, `ActualText` for ligatures and special glyphs, figure alt-text tagging, and heading-level clamping to prevent skipped levels; PDF/UA-2 targets PDF 2.0
### PDF Import
- Import pages from existing PDFs as **Form XObjects** and place them on any destination page
- Import a single page at a user-defined position and scale (`importPage` / `useImportedPage`)
- Append full documents page-by-page, auto-sized to the source page dimensions (`addPageFromImport`)
- Load source PDFs from a file path or raw byte string (`setImportSourceFile` / `setImportSourceData`)
### Other
- **1D and 2D barcodes** via [`tc-lib-barcode`](https://github.com/tecnickcom/tc-lib-barcode)
- Interactive AcroForm fields (buttons, checkboxes, radio buttons, text, combo boxes, list boxes)
- XObject templates and layers with object visibility controls
- Multiple output targets: inline display, forced download, file save, and MIME attachment
- Factur-X / ZUGFeRD workflows via embedded XML in PDF/A-3 documents
- Page compression via the `zlib` PHP extension
---
## Requirements
- **PHP 8.2** or later
- Composer
Optional PHP extensions for extended functionality: `gd`, `zlib`.
Feature-specific prerequisites:
- Digital signatures, timestamps, and LTV workflows require signing certificates/keys and any external TSA or revocation endpoints your configuration references.
- `make preflight` depends on external validation tools when you want standards validation beyond the built-in sample generation.
---
## Installation
For a clean first run:
1. Install the package with Composer.
2. Generate companion font files (see [doc/FONTS.md](doc/FONTS.md)).
3. Run the minimal script using the generated `K_PATH_FONTS` path.
```bash
composer require tecnickcom/tc-lib-pdf
```
Or add to your `composer.json`:
```json
{
"require": {
"tecnickcom/tc-lib-pdf": "^8"
}
}
```
---
## Quick Start
The following example assumes the script lives in your project root. If you place it elsewhere, adjust the `autoload.php` and `K_PATH_FONTS` paths accordingly.
```php
<?php
require(__DIR__ . '/vendor/autoload.php');
\define('K_PATH_FONTS', \realpath(__DIR__ . '/vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf();
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$html = '<h1>Hello, PDF!</h1><p>Generated with tc-lib-pdf.</p>';
$pdf->addHTMLCell(
html: $html,
posx: 15, // mm from left page edge
posy: 20, // mm from top page edge
width: 180, // mm wide (0 = to right margin)
);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF($rawpdf);
```
`getOutPDFString()` returns the raw PDF bytes. `renderPDF()` streams those bytes to the browser; if you need file storage or an email attachment, keep the returned string and write or hand it off yourself.
> **Note:** `realpath()` returns `false` when the fonts directory does not yet exist. If you see `K_PATH_FONTS` errors on first run, verify that fonts were generated after `composer install` (see [doc/FONTS.md](doc/FONTS.md)).
For more complete examples (including invoices, images, barcodes, HTML tables, dedicated HTML selector/form/table showcases, PDF/X, and PDF/UA), see the [examples](examples) directory.
Annotation-focused runnable example: [examples/E027_annotations.php](examples/E027_annotations.php).
To run the bundled examples locally:
```bash
make fonts # generate the companion fonts used by the examples
make server # start a local PHP server
```
Then open <http://localhost:8971/E000_overview.php>.
If the minimal example fails on first run, verify these two points first:
- `K_PATH_FONTS` resolves to an existing generated font directory.
- The companion fonts were generated after `composer install` or `composer update`.
---
## Examples
The [examples](examples) directory is the fastest way to understand supported features and integration patterns in runnable form.
Useful starting points:
- [examples/index.md](examples/index.md): index of available examples.
- [examples/E000_overview.php](examples/E000_overview.php): broad feature overview.
- [examples/E006_minimal.php](examples/E006_minimal.php): minimal PDF generation flow.
- [examples/E043_html_tables.php](examples/E043_html_tables.php): HTML table rendering.
- [examples/E065_import_single_page.php](examples/E065_import_single_page.php): PDF page import.
Selected topic groups in the examples set:
- Document basics (layout, headers/footers, cells, colors, images, text rendering)
- Standards and compliance (PDF/X, PDF/UA, PDF/A workflows)
- Security and signing (encryption, PAdES/PKCS#7 signatures, timestamps, LTV)
- Advanced composition (annotations, templates, layers, page import/reorder)
To preview examples locally:
```bash
make fonts
make server
```
Then open <http://localhost:8971/E000_overview.php>.
---
## In-Depth Documentation
For implementation details, compliance guidance, operational workflows, and advanced usage patterns, see the focused guides in the `doc/` directory:
- Font setup, custom fonts, and third-party font licenses: [doc/FONTS.md](doc/FONTS.md)
- ICC profile details: [doc/ICC_PROFILE.md](doc/ICC_PROFILE.md)
- PDF import API, examples, and fidelity notes: [doc/PDF_IMPORT.md](doc/PDF_IMPORT.md)
- Remote resources and `fileOptions` (`allowedHosts`, `allowedPaths`, `markupAllowedPaths`, cURL policy): [doc/REMOTE_RESOURCES.md](doc/REMOTE_RESOURCES.md)
- External cache for font subsets and images (`CacheInterface`, selective caching): [doc/CACHE.md](doc/CACHE.md)
- Digital signatures, TSA timestamps, and LTV: [doc/DIGITAL_SIGNATURES.md](doc/DIGITAL_SIGNATURES.md)
- PDF/A, PDF/X, and PDF/UA conformance modes: [doc/STANDARDS.md](doc/STANDARDS.md)
- Development, QA, preflight, and packaging workflows: [doc/DEVELOPMENT.md](doc/DEVELOPMENT.md)
---
## Contributing
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before submitting a pull request.
1. Fork the repository and create a feature branch.
2. Write or update tests for your change.
3. Run `make qa` to ensure the full pipeline passes.
4. Open a pull request with a clear description of the change.
Security vulnerabilities should be reported according to [SECURITY.md](SECURITY.md).
+1
View File
@@ -0,0 +1 @@
1
+64
View File
@@ -0,0 +1,64 @@
# Security Policy
This document describes the security policy for **tc-lib-pdf** (the modern successor to [TCPDF](https://tcpdf.org)), a pure-PHP library for programmatically generating PDF documents.
---
## Supported Versions
Security fixes are applied only to the **latest stable release** on the `main` branch.
We strongly recommend always running the latest release.
---
## Reporting a Vulnerability
**Please do not open a public GitHub issue for security vulnerabilities.**
If you discover a security vulnerability — or suspect one — follow responsible disclosure:
1. **Email** the maintainer directly at **[info@tecnick.com](mailto:info@tecnick.com)** with the subject line:
`[SECURITY] tc-lib-pdf <brief description>`
2. Include as much detail as possible (see [What to include](#what-to-include) below).
3. You will receive an acknowledgement as soon as possible.
4. We will work on a fix or mitigation as promptly as the complexity of the issue allows.
If you do not receive a timely response, please follow up by replying to the same email thread.
---
## What to Include
A high-quality report helps us triage and fix issues faster. Please provide:
- **Description** — a clear summary of the vulnerability and its potential impact.
- **Affected component** — which class, method, or feature is involved (e.g., `HTML::render()`, font loading, image processing).
- **Steps to reproduce** — a minimal, self-contained PHP script or unit test that demonstrates the issue.
- **Expected vs. actual behaviour** — what you expected to happen and what actually happened.
- **Environment** — PHP version, OS, library version (output of `composer show tecnickcom/tc-lib-pdf`).
- **CVE / CWE reference** (optional) — if you have already identified a relevant classification.
- **Suggested fix** (optional) — a patch or proposed mitigation if you have one.
---
## Security Best Practices for Integrators
`tc-lib-pdf` processes rich content (HTML, CSS, SVG, fonts, images) which may originate from untrusted sources. Integrators are responsible for sanitising input **before** passing it to the library. We recommend:
- **Validate and sanitise all user-supplied HTML/CSS** before rendering. Use a dedicated HTML sanitiser (e.g., [HTML Purifier](http://htmlpurifier.org/)) when accepting content from end users.
- **Restrict external asset loading.** Use `fileOptions['allowedHosts']` to allow only trusted remote domains, and set `fileOptions['allowedPaths']` when you need to narrow local file reads to specific asset directories. If you override `allowedPaths`, include every required local root because it replaces the defaults.
- **Limit file-system access.** Run the PDF-generation process with the minimum required filesystem permissions. Never pass raw user input as a file path.
- **Keep dependencies up to date.** Run `composer update` regularly and monitor advisories via [Packagist Security Advisories](https://packagist.org/packages/tecnickcom/tc-lib-pdf) or tools such as `composer audit`.
- **Pin versions in production.** Use `composer.lock` and review changes on every update.
---
## Contact
| Channel | Details |
|---------|---------|
| Security email | [info@tecnick.com](mailto:info@tecnick.com) |
| Project website | <https://tcpdf.org> |
| GitHub repository | <https://github.com/tecnickcom/tc-lib-pdf> |
| Packagist | <https://packagist.org/packages/tecnickcom/tc-lib-pdf> |
+1
View File
@@ -0,0 +1 @@
8.68.3
+97
View File
@@ -0,0 +1,97 @@
{
"name": "tecnickcom/tc-lib-pdf",
"description": "PHP PDF Library",
"type": "library",
"homepage": "https://tcpdf.org",
"license": "LGPL-3.0-or-later",
"keywords": [
"tc-lib-pdf",
"TCPDF",
"PDF",
"PDFD32000-2008",
"document"
],
"authors": [
{
"name": "Nicola Asuni",
"email": "info@tecnick.com",
"role": "lead"
}
],
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/tecnickcom"
}
],
"require": {
"php": ">=8.2",
"ext-ctype": "*",
"ext-filter": "*",
"ext-hash": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-pcre": "*",
"ext-xml": "*",
"ext-zlib": "*",
"tecnickcom/tc-lib-barcode": "^2.13",
"tecnickcom/tc-lib-color": "^2.13",
"tecnickcom/tc-lib-pdf-image": "^3.12",
"tecnickcom/tc-lib-pdf-font": "^3.13",
"tecnickcom/tc-lib-file": "^3.7",
"tecnickcom/tc-lib-pdf-encrypt": "^2.9",
"tecnickcom/tc-lib-pdf-sign": "^1.1",
"tecnickcom/tc-lib-unicode-data": "^3.0",
"tecnickcom/tc-lib-unicode": "^3.0",
"tecnickcom/tc-lib-pdf-page": "^4.14",
"tecnickcom/tc-lib-pdf-graph": "^2.15",
"tecnickcom/tc-lib-pdf-parser": "^3.14"
},
"suggest": {
"ext-curl": "Required to contact a Timestamp Authority (RFC 3161) when timestamping signatures",
"ext-intl": "Enables Unicode NFC normalization of the PDF file name"
},
"minimum-stability": "stable",
"prefer-stable": true,
"require-dev": {
"pdepend/pdepend": "^2.16",
"phpunit/phpunit": "^11.5 || ^12.5 || ^13.2"
},
"autoload": {
"psr-4": {
"Com\\Tecnick\\Pdf\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Test\\": "test"
}
},
"archive": {
"exclude": [
"/examples"
]
},
"support": {
"issues": "https://github.com/tecnickcom/tc-lib-pdf/issues",
"source": "https://github.com/tecnickcom/tc-lib-pdf"
},
"scripts": {
"test": "@php -d xdebug.mode=coverage vendor/bin/phpunit --stderr test",
"analyse": ["@analyse:src", "@analyse:test"],
"analyse:src": "mago --config mago.src.toml analyze src",
"analyse:test": "mago --config mago.test.toml analyze test",
"cs-check": ["@cs-check:src", "@cs-check:test"],
"cs-check:src": "mago --config mago.src.toml lint src",
"cs-check:test": "mago --config mago.test.toml lint test",
"cs-fix": "mago fmt src test",
"qa": ["@cs-check", "@analyse", "@test"],
"post-install-cmd": [
"make fonts"
],
"post-update-cmd": [
"make fonts"
]
}
}
+118
View File
@@ -0,0 +1,118 @@
# External Cache
Back to root overview: [README.md](../README.md#in-depth-documentation)
Generating font subsets and processing images (decode, resize, re-encode) is computationally expensive. `tc-lib-pdf` can reuse these results across `Tcpdf` instances and PHP processes through an **optional external cache** that you provide.
No cache backend is shipped: you implement a tiny interface that bridges to whatever store you already use (filesystem, APCu, Redis, a PSR-16 cache, ...). Caching is **disabled by default**: when no cache is supplied, behavior is unchanged.
One cache instance is reused by every cacheable subsystem (currently font subsets and images, more may be added later), so a single backend, connection, and configuration serves them all.
## The CacheInterface
Implement `Com\Tecnick\Pdf\Cache\CacheInterface`:
```php
namespace Com\Tecnick\Pdf\Cache;
interface CacheInterface
{
public function get(string $key): mixed; // stored value, or null on a miss
public function set(string $key, mixed $value): void;
}
```
Both methods MUST be **best-effort** and **MUST NOT throw**: a backend miss or transient failure must surface as `null` (on `get`) or a silent no-op (on `set`). The font and image libraries call the cache directly and do not catch exceptions, so a throwing implementation will break PDF generation.
## Enabling the Cache
Pass your implementation as the `cache` argument of the `Tcpdf` constructor (the last parameter):
```php
$cache = new MyRedisCache(); // implements Com\Tecnick\Pdf\Cache\CacheInterface
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: 'mm',
subsetfont: true, // required for the font subset cache to be exercised
cache: $cache,
);
```
A minimal in-memory implementation (useful for tests or single-request deduplication):
```php
use Com\Tecnick\Pdf\Cache\CacheInterface;
$cache = new class implements CacheInterface {
/** @var array<string, mixed> */
private array $store = [];
public function get(string $key): mixed
{
return $this->store[$key] ?? null;
}
public function set(string $key, mixed $value): void
{
$this->store[$key] = $value;
}
};
```
## What Gets Cached
| Subsystem | Type constant | Cached value | Key prefix |
|-----------|---------------|--------------|------------|
| Font subsets | `CacheInterface::TYPE_FONT` | Raw subset font program (`string`, uncompressed) | `tc-lib-pdf-font:subset:v2:` |
| Images | `CacheInterface::TYPE_IMAGE` | Processed image snapshot (`array`) | `tc-lib-pdf-image:v2:` |
Keys are already namespaced and schema-versioned by each sub-library, so a single shared store is collision-safe. The font subset cache is only consulted when font subsetting is enabled (`subsetfont: true`).
The library never evicts entries: expiration, size limits, and (de)serialization are entirely the backend's responsibility. When an implementation deserializes data it MUST disable object restoration, e.g. `unserialize($data, ['allowed_classes' => false])`.
## Caching Only Some Types
To cache only a subset of the subsystems, implement `Com\Tecnick\Pdf\Cache\SelectiveCacheInterface` (which extends `CacheInterface`) and report which types you handle:
```php
namespace Com\Tecnick\Pdf\Cache;
interface SelectiveCacheInterface extends CacheInterface
{
/** @param CacheInterface::TYPE_* $type */
public function supports(string $type): bool;
}
```
When `supports()` returns `false` for a type, that type is disabled entirely: the cache is never queried or written for it, and your implementation never receives its data. A plain `CacheInterface` (without `supports()`) caches every type.
For example, to cache font subsets but never images:
```php
use Com\Tecnick\Pdf\Cache\CacheInterface;
use Com\Tecnick\Pdf\Cache\SelectiveCacheInterface;
$cache = new class implements SelectiveCacheInterface {
/** @var array<string, mixed> */
private array $store = [];
public function supports(string $type): bool
{
return $type === CacheInterface::TYPE_FONT;
}
public function get(string $key): mixed
{
return $this->store[$key] ?? null;
}
public function set(string $key, mixed $value): void
{
$this->store[$key] = $value;
}
};
```
## Security
The cache store is a **trust boundary**. Cached values are embedded verbatim into generated PDFs, so anyone able to write to the backend can influence document output. Use a store only your application can write to, and always deserialize with object restoration disabled (`['allowed_classes' => false]`).
+36
View File
@@ -0,0 +1,36 @@
# Development and Packaging
Back to root overview: [README.md](../README.md#in-depth-documentation)
## Development
```bash
# Install all development dependencies
make deps
# List all available Make targets
make help
# Run the full quality pipeline (lint, static analysis, tests, coverage)
make qa
# Generate PDF/X + PDF/UA sample matrix and run external validators (if installed)
make preflight
```
Build artifacts and reports are written to the `target/` directory.
## Packaging
The primary distribution channel is Composer. For system-level deployments, RPM and DEB packages are also provided.
```bash
make rpm # build RPM package -> target/RPM/
make deb # build DEB package -> target/DEB/
```
When using the RPM or DEB package, bootstrap the library with its system autoloader:
```php
require_once '/usr/share/php/Com/Tecnick/Pdf/autoload.php';
```
+143
View File
@@ -0,0 +1,143 @@
# Digital Signatures
Back to root overview: [README.md](../README.md#in-depth-documentation)
`tc-lib-pdf` produces detached CMS (PKCS#7) signatures and **PAdES baseline** signatures
(ETSI EN 319 142-1) with optional RFC 3161 timestamps, LTV (Long-Term Validation)
material, and archive timestamps. The cryptography lives in the companion package
[`tecnickcom/tc-lib-pdf-sign`](https://github.com/tecnickcom/tc-lib-pdf-sign).
## Signature profiles
The `profile` option selects the signature format:
| Profile | /SubFilter | What it adds |
| --- | --- | --- |
| `legacy` (default) | `adbe.pkcs7.detached` | ISO 32000-1 detached CMS with the ESS signing-certificate-v2 attribute. |
| `pades-b-b` | `ETSI.CAdES.detached` | PAdES-BASELINE-B: CAdES-based CMS; the signing time is carried by the `/M` dictionary entry (the CMS signing-time attribute is omitted, as the baseline requires). |
| `pades-b-t` | `ETSI.CAdES.detached` | PAdES-BASELINE-T: B-B plus an RFC 3161 signature timestamp embedded in the CMS. |
| `pades-b-lt` | `ETSI.CAdES.detached` | PAdES-BASELINE-LT: B-T plus a Document Security Store (`/DSS`, `/VRI`) with certificates and, where reachable, OCSP/CRL revocation data. |
| `pades-b-lta` | `ETSI.CAdES.detached` + `ETSI.RFC3161` | PAdES-BASELINE-LTA: B-LT plus a `/Type /DocTimeStamp` archive timestamp over the whole document. |
The default profile stays `legacy`, so existing signing output is unchanged unless a PAdES
profile is requested. `digest_algorithm` accepts `sha256` (default), `sha384`, or `sha512`;
RSA and ECDSA signing keys are both supported.
Signature-focused runnable examples:
- [examples/E007_signature_basic.php](../examples/E007_signature_basic.php): PAdES-BASELINE-B signature via the fluent `signature()` facade.
- [examples/E008_signature_timestamp.php](../examples/E008_signature_timestamp.php): PAdES-BASELINE-T signature with an RFC 3161 TSA timestamp.
- [examples/E009_signature_ltv.php](../examples/E009_signature_ltv.php): PAdES-BASELINE-LT signature with LTV material (`/DSS`, `/VRI`).
- [examples/E081_signature_pades_lta.php](../examples/E081_signature_pades_lta.php): PAdES-BASELINE-LTA signature with a document archive timestamp via `upgradeToLta()`.
- [examples/E075_external_signature_injection.php](../examples/E075_external_signature_injection.php): external/remote signing workflow with ByteRange digest export and later CMS signature injection.
## Fluent API: `signature()`
The preferred entry point is the `signature()` facade. Each call is chainable and forwards
to the underlying methods (which remain available as `setSignature()`, `setSignTimeStamp()`,
`setUserRights()`, `setSignatureAppearance()`, and so on).
```php
$pdf->signature()
->configure([
'profile' => 'pades-b-t', // legacy | pades-b-b | pades-b-t | pades-b-lt | pades-b-lta
'digest_algorithm' => 'sha256', // sha256 | sha384 | sha512
'signcert' => 'file:///path/to/cert.pem',
'privkey' => 'file:///path/to/key.pem',
'password' => '',
'extracerts' => 'file:///path/to/chain.pem', // optional issuer chain
'cert_type' => 2,
'info' => [
'Name' => 'Jane Smith',
'Location' => 'London',
'Reason' => 'Document approval',
'ContactInfo' => 'jane@example.com',
],
])
->timestamp([
'enabled' => true,
'host' => 'https://freetsa.org/tsr',
'hash_algorithm' => 'sha256',
'timeout' => 30,
'verify_peer' => true,
]);
$pdf->signature()->appearance()->place(posx: 15, posy: 35, width: 90, height: 20, page: -1, name: 'Signature');
$widgetObjId = $pdf->signature()->widgetObjectId();
```
## Adding a TSA Timestamp (RFC 3161)
For `pades-b-t` and above a timestamp is required. Configure it with
`signature()->timestamp([...])` (or the legacy `setSignTimeStamp([...])`); the RFC 3161
token is embedded in the CMS as the `id-aa-signatureTimeStampToken` unsigned attribute:
```php
$pdf->signature()->timestamp([
'enabled' => true,
'host' => 'https://freetsa.org/tsr',
'username' => '',
'password' => '',
'cert' => '',
'hash_algorithm' => 'sha256', // sha256 | sha384 | sha512
'policy_oid' => '', // optional OID string
'nonce_enabled' => true,
'timeout' => 30,
'verify_peer' => true,
]);
```
## LTV (Long-Term Validation) and archive timestamps
Enable LTV via the `ltv` key inside `configure()`. The library fetches OCSP responses and
CRL payloads from the certificate's AIA and CRL-DP extensions and writes a `/DSS` (with a
`/VRI` map keyed by the uppercase SHA-1 of the signature `/Contents`) in a post-signing
incremental revision:
```php
$pdf->signature()->configure([
'profile' => 'pades-b-lt',
'signcert' => 'file:///path/to/cert.pem',
'privkey' => 'file:///path/to/key.pem',
'password' => '',
'ltv' => [
'enabled' => true,
'embed_ocsp' => true, // fetch OCSP responses
'embed_crl' => true, // fetch CRL payloads (fallback)
'embed_certs' => true, // include certificate DER bytes
'include_dss' => true, // emit /DSS in the catalog
'include_vri' => true, // emit /VRI map keyed by signature SHA-1
],
]);
```
To reach PAdES-BASELINE-LTA, call `upgradeToLta()` (it selects the `pades-b-lta` profile,
forces the DSS on, and adds a `/Type /DocTimeStamp` archive timestamp over the whole
document in a further incremental revision; a TSA must be configured):
```php
$pdf->signature()->configure([/* pades-b-lt + ltv */])->timestamp([/* TSA */])->upgradeToLta();
```
A validator only reports the LT/LTA level when the DSS actually contains the revocation data
for the chain, so the signing certificate must expose reachable OCSP/CRL responders. A
self-signed certificate embeds only its own bytes, so a validator then reports B-T with a
DSS present.
## Generating a Self-Signed Test Certificate
The bundled `examples/data/cert/tcpdf.crt` is a self-signed demo certificate (certificate
and RSA private key in one file). Generate your own with:
```bash
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -sha256 \
-keyout tcpdf.key -out tcpdf.crt \
-subj "/CN=tc-lib-pdf test certificate"
# combine into a single file (as the bundled demo does), or reference them separately
cat tcpdf.crt tcpdf.key > tcpdf.pem
# convert to PKCS#12 if needed
openssl pkcs12 -export -in tcpdf.crt -inkey tcpdf.key -out tcpdf.p12
```
For a real PAdES-BASELINE-LT/LTA validation you need a certificate issued by a CA whose
OCSP responder (AIA) and CRL distribution point are reachable at signing time.
+99
View File
@@ -0,0 +1,99 @@
# Fonts
Back to root overview: [README.md](../README.md#in-depth-documentation)
## Font Setup
When you install `tc-lib-pdf` as a dependency in your project (via `composer require` or `composer install`), the fonts from the companion package [`tc-lib-pdf-font`](https://github.com/tecnickcom/tc-lib-pdf-font) must be generated before they can be used.
Composer does not execute scripts declared by dependencies, so you need to add the font generation step to your **consuming project's** `composer.json` file:
```json
{
"scripts": {
"tc-lib-pdf-fonts": [
"[ -d vendor/tecnickcom/tc-lib-pdf-font ] && make -C vendor/tecnickcom/tc-lib-pdf-font deps fonts || true"
],
"post-install-cmd": [
"@tc-lib-pdf-fonts"
],
"post-update-cmd": [
"@tc-lib-pdf-fonts"
]
}
}
```
This ensures fonts are generated automatically when you run:
```bash
composer install
composer update
composer require ...
```
To also cover `composer dump-autoload` (used in many CI pipelines), add the hook to `post-autoload-dump` as well:
```json
"post-autoload-dump": [
"@tc-lib-pdf-fonts"
]
```
If you prefer to generate fonts manually, run the build in the `tc-lib-pdf-font` package:
```bash
cd vendor/tecnickcom/tc-lib-pdf-font
make fonts
```
Equivalent one-liner from your project root:
```bash
make -C vendor/tecnickcom/tc-lib-pdf-font deps fonts
```
Once fonts are generated, they are cached in `vendor/tecnickcom/tc-lib-pdf-font/target/fonts/` and will not be regenerated unless explicitly rebuilt.
You can also add your own fonts and generate their PHP font data with `tc-lib-pdf-font`. For shared or immutable environments, generate them once into a persistent directory you control (outside `vendor/`) and point `K_PATH_FONTS` to that location.
For a runnable end-to-end custom font workflow, see [examples/E072_import_new_font.php](../examples/E072_import_new_font.php).
Example import commands (from the project root):
```bash
mkdir -p target/fonts/source target/fonts/custom
curl -fL --retry 3 -o target/fonts/source/NotoSans-Regular.ttf \
https://github.com/notofonts/noto-fonts/raw/main/hinted/ttf/NotoSans/NotoSans-Regular.ttf
php vendor/tecnickcom/tc-lib-pdf-font/util/convert.php \
--outpath=target/fonts/custom \
--type=TrueTypeUnicode \
--flags=32 \
--encoding_id=1 \
--fonts=target/fonts/source/NotoSans-Regular.ttf
```
Then point `K_PATH_FONTS` to `target/fonts/custom` (or an absolute path to that directory) before creating the `Tcpdf` instance.
```php
\define('K_PATH_FONTS', '/opt/app/fonts/tc-lib-pdf');
```
This avoids regenerating fonts on every dependency reinstall and lets multiple deployments reuse the same prepared font set.
## Third-Party Fonts
PHP font metadata files under the fonts directory are covered by the project license (GNU LGPL v3). They can be regenerated with the built-in font utilities.
Original source files are renamed for compatibility and compressed with PHP `gzcompress` (`.z` extension) where applicable.
| Prefix | Source | License |
|--------|--------|---------|
| `freefont` | [GNU FreeFont](https://ftp.gnu.org/gnu/freefont/freefont-ttf-20120503.zip) | GNU GPL v3 |
| `pdfa` | [tc-font-pdfa](https://github.com/tecnickcom/tc-font-pdfa) (derived from GNU FreeFont) | GNU GPL v3 |
| `dejavu` | [DejaVu Fonts 2.35](https://sourceforge.net/projects/dejavu/files/dejavu/2.35/dejavu-fonts-ttf-2.35.zip) | Bitstream Vera (with DejaVu public-domain changes) |
| `unifont` | [GNU Unifont 15.1.03](https://www.unifoundry.com/pub/unifont/unifont-15.1.03/unifont-15.1.03.tar.gz) | GPL v2+ with font embedding exception (also distributed under SIL OFL 1.1) |
| `cid0` | [GNU Unifont](http://unifoundry.com/unifont.html) (CID mappings) | GPL v2+ with font embedding exception |
| `core` | [Adobe Core14 AFM](https://partners.adobe.com/public/developer/en/pdf/Core14_AFMs.zip) | Adobe copyright terms (see AFM notices) |
+5
View File
@@ -0,0 +1,5 @@
# ICC Profile
Back to root overview: [README.md](../README.md#in-depth-documentation)
The bundled `sRGB.icc.z` profile (stored gzip-compressed) is sourced from the Debian [`icc-profiles-free`](https://packages.debian.org/source/stable/icc-profiles-free) package.
+64
View File
@@ -0,0 +1,64 @@
# PDF Import
Back to root overview: [README.md](../README.md#in-depth-documentation)
`tc-lib-pdf` can import pages from existing PDFs as Form XObjects and place them on destination pages.
## Source Registration and Page Count
```php
$sourceId = $pdf->setImportSourceFile('/path/to/source.pdf');
// or: $sourceId = $pdf->setImportSourceData($rawPdfBytes);
$count = $pdf->getSourcePageCount($sourceId);
```
The page count is derived from the page tree actually reachable through `/Kids`; the declared `/Count` entry of the `/Pages` dictionary is ignored, so a forged or wrong `/Count` cannot influence how many pages are counted or imported. Structurally broken page trees (missing `/Kids`, unexpected node types, duplicate or cyclic references) raise `ImportCorruptedSourceException`.
The reachable-page walk runs once per registered source: it produces a flattened page index (one effective page dictionary per page, with inherited attributes already resolved) that is cached and reused by `getSourcePageCount()`, `importPage()`, and `importPages()`. Importing all pages of an n-page document therefore costs a single tree walk plus one index lookup per page, instead of one full walk per page.
## Import One Page and Place It
```php
$tpl = $pdf->importPage($sourceId, 1, [
'box' => 'CropBox', // MediaBox|CropBox|BleedBox|TrimBox|ArtBox
'groupXObject' => true,
'cache' => true,
'respectRotation' => true,
]);
$pdf->addPage();
$placed = $pdf->useImportedPage($tpl, 20, 20, 120, 80, [
'keepAspectRatio' => true,
'align' => 'CC', // TL|TC|TR|CL|CC|CR|BL|BC|BR
'clip' => true,
]);
```
## Append Pages from a Source Document
```php
// Append all pages.
$templates = $pdf->appendDocument($sourceId);
// Append only selected pages.
$templates = $pdf->appendDocument($sourceId, [1, 3, 5]);
// Add one imported page sized to the source page.
$tpl = $pdf->addPageFromImport($sourceId, 2);
```
## Import Examples
- Single page import: [examples/E065_import_single_page.php](../examples/E065_import_single_page.php)
- Full document append: [examples/E066_import_document_append.php](../examples/E066_import_document_append.php)
- Advanced N-up composition from imported pages: [examples/E067_import_page_region_nup.php](../examples/E067_import_page_region_nup.php)
## Import Limitations and Fidelity Notes
- Form and annotation semantics are not merged into editable destination structures; pages are imported as Form XObjects.
- Digital signatures in source files are not preserved as valid signatures in the destination output.
- Encrypted source PDFs are currently not importable with the bundled parser backend. Password-like options are accepted by the import API, but encrypted inputs fail with an explicit actionable exception.
- For multi-stream page contents, import normalizes by decoding and concatenating stream bytes; this can change low-level byte representation while preserving rendered appearance in typical cases.
- Transparency-group behavior is conformance-aware: when transparency is disallowed by the active PDF mode (for example PDF/X-1a or PDF/X-3), import suppresses transparency groups to remain compliant.
- Setting `groupXObject` to `false` can reduce output size, but may change compositing on source pages that rely on transparency blending.
+90
View File
@@ -0,0 +1,90 @@
# Remote Resources and fileOptions
Back to root overview: [README.md](../README.md#in-depth-documentation)
By default `tc-lib-pdf` **does not fetch any remote URLs**. Images, fonts, and SVG files referenced by HTTP or HTTPS are blocked unless you explicitly allow the originating hosts. Local file reads are split between internal library IO and markup-originated resource loads, with separate allowlists.
Remote access is controlled by the optional `$fileOptions` array passed as the last argument to the `Tcpdf` constructor (and forwarded to `initClassObjects()`).
## Allowing Remote Hosts
```php
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: 'mm',
fileOptions: [
'allowedHosts' => ['cdn.example.com', 'assets.myapp.io'],
],
);
```
Only the listed host names are permitted. Any attempt to load a resource from an unlisted host is silently blocked. Supply an explicit allowlist rather than a wildcard to limit the attack surface when user-supplied URLs might reach this code path.
## Restricting Local Paths
`allowedPaths` controls which local path prefixes may be read by the shared file helper for internal library IO, such as fonts, and other explicit file operations. If you omit it, the library computes a default set of trusted roots that covers the system temp directory, the `K_PATH_FONTS` directory when that constant is defined, the tc-lib-pdf package root, and, when the package is installed as a Composer dependency, the `vendor/tecnickcom` directory holding the sibling tecnickcom packages. Paths outside these roots, for example project asset directories, are rejected until you list them here. The helper that returns these defaults is `Com\\Tecnick\\Pdf\\Base::defaultFileAllowedPaths()`.
`markupAllowedPaths` controls which local path prefixes may be read when resources are referenced by rendered HTML, CSS, or SVG markup. If you omit it, the library reuses an explicit `allowedPaths` value when one is provided; otherwise it computes a stricter default that excludes the system temp directory. The helper that returns those stricter defaults is `Com\\Tecnick\\Pdf\\Base::defaultMarkupAllowedPaths()`.
Windows absolute paths with drive letters are supported as long as they are absolute and match the trusted root after path normalization. You can provide them in native form (`C:\\...`) or normalized form (`C:/...`), but using the same canonical format for both the allowlist and the resource path is safest.
```php
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: 'mm',
fileOptions: [
'allowedPaths' => [
(string) realpath(sys_get_temp_dir()),
(string) realpath(__DIR__ . '/../storage/pdf-assets'),
(string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'),
],
'markupAllowedPaths' => [
(string) realpath(__DIR__ . '/../storage/pdf-assets'),
(string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'),
],
],
);
```
Supplying `allowedPaths` replaces the internal-file defaults instead of merging with them. Include every local directory the PDF run needs for trusted internal operations, such as image fixtures, custom font directories, or cache-backed assets.
Supplying `markupAllowedPaths` replaces the stricter markup defaults instead of merging with them. Include only directories that should be reachable from rendered markup.
## All fileOptions Keys
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `allowedHosts` | `string[]` | `[]` (none) | Host names the library may fetch over HTTP/HTTPS. Remote loading is disabled when this list is empty. |
| `allowedPaths` | `string[]` | Computed internal trusted roots | Local path prefixes permitted for internal file reads. Passing this key replaces the defaults, so include all required temp/cache/font directories. |
| `markupAllowedPaths` | `string[]` | Explicit `allowedPaths` value, else stricter computed roots | Local path prefixes permitted for file reads triggered by rendered HTML, CSS, or SVG markup. Passing this key replaces the markup defaults. |
| `maxRemoteSize` | `int` | `52428800` (50 MiB) | Maximum bytes accepted for a single remote download. Requests exceeding this limit are aborted. |
| `curlopts` | `array<int, bool\|int\|string>` | `[]` | Per-request cURL options (keyed by `CURLOPT_*` constants) merged on top of the built-in defaults. |
| `defaultCurlOpts` | `array<int, bool\|int\|string>` | `null` | Replaces the built-in default cURL option set entirely. Omit this key to keep the safe defaults. |
| `fixedCurlOpts` | `array<int, bool\|int\|string>` | `null` | cURL options that are always enforced and cannot be overridden by `curlopts` - useful for pinning TLS settings in locked-down environments. |
## Example: Pinning TLS and Setting a Short Timeout
```php
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: 'mm',
fileOptions: [
'allowedHosts' => ['cdn.example.com'],
'allowedPaths' => [
(string) realpath(sys_get_temp_dir()),
(string) realpath(__DIR__ . '/../storage/pdf-assets'),
(string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'),
],
'markupAllowedPaths' => [
(string) realpath(__DIR__ . '/../storage/pdf-assets'),
(string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'),
],
'maxRemoteSize' => 10 * 1024 * 1024, // 10 MiB
'curlopts' => [
CURLOPT_TIMEOUT => 10,
CURLOPT_CONNECTTIMEOUT => 5,
],
'fixedCurlOpts' => [
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
],
],
);
```
+120
View File
@@ -0,0 +1,120 @@
# Standards and Conformance
Back to root overview: [README.md](../README.md#in-depth-documentation)
## PDF/A Archival
`tc-lib-pdf` supports PDF/A output for long-term archival workflows (ISO 19005). Pass the mode string as the `mode` argument to the `Tcpdf` constructor:
```php
// PDF/A-1b (default conformance level when suffix is omitted)
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa1');
// Explicit conformance levels
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa1a'); // PDF/A-1a
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa1b'); // PDF/A-1b
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa2a'); // PDF/A-2a
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa2b'); // PDF/A-2b
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa2u'); // PDF/A-2u
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa3a'); // PDF/A-3a
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa3b'); // PDF/A-3b
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa3u'); // PDF/A-3u
```
| Mode suffix | Conformance | Unicode ToUnicode | Tagged structure |
|-------------|-------------|-------------------|------------------|
| `a` | Level A | required | required |
| `b` | Level B | required | not required |
| `u` | Level U (parts 2/3 only) | required | not required |
PDF/A-3 supports embedding arbitrary file attachments (for example XML invoice payloads). This is the basis for **Factur-X / ZUGFeRD** workflows - embed the structured XML in a PDF/A-3 document and register the relationship via XMP metadata:
```php
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfa3');
// ... build document ...
$pdf->addContentAsEmbeddedFile(
file: 'factur-x.xml',
content: $invoiceXML,
mime: 'text/xml',
afrel: \Com\Tecnick\Pdf\AFRelationship::Alternative,
);
$pdf->setCustomXMP('x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas.rdf:Bag', $xmpBag);
```
Runnable example (invoice with embedded Factur-X XML): [examples/E001_invoice.php](../examples/E001_invoice.php).
## PDF/X Conformance
`tc-lib-pdf` supports multiple PDF/X profiles for print-exchange workflows. Pass the mode string as the `mode` argument to the `Tcpdf` constructor:
```php
// Generic PDF/X alias (maps to the library's baseline print-exchange workflow)
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfx');
// Specific variants
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfx1a'); // PDF/X-1a:2003
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfx3'); // PDF/X-3:2003
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfx4'); // PDF/X-4:2010
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfx5'); // PDF/X-5g:2010
```
Each variant automatically applies the appropriate conformance constraints:
| Mode | Min PDF version | Transparency | Process colors | GTS_PDFXVersion |
|------|-----------------|--------------|----------------|-----------------|
| `pdfx` / `pdfx3` | 1.3 | blocked | CMYK forced | PDF/X-3:2003 |
| `pdfx1a` | 1.3 | blocked | CMYK forced | PDF/X-1a:2003 |
| `pdfx4` | 1.6 | allowed | unrestricted | PDF/X-4:2010 |
| `pdfx5` | 1.6 | allowed | unrestricted | PDF/X-5g:2010 |
All PDF/X modes suppress encryption and JavaScript (not permitted by the ISO 15930 standard).
Runnable examples: [examples/E010_pdfx.php](../examples/E010_pdfx.php) through [examples/E014_pdfx5.php](../examples/E014_pdfx5.php).
## PDF/UA Accessibility
`tc-lib-pdf` supports tagged PDF output conforming to PDF/UA (ISO 14289). Pass the mode string as the `mode` argument to the `Tcpdf` constructor:
```php
// Generic PDF/UA alias
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfua');
// Specific parts
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfua1'); // PDF/UA-1 (PDF 1.7)
$pdf = new \Com\Tecnick\Pdf\Tcpdf(mode: 'pdfua2'); // PDF/UA-2 (PDF 2.0)
```
When a PDF/UA mode is active the library automatically:
- Writes a `StructTreeRoot` with a `ParentTree` that maps every page to its tagged content blocks
- Emits `MarkInfo << /Marked true >>` in the document catalog
- Sets `/Lang` (defaults to `en-US` when not explicitly provided)
- Forces `ViewerPreferences /DisplayDocTitle true`
- Maps HTML heading elements (`h1`-`h6`) to PDF structure roles `H1`-`H6` with level-clamping to prevent skipped heading levels
- Tags text content with MCIDs and wraps each run in the appropriate structure element (`P`, `H1`-`H6`, `Link`, etc.)
- Tags `<img>` elements as `Figure` with their `alt` attribute written as `/Alt` in the structure element
- Emits `ActualText` entries for ligatures and special glyphs so text extraction and screen readers work correctly
- Provides Artifact marked-content helpers for non-semantic content (`beginArtifact()`, `endArtifact()`, `addArtifactContent()`)
To provide the document language explicitly:
```php
$pdf->setLanguageArray(['a_meta_language' => 'de-DE']);
```
To tag decorative or repeated content as Artifact (for example headers, footers, and page numbers):
```php
$pid = $pdf->addPage()['pid'];
$headerOperators = $pdf->graph->getLine(10, 10, 200, 10);
$pdf->addArtifactContent($headerOperators, $pid, 'Pagination', 'Header');
$footerText = $pdf->getTextCell('Page 1', 180, 280, 20, 5);
$pdf->addArtifactContent($footerText, $pid, 'Pagination', 'Footer');
```
In PDF/UA mode, the built-in `defaultPageContent()` page-number footer is emitted as `Artifact` with
`/Type /Pagination /Subtype /Footer`.
Runnable examples: [examples/E015_pdfua.php](../examples/E015_pdfua.php) through [examples/E017_pdfua2.php](../examples/E017_pdfua2.php).
+264
View File
@@ -0,0 +1,264 @@
<?php
/**
* E000_overview.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example_overview.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 000');
$pdf->setTitle('tc-lib-pdf Visual Showcase');
$pdf->setKeywords('TCPDF tc-lib-pdf showcase barcode qr svg image transparency gradients');
$pdf->setPDFFilename('000_overview.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$titleFont = $pdf->font->insert($pdf->pon, 'dejavusans', 'B', 30);
$subtitleFont = $pdf->font->insert($pdf->pon, 'dejavusans', '', 13);
$bodyFont = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
$smallFont = $pdf->font->insert($pdf->pon, 'dejavusans', '', 8);
$imgdir = __DIR__ . '/images';
// ----------
// Add first page
$page01 = $pdf->addPage();
$pdf->setBookmark(name: 'Visual showcase', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'blue');
$pageW = (float) $page01['width'];
$pageH = (float) $page01['height'];
$cardStyle = [
'all' => [
'lineWidth' => 0.35,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'rgb(193,216,242)',
'fillColor' => 'rgb(255,255,255)',
],
];
$lineStyle = [
'all' => [
'lineWidth' => 0.25,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'rgb(147,196,227)',
'fillColor' => 'rgb(147,196,227)',
],
];
$barcodeStyle = [
'lineWidth' => 0,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'black',
'fillColor' => 'black',
];
$textOnDark = $pdf->color->getPdfColor('rgb(244,250,255)');
$textOnLight = $pdf->color->getPdfColor('rgb(22,37,61)');
// Background atmosphere.
$pdf->page->addContent($pdf->graph->getLinearGradient(0, 0, $pageW, $pageH, 'rgb(10,27,60)', 'rgb(0,171,193)', [
0,
0,
1,
1,
]));
$pdf->page->addContent($pdf->graph->getAlpha(0.12));
for ($lineY = -20; $lineY <= 330; $lineY += 12) {
$pdf->page->addContent($pdf->graph->getLine(0, (float) $lineY, $pageW, (float) ($lineY + 95), $lineStyle));
}
$pdf->page->addContent($pdf->graph->getAlpha(0.28));
$pdf->page->addContent($pdf->graph->getCircle(167, 46, 40, 0, 360, 'F', [
'all' => [
'lineWidth' => 0,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'rgb(255,255,255)',
'fillColor' => 'rgb(255,255,255)',
],
]));
$pdf->page->addContent($pdf->graph->getAlpha(1.0));
// Main title block.
$pdf->page->addContent($textOnDark);
$pdf->page->addContent($titleFont['out']);
$pdf->addHTMLCell(
html: '<div style="font-size:30pt; font-weight:bold; color:rgb(244,250,255);">tc-lib-pdf</div>',
posx: 15,
posy: 18,
width: 120,
);
$pdf->page->addContent($subtitleFont['out']);
$pdf->addHTMLCell(
html: '<div style="font-size:13pt; line-height:1.3; color:rgb(244,250,255);">Modern PDF generation in pure PHP: vectors, images, barcodes, and rich page composition.</div>',
posx: 15,
posy: 34,
width: 125,
);
// Left feature card.
$pdf->page->addContent($pdf->graph->getAlpha(0.90));
$pdf->page->addContent($pdf->graph->getRect(12, 56, 89, 137, 'DF', $cardStyle));
$pdf->page->addContent($pdf->graph->getAlpha(1.0));
$pdf->page->addContent($textOnLight);
$pdf->page->addContent($subtitleFont['out']);
$pdf->addHTMLCell(
html: '<div style="font-size:13pt; color:rgb(22,37,61);">Cool Features</div>',
posx: 18,
posy: 64,
width: 72,
);
$pdf->page->addContent($bodyFont['out']);
$features =
'<ul style="font-size:10pt; color:rgb(22,37,61); margin:0; padding-left:12pt;">'
. '<li>Native SVG rendering</li>'
. '<li>JPEG/PNG with transparency</li>'
. '<li>1D/2D barcodes (QR, DataMatrix)</li>'
. '<li>Gradients and vector primitives</li>'
. '<li>HTML/CSS text layout</li>'
. '<li>Digital signatures and PDF/X, PDF/UA support</li>'
. '</ul>';
$pdf->addHTMLCell(html: $features, posx: 18, posy: 74, width: 79);
$logoFile = $imgdir . '/tcpdf_logo.jpg';
if (\is_file($logoFile)) {
$logoId = $pdf->image->add($logoFile);
$pdf->page->addContent($pdf->image->getSetImage($logoId, 153, 10, 40, 13, $pageH));
}
// SVG art panel.
$pdf->page->addContent($pdf->graph->getAlpha(0.92));
$pdf->page->addContent($pdf->graph->getRect(106, 70, 92, 121, 'DF', $cardStyle));
$pdf->page->addContent($pdf->graph->getAlpha(1.0));
$svgFile = $imgdir . '/testsvgblend.svg';
if (\is_file($svgFile)) {
$svgObj = $pdf->addSVG(img: $svgFile, posx: 111, posy: 76, width: 82, height: 108, pageheight: $pageH);
$pdf->page->addContent($pdf->getSetSVG(soid: $svgObj));
}
$pdf->page->addContent($textOnLight);
$pdf->page->addContent($smallFont['out']);
$pdf->addHTMLCell(
html: '<div style="font-size:8pt; text-align:center; color:rgb(22,37,61);">SVG graphic rendered directly into PDF vectors</div>',
posx: 111,
posy: 182,
width: 82,
);
// QR code panel.
$pdf->page->addContent($pdf->graph->getAlpha(0.92));
$pdf->page->addContent($pdf->graph->getRect(12, 198, 186, 76, 'DF', $cardStyle));
$pdf->page->addContent($pdf->graph->getAlpha(1.0));
$pdf->page->addContent($textOnLight);
$pdf->page->addContent($subtitleFont['out']);
$pdf->addHTMLCell(
html: '<div style="font-size:13pt; color:rgb(22,37,61);">Scan to Explore</div>',
posx: 18,
posy: 206,
width: 92,
);
$pdf->page->addContent($bodyFont['out']);
$pdf->addHTMLCell(
html: '<div style="font-size:10pt; line-height:1.25; color:rgb(22,37,61);">This QR code points to the official TCPDF project page:</div>',
posx: 18,
posy: 216,
width: 118,
);
$pdf->addHTMLCell(
html: '<div style="font-size:10pt; color:rgb(22,37,61);"><a href="https://tcpdf.org">https://tcpdf.org</a></div>',
posx: 18,
posy: 228,
width: 118,
);
$pdf->page->addContent($pdf->getBarcode(
type: 'QRCODE,H',
code: 'https://tcpdf.org',
posx: 145,
posy: 206,
width: 45,
height: 45,
padding: [0, 0, 0, 0],
style: $barcodeStyle,
));
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
+380
View File
@@ -0,0 +1,380 @@
<?php
/**
* E001_invoice.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfa3,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 001');
$pdf->setTitle('Example Invoice Factur-X 1.07 / ZUGFeRD 2.3');
$pdf->setKeywords('TCPDF tc-lib-pdf invoice Factur-X ZUGFeRD e-invoice XML');
$pdf->setPDFFilename('001_invoice.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
// ----------
// Factur-X 1.07 / ZUGFeRD 2.3
$pageF01 = $pdf->addPage();
$pdf->setBookmark(name: 'Factur', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: '');
$pdf->page->addContent($bfont1['out']);
$invoiceHTML = '
<h2 style="color:#003366;">INVOICE EXAMPLE</h2>
<table border="0" cellspacing="0" cellpadding="2" style="width:100%;">
<tr>
<td style="width:50%; vertical-align:top;">
<b>Seller</b><br/>
ELEKTRON Industrieservice GmbH<br/>
Geschäftsführer Egon Schrempp Amtsgericht Stuttgart HRB 1234<br/>
Erfurter Strasse 13<br/>
74465 Demoort, DE<br/>
VAT: DE136695976
</td>
<td style="width:50%; vertical-align:top; text-align:right;">
<b>Invoice No.:</b> 181301674<br/>
<b>Invoice Date:</b> 15.11.2024<br/>
<b>Delivery Date:</b> 01.11.2024<br/>
<b>Order Ref.:</b> per Mail vom 01.09.2024<br/>
<b>Project:</b> 13130162
</td>
</tr>
</table>
<br/>
<b>Bill To:</b><br/>
ConsultingService GmbH<br/>
Attn: Liselotte Müller-Lüdenscheidt<br/>
Musterstr. 18<br/>
76138 Karlsruhe, DE<br/>
<br/>
<table border="1" cellspacing="0" cellpadding="3" style="width:100%; text-align:right;">
<tr style="background-color:#003366; color:#ffffff;">
<th style="text-align:left; width:8%;">Pos.</th>
<th style="text-align:left; width:40%;">Description</th>
<th style="width:12%;">Qty</th>
<th style="width:10%;">Unit</th>
<th style="width:15%;">Unit Price</th>
<th style="width:15%;">Amount</th>
</tr>
<tr>
<td style="text-align:left;">01</td>
<td style="text-align:left;">TGA Obermonteur/Monteur<br/><i>Beamermontage</i></td>
<td>3</td>
<td>HUR</td>
<td>43.20</td>
<td>129.60</td>
</tr>
<tr>
<td style="text-align:left;">02</td>
<td style="text-align:left;">Beamer-Deckenhalterung<br/><i>Außerhalb Angebot</i></td>
<td>1</td>
<td>H87</td>
<td>122.50</td>
<td>122.50</td>
</tr>
</table>
<br/>
<table border="0" cellspacing="0" cellpadding="2" style="width:100%;">
<tr>
<td style="width:60%;"></td>
<td style="width:40%;">
<table border="1" cellspacing="0" cellpadding="3" style="width:100%; text-align:right;">
<tr><td style="text-align:left;">Net Total</td><td>EUR 252.10</td></tr>
<tr><td style="text-align:left;">VAT 19%</td><td>EUR 47.90</td></tr>
<tr><td style="text-align:left;"><b>Grand Total</b></td><td><b>EUR 300.00</b></td></tr>
<tr><td style="text-align:left;">Prepaid</td><td>EUR 0.00</td></tr>
<tr><td style="text-align:left;"><b>Due Amount</b></td><td><b>EUR 300.00</b></td></tr>
</table>
</td>
</tr>
</table>
<br/>
<b>Payment Terms:</b> Zahlbar sofort rein netto<br/>
<b>Bank:</b> IBAN DE91100000000123456789<br/>
<b>Payment Reference:</b> Rechnung 181301674<br/>
<br/>
<i>Factur-X 1.07 / ZUGFeRD 2.3 — embedded XML attachment: factur-x.xml</i>
';
$pdf->page->addContent($pdf->getHTMLCell($invoiceHTML, 15, 15, 180));
$invoiceXML = <<<XML
<?xml version='1.0' encoding='UTF-8' ?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocumentContext>
<ram:BusinessProcessSpecifiedDocumentContextParameter>
<ram:ID>Baurechnung</ram:ID>
</ram:BusinessProcessSpecifiedDocumentContextParameter>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>181301674</ram:ID>
<ram:TypeCode>204</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20241115</udt:DateTimeString>
</ram:IssueDateTime>
<ram:IncludedNote>
<ram:Content>Rapport-Nr.: 42389 vom 01.11.2024
Im 2. OG BT1 Besprechungsraum eine Beamerhalterung an die Decke montiert. Dafür eine Deckenplatte ausgesägt. Beamerhalterung zur Montage auseinander gebaut. Ein Stromkabel für den Beamer, ein HDMI Kabel und ein VGA Kabel durch die Halterung gezogen. Beamerhalterung wieder zusammengebaut und Beamer montiert. Beamer verkabelt und ausgerichtet. Decke geschlossen.</ram:Content>
</ram:IncludedNote>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>01</ram:LineID>
<ram:IncludedNote>
<ram:Content>01 Beamermontage
Für die doppelte Verlegung, falls erforderlich.</ram:Content>
</ram:IncludedNote>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>TGA Obermonteur/Monteur</ram:Name>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:GrossPriceProductTradePrice>
<ram:ChargeAmount>43.2</ram:ChargeAmount>
</ram:GrossPriceProductTradePrice>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>43.2</ram:ChargeAmount>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="HUR">3</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>129.6</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>02</ram:LineID>
<ram:IncludedNote>
<ram:Content>02 Außerhalb Angebot</ram:Content>
</ram:IncludedNote>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Beamer-Deckenhalterung</ram:Name>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:GrossPriceProductTradePrice>
<ram:ChargeAmount>122.5</ram:ChargeAmount>
</ram:GrossPriceProductTradePrice>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>122.5</ram:ChargeAmount>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="H87">1</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>122.5</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:ApplicableHeaderTradeAgreement>
<ram:BuyerReference>Liselotte Müller-Lüdenscheidt</ram:BuyerReference>
<ram:SellerTradeParty>
<ram:ID>549910</ram:ID>
<ram:Name>ELEKTRON Industrieservice GmbH</ram:Name>
<ram:Description>Geschäftsführer Egon Schrempp Amtsgericht Stuttgart HRB 1234</ram:Description>
<ram:PostalTradeAddress>
<ram:PostcodeCode>74465</ram:PostcodeCode>
<ram:LineOne>Erfurter Strasse 13</ram:LineOne>
<ram:CityName>Demoort</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">DE136695976</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:ID>16259</ram:ID>
<ram:Name>ConsultingService GmbH</ram:Name>
<ram:PostalTradeAddress>
<ram:PostcodeCode>76138</ram:PostcodeCode>
<ram:LineOne>Musterstr. 18</ram:LineOne>
<ram:CityName>Karlsruhe</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
</ram:BuyerTradeParty>
<ram:SellerOrderReferencedDocument>
<ram:IssuerAssignedID>per Mail vom 01.09.2024</ram:IssuerAssignedID>
</ram:SellerOrderReferencedDocument>
<ram:AdditionalReferencedDocument>
<ram:IssuerAssignedID>13130162</ram:IssuerAssignedID>
<ram:URIID>#ef=Aufmass.png</ram:URIID>
<ram:TypeCode>916</ram:TypeCode>
</ram:AdditionalReferencedDocument>
<ram:AdditionalReferencedDocument>
<ram:IssuerAssignedID>42389</ram:IssuerAssignedID>
<ram:URIID>#ef=ElektronRapport_neu-red.pdf</ram:URIID>
<ram:TypeCode>916</ram:TypeCode>
</ram:AdditionalReferencedDocument>
<ram:SpecifiedProcuringProject>
<ram:ID>13130162</ram:ID>
<ram:Name>Projekt</ram:Name>
</ram:SpecifiedProcuringProject>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeDelivery>
<ram:ActualDeliverySupplyChainEvent>
<ram:OccurrenceDateTime>
<udt:DateTimeString format="102">20241101</udt:DateTimeString>
</ram:OccurrenceDateTime>
</ram:ActualDeliverySupplyChainEvent>
</ram:ApplicableHeaderTradeDelivery>
<ram:ApplicableHeaderTradeSettlement>
<ram:PaymentReference>Rechnung 181301674</ram:PaymentReference>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementPaymentMeans>
<ram:TypeCode>58</ram:TypeCode>
<ram:PayeePartyCreditorFinancialAccount>
<ram:IBANID>DE91100000000123456789</ram:IBANID>
</ram:PayeePartyCreditorFinancialAccount>
</ram:SpecifiedTradeSettlementPaymentMeans>
<ram:ApplicableTradeTax>
<ram:CalculatedAmount>47.9</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>252.1</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradePaymentTerms>
<ram:Description>Zahlbar sofort rein netto</ram:Description>
</ram:SpecifiedTradePaymentTerms>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:LineTotalAmount>252.1</ram:LineTotalAmount>
<ram:ChargeTotalAmount>0</ram:ChargeTotalAmount>
<ram:AllowanceTotalAmount>0</ram:AllowanceTotalAmount>
<ram:TaxBasisTotalAmount>252.1</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">47.9</ram:TaxTotalAmount>
<ram:GrandTotalAmount>300</ram:GrandTotalAmount>
<ram:TotalPrepaidAmount>0</ram:TotalPrepaidAmount>
<ram:DuePayableAmount>300</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:ReceivableSpecifiedTradeAccountingAccount>
<ram:ID>420</ram:ID>
</ram:ReceivableSpecifiedTradeAccountingAccount>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>
XML;
// Factur-X/ZUGFeRD requires the XML to be embedded as text/xml with the
// AFRelationship set to "Alternative" and the exact file name "factur-x.xml".
$pdf->addContentAsEmbeddedFile(
file: 'factur-x.xml',
content: $invoiceXML,
mime: 'text/xml',
afrel: \Com\Tecnick\Pdf\AFRelationship::Alternative,
desc: 'Factur-X/ZUGFeRD electronic invoice',
);
$pdf->setCustomXMP('x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas.rdf:Bag', '<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>Factur-X PDFA Extension Schema</pdfaSchema:schema>
<pdfaSchema:namespaceURI>urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#</pdfaSchema:namespaceURI>
<pdfaSchema:prefix>fx</pdfaSchema:prefix>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>DocumentFileName</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>The name of the embedded XML document</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>DocumentType</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>The type of the hybrid document in capital letters, e.g. INVOICE or ORDER</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>Version</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>The actual version of the standard applying to the embedded XML document</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>ConformanceLevel</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>The conformance level of the embedded XML document</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>');
$pdf->setCustomXMP('x:xmpmeta.rdf:RDF', '<rdf:Description xmlns:fx="urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#" rdf:about="">
<fx:DocumentType>INVOICE</fx:DocumentType>
<fx:DocumentFileName>factur-x.xml</fx:DocumentFileName>
<fx:Version>1.0</fx:Version>
<fx:ConformanceLevel>EN 16931</fx:ConformanceLevel>
</rdf:Description>');
// =============================================================
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+149
View File
@@ -0,0 +1,149 @@
<?php
/**
* E002_font_dump.php
*
* @since 2026-04-19
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 002');
$pdf->setTitle('Font Dump and Glyph Coverage');
$pdf->setKeywords('TCPDF tc-lib-pdf fonts glyphs unicode dump metrics');
$pdf->setPDFFilename('002_font_dump.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$titlefont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 16);
// core font families and styles
$corefonts = [
['family' => 'courier', 'style' => ''],
['family' => 'courier', 'style' => 'B'],
['family' => 'courier', 'style' => 'I'],
['family' => 'courier', 'style' => 'BI'],
['family' => 'helvetica', 'style' => ''],
['family' => 'helvetica', 'style' => 'B'],
['family' => 'helvetica', 'style' => 'I'],
['family' => 'helvetica', 'style' => 'BI'],
['family' => 'times', 'style' => ''],
['family' => 'times', 'style' => 'B'],
['family' => 'times', 'style' => 'I'],
['family' => 'times', 'style' => 'BI'],
['family' => 'symbol', 'style' => ''],
['family' => 'zapfdingbats', 'style' => ''],
];
foreach ($corefonts as $fontcfg) {
$family = $fontcfg['family'];
$style = $fontcfg['style'];
$fontname = $family . ($style !== '' ? ' ' . $style : '');
$page = $pdf->addPage();
$pdf->setBookmark(
name: 'Font: ' . $fontname,
link: '',
level: 0,
page: -1,
posx: 0,
posy: 0,
fstyle: 'B',
color: '',
);
// title
$pdf->page->addContent($titlefont['out']);
$title = $pdf->getTextCell(
'FONT: ' . $fontname,
15,
15,
180,
10,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
);
$pdf->page->addContent($title);
// glyph table font
$charfont = $pdf->font->insert($pdf->pon, $family, $style, 16);
$pdf->page->addContent($charfont['out']);
// render character table (0-255) as a 16x16 grid
for ($i = 0; $i < 256; ++$i) {
$col = $i % 16;
$row = (int) ($i / 16);
$posx = 15 + ($col * 11.25);
$posy = 30 + ($row * 11.25);
$txt = $pdf->uniconv->chr($i);
$cell = $pdf->getTextCell(
$txt,
$posx,
$posy,
11.25,
11.25,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
);
$pdf->page->addContent($cell);
}
$pangram = $pdf->getTextCell(
'The quick brown fox jumps over the lazy dog',
15,
220,
180,
8,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
);
$pdf->page->addContent($pangram);
}
// =============================================================
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,345 @@
<?php
/**
* E003_persian_arabic.php
*
* @since 2026-04-19
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: false,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 003');
$pdf->setTitle('Persian and Arabic RTL Text Rendering');
$pdf->setKeywords('TCPDF tc-lib-pdf persian arabic rtl bidi unicode shaping');
$pdf->setPDFFilename('003_persian_arabic.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent(false);
$bfont = $pdf->font->insert($pdf->pon, 'dejavusans', '', 12);
// ----------
$txtEngPersian = 'PERSIAN: "The process of printing and correctly displaying Persian texts in digital systems requires full support for the Unicode standard and right-to-left layout. In this test text, all letters of the Persian alphabet, including specific letters like "Pe", "Che", "Zhe", and "Gaf", are included to verify their correct connectivity. Additionally, the use of Persian numbers (1234567890) and punctuation marks like commas (,) and semicolons (;) is essential for accurately measuring the alignment of lines and margins on multi-line pages."';
$txtPersian = 'فرآیند چاپ و نمایش صحیح متون فارسی در سیستم‌های دیجیتال نیازمند پشتیبانی کامل از استاندارد یونیکد و چیدمان راست‌به‌چپ است. در این متن آزمایشی، تمامی حروف الفبای فارسی از جمله حروف خاص مانند «پ»، «چ»، «ژ» و «گ» گنجانده شده‌اند تا صحت اتصال آن‌ها بررسی شود. همچنین استفاده از اعداد فارسی (۱۲۳۴۵۶۷۸۹۰) و علائم نگارشی مانند ویرگول (،) و نقطه‌ویرگول (؛) برای سنجش دقیق ترازبندی خطوط و حاشیه‌های صفحات چندخطی الزامی است.';
$htmlPersian = '<p style="direction:rtl;text-align:right;color:#003366;" lang="fa">فرآیند چاپ و نمایش صحیح متون فارسی در سیستم‌های دیجیتال نیازمند پشتیبانی کامل از استاندارد یونیکد و چیدمان راست‌به‌چپ است. در این متن آزمایشی، تمامی حروف الفبای فارسی از جمله حروف خاص مانند <span style="color:#660000;">«پ»، «چ»، «ژ» و «گ»</span> گنجانده شده‌اند تا صحت اتصال آن‌ها بررسی شود. همچنین استفاده از اعداد فارسی <span style="color:#006600;">(۱۲۳۴۵۶۷۸۹۰)</span> و علائم نگارشی مانند ویرگول (،) و نقطه‌ویرگول (؛) برای سنجش دقیق ترازبندی خطوط و حاشیه‌های صفحات چندخطی الزامی است.</p>';
$txtEngArabic = 'ARABIC: "The process of printing and correctly displaying Arabic texts on digital screens requires full support for the Unicode system and right-to-left formatting. This test text contains various letters, including elongated letters and different Hamzas (أ، إ، آ، ء، ئ، ؤ), to ensure the accuracy of letter connectivity. The text also includes Arabic numerals (1234567890) and punctuation marks such as the comma (،) and semicolon (؛) to verify the integrity of line alignment and multi-page margins."';
$txtArabic = 'إن عملية طباعة النصوص العربية وعرضها بشكل صحيح على الشاشات الرقمية تتطلب دعماً كاملاً لنظام اليونيكود والتنسيق من اليمين إلى اليسار. يحتوي هذا النص التجريبي على حروف متنوعة تشمل الحروف الممدودة والهمزات المختلفة مثل (أ، إ، آ، ء، ئ، ؤ) لضمان دقة اتصال الحروف ببعضها. كما يتضمن النص الأرقام العربية (١٢٣٤٥٦٧٨٩٠) وعلامات الترقيم كالفاصلة (،) والفاصلة المنقوطة (؛) للتحقق من سلامة محاذاة الأسطر وهوامش الصفحات المتعددة.';
$htmlArabic = '<p style="direction:rtl;text-align:right;color:#003366;" lang="ar">إن عملية طباعة النصوص العربية وعرضها بشكل صحيح على الشاشات الرقمية تتطلب دعماً كاملاً لنظام اليونيكود والتنسيق من اليمين إلى اليسار. يحتوي هذا النص التجريبي على حروف متنوعة تشمل الحروف الممدودة والهمزات المختلفة مثل <span style="color:#660000;">(أ، إ، آ، ء، ئ، ؤ)</span> لضمان دقة اتصال الحروف ببعضها. كما يتضمن النص الأرقام العربية <span style="color:#006600;">(١٢٣٤٥٦٧٨٩٠)</span> وعلامات الترقيم كالفاصلة (،) والفاصلة المنقوطة (؛) للتحقق من سلامة محاذاة الأسطر وهوامش الصفحات المتعددة.</p>';
$txtEngHebrew = 'HEBREW: "The process of printing and correctly displaying text in Hebrew on digital systems requires full support for the Unicode standard and right-to-left formatting. In this experimental text, all letters of the alphabet are included, including the final letters (ך, ם, ן, ף, ץ) to verify their correct display. Furthermore, the text integrates numbers (1234567890) and various punctuation marks like a comma (,) and semicolon (;) in order to examine line alignment and page margins in long paragraphs."';
$txtHebrew = 'תהליך ההדפסה וההצגה התקינה של טקסט בעברית במערכות דיגיטליות דורש תמיכה מלאה בתקן יוניקוד ובתצורת כתיבה מימין לשמאל. בטקסט ניסיוני זה נכללו כל אותיות האלפבית, כולל האותיות הסופיות (ך, ם, ן, ף, ץ) כדי לבדוק את תקינות התצוגה שלהן. כמו כן, הטקסט משלב מספרים (1234567890) וסימני פיסוק שונים כמו פסיק (,) ונקודה ופסיק (;) על מנת לבחון את יישור השורות ואת שולי העמודים בפסקאות ארוכות.';
$htmlHebrew = '<p style="direction:rtl;text-align:right;color:#003366;" lang="he">תהליך ההדפסה וההצגה התקינה של טקסט בעברית במערכות דיגיטליות דורש תמיכה מלאה בתקן יוניקוד ובתצורת כתיבה מימין לשמאל. בטקסט ניסיוני זה נכללו כל אותיות האלפבית, כולל האותיות הסופיות <span style="color:#660000;">(ך, ם, ן, ף, ץ)</span> כדי לבדוק את תקינות התצוגה שלהן. כמו כן, הטקסט משלב מספרים <span style="color:#006600;">(1234567890)</span> וסימני פיסוק שונים כמו פסיק (,) ונקודה ופסיק (;) על מנת לבחון את יישור השורות ואת שולי העמודים בפסקאות ארוכות.</p>';
// ----------
$psx = 15;
$lnw = 180;
// ----------
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->setRTL(false);
$pdf->addHTMLCell(
html: '<h1>RTL (Right-To-Left) Examples</h1><h2>1. getTextCell()</h2>',
posx: $psx,
posy: 15,
width: $lnw,
);
$pdf->setRTL(false);
$pdf->page->addContent($pdf->getTextCell(
txt: $txtEngPersian,
posx: $psx,
posy: 40,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->setRTL(true);
$pdf->page->addContent($pdf->getTextCell(
txt: $txtPersian,
posx: $psx,
posy: 80,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
));
$pdf->setRTL(false);
$pdf->page->addContent($pdf->getTextCell(
txt: $txtEngArabic,
posx: $psx,
posy: 120,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->setRTL(true);
$pdf->page->addContent($pdf->getTextCell(
txt: $txtArabic,
posx: $psx,
posy: 160,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
));
$pdf->setRTL(false);
$pdf->page->addContent($pdf->getTextCell(
txt: $txtEngHebrew,
posx: $psx,
posy: 200,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->setRTL(true);
$pdf->page->addContent($pdf->getTextCell(
txt: $txtHebrew,
posx: $psx,
posy: 240,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
));
// ----------
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->setRTL(false);
$pdf->addHTMLCell(html: '<h2>2. addTextCell()</h2>', posx: $psx, posy: 15, width: $lnw);
$pdf->setRTL(false);
$pdf->addTextCell(
txt: $txtEngPersian,
posx: $psx,
posy: 40,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
);
$pdf->setRTL(true);
$pdf->addTextCell(
txt: $txtPersian,
posx: $psx,
posy: 80,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
drawcell: false,
);
$pdf->setRTL(false);
$pdf->addTextCell(
txt: $txtEngArabic,
posx: $psx,
posy: 120,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
);
$pdf->setRTL(true);
$pdf->addTextCell(
txt: $txtArabic,
posx: $psx,
posy: 160,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
drawcell: false,
);
$pdf->setRTL(false);
$pdf->addTextCell(
txt: $txtEngHebrew,
posx: $psx,
posy: 200,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
);
$pdf->setRTL(true);
$pdf->addTextCell(
txt: $txtHebrew,
posx: $psx,
posy: 240,
width: $lnw,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
drawcell: false,
);
// ----------
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->setRTL(false);
$pdf->addHTMLCell(html: '<h2>3. getHTMLCell() - simple text</h2>', posx: $psx, posy: 15, width: $lnw);
$pdf->setRTL(false);
$pdf->page->addContent($pdf->getHTMLCell(html: $txtEngPersian, posx: $psx, posy: 40, width: $lnw));
$pdf->setRTL(true);
$pdf->page->addContent($pdf->getHTMLCell(html: $txtPersian, posx: $psx, posy: 80, width: $lnw));
$pdf->setRTL(false);
$pdf->page->addContent($pdf->getHTMLCell(html: $txtEngArabic, posx: $psx, posy: 120, width: $lnw));
$pdf->setRTL(true);
$pdf->page->addContent($pdf->getHTMLCell(html: $txtArabic, posx: $psx, posy: 160, width: $lnw));
$pdf->setRTL(false);
$pdf->page->addContent($pdf->getHTMLCell(html: $txtEngHebrew, posx: $psx, posy: 200, width: $lnw));
$pdf->setRTL(true);
$pdf->page->addContent($pdf->getHTMLCell(html: $txtHebrew, posx: $psx, posy: 240, width: $lnw));
// ----------
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->setRTL(false);
$pdf->addHTMLCell(html: '<h2>4. addtHTMLCell() - simple text</h2>', posx: $psx, posy: 15, width: $lnw);
$pdf->setRTL(false);
$pdf->addHTMLCell(html: $txtEngPersian, posx: $psx, posy: 40, width: $lnw);
$pdf->setRTL(true);
$pdf->addHTMLCell(html: $txtPersian, posx: $psx, posy: 80, width: $lnw);
$pdf->setRTL(false);
$pdf->addHTMLCell(html: $txtEngArabic, posx: $psx, posy: 120, width: $lnw);
$pdf->setRTL(true);
$pdf->addHTMLCell(html: $txtArabic, posx: $psx, posy: 160, width: $lnw);
$pdf->setRTL(false);
$pdf->addHTMLCell(html: $txtEngHebrew, posx: $psx, posy: 200, width: $lnw);
$pdf->setRTL(true);
$pdf->addHTMLCell(html: $txtHebrew, posx: $psx, posy: 240, width: $lnw);
// ----------
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->setRTL(false);
$pdf->addHTMLCell(html: '<h2>5. getHTMLCell() - HTML fragments</h2>', posx: $psx, posy: 15, width: $lnw);
$pdf->page->addContent($pdf->getHTMLCell(html: $txtEngPersian, posx: $psx, posy: 40, width: $lnw));
$pdf->page->addContent($pdf->getHTMLCell(html: $htmlPersian, posx: $psx, posy: 80, width: $lnw));
$pdf->page->addContent($pdf->getHTMLCell(html: $txtEngArabic, posx: $psx, posy: 120, width: $lnw));
$pdf->page->addContent($pdf->getHTMLCell(html: $htmlArabic, posx: $psx, posy: 160, width: $lnw));
$pdf->page->addContent($pdf->getHTMLCell(html: $txtEngHebrew, posx: $psx, posy: 200, width: $lnw));
$pdf->page->addContent($pdf->getHTMLCell(html: $htmlHebrew, posx: $psx, posy: 240, width: $lnw));
// ----------
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->addHTMLCell(html: '<h2>6. addtHTMLCell() - HTML fragments</h2>', posx: $psx, posy: 15, width: $lnw);
$pdf->addHTMLCell(html: $txtEngPersian, posx: $psx, posy: 40, width: $lnw);
$pdf->addHTMLCell(html: $htmlPersian, posx: $psx, posy: 80, width: $lnw);
$pdf->addHTMLCell(html: $txtEngArabic, posx: $psx, posy: 120, width: $lnw);
$pdf->addHTMLCell(html: $htmlArabic, posx: $psx, posy: 160, width: $lnw);
$pdf->addHTMLCell(html: $txtEngHebrew, posx: $psx, posy: 200, width: $lnw);
$pdf->addHTMLCell(html: $htmlHebrew, posx: $psx, posy: 240, width: $lnw);
// ----------
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->addHTMLCell(html: '<h2>7. addtHTMLCell() - HTML fragments with images</h2>', posx: $psx, posy: 15, width: $lnw);
$rtlHtmlWithImage = '<p style="direction:rtl;text-align:right;color:#003366;" lang="fa"><img src="images/tcpdf_logo.jpg" alt="TCPDF logo" height="3mm" border="0" /> فرآیند چاپ و نمایش صحیح متون فارسی در سیستم‌های دیجیتال نیازمند پشتیبانی کامل از استاندارد یونیکد و چیدمان راست‌به‌چپ است. در این متن آزمایشی، تمامی حروف الفبای فارسی از جمله حروف خاص مانند <img src="images/tcpdf_logo.jpg" alt="TCPDF logo" height="3mm" border="0" /> <span style="color:#660000;">«پ»، «چ»، «ژ» و «گ»</span> گنجانده شده‌اند تا صحت اتصال آن‌ها بررسی شود. همچنین استفاده از اعداد فارسی <img src="images/tcpdf_logo.jpg" alt="TCPDF logo" height="3mm" border="0" /> <span style="color:#006600;">(۱۲۳۴۵۶۷۸۹۰)</span> و علائم نگارشی مانند ویرگول (،) و نقطه‌ویرگول (؛) برای سنجش دقیق ترازبندی خطوط و حاشیه‌های صفحات چندخطی الزامی است. <img src="images/tcpdf_logo.jpg" alt="TCPDF logo" height="3mm" border="0" /></p>';
$pdf->addHTMLCell(html: $rtlHtmlWithImage, posx: $psx, posy: 40, width: $lnw);
// ----------
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->addHTMLCell(
html: '<h2>8. addHTMLCell() - RTL paragraph spanning a page break</h2>',
posx: $psx,
posy: 15,
width: $lnw,
);
// A long multi-fragment RTL paragraph placed low on the page so that it overflows
// the page bottom and must continue on the next page. This exercises Stage 2e (RTL
// region/page continuation): the inter-fragment engine must flow the remaining
// right-to-left lines into the next page, keeping them top-down and right-to-left,
// instead of falling back to the forward-cursor renderer. See
// PLAN_RTL_HTML_FRAGMENTS.md increment 4.
$arabicSpanCycle =
$txtArabic
. ' <span style="color:#660000;">'
. $txtArabic
. '</span> '
. $txtArabic
. ' <span style="color:#006600;">'
. $txtArabic
. '</span> ';
$htmlArabicLong =
'<p style="direction:rtl;text-align:right;color:#003366;" lang="ar">' . \str_repeat($arabicSpanCycle, 2) . '</p>';
$pdf->addHTMLCell(html: $htmlArabicLong, posx: $psx, posy: 180, width: $lnw);
// =============================================================
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,107 @@
<?php
/**
* E004_page_formats.php
*
* @since 2026-04-19
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 004');
$pdf->setTitle('Page Formats and Orientation');
$pdf->setKeywords('TCPDF tc-lib-pdf page formats orientation A4 Letter custom size');
$pdf->setPDFFilename('004_page_formats.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent(false);
// ----------
// Get all page formats from the Format class
$formats = \Com\Tecnick\Pdf\Page\Format::FORMAT;
// Insert monospace font
$bfont = $pdf->font->insert($pdf->pon, 'courier', '', 8);
// Conversion factor: points to mm and inches
$mmRatio = \Com\Tecnick\Pdf\Page\Format::UNITRATIO['mm'];
$inRatio = \Com\Tecnick\Pdf\Page\Format::UNITRATIO['in'];
// Loop through all formats
foreach ($formats as $formatName => $dimensions) {
// Add a new page using the named format.
$page = $pdf->addPage(['format' => $formatName]);
// Get dimensions.
$widthPt = $dimensions[0];
$heightPt = $dimensions[1];
// Calculate dimensions in mm and inches.
$widthMm = $widthPt / $mmRatio;
$heightMm = $heightPt / $mmRatio;
$widthIn = $widthPt / $inRatio;
$heightIn = $heightPt / $inRatio;
// Print multiline text at top-left corner.
$x = 2;
$y = 2;
$w = 50;
// Define multiline HTML text to display.
$html = '<h2>' . $formatName . '</h2>';
if ($page['width'] > ($w + (2 * $x))) {
$html .= '<table border="1" cellspacing="0" cellpadding="2" style="text-align:right">
<tr><th>unit</th><th>width</th><th>height</th></tr>';
$html .= \sprintf('<tr><td>mm</td><td>%.3f</td><td>%.3f</td></tr>', $widthMm, $heightMm);
$html .= \sprintf('<tr><td>in</td><td>%.3f</td><td>%.3f</td></tr>', $widthIn, $heightIn);
$html .= \sprintf('<tr><td>pt</td><td>%.3f</td><td>%.3f</td></tr>', $widthPt, $heightPt);
$html .= '</table>';
}
$pdf->page->addContent($bfont['out']);
$pdf->page->addContent($pdf->getHTMLCell($html, $x, $y, $w));
}
// =============================================================
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,321 @@
<?php
/**
* E005_header_footer.php
*
* @since 2026-04-19
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// ----------
/**
* Custom PDF class with a repeating page header and footer.
*
* Extends Tcpdf and overrides defaultPageContent() so that every
* new page automatically receives a branded header and a numbered footer.
* The mechanism is activated by calling enableDefaultPageContent(true).
*/
class PdfWithHeaderFooter extends \Com\Tecnick\Pdf\Tcpdf
{
/** Horizontal margin used by the header and footer bands (mm). */
private const HF_MARGIN = 10.0;
/** Height of the header band (mm). */
private const HEADER_H = 12.0;
/** Height of the footer band (mm). */
private const FOOTER_H = 10.0;
/** Document title shown left-aligned in the header. */
private string $headerTitle = '';
/** Subtitle / date shown right-aligned in the header. */
private string $headerSubtitle = '';
/**
* Set the text displayed in the page header.
*
* @param string $title Left-aligned title.
* @param string $subtitle Right-aligned subtitle (e.g. date or company name).
*/
public function setHeaderText(string $title, string $subtitle): void
{
$this->headerTitle = $title;
$this->headerSubtitle = $subtitle;
}
/**
* Generates the repeating header and footer for every page.
*
* This method is called automatically by setPageContext() whenever a new
* page is added, provided enableDefaultPageContent(true) has been called.
*
* @param int $pid Page index (0-based).
*
* @return string Raw PDF stream prepended to the page content.
*/
public function defaultPageContent(int $pid = -1): string
{
if ($pid < 0) {
$pid = $this->page->getPageId();
}
// Insert the default font once and cache it for subsequent pages.
if ($this->defaultfont === null) {
$this->defaultfont = $this->font->insert($this->pon, 'helvetica', '', 9);
}
$page = $this->page->getPage($pid);
$pw = $page['width'];
$ph = $page['height'];
$lm = self::HF_MARGIN; // left margin x
$rm = $pw - self::HF_MARGIN; // right margin x
$tw = $pw - (2 * self::HF_MARGIN); // usable band width
$lineStyle = [
'lineWidth' => 0.25,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#555555',
];
$out = '';
// ---- HEADER ------------------------------------------------
$headerY = self::HF_MARGIN;
$headerOut = $this->graph->getStartTransform();
$headerOut .= $this->defaultfont['out'];
// Title left-aligned, bold
if ($this->headerTitle !== '') {
$bfontBold = $this->font->insert($this->pon, 'helvetica', 'B', 10);
$headerOut .= $bfontBold['out'];
$headerOut .= $this->color->getPdfColor('#1a3a6b');
$headerOut .= $this->getTextCell(
txt: $this->headerTitle,
posx: $lm,
posy: $headerY,
width: $tw * 0.65,
height: self::HEADER_H,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$headerOut .= $this->defaultfont['out'];
}
// Subtitle right-aligned
if ($this->headerSubtitle !== '') {
$headerOut .= $this->color->getPdfColor('#555555');
$headerOut .= $this->getTextCell(
txt: $this->headerSubtitle,
posx: $lm + ($tw * 0.65),
posy: $headerY,
width: $tw * 0.35,
height: self::HEADER_H,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
);
}
// Separator line below the header
$headerLineY = $headerY + self::HEADER_H;
$headerOut .= $this->graph->getLine($lm, $headerLineY, $rm, $headerLineY, $lineStyle);
$headerOut .= $this->graph->getStopTransform();
$out .= $this->beginArtifact('Pagination', 'Header');
$out .= $headerOut;
$out .= $this->endArtifact();
// ---- FOOTER ------------------------------------------------
$footerLineY = $ph - self::HF_MARGIN - self::FOOTER_H;
$footerOut = $this->graph->getStartTransform();
$footerOut .= $this->defaultfont['out'];
// Separator line above the footer
$footerOut .= $this->graph->getLine($lm, $footerLineY, $rm, $footerLineY, $lineStyle);
// Page number centred
$footerOut .= $this->color->getPdfColor('#555555');
$footerOut .= $this->getTextCell(
txt: 'Page ' . ($pid + 1),
posx: $lm,
posy: $footerLineY,
width: $tw,
height: self::FOOTER_H,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
);
$footerOut .= $this->graph->getStopTransform();
$out .= $this->beginArtifact('Pagination', 'Footer');
$out .= $footerOut;
$out .= $this->endArtifact();
return $out;
}
}
// ----------
// main PDF object using the custom subclass
$pdf = new PdfWithHeaderFooter(
'mm', // string $unit = 'mm',
true, // bool $isunicode = true,
false, // bool $subsetfont = false,
true, // bool $compress = true,
'pdfua1', // string $mode = 'pdfua1',
null, // ?ObjEncrypt $objEncrypt = null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 005');
$pdf->setTitle('Header & Footer Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example header footer');
$pdf->setPDFFilename('005_header_footer.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->setLanguageArray(['a_meta_language' => 'en-US']);
// Set the text that will appear in the header on every page.
$pdf->setHeaderText('My Document Title', \date('Y-m-d'));
// Enable automatic header/footer: calls defaultPageContent() for every new page.
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 11);
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 13);
// ----------
// Add first page
$page01 = $pdf->addPage();
$pdf->setBookmark(name: 'Page 1', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'blue');
// Page content starts below the header band (HF_MARGIN + HEADER_H + separator ≈ 25 mm).
$contentY = 28.0;
$pdf->page->addContent($bfontB['out']);
$title1 = $pdf->getTextCell(
txt: 'Page 1 — Custom Repeating Header & Footer',
posx: 10,
posy: $contentY,
width: 190,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($title1);
$pdf->page->addContent($bfont['out']);
$body1 = 'This is the first page of a document that demonstrates how to add a custom page header and footer that automatically repeats on every new page using tc-lib-pdf.
The header displays the document title (left) and the date (right), separated from the page content by a thin horizontal rule. The footer shows the page number centred at the bottom, also separated by a horizontal rule.
This is achieved by subclassing \Com\Tecnick\Pdf\Tcpdf and overriding the public defaultPageContent() method, then activating the mechanism once with enableDefaultPageContent(true) before adding any pages.
Because setPageContext() calls defaultPageContent() automatically every time addPage() is used, the header and footer appear on every page without any additional code in the page-building section.';
$txt1 = $pdf->getTextCell(
txt: $body1,
posx: 10,
posy: $contentY + 10,
width: 190,
height: 0,
offset: 15,
linespace: 2,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->page->addContent($txt1);
// ----------
// Add second page
$page02 = $pdf->addPage();
$pdf->setBookmark(name: 'Page 2', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'green');
$pdf->page->addContent($bfontB['out']);
$title2 = $pdf->getTextCell(
txt: 'Page 2 — Continued Content',
posx: 10,
posy: $contentY,
width: 190,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($title2);
$pdf->page->addContent($bfont['out']);
$body2 = 'This is the second page. The header and footer are present here too without any manual intervention — the overridden defaultPageContent() method takes care of them automatically.
Any further pages added with addPage() would also receive the same header and footer, making the approach suitable for multi-page reports, invoices, or any document type that requires consistent page decoration.';
$txt2 = $pdf->getTextCell(
txt: $body2,
posx: 10,
posy: $contentY + 10,
width: 190,
height: 0,
offset: 15,
linespace: 2,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->page->addContent($txt2);
// ----------
// Output the PDF
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+59
View File
@@ -0,0 +1,59 @@
<?php
/**
* E006_minimal.php
*
* @since 2026-04-19
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf();
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 006');
$pdf->setTitle('Minimal Example');
$pdf->setKeywords('TCPDF tc-lib-pdf minimal hello world basic');
$pdf->setPDFFilename('006_minimal.pdf');
// Insert font
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// Add first page
$page = $pdf->addPage();
// Add font to the page
$pdf->page->addContent($bfont['out']);
// some HTML content
$html = '<h1>Hello, PDF!</h1><p>Generated with tc-lib-pdf.</p>';
// render the HTML content
$pdf->addHTMLCell(html: $html, posx: 15, posy: 15, width: 180);
// Get the PDF content
$rawpdf = $pdf->getOutPDFString();
// Render the PDF content
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,196 @@
<?php
/**
* E007_signature_basic.php
*
* @since 2026-04-21
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf();
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 007');
$pdf->setTitle('PAdES B-B Digital Signature Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example signature pades pades-b-b');
$pdf->setPDFFilename('007_signature_basic.pdf');
// Insert font before adding the first page.
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 11);
// Add a page and a simple signed-content block.
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$text = 'This document is signed with a PAdES-BASELINE-B signature (/ETSI.CAdES.detached).';
$textCell = $pdf->getTextCell(
txt: $text,
posx: 15,
posy: 20,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($textCell);
$text2 = 'The CMS carries the ESS signing-certificate-v2 attribute required by ETSI EN 319 142-1.';
$textCell2 = $pdf->getTextCell(
txt: $text2,
posx: 15,
posy: 27,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($textCell2);
$certPath = \realpath(__DIR__ . '/data/cert/tcpdf.crt');
if ($certPath === false) {
throw new \RuntimeException('Missing signing certificate: examples/data/cert/tcpdf.crt');
}
$cert = 'file://' . $certPath;
// Preferred fluent API: $pdf->signature()->configure(...). The 'profile' option
// selects the PAdES baseline level (legacy | pades-b-b | pades-b-t | pades-b-lt |
// pades-b-lta); the pre-facade $pdf->setSignature([...]) method still works and
// takes the same array. The signing process and this demo's limitations are
// rendered into the document itself (see the explanatory block added below).
$pdf->signature()->configure([
'profile' => 'pades-b-b',
'digest_algorithm' => 'sha256', // sha256 | sha384 | sha512
'cert_type' => 2,
'info' => [
'ContactInfo' => 'https://github.com/tecnickcom/tc-lib-pdf',
'Location' => 'Demo Office',
'Name' => 'tc-lib-pdf',
'Reason' => 'PAdES-BASELINE-B signature example',
],
'password' => '',
'privkey' => $cert,
'signcert' => $cert,
]);
// Visible signature field plus one extra empty approval signature field.
$pdf->signature()->appearance()->place(posx: 15, posy: 35, width: 75, height: 20, page: -1, name: 'PrimarySignature');
// Optional custom appearance stream for the signature widget (/AP /N).
// Coordinate system starts from the lower-left corner of the widget rectangle.
$sigW = 75.0;
$sigH = 20.0;
$sigTopY = $page['height'] - $sigH;
// styles controls cell fill/border; set font and text color explicitly for AP text.
$sigAppearance = $bfont['out'];
$sigAppearance .= $pdf->color->getPdfColor('rgb(15%,15%,15%)');
$sigAppearance .= $pdf->getTextCell(
txt: 'Digitally signed by tc-lib-pdf',
posx: 0,
posy: $sigTopY,
width: $sigW,
height: $sigH,
offset: 2.5,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [
'all' => [
'fillColor' => 'rgb(92%,96%,100%)',
'lineColor' => 'rgb(20%,32%,60%)',
'lineWidth' => 1.2,
],
],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
);
$pdf->signature()->appearance()->stream(stream: $sigAppearance);
// Exposed object ID of the signature widget annotation.
// This can be useful for low-level workflows or custom QA checks.
$signatureWidgetObjId = $pdf->signature()->widgetObjectId();
$text3 = 'Primary signature widget object ID: ' . $signatureWidgetObjId;
$textCell3 = $pdf->getTextCell(
txt: $text3,
posx: 95,
posy: 35,
width: 100,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($textCell3);
// A second, empty signature field. It is a valid unsigned placeholder (no CMS is
// written to it) that a later party can sign in an approval workflow. Only the
// PrimarySignature field above carries a signature in this example's output.
$pdf->signature()->emptyField(posx: 15, posy: 60, width: 75, height: 20, page: -1, name: 'ApprovalSignature');
// Render the signing process and the demo's limitations into the document so the
// output PDF is self-documenting (this text is page content, not part of the
// signed appearance stream).
$explainHtml = <<<HTML
<h2 style="font-size:11pt;color:#1f2a33;">How this PAdES-BASELINE-B signature is produced</h2>
<p style="font-size:9pt;color:#1f2a33;">The library reserves a <code>/Contents</code> placeholder inside a
<code>/SubFilter /ETSI.CAdES.detached</code> signature field, hashes the document over the two
<code>/ByteRange</code> spans that surround the placeholder, and embeds a detached CMS (PKCS#7). The
CAdES SignerInfo carries the mandatory content-type and message-digest attributes plus the ESS
signing-certificate-v2 attribute required by ETSI EN 319 142-1. Per the baseline the CMS signing-time
attribute is intentionally omitted; the claimed time is carried by the <code>/M</code> dictionary entry
instead. The produced CMS verifies cryptographically against the <code>/ByteRange</code> bytes.</p>
<h2 style="font-size:11pt;color:#7a1f1f;">Limitations of this demo</h2>
<ul style="font-size:9pt;color:#1f2a33;">
<li>B-B has no timestamp, so the <code>/M</code> signing time is a self-asserted claim and is not
cryptographically protected. Use <code>pades-b-t</code> (example E008) for a trusted signing time
backed by an RFC 3161 TSA.</li>
<li>The bundled certificate <code>examples/data/cert/tcpdf.crt</code> is self-signed. The CMS is
cryptographically valid, but the signer identity does not chain to a trusted root, so viewers report
the signature as valid with an unknown or untrusted signer. Sign with a CA-issued certificate for a
trusted identity verdict.</li>
</ul>
HTML;
$pdf->addHTMLCell(html: $explainHtml, posx: 15, posy: 85, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,184 @@
<?php
/**
* E008_signature_timestamp.php
*
* @since 2026-04-21
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf();
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 008');
$pdf->setTitle('PAdES B-T Signature Timestamp Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example signature timestamp tsa pades pades-b-t');
$pdf->setPDFFilename('008_signature_timestamp.pdf');
// Insert font before adding the first page.
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 11);
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$text = 'This document is a PAdES-BASELINE-T signature: B-B plus an RFC 3161 signature timestamp.';
$textCell = $pdf->getTextCell(
txt: $text,
posx: 15,
posy: 20,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($textCell);
$text2 = 'Timestamping requires outbound HTTPS connectivity to the configured TSA endpoint.';
$textCell2 = $pdf->getTextCell(
txt: $text2,
posx: 15,
posy: 27,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($textCell2);
$certPath = \realpath(__DIR__ . '/data/cert/tcpdf.crt');
if ($certPath === false) {
throw new \RuntimeException('Missing signing certificate: examples/data/cert/tcpdf.crt');
}
$cert = 'file://' . $certPath;
// PAdES-BASELINE-T: configure() selects the profile, timestamp() adds the TSA;
// both calls are chainable. The signing process and this demo's limitations are
// rendered into the document itself (see the explanatory block added below).
$pdf
->signature()
->configure([
'profile' => 'pades-b-t',
'digest_algorithm' => 'sha256',
'cert_type' => 2,
'info' => [
'ContactInfo' => 'https://github.com/tecnickcom/tc-lib-pdf',
'Location' => 'Demo Office',
'Name' => 'tc-lib-pdf',
'Reason' => 'PAdES-BASELINE-T timestamped signature example',
],
'password' => '',
'privkey' => $cert,
'signcert' => $cert,
])
->timestamp([
'enabled' => true,
// Public demo endpoint. For production use your trusted TSA service.
'host' => 'https://freetsa.org/tsr',
'username' => '',
'password' => '',
'cert' => '',
'hash_algorithm' => 'sha256',
'policy_oid' => '',
'nonce_enabled' => true,
'timeout' => 30,
'verify_peer' => true,
]);
$pdf
->signature()
->appearance()
->place(posx: 15, posy: 35, width: 90, height: 20, page: -1, name: 'TimestampedSignature');
// Optional appearance stream to clearly indicate timestamped signature intent.
$sigW = 90.0;
$sigH = 20.0;
$sigTopY = $page['height'] - $sigH;
$sigAppearance = $bfont['out'];
$sigAppearance .= $pdf->color->getPdfColor('rgb(20%,20%,20%)');
$sigAppearance .= $pdf->getTextCell(
txt: 'PAdES B-T Timestamped Signature',
posx: 0,
posy: $sigTopY,
width: $sigW,
height: $sigH,
offset: 3.0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [
'all' => [
'fillColor' => 'rgb(96%,96%,90%)',
'lineColor' => 'rgb(35%,35%,10%)',
'lineWidth' => 1.0,
],
],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
);
$pdf->signature()->appearance()->stream(stream: $sigAppearance);
// Render the signing process and the demo's limitations into the document so the
// output PDF is self-documenting (this text is page content, not part of the
// signed appearance stream).
$explainHtml = <<<HTML
<h2 style="font-size:11pt;color:#1f2a33;">How this PAdES-BASELINE-T signature is produced</h2>
<p style="font-size:9pt;color:#1f2a33;">A B-T signature is a B-B signature (example E007) plus a signature
timestamp. The library first builds the CAdES CMS, then sends the SignerInfo signature bytes to the TSA
and embeds the returned RFC 3161 token in the CMS as the <code>id-aa-signatureTimeStampToken</code>
unsigned attribute. The output CMS verifies over the <code>/ByteRange</code> bytes and carries a genuine
TSA token that binds a trusted time to the signature.</p>
<h2 style="font-size:11pt;color:#7a1f1f;">Limitations of this demo</h2>
<ul style="font-size:9pt;color:#1f2a33;">
<li>Timestamping performs a live RFC 3161 request to the TSA over HTTPS. The endpoint used here is the
public freetsa.org demo service; use your own trusted TSA in production. The timestamp is mandatory
for <code>pades-b-t</code> and above, so if the TSA is unreachable or rejects the request the signing
step throws a signing exception and no PDF is produced.</li>
<li>The bundled certificate is self-signed, so (as in E007) the signature is cryptographically valid
but the signer identity is not trusted by viewers. The embedded timestamp itself is a real
freetsa.org token and validates.</li>
</ul>
HTML;
$pdf->addHTMLCell(html: $explainHtml, posx: 15, posy: 65, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,216 @@
<?php
/**
* E009_signature_ltv.php
*
* @since 2026-04-21
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf();
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 009');
$pdf->setTitle('PAdES B-LT Long-Term Validation Signature Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example signature ltv dss vri pades pades-b-lt');
$pdf->setPDFFilename('009_signature_ltv.pdf');
// Insert font before adding the first page.
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 11);
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$text = 'This document is a PAdES-BASELINE-LT signature: B-T plus a Document Security Store (DSS).';
$textCell = $pdf->getTextCell(
txt: $text,
posx: 15,
posy: 20,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($textCell);
$text2 =
'The DSS/VRI embed the certificate and, when its CA exposes reachable OCSP/CRL responders, '
. 'the revocation data needed to verify offline long after expiry.';
$textCell2 = $pdf->getTextCell(
txt: $text2,
posx: 15,
posy: 27,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($textCell2);
$certPath = \realpath(__DIR__ . '/data/cert/tcpdf.crt');
if ($certPath === false) {
throw new \RuntimeException('Missing signing certificate: examples/data/cert/tcpdf.crt');
}
$cert = 'file://' . $certPath;
// PAdES-BASELINE-LT: a B-T signature (so a TSA timestamp is required) plus the
// LTV material (DSS/VRI). The signing process and this demo's limitations are
// rendered into the document itself (see the explanatory block added below).
$pdf
->signature()
->configure([
'profile' => 'pades-b-lt',
'digest_algorithm' => 'sha256',
'cert_type' => 2,
'info' => [
'ContactInfo' => 'https://github.com/tecnickcom/tc-lib-pdf',
'Location' => 'Demo Office',
'Name' => 'tc-lib-pdf',
'Reason' => 'PAdES-BASELINE-LT (DSS/VRI) signature example',
],
'password' => '',
'privkey' => $cert,
'signcert' => $cert,
'ltv' => [
'enabled' => true,
'embed_ocsp' => true,
'embed_crl' => true,
'embed_certs' => true,
'include_dss' => true,
'include_vri' => true,
],
])
->timestamp([
'enabled' => true,
// Public demo endpoint. For production use your trusted TSA service.
'host' => 'https://freetsa.org/tsr',
'username' => '',
'password' => '',
'cert' => '',
'hash_algorithm' => 'sha256',
'policy_oid' => '',
'nonce_enabled' => true,
'timeout' => 30,
'verify_peer' => true,
]);
$pdf->signature()->appearance()->place(posx: 15, posy: 35, width: 90, height: 20, page: -1, name: 'LTVSignature');
// Attach a reusable Form XObject as signature appearance using the official API.
// Any imported page can be used as a visual signature stamp.
$stampSource = __DIR__ . '/data/pdf/E006_example_minimal.pdf';
if (\is_readable($stampSource)) {
$sourceId = $pdf->setImportSourceFile($stampSource);
$tpl = $pdf->importPage(sourceId: $sourceId, pageNum: 1);
$pdf->signature()->appearance()->xobject($tpl->getXobjId());
} else {
// Fallback appearance stream when the optional stamp source is unavailable.
$sigW = 90.0;
$sigH = 20.0;
$sigTopY = $page['height'] - $sigH;
$sigAppearance = $bfont['out'];
$sigAppearance .= $pdf->color->getPdfColor('rgb(15%,15%,15%)');
$sigAppearance .= $pdf->getTextCell(
txt: 'PAdES B-LT signature (DSS/VRI)',
posx: 0,
posy: $sigTopY,
width: $sigW,
height: $sigH,
offset: 3.0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [
'all' => [
'fillColor' => 'rgb(94%,97%,92%)',
'lineColor' => 'rgb(20%,40%,20%)',
'lineWidth' => 1.0,
],
],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
);
$pdf->signature()->appearance()->stream(stream: $sigAppearance);
}
$widgetObjId = $pdf->signature()->widgetObjectId();
$text3 = 'LTV signature widget object ID: ' . $widgetObjId;
$textCell3 = $pdf->getTextCell(
txt: $text3,
posx: 15,
posy: 60,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->page->addContent($textCell3);
// Render the signing process and the demo's limitations into the document so the
// output PDF is self-documenting (this text is page content, not part of the
// signed appearance stream).
$explainHtml = <<<HTML
<h2 style="font-size:11pt;color:#1f2a33;">How this PAdES-BASELINE-LT signature is produced</h2>
<p style="font-size:9pt;color:#1f2a33;">A B-LT signature is a B-T signature (a TSA timestamp is required)
plus long-term validation material. The signing process writes two incremental revisions: first the
CAdES signature with its embedded RFC 3161 signature timestamp (B-T), then a Document Security Store
(<code>/DSS</code> with a <code>/VRI</code> map keyed by the uppercase SHA-1 of the final signature
<code>/Contents</code>) carrying the validation material. OCSP/CRL retrieval follows the certificate's
AIA and CRL-DP extensions so a validator can check the chain offline long after the certificate
expires.</p>
<h2 style="font-size:11pt;color:#7a1f1f;">Limitations of this demo</h2>
<ul style="font-size:9pt;color:#1f2a33;">
<li>A reachable TSA is required (live HTTPS request); if it is unreachable or rejects the request the
signing step throws a signing exception and no PDF is produced.</li>
<li>The bundled self-signed demo certificate exposes neither an OCSP responder nor a CRL distribution
point, so the DSS embeds only the certificate bytes (no revocation data) and a validator reports B-T
with a DSS present rather than a full B-LT. Missing responders are tolerated and do not abort signing.
Sign with a CA-issued certificate whose OCSP/CRL responders are reachable at signing time to obtain a
full B-LT verdict.</li>
</ul>
HTML;
$pdf->addHTMLCell(html: $explainHtml, posx: 15, posy: 70, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+56
View File
@@ -0,0 +1,56 @@
<?php
/**
* E010_pdfx.php
*
* @since 2026-04-25
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
require __DIR__ . '/../vendor/autoload.php';
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfx,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 010');
$pdf->setTitle('PDF/X Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example pdfx');
$pdf->setPDFFilename('010_pdfx.pdf');
$font = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$page = $pdf->addPage();
$pdf->page->addContent($font['out']);
$html =
'<h1>PDF/X</h1>'
. '<p>Mode: pdfx</p>'
. '<p>This generic alias currently maps to the library\'s baseline PDF/X print workflow.</p>'
. '<p>Highlights: PDF/X identification metadata, print-oriented output intent handling, '
. 'CMYK-oriented process colors, and suppression of interactive actions that are not allowed '
. 'in a print-exchange profile.</p>';
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+45
View File
@@ -0,0 +1,45 @@
<?php
/**
* E011_pdfx1a.php
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
require __DIR__ . '/../vendor/autoload.php';
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfx1a,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 011');
$pdf->setTitle('PDF/X-1a Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example pdfx1a');
$pdf->setPDFFilename('011_pdfx1a.pdf');
$font = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$pdf->addPage();
$pdf->page->addContent($font['out']);
$html =
'<h1>PDF/X-1a</h1>'
. '<p>Mode: pdfx1a</p>'
. '<p>PDF/X-1a targets an older, press-safe workflow centered on CMYK and spot colors.</p>'
. '<p>Highlights: minimum PDF 1.3 output, transparency disabled, encryption disabled, '
. 'and interactive annotations/actions suppressed for a classic print handoff.</p>';
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+46
View File
@@ -0,0 +1,46 @@
<?php
/**
* E012_pdfx3.php
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
require __DIR__ . '/../vendor/autoload.php';
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfx3,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 012');
$pdf->setTitle('PDF/X-3 Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example pdfx3');
$pdf->setPDFFilename('012_pdfx3.pdf');
$font = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$pdf->addPage();
$pdf->page->addContent($font['out']);
$html =
'<h1>PDF/X-3</h1>'
. '<p>Mode: pdfx3</p>'
. '<p>PDF/X-3 keeps the print-exchange restrictions of early PDF/X while allowing '
. 'color-managed workflows beyond device CMYK.</p>'
. '<p>Highlights: minimum PDF 1.3 output, output-intent identification, no transparency, '
. 'and no interactive actions that would conflict with print-only delivery.</p>';
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+45
View File
@@ -0,0 +1,45 @@
<?php
/**
* E013_pdfx4.php
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
require __DIR__ . '/../vendor/autoload.php';
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfx4,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 013');
$pdf->setTitle('PDF/X-4 Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example pdfx4');
$pdf->setPDFFilename('013_pdfx4.pdf');
$font = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$pdf->addPage();
$pdf->page->addContent($font['out']);
$html =
'<h1>PDF/X-4</h1>'
. '<p>Mode: pdfx4</p>'
. '<p>PDF/X-4 modernizes print exchange by allowing live transparency in a color-managed workflow.</p>'
. '<p>Highlights: minimum PDF 1.6 output, PDF/X-4 identification metadata, transparency retained, '
. 'encryption disabled, and interactive actions still suppressed for print conformance.</p>';
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+45
View File
@@ -0,0 +1,45 @@
<?php
/**
* E014_pdfx5.php
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
require __DIR__ . '/../vendor/autoload.php';
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfx5,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 014');
$pdf->setTitle('PDF/X-5 Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example pdfx5');
$pdf->setPDFFilename('014_pdfx5.pdf');
$font = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$pdf->addPage();
$pdf->page->addContent($font['out']);
$html =
'<h1>PDF/X-5</h1>'
. '<p>Mode: pdfx5</p>'
. '<p>PDF/X-5 builds on the PDF/X-4 family for more advanced print workflows and external references.</p>'
. '<p>Highlights: minimum PDF 1.6 output, transparency retained, modern PDF/X identification, '
. 'and the same print-safe suppression of unsupported interactive actions.</p>';
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+133
View File
@@ -0,0 +1,133 @@
<?php
/**
* E015_pdfua.php
*
* Demonstrates HTML auto-tagging in 'pdfua' (generic alias) mode.
*
* Every HTML element rendered by addHTMLCell() is automatically mapped to the
* corresponding PDF structure role — no beginStructElem() calls required.
* Compare with E063 which builds the same structure tree manually via the
* lower-level addTextCell() / beginStructElem() API.
*
* Auto-tagged roles exercised here:
* h1-h3 → H1-H3 (heading hierarchy)
* p → P (paragraph)
* blockquote → BlockQuote
* pre → Code
* ul / ol → L (list; li → LI > Lbl + LBody auto-generated)
* figure → Figure
* img[alt] → Figure + /Alt (alt text written to struct-elem dictionary)
* figcaption → Caption
* table → Table (tr → TR, th → TH, td → TD, caption → Caption)
* a → Link
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
require __DIR__ . '/../vendor/autoload.php';
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfua,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setTitle('PDF/UA Auto-Tagged HTML');
$pdf->setSubject('tc-lib-pdf example: 015 — pdfua mode HTML tagging showcase');
$pdf->setKeywords('TCPDF tc-lib-pdf pdfua tagged PDF accessibility HTML auto-tag structure');
$pdf->setPDFFilename('E015_pdfua.pdf');
$pdf->setLanguage(code: 'en-US');
$font = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
// PDF/UA requires every font to be embedded. <code>/<pre> default to the
// non-embedded standard-14 Courier, so register an embedded monospace font and
// select it for those elements.
$pdf->font->insert($pdf->pon, 'dejavusansmono', '', 10);
$pdf->setHTMLMonospaceFont('dejavusansmono');
$pdf->addPage();
$pdf->page->addContent($font['out']);
$imgPath = __DIR__ . '/images/tcpdf_logo.jpg';
$html =
'<h1>PDF/UA — Auto-Tagged HTML Showcase</h1>'
. '<p>Mode: <strong>pdfua</strong> (generic alias). This document demonstrates every HTML'
. ' element that <code>addHTMLCell()</code> auto-tags with a PDF structure role.'
. ' No <code>beginStructElem()</code> calls are required: the library maps each HTML tag'
. ' to its PDF role automatically when a PDF/UA mode is active. The generic'
. ' <em>pdfua</em> alias behaves identically to <em>pdfua1</em> for structure-tree'
. ' output and defaults to PDF 1.7.</p>'
. '<h2>1 — Heading and Paragraph Roles</h2>'
. '<h3>H3 Sub-heading</h3>'
. '<p>The <code>h1</code>-<code>h6</code> tags map to PDF roles <strong>H1-H6</strong>.'
. ' The <code>p</code> tag maps to <strong>P</strong>.'
. ' Heading levels are clamped to prevent skipped levels in the structure tree,'
. ' satisfying the PDF/UA requirement that no heading level may be skipped.</p>'
. '<h2>2 — Block Roles</h2>'
. '<blockquote>&ldquo;Tagged PDF is the foundation of accessible PDF. Without a'
. ' well-formed structure tree, automated tools and assistive technologies cannot'
. ' reliably interpret document content.&rdquo; &mdash; ISO&#160;14289-1 rationale</blockquote>'
. '<p>The <code>blockquote</code> tag maps to <strong>BlockQuote</strong>.</p>'
. '<h2>3 — List Structure (L &gt; LI &gt; Lbl + LBody)</h2>'
. '<p>Both <code>ul</code> and <code>ol</code> map to the <strong>L</strong> role.'
. ' Each <code>li</code> auto-generates <strong>LI &gt; {Lbl, LBody}</strong> children:</p>'
. '<ul>'
. '<li>Unordered item &mdash; <code>ul</code>/<code>li</code> tags produce L/LI structure</li>'
. '<li>The bullet character is placed in a <strong>Lbl</strong> child element</li>'
. '<li>Body text is wrapped in a <strong>LBody</strong> child element</li>'
. '</ul>'
. '<ol>'
. '<li>Ordered item &mdash; the number is auto-generated as the <strong>Lbl</strong></li>'
. '<li>Body text is again auto-wrapped in <strong>LBody</strong></li>'
. '<li>Nesting: L &gt; LI &gt; {Lbl, LBody}</li>'
. '</ol>'
. '<h2>4 — Figure, Image Alt-Text, and Caption</h2>'
. '<figure>'
. '<img src="'
. $imgPath
. '" alt="TCPDF logo: blue text on white background" width="89" height="30" />'
. '<figcaption>Figure 1 &mdash; TCPDF logo. The <code>alt</code> attribute is written as'
. ' <strong>/Alt</strong> in the Figure structure-element dictionary so screen readers'
. ' can describe the image. The <code>figcaption</code> tag maps to'
. ' <strong>Caption</strong>.</figcaption>'
. '</figure>'
. '<h2>5 — Table Structure (Table &gt; TR &gt; TH/TD)</h2>'
. '<table border="1" cellspacing="0" cellpadding="3">'
. '<caption>Table 1 &mdash; HTML tags and their PDF/UA structure roles (auto-tagged by addHTMLCell)</caption>'
. '<tr><th>HTML tag</th><th>PDF structure role</th><th>Category</th></tr>'
. '<tr><td>h1&ndash;h6</td><td>H1&ndash;H6</td><td>Heading</td></tr>'
. '<tr><td>p</td><td>P</td><td>Body</td></tr>'
. '<tr><td>blockquote</td><td>BlockQuote</td><td>Body</td></tr>'
. '<tr><td>pre</td><td>Code</td><td>Body</td></tr>'
. '<tr><td>ul, ol</td><td>L</td><td>List</td></tr>'
. '<tr><td>li</td><td>LI + Lbl + LBody</td><td>List</td></tr>'
. '<tr><td>figure</td><td>Figure</td><td>Illustration</td></tr>'
. '<tr><td>img (with alt)</td><td>Figure + /Alt</td><td>Illustration</td></tr>'
. '<tr><td>figcaption, caption</td><td>Caption</td><td>Illustration</td></tr>'
. '<tr><td>table</td><td>Table</td><td>Table</td></tr>'
. '<tr><td>tr</td><td>TR</td><td>Table</td></tr>'
. '<tr><td>td</td><td>TD</td><td>Table</td></tr>'
. '<tr><td>th</td><td>TH</td><td>Table</td></tr>'
. '<tr><td>a</td><td>Link</td><td>Inline</td></tr>'
. '</table>'
. '<h2>6 — Hyperlink (Link Role)</h2>'
. '<p>This sentence contains a'
. ' <a href="https://github.com/tecnickcom/tc-lib-pdf" style="text-decoration:none;">hyperlink to the tc-lib-pdf repository</a>;'
. ' the <code>a</code> element is tagged with the <strong>Link</strong> structure role'
. ' automatically. The link text is accessible to screen readers via the structure tree.</p>';
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+168
View File
@@ -0,0 +1,168 @@
<?php
/**
* E016_pdfua1.php
*
* Demonstrates HTML auto-tagging in 'pdfua1' (PDF/UA-1, ISO 14289-1) mode.
*
* PDF/UA-1 is the original accessible-PDF profile, standardised as ISO 14289-1:2012
* and built on PDF 1.7. When this mode is active, tc-lib-pdf:
* - Writes StructTreeRoot, ParentTree, and MarkInfo /Marked true
* - Forces ViewerPreferences /DisplayDocTitle true
* - Sets /Lang from the document language (default en-US)
* - Clamps heading levels to prevent skipped H1-H6 levels
* - Emits ActualText for ligatures and special glyphs
*
* Every HTML element rendered by addHTMLCell() is automatically mapped to the
* corresponding PDF structure role — no beginStructElem() calls required.
* Compare with E063 which builds the same structure tree manually.
*
* Auto-tagged roles exercised here:
* h1-h3 → H1-H3 (heading hierarchy)
* p → P (paragraph)
* blockquote → BlockQuote
* pre → Code
* ul / ol → L (list; li → LI > Lbl + LBody auto-generated)
* figure → Figure
* img[alt] → Figure + /Alt (alt text written to struct-elem dictionary)
* figcaption → Caption
* table → Table (tr → TR, th → TH, td → TD, caption → Caption)
* a → Link
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
require __DIR__ . '/../vendor/autoload.php';
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfua1,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setTitle('PDF/UA-1 Auto-Tagged HTML');
$pdf->setSubject('tc-lib-pdf example: 016 — pdfua1 mode HTML tagging showcase');
$pdf->setKeywords('TCPDF tc-lib-pdf pdfua1 PDF/UA-1 ISO14289 tagged PDF accessibility HTML auto-tag');
$pdf->setPDFFilename('E016_pdfua1.pdf');
$pdf->setLanguage(code: 'en-US');
$font = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
// PDF/UA requires every font to be embedded. <code>/<pre> default to the
// non-embedded standard-14 Courier, so register an embedded monospace font and
// select it for those elements.
$pdf->font->insert($pdf->pon, 'dejavusansmono', '', 10);
$pdf->setHTMLMonospaceFont('dejavusansmono');
$pdf->addPage();
$pdf->page->addContent($font['out']);
$imgPath = __DIR__ . '/images/tcpdf_logo.jpg';
$html =
'<h1>PDF/UA-1 — Auto-Tagged HTML Showcase</h1>'
. '<p>Mode: <strong>pdfua1</strong> (ISO&#160;14289-1, PDF&#160;1.7). This document'
. ' demonstrates every HTML element that <code>addHTMLCell()</code> auto-tags with a'
. ' PDF/UA-1 structure role. PDF/UA-1 is the original accessible-PDF standard: it mandates'
. ' a tagged structure tree, document language, <code>MarkInfo /Marked true</code>,'
. ' and <code>ViewerPreferences /DisplayDocTitle true</code>. All of these are written'
. ' automatically when the <em>pdfua1</em> mode is active.</p>'
. '<h2>1 — Heading and Paragraph Roles</h2>'
. '<h3>H3 Sub-heading</h3>'
. '<p>The <code>h1</code>-<code>h6</code> tags map to PDF roles <strong>H1-H6</strong>.'
. ' The <code>p</code> tag maps to <strong>P</strong>.'
. ' PDF/UA-1 requires that heading levels are not skipped: the library clamps each'
. ' heading to at most one level deeper than the previous heading to maintain a'
. ' well-formed outline that screen readers can navigate.</p>'
. '<h2>2 — Block Roles</h2>'
. '<blockquote>&ldquo;Tagged PDF is the foundation of accessible PDF. Without a'
. ' well-formed structure tree, automated tools and assistive technologies cannot'
. ' reliably interpret document content.&rdquo; &mdash; ISO&#160;14289-1 rationale</blockquote>'
. '<p>The <code>blockquote</code> tag maps to <strong>BlockQuote</strong>.</p>'
. '<h2>3 — List Structure (L &gt; LI &gt; Lbl + LBody)</h2>'
. '<p>Both <code>ul</code> and <code>ol</code> map to the <strong>L</strong> role.'
. ' Each <code>li</code> auto-generates <strong>LI &gt; {Lbl, LBody}</strong> children:</p>'
. '<ul>'
. '<li>Unordered item &mdash; <code>ul</code>/<code>li</code> tags produce L/LI structure</li>'
. '<li>The bullet character is placed in a <strong>Lbl</strong> child element</li>'
. '<li>Body text is wrapped in a <strong>LBody</strong> child element</li>'
. '</ul>'
. '<ol>'
. '<li>Ordered item &mdash; the number is auto-generated as the <strong>Lbl</strong></li>'
. '<li>Body text is again auto-wrapped in <strong>LBody</strong></li>'
. '<li>Nesting: L &gt; LI &gt; {Lbl, LBody}</li>'
. '</ol>'
. '<h2>4 — Figure, Image Alt-Text, and Caption</h2>'
. '<figure>'
. '<img src="'
. $imgPath
. '" alt="TCPDF logo: blue text on white background" width="89" height="30" />'
. '<figcaption>Figure 1 &mdash; TCPDF logo. The <code>alt</code> attribute becomes'
. ' <strong>/Alt</strong> in the Figure structure-element dictionary.'
. ' PDF/UA-1 requires every Figure to carry an /Alt or /ActualText entry so'
. ' screen readers can describe non-text content. The <code>figcaption</code> tag'
. ' maps to <strong>Caption</strong>.</figcaption>'
. '</figure>'
. '<h2>5 — Table Structure (Table &gt; TR &gt; TH/TD)</h2>'
. '<table border="1" cellspacing="0" cellpadding="3">'
. '<caption>Table 1 &mdash; HTML tags and their PDF/UA-1 structure roles</caption>'
. '<tr><th>HTML tag</th><th>PDF structure role</th><th>Category</th></tr>'
. '<tr><td>h1&ndash;h6</td><td>H1&ndash;H6</td><td>Heading</td></tr>'
. '<tr><td>p</td><td>P</td><td>Body</td></tr>'
. '<tr><td>blockquote</td><td>BlockQuote</td><td>Body</td></tr>'
. '<tr><td>pre</td><td>Code</td><td>Body</td></tr>'
. '<tr><td>ul, ol</td><td>L</td><td>List</td></tr>'
. '<tr><td>li</td><td>LI + Lbl + LBody</td><td>List</td></tr>'
. '<tr><td>figure</td><td>Figure</td><td>Illustration</td></tr>'
. '<tr><td>img (with alt)</td><td>Figure + /Alt</td><td>Illustration</td></tr>'
. '<tr><td>figcaption, caption</td><td>Caption</td><td>Illustration</td></tr>'
. '<tr><td>table</td><td>Table</td><td>Table</td></tr>'
. '<tr><td>tr</td><td>TR</td><td>Table</td></tr>'
. '<tr><td>td</td><td>TD</td><td>Table</td></tr>'
. '<tr><td>th</td><td>TH</td><td>Table</td></tr>'
. '<tr><td>a</td><td>Link</td><td>Inline</td></tr>'
. '</table>'
. '<h2>6 — Hyperlink (Link Role)</h2>'
. '<p>This sentence contains a'
. ' <a href="https://github.com/tecnickcom/tc-lib-pdf" style="text-decoration:none;">hyperlink to the tc-lib-pdf repository</a>;'
. ' the <code>a</code> element is tagged with the <strong>Link</strong> structure role'
. ' automatically. PDF/UA-1 requires that link annotations have an accessible name,'
. ' which the library derives from the link text in the structure tree.</p>'
. '<h2>7 — Rules, Spans and Empty Cells</h2>'
. '<p>A horizontal rule carries no semantics, so it is emitted as an <strong>Artifact</strong>'
. ' rather than left as untagged content (PDF/UA-1 7.1):</p>'
. '<hr>'
. '<p>Cell <code>colspan</code>/<code>rowspan</code> are recorded on the cell structure'
. ' element as integer <strong>/ColSpan</strong> and <strong>/RowSpan</strong> attributes,'
. ' and an empty cell is kept in the tree so the row keeps its full column count (7.2):</p>'
. '<table border="1" cellspacing="0" cellpadding="3">'
. '<tr><th>Col A</th><th>Col B</th><th>Col C</th></tr>'
. '<tr><td colspan="2">Spans columns A and B</td><td>C</td></tr>'
. '<tr><td rowspan="2">Spans rows</td><td>B2</td><td>C2</td></tr>'
. '<tr><td>B3</td><td></td></tr>'
. '</table>';
// A long table that breaks across pages: the repeated header on each continuation
// page is an Artifact, while the single Table structure element keeps all its rows.
$html .=
'<h2>8 — Page-Spanning Table</h2>'
. '<table border="1" cellspacing="0" cellpadding="2">'
. '<thead><tr><th>#</th><th>Description</th></tr></thead>';
for ($row = 1; $row <= 60; ++$row) {
$html .= '<tr><td>' . $row . '</td><td>Continuation row ' . $row . '</td></tr>';
}
$html .= '</table>';
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+142
View File
@@ -0,0 +1,142 @@
<?php
/**
* E017_pdfua2.php
*
* Demonstrates HTML auto-tagging in 'pdfua2' (PDF/UA-2, ISO 14289-2) mode.
*
* PDF/UA-2 is the second generation of the accessible-PDF standard, standardised
* as ISO 14289-2 and built on PDF 2.0. Key differences from PDF/UA-1:
* - Targets PDF version 2.0 (pdfver 2.0 in catalog)
* - Uses the PDF/UA-2 XMP identification schema
* - The same structure-tree, MarkInfo, /Lang, and heading-clamping rules apply
*
* Every HTML element rendered by addHTMLCell() is automatically mapped to the
* corresponding PDF structure role — no beginStructElem() calls required.
* Compare with E063 which builds the same structure tree manually.
*
* Auto-tagged roles exercised here:
* h1-h3 → H1-H3 (heading hierarchy)
* p → P (paragraph)
* blockquote → BlockQuote
* pre → Code
* ul / ol → L (list; li → LI > Lbl + LBody auto-generated)
* figure → Figure
* img[alt] → Figure + /Alt (alt text written to struct-elem dictionary)
* figcaption → Caption
* table → Table (tr → TR, th → TH, td → TD, caption → Caption)
* a → Link
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
require __DIR__ . '/../vendor/autoload.php';
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfua2,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setTitle('PDF/UA-2 Auto-Tagged HTML');
$pdf->setSubject('tc-lib-pdf example: 017 — pdfua2 mode HTML tagging showcase');
$pdf->setKeywords('TCPDF tc-lib-pdf pdfua2 PDF/UA-2 ISO14289-2 PDF2.0 tagged PDF accessibility HTML auto-tag');
$pdf->setPDFFilename('E017_pdfua2.pdf');
$pdf->setLanguage(code: 'en-US');
$font = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
// PDF/UA requires every font to be embedded. <code>/<pre> default to the
// non-embedded standard-14 Courier, so register an embedded monospace font and
// select it for those elements.
$pdf->font->insert($pdf->pon, 'dejavusansmono', '', 10);
$pdf->setHTMLMonospaceFont('dejavusansmono');
$pdf->addPage();
$pdf->page->addContent($font['out']);
$imgPath = __DIR__ . '/images/tcpdf_logo.jpg';
$html =
'<h1>PDF/UA-2 — Auto-Tagged HTML Showcase</h1>'
. '<p>Mode: <strong>pdfua2</strong> (ISO&#160;14289-2, PDF&#160;2.0). This document'
. ' demonstrates every HTML element that <code>addHTMLCell()</code> auto-tags with a'
. ' PDF/UA-2 structure role. PDF/UA-2 advances the accessible-PDF standard onto the'
. ' PDF&#160;2.0 generation: it uses the updated PDF/UA-2 XMP identification schema'
. ' and targets PDF version 2.0 while applying the same structure-tree, MarkInfo,'
. ' /Lang, and heading-clamping rules as PDF/UA-1. The document infrastructure'
. ' difference is transparent to the HTML tagging layer — all roles map identically.</p>'
. '<h2>1 — Heading and Paragraph Roles</h2>'
. '<h3>H3 Sub-heading</h3>'
. '<p>The <code>h1</code>-<code>h6</code> tags map to PDF roles <strong>H1-H6</strong>.'
. ' The <code>p</code> tag maps to <strong>P</strong>.'
. ' Heading levels are clamped to prevent skipped levels: if an <code>h3</code>'
. ' follows an <code>h1</code> without an intervening <code>h2</code>, the library'
. ' silently promotes it to H2 so the PDF/UA heading-nesting rule is not violated.</p>'
. '<h2>2 — Block Roles</h2>'
. '<blockquote>&ldquo;Tagged PDF is the foundation of accessible PDF. Without a'
. ' well-formed structure tree, automated tools and assistive technologies cannot'
. ' reliably interpret document content.&rdquo; &mdash; ISO&#160;14289 rationale</blockquote>'
. '<p>The <code>blockquote</code> tag maps to <strong>BlockQuote</strong>.</p>'
. '<h2>3 — List Structure (L &gt; LI &gt; Lbl + LBody)</h2>'
. '<p>Both <code>ul</code> and <code>ol</code> map to the <strong>L</strong> role.'
. ' Each <code>li</code> auto-generates <strong>LI &gt; {Lbl, LBody}</strong> children:</p>'
. '<ul>'
. '<li>Unordered item &mdash; <code>ul</code>/<code>li</code> tags produce L/LI structure</li>'
. '<li>The bullet character is placed in a <strong>Lbl</strong> child element</li>'
. '<li>Body text is wrapped in a <strong>LBody</strong> child element</li>'
. '</ul>'
. '<ol>'
. '<li>Ordered item &mdash; the number is auto-generated as the <strong>Lbl</strong></li>'
. '<li>Body text is again auto-wrapped in <strong>LBody</strong></li>'
. '<li>Nesting: L &gt; LI &gt; {Lbl, LBody}</li>'
. '</ol>'
. '<h2>4 — Figure, Image Alt-Text, and Caption</h2>'
. '<figure>'
. '<img src="'
. $imgPath
. '" alt="TCPDF logo: blue text on white background" width="89" height="30" />'
. '<figcaption>Figure 1 &mdash; TCPDF logo. The <code>alt</code> attribute becomes'
. ' <strong>/Alt</strong> in the Figure structure-element dictionary.'
. ' PDF/UA-2 (like PDF/UA-1) requires every Figure to carry an /Alt or'
. ' /ActualText entry. The <code>figcaption</code> tag maps to'
. ' <strong>Caption</strong>.</figcaption>'
. '</figure>'
. '<h2>5 — Table Structure (Table &gt; TR &gt; TH/TD)</h2>'
. '<table border="1" cellspacing="0" cellpadding="3">'
. '<caption>Table 1 &mdash; HTML tags and their PDF/UA-2 structure roles</caption>'
. '<tr><th>HTML tag</th><th>PDF structure role</th><th>Category</th></tr>'
. '<tr><td>h1&ndash;h6</td><td>H1&ndash;H6</td><td>Heading</td></tr>'
. '<tr><td>p</td><td>P</td><td>Body</td></tr>'
. '<tr><td>blockquote</td><td>BlockQuote</td><td>Body</td></tr>'
. '<tr><td>pre</td><td>Code</td><td>Body</td></tr>'
. '<tr><td>ul, ol</td><td>L</td><td>List</td></tr>'
. '<tr><td>li</td><td>LI + Lbl + LBody</td><td>List</td></tr>'
. '<tr><td>figure</td><td>Figure</td><td>Illustration</td></tr>'
. '<tr><td>img (with alt)</td><td>Figure + /Alt</td><td>Illustration</td></tr>'
. '<tr><td>figcaption, caption</td><td>Caption</td><td>Illustration</td></tr>'
. '<tr><td>table</td><td>Table</td><td>Table</td></tr>'
. '<tr><td>tr</td><td>TR</td><td>Table</td></tr>'
. '<tr><td>td</td><td>TD</td><td>Table</td></tr>'
. '<tr><td>th</td><td>TH</td><td>Table</td></tr>'
. '<tr><td>a</td><td>Link</td><td>Inline</td></tr>'
. '</table>'
. '<h2>6 — Hyperlink (Link Role)</h2>'
. '<p>This sentence contains a'
. ' <a href="https://github.com/tecnickcom/tc-lib-pdf" style="text-decoration:none;">hyperlink to the tc-lib-pdf repository</a>;'
. ' the <code>a</code> element is tagged with the <strong>Link</strong> structure role'
. ' automatically. In PDF/UA-2 (as in PDF/UA-1), link annotations must be accessible;'
. ' the library derives the accessible name from the link text in the structure tree.</p>';
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,227 @@
<?php
/**
* E018_html_page_span.php
*
* @since 2026-04-25
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 018');
$pdf->setTitle('HTML Page Span Handling');
$pdf->setKeywords('TCPDF tc-lib-pdf html page span pagination flow layout');
$pdf->setPDFFilename('018_html_page_span.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
$pageV01 = $pdf->addPage();
$bfont6 = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
$pdf->page->addContent($bfont6['out']);
$html = 'Some special characters: &lt; € &euro; &#8364; &amp; è &egrave; &copy; &gt; \\slash \\\\double-slash \\\\\\triple-slash
<h2>List</h2>
List example:
<ol>
<li><b>bold text</b></li>
<li><i>italic text</i></li>
<li><u>underlined text</u></li>
<li><b>b<i>bi<u>biu</u>bi</i>b</b></li>
<li><a href="https://tcpdf.org" dir="ltr">link to https://tcpdf.org</a></li>
<li>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.<br />Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.</li>
<li>SUBLIST
<ol>
<li>row one
<ul>
<li>sublist</li>
</ul>
</li>
<li>row two</li>
</ol>
</li>
<li><b>T</b>E<i>S</i><u>T</u> <del>line through</del></li>
<li><font size="+3">font + 3</font></li>
<li><small>small text</small> normal <small>small text</small> normal <sub>subscript</sub> normal <sup>superscript</sup> normal</li>
</ol>
<dl>
<dt>Coffee</dt>
<dd>Black hot drink</dd>
<dt>Milk</dt>
<dd>White cold drink</dd>
</dl>
<div style="text-align:center">The words &#8220;<span dir="rtl">&#1502;&#1494;&#1500; [mazel] &#1496;&#1493;&#1489; [tov]</span>&#8221; mean &#8220;Congratulations!&#8221;</div>
<p>This is just an example of html code to demonstrate some supported CSS inline styles.
<span style="font-weight: bold;">bold text</span>
<span style="text-decoration: line-through;">line-trough</span>
<span style="text-decoration: underline line-through;">underline and line-trough</span>
<span style="color: rgb(0, 128, 64);">color</span>
<span style="background-color: rgb(255, 0, 0); color: rgb(255, 255, 255);">background color</span>
<span style="font-weight: bold;">bold</span>
<span style="font-size: xx-small;">xx-small</span>
<span style="font-size: x-small;">x-small</span>
<span style="font-size: small;">small</span>
<span style="font-size: medium;">medium</span>
<span style="font-size: large;">large</span>
<span style="font-size: x-large;">x-large</span>
<span style="font-size: xx-large;">xx-large</span>
</p>';
$pdf->addHTMLCell(
html: '<h1>HTML page break example</h1>' . $html . $html . $html . $html,
posx: 20,
posy: 10,
width: 150,
);
// =============================================================
// Styled block (background + border) spanning multiple pages.
$blockchunk =
'<p>'
. 'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium '
. 'doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore '
. 'veritatis et quasi architecto beatae vitae dicta sunt explicabo.'
. '</p>';
$blockhtml =
'<h2>Styled block across pages</h2>'
. '<div style="background-color:#ffeeaa;border:1px solid #888;padding:4px">'
. '<p><b>Block-level container</b> &mdash; the background and border continue '
. 'on each page until the content ends.</p>'
. \str_repeat($blockchunk, 30)
. '</div>';
$pdf->addHTMLCell(html: $blockhtml, posx: 20, posy: 100, width: 150);
// =============================================================
// Table spanning multiple pages with header row replay.
$tableRows = '';
for ($i = 1; $i <= 20; ++$i) {
$tableRows .=
'<tr>'
. '<td>'
. $i
. '</td>'
. '<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</td>'
. '<td style="text-align:right">'
. \number_format($i * 12.34, 2)
. '</td>'
. '</tr>';
}
$tablehtml =
'<h2>Table across pages</h2>'
. '<table border="1" cellpadding="3" cellspacing="0">'
. '<thead>'
. '<tr style="background-color:#cccccc">'
. '<th>#</th><th>Description</th><th>Amount</th>'
. '</tr>'
. '</thead>'
. $tableRows
. '</table>';
$pdf->addHTMLCell(html: $tablehtml, posx: 20, posy: 220, width: 150);
// =============================================================
// Table rows containing DIV blocks with an explicit CSS height.
// Odd rows use a styled DIV (text color + background color), even rows
// use a bare DIV with only the height set: both must produce the same
// row height, and no row may be split across pages.
$tableRowsB = '';
for ($i = 1; $i <= 8; ++$i) {
$divstyleB = ($i % 2) === 1 ? 'color:blue; background-color:yellow; height:100px;' : 'color:#007700; height:100px;';
$tableRowsB .=
'<tr>'
. '<td>'
. $i
. '</td>'
. '<td><div style="'
. $divstyleB
. '">DIV block #'
. $i
. ' (100px high)</div></td>'
. '<td style="text-align:right">'
. \number_format($i * 12.34, 2)
. '</td>'
. '</tr>';
}
$tablehtmlB =
'<h2>Fixed-height DIV blocks inside table cells</h2>'
. '<table border="1" cellpadding="3" cellspacing="0">'
. '<thead>'
. '<tr style="background-color:#cccccc">'
. '<th>#</th><th>Description</th><th>Amount</th>'
. '</tr>'
. '</thead>'
. $tableRowsB
. '</table>';
$pdf->addHTMLCell(html: $tablehtmlB, posx: 20, posy: 170, width: 150);
// =============================================================
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,246 @@
<?php
/**
* E019_page_regions.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 019');
$pdf->setTitle('Page regions example');
$pdf->setKeywords('TCPDF tc-lib-pdf example page regions columns');
$pdf->setPDFFilename('019_page_regions.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$titlefont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 12);
$textfont = $pdf->font->insert($pdf->pon, 'times', '', 11);
// ----------
// Two text regions (columns) generated from the content area.
$leftMargin = 15.0;
$rightMargin = 15.0;
$topMargin = 20.0;
$bottomMargin = 20.0;
$columnGap = 8.0;
$contentWidth = 210.0 - $leftMargin - $rightMargin;
$contentHeight = 297.0 - $topMargin - $bottomMargin;
$columnWidth = ($contentWidth - $columnGap) / 2.0;
$page = $pdf->addPage([
'margin' => [
'PL' => $leftMargin,
'PR' => $rightMargin,
'CT' => $topMargin,
'CB' => $bottomMargin,
],
'region' => [
[
'RX' => $leftMargin,
'RY' => $topMargin,
'RW' => $columnWidth,
'RH' => $contentHeight,
],
[
'RX' => $leftMargin + $columnWidth + $columnGap,
'RY' => $topMargin,
'RW' => $columnWidth,
'RH' => $contentHeight,
],
],
]);
$pdf->page->addContent($titlefont['out']);
$pdf->addTextCell(
txt: 'Page Regions: two flowing columns across pages',
pid: $page['pid'],
posx: 0, // NOTE: addTextCell() coordinates are relative to the current page region
posy: 0,
width: 0,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: false,
);
$pdf->page->addContent($textfont['out']);
$leftPrefix =
'LEFT/RIGHT REGION FLOW. This paragraph is rendered using page regions. '
. 'When the first region is full, writing continues in the next region, '
. 'then on the next page with the same two-region layout. ';
$baseParagraph =
'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque '
. 'laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto '
. 'beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut '
. 'odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. '
. 'Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit.';
$chunks = [];
for ($idx = 1; $idx <= 20; ++$idx) {
$chunks[] = 'Section ' . $idx . '. ' . $leftPrefix . $baseParagraph;
}
$content = "Two columns with automatic region/page breaks\n\n" . \implode("\n\n", $chunks);
$region = $pdf->page->getRegion($page['pid']);
$regionWidth = (float) $region['RW'];
$pdf->addTextCell(
txt: $content,
pid: $page['pid'],
posx: 0,
posy: 12,
width: $regionWidth,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
cell: null,
styles: [],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: false,
);
// --- Same example using HTML ---
$pageH = $pdf->addPage([
'margin' => [
'PL' => $leftMargin,
'PR' => $rightMargin,
'CT' => $topMargin,
'CB' => $bottomMargin,
],
'region' => [
[
'RX' => $leftMargin,
'RY' => $topMargin,
'RW' => $columnWidth,
'RH' => $contentHeight,
],
[
'RX' => $leftMargin + $columnWidth + $columnGap,
'RY' => $topMargin,
'RW' => $columnWidth,
'RH' => $contentHeight,
],
],
]);
$contentHtml =
'<p>'
. \str_replace("\n\n", '</p><p>', \htmlspecialchars($content, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8'))
. '</p>';
$pdf->page->selectRegion(0, $pageH['pid']);
$regionH = $pdf->page->getRegion($pageH['pid']);
$pdf->addHTMLCell(
html: $contentHtml,
posx: (float) $regionH['RX'],
posy: (float) $regionH['RY'],
width: (float) $regionH['RW'],
);
// --- Test add addTextCellXY() for absolute page coordinates ---
$page = $pdf->addPage([
'margin' => [
'PL' => 15.0,
'PR' => 15.0,
'CT' => 15.0,
'CB' => 15.0,
],
]);
$pdf->addTextCellXY(
txt: 'addTextCellXY() uses absolute page coordinates [X=0, Y=0].',
posx: 0,
posy: 0,
drawcell: false,
);
$pdf->addTextCell(
txt: 'addTextCell() uses relative page region coordinates [x=0, y=0] => [X=15, Y=15].',
posx: 0,
posy: 0,
drawcell: false,
);
// =============================================================
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+554
View File
@@ -0,0 +1,554 @@
<?php
/**
* E020_barcodes.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 020');
$pdf->setTitle('All Barcode Types Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example barcodes 1D 2D');
$pdf->setPDFFilename('020_barcodes.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$titlefont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 12);
$textfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 9);
$smallfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 8);
$style = [
'lineWidth' => 0,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'black',
'fillColor' => 'black',
];
$linear = [
[
'type' => 'C128A',
'code' => '0123456789',
'name' => 'Code 128 Set A',
'standard' => 'ISO/IEC 15417',
'use' => 'High-density linear code for control characters, logistics, and internal labeling.',
],
[
'type' => 'C128B',
'code' => '0123456789',
'name' => 'Code 128 Set B',
'standard' => 'ISO/IEC 15417',
'use' => 'High-density linear code for mixed upper/lower-case text in shipping and warehousing.',
],
[
'type' => 'C128C',
'code' => '0123456789',
'name' => 'Code 128 Set C',
'standard' => 'ISO/IEC 15417',
'use' => 'Numeric-optimized Code 128 variant for compact encoded numbers and IDs.',
],
[
'type' => 'C128',
'code' => '0123456789',
'name' => 'Code 128 (Auto)',
'standard' => 'ISO/IEC 15417',
'use' => 'General-purpose high-density linear symbology with automatic charset selection.',
],
[
'type' => 'C39E+',
'code' => '0123456789',
'name' => 'Code 39 Extended + checksum',
'standard' => 'ANSI MH10.8M / USS Code 39',
'use' => 'Extended Code 39 character support with checksum for asset tags and internal IDs.',
],
[
'type' => 'C39E',
'code' => '0123456789',
'name' => 'Code 39 Extended',
'standard' => 'ANSI MH10.8M / USS Code 39',
'use' => 'Expanded Code 39 set used for industrial and inventory labels.',
],
[
'type' => 'C39+',
'code' => '0123456789',
'name' => 'Code 39 + checksum',
'standard' => 'ANSI MH10.8M / USS Code 39',
'use' => 'Code 39 with checksum to improve scan reliability in production environments.',
],
[
'type' => 'C39',
'code' => '0123456789',
'name' => 'Code 39',
'standard' => 'ANSI MH10.8M-1983 (USD-3 / 3 of 9)',
'use' => 'Classic alphanumeric barcode used for parts, equipment, and non-retail logistics.',
],
[
'type' => 'C93',
'code' => '0123456789',
'name' => 'Code 93',
'standard' => 'USS-93',
'use' => 'Compact linear code often used where Code 39 would be too wide.',
],
[
'type' => 'CODABAR',
'code' => '0123456789',
'name' => 'Codabar',
'standard' => 'NW-7 (legacy)',
'use' => 'Legacy symbology used in libraries, blood banks, and some healthcare systems.',
],
[
'type' => 'CODE11',
'code' => '0123456789',
'name' => 'Code 11',
'standard' => 'USD-8 (legacy)',
'use' => 'Numeric code widely used in telecommunications and labeling of equipment.',
],
[
'type' => 'EAN13',
'code' => '0123456789',
'name' => 'EAN-13',
'standard' => 'GS1 EAN/UPC',
'use' => 'Global retail product identification barcode used on consumer goods.',
],
[
'type' => 'EAN2',
'code' => '12',
'name' => 'EAN 2-digit add-on',
'standard' => 'GS1 EAN/UPC add-on',
'use' => 'Supplementary add-on commonly used to encode issue numbers for periodicals.',
],
[
'type' => 'EAN5',
'code' => '12345',
'name' => 'EAN 5-digit add-on',
'standard' => 'GS1 EAN/UPC add-on',
'use' => 'Supplementary add-on often used for suggested retail pricing on books/magazines.',
],
[
'type' => 'EAN8',
'code' => '1234567',
'name' => 'EAN-8',
'standard' => 'GS1 EAN/UPC',
'use' => 'Compact retail barcode for very small consumer packages.',
],
[
'type' => 'I25+',
'code' => '0123456789',
'name' => 'Interleaved 2 of 5 + checksum',
'standard' => 'ITF / Interleaved 2 of 5',
'use' => 'Numeric shipping and carton labels with checksum for improved integrity.',
],
[
'type' => 'I25',
'code' => '0123456789',
'name' => 'Interleaved 2 of 5',
'standard' => 'ITF / Interleaved 2 of 5',
'use' => 'Numeric-only barcode frequently used on corrugated packaging.',
],
[
'type' => 'IMB',
'code' => '01234567094987654321-01234567891',
'name' => 'Intelligent Mail Barcode',
'standard' => 'USPS-B-3200',
'use' => 'USPS mail sorting and tracking barcode for letters and flats.',
],
[
'type' => 'IMBPRE',
'code' => 'AADTFFDFTDADTAADAATFDTDDAAADDTDTTDAFADADDDTFFFDDTTTADFAAADFTDAADA',
'name' => 'IMB pre-processed',
'standard' => 'USPS Intelligent Mail (pre-encoded pattern)',
'use' => 'Feeds a precomputed IMB state pattern when encoding is prepared upstream.',
],
[
'type' => 'KIX',
'code' => '0123456789',
'name' => 'KIX',
'standard' => 'PostNL KIX (4-state)',
'use' => 'Dutch postal customer indexing and routing barcode.',
],
[
'type' => 'MSI+',
'code' => '0123456789',
'name' => 'MSI + checksum',
'standard' => 'MSI Plessey (variant)',
'use' => 'Inventory-oriented numeric coding with checksum for warehouse workflows.',
],
[
'type' => 'MSI',
'code' => '0123456789',
'name' => 'MSI',
'standard' => 'MSI Plessey (variant)',
'use' => 'Numeric barcode used in stock control and shelf labeling.',
],
[
'type' => 'PHARMA2T',
'code' => '0123456789',
'name' => 'Pharmacode two-track',
'standard' => 'Pharmacode (2-track variant)',
'use' => 'Pharmaceutical package line verification using two-track bar patterns.',
],
[
'type' => 'PHARMA',
'code' => '0123456789',
'name' => 'Pharmacode',
'standard' => 'Laetus Pharmacode',
'use' => 'Pharmaceutical packaging control on production lines.',
],
[
'type' => 'PLANET',
'code' => '0123456789',
'name' => 'PLANET',
'standard' => 'USPS PLANET (legacy)',
'use' => 'Legacy USPS mail tracking and routing barcode.',
],
[
'type' => 'POSTNET',
'code' => '0123456789',
'name' => 'POSTNET',
'standard' => 'USPS POSTNET (legacy)',
'use' => 'Legacy USPS ZIP and delivery-point encoding for mail sorting.',
],
[
'type' => 'RMS4CC',
'code' => '0123456789',
'name' => 'RMS4CC',
'standard' => 'Royal Mail 4-State Customer Code',
'use' => 'UK postal addressing and sorting barcode.',
],
[
'type' => 'S25+',
'code' => '0123456789',
'name' => 'Standard 2 of 5 + checksum',
'standard' => 'Standard 2 of 5 (industrial)',
'use' => 'Numeric industrial barcode with checksum for improved reliability.',
],
[
'type' => 'S25',
'code' => '0123456789',
'name' => 'Standard 2 of 5',
'standard' => 'Standard 2 of 5 (industrial)',
'use' => 'Legacy numeric industrial labeling barcode.',
],
[
'type' => 'UPCA',
'code' => '72527273070',
'name' => 'UPC-A',
'standard' => 'GS1 EAN/UPC',
'use' => 'Retail barcode primarily used in North America.',
],
[
'type' => 'UPCE',
'code' => '725277',
'name' => 'UPC-E',
'standard' => 'GS1 EAN/UPC',
'use' => 'Compressed UPC format for small retail packages.',
],
];
$square = [
[
'type' => 'LRAW',
'code' => '0101010101',
'name' => '1D raw mode',
'standard' => 'Library raw mode (non-standard)',
'use' => 'Debug/test mode that directly renders provided 1D bit patterns.',
],
[
'type' => 'SRAW',
'code' => '0101,1010',
'name' => '2D raw mode',
'standard' => 'Library raw mode (non-standard)',
'use' => 'Debug/test mode that directly renders provided 2D bit grids.',
],
[
'type' => 'AZTEC',
'code' => 'ABCDabcd01234',
'name' => 'Aztec Code',
'standard' => 'ISO/IEC 24778:2008',
'use' => 'Compact 2D code used for tickets, transport passes, and mobile scans.',
],
[
'type' => 'AZTEC,50,A,A',
'code' => 'ABCDabcd01234',
'name' => 'Aztec Code (with parameters)',
'standard' => 'ISO/IEC 24778:2008',
'use' => 'Aztec variant with explicit encoder parameters for size/error tuning.',
],
[
'type' => 'PDF417',
'code' => '0123456789',
'name' => 'PDF417',
'standard' => 'ISO/IEC 15438:2006',
'use' => 'Stacked 2D barcode used in transport, identity, and archival workflows.',
],
[
'type' => 'QRCODE',
'code' => '0123456789',
'name' => 'QR Code',
'standard' => 'ISO/IEC 18004',
'use' => 'Widely used 2D code for URLs, mobile interactions, and consumer scanning.',
],
[
'type' => 'QRCODE,H,ST,0,0',
'code' => 'abcdefghijklmnopqrstuvwxy0123456789',
'name' => 'QR Code (with parameters)',
'standard' => 'ISO/IEC 18004',
'use' => 'QR variant with explicit error correction and encoding parameters.',
],
[
'type' => 'DATAMATRIX',
'code' => '0123456789',
'name' => 'Data Matrix (square)',
'standard' => 'ISO/IEC 16022',
'use' => 'Compact 2D marking used in electronics, medical devices, and manufacturing.',
],
[
'type' => 'DATAMATRIX,R',
'code' => '0123456789012345678901234567890123456789',
'name' => 'Data Matrix (rectangular)',
'standard' => 'ISO/IEC 16022',
'use' => 'Rectangular Data Matrix for narrow labels and constrained print areas.',
],
[
'type' => 'DATAMATRIX,S,GS1',
'code' => \chr(232) . '01095011010209171719050810ABCD1234' . \chr(232) . '2110',
'name' => 'GS1 Data Matrix (square)',
'standard' => 'ISO/IEC 16022 with GS1 AIs',
'use' => 'GS1-compliant 2D code for regulated product IDs and traceability.',
],
[
'type' => 'DATAMATRIX,R,GS1',
'code' => \chr(232) . '01095011010209171719050810ABCD1234' . \chr(232) . '2110',
'name' => 'GS1 Data Matrix (rectangular)',
'standard' => 'ISO/IEC 16022 with GS1 AIs',
'use' => 'Rectangular GS1 Data Matrix for compact traceability labels.',
],
];
function formatSampleDataForText(string $sample): string
{
$out = '';
$bytes = \unpack('C*', $sample);
if ($bytes === false) {
return '';
}
foreach ($bytes as $byte) {
if ($byte === 232) {
$out .= '<FNC1>';
continue;
}
if ($byte >= 32 && $byte <= 126) {
$out .= \chr($byte);
continue;
}
$out .= '\\x' . \strtoupper(\str_pad((string) \dechex($byte), 2, '0', STR_PAD_LEFT));
}
return $out;
}
function renderBarcodeSection(
\Com\Tecnick\Pdf\Tcpdf $pdf,
array $items,
string $title,
int $sectionIndex,
array $titlefont,
array $textfont,
array $smallfont,
array $style,
bool $square = false,
): void {
$marginLeft = 12.0;
$marginRight = 12.0;
$marginTop = 15.0;
$marginBottom = 12.0;
$page = $pdf->addPage([
'margin' => [
'PL' => $marginLeft,
'PR' => $marginRight,
'CT' => $marginTop,
'CB' => $marginBottom,
],
]);
$pageWidth = (float) $page['width'];
$pageHeight = (float) $page['height'];
$contentWidth = $pageWidth - $marginLeft - $marginRight;
$maxY = $pageHeight - $marginBottom;
$drawHeader = static function () use (
$pdf,
$titlefont,
$smallfont,
$title,
$sectionIndex,
$marginLeft,
$contentWidth,
): float {
$pdf->page->addContent($titlefont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Barcode Types Catalog (' . $sectionIndex . '/2): ' . $title,
posx: $marginLeft,
posy: 10,
width: $contentWidth,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($smallfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Source type list: tc-lib-barcode example/index.php',
posx: $marginLeft,
posy: 16,
width: $contentWidth,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
return 22.0;
};
$cursorY = $drawHeader();
foreach ($items as $item) {
$sampleText = formatSampleDataForText($item['code']);
$desc = 'Standard: ' . $item['standard'] . "\n" . 'Use: ' . $item['use'] . "\n" . 'Sample data: ' . $sampleText;
$descTopOffset = 4.5;
$descLineSpace = 1.0;
$barcodeGap = 4.0;
$barcodeBlockGap = 8.0;
while (true) {
$descTopY = $cursorY + $descTopOffset;
$descOut = $pdf->getTextCell(
txt: $desc,
posx: $marginLeft,
posy: $descTopY,
width: $contentWidth,
height: 0,
offset: 0,
linespace: $descLineSpace,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$descBBox = $pdf->getLastBBox();
$barcodePosY = (float) $descBBox['y'] + (float) $descBBox['h'] + $barcodeGap;
$barcodeWidth = -1;
$barcodeHeight = $square ? -1 : 10;
if (!$square) {
$ncols = (int) $pdf->barcode->getBarcodeObj($item['type'], $item['code'])->getArray()['ncols'];
$targetMinBarWidth = 0.65;
$barcodeWidth = (int) \max(1, \round($ncols * $targetMinBarWidth));
}
$barcodeModel = $pdf->barcode->getBarcodeObj(
$item['type'],
$item['code'],
$barcodeWidth,
$barcodeHeight,
'black',
[0, 0, 0, 0],
);
$barcodeData = $barcodeModel->getArray();
$itemBottomY = $barcodePosY + (float) $barcodeData['full_height'];
$nextCursorY = $itemBottomY + $barcodeBlockGap;
if ($nextCursorY <= $maxY) {
break;
}
$page = $pdf->addPage([
'margin' => [
'PL' => $marginLeft,
'PR' => $marginRight,
'CT' => $marginTop,
'CB' => $marginBottom,
],
]);
$cursorY = $drawHeader();
}
$pdf->page->addContent($textfont['out']);
$head = '[' . $item['type'] . '] ' . $item['name'];
$pdf->page->addContent($pdf->getTextCell(
txt: $head,
posx: $marginLeft,
posy: $cursorY,
width: $contentWidth,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($smallfont['out']);
$pdf->page->addContent($descOut);
$pdf->page->addContent($pdf->getBarcode(
type: $item['type'],
code: $item['code'],
posx: $marginLeft,
posy: $barcodePosY,
width: $barcodeWidth,
height: $barcodeHeight,
padding: [0, 0, 0, 0],
style: $style,
));
$cursorY = $nextCursorY;
}
}
renderBarcodeSection($pdf, $linear, 'Linear', 1, $titlefont, $textfont, $smallfont, $style, false);
renderBarcodeSection($pdf, $square, 'Square / 2D', 2, $titlefont, $textfont, $smallfont, $style, true);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,226 @@
<?php
/**
* E021_font_stretch_spacing.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 021');
$pdf->setTitle('Font stretching, scaling and spacing');
$pdf->setKeywords('TCPDF tc-lib-pdf example font stretching scaling spacing');
$pdf->setPDFFilename('021_font_stretch_spacing.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$style = [
'all' => [
'lineWidth' => 0.2,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'black',
'fillColor' => '',
],
];
$applyFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, float $spacing, float $stretching, int $size = 11): array {
// $stretching is a font-stack ratio (1.0 = 100%); the percent-valued modes
// below divide by 100 before calling this helper.
$font = $pdf->font->insert($pdf->pon, 'times', '', $size, $spacing, $stretching);
$pdf->page->addContent($font['out']);
return $font;
};
$measureText = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, string $text): array {
$ordarr = $pdf->uniconv->strToOrdArr($text);
$width = $pdf->font->getOrdArrWidth($ordarr);
return [$ordarr, $width];
};
$drawBoxedLine = static function (
\Com\Tecnick\Pdf\Tcpdf $pdf,
array $style,
string $text,
float $x,
float $y,
float $w,
float $h,
float $spacing,
float $stretching,
) use ($applyFont): void {
$font = $applyFont($pdf, $spacing, $stretching, 11);
$pdf->page->addContent($pdf->graph->getRect($x, $y, $w, $h, 'D', $style));
$baseline = $y + 1.5 + $pdf->toUnit($font['ascent']);
$pdf->page->addContent($pdf->getTextLine(txt: $text, posx: $x + 1.5, posy: $baseline));
};
$setNeutralTitleFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf): void {
$font = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 13, 0.0, 1.0);
$pdf->page->addContent($font['out']);
};
$setNeutralTextFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf): void {
$font = $pdf->font->insert($pdf->pon, 'helvetica', '', 9, 0.0, 1.0);
$pdf->page->addContent($font['out']);
};
// Insert one neutral font before addPage() so page context has a valid current font.
$pdf->font->insert($pdf->pon, 'helvetica', '', 10, 0.0, 1.0);
$pdf->addPage();
$setNeutralTitleFont($pdf);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Example 021 - Font stretching, scaling and spacing',
posx: 15,
posy: 15,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$setNeutralTextFont($pdf);
$pdf->page->addContent($pdf->getTextCell(
txt: 'This mirrors the legacy cell stretch sample using tc-lib-pdf font spacing/stretching controls.',
posx: 15,
posy: 23,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$sample = 'TEST CELL STRETCH: scaling and spacing';
$left = 15;
$top = 32;
$cellw = 85;
$cellh = 8;
$labelw = 95;
$applyFont($pdf, 0.0, 1.0, 11);
[$ordarr, $baseWidthPoints] = $measureText($pdf, $sample);
$textChars = \count($ordarr);
$targetWidthPoints = $pdf->toPoints($cellw - 3);
$scaleFitRatio = $baseWidthPoints > $targetWidthPoints ? $targetWidthPoints / $baseWidthPoints : 1.0;
$scaleForceRatio = $targetWidthPoints / $baseWidthPoints;
$scaleFit = 100.0 * $scaleFitRatio;
$scaleForce = 100.0 * $scaleForceRatio;
$spacingFit = 0.0;
if ($baseWidthPoints > $targetWidthPoints && $textChars > 1) {
$spacingFit = ($targetWidthPoints - $baseWidthPoints) / ($textChars - 1);
}
$spacingForce = 0.0;
if ($textChars > 1) {
$spacingForce = ($targetWidthPoints - $baseWidthPoints) / ($textChars - 1);
}
$modes = [
['no stretch', 0.0, 100.0],
['scaling (fit)', 0.0, $scaleFit],
['force scaling', 0.0, $scaleForce],
['spacing (fit)', $spacingFit, 100.0],
['force spacing', $spacingForce, 100.0],
];
$firstBlockEndY = $top;
$row = 0;
foreach ($modes as $mode) {
$y = $top + ($row * ($cellh + 1.5));
$label = \sprintf('%s [stretch=%.1f%%, spacing=%.3fpt]', $mode[0], $mode[2], $mode[1]);
$setNeutralTextFont($pdf);
$pdf->page->addContent($pdf->getTextLine(txt: $label, posx: $left, posy: $y + 5.5));
$drawBoxedLine($pdf, $style, $sample, $left + $labelw, $y, $cellw, $cellh, $mode[1], $mode[2] / 100.0);
$firstBlockEndY = \max($firstBlockEndY, $y + $cellh, $y + 6.5);
++$row;
}
$secondIntroY = $firstBlockEndY + 10.0;
$secondTitleY = $secondIntroY + 8.0;
$secondRowsStartY = $secondTitleY + 9.0;
$setNeutralTextFont($pdf);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Second section: global font stretching and spacing combinations (legacy example style).',
posx: 15,
posy: $secondIntroY,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$setNeutralTitleFont($pdf);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Global stretching/spacing matrix',
posx: 15,
posy: $secondTitleY,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$stretchVals = [90.0, 100.0, 110.0];
$spacingVals = [-0.72, 0.0, 0.72]; // ~ -0.254, 0, +0.254 mm expressed in points.
$sample2 = 'Stretching and spacing test text';
$y = $secondRowsStartY;
foreach ($stretchVals as $stretching) {
foreach ($spacingVals as $spacing) {
$line = \sprintf('Stretching %3.0f%%, Spacing %+.3fpt', $stretching, $spacing);
$setNeutralTextFont($pdf);
$pdf->page->addContent($pdf->getTextLine(txt: $line, posx: 15, posy: $y + 5.5));
$drawBoxedLine($pdf, $style, $sample2, 95, $y, 100, 8, $spacing, $stretching / 100.0);
$y += 9.5;
}
$y += 2;
}
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,224 @@
<?php
/**
* E022_cell_borders.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 022');
$pdf->setTitle('Cell borders');
$pdf->setKeywords('TCPDF tc-lib-pdf example cell borders');
$pdf->setPDFFilename('022_cell_borders.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$setFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, string $family, string $style, int $size): array {
$font = $pdf->font->insert($pdf->pon, $family, $style, $size, 0.0, 1.0);
$pdf->page->addContent($font['out']);
return $font;
};
$getLineStyle = static function (float $width, string $color): array {
return [
'lineWidth' => $width,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => $color,
];
};
$buildCellStyles = static function (array $sideStyles, string $fillColor): array {
$fillStyle = [
'lineWidth' => 0,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#000000', //$fillColor,
'fillColor' => $fillColor,
];
$styles = [
'all' => $fillStyle,
0 => $fillStyle,
1 => $fillStyle,
2 => $fillStyle,
3 => $fillStyle,
];
foreach ($sideStyles as $side => $style) {
$styles[$side] = \array_merge($fillStyle, $style, ['fillColor' => $fillColor]);
}
return $styles;
};
$drawStyledCell = static function (
\Com\Tecnick\Pdf\Tcpdf $pdf,
string $label,
float $x,
float $y,
float $w,
float $h,
array $styles,
float $borderPos,
): void {
$pdf->setDefaultCellBorderPos(borderpos: $borderPos);
$pdf->page->addContent($pdf->getTextCell(' ', $x, $y, $w, $h, styles: $styles, drawcell: true));
$pdf->page->addContent($pdf->color->getPdfColor('black'));
$pdf->page->addContent($pdf->getTextCell(
$label,
$x,
$y,
$w,
$h,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
drawcell: false,
));
};
$pdf->font->insert($pdf->pon, 'helvetica', '', 10, 0.0, 1.0);
$pdf->addPage();
$setFont($pdf, 'helvetica', 'B', 20);
$pdf->page->addContent($pdf->getTextCell(
'Cell Borders',
15,
15,
180,
8,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
$setFont($pdf, 'helvetica', '', 11);
$basicLineStyle = $getLineStyle(0.508, '#0080ff');
$fillColor = '#ffff80';
$maskToSide = ['T' => 0, 'R' => 1, 'B' => 2, 'L' => 3];
$masks = ['1', 'LTRB', 'LTR', 'TRB', 'LRB', 'LTB', 'LT', 'TR', 'RB', 'LB', 'LR', 'TB', 'L', 'T', 'R', 'B'];
$x = 15.0;
$y = 32.0;
$w = 30.0;
$h = 8.0;
foreach ($masks as $mask) {
$active = $mask === '1' ? 'LTRB' : $mask;
$sideStyles = [];
foreach ($maskToSide as $side => $idx) {
if (\strpos($active, $side) !== false) {
$sideStyles[$idx] = $basicLineStyle;
}
}
$drawStyledCell($pdf, $mask, $x, $y, $w, $h, $buildCellStyles($sideStyles, $fillColor), $pdf::BORDERPOS_DEFAULT);
$y += 10.0;
}
$normalBorders = [
0 => \array_merge($getLineStyle(2.0, '#ff0000'), ['lineCap' => 'square']),
1 => \array_merge($getLineStyle(2.0, '#ff0000'), ['lineCap' => 'square']),
2 => \array_merge($getLineStyle(2.0, '#ff0000'), ['lineCap' => 'square']),
3 => \array_merge($getLineStyle(2.0, '#ff0000'), ['lineCap' => 'square']),
];
$multiBorders = [
0 => \array_merge($getLineStyle(2.0, '#00ff00'), ['lineCap' => 'square']),
1 => \array_merge($getLineStyle(2.0, '#ff00ff'), ['lineCap' => 'square']),
2 => \array_merge($getLineStyle(2.0, '#0000ff'), ['lineCap' => 'square']),
3 => \array_merge($getLineStyle(2.0, '#ff0000'), ['lineCap' => 'square']),
];
$advX = $x;
$advY = $y + 5.0;
$drawStyledCell(
$pdf,
'LTRB',
$advX,
$advY,
30,
10,
$buildCellStyles($normalBorders, $fillColor),
$pdf::BORDERPOS_DEFAULT,
);
$advY += 15.0;
$drawStyledCell(
$pdf,
'LTRB',
$advX,
$advY,
30,
10,
$buildCellStyles($multiBorders, $fillColor),
$pdf::BORDERPOS_DEFAULT,
);
$advY += 15.0;
$drawStyledCell(
$pdf,
'LTRB EXT',
$advX,
$advY,
30,
10,
$buildCellStyles($normalBorders, $fillColor),
$pdf::BORDERPOS_EXTERNAL,
);
$advY += 15.0;
$drawStyledCell(
$pdf,
'LTRB INT',
$advX,
$advY,
30,
10,
$buildCellStyles($normalBorders, $fillColor),
$pdf::BORDERPOS_INTERNAL,
);
$pdf->setDefaultCellBorderPos(borderpos: $pdf::BORDERPOS_DEFAULT);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,213 @@
<?php
/**
* E023_cell_alignment.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 023');
$pdf->setTitle('Cell alignment');
$pdf->setKeywords('TCPDF tc-lib-pdf example cell alignment');
$pdf->setPDFFilename('023_cell_alignment.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$setFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, string $family, string $style, int $size): array {
$font = $pdf->font->insert($pdf->pon, $family, $style, $size, 0.0, 1.0);
$pdf->page->addContent($font['out']);
return $font;
};
$getLineStyle = static function (float $width, string $color): array {
return [
'lineWidth' => $width,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => $color,
];
};
$pdf->font->insert($pdf->pon, 'helvetica', '', 10, 0.0, 1.0);
$pdf->addPage();
$setFont($pdf, 'helvetica', 'B', 20);
$pdf->page->addContent($pdf->getTextCell(
'Cell Alignment Example',
15,
15,
180,
8,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
$bodyFont = $setFont($pdf, 'helvetica', '', 11);
$borderStyle = ['all' => $getLineStyle(0.7, '#0080ff')];
$lineStyle = $getLineStyle(0.1, '#ff0000');
$cellDef = [
'margin' => ['T' => 0.0, 'R' => 0.0, 'B' => 0.0, 'L' => 0.0],
'padding' => ['T' => 0.0, 'R' => 0.0, 'B' => 0.0, 'L' => 0.0],
'borderpos' => 0.0,
];
$cellW = 30.0;
$cellH = $pdf->toUnit($bodyFont['size']) * 3.0;
$startX = 15.0;
$fontHeight = $pdf->toUnit($bodyFont['height']);
$fontAscent = $pdf->toUnit($bodyFont['ascent']);
$columns = [
['prefix' => 'Top', 'cellAlign' => 'T'],
['prefix' => 'Center', 'cellAlign' => 'C'],
['prefix' => 'Bottom', 'cellAlign' => 'B'],
['prefix' => 'Ascent', 'cellAlign' => 'A'],
['prefix' => 'Baseline', 'cellAlign' => 'L'],
['prefix' => 'Descent', 'cellAlign' => 'D'],
];
$rows = [
['lineY' => 60.0, 'suffix' => 'Center', 'textAlign' => 'C'],
['lineY' => 90.0, 'suffix' => 'Top', 'textAlign' => 'T'],
['lineY' => 120.0, 'suffix' => 'Bottom', 'textAlign' => 'B'],
];
$getCellTopY = static function (
float $lineY,
float $cellH,
string $cellAlign,
string $textAlign,
float $fontHeight,
float $fontAscent,
): float {
return match ($cellAlign) {
'C' => $lineY - ($cellH / 2.0),
'B' => $lineY - $cellH,
'A' => match ($textAlign) {
'T' => $lineY,
'B' => $lineY - ($cellH - $fontHeight),
default => $lineY - (($cellH - $fontHeight) / 2.0),
},
'L' => match ($textAlign) {
'T' => $lineY - $fontAscent,
'B' => $lineY - ($cellH - ($fontHeight - $fontAscent)),
default => $lineY - ((($cellH - $fontHeight) / 2.0) + $fontAscent),
},
'D' => match ($textAlign) {
'T' => $lineY - $fontHeight,
'B' => $lineY - $cellH,
default => $lineY - (($cellH + $fontHeight) / 2.0),
},
default => $lineY,
};
};
$drawCellBorder = static function (
\Com\Tecnick\Pdf\Tcpdf $pdf,
float $x,
float $y,
float $w,
float $h,
array $style,
): string {
return (
$pdf->graph->getLine($x, $y, $x + $w, $y, $style)
. $pdf->graph->getLine($x + $w, $y, $x + $w, $y + $h, $style)
. $pdf->graph->getLine($x + $w, $y + $h, $x, $y + $h, $style)
. $pdf->graph->getLine($x, $y + $h, $x, $y, $style)
);
};
foreach ($rows as $row) {
$pdf->page->addContent($pdf->graph->getLine(15, $row['lineY'], 195, $row['lineY'], $lineStyle));
foreach ($columns as $idx => $col) {
$x = $startX + ($idx * $cellW);
$y = $getCellTopY($row['lineY'], $cellH, $col['cellAlign'], $row['textAlign'], $fontHeight, $fontAscent);
$label = $col['prefix'] . '-' . $row['suffix'];
$pdf->page->addContent($pdf->color->getPdfColor('black'));
$pdf->page->addContent($pdf->getTextCell(
$label,
$x,
$y,
$cellW,
$cellH,
valign: $row['textAlign'],
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
cell: $cellDef,
drawcell: false,
));
$pdf->page->addContent($drawCellBorder($pdf, $x, $y, $cellW, $cellH, $borderStyle['all']));
}
}
$imagePath = __DIR__ . '/images/tcpdf_cell.png';
if (\is_file($imagePath)) {
$imgId = $pdf->image->add($imagePath);
$page = $pdf->page->getPage($pdf->page->getPageId());
$pdf->page->addContent($pdf->image->getSetImage($imgId, 15, 160, 100, 100, $page['height']));
}
$legend =
"LEGEND:\n\n"
. "X: cell x top-left origin (top-right for RTL)\n"
. "Y: cell y top-left origin (top-right for RTL)\n"
. "CW: cell width\n"
. "CH: cell height\n"
. "LW: line width\n"
. "NRL: normal line position\n"
. "EXT: external line position\n"
. "INT: internal line position\n"
. "ML: margin left\n"
. "MR: margin right\n"
. "MT: margin top\n"
. "MB: margin bottom\n"
. "PL: padding left\n"
. "PR: padding right\n"
. "PT: padding top\n"
. "PB: padding bottom\n"
. "TW: text width\n"
. "FA: font ascent\n"
. "FB: font baseline\n"
. 'FD: font descent';
$setFont($pdf, 'helvetica', '', 10);
$pdf->page->addContent($pdf->getTextCell($legend, 125, 160, 70, 100, 0, 1.25, 'T', 'L', drawcell: false));
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+264
View File
@@ -0,0 +1,264 @@
<?php
/**
* E024_colors.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 024');
$pdf->setTitle('CMYK, RGB and Grayscale colors');
$pdf->setKeywords('TCPDF tc-lib-pdf example cmyk rgb grayscale colors');
$pdf->setPDFFilename('024_colors.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$fontFamily = 'helvetica';
$fontStyle = '';
$fontSize = 10;
$drawColorBlock = static function (
\Com\Tecnick\Pdf\Tcpdf $pdf,
float $x,
float $y,
float $w,
float $h,
string $label,
string $strokeColor,
string $fillColor,
string $textColor,
): void {
$style = [
'all' => [
'lineWidth' => 2.0,
'lineCap' => 'square',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => $strokeColor,
'fillColor' => $fillColor,
],
];
$pdf->page->addContent($pdf->graph->getRect($x, $y, $w, $h, 'DF', $style));
$pdf->page->addContent($pdf->color->getPdfColor($textColor));
$pdf->page->addContent($pdf->getTextCell(
$label,
$x,
$y + $h + 2.0,
$w,
6.0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
};
// Insert the font once and reuse it across all pages.
$pdf->font->insert($pdf->pon, $fontFamily, $fontStyle, $fontSize, 0.0, 1.0);
$pdf->addPage();
$colorIntroHtml = '<h1>Color Example</h1>This page demonstrates core color support powered by <span style="color:#008800">tc-lib-color</span>. The library provides unified parsing and conversion for grayscale, RGB, CMYK, spot, and Lab color models so the same API can target both screen output and print-ready workflows. In this sample, color definitions are applied consistently to fills, strokes, and text to highlight how each model can be mixed in a single PDF document.';
$pdf->addHTMLCell(html: $colorIntroHtml, posx: 15, posy: 28, width: 180);
$posy = 100;
$drawColorBlock($pdf, 30.0, $posy, 30.0, 30.0, 'Cyan', 'cmyk(50,0,0,0)', 'cmyk(100,0,0,0)', 'cmyk(100,0,0,0)');
$drawColorBlock($pdf, 70.0, $posy, 30.0, 30.0, 'Magenta', 'cmyk(0,50,0,0)', 'cmyk(0,100,0,0)', 'cmyk(0,100,0,0)');
$drawColorBlock($pdf, 110.0, $posy, 30.0, 30.0, 'Yellow', 'cmyk(0,0,50,0)', 'cmyk(0,0,100,0)', 'cmyk(0,0,100,0)');
$drawColorBlock($pdf, 150.0, $posy, 30.0, 30.0, 'Black', 'cmyk(0,0,0,50)', 'cmyk(0,0,0,100)', 'cmyk(0,0,0,100)');
$posy += 50;
$drawColorBlock($pdf, 30.0, $posy, 30.0, 30.0, 'Red', 'rgb(255,127,127)', 'rgb(255,0,0)', 'rgb(255,0,0)');
$drawColorBlock($pdf, 70.0, $posy, 30.0, 30.0, 'Green', 'rgb(127,255,127)', 'rgb(0,255,0)', 'rgb(0,255,0)');
$drawColorBlock($pdf, 110.0, $posy, 30.0, 30.0, 'Blue', 'rgb(127,127,255)', 'rgb(0,0,255)', 'rgb(0,0,255)');
$posy += 50;
$drawColorBlock($pdf, 30.0, $posy, 30.0, 30.0, 'Gray', '#bfbfbf', '#7f7f7f', '#7f7f7f');
// ----------
$pdf->addPage();
$spotIntroHtml = '<h1>Example of Spot Colors</h1>Spot colors are single, pre-mixed inks, rather than colors produced by four (CMYK), six (CMYKOG), or more process inks during printing. They are usually supplied by specialized vendors, although many printers also use in-house formulations to match target shades.<br /><br />Because there is no universal open standard for spot colors, users typically rely on vendor color books and manually register both the spot color names and their CMYK equivalents.<br /><br />Common industry spot color systems include:<br /><span style="color:#008800">ANPA-COLOR, DIC, FOCOLTONE, GCMI, HKS, PANTONE, TOYO, TRUMATCH</span>.';
$pdf->addHTMLCell(html: $spotIntroHtml, posx: 15, posy: 28, width: 180);
$pdf->color->addSpotColor('My TCPDF Dark Green', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 1.0,
'magenta' => 0.5,
'yellow' => 0.8,
'key' => 0.45,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('My TCPDF Light Yellow', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.0,
'magenta' => 0.0,
'yellow' => 0.55,
'key' => 0.0,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('My TCPDF Black', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.0,
'magenta' => 0.0,
'yellow' => 0.0,
'key' => 1.0,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('My TCPDF Red', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.3,
'magenta' => 1.0,
'yellow' => 0.9,
'key' => 0.1,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('My TCPDF Green', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 1.0,
'magenta' => 0.3,
'yellow' => 1.0,
'key' => 0.0,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('My TCPDF Blue', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 1.0,
'magenta' => 0.6,
'yellow' => 0.1,
'key' => 0.05,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('My TCPDF Yellow', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.0,
'magenta' => 0.2,
'yellow' => 1.0,
'key' => 0.0,
'alpha' => 0.0,
]));
$drawSpotColorBlock = static function (
\Com\Tecnick\Pdf\Tcpdf $pdf,
float $x,
float $y,
float $w,
float $h,
string $spotName,
float $tint,
): void {
$pdf->page->addContent($pdf->color->getPdfColor($spotName, true, $tint));
$pdf->page->addContent($pdf->color->getPdfColor($spotName, false, $tint));
$pdf->page->addContent($pdf->graph->getRect($x, $y, $w, $h, 'DF'));
$pdf->page->addContent($pdf->color->getPdfColor('My TCPDF Black', false, 1.0));
$pdf->page->addContent($pdf->getTextCell(
$spotName,
$x + $w + 3.0,
$y + 6.0,
110.0,
8.0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
};
$spots = [
'My TCPDF Dark Green',
'My TCPDF Light Yellow',
'My TCPDF Red',
'My TCPDF Green',
'My TCPDF Blue',
'My TCPDF Yellow',
];
$startY = 90.0;
foreach ($spots as $spot) {
$drawSpotColorBlock($pdf, 30.0, $startY, 40.0, 16.0, $spot, 1.0);
$startY += 24.0;
}
// ----------
$pdf->addPage();
$labIntroHtml = '<h1>Example of Lab Spot Colors</h1>Lab-based spot colors define color values using CIE L*a*b* coordinates, which are device-independent and better suited for consistent cross-device color reproduction.<br /><br />The following sample spot colors are registered using <span style="color:#008800">addSpotLabColor()</span> and then rendered with standard spot-color drawing commands.';
$pdf->addHTMLCell(html: $labIntroHtml, posx: 15, posy: 28, width: 180);
$pdf->color->addSpotLabColor('My TCPDF Lab Red', 53.0, 80.0, 67.0);
$pdf->color->addSpotLabColor('My TCPDF Lab Green', 87.0, -86.0, 83.0);
$pdf->color->addSpotLabColor('My TCPDF Lab Blue', 32.0, 79.0, -108.0);
$pdf->color->addSpotLabColor('My TCPDF Lab Orange', 74.0, 24.0, 78.0);
$pdf->color->addSpotLabColor('My TCPDF Lab Violet', 40.0, 60.0, -55.0);
$pdf->color->addSpotLabColor('My TCPDF Lab Gray', 55.0, 0.0, 0.0);
// Advanced Lab spot example: custom whitepoint/blackpoint (D50 with non-zero blackpoint).
$pdf->color->addSpotLabColor(
'My TCPDF Lab D50 Orange',
64.25,
58.5,
71.2,
[0.9642, 1.0000, 0.8251],
[0.0120, 0.0125, 0.0110],
);
// Advanced Lab spot example: constrained a*/b* range and explicit Tint=0 Lab endpoint (col0).
$pdf->color->addSpotLabColor(
'My TCPDF Lab Narrow Blue',
36.0,
62.0,
-76.0,
[0.9505, 1.0000, 1.0890],
[0.0, 0.0, 0.0],
[-96.0, 96.0, -96.0, 96.0],
[92.0, -4.0, -8.0],
);
$labSpots = [
['My TCPDF Lab Red', 1.0],
['My TCPDF Lab Green', 1.0],
['My TCPDF Lab Blue', 1.0],
['My TCPDF Lab Orange', 0.9],
['My TCPDF Lab Violet', 0.85],
['My TCPDF Lab Gray', 1.0],
['My TCPDF Lab D50 Orange', 1.0],
['My TCPDF Lab Narrow Blue', 1.0],
];
$startY = 70.0;
foreach ($labSpots as [$spot, $tint]) {
$drawSpotColorBlock($pdf, 30.0, $startY, 40.0, 16.0, $spot, $tint);
$startY += 24.0;
}
// ----------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,126 @@
<?php
/**
* E025_transparency.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 025');
$pdf->setTitle('Object transparency');
$pdf->setKeywords('TCPDF tc-lib-pdf example object transparency alpha extgstate');
$pdf->setPDFFilename('025_transparency.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$setFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, string $family, string $style, int $size): array {
$font = $pdf->font->insert($pdf->pon, $family, $style, $size, 0.0, 1.0);
$pdf->page->addContent($font['out']);
return $font;
};
$getRectStyle = static function (string $strokeColor, string $fillColor): array {
return [
'all' => [
'lineWidth' => 2.0,
'lineCap' => 'square',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => $strokeColor,
'fillColor' => $fillColor,
],
];
};
// Insert one neutral font before addPage() so page context has a valid current font.
$pdf->font->insert($pdf->pon, 'helvetica', '', 10, 0.0, 1.0);
$page = $pdf->addPage();
$setFont($pdf, 'helvetica', '', 12);
$pdf->page->addContent($pdf->getTextCell(
'You can set the transparency of PDF objects using graph->getAlpha().',
15,
20,
180,
0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
// Opaque red square.
$pdf->page->addContent($pdf->graph->getRect(
30.0,
40.0,
60.0,
60.0,
'DF',
$getRectStyle('rgb(127,0,0)', 'rgb(255,0,0)'),
));
// Semi-transparent objects.
$pdf->page->addContent($pdf->graph->getAlpha(0.5));
$pdf->page->addContent($pdf->graph->getRect(
50.0,
60.0,
60.0,
60.0,
'DF',
$getRectStyle('rgb(0,127,0)', 'rgb(0,255,0)'),
));
$pdf->page->addContent($pdf->graph->getRect(
70.0,
80.0,
60.0,
60.0,
'DF',
$getRectStyle('rgb(0,0,127)', 'rgb(0,0,255)'),
));
$imagePath = __DIR__ . '/images/tcpdf_logo.jpg';
if (\is_file($imagePath)) {
$imageId = $pdf->image->add($imagePath);
$pdf->page->addContent($pdf->image->getSetImage($imageId, 90.0, 100.0, 60.0, 60.0, $page['height']));
}
// Restore full opacity.
$pdf->page->addContent($pdf->graph->getAlpha(1.0));
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,159 @@
<?php
/**
* E026_text_rendering_modes.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 026');
$pdf->setTitle('Text rendering modes and clipping');
$pdf->setKeywords('TCPDF tc-lib-pdf example text rendering mode clipping stroke fill');
$pdf->setPDFFilename('026_text_rendering_modes.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$setFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, string $family, string $style, int $size): array {
$font = $pdf->font->insert($pdf->pon, $family, $style, $size, 0.0, 1.0);
$pdf->page->addContent($font['out']);
return $font;
};
$drawTextModeLine = static function (
\Com\Tecnick\Pdf\Tcpdf $pdf,
string $text,
float $y,
float $strokeWidth,
bool $fill,
bool $stroke,
bool $clip,
int $imageId,
float $pageHeight,
): void {
if ($clip) {
$pdf->page->addContent($pdf->graph->getStartTransform());
}
$pdf->page->addContent($pdf->getTextCell(
$text,
15.0,
$y,
180.0,
0.0,
strokewidth: $strokeWidth,
fill: $fill,
stroke: $stroke,
clip: $clip,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
if ($clip && $imageId > 0) {
$pdf->page->addContent($pdf->image->getSetImage($imageId, 15.0, $y + 2.0, 170.0, 10.0, $pageHeight));
$pdf->page->addContent($pdf->graph->getStopTransform());
}
};
// Insert one neutral font before addPage() so page context has a valid current font.
$pdf->font->insert($pdf->pon, 'helvetica', '', 10, 0.0, 1.0);
$page = $pdf->addPage();
$setFont($pdf, 'helvetica', '', 11);
$pdf->page->addContent($pdf->getTextCell(
'Text rendering modes: fill, stroke, fill+stroke, invisible, and clipping.',
15.0,
20.0,
180.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$setFont($pdf, 'helvetica', '', 22);
$pdf->page->addContent($pdf->color->getPdfColor('black', false));
$pdf->page->addContent($pdf->color->getPdfColor('rgb(255,0,0)', true));
$drawTextModeLine($pdf, 'Fill text', 35.0, 0.0, true, false, false, 0, $page['height']);
$drawTextModeLine($pdf, 'Stroke text', 47.0, 0.2, false, true, false, 0, $page['height']);
$drawTextModeLine($pdf, 'Fill, then stroke text', 59.0, 0.2, true, true, false, 0, $page['height']);
$drawTextModeLine($pdf, 'Neither fill nor stroke text (invisible)', 71.0, 0.0, false, false, false, 0, $page['height']);
$imagePath = __DIR__ . '/images/tcpdf_logo.jpg';
$imageId = 0;
if (\is_file($imagePath)) {
$imageId = $pdf->image->add($imagePath);
}
$drawTextModeLine(
$pdf,
'Fill text and add to path for clipping',
95.0,
0.0,
true,
false,
true,
$imageId,
$page['height'],
);
$drawTextModeLine(
$pdf,
'Stroke text and add to path for clipping',
107.0,
0.3,
false,
true,
true,
$imageId,
$page['height'],
);
$drawTextModeLine(
$pdf,
'Fill, then stroke text and add to path for clipping',
119.0,
0.3,
true,
true,
true,
$imageId,
$page['height'],
);
$drawTextModeLine($pdf, 'Add text to path for clipping', 131.0, 0.0, false, false, true, $imageId, $page['height']);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,553 @@
<?php
/**
* E027_annotations.php
*
* Demonstrates all currently supported annotation subtypes.
*
* @since 2026-04-27
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 027');
$pdf->setTitle('All Supported Annotation Types');
$pdf->setKeywords('TCPDF tc-lib-pdf example annotation all subtypes');
$pdf->setPDFFilename('027_annotations.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$setFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, string $family, string $style, int $size): void {
$font = $pdf->font->insert($pdf->pon, $family, $style, $size, 0.0, 1.0);
$pdf->page->addContent($font['out']);
};
$pdf->font->insert($pdf->pon, 'helvetica', '', 12, 0.0, 1.0);
$leftMargin = 20.0;
$annX = 108.0;
$annW = 40.0;
$rowH = 10.0;
$convertTopToPdfCoordinates = static function (array $coords, float $pageHeight): array {
$out = [];
foreach ($coords as $idx => $val) {
if (!is_numeric($val)) {
continue;
}
$num = (float) $val;
if (($idx % 2) === 1) {
$num = $pageHeight - $num;
}
$out[] = $num;
}
return $out;
};
$normalizeAnnotationOptionCoordinates = static function (array $opt, float $pageHeight) use (
$convertTopToPdfCoordinates,
): array {
$subtype = strtolower((string) ($opt['subtype'] ?? ''));
if ($subtype === 'line' && isset($opt['l']) && is_array($opt['l'])) {
$opt['l'] = $convertTopToPdfCoordinates($opt['l'], $pageHeight);
}
if (in_array($subtype, ['polygon', 'polyline'], true) && isset($opt['vertices']) && is_array($opt['vertices'])) {
$opt['vertices'] = $convertTopToPdfCoordinates($opt['vertices'], $pageHeight);
}
if (in_array($subtype, ['highlight', 'underline', 'squiggly', 'strikeout', 'redact'], true)) {
if (isset($opt['quadpoints']) && is_array($opt['quadpoints'])) {
$quadOut = [];
foreach ($opt['quadpoints'] as $quad) {
if (!is_array($quad)) {
continue;
}
$quadOut[] = $convertTopToPdfCoordinates($quad, $pageHeight);
}
$opt['quadpoints'] = $quadOut;
}
}
if ($subtype === 'ink' && isset($opt['inklist']) && is_array($opt['inklist'])) {
$inkOut = [];
foreach ($opt['inklist'] as $line) {
if (!is_array($line)) {
continue;
}
$inkOut[] = $convertTopToPdfCoordinates($line, $pageHeight);
}
$opt['inklist'] = $inkOut;
}
return $opt;
};
$addRow = static function (
\Com\Tecnick\Pdf\Tcpdf $pdf,
string $label,
float $y,
string $txt,
array $opt,
float $pageHeight,
string $preview = '',
) use ($leftMargin, $annX, $annW, $rowH, $normalizeAnnotationOptionCoordinates): int {
$x = $leftMargin;
$labelW = $annX - $x - 1.0;
$pdf->page->addContent($pdf->getTextCell(
$label,
$x,
$y,
$labelW,
$rowH,
drawcell: true,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
'',
$annX,
$y,
$annW,
$rowH,
drawcell: true,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
if ($preview !== '') {
$pdf->page->addContent($pdf->getTextCell(
$preview,
$annX + 0.4,
$y + 0.3,
$annW - 0.8,
$rowH - 0.6,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
}
$aid = $pdf->setAnnotation(
posx: $annX,
posy: $y,
width: $annW,
height: $rowH,
txt: $txt,
opt: $normalizeAnnotationOptionCoordinates($opt, $pageHeight),
);
$pdf->page->addAnnotRef($aid);
return $aid;
};
$mkQuadPoints = static function (float $x, float $y, float $w, float $h): array {
return [[
$x,
$y + $h,
$x + $w,
$y + $h,
$x,
$y,
$x + $w,
$y,
]];
};
$attachmentPath = __DIR__ . '/data/utf8test.txt';
$soundPath = __DIR__ . '/data/utf8test.txt';
$page1 = $pdf->addPage();
$setFont($pdf, 'helvetica', 'B', 16);
$pdf->page->addContent($pdf->getTextCell(
'All Supported Annotation Types (page 1/2)',
$leftMargin,
10,
190,
9,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$setFont($pdf, 'helvetica', '', 10);
$y = 22.0;
$textAnnotId = $addRow(
$pdf,
'Text',
$y,
"Text note body\nwith two lines",
['subtype' => 'Text', 'name' => 'Comment', 't' => 'Text note'],
$page1['height'],
);
$y += 10.0;
$addRow(
$pdf,
'Link (URI)',
$y,
'https://github.com/tecnickcom/tc-lib-pdf',
['subtype' => 'Link', 'h' => 'I'],
$page1['height'],
'Open URL',
);
$y += 10.0;
$addRow(
$pdf,
'FreeText',
$y,
'FreeText content',
['subtype' => 'FreeText', 'da' => '/F1 10 Tf', 'q' => 1, 'it' => 'FreeTextTypeWriter'],
$page1['height'],
);
$y += 10.0;
$addRow(
$pdf,
'Line',
$y,
'Line annotation',
[
'subtype' => 'Line',
'l' => [$annX + 1.0, $y + ($rowH - 1.0), $annX + $annW - 1.0, $y + 1.0],
'le' => ['OpenArrow', 'ClosedArrow'],
'cap' => true,
'it' => 'LineDimension',
],
$page1['height'],
);
$y += 10.0;
$addRow($pdf, 'Square', $y, 'Square annotation', ['subtype' => 'Square', 'ic' => [0.9, 0.9, 0.5]], $page1['height']);
$y += 10.0;
$addRow($pdf, 'Circle', $y, 'Circle annotation', ['subtype' => 'Circle', 'ic' => [0.7, 0.9, 1.0]], $page1['height']);
$y += 10.0;
$addRow(
$pdf,
'Polygon',
$y,
'Polygon annotation',
[
'subtype' => 'Polygon',
'vertices' => [
$annX,
$y + ($rowH - 2.0),
$annX + 14.0,
$y + 1.5,
$annX + 28.0,
$y + ($rowH - 2.0),
$annX + $annW,
$y + 1.5,
],
],
$page1['height'],
);
$y += 10.0;
$addRow(
$pdf,
'Polyline',
$y,
'Polyline annotation',
[
'subtype' => 'Polyline',
'vertices' => [
$annX,
$y + 1.5,
$annX + 12.0,
$y + ($rowH - 2.0),
$annX + 24.0,
$y + 1.5,
$annX + $annW,
$y + ($rowH - 2.0),
],
'le' => ['Circle', 'Slash'],
],
$page1['height'],
);
$y += 10.0;
$qp1 = $mkQuadPoints($annX, $y, $annW, $rowH);
$addRow(
$pdf,
'Highlight',
$y,
'Highlight annotation',
['subtype' => 'Highlight', 'quadpoints' => $qp1],
$page1['height'],
'Sample text',
);
$y += 10.0;
$qp2 = $mkQuadPoints($annX, $y, $annW, $rowH);
$addRow(
$pdf,
'Underline',
$y,
'Underline annotation',
['subtype' => 'Underline', 'quadpoints' => $qp2],
$page1['height'],
'Sample text',
);
$y += 10.0;
$qp3 = $mkQuadPoints($annX, $y, $annW, $rowH);
$addRow(
$pdf,
'Squiggly',
$y,
'Squiggly annotation',
['subtype' => 'Squiggly', 'quadpoints' => $qp3],
$page1['height'],
'Sample text',
);
$y += 10.0;
$qp4 = $mkQuadPoints($annX, $y, $annW, $rowH);
$addRow(
$pdf,
'StrikeOut',
$y,
'StrikeOut annotation',
['subtype' => 'StrikeOut', 'quadpoints' => $qp4],
$page1['height'],
'Sample text',
);
$y += 10.0;
$addRow($pdf, 'Stamp', $y, 'Stamp annotation', ['subtype' => 'Stamp', 'name' => 'Approved'], $page1['height']);
$y += 10.0;
$addRow($pdf, 'Caret', $y, 'Caret annotation', ['subtype' => 'Caret', 'sy' => 'P'], $page1['height']);
$y += 10.0;
$addRow(
$pdf,
'Ink',
$y,
'Ink annotation',
[
'subtype' => 'Ink',
'inklist' => [[
$annX,
$y + ($rowH - 2.0),
$annX + 14.0,
$y + 2.0,
$annX + 28.0,
$y + ($rowH - 2.0),
$annX + $annW,
$y + 2.5,
]],
],
$page1['height'],
);
$y += 10.0;
$addRow(
$pdf,
'Popup (parent: Text)',
$y,
'Popup annotation',
['subtype' => 'Popup', 'parent' => ['n' => $textAnnotId], 'open' => false],
$page1['height'],
'Popup target',
);
$y += 10.0;
if (is_file($attachmentPath)) {
$addRow(
$pdf,
'FileAttachment',
$y,
'Attached file',
['subtype' => 'FileAttachment', 'name' => 'PushPin', 'fs' => $attachmentPath],
$page1['height'],
);
} else {
$pdf->page->addContent($pdf->getTextCell(
txt: 'FileAttachment skipped: missing examples/data/utf8test.txt',
posx: $leftMargin,
posy: $y,
width: 190,
height: 7,
));
}
$page2 = $pdf->addPage();
$setFont($pdf, 'helvetica', 'B', 16);
$pdf->page->addContent($pdf->getTextCell(
'All Supported Annotation Types (page 2/2)',
$leftMargin,
10,
190,
9,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$setFont($pdf, 'helvetica', '', 10);
$y = 22.0;
if (is_file($soundPath)) {
$addRow(
$pdf,
'Sound',
$y,
'Sound payload',
['subtype' => 'Sound', 'name' => 'Speaker', 'fs' => $soundPath],
$page2['height'],
'Sound icon',
);
} else {
$pdf->page->addContent($pdf->getTextCell(
txt: 'Sound skipped: missing examples/data/utf8test.txt',
posx: $leftMargin,
posy: $y,
width: 190,
height: 7,
));
}
$y += 10.0;
$addRow(
$pdf,
'Movie',
$y,
'Movie payload',
[
'subtype' => 'Movie',
't' => 'Movie title',
'movie' => ['f' => 'sample.mov', 'aspect' => [16.0, 9.0], 'poster' => true],
'a' => ['showcontrols' => true, 'mode' => 'Repeat'],
],
$page2['height'],
'Movie action',
);
$y += 10.0;
$addRow(
$pdf,
'Screen',
$y,
'Screen payload',
[
'subtype' => 'Screen',
't' => 'Screen title',
'mk' => ['r' => 90, 'bc' => [0.1, 0.2, 0.3], 'tp' => 2],
'a' => '/S /Named /N /NextPage',
],
$page2['height'],
'Screen action',
);
$y += 10.0;
$addRow(
$pdf,
'Widget',
$y,
'field-demo',
[
'subtype' => 'Widget',
'h' => 'N',
't' => 'demo_widget',
'v' => 'abc',
'da' => '/F1 10 Tf',
'q' => 0,
],
$page2['height'],
'Widget field',
);
$y += 10.0;
$redactQuad = $mkQuadPoints($annX, $y, $annW, $rowH);
$addRow(
$pdf,
'Redact',
$y,
'Redact payload',
[
'subtype' => 'Redact',
'quadpoints' => $redactQuad,
'overlaytext' => 'REDACTED',
'repeat' => true,
'da' => '/F1 10 Tf',
'q' => 1,
],
$page2['height'],
'Sample text',
);
$y += 10.0;
$addRow(
$pdf,
'Watermark',
$y,
'Watermark payload',
[
'subtype' => 'Watermark',
'fixedprint' => [
'type' => 'FixedPrint',
'matrix' => [1.0, 0.0, 0.0, 1.0, 5.0, 5.0],
'h' => 0.5,
'v' => 0.5,
],
],
$page2['height'],
);
$y += 12.0;
$pdf->page->addContent($pdf->getTextCell(
'Notes: PrinterMark, TrapNet, and 3D are currently intentionally unsupported and are not included in this demo.',
$leftMargin,
$y,
190,
0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,117 @@
<?php
/**
* E028_text_hyphenation.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 028');
$pdf->setTitle('Text hyphenation with soft hyphens');
$pdf->setKeywords('TCPDF tc-lib-pdf example text hyphenation soft hyphen shy html');
$pdf->setPDFFilename('028_text_hyphenation.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$setFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, string $family, string $style, int $size): void {
$font = $pdf->font->insert($pdf->pon, $family, $style, $size, 0.0, 1.0);
$pdf->page->addContent($font['out']);
};
// Insert one neutral font before addPage() so page context has a valid current font.
$pdf->font->insert($pdf->pon, 'helvetica', '', 10, 0.0, 1.0);
$pdf->addPage();
$setFont($pdf, 'helvetica', 'B', 20);
$pdf->page->addContent($pdf->getTextCell(
'Example of Text Hyphenation',
15.0,
22.0,
180.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$setFont($pdf, 'helvetica', '', 10);
$pdf->page->addContent($pdf->getTextCell(
'The text below uses explicit soft hyphens (&shy;) inside words and is rendered in a narrow left-aligned column.',
15.0,
34.0,
180.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$setFont($pdf, 'times', '', 10);
$html =
'<div style="color:#003f7f;text-align:justify">'
. 'On the other hand, we de&shy;nounce with righ&shy;teous in&shy;dig&shy;na&shy;tion '
. 'and dis&shy;like men who are so be&shy;guiled and de&shy;mo&shy;r&shy;al&shy;ized by the charms '
. 'of plea&shy;sure of the mo&shy;ment, so blind&shy;ed by de&shy;sire, that they can&shy;not fore&shy;see '
. 'the pain and trou&shy;ble that are bound to en&shy;sue; and equal blame be&shy;longs to '
. 'those who fail in their du&shy;ty through weak&shy;ness of will, which is the same as '
. 'say&shy;ing through shrink&shy;ing from toil and pain. Th&shy;ese cas&shy;es are per&shy;fect&shy;ly '
. 'sim&shy;ple and easy to distin&shy;guish. In a free hour, when our pow&shy;er of choice is '
. 'un&shy;tram&shy;melled and when noth&shy;ing pre&shy;vents our be&shy;ing able to do what we like '
. 'best, ev&shy;ery plea&shy;sure is to be wel&shy;comed and ev&shy;ery pain avoid&shy;ed. But in '
. 'cer&shy;tain cir&shy;cum&shy;s&shy;tances and ow&shy;ing to the claims of du&shy;ty or the obli&shy;ga&shy;tions '
. 'of busi&shy;ness it will fre&shy;quent&shy;ly oc&shy;cur that plea&shy;sures have to be '
. 're&shy;pu&shy;di&shy;at&shy;ed and an&shy;noy&shy;ances ac&shy;cept&shy;ed. The wise man there&shy;fore al&shy;ways holds '
. 'in th&shy;ese mat&shy;ters to this prin&shy;ci&shy;ple of se&shy;lec&shy;tion: he re&shy;jects plea&shy;sures to '
. 'se&shy;cure other greater plea&shy;sures, or else he en&shy;dures pains to avoid worse pains.'
. '</div>';
$cellBorderStyles = [
'all' => [
'lineWidth' => 0.2,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'red',
'fillColor' => '',
],
];
$pdf->addHTMLCell(html: $html, posx: 15.0, posy: 45.0, width: 42.0, height: 0.0, cell: null, styles: $cellBorderStyles);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,135 @@
<?php
/**
* E029_xobject_template.php
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 029');
$pdf->setTitle('XObject template');
$pdf->setKeywords('TCPDF tc-lib-pdf example xobject template transparency clipping');
$pdf->setPDFFilename('029_xobject_template.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$setFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, string $family, string $style, int $size): array {
$font = $pdf->font->insert($pdf->pon, $family, $style, $size, 0.0, 1.0);
$pdf->page->addContent($font['out']);
return $font;
};
// Insert one neutral font before addPage() so page context has a valid current font.
$pdf->font->insert($pdf->pon, 'helvetica', '', 10, 0.0, 1.0);
$pdf->addPage();
$setFont($pdf, 'helvetica', 'B', 20);
$pdf->page->addContent($pdf->getTextCell(
'XObject Templates',
15.0,
22.0,
180.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
$setFont($pdf, 'helvetica', '', 10);
$pdf->page->addContent($pdf->getTextCell(
'The same template object is rendered multiple times with different sizes and alpha values.',
15.0,
34.0,
180.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$templateWidth = 60.0;
$templateHeight = 60.0;
$templateGroup = ['CS' => 'DeviceRGB', 'I' => true, 'K' => false];
$templateId = $pdf->newXObjectTemplate($templateWidth, $templateHeight, $templateGroup);
$imageId = 0;
$imagePath = __DIR__ . '/images/tcpdf_logo.jpg';
if (\is_file($imagePath)) {
$imageId = $pdf->image->add($imagePath);
$pdf->addXObjectImageID($templateId, $imageId);
}
$templateContent = $pdf->graph->getStartTransform();
$templateContent .= $pdf->graph->getStarPolygon(30.0, 30.0, 29.0, 10, 3, 0.0, 'CNZ');
if ($imageId > 0) {
$templateContent .= $pdf->image->getSetImage($imageId, 0.0, 0.0, $templateWidth, $templateHeight, $templateHeight);
}
$templateContent .= $pdf->graph->getStopTransform();
$templateFont = $pdf->font->insert($pdf->pon, 'times', 'B', 15, 0.0, 1.0);
$pdf->addXObjectFontID($templateId, $templateFont['key']);
$templateContent .= $templateFont['out'];
$templateContent .= $pdf->color->getPdfColor('rgb(255,0,0)', true);
$templateContent .= $pdf->getTextCell(
'Template',
0.0,
22.0,
$templateWidth,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
);
$pdf->addXObjectContent($templateId, $templateContent);
$pdf->exitXObjectTemplate();
$instances = [
[0.4, 15.0, 50.0, 20.0],
[0.6, 27.0, 62.0, 40.0],
[0.8, 55.0, 85.0, 60.0],
[1.0, 95.0, 125.0, 80.0],
];
foreach ($instances as [$alpha, $x, $y, $size]) {
$pdf->page->addContent($pdf->graph->getAlpha((float) $alpha));
$pdf->page->addContent($pdf->getXObjectTemplate($templateId, (float) $x, (float) $y, (float) $size, (float) $size));
}
$pdf->page->addContent($pdf->graph->getAlpha(1.0));
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
+518
View File
@@ -0,0 +1,518 @@
<?php
/**
* E030_svg.php
*
* Dedicated SVG rendering example.
*
* Exercises all SVG image files shipped in the examples/images/ directory and
* serves as a visual regression baseline for SVG feature development.
*
* @since 2026-04-26
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example_svg.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// ---------------------------------------------------------------------------
// SVG directory
$svgdir = __DIR__ . '/images';
// ---------------------------------------------------------------------------
// PDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 030 — SVG');
$pdf->setTitle('SVG Rendering Example');
$pdf->setKeywords('TCPDF tc-lib-pdf SVG example');
$pdf->setPDFFilename('030_svg.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// Insert a font for page labels
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
// ---------------------------------------------------------------------------
// Page 1 — tcpdf_box.svg
//
// A multi-path box illustration that exercises fill, stroke, fill-rule and
// transform handling.
// ---------------------------------------------------------------------------
$page01 = $pdf->addPage();
$pdf->setBookmark(name: 'tcpdf_box.svg', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'blue');
$pdf->page->addContent($bfont['out']);
// Label
$pdf->addTextCell(
txt: 'tcpdf_box.svg — paths, fill, stroke, fill-rule',
pid: -1,
posx: 10,
posy: 10,
width: 190,
height: 6,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: false,
);
// Render at two different sizes to check scaling
$svg01a = $pdf->addSVG(
img: $svgdir . '/tcpdf_box.svg',
posx: 10,
posy: 20,
width: 180,
height: 120,
pageheight: $page01['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg01a));
$svg01b = $pdf->addSVG(
img: $svgdir . '/tcpdf_box.svg',
posx: 10,
posy: 148,
width: 90,
height: 60,
pageheight: $page01['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg01b));
$svg01c = $pdf->addSVG(
img: $svgdir . '/tcpdf_box.svg',
posx: 105,
posy: 148,
width: 45,
height: 30,
pageheight: $page01['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg01c));
// ---------------------------------------------------------------------------
// Page 2 — testsvg.svg
//
// Exercises linear and radial gradients, clip-paths, complex paths, text and
// image embedding inside an SVG document.
// ---------------------------------------------------------------------------
$page02 = $pdf->addPage();
$pdf->setBookmark(name: 'testsvg.svg', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'green');
$pdf->page->addContent($bfont['out']);
$pdf->addTextCell(
txt: 'testsvg.svg — gradients, clip-paths, text, embedded image',
pid: -1,
posx: 10,
posy: 10,
width: 190,
height: 6,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: false,
);
// Full-width render
$svg02a = $pdf->addSVG(
img: $svgdir . '/testsvg.svg',
posx: 10,
posy: 20,
width: 190,
height: 95,
pageheight: $page02['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg02a));
// Smaller render to verify scaling
$svg02b = $pdf->addSVG(
img: $svgdir . '/testsvg.svg',
posx: 10,
posy: 120,
width: 95,
height: 47,
pageheight: $page02['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg02b));
$svg02c = $pdf->addSVG(
img: $svgdir . '/testsvg.svg',
posx: 110,
posy: 120,
width: 47,
height: 23,
pageheight: $page02['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg02c));
// ---------------------------------------------------------------------------
// Page 3 — testsvgblend.svg
//
// Exercises mix-blend-mode (multiply, screen, darken …) and opacity.
// ---------------------------------------------------------------------------
$page03 = $pdf->addPage();
$pdf->setBookmark(name: 'testsvgblend.svg', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'red');
$pdf->page->addContent($bfont['out']);
$pdf->addTextCell(
txt: 'testsvgblend.svg — blend modes, opacity',
pid: -1,
posx: 10,
posy: 10,
width: 190,
height: 6,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: false,
);
$svg03a = $pdf->addSVG(
img: $svgdir . '/testsvgblend.svg',
posx: 10,
posy: 20,
width: 190,
height: 127,
pageheight: $page03['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg03a));
// Side-by-side scaled copies
$svg03b = $pdf->addSVG(
img: $svgdir . '/testsvgblend.svg',
posx: 10,
posy: 155,
width: 90,
height: 60,
pageheight: $page03['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg03b));
$svg03c = $pdf->addSVG(
img: $svgdir . '/testsvgblend.svg',
posx: 105,
posy: 155,
width: 90,
height: 60,
pageheight: $page03['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg03c));
// ---------------------------------------------------------------------------
// Page 4 — tux.svg
//
// The Tux penguin — a complex multi-path SVG with fine detail and many style
// attributes (fill, stroke, stroke-width, opacity).
//
// Copyright: Larry Ewing — permits any use with proper attribution.
// ---------------------------------------------------------------------------
$page04 = $pdf->addPage();
$pdf->setBookmark(name: 'tux.svg', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'black');
$pdf->page->addContent($bfont['out']);
$pdf->addTextCell(
txt: 'tux.svg — complex multi-path illustration (The copyright holder of the Tux image is Larry Ewing)',
pid: -1,
posx: 10,
posy: 10,
width: 190,
height: 6,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: false,
);
// Large centred render
$svg04a = $pdf->addSVG(
img: $svgdir . '/tux.svg',
posx: 55,
posy: 20,
width: 100,
height: 121,
pageheight: $page04['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg04a));
// Three scaled thumbnails
$svg04b = $pdf->addSVG(
img: $svgdir . '/tux.svg',
posx: 10,
posy: 148,
width: 55,
height: 66,
pageheight: $page04['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg04b));
$svg04c = $pdf->addSVG(
img: $svgdir . '/tux.svg',
posx: 70,
posy: 148,
width: 40,
height: 48,
pageheight: $page04['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg04c));
$svg04d = $pdf->addSVG(
img: $svgdir . '/tux.svg',
posx: 115,
posy: 148,
width: 25,
height: 30,
pageheight: $page04['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg04d));
// ---------------------------------------------------------------------------
// Page 5 — all four images together (overview grid)
// ---------------------------------------------------------------------------
$page05 = $pdf->addPage();
$pdf->setBookmark(
name: 'SVG overview grid',
link: '',
level: 0,
page: -1,
posx: 0,
posy: 0,
fstyle: 'B',
color: 'purple',
);
$pdf->page->addContent($bfont['out']);
$pdf->addTextCell(
txt: 'SVG overview grid — all four images at equal size',
pid: -1,
posx: 10,
posy: 10,
width: 190,
height: 6,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: false,
);
// Row 1
$s1 = $pdf->addSVG(
img: $svgdir . '/tcpdf_box.svg',
posx: 10,
posy: 20,
width: 90,
height: 60,
pageheight: $page05['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $s1));
$s2 = $pdf->addSVG(
img: $svgdir . '/testsvg.svg',
posx: 105,
posy: 20,
width: 90,
height: 60,
pageheight: $page05['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $s2));
// Row 2
$s3 = $pdf->addSVG(
img: $svgdir . '/testsvgblend.svg',
posx: 10,
posy: 88,
width: 90,
height: 60,
pageheight: $page05['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $s3));
$s4 = $pdf->addSVG(
img: $svgdir . '/tux.svg',
posx: 105,
posy: 88,
width: 90,
height: 109,
pageheight: $page05['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $s4));
// ---------------------------------------------------------------------------
// Page 6 — abstract_a4_features.svg (full-page portrait)
//
// Dedicated A4 portrait abstract composition that concentrates most SVG
// features currently supported by this library in one single visual sample.
// ---------------------------------------------------------------------------
$page06 = $pdf->addPage();
$pdf->setBookmark(
name: 'abstract_a4_features.svg',
link: '',
level: 0,
page: -1,
posx: 0,
posy: 0,
fstyle: 'B',
color: 'orange',
);
$svg06a = $pdf->addSVG(
img: $svgdir . '/abstract_a4_features.svg',
posx: 0,
posy: 0,
width: $page06['width'],
height: $page06['height'],
pageheight: $page06['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg06a));
// ---------------------------------------------------------------------------
// Page 7 — path_text_regressions.svg (full-page portrait)
//
// Visual regression sheet for the path and text parsing rules that are easy to
// break: numbers with no leading digit, packed decimals, scientific notation,
// elliptical arc flags packed against their neighbours, the smooth curve
// commands 'S' and 'T', compound subpaths under both fill rules, text
// anchoring, XML white space handling and multi chunk text layout.
//
// Every shape is drawn twice, as a pale fill from a verbose path and as a dark
// outline from the compact equivalent: the outline must trace the fill exactly.
// ---------------------------------------------------------------------------
$page07 = $pdf->addPage();
$pdf->setBookmark(
name: 'path_text_regressions.svg',
link: '',
level: 0,
page: -1,
posx: 0,
posy: 0,
fstyle: 'B',
color: 'purple',
);
$svg07a = $pdf->addSVG(
img: $svgdir . '/path_text_regressions.svg',
posx: 0,
posy: 0,
width: $page07['width'],
height: $page07['height'],
pageheight: $page07['height'],
);
$pdf->page->addContent($pdf->getSetSVG(soid: $svg07a));
// ---------------------------------------------------------------------------
// Output
// ---------------------------------------------------------------------------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,721 @@
<?php
/**
* E031_html_features.php
*
* @since 2026-04-27
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 031');
$pdf->setTitle('HTML Features Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example HTML selectors forms table tags');
$pdf->setPDFFilename('031_html_features.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$page01 = $pdf->addPage();
$pdf->setBookmark(
name: 'HTML feature showcase',
link: '',
level: 0,
page: -1,
posx: 0,
posy: 0,
fstyle: 'B',
color: '',
);
$pdf->page->addContent($bfont['out']);
// NOTE:
// The HTML parser expects input already cleaned to XHTML-like markup (e.g. via tidyHTML),
// and attribute extraction is intentionally based on double-quoted values.
// Unsupported interactive selectors intentionally remain no-op (for example :hover).
// For table-structure tags, this example demonstrates current support for
// caption/colgroup/col/tfoot; unsupported styling on colgroup/col remains
// intentionally a no-op in this conservative implementation.
$html = <<<HTML
<style>
.demo {
font-size: 10pt;
color: #222222;
}
.demo h2 {
color: #003366;
margin: 0 0 3mm 0;
}
.demo .selector-list li:first-child {
color: #0b6e4f;
font-weight: bold;
}
.demo .selector-list li:last-child {
color: #8a1c7c;
}
.demo .selector-list li:nth-child(2n+1) {
background-color: #f4f7ff;
}
.demo .selector-list li:nth-child(-n+2) {
text-decoration: underline;
}
.demo .group > p + a.target {
color: #0066cc;
}
.demo .group > p ~ a.target {
font-weight: bold;
}
.demo .generated::before {
content: "[PRE] ";
color: #b03030;
}
.demo .generated::after {
content: " [POST]";
font-weight: bold;
}
.demo p:empty {
background-color: #fff2cc;
border: 0.2mm solid #c7a600;
height: 4mm;
}
.demo a:link {
text-decoration: underline;
}
.demo table {
border: 0.2mm solid #666666;
}
.demo th {
background-color: #dddddd;
}
.demo input,
.demo select,
.demo textarea {
border: 0.2mm solid #777777;
}
.note {
font-size: 8pt;
color: #555555;
}
</style>
<div class="demo">
<h2>HTML Feature Showcase</h2>
<p>This example demonstrates currently implemented selector, table, and form behavior in the HTML engine.</p>
<h3>1) Pseudo-class selectors</h3>
<ul class="selector-list">
<li>First item (first-child + odd + -n+2)</li>
<li>Second item (-n+2)</li>
<li>Third item (odd)</li>
<li>Fourth item (last-child)</li>
</ul>
<h3>2) Combinators (+ and ~)</h3>
<div class="group">
<p>Adjacent sibling trigger paragraph</p>
<a class="target" href="https://github.com/tecnickcom/tc-lib-pdf">Adjacent + general sibling target link</a>
</div>
<h3>3) Empty pseudo-class</h3>
<p></p>
<h3>4) Pseudo-elements (::before / ::after, text-only content)</h3>
<p><span class="generated">Generated marker sample</span></p>
<h3>5) Stable table rendering with selectors applied</h3>
<table border="1" cellpadding="2" cellspacing="0" style="width:100%">
<tr>
<th style="width:20%">Key</th>
<th style="width:80%">Value</th>
</tr>
<tr>
<td>first-child</td>
<td>Supported</td>
</tr>
<tr>
<td>last-child</td>
<td>Supported</td>
</tr>
<tr>
<td>nth-child</td>
<td>Supports n, odd/even, and an+b (for example 2n+1, -n+2)</td>
</tr>
<tr>
<td>empty</td>
<td>Supported</td>
</tr>
<tr>
<td>link</td>
<td>Supported for anchors with href</td>
</tr>
</table>
<h3>6) Table structure tags (caption, colgroup, col, tfoot)</h3>
<table border="1" cellpadding="2" cellspacing="0" style="width:100%">
<caption>Quarterly feature status</caption>
<colgroup>
<col width="22%">
<col span="2" width="78%">
</colgroup>
<thead>
<tr>
<th>Area</th>
<th>State</th>
<th>Notes</th>
</tr>
</thead>
<tr>
<td>caption</td>
<td>Rendered</td>
<td>Displayed as a block-level table title.</td>
</tr>
<tr>
<td>colgroup/col width hints</td>
<td>Applied</td>
<td>Used as precomputed column width hints before first-row fallback.</td>
</tr>
<tfoot>
<tr>
<td>tfoot</td>
<td>Parsed</td>
<td>Footer row contributes to table row/column bookkeeping.</td>
</tr>
</tfoot>
</table>
<h3>7) Form semantics (labels, field flags, select precedence)</h3>
<p>
<label for="contact_email">Email (for-id label):</label>
<input type="email" id="contact_email" name="contact_email" value="user@example.com" maxlength="64" width="150">
</p>
<p>
<label>Phone (wrapping label fallback):
<input type="text" name="contact_phone" value="+39 010 123 456" size="70" width="150">
</label>
</p>
<p>
<label for="priority_pick">Priority:</label>
<select id="priority_pick" name="priority_pick" value="normal" width="200">
<optgroup label="Standard">
<option value="normal">Normal</option>
<option value="low">Low</option>
</optgroup>
<optgroup label="Urgent">
<option value="high" selected>High (explicit selected wins)</option>
<option value="critical">Critical</option>
</optgroup>
</select>
</p>
<p>
<label for="notes">Notes (disabled textarea mapping):</label>
<textarea id="notes" name="notes" cols="32" rows="2" maxlength="120" disabled>Handled by support queue</textarea>
<br>
<span class="note">Visible fallback text: Handled by support queue</span>
</p>
<p class="note">
Unsupported selector examples intentionally remain unmatched: :hover, :focus, :active, and :visited.
</p>
<p class="note">
Pseudo-elements are intentionally limited to text-only `content:"..."` behavior in this conservative implementation.
</p>
<p class="note">
Unsupported styling on structural tags like colgroup/col is intentionally ignored in this conservative HTML engine pass.
</p>
<p class="note">
This form section is focused on parser/field-mapping behavior; it is not intended to replicate browser form UX.
</p>
</div>
HTML;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
// ----------
// HTML B
$pageV02 = $pdf->addPage();
$rect = $pdf->graph->getRect(160, 36, 30, 50, 'DF');
$pdf->page->addContent($rect);
$textcolors = '<h2>Text</h1>';
$bgcolors = '<h2>Background</h2>';
$csscolortypes =
'<h2>CSS Color Types</h2>'
. '<table border="1" cellpadding="3" cellspacing="2" style="width:100%; font-size:9pt;">'
. '<tr><th style="width:20%">Type</th><th style="width:80%">Example</th></tr>'
. '<tr><td>named</td><td style="background-color: navy; color: #ffffff;">background-color: navy</td></tr>'
. '<tr><td>gray</td><td style="background-color: g(50%); color: #ffffff;">background-color: g(50%)</td></tr>'
. '<tr><td>hex short</td><td style="background-color: #0f0; color: #000000;">background-color: #0f0</td></tr>'
. '<tr><td>hex long</td><td style="background-color: #1e90ff; color: #ffffff;">background-color: #1e90ff</td></tr>'
. '<tr><td>rgb</td><td style="background-color: rgb(255, 99, 71); color: #000000;">background-color: rgb(255, 99, 71)</td></tr>'
. '<tr><td>rgba</td><td style="background-color: rgba(30, 144, 255, 0.25); color: #000000;">background-color: rgba(30, 144, 255, 0.25)</td></tr>'
. '<tr><td>hsl</td><td style="background-color: hsl(120, 100%, 25%); color: #ffffff;">background-color: hsl(120, 100%, 25%)</td></tr>'
. '<tr><td>hsla</td><td style="background-color: hsla(300, 100%, 50%, 0.20); color: #000000;">background-color: hsla(300, 100%, 50%, 0.20)</td></tr>'
. '<tr><td>lab</td><td style="background-color: lab(54.29% -19.04 38.25); color: #ffffff;">background-color: lab(54.29% -19.04 38.25)</td></tr>'
. '<tr><td>lab alpha</td><td style="background-color: lab(54.29% -19.04 38.25 / 0.35); color: #ffffff;">background-color: lab(54.29% -19.04 38.25 / 0.35)</td></tr>'
. '<tr><td>cmyk</td><td style="background-color: cmyk(67, 33, 0, 25); color: #ffffff;">background-color: cmyk(67, 33, 0, 25)</td></tr>'
. '</table>';
foreach ($pdf->color::WEBHEX as $k => $v) {
$textcolors .= '<span color="#' . $v . '">' . $k . '</span> ';
$bgcolors .= '<span bgcolor="#' . $v . '" color="#333333">' . $k . '</span> ';
}
$html_02 = '<h1>HTML Colors</h1>' . $csscolortypes . '<hr />' . $textcolors . '<hr />' . $bgcolors;
$pdf->addHTMLCell(html: $html_02, posx: 20, posy: 10, width: 180);
// ----------
// HTML D - Transparency Showcase
$pageV02T = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->setBookmark(
name: 'Transparency color showcase',
link: '',
level: 0,
page: -1,
posx: 0,
posy: 0,
fstyle: 'B',
color: '',
);
$baseRectStyle = [
'lineWidth' => 0,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'miterLimit' => 10,
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'none',
'fillColor' => 'rgb(0, 64, 128)',
];
$pdf->page->addContent($pdf->graph->getRect(20, 24, 170, 96, 'F', ['all' => $baseRectStyle]));
$html_transparency = <<<HTML
<style>
.alpha-demo {
font-size: 10pt;
color: #ffffff;
}
.alpha-demo h2 {
margin: 0 0 2mm 0;
font-size: 16pt;
color: rgba(255, 255, 255, 0.62);
}
.alpha-demo .line-a {
font-size: 12pt;
color: rgba(255, 255, 255, 0.48);
margin: 0 0 2mm 0;
}
.alpha-demo .line-b {
font-size: 12pt;
color: hsla(60, 100%, 86%, 0.58);
margin: 0 0 3mm 0;
}
.alpha-demo .bg-strip {
margin-top: 1mm;
padding: 1.3mm 1.8mm;
background-color: rgba(0, 0, 0, 0.30);
color: rgba(255, 255, 255, 0.9);
}
.alpha-demo .swatch {
display: inline-block;
margin-right: 2mm;
padding: 0.7mm 1.6mm;
color: rgba(0, 0, 0, 0.86);
font-weight: bold;
}
.alpha-demo .swatch-rgba {
background-color: rgba(30, 144, 255, 0.32);
}
.alpha-demo .swatch-hsla {
background-color: hsla(300, 100%, 60%, 0.30);
}
.alpha-demo .swatch-rgba2 {
background-color: rgba(255, 255, 255, 0.45);
}
</style>
<div class="alpha-demo">
<h2>Transparency Color Showcase</h2>
<p class="line-a">RGBA text: white alpha at 0.48 over a solid blue rectangle</p>
<p class="line-b">HSLA text: warm yellow alpha at 0.58 over the same blue base</p>
<div class="bg-strip">Background object with alpha: rgba(0, 0, 0, 0.30)</div>
<p>
<span class="swatch swatch-rgba">RGBA bg 0.32</span>
<span class="swatch swatch-hsla">HSLA bg 0.30</span>
<span class="swatch swatch-rgba2">RGBA bg 0.45</span>
</p>
</div>
HTML;
$pdf->addHTMLCell(html: $html_transparency, posx: 24, posy: 28, width: 162);
// ----------
// HTML C
$pageV03 = $pdf->addPage();
$html_03 = '<h1>Various HTML Tests</h1>
<font face="courier"><b>thisisaverylongword</b></font> <font face="helvetica"><i>thisisanotherverylongword</i></font> <font face="times"><b>thisisaverylongword</b></font> thisisanotherverylongword <font face="times">thisisaverylongword</font> <font face="courier"><b>thisisaverylongword</b></font> <font face="helvetica"><i>thisisanotherverylongword</i></font> <font face="times"><b>thisisaverylongword</b></font> thisisanotherverylongword <font face="times">thisisaverylongword</font> <font face="courier"><b>thisisaverylongword</b></font> <font face="helvetica"><i>thisisanotherverylongword</i></font> <font face="times"><b>thisisaverylongword</b></font> thisisanotherverylongword <font face="times">thisisaverylongword</font> <font face="courier"><b>thisisaverylongword</b></font> <font face="helvetica"><i>thisisanotherverylongword</i></font> <font face="times"><b>thisisaverylongword</b></font> thisisanotherverylongword <font face="times">thisisaverylongword</font> <font face="courier"><b>thisisaverylongword</b></font> <font face="helvetica"><i>thisisanotherverylongword</i></font> <font face="times"><b>thisisaverylongword</b></font> thisisanotherverylongword <font face="times">thisisaverylongword</font>';
// Test fonts nesting
$htmlt1 = 'Default <font face="courier">Courier <font face="helvetica">Helvetica <font face="times">Times <font face="dejavusans">dejavusans </font>Times </font>Helvetica </font>Courier </font>Default';
$htmlt2 = '<small>small text</small> normal <small>small text</small> normal <sub>subscript</sub> normal <sup>superscript</sup> normal';
$htmlt3 = '<font size="10" color="#ff7f50">The</font> <font size="10" color="#6495ed">quick</font> <font size="14" color="#dc143c">brown</font> <font size="18" color="#008000">fox</font> <font size="22"><a href="https://tcpdf.org">jumps</a></font> <font size="22" color="#a0522d">over</font> <font size="18" color="#da70d6">the</font> <font size="14" color="#9400d3">lazy</font> <font size="10" color="#4169ef">dog</font>.';
$html_03 .=
'<br />' . $htmlt1 . '<br />' . $htmlt2 . '<br />' . $htmlt3 . '<br />' . $htmlt3 . '<br />' . $htmlt2 . '<br />';
$html_03 .= '<div><span style="font-size:8pt; line-height:110%;">#<span style="font-size:18pt">1</span>a</span></div>';
$html_03 .= <<<EOF
<hr />
<h2>Div Blocks</h2>
<div style="background-color:#880000;color:white;">
Hello World!<br />
Hello
</div>
<pre style="background-color:#336699;color:white;">
int main() {
printf("HelloWorld");
return 0;
}
</pre>
<tt>Monospace font</tt>, normal font, <tt>monospace font</tt>, normal font.
<br />
<div style="background-color:#880000;color:white;">DIV LEVEL 1<div style="background-color:#008800;color:white;">DIV LEVEL 2</div>DIV LEVEL 1</div>
<br />
<span style="background-color:#880000;color:white;">SPAN LEVEL 1 <span style="background-color:#008800;color:white;">SPAN LEVEL 2</span> SPAN LEVEL 1</span>
EOF;
$pdf->addHTMLCell(html: $html_03, posx: 20, posy: 10, width: 180);
// ----------
// HTML E-A
$pageV05A = $pdf->addPage();
$html_05A = '<h2>HTML Text Alignment (A)</h2>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. <em>Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur?</em> <em>Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</em><br /><br /><b>A</b> + <b>B</b> = <b>C</b> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>B</i> = <i>A</i> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>A</i> = <i>B</i> -&gt; &nbsp;&nbsp; <b>A</b> + <b>B</b> = <b>C</b> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>B</i> = <i>A</i> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>A</i> = <i>B</i> -&gt; &nbsp;&nbsp; <b>A</b> + <b>B</b> = <b>C</b> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>B</i> = <i>A</i> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>A</i> = <i>B</i> -&gt; &nbsp;&nbsp; <b>A</b> + <b>B</b> = <b>C</b> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>B</i> = <i>A</i> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>A</i> = <i>B</i> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <b>A</b> + <b>B</b> = <b>C</b> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>B</i> = <i>A</i> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>A</i> = <i>B</i> -&gt; &nbsp;&nbsp; <b>A</b> + <b>B</b> = <b>C</b> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>B</i> = <i>A</i> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>A</i> = <i>B</i> -&gt; &nbsp;&nbsp; <b>A</b> + <b>B</b> = <b>C</b> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>B</i> = <i>A</i> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>A</i> = <i>B</i> -&gt; &nbsp;&nbsp; <b>A</b> + <b>B</b> = <b>C</b> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>B</i> = <i>A</i> &nbsp;&nbsp; -&gt; &nbsp;&nbsp; <i>C</i> - <i>A</i> = <i>B</i><br /><br /><b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u> <b>Bold</b><i>Italic</i><u>Underlined</u>';
$pdf->addHTMLCell(html: $html_05A, posx: 20, posy: 10, width: 180);
// ----------
// HTML E-L
$html_05L = '<p>Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo. Lima Mike November Oscar Papa Quebec Romeo (<em>Sierra-Tango</em>) Uniform Victor Whiskey (<em>Xray-Yankee</em>). Zulu. Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo. Lima Mike November Oscar Papa Quebec Romeo (<em>Sierra-Tango</em>) Uniform Victor Whiskey (<em>Xray-Yankee</em>). Zulu. Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo. Lima Mike November Oscar Papa Quebec Romeo (<em>Sierra-Tango</em>) Uniform Victor Whiskey (<em>Xray-Yankee</em>). Zulu.</p>';
$pdf->addHTMLCell(html: $html_05L, posx: 20, posy: 100, width: 180);
// ----------
// HTML E-B
$pageV05B = $pdf->addPage();
$html_05B = '<h2>HTML Text Alignment (B)</h2>
<div style="text-align:justify;">JUSTIFY: Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xray Yankee Zulu Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa
Quebec Romeo</div>
<br />
<div style="text-align:left;">LEFT: Alfa <i>Bravo</i> Charlie <i>Delta</i> Echo <i>Foxtrot</i> Golf <i>Hotel</i> India <i>Juliett</i> Kilo <i>Lima</i> Mike <i>November</i> Oscar <i>Papa</i> Quebec <i>Romeo</i> Sierra <i>Tango</i> Uniform <i>Victor</i> Whiskey <i>Xray</i> Yankee <i>Zulu</i></div>
<br />
<div style="text-align:center;">CENTER: Alfa <i>Bravo</i> Charlie <i>Delta</i> Echo <i>Foxtrot</i> Golf <i>Hotel</i> India <i>Juliett</i> Kilo <i>Lima</i> Mike <i>November</i> Oscar <i>Papa</i> Quebec <i>Romeo</i> Sierra <i>Tango</i> Uniform <i>Victor</i> Whiskey <i>Xray</i> Yankee <i>Zulu</i></div>
<br />
<div style="text-align:right;">RIGHT: Alfa <i>Bravo</i> Charlie <i>Delta</i> Echo <i>Foxtrot</i> Golf <i>Hotel</i> India <i>Juliett</i> Kilo <i>Lima</i> Mike <i>November</i> Oscar <i>Papa</i> Quebec <i>Romeo</i> Sierra <i>Tango</i> Uniform <i>Victor</i> Whiskey <i>Xray</i> Yankee <i>Zulu</i></div>
<br />
<div style="text-align:justify;">JUSTIFY: Alfa <i>Bravo</i> Charlie <i>Delta</i> Echo <i>Foxtrot</i> Golf <i>Hotel</i> India <i>Juliett</i> Kilo <i>Lima</i> Mike <i>November</i> Oscar <i>Papa</i> Quebec <i>Romeo</i> Sierra <i>Tango</i> Uniform <i>Victor</i> Whiskey <i>Xray</i> Yankee <i>Zulu</i></div>
<br />
<div style="text-align:justify;">JUSTIFY: Alfa <i>Bravo</i> Charlie <i>Delta</i> Echo <img src="images/tcpdf_logo.jpg" alt="TCPDF logo" width="89" height="30" border="0" /><img src="images/tcpdf_box.svg" alt="TCPDF box" width="100" height="67" border="0" /> <i>Foxtrot</i> Golf <i>Hotel</i> India <i>Juliett</i> Kilo <i>Lima</i> Mike <i>November</i> Oscar <i>Papa</i> Quebec <i>Romeo</i> Sierra <i>Tango</i> Uniform <i>Victor</i> Whiskey <i>Xray</i> Yankee <i>Zulu</i></div>
<br />
<div style="text-align:left;">LEFT: Alfa <i>Bravo</i> Charlie <i>Delta</i> Echo <img src="images/tcpdf_logo.jpg" alt="TCPDF logo" width="89" height="30" border="0" /><img src="images/tcpdf_box.svg" alt="TCPDF box" width="100" height="67" border="0" /> <i>Foxtrot</i> Golf <i>Hotel</i> India <i>Juliett</i> Kilo <i>Lima</i> Mike <i>November</i> Oscar <i>Papa</i> Quebec <i>Romeo</i> Sierra <i>Tango</i> Uniform <i>Victor</i> Whiskey <i>Xray</i> Yankee <i>Zulu</i></div>
<br />
<div style="text-align:center;">CENTER: Alfa <i>Bravo</i> Charlie <i>Delta</i> Echo <img src="images/tcpdf_logo.jpg" alt="TCPDF logo" width="89" height="30" border="0" /><img src="images/tcpdf_box.svg" alt="TCPDF box" width="100" height="67" border="0" /> <i>Foxtrot</i> Golf <i>Hotel</i> India <i>Juliett</i> Kilo <i>Lima</i> Mike <i>November</i> Oscar <i>Papa</i> Quebec <i>Romeo</i> Sierra <i>Tango</i> Uniform <i>Victor</i> Whiskey <i>Xray</i> Yankee <i>Zulu</i></div>
<br />
<div style="text-align:right;">RIGHT: Alfa <i>Bravo</i> Charlie <i>Delta</i> Echo <img src="images/tcpdf_logo.jpg" alt="TCPDF logo" width="89" height="30" border="0" /><img src="images/tcpdf_box.svg" alt="TCPDF box" width="100" height="67" border="0" /> <i>Foxtrot</i> Golf <i>Hotel</i> India <i>Juliett</i> Kilo <i>Lima</i> Mike <i>November</i> Oscar <i>Papa</i> Quebec <i>Romeo</i> Sierra <i>Tango</i> Uniform <i>Victor</i> Whiskey <i>Xray</i> Yankee <i>Zulu</i></div>
<br /><hr />
<div><span style="text-align:justify;">a <u>abc</u> abcdefghijkl (abcdef) abcdefg <b>abcdefghi</b> a ((abc)) abcd <img src="images/tcpdf_logo.jpg" alt="TCPDF logo" width="89" height="30" border="0" /> abcdef abcdefg <b>abcdefghi</b> a abc abcd abcdef abcdefg <b>abcdefghi</b> a abc abcd abcdef abcdefg <b>abcdefghi</b> a <u>abc</u> abcd abcdef abcdefg <b>abcdefghi</b> a abc \(abcd\) abcdef abcdefg <b>abcdefghi</b> a abc \\\(abcd\\\) abcdef abcdefg <b>abcdefghi</b> a abc abcd abcdef abcdefg <b>abcdefghi</b> a abc abcd abcdef abcdefg <b>abcdefghi</b> a abc abcd abcdef abcdefg abcdefghi a abc abcd <a href="https://tcpdf.org">abcdef abcdefg</a> start a abc before <span style="background-color:yellow">yellow color</span> after a abc abcd abcdef abcdefg abcdefghi a abc abcd end abcdefg abcdefghi a abc abcd abcdef abcdefg abcdefghi a abc abcd abcdef abcdefg abcdefghi a abc abcd abcdef abcdefg abcdefghi a abc abcd abcdef abcdefg abcdefghi a abc abcd abcdef abcdefg abcdefghi a abc abcd abcdef abcdefg abcdefghi a abc abcd abcdef abcdefg abcdefghi<br />abcd abcdef abcdefg abcdefghi<br />abcd abcde abcdef</span></div><hr />';
$pdf->addHTMLCell(html: $html_05B, posx: 20, posy: 10, width: 180);
// ----------
// HTML F
$pageV06 = $pdf->addPage();
$html_06 = <<<EOF
<!-- EXAMPLE OF CSS STYLE -->
<style>
h1 {
color: navy;
font-family: times;
font-size: 24pt;
text-decoration: underline;
}
p.first {
color: #003300;
font-family: helvetica;
font-size: 12pt;
}
p.first span {
color: #006600;
font-style: italic;
}
p#second {
color: rgb(00,63,127);
font-family: times;
font-size: 12pt;
text-align: justify;
}
p#second > span {
background-color: #FFFFAA;
}
table.first {
color: #003300;
font-family: helvetica;
font-size: 8pt;
border-left: 3px solid red;
border-right: 3px solid #FF00FF;
border-top: 3px solid green;
border-bottom: 3px solid blue;
background-color: #ccffcc;
}
td {
border: 2px solid blue;
background-color: #ffffee;
}
td.second {
border: 2px dashed green;
}
div.test {
color: #CC0000;
background-color: #FFFF66;
font-family: helvetica;
font-size: 10pt;
border-style: solid solid solid solid;
border-width: 2px 2px 2px 2px;
border-color: green #FF00FF blue red;
text-align: center;
}
.lowercase {
text-transform: lowercase;
}
.uppercase {
text-transform: uppercase;
}
.capitalize {
text-transform: capitalize;
}
</style>
<h1 class="title">Example of <i style="color:#990000">XHTML + CSS</i></h1>
<p class="first">Example of paragraph with class selector. <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sed imperdiet lectus. Phasellus quis velit velit, non condimentum quam. Sed neque urna, ultrices ac volutpat vel, laoreet vitae augue. Sed vel velit erat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras eget velit nulla, eu sagittis elit. Nunc ac arcu est, in lobortis tellus. Praesent condimentum rhoncus sodales. In hac habitasse platea dictumst. Proin porta eros pharetra enim tincidunt dignissim nec vel dolor. Cras sapien elit, ornare ac dignissim eu, ultricies ac eros. Maecenas augue magna, ultrices a congue in, mollis eu nulla. Nunc venenatis massa at est eleifend faucibus. Vivamus sed risus lectus, nec interdum nunc.</span></p>
<p id="second">Example of paragraph with ID selector. <span>Fusce et felis vitae diam lobortis sollicitudin. Aenean tincidunt accumsan nisi, id vehicula quam laoreet elementum. Phasellus egestas interdum erat, et viverra ipsum ultricies ac. Praesent sagittis augue at augue volutpat eleifend. Cras nec orci neque. Mauris bibendum posuere blandit. Donec feugiat mollis dui sit amet pellentesque. Sed a enim justo. Donec tincidunt, nisl eget elementum aliquam, odio ipsum ultrices quam, eu porttitor ligula urna at lorem. Donec varius, eros et convallis laoreet, ligula tellus consequat felis, ut ornare metus tellus sodales velit. Duis sed diam ante. Ut rutrum malesuada massa, vitae consectetur ipsum rhoncus sed. Suspendisse potenti. Pellentesque a congue massa.</span></p>
<div class="test">example of DIV with border and fill.
<br />Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<br /><span class="lowercase">text-transform <b>LOWERCASE</b> Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
<br /><span class="uppercase">text-transform <b>uppercase</b> Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
<br /><span class="capitalize">text-transform <b>cAPITALIZE</b> Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
</div>
EOF;
$pdf->addHTMLCell(html: $html_06, posx: 20, posy: 10, width: 180);
// ----------
// HTML A
$pageV01 = $pdf->addPage();
$bfont6 = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
$pdf->page->addContent($bfont6['out']);
$style_cell = [
'all' => [
'lineWidth' => 1,
'lineCap' => 'round',
'lineJoin' => 'round',
'miterLimit' => 1,
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'green',
'fillColor' => '#fdfca5',
],
];
$html_01 = '<h1>HTML Example A</h1>
Some special characters: &lt; € &euro; &#8364; &amp; è &egrave; &copy; &gt; \\slash \\\\double-slash \\\\\\triple-slash
<h2>List</h2>
List example:
<ol>
<li><b>bold text</b></li>
<li><i>italic text</i></li>
<li><u>underlined text</u></li>
<li><b>b<i>bi<u>biu</u>bi</i>b</b></li>
<li><a href="https://tcpdf.org" dir="ltr">link to https://tcpdf.org</a></li>
<li>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.<br />Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.</li>
<li>SUBLIST
<ol>
<li>row one
<ul>
<li>sublist</li>
</ul>
</li>
<li>row two</li>
</ol>
</li>
<li><b>T</b>E<i>S</i><u>T</u> <del>line through</del></li>
<li><font size="+3">font + 3</font></li>
<li><small>small text</small> normal <small>small text</small> normal <sub>subscript</sub> normal <sup>superscript</sup> normal</li>
</ol>
<dl>
<dt>Coffee</dt>
<dd>Black hot drink</dd>
<dt>Milk</dt>
<dd>White cold drink</dd>
</dl>
<div style="text-align:center">The words &#8220;<span dir="rtl">&#1502;&#1494;&#1500; [mazel] &#1496;&#1493;&#1489; [tov]</span>&#8221; mean &#8220;Congratulations!&#8221;</div>
<p>This is just an example of html code to demonstrate some supported CSS inline styles.
<span style="font-weight: bold;">bold text</span>
<span style="text-decoration: line-through;">line-trough</span>
<span style="text-decoration: underline line-through;">underline and line-trough</span>
<span style="color: rgb(0, 128, 64);">color</span>
<span style="background-color: rgb(255, 0, 0); color: rgb(255, 255, 255);">background color</span>
<span style="font-weight: bold;">bold</span>
<span style="font-size: xx-small;">xx-small</span>
<span style="font-size: x-small;">x-small</span>
<span style="font-size: small;">small</span>
<span style="font-size: medium;">medium</span>
<span style="font-size: large;">large</span>
<span style="font-size: x-large;">x-large</span>
<span style="font-size: xx-large;">xx-large</span>
</p>';
$pdf->addHTMLCell(html: $html_01, posx: 20, posy: 10, width: 150, height: 0, cell: null, styles: $style_cell);
// ----------
$pageVC = $pdf->addPage();
$pdf->page->addContent($bfont6['out']);
$html_vc1 = <<<HTML
<style>
.vc-box {
width: 150mm;
height: 90mm;
border: 0.6mm solid #003366;
background-color: #e9f2ff;
}
.vc-fill {
width: 100%;
height: 100%;
}
.vc-content {
vertical-align: middle;
text-align: center;
color: #002244;
}
</style>
<div class="vc-box">
<table class="vc-fill" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="vc-content">
<h1>Vertically Centered Block</h1>
<p>This content is centered in the middle of the box.</p>
<p><b>HTML:</b> headings, paragraphs, and inline formatting.</p>
</td>
</tr>
</table>
</div>
HTML;
$pdf->addHTMLCell(html: $html_vc1, posx: 20, posy: 20);
// ----------
$html_vc2 = <<<HTML
<table border="1" cellpadding="0" cellspacing="0" style="width:150mm; height:100mm; background-color:blue; border-color:#003366;">
<tr>
<td style="height:100mm; vertical-align:middle; text-align:center; color:cmyk(100,0,0,0);">
<h1>Vertically Centered Block</h1>
<p>This content is centered in the middle of the box.</p>
<p><b>HTML:</b> headings, paragraphs, and inline formatting.</p>
</td>
</tr>
</table>
HTML;
$pdf->addHTMLCell(html: $html_vc2, posx: 20, posy: 120, width: 150, height: 100);
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,302 @@
<?php
/**
* E032_html_lists.php
*
* @since 2026-04-28
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 032');
$pdf->setTitle('HTML List Item CSS Variations');
$pdf->setKeywords('TCPDF tc-lib-pdf example html list css ul ol li');
$pdf->setPDFFilename('032_html_lists.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$bfont = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
// $bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$pdf->addPage();
$pdf->setBookmark(
name: 'HTML list item CSS variations',
link: '',
level: 0,
page: -1,
posx: 0,
posy: 0,
fstyle: 'B',
color: '',
);
$pdf->page->addContent($bfont['out']);
// This example showcases all currently supported list-item CSS behavior:
// - list-style-type
// - list-style-position (inside / outside)
// - margin-left / padding-left
// - text-indent first-line behavior on list-item text
// - nested OL/UL interactions
// - li::marker styling (color, font-weight, font-style)
// - list-style-image CSS property parsing
$html = <<<HTML
<style>
body {
color: #222222;
font-size: 10pt;
}
h1 {
color: #0f2b46;
margin-bottom: 3mm;
}
h2 {
color: #1e4f80;
margin-top: 4mm;
margin-bottom: 2mm;
}
.note {
font-size: 8pt;
color: #5b5b5b;
}
.panel {
background-color: #f7fbff;
padding: 2mm;
margin-bottom: 2.5mm;
}
/* Variation set: list-style-type */
.ul-disc { list-style-type: disc; }
.ul-circle { list-style-type: circle; }
.ul-square { list-style-type: square; }
.ol-decimal { list-style-type: decimal; }
.ol-upper-alpha { list-style-type: upper-alpha; }
.ol-lower-roman { list-style-type: lower-roman; }
/* Variation set: list-style-position */
.pos-inside { list-style-position: inside; }
.pos-outside { list-style-position: outside; }
/* Variation set: margin/padding on containers and items */
.list-shift-a {
margin-left: 0mm;
padding-left: 3mm;
}
.list-shift-b {
margin-left: 6mm;
padding-left: 1mm;
}
.list-shift-c {
margin-left: 10mm;
padding-left: 0mm;
}
.list-shift-a li { margin-left: 0mm; padding-left: 0mm; }
.list-shift-b li { margin-left: 1mm; padding-left: 1mm; }
.list-shift-c li { margin-left: 2mm; padding-left: 0mm; }
/* Variation set: text-indent (first-line and hanging) */
.indent-first li { text-indent: 4mm; }
.indent-hanging li { text-indent: -2mm; }
/* Variation set: li::marker styling */
.marker-red li::marker { color: red; }
.marker-blue li::marker { color: blue; }
.marker-green li::marker { color: #008000; }
/* Variation set: list-style-image (custom bullets) */
.list-img-svg { list-style-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4IiBoZWlnaHQ9IjgiPjxjaXJjbGUgY3g9IjQiIGN5PSI0IiByPSI0IiBmaWxsPSIjRkY2NjAwIi8+PC9zdmc+); }
/* Visual hint only */
.marker-guide,
.marker-line {
border-left: 0.2mm solid #ff0000;
padding-left: 1.5mm;
}
</style>
<h1>HTML List Item CSS Variations (Current Support)</h1>
<div class="panel">
<h2>1) Unordered list styles (`list-style-type`)</h2>
<ul class="ul-disc marker-guide">
<li>Disc marker</li>
<li>Nested sample
<ul class="ul-circle">
<li>Circle marker</li>
<li>Another nested item</li>
</ul>
</li>
</ul>
<ul class="ul-square marker-guide">
<li>Square marker</li>
<li>Second square marker item</li>
</ul>
</div>
<div class="panel">
<h2>2) Ordered list styles (`list-style-type`)</h2>
<ol class="ol-decimal marker-guide">
<li>Decimal marker</li>
<li>Second item</li>
</ol>
<ol class="ol-upper-alpha marker-guide">
<li>Upper-alpha marker</li>
<li>Second item</li>
</ol>
<ol class="ol-lower-roman marker-guide">
<li>Lower-roman marker</li>
<li>Second item</li>
</ol>
</div>
<div class="panel">
<h2>3) Marker position (`list-style-position`)</h2>
<ul class="ul-disc pos-outside marker-guide">
<li>Outside marker with long text to show wrap behavior for this rendering path in the PDF engine.</li>
<li>Second outside item</li>
</ul>
<ul class="ul-disc pos-inside marker-guide">
<li>Inside marker with long text to show wrap behavior for this rendering path in the PDF engine.</li>
<li>Second inside item</li>
</ul>
</div>
<div class="panel">
<h2>4) Container and item indentation (`margin-left` / `padding-left`)</h2>
<ul class="ul-disc list-shift-a marker-guide">
<li>Shift A</li>
<li>Shift A nested
<ol class="ol-decimal">
<li>Nested ordered item</li>
</ol>
</li>
</ul>
<ul class="ul-disc list-shift-b marker-guide">
<li>Shift B</li>
<li>Shift B nested
<ol class="ol-upper-alpha">
<li>Nested ordered item</li>
</ol>
</li>
</ul>
<ul class="ul-disc list-shift-c marker-guide">
<li>Shift C</li>
<li>Shift C nested
<ol class="ol-lower-roman">
<li>Nested ordered item</li>
</ol>
</li>
</ul>
</div>
<div class="panel">
<h2>5) Mixed nesting matrix (`ol` inside `ul`, `ul` inside `ol`)</h2>
<ul class="ul-square marker-guide">
<li>UL root item
<ol class="ol-decimal pos-outside">
<li>OL nested in UL
<ul class="ul-circle pos-inside">
<li>UL nested in OL</li>
<li>Second nested UL item</li>
</ul>
</li>
</ol>
</li>
</ul>
</div>
<div class="panel">
<h2>6) List text indentation (`text-indent`)</h2>
<ol class="ol-decimal indent-first marker-guide">
<li>First-line indent with long text to make wrapping visible in the list-item rendering path. First-line indent with long text to make wrapping visible in the list-item rendering path.</li>
<li><span>First-line</span> indent with long text to <span>make</span> wrapping visible in the list-item rendering path. (B) First-line indent with long text to make wrapping visible in the list-item rendering path.</li>
<li>Second item with first-line indent style applied.</li>
</ol>
<ol class="ol-decimal indent-hanging marker-guide">
<li>Hanging-indent style (negative text-indent) with long text to highlight first-line offset behavior.</li>
<li>Second item using the same hanging-indent rule.</li>
</ol>
</div>
<div class="panel">
<h2>7) Marker color styling (`li::marker`)</h2>
<ol class="ol-decimal marker-red marker-guide">
<li>Red marker color applied via li::marker selector</li>
<li>Second item with red marker styling</li>
</ol>
<ul class="ul-disc marker-blue marker-guide">
<li>Blue marker color applied via li::marker selector</li>
<li>Second unordered item with blue marker</li>
</ul>
<ol class="ol-upper-alpha marker-green marker-guide">
<li>Green marker color applied via li::marker selector</li>
<li>Second item with green marker styling</li>
</ol>
</div>
<div class="panel">
<h2>8) Custom bullet images (`list-style-image`)</h2>
<p class="note">Note: Image bullet rendering is subject to image loading availability and fallback behavior.</p>
<ul class="list-img-svg marker-guide">
<li>Custom SVG bullet marker image</li>
<li>Second item with custom image bullet</li>
<li>Nested list with custom bullets
<ul class="list-img-svg">
<li>Nested custom image bullet item</li>
<li>Another nested custom image bullet</li>
</ul>
</li>
</ul>
</div>
HTML;
$pdf->addHTMLCell(html: $html, posx: 10, posy: 20, width: 190);
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,295 @@
<?php
/**
* E033_geometric_transformations.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 033');
$pdf->setTitle('Geometric Transformations');
$pdf->setKeywords('TCPDF tc-lib-pdf transformations rotate scale translate skew matrix');
$pdf->setPDFFilename('033_geometric_transformations.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 8);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
$page01 = $pdf->addPage();
$pdf->setBookmark(name: 'Transformations', link: '', level: 1);
// Document title and explanation
$titleHtml = <<<HTML
<h1 style="font-size:16pt; color:#003366;">Geometric Transformations</h1>
<p style="font-size:9pt; color:#333333;">
This example demonstrates the geometric transformation operations available in tc-lib-pdf:
<b>scaling</b>, <b>translation</b>, <b>rotation</b>, <b>skewing</b>, <b>mirroring</b>,
and combined <b>affine transformations.</b>
Each operation is shown with a reference rectangle (black) and the transformed result (red).
</p>
HTML;
$pdf->addHTMLCell(html: $titleHtml, posx: 15, posy: 15, width: 180);
// Geometric Transformations
$rect_style = [
'lineWidth' => 0.3,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'black',
'fillColor' => 'black',
];
$transform_style = [
'lineWidth' => 0.3,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'red',
'fillColor' => 'red',
];
$pdf->page->addContent($pdf->getTextCell(
'Scaling: 150% around (50, 80)',
50,
60,
65,
6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
// Scaling
$t1 = $pdf->graph->getBasicRect(50, 70, 40, 10, 'D', $rect_style);
$t1 .= $pdf->graph->getStartTransform();
// Scale by 150% centered by (50,80) which is the lower left corner of the rectangle
$t1 .= $pdf->graph->getScaling(1.5, 1.5, 50, 80);
$t1 .= $pdf->graph->getBasicRect(50, 70, 40, 10, 'D', $transform_style);
$t1 .= $pdf->graph->getStopTransform();
$pdf->page->addContent($t1);
$pdf->page->addContent($pdf->getTextCell(
'Translation: +7 x, +5 y',
125,
66,
65,
6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
// Translation
$t2 = $pdf->graph->getBasicRect(125, 70, 40, 10, 'D');
$t2 .= $pdf->graph->getStartTransform();
// Translate 7 to the right, 5 to the bottom
$t2 .= $pdf->graph->getTranslation(7, 5);
$t2 .= $pdf->graph->getBasicRect(125, 70, 40, 10, 'D', $transform_style);
$t2 .= $pdf->graph->getStopTransform();
$pdf->page->addContent($t2);
$pdf->page->addContent($pdf->getTextCell(
'Rotation: 20 degrees around (70, 110)',
50,
112,
70,
6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
// Rotation
$t3 = $pdf->graph->getBasicRect(70, 100, 40, 10, 'D');
$t3 .= $pdf->graph->getStartTransform();
// Rotate 20 degrees counter-clockwise centered by (70,110) which is the lower left corner of the rectangle
$t3 .= $pdf->graph->getRotation(20, 70, 110);
$t3 .= $pdf->graph->getBasicRect(70, 100, 40, 10, 'D', $transform_style);
$t3 .= $pdf->graph->getStopTransform();
$pdf->page->addContent($t3);
$pdf->page->addContent($pdf->getTextCell(
'Skewing: 30 degrees on x-axis',
125,
96,
65,
6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
// Skewing
$t4 = $pdf->graph->getBasicRect(125, 100, 40, 10, 'D');
$t4 .= $pdf->graph->getStartTransform();
// skew 30 degrees along the x-axis centered by (125,110) which is the lower left corner of the rectangle
$t4 .= $pdf->graph->getSkewing(30, 0, 125, 110);
$t4 .= $pdf->graph->getBasicRect(125, 100, 40, 10, 'D', $transform_style);
$t4 .= $pdf->graph->getStopTransform();
$pdf->page->addContent($t4);
$pdf->page->addContent($pdf->getTextCell(
'Horizontal mirror at x = 70',
50,
126,
65,
6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
// Mirroring Horizontally
$t5 = $pdf->graph->getBasicRect(70, 130, 40, 10, 'D');
$t5 .= $pdf->graph->getStartTransform();
// mirror horizontally with axis of reflection at x-position 70 (left side of the rectangle)
$t5 .= $pdf->graph->getHorizMirroring(70);
$t5 .= $pdf->graph->getBasicRect(70, 130, 40, 10, 'D', $transform_style);
$t5 .= $pdf->graph->getStopTransform();
$pdf->page->addContent($t5);
$pdf->page->addContent($pdf->getTextCell(
'Vertical mirror at y = 140',
125,
126,
65,
6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
// Mirroring Vertically
$t6 = $pdf->graph->getBasicRect(125, 130, 40, 10, 'D');
$t6 .= $pdf->graph->getStartTransform();
// mirror vertically with axis of reflection at y-position 140 (bottom side of the rectangle)
$t6 .= $pdf->graph->getVertMirroring(140);
$t6 .= $pdf->graph->getBasicRect(125, 130, 40, 10, 'D', $transform_style);
$t6 .= $pdf->graph->getStopTransform();
$pdf->page->addContent($t6);
$pdf->page->addContent($pdf->getTextCell(
'Point reflection at (70, 170)',
50,
156,
65,
6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
// Point Reflection
$t7 = $pdf->graph->getBasicRect(70, 160, 40, 10, 'D');
$t7 .= $pdf->graph->getStartTransform();
// point reflection at the lower left point of rectangle
$t7 .= $pdf->graph->getPointMirroring(70, 170);
$t7 .= $pdf->graph->getBasicRect(70, 160, 40, 10, 'D', $transform_style);
$t7 .= $pdf->graph->getStopTransform();
$pdf->page->addContent($t7);
$pdf->page->addContent($pdf->getTextCell(
'Line reflection through (120, 170), angle -20 degrees',
125,
156,
70,
6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
));
// Mirroring against a straigth line described by a point (120, 120) and an angle -20°
$angle = -20;
$px = 120;
$py = 170;
// just for visualisation: the straight line to mirror against
$t8 = $pdf->graph->getLine($px - 1, $py - 1, $px + 1, $py + 1, [
'lineColor' => 'green',
]);
$t8 .= $pdf->graph->getLine($px - 1, $py + 1, $px + 1, $py - 1, [
'lineColor' => 'green',
]);
$t8 .= $pdf->graph->getStartTransform();
$t8 .= $pdf->graph->getRotation($angle, $px, $py);
$t8 .= $pdf->graph->getLine($px - 5, $py, $px + 60, $py, [
'lineColor' => 'green',
]);
$t8 .= $pdf->graph->getStopTransform();
$t8 .= $pdf->graph->getBasicRect(125, 160, 40, 10, 'D', $rect_style);
$t8 .= $pdf->graph->getStartTransform();
// mirror against the straight line
$t8 .= $pdf->graph->getReflection($angle, $px, $py);
$t8 .= $pdf->graph->getBasicRect(125, 160, 40, 10, 'D', $transform_style);
$t8 .= $pdf->graph->getStopTransform();
$pdf->page->addContent($t8);
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,310 @@
<?php
/**
* E034_color_gradient_mesh.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 034');
$pdf->setTitle('Color Gradient Mesh');
$pdf->setKeywords('TCPDF tc-lib-pdf color gradient mesh shading patch');
$pdf->setPDFFilename('034_color_gradient_mesh.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
$page01 = $pdf->addPage();
$pdf->setBookmark(name: 'Color Gradients', link: '', level: 1);
$html =
'<h1>Color Gradients and Meshes</h1>'
. '<p>This page demonstrates linear and radial gradients together with Coons patch meshes rendered by tc-lib-pdf.</p>';
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
// Linear gradient
$lingrad = $pdf->graph->getLinearGradient(20, 80, 80, 80, 'red', 'blue', [0, 0, 1, 0]);
$pdf->page->addContent($lingrad);
// Radial Gradient
$radgrad = $pdf->graph->getRadialGradient(110, 80, 80, 80, 'white', 'black', [0.5, 0.5, 1, 1, 1.2]);
$pdf->page->addContent($radgrad);
// CoonsPatchMesh
$coonspatchmesh1 = $pdf->graph->getCoonsPatchMeshWithCoords(20, 180, 80, 80, 'yellow', 'blue', 'green', 'red');
$pdf->page->addContent($coonspatchmesh1);
// set the coordinates for the cubic Bézier points x1,y1 ... x12, y12 of the patch
$coords = [
0.00,
0.00,
0.33,
0.20,
//lower left
0.67,
0.00,
1.00,
0.00,
0.80,
0.33,
//lower right
0.80,
0.67,
1.00,
1.00,
0.67,
0.80,
//upper right
0.33,
1.00,
0.00,
1.00,
0.20,
0.67,
//upper left
0.00,
0.33,
]; //lower left
// paint a coons patch gradient with the above coordinates
$coonspatchmesh2 = $pdf->graph->getCoonsPatchMeshWithCoords(
110,
180,
80,
80,
'yellow',
'blue',
'green',
'red',
$coords,
0,
1,
);
$pdf->page->addContent($coonspatchmesh2);
// ----------
$page02 = $pdf->addPage();
$pdf->setBookmark(name: 'Color gradient mesh', link: '', level: 1);
// first patch: f = 0
$patch_array[0]['f'] = 0;
$patch_array[0]['points'] = [
0.00,
0.00,
0.33,
0.00,
0.67,
0.00,
1.00,
0.00,
1.00,
0.33,
0.8,
0.67,
1.00,
1.00,
0.67,
0.8,
0.33,
1.80,
0.00,
1.00,
0.00,
0.67,
0.00,
0.33,
];
$patch_array[0]['colors'][0] = [
'red' => 1,
'green' => 1,
'blue' => 0,
'alpha' => 1,
];
$patch_array[0]['colors'][1] = [
'red' => 1,
'green' => 1,
'blue' => 1,
'alpha' => 1,
];
$patch_array[0]['colors'][2] = [
'red' => 0,
'green' => 1,
'blue' => 0,
'alpha' => 1,
];
$patch_array[0]['colors'][3] = [
'red' => 1,
'green' => 0,
'blue' => 0,
'alpha' => 1,
];
// second patch - above the other: f = 2
$patch_array[1]['f'] = 2;
$patch_array[1]['points'] = [
0.00,
1.33,
0.00,
1.67,
0.00,
2.00,
0.33,
2.00,
0.67,
2.00,
1.00,
2.00,
1.00,
1.67,
1.5,
1.33,
];
$patch_array[1]['colors'][0] = [
'red' => 0,
'green' => 0,
'blue' => 0,
'alpha' => 1,
];
$patch_array[1]['colors'][1] = [
'red' => 1,
'green' => 0,
'blue' => 1,
'alpha' => 1,
];
// third patch - right of the above: f = 3
$patch_array[2]['f'] = 3;
$patch_array[2]['points'] = [
1.33,
0.80,
1.67,
1.50,
2.00,
1.00,
2.00,
1.33,
2.00,
1.67,
2.00,
2.00,
1.67,
2.00,
1.33,
2.00,
];
$patch_array[2]['colors'][0] = [
'red' => 0,
'green' => 1,
'blue' => 1,
'alpha' => 1,
];
$patch_array[2]['colors'][1] = [
'red' => 0,
'green' => 0,
'blue' => 0,
'alpha' => 1,
];
// fourth patch - below the above, which means left(?) of the above: f = 1
$patch_array[3]['f'] = 1;
$patch_array[3]['points'] = [
2.00,
0.67,
2.00,
0.33,
2.00,
0.00,
1.67,
0.00,
1.33,
0.00,
1.00,
0.00,
1.00,
0.33,
0.8,
0.67,
];
$patch_array[3]['colors'][0] = [
'red' => 0,
'green' => 0,
'blue' => 0,
'alpha' => 1,
];
$patch_array[3]['colors'][1] = [
'red' => 0,
'green' => 0,
'blue' => 1,
'alpha' => 1,
];
$coonspatchmesh3 = $pdf->graph->getCoonsPatchMesh(0, 0, 210, 297, $patch_array, 0, 2);
$pdf->page->addContent($coonspatchmesh3);
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,280 @@
<?php
/**
* E035_registration_marks.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 035');
$pdf->setTitle('Registration Marks');
$pdf->setKeywords('TCPDF tc-lib-pdf registration marks prepress crop bleed print');
$pdf->setPDFFilename('035_registration_marks.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
$page01 = $pdf->addPage();
$pdf->setBookmark(name: 'Crop Marks and Color Maps', link: '', level: 1);
$html = <<<HTML
<h1>Registration Marks</h1>
<p>
This example shows crop marks, registration targets, and color registration bars,
including spot-color bars used for print alignment checks.
</p>
HTML;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 15, width: 180);
$style1 = [
'lineWidth' => 0.5,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'darkorange',
'fillColor' => 'palegreen',
];
$pdf->graph->add($style1);
$style2 = $pdf->graph->getCurrentStyleArray();
$posx = 60;
$posy = 120;
// Crop Marks
$crpmrk1 = $pdf->graph->getCropMark($posx, $posy, 10, 10, 'TL', $style2);
$pdf->page->addContent($crpmrk1);
$crpmrk2 = $pdf->graph->getCropMark($posx + 90, $posy, 10, 10, 'TR', $style2);
$pdf->page->addContent($crpmrk2);
$crpmrk3 = $pdf->graph->getCropMark($posx, $posy + 50, 10, 10, 'BL', $style2);
$pdf->page->addContent($crpmrk3);
$crpmrk4 = $pdf->graph->getCropMark($posx + 90, $posy + 50, 10, 10, 'BR', $style2);
$pdf->page->addContent($crpmrk4);
$crpmrk5 = $pdf->graph->getCropMark($posx + 45, $posy - 5, 5, 5, 'LTR');
$pdf->page->addContent($crpmrk5);
$crpmrk6 = $pdf->graph->getCropMark($posx + 45, $posy + 55, 5, 5, 'LBR');
$pdf->page->addContent($crpmrk6);
$crpmrk7 = $pdf->graph->getCropMark($posx - 5, $posy + 25, 5, 5, 'TLB');
$pdf->page->addContent($crpmrk7);
$crpmrk8 = $pdf->graph->getCropMark($posx + 95, $posy + 25, 5, 5, 'TRB');
$pdf->page->addContent($crpmrk8);
$crpmrk9 = $pdf->graph->getCropMark($posx + 45, $posy + 70, 5, 5, 'TLBR', [
'lineColor' => 'lime',
]);
$pdf->page->addContent($crpmrk9);
// Registration Marks
$regmrk1 = $pdf->graph->getRegistrationMark($posx - 10, $posy - 10, 5, false);
$pdf->page->addContent($regmrk1);
$regmrk2 = $pdf->graph->getRegistrationMark($posx + 100, $posy - 10, 5, true);
$pdf->page->addContent($regmrk2);
$regmrk3 = $pdf->graph->getRegistrationMark($posx - 10, $posy + 60, 5, true);
$pdf->page->addContent($regmrk3);
$regmrk4 = $pdf->graph->getRegistrationMark($posx + 100, $posy + 60, 5, false, 'blue');
$pdf->page->addContent($regmrk4);
// CYMK Registration Mark
$regmrk5 = $pdf->graph->getCmykRegistrationMark($posx + 100, $posy + 85, 8);
$pdf->page->addContent($regmrk5);
// Add Spot Colors
$color_custom_dark_green = new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 1,
'magenta' => 0.5,
'yellow' => 0.8,
'key' => 0.45,
'alpha' => 0,
]);
$pdf->color->addSpotColor('Custom Dark Green', $color_custom_dark_green);
$color_custom_light_yellow = new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0,
'magenta' => 0,
'yellow' => 0.55,
'key' => 0,
'alpha' => 0,
]);
$pdf->color->addSpotColor('Custom Light Yellow', $color_custom_light_yellow);
$color_custom_black = new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0,
'magenta' => 0,
'yellow' => 0,
'key' => 1,
'alpha' => 0,
]);
$pdf->color->addSpotColor('Custom Black', $color_custom_black);
$color_custom_red = new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.3,
'magenta' => 1,
'yellow' => 0.9,
'key' => 0.1,
'alpha' => 0,
]);
$pdf->color->addSpotColor('Custom Red', $color_custom_red);
$color_custom_green = new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 1,
'magenta' => 0.3,
'yellow' => 1,
'key' => 0,
'alpha' => 0,
]);
$pdf->color->addSpotColor('Custom Green', $color_custom_green);
$color_custom_blue = new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 1,
'magenta' => 0.6,
'yellow' => 0.1,
'key' => 0.05,
'alpha' => 0,
]);
$pdf->color->addSpotColor('Custom Blue', $color_custom_blue);
$color_custom_yellow = new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0,
'magenta' => 0.2,
'yellow' => 1,
'key' => 0,
'alpha' => 0,
]);
$pdf->color->addSpotColor('Custom Yellow', $color_custom_yellow);
$style2 = [
'lineWidth' => 0,
'lineCap' => 0,
'lineJoin' => 0,
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'white',
'fillColor' => 'white',
];
$pdf->page->addContent($pdf->graph->getStyleCmd($style2));
// Color Registration Bars with spot colors
$colregspot = $pdf->graph->getColorRegistrationBar($posx - 20, $posy + 80, 100, 10, true, [
['black'],
['red'],
['green'],
['blue'],
['Custom Dark Green'],
['Custom Light Yellow'],
['Custom Black'],
['Custom Red'],
['Custom Green'],
['Custom Blue'],
['Custom Yellow'],
]);
$pdf->page->addContent($colregspot);
$colreg1 = $pdf->graph->getColorRegistrationBar($posx, $posy, 40, 40, false);
$pdf->page->addContent($colreg1);
$colreg2 = $pdf->graph->getColorRegistrationBar($posx + 40, $posy, 40, 40, true);
$pdf->page->addContent($colreg2);
$barcols = [
['black'],
['white'],
['red'],
['green'],
['blue'],
['cyan'],
['magenta'],
['yellow'],
['gray'],
['black'],
];
$colreg3 = $pdf->graph->getColorRegistrationBar($posx, $posy + 45, 80, 5, true, $barcols);
$pdf->page->addContent($colreg3);
$colreg4 = $pdf->graph->getColorRegistrationBar($posx + 85, $posy, 5, 50, false, $barcols);
$pdf->page->addContent($colreg4);
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,523 @@
<?php
/**
* E036_graphic_methods.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 036');
$pdf->setTitle('Graphic Methods');
$pdf->setKeywords('TCPDF tc-lib-pdf graphic methods lines curves polygons shapes');
$pdf->setPDFFilename('036_graphic_methods.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
$style1 = [
'lineWidth' => 0.5,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [5, 2, 1, 2],
'dashPhase' => 0,
'lineColor' => 'red',
'fillColor' => 'powderblue',
];
$style2 = [
'lineWidth' => 0.5,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'green',
'fillColor' => 'powderblue',
];
$style3 = [
'lineWidth' => 1,
'lineCap' => 'round',
'lineJoin' => 'round',
'dashArray' => [6, 2],
'dashPhase' => 0,
'lineColor' => 'blue',
'fillColor' => 'powderblue',
];
$style4 = [
'all' => [
'lineWidth' => 0.5,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'miterLimit' => 0.5,
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'black',
'fillColor' => 'aliceblue',
],
// TOP
0 => [
'lineWidth' => 0.25,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'red',
'fillColor' => 'powderblue',
],
// RIGHT
1 => [
'lineWidth' => 0.25,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'green',
'fillColor' => 'powderblue',
],
// BOTTOM
2 => [
'lineWidth' => 0.50,
'lineCap' => 'round',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'blue',
'fillColor' => 'powderblue',
],
// LEFT
3 => [
'lineWidth' => 0.75,
'lineCap' => 'square',
'lineJoin' => 'miter',
'dashArray' => [6, 3, 2, 3],
'dashPhase' => 0,
'lineColor' => 'yellow',
'fillColor' => 'powderblue',
],
];
$style5 = [
'lineWidth' => 0.25,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'darkblue',
'fillColor' => 'mistyrose',
];
$style6 = [
'lineWidth' => 0.5,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [3, 3],
'dashPhase' => 0,
'lineColor' => 'green',
'fillColor' => 'powderblue',
];
$style7 = [
'lineWidth' => 0.5,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'darkorange',
'fillColor' => 'palegreen',
];
// ----------
$page01 = $pdf->addPage();
$titleHtml = <<<HTML
<h1 style="font-size:16pt; color:#003366;">Graphic Methods</h1>
<p style="font-size:9pt; color:#333333;">
This example presents a compact visual overview of the drawing primitives available in tc-lib-pdf,
including pie sectors, ellipses, lines, rectangles, curves, polygons, regular polygons, star polygons,
rounded rectangles, and arrow styles.
</p>
HTML;
$pdf->addHTMLCell(html: $titleHtml, posx: 15, posy: 15, width: 180);
$xc = 105;
$yc = 150;
$r = 50;
$pie1 = $pdf->graph->getPieSector($xc, $yc, $r, 20, 120, 'FD', $style5, 2);
$pdf->page->addContent($pie1);
$pie2 = $pdf->graph->getPieSector($xc, $yc, $r, 120, 250, 'FD', $style6, 2);
$pdf->page->addContent($pie2);
$pie3 = $pdf->graph->getPieSector($xc, $yc, $r, 250, 20, 'FD', $style7, 2);
$pdf->page->addContent($pie3);
// ----------
$page02 = $pdf->addPage();
$pdf->setBookmark(name: 'Ellipse', link: '', level: 1);
// center of ellipse
$xc = 100;
$yc = 100;
// X Y axis
$arc1 = $pdf->graph->getLine($xc - 50, $yc, $xc + 50, $yc, $style1);
$pdf->page->addContent($arc1);
$arc2 = $pdf->graph->getLine($xc, $yc - 50, $xc, $yc + 50, $style2);
$pdf->page->addContent($arc2);
// ellipse axis
$arc3 = $pdf->graph->getLine($xc - 50, $yc - 50, $xc + 50, $yc + 50, $style3);
$pdf->page->addContent($arc3);
$arc4 = $pdf->graph->getLine($xc - 50, $yc + 50, $xc + 50, $yc - 50, $style5);
$pdf->page->addContent($arc4);
// ellipse
$arc5 = $pdf->graph->getEllipse($xc, $yc, 30, 15, 45, 0, 360, 'D', $style6, 2);
$pdf->page->addContent($arc5);
// ellipse arc
$arc6 = $pdf->graph->getEllipse($xc, $yc, 30, 15, 45, 45, 90, 'D', $style7, 2);
$pdf->page->addContent($arc6);
// ----------
$page03 = $pdf->addPage();
// Line
$line1 = $pdf->graph->getLine(5, 10, 80, 30, $style1);
$pdf->page->addContent($line1);
$line2 = $pdf->graph->getLine(5, 10, 5, 30, $style2);
$pdf->page->addContent($line2);
$line3 = $pdf->graph->getLine(5, 10, 80, 10, $style3);
$pdf->page->addContent($line3);
// Rectangle
$rect1 = $pdf->graph->getRect(100, 10, 40, 20, 'DF', $style4);
$pdf->page->addContent($rect1);
$rect2 = $pdf->graph->getRect(145, 10, 40, 20, 'D', [
'all' => $style3,
]);
$pdf->page->addContent($rect2);
// Curve
$curve1 = $pdf->graph->getCurve(5, 40, 30, 55, 70, 45, 60, 75, '', $style6);
$pdf->page->addContent($curve1);
$curve2 = $pdf->graph->getCurve(80, 40, 70, 75, 150, 45, 100, 75, 'F', $style6);
$pdf->page->addContent($curve2);
$curve3 = $pdf->graph->getCurve(140, 40, 150, 55, 180, 45, 200, 75, 'DF', $style6);
$pdf->page->addContent($curve3);
// Circle
$circle1 = $pdf->graph->getCircle(25, 105, 20);
$pdf->page->addContent($circle1);
$circle2 = $pdf->graph->getCircle(25, 105, 10, 90, 180, '', $style7);
$pdf->page->addContent($circle2);
$circle3 = $pdf->graph->getCircle(25, 105, 10, 270, 360, 'F');
$pdf->page->addContent($circle3);
$circle4 = $pdf->graph->getCircle(25, 105, 10, 270, 360, 'C', $style7);
$pdf->page->addContent($circle4);
// Ellipse
$ellipse1 = $pdf->graph->getEllipse(100, 103, 40, 20);
$pdf->page->addContent($ellipse1);
$ellipse2 = $pdf->graph->getEllipse(100, 105, 20, 10, 0, 90, 180, '', $style7);
$pdf->page->addContent($ellipse2);
$ellipse3 = $pdf->graph->getEllipse(100, 105, 20, 10, 0, 270, 360, 'DF', $style7);
$pdf->page->addContent($ellipse3);
$ellipse4 = $pdf->graph->getEllipse(175, 103, 30, 15, 45);
$pdf->page->addContent($ellipse4);
$ellipse5 = $pdf->graph->getEllipse(175, 105, 15, 7.50, 45, 90, 180, '', $style7);
$pdf->page->addContent($ellipse5);
$ellipse6 = $pdf->graph->getEllipse(175, 105, 15, 7.50, 45, 270, 360, 'F', $style7, 4);
$pdf->page->addContent($ellipse6);
// Polygon
$polygon1 = $pdf->graph->getPolygon([5, 135, 45, 135, 15, 165], 's');
$pdf->page->addContent($polygon1);
$polygon2 = $pdf->graph->getPolygon([60, 135, 80, 135, 80, 155, 70, 165, 50, 155], 'DF', [
$style6,
$style7,
$style7,
$style6,
$style6,
]);
$pdf->page->addContent($polygon2);
$polygon3 = $pdf->graph->getPolygon([120, 135, 140, 135, 150, 155, 110, 155], 'D', [
$style5,
$style6,
$style7,
$style6,
]);
$pdf->page->addContent($polygon3);
$polygon4 = $pdf->graph->getPolygon([160, 135, 190, 155, 170, 155, 200, 160, 160, 165], 'DF', [
'all' => $style6,
]);
$pdf->page->addContent($polygon4);
$polygon5 = $pdf->graph->getPolygon([80, 165, 90, 160, 100, 165, 110, 160, 120, 165, 130, 160, 140, 165], 'D', [
'all' => $style1,
]);
$pdf->page->addContent($polygon5);
// Regular Polygon
$regpoly1 = $pdf->graph->getRegularPolygon(
20,
190,
15,
6,
0,
'b',
[
'all' => $style6,
],
's',
$style5,
);
$pdf->page->addContent($regpoly1);
$regpoly2 = $pdf->graph->getRegularPolygon(55, 190, 15, 6, 0, 's');
$pdf->page->addContent($regpoly2);
$regpoly3 = $pdf->graph->getRegularPolygon(55, 190, 10, 6, 45, 'DF', [
$style6,
$style5,
$style7,
$style5,
$style7,
$style7,
]);
$pdf->page->addContent($regpoly3);
$regpoly4 = $pdf->graph->getRegularPolygon(
90,
190,
15,
3,
0,
'b',
[
'all' => $style5,
],
'F',
$style6,
);
$pdf->page->addContent($regpoly4);
$regpoly5 = $pdf->graph->getRegularPolygon(
125,
190,
15,
4,
30,
'b',
[
'all' => $style5,
],
's',
$style1,
);
$pdf->page->addContent($regpoly5);
$regpoly6 = $pdf->graph->getRegularPolygon(160, 190, 15, 10, 0, 's');
$pdf->page->addContent($regpoly6);
// Star Polygon
$startpoly1 = $pdf->graph->getStarPolygon(
20,
230,
15,
20,
3,
0,
's',
[
'all' => $style2,
],
'b',
$style5,
);
$pdf->page->addContent($startpoly1);
$startpoly2 = $pdf->graph->getStarPolygon(55, 230, 15, 12, 5, 15, 's');
$pdf->page->addContent($startpoly2);
$startpoly3 = $pdf->graph->getStarPolygon(
55,
230,
7,
12,
5,
45,
'b',
[
'all' => $style7,
],
'F',
$style6,
);
$pdf->page->addContent($startpoly3);
$startpoly4 = $pdf->graph->getStarPolygon(
90,
230,
15,
20,
6,
0,
's',
[
'all' => $style5,
],
'F',
$style6,
);
$pdf->page->addContent($startpoly4);
$startpoly5 = $pdf->graph->getStarPolygon(
125,
230,
15,
5,
2,
30,
's',
[
'all' => $style5,
],
's',
$style6,
);
$pdf->page->addContent($startpoly5);
$startpoly6 = $pdf->graph->getStarPolygon(160, 230, 15, 10, 3, 0, 's');
$pdf->page->addContent($startpoly6);
$startpoly7 = $pdf->graph->getStarPolygon(160, 230, 7, 50, 26, 10, 's');
$pdf->page->addContent($startpoly7);
// Rounded Rectangle
$roundrect1 = $pdf->graph->getRoundedRect(5, 255, 40, 30, 3.50, 3.50, '1111', 'DF');
$pdf->page->addContent($roundrect1);
$roundrect2 = $pdf->graph->getRoundedRect(50, 255, 40, 30, 6.50, 6.50, '1000', 'b');
$pdf->page->addContent($roundrect2);
$roundrect3 = $pdf->graph->getRoundedRect(95, 255, 40, 30, 10.0, 5.0, '1111', 's', $style6);
$pdf->page->addContent($roundrect3);
$roundrect4 = $pdf->graph->getRoundedRect(140, 255, 40, 30, 8.0, 8.0, '0101', 'DF', $style6);
$pdf->page->addContent($roundrect4);
// Arrows
$arrow1 = $pdf->graph->getArrow(200, 280, 185, 266, 0, 5, 15);
$pdf->page->addContent($arrow1);
$arrow2 = $pdf->graph->getArrow(200, 280, 190, 263, 1, 5, 15);
$pdf->page->addContent($arrow2);
$arrow3 = $pdf->graph->getArrow(200, 280, 195, 261, 2, 5, 15);
$pdf->page->addContent($arrow3);
$arrow4 = $pdf->graph->getArrow(200, 280, 200, 260, 3, 5, 15);
$pdf->page->addContent($arrow4);
// ----------
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,151 @@
<?php
/**
* E037_image_methods.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 037');
$pdf->setTitle('Image Methods');
$pdf->setKeywords('TCPDF tc-lib-pdf image methods jpeg png svg placement scaling');
$pdf->setPDFFilename('037_image_methods.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
// Add first page
$page01 = $pdf->addPage();
$html = <<<HTML
<h1 style="font-size:16pt; color:#003366;">Image Methods</h1>
<p style="font-size:9pt; color:#333333;">
This example shows how to place JPEG and PNG images with different color models and transparency.
</p>
HTML;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 15, width: 180);
$posx = 5;
$posy = 40;
// Cell grid geometry: 5 columns, each sample rendered $cellw units wide.
$cellw = 40;
$rowh = 20;
$cols = 5;
// Sample images exercising different color models and transparency.
$samples = [
'200x100_CMYK.jpg', // CMYK JPEG sample.
'200x100_GRAY.jpg', // Grayscale JPEG sample.
'200x100_GRAY.png', // Grayscale PNG sample.
'200x100_INDEX16.png', // Indexed-color PNG with a 16-color palette.
'200x100_INDEX256.png', // Indexed-color PNG with a 256-color palette.
'200x100_INDEXALPHA.png', // Indexed-color PNG with alpha transparency.
'200x100_RGB.jpg', // RGB JPEG sample.
'200x100_RGB.png', // RGB PNG sample.
'200x100_RGBALPHA.png', // RGB PNG with alpha transparency.
'200x100_RGBICC.jpg', // RGB JPEG carrying an ICC color profile.
'200x100_RGBICC.png', // RGB PNG carrying an ICC color profile.
'200x100_RGBINT.png', // RGB PNG variant (alternate RGB image path).
];
// Add Images
//
// Each sample is rendered preserving its original aspect ratio: only the target
// width is provided and the matching height is computed directly from the
// intrinsic image size by image->getImageDimensionsByKey().
foreach ($samples as $index => $name) {
$file = $imgdir . '/' . $name;
// Import the image; add() returns the image instance ID used for placement.
$iid = $pdf->image->add($file);
// Resolve the cache key for the imported image.
$key = $pdf->image->getKey($file);
// Derive the rendered dimensions from the intrinsic image size: a single
// target side (width) is given and the other (height) is computed to
// preserve the original aspect ratio.
$dim = $pdf->image->getImageDimensionsByKey($key, $cellw);
$col = $index % $cols;
$row = \intdiv($index, $cols);
$imgx = $posx + ($col * $cellw);
$imgy = $posy + ($row * $rowh);
$out = $pdf->image->getSetImage($iid, $imgx, $imgy, $dim['width'], $dim['height'], $page01['height']);
$pdf->page->addContent($out);
}
// getImageDimensionsByKey() supports additional resolution modes, for example:
// $pdf->image->getImageDimensionsByKey($key); // original pixel size
// $pdf->image->getImageDimensionsByKey($key, 0, 30); // derive width from height
// $pdf->image->getImageDimensionsByKey($key, 40, 40, true); // scale to fit a 40x40 box
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,106 @@
<?php
/**
* E038_image_clipping.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 038');
$pdf->setTitle('Image Clipping');
$pdf->setKeywords('TCPDF tc-lib-pdf image clipping masks clipping paths');
$pdf->setPDFFilename('038_image_clipping.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
$page10 = $pdf->addPage();
$html = <<<HTML
<h1 style="font-size:16pt; color:#003366;">Image Clipping</h1>
<p style="font-size:9pt; color:#333333;">
Demonstrates how to apply a clipping mask to an image using a star polygon path.
</p>
HTML;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 15, width: 180);
// Clipping Mask
$posx = 80;
$posy = 100;
$cnz = $pdf->graph->getStartTransform();
$cnz .= $pdf->graph->getStarPolygon($posx, $posy, 40, 10, 3, 0, 'CNZ');
$clipimg = $pdf->image->add($imgdir . '/200x100_CMYK.jpg');
$cnz .= $pdf->image->getSetImage($clipimg, $posx - 40, $posy - 40, 80, 80, $page10['height']);
$cnz .= $pdf->graph->getStopTransform();
$pdf->page->addContent($cnz);
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,588 @@
<?php
/**
* E039_text_methods.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 039');
$pdf->setTitle('Text Methods');
$pdf->setKeywords('TCPDF tc-lib-pdf text methods alignment spacing writing');
$pdf->setPDFFilename('039_text_methods.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
$page01 = $pdf->addPage();
$pdf->setBookmark(name: 'Text', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: '');
// Add an internal link to this page
$page01_link = $pdf->addInternalLink();
$styletxt = [
'lineWidth' => 0.25,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'red',
'fillColor' => 'black',
];
$pdf->graph->add($styletxt);
$bfont2 = $pdf->font->insert($pdf->pon, 'times', 'BI', 24);
$pdf->page->addContent($bfont2['out']);
// alternative to set the current font (last entry in the font stack):
// $pdf->page->addContent($pdf->font->getOutCurrentFont());
// Add text
$txt = $pdf->getTextLine(
txt: 'Test PDF text with justification (stretching) % %% %%%',
posx: 0,
posy: $pdf->toUnit($bfont2['ascent']),
width: $page01['width'],
);
$pdf->page->addContent($txt);
$bbox = $pdf->getLastBBox();
// Add text
$txt2 = $pdf->getTextLine(
txt: 'Link to https://tcpdf.org',
posx: 15,
posy: $bbox['y'] + $bbox['h'] + $pdf->toUnit($bfont2['ascent']),
width: 0,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
txtanchor: 'S',
shadow: [
'xoffset' => 0.5,
'yoffset' => 0.5,
'opacity' => 0.5,
'mode' => 'Normal',
'color' => 'red',
],
);
$pdf->page->addContent($txt2);
// get the coordinates of the box containing the last added text string.
$bbox = $pdf->getLastBBox();
$aoid1 = $pdf->setLink(
posx: $bbox['x'],
posy: $bbox['y'],
width: $bbox['w'],
height: $bbox['h'],
link: 'https://tcpdf.org',
);
$pdf->page->addAnnotRef($aoid1);
// -----------------------------------------------
// add a text column with automatic line breaking
$bfont3 = $pdf->font->insert($pdf->pon, 'courier', '', 14);
$pdf->page->addContent($bfont3['out']);
$txt3 =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
. "\n"
. 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
$col = $pdf->color->getPdfColor('blue');
$pdf->page->addContent($col);
// single block of text
$txtbox = $pdf->getTextCell(
txt: $txt3,
posx: 20,
posy: 30,
width: 150,
height: 0,
offset: 15,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
cell: null,
styles: [],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: false,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$pdf->page->addContent($txtbox);
$col = $pdf->color->getPdfColor('black');
$pdf->page->addContent($col);
$bfont4 = $pdf->font->insert($pdf->pon, 'freeserif', 'I', 12);
$pdf->page->addContent($bfont4['out']);
$pdf->setDefaultCellPadding(top: 2, right: 2, bottom: 2, left: 2);
// Text cell
$style_cell = [
'all' => [
'lineWidth' => 1,
'lineCap' => 'round',
'lineJoin' => 'round',
'miterLimit' => 1,
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'green',
'fillColor' => 'yellow',
],
];
$pdf->setDefaultCellBorderPos(borderpos: $pdf::BORDERPOS_DEFAULT);
$txtcell1 = $pdf->getTextCell(
txt: 'DEFAULT',
posx: 20,
posy: 100,
width: 0,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
cell: null,
styles: $style_cell,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$pdf->page->addContent($txtcell1);
$pdf->setDefaultCellBorderPos(borderpos: $pdf::BORDERPOS_EXTERNAL);
$txtcell2 = $pdf->getTextCell(
txt: 'EXTERNAL',
posx: 49,
posy: 100,
width: 0,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
cell: null,
styles: $style_cell,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$pdf->page->addContent($txtcell2);
$pdf->setDefaultCellBorderPos(borderpos: $pdf::BORDERPOS_INTERNAL);
$txtcell2 = $pdf->getTextCell(
txt: 'INTERNAL',
posx: 80,
posy: 100,
width: 0,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
cell: null,
styles: $style_cell,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$pdf->page->addContent($txtcell2);
$defbstyle = [
'lineWidth' => $pdf->toUnit(1),
'lineCap' => 'square',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#333333',
'fillColor' => '#cccccc',
];
$bstyle = [
'all' => $defbstyle,
0 => $defbstyle, // TOP
1 => $defbstyle, // RIGHT
2 => $defbstyle, // BOTTOM
3 => $defbstyle, // LEFT
];
$bstyle[0]['lineColor'] = $bstyle[3]['lineColor'] = '#e7e7e7';
$pdf->setDefaultCellBorderPos(borderpos: $pdf::BORDERPOS_DEFAULT);
$txtcell3 = $pdf->getTextCell(
txt: 'BUTTON',
posx: 120,
posy: 100,
width: 0,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
cell: null,
styles: $bstyle,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$pdf->page->addContent($txtcell3);
$pdf->setDefaultCellBorderPos(borderpos: $pdf::BORDERPOS_DEFAULT);
$txtcell2 = $pdf->getTextCell(
txt: $txt3,
posx: 20,
posy: 120,
width: 150,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
cell: null,
styles: $style_cell,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$pdf->page->addContent($txtcell2);
$bfont4 = $pdf->font->insert($pdf->pon, 'dejavusans', '', 14);
$pdf->page->addContent($bfont4['out']);
$pdf->setDefaultCellPadding(top: 2, right: 2, bottom: 2, left: 2);
$style_cell_b = [
'all' => [
'lineWidth' => 0.5,
'lineCap' => 'round',
'lineJoin' => 'round',
'miterLimit' => 0.5,
'dashArray' => [0, 1],
'dashPhase' => 2,
'lineColor' => 'red',
],
];
// block of text between two page regions
$pdf->addTextCell(
txt: "\u{27A0}" . $txt3,
pid: -1,
posx: 20,
posy: 165,
width: 150,
height: 0,
offset: 15,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
cell: null,
styles: $style_cell_b,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$txt4 = 'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?';
$txt5 = 'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.';
$pdf->enableZeroWidthBreakPoints(true);
$pdf->addTextCell(
txt: 'TEST-TEXT-ENABLE-AUTO-BREAK-POINTS',
pid: -1,
posx: 20,
posy: 233,
width: 85,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: $style_cell,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$pdf->enableZeroWidthBreakPoints(false);
$pdf->addTextCell(
txt: 'TEST-TEXT-DISABLE-AUTO-BREAK-POINTS',
pid: -1,
posx: 20,
posy: 252,
width: 85,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: $style_cell,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
// Hyphenation example
// TEX hyphenation patterns can be downloaded from:
// https://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/tex
//
//$hyphen_patterns = $pdf->loadTexHyphenPatterns('../../RESOURCES/hyph-la-x-classic.tex');
//$pdf->setTexHyphenPatterns($hyphen_patterns);
// block of text between two page regions
$pdf->addTextCell(
txt: $txt3 . "\n" . $txt4 . "\n" . $txt5,
pid: -1,
posx: 20,
posy: 265,
width: 120,
height: 0,
offset: 15,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
cell: null,
styles: $style_cell,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$pdf->addTextCell(
txt: 'overline, linethrough and underline',
pid: -1,
posx: 15,
posy: 50,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: null,
styles: [],
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: true,
linethrough: true,
overline: true,
clip: false,
drawcell: false,
forcedir: \Com\Tecnick\Unicode\TextDirection::Auto,
shadow: null,
);
$pdf->addTextCell(
txt: 'addTextCell()' . PHP_EOL . 'First line.' . PHP_EOL . 'Second Line.',
posx: 15,
posy: 70,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
drawcell: false,
);
$pdf->page->addContent($pdf->getTextCell(
txt: 'getTextCell()' . PHP_EOL . 'First line.' . PHP_EOL . 'Second Line.',
posx: 15,
posy: 90,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,288 @@
<?php
/**
* E040_annotation_form.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 040');
$pdf->setTitle('Annotation Form Widgets');
$pdf->setKeywords('TCPDF tc-lib-pdf annotations form widgets acroform fields');
$pdf->setPDFFilename('040_annotation_form.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
// Annotation Form Fields
$page1 = $pdf->addPage();
$bfont1 = $pdf->font->insert($pdf->pon, 'dejavusans', '', 12);
$pdf->page->addContent($bfont1['out']);
$html = <<<HTML
<h1 style="font-size:16pt; color:#003366;">Annotation Form Example</h1>
<p style="font-size:9pt; color:#333333;">
This page demonstrates interactive PDF form fields including text input,
radio buttons, a checkbox, combo and list boxes, plus reset, print, and
submit actions.
</p>
HTML;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 15, width: 180);
$posx = 80;
$posy = 100;
$labelWidth = 62;
$labelX = 15;
$labelStyle = 'font-size:9pt; color:#333333; text-align:right;';
// text
$pdf->addHTMLCell(
html: '<p style="' . $labelStyle . '">Text Input:</p>',
posx: $labelX,
posy: $posy,
width: $labelWidth,
height: 5,
);
$fftextid = $pdf->addFFText('test_text', $posx, $posy, 50, 5);
$pdf->page->addAnnotRef($fftextid);
// radiobuttons
$pdf->addHTMLCell(
html: '<p style="' . $labelStyle . '">Radio Option One:</p>',
posx: $labelX,
posy: $posy + 10,
width: $labelWidth,
height: 5,
);
$pdf->addHTMLCell(
html: '<p style="' . $labelStyle . '">Radio Option Two:</p>',
posx: $labelX,
posy: $posy + 15,
width: $labelWidth,
height: 5,
);
$pdf->addHTMLCell(
html: '<p style="' . $labelStyle . '">Radio Option Three:</p>',
posx: $labelX,
posy: $posy + 20,
width: $labelWidth,
height: 5,
);
$ffrbid1 = $pdf->addFFRadioButton(name: 'test_radiobutton', posx: $posx, posy: $posy + 10, width: 5, onvalue: 'one');
$ffrbid2 = $pdf->addFFRadioButton(
name: 'test_radiobutton',
posx: $posx,
posy: $posy + 15,
width: 5,
onvalue: 'two',
checked: true,
);
$ffrbid3 = $pdf->addFFRadioButton(name: 'test_radiobutton', posx: $posx, posy: $posy + 20, width: 5, onvalue: 'three');
$pdf->page->addAnnotRef($ffrbid1);
$pdf->page->addAnnotRef($ffrbid2);
$pdf->page->addAnnotRef($ffrbid3);
// checkbox
$pdf->addHTMLCell(
html: '<p style="' . $labelStyle . '">Checkbox:</p>',
posx: $labelX,
posy: $posy + 30,
width: $labelWidth,
height: 5,
);
$ffckbxid1 = $pdf->addFFCheckBox('test_checkbox', $posx, $posy + 30, 5);
$pdf->page->addAnnotRef($ffckbxid1);
// combobox
$pdf->addHTMLCell(
html: '<p style="' . $labelStyle . '">Combo Box:</p>',
posx: $labelX,
posy: $posy + 40,
width: $labelWidth,
height: 5,
);
$ffcmbxid1 = $pdf->addFFComboBox('test_combobox', $posx, $posy + 40, 50, 5, [
['0', 'one'],
['1', 'two'],
['2', 'three'],
]);
$pdf->page->addAnnotRef($ffcmbxid1);
// listbox
$pdf->addHTMLCell(
html: '<p style="' . $labelStyle . '">List Box:</p>',
posx: $labelX,
posy: $posy + 50,
width: $labelWidth,
height: 5,
);
$fflsbxid1 = $pdf->addFFListBox(
'test_listbox',
$posx,
$posy + 50,
50,
15,
['one', 'two', 'three'],
['subtype' => 'Widget'],
['multipleSelection' => 'true'],
);
$pdf->page->addAnnotRef($fflsbxid1);
// button - reset form
$pdf->addHTMLCell(
html: '<p style="' . $labelStyle . '">Actions:</p>',
posx: $labelX,
posy: $posy + 70,
width: $labelWidth,
height: 5,
);
$ffbtnid1 = $pdf->addFFButton(
name: 'reset',
posx: $posx,
posy: $posy + 70,
width: 20,
height: 5,
caption: 'Reset',
action: ['S' => 'ResetForm'],
opt: ['subtype' => 'Widget'],
jsp: [
'lineWidth' => 2,
'borderStyle' => 'beveled',
'fillColor' => '#80c4ff',
'strokeColor' => '#404040',
],
);
$pdf->page->addAnnotRef($ffbtnid1);
// button - print document
$ffbtnid2 = $pdf->addFFButton(
name: 'print',
posx: $posx + 20,
posy: $posy + 70,
width: 20,
height: 5,
caption: 'Print',
action: 'Print()',
opt: ['subtype' => 'Widget'],
jsp: [
'lineWidth' => 2,
'borderStyle' => 'beveled',
'fillColor' => '#80c4ff',
'strokeColor' => '#404040',
],
);
$pdf->page->addAnnotRef($ffbtnid2);
// button - submit form
$ffbtnid3 = $pdf->addFFButton(
name: 'submit',
posx: $posx + 40,
posy: $posy + 70,
width: 20,
height: 5,
caption: 'Submit',
action: [
'S' => 'SubmitForm',
'F' => 'http://localhost/printvars.php',
'Flags' => ['ExportFormat'],
],
opt: ['subtype' => 'Widget'],
jsp: [
'lineWidth' => 2,
'borderStyle' => 'beveled',
'fillColor' => '#80c4ff',
'strokeColor' => '#404040',
],
);
$pdf->page->addAnnotRef($ffbtnid3);
// JavaScript form validation functions
$formjs = <<<EOD
function CheckField(name,message) {
var f = getField(name);
if(f.value == '') {
app.alert(message);
f.setFocus();
return false;
}
return true;
}
function Print() {
if(!CheckField('test_text','test_text is mandatory')) {return;}
print();
}
EOD;
// Add raw JavaScript code
$pdf->appendRawJavaScript(script: $formjs);
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,122 @@
<?php
/**
* E041_layers_visibility.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 041');
$pdf->setTitle('Layer Visibility (OCG)');
$pdf->setKeywords('TCPDF tc-lib-pdf layers visibility OCG optional content groups');
$pdf->setPDFFilename('041_layers_visibility.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 14);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
// Layers
$page1 = $pdf->addPage();
$pdf->page->addContent($pdf->getTextCell(
txt: 'Layers and Optional Content Groups',
posx: 15,
posy: 15,
width: 180,
height: 8,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'This page contains objects assigned to different visibility layers.',
posx: 15,
posy: 28,
width: 180,
height: 6,
));
$pdf->page->addContent($pdf->getTextCell('Always visible text', 15, 40, 120, 7, 0, 0, 'T', 'L', drawcell: true));
$lyrScreen = $pdf->newLayer(name: 'screen_only', intent: ['view' => true], print: false, view: true, lock: false);
$pdf->page->addContent($lyrScreen);
$pdf->page->addContent($pdf->getTextCell('Screen-only layer content', 15, 54, 120, 7, 0, 0, 'T', 'L', drawcell: true));
$pdf->page->addContent($pdf->closeLayer());
$lyrPrint = $pdf->newLayer(name: 'print_only', intent: ['design' => true], print: true, view: false, lock: false);
$pdf->page->addContent($lyrPrint);
$pdf->page->addContent($pdf->getTextCell('Print-only layer content', 15, 66, 120, 7, 0, 0, 'T', 'L', drawcell: true));
$pdf->page->addContent($pdf->closeLayer());
$pdf->page->addContent($pdf->getTextCell(
txt: 'Viewer support for layer toggles varies by PDF reader.',
posx: 15,
posy: 82,
width: 180,
height: 6,
));
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
+124
View File
@@ -0,0 +1,124 @@
<?php
/**
* E042_html_form.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 042');
$pdf->setTitle('HTML Form Elements');
$pdf->setKeywords('TCPDF tc-lib-pdf html forms input select textarea rendering');
$pdf->setPDFFilename('042_html_form.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
// HTML G
$pageV07 = $pdf->addPage();
$html_07 = <<<EOD
<h1>XHTML Form Example</h1>
<form method="post" action="http://localhost/printvars.php" enctype="multipart/form-data">
<label for="name">name:</label> <input type="text" name="name" value="" size="20" maxlength="30" /><br />
<label for="password">password:</label> <input type="password" name="password" value="" size="20" maxlength="30" /><br /><br />
<label for="infile">file:</label> <input type="file" name="userfile" size="20" /><br /><br />
<input type="checkbox" name="agree" value="1" checked="checked" /> <label for="agree">I agree </label><br /><br />
<input type="radio" name="radioquestion" id="rqa" value="1" /> <label for="rqa">one</label><br />
<input type="radio" name="radioquestion" id="rqb" value="2" checked="checked"/> <label for="rqb">two</label><br />
<input type="radio" name="radioquestion" id="rqc" value="3" /> <label for="rqc">three</label><br /><br />
<label for="selection">select:</label>
<select name="selection" size="0">
<option value="0">zero</option>
<option value="1">one</option>
<option value="2">two</option>
<option value="3">three</option>
</select><br /><br />
<label for="selection">select:</label>
<select name="multiselection" size="2" multiple="multiple">
<option value="0">zero</option>
<option value="1">one</option>
<option value="2">two</option>
<option value="3">three</option>
</select><br /><br /><br />
<label for="text">text area:</label><br />
<textarea cols="40" rows="3" name="text">line one
line two</textarea><br />
<br /><br /><br />
<input type="reset" name="reset" value="Reset" />
<input type="submit" name="submit" value="Submit" />
<input type="button" name="print" value="Print" onclick="print()" />
<input type="hidden" name="hiddenfield" value="OK" />
<br />
</form>
EOD;
$pdf->addHTMLCell(html: $html_07, posx: 30, posy: 20);
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,398 @@
<?php
/**
* E043_html_tables.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 043');
$pdf->setTitle('HTML Tables');
$pdf->setKeywords('TCPDF tc-lib-pdf html tables thead tbody cell borders layout');
$pdf->setPDFFilename('043_html_tables.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
$page1 = $pdf->addPage();
$subtable = '<table border="1" cellspacing="6" cellpadding="4"><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>';
$html1 = '<h2>HTML TABLES (A)</h2>
<table border="1" cellspacing="3" cellpadding="4">
<tr>
<th align="center">#</th>
<th align="right">RIGHT align</th>
<th align="left">LEFT align</th>
<th>4A</th>
</tr>
<tr>
<td>1</td>
<td bgcolor="#cccccc" align="center" colspan="2">A1 ex<i>amp</i>le <a href="https://tcpdf.org">link</a> column span. One two tree four five six seven eight nine ten.<br />line after br<br /><small>small text</small> normal <sub>subscript</sub> normal <sup>superscript</sup> normal bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla<ol><li>first<ol><li>sublist</li><li>sublist</li></ol></li><li>second</li></ol><small color="#FF0000" bgcolor="#FFFF00">small small small small small small small small small small small small small small small small small small small small</small></td>
<td>4B</td>
</tr>
<tr>
<td>' . $subtable . '</td>
<td bgcolor="#0000FF" color="yellow" align="center">A2 € &euro; &#8364; &amp; è &egrave;<br/>A2 € &euro; &#8364; &amp; è &egrave;</td>
<td bgcolor="#FFFF00" align="left"><span color="#FF0000">Red</span> Yellow BG</td>
<td>4C</td>
</tr>
<tr>
<td>1A</td>
<td rowspan="2" colspan="2" bgcolor="#FFFFCC">2AA<br />2AB<br />2AC</td>
<td bgcolor="#FF0000">4D</td>
</tr>
<tr>
<td>1B</td>
<td>4E</td>
</tr>
<tr>
<td>1C</td>
<td>2C</td>
<td>3C</td>
<td>4F</td>
</tr>
</table>';
$pdf->addHTMLCell(html: $html1, posx: 20, posy: 10, width: 170);
// ----------
$page2 = $pdf->addPage();
$html2 = <<<EOF
<!-- EXAMPLE OF CSS STYLE -->
<style>
table.first {
color: #003300;
font-family: helvetica;
font-size: 8pt;
border-left: 3px solid red;
border-right: 3px solid #FF00FF;
border-top: 3px solid green;
border-bottom: 3px solid blue;
background-color: #ccffcc;
}
td {
border: 2px solid blue;
background-color: #ffffee;
}
td.second {
border: 2px dashed green;
}
</style>
<h2>HTML TABLES (B)</h2>
<table class="first" cellpadding="4" cellspacing="6">
<tr>
<td width="30" align="center"><b>No.</b></td>
<td width="140" align="center" bgcolor="#FFFF00"><b>XXXX</b></td>
<td width="140" align="center"><b>XXXX</b></td>
<td width="80" align="center"> <b>XXXX</b></td>
<td width="80" align="center"><b>XXXX</b></td>
<td width="45" align="center"><b>XXXX</b></td>
</tr>
<tr>
<td width="30" align="center">1.</td>
<td width="140" rowspan="6" class="second">BRDD<br />XXXX<br />XXXX<br />XXXX<br />XXXX<br />XXXX<br />XXXX<br />XXXX</td>
<td width="140">XXXX<br />XXXX</td>
<td width="80">XXXX<br />XXXX</td>
<td width="80">XXXX</td>
<td align="center" width="45">XXXX<br />XXXX</td>
</tr>
<tr>
<td width="30" align="center" rowspan="3">2.</td>
<td width="140" rowspan="3">XXXX<br />XXXX</td>
<td width="80">XXXX<br />XXXX</td>
<td width="80">XXXX<br />XXXX</td>
<td align="center" width="45">XXXX<br />XXXX</td>
</tr>
<tr>
<td width="80">XXXX<br />XXXX<br />XXXX<br />XXXX</td>
<td width="80">XXXX<br />XXXX</td>
<td align="center" width="45">XXXX<br />XXXX</td>
</tr>
<tr>
<td width="80" rowspan="2" >XXXX<br />XXXX<br />XXXX<br />XXXX<br />XXXX<br />XXXX<br />XXXX<br />XXXX</td>
<td width="80">XXXX<br />XXXX</td>
<td align="center" width="45">XXXX<br />XXXX</td>
</tr>
<tr>
<td width="30" align="center">3.</td>
<td width="140">XXXX<br />XXXX</td>
<td width="80">XXXX<br />XXXX</td>
<td align="center" width="45">XXXX<br />XXXX</td>
</tr>
<tr bgcolor="#FFFF80">
<td width="30" align="center">4.</td>
<td width="140" bgcolor="#00CC00" color="#FFFF00">XXXX<br />XXXX</td>
<td width="80">XXXX<br />XXXX</td>
<td width="80">XXXX<br />XXXX</td>
<td align="center" width="45">XXXX<br />XXXX</td>
</tr>
</table>
EOF;
$pdf->addHTMLCell(html: $html2, posx: 20, posy: 10, width: 180);
// ----------
$page3 = $pdf->addPage();
$html3 = <<<EOD
<h2>HTML TABLES (C)</h2>
<table cellspacing="0" cellpadding="1" border="1" style="font-size:x-small;">
<tr>
<td rowspan="3">COL 1 - ROW 1<br />COLSPAN 3</td>
<td>COL 2 - ROW 1</td>
<td>COL 3 - ROW 1</td>
</tr>
<tr>
<td rowspan="2">COL 2 - ROW 2 - COLSPAN 2<br />text line<br />text line<br />text line<br />text line</td>
<td>COL 3 - ROW 2</td>
</tr>
<tr>
<td>COL 3 - ROW 3</td>
</tr>
</table>
<hr />
<table cellspacing="0" cellpadding="1" border="1" style="font-size:x-small;">
<tr>
<td rowspan="3">COL 1 - ROW 1<br />COLSPAN 3<br />text line<br />text line<br />text line<br />text line<br />text line<br />text line</td>
<td>COL 2 - ROW 1</td>
<td>COL 3 - ROW 1</td>
</tr>
<tr>
<td rowspan="2">COL 2 - ROW 2 - COLSPAN 2<br />text line<br />text line<br />text line<br />text line</td>
<td>COL 3 - ROW 2</td>
</tr>
<tr>
<td>COL 3 - ROW 3</td>
</tr>
</table>
<hr />
<table cellspacing="0" cellpadding="1" border="1" style="font-size:x-small;">
<tr>
<td rowspan="3">COL 1 - ROW 1<br />COLSPAN 3<br />text line<br />text line<br />text line<br />text line<br />text line<br />text line</td>
<td>COL 2 - ROW 1</td>
<td>COL 3 - ROW 1</td>
</tr>
<tr>
<td rowspan="2">COL 2 - ROW 2 - COLSPAN 2<br />text line<br />text line<br />text line<br />text line</td>
<td>COL 3 - ROW 2<br />text line<br />text line</td>
</tr>
<tr>
<td>COL 3 - ROW 3</td>
</tr>
</table>
<hr />
<table border="1" style="font-size:x-small;">
<tr>
<th rowspan="3">Left column</th>
<th colspan="5">Heading Column Span 5</th>
<th colspan="9">Heading Column Span 9</th>
</tr>
<tr>
<th rowspan="2">Rowspan 2<br />This is some text that fills the table cell.</th>
<th colspan="2">span 2</th>
<th colspan="2">span 2</th>
<th rowspan="2">2 rows</th>
<th colspan="8">Colspan 8</th>
</tr>
<tr>
<th>1a</th>
<th>2a</th>
<th>1b</th>
<th>2b</th>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
<th>8</th>
</tr>
</table>
EOD;
$pdf->addHTMLCell(html: $html3, posx: 20, posy: 10, width: 180);
// ----------
$page4 = $pdf->addPage();
$html4 = '<h2>HTML TABLES (D)</h2>
<table border="1" cellspacing="3" cellpadding="4">
<tr>
<td align="left"><span>1L</span> <span>Alfa</span> <span>Bravo</span> <span>Charlie</span> <span>Delta</span> <span>Echo</span> <span>Foxtrot</span> <span>Golf</span> <span>Hotel</span> <span>India</span> <span>Juliett</span> <span>Kilo</span> <span>Lima</span> <span>Mike</span> <span>November</span> <span>Oscar</span> <span>Papa</span> <span>Quebec</span> <span>Romeo</span> <span>Sierra</span> <span>Tango</span> <span>Uniform</span> <span>Victor</span> <span>Whiskey</span> <span>Xray</span> <span>Yankee</span> <span>Zulu</span></td>
</tr>
<tr>
<td align="center"><span>1C</span> <span>Alfa</span> <span>Bravo</span> <span>Charlie</span> <span>Delta</span> <span>Echo</span> <span>Foxtrot</span> <span>Golf</span> <span>Hotel</span> <span>India</span> <span>Juliett</span> <span>Kilo</span> <span>Lima</span> <span>Mike</span> <span>November</span> <span>Oscar</span> <span>Papa</span> <span>Quebec</span> <span>Romeo</span> <span>Sierra</span> <span>Tango</span> <span>Uniform</span> <span>Victor</span> <span>Whiskey</span> <span>Xray</span> <span>Yankee</span> <span>Zulu</span></td>
</tr>
<tr>
<td align="right"><span>1R</span> <span>Alfa</span> <span>Bravo</span> <span>Charlie</span> <span>Delta</span> <span>Echo</span> <span>Foxtrot</span> <span>Golf</span> <span>Hotel</span> <span>India</span> <span>Juliett</span> <span>Kilo</span> <span>Lima</span> <span>Mike</span> <span>November</span> <span>Oscar</span> <span>Papa</span> <span>Quebec</span> <span>Romeo</span> <span>Sierra</span> <span>Tango</span> <span>Uniform</span> <span>Victor</span> <span>Whiskey</span> <span>Xray</span> <span>Yankee</span> <span>Zulu</span></td>
</tr>
<tr>
<td align="left"><span>2L</span> A1 ex<i>amp</i>le <a href="https://tcpdf.org">link</a> column span. Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xray Yankee Zulu.</td>
</tr>
<tr>
<td align="center"><span>2C</span> A1 ex<i>amp</i>le <a href="https://tcpdf.org">link</a> column span. Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xray Yankee Zulu.</td>
</tr>
<tr>
<td align="right"><span>2R</span> A1 ex<i>amp</i>le <a href="https://tcpdf.org">link</a> column span. Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xray Yankee Zulu.</td>
</tr>
<tr>
<td align="left"><small>3L small text</small> Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xray Yankee Zulu</td>
</tr>
<tr>
<td align="center"><small>3C small text</small> Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xray Yankee Zulu</td>
</tr>
<tr>
<td align="right"><small>3R small text</small> Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xray Yankee Zulu</td>
</tr>
</table>
<hr />
<table border="1" cellspacing="3" cellpadding="4">
<tr>
<td align="left"><img src="images/tcpdf_logo.jpg" alt="TCPDF logo" width="60" height="20" border="0" /></td>
</tr>
<tr>
<td align="center"><img src="images/tcpdf_logo.jpg" alt="TCPDF logo" width="60" height="20" border="0" /></td>
</tr>
<tr>
<td align="right"><img src="images/tcpdf_logo.jpg" alt="TCPDF logo" width="60" height="20" border="0" /></td>
</tr>
</table>';
$pdf->addHTMLCell(html: $html4, posx: 20, posy: 10, width: 180);
// ----------
$html5A = '<table border="0" cellpadding="5" cellspacing="0">
<tr>
<th align="left" style="font-size:32pt; color:rgba(255, 0, 0, 0.5);">
TEST
</th>
</tr>
</table>';
$pdf->addHTMLCell(html: $html5A, posx: 20, posy: 160, width: 180);
$html5B = '<table border="0" cellpadding="5" cellspacing="0">
<tr>
<th align="left" style="font-size:32pt; color:rgba(0, 255, 0, 0.5); padding-left:10mm;">
TEST
</th>
</tr>
</table>';
$pdf->addHTMLCell(html: $html5B, posx: 20, posy: 160, width: 180);
// ----------
$pdf->page->addContent($pdf->graph->getLine(0, 190, 210, 190, ['lineWidth' => 6, 'lineColor' => 'rgb(255, 0, 0)']));
$pdf->addHTMLCell(
'<table border="0"><tr><td style="border-bottom:6mm solid rgb(255, 0, 0);"></td></tr></table>',
0,
195,
);
// ----------
$page5 = $pdf->addPage();
// Percentage widths are resolved against the containing element:
// an image with width:100% fills its fixed-width table cell (issue #247).
$html6 = '<h2>HTML TABLES (E): PERCENTAGE WIDTHS</h2>
<table style="width:100%;" border="1" cellspacing="0" cellpadding="2">
<tr>
<td style="width:25%;">25% column</td>
<td style="width:75%;">75% column</td>
</tr>
<tr>
<td style="width:25%;"><img src="images/tcpdf_logo.jpg" alt="TCPDF logo" style="width:100%;" /></td>
<td style="width:75%;"><img src="images/tcpdf_logo.jpg" alt="TCPDF logo" style="width:50%;" /></td>
</tr>
</table>
<br />
<table style="width:160mm;" border="1" cellspacing="0" cellpadding="2">
<tr>
<td style="width:40mm;"><img src="images/tcpdf_logo.jpg" alt="TCPDF logo" style="width:100%;" /></td>
<td>The image on the left fills the width of its 40mm cell (width:100%).</td>
</tr>
</table>
<hr style="width:50%;" />
<p>The horizontal rule above spans 50% of the container width.</p>';
$pdf->addHTMLCell(html: $html6, posx: 20, posy: 10, width: 180);
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
+242
View File
@@ -0,0 +1,242 @@
<?php
/**
* E044_toc_index.php
*
* @since 2017-05-08
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
\define('OUTPUT_FILE', \realpath(__DIR__ . '/../target') . '/example.pdf');
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 044');
$pdf->setTitle('Table of Contents and Index');
$pdf->setKeywords('TCPDF tc-lib-pdf toc index bookmarks pagination');
$pdf->setPDFFilename('044_toc_index.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont1 = $pdf->font->insert($pdf->pon, 'dejavusans', '', 12);
// The bold heading font is derived from the base font with cloneFont() instead of being inserted
// explicitly: the bold definition file (dejavusansb.json) is resolved from the requested style,
// so the headings below and the top-level entries of the generated TOC (that addTOC() renders by
// cloning the current font with the 'B' style) are really bold and not just the regular glyphs.
$bfont2 = $pdf->font->cloneFont($pdf->pon, null, 'B', 18);
$bfont3 = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
// test images directory
$imgdir = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/');
// ----------
$sections = [
[
'title' => 'Introduction',
'summary' => 'This page introduces the outline entries that will be collected by addTOC into a generated table of contents.',
'topic' => 'The top-level bookmark uses the same explicit style arguments shown in the overview example.',
'color' => 'blue',
],
[
'title' => 'Planning',
'summary' => 'This page adds another destination so the table of contents can render a second page number.',
'topic' => 'A second level bookmark is also added on each page to show indented TOC entries.',
'color' => 'green',
],
[
'title' => 'Execution',
'summary' => 'This page stands in for the main body of the document and contributes more outline targets.',
'topic' => 'Using separate pages makes the addTOC output easier to verify visually.',
'color' => 'red',
],
[
'title' => 'Results',
'summary' => 'This last content page completes the sample before the dedicated TOC page is inserted.',
'topic' => 'The final PDF contains five pages total: four content pages and one generated TOC page.',
'color' => 'orange',
],
];
foreach ($sections as $index => $section) {
$page = $pdf->addPage();
$pdf->setBookmark(
name: $section['title'],
link: '',
level: 0,
page: -1,
posx: 0,
posy: 0,
fstyle: 'B',
color: $section['color'],
);
$pdf->setBookmark(
name: $section['title'] . ' notes',
link: '',
level: 1,
page: -1,
posx: 0,
posy: 0,
fstyle: '',
color: 'black',
);
$pdf->page->addContent($bfont2['out'], $page['pid']);
$pdf->page->addContent(
$pdf->getTextCell(
txt: 'Example 044 - ' . $section['title'],
posx: 15,
posy: 20,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
),
$page['pid'],
);
$pdf->page->addContent($bfont3['out'], $page['pid']);
$pdf->page->addContent(
$pdf->getTextCell(
txt: 'Page ' . ($index + 1) . ' of 4',
posx: 15,
posy: 34,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
),
$page['pid'],
);
$pdf->page->addContent(
$pdf->getTextCell(
txt: $section['summary'],
posx: 15,
posy: 48,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
),
$page['pid'],
);
$pdf->page->addContent(
$pdf->getTextCell(
txt: $section['topic'],
posx: 15,
posy: 62,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
),
$page['pid'],
);
}
// ----------
$pageTOC = $pdf->addPage();
$pdf->setBookmark(name: 'TOC', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'black');
$pdf->page->addContent($bfont2['out'], $pageTOC['pid']);
$pdf->page->addContent(
$pdf->getTextCell(
txt: 'Table of contents',
posx: 15,
posy: 18,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
),
$pageTOC['pid'],
);
$pdf->page->addContent($bfont1['out'], $pageTOC['pid']);
$pdf->setDefaultCellMargin(top: 0, right: 0, bottom: 0, left: 0);
$pdf->setDefaultCellPadding(top: 1, right: 1, bottom: 1, left: 1);
$style_cell_toc = [
'all' => [
'lineWidth' => 0,
'lineCap' => 'round',
'lineJoin' => 'round',
'miterLimit' => 0,
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '',
],
];
$pdf->graph->add($style_cell_toc);
$pdf->addTOC(page: -1, posx: 15, posy: 30, width: 170, rtl: false);
// =============================================================
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// ----------
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,97 @@
<?php
/**
* E045_encryption_and_permissions.php
*
* @since 2026-04-27
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
$fileId = \md5('E045_encryption_and_permissions');
$encrypt = new \Com\Tecnick\Pdf\Encrypt\Encrypt(
enabled: true,
file_id: $fileId,
mode: 2,
permissions: ['modify', 'copy', 'annot-forms', 'assemble'],
user_pass: 'demo-user',
owner_pass: 'demo-owner',
);
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: $encrypt,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 045');
$pdf->setTitle('Encryption and Permissions');
$pdf->setKeywords('TCPDF tc-lib-pdf encryption permissions user password owner password rights');
$pdf->setPDFFilename('E045_encryption_and_permissions.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Insert fonts
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$page01 = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$html = <<<HTML
<h1>Encryption and Permissions</h1>
<p>This document demonstrates PDF encryption and permission controls using tc-lib-pdf.
The file is protected with a user password (<em>demo-user</em>) and an owner password (<em>demo-owner</em>).
Encryption restricts unauthorized access while the owner password grants full control.
The allowed permissions for this document are: <strong>modify</strong>, <strong>copy</strong>,
<strong>annot-forms</strong>, and <strong>assemble</strong>.
All other operations, such as printing, are denied.</p>
HTML;
$pdf->addHTMLCell(html: $html, posx: 20, posy: 10, width: 180);
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
+151
View File
@@ -0,0 +1,151 @@
<?php
/**
* E046_pangrams.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 046');
$pdf->setTitle('Pangrams');
$pdf->setKeywords('TCPDF tc-lib-pdf example pangrams unicode multilingual');
$pdf->setPDFFilename('046_pangrams.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent(false);
// unifont covers the full Unicode BMP including Arabic, Hebrew, Thai,
// Devanagari, Hangul, and CJK Unified Ideographs.
$bfont = $pdf->font->insert($pdf->pon, 'unifont', '', 14);
$page = $pdf->addPage(['format' => 'A4']);
$pdf->page->addContent($bfont['out']);
$html = '<h1>Pangrams</h1>';
// English
$html .= '<h2>English</h2>';
$html .= '<p>The quick brown fox jumps over the lazy dog.</p>';
// Arabic (RTL)
$html .= '<h2>Arabic (العربية)</h2>';
$html .=
'<div style="text-align:right">' . 'صِف خَلقَ خَودِكِ كَمِثلِ الشَّمسِ إِذ بَزَغَت' . ' — يَحظى الضَّجيعُ بِها نَجلاءَ مِعطارِ' . '</div>';
// Hebrew (RTL)
$html .= '<h2>Hebrew (עברית)</h2>';
$html .= '<div style="text-align:right">' . 'דג סקרן שט בים מאוכזב ולפתע מצא חברה' . '</div>';
// Thai
$html .= '<h2>Thai (ภาษาไทย)</h2>';
$html .=
'<p>'
. 'เป็นมนุษย์สุดประเสริฐเลิศคุณค่า'
. ' กว่าบรรดาฝูงสัตว์เดรัจฉาน'
. ' จงฝ่าฟันพัฒนาวิชาการ'
. ' อย่าล้างผลาญฤๅเข่นฆ่าบีฑาใคร'
. '</p>';
// Devanagari (Hindi)
$html .= '<h2>Devanagari (देवनागरी)</h2>';
$html .= '<p>' . 'ऋषि सुयश ने प्रख्यात ग्रंथ की रचना करके हमें ज्ञान दिया।' . '</p>';
// Hangul (Korean)
$html .= '<h2>Hangul (한글)</h2>';
$html .= '<p>' . '키스의 고유조건은 입술끼리 만나야 하고' . ' 특별한 기술은 필요치 않다' . '</p>';
// Greek
$html .= '<h2>Greek (Ελληνικά)</h2>';
$html .= '<p>' . 'Ξεσκεπάζω την ψυχοφθόρα βδελυγμία.' . '</p>';
// Japanese
$html .= '<h2>Japanese (日本語)</h2>';
$html .=
'<p>'
. 'いろはにほへと ちりぬるを わかよたれそ つねならむ'
. ' うゐのおくやま けふこえて あさきゆめみし ゑひもせす'
. '</p>';
// Russian
$html .= '<h2>Russian (Русский)</h2>';
$html .= '<p>' . 'Съешь же ещё этих мягких французских булок, да выпей чаю.' . '</p>';
// Armenian
$html .= '<h2>Armenian (Հայերեն)</h2>';
$html .= '<p>' . 'Բոլոր մարդիկ ծնվում են ազատ և հավասար իրենց արժանապատ­վությամբ և իրա­վուն­քնե­րով։' . '</p>';
// Georgian
$html .= '<h2>Georgian (ქართული)</h2>';
$html .= '<p>' . 'გთხოვთ ახლავე გაიაროთ რეგისტრაცია უფასო პროგრამაზე.' . '</p>';
// Tamil
$html .= '<h2>Tamil (தமிழ்)</h2>';
$html .= '<p>' . 'யாமறிந்த மொழிகளிலே தமிழ்மொழி போல் இனிதாவது எங்கும் காணோம்.' . '</p>';
// Bengali
$html .= '<h2>Bengali (বাংলা)</h2>';
$html .= '<p>' . 'সুপ্রিয় বন্ধুগণ, আমরা বাংলা ভাষায় কথা বলি।' . '</p>';
// Amharic
$html .= '<h2>Amharic (አማርኛ)</h2>';
$html .= '<p>' . 'ሰው ናቸው ወይስ ሃሳቤን ሳልጨርስ ትዕዛዙን ሰጡ?' . '</p>';
// Khmer
$html .= '<h2>Khmer (ភាសាខ្មែរ)</h2>';
$html .= '<p>' . 'ខ្ញុំអាចញ៉ាំកញ្ចក់បានដោយគ្មានបញ្ហា។' . '</p>';
// Tibetan
$html .= '<h2>Tibetan (བོད་སྐད།)</h2>';
$html .= '<p>' . 'ང་གཤེགས་ཀྱི་ཡིག་གུ་ཟ་ཐུབ་ཀྱི་ཡོད། དེས་ང་ལ་གནོད་པ་མི་བྱེད།' . '</p>';
// Chinese (Traditional — Thousand Character Classic)
$html .= '<h2>Chinese (中文)</h2>';
$html .= '<p>' . '天地玄黄,宇宙洪荒。日月盈昃,辰宿列张。' . '寒来暑往,秋收冬藏。闰余成岁,律吕调阳。' . '</p>';
$pdf->addHTMLCell(html: $html, posx: 10, posy: 10, width: 190);
// =============================================================
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,180 @@
<?php
/**
* E047_remote_resources_security.php
*
* Demonstrates fileOptions host allowlisting and remote fetch controls while
* keeping the default run deterministic (no network dependency required).
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$imgdir = (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images');
$allowedPaths = array_values(array_unique(array_filter([
K_PATH_FONTS,
$imgdir,
])));
$curlopts = [];
$fixedCurlOpts = [];
if (defined('CURLOPT_CONNECTTIMEOUT')) {
$curlopts[CURLOPT_CONNECTTIMEOUT] = 4;
}
if (defined('CURLOPT_TIMEOUT')) {
$curlopts[CURLOPT_TIMEOUT] = 8;
}
if (defined('CURLOPT_SSL_VERIFYHOST')) {
$fixedCurlOpts[CURLOPT_SSL_VERIFYHOST] = 2;
}
if (defined('CURLOPT_SSL_VERIFYPEER')) {
$fixedCurlOpts[CURLOPT_SSL_VERIFYPEER] = true;
}
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
fileOptions: [
'allowedHosts' => ['assets.example.com'],
'allowedPaths' => $allowedPaths,
'maxRemoteSize' => 1024 * 1024,
'curlopts' => $curlopts,
'fixedCurlOpts' => $fixedCurlOpts,
],
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 047');
$pdf->setTitle('Remote Resource Security');
$pdf->setKeywords('TCPDF tc-lib-pdf example remote resources fileOptions security');
$pdf->setPDFFilename('047_remote_resources_security.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$titlefont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
$textfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$smallfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 8);
$page = $pdf->addPage(['format' => 'A4']);
$pdf->page->addContent($titlefont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Remote Resources and fileOptions Security',
posx: 15,
posy: 14,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($textfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: "This example configures host allowlisting, local path allowlisting, and transfer limits.\n"
. "It renders local assets by default for deterministic runs.\n"
. 'Optional remote probe can be enabled with ?probe=1 in browser mode.',
posx: 15,
posy: 26,
width: 180,
height: 0,
offset: 0,
linespace: 1.3,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$localImage = $imgdir . '/200x100_RGB.png';
$statusY = 60.0;
try {
$iid = $pdf->image->add($localImage);
$pdf->page->addContent($pdf->image->getSetImage($iid, 15, 54, 70, 35, (float) $page['height']));
$localStatus = 'Local image load: OK (' . basename($localImage) . ')';
} catch (\Throwable $e) {
$localStatus = 'Local image load: FAILED - ' . $e->getMessage();
}
$pdf->page->addContent($smallfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: $localStatus,
posx: 90,
posy: $statusY,
width: 105,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$probe = false;
if (PHP_SAPI !== 'cli') {
$probe = isset($_GET['probe']) && $_GET['probe'] === '1';
}
$remoteStatus = 'Remote probe: skipped (add ?probe=1 to run a blocked-host fetch attempt).';
if ($probe) {
try {
$pdf->image->add('https://untrusted.example.invalid/logo.png');
$remoteStatus = 'Remote probe: request did not raise an exception (check your environment wrappers).';
} catch (\Throwable $e) {
$remoteStatus = 'Remote probe: blocked/failure as expected - ' . $e->getMessage();
}
}
$pdf->page->addContent($pdf->getTextCell(
txt: $remoteStatus,
posx: 15,
posy: 96,
width: 180,
height: 0,
offset: 0,
linespace: 1.2,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($textfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: "Configured policy:\n"
. "- allowedHosts: assets.example.com\n"
. "- allowedPaths: fonts + image fixture directories\n"
. "- maxRemoteSize: 1 MiB\n"
. "- curlopts: short connect/transfer timeout\n"
. '- fixedCurlOpts: TLS peer+host verification',
posx: 15,
posy: 112,
width: 180,
height: 0,
offset: 0,
linespace: 1.35,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,217 @@
<?php
/**
* E048_page_boxes_prepress.php
*
* Demonstrates Media/Crop/Bleed/Trim/Art page boxes for print-oriented
* workflows, plus visible guides for quick prepress validation.
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 048');
$pdf->setTitle('Page Boxes and Prepress Guides');
$pdf->setKeywords('TCPDF tc-lib-pdf example page boxes MediaBox CropBox BleedBox TrimBox ArtBox');
$pdf->setPDFFilename('048_page_boxes_prepress.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$probe = false;
if (PHP_SAPI === 'cli') {
$probe = (string) getenv('TC_EXAMPLE_PROBE') === '1';
} else {
$probe = isset($_GET['probe']) && $_GET['probe'] === '1';
}
$titlefont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 13);
$textfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 9);
$pageBoxesMm = [
'CropBox' => ['llx' => 5.0, 'lly' => 5.0, 'urx' => 205.0, 'ury' => 292.0],
'BleedBox' => ['llx' => 3.0, 'lly' => 3.0, 'urx' => 207.0, 'ury' => 294.0],
'TrimBox' => ['llx' => 10.0, 'lly' => 10.0, 'urx' => 200.0, 'ury' => 287.0],
'ArtBox' => ['llx' => 20.0, 'lly' => 20.0, 'urx' => 190.0, 'ury' => 277.0],
];
$pageBoxesPt = [];
foreach ($pageBoxesMm as $name => $coords) {
$pageBoxesPt[$name] = [
'llx' => $pdf->toPoints((float) $coords['llx']),
'lly' => $pdf->toPoints((float) $coords['lly']),
'urx' => $pdf->toPoints((float) $coords['urx']),
'ury' => $pdf->toPoints((float) $coords['ury']),
];
}
$page = $pdf->addPage([
'format' => 'A4',
'box' => $pageBoxesPt,
]);
$pdf->page->addContent($titlefont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Page Boxes: Media/Crop/Bleed/Trim/Art',
posx: 25,
posy: 14,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($textfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: "This sheet overlays the configured page boxes to verify prepress bounds.\n"
. 'Coordinates are defined in page-box metadata and rendered here as guides.',
posx: 25,
posy: 24,
width: 180,
height: 0,
offset: 0,
linespace: 1.2,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$styles = [
'MediaBox' => ['all' => ['lineWidth' => 0.3, 'lineColor' => '#000000', 'dashArray' => []]],
'CropBox' => ['all' => ['lineWidth' => 0.3, 'lineColor' => '#1f77b4', 'dashArray' => [2, 1]]],
'BleedBox' => ['all' => ['lineWidth' => 0.3, 'lineColor' => '#d62728', 'dashArray' => [1, 1]]],
'TrimBox' => ['all' => ['lineWidth' => 0.35, 'lineColor' => '#2ca02c', 'dashArray' => []]],
'ArtBox' => ['all' => ['lineWidth' => 0.3, 'lineColor' => '#9467bd', 'dashArray' => [3, 1]]],
];
$boxes =
[
'MediaBox' => [
'llx' => 0.0,
'lly' => 0.0,
'urx' => (float) $page['width'],
'ury' => (float) $page['height'],
],
] + $pageBoxesMm;
$pageHeight = (float) $page['height'];
$labelY = 46.0;
foreach (['MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'] as $name) {
if (!isset($boxes[$name])) {
continue;
}
$box = $boxes[$name];
$x = (float) ($box['llx'] ?? 0.0);
$y = $pageHeight - (float) ($box['ury'] ?? 0.0);
$w = (float) ($box['urx'] ?? 0.0) - (float) ($box['llx'] ?? 0.0);
$h = (float) ($box['ury'] ?? 0.0) - (float) ($box['lly'] ?? 0.0);
$pdf->page->addContent($pdf->graph->getRect($x, $y, $w, $h, 'D', $styles[$name]));
$pdf->page->addContent($pdf->getTextCell(
txt: $name
. sprintf(
' [%.1f, %.1f, %.1f, %.1f]',
(float) ($box['llx'] ?? 0.0),
(float) ($box['lly'] ?? 0.0),
(float) ($box['urx'] ?? 0.0),
(float) ($box['ury'] ?? 0.0),
),
posx: 25,
posy: $labelY,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$labelY += 5.0;
}
$pdf->page->addContent($pdf->getTextCell(
txt: "Tip: use TrimBox as the final cut size and keep critical artwork inside ArtBox.\n"
. 'BleedBox extends beyond trim to avoid white edges after cutting.',
posx: 25,
posy: 72,
width: 180,
height: 0,
offset: 0,
linespace: 1.2,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$rawpdf = $pdf->getOutPDFString();
if ($probe) {
$matches = [];
$probeRows = [];
$pointsPerUnit = $pdf->toPoints(1.0);
$pattern = '/\\/(MediaBox|CropBox|BleedBox|TrimBox|ArtBox)\\s*\\[\\s*([0-9.+-]+)\\s+([0-9.+-]+)\\s+([0-9.+-]+)\\s+([0-9.+-]+)\\s*\\]/';
if (\preg_match_all($pattern, $rawpdf, $matches, PREG_SET_ORDER) === false) {
$probeRows[] = 'Page-box probe: unable to parse page-box dictionaries.';
} else {
foreach ($matches as $vals) {
$name = (string) $vals[1];
$llx = (float) $vals[2];
$lly = (float) $vals[3];
$urx = (float) $vals[4];
$ury = (float) $vals[5];
$wPt = $urx - $llx;
$hPt = $ury - $lly;
$probeRows[] = sprintf(
'%s PDF=[%.3f %.3f %.3f %.3f] sizePt=%.3fx%.3f sizeUnit=%.3fx%.3f',
$name,
$llx,
$lly,
$urx,
$ury,
$wPt,
$hPt,
$pointsPerUnit > 0 ? $wPt / $pointsPerUnit : 0.0,
$pointsPerUnit > 0 ? $hPt / $pointsPerUnit : 0.0,
);
}
}
$output = "\n[E048 page-box probe]\n" . implode("\n", $probeRows) . "\n";
if (PHP_SAPI === 'cli') {
fwrite(STDERR, $output);
} else {
error_log($output);
}
}
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,131 @@
<?php
/**
* E049_output_targets_integration.php
*
* Demonstrates routing one generated PDF to multiple output targets:
* render, download, save, and MIME attachment payload.
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 049');
$pdf->setTitle('Output Targets Integration');
$pdf->setKeywords('TCPDF tc-lib-pdf example output render download save mime');
$pdf->setPDFFilename('049_output_targets_integration.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$titlefont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
$textfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$pdf->addPage(['format' => 'A4']);
$mode = 'render';
if (PHP_SAPI === 'cli') {
$mode = (string) ($argv[1] ?? 'render');
} elseif (isset($_GET['mode']) && is_string($_GET['mode'])) {
$mode = $_GET['mode'];
}
$mode = strtolower(trim($mode));
$allowedModes = ['render', 'download', 'save', 'mime'];
if (!in_array($mode, $allowedModes, true)) {
$mode = 'render';
}
$pdf->page->addContent($titlefont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Output Targets Integration',
posx: 15,
posy: 16,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($textfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: "This example creates one raw PDF payload and sends it through different targets.\n"
. 'Current mode: '
. strtoupper($mode)
. "\n"
. "Browser usage: ?mode=render|download|save|mime\n"
. 'CLI usage: php examples/E049_output_targets_integration.php render',
posx: 15,
posy: 30,
width: 180,
height: 0,
offset: 0,
linespace: 1.35,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$rawpdf = $pdf->getOutPDFString();
if ($mode === 'download') {
$pdf->downloadPDF(rawpdf: $rawpdf);
exit();
}
if ($mode === 'save') {
$targetDir = (string) realpath(__DIR__ . '/../target');
$pdf->savePDF(path: $targetDir, rawpdf: $rawpdf);
if (PHP_SAPI !== 'cli') {
header('Content-Type: text/plain; charset=utf-8');
}
echo 'Saved PDF to: ' . $targetDir . '/' . '049_output_targets_integration.pdf' . "\n";
exit();
}
if ($mode === 'mime') {
$mimeAttachment = $pdf->getMIMEAttachmentPDF(rawpdf: $rawpdf);
if (PHP_SAPI !== 'cli') {
header('Content-Type: text/plain; charset=utf-8');
}
echo "MIME attachment preview (first 20 lines):\n\n";
$lines = explode("\n", str_replace("\r\n", "\n", $mimeAttachment));
echo implode("\n", array_slice($lines, 0, 20));
echo "\n\nTotal MIME length: " . strlen($mimeAttachment) . " bytes\n";
exit();
}
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,158 @@
<?php
/**
* E050_shipping_label_barcodes.php
*
* Demonstrates a production-style shipping label with 1D and 2D barcodes,
* quiet-zone padding, and human-readable tracking details.
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 050');
$pdf->setTitle('Shipping Label with Barcodes');
$pdf->setKeywords('TCPDF tc-lib-pdf example shipping label barcode qrcode code128');
$pdf->setPDFFilename('050_shipping_label_barcodes.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// Initialize default font for text rendering
$defaultFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 8);
$pdf->addPage([
'width' => 100.0,
'height' => 150.0,
'margin' => [
'PL' => 5.0,
'PR' => 5.0,
'CT' => 5.0,
'CB' => 5.0,
],
]);
$frameStyle = ['all' => ['lineWidth' => 0.35, 'lineColor' => '#111111']];
$sepStyle = ['all' => ['lineWidth' => 0.2, 'lineColor' => '#555555', 'dashArray' => [2, 1]]];
$barcodeStyle = [
'lineWidth' => 0,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => 'black',
'fillColor' => 'black',
];
// Draw frame and separators
$pdf->page->addContent($pdf->graph->getRect(4, 4, 92, 142, 'D', $frameStyle));
$pdf->page->addContent($pdf->graph->getLine(4, 35, 96, 35, $sepStyle['all']));
$pdf->page->addContent($pdf->graph->getLine(4, 94, 96, 94, $sepStyle['all']));
// Header section
$headerHtml = '<div style="text-align: left;">
<p style="font-family: helvetica; font-weight: bold; font-size: 11pt; margin: 0;">EXPRESS SHIPPING</p>
<p style="font-family: helvetica; font-weight: bold; font-size: 11pt; margin: 0;">PRIORITY</p>
</div>';
$pdf->addHTMLCell(html: $headerHtml, posx: 8, posy: 9, width: 50);
// QR code in top-right
$qrPayload = "TRACK:1Z999AA10123456784\nORDER:SO-2026-05-0018\nSERVICE:EXPRESS-24\nFROM:NL-RTM-W17\nTO:DE-HAM-ACME";
$pdf->page->addContent($pdf->getBarcode(
type: 'QRCODE,M',
code: $qrPayload,
posx: 72,
posy: 9,
width: 20,
height: 20,
padding: [1, 1, 1, 1],
style: $barcodeStyle,
));
// Address section
$addressHtml = '<table cellpadding="1" cellspacing="0" border="0" width="100%">
<tr>
<td width="50%" style="vertical-align: top; font-family: helvetica; font-size: 8pt;">
<p style="font-weight: bold; margin: 0; padding-bottom: 1mm;"><u>SHIP FROM</u></p>
<p style="margin: 0; line-height: 1.3;">Warehouse 17<br/>42 Logistics Rd<br/>Rotterdam, NL</p>
</td>
<td width="50%" style="vertical-align: top; font-family: helvetica; font-size: 8pt;">
<p style="font-weight: bold; margin: 0; padding-bottom: 1mm;"><u>SHIP TO</u></p>
<p style="margin: 0; line-height: 1.3;">Acme Retail<br/>18 Harbor Street<br/>Hamburg, DE</p>
</td>
</tr>
</table>
<p style="font-family: helvetica; font-size: 8pt; margin: 1mm 0 0 0; text-align: center;">
<em>Scan for full shipment payload</em>
</p>';
$pdf->addHTMLCell(html: $addressHtml, posx: 8, posy: 37, width: 84);
$tracking = '1Z999AA10123456784';
$orderRef = 'SO-2026-05-0018';
$service = 'EXPRESS-24';
// Tracking section
$trackingHtml =
'<div style="font-family: courier; font-weight: bold; font-size: 11pt; margin-bottom: 2mm;">
'
. htmlspecialchars($tracking)
. '
</div>
<ul style="font-family: helvetica; font-size: 8pt;">
<li>Order: '
. htmlspecialchars($orderRef)
. '</li>
<li>Service: '
. htmlspecialchars($service)
. '</li>
</ul>';
$pdf->addHTMLCell(html: $trackingHtml, posx: 8, posy: 96, width: 84);
// 1D barcode
$pdf->page->addContent($pdf->getBarcode(
type: 'C128',
code: $tracking,
posx: 8,
posy: 115,
width: 80,
height: 14,
padding: [1, 1, 1, 1],
style: $barcodeStyle,
));
// Footer text
$footerHtml = '<p style="font-family: helvetica; font-size: 8pt; margin: 0; text-align: left; line-height: 1.2;">
Quiet zones are intentionally reserved around both symbols to improve scanner reliability.
</p>';
$pdf->addHTMLCell(html: $footerHtml, posx: 8, posy: 136, width: 84);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,283 @@
<?php
/**
* E051_viewer_preferences_navigation.php
*
* Demonstrates viewer preference configuration, display mode selection,
* bookmarks, named destinations, and internal link annotations.
*
* What this demonstrates:
* - setDisplayMode(): initial zoom, page layout, and page mode.
* - setViewerPreferences(): HideToolbar, CenterWindow, Duplex, PrintScaling, etc.
* - setNamedDestination(): register named jump targets for cross-document linking.
* - addInternalLink(): page/position link ID for use inside the same document.
* - setLink() + page->addAnnotRef(): clickable rectangle annotations.
* - setBookmark(): outline entries that drive the bookmark panel.
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 051');
$pdf->setTitle('Viewer Preferences & Navigation');
$pdf->setKeywords('TCPDF tc-lib-pdf viewer preferences navigation bookmarks named destinations links');
$pdf->setPDFFilename('051_viewer_preferences_navigation.pdf');
// ----------
// Viewer preferences
// The viewer will open the document in single-page layout with outline panel
// visible. The window is centered on screen and the document title shown in
// the title bar.
$pdf->setViewerPreferences([
'DisplayDocTitle' => true,
'CenterWindow' => true,
'FitWindow' => false,
'HideToolbar' => false,
'HideMenubar' => false,
'Duplex' => 'DuplexFlipLongEdge',
'PrintScaling' => 'none',
'NumCopies' => 1,
'PrintPageRange' => [1, 4],
'NonFullScreenPageMode' => 'UseOutlines',
]);
// Display mode: open at full width, single-page layout, with outlines panel.
$pdf->setDisplayMode(
zoom: \Com\Tecnick\Pdf\DisplayZoom::FullWidth,
layout: \Com\Tecnick\Pdf\Page\PageLayout::SinglePage,
mode: \Com\Tecnick\Pdf\Page\PageDisplayMode::UseOutlines,
);
$pdf->enableDefaultPageContent();
// ----------
$headFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 16);
$subFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 11);
$bodyFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
// ===| Page 1 Cover |======================================================
$page1 = $pdf->addPage(['format' => 'A4']);
// Register a named destination at the top of page 1. External documents or
// bookmark links can jump here via '#cover'.
$pdf->setNamedDestination(name: 'cover', page: $page1['pid'], posx: 0, posy: 0);
$pdf->setBookmark(
name: 'Cover',
link: '#cover',
level: 0,
page: $page1['pid'],
posx: 0,
posy: 0,
fstyle: 'B',
color: 'darkblue',
);
$coverHtml = '<h1 style="font-family: helvetica; color: #1a3c6e; text-align: center; margin-top: 30mm;">
Viewer Preferences &amp; Navigation Demo
</h1>
<p style="font-family: helvetica; font-size: 11pt; text-align: center; color: #444444;">
tc-lib-pdf · Example 051
</p>
<hr/>
<p style="font-family: helvetica; font-size: 10pt;">
This document demonstrates:
</p>
<ul style="font-family: helvetica; font-size: 10pt;">
<li><strong>Viewer preferences</strong> — initial layout, duplex, print scaling, and window behavior.</li>
<li><strong>Display mode</strong> — zoom, page layout, and panel visibility on open.</li>
<li><strong>Named destinations</strong> — stable jump targets for cross-document links.</li>
<li><strong>Internal link annotations</strong> — clickable regions mapped to page positions.</li>
<li><strong>Bookmarks / outlines</strong> — hierarchical navigation panel entries.</li>
</ul>
<p style="font-family: helvetica; font-size: 10pt; margin-top: 8mm;">
The navigation buttons on page 4 use <code>setLink</code> and <code>addInternalLink</code>
to create clickable rectangles that jump to each chapter page.
</p>';
$pdf->addHTMLCell(html: $coverHtml, posx: 15, posy: 20, width: 170);
// ===| Page 2 Chapter 1 |==================================================
$page2 = $pdf->addPage(['format' => 'A4']);
$destChapter1 = $pdf->setNamedDestination(name: 'chapter-1', page: $page2['pid'], posx: 0, posy: 0);
$pdf->setBookmark(
name: 'Chapter 1 — Overview',
link: $destChapter1,
level: 0,
page: $page2['pid'],
posx: 0,
posy: 0,
fstyle: 'B',
color: 'darkgreen',
);
$pdf->setBookmark(name: '1.1 Introduction', link: '', level: 1, page: $page2['pid'], posx: 0, posy: 25);
$ch1Html = '<h2 style="font-family: helvetica; color: #1a6e3c;">Chapter 1 — Overview</h2>
<p style="font-family: helvetica; font-size: 10pt;">
This chapter is the target of the named destination <code>chapter-1</code>.
Clicking the button on the navigation page jumps here via an internal link annotation.
</p>
<h3 style="font-family: helvetica; color: #1a6e3c;">1.1 Introduction</h3>
<p style="font-family: helvetica; font-size: 10pt;">
The <code>setNamedDestination()</code> method registers a jump target that can be referenced
from bookmarks (using the <code>#</code> prefix), from link annotations, or from external
documents pointing to this file.
</p>
<p style="font-family: helvetica; font-size: 10pt;">
Named destinations are stored in the PDF <em>/Dests</em> dictionary and are independent
of page numbering — they remain stable even if pages are reordered.
</p>';
$pdf->addHTMLCell(html: $ch1Html, posx: 15, posy: 20, width: 170);
// ===| Page 3 Chapter 2 |==================================================
$page3 = $pdf->addPage(['format' => 'A4']);
$destChapter2 = $pdf->setNamedDestination(name: 'chapter-2', page: $page3['pid'], posx: 0, posy: 0);
$pdf->setBookmark(
name: 'Chapter 2 — Viewer Preferences',
link: $destChapter2,
level: 0,
page: $page3['pid'],
posx: 0,
posy: 0,
fstyle: 'B',
color: 'darkred',
);
$pdf->setBookmark(name: '2.1 Preference Keys', link: '', level: 1, page: $page3['pid'], posx: 0, posy: 25);
$prefTable = '<h2 style="font-family: helvetica; color: #6e1a1a;">Chapter 2 — Viewer Preferences</h2>
<p style="font-family: helvetica; font-size: 10pt;">
The <code>setViewerPreferences()</code> method populates the <em>/ViewerPreferences</em>
dictionary in the PDF catalog. The following preferences are active in this document:
</p>
<table border="1" cellpadding="2" cellspacing="0" style="font-family: helvetica; font-size: 9pt; width: 100%;">
<tr style="background-color: #dddddd;">
<th>Key</th><th>Value</th><th>Effect</th>
</tr>
<tr><td>DisplayDocTitle</td><td>true</td><td>Show PDF title in window title bar.</td></tr>
<tr><td>CenterWindow</td><td>true</td><td>Center the viewer window on screen.</td></tr>
<tr><td>Duplex</td><td>DuplexFlipLongEdge</td><td>Default duplex setting for print dialog.</td></tr>
<tr><td>PrintScaling</td><td>none</td><td>Disable auto-scaling in print dialog.</td></tr>
<tr><td>NumCopies</td><td>1</td><td>Default copy count in print dialog.</td></tr>
<tr><td>PrintPageRange</td><td>14</td><td>Default page range in print dialog.</td></tr>
<tr><td>NonFullScreenPageMode</td><td>UseOutlines</td><td>Show outline panel when exiting full screen.</td></tr>
</table>';
$pdf->addHTMLCell(html: $prefTable, posx: 15, posy: 20, width: 170);
// ===| Page 4 Navigation Map |=============================================
$page4 = $pdf->addPage(['format' => 'A4']);
$pdf->setBookmark(
name: 'Navigation Map',
link: '',
level: 0,
page: $page4['pid'],
posx: 0,
posy: 0,
fstyle: '',
color: 'gray',
);
$navHtml = '<h2 style="font-family: helvetica; color: #333333;">Navigation Map</h2>
<p style="font-family: helvetica; font-size: 10pt;">
Each button below is a clickable link annotation (<code>setLink</code> + <code>addAnnotRef</code>)
that uses <code>addInternalLink()</code> to jump to the start of a specific page.
</p>
<p style="font-family: helvetica; font-size: 10pt; margin-bottom: 8mm;">
Alternatively, the bookmark panel (outline) on the left provides the same navigation
via the bookmarks registered with <code>setBookmark()</code>.
</p>';
$pdf->addHTMLCell(html: $navHtml, posx: 15, posy: 20, width: 170);
// Define style for button boxes
$btnFill = ['all' => ['lineWidth' => 0.3, 'lineColor' => '#333366', 'fillColor' => '#e8eef8']];
$btnFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 11);
$pdf->page->addContent($btnFont['out']);
// Helper to draw a navigation button and attach a link annotation
$drawNavButton = static function (
\Com\Tecnick\Pdf\Tcpdf $pdf,
float $bx,
float $by,
float $bw,
float $bh,
string $label,
int $targetPage,
array $btnFill,
) use ($page4): void {
$pdf->page->addContent($pdf->graph->getRect($bx, $by, $bw, $bh, 'DF', $btnFill));
// Reset fill color to dark text after the rect draw, which left the fill color
// set to the button background color, making the label text invisible.
$pdf->page->addContent($pdf->color->getPdfColor('#333366'));
$pdf->page->addContent($pdf->getTextCell(
txt: $label,
posx: $bx,
posy: $by + 3,
width: $bw,
height: $bh - 3,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
$lnkid = $pdf->addInternalLink($targetPage, 0);
$annid = $pdf->setLink(posx: $bx, posy: $by, width: $bw, height: $bh, link: $lnkid);
$pdf->page->addAnnotRef($annid);
};
$drawNavButton($pdf, 25, 80, 70, 14, 'Go to Cover (page 1)', $page1['pid'], $btnFill);
$drawNavButton($pdf, 25, 100, 70, 14, 'Go to Chapter 1 (page 2)', $page2['pid'], $btnFill);
$drawNavButton($pdf, 25, 120, 70, 14, 'Go to Chapter 2 (page 3)', $page3['pid'], $btnFill);
// Restore the plain body font in the internal font stack before addHTMLCell;
// font->insert with 'B' above left the stack on the bold variant and would
// cause addHTMLCell to capture/restore 'helveticabB' → undefined key warning.
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
// Show named destination links using '#name' notation in bookmarks
$namedNoteHtml = '<p style="font-family: helvetica; font-size: 9pt; color: #555555; margin-top: 10mm;">
The bookmark entries above the buttons use <code>setNamedDestination()</code> targets
(<code>#cover</code>, <code>#chapter-1</code>, <code>#chapter-2</code>).
Named destinations survive page renumbering and can be referenced from external documents.
</p>';
$pdf->addHTMLCell(html: $namedNoteHtml, posx: 15, posy: 145, width: 170);
// ----------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,195 @@
<?php
/**
* E052_custom_fonts_fallback.php
*
* Demonstrates custom font selection, Unicode coverage strategy, and
* font-family fallback across multiple scripts.
*
* What this demonstrates:
* - font->insert(): loading built-in and extended font families.
* - Subset vs full-embed tradeoff (constructor $subsetfont flag).
* - Choosing fonts by Unicode coverage: helvetica (Latin), dejavusans
* (Latin + Greek + Cyrillic + IPA), unifont (full Unicode BMP).
* - HTML font-family CSS mapping to pre-loaded font slots.
* - Per-script font selection in addHTMLCell markup.
*
* Companion libraries: tc-lib-pdf-font, tc-lib-unicode, tc-lib-unicode-data.
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
// K_PATH_FONTS must point to the directory holding .z and .php font metric files.
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// Subset font embedding: when $subsetfont = true (constructor arg 3), only the
// glyphs actually used in the document are embedded. This produces smaller PDFs
// but makes the font unsuitable for later editing. Set to false for archival PDFs
// (PDF/A, PDF/UA) or whenever the full character set must be available.
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 052');
$pdf->setTitle('Custom Fonts & Unicode Fallback');
$pdf->setKeywords('TCPDF tc-lib-pdf font unicode multilingual fallback subset dejavusans unifont');
$pdf->setPDFFilename('052_custom_fonts_fallback.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Pre-load all font families that will be used in the document.
// font->insert() returns a font descriptor array. The 'out' key contains
// the PDF operator stream that must be injected onto a page before the
// font is used. Loading fonts before addPage() registers them in the
// font object pool; injecting 'out' at the start of each page activates
// the font for that page.
// Helvetica: core Type-1, no embedding required, Latin + standard symbols only.
$fHelvetica = $pdf->font->insert($pdf->pon, 'helvetica', '', 11);
$fHelveticaB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 11);
// Times New Roman (core): serif alternative for body text, Latin only.
$fTimes = $pdf->font->insert($pdf->pon, 'times', '', 11);
// Courier (core): monospaced, Latin only; good for code samples.
$fCourier = $pdf->font->insert($pdf->pon, 'courier', '', 10);
// DejaVu Sans: TrueType, Latin + Greek + Cyrillic + IPA + many other scripts.
// Covers most Western European, Eastern European, and Slavic languages.
$fDejavu = $pdf->font->insert($pdf->pon, 'dejavusans', '', 11);
// Unifont: TrueType, very broad Unicode BMP (plane 0) coverage including Arabic,
// Hebrew, Thai, Devanagari, and many additional scripts.
// File size is large; use subset=true when embedding it in production PDFs.
$fUnifont = $pdf->font->insert($pdf->pon, 'unifont', '', 11);
// ===| Page 1 Font Coverage Overview |=====================================
$pdf->addPage(['format' => 'A4']);
$pdf->page->addContent($fHelvetica['out']);
$overviewHtml = '<h1 style="font-family: helvetica;">Font Selection &amp; Unicode Fallback</h1>
<p style="font-family: helvetica; font-size: 10pt;">
PDF viewers cannot substitute fonts automatically — every glyph must come from
an embedded font resource. The strategy is:
</p>
<ol style="font-family: helvetica; font-size: 10pt;">
<li>Use <strong>core Type-1 fonts</strong> (helvetica, times, courier) for Latin-only text to
avoid embedding overhead.</li>
<li>Upgrade to <strong>DejaVu Sans</strong> for pages that mix Latin with Greek, Cyrillic, or
extended Latin (diacritics, IPA).</li>
<li>Use <strong>Unifont</strong> for pages that require full Unicode BMP coverage (Arabic, Hebrew,
Thai, Devanagari, Hangul, CJK).</li>
</ol>
<p style="font-family: helvetica; font-size: 10pt;">
In HTML cells, specify the font via <code>style="font-family: dejavusans"</code> or
<code>style="font-family: unifont"</code>.
The name must match the lowercase font identifier known to tc-lib-pdf-font.
</p>
<hr/>
<h2 style="font-family: helvetica;">Core Type-1 Fonts (Latin)</h2>
<p style="font-family: helvetica; font-size: 11pt;">
<span style="font-family: helvetica;">Helvetica — The quick brown fox jumps over the lazy dog.</span>
</p>
<p style="font-family: times; font-size: 11pt;">
<span style="font-family: times;">Times — The quick brown fox jumps over the lazy dog.</span>
</p>
<p style="font-family: courier; font-size: 10pt;">
<span style="font-family: courier;">Courier — The quick brown fox jumps over the lazy dog.</span>
</p>';
$pdf->addHTMLCell(html: $overviewHtml, posx: 15, posy: 20, width: 175);
// ===| Page 2 DejaVu Sans: Latin + Greek + Cyrillic |======================
$pdf->addPage(['format' => 'A4']);
$pdf->page->addContent($fDejavu['out']);
$dejavuHtml = '<h2 style="font-family: dejavusans;">DejaVu Sans — Extended Coverage</h2>
<p style="font-family: dejavusans; font-size: 10pt;">
DejaVu Sans covers Latin, Greek, Cyrillic, Armenian, Georgian, Hebrew,
Arabic (basic), and many more. Use it when helvetica glyphs are missing.
</p>
<h3 style="font-family: dejavusans;">Latin Extended</h3>
<p style="font-family: dejavusans; font-size: 11pt;">
Héllo Wörld — café, naïve, résumé, Ångström, façade, Ñoño.
</p>
<h3 style="font-family: dejavusans;">Greek (Ελληνικά)</h3>
<p style="font-family: dejavusans; font-size: 11pt;">
Ξεσκεπάζω την ψυχοφθόρα βδελυγμία.
</p>
<h3 style="font-family: dejavusans;">Cyrillic (Русский)</h3>
<p style="font-family: dejavusans; font-size: 11pt;">
Съешь же ещё этих мягких французских булок, да выпей чаю.
</p>
<h3 style="font-family: dejavusans;">IPA Phonetics</h3>
<p style="font-family: dejavusans; font-size: 11pt;">
/ˌɪntəˈnæʃənl/ · /fəˈnɛtɪk/ · /ˈælfəbɪt/
</p>
<hr/>
<p style="font-family: dejavusans; font-size: 9pt; color: #555555;">
Subset tip: dejavusans is ~750 KB uncompressed. With <code>$subsetfont = true</code> and
only the glyphs used in this page, the embedded subset would be much smaller.
For documents mixing many scripts across many pages, subset embedding is recommended.
</p>';
$pdf->addHTMLCell(html: $dejavuHtml, posx: 15, posy: 20, width: 175);
// ===| Page 3 Unifont: Full BMP |==========================================
$pdf->addPage(['format' => 'A4']);
$pdf->page->addContent($fUnifont['out']);
$unifontHtml = '<h2 style="font-family: unifont; font-size: 11pt; font-weight: normal;">Unifont — Full Unicode BMP Coverage</h2>
<p style="font-family: unifont; font-size: 11pt;">
Unifont offers very broad Unicode Plane 0 (BMP) coverage.
Use it as a last-resort fallback for scripts not covered by DejaVu.
Production PDFs should always use <code>$subsetfont = true</code> with Unifont.
</p>
<h3 style="font-family: unifont; font-size: 11pt; font-weight: normal;">Arabic (العربية) — RTL</h3>
<p style="font-family: unifont; font-size: 11pt; text-align: right;">
صِف خَلقَ خَودِكِ كَمِثلِ الشَّمسِ إِذ بَزَغَت
</p>
<h3 style="font-family: unifont; font-size: 11pt; font-weight: normal;">Hebrew (עברית) — RTL</h3>
<p style="font-family: unifont; font-size: 11pt; text-align: right;">
דג סקרן שט בים מאוכזב ולפתע מצא חברה
</p>
<h3 style="font-family: unifont; font-size: 11pt; font-weight: normal;">Devanagari (हिन्दी)</h3>
<p style="font-family: unifont; font-size: 11pt;">
ऋषि सुयश ने प्रख्यात ग्रंथ की रचना करके हमें ज्ञान दिया।
</p>
<hr/>
<p style="font-family: unifont; font-size: 11pt; color: #555555;">
Fallback strategy: attempt to render with the most specific font first.
Only escalate to Unifont for code points absent from narrower families.
This minimises file size while ensuring every glyph is available.
</p>';
$pdf->addHTMLCell(html: $unifontHtml, posx: 15, posy: 20, width: 175);
// ----------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,288 @@
<?php
/**
* E053_spot_overprint_proof.php
*
* Demonstrates spot color registration, tint ramp generation, and
* overprint simulation using tc-lib-color and tc-lib-pdf-graph.
*
* What this demonstrates:
* - color->addSpotColor(): register named spot colors with CMYK equivalents.
* - color->getPdfColor($name, $stroke, $tint): apply spot ink at any tint level.
* - Tint ramps: a sequence of filled rectangles stepping from 0% to 100% tint.
* - Layered spot rectangles: simulate overprint by stacking semi-transparent shapes.
* - Companion lib: tc-lib-color (spot/Lab color model), tc-lib-pdf-graph (rects).
*
* Note: tc-lib-pdf does not expose a native /OP (overprint) ExtGState toggle.
* Overprint simulation here uses stacked shapes with varying tints to give a
* visual impression of ink mixing, which is representative for screen proofing.
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 053');
$pdf->setTitle('Spot Color & Overprint Proof');
$pdf->setKeywords('TCPDF tc-lib-pdf spot color CMYK tint ramp overprint proof');
$pdf->setPDFFilename('053_spot_overprint_proof.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// ----------
// Register spot colors with CMYK fallback values.
// These simulate industry-standard spot-like named inks. In a real press
// workflow the names would match the RIP spot-color library exactly.
$pdf->color->addSpotColor('PROOF Cyan', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 1.0,
'magenta' => 0.0,
'yellow' => 0.0,
'key' => 0.0,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('PROOF Magenta', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.0,
'magenta' => 1.0,
'yellow' => 0.0,
'key' => 0.0,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('PROOF Yellow', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.0,
'magenta' => 0.0,
'yellow' => 1.0,
'key' => 0.0,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('PROOF Black', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.0,
'magenta' => 0.0,
'yellow' => 0.0,
'key' => 1.0,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('PROOF Warm Red', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.0,
'magenta' => 0.85,
'yellow' => 0.9,
'key' => 0.02,
'alpha' => 0.0,
]));
$pdf->color->addSpotColor('PROOF Reflex Blue', new \Com\Tecnick\Color\Model\Cmyk([
'cyan' => 0.93,
'magenta' => 0.75,
'yellow' => 0.0,
'key' => 0.02,
'alpha' => 0.0,
]));
// ----------
$labelFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 8);
$noteFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 8);
// ===| Page 1 Tint Ramps |=================================================
$pdf->addPage(['format' => 'A4']);
$pdf->page->addContent($labelFont['out']);
$introHtml = '<h1 style="font-family: helvetica;">Spot Color Proof — Tint Ramps</h1>
<p style="font-family: helvetica; font-size: 9pt;">
Each row shows a named spot color at tints from 10% to 100%.
Spot colors are registered via <code>color-&gt;addSpotColor()</code> with a CMYK equivalent
and applied using <code>color-&gt;getPdfColor($name, $stroke, $tint)</code>.
The label column shows the spot name as it would appear in a PDF color dictionary.
</p>';
$pdf->addHTMLCell(html: $introHtml, posx: 15, posy: 20, width: 175);
// List of spots to render and their display label color
/** @var array<int, array{name: string, label: string}> $spotRows */
$spotRows = [
['name' => 'PROOF Cyan', 'label' => '#007799'],
['name' => 'PROOF Magenta', 'label' => '#990077'],
['name' => 'PROOF Yellow', 'label' => '#887700'],
['name' => 'PROOF Black', 'label' => '#222222'],
['name' => 'PROOF Warm Red', 'label' => '#993300'],
['name' => 'PROOF Reflex Blue', 'label' => '#001177'],
];
$tintSteps = 10;
$swatchW = 14.0;
$swatchH = 10.0;
$labelW = 42.0;
$rowGap = 2.0;
$startX = 15.0;
$startY = 55.0;
// Draw tint scale header
$pdf->page->addContent($noteFont['out']);
for ($t = 1; $t <= $tintSteps; $t++) {
$tx = $startX + $labelW + (($t - 1) * $swatchW);
$pdf->page->addContent($pdf->getTextCell(
txt: (string) ($t * 10) . '%',
posx: $tx,
posy: $startY - 5,
width: $swatchW,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
}
$rowY = $startY;
foreach ($spotRows as $row) {
$spotName = $row['name'];
$labelColor = $row['label'];
// Spot name label
$pdf->page->addContent($labelFont['out']);
$pdf->page->addContent($pdf->color->getPdfColor($labelColor, true, 1.0));
$pdf->page->addContent($pdf->color->getPdfColor($labelColor, false, 1.0));
$pdf->page->addContent($pdf->getTextCell(
txt: $spotName,
posx: $startX,
posy: $rowY + 2,
width: $labelW,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// Draw tint swatches 10%100%
for ($t = 1; $t <= $tintSteps; $t++) {
$tint = $t / $tintSteps;
$sx = $startX + $labelW + (($t - 1) * $swatchW);
// Set fill and stroke to spot color at this tint level
$pdf->page->addContent($pdf->color->getPdfColor($spotName, true, $tint));
$pdf->page->addContent($pdf->color->getPdfColor($spotName, false, $tint));
$pdf->page->addContent($pdf->graph->getRect($sx, $rowY, $swatchW, $swatchH, 'DF'));
}
$rowY += $swatchH + $rowGap;
}
// Reset to black for subsequent text
$pdf->page->addContent($pdf->color->getPdfColor('black', true, 1.0));
$pdf->page->addContent($pdf->color->getPdfColor('black', false, 1.0));
$pdf->page->addContent($noteFont['out']);
$footNote = '<p style="font-family: helvetica; font-size: 8pt; color: #555555; margin-top: 4mm;">
Spot colors use the PDF <em>Separation</em> color space with a CMYK alternate.
Screen rendering uses the CMYK alternate; print output uses the named ink.
</p>';
$pdf->addHTMLCell(html: $footNote, posx: 15, posy: 170, width: 175);
// ===| Page 2 Stacked Spot Layers (Overprint Simulation) |=================
$pdf->addPage(['format' => 'A4']);
$pdf->page->addContent($labelFont['out']);
$overHtml = '<h1 style="font-family: helvetica;">Overprint Simulation — Stacked Spot Layers</h1>
<p style="font-family: helvetica; font-size: 9pt;">
Two spot inks are drawn in overlapping rectangles at varying tints to simulate
the visual effect of overprinting. In a real press workflow the RIP controls overprint
behaviour via the <em>/OP</em> and <em>/op</em> ExtGState flags.
This page shows the CMYK-alternate screen rendering of the overlap.
</p>';
$pdf->addHTMLCell(html: $overHtml, posx: 15, posy: 20, width: 175);
// Overprint grid: every combination of Cyan × Magenta at 25/50/75/100% tints
$ox = 25.0;
$oy = 55.0;
$cellSize = 22.0;
$tints = [0.25, 0.5, 0.75, 1.0];
$labelNames = ['25%', '50%', '75%', '100%'];
// Column headers (Cyan tint)
$pdf->page->addContent($labelFont['out']);
foreach ($labelNames as $ci => $clab) {
$pdf->page->addContent($pdf->getTextCell(
txt: 'C ' . $clab,
posx: $ox + 12 + ($ci * $cellSize),
posy: $oy - 7,
width: $cellSize,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
}
foreach ($tints as $mi => $mTint) {
// Row header (Magenta tint)
$pdf->page->addContent($pdf->getTextCell(
txt: 'M ' . $labelNames[$mi],
posx: $ox,
posy: $oy + ($mi * $cellSize) + 5,
width: 12,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
foreach ($tints as $ci => $cTint) {
$cx = $ox + 12 + ($ci * $cellSize);
$cy = $oy + ($mi * $cellSize);
// Draw Cyan layer first, then Magenta layer on top
$pdf->page->addContent($pdf->color->getPdfColor('PROOF Cyan', false, $cTint));
$pdf->page->addContent($pdf->graph->getRect($cx, $cy, $cellSize, $cellSize, 'F'));
$pdf->page->addContent($pdf->color->getPdfColor('PROOF Magenta', false, $mTint));
$pdf->page->addContent($pdf->graph->getAlpha(0.6)); // partial alpha to show both layers
$pdf->page->addContent($pdf->graph->getRect($cx, $cy, $cellSize, $cellSize, 'F'));
$pdf->page->addContent($pdf->graph->getAlpha(1.0)); // restore
}
}
// Reset colors
$pdf->page->addContent($pdf->color->getPdfColor('black', true, 1.0));
$pdf->page->addContent($pdf->color->getPdfColor('black', false, 1.0));
$overNote = '<p style="font-family: helvetica; font-size: 8pt; color: #555555; margin-top: 4mm;">
Each cell renders Cyan at the column tint, then Magenta at the row tint with 60% opacity —
approximating a soft-proof of two overprinted inks on a white substrate.
</p>';
$pdf->addHTMLCell(html: $overNote, posx: 15, posy: 165, width: 175);
// ----------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,288 @@
<?php
/**
* E054_page_groups_numbering.php
*
* Demonstrates publishing-style mixed page numbering: Roman-numeral front matter
* followed by Arabic body pages, with a generated table of contents and
* multi-level bookmarks as cross-reference anchors.
*
* What this demonstrates:
* - setBookmark(): hierarchical outline entries with styles and colors.
* - setNamedDestination(): stable jump targets independent of page order.
* - addTOC(): auto-generated TOC page linked back to bookmarked sections.
* - setDisplayMode(): open with outlines panel showing.
* - Logical vs physical page numbering: inject visible Roman/Arabic labels
* via page header content while the PDF page object index remains 0-based.
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 054');
$pdf->setTitle('Page Groups & Mixed Numbering');
$pdf->setKeywords('TCPDF tc-lib-pdf page groups numbering roman arabic TOC bookmarks');
$pdf->setPDFFilename('054_page_groups_numbering.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
// Open with the outline panel visible and full-width zoom.
$pdf->setDisplayMode(
zoom: \Com\Tecnick\Pdf\DisplayZoom::FullWidth,
layout: \Com\Tecnick\Pdf\Page\PageLayout::SinglePage,
mode: \Com\Tecnick\Pdf\Page\PageDisplayMode::UseOutlines,
);
$pdf->enableDefaultPageContent();
// ----------
// Fonts
$titleFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 16);
$chFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
$bodyFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$smallFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
// ----------
// Helpers
/** Convert an integer 13999 to an uppercase Roman numeral. */
$toRoman = static function (int $n): string {
$map = [
1000 => 'M',
900 => 'CM',
500 => 'D',
400 => 'CD',
100 => 'C',
90 => 'XC',
50 => 'L',
40 => 'XL',
10 => 'X',
9 => 'IX',
5 => 'V',
4 => 'IV',
1 => 'I',
];
$result = '';
foreach ($map as $val => $sym) {
while ($n >= $val) {
$result .= $sym;
$n -= $val;
}
}
return $result;
};
/**
* Render a page number footer for the current page.
* @param \Com\Tecnick\Pdf\Tcpdf $pdf
* @param int $pid Page PID
* @param string $label Visible page number string (e.g. "I", "1")
* @param array{out: string} $font Font descriptor
*/
$addPageFooter = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, int $pid, string $label, array $font): void {
$pdf->page->addContent($font['out'], $pid);
$pdf->page->addContent(
$pdf->getTextCell(
txt: '— ' . $label . ' —',
posx: 0,
posy: 282,
width: 210,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
),
$pid,
);
};
// ===| FRONT MATTER |========================================================
// Pages i, ii (TOC placeholder + Preface)
// These pages use Roman numerals in the footer.
// ----- Page i: TOC placeholder -----
$tocPage = $pdf->addPage(['format' => 'A4']);
$pdf->setBookmark(
name: 'Table of Contents',
link: '',
level: 0,
page: $tocPage['pid'],
posx: 0,
posy: 0,
fstyle: 'B',
color: 'gray',
);
$addPageFooter($pdf, $tocPage['pid'], $toRoman(1), $smallFont);
// Reserve header text for this page
$pdf->page->addContent($titleFont['out'], $tocPage['pid']);
$pdf->page->addContent(
$pdf->getTextCell(
txt: 'TABLE OF CONTENTS',
posx: 15,
posy: 20,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
),
$tocPage['pid'],
);
// The actual TOC content is injected by addTOC() at the end of the script.
// ----- Page ii: Preface -----
$prefacePage = $pdf->addPage(['format' => 'A4']);
$pdf->setBookmark(
name: 'Preface',
link: '',
level: 0,
page: $prefacePage['pid'],
posx: 0,
posy: 0,
fstyle: '',
color: 'gray',
);
$addPageFooter($pdf, $prefacePage['pid'], $toRoman(2), $smallFont);
$prefaceHtml = '<h2 style="font-family: helvetica; text-align: center;">Preface</h2>
<p style="font-family: helvetica; font-size: 10pt;">
This document demonstrates mixed page numbering conventions used in publishing workflows.
Front matter (title, preface, table of contents) carries Roman numerals (i, ii, iii …),
while the body text uses Arabic numerals (1, 2, 3 …).
</p>
<p style="font-family: helvetica; font-size: 10pt;">
In tc-lib-pdf, both logical page numbers displayed in footers and structural bookmarks are
controlled independently. The PDF page object index is always zero-based; the visible label
is rendered as regular text content. Named destinations remain stable regardless of the
visible numbering scheme.
</p>';
$pdf->addHTMLCell(html: $prefaceHtml, posx: 15, posy: 30, width: 175, height: $prefacePage['pid']);
// ===| BODY |================================================================
// Chapters 13, Arabic page numbers 13
/** @var array<int, array{title: string, sub: list<string>, color: string, bodyText: string}> $chapters */
$chapters = [
[
'title' => 'Chapter 1 — Foundations',
'sub' => ['1.1 History', '1.2 Key Concepts'],
'color' => '#1a3c6e',
'bodyText' => 'This chapter introduces the foundational ideas. Page number 1 (Arabic) appears in the footer. The bookmark entry for this chapter points to this page via a named destination.',
],
[
'title' => 'Chapter 2 — Methods',
'sub' => ['2.1 Approach A', '2.2 Approach B'],
'color' => '#1a6e3c',
'bodyText' => 'This chapter details the methodology. Page number 2 (Arabic) appears below. Bookmark level-1 entries for subsections use the same page pid with a vertical offset.',
],
[
'title' => 'Chapter 3 — Results',
'sub' => ['3.1 Findings', '3.2 Conclusions'],
'color' => '#6e1a1a',
'bodyText' => 'Final chapter with results and conclusions. Page number 3 (Arabic). The TOC on page i links back to all three chapter bookmarks.',
],
];
foreach ($chapters as $idx => $ch) {
$arabicNum = $idx + 1;
$chPage = $pdf->addPage(['format' => 'A4']);
// Register named destination so TOC links are stable.
$destName = $pdf->setNamedDestination(name: 'chapter-' . $arabicNum, page: $chPage['pid'], posx: 0, posy: 0);
// Top-level bookmark (bold, chapter color)
$pdf->setBookmark(
name: $ch['title'],
link: $destName,
level: 0,
page: $chPage['pid'],
posx: 0,
posy: 0,
fstyle: 'B',
color: $ch['color'],
);
// Sub-section bookmarks (indented, normal weight)
foreach ($ch['sub'] as $si => $subTitle) {
$subY = 70.0 + ($si * 20.0);
$pdf->setBookmark(
name: $subTitle,
link: '',
level: 1,
page: $chPage['pid'],
posx: 0,
posy: $subY,
fstyle: '',
color: $ch['color'],
);
}
// Page footer with Arabic numeral
$addPageFooter($pdf, $chPage['pid'], (string) $arabicNum, $smallFont);
// Chapter content
$chHtml =
'<h2 style="font-family: helvetica; color: '
. $ch['color']
. ';">'
. htmlspecialchars($ch['title'])
. '</h2>
<p style="font-family: helvetica; font-size: 10pt;">'
. htmlspecialchars($ch['bodyText'])
. '</p>';
foreach ($ch['sub'] as $subTitle) {
$chHtml .=
'<h3 style="font-family: helvetica; color: '
. $ch['color']
. ';">'
. htmlspecialchars($subTitle)
. '</h3>
<p style="font-family: helvetica; font-size: 10pt;">Section content would go here.</p>';
}
$pdf->addHTMLCell(html: $chHtml, posx: 15, posy: 20, width: 175, height: $chPage['pid']);
}
// ===| GENERATE TOC |========================================================
// addTOC() injects the outline entries onto the reserved TOC page (page i),
// starting at y=35 (below the "TABLE OF CONTENTS" heading).
// It uses the current font for rendering — activate bodyFont first.
$pdf->page->addContent($bodyFont['out'], $tocPage['pid']);
$pdf->addTOC(page: $tocPage['pid'], posx: 15, posy: 35, width: 175);
// ----------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,417 @@
<?php
/**
* E055_page_reorder_booklet.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate page reordering using tc-lib-pdf-page's Page::move() method.
*
* Scenario:
* - Add pages in "draft" logical order: Introduction, Appendix, Chapter 1, Chapter 2.
* - After authoring, reorder so the final reading order is:
* 1. Introduction, 2. Chapter 1, 3. Chapter 2, 4. Appendix
* - Each page shows its original draft index vs. its final booklet position.
* - A booklet-spread grid is drawn on a landscape summary page to show how pages
* pair up for saddle-stitch imposition (spread 1: pages 4 & 1; spread 2: pages 2 & 3).
*/
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 055');
$pdf->setTitle('Page Reorder and Booklet Imposition');
$pdf->setKeywords('TCPDF tc-lib-pdf example page reorder booklet imposition spread');
$pdf->setPDFFilename('E055_page_reorder_booklet.pdf');
$pdf->enableDefaultPageContent();
// Insert default font before first page.
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 11);
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
// -----------------------------------------------------------------------
// Phase 1 Author pages in draft order (Introduction, Appendix, Ch1, Ch2)
// -----------------------------------------------------------------------
// Page index 0 Introduction
$page0 = $pdf->addPage(['format' => 'A5']);
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Introduction',
posx: 15,
posy: 20,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: '(Draft index 0)',
posx: 15,
posy: 36,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Welcome to the tc-lib-pdf page-reorder demo. This page was authored first'
. ' and will remain in position 0 after reordering.',
posx: 15,
posy: 46,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// Page index 1 Appendix (will be moved to the end later)
$page1 = $pdf->addPage(['format' => 'A5']);
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Appendix',
posx: 15,
posy: 20,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: '(Draft index 1 will move to index 3)',
posx: 15,
posy: 36,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'This Appendix page was authored second but belongs at the end.'
. ' Page::move() will relocate it after Chapter 2.',
posx: 15,
posy: 46,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// Page index 2 Chapter 1
$page2 = $pdf->addPage(['format' => 'A5']);
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Chapter 1',
posx: 15,
posy: 20,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: '(Draft index 2 will move to index 1)',
posx: 15,
posy: 36,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Chapter 1 content. After reordering this becomes the second page (index 1),'
. ' immediately after the Introduction.',
posx: 15,
posy: 46,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// Page index 3 Chapter 2
$page3 = $pdf->addPage(['format' => 'A5']);
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Chapter 2',
posx: 15,
posy: 20,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: '(Draft index 3 will move to index 2)',
posx: 15,
posy: 36,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Chapter 2 content. After reordering this becomes the third page (index 2),'
. ' between Chapter 1 and the Appendix.',
posx: 15,
posy: 46,
width: 120,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// -----------------------------------------------------------------------
// Phase 2 Reorder using Page::move()
//
// Current order: [0:Intro] [1:Appendix] [2:Ch1] [3:Ch2]
// Target order: [0:Intro] [1:Ch1] [2:Ch2] [3:Appendix]
//
// Step A: move Ch1 (index 2) to index 1 → [Intro, Ch1, Appendix, Ch2]
// Step B: move Ch2 (now index 3) to index 2 → [Intro, Ch1, Ch2, Appendix]
//
// Note: Page::move() only moves a page to a LOWER (earlier) index.
// -----------------------------------------------------------------------
$pdf->page->move(2, 1);
// Order is now: [0:Intro] [1:Ch1] [2:Appendix] [3:Ch2]
$pdf->page->move(3, 2);
// Order is now: [0:Intro] [1:Ch1] [2:Ch2] [3:Appendix]
// -----------------------------------------------------------------------
// Phase 3 Booklet imposition summary page (A4 landscape)
//
// For a 4-page saddle-stitch booklet the imposition pairs are:
// Physical sheet front: page 4 (right) | page 1 (left) → spread 1
// Physical sheet back: page 2 (left) | page 3 (right) → spread 2
// -----------------------------------------------------------------------
$summaryPage = $pdf->addPage(['format' => 'A4', 'orientation' => 'L']);
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Booklet Imposition Summary (saddle-stitch, 4 pages)',
posx: 15,
posy: 12,
width: 260,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
$pdf->page->addContent($bfont['out']);
// Draw spread grid
$graph = $pdf->graph;
// Spread 1 outer sheet (pages 4 & 1)
$spread1X = 15.0;
$spread1Y = 30.0;
$spreadW = 118.0;
$spreadH = 75.0;
$halfW = $spreadW / 2;
// outer spread box
$pdf->page->addContent($graph->getRect($spread1X, $spread1Y, $spreadW, $spreadH, 'D', [], []));
// centre fold line
$pdf->page->addContent($graph->getLine($spread1X + $halfW, $spread1Y, $spread1X + $halfW, $spread1Y + $spreadH));
// labels
$pdf->page->addContent($pdf->getTextCell(
txt: 'Spread 1 Outer sheet',
posx: $spread1X,
posy: $spread1Y - 6,
width: $spreadW,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Page 4 (Appendix)',
posx: $spread1X + 2,
posy: $spread1Y + 4,
width: $halfW - 4,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Right side of sheet',
posx: $spread1X + 2,
posy: $spread1Y + 12,
width: $halfW - 4,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Page 1 (Introduction)',
posx: $spread1X + $halfW + 2,
posy: $spread1Y + 4,
width: $halfW - 4,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Left side of sheet',
posx: $spread1X + $halfW + 2,
posy: $spread1Y + 12,
width: $halfW - 4,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// Spread 2 inner sheet (pages 2 & 3)
$spread2X = $spread1X + $spreadW + 20.0;
$pdf->page->addContent($graph->getRect($spread2X, $spread1Y, $spreadW, $spreadH, 'D', [], []));
$pdf->page->addContent($graph->getLine($spread2X + $halfW, $spread1Y, $spread2X + $halfW, $spread1Y + $spreadH));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Spread 2 Inner sheet',
posx: $spread2X,
posy: $spread1Y - 6,
width: $spreadW,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Page 2 (Chapter 1)',
posx: $spread2X + 2,
posy: $spread1Y + 4,
width: $halfW - 4,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Left side of sheet',
posx: $spread2X + 2,
posy: $spread1Y + 12,
width: $halfW - 4,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Page 3 (Chapter 2)',
posx: $spread2X + $halfW + 2,
posy: $spread1Y + 4,
width: $halfW - 4,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Right side of sheet',
posx: $spread2X + $halfW + 2,
posy: $spread1Y + 12,
width: $halfW - 4,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// Fold / cut annotation
$pdf->page->addContent($bfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Fold along centre fold lines, then saddle-stitch through the fold.'
. ' The final reading order after binding: Introduction → Chapter 1 → Chapter 2 → Appendix.',
posx: 15,
posy: 120,
width: 260,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// -----------------------------------------------------------------------
// Output
// -----------------------------------------------------------------------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,252 @@
<?php
/**
* E056_encryption_cert_recipients.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate certificate-based (public-key) PDF encryption with a permission matrix.
*
* Certificate-based encryption lets you define a list of recipients; each recipient's
* public-key certificate is used to wrap the document key. Only holders of the
* corresponding private key can decrypt the document. No shared password is required.
*
* The example creates two logical recipients from the same demo certificate to show
* how different permission sets can be defined per-recipient:
* Recipient A read-only (print only)
* Recipient B reviewer (print + annotate)
*
* The demo certificate shipped with the examples is a self-signed X.509 cert.
* To create your own certificate:
* openssl req -x509 -nodes -days 365000 -newkey rsa:2048 \
* -keyout cert.pem -out cert.pem
*/
$certPath = \realpath(__DIR__ . '/data/cert/tcpdf.crt');
if ($certPath === false) {
throw new \RuntimeException('Missing demo certificate: examples/data/cert/tcpdf.crt');
}
$certUri = 'file://' . $certPath;
$mode = PHP_SAPI === 'cli' ? 'encrypted' : 'preview';
if (PHP_SAPI === 'cli') {
$mode = (string) ($argv[1] ?? 'encrypted');
} elseif (isset($_GET['mode']) && \is_string($_GET['mode'])) {
$mode = $_GET['mode'];
}
$mode = \strtolower(\trim($mode));
if (!\in_array($mode, ['encrypted', 'preview'], true)) {
$mode = PHP_SAPI === 'cli' ? 'encrypted' : 'preview';
}
$useEncryption = $mode === 'encrypted';
// Build recipient list:
// Each entry has 'c' (certificate path/URI) and 'p' (allowed permissions array).
$pubkeys = [
[
'c' => $certUri,
'p' => ['print'], // Recipient A: print-only
],
[
'c' => $certUri,
'p' => ['print', 'annot-forms'], // Recipient B: print + annotate
],
];
$fileId = \md5('E056_encryption_cert_recipients');
$encrypt = null;
if ($useEncryption) {
// AES-128 (mode 2) with certificate recipients no shared password needed.
$encrypt = new \Com\Tecnick\Pdf\Encrypt\Encrypt(
true, // enabled
$fileId,
2, // mode: AES-128
[], // global permissions (empty controlled per-recipient)
'', // no user password
'', // no owner password
$pubkeys, // certificate recipients
true, // encryptMetadata
true, // encryptEmbeddedFiles
);
}
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: $encrypt,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 056');
$pdf->setTitle('Certificate-Based Recipient Encryption');
$pdf->setKeywords('TCPDF tc-lib-pdf example encryption certificate recipients public-key');
$pdf->setPDFFilename('E056_encryption_cert_recipients.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 12);
// -----------------------------------------------------------------------
// Page 1 Overview
// -----------------------------------------------------------------------
$page1 = $pdf->addPage();
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Certificate-Based Recipient Encryption',
posx: 15,
posy: 15,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bfont['out']);
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$html = <<<HTML
<p><b>How it works</b></p>
<p>Certificate-based encryption (also called <em>public-key encryption</em>) wraps the
document encryption key using each recipient's X.509 public-key certificate.
No shared password is distributed; only the holder of the matching private key can
open the document.</p>
<p><b>Recipient permission matrix</b></p>
<table border="1" cellpadding="4" cellspacing="0">
<tr>
<th>Recipient</th>
<th>Certificate</th>
<th>Allowed permissions</th>
</tr>
<tr>
<td>Recipient A (read-only)</td>
<td>tcpdf.crt (demo)</td>
<td>print</td>
</tr>
<tr>
<td>Recipient B (reviewer)</td>
<td>tcpdf.crt (demo)</td>
<td>print, annot-forms</td>
</tr>
</table>
<p><b>Encryption parameters</b></p>
<ul>
<li>Output mode: MODE_PLACEHOLDER</li>
<li>Algorithm: AES-128 (mode 2) when mode is <em>encrypted</em></li>
<li>Metadata encrypted: yes (encrypted mode)</li>
<li>Embedded files encrypted: yes (encrypted mode)</li>
<li>User/owner password: none (certificate recipients only, encrypted mode)</li>
</ul>
<p><b>Creating your own certificate</b></p>
<p>Use the following OpenSSL command to generate a self-signed certificate suitable for
recipient encryption:</p>
<p><em>openssl req -x509 -nodes -days 365000 -newkey rsa:2048 -keyout cert.pem -out cert.pem</em></p>
HTML;
$modeLabel = $useEncryption ? 'encrypted (certificate recipients)' : 'preview (unencrypted for browser compatibility)';
$html = \str_replace('MODE_PLACEHOLDER', $modeLabel, $html);
$pdf->addHTMLCell(html: $html, posx: 15, posy: 30, width: 180);
// -----------------------------------------------------------------------
// Page 2 Permissions reference
// -----------------------------------------------------------------------
$page2 = $pdf->addPage();
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'PDF Permission Flags Reference',
posx: 15,
posy: 15,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bfont['out']);
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$html2 = <<<HTML
<p><b>Available permission identifiers</b></p>
<table border="1" cellpadding="4" cellspacing="0">
<tr><th>Identifier</th><th>Description</th></tr>
<tr><td>print</td><td>Print the document (low-quality).</td></tr>
<tr><td>modify</td><td>Modify document content (not covered by more specific flags).</td></tr>
<tr><td>copy</td><td>Copy or extract text and graphics.</td></tr>
<tr><td>annot-forms</td><td>Add/modify annotations and fill interactive forms.</td></tr>
<tr><td>fill-forms</td><td>Fill in existing interactive form fields.</td></tr>
<tr><td>extract</td><td>Extract content for accessibility.</td></tr>
<tr><td>assemble</td><td>Insert/rotate/delete pages, create bookmarks and thumbnails.</td></tr>
<tr><td>print-high</td><td>Print at high quality (faithful digital reproduction).</td></tr>
</table>
<p><b>Notes</b></p>
<ul>
<li>With certificate recipients, permission flags are embedded inside the CMS enveloped-data
structure for each recipient separately.</li>
<li>Viewer enforcement of permissions depends on the PDF reader implementation.</li>
<li>Combining certificate encryption with a digital signature (E057) provides both
access control and document integrity assurance.</li>
</ul>
HTML;
$pdf->addHTMLCell(html: $html2, posx: 15, posy: 30, width: 180);
// -----------------------------------------------------------------------
// Output
// -----------------------------------------------------------------------
$rawpdf = $pdf->getOutPDFString();
if (PHP_SAPI !== 'cli') {
if ($useEncryption) {
// Most browser-native PDF viewers do not support certificate-based decryption.
// Force a download so the file can be opened with a compatible PDF reader.
$pdf->downloadPDF(rawpdf: $rawpdf);
exit();
}
$pdf->renderPDF(rawpdf: $rawpdf);
exit();
}
echo $rawpdf;
@@ -0,0 +1,286 @@
<?php
/**
* E057_multisignature_incremental.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate a multi-signature approval flow.
*
* In a real approval workflow a document typically accumulates one signature
* per approver in sequence (incremental signatures). tc-lib-pdf supports this
* pattern by:
*
* 1. setSignature() primary signing certificate (author / certifier)
* 2. setSignatureAppearance() visible widget for the primary signature
* 3. addEmptySignatureAppearance() empty widget placeholders for each subsequent approver
*
* The primary signature field (cert_type 2 = MDP/Author) locks the document
* against further modifications while still permitting subsequent approvals to
* fill in the reserved empty fields.
*
* This example creates a 3-page document:
* Page 1 contract / document body
* Page 2 approval form with 3 signature fields:
* · Author (primary pre-signed by the library)
* · Reviewer (empty approval field)
* · Manager (empty approval field)
* Page 3 signing guide / workflow description
*/
$certPath = \realpath(__DIR__ . '/data/cert/tcpdf.crt');
if ($certPath === false) {
throw new \RuntimeException('Missing signing certificate: examples/data/cert/tcpdf.crt');
}
$cert = 'file://' . $certPath;
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 057');
$pdf->setTitle('Multi-Signature Approval Flow');
$pdf->setKeywords('TCPDF tc-lib-pdf example signature approval multi-signature incremental');
$pdf->setPDFFilename('E057_multisignature_incremental.pdf');
$pdf->enableDefaultPageContent();
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 12);
// -----------------------------------------------------------------------
// Page 1 Document body (contract)
// -----------------------------------------------------------------------
$page1 = $pdf->addPage();
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'SERVICE AGREEMENT EXAMPLE',
posx: 15,
posy: 15,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
$pdf->page->addContent($bfont['out']);
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$html1 = <<<HTML
<p>This Service Agreement ("Agreement") is entered into as of the date of the last signature below
between <strong>Client</strong> and <strong>Service Provider</strong>.</p>
<p><b>1. Services.</b> Service Provider agrees to deliver software development services as described
in Schedule A attached hereto.</p>
<p><b>2. Payment.</b> Client agrees to pay the fees set forth in Schedule B within 30 days of invoice.</p>
<p><b>3. Term.</b> This Agreement commences on the Effective Date and continues for 12 months unless
earlier terminated by either party upon 30 days written notice.</p>
<p><b>4. Confidentiality.</b> Each party agrees to keep the other party's confidential information
strictly confidential and not to disclose it to any third party without prior written consent.</p>
<p><b>5. Governing Law.</b> This Agreement shall be governed by the laws of the applicable jurisdiction.</p>
<p>The parties have executed this Agreement as of the dates indicated in the signature fields on
the following page.</p>
HTML;
$pdf->addHTMLCell(html: $html1, posx: 15, posy: 30, width: 180);
// -----------------------------------------------------------------------
// Page 2 Approval / signature form
// -----------------------------------------------------------------------
$page2 = $pdf->addPage();
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'SIGNATURE PAGE EXAMPLE',
posx: 15,
posy: 15,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
$pdf->page->addContent($bfont['out']);
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$pdf->page->addContent($pdf->getTextCell(
txt: 'The following parties have reviewed and approved this Agreement:',
posx: 15,
posy: 30,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// Signature box helper labels (rendered as PDF text cells)
$labels = [
['role' => 'Author / Certifier', 'name' => 'John Doe', 'y' => 45],
['role' => 'Reviewer', 'name' => '(awaiting sig.)', 'y' => 90],
['role' => 'Manager', 'name' => '(awaiting sig.)', 'y' => 135],
];
foreach ($labels as $lbl) {
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: $lbl['role'],
posx: 15,
posy: (float) $lbl['y'],
width: 85,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bfont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Name: ' . $lbl['name'],
posx: 15,
posy: (float) ($lbl['y'] + 7),
width: 85,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($pdf->getTextCell(
txt: 'Date: ___________________',
posx: 15,
posy: (float) ($lbl['y'] + 14),
width: 85,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
}
// -----------------------------------------------------------------------
// Register the primary (author/certifier) digital signature.
// cert_type = 2 → MDP / author signature
// -----------------------------------------------------------------------
$pdf->setSignature([
'cert_type' => 2,
'info' => [
'ContactInfo' => 'https://github.com/tecnickcom/tc-lib-pdf',
'Location' => 'Demo Office',
'Name' => 'John Doe',
'Reason' => 'Author / certifier signature approval workflow example',
],
'password' => '',
'privkey' => $cert,
'signcert' => $cert,
]);
// Primary signature appearance placed on page 2 at the first slot.
$pdf->setSignatureAppearance(posx: 100, posy: 45, width: 90, height: 35, page: $page2['pid'], name: 'Author');
// Empty approval fields for the remaining approvers.
$pdf->addEmptySignatureAppearance(posx: 100, posy: 90, width: 90, height: 35, page: $page2['pid'], name: 'Reviewer');
$pdf->addEmptySignatureAppearance(posx: 100, posy: 135, width: 90, height: 35, page: $page2['pid'], name: 'Manager');
// -----------------------------------------------------------------------
// Page 3 Workflow guide
// -----------------------------------------------------------------------
$page3 = $pdf->addPage();
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Multi-Signature Workflow Guide',
posx: 15,
posy: 15,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bfont['out']);
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$html3 = <<<HTML
<p><b>How incremental multi-signature works in tc-lib-pdf</b></p>
<p>A PDF approval workflow typically progresses through these steps:</p>
<ol>
<li><b>Author creates and certifies the document.</b>
The primary signature (Author field) is applied by <code>setSignature()</code> with
<em>cert_type 2</em> (MDP). This locks the document against structural changes
while reserving the empty approval fields for subsequent signers.</li>
<li><b>Reviewer opens the document, validates the Author signature, and applies their
signature to the Reviewer field.</b> In a PDF viewer (e.g. Acrobat) the reviewer
uses the reserved empty widget. Each subsequent signature is appended to the file
as an incremental update, preserving all prior signature data.</li>
<li><b>Manager repeats the same process for the Manager field.</b></li>
</ol>
<p><b>tc-lib-pdf API summary</b></p>
<table border="1" cellpadding="4" cellspacing="0">
<tr><th>Method</th><th>Purpose</th></tr>
<tr><td>setSignature()</td><td>Register the primary signing certificate and options.</td></tr>
<tr><td>setSignatureAppearance()</td><td>Visible widget for the primary signature.</td></tr>
<tr><td>addEmptySignatureAppearance()</td><td>Reserve an empty widget for a future approver.</td></tr>
<tr><td>setSignTimeStamp()</td><td>Optionally attach an RFC 3161 TSA timestamp (see E008).</td></tr>
</table>
<p><b>Certification levels (cert_type)</b></p>
<table border="1" cellpadding="4" cellspacing="0">
<tr><th>cert_type</th><th>Meaning</th></tr>
<tr><td>1</td><td>No changes permitted after signing.</td></tr>
<tr><td>2</td><td>Form fills and approval signatures permitted (MDP).</td></tr>
<tr><td>3</td><td>Annotations and form fills permitted.</td></tr>
</table>
HTML;
$pdf->addHTMLCell(html: $html3, posx: 15, posy: 30, width: 180);
// -----------------------------------------------------------------------
// Output
// -----------------------------------------------------------------------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,276 @@
<?php
/**
* E058_embedded_files_portfolio.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate PDF as a document container (portfolio / package).
*
* Multiple file types are embedded inside the PDF using:
* addEmbeddedFile() embed a file from disk
* addContentAsEmbeddedFile() embed content generated in-memory (CSV, XML, JSON)
*
* Each embedded file is then linked to an annotation on the document page so
* that clicking the annotation in a viewer opens or exports the attachment.
*
* Embedded files:
* invoice.csv tabular invoice data
* report.xml XML report
* config.json JSON configuration
*/
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 058');
$pdf->setTitle('Embedded Files Portfolio');
$pdf->setKeywords('TCPDF tc-lib-pdf example embedded files portfolio attachment csv xml json');
$pdf->setPDFFilename('E058_embedded_files_portfolio.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 12);
// -----------------------------------------------------------------------
// Build the in-memory attachment content
// -----------------------------------------------------------------------
$csvContent = <<<CSV
item_no,description,qty,unit_price,total
1,"Widget A",10,9.99,99.90
2,"Widget B",5,24.50,122.50
3,"Service Fee",1,150.00,150.00
,,,,
,,"Subtotal",,372.40
,,"Tax (10%)",,37.24
,,"Total",,409.64
CSV;
$xmlContent = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<report generated="2026-05-01" version="1.0">
<summary>
<title>Monthly Sales Report</title>
<period>April 2026</period>
<currency>USD</currency>
</summary>
<items>
<item id="1"><name>Widget A</name><qty>10</qty><revenue>99.90</revenue></item>
<item id="2"><name>Widget B</name><qty>5</qty><revenue>122.50</revenue></item>
<item id="3"><name>Service Fee</name><qty>1</qty><revenue>150.00</revenue></item>
</items>
<totals>
<subtotal>372.40</subtotal>
<tax>37.24</tax>
<total>409.64</total>
</totals>
</report>
XML;
$jsonContent = \json_encode(
[
'application' => 'tc-lib-pdf demo',
'version' => '1.0',
'settings' => [
'compress' => true,
'unicode' => true,
'subset_fonts' => false,
'pdf_version' => '1.7',
],
'output' => [
'mode' => 'render',
'filename' => 'E058_embedded_files_portfolio.pdf',
],
],
\JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES,
);
if ($jsonContent === false) {
throw new \RuntimeException('Failed to encode JSON content');
}
// Embed content generated in-memory
$pdf->addContentAsEmbeddedFile(
file: 'invoice.csv',
content: $csvContent,
mime: 'text/csv',
afrel: \Com\Tecnick\Pdf\AFRelationship::Data,
desc: 'Invoice line items',
);
$pdf->addContentAsEmbeddedFile(
file: 'report.xml',
content: $xmlContent,
mime: 'application/xml',
afrel: \Com\Tecnick\Pdf\AFRelationship::Data,
desc: 'Monthly sales report',
);
$pdf->addContentAsEmbeddedFile(
file: 'config.json',
content: $jsonContent,
mime: 'application/json',
afrel: \Com\Tecnick\Pdf\AFRelationship::Source,
desc: 'Library configuration',
);
// -----------------------------------------------------------------------
// Page 1 Portfolio overview
// -----------------------------------------------------------------------
$page1 = $pdf->addPage();
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Embedded Files Portfolio',
posx: 15,
posy: 15,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
$pdf->page->addContent($bfont['out']);
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$html = <<<HTML
<p>This PDF acts as a self-contained package by embedding three data files in different formats.
Click the annotation icons on this page (or use your PDF viewer's attachment panel) to
access the files.</p>
<p><b>Embedded attachments</b></p>
<table border="1" cellpadding="4" cellspacing="0">
<tr><th>Filename</th><th>MIME type</th><th>AFRelationship</th><th>Description</th></tr>
<tr><td>invoice.csv</td><td>text/csv</td><td>Data</td><td>Invoice line items</td></tr>
<tr><td>report.xml</td><td>application/xml</td><td>Data</td><td>Monthly sales report</td></tr>
<tr><td>config.json</td><td>application/json</td><td>Source</td><td>Library configuration</td></tr>
</table>
<p><b>API used</b></p>
<table border="1" cellpadding="4" cellspacing="0">
<tr><th>Method</th><th>Use case</th></tr>
<tr><td>addEmbeddedFile()</td><td>Embed a file read from disk.</td></tr>
<tr><td>addContentAsEmbeddedFile()</td><td>Embed content generated in memory.</td></tr>
<tr><td>setAnnotation() with Fileattachment subtype</td><td>Visible annotation linking to an embedded file.</td></tr>
</table>
<p>The <em>AFRelationship</em> value categorises each attachment for PDF processors:
<em>Source</em> = the source that produced this PDF; <em>Data</em> = data associated with the PDF.</p>
HTML;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 30, width: 180);
// -----------------------------------------------------------------------
// Attachment annotations link visible icons to each embedded file
// Annotation type 'FileAttachment', opt 'fs' = file key, 'name' = icon name
// -----------------------------------------------------------------------
$annotations = [
['file' => 'invoice.csv', 'label' => 'invoice.csv', 'x' => 15, 'y' => 175],
['file' => 'report.xml', 'label' => 'report.xml', 'x' => 60, 'y' => 175],
['file' => 'config.json', 'label' => 'config.json', 'x' => 105, 'y' => 175],
];
foreach ($annotations as $ann) {
$annotId = $pdf->setAnnotation(
posx: (float) $ann['x'],
posy: (float) $ann['y'],
width: 40.0,
height: 10.0,
txt: (string) $ann['label'],
opt: [
'subtype' => 'FileAttachment',
'fs' => $ann['file'],
'name' => 'PushPin',
'f' => 4,
],
);
$pdf->page->addAnnotRef($annotId, $page1['pid']);
}
// -----------------------------------------------------------------------
// Page 2 Content preview
// -----------------------------------------------------------------------
$page2 = $pdf->addPage();
$pdf->page->addContent($bfontB['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Attachment Content Preview',
posx: 15,
posy: 15,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
$pdf->page->addContent($bfont['out']);
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$previewHtml = <<<HTML
<p><b>invoice.csv (first few lines)</b></p>
<pre>item_no,description,qty,unit_price,total
1,"Widget A",10,9.99,99.90
2,"Widget B",5,24.50,122.50
3,"Service Fee",1,150.00,150.00</pre>
<p><b>report.xml (excerpt)</b></p>
<pre>&lt;report generated="2026-05-01" version="1.0"&gt;
&lt;summary&gt;&lt;title&gt;Monthly Sales Report&lt;/title&gt;&lt;/summary&gt;
&lt;totals&gt;
&lt;subtotal&gt;372.40&lt;/subtotal&gt;
&lt;total&gt;409.64&lt;/total&gt;
&lt;/totals&gt;
&lt;/report&gt;</pre>
<p><b>config.json (excerpt)</b></p>
<pre>{
"application": "tc-lib-pdf demo",
"version": "1.0",
"settings": {
"compress": true,
"unicode": true
}
}</pre>
HTML;
$pdf->addHTMLCell(html: $previewHtml, posx: 15, posy: 30, width: 180);
// -----------------------------------------------------------------------
// Output
// -----------------------------------------------------------------------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,175 @@
<?php
/**
* E059_document_javascript.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate document-level JavaScript actions.
*
* This example covers three distinct attachment mechanisms:
*
* appendRawJavaScript()
* Appends raw JS to the single global script string. The global script
* runs as a document-open action (equivalent to \WillSave / open trigger
* in Acrobat JS terms). Useful for lightweight one-shot initialization
* that must not duplicate logic across multiple calls.
*
* addRawJavaScriptObj($script, onload: true)
* Creates a separate JS object that is executed when the document is
* opened (document-level open action). Suitable for modular scripts
* that need to be kept independent of the global string.
*
* addRawJavaScriptObj($script, onload: false)
* Creates a JS object that is embedded but NOT wired as an open action.
* It can be referenced by annotation actions or triggered programmatically
* by other scripts (e.g. via a named action or a button field).
*
* NOTE: JavaScript is silently suppressed in PDF/A, PDF/X, and PDF/UA modes
* because those standards forbid embedded scripts. This example therefore
* uses plain (unconstrained) PDF output.
*
* NOTE: Script execution requires a PDF reader with Acrobat JavaScript support
* (e.g. Adobe Acrobat / Adobe Reader). Many open-source readers do not run JS.
*/
// main TCPDF object — plain mode (no PDF/A, PDF/X, PDF/UA constraint)
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 059');
$pdf->setTitle('Document-Level JavaScript Actions');
$pdf->setKeywords('TCPDF tc-lib-pdf example javascript document open close print save trigger');
$pdf->setPDFFilename('E059_document_javascript.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// -----------------------------------------------------------------------
// 1. appendRawJavaScript — global JS string (document-open trigger)
// -----------------------------------------------------------------------
// The JS string is accumulated and emitted as a single named JS object
// ("EmbeddedJS"). It fires when the document is opened.
$pdf->appendRawJavaScript(script: 'app.alert("Document opened via appendRawJavaScript.", 3);' . "\n");
// Multiple calls are concatenated in order.
$pdf->appendRawJavaScript(
script: 'app.addStateChangeHandler("WillClose", function() {'
. "\n"
. ' app.alert("Document is closing (WillClose handler).", 3);'
. "\n"
. '});'
. "\n",
);
// -----------------------------------------------------------------------
// 2. addRawJavaScriptObj — separate JS object, executed on open
// -----------------------------------------------------------------------
// Returns the PDF object ID so you can cross-reference if needed.
$onloadObjId = $pdf->addRawJavaScriptObj(
// Register a WillPrint event to warn the user before printing.
'app.addStateChangeHandler("WillPrint", function() {'
. "\n"
. ' app.alert("Document will be printed.", 3);'
. "\n"
. '});'
. "\n",
true, // onload: execute when the document is opened
);
// -----------------------------------------------------------------------
// 3. addRawJavaScriptObj — separate JS object, NOT an open action
// -----------------------------------------------------------------------
// This object is embedded in the PDF but is NOT automatically executed.
// A button field or annotation action can trigger it by name.
$idleObjId = $pdf->addRawJavaScriptObj(
// A utility helper function that can be called from other scripts.
'function showDocInfo() {'
. "\n"
. ' var msg = "Title: " + this.info.Title + "\\n"'
. "\n"
. ' + "Author: " + this.info.Author;'
. "\n"
. ' app.alert(msg, 3);'
. "\n"
. '}'
. "\n",
false, // onload: false — NOT executed automatically
);
// -----------------------------------------------------------------------
// Page 1 — Explanation
// -----------------------------------------------------------------------
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
$page1 = $pdf->addPage();
$html = <<<HTML
<h1 style="font-size:16pt; color:#003366;">Document-Level JavaScript Actions</h1>
<p style="font-size:10pt; color:#333333;">
This PDF demonstrates three distinct ways to attach JavaScript to a document
using tc-lib-pdf. The scripts are embedded in the PDF structure; a compatible
reader with Acrobat JavaScript support is required to execute them.
</p>
<h2 style="font-size:13pt; color:#005599;">1. appendRawJavaScript()</h2>
<p style="font-size:9pt; color:#333333;">
Appends raw JS to the global script string. The accumulated string fires when
the document is opened. Two separate calls were made in this example: one to
show an alert on open, and one to register a <code>WillClose</code> handler.
</p>
<h2 style="font-size:13pt; color:#005599;">2. addRawJavaScriptObj(\$script, onload: true)</h2>
<p style="font-size:9pt; color:#333333;">
Creates a separate JS object (PDF object ID: <b>{$onloadObjId}</b>) and wires it as an
open action so it executes when the document is opened. In this example the
script registers a <code>WillPrint</code> event handler.
</p>
<h2 style="font-size:13pt; color:#005599;">3. addRawJavaScriptObj(\$script, onload: false)</h2>
<p style="font-size:9pt; color:#333333;">
Creates a JS object (PDF object ID: <b>{$idleObjId}</b>) that is embedded but NOT
executed automatically. It defines a helper function <code>showDocInfo()</code>
that other scripts or button-field actions can invoke.
</p>
<h2 style="font-size:13pt; color:#005599;">Suppression in conformance modes</h2>
<p style="font-size:9pt; color:#333333;">
All three JS APIs silently return without effect when the PDF is created in
PDF/A, PDF/X, or PDF/UA mode, because those standards forbid embedded scripts.
</p>
HTML;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,226 @@
<?php
/**
* E060_custom_xmp_metadata.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate custom XMP namespace injection via setCustomXMP().
*
* PDF files carry an XMP metadata stream alongside the classic Info dictionary.
* The tc-lib-pdf library always emits standard namespaces (dc:, xmp:, pdf:,
* xmpMM:, pdfaid:, etc.). setCustomXMP() lets you inject arbitrary additional
* XMP fragments into specific positions within the XMP envelope.
*
* Valid keys and their injection points
* ──────────────────────────────────────
* 'x:xmpmeta'
* Inserted just before </x:xmpmeta> — top-level envelope extension.
*
* 'x:xmpmeta.rdf:RDF'
* Inserted just before </rdf:RDF> — adds a new rdf:Description block.
*
* 'x:xmpmeta.rdf:RDF.rdf:Description'
* Inserted into the main rdf:Description — adds properties to the
* existing shared description block (dc, xmp, pdf properties).
*
* 'x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas'
* 'x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas.rdf:Bag'
* PDF/A extension schema declarations (only meaningful in PDF/A mode).
*
* IMPORTANT: the injected XML is NOT validated by the library. You are
* responsible for well-formed, namespace-correct XMP. Malformed XMP will
* not prevent PDF generation but may confuse metadata readers.
*
* Use cases illustrated here:
* 1. IPTC Core rights metadata (photojournalism / stock-photography DAM)
* 2. XMP-MM version history (document management / DAM systems)
* 3. Custom application metadata (arbitrary key-value pairs for integration)
*/
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 060');
$pdf->setTitle('Custom XMP Metadata Injection');
$pdf->setKeywords('TCPDF tc-lib-pdf example xmp metadata iptc dublin core dam archiving');
$pdf->setPDFFilename('E060_custom_xmp_metadata.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// -----------------------------------------------------------------------
// 1. IPTC Core rights and creator metadata
// Injected as a new rdf:Description block inside rdf:RDF.
// -----------------------------------------------------------------------
$iptcXmp = <<<'XMP'
<rdf:Description rdf:about=""
xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/">
<Iptc4xmpCore:CopyrightNotice>Copyright © 2026 Tecnick.com LTD. All rights reserved.</Iptc4xmpCore:CopyrightNotice>
<Iptc4xmpCore:CreatorContactInfo>
<rdf:Description>
<Iptc4xmpCore:CiEmailWork>info@tecnick.com</Iptc4xmpCore:CiEmailWork>
<Iptc4xmpCore:CiUrlWork>https://tecnick.com</Iptc4xmpCore:CiUrlWork>
</rdf:Description>
</Iptc4xmpCore:CreatorContactInfo>
<Iptc4xmpCore:UsageTerms>
<rdf:Alt>
<rdf:li xml:lang="en">Permission is granted to reproduce for personal and commercial use,
citing the source. Modification is not allowed.</rdf:li>
</rdf:Alt>
</Iptc4xmpCore:UsageTerms>
</rdf:Description>
XMP;
// Key: inject before </rdf:RDF>
$pdf->setCustomXMP('x:xmpmeta.rdf:RDF', $iptcXmp);
// -----------------------------------------------------------------------
// 2. XMP Media Management — version / revision history
// Also injected as a new rdf:Description block.
// -----------------------------------------------------------------------
$xmpMmXmp = <<<'XMP'
<rdf:Description rdf:about=""
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#">
<xmpMM:DocumentID>urn:uuid:tc-lib-pdf-example-060-2026</xmpMM:DocumentID>
<xmpMM:InstanceID>urn:uuid:tc-lib-pdf-example-060-instance-001</xmpMM:InstanceID>
<xmpMM:VersionID>1.0</xmpMM:VersionID>
<xmpMM:History>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<stEvt:action>created</stEvt:action>
<stEvt:when>2026-05-01T00:00:00+00:00</stEvt:when>
<stEvt:softwareAgent>tc-lib-pdf</stEvt:softwareAgent>
</rdf:li>
</rdf:Seq>
</xmpMM:History>
<xmpMM:DerivedFrom rdf:parseType="Resource">
<stRef:documentID>urn:uuid:tc-lib-pdf-example-059-2026</stRef:documentID>
<stRef:versionID>1.0</stRef:versionID>
</xmpMM:DerivedFrom>
</rdf:Description>
XMP;
$pdf->setCustomXMP('x:xmpmeta.rdf:RDF', $xmpMmXmp);
// -----------------------------------------------------------------------
// 3. Custom application metadata (key-value pairs for system integration)
// Injected as extra properties in the existing shared rdf:Description.
// -----------------------------------------------------------------------
$appXmp = <<<'XMP'
<tcpdf:exampleId>E060</tcpdf:exampleId>
<tcpdf:generatedBy>tc-lib-pdf</tcpdf:generatedBy>
<tcpdf:generationDate>2026-05-01</tcpdf:generationDate>
XMP;
// Note: this key injects into the existing shared rdf:Description block.
// The tcpdf: prefix must be declared in the same Description or a parent.
// For self-contained safety we wrap it in its own Description with the ns decl.
$appXmpBlock = <<<'XMP'
<rdf:Description rdf:about=""
xmlns:tcpdf="https://github.com/tecnickcom/tc-lib-pdf/ns/xmp/1.0/">
<tcpdf:exampleId>E060</tcpdf:exampleId>
<tcpdf:generatedBy>tc-lib-pdf</tcpdf:generatedBy>
<tcpdf:generationDate>2026-05-01</tcpdf:generationDate>
<tcpdf:featureTag>custom-xmp-metadata</tcpdf:featureTag>
</rdf:Description>
XMP;
$pdf->setCustomXMP('x:xmpmeta.rdf:RDF', $appXmpBlock);
// -----------------------------------------------------------------------
// Page — explanation and key summary
// -----------------------------------------------------------------------
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
$page1 = $pdf->addPage();
$html = <<<'HTML'
<h1 style="font-size:16pt; color:#003366;">Custom XMP Metadata Injection</h1>
<p style="font-size:10pt; color:#333333;">
This PDF embeds three custom XMP metadata blocks beyond the standard dc:/xmp:
namespaces that tc-lib-pdf emits automatically. Inspect the XMP stream with
a PDF inspector or <code>exiftool -XMP -b output.pdf</code> to verify.
</p>
<h2 style="font-size:13pt; color:#005599;">Block 1 — IPTC Core rights</h2>
<p style="font-size:9pt; color:#333333;">
Namespace: <b>Iptc4xmpCore</b> (http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/)<br />
Injection key: <code>x:xmpmeta.rdf:RDF</code><br />
Contains: CopyrightNotice, CreatorContactInfo (email, URL), UsageTerms.<br />
Typical use: photojournalism DAM, stock-photography catalogues.
</p>
<h2 style="font-size:13pt; color:#005599;">Block 2 — XMP-MM version history</h2>
<p style="font-size:9pt; color:#333333;">
Namespace: <b>xmpMM</b> (http://ns.adobe.com/xap/1.0/mm/)<br />
Injection key: <code>x:xmpmeta.rdf:RDF</code><br />
Contains: DocumentID, InstanceID, VersionID, History (sequence of events), DerivedFrom.<br />
Typical use: document management systems, digital asset lineage tracking.
</p>
<h2 style="font-size:13pt; color:#005599;">Block 3 — Custom application metadata</h2>
<p style="font-size:9pt; color:#333333;">
Namespace: <b>tcpdf</b> (https://github.com/tecnickcom/tc-lib-pdf/ns/xmp/1.0/)<br />
Injection key: <code>x:xmpmeta.rdf:RDF</code><br />
Contains: exampleId, generatedBy, generationDate, featureTag.<br />
Typical use: integration metadata for build pipelines, print-MIS systems, or
any workflow that needs to carry arbitrary key-value pairs inside the PDF
without modifying the Info dictionary.
</p>
<h2 style="font-size:13pt; color:#005599;">Important notes</h2>
<p style="font-size:9pt; color:#333333;">
• The injected XML is embedded as-is; the library does NOT validate XMP well-formedness.<br />
• Multiple calls with the same key are concatenated in insertion order.<br />
• Injection into <code>x:xmpmeta.rdf:RDF.rdf:Description</code> adds properties to the
shared Description block; prefer a standalone Description block (as done here) to
keep namespace declarations self-contained.<br />
• All standard metadata (title, author, subject, keywords, dates, producer) is still
emitted automatically and is not affected by setCustomXMP().
</p>
HTML;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,244 @@
<?php
/**
* E061_user_rights_reader_extensions.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// NOTE: This example requires a signing certificate. Run the following to create a self-signed one:
// cd examples/data/cert
// openssl req -x509 -nodes -days 365000 -newkey rsa:2048 -keyout tcpdf.crt -out tcpdf.crt \
// -subj "/CN=tc-lib-pdf demo"
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate Adobe Reader extension rights via setUserRights().
*
* Background
* ──────────
* Standard PDF encryption (setEncrypt / E045) controls which operations a
* user is *prevented* from performing. That is a restriction model.
*
* Reader Extensions (UR — Usage Rights) are a complementary *enablement*
* model: they grant additional privileges to free Adobe Reader users that
* Reader would otherwise reserve for Acrobat Pro. Common examples:
* • Filling in forms and saving the filled data
* • Creating, modifying, and deleting annotations (comments, stamps, etc.)
* • Signing existing signature fields
* • Exporting embedded files
*
* Implementation
* ──────────────
* UR rights are embedded via a special /UR3 signature object (SubFilter:
* adbe.pkcs7.detached with cert_type = 0, i.e. no DocMDP constraint).
* This is distinct from a CMS approval or certification signature:
* • cert_type > 0 → DocMDP (document modification detection / certification)
* • cert_type = 0 → UR (usage-rights extension)
*
* setUserRights() accepts an array of right categories:
* enabled (bool) must be true to activate UR output
* document (string) document-level rights e.g. '/FullSave'
* form (string) form-filling rights e.g. '/FillIn /Export'
* formex (string) form extras e.g. '/BarcodePlaintext'
* annots (string) annotation rights e.g. '/Create /Delete /Modify /Copy /Import /Export'
* signature (string) signing rights e.g. '/Modify'
* ef (string) embedded-file rights e.g. '/Create /Delete /Modify /Import'
*
* The companion setSignature() call wires the UR3 signature using the
* supplied certificate. cert_type = 0 selects the UR branch in the output
* layer (see Output::getOutSignatureUserRights).
*
* IMPORTANT: UR rights are only honoured by Adobe Reader / Acrobat. Most
* open-source readers do not enforce or display UR distinctions.
*/
$certPath = \realpath(__DIR__ . '/data/cert/tcpdf.crt');
if ($certPath === false) {
throw new \RuntimeException(
'Missing signing certificate: examples/data/cert/tcpdf.crt'
. "\n"
. 'Create it with:'
. "\n"
. ' openssl req -x509 -nodes -days 365000 -newkey rsa:2048 \\'
. "\n"
. ' -keyout examples/data/cert/tcpdf.crt -out examples/data/cert/tcpdf.crt \\'
. "\n"
. ' -subj "/CN=tc-lib-pdf demo"',
);
}
$cert = 'file://' . $certPath;
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 061');
$pdf->setTitle('Reader Extension Rights (UR Signature)');
$pdf->setKeywords('TCPDF tc-lib-pdf example user rights reader extensions UR signature form annotation');
$pdf->setPDFFilename('E061_user_rights_reader_extensions.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// -----------------------------------------------------------------------
// Enable Reader Extension Rights
// -----------------------------------------------------------------------
// Grant form-filling, commenting, and signing rights to free Adobe Reader.
$pdf->setUserRights([
'enabled' => true,
'document' => '/FullSave',
'form' => '/FillIn /Export',
'annots' => '/Create /Delete /Modify /Copy /Import /Export',
'signature' => '/Modify',
'ef' => '/Create /Delete /Modify /Import',
]);
// Wire the UR3 signature (cert_type = 0 → UR path, not DocMDP).
$pdf->setSignature([
'cert_type' => 0, // 0 = UR, not DocMDP certification
'password' => '',
'privkey' => $cert,
'signcert' => $cert,
'info' => [
'Name' => 'tc-lib-pdf',
'Location' => 'Demo',
'Reason' => 'Enabling Reader Extension Rights',
'ContactInfo' => 'https://github.com/tecnickcom/tc-lib-pdf',
],
]);
// -----------------------------------------------------------------------
// Add a simple interactive text field to exercise form-filling rights
// -----------------------------------------------------------------------
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
$page1 = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
// Re-insert non-bold base font so the font stack is in a clean state
// before addHTMLCell starts its height-estimation pass.
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
// Info block
$html = <<<'HTML'
<h1 style="font-size:16pt; color:#003366;">Reader Extension Rights (UR Signature)</h1>
<p style="font-size:10pt; color:#333333;">
This PDF uses a /UR3 usage-rights signature to grant additional privileges to
free Adobe Reader users beyond the default read-only experience.
</p>
<h2 style="font-size:13pt; color:#005599;">Rights enabled in this document</h2>
<table border="0" cellpadding="3">
<tr><td style="font-size:9pt; color:#003366; font-weight:bold; width:90mm;">Category</td>
<td style="font-size:9pt; color:#003366; font-weight:bold;">Granted rights</td></tr>
<tr><td style="font-size:9pt;">Document</td>
<td style="font-size:9pt;">/FullSave — Reader may save a filled-in copy</td></tr>
<tr><td style="font-size:9pt;">Form</td>
<td style="font-size:9pt;">/FillIn /Export — fill fields and export data</td></tr>
<tr><td style="font-size:9pt;">Annotations</td>
<td style="font-size:9pt;">/Create /Delete /Modify /Copy /Import /Export</td></tr>
<tr><td style="font-size:9pt;">Signature</td>
<td style="font-size:9pt;">/Modify — apply or clear a digital signature</td></tr>
<tr><td style="font-size:9pt;">Embedded files</td>
<td style="font-size:9pt;">/Create /Delete /Modify /Import</td></tr>
</table>
<h2 style="font-size:13pt; color:#005599;">Interactive field (test form-fill right)</h2>
<p style="font-size:9pt; color:#333333;">
The text field below can be filled and <b>saved</b> in free Adobe Reader because
the FullSave document right has been enabled. Without UR rights, Reader would
only allow printing a filled-in copy, not saving it.
</p>
HTML;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
// Add a text field that exercises the form-filling right.
$ffid = $pdf->addFFText('ur_test_field', 15, 140, 120, 8, ['value' => 'Type here and save...']);
$pdf->page->addAnnotRef($ffid);
// Label for the field.
// Re-insert non-bold font before getTextCell to keep the font stack clean.
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$pdf->page->addContent($pdf->getTextCell(
'Saveable text field:',
15.0,
133.0,
60.0,
6.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// -----------------------------------------------------------------------
// Page 2 — Contrast with DocMDP certification (cert_type > 0)
// -----------------------------------------------------------------------
$page2 = $pdf->addPage();
// Re-insert non-bold base font before addHTMLCell.
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$html2 = <<<'HTML'
<h1 style="font-size:16pt; color:#003366;">UR vs DocMDP — Key Differences</h1>
<h2 style="font-size:13pt; color:#005599;">cert_type = 0 → Usage Rights (UR3) — this document</h2>
<p style="font-size:9pt; color:#333333;">
Purpose: <b>grant</b> extra privileges to Reader.<br />
Signature type: /UR3 — not a DocMDP certification.<br />
Does not constrain future modifications.<br />
Reader shows a "Reader Extensions" badge rather than a certification badge.<br />
Created via: <code>setUserRights([...]) + setSignature(['cert_type' =&gt; 0, ...])</code>
</p>
<h2 style="font-size:13pt; color:#005599;">cert_type = 1/2/3 → DocMDP Certification</h2>
<p style="font-size:9pt; color:#333333;">
Purpose: <b>certify</b> authorship and <b>restrict</b> allowed changes.<br />
cert_type 1 = no changes allowed; 2 = form-filling + signing; 3 = + annotations.<br />
Reader shows a certification badge; further changes break the signature seal.<br />
Created via: <code>setSignature(['cert_type' =&gt; 2, ...])</code> (see E007E009)
</p>
<h2 style="font-size:13pt; color:#005599;">Standard encryption (E045) vs UR rights</h2>
<p style="font-size:9pt; color:#333333;">
Standard PDF encryption sets a <b>permission bitmap</b> that <b>prevents</b> operations
(printing, copying, modifying) unless the owner password is supplied.<br />
UR rights are orthogonal: they <b>enable</b> specific Reader features that are
otherwise locked regardless of password protection. Both mechanisms can be
combined in the same document.
</p>
HTML;
$pdf->addHTMLCell(html: $html2, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,207 @@
<?php
/**
* E062_bidi_mixed_rtl_ltr.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// Unifont covers ~65 000 Unicode glyphs (including per-glyph cbbox data).
// Loading its full metric array exceeds the 128 MB PHP default.
\ini_set('memory_limit', '256M');
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate mixed bidirectional (BiDi) text layout using setRTL().
*
* tc-lib-pdf supports Unicode BiDi text via tc-lib-unicode. The document
* direction is set with setRTL(bool):
*
* setRTL(false) — LTR default (Latin, CJK, etc.)
* setRTL(true) — RTL default (Arabic, Hebrew, Persian, Urdu, etc.)
*
* The direction affects:
* • Cell and text origin anchoring (LTR: x is left edge; RTL: x is right edge)
* • HTML block-level alignment defaults (text-align)
* • Cursor advancement direction
*
* Within an HTML block the Unicode BiDi algorithm handles inline direction
* switches automatically for mixed runs (e.g. Hebrew words embedded inside
* an English sentence or vice-versa).
*
* Difference from E003 (pure Arabic/Persian):
* E003 uses a pure RTL document with no LTR direction switching.
* This example focuses on the MID-DOCUMENT switch between RTL and LTR pages
* and on truly mixed paragraphs that contain both Hebrew and Latin text
* on the same line, which is common in real-world bilingual publishing.
*
* Font note: Unifont contains glyphs for virtually all Unicode scripts and
* correctly supports BiDi rendering, making it the safest choice for
* multilingual examples.
*/
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 062');
$pdf->setTitle('Mixed BiDi RTL/LTR Typography');
$pdf->setKeywords('TCPDF tc-lib-pdf example bidi rtl ltr hebrew arabic latin mixed direction');
$pdf->setPDFFilename('E062_bidi_mixed_rtl_ltr.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent(false);
// Unifont — comprehensive Unicode coverage including Hebrew
$bfont = $pdf->font->insert($pdf->pon, 'unifont', '', 12);
// ===================================================================
// Page 1 — LTR document with embedded RTL runs
// ===================================================================
$pdf->setRTL(false); // default: LTR
$page1 = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$html1 = <<<'HTML'
<h1 style="font-size:15pt; color:#003366;">Page 1 — LTR document, embedded RTL runs</h1>
<p style="font-size:10pt; color:#333333;">
This paragraph is in English (LTR). Hebrew words are embedded inline and the
Unicode BiDi algorithm resolves their direction automatically without any
explicit markup. For example: the Hebrew word for "peace" is שָׁלוֹם and the
word for "hello" is שָׁלוֹם (shalom). The sentence continues in English after
each Hebrew run.
</p>
<p style="font-size:10pt; color:#333333;">
A mixed technical phrase: the algorithm (אַלְגּוֹרִיתְם) runs in
O(n log n) time. The constant (קְבוּעַ) is defined as 3.14.
Numbers like 1, 2, 3 remain LTR regardless of surrounding script context.
</p>
<p style="font-size:10pt; color:#333333;">
Quotation from the Hebrew Bible (Psalm 133:1, LTR paragraph):
<span style="color:#660000;">הִנֵּה מַה טוֹב וּמַה נָּעִים שֶׁבֶת אַחִים גַּם יָחַד</span>
— "How good and pleasant it is when brothers dwell together in unity."
</p>
<h2 style="font-size:12pt; color:#005599;">Right-aligned Hebrew block (LTR document, explicit alignment)</h2>
<p style="font-size:10pt; text-align:right; color:#333333;">
בְּרֵאשִׁית בָּרָא אֱלֹהִים אֵת הַשָּׁמַיִם וְאֵת הָאָרֶץ<br />
וְהָאָרֶץ הָיְתָה תֹהוּ וָבֹהוּ וְחֹשֶׁךְ עַל פְּנֵי תְהוֹם<br />
וְרוּחַ אֱלֹהִים מְרַחֶפֶת עַל פְּנֵי הַמָּיִם
</p>
<p style="font-size:8pt; color:#666666; text-align:right;">Genesis 1:1-2 (Hebrew)</p>
HTML;
$pdf->addHTMLCell(html: $html1, posx: 10, posy: 10, width: 190);
// ===================================================================
// Page 2 — RTL document with embedded LTR runs
// ===================================================================
$pdf->setRTL(true); // switch to RTL for this page
$page2 = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$html2 = <<<'HTML'
<h1 style="font-size:15pt; color:#003366;">עמוד 2 — מסמך RTL עם רצפי LTR</h1>
<p style="font-size:10pt; color:#333333; text-align:right;">
פסקה זו נכתבת בעברית (RTL). מונחים אנגליים כגון <span dir="ltr">Unicode BiDi</span>
ו-<span dir="ltr">tc-lib-pdf</span> מוטמעים בשורה, ואלגוריתם ה-BiDi מטפל בכיוונם
אוטומטית. המשפט ממשיך בעברית לאחר כל רצף אנגלי.
</p>
<p style="font-size:10pt; color:#333333; text-align:right;">
ניתן לשלב מספרים כגון 42, 3.14 ו-100% בתוך טקסט עברי ללא
סימון מיוחד. הם מוצגים בסדר קריאה נכון בהקשרם.
</p>
<p style="font-size:10pt; color:#333333; text-align:right;">
משפט טכני מעורב: הפונקציה
<span dir="ltr" style="color:#0000aa; font-weight:bold;">setRTL(true)</span>
מגדירה את כיוון מסמך ברירת המחדל ל-RTL. ניתן להחליף כיוון
בכל עמוד על ידי קריאה ל-<span dir="ltr" style="color:#0000aa;">setRTL(false)</span>
לפני <span dir="ltr" style="color:#0000aa;">addPage()</span>.
</p>
HTML;
$pdf->addHTMLCell(html: $html2, posx: 10, posy: 10, width: 190);
// ===================================================================
// Page 3 — Back to LTR: summary and guidance
// ===================================================================
$pdf->setRTL(false); // back to LTR
$page3 = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
$html3 = <<<'HTML'
<h1 style="font-size:15pt; color:#003366;">Page 3 — Summary and Guidance</h1>
<h2 style="font-size:12pt; color:#005599;">setRTL() scope</h2>
<p style="font-size:9pt; color:#333333;">
<code>setRTL()</code> sets the default document direction. Call it <b>before</b>
<code>addPage()</code> to apply the direction to the new page context. Changing
direction mid-page is possible but affects all subsequent cell-placement calls on
that page.
</p>
<h2 style="font-size:12pt; color:#005599;">Inline BiDi — no markup needed</h2>
<p style="font-size:9pt; color:#333333;">
The Unicode BiDi algorithm (UAX #9) handles inline direction switches
automatically. You do not need explicit <code>dir="rtl"</code> or
<code>dir="ltr"</code> attributes for most runs — the script category of each
character determines its display direction. Use explicit <code>dir</code>
only when the context-based algorithm would resolve the direction incorrectly
(e.g. punctuation adjacent to a direction boundary).
</p>
<h2 style="font-size:12pt; color:#005599;">Font coverage</h2>
<p style="font-size:9pt; color:#333333;">
This example uses <b>Unifont</b>, which covers virtually all Unicode blocks
including Hebrew, Arabic, Devanagari, CJK, and Latin. For production use,
consider a font with better kerning and glyph quality for your primary script
(DejaVu Sans for Latin+Hebrew, Noto fonts for full multilingual coverage).
</p>
<h2 style="font-size:12pt; color:#005599;">Pure RTL scripts — see also</h2>
<p style="font-size:9pt; color:#333333;">
For documents that are entirely in Arabic or Persian (no mixed LTR content)
see <b>E003_persian_arabic.php</b>, which demonstrates <code>setRTL(false)</code>
(the document is LTR but the HTML content uses <code>text-align:right</code>
combined with Unicode RTL codepoints).
</p>
HTML;
$pdf->addHTMLCell(html: $html3, posx: 10, posy: 10, width: 190);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,842 @@
<?php
/**
* E063_manual_tag_tree.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate manual PDF/UA structure-element tagging via
* beginStructElem() / endStructElem().
*
* Background
* ──────────
* Accessible (tagged) PDFs carry a logical structure tree that screen readers
* use to navigate and read content in reading order. When tc-lib-pdf operates
* in a pdfua or pdfua1/pdfua2 mode it auto-tags content produced by
* addHTMLCell() using the HTML semantic elements (h1→H1, p→P, etc.).
*
* For content produced with the lower-level text APIs — or for cases
* where you need precise control over structure roles — you can bracket each
* logical block with:
*
* beginStructElem(string $role, int $pid)
* Opens a structure-element bracket. All tagged content produced
* until the matching endStructElem() is associated with this element.
* $role is a PDF structure role (see PDF 32000 §14.8):
* Headings : H, H1 … H6
* Body : P, Blockquote, Note, Caption
* List : L, LI, Lbl, LBody
* Table : Table, TR, TH, TD
* Inline : Span, Link, Reference, BibEntry
* Other : Document, Part, Sect, Art, Figure, TOC, TOCI
* $pid is the page ID from addPage()['pid'].
*
* endStructElem()
* Closes the current bracket. The completed element (with all its
* tagged content MCIDs) is appended to the structure log.
* Empty elements (no tagged content inside the bracket) are silently
* discarded.
*
* When pdfuaMode is '' (plain PDF mode) both methods are no-ops, so the
* same code runs safely in plain and PDF/UA modes without changes.
*
* NOTE: beginStructElem()/endStructElem() only associate MCIDs with content
* emitted through the tagged text path. For manual tagging, use addTextCell()
* (second argument is the page ID) rather than writing raw getTextCell()
* output directly with page->addContent().
*
* This example uses 'pdfua1' mode and demonstrates the full manual-tagging
* surface across four pages:
*
* Page 1 — Heading roles (H1, H2, H3), P, Figure, Caption
* Page 2 — Note, Blockquote, multi-paragraph body
* Page 3 — Sect/Art grouping containers; list structure (L > LI > Lbl + LBody)
* Page 4 — Table nesting (Table > TR > TH / TD); Figure with /Alt alternate text
*
* Non-semantic decorative content can be emitted as Artifact marked-content
* with addArtifactContent() or, at the low level, by writing the strings
* returned by beginArtifact() / endArtifact() with page->addContent().
* This example uses addArtifactContent() to add decorative separator rules as
* /Artifact with /Type /Layout.
*
* All content is tagged with addTextCell() inside explicit struct-elem brackets.
* Compare with E015E017 which enable pdfua mode via addHTMLCell (auto-tagged).
*/
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::Pdfua1,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 063');
$pdf->setTitle('Manual Tag Tree (PDF/UA Structure Elements)');
$pdf->setKeywords('TCPDF tc-lib-pdf example pdfua tagged structure accessibility wcag screen reader');
$pdf->setPDFFilename('E063_manual_tag_tree.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$leftMargin = 15.0;
$rightMargin = 15.0;
$pdf->setDefaultCellMargin(top: 0.0, right: $rightMargin, bottom: 0.0, left: 0.0);
$setFont = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, string $family, string $style, int $size): array {
if ($family === 'helvetica') {
$family = 'dejavusans';
}
$font = $pdf->font->insert($pdf->pon, $family, $style, $size);
$pdf->page->addContent($font['out']);
return $font;
};
$addDecorativeSeparator = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, int $pid, float $posY) use ($leftMargin): void {
$lineStyle = [
'all' => [
'lineWidth' => 0.25,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#bdbdbd',
'fillColor' => '',
],
];
$pdf->addArtifactContent($pdf->graph->getLine($leftMargin, $posY, 195.0, $posY, $lineStyle), $pid, 'Layout');
};
// -----------------------------------------------------------------------
// Font setup — insert before addPage()
// -----------------------------------------------------------------------
$pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
// -----------------------------------------------------------------------
// Page 1 — H1, H2, H3, P, Caption, Figure structure elements
// -----------------------------------------------------------------------
$page1 = $pdf->addPage();
$pid1 = $page1['pid'];
$addDecorativeSeparator($pdf, $pid1, 16.0);
// --- H1 heading ---
$setFont($pdf, 'helvetica', 'B', 16);
$pdf->beginStructElem(role: 'H1', pid: $pid1);
$pdf->addTextCell(
'Manual Tag Tree — PDF/UA Structure Elements',
$pid1,
$leftMargin,
20.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
// --- Introductory paragraph (P) ---
$setFont($pdf, 'helvetica', '', 10);
$intro =
'This document uses beginStructElem() and endStructElem() to build '
. 'the accessibility structure tree manually. Every piece of text is '
. 'wrapped in an explicit structure-element bracket that maps content '
. 'to a PDF logical role (H1, H2, H3, P, Caption). A screen reader can '
. 'navigate the heading hierarchy and read body paragraphs in the correct '
. 'logical order.';
$pdf->beginStructElem(role: 'P', pid: $pid1);
$pdf->addTextCell(
$intro,
$pid1,
$leftMargin,
32.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
// --- Section 1: H2 + P ---
$setFont($pdf, 'helvetica', 'B', 14);
$pdf->beginStructElem(role: 'H2', pid: $pid1);
$pdf->addTextCell(
'Section 1 — Heading Roles',
$pid1,
$leftMargin,
55.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
$setFont($pdf, 'helvetica', '', 10);
$sec1body =
'Structure roles H1 through H6 define the heading hierarchy. '
. 'H1 is the document title; H2 is a top-level section heading; H3 is a '
. 'sub-section; and so on. Screen readers present this outline as a '
. 'navigable table of contents. Nesting is implied by the sequence of '
. 'structure elements logged by endStructElem().';
$pdf->beginStructElem(role: 'P', pid: $pid1);
$pdf->addTextCell(
$sec1body,
$pid1,
$leftMargin,
64.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
// --- Sub-section: H3 ---
$setFont($pdf, 'helvetica', 'BI', 11);
$pdf->beginStructElem(role: 'H3', pid: $pid1);
$pdf->addTextCell(
'1.1 — Sub-section with H3 role',
$pid1,
$leftMargin,
85.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
$setFont($pdf, 'helvetica', '', 10);
$pdf->beginStructElem(role: 'P', pid: $pid1);
$pdf->addTextCell(
'An H3 element is used here to show a third level in the outline. '
. 'The structure tree will contain: H1 > H2 > H3 > P, reflecting the '
. 'logical reading order of this page.',
$pid1,
$leftMargin,
93.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
// --- Section 2: H2 + P + Figure + Caption ---
$setFont($pdf, 'helvetica', 'B', 14);
$pdf->beginStructElem(role: 'H2', pid: $pid1);
$pdf->addTextCell(
'Section 2 — Figure and Caption Roles',
$pid1,
$leftMargin,
113.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
$setFont($pdf, 'helvetica', '', 10);
$pdf->beginStructElem(role: 'P', pid: $pid1);
$pdf->addTextCell(
'The Figure role wraps graphical content. '
. 'A Caption role immediately following a Figure provides an '
. 'accessible description of the visual element for screen-reader users.',
$pid1,
$leftMargin,
122.0,
150,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
// Draw and tag a simple coloured rectangle as Figure content.
$figStyle = [
'all' => [
'lineWidth' => 0.5,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#336699',
'fillColor' => '#cce0ff',
],
];
// The bounding box is required by PDF/UA for figures contained on a single page.
// It is expressed in points, from the bottom-left page corner.
$pdf->addTaggedFigureContent(
$pdf->graph->getStartTransform() . $pdf->graph->getRect($leftMargin, 143.0, 180.0, 25.0, 'DF', $figStyle)
. $pdf->graph->getStopTransform(),
$pid1,
'Blue placeholder rectangle representing a figure area',
[
$pdf->toPoints($leftMargin),
$pdf->toPoints($page1['height'] - 143.0 - 25.0),
$pdf->toPoints($leftMargin + 180.0),
$pdf->toPoints($page1['height'] - 143.0),
],
);
// Caption for the figure.
$pdf->page->addContent($pdf->color->getPdfColor('black'), $pid1);
$setFont($pdf, 'helvetica', 'I', 8);
$pdf->beginStructElem(role: 'Caption', pid: $pid1);
$pdf->addTextCell(
'Figure 1 — Placeholder rectangle representing graphical content (Caption role)',
$pid1,
$leftMargin,
170.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
);
$pdf->endStructElem();
// -----------------------------------------------------------------------
// Page 2 — Note, Blockquote, and multi-paragraph body
// -----------------------------------------------------------------------
$page2 = $pdf->addPage();
$pid2 = $page2['pid'];
$addDecorativeSeparator($pdf, $pid2, 16.0);
$setFont($pdf, 'helvetica', 'B', 14);
$pdf->beginStructElem(role: 'H2', pid: $pid2);
$pdf->addTextCell(
'Page 2 — Note, Blockquote, and Multi-element Brackets',
$pid2,
$leftMargin,
20.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
$setFont($pdf, 'helvetica', '', 10);
// Note element (with explicit ID for validator compatibility)
$pdf->beginStructElem(role: 'Note', pid: $pid2, alt: null, attr: ['ID' => 'note-p2-01']);
$pdf->addTextCell(
'NOTE: beginStructElem() / endStructElem() are no-ops in plain PDF mode '
. '(pdfuaMode = ""). The same code runs safely in both plain and PDF/UA '
. 'modes without conditional branching.',
$pid2,
$leftMargin,
32.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
// Blockquote element
$setFont($pdf, 'helvetica', 'BI', 11);
$pdf->beginStructElem(role: 'BlockQuote', pid: $pid2);
$pdf->addTextCell(
'"Tagged PDF is the foundation of accessible PDF. '
. 'Without a well-formed structure tree, automated tools and assistive '
. 'technologies cannot reliably interpret document content."',
$pid2,
$leftMargin,
52.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
$setFont($pdf, 'helvetica', 'I', 8);
$pdf->beginStructElem(role: 'Caption', pid: $pid2);
$pdf->addTextCell(
'— ISO 14289-1:2012 (PDF/UA-1) rationale',
$pid2,
$leftMargin,
72.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
);
$pdf->endStructElem();
// Multi-paragraph body section
$setFont($pdf, 'helvetica', '', 10);
$paras = [
'Each call to endStructElem() closes the innermost open bracket. '
. 'Completed elements keep their nested parent-child relationships, so '
. 'the emitted structure tree follows the bracket hierarchy and preserves '
. 'the reading order of nested content blocks.',
'A structure bracket that receives no tagged content (i.e. no addTextCell '
. 'call between beginStructElem and endStructElem) is silently discarded. '
. 'This prevents empty structure nodes from appearing in the tree.',
'Pages 3 and 4 of this document demonstrate the remaining role categories: '
. 'grouping containers (Sect, Art), list structure (L, LI, Lbl, LBody), '
. 'and table structure (Table, TR, TH, TD) — each requiring multi-level '
. 'nested brackets that the library resolves at getOutPDFString() time.',
];
$yPos = 85.0;
foreach ($paras as $para) {
$pdf->beginStructElem(role: 'P', pid: $pid2);
$pdf->addTextCell(
$para,
$pid2,
$leftMargin,
$yPos,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
$yPos += 25.0;
}
// -----------------------------------------------------------------------
// Page 3 — Sect/Art grouping containers + List structure
// -----------------------------------------------------------------------
$page3 = $pdf->addPage();
$pid3 = $page3['pid'];
$addDecorativeSeparator($pdf, $pid3, 16.0);
$setFont($pdf, 'helvetica', 'B', 16);
$pdf->beginStructElem(role: 'H2', pid: $pid3);
$pdf->addTextCell(
'Page 3 — Grouping Containers and List Structure',
$pid3,
$leftMargin,
20.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
// ── Sect wrapping a sub-section (H3 + P inside a Sect container) ──────
$setFont($pdf, 'helvetica', '', 10);
$pdf->beginStructElem(role: 'Sect', pid: $pid3);
$setFont($pdf, 'helvetica', 'B', 13);
$pdf->beginStructElem(role: 'H3', pid: $pid3);
$pdf->addTextCell(
'3.1 — Sect Container',
$pid3,
$leftMargin,
34.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
$setFont($pdf, 'helvetica', '', 10);
$pdf->beginStructElem(role: 'P', pid: $pid3);
$pdf->addTextCell(
'The Sect (Section) role is a grouping container: it contributes no '
. 'content of its own but wraps related blocks — here an H3 and this P '
. 'paragraph — into a single logical section node in the structure tree. '
. 'Screen readers and PDF validators see the H3 + P as children of the Sect.',
$pid3,
$leftMargin,
43.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
$pdf->endStructElem(); // Sect
// ── Art container ─────────────────────────────────────────────────────
$pdf->beginStructElem(role: 'Art', pid: $pid3);
$setFont($pdf, 'helvetica', 'B', 13);
$pdf->beginStructElem(role: 'H3', pid: $pid3);
$pdf->addTextCell(
'3.2 — Art Container',
$pid3,
$leftMargin,
65.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
$setFont($pdf, 'helvetica', '', 10);
$pdf->beginStructElem(role: 'P', pid: $pid3);
$pdf->addTextCell(
'The Art (Article) role marks a self-contained composition: its content '
. 'could stand alone as a discrete piece. It is distinguished from Sect '
. 'in that Sect groups thematically related material within a larger work, '
. 'while Art denotes an independently distributable unit.',
$pid3,
$leftMargin,
74.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
$pdf->endStructElem(); // Art
// ── List: L > LI > {Lbl + LBody} ─────────────────────────────────────
$setFont($pdf, 'helvetica', 'B', 13);
$pdf->beginStructElem(role: 'H3', pid: $pid3);
$pdf->addTextCell(
'3.3 — List Structure (L > LI > Lbl + LBody)',
$pid3,
$leftMargin,
97.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
$setFont($pdf, 'helvetica', '', 10);
$pdf->beginStructElem(role: 'P', pid: $pid3);
$pdf->addTextCell(
'Each list item (LI) contains a label (Lbl) and a body (LBody). '
. 'The outer L bracket wraps all items into one logical list node.',
$pid3,
$leftMargin,
107.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
$listItems = [
['Headings (H1H6)', 'Define the document outline navigated by screen readers.'],
['Body roles (P, Note, Blockquote)', 'Carry prose content at the block level.'],
['Grouping roles (Sect, Art, Part)', 'Wrap related blocks without adding visible content.'],
['List roles (L, LI, Lbl, LBody)', 'Express enumerated or bulleted list structure.'],
['Table roles (Table, TR, TH, TD)', 'Capture row/column relationships in data tables.'],
];
$pdf->beginStructElem(role: 'L', pid: $pid3);
$itemY = 122.0;
foreach ($listItems as [$label, $body]) {
$pdf->beginStructElem(role: 'LI', pid: $pid3);
$setFont($pdf, 'helvetica', 'B', 10);
$pdf->beginStructElem(role: 'Lbl', pid: $pid3);
$pdf->addTextCell(
'• ' . $label,
$pid3,
$leftMargin,
$itemY,
65.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem(); // Lbl
$setFont($pdf, 'helvetica', '', 10);
$pdf->beginStructElem(role: 'LBody', pid: $pid3);
$pdf->addTextCell(
$body,
$pid3,
$leftMargin + 65.0,
$itemY,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem(); // LBody
$pdf->endStructElem(); // LI
$itemY += 8.0;
}
$pdf->endStructElem(); // L
// -----------------------------------------------------------------------
// Page 4 — Table nesting + Figure with /Alt alternate description
// -----------------------------------------------------------------------
$page4 = $pdf->addPage();
$pid4 = $page4['pid'];
$addDecorativeSeparator($pdf, $pid4, 16.0);
$setFont($pdf, 'helvetica', 'B', 16);
$pdf->beginStructElem(role: 'H2', pid: $pid4);
$pdf->addTextCell(
'Page 4 — Table Structure and Figure with Alt-Text',
$pid4,
$leftMargin,
20.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
// ── Table ─────────────────────────────────────────────────────────────
$setFont($pdf, 'helvetica', '', 10);
$pdf->beginStructElem(role: 'P', pid: $pid4);
$pdf->addTextCell(
'The Table role wraps a complete data table. Each row is a TR; header '
. 'cells are TH; data cells are TD. All brackets must be closed '
. 'inside-out before moving to the next row.',
$pid4,
$leftMargin,
32.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
// Column geometry (three columns)
$colX = [$leftMargin, $leftMargin + 55.0, $leftMargin + 120.0];
$colW = [55.0, 65.0, 60.0];
$rowH = 8.0;
$tableY = 52.0;
$tableHeaders = ['Structure Role', 'Category', 'Nesting context'];
$tableHeaderIds = ['th-role', 'th-category', 'th-context'];
$tableRows = [
['H1H6', 'Heading', 'Document / Sect / Art'],
['P', 'Body', 'Document / Sect / Art'],
['Note, Blockquote', 'Body', 'Document / Sect / Art'],
['L', 'List', 'Document / Sect'],
['LI', 'List item', 'Inside L'],
['Lbl / LBody', 'List parts', 'Inside LI'],
['Table', 'Table', 'Document / Sect'],
['TR', 'Table row', 'Inside Table'],
['TH / TD', 'Table cell', 'Inside TR'],
['Sect / Art / Part', 'Container', 'Document-level'],
['Figure', 'Graphic', 'Document / Sect'],
['Caption', 'Caption', 'After Figure'],
];
$pdf->beginStructElem(role: 'Table', pid: $pid4);
// Header row
$pdf->beginStructElem(role: 'TR', pid: $pid4);
foreach ($tableHeaders as $idx => $hdr) {
$setFont($pdf, 'helvetica', 'B', 9);
$pdf->beginStructElem(role: 'TH', pid: $pid4, alt: null, attr: [
'O' => 'Table',
'ID' => $tableHeaderIds[$idx],
'Scope' => 'Column',
]);
$pdf->addTextCell(
$hdr,
$pid4,
$colX[$idx],
$tableY,
$colW[$idx],
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem(); // TH
}
$pdf->endStructElem(); // TR (header)
// Data rows
foreach ($tableRows as $row) {
$tableY += $rowH;
$pdf->beginStructElem(role: 'TR', pid: $pid4);
foreach ($row as $idx => $cell) {
$setFont($pdf, 'helvetica', '', 9);
$pdf->beginStructElem(role: 'TD', pid: $pid4, alt: null, attr: [
'O' => 'Table',
'Headers' => $tableHeaderIds[$idx],
]);
$pdf->addTextCell(
$cell,
$pid4,
$colX[$idx],
$tableY,
$colW[$idx],
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem(); // TD
}
$pdf->endStructElem(); // TR
}
$pdf->endStructElem(); // Table
// ── Figure with /Alt alternate description ────────────────────────────
$figY = $tableY + 18.0;
$setFont($pdf, 'helvetica', 'B', 13);
$pdf->beginStructElem(role: 'H3', pid: $pid4);
$pdf->addTextCell(
'4.1 — Figure with /Alt Entry',
$pid4,
$leftMargin,
$figY,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
);
$pdf->endStructElem();
$figY += 10.0;
$setFont($pdf, 'helvetica', '', 10);
$pdf->beginStructElem(role: 'P', pid: $pid4);
$pdf->addTextCell(
'For non-text graphics, use addTaggedFigureContent() and pass an /Alt '
. 'description for accessibility. The string is written into the Figure '
. 'structure-element dictionary and read aloud by screen readers when '
. 'the graphical content cannot be represented as plain text.',
$pid4,
$leftMargin,
$figY,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
);
$pdf->endStructElem();
$figY += 22.0;
// Draw a simple placeholder rectangle for the figure.
$figStyle = [
'all' => [
'lineWidth' => 0.5,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#4a7c59',
'fillColor' => '#d6ead9',
],
];
$pdf->addTaggedFigureContent(
$pdf->graph->getStartTransform() . $pdf->graph->getRect($leftMargin, $figY, 180.0, 22.0, 'DF', $figStyle)
. $pdf->graph->getStopTransform(),
$pid4,
'Horizontal bar chart illustrating the relative nesting depth of each PDF structure role category',
[
$pdf->toPoints($leftMargin),
$pdf->toPoints($page4['height'] - $figY - 22.0),
$pdf->toPoints($leftMargin + 180.0),
$pdf->toPoints($page4['height'] - $figY),
],
);
$pdf->page->addContent($pdf->color->getPdfColor('black'), $pid4);
$setFont($pdf, 'helvetica', 'I', 8);
$pdf->beginStructElem(role: 'Caption', pid: $pid4);
$pdf->addTextCell(
'Figure 2 — Placeholder for a bar chart; /Alt text is written to the Figure struct-element dictionary',
$pid4,
$leftMargin,
$figY + 24.0,
0.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
);
$pdf->endStructElem();
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,337 @@
<?php
/**
* E064_custom_hyphenation_dictionary.php
*
* @since 2026-05-01
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Demonstrate external TeX hyphenation pattern loading via
* loadTexHyphenPatterns() + setTexHyphenPatterns().
*
* Background
* ──────────
* tc-lib-pdf inherits hyphenation support from the tc-lib-unicode library.
* By default NO hyphenation patterns are loaded; hyphenation only occurs
* when explicit soft-hyphens (&shy;) are present in the source text
* (see E028_text_hyphenation.php for that approach).
*
* When you call setTexHyphenPatterns() with a loaded pattern array, the
* library automatically inserts soft-hyphen break points based on the
* pattern rules while laying out each text run. This is especially useful
* for narrow columns, justified text, or languages with long compound words
* (German, Dutch, Finnish, etc.) where manual soft-hyphen insertion would
* be impractical.
*
* API
* ───
* loadTexHyphenPatterns(string $file): array<string,string>
* Parses a TeX .tex pattern file (the standard format used by CTAN
* packages such as hyph-utf8) and returns a pattern array.
* The file must contain a \patterns{ ... } block.
*
* setTexHyphenPatterns(array $patterns): void
* Installs the pattern array as the active hyphenation dictionary.
* Pass an empty array [] to disable automatic hyphenation.
*
* Full pattern files for ~100 languages are available from CTAN:
* https://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/tex
*
* This example uses a small Dutch sample pattern file bundled with the
* examples to keep the demo self-contained. The same technique works with
* any complete CTAN TeX pattern file.
*
* Comparison layout
* ─────────────────
* The page shows the same Dutch paragraph rendered twice side by side:
* Left column — NO patterns loaded (only explicit &shy; break points)
* Right column — Dutch sample patterns loaded via loadTexHyphenPatterns()
*
* Dutch is a good demonstration language because it has many long compound
* words (e.g. "woordenschat", "zelfstandigheid", "werkgelegenheid") that
* benefit greatly from automatic hyphenation in narrow columns.
*/
// Path to the bundled sample Dutch TeX pattern file.
$patternFile = \realpath(__DIR__ . '/data/hyph-nl-sample.tex');
if ($patternFile === false) {
throw new \RuntimeException('Missing pattern file: examples/data/hyph-nl-sample.tex');
}
// -----------------------------------------------------------------------
// Common document setup
// -----------------------------------------------------------------------
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 064');
$pdf->setTitle('Custom TeX Hyphenation Dictionary');
$pdf->setKeywords('TCPDF tc-lib-pdf example hyphenation tex patterns dutch custom dictionary');
$pdf->setPDFFilename('E064_custom_hyphenation_dictionary.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// -----------------------------------------------------------------------
// Load the Dutch pattern file BEFORE generating content.
// -----------------------------------------------------------------------
// Step 1: parse the .tex file into a pattern array.
$dutchPatterns = $pdf->loadTexHyphenPatterns(file: $patternFile);
// Step 2: install as the active dictionary.
$pdf->setTexHyphenPatterns(patterns: $dutchPatterns);
// -----------------------------------------------------------------------
// Fonts
// -----------------------------------------------------------------------
$fontTitle = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
$fontH2 = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 11);
$fontBody = $pdf->font->insert($pdf->pon, 'times', '', 9);
$fontCode = $pdf->font->insert($pdf->pon, 'courier', '', 8);
$fontSmall = $pdf->font->insert($pdf->pon, 'helvetica', '', 8);
// -----------------------------------------------------------------------
// Dutch sample text — a paragraph with long compound words.
// -----------------------------------------------------------------------
$dutchPara =
'De Nederlandse taal staat bekend om zijn lange samenstellingen. '
. 'Woorden zoals woordenschatontwikkeling, zelfstandigheidsbewegingen, '
. 'werkgelegenheidsbeleid, verantwoordelijkheidsgevoelens, '
. 'maatschappelijkontwikkelingswerk en informatietechnologieonderwijs '
. 'zijn perfecte voorbeelden van samenstellingen die automatische '
. 'woordafbreking vereisen voor een goede leesbaarheid in smalle kolommen. '
. 'Zonder patronen worden zulke woorden niet afgebroken en loopt de tekst '
. 'over de kolomrand of ontstaan er grote witruimten bij uitgevulde tekst. '
. 'Met de juiste TeX-patronen worden breekpunten automatisch ingevoegd '
. 'op de linguistisch correcte syllabegrenzen van elk woord.';
// -----------------------------------------------------------------------
// Page 1 — Side-by-side comparison
// -----------------------------------------------------------------------
$page1 = $pdf->addPage();
// --- Title ---
$pdf->page->addContent($fontTitle['out']);
$pdf->page->addContent($pdf->getTextCell(
'Custom TeX Hyphenation Dictionary',
15.0,
18.0,
180.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($fontSmall['out']);
$pdf->page->addContent($pdf->getTextCell(
'loadTexHyphenPatterns() + setTexHyphenPatterns() — Dutch sample patterns vs no patterns',
15.0,
27.0,
180.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// --- Column headers ---
$pdf->page->addContent($fontH2['out']);
$pdf->page->addContent($pdf->getTextCell(
'Without patterns',
15.0,
37.0,
87.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
$pdf->page->addContent($pdf->getTextCell(
'With Dutch patterns',
108.0,
37.0,
87.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
// Separator line between header and body
$sepStyle = [
'lineWidth' => 0.3,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#336699',
'fillColor' => '',
];
$pdf->page->addContent($pdf->graph->getLine(15.0, 43.5, 195.0, 43.5, $sepStyle));
// Vertical divider between columns
$divStyle = [
'lineWidth' => 0.2,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#aaaaaa',
'fillColor' => '',
];
$pdf->page->addContent($pdf->graph->getLine(103.0, 44.0, 103.0, 200.0, $divStyle));
// -----------------------------------------------------------------------
// Left column — no automatic hyphenation
// -----------------------------------------------------------------------
// Temporarily disable patterns.
$pdf->setTexHyphenPatterns(patterns: []);
$leftHtml = '<div style="text-align:justify; font-size:9pt; color:#222222;">' . $dutchPara . '</div>';
// Re-insert non-bold font before left column addHTMLCell.
$pdf->font->insert($pdf->pon, 'helvetica', '', 9);
$pdf->addHTMLCell(html: $leftHtml, posx: 15.0, posy: 45.0, width: 87.0);
// -----------------------------------------------------------------------
// Right column — Dutch patterns active
// -----------------------------------------------------------------------
// Re-enable Dutch patterns.
$pdf->setTexHyphenPatterns(patterns: $dutchPatterns);
$rightHtml = '<div style="text-align:justify; font-size:9pt; color:#222222;">' . $dutchPara . '</div>';
// Reset font stack to non-bold before second addHTMLCell.
$pdf->font->insert($pdf->pon, 'helvetica', '', 9);
$pdf->addHTMLCell(html: $rightHtml, posx: 108.0, posy: 45.0, width: 87.0);
// -----------------------------------------------------------------------
// Pattern statistics block (below columns)
// -----------------------------------------------------------------------
$pdf->page->addContent($fontSmall['out']);
$patternCount = \count($dutchPatterns);
$pdf->page->addContent($pdf->getTextCell(
"Pattern file: examples/data/hyph-nl-sample.tex | Patterns loaded: {$patternCount}"
. ' | Source: CTAN hyph-utf8 (excerpt)',
15.0,
205.0,
180.0,
0.0,
drawcell: false,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
));
// -----------------------------------------------------------------------
// Page 2 — API reference and usage guidance
// -----------------------------------------------------------------------
$page2 = $pdf->addPage();
// Re-insert non-bold base font so the font stack is clean before addHTMLCell.
$pdf->font->insert($pdf->pon, 'helvetica', '', 8);
$html2 = <<<'HTML'
<h1 style="font-size:14pt; color:#003366;">API Reference and Usage Guidance</h1>
<h2 style="font-size:11pt; color:#005599;">loadTexHyphenPatterns(string $file): array</h2>
<p style="font-size:9pt; color:#333333;">
Reads a TeX hyphenation pattern file and returns an associative array mapping
pattern keys to their weighted strings. The file must contain a
<code>\patterns{ ... }</code> block in standard TeX format. Comments
(lines starting with <code>%</code>) are stripped before parsing.
</p>
<p style="font-size:9pt; color:#333333;">
The returned array can be stored and reused across multiple PDF objects
without re-reading the file:
</p>
<pre style="font-size:8pt; color:#003366; background:#f0f4ff;">
$patterns = $pdf->loadTexHyphenPatterns(lang: '/path/to/hyph-en-us.tex');
$pdf->setTexHyphenPatterns(lang: $patterns);
</pre>
<h2 style="font-size:11pt; color:#005599;">setTexHyphenPatterns(array $patterns): void</h2>
<p style="font-size:9pt; color:#333333;">
Installs the pattern array as the active hyphenation dictionary. Hyphenation
applies to all subsequent text layout calls. Pass an empty array
<code>[]</code> to disable automatic hyphenation.
</p>
<h2 style="font-size:11pt; color:#005599;">Obtaining pattern files</h2>
<p style="font-size:9pt; color:#333333;">
Full-quality pattern files for approximately 100 languages are available from
the CTAN hyph-utf8 package:<br />
<b>https://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/tex</b><br /><br />
Notable files:
</p>
<table border="0" cellpadding="2">
<tr><td style="font-size:8pt; width:55mm; color:#003366; font-weight:bold;">File</td>
<td style="font-size:8pt; color:#003366; font-weight:bold;">Language</td></tr>
<tr><td style="font-size:8pt;">hyph-en-us.tex</td><td style="font-size:8pt;">English (US)</td></tr>
<tr><td style="font-size:8pt;">hyph-de-1996.tex</td><td style="font-size:8pt;">German (1996 reform)</td></tr>
<tr><td style="font-size:8pt;">hyph-nl.tex</td><td style="font-size:8pt;">Dutch</td></tr>
<tr><td style="font-size:8pt;">hyph-fr.tex</td><td style="font-size:8pt;">French</td></tr>
<tr><td style="font-size:8pt;">hyph-pl.tex</td><td style="font-size:8pt;">Polish</td></tr>
<tr><td style="font-size:8pt;">hyph-fi.tex</td><td style="font-size:8pt;">Finnish</td></tr>
<tr><td style="font-size:8pt;">hyph-pt.tex</td><td style="font-size:8pt;">Portuguese</td></tr>
<tr><td style="font-size:8pt;">hyph-sv.tex</td><td style="font-size:8pt;">Swedish</td></tr>
</table>
<h2 style="font-size:11pt; color:#005599;">Soft hyphens vs automatic patterns — when to use each</h2>
<p style="font-size:9pt; color:#333333;">
<b>&amp;shy; (soft hyphens in HTML)</b> — best for single documents with known content
where exact break points matter (e.g. trade-marked compound names, proper nouns
that patterns would get wrong). See E028_text_hyphenation.php.<br /><br />
<b>setTexHyphenPatterns()</b> — best for documents with large amounts of flowing
text where manual annotation is impractical (articles, books, reports). Patterns
are applied automatically to every word during layout without modifying the source
string.
</p>
<h2 style="font-size:11pt; color:#005599;">Switching languages mid-document</h2>
<p style="font-size:9pt; color:#333333;">
You can switch the active dictionary between pages or even between HTML cells
by calling <code>setTexHyphenPatterns()</code> again with a different pattern
array. Load all required pattern arrays at startup to avoid disk I/O during
layout, then swap them as needed.
</p>
HTML;
$pdf->addHTMLCell(html: $html2, posx: 15.0, posy: 20.0, width: 180.0);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,86 @@
<?php
/**
* E065_import_single_page.php
*
* @since 2026-05-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// ---- Step 1: create a source PDF to import from ----
$src = new \Com\Tecnick\Pdf\Tcpdf();
$src->setCreator('tc-lib-pdf');
$src->setAuthor('Nicola Asuni');
$src->setSubject('tc-lib-pdf example: 065 source');
$src->setTitle('Import Single Page - Source');
$src->setKeywords('TCPDF tc-lib-pdf import single page source template');
$src->setPDFFilename('065_import_single_page_src.pdf');
$bfont = $src->font->insert($src->pon, 'helvetica', '', 14);
$srcPage = $src->addPage();
$src->page->addContent($bfont['out']);
$src->addHTMLCell(html: '<h1>Source document</h1><p>This page will be imported.</p>', posx: 15, posy: 20, width: 160);
$sourcePdfData = $src->getOutPDFString();
// ---- Step 2: create a new document and import the source page ----
$pdf = new \Com\Tecnick\Pdf\Tcpdf();
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 065');
$pdf->setTitle('Import Single Page - Destination');
$pdf->setKeywords('TCPDF tc-lib-pdf import single page destination cropbox template');
$pdf->setPDFFilename('065_import_single_page.pdf');
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
// Register the source document from its raw bytes.
$sourceId = $pdf->setImportSourceData($sourcePdfData);
// Count pages available in the source.
$pageCount = $pdf->getSourcePageCount($sourceId);
// Import page 1 as a reusable Form XObject template.
$tpl = $pdf->importPage(sourceId: $sourceId, pageNum: 1);
// Add a new page to the destination document.
$page = $pdf->addPage();
$pdf->page->addContent($bfont['out']);
// Place the imported page as a thumbnail (100 mm wide) at (20, 20).
// The height is computed automatically to preserve the aspect ratio.
$placed = $pdf->useImportedPage(tpl: $tpl, xpos: 20, ypos: 20, width: 140);
// Add an annotation below the imported page.
$pdf->addHTMLCell(
html: '<p>Imported ' . $pageCount . ' page(s). Placed page 1 above (scaled).</p>',
posx: 15,
posy: (float) $placed['y'] + (float) $placed['height'] + 5,
width: 160,
);
// Get and render the PDF content.
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,105 @@
<?php
/**
* E066_import_document_append.php
*
* @since 2026-05-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// ---- Step 1: build a multi-page source PDF ----
$src = new \Com\Tecnick\Pdf\Tcpdf();
$src->setCreator('tc-lib-pdf');
$src->setAuthor('Nicola Asuni');
$src->setSubject('tc-lib-pdf example: 066 source');
$src->setTitle('Import Document Append - Source');
$src->setKeywords('TCPDF tc-lib-pdf import document append source template');
$src->setPDFFilename('066_import_document_append_src.pdf');
$bfont = $src->font->insert($src->pon, 'helvetica', '', 14);
$pages = ['First page', 'Second page', 'Third page'];
foreach ($pages as $idx => $label) {
$srcPage = $src->addPage();
$src->page->addContent($bfont['out']);
$src->addHTMLCell(
html: '<h2>Source: '
. \htmlspecialchars($label)
. '</h2>'
. '<p>Page '
. ($idx + 1)
. ' of '
. \count($pages)
. ' in the source document.</p>',
posx: 15,
posy: 20,
width: 160,
);
}
$sourcePdfData = $src->getOutPDFString();
// ---- Step 2: create destination document with an intro page ----
$pdf = new \Com\Tecnick\Pdf\Tcpdf();
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 066');
$pdf->setTitle('Import Document Append - Destination');
$pdf->setKeywords('TCPDF tc-lib-pdf import append merge destination template');
$pdf->setPDFFilename('066_import_document_append.pdf');
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->addHTMLCell(
html: '<h1>Document append demo</h1>' . '<p>The following pages are imported from a separate source document.</p>',
posx: 15,
posy: 20,
width: 160,
);
// ---- Step 3: register source and append all its pages ----
$sourceId = $pdf->setImportSourceData($sourcePdfData);
$pageCount = $pdf->getSourcePageCount($sourceId);
// appendDocument adds one new destination page per source page and places the template.
$templates = $pdf->appendDocument($sourceId);
// ---- Step 4: add a closing page with a summary ----
$pdf->addPage();
$pdf->page->addContent($bfont['out']);
$pdf->addHTMLCell(
html: '<p>Appended ' . \count($templates) . ' page(s) from source (' . $pageCount . ' available).</p>',
posx: 15,
posy: 20,
width: 160,
);
// Render
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,153 @@
<?php
/**
* E067_import_page_region_nup.php
*
* @since 2026-05-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// ---- Step 1: build a 4-page source document ----
$src = new \Com\Tecnick\Pdf\Tcpdf();
$src->setCreator('tc-lib-pdf');
$src->setAuthor('Nicola Asuni');
$src->setSubject('tc-lib-pdf example: 067 source');
$src->setTitle('Import Page Region N-up - Source');
$src->setKeywords('TCPDF tc-lib-pdf import page region nup source template');
$src->setPDFFilename('067_import_page_region_nup_src.pdf');
$srcFont = $src->font->insert($src->pon, 'helvetica', '', 14);
$cards = [
['title' => 'Card A', 'color' => '#ffd7b5', 'text' => 'Quarterly sales summary'],
['title' => 'Card B', 'color' => '#b8f2e6', 'text' => 'Regional performance map'],
['title' => 'Card C', 'color' => '#cde7ff', 'text' => 'Customer retention trends'],
['title' => 'Card D', 'color' => '#f3d9fa', 'text' => 'Fulfillment and logistics KPIs'],
];
foreach ($cards as $idx => $card) {
$src->addPage();
// Colored background fill.
$bgStyle = [
'all' => [
'lineWidth' => 0,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => $card['color'],
'fillColor' => $card['color'],
],
];
$src->page->addContent($src->graph->getRect(0, 0, 210, 297, 'F', $bgStyle));
// Render page content.
$src->page->addContent($srcFont['out']);
$src->addHTMLCell(
html: '<h2>'
. \htmlspecialchars($card['title'])
. '</h2>'
. '<p>'
. \htmlspecialchars($card['text'])
. '</p>'
. '<p>Page '
. ($idx + 1)
. ' of '
. \count($cards)
. '</p>',
posx: 20,
posy: 40,
width: 170,
);
}
$sourcePdfData = $src->getOutPDFString();
// ---- Step 2: import and compose a 2x2 N-up destination page ----
$pdf = new \Com\Tecnick\Pdf\Tcpdf();
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 067');
$pdf->setTitle('Import Page Region N-up - Destination');
$pdf->setKeywords('TCPDF tc-lib-pdf import page region nup destination imposition');
$pdf->setPDFFilename('067_import_page_region_nup.pdf');
$labelFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$sourceId = $pdf->setImportSourceData($sourcePdfData);
$pdf->addPage();
$pdf->page->addContent($labelFont['out']);
$margin = 12.0;
$gap = 6.0;
$gridWidth = 210.0 - (2 * $margin) - $gap;
$gridHeight = 297.0 - (2 * $margin) - $gap;
$cellWidth = $gridWidth / 2.0;
$cellHeight = $gridHeight / 2.0;
$positions = [
[$margin, $margin],
[$margin + $cellWidth + $gap, $margin],
[$margin, $margin + $cellHeight + $gap],
[$margin + $cellWidth + $gap, $margin + $cellHeight + $gap],
];
$borderStyle = [
'all' => [
'lineWidth' => 0.3,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#404040',
'fillColor' => '',
],
];
foreach ($positions as $idx => [$x, $y]) {
$tpl = $pdf->importPage(sourceId: $sourceId, pageNum: $idx + 1, options: ['box' => 'CropBox', 'cache' => true]);
$pdf->useImportedPage(tpl: $tpl, xpos: $x, ypos: $y, width: $cellWidth, height: $cellHeight, options: [
'keepAspectRatio' => true,
'align' => 'CC',
'clip' => true,
]);
// Draw outer cell frame over the imported content.
$pdf->page->addContent($pdf->graph->getRect($x, $y, $cellWidth, $cellHeight, 'D', $borderStyle));
$pdf->page->addContent($labelFont['out']);
$pdf->addHTMLCell(
html: '<span style="font-size:9px">Imported page ' . ($idx + 1) . '</span>',
posx: $x + 2,
posy: $y + 2,
width: 40,
);
}
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,89 @@
<?php
/**
* E068_font_subset.php
*
* @since 2026-05-04
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// main TCPDF object with font subsetting enabled
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: true,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 068');
$pdf->setTitle('Font Subsetting Example');
$pdf->setKeywords('TCPDF tc-lib-pdf example font subsetting');
$pdf->setPDFFilename('068_font_subset.pdf');
// Insert DejaVu Sans font for subsetting test
$bfont = $pdf->font->insert($pdf->pon, 'dejavusans', '', 12);
// Add first page
$page = $pdf->addPage();
// Add font to the page
$pdf->page->addContent($bfont['out']);
// HTML content demonstrating varied character sets for subsetting
$html = <<<'EOD'
<h1>Font Subsetting Example</h1>
<p>This document demonstrates <b>font subsetting</b> with DejaVu Sans font.</p>
<p>Font subsetting reduces the file size by including only the characters
that are actually used in the document, rather than embedding the entire font.</p>
<h2>Character Sets</h2>
<p><b>English:</b> THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG - the quick brown fox jumps over the lazy dog.</p>
<p><b>Numbers:</b> 0123456789 · π ≈ 3.14159</p>
<p><b>Punctuation:</b> !@#$%^&*()[]{}–—«»‹›„“”‚‘’"'</p>
<p><b>Symbols:</b> © ® ™ € £ ¥ ¢ § ¶ • ‰</p>
<h2>Benefits of Font Subsetting</h2>
<ul>
<li>Reduced PDF file size</li>
<li>Faster document transmission</li>
<li>Only embedded characters are included</li>
<li>Maintains font fidelity for used characters</li>
</ul>
<h2>Text Samples</h2>
<p>Regular text in DejaVu Sans font for testing character coverage.</p>
<p><b>Bold text</b> is also included in the subset.</p>
EOD;
// render the HTML content
$pdf->addHTMLCell(html: $html, posx: 15, posy: 15, width: 180);
// Get the PDF content
$rawpdf = $pdf->getOutPDFString();
// Render the PDF content
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,216 @@
<?php
/**
* E069_html_line_height.php
*
* @since 2026-04-27
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
// autoloader when using RPM or DEB package installation
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
// main TCPDF object
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
// ----------
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 069');
$pdf->setTitle('HTML Line Height');
$pdf->setKeywords('TCPDF tc-lib-pdf html css line-height typography spacing');
$pdf->setPDFFilename('069_html_line_height.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
// Insert DejaVu Sans font for subsetting test
$bfont = $pdf->font->insert($pdf->pon, 'dejavusans', '', 12);
// Add first page
$page = $pdf->addPage();
// Add font to the page
$pdf->page->addContent($bfont['out']);
$html = <<<EOF
<h1>Line-Height CSS Tests</h1>
<h2>1) Line-height with percentages</h2>
<p style="line-height: 100%;">
Line-height: 100%<br />
This paragraph uses a line-height of 100%, which is the same as the font size.
Each line should be tightly spaced without extra vertical space.
This is useful when you want compact text layout.
</p>
<p style="line-height: 150%;">
Line-height: 150%<br />
This paragraph uses a line-height of 150%, which adds moderate spacing between lines.
The lines are more readable and have better visual separation.
This is a common default setting for body text in many documents.
</p>
<p style="line-height: 200%;">
Line-height: 200%<br />
This paragraph uses a line-height of 200%, which doubles the font size for vertical spacing.
The lines have significant space between them, making the text very readable.
This is often used for accessibility or when line spacing is deliberately increased.
</p>
<h2>2) Line-height with absolute units</h2>
<p style="line-height: 10pt;">
Line-height: 10pt<br />
This paragraph uses an absolute line-height of 10 points.
The spacing between lines is fixed at exactly 10 points.
The font size remains 10pt, so this creates tightly packed lines.
</p>
<p style="line-height: 15pt;">
Line-height: 15pt<br />
This paragraph uses an absolute line-height of 15 points.
Each line has exactly 15 points of vertical space allocated.
This provides moderate spacing for improved readability.
</p>
<p style="line-height: 20pt;">
Line-height: 20pt<br />
This paragraph uses an absolute line-height of 20 points.
The lines are well-spaced with 20 points of vertical space each.
This creates a more spacious and airy appearance in the document.
</p>
<h2>3) Line-height with relative units (em)</h2>
<p style="line-height: 0.8em;">
Line-height: 0.8em<br />
This paragraph uses a line-height of 0.8em, which is 80% of the current font size.
The lines are compressed together with minimal vertical space.
This is useful for creating a compact visual effect.
</p>
<p style="line-height: 1.2em;">
Line-height: 1.2em<br />
This paragraph uses a line-height of 1.2em, which is 120% of the current font size.
The lines have a balanced amount of spacing for good readability.
This is similar to using a 120% line-height percentage.
</p>
<p style="line-height: 2em;">
Line-height: 2em<br />
This paragraph uses a line-height of 2em, which is 200% of the current font size.
The lines are widely separated with generous vertical spacing.
This creates a very open and spacious text layout.
</p>
<h2>4) Line-height with unitless numbers</h2>
<p style="line-height: 0.9;">
Line-height: 0.9 (unitless)<br />
This paragraph uses a unitless line-height value of 0.9.
The multiplier is applied to the font size (0.9 × 10pt = 9pt).
This creates compact line spacing.
</p>
<p style="line-height: 1.5;">
Line-height: 1.5 (unitless)<br />
This paragraph uses a unitless line-height value of 1.5.
The multiplier is applied to the font size (1.5 × 10pt = 15pt).
This is a very popular setting for comfortable reading.
The spacing is proportional to the font size.
</p>
<p style="line-height: 2.5;">
Line-height: 2.5 (unitless)<br />
This paragraph uses a unitless line-height value of 2.5.
The multiplier is applied to the font size (2.5 × 10pt = 25pt).
The lines are very widely spaced and easy to read.
This is often used for accessibility requirements.
</p>
<h2>5) Line-height with millimeters</h2>
<p style="line-height: 5mm;">
Line-height: 5mm<br />
This paragraph uses an absolute line-height of 5 millimeters.
The vertical spacing between lines is exactly 5mm.
Different units like millimeters are sometimes used in print design.
</p>
<p style="line-height: 8mm;">
Line-height: 8mm<br />
This paragraph uses an absolute line-height of 8 millimeters.
The increased spacing makes the text more readable and open.
Millimeters are useful for precise layout control in documents.
</p>
<h2>6) Comparison: normal vs specific values</h2>
<p style="line-height: normal;">
Line-height: normal<br />
This paragraph uses the default line-height value (typically around 1.0-1.2).
The spacing is determined by the browser or PDF renderer default.
This is the baseline for comparison with other values.
</p>
<p style="line-height: 1;">
Line-height: 1 (unitless)<br />
This paragraph uses a unitless line-height of exactly 1.
The vertical space is the same as the font size.
This creates tightly spaced lines with minimal separation.
</p>
<div style="page-break-before:always;font-size:5mm;">
<p style="color:black;">(1) default<br/>line<br/>height</p>
<p style="color:grey; line-height:normal;">(2) normal<br/>line<br/>height</p>
<p style="color:orange; line-height:100%;">(3) 100%<br/>line<br/>height</p>
<p style="color:red; line-height:50%;">(4) 50%<br/>line<br/>height</p>
<p style="color:green; line-height:150%;">(5) 150%<br/>line<br/>height</p>
<p style="color:blue; line-height:10mm;">(6) 10mm<br/>line<br/>height</p>
<p style="color:violet; line-height:20mm;">(7) 20mm<br/>line<br/>height</p>
</div>
<div style="page-break-before:always;font-size:20mm;">
<div style="line-height:20mm; color:green;">check<br/>line<br/>after<br/>BR</div>
<div style="line-height:20mm; color:red;">check<br/>line<br/>after<div>DIV</div></div>
</div>
EOF;
$pdf->addHTMLCell(html: $html, posx: 20, posy: 10, width: 150);
// ----------
// get PDF document as raw string
$rawpdf = $pdf->getOutPDFString();
// Various output modes:
//$pdf->savePDF(\dirname(__DIR__).'/target', $rawpdf);
$pdf->renderPDF(rawpdf: $rawpdf);
//$pdf->downloadPDF($rawpdf);
//echo $pdf->getMIMEAttachmentPDF($rawpdf);
@@ -0,0 +1,195 @@
<?php
/**
* E070_default_page_content_import.php
*
* @since 2026-05-05
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
/**
* Custom PDF class that imports and reuses a source page as default page content.
*/
class PdfWithImportedDefaultPage extends \Com\Tecnick\Pdf\Tcpdf
{
/** @var ?\Com\Tecnick\Pdf\Import\PageTemplateInterface */
private $defaultTpl = null;
/**
* Register source data and pick the page template to be used as default content.
*
* This uses the same import flow as E065:
* - setImportSourceData(...)
* - importPage(...)
*
* @param string $sourcePdfData Raw source PDF bytes.
* @param int $sourcePageNum 1-based source page number to import.
*/
public function setDefaultImportedPage(string $sourcePdfData, int $sourcePageNum = 1): void
{
$sourceId = $this->setImportSourceData($sourcePdfData);
$pageCount = $this->getSourcePageCount($sourceId);
if ($sourcePageNum < 1 || $sourcePageNum > $pageCount) {
throw new \InvalidArgumentException('Requested source page is out of range. Available pages: '
. $pageCount);
}
$this->defaultTpl = $this->importPage(sourceId: $sourceId, pageNum: $sourcePageNum, options: [
'box' => 'CropBox',
'cache' => true,
]);
}
/**
* Place the imported template on every new page.
*
* @param int $pid Page index (0-based).
*
* @return string Empty stream; content is added directly by useImportedPage().
*/
public function defaultPageContent(int $pid = -1): string
{
if ($this->defaultTpl === null) {
return '';
}
if ($pid < 0) {
$pid = $this->page->getPageId();
}
$page = $this->page->getPage($pid);
// Fill the whole destination page with the imported source page.
$this->useImportedPage(
tpl: $this->defaultTpl,
xpos: 0.0,
ypos: 0.0,
width: (float) $page['width'],
height: (float) $page['height'],
options: [
'keepAspectRatio' => true,
'align' => 'CC',
'clip' => true,
],
);
return '';
}
}
// ---- Step 1: build a source PDF that will be imported ----
$src = new \Com\Tecnick\Pdf\Tcpdf();
$srcFont = $src->font->insert($src->pon, 'helvetica', '', 14);
$src->addPage();
$src->page->addContent($srcFont['out']);
$src->addHTMLCell(
html: '<h1>Imported default page (source)</h1>'
. '<p>This page is imported and reused as defaultPageContent() in E070.</p>',
posx: 20,
posy: 30,
width: 170,
);
$sourcePdfData = $src->getOutPDFString();
// ---- Step 2: create destination PDF using the custom subclass ----
$pdf = new PdfWithImportedDefaultPage('mm', true, false, true, '', null);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 070');
$pdf->setTitle('Default Page Content from Imported PDF');
$pdf->setKeywords('TCPDF tc-lib-pdf example import defaultPageContent template');
$pdf->setPDFFilename('070_default_page_content_import.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
// Reuse source page 1 as the default page content for all pages.
$pdf->setDefaultImportedPage($sourcePdfData, 1);
$pdf->enableDefaultPageContent();
$bodyFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$titleFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 13);
// Page 1
$pdf->addPage();
$pdf->page->addContent($titleFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Page 1 - Foreground content',
posx: 20,
posy: 85,
width: 170,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bodyFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'The page background comes from an imported PDF page loaded via setImportSourceData() and importPage(), then applied in defaultPageContent().',
posx: 20,
posy: 95,
width: 170,
height: 0,
offset: 0,
linespace: 2,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
));
// Page 2
$pdf->addPage();
$pdf->page->addContent($titleFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Page 2 - Same imported default content',
posx: 20,
posy: 85,
width: 170,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($bodyFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Because defaultPageContent() runs whenever addPage() is called, the imported source page is automatically placed on every new page.',
posx: 20,
posy: 95,
width: 170,
height: 0,
offset: 0,
linespace: 2,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
));
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,186 @@
<?php
/**
* E071_gettextcell_bbox_metrics.php
*
* @since 2026-05-06
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 071');
$pdf->setTitle('getTextCell bbox metrics');
$pdf->setKeywords('TCPDF tc-lib-pdf example getTextCell bbox textbbox cellbbox');
$pdf->setPDFFilename('071_gettextcell_bbox_metrics.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();
$baseFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$pdf->addPage();
$titleFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
$textFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$monoFont = $pdf->font->insert($pdf->pon, 'courier', '', 10);
$pdf->page->addContent($titleFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'E071: getTextCell multiline bbox metrics',
posx: 20,
posy: 20,
width: 170,
height: 0,
offset: 0,
linespace: 0,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($textFont['out']);
$blockStyles = [
'all' => [
'lineWidth' => 0.4,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#006699',
'fillColor' => '#f3f8fb',
],
];
// Custom cell arrays use internal points, so convert desired 5 mm padding first.
$padding5mm = $pdf->toPoints(5.0);
$cellPadding5mm = [
'margin' => [
'T' => 0,
'R' => 0,
'B' => 0,
'L' => 0,
],
'padding' => [
'T' => $padding5mm,
'R' => $padding5mm,
'B' => $padding5mm,
'L' => $padding5mm,
],
];
$multiline =
'Line 1: This text uses getTextCell with automatic wrapping.'
. "\n"
. 'Line 2: The border is visible so the cell bounds can be compared.'
. "\n"
. 'Line 3: Metrics printed below come from getLastBBox(), getLastTextBBox(), and getLastCellBBox().';
$pdf->page->addContent($pdf->getTextCell(
txt: $multiline,
posx: 20,
posy: 35,
width: 170,
height: 0,
offset: 0,
linespace: 1.5,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
cell: $cellPadding5mm,
styles: $blockStyles,
strokewidth: 0,
wordspacing: 0,
leading: 0,
rise: 0,
jlast: true,
fill: true,
stroke: false,
underline: false,
linethrough: false,
overline: false,
clip: false,
drawcell: true,
));
$bbox = $pdf->getLastBBox();
$textbbox = $pdf->getLastTextBBox();
$cellbbox = $pdf->getLastCellBBox();
$fmtMetric = static fn(float $value): string => \sprintf('%7.3F', $value);
$pdf->page->addContent($monoFont['out']);
$metricsText =
'bbox : x='
. $fmtMetric($bbox['x'])
. ' y='
. $fmtMetric($bbox['y'])
. ' w='
. $fmtMetric($bbox['w'])
. ' h='
. $fmtMetric($bbox['h'])
. "\n"
. 'textbbox : x='
. $fmtMetric($textbbox['x'])
. ' y='
. $fmtMetric($textbbox['y'])
. ' w='
. $fmtMetric($textbbox['w'])
. ' h='
. $fmtMetric($textbbox['h'])
. "\n"
. 'cellbbox : x='
. $fmtMetric($cellbbox['x'])
. ' y='
. $fmtMetric($cellbbox['y'])
. ' w='
. $fmtMetric($cellbbox['w'])
. ' h='
. $fmtMetric($cellbbox['h']);
$metricsPosY = $cellbbox['y'] + $cellbbox['h'] + 8;
$pdf->page->addContent($pdf->getTextCell(
txt: $metricsText,
posx: 20,
posy: $metricsPosY,
width: 170,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
@@ -0,0 +1,262 @@
<?php
/**
* E072_import_new_font.php
*
* Demonstrates a direct custom-font workflow:
* 1. Download Noto Sans Regular.
* 2. Convert it with tc-lib-pdf-font.
* 3. Start a PDF page and render text using that imported font.
*
* @since 2026-05-06
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
require __DIR__ . '/../vendor/autoload.php';
$defaultFontsDir = (string) \realpath(\dirname(__DIR__) . '/vendor/tecnickcom/tc-lib-pdf-font/target/fonts');
$sourceFontsDir = \dirname(__DIR__) . '/target/fonts/source';
$customFontsDir = \dirname(__DIR__) . '/target/fonts/custom';
$notoFileName = 'NotoSans-Regular.ttf';
$notoUrl = 'https://github.com/notofonts/noto-fonts/raw/main/hinted/ttf/NotoSans/NotoSans-Regular.ttf';
$notoSourcePath = $sourceFontsDir . '/' . $notoFileName;
$fontFamily = 'notosans';
$fontSetupError = '';
$customFontsReal = '';
/**
* Download a file from URL to local destination.
*
* @throws RuntimeException in case of download failure
*/
function downloadFile(string $url, string $destination, int $timeout = 30): void
{
$ctx = \stream_context_create([
'http' => [
'follow_location' => 1,
'max_redirects' => 5,
'timeout' => $timeout,
],
]);
$data = @\file_get_contents($url, false, $ctx);
if ($data === false) {
throw new \RuntimeException('Unable to download font from: ' . $url);
}
if (@\file_put_contents($destination, $data) === false) {
throw new \RuntimeException('Unable to save downloaded font to: ' . $destination);
}
}
try {
if (!\is_dir($sourceFontsDir)) {
if (!@\mkdir($sourceFontsDir, 0755, true) && !\is_dir($sourceFontsDir)) {
throw new \RuntimeException('Unable to create directory: ' . $sourceFontsDir);
}
}
if (!\is_dir($customFontsDir)) {
if (!@\mkdir($customFontsDir, 0755, true) && !\is_dir($customFontsDir)) {
throw new \RuntimeException('Unable to create directory: ' . $customFontsDir);
}
}
$customFontsReal = (string) \realpath($customFontsDir);
if (empty($customFontsReal)) {
throw new \RuntimeException('Unable to resolve custom fonts directory path.');
}
$convertedFontPath = $customFontsReal . '/' . $fontFamily . '.json';
// 1) Download Noto Sans Regular only when needed.
if (!\is_file($convertedFontPath) && !\is_file($notoSourcePath)) {
downloadFile($notoUrl, $notoSourcePath, 60);
}
// 2) Convert it to tc-lib-pdf-font format when not already converted.
if (!\is_file($convertedFontPath)) {
$sourceFontPath = (string) \realpath($notoSourcePath);
if (empty($sourceFontPath)) {
throw new \RuntimeException('Unable to resolve downloaded font path.');
}
try {
$import = new \Com\Tecnick\Pdf\Font\Import(
$sourceFontPath,
$customFontsReal . '/',
'TrueTypeUnicode',
'',
32,
3,
1,
false,
);
$fontFamily = $import->getFontName();
$convertedFontPath = $customFontsReal . '/' . $fontFamily . '.json';
} catch (\Throwable $e) {
$alreadyImported = \strpos($e->getMessage(), 'already imported:') !== false;
if ($alreadyImported && \preg_match('/([a-z0-9_\-]+)\.json$/i', $e->getMessage(), $match) === 1) {
$fontFamily = \strtolower((string) $match[1]);
$convertedFontPath = $customFontsReal . '/' . $fontFamily . '.json';
} else {
throw $e;
}
}
}
if (!\is_file($convertedFontPath)) {
throw new \RuntimeException('Converted font definition file not found: ' . $convertedFontPath);
}
} catch (\Throwable $e) {
$fontSetupError = $e->getMessage();
}
if (empty($fontSetupError) && !empty($customFontsReal)) {
// The HTML <pre> tag defaults to courier in this renderer.
// Copy core courier metrics into the custom font directory when missing.
$defaultCourier = $defaultFontsDir . '/core/courier.json';
$customCourier = $customFontsReal . '/courier.json';
if (\is_file($defaultCourier) && !\is_file($customCourier)) {
@\copy($defaultCourier, $customCourier);
}
}
$fontsRoot = !empty($fontSetupError) || empty($customFontsReal) ? $defaultFontsDir : $customFontsReal;
\define('K_PATH_FONTS', $fontsRoot);
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: true,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 072');
$pdf->setTitle('Import and Use a New Font');
$pdf->setKeywords('TCPDF tc-lib-pdf font import custom ttf otf noto sans');
$pdf->setPDFFilename('072_import_new_font.pdf');
$manualFontFamily = !empty($fontSetupError) ? 'helvetica' : $fontFamily;
$manualInstructions =
'<p style="font-family: '
. \htmlspecialchars($manualFontFamily, ENT_QUOTES)
. ';">Manual conversion and import commands:</p>'
. '<p style="font-family: '
. \htmlspecialchars($manualFontFamily, ENT_QUOTES)
. ';">Run these commands from the project root:</p>'
. '<pre style="color:darkgreen;font-family: '
. \htmlspecialchars($manualFontFamily, ENT_QUOTES)
. '; font-size: 9pt; line-height: 1.35;">'
. 'mkdir -p target/fonts/source target/fonts/custom'
. "\n\n"
. 'curl -fL --retry 3 -o target/fonts/source/'
. \htmlspecialchars($notoFileName, ENT_QUOTES)
. ' '
. \htmlspecialchars($notoUrl, ENT_QUOTES)
. "\n\n"
. 'php vendor/tecnickcom/tc-lib-pdf-font/util/convert.php \\'
. "\n"
. ' --outpath=target/fonts/custom \\'
. "\n"
. ' --type=TrueTypeUnicode \\'
. "\n"
. ' --flags=32 \\'
. "\n"
. ' --encoding_id=1 \\'
. "\n"
. ' --fonts=target/fonts/source/'
. \htmlspecialchars($notoFileName, ENT_QUOTES)
. '</pre>'
. '<p style="font-family: '
. \htmlspecialchars($manualFontFamily, ENT_QUOTES)
. ';">Then run this example again; it will load the generated '
. 'target/fonts/custom/'
. \htmlspecialchars($fontFamily, ENT_QUOTES)
. '.json.</p>';
if (!empty($fontSetupError)) {
$baseFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
$pdf->addPage(['format' => 'A4']);
$pdf->page->addContent($baseFont['out']);
$html =
'<h1 style="font-family: helvetica;">Font Setup Failed</h1>'
. '<p style="font-family: helvetica;">The example could not download or convert Noto Sans automatically.</p>'
. '<p style="font-family: helvetica;"><b>Error:</b> '
. \htmlspecialchars($fontSetupError, ENT_QUOTES)
. '</p>'
. '<p style="font-family: helvetica;">If the environment is offline, run once with internet access or pre-populate:</p>'
. '<p style="font-family: helvetica;">target/fonts/source/'
. \htmlspecialchars($notoFileName, ENT_QUOTES)
. ' and target/fonts/custom/'
. \htmlspecialchars($fontFamily, ENT_QUOTES)
. '.json.</p>'
. '<hr/>'
. $manualInstructions;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
return;
}
$notoFont = $pdf->font->insert($pdf->pon, $fontFamily, '', 10);
// 3) Start the PDF page and render content using the imported font.
$pdf->addPage(['format' => 'A4']);
$pdf->page->addContent($notoFont['out']);
$html =
'<h1 style="font-family: '
. \htmlspecialchars($fontFamily, ENT_QUOTES)
. ';">Custom Font Imported Automatically</h1>'
. '<p style="font-family: '
. \htmlspecialchars($fontFamily, ENT_QUOTES)
. ';">Downloaded from: '
. \htmlspecialchars($notoUrl, ENT_QUOTES)
. '</p>'
. '<p style="font-family: '
. \htmlspecialchars($fontFamily, ENT_QUOTES)
. ';">Converted source: target/fonts/source/'
. \htmlspecialchars($notoFileName, ENT_QUOTES)
. '</p>'
. '<p style="font-family: '
. \htmlspecialchars($fontFamily, ENT_QUOTES)
. ';">Imported family: <b>'
. \htmlspecialchars($fontFamily, ENT_QUOTES)
. '</b></p>'
. '<hr/>'
. '<p style="font-family: '
. \htmlspecialchars($fontFamily, ENT_QUOTES)
. '; font-size: 16pt;">'
. 'The quick brown fox jumps over the lazy dog. 0123456789'
. '</p>'
. '<hr/>'
. $manualInstructions;
$pdf->addHTMLCell(html: $html, posx: 15, posy: 20, width: 180);
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,373 @@
<?php
/**
* E074_text_cell_fit_modes.php
*
* Demonstrates getTextCell/addTextCell fit modes:
* T, S, F and disabled behavior.
*
* @since 2026-05-19
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
// autoloader when using Composer
require __DIR__ . '/../vendor/autoload.php';
// define fonts directory
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf();
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 074');
$pdf->setTitle('Text Cell Fit Modes');
$pdf->setKeywords('TCPDF tc-lib-pdf example text cell fit T S F');
$pdf->setPDFFilename('074_text_cell_fit_modes.pdf');
$titleFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
// ----------
$pdf->addPage();
$pdf->page->addContent($titleFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'getTextCell() fit mode',
posx: 15,
posy: 15,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$baseFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 11);
$cellStyle = [
'all' => [
'lineWidth' => 0.3,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#4a5b70',
'fillColor' => '#ffffff',
],
];
$pdf->page->addContent($baseFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Long text scenario (overflow):',
posx: 15,
posy: 30,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$longSample = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
$modesL = [
\Com\Tecnick\Pdf\TextFitMode::Truncate,
\Com\Tecnick\Pdf\TextFitMode::Stretch,
\Com\Tecnick\Pdf\TextFitMode::ShrinkFont,
\Com\Tecnick\Pdf\TextFitMode::Off,
];
$modeY = 40;
foreach ($modesL as $mode) {
$label = $mode === \Com\Tecnick\Pdf\TextFitMode::Off ? 'fit=(disabled)' : 'fit=' . $mode->value;
$pdf->page->addContent($pdf->getTextCell(
txt: $label,
posx: 15,
posy: $modeY,
width: 24,
height: 6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
));
// one line
$pdf->page->addContent($pdf->getTextCell(
txt: $longSample,
posx: 41,
posy: $modeY,
width: 160,
height: 5,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
styles: $cellStyle,
fit: $mode,
));
// multiple lines
$pdf->page->addContent($pdf->getTextCell(
txt: $longSample,
posx: 41,
posy: $modeY + 8,
width: 80,
height: 10,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
styles: $cellStyle,
fit: $mode,
));
$modeY += 25;
}
// ----------
$page = $pdf->addPage();
$pid = $page['pid'];
$pdf->page->addContent($titleFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'addTextCell() fit mode',
posx: 15,
posy: 15,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($baseFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Long text scenario (overflow):',
posx: 15,
posy: 30,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$modeY = 40;
foreach ($modesL as $mode) {
$label = $mode === \Com\Tecnick\Pdf\TextFitMode::Off ? 'fit=(disabled)' : 'fit=' . $mode->value;
$pdf->page->addContent($pdf->getTextCell(
txt: $label,
posx: 15,
posy: $modeY,
width: 24,
height: 6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
));
// one line
$pdf->addTextCell(
txt: $longSample,
pid: $pid,
posx: 41,
posy: $modeY,
width: 160,
height: 5,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
styles: $cellStyle,
fit: $mode,
);
// multiple lines
$pdf->addTextCell(
txt: $longSample,
pid: $pid,
posx: 41,
posy: $modeY + 8,
width: 80,
height: 10,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
styles: $cellStyle,
fit: $mode,
);
$modeY += 25;
}
// ----------
// /\/\/\/\/\
// ----------
$pdf->addPage();
$pdf->page->addContent($titleFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'getTextCell() fit mode long word (hyphenated)',
posx: 15,
posy: 15,
width: 180,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$baseFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 11);
$cellStyle = [
'all' => [
'lineWidth' => 0.3,
'lineCap' => 'butt',
'lineJoin' => 'miter',
'dashArray' => [],
'dashPhase' => 0,
'lineColor' => '#4a5b70',
'fillColor' => '#ffffff',
],
];
$pdf->page->addContent($baseFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Long word scenario (overflow):',
posx: 15,
posy: 30,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
// Define a custom hyphenation pattern that splits a word after each character.
// See loadTexHyphenPatterns() for language-specific hyphenation patterns.
$singleCharPattern = [];
foreach (range('a', 'z') as $letter) {
$singleCharPattern[$letter] = $letter . '1';
}
$pdf->setTexHyphenPatterns(patterns: $singleCharPattern);
$longWord = 'Loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliqua.';
$modesL = [
\Com\Tecnick\Pdf\TextFitMode::Truncate,
\Com\Tecnick\Pdf\TextFitMode::Stretch,
\Com\Tecnick\Pdf\TextFitMode::ShrinkFont,
\Com\Tecnick\Pdf\TextFitMode::Off,
];
$modeY = 40;
foreach ($modesL as $mode) {
$label = $mode === \Com\Tecnick\Pdf\TextFitMode::Off ? 'fit=(disabled)' : 'fit=' . $mode->value;
$pdf->page->addContent($pdf->getTextCell(
txt: $label,
posx: 15,
posy: $modeY,
width: 24,
height: 6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
));
// one line
$pdf->page->addContent($pdf->getTextCell(
txt: $longWord,
posx: 41,
posy: $modeY,
width: 160,
height: 5,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
styles: $cellStyle,
fit: $mode,
));
// multiple lines
$pdf->page->addContent($pdf->getTextCell(
txt: $longWord,
posx: 41,
posy: $modeY + 8,
width: 80,
height: 10,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
styles: $cellStyle,
fit: $mode,
));
$modeY += 25;
}
// ----------
$page = $pdf->addPage();
$pid = $page['pid'];
$pdf->page->addContent($titleFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'addTextCell() fit mode long word (hyphenated)',
posx: 15,
posy: 15,
width: 180,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$pdf->page->addContent($baseFont['out']);
$pdf->page->addContent($pdf->getTextCell(
txt: 'Long word scenario (overflow):',
posx: 15,
posy: 30,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));
$modeY = 40;
foreach ($modesL as $mode) {
$label = $mode === \Com\Tecnick\Pdf\TextFitMode::Off ? 'fit=(disabled)' : 'fit=' . $mode->value;
$pdf->page->addContent($pdf->getTextCell(
txt: $label,
posx: 15,
posy: $modeY,
width: 24,
height: 6,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
));
// one line
$pdf->addTextCell(
txt: $longWord,
pid: $pid,
posx: 41,
posy: $modeY,
width: 160,
height: 5,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
styles: $cellStyle,
fit: $mode,
);
// multiple lines
$pdf->addTextCell(
txt: $longWord,
pid: $pid,
posx: 41,
posy: $modeY + 8,
width: 80,
height: 10,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
styles: $cellStyle,
fit: $mode,
);
$modeY += 25;
}
// ----------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);

Some files were not shown because too many files have changed in this diff Show More