# Fonts

Back to root overview: [README.md](../README.md#in-depth-documentation)

## Font Setup

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 add the font generation step to your **consuming project's** `composer.json` file:

```json
{
  "scripts": {
    "tc-lib-pdf-fonts": [
      "@php vendor/tecnickcom/tc-lib-pdf-font/util/build_fonts.php"
    ],
    "post-install-cmd": [
      "@tc-lib-pdf-fonts"
    ],
    "post-update-cmd": [
      "@tc-lib-pdf-fonts"
    ]
  }
}
```

Fonts are then generated by:

```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"
]
```

To generate fonts manually, run from your project root:

```bash
php vendor/tecnickcom/tc-lib-pdf-font/util/build_fonts.php
```

The build requires only PHP and Composer (no `make` or POSIX shell), so it also runs on Windows.

Generated fonts are kept in `vendor/tecnickcom/tc-lib-pdf-font/target/fonts/` and are not regenerated unless explicitly rebuilt.

A missing asset directory is reported as `unable to read file: helvetica.json` when the first page is written.

`tecnickcom/tcpdf` 7.x is a compatibility facade over this package and uses the same font assets, so it needs the same generation step.

Custom fonts can be converted to PHP font data with `tc-lib-pdf-font`. For shared or immutable environments, generate them once into a persistent directory outside `vendor/` and point `K_PATH_FONTS` to that location.

Runnable custom font workflow: [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=10 \
    --fonts=target/fonts/source/NotoSans-Regular.ttf
```

`--encoding_id=10` selects the full Unicode cmap subtable of the font, which is required to render characters above U+FFFF. See [Unicode Text Encoding](#unicode-text-encoding).

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');
```

## Unicode Text Encoding

A `TrueTypeUnicode` font is written as a composite (Type0) font with the `Identity-H` encoding, so every character in a content stream is a 2-byte code. That code is the glyph index of the font (`/CIDToGIDMap /Identity`), and the mapping back to Unicode is carried by a `/ToUnicode` CMap generated for the glyphs the document uses. Text search, copy and paste, and assistive technologies read that CMap.

Consequences for a document:

- Characters above U+FFFF are rendered, given the font conversion described below.
- No `CIDToGIDMap` stream is embedded.
- The `.ctg.z` file of a font is read when the text is encoded, not only when the font is embedded. It must stay next to the `.json` definition file.

### Supplementary-Plane Characters

Characters outside the Basic Multilingual Plane, such as the mathematical alphanumeric symbols (U+1D400 to U+1D7FF), emoji, and the CJK extensions, are mapped by the format 12 cmap subtable of a font. That subtable is only read when the font is converted with `--encoding_id=10`:

```bash
php vendor/tecnickcom/tc-lib-pdf-font/util/convert.php \
    --outpath=target/fonts/custom \
    --type=TrueTypeUnicode \
    --flags=32 \
    --encoding_id=10 \
    --fonts=target/fonts/source/MyFont-Regular.ttf
```

`--encoding_id=10` is a safe default for any `TrueTypeUnicode` font: a font without a format 12 subtable falls back to the BMP one.

The fonts bundled with `tc-lib-pdf-font` are converted with `--encoding_id=1` and therefore cover the BMP only. To use a supplementary-plane character, convert a font that contains it with `--encoding_id=10` and point `K_PATH_FONTS` to the output directory.

A character with no glyph in the font is drawn as `.notdef`, as with any other missing character.

### Font Selection and the Current Font

A string is always measured and encoded with the current font of the stack. Adding the font operator of another font to the page content does not change that, so the two must be kept in agreement:

```php
// Wrong: the text is measured and encoded with the font that is current on the stack,
// which is $fontB here, whatever operator was added to the page.
$fontA = $pdf->font->insert($pdf->pon, 'dejavusans', 'B', 18);
$fontB = $pdf->font->insert($pdf->pon, 'dejavusans', '', 10);
$pdf->page->addContent($fontA['out']);
$pdf->page->addContent($pdf->getTextCell(txt: 'Heading', posx: 15, posy: 20, width: 180));
```

Make the wanted font current instead, with `insert()` or by cloning an already inserted one:

```php
$fontA = $pdf->font->cloneFont($pdf->pon, $fontA['idx'], null, $fontA['size']);
$pdf->page->addContent($fontA['out']);
$pdf->page->addContent($pdf->getTextCell(txt: 'Heading', posx: 15, posy: 20, width: 180));
```

`cloneFont()` inherits the size of the current font when `$size` is null, so pass the size explicitly to keep the one of the cloned font. See [examples/E044_toc_index.php](../examples/E044_toc_index.php).

With a byte font (Core, TrueType, Type1) a mismatch only affects the metrics used for layout, since the character codes mean the same thing in every font. With a `TrueTypeUnicode` font the codes are the glyph indices of the font that encoded them, so a mismatch would draw unrelated glyphs: a text object using such a font therefore selects it explicitly, and the current font of the stack wins over any operator previously added to the page.

### Missing Style Variations

A family ships one definition file per style variation, named after the font key: `unifont.json`, `unifontb.json`, `unifonti.json`, `unifontbi.json`. When the file of the requested style is absent the regular one is loaded in its place and the style is painted by the text operators:

| Style | Rendering |
|-------|-----------|
| bold | the glyphs are stroked as well as filled (text rendering mode 2), with a stroke width of a thirtieth of the font size |
| italic | the text matrix is sheared by the italic angle of the fallback descriptor (11 degrees) |

Nothing has to be enabled. `<b>`, `<i>`, the CSS `font-weight` and `font-style` properties and the `B` / `I` style arguments of `font->insert()` work the same whether the variation exists or not. GNU Unifont, which has no bold and no italic, is the case this covers. See [examples/E052_custom_fonts_fallback.php](../examples/E052_custom_fonts_fallback.php).

Every style of such a family resolves to the one font it does ship, so the glyph program is held in memory once and embedded once no matter how many styles the document uses, and `/BaseFont` names the font that is actually embedded. `font->insert()` returns the requested style in `style` and the part of it being synthesized in `fakestyle`:

```php
$bold = $pdf->font->insert($pdf->pon, 'unifont', 'B', 12);
// $bold['key'] === 'unifont', $bold['style'] === 'B', $bold['fakestyle'] === 'B'
```

A font that does provide the requested variation is never synthesized, and `fakestyle` is then empty.

Limitations:

- **The painted glyphs are wider than the advance they were measured with.** Only the drawing changes; the advance widths that drive line breaking, justification and cell fitting come from the font definition and are untouched, so a synthetic style lays out exactly like the regular one and a document does not reflow. The cost is that the ink spills past the box it was measured in: a stroked stem by half the stroke width on each edge, and a sheared ascender by the slant times the ascent, which at 18 pt Unifont is about 1.2 mm at the end of a run. A synthetic italic run can therefore touch the run that follows it, and an inline background, border or link rectangle can stop short of the ink. A designed italic absorbs this in its side bearings; a synthesized one cannot.
- **A synthetic bold is stroked with the current stroke colour.** The HTML renderer sets it to the text colour. A caller of `getTextCell()` or `getTextLine()` sets the fill colour itself and must set the stroke colour alongside it, otherwise a coloured synthetic bold is outlined in the stroke colour left by an earlier operation:

```php
$pdf->page->addContent($pdf->color->getPdfFillColor('red') . $pdf->color->getPdfStrokeColor('red'));
```

- **Underline, overline and line-through are drawn as rectangles after the text object**, so they are neither sheared with a synthetic italic nor thickened with a synthetic bold.
- **Text that is neither filled nor stroked is left alone**, so invisible text and text used as a clipping source are not emboldened.

Text extraction, search, `ActualText` and tagging are unaffected: the glyph codes do not change. A real bold or italic font is always preferable; the synthesis is a fallback.

## 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) |
