Skip to content

Fonts

phpdftk supports every font kind in ISO 32000-2: the 14 standard Type 1 fonts that every viewer ships, custom TrueType and Type 1 fonts with automatic subsetting, Type 0 composite (CID) fonts for full Unicode coverage, and Type 3 fonts whose glyph procedures are PDF content streams.

The 14 standard fonts — Helvetica, Times, Courier in their weights — are always available, embedded in zero bytes, and perfect for body text when you don’t need a custom typeface.

use Phpdftk\Pdf\Core\Font\StandardFont;
use Phpdftk\Pdf\Core\Font\Type1Font;
use Phpdftk\Pdf\Writer\PdfWriter;
$writer = new PdfWriter();
$page = $writer->addPage();
// The 14 standard Type1 fonts are always available — every PDF viewer ships them.
// They cost zero bytes in the file and are perfect for prose, code, and tabular data
// when you don't need a custom typeface.
$specimens = [
[StandardFont::Helvetica, 'Helvetica - the workhorse sans-serif'],
[StandardFont::HelveticaBold, 'Helvetica-Bold - a bolder workhorse'],
[StandardFont::HelveticaOblique, 'Helvetica-Oblique - italicised'],
[StandardFont::TimesRoman, 'Times-Roman - a serif for body copy'],
[StandardFont::TimesBold, 'Times-Bold - a bolder serif'],
[StandardFont::TimesItalic, 'Times-Italic - the cursive serif'],
[StandardFont::Courier, 'Courier - monospace for code 0123456789'],
[StandardFont::CourierBold, 'Courier-Bold - bolder monospace'],
];
$y = 740;
foreach ($specimens as [$face, $caption]) {
$name = $writer->addFont(new Type1Font($face))->getResourceName();
$cs = $writer->addContentStream($page);
$cs->beginText()->setFont($name, 16)->moveTextPosition(72, $y)
->showText($caption)->endText();
$y -= 36;
}
$writer->save('type1.pdf');

TrueTypeFont::fromFile() parses a .ttf and registers it as a simple (non-composite) font. PdfWriter::addFont() embeds and subsets the program so only the WinAnsi-mapped glyphs you actually use are written into the file.

use Phpdftk\Pdf\Core\Font\StandardFont;
use Phpdftk\Pdf\Core\Font\TrueTypeFont;
use Phpdftk\Pdf\Core\Font\Type1Font;
use Phpdftk\Pdf\Writer\PdfWriter;
// TrueTypeFont::fromFile() reads a .ttf, parses widths and metrics, and registers it as
// a simple (non-composite) PDF font. Calling addFont() embeds and subsets the program
// so only the glyphs needed for WinAnsi-encoded text are written into the file.
$writer = new PdfWriter();
$page = $writer->addPage();
$caption = $writer->addFont(new Type1Font(StandardFont::HelveticaBold))->getResourceName();
$fontPath = __DIR__ . '/../../../vendor/mpdf/mpdf/ttfonts/DejaVuSerif.ttf';
$dejavu = TrueTypeFont::fromFile($fontPath);
$dejavuName = $writer->addFont($dejavu)->getResourceName();
$cs = $writer->addContentStream($page);
$cs->beginText()->setFont($caption, 22)->moveTextPosition(72, 740)
->showText('TrueType embedding & subsetting')->endText();
$lines = [
[700, 'DejaVu Serif at 28pt — the font program is embedded.', 28],
[650, 'Only the WinAnsi-mapped glyphs you use are written.', 18],
[620, 'Smaller files than a full font, identical fidelity.', 18],
[580, '"Lorem ipsum dolor sit amet, consectetur adipiscing."', 14],
[560, 'The quick brown fox jumps over the lazy dog.', 14],
[540, '0 1 2 3 4 5 6 7 8 9 - $ % & @ # ! ? * +', 14],
];
foreach ($lines as [$y, $text, $size]) {
$cs->beginText()->setFont($dejavuName, $size)->moveTextPosition(72, $y)
->showText($text)->endText();
}
$writer->save('truetype-subset.pdf');

For non-WinAnsi text — CJK, Cyrillic, Greek, mathematical symbols — use addCompositeFont(). The font is embedded as a Type 0 CID font with a ToUnicode CMap, automatically subset to the codepoints you provide.

