mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
Add very basic support for generating PDF documents
Summary: Ref T13358. This is very minimal, but technically works. The eventual goal is to generate PDF invoices to make my life easier when I have to interact with Enterprise Vendor Procurement. Test Plan: {F6672439} Maniphest Tasks: T13358 Differential Revision: https://secure.phabricator.com/D20692
This commit is contained in:
parent
b81c8380fb
commit
f5c380bfc9
15 changed files with 625 additions and 0 deletions
|
@ -3878,7 +3878,21 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOwnersPathsSearchEngineAttachment' => 'applications/owners/engineextension/PhabricatorOwnersPathsSearchEngineAttachment.php',
|
||||
'PhabricatorOwnersSchemaSpec' => 'applications/owners/storage/PhabricatorOwnersSchemaSpec.php',
|
||||
'PhabricatorOwnersSearchField' => 'applications/owners/searchfield/PhabricatorOwnersSearchField.php',
|
||||
'PhabricatorPDFCatalogObject' => 'applications/phortune/pdf/PhabricatorPDFCatalogObject.php',
|
||||
'PhabricatorPDFContentsObject' => 'applications/phortune/pdf/PhabricatorPDFContentsObject.php',
|
||||
'PhabricatorPDFDocumentEngine' => 'applications/files/document/PhabricatorPDFDocumentEngine.php',
|
||||
'PhabricatorPDFFontObject' => 'applications/phortune/pdf/PhabricatorPDFFontObject.php',
|
||||
'PhabricatorPDFFragment' => 'applications/phortune/pdf/PhabricatorPDFFragment.php',
|
||||
'PhabricatorPDFFragmentOffset' => 'applications/phortune/pdf/PhabricatorPDFFragmentOffset.php',
|
||||
'PhabricatorPDFGenerator' => 'applications/phortune/pdf/PhabricatorPDFGenerator.php',
|
||||
'PhabricatorPDFHeadFragment' => 'applications/phortune/pdf/PhabricatorPDFHeadFragment.php',
|
||||
'PhabricatorPDFInfoObject' => 'applications/phortune/pdf/PhabricatorPDFInfoObject.php',
|
||||
'PhabricatorPDFIterator' => 'applications/phortune/pdf/PhabricatorPDFIterator.php',
|
||||
'PhabricatorPDFObject' => 'applications/phortune/pdf/PhabricatorPDFObject.php',
|
||||
'PhabricatorPDFPageObject' => 'applications/phortune/pdf/PhabricatorPDFPageObject.php',
|
||||
'PhabricatorPDFPagesObject' => 'applications/phortune/pdf/PhabricatorPDFPagesObject.php',
|
||||
'PhabricatorPDFResourcesObject' => 'applications/phortune/pdf/PhabricatorPDFResourcesObject.php',
|
||||
'PhabricatorPDFTailFragment' => 'applications/phortune/pdf/PhabricatorPDFTailFragment.php',
|
||||
'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php',
|
||||
'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php',
|
||||
'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php',
|
||||
|
@ -10101,7 +10115,24 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOwnersPathsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
'PhabricatorOwnersSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhabricatorOwnersSearchField' => 'PhabricatorSearchTokenizerField',
|
||||
'PhabricatorPDFCatalogObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFContentsObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFDocumentEngine' => 'PhabricatorDocumentEngine',
|
||||
'PhabricatorPDFFontObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFFragment' => 'Phobject',
|
||||
'PhabricatorPDFFragmentOffset' => 'Phobject',
|
||||
'PhabricatorPDFGenerator' => 'Phobject',
|
||||
'PhabricatorPDFHeadFragment' => 'PhabricatorPDFFragment',
|
||||
'PhabricatorPDFInfoObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFIterator' => array(
|
||||
'Phobject',
|
||||
'Iterator',
|
||||
),
|
||||
'PhabricatorPDFObject' => 'PhabricatorPDFFragment',
|
||||
'PhabricatorPDFPageObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFPagesObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFResourcesObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFTailFragment' => 'PhabricatorPDFFragment',
|
||||
'PhabricatorPHDConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPHID' => 'Phobject',
|
||||
'PhabricatorPHIDConstants' => 'Phobject',
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFCatalogObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $pagesObject;
|
||||
|
||||
public function setPagesObject(PhabricatorPDFPagesObject $pages_object) {
|
||||
$this->pagesObject = $this->newChildObject($pages_object);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPagesObject() {
|
||||
return $this->pagesObject;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/Type /Catalog');
|
||||
|
||||
$pages_object = $this->getPagesObject();
|
||||
if ($pages_object) {
|
||||
$this->writeLine('/Pages %d 0 R', $pages_object->getObjectIndex());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFContentsObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $rawContent;
|
||||
|
||||
public function setRawContent($raw_content) {
|
||||
$this->rawContent = $raw_content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRawContent() {
|
||||
return $this->rawContent;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$data = $this->getRawContent();
|
||||
|
||||
$stream_length = $this->newStream($data);
|
||||
|
||||
$this->writeLine('/Filter /FlateDecode /Length %d', $stream_length);
|
||||
}
|
||||
|
||||
}
|
14
src/applications/phortune/pdf/PhabricatorPDFFontObject.php
Normal file
14
src/applications/phortune/pdf/PhabricatorPDFFontObject.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFFontObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/Type /Font');
|
||||
|
||||
$this->writeLine('/BaseFont /Helvetica-Bold');
|
||||
$this->writeLine('/Subtype /Type1');
|
||||
$this->writeLine('/Encoding /WinAnsiEncoding');
|
||||
}
|
||||
|
||||
}
|
38
src/applications/phortune/pdf/PhabricatorPDFFragment.php
Normal file
38
src/applications/phortune/pdf/PhabricatorPDFFragment.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorPDFFragment
|
||||
extends Phobject {
|
||||
|
||||
private $rope;
|
||||
|
||||
public function getAsBytes() {
|
||||
$this->rope = new PhutilRope();
|
||||
|
||||
$this->writeFragment();
|
||||
|
||||
$rope = $this->rope;
|
||||
$this->rope = null;
|
||||
|
||||
return $rope->getAsString();
|
||||
}
|
||||
|
||||
public function hasRefTableEntry() {
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract protected function writeFragment();
|
||||
|
||||
final protected function writeLine($pattern) {
|
||||
$pattern = $pattern."\n";
|
||||
|
||||
$argv = func_get_args();
|
||||
$argv[0] = $pattern;
|
||||
|
||||
$line = call_user_func_array('sprintf', $argv);
|
||||
|
||||
$this->rope->append($line);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFFragmentOffset
|
||||
extends Phobject {
|
||||
|
||||
private $fragment;
|
||||
private $offset;
|
||||
|
||||
public function setFragment(PhabricatorPDFFragment $fragment) {
|
||||
$this->fragment = $fragment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFragment() {
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
public function setOffset($offset) {
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOffset() {
|
||||
return $this->offset;
|
||||
}
|
||||
|
||||
}
|
59
src/applications/phortune/pdf/PhabricatorPDFGenerator.php
Normal file
59
src/applications/phortune/pdf/PhabricatorPDFGenerator.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFGenerator
|
||||
extends Phobject {
|
||||
|
||||
private $objects = array();
|
||||
private $hasIterator = false;
|
||||
|
||||
private $infoObject;
|
||||
private $catalogObject;
|
||||
|
||||
public function addObject(PhabricatorPDFObject $object) {
|
||||
if ($this->hasIterator) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This generator has already emitted an iterator. You can not '.
|
||||
'modify the PDF document after you begin writing it.'));
|
||||
}
|
||||
|
||||
$this->objects[] = $object;
|
||||
$index = count($this->objects);
|
||||
|
||||
$object->setGenerator($this, $index);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getObjects() {
|
||||
return $this->objects;
|
||||
}
|
||||
|
||||
public function newIterator() {
|
||||
$this->hasIterator = true;
|
||||
return id(new PhabricatorPDFIterator())
|
||||
->setGenerator($this);
|
||||
}
|
||||
|
||||
public function setInfoObject(PhabricatorPDFInfoObject $info_object) {
|
||||
$this->addObject($info_object);
|
||||
$this->infoObject = $info_object;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInfoObject() {
|
||||
return $this->infoObject;
|
||||
}
|
||||
|
||||
public function setCatalogObject(
|
||||
PhabricatorPDFCatalogObject $catalog_object) {
|
||||
$this->addObject($catalog_object);
|
||||
$this->catalogObject = $catalog_object;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCatalogObject() {
|
||||
return $this->catalogObject;
|
||||
}
|
||||
|
||||
}
|
10
src/applications/phortune/pdf/PhabricatorPDFHeadFragment.php
Normal file
10
src/applications/phortune/pdf/PhabricatorPDFHeadFragment.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFHeadFragment
|
||||
extends PhabricatorPDFFragment {
|
||||
|
||||
protected function writeFragment() {
|
||||
$this->writeLine('%s', '%PDF-1.3');
|
||||
}
|
||||
|
||||
}
|
11
src/applications/phortune/pdf/PhabricatorPDFInfoObject.php
Normal file
11
src/applications/phortune/pdf/PhabricatorPDFInfoObject.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFInfoObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
final protected function writeObject() {
|
||||
$this->writeLine('/Producer (Phabricator 20190801)');
|
||||
$this->writeLine('/CreationDate (D:%s)', date('YmdHis'));
|
||||
}
|
||||
|
||||
}
|
103
src/applications/phortune/pdf/PhabricatorPDFIterator.php
Normal file
103
src/applications/phortune/pdf/PhabricatorPDFIterator.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFIterator
|
||||
extends Phobject
|
||||
implements Iterator {
|
||||
|
||||
private $generator;
|
||||
private $hasRewound;
|
||||
|
||||
private $fragments;
|
||||
private $fragmentKey;
|
||||
private $fragmentBytes;
|
||||
private $fragmentOffsets = array();
|
||||
private $byteLength;
|
||||
|
||||
public function setGenerator(PhabricatorPDFGenerator $generator) {
|
||||
if ($this->generator) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This iterator already has a generator. You can not modify the '.
|
||||
'generator for a given iterator.'));
|
||||
}
|
||||
|
||||
$this->generator = $generator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGenerator() {
|
||||
if (!$this->generator) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This PDF iterator has no associated PDF generator.'));
|
||||
}
|
||||
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
public function getFragmentOffsets() {
|
||||
return $this->fragmentOffsets;
|
||||
}
|
||||
|
||||
public function current() {
|
||||
return $this->fragmentBytes;
|
||||
}
|
||||
|
||||
public function key() {
|
||||
return $this->framgentKey;
|
||||
}
|
||||
|
||||
public function next() {
|
||||
$this->fragmentKey++;
|
||||
|
||||
if (!$this->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fragment = $this->fragments[$this->fragmentKey];
|
||||
|
||||
$this->fragmentOffsets[] = id(new PhabricatorPDFFragmentOffset())
|
||||
->setFragment($fragment)
|
||||
->setOffset($this->byteLength);
|
||||
|
||||
$bytes = $fragment->getAsBytes();
|
||||
|
||||
$this->fragmentBytes = $bytes;
|
||||
$this->byteLength += strlen($bytes);
|
||||
}
|
||||
|
||||
public function rewind() {
|
||||
if ($this->hasRewound) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'PDF iterators may not be rewound. Create a new iterator to emit '.
|
||||
'another PDF.'));
|
||||
}
|
||||
|
||||
$generator = $this->getGenerator();
|
||||
$objects = $generator->getObjects();
|
||||
|
||||
$this->fragments = array();
|
||||
$this->fragments[] = new PhabricatorPDFHeadFragment();
|
||||
|
||||
foreach ($objects as $object) {
|
||||
$this->fragments[] = $object;
|
||||
}
|
||||
|
||||
$this->fragments[] = id(new PhabricatorPDFTailFragment())
|
||||
->setIterator($this);
|
||||
|
||||
$this->hasRewound = true;
|
||||
|
||||
$this->fragmentKey = -1;
|
||||
$this->byteLength = 0;
|
||||
|
||||
$this->next();
|
||||
}
|
||||
|
||||
public function valid() {
|
||||
return isset($this->fragments[$this->fragmentKey]);
|
||||
}
|
||||
|
||||
}
|
95
src/applications/phortune/pdf/PhabricatorPDFObject.php
Normal file
95
src/applications/phortune/pdf/PhabricatorPDFObject.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorPDFObject
|
||||
extends PhabricatorPDFFragment {
|
||||
|
||||
private $generator;
|
||||
private $objectIndex;
|
||||
private $children = array();
|
||||
private $streams = array();
|
||||
|
||||
final public function hasRefTableEntry() {
|
||||
return true;
|
||||
}
|
||||
|
||||
final protected function writeFragment() {
|
||||
$this->writeLine('%d 0 obj', $this->getObjectIndex());
|
||||
$this->writeLine('<<');
|
||||
$this->writeObject();
|
||||
$this->writeLine('>>');
|
||||
|
||||
$streams = $this->streams;
|
||||
$this->streams = array();
|
||||
foreach ($streams as $stream) {
|
||||
$this->writeLine('stream');
|
||||
$this->writeLine('%s', $stream);
|
||||
$this->writeLine('endstream');
|
||||
}
|
||||
|
||||
$this->writeLine('endobj');
|
||||
}
|
||||
|
||||
final public function setGenerator(
|
||||
PhabricatorPDFGenerator $generator,
|
||||
$index) {
|
||||
|
||||
if ($this->getGenerator()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This PDF object is already registered with a PDF generator. You '.
|
||||
'can not register an object with more than one generator.'));
|
||||
}
|
||||
|
||||
$this->generator = $generator;
|
||||
$this->objectIndex = $index;
|
||||
|
||||
foreach ($this->getChildren() as $child) {
|
||||
$generator->addObject($child);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getGenerator() {
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
final public function getObjectIndex() {
|
||||
if (!$this->objectIndex) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Trying to get index for object ("%s") which has not been '.
|
||||
'registered with a generator.',
|
||||
get_class($this)));
|
||||
}
|
||||
|
||||
return $this->objectIndex;
|
||||
}
|
||||
|
||||
final protected function newChildObject(PhabricatorPDFObject $object) {
|
||||
if ($this->generator) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Trying to add a new PDF Object child after already registering '.
|
||||
'the object with a generator.'));
|
||||
}
|
||||
|
||||
$this->children[] = $object;
|
||||
return $object;
|
||||
}
|
||||
|
||||
private function getChildren() {
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
abstract protected function writeObject();
|
||||
|
||||
final protected function newStream($raw_data) {
|
||||
$stream_data = gzcompress($raw_data);
|
||||
|
||||
$this->streams[] = $stream_data;
|
||||
|
||||
return strlen($stream_data);
|
||||
}
|
||||
|
||||
}
|
48
src/applications/phortune/pdf/PhabricatorPDFPageObject.php
Normal file
48
src/applications/phortune/pdf/PhabricatorPDFPageObject.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFPageObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $pagesObject;
|
||||
private $contentsObject;
|
||||
private $resourcesObject;
|
||||
|
||||
public function setPagesObject(PhabricatorPDFPagesObject $pages) {
|
||||
$this->pagesObject = $pages;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setContentsObject(PhabricatorPDFContentsObject $contents) {
|
||||
$this->contentsObject = $this->newChildObject($contents);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setResourcesObject(PhabricatorPDFResourcesObject $resources) {
|
||||
$this->resourcesObject = $this->newChildObject($resources);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/Type /Page');
|
||||
|
||||
$pages_object = $this->pagesObject;
|
||||
$contents_object = $this->contentsObject;
|
||||
$resources_object = $this->resourcesObject;
|
||||
|
||||
if ($pages_object) {
|
||||
$pages_index = $pages_object->getObjectIndex();
|
||||
$this->writeLine('/Parent %d 0 R', $pages_index);
|
||||
}
|
||||
|
||||
if ($contents_object) {
|
||||
$contents_index = $contents_object->getObjectIndex();
|
||||
$this->writeLine('/Contents %d 0 R', $contents_index);
|
||||
}
|
||||
|
||||
if ($resources_object) {
|
||||
$resources_index = $resources_object->getObjectIndex();
|
||||
$this->writeLine('/Resources %d 0 R', $resources_index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
38
src/applications/phortune/pdf/PhabricatorPDFPagesObject.php
Normal file
38
src/applications/phortune/pdf/PhabricatorPDFPagesObject.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFPagesObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $pageObjects = array();
|
||||
|
||||
public function addPageObject(PhabricatorPDFPageObject $page) {
|
||||
$page->setPagesObject($this);
|
||||
$this->pageObjects[] = $this->newChildObject($page);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPageObjects() {
|
||||
return $this->pageObjects;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/Type /Pages');
|
||||
|
||||
$page_objects = $this->getPageObjects();
|
||||
|
||||
$this->writeLine('/Count %d', count($page_objects));
|
||||
$this->writeLine('/MediaBox [%d %d %0.2f %0.2f]', 0, 0, 595.28, 841.89);
|
||||
|
||||
if ($page_objects) {
|
||||
$kids = array();
|
||||
foreach ($page_objects as $page_object) {
|
||||
$kids[] = sprintf(
|
||||
'%d 0 R',
|
||||
$page_object->getObjectIndex());
|
||||
}
|
||||
|
||||
$this->writeLine('/Kids [%s]', implode(' ', $kids));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFResourcesObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $fontObjects = array();
|
||||
|
||||
public function addFontObject(PhabricatorPDFFontObject $font) {
|
||||
$this->fontObjects[] = $this->newChildObject($font);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFontObjects() {
|
||||
return $this->fontObjects;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
|
||||
|
||||
$fonts = $this->getFontObjects();
|
||||
foreach ($fonts as $font) {
|
||||
$this->writeLine('/Font <<');
|
||||
$this->writeLine('/F%d %d 0 R', 1, $font->getObjectIndex());
|
||||
$this->writeLine('>>');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
72
src/applications/phortune/pdf/PhabricatorPDFTailFragment.php
Normal file
72
src/applications/phortune/pdf/PhabricatorPDFTailFragment.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFTailFragment
|
||||
extends PhabricatorPDFFragment {
|
||||
|
||||
private $iterator;
|
||||
|
||||
public function setIterator(PhabricatorPDFIterator $iterator) {
|
||||
$this->iterator = $iterator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIterator() {
|
||||
return $this->iterator;
|
||||
}
|
||||
|
||||
protected function writeFragment() {
|
||||
$iterator = $this->getIterator();
|
||||
$generator = $iterator->getGenerator();
|
||||
$objects = $generator->getObjects();
|
||||
|
||||
$xref_offset = null;
|
||||
|
||||
$this->writeLine('xref');
|
||||
$this->writeLine('0 %d', count($objects) + 1);
|
||||
$this->writeLine('%010d %05d f ', 0, 0xFFFF);
|
||||
|
||||
$offset_map = array();
|
||||
|
||||
$fragment_offsets = $iterator->getFragmentOffsets();
|
||||
foreach ($fragment_offsets as $fragment_offset) {
|
||||
$fragment = $fragment_offset->getFragment();
|
||||
$offset = $fragment_offset->getOffset();
|
||||
|
||||
if ($fragment === $this) {
|
||||
$xref_offset = $offset;
|
||||
}
|
||||
|
||||
if (!$fragment->hasRefTableEntry()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$offset_map[$fragment->getObjectIndex()] = $offset;
|
||||
}
|
||||
|
||||
ksort($offset_map);
|
||||
|
||||
foreach ($offset_map as $offset) {
|
||||
$this->writeLine('%010d %05d n ', $offset, 0);
|
||||
}
|
||||
|
||||
$this->writeLine('trailer');
|
||||
$this->writeLine('<<');
|
||||
$this->writeLine('/Size %d', count($objects) + 1);
|
||||
|
||||
$info_object = $generator->getInfoObject();
|
||||
if ($info_object) {
|
||||
$this->writeLine('/Info %d 0 R', $info_object->getObjectIndex());
|
||||
}
|
||||
|
||||
$catalog_object = $generator->getCatalogObject();
|
||||
if ($catalog_object) {
|
||||
$this->writeLine('/Root %d 0 R', $catalog_object->getObjectIndex());
|
||||
}
|
||||
|
||||
$this->writeLine('>>');
|
||||
$this->writeLine('startxref');
|
||||
$this->writeLine('%d', $xref_offset);
|
||||
$this->writeLine('%s', '%%EOF');
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue