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.

Check for Transparency

This demo checks the resources of all pages and form XObjects for elements which will invoke transparency.

This demo does not checks whether these resources are used or not.

PHP
<?php

use setasign\SetaPDF2\Demos\Inspector\TransparencyInspector;
use setasign\SetaPDF2\Core\Document;

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

// prepare some files
$files = glob($assetsDirectory . '/pdfs/misc/*.pdf');
$files[] = $assetsDirectory . '/pdfs/Brand-Guide.pdf';

$path = displayFiles($files);

// require the text processor class
require_once $classesDirectory . '/Inspector/TransparencyInspector.php';

$document = Document::loadByFilename($path);

$inspector = new TransparencyInspector($document);
$transparencyElements = $inspector->process();

foreach ($transparencyElements as $element) {
    echo 'Type: ' . $element['type'] . '<br />';
    echo 'Info: ' . $element['info'] . '<br />';
    echo 'Location: ' . $element['location'] . '<br />';
    echo 'Data (class name): ' . get_class($element['data']) . '<br />';
    echo "<br />";
}

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

namespace setasign\SetaPDF2\Demos\Inspector;

use setasign\SetaPDF2\Core\Document;
use setasign\SetaPDF2\Core\Resource\ResourceInterface;
use setasign\SetaPDF2\Core\Type\PdfDictionary;
use setasign\SetaPDF2\Core\XObject\XObject;
use setasign\SetaPDF2\Core\XObject\Form;
use setasign\SetaPDF2\Core\XObject\Image;
use setasign\SetaPDF2\NotImplementedException;

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

    /**
     * Information about the currently processed "location"
     *
     * @var string
     */
    protected $_currentLocation = [];

    /**
     * Found elements
     *
     * @var array
     */
    protected $_elements = [];

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

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

    /**
     * Get elements that invoke transparency behavior in the document.
     *
     * @return array
     */
    public function process()
    {
        $this->_elements = [];
        $pages = $this->_document->getCatalog()->getPages();

        for ($pageNo = 1, $pageCount = $pages->count(); $pageNo <= $pageCount; $pageNo++) {
            $page = $pages->getPage($pageNo);

            $this->_currentLocation = ['Page ' . $pageNo];

            $xObjects = $page->getCanvas()->getResources(true, false, ResourceInterface::TYPE_X_OBJECT);
            if ($xObjects) {
                $this->_processXObjects($xObjects);
            }

            $graphicStates = $page->getCanvas()->getResources(true, false, ResourceInterface::TYPE_EXT_G_STATE);
            if ($graphicStates) {
                $this->_processGraphicStates($graphicStates);
            }
        }

        return $this->_elements;
    }

    /**
     * Check graphic states for transparency.
     *
     * @param PdfDictionary $graphicStates
     */
    protected function _processGraphicStates(PdfDictionary $graphicStates)
    {
        $root = $this->_currentLocation;
        foreach ($graphicStates AS $name => $graphicState) {
            $this->_currentLocation = $root;
            $this->_currentLocation[] = 'GraphicState (' . $name . ')';

            $dictionary = $graphicState->ensure();
            if (!$dictionary instanceof PdfDictionary) {
                continue;
            }

            if (isset($dictionary['SMask']) && $dictionary->getValue('SMask')->ensure()->getValue() !== 'None') {
                $this->_addTransparentElement('GraphicState', $dictionary, 'Graphic state with SMask entry');
                continue;
            }

            if (isset($dictionary['CA']) && $dictionary->getValue('CA')->getValue() != 1.0) {
                $this->_addTransparentElement(
                    'GraphicState',
                    $dictionary,
                    'Graphic state with "CA" value of ' . sprintf('%.5F', $dictionary->getValue('CA')->getValue())
                );
            }

            if (isset($dictionary['ca']) && $dictionary->getValue('ca')->getValue() != 1.0) {
                $this->_addTransparentElement(
                    'GraphicState',
                    $dictionary,
                    'Graphic state with "ca" value of ' . sprintf('%.5F', $dictionary->getValue('CA')->getValue())
                );
            }
        }

        $this->_currentLocation = $root;
    }

    /**
     * Check XObjects for transparency.
     *
     * @param PdfDictionary $xObjects
     * @throws NotImplementedException
     */
    protected function _processXObjects(PdfDictionary $xObjects)
    {
        $root = $this->_currentLocation;
        foreach ($xObjects AS $name => $xObject) {
            $this->_currentLocation = $root;
            $this->_currentLocation[] = 'XObject (' . $name . ')';

            $xObject = XObject::get($xObject);

            // images
            if ($xObject instanceof Image) {
                $dictionary = $xObject->getIndirectObject()->ensure()->getValue();

                /* An image XObject may contain its own soft-mask image in the form of a subsidiary image XObject in the
                 * SMask entry of the image dictionary (see “Image Dictionaries”). This mask, if present, shall override
                 * any explicit or colour key mask specified by the image dictionary’s Mask entry. Either form of mask
                 * in the image dictionary shall override the current soft mask in the graphics state.
                 */
                if (isset($dictionary['SMask'])) {
                    $this->_addTransparentElement('Image', $xObject, 'Image with SMask entry');
                    continue;
                }

                /* An image XObject that has a JPXDecode filter as its data source may specify an SMaskInData entry,
                 * indicating that the soft mask is embedded in the data stream (see “JPXDecode Filter”).
                 */
                if ($dictionary->getValue('Filter')->getValue() === 'JPXDecode') {
                    if (isset($dictionary['SMaskInData']) && $dictionary->getValue('SMaskInData')->getValue() != 0) {
                        $this->_addTransparentElement(
                            'Image',
                            $xObject,
                            'Image with JPXDecode filter and SMaskInData entry'
                        );
                        continue;
                    }
                }

            // form XObjects
            } elseif ($xObject instanceof Form) {
                if (isset($this->_xObjectObjectIds[$xObject->getIndirectObject()->getObjectId()])) {
                    // recursion
                    continue;
                }
                $this->_xObjectObjectIds[$xObject->getIndirectObject()->getObjectId()] = true;

                $_xObjects = $xObject->getCanvas()->getResources(true, false, ResourceInterface::TYPE_X_OBJECT);
                if ($_xObjects) {
                    $this->_processXObjects($_xObjects);
                }
                unset($this->_xObjectObjectIds[$xObject->getIndirectObject()->getObjectId()]);

                $graphicStates = $xObject->getCanvas()->getResources(true, false, ResourceInterface::TYPE_EXT_G_STATE);
                if ($graphicStates) {
                    $this->_processGraphicStates($graphicStates);
                }
            }
        }
    }

    protected function _addTransparentElement($type, $data, $info)
    {
        $this->_elements[] = [
            'type' => $type,
            'data' => $data,
            'info' => $info,
            'location' => join(', ', $this->_currentLocation)
        ];
    }
}