use Phpdftk\FontParser\TrueTypeParser;
use Phpdftk\Pdf\Core\Font\StandardFont;
use Phpdftk\Pdf\Core\Font\Type1Font;
use Phpdftk\Pdf\Writer\PdfWriter;
// Type 0 (composite) fonts let you write any Unicode character a TTF font supports
// — no encoding tricks, no WinAnsi limitations. The font is automatically subset
// so only the glyphs you actually use are embedded.
$writer = new PdfWriter();
$page = $writer->addPage();
$caption = $writer->addFont(new Type1Font(StandardFont::HelveticaBold))->getResourceName();
$fontPath = __DIR__ . '/../../../vendor/mpdf/mpdf/ttfonts/DejaVuSans.ttf';
$ttData = (new TrueTypeParser($fontPath))->parse();
$samples = [
'Latin Extended: café · résumé · naïve · jalapeño · København',
'Cyrillic: Привет, мир! Я люблю PDFs.',
'Greek: Καλημέρα κόσμε — αβγδεζηθ',
'Mathematical: ∑ x² ≤ ∫ f(x) dx · π ≈ 3.14159 · ∞ ≠ 0',
'Symbols: © ® ™ § ¶ † ‡ • ‰ ‹ › « » ←↑→↓ ↔',
];
// Collect every codepoint actually used across all samples so the embedded font
// is subset to exactly those glyphs.
$allText = implode(' ', $samples);
$codepoints = array_values(array_unique(array_map('mb_ord', mb_str_split($allText))));
$dejavu = $writer->addCompositeFont($ttData, $codepoints);
$dejavuName = $dejavu->getResourceName();
// Post-subset Unicode → GID map from the font handle. Subsetting renumbers
// the kept glyphs, so the original $ttData->fullUnicodeToGid points at
// the wrong slots in the embedded font.
$unicodeToGid = $dejavu->getUnicodeToGidMap();
$encodeHex = static function (string $text, array $unicodeToGid): string {
$hex = '';
foreach (mb_str_split($text) as $char) {
$gid = $unicodeToGid[mb_ord($char)] ?? 0;
$hex .= sprintf('%04X', $gid);
}
return $hex;
};
$cs = $writer->addContentStream($page);
$cs->beginText()->setFont($caption, 22)->moveTextPosition(72, 740)
->showText('Unicode via Type 0 (CID) fonts')->endText();
$y = 690;
foreach ($samples as $line) {
$cs->beginText()->setFont($dejavuName, 14)->moveTextPosition(72, $y)
->showTextHex($encodeHex($line, $unicodeToGid))->endText();
$y -= 40;
}
$writer->save('unicode-cid.pdf');

A Type3Font lets you ship arbitrary PDF content streams as glyphs — perfect for pictograms, dingbats, or any vector mark that should behave like text and scale to any size.

use Phpdftk\Pdf\Core\Content\ContentStream;
use Phpdftk\Pdf\Core\Font\Encoding;
use Phpdftk\Pdf\Core\Font\StandardFont;
use Phpdftk\Pdf\Core\Font\Type1Font;
use Phpdftk\Pdf\Core\Font\Type3Font;
use Phpdftk\Pdf\Core\PdfArray;
use Phpdftk\Pdf\Core\PdfDictionary;
use Phpdftk\Pdf\Core\PdfName;
use Phpdftk\Pdf\Core\PdfNumber;
use Phpdftk\Pdf\Writer\PdfWriter;
// A Type 3 font lets you ship arbitrary PDF content streams as glyphs — perfect for
// pictograms, custom dingbats, or any vector mark that should behave like text.
$writer = new PdfWriter();
$page = $writer->addPage();
$caption = $writer->addFont(new Type1Font(StandardFont::HelveticaBold))->getResourceName();
// Glyph 'square' — a filled square spanning the 700×700 glyph space.
$squareProc = new ContentStream();
$squareProc
->setGlyphWidthAndBoundingBox(700, 0, 0, 0, 700, 700)
->rectangle(100, 100, 500, 500)
->fill();
// Glyph 'triangle' — a filled equilateral-ish triangle.
$triangleProc = new ContentStream();
$triangleProc
->setGlyphWidthAndBoundingBox(700, 0, 0, 0, 700, 700)
->moveTo(350, 650)
->lineTo(50, 100)
->lineTo(650, 100)
->closePath()
->fill();
// Glyph 'star' — a five-point star, painted with a single subpath.
$starProc = new ContentStream();
$starProc
->setGlyphWidthAndBoundingBox(700, 0, 0, 0, 700, 700)
->moveTo(350, 660)
->lineTo(431, 446)
->lineTo(660, 437)
->lineTo(478, 297)
->lineTo(553, 80)
->lineTo(350, 200)
->lineTo(147, 80)
->lineTo(222, 297)
->lineTo(40, 437)
->lineTo(269, 446)
->closePath()
->fill();
$squareRef = $writer->register($squareProc);
$triangleRef = $writer->register($triangleProc);
$starRef = $writer->register($starProc);
// Encoding: map ASCII 'A','B','C' to the three glyph names.
$encoding = new Encoding();
$encoding->differences = new PdfArray([
new PdfNumber(65),
new PdfName('square'),
new PdfName('triangle'),
new PdfName('star'),
]);
$encodingRef = $writer->register($encoding);
$font = new Type3Font('DemoType3');
$font->fontBBox = new PdfArray([
new PdfNumber(0), new PdfNumber(0), new PdfNumber(700), new PdfNumber(700),
]);
$font->firstChar = 65;
$font->lastChar = 67;
$font->widths = new PdfArray([
new PdfNumber(700), new PdfNumber(700), new PdfNumber(700),
]);
$font->encoding = $encodingRef;
$font->addCharProc('square', $squareRef);
$font->addCharProc('triangle', $triangleRef);
$font->addCharProc('star', $starRef);
$font->resources = new PdfDictionary(['ProcSet' => new PdfArray([new PdfName('PDF')])]);
$customName = $writer->addFont($font)->getResourceName();
$cs = $writer->addContentStream($page);
$cs->beginText()->setFont($caption, 22)->moveTextPosition(72, 740)
->showText('Type 3 custom glyphs')->endText();
$cs->beginText()->setFont($caption, 11)->moveTextPosition(72, 700)
->showText('A, B and C have been remapped to a square, triangle and star.')
->endText();
// Paint "ABC" at 96pt.
$cs->beginText()->setFont($customName, 96)->moveTextPosition(72, 540)
->showText('ABC')->endText();
// Mixed with regular text on the next line.
$cs->beginText()->setFont($customName, 32)->moveTextPosition(72, 460)
->showText('AAA BBB CCC')->endText();
$writer->save('type3-custom.pdf');