PageRenderTime 75ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/filemanager/tp/dompdf/lib/class.pdf.php

https://github.com/muchael/expressolivre
PHP | 5635 lines | 2624 code | 1946 blank | 1065 comment | 608 complexity | 070711c7aa2ed1ae6fc7a337afeaaab9 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-2-Clause, BSD-3-Clause, AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Cpdf
  4. *
  5. * http://www.ros.co.nz/pdf
  6. *
  7. * A PHP class to provide the basic functionality to create a pdf document without
  8. * any requirement for additional modules.
  9. *
  10. * Note that they companion class CezPdf can be used to extend this class and dramatically
  11. * simplify the creation of documents.
  12. *
  13. * Extended by Orion Richardson to support Unicode / UTF-8 characters using
  14. * TCPDF and others as a guide.
  15. *
  16. * IMPORTANT NOTE
  17. * there is no warranty, implied or otherwise with this software.
  18. *
  19. * LICENCE
  20. * This code has been placed in the Public Domain for all to enjoy.
  21. *
  22. * @author Wayne Munro <pdf@ros.co.nz>
  23. * @contributor Orion Richardson <orionr@yahoo.com>
  24. * @contributor Helmut Tischer <htischer@weihenstephan.org>
  25. * @version 009
  26. * @package Cpdf
  27. *
  28. * Changes
  29. * @contributor Helmut Tischer <htischer@weihenstephan.org>
  30. * @version 0.5.1.htischer.20090507
  31. * - On multiple identical png and jpg images, put only one copy into the pdf file and refer to it.
  32. * This reduces file size and rendering time.
  33. * - Allow font metrics cache to be a different folder as the font metrics. This allows a read only installation.
  34. * - Allow adding images directly from a gd object. This increases performance by avoiding temporary files.
  35. * - On png image files remove alpa channel to allow display of typical png files in pdf.
  36. * - On addImage avoid temporary file. Todo: Duplicate Image (currently not used)
  37. * - Add a check function, whether image is already cached, This avoids double creation by caller which saves
  38. * CPU time and memory.
  39. * @contributor Helmut Tischer <htischer@weihenstephan.org>
  40. * @version dompdf_trunk_with_helmut_mods.20090524
  41. * - Allow temp and fontcache folders to be passed in by class creator
  42. * @version dompdf_trunk_with_helmut_mods.20090528
  43. * - typo 'decent' instead of 'descent' at various locations made getFontDescender worthless
  44. */
  45. class Cpdf {
  46. /**
  47. * the current number of pdf objects in the document
  48. */
  49. public $numObj = 0;
  50. /**
  51. * this array contains all of the pdf objects, ready for final assembly
  52. */
  53. public $objects = array();
  54. /**
  55. * the objectId (number within the objects array) of the document catalog
  56. */
  57. public $catalogId;
  58. /**
  59. * array carrying information about the fonts that the system currently knows about
  60. * used to ensure that a font is not loaded twice, among other things
  61. */
  62. public $fonts = array();
  63. /**
  64. * a record of the current font
  65. */
  66. public $currentFont = '';
  67. /**
  68. * the current base font
  69. */
  70. public $currentBaseFont = '';
  71. /**
  72. * the number of the current font within the font array
  73. */
  74. public $currentFontNum = 0;
  75. /**
  76. *
  77. */
  78. public $currentNode;
  79. /**
  80. * object number of the current page
  81. */
  82. public $currentPage;
  83. /**
  84. * object number of the currently active contents block
  85. */
  86. public $currentContents;
  87. /**
  88. * number of fonts within the system
  89. */
  90. public $numFonts = 0;
  91. /**
  92. * Number of graphic state resources used
  93. */
  94. private $numStates = 0;
  95. /**
  96. * current colour for fill operations, defaults to inactive value, all three components should be between 0 and 1 inclusive when active
  97. */
  98. public $currentColour = array('r'=>-1, 'g'=>-1, 'b'=>-1);
  99. /**
  100. * current colour for stroke operations (lines etc.)
  101. */
  102. public $currentStrokeColour = array('r'=>-1, 'g'=>-1, 'b'=>-1);
  103. /**
  104. * current style that lines are drawn in
  105. */
  106. public $currentLineStyle = '';
  107. /**
  108. * current line transparency (partial graphics state)
  109. */
  110. public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
  111. /**
  112. * current fill transparency (partial graphics state)
  113. */
  114. public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
  115. /**
  116. * an array which is used to save the state of the document, mainly the colours and styles
  117. * it is used to temporarily change to another state, the change back to what it was before
  118. */
  119. public $stateStack = array();
  120. /**
  121. * number of elements within the state stack
  122. */
  123. public $nStateStack = 0;
  124. /**
  125. * number of page objects within the document
  126. */
  127. public $numPages = 0;
  128. /**
  129. * object Id storage stack
  130. */
  131. public $stack = array();
  132. /**
  133. * number of elements within the object Id storage stack
  134. */
  135. public $nStack = 0;
  136. /**
  137. * an array which contains information about the objects which are not firmly attached to pages
  138. * these have been added with the addObject function
  139. */
  140. public $looseObjects = array();
  141. /**
  142. * array contains infomation about how the loose objects are to be added to the document
  143. */
  144. public $addLooseObjects = array();
  145. /**
  146. * the objectId of the information object for the document
  147. * this contains authorship, title etc.
  148. */
  149. public $infoObject = 0;
  150. /**
  151. * number of images being tracked within the document
  152. */
  153. public $numImages = 0;
  154. /**
  155. * an array containing options about the document
  156. * it defaults to turning on the compression of the objects
  157. */
  158. public $options = array('compression'=>1);
  159. /**
  160. * the objectId of the first page of the document
  161. */
  162. public $firstPageId;
  163. /**
  164. * used to track the last used value of the inter-word spacing, this is so that it is known
  165. * when the spacing is changed.
  166. */
  167. public $wordSpaceAdjust = 0;
  168. /**
  169. * the object Id of the procset object
  170. */
  171. public $procsetObjectId;
  172. /**
  173. * store the information about the relationship between font families
  174. * this used so that the code knows which font is the bold version of another font, etc.
  175. * the value of this array is initialised in the constuctor function.
  176. */
  177. public $fontFamilies = array();
  178. /**
  179. * folder for php serialized formats of font metrics files.
  180. * If empty string, use same folder as original metrics files.
  181. * This can be passed in from class creator.
  182. * If this folder does not exist or is not writable, Cpdf will be **much** slower.
  183. * Because of potential trouble with php safe mode, folder cannot be created at runtime.
  184. */
  185. public $fontcache = '';
  186. /**
  187. * temporary folder.
  188. * If empty string, will attempty system tmp folder.
  189. * This can be passed in from class creator.
  190. * Only used for conversion of gd images to jpeg images.
  191. */
  192. public $tmp = '';
  193. /**
  194. * track if the current font is bolded or italicised
  195. */
  196. public $currentTextState = '';
  197. /**
  198. * messages are stored here during processing, these can be selected afterwards to give some useful debug information
  199. */
  200. public $messages = '';
  201. /**
  202. * the ancryption array for the document encryption is stored here
  203. */
  204. public $arc4 = '';
  205. /**
  206. * the object Id of the encryption information
  207. */
  208. public $arc4_objnum = 0;
  209. /**
  210. * the file identifier, used to uniquely identify a pdf document
  211. */
  212. public $fileIdentifier = '';
  213. /**
  214. * a flag to say if a document is to be encrypted or not
  215. */
  216. public $encrypted = 0;
  217. /**
  218. * the ancryption key for the encryption of all the document content (structure is not encrypted)
  219. */
  220. public $encryptionKey = '';
  221. /**
  222. * array which forms a stack to keep track of nested callback functions
  223. */
  224. public $callback = array();
  225. /**
  226. * the number of callback functions in the callback array
  227. */
  228. public $nCallback = 0;
  229. /**
  230. * store label->id pairs for named destinations, these will be used to replace internal links
  231. * done this way so that destinations can be defined after the location that links to them
  232. */
  233. public $destinations = array();
  234. /**
  235. * store the stack for the transaction commands, each item in here is a record of the values of all the
  236. * publiciables within the class, so that the user can rollback at will (from each 'start' command)
  237. * note that this includes the objects array, so these can be large.
  238. */
  239. public $checkpoint = '';
  240. /* Table of Image origin filenames and image labels which were already added with o_image().
  241. * Allows to merge identical images
  242. */
  243. public $imagelist = array();
  244. /**
  245. * whether the text passed in should be treated as Unicode or just local character set.
  246. */
  247. public $isUnicode = false;
  248. /**
  249. * class constructor
  250. * this will start a new document
  251. * @var array array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
  252. * @var boolean whether text will be treated as Unicode or not.
  253. */
  254. function Cpdf ($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '') {
  255. $this->isUnicode = $isUnicode;
  256. $this->fontcache = $fontcache;
  257. $this->tmp = $tmp;
  258. $this->newDocument($pageSize);
  259. // also initialize the font families that are known about already
  260. $this->setFontFamily('init');
  261. // $this->fileIdentifier = md5('xxxxxxxx'.time());
  262. }
  263. /**
  264. * Document object methods (internal use only)
  265. *
  266. * There is about one object method for each type of object in the pdf document
  267. * Each function has the same call list ($id,$action,$options).
  268. * $id = the object ID of the object, or what it is to be if it is being created
  269. * $action = a string specifying the action to be performed, though ALL must support:
  270. * 'new' - create the object with the id $id
  271. * 'out' - produce the output for the pdf object
  272. * $options = optional, a string or array containing the various parameters for the object
  273. *
  274. * These, in conjunction with the output function are the ONLY way for output to be produced
  275. * within the pdf 'file'.
  276. */
  277. /**
  278. *destination object, used to specify the location for the user to jump to, presently on opening
  279. */
  280. function o_destination($id, $action, $options = '') {
  281. if ($action != 'new') {
  282. $o = & $this->objects[$id];
  283. }
  284. switch ($action) {
  285. case 'new':
  286. $this->objects[$id] = array('t'=>'destination', 'info'=>array());
  287. $tmp = '';
  288. switch ($options['type']) {
  289. case 'XYZ':
  290. case 'FitR':
  291. $tmp = ' '.$options['p3'].$tmp;
  292. case 'FitH':
  293. case 'FitV':
  294. case 'FitBH':
  295. case 'FitBV':
  296. $tmp = ' '.$options['p1'].' '.$options['p2'].$tmp;
  297. case 'Fit':
  298. case 'FitB':
  299. $tmp = $options['type'].$tmp;
  300. $this->objects[$id]['info']['string'] = $tmp;
  301. $this->objects[$id]['info']['page'] = $options['page'];
  302. }
  303. break;
  304. case 'out':
  305. $tmp = $o['info'];
  306. $res = "\n".$id." 0 obj\n".'['.$tmp['page'].' 0 R /'.$tmp['string']."]\nendobj";
  307. return $res;
  308. break;
  309. }
  310. }
  311. /**
  312. * set the viewer preferences
  313. */
  314. function o_viewerPreferences($id, $action, $options = '') {
  315. if ($action != 'new') {
  316. $o = & $this->objects[$id];
  317. }
  318. switch ($action) {
  319. case 'new':
  320. $this->objects[$id] = array('t'=>'viewerPreferences', 'info'=>array());
  321. break;
  322. case 'add':
  323. foreach($options as $k=>$v) {
  324. switch ($k) {
  325. case 'HideToolbar':
  326. case 'HideMenubar':
  327. case 'HideWindowUI':
  328. case 'FitWindow':
  329. case 'CenterWindow':
  330. case 'NonFullScreenPageMode':
  331. case 'Direction':
  332. $o['info'][$k] = $v;
  333. break;
  334. }
  335. }
  336. break;
  337. case 'out':
  338. $res = "\n".$id." 0 obj\n".'<< ';
  339. foreach($o['info'] as $k=>$v) {
  340. $res.= "\n/".$k.' '.$v;
  341. }
  342. $res.= "\n>>\n";
  343. return $res;
  344. break;
  345. }
  346. }
  347. /**
  348. * define the document catalog, the overall controller for the document
  349. */
  350. function o_catalog($id, $action, $options = '') {
  351. if ($action != 'new') {
  352. $o = & $this->objects[$id];
  353. }
  354. switch ($action) {
  355. case 'new':
  356. $this->objects[$id] = array('t'=>'catalog', 'info'=>array());
  357. $this->catalogId = $id;
  358. break;
  359. case 'outlines':
  360. case 'pages':
  361. case 'openHere':
  362. $o['info'][$action] = $options;
  363. break;
  364. case 'viewerPreferences':
  365. if (!isset($o['info']['viewerPreferences'])) {
  366. $this->numObj++;
  367. $this->o_viewerPreferences($this->numObj, 'new');
  368. $o['info']['viewerPreferences'] = $this->numObj;
  369. }
  370. $vp = $o['info']['viewerPreferences'];
  371. $this->o_viewerPreferences($vp, 'add', $options);
  372. break;
  373. case 'out':
  374. $res = "\n".$id." 0 obj\n".'<< /Type /Catalog';
  375. foreach($o['info'] as $k=>$v) {
  376. switch ($k) {
  377. case 'outlines':
  378. $res.= "\n".'/Outlines '.$v.' 0 R';
  379. break;
  380. case 'pages':
  381. $res.= "\n".'/Pages '.$v.' 0 R';
  382. break;
  383. case 'viewerPreferences':
  384. $res.= "\n".'/ViewerPreferences '.$o['info']['viewerPreferences'].' 0 R';
  385. break;
  386. case 'openHere':
  387. $res.= "\n".'/OpenAction '.$o['info']['openHere'].' 0 R';
  388. break;
  389. }
  390. }
  391. $res.= " >>\nendobj";
  392. return $res;
  393. break;
  394. }
  395. }
  396. /**
  397. * object which is a parent to the pages in the document
  398. */
  399. function o_pages($id, $action, $options = '') {
  400. if ($action != 'new') {
  401. $o = & $this->objects[$id];
  402. }
  403. switch ($action) {
  404. case 'new':
  405. $this->objects[$id] = array('t'=>'pages', 'info'=>array());
  406. $this->o_catalog($this->catalogId, 'pages', $id);
  407. break;
  408. case 'page':
  409. if (!is_array($options)) {
  410. // then it will just be the id of the new page
  411. $o['info']['pages'][] = $options;
  412. } else {
  413. // then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
  414. // and pos is either 'before' or 'after', saying where this page will fit.
  415. if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
  416. $i = array_search($options['rid'], $o['info']['pages']);
  417. if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) {
  418. // then there is a match
  419. // make a space
  420. switch ($options['pos']) {
  421. case 'before':
  422. $k = $i;
  423. break;
  424. case 'after':
  425. $k = $i+1;
  426. break;
  427. default:
  428. $k = -1;
  429. break;
  430. }
  431. if ($k >= 0) {
  432. for ($j = count($o['info']['pages']) -1;$j >= $k;$j--) {
  433. $o['info']['pages'][$j+1] = $o['info']['pages'][$j];
  434. }
  435. $o['info']['pages'][$k] = $options['id'];
  436. }
  437. }
  438. }
  439. }
  440. break;
  441. case 'procset':
  442. $o['info']['procset'] = $options;
  443. break;
  444. case 'mediaBox':
  445. $o['info']['mediaBox'] = $options;
  446. // which should be an array of 4 numbers
  447. break;
  448. case 'font':
  449. $o['info']['fonts'][] = array('objNum'=>$options['objNum'], 'fontNum'=>$options['fontNum']);
  450. break;
  451. case 'extGState':
  452. $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
  453. break;
  454. case 'xObject':
  455. $o['info']['xObjects'][] = array('objNum'=>$options['objNum'], 'label'=>$options['label']);
  456. break;
  457. case 'out':
  458. if (count($o['info']['pages'])) {
  459. $res = "\n".$id." 0 obj\n<< /Type /Pages\n/Kids [";
  460. foreach($o['info']['pages'] as $k=>$v) {
  461. $res.= $v." 0 R\n";
  462. }
  463. $res.= "]\n/Count ".count($this->objects[$id]['info']['pages']);
  464. if ( (isset($o['info']['fonts']) && count($o['info']['fonts'])) ||
  465. isset($o['info']['procset']) ||
  466. (isset($o['info']['extGStates']) && count($o['info']['extGStates']))) {
  467. $res.= "\n/Resources <<";
  468. if (isset($o['info']['procset'])) {
  469. $res.= "\n/ProcSet ".$o['info']['procset']." 0 R";
  470. }
  471. if (isset($o['info']['fonts']) && count($o['info']['fonts'])) {
  472. $res.= "\n/Font << ";
  473. foreach($o['info']['fonts'] as $finfo) {
  474. $res.= "\n/F".$finfo['fontNum']." ".$finfo['objNum']." 0 R";
  475. }
  476. $res.= "\n>>";
  477. }
  478. if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
  479. $res.= "\n/XObject << ";
  480. foreach($o['info']['xObjects'] as $finfo) {
  481. $res.= "\n/".$finfo['label']." ".$finfo['objNum']." 0 R";
  482. }
  483. $res.= "\n>>";
  484. }
  485. if ( isset($o['info']['extGStates']) && count($o['info']['extGStates'])) {
  486. $res.= "\n/ExtGState << ";
  487. foreach ($o['info']['extGStates'] as $gstate) {
  488. $res.= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R";
  489. }
  490. $res.= "\n>>";
  491. }
  492. $res.= "\n>>";
  493. if (isset($o['info']['mediaBox'])) {
  494. $tmp = $o['info']['mediaBox'];
  495. $res.= "\n/MediaBox [".sprintf('%.3F', $tmp[0]) .' '.sprintf('%.3F', $tmp[1]) .' '.sprintf('%.3F', $tmp[2]) .' '.sprintf('%.3F', $tmp[3]) .']';
  496. }
  497. }
  498. $res.= "\n >>\nendobj";
  499. } else {
  500. $res = "\n".$id." 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
  501. }
  502. return $res;
  503. break;
  504. }
  505. }
  506. /**
  507. * define the outlines in the doc, empty for now
  508. */
  509. function o_outlines($id, $action, $options = '') {
  510. if ($action != 'new') {
  511. $o = & $this->objects[$id];
  512. }
  513. switch ($action) {
  514. case 'new':
  515. $this->objects[$id] = array('t'=>'outlines', 'info'=>array('outlines'=>array()));
  516. $this->o_catalog($this->catalogId, 'outlines', $id);
  517. break;
  518. case 'outline':
  519. $o['info']['outlines'][] = $options;
  520. break;
  521. case 'out':
  522. if (count($o['info']['outlines'])) {
  523. $res = "\n".$id." 0 obj\n<< /Type /Outlines /Kids [";
  524. foreach($o['info']['outlines'] as $k=>$v) {
  525. $res.= $v." 0 R ";
  526. }
  527. $res.= "] /Count ".count($o['info']['outlines']) ." >>\nendobj";
  528. } else {
  529. $res = "\n".$id." 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
  530. }
  531. return $res;
  532. break;
  533. }
  534. }
  535. /**
  536. * an object to hold the font description
  537. */
  538. function o_font($id, $action, $options = '') {
  539. if ($action != 'new') {
  540. $o = & $this->objects[$id];
  541. }
  542. switch ($action) {
  543. case 'new':
  544. $this->objects[$id] = array('t' => 'font', 'info' => array('name' => $options['name'], 'fontFileName' => $options['fontFileName'], 'SubType' => 'Type1'));
  545. $fontNum = $this->numFonts;
  546. $this->objects[$id]['info']['fontNum'] = $fontNum;
  547. // deal with the encoding and the differences
  548. if (isset($options['differences'])) {
  549. // then we'll need an encoding dictionary
  550. $this->numObj++;
  551. $this->o_fontEncoding($this->numObj, 'new', $options);
  552. $this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
  553. } else if (isset($options['encoding'])) {
  554. // we can specify encoding here
  555. switch ($options['encoding']) {
  556. case 'WinAnsiEncoding':
  557. case 'MacRomanEncoding':
  558. case 'MacExpertEncoding':
  559. $this->objects[$id]['info']['encoding'] = $options['encoding'];
  560. break;
  561. case 'none':
  562. break;
  563. default:
  564. $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
  565. break;
  566. }
  567. } else {
  568. $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
  569. }
  570. if ($this->isUnicode) {
  571. // For Unicode fonts, we need to incorporate font data into
  572. // sub-sections that are linked from the primary font section.
  573. // Look at o_fontGIDtoCID and o_fontDescendentCID functions
  574. // for more informaiton.
  575. //
  576. // All of this code is adapted from the excellent changes made to
  577. // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
  578. $toUnicodeId = ++$this->numObj;
  579. $this->o_contents($toUnicodeId, 'new', 'raw');
  580. $this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
  581. $stream = "/CIDInit /ProcSet findresource begin\n";
  582. $stream.= "12 dict begin\n";
  583. $stream.= "begincmap\n";
  584. $stream.= "/CIDSystemInfo\n";
  585. $stream.= "<</Registry (Adobe)\n";
  586. $stream.= "/Ordering (UCS)\n";
  587. $stream.= "/Supplement 0\n";
  588. $stream.= ">> def\n";
  589. $stream.= "/CMapName /Adobe-Identity-UCS def\n";
  590. $stream.= "/CMapType 2 def\n";
  591. $stream.= "1 begincodespacerange\n";
  592. $stream.= "<0000> <FFFF>\n";
  593. $stream.= "endcodespacerange\n";
  594. $stream.= "1 beginbfrange\n";
  595. $stream.= "<0000> <FFFF> <0000>\n";
  596. $stream.= "endbfrange\n";
  597. $stream.= "endcmap\n";
  598. $stream.= "CMapName currentdict /CMap defineresource pop\n";
  599. $stream.= "end\n";
  600. $stream.= "end\n";
  601. $res = "<</Length " . mb_strlen($stream) . " >>\n";
  602. $res .= "stream\n" . $stream . "endstream";
  603. $this->objects[$toUnicodeId]['c'] = $res;
  604. $cidFontId = ++$this->numObj;
  605. $this->o_fontDescendentCID($cidFontId, 'new', $options);
  606. $this->objects[$id]['info']['cidFont'] = $cidFontId;
  607. }
  608. // also tell the pages node about the new font
  609. $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
  610. break;
  611. case 'add':
  612. foreach ($options as $k => $v) {
  613. switch ($k) {
  614. case 'BaseFont':
  615. $o['info']['name'] = $v;
  616. break;
  617. case 'FirstChar':
  618. case 'LastChar':
  619. case 'Widths':
  620. case 'FontDescriptor':
  621. case 'SubType':
  622. $this->addMessage('o_font '.$k." : ".$v);
  623. $o['info'][$k] = $v;
  624. break;
  625. }
  626. }
  627. // pass values down to descendent font
  628. if (isset($o['info']['cidFont'])) {
  629. $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
  630. }
  631. break;
  632. case 'out':
  633. if ($this->isUnicode) {
  634. // For Unicode fonts, we need to incorporate font data into
  635. // sub-sections that are linked from the primary font section.
  636. // Look at o_fontGIDtoCID and o_fontDescendentCID functions
  637. // for more informaiton.
  638. //
  639. // All of this code is adapted from the excellent changes made to
  640. // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
  641. $res = "\n".$id." 0 obj\n<</Type /Font\n/Subtype /Type0\n";
  642. $res.= "/BaseFont /".$o['info']['name']."\n";
  643. // The horizontal identity mapping for 2-byte CIDs; may be used
  644. // with CIDFonts using any Registry, Ordering, and Supplement values.
  645. $res.= "/Encoding /Identity-H\n";
  646. $res.= "/DescendantFonts [".$o['info']['cidFont']." 0 R]\n";
  647. $res.= "/ToUnicode ".$o['info']['toUnicode']." 0 R\n";
  648. $res.= ">>\n";
  649. $res.= "endobj";
  650. } else {
  651. $res = "\n".$id." 0 obj\n<< /Type /Font\n/Subtype /".$o['info']['SubType']."\n";
  652. $res.= "/Name /F".$o['info']['fontNum']."\n";
  653. $res.= "/BaseFont /".$o['info']['name']."\n";
  654. if (isset($o['info']['encodingDictionary'])) {
  655. // then place a reference to the dictionary
  656. $res.= "/Encoding ".$o['info']['encodingDictionary']." 0 R\n";
  657. } else if (isset($o['info']['encoding'])) {
  658. // use the specified encoding
  659. $res.= "/Encoding /".$o['info']['encoding']."\n";
  660. }
  661. if (isset($o['info']['FirstChar'])) {
  662. $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
  663. }
  664. if (isset($o['info']['LastChar'])) {
  665. $res.= "/LastChar ".$o['info']['LastChar']."\n";
  666. }
  667. if (isset($o['info']['Widths'])) {
  668. $res.= "/Widths ".$o['info']['Widths']." 0 R\n";
  669. }
  670. if (isset($o['info']['FontDescriptor'])) {
  671. $res.= "/FontDescriptor ".$o['info']['FontDescriptor']." 0 R\n";
  672. }
  673. $res.= ">>\n";
  674. $res.= "endobj";
  675. }
  676. return $res;
  677. break;
  678. }
  679. }
  680. /**
  681. * a font descriptor, needed for including additional fonts
  682. */
  683. function o_fontDescriptor($id, $action, $options = '') {
  684. if ($action != 'new') {
  685. $o = & $this->objects[$id];
  686. }
  687. switch ($action) {
  688. case 'new':
  689. $this->objects[$id] = array('t'=>'fontDescriptor', 'info'=>$options);
  690. break;
  691. case 'out':
  692. $res = "\n".$id." 0 obj\n<< /Type /FontDescriptor\n";
  693. foreach ($o['info'] as $label => $value) {
  694. switch ($label) {
  695. case 'Ascent':
  696. case 'CapHeight':
  697. case 'Descent':
  698. case 'Flags':
  699. case 'ItalicAngle':
  700. case 'StemV':
  701. case 'AvgWidth':
  702. case 'Leading':
  703. case 'MaxWidth':
  704. case 'MissingWidth':
  705. case 'StemH':
  706. case 'XHeight':
  707. case 'CharSet':
  708. if (mb_strlen($value)) {
  709. $res.= '/'.$label.' '.$value."\n";
  710. }
  711. break;
  712. case 'FontFile':
  713. case 'FontFile2':
  714. case 'FontFile3':
  715. $res.= '/'.$label.' '.$value." 0 R\n";
  716. break;
  717. case 'FontBBox':
  718. $res.= '/'.$label.' ['.$value[0].' '.$value[1].' '.$value[2].' '.$value[3]."]\n";
  719. break;
  720. case 'FontName':
  721. $res.= '/'.$label.' /'.$value."\n";
  722. break;
  723. }
  724. }
  725. $res.= ">>\nendobj";
  726. return $res;
  727. break;
  728. }
  729. }
  730. /**
  731. * the font encoding
  732. */
  733. function o_fontEncoding($id, $action, $options = '') {
  734. if ($action != 'new') {
  735. $o = & $this->objects[$id];
  736. }
  737. switch ($action) {
  738. case 'new':
  739. // the options array should contain 'differences' and maybe 'encoding'
  740. $this->objects[$id] = array('t'=>'fontEncoding', 'info'=>$options);
  741. break;
  742. case 'out':
  743. $res = "\n".$id." 0 obj\n<< /Type /Encoding\n";
  744. if (!isset($o['info']['encoding'])) {
  745. $o['info']['encoding'] = 'WinAnsiEncoding';
  746. }
  747. if ($o['info']['encoding'] != 'none') {
  748. $res.= "/BaseEncoding /".$o['info']['encoding']."\n";
  749. }
  750. $res.= "/Differences \n[";
  751. $onum = -100;
  752. foreach($o['info']['differences'] as $num=>$label) {
  753. if ($num != $onum+1) {
  754. // we cannot make use of consecutive numbering
  755. $res.= "\n".$num." /".$label;
  756. } else {
  757. $res.= " /".$label;
  758. }
  759. $onum = $num;
  760. }
  761. $res.= "\n]\n>>\nendobj";
  762. return $res;
  763. break;
  764. }
  765. }
  766. /**
  767. * a descendent cid font, needed for unicode fonts
  768. */
  769. function o_fontDescendentCID($id, $action, $options = '') {
  770. if ($action != 'new') {
  771. $o = & $this->objects[$id];
  772. }
  773. switch ($action) {
  774. case 'new':
  775. $this->objects[$id] = array('t'=>'fontDescendentCID', 'info'=>$options);
  776. // we need a CID system info section
  777. $cidSystemInfoId = ++$this->numObj;
  778. $this->o_contents($cidSystemInfoId, 'new', 'raw');
  779. $this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
  780. $res= "<</Registry (Adobe)\n"; // A string identifying an issuer of character collections
  781. $res.= "/Ordering (UCS)\n"; // A string that uniquely names a character collection issued by a specific registry
  782. $res.= "/Supplement 0\n"; // The supplement number of the character collection.
  783. $res.= ">>";
  784. $this->objects[$cidSystemInfoId]['c'] = $res;
  785. // and a CID to GID map
  786. $cidToGidMapId = ++$this->numObj;
  787. $this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
  788. $this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
  789. break;
  790. case 'add':
  791. foreach ($options as $k => $v) {
  792. switch ($k) {
  793. case 'BaseFont':
  794. $o['info']['name'] = $v;
  795. break;
  796. case 'FirstChar':
  797. case 'LastChar':
  798. case 'MissingWidth':
  799. case 'FontDescriptor':
  800. case 'SubType':
  801. $this->addMessage('o_fontDescendentCID '.$k." : ".$v);
  802. $o['info'][$k] = $v;
  803. break;
  804. }
  805. }
  806. // pass values down to cid to gid map
  807. $this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
  808. break;
  809. case 'out':
  810. $res = "\n".$id." 0 obj\n";
  811. $res.= "<</Type /Font\n";
  812. $res.= "/Subtype /CIDFontType2\n";
  813. $res.= "/BaseFont /".$o['info']['name']."\n";
  814. $res.= "/CIDSystemInfo ".$o['info']['cidSystemInfo']." 0 R\n";
  815. // if (isset($o['info']['FirstChar'])) {
  816. //
  817. // $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
  818. // }
  819. // if (isset($o['info']['LastChar'])) {
  820. //
  821. // $res.= "/LastChar ".$o['info']['LastChar']."\n";
  822. // }
  823. if (isset($o['info']['FontDescriptor'])) {
  824. $res.= "/FontDescriptor ".$o['info']['FontDescriptor']." 0 R\n";
  825. }
  826. if (isset($o['info']['MissingWidth'])) {
  827. $res.= "/DW ".$o['info']['MissingWidth']."\n";
  828. }
  829. if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
  830. $cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
  831. $w = '';
  832. foreach ($cid_widths as $cid => $width) {
  833. $w .= $cid.' ['.$width.'] ';
  834. }
  835. $res.= "/W [".$w."]\n";
  836. }
  837. $res.= "/CIDToGIDMap ".$o['info']['cidToGidMap']." 0 R\n";
  838. $res.= ">>\n";
  839. $res.= "endobj";
  840. return $res;
  841. break;
  842. }
  843. }
  844. /**
  845. * a font glyph to character map, needed for unicode fonts
  846. */
  847. function o_fontGIDtoCIDMap($id, $action, $options = '') {
  848. if ($action != 'new') {
  849. $o = & $this->objects[$id];
  850. }
  851. switch ($action) {
  852. case 'new':
  853. $this->objects[$id] = array('t'=>'fontGIDtoCIDMap', 'info'=>$options);
  854. break;
  855. case 'out':
  856. $res = "\n".$id." 0 obj\n";
  857. $tmp = $this->fonts[$o['info']['fontFileName']]['CIDtoGID'] = base64_decode($this->fonts[$o['info']['fontFileName']]['CIDtoGID']);
  858. $compressed = isset($this->fonts[$o['info']['fontFileName']]['CIDtoGID_Compressed']) &&
  859. $this->fonts[$o['info']['fontFileName']]['CIDtoGID_Compressed'];
  860. if (!$compressed && isset($o['raw'])) {
  861. $res.= $tmp;
  862. } else {
  863. $res.= "<<";
  864. if (!$compressed && function_exists('gzcompress') && $this->options['compression']) {
  865. // then implement ZLIB based compression on this content stream
  866. $compressed = true;
  867. $tmp = gzcompress($tmp, 6);
  868. }
  869. if ($compressed) {
  870. $res.= "\n/Filter /FlateDecode";
  871. }
  872. $res.= "\n/Length ".mb_strlen($tmp) .">>\nstream\n".$tmp."\nendstream";
  873. }
  874. $res.= "\nendobj";
  875. return $res;
  876. break;
  877. }
  878. }
  879. /**
  880. * the document procset, solves some problems with printing to old PS printers
  881. */
  882. function o_procset($id, $action, $options = '') {
  883. if ($action != 'new') {
  884. $o = & $this->objects[$id];
  885. }
  886. switch ($action) {
  887. case 'new':
  888. $this->objects[$id] = array('t'=>'procset', 'info'=>array('PDF'=>1, 'Text'=>1));
  889. $this->o_pages($this->currentNode, 'procset', $id);
  890. $this->procsetObjectId = $id;
  891. break;
  892. case 'add':
  893. // this is to add new items to the procset list, despite the fact that this is considered
  894. // obselete, the items are required for printing to some postscript printers
  895. switch ($options) {
  896. case 'ImageB':
  897. case 'ImageC':
  898. case 'ImageI':
  899. $o['info'][$options] = 1;
  900. break;
  901. }
  902. break;
  903. case 'out':
  904. $res = "\n".$id." 0 obj\n[";
  905. foreach ($o['info'] as $label=>$val) {
  906. $res.= '/'.$label.' ';
  907. }
  908. $res.= "]\nendobj";
  909. return $res;
  910. break;
  911. }
  912. }
  913. /**
  914. * define the document information
  915. */
  916. function o_info($id, $action, $options = '') {
  917. if ($action != 'new') {
  918. $o = & $this->objects[$id];
  919. }
  920. switch ($action) {
  921. case 'new':
  922. $this->infoObject = $id;
  923. $date = 'D:'.@date('Ymd');
  924. $this->objects[$id] = array('t'=>'info', 'info'=>array('Creator'=>'R and OS php pdf writer, http://www.ros.co.nz', 'CreationDate'=>$date));
  925. break;
  926. case 'Title':
  927. case 'Author':
  928. case 'Subject':
  929. case 'Keywords':
  930. case 'Creator':
  931. case 'Producer':
  932. case 'CreationDate':
  933. case 'ModDate':
  934. case 'Trapped':
  935. $o['info'][$action] = $options;
  936. break;
  937. case 'out':
  938. if ($this->encrypted) {
  939. $this->encryptInit($id);
  940. }
  941. $res = "\n".$id." 0 obj\n<<\n";
  942. foreach ($o['info'] as $k=>$v) {
  943. $res.= '/'.$k.' (';
  944. // dates must be outputted as-is, without Unicode transformations
  945. $raw = ($k == 'CreationDate' || $k == 'ModDate');
  946. $c = $v;
  947. if ($this->encrypted) {
  948. $c = $this->ARC4($c);
  949. }
  950. $res.= ($raw) ? $c : $this->filterText($c);
  951. $res.= ")\n";
  952. }
  953. $res.= ">>\nendobj";
  954. return $res;
  955. break;
  956. }
  957. }
  958. /**
  959. * an action object, used to link to URLS initially
  960. */
  961. function o_action($id, $action, $options = '') {
  962. if ($action != 'new') {
  963. $o = & $this->objects[$id];
  964. }
  965. switch ($action) {
  966. case 'new':
  967. if (is_array($options)) {
  968. $this->objects[$id] = array('t'=>'action', 'info'=>$options, 'type'=>$options['type']);
  969. } else {
  970. // then assume a URI action
  971. $this->objects[$id] = array('t'=>'action', 'info'=>$options, 'type'=>'URI');
  972. }
  973. break;
  974. case 'out':
  975. if ($this->encrypted) {
  976. $this->encryptInit($id);
  977. }
  978. $res = "\n".$id." 0 obj\n<< /Type /Action";
  979. switch ($o['type']) {
  980. case 'ilink':
  981. // there will be an 'label' setting, this is the name of the destination
  982. $res.= "\n/S /GoTo\n/D ".$this->destinations[(string)$o['info']['label']]." 0 R";
  983. break;
  984. case 'URI':
  985. $res.= "\n/S /URI\n/URI (";
  986. if ($this->encrypted) {
  987. $res.= $this->filterText($this->ARC4($o['info']));
  988. } else {
  989. $res.= $this->filterText($o['info']);
  990. }
  991. $res.= ")";
  992. break;
  993. }
  994. $res.= "\n>>\nendobj";
  995. return $res;
  996. break;
  997. }
  998. }
  999. /**
  1000. * an annotation object, this will add an annotation to the current page.
  1001. * initially will support just link annotations
  1002. */
  1003. function o_annotation($id, $action, $options = '') {
  1004. if ($action != 'new') {
  1005. $o = & $this->objects[$id];
  1006. }
  1007. switch ($action) {
  1008. case 'new':
  1009. // add the annotation to the current page
  1010. $pageId = $this->currentPage;
  1011. $this->o_page($pageId, 'annot', $id);
  1012. // and add the action object which is going to be required
  1013. switch ($options['type']) {
  1014. case 'link':
  1015. $this->objects[$id] = array('t'=>'annotation', 'info'=>$options);
  1016. $this->numObj++;
  1017. $this->o_action($this->numObj, 'new', $options['url']);
  1018. $this->objects[$id]['info']['actionId'] = $this->numObj;
  1019. break;
  1020. case 'ilink':
  1021. // this is to a named internal link
  1022. $label = $options['label'];
  1023. $this->objects[$id] = array('t'=>'annotation', 'info'=>$options);
  1024. $this->numObj++;
  1025. $this->o_action($this->numObj, 'new', array('type'=>'ilink', 'label'=>$label));
  1026. $this->objects[$id]['info']['actionId'] = $this->numObj;
  1027. break;
  1028. }
  1029. break;
  1030. case 'out':
  1031. $res = "\n".$id." 0 obj\n<< /Type /Annot";
  1032. switch ($o['info']['type']) {
  1033. case 'link':
  1034. case 'ilink':
  1035. $res.= "\n/Subtype /Link";
  1036. break;
  1037. }
  1038. $res.= "\n/A ".$o['info']['actionId']." 0 R";
  1039. $res.= "\n/Border [0 0 0]";
  1040. $res.= "\n/H /I";
  1041. $res.= "\n/Rect [ ";
  1042. foreach($o['info']['rect'] as $v) {
  1043. $res.= sprintf("%.4F ", $v);
  1044. }
  1045. $res.= "]";
  1046. $res.= "\n>>\nendobj";
  1047. return $res;
  1048. break;
  1049. }
  1050. }
  1051. /**
  1052. * a page object, it also creates a contents object to hold its contents
  1053. */
  1054. function o_page($id, $action, $options = '') {
  1055. if ($action != 'new') {
  1056. $o = & $this->objects[$id];
  1057. }
  1058. switch ($action) {
  1059. case 'new':
  1060. $this->numPages++;
  1061. $this->objects[$id] = array('t'=>'page', 'info'=>array('parent'=>$this->currentNode, 'pageNum'=>$this->numPages));
  1062. if (is_array($options)) {
  1063. // then this must be a page insertion, array shoudl contain 'rid','pos'=[before|after]
  1064. $options['id'] = $id;
  1065. $this->o_pages($this->currentNode, 'page', $options);
  1066. } else {
  1067. $this->o_pages($this->currentNode, 'page', $id);
  1068. }
  1069. $this->currentPage = $id;
  1070. //make a contents object to go with this page
  1071. $this->numObj++;
  1072. $this->o_contents($this->numObj, 'new', $id);
  1073. $this->currentContents = $this->numObj;
  1074. $this->objects[$id]['info']['contents'] = array();
  1075. $this->objects[$id]['info']['contents'][] = $this->numObj;
  1076. $match = ($this->numPages%2 ? 'odd' : 'even');
  1077. foreach($this->addLooseObjects as $oId=>$target) {
  1078. if ($target == 'all' || $match == $target) {
  1079. $this->objects[$id]['info']['contents'][] = $oId;
  1080. }
  1081. }
  1082. break;
  1083. case 'content':
  1084. $o['info']['contents'][] = $options;
  1085. break;
  1086. case 'annot':
  1087. // add an annotation to this page
  1088. if (!isset($o['info']['annot'])) {
  1089. $o['info']['annot'] = array();
  1090. }
  1091. // $options should contain the id of the annotation dictionary
  1092. $o['info']['annot'][] = $options;
  1093. break;
  1094. case 'out':
  1095. $res = "\n".$id." 0 obj\n<< /Type /Page";
  1096. $res.= "\n/Parent ".$o['info']['parent']." 0 R";
  1097. if (isset($o['info']['annot'])) {
  1098. $res.= "\n/Annots [";
  1099. foreach($o['info']['annot'] as $aId) {
  1100. $res.= " ".$aId." 0 R";
  1101. }
  1102. $res.= " ]";
  1103. }
  1104. $count = count($o['info']['contents']);
  1105. if ($count == 1) {
  1106. $res.= "\n/Contents ".$o['info']['contents'][0]." 0 R";
  1107. } else if ($count>1) {
  1108. $res.= "\n/Contents [\n";
  1109. // reverse the page contents so added objects are below normal content
  1110. //foreach (array_reverse($o['info']['contents']) as $cId) {
  1111. // Back to normal now that I've got transparency working --Benj
  1112. foreach ($o['info']['contents'] as $cId) {
  1113. $res.= $cId." 0 R\n";
  1114. }
  1115. $res.= "]";
  1116. }
  1117. $res.= "\n>>\nendobj";
  1118. return $res;
  1119. break;
  1120. }
  1121. }
  1122. /**
  1123. * the contents objects hold all of the content which appears on pages
  1124. */
  1125. function o_contents($id, $action, $options = '') {
  1126. if ($action != 'new') {
  1127. $o = & $this->objects[$id];
  1128. }
  1129. switch ($action) {
  1130. case 'new':
  1131. $this->objects[$id] = array('t'=>'contents', 'c'=>'', 'info'=>array());
  1132. if (mb_strlen($options) && intval($options)) {
  1133. // then this contents is the primary for a page
  1134. $this->objects[$id]['onPage'] = $options;
  1135. } else if ($options == 'raw') {
  1136. // then this page contains some other type of system object
  1137. $this->objects[$id]['raw'] = 1;
  1138. }
  1139. break;
  1140. case 'add':
  1141. // add more options to the decleration
  1142. foreach ($options as $k=>$v) {
  1143. $o['info'][$k] = $v;
  1144. }
  1145. case 'out':
  1146. $tmp = $o['c'];
  1147. $res = "\n".$id." 0 obj\n";
  1148. if (isset($this->objects[$id]['raw'])) {
  1149. $res.= $tmp;
  1150. } else {
  1151. $res.= "<<";
  1152. if (function_exists('gzcompress') && $this->options['compression']) {
  1153. // then implement ZLIB based compression on this content stream
  1154. $res.= " /Filter /FlateDecode";
  1155. $tmp = gzcompress($tmp, 6);
  1156. }
  1157. if ($this->encrypted) {
  1158. $this->encryptInit($id);
  1159. $tmp = $this->ARC4($tmp);
  1160. }
  1161. foreach($o['info'] as $k=>$v) {
  1162. $res.= "\n/".$k.' '.$v;
  1163. }
  1164. $res.= "\n/Length ".mb_strlen($tmp) ." >>\nstream\n".$tmp."\nendstream";
  1165. }
  1166. $res.= "\nendobj";
  1167. return $res;
  1168. break;
  1169. }
  1170. }
  1171. /**
  1172. * an image object, will be an XObject in the document, includes description and data
  1173. */
  1174. function o_image($id, $action, $options = '') {
  1175. if ($action != 'new') {
  1176. $o = & $this->objects[$id];
  1177. }
  1178. switch ($action) {
  1179. case 'new':
  1180. // make the new object
  1181. $this->objects[$id] = array('t'=>'image', 'data'=>&$options['data'], 'info'=>array());
  1182. $this->objects[$id]['info']['Type'] = '/XObject';
  1183. $this->objects[$id]['info']['Subtype'] = '/Image';
  1184. $this->objects[$id]['info']['Width'] = $options['iw'];
  1185. $this->objects[$id]['info']['Height'] = $options['ih'];
  1186. if (!isset($options['type']) || $options['type'] == 'jpg') {
  1187. if (!isset($options['channels'])) {
  1188. $options['channels'] = 3;
  1189. }
  1190. switch ($options['channels']) {
  1191. case 1:
  1192. $this->objects[$id]['info']['ColorSpace'] = '/DeviceGray';
  1193. break;
  1194. default:
  1195. $this->objects[$id]['info']['ColorSpace'] = '/DeviceRGB';
  1196. break;
  1197. }
  1198. $this->objects[$id]['info']['Filter'] = '/DCTDecode';
  1199. $this->objects[$id]['info']['BitsPerComponent'] = 8;
  1200. } else if ($options['type'] == 'png') {
  1201. $this->objects[$id]['info']['Filter'] = '/FlateDecode';
  1202. $this->objects[$id]['info']['DecodeParms'] = '<< /Predictor 15 /Colors '.$options['ncolor'].' /Columns '.$options['iw'].' /BitsPerComponent '.$options['bitsPerComponent'].'>>';
  1203. if (mb_strlen($options['pdata'])) {
  1204. $tmp = ' [ /Indexed /DeviceRGB '.(mb_strlen($options['pdata']) /3-1) .' ';
  1205. $this->numObj++;
  1206. $this->o_contents($this->numObj, 'new');
  1207. $this->objects[$this->numObj]['c'] = $options['pdata'];
  1208. $tmp.= $this->numObj.' 0 R';
  1209. $tmp.= ' ]';
  1210. $this->objects[$id]['info']['ColorSpace'] = $tmp;
  1211. if (isset($options['transparency'])) {
  1212. switch ($options['transparency']['type']) {
  1213. case 'indexed':
  1214. $tmp = ' [ '.$options['transparency']['data'].' '.$options['transparency']['data'].'] ';
  1215. $this->objects[$id]['info']['Mask'] = $tmp;
  1216. break;
  1217. case 'color-key':
  1218. $tmp = ' [ '.
  1219. $options['transparency']['r'] . ' ' . $options['transparency']['r'] .
  1220. $options['transparency']['g'] . ' ' . $options['transparency']['g'] .
  1221. $options['transparency']['b'] . ' ' . $options['transparency']['b'] .
  1222. ' ] ';
  1223. $this->objects[$id]['info']['Mask'] = $tmp;
  1224. pre_r($tmp);
  1225. break;
  1226. }
  1227. }
  1228. } else {
  1229. if (isset($options['transparency'])) {
  1230. switch ($options['transparency']['type']) {
  1231. case 'indexed':
  1232. $tmp = ' [ '.$options['transparency']['data'].' '.$options['transparency']['data'].'] ';
  1233. $this->objects[$id]['info']['Mask'] = $tmp;
  1234. break;
  1235. case 'color-key':
  1236. $tmp = ' [ '.
  1237. $options['transparency']['r'] . ' ' . $options['transparency']['r'] . ' ' .
  1238. $options['transparency']['g'] . ' ' . $options['transparency']['g'] . ' ' .
  1239. $options['transparency']['b'] . ' ' . $options['transparency']['b'] .
  1240. ' ] ';
  1241. $this->objects[$id]['info']['Mask'] = $tmp;
  1242. break;
  1243. }
  1244. }
  1245. $this->objects[$id]['info']['ColorSpace'] = '/'.$options['color'];
  1246. }
  1247. $this->objects[$id]['info']['BitsPerComponent'] = $options['bitsPerComponent'];
  1248. }
  1249. // assign it a place in the named resource dictionary as an external object, according to
  1250. // the label passed in with it.
  1251. $this->o_pages($this->currentNode, 'xObject', array('label'=>$options['label'], 'objNum'=>$id));
  1252. // also make sure that we have the right procset object for it.
  1253. $this->o_procset($this->procsetObjectId, 'add', 'ImageC');
  1254. break;
  1255. case 'out':
  1256. $tmp = &$o['data'];
  1257. $res = "\n".$id." 0 obj\n<<";
  1258. foreach($o['info'] as $k=>$v) {
  1259. $res.= "\n/".$k.' '.$v;
  1260. }
  1261. if ($this->encrypted) {
  1262. $this->encryptInit($id);
  1263. $tmp = $this->ARC4($tmp);
  1264. }
  1265. $res.= "\n/Length ".mb_strlen($tmp, '8bit') .">>\nstream\n".$tmp."\nendstream\nendobj";
  1266. return $res;
  1267. break;
  1268. }
  1269. }
  1270. /**
  1271. * graphics state object
  1272. */
  1273. function o_extGState($id, $action, $options = "") {
  1274. static $valid_params = array("LW", "LC", "LC", "LJ", "ML",
  1275. "D", "RI", "OP", "op", "OPM",
  1276. "Font", "BG", "BG2", "UCR",
  1277. "TR", "TR2", "HT", "FL",
  1278. "SM", "SA", "BM", "SMask",
  1279. "CA", "ca", "AIS", "TK");
  1280. if ( $action != "new") {
  1281. $o = & $this->objects[$id];
  1282. }
  1283. switch ($action) {
  1284. case "new":
  1285. $this->objects[$id] = array('t' => 'extGState', 'info' => $options);
  1286. // Tell the pages about the new resource
  1287. $this->numStates++;
  1288. $this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
  1289. break;
  1290. case "out":
  1291. $res =
  1292. "\n" . $id . " 0 obj\n".
  1293. "<< /Type /ExtGState\n";
  1294. foreach ($o["info"] as $parameter => $value) {
  1295. if ( !in_array($parameter, $valid_params))
  1296. continue;
  1297. $res.= "/$parameter $value\n";
  1298. }
  1299. $res.=
  1300. ">>\n".
  1301. "endobj";
  1302. return $res;
  1303. }
  1304. }
  1305. /**
  1306. * encryption object.
  1307. */
  1308. function o_encryption($id, $action, $options = '') {
  1309. if ($action != 'new') {
  1310. $o = & $this->objects[$id];
  1311. }
  1312. switch ($action) {
  1313. case 'new':
  1314. // make the new object
  1315. $this->objects[$id] = array('t'=>'encryption', 'info'=>$options);
  1316. $this->arc4_objnum = $id;
  1317. // figure out the additional paramaters required
  1318. $pad = chr(0x28) .chr(0xBF) .chr(0x4E) .chr(0x5E) .chr(0x4E) .chr(0x75) .chr(0x8A) .chr(0x41) .chr(0x64) .chr(0x00) .chr(0x4E) .chr(0x56) .chr(0xFF) .chr(0xFA) .chr(0x01) .chr(0x08) .chr(0x2E) .chr(0x2E) .chr(0x00) .chr(0xB6) .chr(0xD0) .chr(0x68) .chr(0x3E) .chr(0x80) .chr(0x2F) .chr(0x0C) .chr(0xA9) .chr(0xFE) .chr(0x64) .chr(0x53) .chr(0x69) .chr(0x7A);
  1319. $len = mb_strlen($options['owner']);
  1320. if ($len>32) {
  1321. $owner = substr($options['owner'], 0, 32);
  1322. } else if ($len<32) {
  1323. $owner = $options['owner'].substr($pad, 0, 32-$len);
  1324. } else {
  1325. $owner = $options['owner'];
  1326. }
  1327. $len = mb_strlen($options['user']);
  1328. if ($len>32) {
  1329. $user = substr($options['user'], 0, 32);
  1330. } else if ($len<32) {
  1331. $user = $options['user'].substr($pad, 0, 32-$len);
  1332. } else {
  1333. $user = $options['user'];
  1334. }
  1335. $tmp = $this->md5_16($owner);
  1336. $oke

Large files files are truncated, but you can click here to view the full file