SetaPDF Demos

There seems to be a problem loading the components. Please check your PHP error logs for details!

Common issues could be that you missed to install the trial license or that you are using a trial version on an unsupported PHP version.

Get Fonts

This demo extracts information about fonts in a PDF document.

It walks through all pages and resources recursively and put all found fonts into an array. It also checkes the default resources in the AcroForm dictionary for fonts.

PHP
<?php

use setasign\SetaPDF2\Demos\Inspector\FontInspector;
use setasign\SetaPDF2\Core\Font\Font;

// load and register the autoload function
require_once '../../../../../bootstrap.php';

// prepare some files
$files = glob($assetsDirectory . '/pdfs/forms/Sunnysunday-Example.pdf');
$files[] = $assetsDirectory . '/pdfs/Brand-Guide.pdf';
$files[] = $assetsDirectory . '/pdfs/Fact-Sheet-form.pdf';
$files[] = $assetsDirectory . '/pdfs/misc/Handwritten-Signature.pdf';

$path = displayFiles($files);

require_once $classesDirectory . '/Inspector/FontInspector.php';

$fontInspector = new FontInspector($path);
$fontObjects = $fontInspector->resolveFonts();

foreach ($fontObjects AS $fontObject) {
    try {
        $font = Font::get($fontObject);
    } catch (Exception $e) {
        echo $e->getMessage();
        continue;
    }

    echo 'Font name: <b>' . $font->getFontName() . '</b> (' . $font->getType() . ')<br />';
    echo 'Embedded: ' . ($fontInspector->isFontEmbedded($font) ? 'yes' : 'no');
    echo '<br /><br />';
}

if (count($fontObjects) === 0) {
    echo 'No fonts found.';
}
PHP
<?php

namespace setasign\SetaPDF2\Demos\Inspector;

use setasign\SetaPDF2\Core\Document;
use setasign\SetaPDF2\Core\Document\Page;
use setasign\SetaPDF2\Core\Font\Font;
use setasign\SetaPDF2\Core\Resource\ResourceInterface;
use setasign\SetaPDF2\Core\Type\PdfDictionary;
use setasign\SetaPDF2\Core\Type\PdfStream;
use setasign\SetaPDF2\Core\XObject\XObject;
use setasign\SetaPDF2\Core\XObject\Form;
use setasign\SetaPDF2\NotImplementedException;

/**
 * Class FontInspector
 */
class FontInspector
{
    /**
     * @var Document
     */
    protected $_document;

    /**
     * All found font references
     */
    public $fonts = [];

    /**
     * All object ids of visited XObjects to prevent circular references
     *
     * @var array
     */
    private $_xObjectObjectIds = [];

    /**
     * The constructor
     *
     * @param $path
     */
    public function __construct($path)
    {
        $this->_document = Document::loadByFilename($path);
    }

    /**
     * Resolves all indirect objects of fonts in the document
     *
     * @return array
     */
    public function resolveFonts()
    {
        $pages = $this->_document->getCatalog()->getPages();
        for ($pageNo = 1, $pageCount = $pages->count(); $pageNo <= $pageCount; $pageNo++) {
            $page = $pages->getPage($pageNo);
            $this->_resolveFonts($page);

            // Fonts from annotations / appearance streams
            $annotations = $page->getAnnotations()->getAll();
            foreach ($annotations AS $annotation) {
                $dict = $annotation->getDictionary();
                $ap = $dict->getValue('AP');
                if ($ap === null) {
                    continue;
                }

                foreach ($ap AS $type => $value) {
                    $object = $value->ensure();
                    if ($object instanceof PdfStream) {
                        $this->_resolveFonts($annotation->getAppearance($type));

                    } elseif ($object instanceof PdfDictionary) {
                        foreach ($object AS $subType => $subValue) {
                            $subObject = $subValue->ensure();
                            if ($subObject instanceof PdfStream) {
                                $this->_resolveFonts($annotation->getAppearance($type, $subType));
                            }
                        }
                    }
                }
            }
        }

        // DR entry in AcroForm dictionary
        $acroForm = $this->_document->getCatalog()->getAcroForm();
        $dict = $acroForm->getDictionary(false);
        if ($dict) {
            $dr = $dict->getValue('DR');
            if ($dr && $dr->ensure()->offsetExists('Font')) {
                $fonts = $dr->ensure()->getValue('Font')->ensure();
                $this->_remFonts($fonts);
            }
        }

        return $this->fonts;
    }

    /**
     * Walks through a dictionary and saves the found font object references
     *
     * @param PdfDictionary $fonts
     */
    protected function _remFonts(PdfDictionary $fonts)
    {
        foreach ($fonts AS $fontIndirectObject) {
            $key = $fontIndirectObject->getObjectId() . '-' . $fontIndirectObject->getGen();
            if (isset($this->fonts[$key])) {
                continue;
            }

            $this->fonts[$key] = $fontIndirectObject;
        }
    }

    /**
     * Resolves the fonts of a page or x-object
     *
     * @param Page|Form $object
     */
    protected function _resolveFonts($object)
    {
        $fonts = $object->getCanvas()->getResources(true, false, ResourceInterface::TYPE_FONT);
        if ($fonts) {
            $this->_remFonts($fonts);
        }

        $xObjects = $object->getCanvas()->getResources(true, false, ResourceInterface::TYPE_X_OBJECT);
        if (!$xObjects) {
            return;
        }

        foreach ($xObjects AS $xObjectIndirectObject) {
            $dict = $xObjectIndirectObject->ensure()->getValue();
            if ($dict->getValue('Subtype')->getValue() !== 'Form') {
                continue;
            }

            if (isset($this->_xObjectObjectIds[$xObjectIndirectObject->getObjectId()])) {
                // recursion
                continue;
            }
            $this->_xObjectObjectIds[$xObjectIndirectObject->getObjectId()] = true;

            $xObject = XObject::get($xObjectIndirectObject);
            $this->_resolveFonts($xObject);

            unset($this->_xObjectObjectIds[$xObject->getIndirectObject()->getObjectId()]);
        }
    }

    /**
     * Checks if a font program is embedded
     *
     * @param Font $font
     * @return bool
     * @throws NotImplementedException
     */
    public function isFontEmbedded(Font $font)
    {
        $dict = $font->getIndirectObject($this->_document)->ensure();

        switch ($font->getType()) {
            case 'Type0':
                $descendantFonts = $dict->getValue('DescendantFonts');
                if ($descendantFonts === null) {
                    return false;
                }

                $descendantFonts = $descendantFonts->ensure();
                // PDF supports only a single descendant, which shall be a CIDFont.
                $cidfont = $descendantFonts->offsetGet(0);
                if (!$cidfont) {
                    return false;
                }

                $dict = $cidfont->ensure();
                // fall through
            case 'Type1':
            case 'TrueType':
            case 'MMType1':
                $fontDescriptor = $dict->getValue('FontDescriptor');
                if ($fontDescriptor === null) {
                    return false;
                }

                $fontDescriptor = $fontDescriptor->ensure();

                foreach (['FontFile', 'FontFile2', 'FontFile3'] AS $key) {
                    $fontFile = $fontDescriptor->getValue($key);
                    if ($fontFile && $fontFile->ensure() instanceof PdfStream)
                        return true;
                }

                return false;
            case 'Type3':
                throw new NotImplementedException('Type3 fonts are not supported.');
        }
    }
}