Full Code of Bacon/BaconPdf for AI

master 5f2e6b4eea07 cached
59 files
118.6 KB
32.2k tokens
244 symbols
1 requests
Download .txt
Repository: Bacon/BaconPdf
Branch: master
Commit: 5f2e6b4eea07
Files: 59
Total size: 118.6 KB

Directory structure:
gitextract_s1qjpede/

├── .coveralls.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── benchmark/
│   └── ObjectWriterEvent.php
├── composer.json
├── doc/
│   ├── .gitignore
│   ├── Makefile
│   ├── _static/
│   │   └── theme_overrides.css
│   ├── conf.py
│   ├── index.rst
│   └── metadata.rst
├── example/
│   ├── .gitignore
│   └── empty-page.php
├── phpcs.xml
├── phpunit.xml.dist
├── src/
│   ├── DocumentInformation.php
│   ├── Encryption/
│   │   ├── AbstractEncryption.php
│   │   ├── BitMask.php
│   │   ├── EncryptionInterface.php
│   │   ├── NullEncryption.php
│   │   ├── Pdf11Encryption.php
│   │   ├── Pdf14Encryption.php
│   │   ├── Pdf16Encryption.php
│   │   └── Permissions.php
│   ├── Exception/
│   │   ├── DomainException.php
│   │   ├── ExceptionInterface.php
│   │   ├── InvalidArgumentException.php
│   │   ├── OutOfBoundsException.php
│   │   ├── OutOfRangeException.php
│   │   ├── RuntimeException.php
│   │   ├── UnexpectedValueException.php
│   │   ├── UnsupportedPasswordException.php
│   │   └── WriterClosedException.php
│   ├── Options/
│   │   ├── EncryptionOptions.php
│   │   ├── PdfWriterOptions.php
│   │   └── RasterImageOptions.php
│   ├── Page.php
│   ├── PdfWriter.php
│   ├── RasterImage.php
│   ├── Rectangle.php
│   ├── Utils/
│   │   └── StringUtils.php
│   └── Writer/
│       ├── DocumentWriter.php
│       ├── ObjectWriter.php
│       └── PageWriter.php
└── test/
    ├── Encryption/
    │   ├── AbstractEncryptionTest.php
    │   ├── AbstractEncryptionTestCase.php
    │   ├── BitMaskTest.php
    │   ├── NullEncryptionTest.php
    │   ├── Pdf11EncryptionTest.php
    │   ├── Pdf14EncryptionTest.php
    │   ├── Pdf16EncryptionTest.php
    │   ├── PermissionsTest.php
    │   └── _files/
    │       ├── pdf11-encrypt-entry.txt
    │       ├── pdf14-encrypt-entry.txt
    │       └── pdf16-encrypt-entry.txt
    ├── TestHelper/
    │   └── MemoryObjectWriter.php
    └── Writer/
        └── ObjectWriterTest.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .coveralls.yml
================================================
coverage_clover: clover.xml
json_path: coveralls-upload.json
src_dir: src


================================================
FILE: .gitignore
================================================
/nbproject
/vendor
/composer.lock


================================================
FILE: .travis.yml
================================================
sudo: false

language: php

cache:
  directories:
    - $HOME/.composer/cache

matrix:
  fast_finish: true
  include:
    - php: 5.5
      env:
        - EXECUTE_CS_CHECK=true
    - php: 5.6
      env:
        - EXECUTE_TEST_COVERALLS=true
    - php: 7
    - php: hhvm

before_install:
  - if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi
  - composer self-update
  - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then composer require --dev --no-update satooshi/php-coveralls dev-master ; fi

install:
  - travis_retry composer install --no-interaction

script:
  - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/phpunit --coverage-clover clover.xml ; fi
  - if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then ./vendor/bin/phpunit ; fi
  - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/phpcs ; fi

after_script:
  - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/coveralls ; fi


================================================
FILE: LICENSE
================================================
Copyright (c) 2015, Ben Scholzen (DASPRiD)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# Bacon PDF

[![Build Status](https://api.travis-ci.org/Bacon/BaconPdf.png?branch=master)](http://travis-ci.org/Bacon/BaconPdf)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Bacon/BaconPdf/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Bacon/BaconPdf/?branch=master)
[![Coverage Status](https://coveralls.io/repos/Bacon/BaconPdf/badge.svg?branch=master&service=github)](https://coveralls.io/github/Bacon/BaconPdf?branch=master)
[![Documentation Status](https://readthedocs.org/projects/baconpdf/badge/?version=latest)](http://baconpdf.readthedocs.org/en/latest/?badge=latest)

## Introduction
BaconPdf is a new PDF library for PHP with a clean interface. It comes with both writing and reading capabilities for
PDFs up to version 1.7.

## Documentation
You can find the latest documentation over at Read the Docs:
https://baconpdf.readthedocs.org/en/latest/

## Running benchmarks
When doing performance sensitive changes to core classes, make sure to run the benchmarks before and after making your
changes to ensure that they don't cause a huge impact:

```bash
php vendor/bin/athletic -p benchmark -b vendor/autoload.php
```

================================================
FILE: benchmark/ObjectWriterEvent.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfBenchmark;

use Athletic\AthleticEvent;
use Bacon\Pdf\Writer\ObjectWriter;
use SplFileObject;

class ObjectWriterEvent extends AthleticEvent
{
    /**
     * @var ObjectWriter
     */
    private $objectWriter;

    /**
     * {@inheritdoc}
     */
    public function setUp()
    {
        $this->objectWriter = new ObjectWriter(new SplFileObject('php://memory', 'w+'));
    }

    /**
     * @iterations 10000
     */
    public function writeRawLine()
    {
        $this->objectWriter->writeRawLine('foo');
    }

    /**
     * @iterations 10000
     */
    public function startDictionary()
    {
        $this->objectWriter->startDictionary();
    }

    /**
     * @iterations 10000
     */
    public function endDictionary()
    {
        $this->objectWriter->endDictionary();
    }

    /**
     * @iterations 10000
     */
    public function startArray()
    {
        $this->objectWriter->startArray();
    }

    /**
     * @iterations 10000
     */
    public function endArray()
    {
        $this->objectWriter->endArray();
    }

    /**
     * @iterations 10000
     */
    public function writeNull()
    {
        $this->objectWriter->writeNull();
    }

    /**
     * @iterations 10000
     */
    public function writeBoolean()
    {
        $this->objectWriter->writeBoolean(true);
    }

    /**
     * @iterations 10000
     */
    public function writeIntegerNumber()
    {
        $this->objectWriter->writeNumber(1);
    }

    /**
     * @iterations 10000
     */
    public function writeFloatNumber()
    {
        $this->objectWriter->writeNumber(1.1);
    }

    /**
     * @iterations 10000
     */
    public function writeName()
    {
        $this->objectWriter->writeName('foo');
    }

    /**
     * @iterations 10000
     */
    public function writeLiteralString()
    {
        $this->objectWriter->writeLiteralString('foo');
    }

    /**
     * @iterations 10000
     */
    public function writeHexadecimalString()
    {
        $this->objectWriter->writeHexadecimalString('foo');
    }
}


================================================
FILE: composer.json
================================================
{
    "name": "bacon/bacon-pdf",
    "description": "BaconPdf is a powerful PDF library.",
    "license" : "BSD-2-Clause",
    "homepage": "https://github.com/Bacon/BaconPdf",
    "require": {
        "php": "^5.5.11|^7.0"
    },
    "authors": [
        {
            "name": "Ben Scholzen 'DASPRiD'",
            "email": "mail@dasprids.de",
            "homepage": "http://www.dasprids.de",
            "role": "Developer"
        }
    ],
    "autoload": {
        "psr-4": {
            "Bacon\\Pdf\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Bacon\\PdfBenchmark\\": "benchmark/",
            "Bacon\\PdfTest\\": "test/"
        }
    },
    "require-dev": {
        "ext-openssl": "*",
        "squizlabs/php_codesniffer": "^2.4",
        "phpunit/phpunit": "^4.8|^5.0",
        "athletic/athletic": "^0.1.8"
    },
    "suggest": {
        "ext-openssl": "Allows using the encryption features"
    }
}


================================================
FILE: doc/.gitignore
================================================
/_build

================================================
FILE: doc/Makefile
================================================
# Makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS    =
SPHINXBUILD   = sphinx-build
PAPER         =
BUILDDIR      = _build

# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif

# Internal variables.
PAPEROPT_a4     = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .

.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext

help:
	@echo "Please use \`make <target>' where <target> is one of"
	@echo "  html       to make standalone HTML files"
	@echo "  livehtml   to make live HTML files"
	@echo "  dirhtml    to make HTML files named index.html in directories"
	@echo "  singlehtml to make a single large HTML file"
	@echo "  pickle     to make pickle files"
	@echo "  json       to make JSON files"
	@echo "  htmlhelp   to make HTML files and a HTML help project"
	@echo "  qthelp     to make HTML files and a qthelp project"
	@echo "  applehelp  to make an Apple Help Book"
	@echo "  devhelp    to make HTML files and a Devhelp project"
	@echo "  epub       to make an epub"
	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
	@echo "  text       to make text files"
	@echo "  man        to make manual pages"
	@echo "  texinfo    to make Texinfo files"
	@echo "  info       to make Texinfo files and run them through makeinfo"
	@echo "  gettext    to make PO message catalogs"
	@echo "  changes    to make an overview of all changed/added/deprecated items"
	@echo "  xml        to make Docutils-native XML files"
	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
	@echo "  linkcheck  to check all external links for integrity"
	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
	@echo "  coverage   to run coverage check of the documentation (if enabled)"

clean:
	rm -rf $(BUILDDIR)/*

html:
	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
	@echo
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

livehtml:
	sphinx-autobuild -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html

dirhtml:
	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
	@echo
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."

singlehtml:
	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
	@echo
	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."

pickle:
	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
	@echo
	@echo "Build finished; now you can process the pickle files."

json:
	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
	@echo
	@echo "Build finished; now you can process the JSON files."

htmlhelp:
	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
	@echo
	@echo "Build finished; now you can run HTML Help Workshop with the" \
	      ".hhp project file in $(BUILDDIR)/htmlhelp."

qthelp:
	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
	@echo
	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/BaconPdf.qhcp"
	@echo "To view the help file:"
	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/BaconPdf.qhc"

applehelp:
	$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
	@echo
	@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
	@echo "N.B. You won't be able to view it unless you put it in" \
	      "~/Library/Documentation/Help or install it in your application" \
	      "bundle."

devhelp:
	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
	@echo
	@echo "Build finished."
	@echo "To view the help file:"
	@echo "# mkdir -p $$HOME/.local/share/devhelp/BaconPdf"
	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/BaconPdf"
	@echo "# devhelp"

epub:
	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
	@echo
	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."

latex:
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
	@echo
	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
	@echo "Run \`make' in that directory to run these through (pdf)latex" \
	      "(use \`make latexpdf' here to do that automatically)."

latexpdf:
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
	@echo "Running LaTeX files through pdflatex..."
	$(MAKE) -C $(BUILDDIR)/latex all-pdf
	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."

latexpdfja:
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
	@echo "Running LaTeX files through platex and dvipdfmx..."
	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."

text:
	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
	@echo
	@echo "Build finished. The text files are in $(BUILDDIR)/text."

man:
	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
	@echo
	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."

texinfo:
	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
	@echo
	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
	@echo "Run \`make' in that directory to run these through makeinfo" \
	      "(use \`make info' here to do that automatically)."

info:
	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
	@echo "Running Texinfo files through makeinfo..."
	make -C $(BUILDDIR)/texinfo info
	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."

gettext:
	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
	@echo
	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."

changes:
	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
	@echo
	@echo "The overview file is in $(BUILDDIR)/changes."

linkcheck:
	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
	@echo
	@echo "Link check complete; look for any errors in the above output " \
	      "or in $(BUILDDIR)/linkcheck/output.txt."

doctest:
	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
	@echo "Testing of doctests in the sources finished, look at the " \
	      "results in $(BUILDDIR)/doctest/output.txt."

coverage:
	$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
	@echo "Testing of coverage in the sources finished, look at the " \
	      "results in $(BUILDDIR)/coverage/python.txt."

xml:
	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
	@echo
	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."

pseudoxml:
	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
	@echo
	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."


================================================
FILE: doc/_static/theme_overrides.css
================================================
/* override table width restrictions */
@media screen and (min-width: 767px) {
    .wy-table-responsive table td {
        white-space: normal !important;
    }

    .wy-table-responsive {
        overflow: visible !important;
    }
}


================================================
FILE: doc/conf.py
================================================
# -*- coding: utf-8 -*-
#
# BaconPdf documentation build configuration file, created by
# sphinx-quickstart on Sat Nov 28 15:36:16 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os
import shlex

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'BaconPdf'
copyright = u'2015, Ben Scholzen (DASPRiD)'
author = u'Ben Scholzen (DASPRiD)'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0'
# The full version, including alpha/beta/rc tags.
release = '1.0.0dev'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False


# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'alabaster'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
#html_search_language = 'en'

# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}

# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'

# Output file base name for HTML help builder.
htmlhelp_basename = 'BaconPdfdoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',

# Latex figure (float) alignment
#'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
  (master_doc, 'BaconPdf.tex', u'BaconPdf Documentation',
   u'Ben Scholzen (DASPRiD)', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True


# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'baconpdf', u'BaconPdf Documentation',
     [author], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False


# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  (master_doc, 'BaconPdf', u'BaconPdf Documentation',
   author, 'BaconPdf', 'One line description of project.',
   'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False

# Set up ReadTheDocs theme
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

def setup(app):
    # overrides for wide tables in RTD theme
    app.add_stylesheet('theme_overrides.css')

# Set up PHP syntax highlights
from sphinx.highlighting import lexers
from pygments.lexers.web import PhpLexer
lexers["php"] = PhpLexer(startinline=True, linenos=1)
lexers["php-annotations"] = PhpLexer(startinline=True, linenos=1)
primary_domain = "php"
highlight_language = "php"

rst_prolog = """
.. role:: hidden
   :class: hidden
"""

================================================
FILE: doc/index.rst
================================================
Welcome to BaconPdf's documentation!
====================================

`BaconPdf`_ is a powerful yet simple PDF library written in PHP. It aims to support
the full PDF 1.7 standard. This documentation aims to guide you through the usage of the library. If you encounter
issues in the documentation or think that some topic needs more explanations, feel free to `open an issue`_ on GitHub.

In cases where this documentation refers to chapters of the PDF specification, those are always for version `1.7`_.

.. _BaconPdf: https://github.com/Bacon/BaconPdf
.. _open an issue: https://github.com/Bacon/BaconPdf/issues
.. _1.7: http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf

.. toctree::
   :caption: Table of Contents
   :maxdepth: 2

   metadata

================================================
FILE: doc/metadata.rst
================================================
Handling document metadata
==========================

PDF documents can hold general information, like the document's title, author and such, also known as metadata. To set
or retrieve them, you have to obtain the ``DocumentInformation`` object from the PDF writer::

    $information = $pdfWriter->getDocumentInformation();

You can then set or remove metadata with their respective methods::

    $information->set('Title', 'My awesome PDF document');
    $information->remove('Author');

When retrieving metadata, it can happen that these do not exist. Since BaconPdf follows a strict API, it will throw an
exception when the requested entry does not exist. You are advised to always check for the existence of the entry you
want to retrieve before actually retrieving it::

    if ($information->has('Title') {
        $title = $information->get('Title');
    } else {
        $title = '';
    }

Since the ``get()`` method will always return a string, there are two special cases in the metadata which must be
retrieved via special methods, namely ``CreationDate`` and ``ModDate``. Even though those entries have their own methods
for retrieval, checking their existence is still done via the ``has()`` method::

    if ($information->has('CreationDate')) {
        $creationDate = $information->getCreationDate();
    }

    if ($information->has('ModDate')) {
        $modificationDate = $information->getModificationDate();
    }

While the PDF specification names a list of standard entries in the metadata, it also allows arbitrary entries, thus the
``Info`` object does not distinguish between them, except for a few exceptions. Those exceptions are ``CreationDate`` and
``ModDate``, which cannot be set manually, but will always be manages by BaconPdf itself. Another exception is the
``Trapped`` entry, which is limited to three possible values (``True``, ``False`` and ``Unkown``). Every other entry can
be any kind of text string. Keep in mind that the key names are case-sensitive, so setting the standard-entries with all
lower-cased keys will not work. The standard entries are the following:

Standard PDF Metadata
---------------------

.. list-table::
   :widths: 1 9
   :header-rows: 1

   * - Key
     - Description
   * - ``Title``
     - The document's title.
   * - ``Author``
     - The name of the person who created the document.
   * - ``Subject``
     - The subject of the document.
   * - ``Keywords``
     - Keywords associated with the document.
   * - ``Creator``
     - If the document was converted to PDF from another format, the name of the application that created the original
       document from which it was converted. This would usually be the name of your application.
   * - ``Producer``
     - If the document was converted to PDF from another format, the name of the application that converted it to PDF.
       This entry defaults to "BaconPdf".
   * - ``Trapped``
     - Whether the document contains trapping information.

For more information on those entries, see chapter 10.2.1 of the PDF specification.


================================================
FILE: example/.gitignore
================================================
/*.pdf


================================================
FILE: example/empty-page.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

use Bacon\Pdf\PdfWriter;

require_once __DIR__ . '/../vendor/autoload.php';

$writer = PdfWriter::toFile(__DIR__ . '/empty-page.pdf');
$writer->getDocumentInformation()->set('Title', 'Empty Page Example');
$writer->addPage(595, 842);
$writer->endDocument();


================================================
FILE: phpcs.xml
================================================
<?xml version="1.0"?>
<ruleset name="BaconPdf">
    <file>./src</file>
    <file>./test</file>
    
    <rule ref="PSR2"/>
    <rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
    
    <rule ref="Generic.Files.LineLength">
        <exclude-pattern>test/*</exclude-pattern>
    </rule>
</ruleset>



================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         backupGlobals="false"
         backupStaticAttributes="false"
         beStrictAboutChangesToGlobalState="true"
         beStrictAboutOutputDuringTests="true"
         beStrictAboutTestsThatDoNotTestAnything="true"
         beStrictAboutTodoAnnotatedTests="true"
         forceCoversAnnotation="true"
         colors="true">
    <testsuite name="BaconPdf Test Suite">
        <directory>./test</directory>
    </testsuite>

    <filter>
        <whitelist addUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./src</directory>
        </whitelist>
    </filter>
</phpunit>


================================================
FILE: src/DocumentInformation.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf;

use Bacon\Pdf\Exception\DomainException;
use Bacon\Pdf\Utils\StringUtils;
use Bacon\Pdf\Writer\ObjectWriter;
use DateTimeImmutable;
use OutOfBoundsException;

final class DocumentInformation
{
    /**
     * @var array
     */
    private $data = ['Producer' => 'BaconPdf'];

    /**
     * Sets an entry in the information dictionary.
     *
     * The CreationData and ModDate values are restricted for internal use, so trying to set them will trigger an
     * exception. Setting the "Trapped" value is allowed, but it must be one of the values "True", "False" or "Unknown".
     * You can set any key in here, but the following are the standard keys recognized by the PDF standard. Keep in mind
     * that the keys are case-sensitive:
     *
     * Title, Author, Subject, Keywords, Creator, Producer and Trapped.
     *
     * @param  string $key
     * @param  string $value
     * @throws DomainException
     */
    public function set($key, $value)
    {
        if ('CreationDate' === $key || 'ModDate' === $key) {
            throw new DomainException('CreationDate and ModDate must not be set manually');
        }

        if ('Trapped' === $key) {
            if (!in_array($value, ['True', 'False', 'Unknown'])) {
                throw new DomainException('Value for "Trapped" must be either "True", "False" or "Unknown"');
            }

            $this->data['Trapped'] = $value;
            return;
        }

        $this->data[$key] = $value;
    }

    /**
     * Removes an entry from the information dictionary.
     *
     * @param string $key
     */
    public function remove($key)
    {
        unset($this->data[$key]);
    }

    /**
     * Checks whether an entry exists in the information dictionary.
     *
     * @param  string $key
     * @return bool
     */
    public function has($key)
    {
        return array_key_exists($key, $this->data);
    }

    /**
     * Retrieves the value for a specific entry in the information dictionary.
     *
     * You may retrieve any entry from the information dictionary through this method, except for "CreationData" and
     * "ModDate". Those two entries have their own respective methods to be retrieved.
     *
     * @param  string $key
     * @return string
     * @throws DomainException
     * @throws OutOfBoundsException
     */
    public function get($key)
    {
        if ('CreationDate' === $key || 'ModDate' === $key) {
            throw new DomainException('CreationDate and ModDate must be retrieved through their respective methods');
        }

        if (!array_key_exists($key, $this->data)) {
            throw new OutOfBoundsException(sprintf('Entry for key "%s" not found', $key));
        }

        return $this->data[$key];
    }

    /**
     * @return DateTimeImmutable
     */
    public function getCreationDate()
    {
        return $this->retrieveDate('CreationDate');
    }

    /**
     * @return DateTimeImmutable
     */
    public function getModificationDate()
    {
        return $this->retrieveDate('ModDate');
    }

    /**
     * Writes the info dictionary.
     *
     * @param ObjectWriter $objectWriter
     * @internal
     */
    public function writeInfoDictionary(ObjectWriter $objectWriter)
    {
        $objectWriter->startDictionary();

        foreach ($this->data as $key => $value) {
            $objectWriter->writeName($key);

            switch ($key) {
                case 'CreationDate':
                case 'ModDate':
                    $objectWriter->writeLiteralString(StringUtils::formatDateTime($value));
                    break;

                case 'Trapped':
                    $objectWriter->writeName($value);
                    break;

                default:
                    $objectWriter->writeLiteralString(StringUtils::encodeString($value));
            }
        }

        $objectWriter->endDictionary();
    }

    /**
     * @param  string $key
     * @return DateTimeImmutable
     * @throws OutOfBoundsException
     */
    private function retrieveDate($key)
    {
        if (!array_key_exists($key, $this->data)) {
            throw new OutOfBoundsException(sprintf('Entry for key "%s" not found', $key));
        }

        return $this->data[$key];
    }
}


================================================
FILE: src/Encryption/AbstractEncryption.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Encryption;

use Bacon\Pdf\Exception\RuntimeException;
use Bacon\Pdf\Exception\UnexpectedValueException;
use Bacon\Pdf\Exception\UnsupportedPasswordException;
use Bacon\Pdf\Options\EncryptionOptions;
use Bacon\Pdf\Writer\ObjectWriter;

abstract class AbstractEncryption implements EncryptionInterface
{
    // @codingStandardsIgnoreStart
    const ENCRYPTION_PADDING = "\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a";
    // @codingStandardsIgnoreEnd

    /**
     * @var string
     */
    private $encryptionKey;

    /**
     * @var string
     */
    private $userEntry;

    /**
     * @var string
     */
    private $ownerEntry;

    /**
     * @var Permissions
     */
    private $userPermissions;

    /**
     * @param  string      $permanentFileIdentifier
     * @param  string      $userPassword
     * @param  string      $ownerPassword
     * @param  Permissions $userPermissions
     * @throws UnexpectedValueException
     */
    public function __construct(
        $permanentFileIdentifier,
        $userPassword,
        $ownerPassword,
        Permissions $userPermissions
    ) {
        // @codeCoverageIgnoreStart
        if (!extension_loaded('openssl')) {
            throw new RuntimeException('The OpenSSL extension is required for encryption');
        }
        // @codeCoverageIgnoreEnd

        $encodedUserPassword  = $this->encodePassword($userPassword);
        $encodedOwnerPassword = $this->encodePassword($ownerPassword);

        $revision  = $this->getRevision();
        $keyLength = $this->getKeyLength() / 8;

        if (!in_array($keyLength, [40 / 8, 128 / 8])) {
            throw new UnexpectedValueException('Key length must be either 40 or 128');
        }

        $this->ownerEntry = $this->computeOwnerEntry(
            $encodedOwnerPassword,
            $encodedUserPassword,
            $revision,
            $keyLength
        );

        if (2 === $revision) {
            list($this->userEntry, $this->encryptionKey) = $this->computeUserEntryRev2(
                $encodedUserPassword,
                $this->ownerEntry,
                $revision,
                $permanentFileIdentifier
            );
        } else {
            list($this->userEntry, $this->encryptionKey) = $this->computeUserEntryRev3OrGreater(
                $encodedUserPassword,
                $revision,
                $keyLength,
                $this->ownerEntry,
                $userPermissions->toInt($revision),
                $permanentFileIdentifier
            );
        }

        $this->userPermissions = $userPermissions;
    }

    /**
     * Returns an encryption fitting for a specific PDF version.
     *
     * @param  string            $pdfVersion
     * @param  string            $permanentFileIdentifier
     * @param  EncryptionOptions $options
     * @return EncryptionInterface
     */
    public static function forPdfVersion($pdfVersion, $permanentFileIdentifier, EncryptionOptions $options)
    {
        if (version_compare($pdfVersion, '1.6', '>=')) {
            $encryptionClassName = Pdf16Encryption::class;
        } elseif (version_compare($pdfVersion, '1.4', '>=')) {
            $encryptionClassName = Pdf14Encryption::class;
        } else {
            $encryptionClassName = Pdf11Encryption::class;
        }

        return new $encryptionClassName(
            $permanentFileIdentifier,
            $options->getUserPassword(),
            $options->getOwnerPassword(),
            $options->getUserPermissions()
        );
    }

    /**
     * {@inheritdoc}
     */
    public function writeEncryptEntry(ObjectWriter $objectWriter)
    {
        $objectWriter->writeName('Encrypt');
        $objectWriter->startDictionary();

        $objectWriter->writeName('Filter');
        $objectWriter->writeName('Standard');

        $objectWriter->writeName('V');
        $objectWriter->writeNumber($this->getAlgorithm());

        $objectWriter->writeName('R');
        $objectWriter->writeNumber($this->getRevision());

        $objectWriter->writeName('O');
        $objectWriter->writeHexadecimalString($this->ownerEntry);

        $objectWriter->writeName('U');
        $objectWriter->writeHexadecimalString($this->userEntry);

        $objectWriter->writeName('P');
        $objectWriter->writeNumber($this->userPermissions->toInt($this->getRevision()));

        $this->writeAdditionalEncryptDictionaryEntries($objectWriter);

        $objectWriter->endDictionary();
    }

    /**
     * Adds additional entries to the encrypt dictionary if required.
     *
     * @param ObjectWriter $objectWriter
     */
    protected function writeAdditionalEncryptDictionaryEntries(ObjectWriter $objectWriter)
    {
    }

    /**
     * Returns the revision number of the encryption.
     *
     * @return int
     */
    abstract protected function getRevision();

    /**
     * Returns the algorithm number of the encryption.
     *
     * @return int
     */
    abstract protected function getAlgorithm();

    /**
     * Returns the key length to be used.
     *
     * The returned value must be either 40 or 128.
     *
     * @return int
     */
    abstract protected function getKeyLength();

    /**
     * Computes an individual ecryption key for an object.
     *
     * @param  string $objectNumber
     * @param  string $generationNumber
     * @return string
     */
    protected function computeIndividualEncryptionKey($objectNumber, $generationNumber)
    {
        return substr(md5(
            $this->encryptionKey
            . substr(pack('V', $objectNumber), 0, 3)
            . substr(pack('V', $generationNumber), 0, 2),
            true
        ), 0, min(16, strlen($this->encryptionKey) + 5));
    }

    /**
     * Encodes a given password into latin-1 and performs length check.
     *
     * @param  string $password
     * @return string
     * @throws UnsupportedPasswordException
     */
    private function encodePassword($password)
    {
        set_error_handler(function () {
        }, E_NOTICE);
        $encodedPassword = iconv('UTF-8', 'ISO-8859-1', $password);
        restore_error_handler();

        if (false === $encodedPassword) {
            throw new UnsupportedPasswordException('Password contains non-latin-1 characters');
        }

        if (strlen($encodedPassword) > 32) {
            throw new UnsupportedPasswordException('Password is longer than 32 characters');
        }

        return $encodedPassword;
    }

    /**
     * Computes the encryption key as defined by algorithm 3.2 in 3.5.2.
     *
     * @param  string $password
     * @param  int    $revision
     * @param  int    $keyLength
     * @param  string $ownerEntry
     * @param  int    $permissions
     * @param  string $permanentFileIdentifier
     * @param  bool   $encryptMetadata
     * @return string
     */
    private function computeEncryptionKey(
        $password,
        $revision,
        $keyLength,
        $ownerEntry,
        $permissions,
        $permanentFileIdentifier,
        $encryptMetadata = true
    ) {
        $string = substr($password . self::ENCRYPTION_PADDING, 0, 32)
                . $ownerEntry
                . pack('V', $permissions)
                . $permanentFileIdentifier;

        if ($revision >= 4 && $encryptMetadata) {
            $string .= "\0xff\0xff\0xff\0xff";
        }

        $hash = md5($string, true);

        if ($revision >= 3) {
            for ($i = 0; $i < 50; ++$i) {
                $hash = md5(substr($hash, 0, $keyLength), true);
            }

            return substr($hash, 0, $keyLength);
        }

        return substr($hash, 0, 5);
    }

    /**
     * Computes the owner entry as defined by algorithm 3.3 in 3.5.2.
     *
     * @param  string $ownerPassword
     * @param  string $userPassword
     * @param  int    $revision
     * @param  int    $keyLength
     * @return string
     */
    private function computeOwnerEntry($ownerPassword, $userPassword, $revision, $keyLength)
    {
        $hash = md5(substr($ownerPassword . self::ENCRYPTION_PADDING, 0, 32), true);

        if ($revision >= 3) {
            for ($i = 0; $i < 50; ++$i) {
                $hash = md5($hash, true);
            }

            $key = substr($hash, 0, $keyLength);
        } else {
            $key = substr($hash, 0, 5);
        }

        $value = openssl_encrypt(substr($userPassword . self::ENCRYPTION_PADDING, 0, 32), 'rc4', $key, true);

        if ($revision >= 3) {
            $value = self::applyRc4Loop($value, $key, $keyLength);
        }

        return $value;
    }

    /**
     * Computes the user entry (rev 2) as defined by algorithm 3.4 in 3.5.2.
     *
     * @param  string $userPassword
     * @param  string $ownerEntry
     * @param  int    $userPermissionFlags
     * @param  string $permanentFileIdentifier
     * @return string[]
     */
    private function computeUserEntryRev2($userPassword, $ownerEntry, $userPermissionFlags, $permanentFileIdentifier)
    {
        $key = self::computeEncryptionKey(
            $userPassword,
            2,
            5,
            $ownerEntry,
            $userPermissionFlags,
            $permanentFileIdentifier
        );

        return [
            openssl_encrypt(self::ENCRYPTION_PADDING, 'rc4', $key, true),
            $key
        ];
    }

    /**
     * Computes the user entry (rev 3 or greater) as defined by algorithm 3.5 in 3.5.2.
     *
     * @param  string $userPassword
     * @param  int    $revision
     * @param  int    $keyLength
     * @param  string $ownerEntry
     * @param  int    $permissions
     * @param  string $permanentFileIdentifier
     * @return string[]
     */
    private function computeUserEntryRev3OrGreater(
        $userPassword,
        $revision,
        $keyLength,
        $ownerEntry,
        $permissions,
        $permanentFileIdentifier
    ) {
        $key = self::computeEncryptionKey(
            $userPassword,
            $revision,
            $keyLength,
            $ownerEntry,
            $permissions,
            $permanentFileIdentifier
        );

        $hash  = md5(self::ENCRYPTION_PADDING . $permanentFileIdentifier, true);
        $value = self::applyRc4Loop(openssl_encrypt($hash, 'rc4', $key, true), $key, $keyLength);
        $value .= openssl_random_pseudo_bytes(16);

        return [
            $value,
            $key
        ];
    }

    /**
     * Applies loop RC4 encryption.
     *
     * @param  string $value
     * @param  string $key
     * @param  int    $keyLength
     * @return string
     */
    private function applyRc4Loop($value, $key, $keyLength)
    {
        for ($i = 1; $i <= 19; ++$i) {
            $newKey = '';

            for ($j = 0; $j < $keyLength; ++$j) {
                $newKey .= chr(ord($key[$j]) ^ $i);
            }

            $value = openssl_encrypt($value, 'rc4', $newKey, true);
        }

        return $value;
    }
}


================================================
FILE: src/Encryption/BitMask.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Encryption;

/**
 * Bit mask for representing permissions.
 */
final class BitMask
{
    /**
     * @var int
     */
    private $value = 0;

    /**
     * Sets
     *
     * @param int  $bit
     * @param bool $value
     */
    public function set($bit, $value)
    {
        if ($value) {
            $this->value |= (1 << $bit);
            return;
        }

        $this->value &= ~(1 << $bit);
    }

    /**
     * @return int
     */
    public function toInt()
    {
        return $this->value;
    }
}


================================================
FILE: src/Encryption/EncryptionInterface.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Encryption;

use Bacon\Pdf\Writer\ObjectWriter;

interface EncryptionInterface
{
    /**
     * Encrypts a string.
     *
     * @param  string $plaintext
     * @param  int    $objectNumber
     * @param  int    $generationNumber
     * @return string
     */
    public function encrypt($plaintext, $objectNumber, $generationNumber);

    /**
     * Writes the encrypt dictionary prefixed by the "/Encrypt" name.
     *
     * @param ObjectWriter $objectWriter
     */
    public function writeEncryptEntry(ObjectWriter $objectWriter);
}


================================================
FILE: src/Encryption/NullEncryption.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Encryption;

use Bacon\Pdf\Writer\ObjectWriter;

/**
 * Null encryption which will just return plaintext.
 */
final class NullEncryption implements EncryptionInterface
{
    /**
     * {@inheritdoc}
     */
    public function encrypt($plaintext, $objectNumber, $generationNumber)
    {
        return $plaintext;
    }

    /**
     * {@inheritdoc}
     */
    public function writeEncryptEntry(ObjectWriter $objectWriter)
    {
    }
}


================================================
FILE: src/Encryption/Pdf11Encryption.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Encryption;

/**
 * Encryption for handling PDF version 1.1 and above.
 */
class Pdf11Encryption extends AbstractEncryption
{
    /**
     * {@inheritdoc}
     */
    public function encrypt($plaintext, $objectNumber, $generationNumber)
    {
        return openssl_encrypt(
            $plaintext,
            'rc4',
            $this->computeIndividualEncryptionKey($objectNumber, $generationNumber),
            true
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function getRevision()
    {
        return 2;
    }

    /**
     * {@inheritdoc}
     */
    protected function getAlgorithm()
    {
        return 1;
    }

    /**
     * {@inheritdoc}
     */
    protected function getKeyLength()
    {
        return 40;
    }
}


================================================
FILE: src/Encryption/Pdf14Encryption.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Encryption;

use Bacon\Pdf\Writer\ObjectWriter;

/**
 * Encryption for handling PDF version 1.4 and above.
 */
class Pdf14Encryption extends Pdf11Encryption
{
    /**
     * {@inheritdoc}
     */
    protected function writeAdditionalEncryptDictionaryEntries(ObjectWriter $objectWriter)
    {
        parent::writeAdditionalEncryptDictionaryEntries($objectWriter);

        $objectWriter->writeName('Length');
        $objectWriter->writeNumber(128);
    }

    /**
     * {@inheritdoc}
     */
    protected function getRevision()
    {
        return 3;
    }

    /**
     * {@inheritdoc}
     */
    protected function getAlgorithm()
    {
        return 2;
    }

    /**
     * {@inheritdoc}
     */
    protected function getKeyLength()
    {
        return 128;
    }
}


================================================
FILE: src/Encryption/Pdf16Encryption.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Encryption;

use Bacon\Pdf\Writer\ObjectWriter;

/**
 * Encryption for handling PDF version 1.6 and above.
 */
class Pdf16Encryption extends Pdf14Encryption
{
    /**
     * {@inheritdoc}
     */
    protected function writeAdditionalEncryptDictionaryEntries(ObjectWriter $objectWriter)
    {
        parent::writeAdditionalEncryptDictionaryEntries($objectWriter);

        $objectWriter->writeName('CF');
        $objectWriter->startDictionary();

        $objectWriter->writeName('StdCF');
        $objectWriter->startDictionary();

        $objectWriter->writeName('Type');
        $objectWriter->writeName('CryptFilter');

        $objectWriter->writeName('CFM');
        $objectWriter->writeName('AESV2');

        $objectWriter->writeName('Length');
        $objectWriter->writeNumber(128);

        $objectWriter->endDictionary();
        $objectWriter->endDictionary();

        $objectWriter->writeName('StrF');
        $objectWriter->writeName('StdCF');

        $objectWriter->writeName('StmF');
        $objectWriter->writeName('StdCF');
    }

    /**
     * {@inheritdoc}
     */
    public function encrypt($plaintext, $objectNumber, $generationNumber)
    {
        $initializationVector = openssl_random_pseudo_bytes(16);

        return $initializationVector . openssl_encrypt(
            $plaintext,
            'aes-128-cbc',
            $this->computeIndividualEncryptionKey($objectNumber, $generationNumber) . "\x73\x41\x6c\x54",
            true,
            $initializationVector
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function getRevision()
    {
        return 4;
    }

    /**
     * {@inheritdoc}
     */
    protected function getAlgorithm()
    {
        return 4;
    }
}


================================================
FILE: src/Encryption/Permissions.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Encryption;

/**
 * Permissions as defined in table 3.20 in section 3.5.
 */
final class Permissions
{
    /**
     * @var bool
     */
    private $mayPrint;

    /**
     * @var bool
     */
    private $mayPrintHighResolution;

    /**
     * @var bool
     */
    private $mayModify;

    /**
     * @var bool
     */
    private $mayCopy;

    /**
     * @var bool
     */
    private $mayAnnotate;

    /**
     * @var bool
     */
    private $mayFillInForms;

    /**
     * @var bool
     */
    private $mayExtractForAccessibility;

    /**
     * @var bool
     */
    private $mayAssemble;

    /**
     * @param bool $mayPrint
     * @param bool $mayPrintHighResolution
     * @param bool $mayModify
     * @param bool $mayCopy
     * @param bool $mayAnnotate
     * @param bool $mayFillInForms
     * @param bool $mayExtractForAccessibility
     * @param bool $mayAssemble
     */
    public function __construct(
        $mayPrint,
        $mayPrintHighResolution,
        $mayModify,
        $mayCopy,
        $mayAnnotate,
        $mayFillInForms,
        $mayExtractForAccessibility,
        $mayAssemble
    ) {
        $this->mayPrint                   = $mayPrint;
        $this->mayPrintHighResolution     = $mayPrintHighResolution;
        $this->mayModify                  = $mayModify;
        $this->mayCopy                    = $mayCopy;
        $this->mayAnnotate                = $mayAnnotate;
        $this->mayFillInForms             = $mayFillInForms;
        $this->mayExtractForAccessibility = $mayExtractForAccessibility;
        $this->mayAssemble                = $mayAssemble;
    }

    /**
     * Creates permissions which allow nothing.
     *
     * @return self
     */
    public static function allowNothing()
    {
        return new self(false, false, false, false, false, false, false, false);
    }

    /**
     * Creates permissions which allow everything.
     *
     * @return self
     */
    public static function allowEverything()
    {
        return new self(true, true, true, true, true, true, true, true);
    }

    /**
     * Convert the permissions to am integer bit mask.
     *
     * {@internal Keep in mind that the bit positions named in the PDF reference are counted from 1, while in here they
     * are counted from 0.}}
     *
     * @param  int $revision
     * @return int
     */
    public function toInt($revision)
    {
        $bitMask = new BitMask();
        $bitMask->set(2, $this->mayPrint);
        $bitMask->set(3, $this->mayModify);
        $bitMask->set(4, $this->mayCopy);
        $bitMask->set(5, $this->mayAnnotate);

        if ($revision >= 3) {
            $bitMask->set(8, $this->mayFillInForms);
            $bitMask->set(9, $this->mayExtractForAccessibility);
            $bitMask->set(10, $this->mayAssemble);
            $bitMask->set(11, $this->mayPrintHighResolution);
        }

        return $bitMask->toInt();
    }
}


================================================
FILE: src/Exception/DomainException.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Exception;

class DomainException extends \DomainException implements ExceptionInterface
{
}


================================================
FILE: src/Exception/ExceptionInterface.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Exception;

interface ExceptionInterface
{
}


================================================
FILE: src/Exception/InvalidArgumentException.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Exception;

class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}


================================================
FILE: src/Exception/OutOfBoundsException.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Exception;

class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
{
}


================================================
FILE: src/Exception/OutOfRangeException.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Exception;

class OutOfRangeException extends \OutOfRangeException implements ExceptionInterface
{
}


================================================
FILE: src/Exception/RuntimeException.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Exception;

class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}


================================================
FILE: src/Exception/UnexpectedValueException.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Exception;

class UnexpectedValueException extends \UnexpectedValueException implements ExceptionInterface
{
}


================================================
FILE: src/Exception/UnsupportedPasswordException.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Exception;

class UnsupportedPasswordException extends \DomainException implements ExceptionInterface
{
}


================================================
FILE: src/Exception/WriterClosedException.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Exception;

class WriterClosedException extends \RuntimeException implements ExceptionInterface
{
}


================================================
FILE: src/Options/EncryptionOptions.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Options;

use Bacon\Pdf\Encryption\Permissions;

final class EncryptionOptions
{
    /**
     * @var string
     */
    private $userPassword;

    /**
     * @var string
     */
    private $ownerPassword;

    /**
     * @var Permissions
     */
    private $userPermissions;

    /**
     * @param string           $userPassword
     * @param string|null      $ownerPassword
     * @param Permissions|null $userPermissions
     */
    public function __construct($userPassword, $ownerPassword = null, Permissions $userPermissions = null)
    {
        $this->userPassword = $userPassword;
        $this->ownerPassword = (null !== $ownerPassword ? $ownerPassword : $userPassword);
        $this->userPermissions = (null !== $userPermissions ? $userPermissions : Permissions::allowEverything());
    }

    /**
     * @return string
     */
    public function getUserPassword()
    {
        return $this->userPassword;
    }

    /**
     * @return string
     */
    public function getOwnerPassword()
    {
        return $this->ownerPassword;
    }

    /**
     * @return Permissions
     */
    public function getUserPermissions()
    {
        return $this->userPermissions;
    }
}


================================================
FILE: src/Options/PdfWriterOptions.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Options;

use Bacon\Pdf\Encryption\AbstractEncryption;
use Bacon\Pdf\Encryption\EncryptionInterface;
use Bacon\Pdf\Encryption\NullEncryption;
use Bacon\Pdf\Exception\DomainException;

final class PdfWriterOptions
{
    /**
     * @var string
     */
    private $pdfVersion;

    /**
     * @var EncryptionOptions|null
     */
    private $encryptionOptions;

    /**
     * @param  string $pdfVersion
     * @throws DomainException
     */
    public function __construct($pdfVersion = '1.7')
    {
        if (!in_array($pdfVersion, ['1.3', '1.4', '1.5', '1.6', '1.7'])) {
            throw new DomainException('PDF version is not in the supported range (1.3 - 1.7)');
        }

        $this->pdfVersion = $pdfVersion;
    }

    /**
     * Returns the PDF version to use for the document.
     *
     * @return string
     */
    public function getPdfVersion()
    {
        return $this->pdfVersion;
    }

    /**
     * Sets encryption options.
     *
     * @param EncryptionOptions $encryptionOptions
     */
    public function setEncryptionOptions(EncryptionOptions $encryptionOptions)
    {
        $this->encryptionOptions = $encryptionOptions;
    }

    /**
     * @param  string $permanentFileIdentifier
     * @return EncryptionInterface
     */
    public function getEncryption($permanentFileIdentifier)
    {
        if (null === $this->encryptionOptions) {
            return new NullEncryption();
        }

        return AbstractEncryption::forPdfVersion($this->pdfVersion, $permanentFileIdentifier, $this->encryptionOptions);
    }
}


================================================
FILE: src/Options/RasterImageOptions.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Options;

final class RasterImageOptions
{
    /**
     * @var bool
     */
    private $useLossyCompression;

    /**
     * @var int
     */
    private $lossyCompressionQuality;

    private function __construct($useLossyCompression = false, $lossyCompressionQuality = 100)
    {
        $this->useLossyCompression = $useLossyCompression;
        $this->lossyCompressionQuality = $lossyCompressionQuality;
    }

    public function useLossyCompression()
    {
        return $this->useLossyCompression;
    }

    public function getLossyCompressionQuality()
    {
        return $this->lossyCompressionQuality;
    }
}


================================================
FILE: src/Page.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf;

use Bacon\Pdf\Writer\PageWriter;

final class Page
{
    /**
     * @var PageWriter
     */
    private $pageWriter;

    /**
     * @param PageWriter $pageWriter
     * @param float      $width
     * @param float      $height
     */
    public function __construct(PageWriter $pageWriter, $width, $height)
    {
        $this->pageWriter = $pageWriter;
        $this->pageWriter->setBox('MediaBox', new Rectangle(0, 0, $width, $height));
    }

    /**
     * Sets the crop box to which the page should be cropped to for displaying or printing.
     *
     * @param Rectangle $cropBox
     */
    public function setCropBox(Rectangle $cropBox)
    {
        $this->pageWriter->setBox('CropBox', $cropBox);
    }

    /**
     * Sets the bleed box to which the page should be clipped in a production environment.
     *
     * @param Rectangle $bleedBox
     */
    public function setBleedBox(Rectangle $bleedBox)
    {
        $this->pageWriter->setBox('BleedBox', $bleedBox);
    }

    /**
     * Sets the trim box to which the finished page should be trimmed.
     *
     * @param Rectangle $trimBox
     */
    public function setTrimBox(Rectangle $trimBox)
    {
        $this->pageWriter->setBox('TrimBox', $trimBox);
    }

    /**
     * Sets the art box which contains the meaningful content of the page.
     *
     * @param Rectangle $artBox
     */
    public function setArtBox(Rectangle $artBox)
    {
        $this->pageWriter->setBox('ArtBox', $artBox);
    }

    /**
     * Rotates the page for output.
     *
     * The supplied value must be a multiple of 90, so either 0, 90, 180 oder 270.
     *
     * @param int $degrees
     */
    public function rotate($degrees)
    {
        $this->pageWriter->setRotation($degrees);
    }
}


================================================
FILE: src/PdfWriter.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf;

use Bacon\Pdf\Encryption\EncryptionInterface;
use Bacon\Pdf\Options\PdfWriterOptions;
use Bacon\Pdf\Writer\DocumentWriter;
use Bacon\Pdf\Writer\ObjectWriter;
use Bacon\Pdf\Writer\PageWriter;
use SplFileObject;

class PdfWriter
{
    /**
     * @var PdfWriterOptions
     */
    private $options;

    /**
     * @var ObjectWriter
     */
    private $objectWriter;

    /**
     * @var DocumentWriter
     */
    private $documentWriter;

    /**
     * @var EncryptionInterface
     */
    private $encryption;

    /**
     * @param SplFileObject    $fileObject
     * @param PdfWriterOptions $options
     */
    public function __construct(SplFileObject $fileObject, PdfWriterOptions $options = null)
    {
        if (null === $options) {
            $options = new PdfWriterOptions();
        }

        $this->options = $options;

        $fileIdentifier = md5(microtime(), true);
        $this->objectWriter = new ObjectWriter($fileObject);
        $this->documentWriter = new DocumentWriter($this->objectWriter, $options, $fileIdentifier);
        $this->encryption = $options->getEncryption($fileIdentifier);
    }

    /**
     * Returns the document information object.
     *
     * @return DocumentInformation
     */
    public function getDocumentInformation()
    {
        return $this->documentWriter->getDocumentInformation();
    }

    /**
     * Adds a page to the document.
     *
     * @param  float $width
     * @param  float $height
     * @return Page
     */
    public function addPage($width, $height)
    {
        $pageWriter = new PageWriter($this->objectWriter);
        $this->documentWriter->addPageWriter($pageWriter);

        return new Page($pageWriter, $width, $height);
    }

    /**
     * Imports a raster image into the document.
     *
     * @param  strings $filename
     * @param  bool    $useLossyCompression
     * @param  int     $compressionQuality
     * @return Image
     */
    public function importRasterImage($filename, $useLossyCompression = false, $compressionQuality = 80)
    {
        return new RasterImage(
            $this->objectWriter,
            $filename,
            $this->options->getPdfVersion(),
            $useLossyCompression,
            $compressionQuality
        );
    }

    /**
     * Ends the document by writing all pending data.
     *
     * While the PDF writer will remove all references to the passed in file object in itself to avoid further writing
     * and to allow the file pointer to be closed, the callee may still have a reference to it. If that is the case,
     * make sure to unset it if you don't need it.
     *
     * Any further attempts to append data to the PDF writer will result in an exception.
     */
    public function endDocument()
    {
        $this->documentWriter->endDocument($this->encryption);
    }

    /**
     * Creates a PDF writer which writes everything to a file.
     *
     * @param  string                $filename
     * @param  PdfWriterOptions|null $options
     * @return static
     */
    public static function toFile($filename, PdfWriterOptions $options = null)
    {
        return new static(new SplFileObject($filename, 'wb'), $options);
    }

    /**
     * Creates a PDF writer which outputs everything to the STDOUT.
     *
     * Make sure to send the appropriate headers beforehand if you are in a web environment.
     *
     * @param  PdfWriterOptions|null $options
     * @return static
     */
    public static function output(PdfWriterOptions $options = null)
    {
        return new static(new SplFileObject('php://stdout', 'wb'), $options);
    }
}


================================================
FILE: src/RasterImage.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf;

use Bacon\Pdf\Exception\DomainException;
use Bacon\Pdf\Writer\ObjectWriter;
use Imagick;
use Symfony\Component\Yaml\Exception\RuntimeException;

final class RasterImage
{
    /**
     * @var int
     */
    private $id;

    /**
     * @var int
     */
    private $width;

    /**
     * @var int
     */
    private $height;

    /**
     * @param  ObjectWriter $objectWriter
     * @param  string       $filename
     * @param  string       $pdfVersion
     * @param  bool         $useLossyCompression
     * @param  int          $compressionQuality
     * @throws DomainException
     * @throws RuntimeException
     */
    public function __construct(
        ObjectWriter $objectWriter,
        $filename,
        $pdfVersion,
        $useLossyCompression,
        $compressionQuality
    ) {
        if ($compressionQuality < 0 || $compressionQuality > 100) {
            throw new DomainException('Compression quality must be a value between 0 and 100');
        }

        $image = new Imagick($filename);
        $image->stripImage();

        $this->width = $image->getImageWidth();
        $this->height = $image->getImageHeight();

        $filter     = $this->determineFilter($useLossyCompression, $pdfVersion);
        $colorSpace = $this->determineColorSpace($image);
        $this->setFitlerParameters($image, $filter, $colorSpace, $compressionQuality);

        $shadowMaskInData = null;
        $shadowMaskId = null;

        if (Imagick::ALPHACHANNEL_UNDEFINED !== $image->getImageAlphaChannel()) {
            if (version_compare($pdfVersion, '1.4', '>=')) {
                throw new RuntimeException('Transparent images require PDF version 1.4 or higher');
            }

            if ($filter === 'JPXDecode') {
                $shadowMaskInData = 1;
            } else {
                $shadowMaskId = $this->createShadowMask($objectWriter, $image, $filter);
            }
        }

        $streamData = $image->getImageBlob();
        $image->clear();

        $this->id = $objectWriter->startObject();
        $objectWriter->startDictionary();
        $this->writeCommonDictionaryEntries($objectWriter, $colorSpace, strlen($streamData), $filter);

        if (null !== $shadowMaskInData) {
            $objectWriter->writeName('SMaskInData');
            $objectWriter->writeNumber($shadowMaskInData);
        } elseif (null !== $shadowMaskId) {
            $objectWriter->writeName('SMask');
            $objectWriter->writeIndirectReference($shadowMaskId);
        }

        $objectWriter->startStream();
        $objectWriter->writeRaw($streamData);
        $objectWriter->endStream();
        $objectWriter->endObject();
    }

    /**
     * Returns the object number of the imported image.
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Returns the width of the image in pixels.
     *
     * @return int
     */
    public function getWidth()
    {
        return $this->width;
    }

    /**
     * Returns the height of the image in pixels.
     *
     * @return int
     */
    public function getHeight()
    {
        return $this->height;
    }

    /**
     * @param  bool   $useLossyCompression
     * @param  string $pdfVersion
     * @return string
     */
    private function determineFilter($useLossyCompression, $pdfVersion)
    {
        if (!$useLossyCompression) {
            return 'FlateDecode';
        }

        if (version_compare($pdfVersion, '1.5', '>=')) {
            return 'JPXDecode';
        }

        return 'DCTDecode';
    }

    /**
     * Determines the color space of an image.
     *
     * @param  Imagick $image
     * @return string
     * @throws DomainException
     */
    private function determineColorSpace(Imagick $image)
    {
        switch ($image->getColorSpace()) {
            case Imagick::COLORSPACE_GRAY:
                return 'DeviceGray';

            case Imagick::COLORSPACE_RGB:
                return 'DeviceRGB';

            case Imagick::COLORSPACE_CMYK:
                return 'DeviceCMYK';
        }

        throw new DomainException('Image has an unsupported colorspace, must be gray, RGB or CMYK');
    }

    /**
     * Creates a shadow mask from an image's alpha channel.
     *
     * @param  ObjectWriter $objectWriter
     * @param  Imagick      $image
     * @param  string       $filter
     * @return int
     */
    private function createShadowMask(ObjectWriter $objectWriter, Imagick $image, $filter)
    {
        $shadowMask = clone $image;
        $shadowMask->separateImageChannel(Imagick::CHANNEL_ALPHA);

        if ('FlateDecode' === $filter) {
            $image->setImageFormat('GRAY');
        }
        
        $streamData = $shadowMask->getImageBlob();
        $shadowMask->clear();

        $id = $objectWriter->startObject();

        $objectWriter->startDictionary();
        $this->writeCommonDictionaryEntries($objectWriter, 'DeviceGray', strlen($streamData), $filter);
        $objectWriter->endDictionary();

        $objectWriter->startStream();
        $objectWriter->writeRaw($streamData);
        $objectWriter->endStream();

        $objectWriter->endObject();
        return $id;
    }

    /**
     * Writes common dictionary entries shared between actual images and their soft masks.
     *
     * @param ObjectWriter $objectWriter
     * @param string       $colorSpace
     * @param int          $length
     * @param string       $filter
     * @param int|null     $shadowMaskId
     */
    private function writeCommonDictionaryEntries(ObjectWriter $objectWriter, $colorSpace, $length, $filter)
    {
        $objectWriter->writeName('Type');
        $objectWriter->writeName('XObject');

        $objectWriter->writeName('Subtype');
        $objectWriter->writeName('Image');

        $objectWriter->writeName('Width');
        $objectWriter->writeNumber($this->width);

        $objectWriter->writeName('Height');
        $objectWriter->writeNumber($this->height);

        $objectWriter->writeName('ColorSpace');
        $objectWriter->writeName($colorSpace);

        $objectWriter->writeName('BitsPerComponent');
        $objectWriter->writeNumber(8);

        $objectWriter->writeName('Length');
        $objectWriter->writeNumber($length);

        $objectWriter->writeName('Filter');
        $objectWriter->writeName($filter);
    }

    /**
     * Sets the filter parameters for the image.
     *
     * @param Imagick $image
     * @param string  $filter
     * @param string  $colorSpace
     * @param int     $compressionQuality
     */
    private function setFitlerParameters(Imagick $image, $filter, $colorSpace, $compressionQuality)
    {
        switch ($filter) {
            case 'JPXDecode':
                $image->setImageFormat('J2K');
                $image->setImageCompression(Imagick::COMPRESSION_JPEG2000);
                break;

            case 'DCTDecode':
                $image->setImageFormat('JPEG');
                $image->setImageCompression(Imagick::COMPRESSION_JPEG);
                break;

            case 'FlateDecode':
                $image->setImageFormat([
                    'DeviceGray' => 'GRAY',
                    'DeviceRGB' => 'RGB',
                    'DeviceCMYK' => 'CMYK',
                ][$colorSpace]);
                $image->setImageCompression(Imagick::COMPRESSION_ZIP);
                break;
        }

        $image->setImageCompressionQuality($compressionQuality);
    }
}


================================================
FILE: src/Rectangle.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf;

use Bacon\Pdf\Writer\ObjectWriter;

/**
 * Rectangle data structure as defiend in section 3.8.4.
 */
final class Rectangle
{
    /**
     * @var float
     */
    private $x1;

    /**
     * @var float
     */
    private $y1;

    /**
     * @var float
     */
    private $x2;

    /**
     * @var float
     */
    private $y2;

    /**
     * Creates a new rectangle.
     *
     * It doesn't matter in which way you specify the corners, as they will internally be normalized.
     *
     * @param float $x1
     * @param float $y1
     * @param float $x2
     * @param float $y2
     */
    public function __construct($x1, $y1, $x2, $y2)
    {
        $this->x1 = min($x1, $x2);
        $this->y1 = min($y1, $y2);
        $this->x2 = max($x1, $x2);
        $this->y2 = max($y1, $y2);
    }

    /**
     * Writes the rectangle object to a writer.
     *
     * @param ObjectWriter $objectWriter
     * @internal
     */
    public function writeRectangleArray(ObjectWriter $objectWriter)
    {
        $objectWriter->startArray();
        $objectWriter->writeNumber($this->x1);
        $objectWriter->writeNumber($this->y1);
        $objectWriter->writeNumber($this->x2);
        $objectWriter->writeNumber($this->y2);
        $objectWriter->endArray();
    }
}


================================================
FILE: src/Utils/StringUtils.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Utils;

use DateTimeInterface;

final class StringUtils
{
    public function __construct()
    {
    }

    /**
     * Encodes a string according to section 3.8.1.
     *
     * @param  string $string
     * @return string
     */
    public static function encodeString($string)
    {
        return "\xfe\xff" . iconv('UTF-8', 'UTF-16BE', $string);
    }

    /**
     * Formats a date according to section 3.8.3.
     *
     * @param  DateTimeInterface $dateTime
     * @return string
     */
    public static function formatDateTime(DateTimeInterface $dateTime)
    {
        $timeString = $dateTime->format('\D\:YmdHis');

        if (0 === $dateTime->getTimezone()->getOffset()) {
            return $timeString . 'Z';
        }

        return $timeString . strtr(':', "'", $dateTime->format('P')) . "'";
    }
}


================================================
FILE: src/Writer/DocumentWriter.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Writer;

use Bacon\Pdf\DocumentInformation;
use Bacon\Pdf\Encryption\EncryptionInterface;
use Bacon\Pdf\Options\PdfWriterOptions;

class DocumentWriter
{
    /**
     * @var ObjectWriter
     */
    private $objectWriter;

    /**
     * @var PdfWriterOptions
     */
    private $options;

    /**
     * @var string
     */
    private $permanentFileIdentifier;

    /**
     * @var string
     */
    private $changingFileIdentifier;

    /**
     * @var int
     */
    private $pageTreeId;

    /**
     * @var PageWriter[]
     */
    private $pageWriters = [];

    /**
     * @var int[]
     */
    private $pageIds = [];

    /**
     * @var DocumentInformation
     */
    private $documentInformation;

    /**
     * @param ObjectWriter $objectWriter
     * @param string       $fileIdentifier
     */
    public function __construct(ObjectWriter $objectWriter, PdfWriterOptions $options, $fileIdentifier)
    {
        $this->objectWriter = $objectWriter;
        $this->options = $options;

        $this->objectWriter->writeRawLine(sprintf("%%PDF-%s", $this->options->getPdfVersion()));
        $this->objectWriter->writeRawLine("%\xff\xff\xff\xff");

        $this->permanentFileIdentifier = $this->changingFileIdentifier = $fileIdentifier;
        $this->pageTreeId = $this->objectWriter->allocateObjectId();
        $this->documentInformation = new DocumentInformation();
    }

    /**
     * Returns the document information object.
     *
     * @return DocumentInformation
     */
    public function getDocumentInformation()
    {
        return $this->documentInformation;
    }

    /**
     * Adds a page writer for the page tree.
     *
     * @param PageWriter $pageWriter
     */
    public function addPageWriter(PageWriter $pageWriter)
    {
        $this->pageWriters[] = $pageWriter;
    }

    /**
     * Ends the document.
     *
     * @param EncryptionInterface $encryption
     */
    public function endDocument(EncryptionInterface $encryption)
    {
        $this->closeRemainingPages();
        $this->writePageTree();
        $documentInformationId = $this->writeDocumentInformation();
        $documentCatalogId = $this->writeDocumentCatalog();

        $xrefOffset = $this->writeCrossReferenceTable();
        $this->writeTrailer($documentInformationId, $documentCatalogId, $encryption);
        $this->writeFooter($xrefOffset);
    }

    /**
     * Closes pages which haven't been explicitly closed yet.
     */
    private function closeRemainingPages()
    {
        foreach ($this->pageWriters as $key => $pageWriter) {
            $this->pageIds[] = $pageWriter->writePage($this->objectWriter, $this->pageTreeId);
            unset($this->pageWriters[$key]);
        }
    }

    /**
     * Writes the page tree.
     */
    private function writePageTree()
    {
        $this->objectWriter->startObject($this->pageTreeId);
        $this->objectWriter->startDictionary();

        $this->objectWriter->writeName('Type');
        $this->objectWriter->writeName('Pages');

        $this->objectWriter->writeName('Kids');
        $this->objectWriter->startArray();

        sort($this->pageIds, SORT_NUMERIC);
        foreach ($this->pageIds as $pageId) {
            $this->objectWriter->writeIndirectReference($pageId);
        }

        $this->objectWriter->endArray();

        $this->objectWriter->writeName('Count');
        $this->objectWriter->writeNumber(count($this->pageIds));

        $this->objectWriter->endDictionary();
        $this->objectWriter->endObject();
    }

    /**
     * Writes the document information.
     *
     * @return int
     */
    private function writeDocumentInformation()
    {
        $id = $this->objectWriter->startObject();
        $this->documentInformation->writeInfoDictionary($this->objectWriter);
        $this->objectWriter->endObject();
        return $id;
    }

    /**
     * Writes the document catalog.
     *
     * @return int
     */
    private function writeDocumentCatalog()
    {
        $id = $this->objectWriter->startObject();
        $this->objectWriter->startDictionary();

        $this->objectWriter->writeName('Type');
        $this->objectWriter->writeName('Catalog');

        $this->objectWriter->writeName('Pages');
        $this->objectWriter->writeIndirectReference($this->pageTreeId);

        $this->objectWriter->endDictionary();
        $this->objectWriter->endObject();
        return $id;
    }

    /**
     * Writes the cross-reference table.
     *
     * @return int
     */
    private function writeCrossReferenceTable()
    {
        $xrefOffset = $this->objectWriter->getCurrentOffset();
        $objectOffsets = $this->objectWriter->getObjectOffsets();
        ksort($objectOffsets, SORT_NUMERIC);

        $this->objectWriter->writeRawLine('xref');
        $this->objectWriter->writeRawLine(sprintf('0 %d', count($objectOffsets) + 1));
        $this->objectWriter->writeRawLine(sprintf('%010d %05d f ', 0, 65535));

        foreach ($objectOffsets as $offset) {
            $this->objectWriter->writeRawLine(sprintf('%010d %05d n ', $offset, 0));
        }

        return $xrefOffset;
    }

    /**
     * Writes the trailer.
     *
     * @param int                 $documentInformationId
     * @param int                 $documentCatalogId
     * @param EncryptionInterface $encryption
     */
    private function writeTrailer($documentInformationId, $documentCatalogId, EncryptionInterface $encryption)
    {
        $this->objectWriter->writeRawLine('trailer');
        $this->objectWriter->startDictionary();

        $this->objectWriter->writeName('Id');
        $this->objectWriter->startArray();
        $this->objectWriter->writeHexadecimalString($this->permanentFileIdentifier);
        $this->objectWriter->writeHexadecimalString($this->changingFileIdentifier);
        $this->objectWriter->endArray();

        $this->objectWriter->writeName('Info');
        $this->objectWriter->writeIndirectReference($documentInformationId);

        $this->objectWriter->writeName('Root');
        $this->objectWriter->writeIndirectReference($documentCatalogId);

        $encryption->writeEncryptEntry($this->objectWriter);

        $this->objectWriter->endDictionary();
    }

    /**
     * Writes the footer.
     *
     * @param int $xrefOffset
     */
    private function writeFooter($xrefOffset)
    {
        $this->objectWriter->writeRawLine('');
        $this->objectWriter->writeRawLine('startxref');
        $this->objectWriter->writeRawLine((string) $xrefOffset);
        $this->objectWriter->writeRawLine("%%%EOF");
    }
}


================================================
FILE: src/Writer/ObjectWriter.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Writer;

use Bacon\Pdf\Exception\InvalidArgumentException;
use SplFileObject;

/**
 * Writer responsible for writing objects to a stream.
 *
 * While the PDF specification tells that there is a line limit of 255 characters, not even Adobe's own PDF library
 * respects this limit. We ignore it as well, as it imposes a huge impact on the performance of the writer.
 *
 * {@internal This is a very performance sensitive class, which is why some code may look duplicated. Before thinking
 * about refactoring these parts, take a good look and the supplied benchmarks and verify that your changes
 * do not affect the performance in a bad way. Keep in mind that the methods in this writer are called quite
 * often.}}
 */
class ObjectWriter
{
    /**
     * @var SplFileObject
     */
    private $fileObject;

    /**
     * @var bool
     */
    private $requiresWhitespace = false;

    /**
     * @var int
     */
    private $lastAllocatedObjectId = 0;

    /**
     * @var int[]
     */
    private $objectOffsets = [];

    /**
     * @param SplFileObject $fileObject
     */
    public function __construct(SplFileObject $fileObject)
    {
        $this->fileObject = $fileObject;
    }

    /**
     * Returns the current position in the file.
     *
     * @return int
     */
    public function getCurrentOffset()
    {
        return $this->fileObject->ftell();
    }

    /**
     * Writes a raw data line to the stream.
     *
     * A newline character is appended after the data. Keep in mind that you may still be after a token which requires
     * a following whitespace, depending on the context you are in.
     *
     * @param string $data
     */
    public function writeRawLine($data)
    {
        $this->fileObject->fwrite($data . "\n");
    }

    /**
     * Writes raw data to the stream.
     *
     * @param string $data
     */
    public function writeRaw($data)
    {
        $this->fileObject->fwrite($data);
    }

    /**
     * Returns all object offsets.
     *
     * @return int
     */
    public function getObjectOffsets()
    {
        return $this->objectOffsets;
    }

    /**
     * Allocates a new ID for an object.
     *
     * @return int
     */
    public function allocateObjectId()
    {
        return ++$this->lastAllocatedObjectId;
    }

    /**
     * Starts an object.
     *
     * If the object ID is omitted, a new one is allocated.
     *
     * @param  int|null $objectId
     * @return int
     */
    public function startObject($objectId = null)
    {
        if (null === $objectId) {
            $objectId = ++$this->lastAllocatedObjectId;
        }

        $this->objectOffsets[$objectId] = $this->fileObject->ftell();
        $this->fileObject->fwrite(sprintf("%d 0 obj\n", $objectId));

        return $objectId;
    }

    /**
     * Ends an object.
     */
    public function endObject()
    {
        $this->fileObject->fwrite("\nendobj\n");
    }

    /**
     * Starts a stream.
     */
    public function startStream()
    {
        $this->fileObject->fwrite("stream\n");
    }

    public function endStream()
    {
        $this->fileObject->fwrite("\nendstream\n");
    }

    /**
     * Writes an indirect reference
     *
     * @param int $objectId
     */
    public function writeIndirectReference($objectId)
    {
        if ($this->requiresWhitespace) {
            $this->fileObject->fwrite(sprintf(' %d 0 R', $objectId));
        } else {
            $this->fileObject->fwrite(sprintf('%d 0 R', $objectId));
        }

        $this->requiresWhitespace = true;
    }

    /**
     * Starts a dictionary.
     */
    public function startDictionary()
    {
        $this->fileObject->fwrite('<<');
        $this->requiresWhitespace = false;
    }

    /**
     * Ends a dictionary.
     */
    public function endDictionary()
    {
        $this->fileObject->fwrite('>>');
        $this->requiresWhitespace = false;
    }

    /**
     * Starts an array.
     */
    public function startArray()
    {
        $this->fileObject->fwrite('[');
        $this->requiresWhitespace = false;
    }

    /**
     * Ends an array.
     */
    public function endArray()
    {
        $this->fileObject->fwrite(']');
        $this->requiresWhitespace = false;
    }

    /**
     * Writes a null value.
     */
    public function writeNull()
    {
        if ($this->requiresWhitespace) {
            $this->fileObject->fwrite(' null');
        } else {
            $this->fileObject->fwrite('null');
        }

        $this->requiresWhitespace = true;
    }

    /**
     * Writes a boolean.
     *
     * @param bool $boolean
     */
    public function writeBoolean($boolean)
    {
        if ($this->requiresWhitespace) {
            $this->fileObject->fwrite($boolean ? ' true' : ' false');
        } else {
            $this->fileObject->fwrite($boolean ? 'true' : 'false');
        }

        $this->requiresWhitespace = true;
    }

    /**
     * Writes a number.
     *
     * @param  int|float $number
     * @throws InvalidArgumentException
     */
    public function writeNumber($number)
    {
        if ($this->requiresWhitespace) {
            $this->fileObject->fwrite(' ' . (rtrim(sprintf('%.6F', $number), '0.') ?: '0'));
        } else {
            $this->fileObject->fwrite(rtrim(sprintf('%.6F', $number), '0.') ?: '0');
        }

        $this->requiresWhitespace = true;
    }

    /**
     * Writes a name.
     *
     * @param string $name
     */
    public function writeName($name)
    {
        $this->fileObject->fwrite('/' . $name);
        $this->requiresWhitespace = true;
    }

    /**
     * Writes a literal string.
     *
     * The string itself is splitted into multiple lines after 248 characters. We chose that specific limit to avoid
     * splitting mutli-byte characters in half.
     *
     * @param string $string
     */
    public function writeLiteralString($string)
    {
        $this->fileObject->fwrite('(' . strtr($string, ['(' => '\\(', ')' => '\\)', '\\' => '\\\\']) . ')');
        $this->requiresWhitespace = false;
    }

    /**
     * Writes a hexadecimal string.
     *
     * @param string $string
     */
    public function writeHexadecimalString($string)
    {
        $this->fileObject->fwrite('<' . bin2hex($string) . '>');
        $this->requiresWhitespace = false;
    }
}


================================================
FILE: src/Writer/PageWriter.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\Pdf\Writer;

use Bacon\Pdf\Rectangle;
use DomainException;

class PageWriter
{
    /**
     * @var ObjectWriter
     */
    private $objectWriter;

    /**
     * @var int
     */
    private $pageId;

    /**
     * @var Rectangle[]
     */
    private $boxes = [];

    /**
     * @var int|null
     */
    private $rotation;

    /**
     * @var string
     */
    private $contentStream = '';

    /**
     * @param ObjectWriter $objectWriter
     */
    public function __construct(ObjectWriter $objectWriter)
    {
        $this->objectWriter = $objectWriter;
        $this->pageId = $this->objectWriter->allocateObjectId();
    }

    /**
     * Sets a box for the page.
     *
     * @param string    $name
     * @param Rectangle $box
     */
    public function setBox($name, Rectangle $box)
    {
        $this->boxes[$name] = $box;
    }

    /**
     * Sets the rotation of the page.
     *
     * @param  int $degrees
     * @throws DomainException
     */
    public function setRotation($degrees)
    {
        if (!in_array($degrees, [0, 90, 180, 270])) {
            throw new DomainException('Degrees value must be a multiple of 90');
        }

        $this->rotation = $degrees;
    }

    /**
     * Appends data to the content stream.
     *
     * @param string $data
     */
    public function appendContentStream($data)
    {
        $this->contentStream .= $data;
    }

    /**
     * Writes the page contents and definition to the writer.
     *
     * @param  ObjectWriter $objectWriter
     * @param  int          $pageTreeId
     * @return int
     */
    public function writePage(ObjectWriter $objectWriter, $pageTreeId)
    {
        $objectWriter->startObject($this->pageId);
        $objectWriter->startDictionary();
        $objectWriter->writeName('Type');
        $objectWriter->writeName('Page');

        $objectWriter->writeName('Parent');
        $objectWriter->writeIndirectReference($pageTreeId);

        $objectWriter->writeName('Resources');
        $objectWriter->startDictionary();
        $objectWriter->endDictionary();

        $objectWriter->writeName('Contents');
        $objectWriter->startArray();
        $objectWriter->endArray();

        foreach ($this->boxes as $name => $box) {
            $objectWriter->writeName($name);
            $box->writeRectangleArray($objectWriter);
        }

        if (null !== $this->rotation) {
            $objectWriter->writeName('Rotate');
            $objectWriter->writeNumber($this->rotation);
        }

        $objectWriter->endDictionary();
        $objectWriter->endObject();
        return $this->pageId;
    }
}


================================================
FILE: test/Encryption/AbstractEncryptionTest.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\Encryption;

use Bacon\Pdf\Encryption\AbstractEncryption;
use Bacon\Pdf\Encryption\Pdf11Encryption;
use Bacon\Pdf\Encryption\Pdf14Encryption;
use Bacon\Pdf\Encryption\Pdf16Encryption;
use Bacon\Pdf\Encryption\Permissions;
use Bacon\Pdf\Exception\UnexpectedValueException;
use Bacon\Pdf\Exception\UnsupportedPasswordException;
use Bacon\Pdf\Options\EncryptionOptions;
use PHPUnit_Framework_TestCase as TestCase;

/**
 * @covers \Bacon\Pdf\Encryption\AbstractEncryption
 */
class AbstractEncryptionTest extends TestCase
{
    public function testForPdfVersion()
    {
        $this->assertInstanceOf(
            Pdf11Encryption::class,
            AbstractEncryption::forPdfVersion('1.3', '', new EncryptionOptions(''))
        );

        $this->assertInstanceOf(
            Pdf14Encryption::class,
            AbstractEncryption::forPdfVersion('1.4', '', new EncryptionOptions(''))
        );

        $this->assertInstanceOf(
            Pdf14Encryption::class,
            AbstractEncryption::forPdfVersion('1.5', '', new EncryptionOptions(''))
        );

        $this->assertInstanceOf(
            Pdf16Encryption::class,
            AbstractEncryption::forPdfVersion('1.6', '', new EncryptionOptions(''))
        );

        $this->assertInstanceOf(
            Pdf16Encryption::class,
            AbstractEncryption::forPdfVersion('1.7', '', new EncryptionOptions(''))
        );
    }

    public function testTooLongishUserPassword()
    {
        $this->setExpectedException(UnsupportedPasswordException::class, 'Password is longer than 32 characters');
        $this->getAbstractEncryption()->__construct('', str_repeat('a', 33), '', Permissions::allowNothing());
    }

    public function testTooLongishOwnerPassword()
    {
        $this->setExpectedException(UnsupportedPasswordException::class, 'Password is longer than 32 characters');
        $this->getAbstractEncryption()->__construct('', '', str_repeat('a', 33), Permissions::allowNothing());
    }

    public function testUserPasswordWithInvalidCharacters()
    {
        $this->setExpectedException(UnsupportedPasswordException::class, 'Password contains non-latin-1 characters');
        $this->getAbstractEncryption()->__construct('', 'Ŧ', '', Permissions::allowNothing());
    }

    public function testOwnerPasswordWithInvalidCharacters()
    {
        $this->setExpectedException(UnsupportedPasswordException::class, 'Password contains non-latin-1 characters');
        $this->getAbstractEncryption()->__construct('', '', 'Ŧ', Permissions::allowNothing());
    }

    public function testAbstractReturnsInvalidKeyLength()
    {
        $this->setExpectedException(UnexpectedValueException::class, 'Key length must be either 40 or 128');
        $this->getAbstractEncryption(100)->__construct('', '', '', Permissions::allowNothing());
    }

    /**
     * @return AbstractEncryption
     */
    private function getAbstractEncryption($keyLength = 128)
    {
        $encryption = $this->getMockForAbstractClass(AbstractEncryption::class, [], '', false);
        $encryption->expects($this->any())->method('getKeyLength')->willReturn($keyLength);
        $encryption->expects($this->any())->method('getRevision')->willReturn(2);
        $encryption->expects($this->any())->method('getAlgorithm')->willReturn(1);
        return $encryption;
    }
}


================================================
FILE: test/Encryption/AbstractEncryptionTestCase.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\Encryption;

use Bacon\Pdf\Encryption\AbstractEncryption;
use Bacon\Pdf\Encryption\Permissions;
use Bacon\PdfTest\TestHelper\MemoryObjectWriter;
use PHPUnit_Framework_TestCase as TestCase;
use ReflectionClass;

/**
 * @covers \Bacon\Pdf\Encryption\AbstractEncryption
 */
abstract class AbstractEncryptionTestCase extends TestCase
{
    /**
     * @dataProvider encryptionTestData
     * @param string      $plaintext
     * @param string      $userPassword
     * @param string|null $ownerPassword
     * @param int         $objectNumber
     * @param int         $generationNumber
     */
    public function testEncrypt(
        $plaintext,
        $userPassword,
        $ownerPassword,
        $objectNumber,
        $generationNumber
    ) {
        $encryption = $this->createEncryption($userPassword, $ownerPassword);

        $reflectionClass = new ReflectionClass($encryption);
        $reflectionMethod = $reflectionClass->getMethod('computeIndividualEncryptionKey');
        $reflectionMethod->setAccessible(true);
        $key = $reflectionMethod->invoke($encryption, $objectNumber, $generationNumber);

        $encryptedText = $encryption->encrypt($plaintext, $objectNumber, $generationNumber);
        $decryptedText = $this->decrypt($encryptedText, $key);

        $this->assertSame($plaintext, $decryptedText);
    }

    public function testWriteEncryptEntry()
    {
        $encryption = $this->createEncryption('foo', 'bar');
        $memoryObjectWriter = new MemoryObjectWriter();
        $encryption->writeEncryptEntry($memoryObjectWriter);

        $this->assertStringMatchesFormat($this->getExpectedEntry(), $memoryObjectWriter->getData());
    }

    /**
     * @return array
     */
    abstract public function encryptionTestData();

    /**
     * @param  string           $userPassword
     * @param  string|null      $ownerPassword
     * @param  Permissions|null $userPermissions
     * @return AbstractEncryption
     */
    abstract protected function createEncryption(
        $userPassword,
        $ownerPassword = null,
        Permissions $userPermissions = null
    );

    /**
     * @param  string $encryptedText
     * @param  string $key
     * @return string
     */
    abstract protected function decrypt($encryptedText, $key);

    /**
     * @return string
     */
    abstract protected function getExpectedEntry();
}


================================================
FILE: test/Encryption/BitMaskTest.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\Encryption;

use Bacon\Pdf\Encryption\BitMask;
use PHPUnit_Framework_TestCase as TestCase;

/**
 * @covers \Bacon\Pdf\Encryption\BitMask
 */
class BitMaskTest extends TestCase
{
    public function testDefault()
    {
        $bitMask = new BitMask();
        $this->assertSame(0, $bitMask->toInt());
    }

    public function testSetBit()
    {
        $bitMask = new BitMask();
        $bitMask->set(0, true);
        $bitMask->set(1, true);
        $this->assertSame(3, $bitMask->toInt());
        $bitMask->set(0, false);
        $this->assertSame(2, $bitMask->toInt());
    }
}


================================================
FILE: test/Encryption/NullEncryptionTest.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\Encryption;

use Bacon\Pdf\Encryption\NullEncryption;
use Bacon\PdfTest\TestHelper\MemoryObjectWriter;
use PHPUnit_Framework_TestCase as TestCase;

/**
 * @covers \Bacon\Pdf\Encryption\NullEncryption
 */
class NullEncryptionTest extends TestCase
{
    public function testEncryptReturnsPlaintext()
    {
        $encryption = new NullEncryption();
        $this->assertSame('foo', $encryption->encrypt('foo', 1, 1));
    }

    public function testWriteEncryptEntryWritesNothing()
    {
        $encryption = new NullEncryption();
        $objectWriter = new MemoryObjectWriter();
        $encryption->writeEncryptEntry($objectWriter);
        $this->assertSame('', $objectWriter->getData());
    }
}


================================================
FILE: test/Encryption/Pdf11EncryptionTest.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\Encryption;

use Bacon\Pdf\Encryption\Pdf11Encryption;
use Bacon\Pdf\Encryption\Permissions;

/**
 * @covers \Bacon\Pdf\Encryption\AbstractEncryption
 * @covers \Bacon\Pdf\Encryption\Pdf11Encryption
 */
class Pdf11EncryptionTest extends AbstractEncryptionTestCase
{
    /**
     * {@inheritdoc}
     */
    public function encryptionTestData()
    {
        return [
            'same-numbers' => ['test', 'foo', null, 1, 1],
            'changed-generation-number' => ['test', 'foo', null, 1, 2],
            'changed-object-number' => ['test', 'foo', null, 2, 1],
            'both-numbers-changed' => ['test', 'foo', null, 2, 2],
            'changed-user-password' => ['test', 'bar', null, 1, 1],
            'added-owner-password' => ['test', 'bar', 'baz', 1, 1],
        ];
    }

    /**
     * {@inheritdoc}
     */
    protected function createEncryption(
        $userPassword,
        $ownerPassword = null,
        Permissions $userPermissions = null
    ) {
        return new Pdf11Encryption(
            md5('test', true),
            $userPassword,
            $ownerPassword ?: $userPassword,
            $userPermissions ?: Permissions::allowNothing()
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function decrypt($encryptedText, $key)
    {
        return openssl_decrypt($encryptedText, 'rc4', $key, OPENSSL_RAW_DATA);
    }

    /**
     * {@inheritdoc}
     */
    protected function getExpectedEntry()
    {
        return file_get_contents(__DIR__ . '/_files/pdf11-encrypt-entry.txt');
    }
}


================================================
FILE: test/Encryption/Pdf14EncryptionTest.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\Encryption;

use Bacon\Pdf\Encryption\Pdf14Encryption;
use Bacon\Pdf\Encryption\Permissions;

/**
 * @covers \Bacon\Pdf\Encryption\AbstractEncryption
 * @covers \Bacon\Pdf\Encryption\Pdf14Encryption
 */
class Pdf14EncryptionTest extends AbstractEncryptionTestCase
{
    /**
     * {@inheritdoc}
     */
    public function encryptionTestData()
    {
        return [
            'same-numbers' => ['test', 'foo', null, 1, 1],
            'changed-generation-number' => ['test', 'foo', null, 1, 2],
            'changed-object-number' => ['test', 'foo', null, 2, 1],
            'both-numbers-changed' => ['test', 'foo', null, 2, 2],
            'changed-user-password' => ['test', 'bar', null, 1, 1],
            'added-owner-password' => ['test', 'bar', 'baz', 1, 1],
        ];
    }

    /**
     * {@inheritdoc}
     */
    protected function createEncryption(
        $userPassword,
        $ownerPassword = null,
        Permissions $userPermissions = null
    ) {
        return new Pdf14Encryption(
            md5('test', true),
            $userPassword,
            $ownerPassword ?: $userPassword,
            $userPermissions ?: Permissions::allowNothing()
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function decrypt($encryptedText, $key)
    {
        return openssl_decrypt($encryptedText, 'rc4', $key, OPENSSL_RAW_DATA);
    }

    /**
     * {@inheritdoc}
     */
    protected function getExpectedEntry()
    {
        return file_get_contents(__DIR__ . '/_files/pdf14-encrypt-entry.txt');
    }
}


================================================
FILE: test/Encryption/Pdf16EncryptionTest.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\Encryption;

use Bacon\Pdf\Encryption\Pdf16Encryption;
use Bacon\Pdf\Encryption\Permissions;

/**
 * @covers \Bacon\Pdf\Encryption\AbstractEncryption
 * @covers \Bacon\Pdf\Encryption\Pdf16Encryption
 */
class Pdf16EncryptionTest extends AbstractEncryptionTestCase
{
    /**
     * {@inheritdoc}
     */
    public function encryptionTestData()
    {
        return [
            'same-numbers' => ['test', 'foo', null, 1, 1],
            'changed-generation-number' => ['test', 'foo', null, 1, 2],
            'changed-object-number' => ['test', 'foo', null, 2, 1],
            'both-numbers-changed' => ['test', 'foo', null, 2, 2],
            'changed-user-password' => ['test', 'bar', null, 1, 1],
            'added-owner-password' => ['test', 'bar', 'baz', 1, 1],
        ];
    }

    /**
     * {@inheritdoc}
     */
    protected function createEncryption(
        $userPassword,
        $ownerPassword = null,
        Permissions $userPermissions = null
    ) {
        return new Pdf16Encryption(
            md5('test', true),
            $userPassword,
            $ownerPassword ?: $userPassword,
            $userPermissions ?: Permissions::allowNothing()
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function decrypt($encryptedText, $key)
    {
        return openssl_decrypt(
            substr($encryptedText, 16),
            'aes-128-cbc',
            $key,
            OPENSSL_RAW_DATA,
            substr($encryptedText, 0, 16)
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function getExpectedEntry()
    {
        return file_get_contents(__DIR__ . '/_files/pdf16-encrypt-entry.txt');
    }
}


================================================
FILE: test/Encryption/PermissionsTest.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\Encryption;

use Bacon\Pdf\Encryption\Permissions;
use PHPUnit_Framework_TestCase as TestCase;
use ReflectionClass;

/**
 * @covers \Bacon\Pdf\Encryption\Permissions
 */
class PermissionsTest extends TestCase
{
    public function testZeroPermissions()
    {
        $permissions = Permissions::allowNothing();
        $this->assertSame(0, $permissions->toInt(2));
        $this->assertSame(0, $permissions->toInt(3));
    }

    public function testFullPermissions()
    {
        $permissions = Permissions::allowEverything();
        $this->assertSame(60, $permissions->toInt(2));
        $this->assertSame(3900, $permissions->toInt(3));
    }

    /**
     * @dataProvider individualPermissions
     */
    public function testIndividualPermissions($flagPosition, $rev2Value, $rev3Value)
    {
        $args = array_fill(0, 8, false);
        $args[$flagPosition] = true;

        $reflectionClass = new ReflectionClass(Permissions::class);
        $permissions = $reflectionClass->newInstanceArgs($args);
        $this->assertSame($rev2Value, $permissions->toInt(2));
        $this->assertSame($rev3Value, $permissions->toInt(3));
    }

    /**
     * @return array
     */
    public function individualPermissions()
    {
        return [
            'may-print' => [0, 4, 4],
            'may-print-high-resolution' => [1, 0, 2048],
            'may-modify' => [2, 8, 8],
            'may-copy' => [3, 16, 16],
            'may-annotate' => [4, 32, 32],
            'may-fill-in-forms' => [5, 0, 256],
            'may-extract-for-accessibility' => [6, 0, 512],
            'may-assemble' => [7, 0, 1024],
        ];
    }
}


================================================
FILE: test/Encryption/_files/pdf11-encrypt-entry.txt
================================================
/Encrypt
<<
/Filter
/Standard
/V
1
/R
2
/O
<947319c0b0ba83c01223fc7a3c39ef0a88ac4cc19e6b9e86f889d81f56ba57c4>
/U
<9dce9fbdfab50815486c48b3d9d8bb48a3d6f86e3a3768a3e8c8fb2b074b0658>
/P
0
>>


================================================
FILE: test/Encryption/_files/pdf14-encrypt-entry.txt
================================================
/Encrypt
<<
/Filter
/Standard
/V
2
/R
3
/O
<85dafdd50f5179a0aacf58d9c59c34ab55274f38c85a0b2d9ae68606ecd290be>
/U
<f4590529d9ae74ae6f1f9be11963d7bb%x>
/P
0
/Length
128
>>


================================================
FILE: test/Encryption/_files/pdf16-encrypt-entry.txt
================================================
/Encrypt
<<
/Filter
/Standard
/V
4
/R
4
/O
<85dafdd50f5179a0aacf58d9c59c34ab55274f38c85a0b2d9ae68606ecd290be>
/U
<71f71069a89277d41eafa571fdeccaa0%x>
/P
0
/Length
128
/CF
<<
/StdCF
<<
/Type
/CryptFilter
/CFM
/AESV2
/Length
128
>>
>>
/StrF
/StdCF
/StmF
/StdCF
>>


================================================
FILE: test/TestHelper/MemoryObjectWriter.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\TestHelper;

use Bacon\Pdf\Exception\InvalidArgumentException;
use Bacon\Pdf\Writer\ObjectWriter;
use SplFileObject;

/**
 * This is a memory object writer which will ignore formatting for predictable test data.
 */
class MemoryObjectWriter extends ObjectWriter
{
    /**
     * @var SplFileObject
     */
    private $fileObject;

    /**
     * {@inheritdoc}
     */
    public function __construct()
    {
        $this->fileObject = new SplFileObject('php://memory', 'w+b');
    }

    /**
     * {@inheritdoc}
     */
    public function writeRawLine($data)
    {
        $this->fileObject->fwrite($data. "\n");
    }

    /**
     * {@inheritdoc}
     */
    public function currentOffset()
    {
        return $this->fileObject->ftell();
    }

    /**
     * {@inheritdoc}
     */
    public function startDictionary()
    {
        $this->fileObject->fwrite("<<\n");
    }

    /**
     * {@inheritdoc}
     */
    public function endDictionary()
    {
        $this->fileObject->fwrite(">>\n");
    }

    /**
     * {@inheritdoc}
     */
    public function startArray()
    {
        $this->fileObject->fwrite("]\n");
    }

    /**
     * {@inheritdoc}
     */
    public function endArray()
    {
        $this->fileObject->fwrite("[\n");
    }

    /**
     * {@inheritdoc}
     */
    public function writeNull()
    {
        $this->fileObject->fwrite("null\n");
    }

    /**
     * {@inheritdoc}
     */
    public function writeBoolean($boolean)
    {
        $this->fileObject->fwrite(($boolean ? 'true' : 'false') . "\n");
    }

    /**
     * {@inheritdoc}
     */
    public function writeNumber($number)
    {
        if (is_int($number)) {
            $value = (string) $number;
        } elseif (is_float($number)) {
            $value = sprintf('%F', $number);
        } else {
            throw new InvalidArgumentException(sprintf(
                'Expected int or float, got %s',
                gettype($number)
            ));
        }

        $this->fileObject->fwrite($value . "\n");
    }

    /**
     * {@inheritdoc}
     */
    public function writeName($name)
    {
        $this->fileObject->fwrite('/' . $name . "\n");
    }

    /**
     * {@inheritdoc}
     */
    public function writeLiteralString($string)
    {
        $this->fileObject->fwrite('(' . strtr($string, ['(' => '\\(', ')' => '\\)', '\\' => '\\\\']) . ")\n");
    }

    /**
     * {@inheritdoc}
     */
    public function writeHexadecimalString($string)
    {
        $this->fileObject->fwrite('<' . bin2hex($string) . ">\n");
    }

    /**
     * @return string
     */
    public function getData()
    {
        $currentPos = $this->fileObject->ftell();

        if ($currentPos === 0) {
            return '';
        }

        $this->fileObject->fseek(0);
        $data = $this->fileObject->fread($currentPos);
        $this->fileObject->fseek($currentPos);

        return $data;
    }
}


================================================
FILE: test/Writer/ObjectWriterTest.php
================================================
<?php
/**
 * BaconPdf
 *
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
 * @copyright 2015 Ben Scholzen (DASPRiD)
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace Bacon\PdfTest\Writer;

use Bacon\Pdf\Writer\ObjectWriter;
use PHPUnit_Framework_TestCase as TestCase;
use SplFileObject;

/**
 * @covers \Bacon\Pdf\Writer\ObjectWriter
 */
class ObjectWriterTest extends TestCase
{
    /**
     * @var SplFileObject
     */
    private $fileObject;

    /**
     * @var ObjectWriter
     */
    private $objectWriter;

    /**
     * {@inheritdoc}
     */
    public function setUp()
    {
        $this->fileObject = new SplFileObject('php://memory', 'w+b');
        $this->objectWriter = new ObjectWriter($this->fileObject);
    }

    public function testGetCurrentOffset()
    {
        $this->assertSame(0, $this->objectWriter->getCurrentOffset());
        $this->fileObject->fwrite('foo');
        $this->assertSame(3, $this->objectWriter->getCurrentOffset());
    }

    public function testObjectNumberAllocation()
    {
        $this->assertSame(1, $this->objectWriter->allocateObjectId());
        $this->assertSame(2, $this->objectWriter->allocateObjectId());
        $this->assertSame(3, $this->objectWriter->allocateObjectId());
    }

    public function testGetObjectOffsets()
    {
        $this->objectWriter->startObject();
        $this->objectWriter->startObject();
        $this->objectWriter->startObject();

        $this->assertSame([1 => 0, 2 => 8, 3 => 16], $this->objectWriter->getObjectOffsets());
    }

    public function testStartObjectWithoutObjectId()
    {
        $this->objectWriter->startObject();
        $this->objectWriter->startObject();
        $this->assertSame("1 0 obj\n2 0 obj\n", $this->getFileObjectData());
    }

    public function testStartObjectWithObjectId()
    {
        $this->objectWriter->startObject(10);
        $this->assertSame("10 0 obj\n", $this->getFileObjectData());
    }

    public function testWriteIndirectReference()
    {
        $this->objectWriter->writeIndirectReference(1);
        $this->assertSame('1 0 R', $this->getFileObjectData());
    }

    public function testEndObject()
    {
        $this->objectWriter->endObject();
        $this->assertSame("\nendobj\n", $this->getFileObjectData());
    }

    public function testWriteRawLine()
    {
        $this->objectWriter->writeRawLine('foo');
        $this->assertSame("foo\n", $this->getFileObjectData());
    }

    public function testStartDictionary()
    {
        $this->objectWriter->startDictionary();
        $this->assertSame('<<', $this->getFileObjectData());
    }

    public function testEndDictionary()
    {
        $this->objectWriter->endDictionary();
        $this->assertSame('>>', $this->getFileObjectData());
    }

    public function testStartArray()
    {
        $this->objectWriter->startArray();
        $this->assertSame('[', $this->getFileObjectData());
    }

    public function testEndArray()
    {
        $this->objectWriter->endArray();
        $this->assertSame(']', $this->getFileObjectData());
    }

    public function testWriteNull()
    {
        $this->objectWriter->writeNull();
        $this->assertSame('null', $this->getFileObjectData());
    }

    public function testWriteBooleanTrue()
    {
        $this->objectWriter->writeBoolean(true);
        $this->assertSame('true', $this->getFileObjectData());
    }

    public function testWriteBooleanFalse()
    {
        $this->objectWriter->writeBoolean(false);
        $this->assertSame('false', $this->getFileObjectData());
    }

    public function testWriteIntegerNumber()
    {
        $this->objectWriter->writeNumber(0);
        $this->assertSame('0', $this->getFileObjectData());
        $this->objectWriter->writeNumber(12);
        $this->assertSame('0 12', $this->getFileObjectData());
        $this->objectWriter->writeNumber(0);
        $this->assertSame('0 12 0', $this->getFileObjectData());
    }

    public function testWriteFloatNumber()
    {
        $this->objectWriter->writeNumber(12.3456789123);
        $this->assertSame('12.345679', $this->getFileObjectData());
        $this->objectWriter->writeNumber(12.);
        $this->assertSame('12.345679 12', $this->getFileObjectData());
    }

    public function testWriteName()
    {
        $this->objectWriter->writeName('foo');
        $this->assertSame('/foo', $this->getFileObjectData());
    }

    public function testWriteLiteralString()
    {
        $this->objectWriter->writeLiteralString('foo(bar\\baz)bat');
        $this->assertSame('(foo\\(bar\\\\baz\\)bat)', $this->getFileObjectData());
    }

    public function testWriteHexadecimalString()
    {
        $this->objectWriter->writeHexadecimalString('foo');
        $this->assertSame('<666f6f>', $this->getFileObjectData());
    }

    /**
     * @dataProvider whitespaceTestData
     */
    public function testWhitespaceHandling(array $methodCalls, $expectedData)
    {
        foreach ($methodCalls as $methodCall) {
            if (!array_key_exists(1, $methodCall)) {
                $methodCall[1] = [];
            }

            call_user_func_array([$this->objectWriter, $methodCall[0]], $methodCall[1]);
        }

        $this->assertSame($expectedData, $this->getFileObjectData());
    }

    /**
     * @return array
     */
    public function whitespaceTestData()
    {
        return [
            [[
                ['writeIndirectReference', [1]],
                ['writeIndirectReference', [2]],
            ], '1 0 R 2 0 R'],
            [[
                ['startDictionary'],
                ['endDictionary'],
            ], '<<>>'],
            [[
                ['startArray'],
                ['endArray'],
            ], '[]'],
            [[
                ['startDictionary'],
                ['writeNull'],
                ['endDictionary'],
            ], '<<null>>'],
            [[
                ['startArray'],
                ['writeNull'],
                ['endArray'],
            ], '[null]'],
            [[
                ['writeNull'],
                ['writeNull'],
            ], 'null null'],
            [[
                ['writeBoolean', [true]],
                ['writeBoolean', [false]],
            ], 'true false'],
            [[
                ['writeNumber', [1]],
                ['writeNumber', [1.1]],
            ], '1 1.1'],
            [[
                ['writeNumber', [0]],
                ['writeNumber', [0.0]],
            ], '0 0'],
            [[
                ['writeLiteralString', ['foo']],
                ['writeLiteralString', ['bar']],
            ], '(foo)(bar)'],
            [[
                ['writeHexadecimalString', ['foo']],
                ['writeHexadecimalString', ['bar']],
            ], '<666f6f><626172>'],
            [[
                ['writeNull'],
                ['writeRawLine', ['foo']],
            ], "nullfoo\n"],
        ];
    }

    /**
     * @return string
     */
    private function getFileObjectData()
    {
        $offset = $this->fileObject->ftell();
        $this->fileObject->fseek(0);
        $data = $this->fileObject->fread($offset);
        $this->fileObject->fseek($offset);
        return $data;
    }
}
Download .txt
gitextract_s1qjpede/

├── .coveralls.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── benchmark/
│   └── ObjectWriterEvent.php
├── composer.json
├── doc/
│   ├── .gitignore
│   ├── Makefile
│   ├── _static/
│   │   └── theme_overrides.css
│   ├── conf.py
│   ├── index.rst
│   └── metadata.rst
├── example/
│   ├── .gitignore
│   └── empty-page.php
├── phpcs.xml
├── phpunit.xml.dist
├── src/
│   ├── DocumentInformation.php
│   ├── Encryption/
│   │   ├── AbstractEncryption.php
│   │   ├── BitMask.php
│   │   ├── EncryptionInterface.php
│   │   ├── NullEncryption.php
│   │   ├── Pdf11Encryption.php
│   │   ├── Pdf14Encryption.php
│   │   ├── Pdf16Encryption.php
│   │   └── Permissions.php
│   ├── Exception/
│   │   ├── DomainException.php
│   │   ├── ExceptionInterface.php
│   │   ├── InvalidArgumentException.php
│   │   ├── OutOfBoundsException.php
│   │   ├── OutOfRangeException.php
│   │   ├── RuntimeException.php
│   │   ├── UnexpectedValueException.php
│   │   ├── UnsupportedPasswordException.php
│   │   └── WriterClosedException.php
│   ├── Options/
│   │   ├── EncryptionOptions.php
│   │   ├── PdfWriterOptions.php
│   │   └── RasterImageOptions.php
│   ├── Page.php
│   ├── PdfWriter.php
│   ├── RasterImage.php
│   ├── Rectangle.php
│   ├── Utils/
│   │   └── StringUtils.php
│   └── Writer/
│       ├── DocumentWriter.php
│       ├── ObjectWriter.php
│       └── PageWriter.php
└── test/
    ├── Encryption/
    │   ├── AbstractEncryptionTest.php
    │   ├── AbstractEncryptionTestCase.php
    │   ├── BitMaskTest.php
    │   ├── NullEncryptionTest.php
    │   ├── Pdf11EncryptionTest.php
    │   ├── Pdf14EncryptionTest.php
    │   ├── Pdf16EncryptionTest.php
    │   ├── PermissionsTest.php
    │   └── _files/
    │       ├── pdf11-encrypt-entry.txt
    │       ├── pdf14-encrypt-entry.txt
    │       └── pdf16-encrypt-entry.txt
    ├── TestHelper/
    │   └── MemoryObjectWriter.php
    └── Writer/
        └── ObjectWriterTest.php
Download .txt
SYMBOL INDEX (244 symbols across 41 files)

FILE: benchmark/ObjectWriterEvent.php
  class ObjectWriterEvent (line 16) | class ObjectWriterEvent extends AthleticEvent
    method setUp (line 26) | public function setUp()
    method writeRawLine (line 34) | public function writeRawLine()
    method startDictionary (line 42) | public function startDictionary()
    method endDictionary (line 50) | public function endDictionary()
    method startArray (line 58) | public function startArray()
    method endArray (line 66) | public function endArray()
    method writeNull (line 74) | public function writeNull()
    method writeBoolean (line 82) | public function writeBoolean()
    method writeIntegerNumber (line 90) | public function writeIntegerNumber()
    method writeFloatNumber (line 98) | public function writeFloatNumber()
    method writeName (line 106) | public function writeName()
    method writeLiteralString (line 114) | public function writeLiteralString()
    method writeHexadecimalString (line 122) | public function writeHexadecimalString()

FILE: doc/conf.py
  function setup (line 291) | def setup(app):

FILE: src/DocumentInformation.php
  class DocumentInformation (line 18) | final class DocumentInformation
    method set (line 39) | public function set($key, $value)
    method remove (line 62) | public function remove($key)
    method has (line 73) | public function has($key)
    method get (line 89) | public function get($key)
    method getCreationDate (line 105) | public function getCreationDate()
    method getModificationDate (line 113) | public function getModificationDate()
    method writeInfoDictionary (line 124) | public function writeInfoDictionary(ObjectWriter $objectWriter)
    method retrieveDate (line 154) | private function retrieveDate($key)

FILE: src/Encryption/AbstractEncryption.php
  class AbstractEncryption (line 18) | abstract class AbstractEncryption implements EncryptionInterface
    method __construct (line 51) | public function __construct(
    method forPdfVersion (line 109) | public static function forPdfVersion($pdfVersion, $permanentFileIdenti...
    method writeEncryptEntry (line 130) | public function writeEncryptEntry(ObjectWriter $objectWriter)
    method writeAdditionalEncryptDictionaryEntries (line 163) | protected function writeAdditionalEncryptDictionaryEntries(ObjectWrite...
    method getRevision (line 172) | abstract protected function getRevision();
    method getAlgorithm (line 179) | abstract protected function getAlgorithm();
    method getKeyLength (line 188) | abstract protected function getKeyLength();
    method computeIndividualEncryptionKey (line 197) | protected function computeIndividualEncryptionKey($objectNumber, $gene...
    method encodePassword (line 214) | private function encodePassword($password)
    method computeEncryptionKey (line 244) | private function computeEncryptionKey(
    method computeOwnerEntry (line 284) | private function computeOwnerEntry($ownerPassword, $userPassword, $rev...
    method computeUserEntryRev2 (line 316) | private function computeUserEntryRev2($userPassword, $ownerEntry, $use...
    method computeUserEntryRev3OrGreater (line 344) | private function computeUserEntryRev3OrGreater(
    method applyRc4Loop (line 379) | private function applyRc4Loop($value, $key, $keyLength)

FILE: src/Encryption/BitMask.php
  class BitMask (line 15) | final class BitMask
    method set (line 28) | public function set($bit, $value)
    method toInt (line 41) | public function toInt()

FILE: src/Encryption/EncryptionInterface.php
  type EncryptionInterface (line 14) | interface EncryptionInterface
    method encrypt (line 24) | public function encrypt($plaintext, $objectNumber, $generationNumber);
    method writeEncryptEntry (line 31) | public function writeEncryptEntry(ObjectWriter $objectWriter);

FILE: src/Encryption/NullEncryption.php
  class NullEncryption (line 17) | final class NullEncryption implements EncryptionInterface
    method encrypt (line 22) | public function encrypt($plaintext, $objectNumber, $generationNumber)
    method writeEncryptEntry (line 30) | public function writeEncryptEntry(ObjectWriter $objectWriter)

FILE: src/Encryption/Pdf11Encryption.php
  class Pdf11Encryption (line 15) | class Pdf11Encryption extends AbstractEncryption
    method encrypt (line 20) | public function encrypt($plaintext, $objectNumber, $generationNumber)
    method getRevision (line 33) | protected function getRevision()
    method getAlgorithm (line 41) | protected function getAlgorithm()
    method getKeyLength (line 49) | protected function getKeyLength()

FILE: src/Encryption/Pdf14Encryption.php
  class Pdf14Encryption (line 17) | class Pdf14Encryption extends Pdf11Encryption
    method writeAdditionalEncryptDictionaryEntries (line 22) | protected function writeAdditionalEncryptDictionaryEntries(ObjectWrite...
    method getRevision (line 33) | protected function getRevision()
    method getAlgorithm (line 41) | protected function getAlgorithm()
    method getKeyLength (line 49) | protected function getKeyLength()

FILE: src/Encryption/Pdf16Encryption.php
  class Pdf16Encryption (line 17) | class Pdf16Encryption extends Pdf14Encryption
    method writeAdditionalEncryptDictionaryEntries (line 22) | protected function writeAdditionalEncryptDictionaryEntries(ObjectWrite...
    method encrypt (line 54) | public function encrypt($plaintext, $objectNumber, $generationNumber)
    method getRevision (line 70) | protected function getRevision()
    method getAlgorithm (line 78) | protected function getAlgorithm()

FILE: src/Encryption/Permissions.php
  class Permissions (line 15) | final class Permissions
    method __construct (line 67) | public function __construct(
    method allowNothing (line 92) | public static function allowNothing()
    method allowEverything (line 102) | public static function allowEverything()
    method toInt (line 116) | public function toInt($revision)

FILE: src/Exception/DomainException.php
  class DomainException (line 12) | class DomainException extends \DomainException implements ExceptionInter...

FILE: src/Exception/ExceptionInterface.php
  type ExceptionInterface (line 12) | interface ExceptionInterface

FILE: src/Exception/InvalidArgumentException.php
  class InvalidArgumentException (line 12) | class InvalidArgumentException extends \InvalidArgumentException impleme...

FILE: src/Exception/OutOfBoundsException.php
  class OutOfBoundsException (line 12) | class OutOfBoundsException extends \OutOfBoundsException implements Exce...

FILE: src/Exception/OutOfRangeException.php
  class OutOfRangeException (line 12) | class OutOfRangeException extends \OutOfRangeException implements Except...

FILE: src/Exception/RuntimeException.php
  class RuntimeException (line 12) | class RuntimeException extends \RuntimeException implements ExceptionInt...

FILE: src/Exception/UnexpectedValueException.php
  class UnexpectedValueException (line 12) | class UnexpectedValueException extends \UnexpectedValueException impleme...

FILE: src/Exception/UnsupportedPasswordException.php
  class UnsupportedPasswordException (line 12) | class UnsupportedPasswordException extends \DomainException implements E...

FILE: src/Exception/WriterClosedException.php
  class WriterClosedException (line 12) | class WriterClosedException extends \RuntimeException implements Excepti...

FILE: src/Options/EncryptionOptions.php
  class EncryptionOptions (line 14) | final class EncryptionOptions
    method __construct (line 36) | public function __construct($userPassword, $ownerPassword = null, Perm...
    method getUserPassword (line 46) | public function getUserPassword()
    method getOwnerPassword (line 54) | public function getOwnerPassword()
    method getUserPermissions (line 62) | public function getUserPermissions()

FILE: src/Options/PdfWriterOptions.php
  class PdfWriterOptions (line 17) | final class PdfWriterOptions
    method __construct (line 33) | public function __construct($pdfVersion = '1.7')
    method getPdfVersion (line 47) | public function getPdfVersion()
    method setEncryptionOptions (line 57) | public function setEncryptionOptions(EncryptionOptions $encryptionOpti...
    method getEncryption (line 66) | public function getEncryption($permanentFileIdentifier)

FILE: src/Options/RasterImageOptions.php
  class RasterImageOptions (line 12) | final class RasterImageOptions
    method __construct (line 24) | private function __construct($useLossyCompression = false, $lossyCompr...
    method useLossyCompression (line 30) | public function useLossyCompression()
    method getLossyCompressionQuality (line 35) | public function getLossyCompressionQuality()

FILE: src/Page.php
  class Page (line 14) | final class Page
    method __construct (line 26) | public function __construct(PageWriter $pageWriter, $width, $height)
    method setCropBox (line 37) | public function setCropBox(Rectangle $cropBox)
    method setBleedBox (line 47) | public function setBleedBox(Rectangle $bleedBox)
    method setTrimBox (line 57) | public function setTrimBox(Rectangle $trimBox)
    method setArtBox (line 67) | public function setArtBox(Rectangle $artBox)
    method rotate (line 79) | public function rotate($degrees)

FILE: src/PdfWriter.php
  class PdfWriter (line 19) | class PdfWriter
    method __construct (line 45) | public function __construct(SplFileObject $fileObject, PdfWriterOption...
    method getDocumentInformation (line 64) | public function getDocumentInformation()
    method addPage (line 76) | public function addPage($width, $height)
    method importRasterImage (line 92) | public function importRasterImage($filename, $useLossyCompression = fa...
    method endDocument (line 112) | public function endDocument()
    method toFile (line 124) | public static function toFile($filename, PdfWriterOptions $options = n...
    method output (line 137) | public static function output(PdfWriterOptions $options = null)

FILE: src/RasterImage.php
  class RasterImage (line 17) | final class RasterImage
    method __construct (line 43) | public function __construct(
    method getId (line 105) | public function getId()
    method getWidth (line 115) | public function getWidth()
    method getHeight (line 125) | public function getHeight()
    method determineFilter (line 135) | private function determineFilter($useLossyCompression, $pdfVersion)
    method determineColorSpace (line 155) | private function determineColorSpace(Imagick $image)
    method createShadowMask (line 179) | private function createShadowMask(ObjectWriter $objectWriter, Imagick ...
    method writeCommonDictionaryEntries (line 214) | private function writeCommonDictionaryEntries(ObjectWriter $objectWrit...
    method setFitlerParameters (line 249) | private function setFitlerParameters(Imagick $image, $filter, $colorSp...

FILE: src/Rectangle.php
  class Rectangle (line 17) | final class Rectangle
    method __construct (line 49) | public function __construct($x1, $y1, $x2, $y2)
    method writeRectangleArray (line 63) | public function writeRectangleArray(ObjectWriter $objectWriter)

FILE: src/Utils/StringUtils.php
  class StringUtils (line 14) | final class StringUtils
    method __construct (line 16) | public function __construct()
    method encodeString (line 26) | public static function encodeString($string)
    method formatDateTime (line 37) | public static function formatDateTime(DateTimeInterface $dateTime)

FILE: src/Writer/DocumentWriter.php
  class DocumentWriter (line 16) | class DocumentWriter
    method __construct (line 62) | public function __construct(ObjectWriter $objectWriter, PdfWriterOptio...
    method getDocumentInformation (line 80) | public function getDocumentInformation()
    method addPageWriter (line 90) | public function addPageWriter(PageWriter $pageWriter)
    method endDocument (line 100) | public function endDocument(EncryptionInterface $encryption)
    method closeRemainingPages (line 115) | private function closeRemainingPages()
    method writePageTree (line 126) | private function writePageTree()
    method writeDocumentInformation (line 156) | private function writeDocumentInformation()
    method writeDocumentCatalog (line 169) | private function writeDocumentCatalog()
    method writeCrossReferenceTable (line 190) | private function writeCrossReferenceTable()
    method writeTrailer (line 214) | private function writeTrailer($documentInformationId, $documentCatalog...
    method writeFooter (line 241) | private function writeFooter($xrefOffset)

FILE: src/Writer/ObjectWriter.php
  class ObjectWriter (line 26) | class ObjectWriter
    method __construct (line 51) | public function __construct(SplFileObject $fileObject)
    method getCurrentOffset (line 61) | public function getCurrentOffset()
    method writeRawLine (line 74) | public function writeRawLine($data)
    method writeRaw (line 84) | public function writeRaw($data)
    method getObjectOffsets (line 94) | public function getObjectOffsets()
    method allocateObjectId (line 104) | public function allocateObjectId()
    method startObject (line 117) | public function startObject($objectId = null)
    method endObject (line 132) | public function endObject()
    method startStream (line 140) | public function startStream()
    method endStream (line 145) | public function endStream()
    method writeIndirectReference (line 155) | public function writeIndirectReference($objectId)
    method startDictionary (line 169) | public function startDictionary()
    method endDictionary (line 178) | public function endDictionary()
    method startArray (line 187) | public function startArray()
    method endArray (line 196) | public function endArray()
    method writeNull (line 205) | public function writeNull()
    method writeBoolean (line 221) | public function writeBoolean($boolean)
    method writeNumber (line 238) | public function writeNumber($number)
    method writeName (line 254) | public function writeName($name)
    method writeLiteralString (line 268) | public function writeLiteralString($string)
    method writeHexadecimalString (line 279) | public function writeHexadecimalString($string)

FILE: src/Writer/PageWriter.php
  class PageWriter (line 15) | class PageWriter
    method __construct (line 45) | public function __construct(ObjectWriter $objectWriter)
    method setBox (line 57) | public function setBox($name, Rectangle $box)
    method setRotation (line 68) | public function setRotation($degrees)
    method appendContentStream (line 82) | public function appendContentStream($data)
    method writePage (line 94) | public function writePage(ObjectWriter $objectWriter, $pageTreeId)

FILE: test/Encryption/AbstractEncryptionTest.php
  class AbstractEncryptionTest (line 25) | class AbstractEncryptionTest extends TestCase
    method testForPdfVersion (line 27) | public function testForPdfVersion()
    method testTooLongishUserPassword (line 55) | public function testTooLongishUserPassword()
    method testTooLongishOwnerPassword (line 61) | public function testTooLongishOwnerPassword()
    method testUserPasswordWithInvalidCharacters (line 67) | public function testUserPasswordWithInvalidCharacters()
    method testOwnerPasswordWithInvalidCharacters (line 73) | public function testOwnerPasswordWithInvalidCharacters()
    method testAbstractReturnsInvalidKeyLength (line 79) | public function testAbstractReturnsInvalidKeyLength()
    method getAbstractEncryption (line 88) | private function getAbstractEncryption($keyLength = 128)

FILE: test/Encryption/AbstractEncryptionTestCase.php
  class AbstractEncryptionTestCase (line 21) | abstract class AbstractEncryptionTestCase extends TestCase
    method testEncrypt (line 31) | public function testEncrypt(
    method testWriteEncryptEntry (line 51) | public function testWriteEncryptEntry()
    method encryptionTestData (line 63) | abstract public function encryptionTestData();
    method createEncryption (line 71) | abstract protected function createEncryption(
    method decrypt (line 82) | abstract protected function decrypt($encryptedText, $key);
    method getExpectedEntry (line 87) | abstract protected function getExpectedEntry();

FILE: test/Encryption/BitMaskTest.php
  class BitMaskTest (line 18) | class BitMaskTest extends TestCase
    method testDefault (line 20) | public function testDefault()
    method testSetBit (line 26) | public function testSetBit()

FILE: test/Encryption/NullEncryptionTest.php
  class NullEncryptionTest (line 19) | class NullEncryptionTest extends TestCase
    method testEncryptReturnsPlaintext (line 21) | public function testEncryptReturnsPlaintext()
    method testWriteEncryptEntryWritesNothing (line 27) | public function testWriteEncryptEntryWritesNothing()

FILE: test/Encryption/Pdf11EncryptionTest.php
  class Pdf11EncryptionTest (line 19) | class Pdf11EncryptionTest extends AbstractEncryptionTestCase
    method encryptionTestData (line 24) | public function encryptionTestData()
    method createEncryption (line 39) | protected function createEncryption(
    method decrypt (line 55) | protected function decrypt($encryptedText, $key)
    method getExpectedEntry (line 63) | protected function getExpectedEntry()

FILE: test/Encryption/Pdf14EncryptionTest.php
  class Pdf14EncryptionTest (line 19) | class Pdf14EncryptionTest extends AbstractEncryptionTestCase
    method encryptionTestData (line 24) | public function encryptionTestData()
    method createEncryption (line 39) | protected function createEncryption(
    method decrypt (line 55) | protected function decrypt($encryptedText, $key)
    method getExpectedEntry (line 63) | protected function getExpectedEntry()

FILE: test/Encryption/Pdf16EncryptionTest.php
  class Pdf16EncryptionTest (line 19) | class Pdf16EncryptionTest extends AbstractEncryptionTestCase
    method encryptionTestData (line 24) | public function encryptionTestData()
    method createEncryption (line 39) | protected function createEncryption(
    method decrypt (line 55) | protected function decrypt($encryptedText, $key)
    method getExpectedEntry (line 69) | protected function getExpectedEntry()

FILE: test/Encryption/PermissionsTest.php
  class PermissionsTest (line 19) | class PermissionsTest extends TestCase
    method testZeroPermissions (line 21) | public function testZeroPermissions()
    method testFullPermissions (line 28) | public function testFullPermissions()
    method testIndividualPermissions (line 38) | public function testIndividualPermissions($flagPosition, $rev2Value, $...
    method individualPermissions (line 52) | public function individualPermissions()

FILE: test/TestHelper/MemoryObjectWriter.php
  class MemoryObjectWriter (line 19) | class MemoryObjectWriter extends ObjectWriter
    method __construct (line 29) | public function __construct()
    method writeRawLine (line 37) | public function writeRawLine($data)
    method currentOffset (line 45) | public function currentOffset()
    method startDictionary (line 53) | public function startDictionary()
    method endDictionary (line 61) | public function endDictionary()
    method startArray (line 69) | public function startArray()
    method endArray (line 77) | public function endArray()
    method writeNull (line 85) | public function writeNull()
    method writeBoolean (line 93) | public function writeBoolean($boolean)
    method writeNumber (line 101) | public function writeNumber($number)
    method writeName (line 120) | public function writeName($name)
    method writeLiteralString (line 128) | public function writeLiteralString($string)
    method writeHexadecimalString (line 136) | public function writeHexadecimalString($string)
    method getData (line 144) | public function getData()

FILE: test/Writer/ObjectWriterTest.php
  class ObjectWriterTest (line 19) | class ObjectWriterTest extends TestCase
    method setUp (line 34) | public function setUp()
    method testGetCurrentOffset (line 40) | public function testGetCurrentOffset()
    method testObjectNumberAllocation (line 47) | public function testObjectNumberAllocation()
    method testGetObjectOffsets (line 54) | public function testGetObjectOffsets()
    method testStartObjectWithoutObjectId (line 63) | public function testStartObjectWithoutObjectId()
    method testStartObjectWithObjectId (line 70) | public function testStartObjectWithObjectId()
    method testWriteIndirectReference (line 76) | public function testWriteIndirectReference()
    method testEndObject (line 82) | public function testEndObject()
    method testWriteRawLine (line 88) | public function testWriteRawLine()
    method testStartDictionary (line 94) | public function testStartDictionary()
    method testEndDictionary (line 100) | public function testEndDictionary()
    method testStartArray (line 106) | public function testStartArray()
    method testEndArray (line 112) | public function testEndArray()
    method testWriteNull (line 118) | public function testWriteNull()
    method testWriteBooleanTrue (line 124) | public function testWriteBooleanTrue()
    method testWriteBooleanFalse (line 130) | public function testWriteBooleanFalse()
    method testWriteIntegerNumber (line 136) | public function testWriteIntegerNumber()
    method testWriteFloatNumber (line 146) | public function testWriteFloatNumber()
    method testWriteName (line 154) | public function testWriteName()
    method testWriteLiteralString (line 160) | public function testWriteLiteralString()
    method testWriteHexadecimalString (line 166) | public function testWriteHexadecimalString()
    method testWhitespaceHandling (line 175) | public function testWhitespaceHandling(array $methodCalls, $expectedData)
    method whitespaceTestData (line 191) | public function whitespaceTestData()
    method getFileObjectData (line 250) | private function getFileObjectData()
Condensed preview — 59 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (130K chars).
[
  {
    "path": ".coveralls.yml",
    "chars": 74,
    "preview": "coverage_clover: clover.xml\njson_path: coveralls-upload.json\nsrc_dir: src\n"
  },
  {
    "path": ".gitignore",
    "chars": 34,
    "preview": "/nbproject\n/vendor\n/composer.lock\n"
  },
  {
    "path": ".travis.yml",
    "chars": 960,
    "preview": "sudo: false\n\nlanguage: php\n\ncache:\n  directories:\n    - $HOME/.composer/cache\n\nmatrix:\n  fast_finish: true\n  include:\n  "
  },
  {
    "path": "LICENSE",
    "chars": 1308,
    "preview": "Copyright (c) 2015, Ben Scholzen (DASPRiD)\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with"
  },
  {
    "path": "README.md",
    "chars": 1157,
    "preview": "# Bacon PDF\n\n[![Build Status](https://api.travis-ci.org/Bacon/BaconPdf.png?branch=master)](http://travis-ci.org/Bacon/Ba"
  },
  {
    "path": "benchmark/ObjectWriterEvent.php",
    "chars": 2295,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "composer.json",
    "chars": 947,
    "preview": "{\n    \"name\": \"bacon/bacon-pdf\",\n    \"description\": \"BaconPdf is a powerful PDF library.\",\n    \"license\" : \"BSD-2-Clause"
  },
  {
    "path": "doc/.gitignore",
    "chars": 7,
    "preview": "/_build"
  },
  {
    "path": "doc/Makefile",
    "chars": 7534,
    "preview": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD "
  },
  {
    "path": "doc/_static/theme_overrides.css",
    "chars": 235,
    "preview": "/* override table width restrictions */\n@media screen and (min-width: 767px) {\n    .wy-table-responsive table td {\n     "
  },
  {
    "path": "doc/conf.py",
    "chars": 9787,
    "preview": "# -*- coding: utf-8 -*-\n#\n# BaconPdf documentation build configuration file, created by\n# sphinx-quickstart on Sat Nov 2"
  },
  {
    "path": "doc/index.rst",
    "chars": 785,
    "preview": "Welcome to BaconPdf's documentation!\n====================================\n\n`BaconPdf`_ is a powerful yet simple PDF libr"
  },
  {
    "path": "doc/metadata.rst",
    "chars": 3060,
    "preview": "Handling document metadata\n==========================\n\nPDF documents can hold general information, like the document's t"
  },
  {
    "path": "example/.gitignore",
    "chars": 7,
    "preview": "/*.pdf\n"
  },
  {
    "path": "example/empty-page.php",
    "chars": 494,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "phpcs.xml",
    "chars": 301,
    "preview": "<?xml version=\"1.0\"?>\n<ruleset name=\"BaconPdf\">\n    <file>./src</file>\n    <file>./test</file>\n    \n    <rule ref=\"PSR2\""
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 826,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNam"
  },
  {
    "path": "src/DocumentInformation.php",
    "chars": 4505,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Encryption/AbstractEncryption.php",
    "chars": 11239,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Encryption/BitMask.php",
    "chars": 772,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Encryption/EncryptionInterface.php",
    "chars": 796,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Encryption/NullEncryption.php",
    "chars": 694,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Encryption/Pdf11Encryption.php",
    "chars": 1013,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Encryption/Pdf14Encryption.php",
    "chars": 1034,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Encryption/Pdf16Encryption.php",
    "chars": 1991,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Encryption/Permissions.php",
    "chars": 3179,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Exception/DomainException.php",
    "chars": 349,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Exception/ExceptionInterface.php",
    "chars": 301,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Exception/InvalidArgumentException.php",
    "chars": 367,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Exception/OutOfBoundsException.php",
    "chars": 359,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Exception/OutOfRangeException.php",
    "chars": 357,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Exception/RuntimeException.php",
    "chars": 351,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Exception/UnexpectedValueException.php",
    "chars": 367,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Exception/UnsupportedPasswordException.php",
    "chars": 362,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Exception/WriterClosedException.php",
    "chars": 356,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Options/EncryptionOptions.php",
    "chars": 1449,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Options/PdfWriterOptions.php",
    "chars": 1817,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Options/RasterImageOptions.php",
    "chars": 880,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Page.php",
    "chars": 2016,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/PdfWriter.php",
    "chars": 3877,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/RasterImage.php",
    "chars": 7731,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Rectangle.php",
    "chars": 1527,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Utils/StringUtils.php",
    "chars": 1078,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Writer/DocumentWriter.php",
    "chars": 6842,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Writer/ObjectWriter.php",
    "chars": 6589,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "src/Writer/PageWriter.php",
    "chars": 2877,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Encryption/AbstractEncryptionTest.php",
    "chars": 3590,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Encryption/AbstractEncryptionTestCase.php",
    "chars": 2627,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Encryption/BitMaskTest.php",
    "chars": 844,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Encryption/NullEncryptionTest.php",
    "chars": 961,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Encryption/Pdf11EncryptionTest.php",
    "chars": 1802,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Encryption/Pdf14EncryptionTest.php",
    "chars": 1802,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Encryption/Pdf16EncryptionTest.php",
    "chars": 1923,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Encryption/PermissionsTest.php",
    "chars": 1894,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Encryption/_files/pdf11-encrypt-entry.txt",
    "chars": 188,
    "preview": "/Encrypt\n<<\n/Filter\n/Standard\n/V\n1\n/R\n2\n/O\n<947319c0b0ba83c01223fc7a3c39ef0a88ac4cc19e6b9e86f889d81f56ba57c4>\n/U\n<9dce9f"
  },
  {
    "path": "test/Encryption/_files/pdf14-encrypt-entry.txt",
    "chars": 170,
    "preview": "/Encrypt\n<<\n/Filter\n/Standard\n/V\n2\n/R\n3\n/O\n<85dafdd50f5179a0aacf58d9c59c34ab55274f38c85a0b2d9ae68606ecd290be>\n/U\n<f45905"
  },
  {
    "path": "test/Encryption/_files/pdf16-encrypt-entry.txt",
    "chars": 262,
    "preview": "/Encrypt\n<<\n/Filter\n/Standard\n/V\n4\n/R\n4\n/O\n<85dafdd50f5179a0aacf58d9c59c34ab55274f38c85a0b2d9ae68606ecd290be>\n/U\n<71f710"
  },
  {
    "path": "test/TestHelper/MemoryObjectWriter.php",
    "chars": 3173,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  },
  {
    "path": "test/Writer/ObjectWriterTest.php",
    "chars": 7289,
    "preview": "<?php\n/**\n * BaconPdf\n *\n * @link      http://github.com/Bacon/BaconPdf For the canonical source repository\n * @copyrigh"
  }
]

About this extraction

This page contains the full source code of the Bacon/BaconPdf GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 59 files (118.6 KB), approximately 32.2k tokens, and a symbol index with 244 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!