WooCommerce PDF Invoices & Packing Slips - Version 1.0.0

Version Description

  • First release
Download this release

Release Info

Developer pomegranate
Plugin Icon 128x128 WooCommerce PDF Invoices & Packing Slips
Version 1.0.0
Comparing to
See all releases

Version 1.0.0

Files changed (97) hide show
  1. css/style.css +62 -0
  2. images/invoice.png +0 -0
  3. images/packing-slip.png +0 -0
  4. includes/class-wcpdf-export.php +386 -0
  5. includes/class-wcpdf-settings.php +674 -0
  6. includes/class-wcpdf-writepanels.php +115 -0
  7. js/media-upload.js +71 -0
  8. js/script.js +21 -0
  9. languages/wpo_wcpdf-nl_NL.mo +0 -0
  10. languages/wpo_wcpdf-nl_NL.po +369 -0
  11. lib/dompdf/.gitattributes +13 -0
  12. lib/dompdf/.gitignore +8 -0
  13. lib/dompdf/.gitmodules +3 -0
  14. lib/dompdf/CONTRIBUTING.md +73 -0
  15. lib/dompdf/LICENSE.LGPL +456 -0
  16. lib/dompdf/README.md +93 -0
  17. lib/dompdf/composer.json +23 -0
  18. lib/dompdf/dompdf.php +285 -0
  19. lib/dompdf/dompdf_config.custom.inc.php +31 -0
  20. lib/dompdf/dompdf_config.inc.php +393 -0
  21. lib/dompdf/include/absolute_positioner.cls.php +125 -0
  22. lib/dompdf/include/abstract_renderer.cls.php +759 -0
  23. lib/dompdf/include/attribute_translator.cls.php +592 -0
  24. lib/dompdf/include/autoload.inc.php +86 -0
  25. lib/dompdf/include/block_frame_decorator.cls.php +234 -0
  26. lib/dompdf/include/block_frame_reflower.cls.php +805 -0
  27. lib/dompdf/include/block_positioner.cls.php +57 -0
  28. lib/dompdf/include/block_renderer.cls.php +230 -0
  29. lib/dompdf/include/cached_pdf_decorator.cls.php +164 -0
  30. lib/dompdf/include/canvas.cls.php +385 -0
  31. lib/dompdf/include/canvas_factory.cls.php +63 -0
  32. lib/dompdf/include/cellmap.cls.php +790 -0
  33. lib/dompdf/include/cpdf_adapter.cls.php +877 -0
  34. lib/dompdf/include/css_color.cls.php +276 -0
  35. lib/dompdf/include/dompdf.cls.php +1062 -0
  36. lib/dompdf/include/dompdf_exception.cls.php +26 -0
  37. lib/dompdf/include/dompdf_image_exception.cls.php +26 -0
  38. lib/dompdf/include/file.skel +8 -0
  39. lib/dompdf/include/fixed_positioner.cls.php +88 -0
  40. lib/dompdf/include/font_metrics.cls.php +363 -0
  41. lib/dompdf/include/frame.cls.php +1191 -0
  42. lib/dompdf/include/frame_decorator.cls.php +686 -0
  43. lib/dompdf/include/frame_factory.cls.php +252 -0
  44. lib/dompdf/include/frame_reflower.cls.php +453 -0
  45. lib/dompdf/include/frame_tree.cls.php +239 -0
  46. lib/dompdf/include/functions.inc.php +1026 -0
  47. lib/dompdf/include/gd_adapter.cls.php +840 -0
  48. lib/dompdf/include/image_cache.cls.php +183 -0
  49. lib/dompdf/include/image_frame_decorator.cls.php +80 -0
  50. lib/dompdf/include/image_frame_reflower.cls.php +186 -0
  51. lib/dompdf/include/image_renderer.cls.php +119 -0
  52. lib/dompdf/include/inline_frame_decorator.cls.php +74 -0
  53. lib/dompdf/include/inline_frame_reflower.cls.php +66 -0
  54. lib/dompdf/include/inline_positioner.cls.php +70 -0
  55. lib/dompdf/include/inline_renderer.cls.php +190 -0
  56. lib/dompdf/include/javascript_embedder.cls.php +37 -0
  57. lib/dompdf/include/line_box.cls.php +252 -0
  58. lib/dompdf/include/list_bullet_frame_decorator.cls.php +65 -0
  59. lib/dompdf/include/list_bullet_frame_reflower.cls.php +33 -0
  60. lib/dompdf/include/list_bullet_image_frame_decorator.cls.php +143 -0
  61. lib/dompdf/include/list_bullet_positioner.cls.php +73 -0
  62. lib/dompdf/include/list_bullet_renderer.cls.php +236 -0
  63. lib/dompdf/include/null_frame_decorator.cls.php +26 -0
  64. lib/dompdf/include/null_frame_reflower.cls.php +21 -0
  65. lib/dompdf/include/null_positioner.cls.php +23 -0
  66. lib/dompdf/include/page_cache.cls.php +126 -0
  67. lib/dompdf/include/page_frame_decorator.cls.php +592 -0
  68. lib/dompdf/include/page_frame_reflower.cls.php +186 -0
  69. lib/dompdf/include/pdflib_adapter.cls.php +1085 -0
  70. lib/dompdf/include/php_evaluator.cls.php +48 -0
  71. lib/dompdf/include/positioner.cls.php +51 -0
  72. lib/dompdf/include/renderer.cls.php +290 -0
  73. lib/dompdf/include/style.cls.php +2435 -0
  74. lib/dompdf/include/stylesheet.cls.php +1419 -0
  75. lib/dompdf/include/table_cell_frame_decorator.cls.php +102 -0
  76. lib/dompdf/include/table_cell_frame_reflower.cls.php +119 -0
  77. lib/dompdf/include/table_cell_positioner.cls.php +28 -0
  78. lib/dompdf/include/table_cell_renderer.cls.php +155 -0
  79. lib/dompdf/include/table_frame_decorator.cls.php +334 -0
  80. lib/dompdf/include/table_frame_reflower.cls.php +578 -0
  81. lib/dompdf/include/table_row_frame_decorator.cls.php +48 -0
  82. lib/dompdf/include/table_row_frame_reflower.cls.php +61 -0
  83. lib/dompdf/include/table_row_group_frame_decorator.cls.php +66 -0
  84. lib/dompdf/include/table_row_group_frame_reflower.cls.php +59 -0
  85. lib/dompdf/include/table_row_group_renderer.cls.php +40 -0
  86. lib/dompdf/include/table_row_positioner.cls.php +35 -0
  87. lib/dompdf/include/tcpdf_adapter.cls.php +628 -0
  88. lib/dompdf/include/text_frame_decorator.cls.php +173 -0
  89. lib/dompdf/include/text_frame_reflower.cls.php +441 -0
  90. lib/dompdf/include/text_renderer.cls.php +152 -0
  91. lib/dompdf/index.php +1 -0
  92. lib/dompdf/lib/class.pdf.php +4613 -0
  93. lib/dompdf/lib/fonts/Courier-Bold.afm +344 -0
  94. lib/dompdf/lib/fonts/Courier-BoldOblique.afm +344 -0
  95. lib/dompdf/lib/fonts/Courier-Oblique.afm +344 -0
  96. lib/dompdf/lib/fonts/Courier.afm +344 -0
  97. lib/dompdf/lib/fonts/Helvetica-Bold.afm +2198 -0
css/style.css ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #woocommerce-delivery-notes-box .inside {
2
+ text-align: left;
3
+ position: relative;
4
+ }
5
+ #woocommerce-delivery-notes-box .inside ul {
6
+ overflow: hidden;
7
+ margin: 0px;
8
+ }
9
+ #woocommerce-delivery-notes-box .inside ul li {
10
+ float: left;
11
+ margin: 3px;
12
+ }
13
+ #woocommerce-delivery-notes-box .inside img.loading {
14
+ position: absolute;
15
+ right: 7px;
16
+ top: 0px;
17
+ }
18
+ #woocommerce-delivery-notes-box .inside img.loading,
19
+ .type-shop_order .column-order_actions img.loading {
20
+ display: none;
21
+ vertical-align: middle;
22
+ }
23
+ .type-shop_order .column-order_actions img.loading {
24
+ float: left;
25
+ margin-top: 3px;
26
+ }
27
+ .type-shop_order .column-order_actions .print-preview-button span {
28
+ display: none;
29
+ }
30
+ #company-logo-placeholder {
31
+ display: block;
32
+ }
33
+ #company-logo-placeholder.loading {
34
+ background-image: url("../../../../wp-admin/images/wpspin_light.gif");
35
+ background-repeat: no-repeat;
36
+ height: 20px;
37
+ }
38
+ #company-logo-placeholder img {
39
+ cursor: pointer;
40
+ }
41
+
42
+ .type-shop_order .column-order_actions a.button.wpo_wcpdf {
43
+ padding: 0 2px;
44
+ }
45
+
46
+ .type-shop_order .column-order_actions a.button.wpo_wcpdf img {
47
+ width: 16px;
48
+ }
49
+
50
+ /* Settings page styles */
51
+ #img-header_logo {
52
+ max-height: 200px;
53
+ width: auto;
54
+ }
55
+
56
+ span.wpo-warning {
57
+ display: inline-block;
58
+ border: 1px solid red;
59
+ border-left: 4px solid red;
60
+ padding: 5px 15px;
61
+ background-color: white;
62
+ }
images/invoice.png ADDED
Binary file
images/packing-slip.png ADDED
Binary file
includes/class-wcpdf-export.php ADDED
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * PDF Export class
5
+ */
6
+ if ( ! class_exists( 'WooCommerce_PDF_Invoices_Export' ) ) {
7
+
8
+ class WooCommerce_PDF_Invoices_Export {
9
+
10
+ public $template_directory_name;
11
+ public $template_base_path;
12
+ public $template_default_base_path;
13
+ public $template_default_base_uri;
14
+ public $template_path;
15
+
16
+ public $order;
17
+ public $template_type;
18
+ public $order_id;
19
+ public $output_body;
20
+
21
+ /**
22
+ * Constructor
23
+ */
24
+ public function __construct() {
25
+ global $woocommerce;
26
+ $this->order = new WC_Order();
27
+ $this->general_settings = get_option('wpo_wcpdf_general_settings');
28
+ $this->template_settings = get_option('wpo_wcpdf_template_settings');
29
+
30
+ $this->template_directory_name = 'pdf';
31
+ $this->template_base_path = (defined('WC_TEMPLATE_PATH')?WC_TEMPLATE_PATH:$woocommerce->template_url) . $this->template_directory_name . '/';
32
+ $this->template_default_base_path = WooCommerce_PDF_Invoices::$plugin_path . 'templates/' . $this->template_directory_name . '/';
33
+ $this->template_default_base_uri = WooCommerce_PDF_Invoices::$plugin_url . 'templates/' . $this->template_directory_name . '/';
34
+
35
+ $this->template_path = $this->template_settings['template_path'];
36
+
37
+ add_action('wp_ajax_generate_wpo_wcpdf', array($this, 'generate_pdf_ajax'));
38
+ add_filter( 'woocommerce_email_attachments', array( $this, 'attach_pdf_to_email' ), 99, 3);
39
+ }
40
+
41
+ /**
42
+ * Generate the template output
43
+ */
44
+ public function generate_pdf( $template_type, $order_ids ) {
45
+ $this->template_type = $template_type;
46
+ $this->order_ids = $order_ids;
47
+
48
+ $output_html = array();
49
+ foreach ($order_ids as $order_id) {
50
+ $this->order = new WC_Order( $order_id );
51
+ $template = $this->template_path . '/' . $template_type . '.php';
52
+ $output_html[$order_id] = $this->get_template($template);
53
+ }
54
+
55
+ $page_break = "\n<div style=\"page-break-before: always;\"></div>\n";
56
+ $this->output_body = implode($page_break, $output_html);
57
+
58
+ $template_wrapper = $this->template_path . '/html-document-wrapper.php';
59
+ $complete_pdf = $this->get_template($template_wrapper);
60
+
61
+ // clean up special characters
62
+ $complete_pdf = utf8_decode(mb_convert_encoding($complete_pdf, 'HTML-ENTITIES', 'UTF-8'));
63
+
64
+ // die($complete_pdf); //output html to browser for debug
65
+
66
+ require_once( WooCommerce_PDF_Invoices::$plugin_path . "lib/dompdf/dompdf_config.inc.php" );
67
+ $dompdf = new DOMPDF();
68
+ $dompdf->load_html($complete_pdf);
69
+ $dompdf->set_paper($this->template_settings['paper_size'], 'portrait');
70
+ $dompdf->render();
71
+
72
+ return $dompdf;
73
+ }
74
+
75
+ /**
76
+ * Stream PDF
77
+ */
78
+ public function stream_pdf( $template_type, $order_ids, $filename ) {
79
+ $pdf = $this->generate_pdf( $template_type, $order_ids );
80
+ $pdf->stream($filename);
81
+ }
82
+
83
+ /**
84
+ * Get PDF
85
+ */
86
+ public function get_pdf( $template_type, $order_ids ) {
87
+ $pdf = $this->generate_pdf( $template_type, $order_ids );
88
+ return $pdf->output();
89
+ }
90
+
91
+ /**
92
+ * Load and generate the template output with ajax
93
+ */
94
+ public function generate_pdf_ajax() {
95
+
96
+ // Check the nonce
97
+ if( empty( $_GET['action'] ) || ! is_user_logged_in() || !check_admin_referer( $_GET['action'] ) ) {
98
+ wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
99
+ }
100
+
101
+ // Check if all parameters are set
102
+ if( empty( $_GET['template_type'] ) || empty( $_GET['order_ids'] ) ) {
103
+ wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
104
+ }
105
+
106
+ // Check the user privileges
107
+ if( !current_user_can( 'manage_woocommerce_orders' ) && !current_user_can( 'edit_shop_orders' ) ) {
108
+ wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
109
+ }
110
+
111
+ $order_ids = (array) explode('x',$_GET['order_ids']);
112
+
113
+ $template_type = $_GET['template_type'];
114
+ if ($template_type == 'invoice' ) {
115
+ $template_name = _n( 'invoice', 'invoices', count($order_ids), 'wpo_wcpdf' );
116
+ } else {
117
+ $template_name = _n( 'packing-slip', 'packing-slips', count($order_ids), 'wpo_wcpdf' );
118
+ }
119
+
120
+
121
+ // Filename
122
+ if ( count($order_ids) > 1 ) {
123
+ $filename = $template_name . '-' . date('Y-m-d') . '.pdf'; // 'invoices-2020-11-11.pdf'
124
+ } else {
125
+ $order = new WC_Order ( $order_ids[0] );
126
+ $order_number = ltrim( $order->get_order_number(), '#' );
127
+ $filename = $template_name . '-' . $order_number . '.pdf'; // 'packing-slip-123456.pdf'
128
+ }
129
+ $filename = apply_filters( 'wpo_wcpdf_bulk_filename', $filename, $order_ids, $template_name );
130
+
131
+ // Generate the output
132
+ //$this->stream_pdf( $template_type, $order_ids, $filename );
133
+
134
+ $invoice = $this->get_pdf( $template_type, $order_ids );
135
+
136
+ // Get output setting
137
+ $output_mode = isset($this->general_settings['download_display'])?$this->general_settings['download_display']:'';
138
+
139
+ // Switch headers according to output setting
140
+ if ( $output_mode == 'display' || empty($output_mode) ) {
141
+ header('Content-type: application/pdf');
142
+ header('Content-Disposition: inline; filename="'.$filename.'"');
143
+ } else {
144
+ header('Content-Description: File Transfer');
145
+ header('Content-Type: application/octet-stream');
146
+ header('Content-Disposition: attachment; filename="'.$filename.'"');
147
+ header('Content-Transfer-Encoding: binary');
148
+ header('Connection: Keep-Alive');
149
+ header('Expires: 0');
150
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
151
+ header('Pragma: public');
152
+ }
153
+
154
+ // output PDF data
155
+ echo($invoice);
156
+
157
+ exit;
158
+ }
159
+
160
+ /**
161
+ * Attach invoice to completed order or customer invoice email
162
+ */
163
+ public function attach_pdf_to_email ( $attachments, $status , $order ) {
164
+ if (!isset($this->general_settings['email_pdf']))
165
+ return;
166
+
167
+ // clear temp folder (from http://stackoverflow.com/a/13468943/1446634)
168
+ $tmp_path = WooCommerce_PDF_Invoices::$plugin_path . 'tmp/';
169
+ array_map('unlink', ( glob( $tmp_path.'*' ) ?: array() ) );
170
+
171
+ $order_status = apply_filters( 'wpo_wcpdf_attach_to_status', 'completed' );
172
+
173
+ if( isset( $status ) && ( $status=="customer_" . $order_status . "_order" || $status == "customer_invoice" ) ) {
174
+ $order_number = ltrim( $order->get_order_number(), '#' );
175
+ $pdf_filename_prefix = __( 'invoice', 'wpo_wcpdf' );
176
+ $pdf_filename = $pdf_filename_prefix . '-' . $order_number . '.pdf';
177
+ $pdf_filename = apply_filters( 'wpo_wcpdf_attachment_filename', $pdf_filename, $order_number );
178
+ $pdf_path = $tmp_path . $pdf_filename;
179
+
180
+ $invoice = $this->get_pdf( 'invoice', (array) $order->id );
181
+ file_put_contents ( $pdf_path, $invoice );
182
+ $attachments[] = $pdf_path;
183
+ }
184
+
185
+ return $attachments;
186
+ }
187
+
188
+ /**
189
+ * Return evaluated template contents
190
+ */
191
+ public function get_template( $file ) {
192
+ ob_start();
193
+ if (file_exists($file)) {
194
+ include($file);
195
+ }
196
+ return ob_get_clean();
197
+ }
198
+
199
+ /**
200
+ * Get the current order
201
+ */
202
+ public function get_order() {
203
+ return $this->order;
204
+ }
205
+
206
+ /**
207
+ * Get the current order items
208
+ */
209
+ public function get_order_items() {
210
+ global $woocommerce;
211
+ global $_product;
212
+
213
+ $items = $this->order->get_items();
214
+ $data_list = array();
215
+
216
+ if( sizeof( $items ) > 0 ) {
217
+ foreach ( $items as $item ) {
218
+ // Array with data for the pdf template
219
+ $data = array();
220
+
221
+ // Set the id
222
+ $data['product_id'] = $item['product_id'];
223
+ $data['variation_id'] = $item['variation_id'];
224
+
225
+ // Set item name
226
+ $data['name'] = $item['name'];
227
+
228
+ // Set item quantity
229
+ $data['quantity'] = $item['qty'];
230
+
231
+ // Set the subtotal for the number of products
232
+ $data['line_total'] = $item['line_total'];
233
+ $data['line_tax'] = $item['line_tax'];
234
+ $data['tax_rates'] = $this->get_tax_rate( $item['tax_class'], $item['line_total'], $item['line_tax'] );
235
+
236
+ // Set the final subtotal for all products
237
+ $data['line_subtotal'] = $item['line_subtotal'];
238
+ $data['line_subtotal_tax'] = $item['line_subtotal_tax'];
239
+ $data['ex_price'] = $this->get_formatted_item_price ( $item, 'total', 'excl' );
240
+ $data['price'] = $this->get_formatted_item_price ( $item, 'total' );
241
+
242
+ // Calculate the single price with the same rules as the formatted line subtotal (!)
243
+ $data['ex_single_price'] = $this->get_formatted_item_price ( $item, 'single', 'excl' );
244
+ $data['single_price'] = $this->get_formatted_item_price ( $item, 'single' );
245
+
246
+ // Set item meta and replace it when it is empty
247
+ $meta = new WC_Order_Item_Meta( $item['item_meta'] );
248
+ $data['meta'] = $meta->display( false, true );
249
+
250
+ // Pass complete item array
251
+ $data['item'] = $item;
252
+
253
+ // Create the product to display more info
254
+ $data['product'] = null;
255
+
256
+ $product = $this->order->get_product_from_item( $item );
257
+
258
+ // Checking fo existance, thanks to MDesigner0
259
+ if(!empty($product)) {
260
+ // Set the thumbnail id
261
+ $data['thumbnail_id'] = $this->get_thumbnail_id( $product->id );
262
+
263
+ // Set the thumbnail (full img tag)
264
+ $data['thumbnail'] = $product->get_image( 'shop_thumbnail', array( 'title' => '' ) );
265
+
266
+ // Set the single price (turned off to use more consistent calculated price)
267
+ // $data['single_price'] = woocommerce_price ( $product->get_price() );
268
+
269
+ // Set item SKU
270
+ $data['sku'] = $product->get_sku();
271
+
272
+ // Set item weight
273
+ $data['weight'] = $product->get_weight();
274
+
275
+ // Set item dimensions
276
+ $data['dimensions'] = $product->get_dimensions();
277
+
278
+ // Pass complete product object
279
+ $data['product'] = $product;
280
+
281
+ }
282
+
283
+ $data_list[] = apply_filters( 'wpo_wcpdf_order_item_data', $data );
284
+ }
285
+ }
286
+
287
+ return apply_filters( 'wpo_wcpdf_order_items_data', $data_list );
288
+ }
289
+
290
+ /**
291
+ * Gets price - formatted for display.
292
+ *
293
+ * @access public
294
+ * @param mixed $item
295
+ * @return string
296
+ */
297
+ public function get_formatted_item_price ( $item, $type, $tax_display = '' ) {
298
+ $item_price = 0;
299
+ $divider = ($type == 'single')?$item['qty']:1; //divide by 1 if $type is not 'single' (thus 'total')
300
+
301
+ if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) )
302
+ return;
303
+
304
+ if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 ) {
305
+ // WC 2.1 or newer is used
306
+ if ( $tax_display == 'excl' ) {
307
+ $item_price = wc_price( ($this->order->get_line_subtotal( $item )) / $divider, array( 'currency' => $this->order->get_order_currency() ) );
308
+ } else {
309
+ $item_price = wc_price( ($this->order->get_line_subtotal( $item, true )) / $divider, array('currency' => $this->order->get_order_currency()) );
310
+ }
311
+ } else {
312
+ // Backwards compatibility
313
+ if ( $tax_display == 'excl' ) {
314
+ $item_price = woocommerce_price( ($this->order->get_line_subtotal( $item )) / $divider );
315
+ } else {
316
+ $item_price = woocommerce_price( ($this->order->get_line_subtotal( $item, true )) / $divider );
317
+ }
318
+ }
319
+
320
+
321
+ return $item_price;
322
+ }
323
+ /**
324
+ * Get the tax rates/percentages for a given tax class
325
+ * @param string $tax_class tax class slug
326
+ * @return string $tax_rates imploded list of tax rates
327
+ */
328
+ public function get_tax_rate( $tax_class, $line_total, $line_tax ) {
329
+ if (empty($tax_class))
330
+ $tax_class = 'standard';
331
+
332
+ if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 ) {
333
+ // WC 2.1 or newer is used
334
+ $tax = new WC_Tax();
335
+ $taxes = $tax->get_rates( $tax_class );
336
+
337
+ foreach ($taxes as $tax) {
338
+ $tax_rates[$tax['label']] = round( $tax['rate'], 2 ).'%';
339
+ }
340
+
341
+ if (empty($tax_rates))
342
+ $tax_rates = (array) '-';
343
+
344
+ $tax_rates = implode(' ,', $tax_rates );
345
+ } else {
346
+ // Backwards compatibility: calulate tax from line items
347
+ $tax_rates = round( ($line_tax / $line_total)*100, 1 ).'%';
348
+ }
349
+
350
+ return $tax_rates;
351
+ }
352
+
353
+ /**
354
+ * Get order custom field
355
+ */
356
+ public function get_order_field( $field ) {
357
+ if( isset( $this->get_order()->order_custom_fields[$field] ) ) {
358
+ return $this->get_order()->order_custom_fields[$field][0];
359
+ }
360
+ return;
361
+ }
362
+
363
+ /**
364
+ * Returns the main product image ID
365
+ * Adapted from the WC_Product class
366
+ *
367
+ * @access public
368
+ * @return string
369
+ */
370
+ public function get_thumbnail_id ( $product_id ) {
371
+ global $woocommerce;
372
+
373
+ if ( has_post_thumbnail( $product_id ) ) {
374
+ $thumbnail_id = get_post_thumbnail_id ( $product_id );
375
+ } elseif ( ( $parent_id = wp_get_post_parent_id( $product_id ) ) && has_post_thumbnail( $product_id ) ) {
376
+ $thumbnail_id = get_post_thumbnail_id ( $parent_id );
377
+ } else {
378
+ $thumbnail_id = $woocommerce->plugin_url() . '/assets/images/placeholder.png';
379
+ }
380
+
381
+ return $thumbnail_id;
382
+ }
383
+
384
+ }
385
+
386
+ }
includes/class-wcpdf-settings.php ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Settings class
5
+ */
6
+ if ( ! class_exists( 'WooCommerce_PDF_Invoices_Settings' ) ) {
7
+
8
+ class WooCommerce_PDF_Invoices_Settings {
9
+
10
+ public static $options_page_hook;
11
+ public static $general_settings;
12
+ public static $template_settings;
13
+
14
+ public function __construct() {
15
+ add_action( 'admin_menu', array( &$this, 'menu' ) ); // Add menu.
16
+ add_action( 'admin_init', array( &$this, 'init_settings' ) ); // Registers settings
17
+ add_action( 'admin_enqueue_scripts', array( &$this, 'load_scripts_styles' ) ); // Load scripts
18
+
19
+ // Add links to WordPress plugins page
20
+ add_filter( 'plugin_action_links_'.WooCommerce_PDF_Invoices::$plugin_basename, array( &$this, 'wpo_wcpdf_add_settings_link' ) );
21
+ add_filter( 'plugin_row_meta', array( $this, 'add_support_links' ), 10, 2 );
22
+
23
+ $this->general_settings = get_option('wpo_wcpdf_general_settings');
24
+ $this->template_settings = get_option('wpo_wcpdf_template_settings');
25
+ }
26
+
27
+ public function menu() {
28
+ if (class_exists('WPOvernight_Core')) {
29
+ $parent_slug = 'wpo-core-menu';
30
+ } else {
31
+ $parent_slug = 'woocommerce';
32
+ }
33
+
34
+ $this->options_page_hook = add_submenu_page(
35
+ $parent_slug,
36
+ __( 'PDF Invoices', 'wpo_wcpdf' ),
37
+ __( 'PDF Invoices', 'wpo_wcpdf' ),
38
+ 'manage_options',
39
+ 'wpo_wcpdf_options_page',
40
+ array( $this, 'settings_page' )
41
+ );
42
+ }
43
+
44
+ /**
45
+ * Styles for settings page
46
+ */
47
+ public function load_scripts_styles ( $hook ) {
48
+ if( $hook != $this->options_page_hook )
49
+ return;
50
+
51
+ wp_enqueue_script( 'wcpdf-upload-js', plugins_url( 'js/media-upload.js' , dirname(__FILE__) ) );
52
+ wp_enqueue_style( 'wpo-wcpdf', WooCommerce_PDF_Invoices::$plugin_url . 'css/style.css' );
53
+ wp_enqueue_media();
54
+ }
55
+
56
+ /**
57
+ * Add settings link to plugins page
58
+ */
59
+ public function wpo_wcpdf_add_settings_link( $links ) {
60
+ $settings_link = '<a href="admin.php?page=wpo_wcpdf_options_page">'. __( 'Settings', 'woocommerce' ) . '</a>';
61
+ array_push( $links, $settings_link );
62
+ return $links;
63
+ }
64
+
65
+ /**
66
+ * Add various support links to plugin page
67
+ * after meta (version, authors, site)
68
+ */
69
+ public function add_support_links( $links, $file ) {
70
+ if ( !current_user_can( 'install_plugins' ) ) {
71
+ return $links;
72
+ }
73
+
74
+ if ( $file == WooCommerce_PDF_Invoices::$plugin_basename ) {
75
+ $links[] = '<a href="..." target="_blank" title="' . __( '...', 'wpo_wcpdf' ) . '">' . __( '...', 'wpo_wcpdf' ) . '</a>';
76
+ }
77
+ return $links;
78
+ }
79
+
80
+ public function settings_page() {
81
+ $active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'general';
82
+ ?>
83
+
84
+ <div class="wrap">
85
+ <div class="icon32" id="icon-options-general"><br /></div>
86
+ <h2><?php _e( 'WooCommerce PDF Invoices', 'wpo_wcpdf' ); ?></h2>
87
+ <h2 class="nav-tab-wrapper">
88
+ <a href="?page=wpo_wcpdf_options_page&tab=general" class="nav-tab <?php echo $active_tab == 'general' ? 'nav-tab-active' : ''; ?>"><?php _e('General','wpo_wcpdf') ?></a>
89
+ <a href="?page=wpo_wcpdf_options_page&tab=template" class="nav-tab <?php echo $active_tab == 'template' ? 'nav-tab-active' : ''; ?>"><?php _e('Template','wpo_wcpdf') ?></a>
90
+ </h2>
91
+
92
+ <?php if (!class_exists('WooCommerce_PDF_IPS_Templates')) { ?>
93
+
94
+ <div class="wcpdf-pro-templates" style="border: 1px solid #ccc; border-radius: 5px; padding: 10px; margin-top: 15px; background-color: #eee;">
95
+ <?php _e('Looking for more advanced templates? Check out the Premium PDF Invoice & Packing Slips templates at <a href="#">wpovernight.com</a>.', 'wpo_wcpdf');?>
96
+ </div>
97
+
98
+ <?php } ?>
99
+
100
+ <form method="post" action="options.php">
101
+ <?php
102
+ switch ($active_tab) {
103
+ case 'general':
104
+ settings_fields( 'wpo_wcpdf_general_settings' );
105
+ do_settings_sections( 'wpo_wcpdf_general_settings' );
106
+ break;
107
+ case 'template':
108
+ settings_fields( 'wpo_wcpdf_template_settings' );
109
+ do_settings_sections( 'wpo_wcpdf_template_settings' );
110
+ break;
111
+ default:
112
+ settings_fields( 'wpo_wcpdf_general_settings' );
113
+ do_settings_sections( 'wpo_wcpdf_general_settings' );
114
+ break;
115
+ }
116
+
117
+ submit_button();
118
+ ?>
119
+
120
+ </form>
121
+
122
+ </div>
123
+
124
+ <?php
125
+ }
126
+
127
+ /**
128
+ * User settings.
129
+ *
130
+ */
131
+
132
+ public function init_settings() {
133
+ global $woocommerce;
134
+
135
+ /**************************************/
136
+ /*********** GENERAL SETTINGS *********/
137
+ /**************************************/
138
+
139
+ $option = 'wpo_wcpdf_general_settings';
140
+
141
+ // Create option in wp_options.
142
+ if ( false == get_option( $option ) ) {
143
+ add_option( $option );
144
+ }
145
+
146
+ // Section.
147
+ add_settings_section(
148
+ 'general_settings',
149
+ __( 'General settings', 'wpo_wcpdf' ),
150
+ array( &$this, 'section_options_callback' ),
151
+ $option
152
+ );
153
+
154
+ add_settings_field(
155
+ 'download_display',
156
+ __( 'How do you want to view the PDF?', 'wpo_wcpdf' ),
157
+ array( &$this, 'radio_element_callback' ),
158
+ $option,
159
+ 'general_settings',
160
+ array(
161
+ 'menu' => $option,
162
+ 'id' => 'download_display',
163
+ 'options' => array(
164
+ 'download' => __( 'Download the PDF' , 'wpo_wcpdf' ),
165
+ 'display' => __( 'Open the PDF in a new browser tab/window' , 'wpo_wcpdf' ),
166
+ ),
167
+ )
168
+ );
169
+
170
+ $tmp_path = WooCommerce_PDF_Invoices::$plugin_path . 'tmp/';
171
+ $tmp_path_check = !is_writable( $tmp_path );
172
+
173
+ add_settings_field(
174
+ 'email_pdf',
175
+ __( 'Email invoice (attach to order confirmation or invoice email)', 'wpo_wcpdf' ),
176
+ array( &$this, 'checkbox_element_callback' ),
177
+ $option,
178
+ 'general_settings',
179
+ array(
180
+ 'menu' => $option,
181
+ 'id' => 'email_pdf',
182
+ 'description' => $tmp_path_check ? '<span class="wpo-warning">' . sprintf( __( 'It looks like the temp folder (<code>%s</code>) is not writable, check the permissions for this folder! Without having write access to this folder, the plugin will not be able to email invoices.', 'wpo_wcpdf' ), $tmp_path ).'</span>':'',
183
+ )
184
+ );
185
+
186
+ // Register settings.
187
+ register_setting( $option, $option, array( &$this, 'validate_options' ) );
188
+
189
+
190
+ /**************************************/
191
+ /********** TEMPLATE SETTINGS *********/
192
+ /**************************************/
193
+
194
+ $option = 'wpo_wcpdf_template_settings';
195
+
196
+ // Create option in wp_options.
197
+ if ( false == get_option( $option ) ) {
198
+ add_option( $option );
199
+ }
200
+
201
+ // Section.
202
+ add_settings_section(
203
+ 'template_settings',
204
+ __( 'PDF Template settings', 'wpo_wcpdf' ),
205
+ array( &$this, 'section_options_callback' ),
206
+ $option
207
+ );
208
+
209
+ add_settings_field(
210
+ 'template_path',
211
+ __( 'Choose a template', 'wpo_wcpdf' ),
212
+ array( &$this, 'select_element_callback' ),
213
+ $option,
214
+ 'template_settings',
215
+ array(
216
+ 'menu' => $option,
217
+ 'id' => 'template_path',
218
+ 'options' => $this->find_templates(),
219
+ 'description' => __( 'Want to use your own template? Copy the files from <code>woocommerce-pdf-invoices-packing-slips/templates/pdf/Simple/</code> to <code>yourtheme/woocommerce/pdf/yourtemplate/</code> to customize them' , 'wpo_wcpdf' ),
220
+ )
221
+ );
222
+
223
+ add_settings_field(
224
+ 'paper_size',
225
+ __( 'Paper size', 'wpo_wcpdf' ),
226
+ array( &$this, 'select_element_callback' ),
227
+ $option,
228
+ 'template_settings',
229
+ array(
230
+ 'menu' => $option,
231
+ 'id' => 'paper_size',
232
+ 'options' => array(
233
+ 'a4' => __( 'A4' , 'wpo_wcpdf' ),
234
+ 'letter' => __( 'Letter' , 'wpo_wcpdf' ),
235
+ ),
236
+ )
237
+ );
238
+
239
+ add_settings_field(
240
+ 'header_logo',
241
+ __( 'Shop header/logo', 'wpo_wcpdf' ),
242
+ array( &$this, 'media_upload_callback' ),
243
+ $option,
244
+ 'template_settings',
245
+ array(
246
+ 'menu' => $option,
247
+ 'id' => 'header_logo',
248
+ 'uploader_title' => __( 'Select or upload your invoice header/logo', 'wpo_wcpdf' ),
249
+ 'uploader_button_text' => __( 'Set image', 'wpo_wcpdf' ),
250
+ 'remove_button_text' => __( 'Remove image', 'wpo_wcpdf' ),
251
+ //'description' => __( '...', 'wpo_wcpdf' ),
252
+ )
253
+ );
254
+
255
+ add_settings_field(
256
+ 'shop_name',
257
+ __( 'Shop Name', 'wpo_wcpdf' ),
258
+ array( &$this, 'text_element_callback' ),
259
+ $option,
260
+ 'template_settings',
261
+ array(
262
+ 'menu' => $option,
263
+ 'id' => 'shop_name',
264
+ 'size' => '72',
265
+ )
266
+ );
267
+
268
+ add_settings_field(
269
+ 'shop_address',
270
+ __( 'Shop Address', 'wpo_wcpdf' ),
271
+ array( &$this, 'textarea_element_callback' ),
272
+ $option,
273
+ 'template_settings',
274
+ array(
275
+ 'menu' => $option,
276
+ 'id' => 'shop_address',
277
+ 'width' => '28',
278
+ 'height' => '8',
279
+ //'description' => __( '...', 'wpo_wcpdf' ),
280
+ )
281
+ );
282
+
283
+ /*
284
+ add_settings_field(
285
+ 'personal_notes',
286
+ __( 'Personal notes', 'wpo_wcpdf' ),
287
+ array( &$this, 'textarea_element_callback' ),
288
+ $option,
289
+ 'template_settings',
290
+ array(
291
+ 'menu' => $option,
292
+ 'id' => 'personal_notes',
293
+ 'width' => '72',
294
+ 'height' => '4',
295
+ //'description' => __( '...', 'wpo_wcpdf' ),
296
+ )
297
+ );
298
+ */
299
+
300
+ add_settings_field(
301
+ 'footer',
302
+ __( 'Footer: terms & conditions, policies, etc.', 'wpo_wcpdf' ),
303
+ array( &$this, 'textarea_element_callback' ),
304
+ $option,
305
+ 'template_settings',
306
+ array(
307
+ 'menu' => $option,
308
+ 'id' => 'footer',
309
+ 'width' => '72',
310
+ 'height' => '4',
311
+ //'description' => __( '...', 'wpo_wcpdf' ),
312
+ )
313
+ );
314
+
315
+ // Section.
316
+ add_settings_section(
317
+ 'extra_template_fields',
318
+ __( 'Extra template fields', 'wpo_wcpdf' ),
319
+ array( &$this, 'custom_fields_section' ),
320
+ $option
321
+ );
322
+
323
+ add_settings_field(
324
+ 'extra_1',
325
+ __( 'Extra field 1', 'wpo_wcpdf' ),
326
+ array( &$this, 'textarea_element_callback' ),
327
+ $option,
328
+ 'extra_template_fields',
329
+ array(
330
+ 'menu' => $option,
331
+ 'id' => 'extra_1',
332
+ 'width' => '28',
333
+ 'height' => '8',
334
+ 'description' => __( 'This is footer column 1 in the <i>Modern (Premium)</i> template', 'wpo_wcpdf' ),
335
+ )
336
+ );
337
+
338
+ add_settings_field(
339
+ 'extra_2',
340
+ __( 'Extra field 2', 'wpo_wcpdf' ),
341
+ array( &$this, 'textarea_element_callback' ),
342
+ $option,
343
+ 'extra_template_fields',
344
+ array(
345
+ 'menu' => $option,
346
+ 'id' => 'extra_2',
347
+ 'width' => '28',
348
+ 'height' => '8',
349
+ 'description' => __( 'This is footer column 2 in the <i>Modern (Premium)</i> template', 'wpo_wcpdf' ),
350
+ )
351
+ );
352
+
353
+ add_settings_field(
354
+ 'extra_3',
355
+ __( 'Extra field 3', 'wpo_wcpdf' ),
356
+ array( &$this, 'textarea_element_callback' ),
357
+ $option,
358
+ 'extra_template_fields',
359
+ array(
360
+ 'menu' => $option,
361
+ 'id' => 'extra_3',
362
+ 'width' => '28',
363
+ 'height' => '8',
364
+ 'description' => __( 'This is footer column 3 in the <i>Modern (Premium)</i> template', 'wpo_wcpdf' ),
365
+ )
366
+ );
367
+
368
+ // Register settings.
369
+ register_setting( $option, $option, array( &$this, 'validate_options' ) );
370
+
371
+ // Register defaults if settings empty (might not work in case there's only checkboxes and they're all disabled)
372
+ $option_values = get_option($option);
373
+ if ( empty( $option_values ) )
374
+ $this->default_settings();
375
+ }
376
+
377
+ /**
378
+ * Set default settings.
379
+ */
380
+ public function default_settings() {
381
+ global $wpo_wcpdf;
382
+
383
+ $default_general = array(
384
+ 'download_display' => 'download',
385
+ 'email_pdf' => '1',
386
+ );
387
+
388
+ $default_template = array(
389
+ 'paper_size' => 'a4',
390
+ 'template_path' => $wpo_wcpdf->export->template_default_base_path . 'Simple',
391
+ );
392
+
393
+ update_option( 'wpo_wcpdf_general_settings', $default_general );
394
+ update_option( 'wpo_wcpdf_template_settings', $default_template );
395
+ }
396
+
397
+ // Text element callback.
398
+ public function text_element_callback( $args ) {
399
+ $menu = $args['menu'];
400
+ $id = $args['id'];
401
+ $size = isset( $args['size'] ) ? $args['size'] : '25';
402
+
403
+ $options = get_option( $menu );
404
+
405
+ if ( isset( $options[$id] ) ) {
406
+ $current = $options[$id];
407
+ } else {
408
+ $current = isset( $args['default'] ) ? $args['default'] : '';
409
+ }
410
+
411
+ $html = sprintf( '<input type="text" id="%1$s" name="%2$s[%1$s]" value="%3$s" size="%4$s"/>', $id, $menu, $current, $size );
412
+
413
+ // Displays option description.
414
+ if ( isset( $args['description'] ) ) {
415
+ $html .= sprintf( '<p class="description">%s</p>', $args['description'] );
416
+ }
417
+
418
+ echo $html;
419
+ }
420
+
421
+ // Text element callback.
422
+ public function textarea_element_callback( $args ) {
423
+ $menu = $args['menu'];
424
+ $id = $args['id'];
425
+ $width = $args['width'];
426
+ $height = $args['height'];
427
+
428
+ $options = get_option( $menu );
429
+
430
+ if ( isset( $options[$id] ) ) {
431
+ $current = $options[$id];
432
+ } else {
433
+ $current = isset( $args['default'] ) ? $args['default'] : '';
434
+ }
435
+
436
+ $html = sprintf( '<textarea id="%1$s" name="%2$s[%1$s]" cols="%4$s" rows="%5$s"/>%3$s</textarea>', $id, $menu, $current, $width, $height );
437
+
438
+ // Displays option description.
439
+ if ( isset( $args['description'] ) ) {
440
+ $html .= sprintf( '<p class="description">%s</p>', $args['description'] );
441
+ }
442
+
443
+ echo $html;
444
+ }
445
+
446
+
447
+ /**
448
+ * Checkbox field callback.
449
+ *
450
+ * @param array $args Field arguments.
451
+ *
452
+ * @return string Checkbox field.
453
+ */
454
+ public function checkbox_element_callback( $args ) {
455
+ $menu = $args['menu'];
456
+ $id = $args['id'];
457
+
458
+ $options = get_option( $menu );
459
+
460
+ if ( isset( $options[$id] ) ) {
461
+ $current = $options[$id];
462
+ } else {
463
+ $current = isset( $args['default'] ) ? $args['default'] : '';
464
+ }
465
+
466
+ $html = sprintf( '<input type="checkbox" id="%1$s" name="%2$s[%1$s]" value="1"%3$s />', $id, $menu, checked( 1, $current, false ) );
467
+
468
+ //$html .= sprintf( '<label for="%s"> %s</label><br />', $id, __( 'Activate/Deactivate', 'wpo_wcpdf' ) );
469
+
470
+ // Displays option description.
471
+ if ( isset( $args['description'] ) ) {
472
+ $html .= sprintf( '<p class="description">%s</p>', $args['description'] );
473
+ }
474
+
475
+ echo $html;
476
+ }
477
+
478
+ /**
479
+ * Select element callback.
480
+ *
481
+ * @param array $args Field arguments.
482
+ *
483
+ * @return string Select field.
484
+ */
485
+ public function select_element_callback( $args ) {
486
+ $menu = $args['menu'];
487
+ $id = $args['id'];
488
+
489
+ $options = get_option( $menu );
490
+
491
+ if ( isset( $options[$id] ) ) {
492
+ $current = $options[$id];
493
+ } else {
494
+ $current = isset( $args['default'] ) ? $args['default'] : '';
495
+ }
496
+
497
+ $html = sprintf( '<select id="%1$s" name="%2$s[%1$s]">', $id, $menu );
498
+
499
+ foreach ( $args['options'] as $key => $label ) {
500
+ $html .= sprintf( '<option value="%s"%s>%s</option>', $key, selected( $current, $key, false ), $label );
501
+ }
502
+
503
+ $html .= '</select>';
504
+
505
+ // Displays option description.
506
+ if ( isset( $args['description'] ) ) {
507
+ $html .= sprintf( '<p class="description">%s</p>', $args['description'] );
508
+ }
509
+
510
+ echo $html;
511
+ }
512
+
513
+ /**
514
+ * Displays a radio settings field
515
+ *
516
+ * @param array $args settings field args
517
+ */
518
+ public function radio_element_callback( $args ) {
519
+ $menu = $args['menu'];
520
+ $id = $args['id'];
521
+
522
+ $options = get_option( $menu );
523
+
524
+ if ( isset( $options[$id] ) ) {
525
+ $current = $options[$id];
526
+ } else {
527
+ $current = isset( $args['default'] ) ? $args['default'] : '';
528
+ }
529
+
530
+ $html = '';
531
+ foreach ( $args['options'] as $key => $label ) {
532
+ $html .= sprintf( '<input type="radio" class="radio" id="%1$s[%2$s][%3$s]" name="%1$s[%2$s]" value="%3$s"%4$s />', $menu, $id, $key, checked( $current, $key, false ) );
533
+ $html .= sprintf( '<label for="%1$s[%2$s][%3$s]"> %4$s</label><br>', $menu, $id, $key, $label);
534
+ }
535
+
536
+ // Displays option description.
537
+ if ( isset( $args['description'] ) ) {
538
+ $html .= sprintf( '<p class="description">%s</p>', $args['description'] );
539
+ }
540
+
541
+ echo $html;
542
+ }
543
+
544
+ /**
545
+ * Media upload callback.
546
+ *
547
+ * @param array $args Field arguments.
548
+ *
549
+ * @return string Media upload button & preview.
550
+ */
551
+ public function media_upload_callback( $args ) {
552
+ $menu = $args['menu'];
553
+ $id = $args['id'];
554
+ $options = get_option( $menu );
555
+
556
+ if ( isset( $options[$id] ) ) {
557
+ $current = $options[$id];
558
+ } else {
559
+ $current = isset( $args['default'] ) ? $args['default'] : '';
560
+ }
561
+
562
+ $uploader_title = $args['uploader_title'];
563
+ $uploader_button_text = $args['uploader_button_text'];
564
+ $remove_button_text = $args['remove_button_text'];
565
+
566
+ $html = '';
567
+ if( !empty($current) ) {
568
+ $attachment = wp_get_attachment_image_src( $current, 'full', false );
569
+
570
+ $attachment_src = $attachment[0];
571
+ $attachment_width = $attachment[1];
572
+ $attachment_height = $attachment[2];
573
+
574
+ $attachment_resolution = round($attachment_height/(3/2.54));
575
+
576
+ $html .= sprintf('<img src="%1$s" style="display:block" id="img-%4$s"/>', $attachment_src, $attachment_width, $attachment_height, $id );
577
+ $html .= '<div class="attachment-resolution"><p class="description">'.__('Image resolution').': '.$attachment_resolution.'dpi (default height = 3cm)</p></div>';
578
+ $html .= sprintf('<span class="button remove_image_button" data-input_id="%1$s">%2$s</span>', $id, $remove_button_text );
579
+ }
580
+
581
+ $html .= sprintf( '<input id="%1$s" name="%2$s[%1$s]" type="hidden" value="%3$s" />', $id, $menu, $current );
582
+
583
+ $html .= sprintf( '<span class="button upload_image_button %4$s" data-uploader_title="%1$s" data-uploader_button_text="%2$s" data-remove_button_text="%3$s" data-input_id="%4$s">%2$s</span>', $uploader_title, $uploader_button_text, $remove_button_text, $id );
584
+
585
+ // Displays option description.
586
+ if ( isset( $args['description'] ) ) {
587
+ $html .= sprintf( '<p class="description">%s</p>', $args['description'] );
588
+ }
589
+
590
+ echo $html;
591
+ }
592
+
593
+ /**
594
+ * Section null callback.
595
+ *
596
+ * @return void.
597
+ */
598
+ public function section_options_callback() {
599
+ }
600
+
601
+ /**
602
+ * Section null callback.
603
+ *
604
+ * @return void.
605
+ */
606
+ public function custom_fields_section() {
607
+ _e( 'These are used for the (optional) footer columns in the <em>Modern (Premium)</em> template, but can also be used for other elements in your custom template' , 'wpo_wcpdf' );
608
+ }
609
+
610
+ /**
611
+ * Validate options.
612
+ *
613
+ * @param array $input options to valid.
614
+ *
615
+ * @return array validated options.
616
+ */
617
+ public function validate_options( $input ) {
618
+ // Create our array for storing the validated options.
619
+ $output = array();
620
+
621
+ // Loop through each of the incoming options.
622
+ foreach ( $input as $key => $value ) {
623
+
624
+ // Check to see if the current option has a value. If so, process it.
625
+ if ( isset( $input[$key] ) ) {
626
+
627
+ // Strip all HTML and PHP tags and properly handle quoted strings.
628
+ $output[$key] = strip_tags( stripslashes( $input[$key] ) );
629
+
630
+ // Or alternatively: don't strip HTML! :o)
631
+ //$output[$key] = stripslashes( $input[$key] );
632
+ }
633
+ }
634
+
635
+ // Return the array processing any additional functions filtered by this action.
636
+ return apply_filters( 'wpo_wcpdf_validate_input', $output, $input );
637
+ }
638
+
639
+ /**
640
+ * List templates in plugin folder, theme folder & child theme folder
641
+ * @return array template path => template name
642
+ */
643
+ public function find_templates() {
644
+ global $wpo_wcpdf;
645
+ $installed_templates = array();
646
+
647
+ // get base paths
648
+ $template_paths = array (
649
+ // note the order: child-theme before theme, so that array_unique filters out parent doubles
650
+ 'default' => $wpo_wcpdf->export->template_default_base_path,
651
+ 'child-theme' => get_stylesheet_directory() . '/' . $wpo_wcpdf->export->template_base_path,
652
+ 'theme' => get_template_directory() . '/' . $wpo_wcpdf->export->template_base_path,
653
+ );
654
+
655
+ $template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths );
656
+
657
+ foreach ($template_paths as $template_source => $template_path) {
658
+ $dirs = glob( $template_path . '*' , GLOB_ONLYDIR);
659
+
660
+ foreach ($dirs as $dir) {
661
+ if ( file_exists($dir."/invoice.php") && file_exists($dir."/packing-slip.php"))
662
+ $installed_templates[$dir] = basename($dir);
663
+ }
664
+ }
665
+
666
+ // remove parent doubles
667
+ $installed_templates = array_unique($installed_templates);
668
+
669
+ return $installed_templates;
670
+ }
671
+
672
+ } // end class WooCommerce_PDF_Invoices_Settings
673
+
674
+ } // end class_exists
includes/class-wcpdf-writepanels.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Writepanel class
5
+ */
6
+ if ( !class_exists( 'WooCommerce_PDF_Invoices_Writepanels' ) ) {
7
+
8
+ class WooCommerce_PDF_Invoices_Writepanels {
9
+
10
+ /**
11
+ * Constructor
12
+ */
13
+ public function __construct() {
14
+ add_action( 'woocommerce_admin_order_actions_end', array( $this, 'add_listing_actions' ) );
15
+ add_action( 'add_meta_boxes_shop_order', array( $this, 'add_box' ) );
16
+ add_action( 'admin_print_scripts', array( $this, 'add_scripts' ) );
17
+ add_action( 'admin_print_styles', array( $this, 'add_styles' ) );
18
+ add_action( 'admin_footer-edit.php', array(&$this, 'bulk_actions') );
19
+ $this->general_settings = get_option('wpo_wcpdf_general_settings');
20
+ $this->template_settings = get_option('wpo_wcpdf_template_settings');
21
+ }
22
+
23
+ /**
24
+ * Add the styles
25
+ */
26
+ public function add_styles() {
27
+ if( $this->is_order_edit_page() ) {
28
+ wp_enqueue_style( 'thickbox' );
29
+ wp_enqueue_style( 'wpo-wcpdf', WooCommerce_PDF_Invoices::$plugin_url . 'css/style.css' );
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Add the scripts
35
+ */
36
+ public function add_scripts() {
37
+ if( $this->is_order_edit_page() ) {
38
+ wp_enqueue_script( 'wpo-wcpdf', WooCommerce_PDF_Invoices::$plugin_url . 'js/script.js', array( 'jquery' ) );
39
+ wp_localize_script(
40
+ 'wpo-wcpdf',
41
+ 'wpo_wcpdf_ajax',
42
+ array(
43
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ), // URL to WordPress ajax handling page
44
+ 'nonce' => wp_create_nonce('generate_wpo_wcpdf')
45
+ )
46
+ );
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Is order page
52
+ */
53
+ public function is_order_edit_page() {
54
+ global $post_type;
55
+ if( $post_type == 'shop_order' ) {
56
+ return true;
57
+ } else {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Add PDF actions to the orders listing
64
+ */
65
+ public function add_listing_actions( $order ) {
66
+ ?>
67
+ <a href="<?php echo wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=invoice&order_ids=' . $order->id ), 'generate_wpo_wcpdf' ); ?>" class="button tips wpo_wcpdf" target="_blank" alt="<?php esc_attr_e( 'PDF invoice', 'wpo_wcpdf' ); ?>" data-tip="<?php esc_attr_e( 'PDF invoice', 'wpo_wcpdf' ); ?>">
68
+ <img src="<?php echo WooCommerce_PDF_Invoices::$plugin_url . 'images/invoice.png'; ?>" alt="<?php esc_attr_e( 'PDF invoice', 'wpo_wcpdf' ); ?>" width="16">
69
+ </a>
70
+ <a href="<?php echo wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=packing-slip&order_ids=' . $order->id ), 'generate_wpo_wcpdf' ); ?>" class="button tips wpo_wcpdf" target="_blank" alt="<?php esc_attr_e( 'PDF Packing Slip', 'wpo_wcpdf' ); ?>" data-tip="<?php esc_attr_e( 'PDF Packing Slip', 'wpo_wcpdf' ); ?>">
71
+ <img src="<?php echo WooCommerce_PDF_Invoices::$plugin_url . 'images/packing-slip.png'; ?>" alt="<?php esc_attr_e( 'PDF Packing Slip', 'wpo_wcpdf' ); ?>" width="16">
72
+ </a>
73
+ <?php
74
+ }
75
+
76
+ /**
77
+ * Add the meta box on the single order page
78
+ */
79
+ public function add_box() {
80
+ add_meta_box( 'wpo_wcpdf-box', __( 'Create PDF', 'wpo_wcpdf' ), array( $this, 'create_box_content' ), 'shop_order', 'side', 'default' );
81
+ }
82
+
83
+ /**
84
+ * Create the meta box content on the single order page
85
+ */
86
+ public function create_box_content() {
87
+ global $post_id;
88
+ ?>
89
+ <ul class="wpo_wcpdf-actions">
90
+ <li><a href="<?php echo wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=invoice&order_ids=' . $post_id ), 'generate_wpo_wcpdf' ); ?>" class="button" target="_blank" alt="<?php esc_attr_e( 'PDF Invoice', 'wpo_wcpdf' ); ?>"><?php _e( 'PDF invoice', 'wpo_wcpdf' ); ?></a></li>
91
+ <li><a href="<?php echo wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=packing-slip&order_ids=' . $post_id ), 'generate_wpo_wcpdf' ); ?>" class="button" target="_blank" alt="<?php esc_attr_e( 'PDF Packing Slip', 'wpo_wcpdf' ); ?>"><?php _e( 'PDF Packing Slip', 'wpo_wcpdf' ); ?></a></li>
92
+ </ul>
93
+ <?php
94
+ }
95
+ /**
96
+ * Add actions to menu
97
+ */
98
+ public function bulk_actions() {
99
+ global $post_type;
100
+
101
+ if ( 'shop_order' == $post_type ) {
102
+ ?>
103
+ <script type="text/javascript">
104
+ jQuery(document).ready(function() {
105
+ jQuery('<option>').val('invoice').text('<?php _e( 'PDF Invoices', 'wpo_wcpdf' )?>').appendTo("select[name='action']");
106
+ jQuery('<option>').val('invoice').text('<?php _e( 'PDF Invoices', 'wpo_wcpdf' )?>').appendTo("select[name='action2']");
107
+ jQuery('<option>').val('packing-slip').text('<?php _e( 'PDF Packing Slips', 'wpo_wcpdf' )?>').appendTo("select[name='action']");
108
+ jQuery('<option>').val('packing-slip').text('<?php _e( 'PDF Packing Slips', 'wpo_wcpdf' )?>').appendTo("select[name='action2']");
109
+ });
110
+ </script>
111
+ <?php
112
+ }
113
+ }
114
+ }
115
+ }
js/media-upload.js ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Thanks to Mike Jolley!
2
+ // http://mikejolley.com/2012/12/using-the-new-wordpress-3-5-media-uploader-in-plugins/
3
+
4
+ jQuery(document).ready(function($) {
5
+
6
+ // Uploading files
7
+ var file_frame;
8
+
9
+ jQuery('.upload_image_button').live('click', function( event ){
10
+
11
+ // get input field id from data-input_id
12
+ input_id = '#'+jQuery( this ).data( 'input_id' );
13
+ input_id_class = '.'+jQuery( this ).data( 'input_id' );
14
+ input_id_clean = jQuery( this ).data( 'input_id' );
15
+
16
+ // get remove button text
17
+ remove_button_text = jQuery( this ).data( 'remove_button_text' );
18
+
19
+ event.preventDefault();
20
+
21
+ // If the media frame already exists, reopen it.
22
+ if ( file_frame ) {
23
+ file_frame.open();
24
+ return;
25
+ }
26
+
27
+
28
+ // Create the media frame.
29
+ file_frame = wp.media.frames.file_frame = wp.media({
30
+ title: jQuery( this ).data( 'uploader_title' ),
31
+ button: {
32
+ text: jQuery( this ).data( 'uploader_button_text' ),
33
+ },
34
+ multiple: false // Set to true to allow multiple files to be selected
35
+ });
36
+
37
+ // When an image is selected, run a callback.
38
+ file_frame.on( 'select', function() {
39
+ // We set multiple to false so only get one image from the uploader
40
+ attachment = file_frame.state().get('selection').first().toJSON();
41
+
42
+ // set the value of the input field to the attachment id
43
+ jQuery( input_id ).val(attachment.id);
44
+
45
+ if (jQuery( '#img-'+input_id_clean ).length){
46
+ jQuery( '#img-'+input_id_clean ).attr("src", attachment.url );
47
+ } else {
48
+ // show image & remove button
49
+ attachment_img = '<img src="'+attachment.url+'" style="display:block" id="img-'+input_id_clean+'"/>';
50
+ remove_button = '<span class="button remove_image_button" data-input_id="'+input_id_clean+'">'+remove_button_text+'</span>';
51
+ jQuery( input_id ).before(attachment_img+remove_button);
52
+
53
+ }
54
+ });
55
+
56
+ // Finally, open the modal
57
+ file_frame.open();
58
+ });
59
+
60
+ jQuery('.remove_image_button').live('click', function( event ){
61
+
62
+
63
+ // get input field from data-input_id
64
+ input_id = '#'+jQuery( this ).data( 'input_id' );
65
+ img_id = '#img-'+jQuery( this ).data( 'input_id' );
66
+
67
+ jQuery( input_id ).val('');
68
+ jQuery( img_id ).remove();
69
+ jQuery( this ).remove();
70
+ });
71
+ });
js/script.js ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+
3
+ $("#doaction, #doaction2").click(function (event) {
4
+ var actionselected = $(this).attr("id").substr(2);
5
+ var pdf = $('select[name="' + actionselected + '"]').val();
6
+ if ( pdf == "packing-slip" || pdf == "invoice") {
7
+ event.preventDefault();
8
+ var checked = [];
9
+ $('tbody th.check-column input[type="checkbox"]:checked').each(
10
+ function() {
11
+ checked.push($(this).val());
12
+ }
13
+ );
14
+
15
+ var order_ids=checked.join('x');
16
+ url = wpo_wcpdf_ajax.ajaxurl+'?action=generate_wpo_wcpdf&template_type='+pdf+'&order_ids='+order_ids+'&_wpnonce='+wpo_wcpdf_ajax.nonce;
17
+ window.open(url,'_blank');
18
+ }
19
+ });
20
+ });
21
+
languages/wpo_wcpdf-nl_NL.mo ADDED
Binary file
languages/wpo_wcpdf-nl_NL.po ADDED
@@ -0,0 +1,369 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: WP Menu Cart\n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: 2014-01-12 20:16+0100\n"
6
+ "PO-Revision-Date: 2014-01-12 20:16+0100\n"
7
+ "Last-Translator: Ewout Fernhout <chocolade@extrapuur.nl>\n"
8
+ "Language-Team: WP Overnight <support@wpovernight.com>\n"
9
+ "Language: nl_NL\n"
10
+ "MIME-Version: 1.0\n"
11
+ "Content-Type: text/plain; charset=UTF-8\n"
12
+ "Content-Transfer-Encoding: 8bit\n"
13
+ "Plural-Forms: nplurals=2; plural=n != 1;\n"
14
+ "X-Poedit-SourceCharset: UTF-8\n"
15
+ "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;"
16
+ "_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2\n"
17
+ "X-Poedit-Basepath: ../\n"
18
+ "X-Textdomain-Support: yes\n"
19
+ "X-Generator: Poedit 1.6.3\n"
20
+ "X-Poedit-SearchPath-0: .\n"
21
+
22
+ #: woocommerce-pdf-invoices-packingslips.php:106
23
+ #, php-format
24
+ msgid ""
25
+ "WooCommerce PDF Invoices & Packing Slips requires <a href=\"%s"
26
+ "\">WooCommerce</a> to be installed & activated!"
27
+ msgstr ""
28
+
29
+ #: woocommerce-pdf-invoices-packingslips.php:204
30
+ #: woocommerce-pdf-invoices-packingslips.php:240
31
+ msgid "N/A"
32
+ msgstr ""
33
+
34
+ #: woocommerce-pdf-invoices-packingslips.php:341
35
+ msgid "Subtotal"
36
+ msgstr "Subtotaal"
37
+
38
+ #: woocommerce-pdf-invoices-packingslips.php:359
39
+ msgid "Shipping"
40
+ msgstr "Verzendkosten"
41
+
42
+ #: woocommerce-pdf-invoices-packingslips.php:374
43
+ msgid "Discount"
44
+ msgstr "Korting"
45
+
46
+ #: woocommerce-pdf-invoices-packingslips.php:432
47
+ msgid "Total ex. VAT"
48
+ msgstr "Totaal excl. BTW"
49
+
50
+ #: woocommerce-pdf-invoices-packingslips.php:435
51
+ msgid "Total"
52
+ msgstr "Totaal"
53
+
54
+ #: includes/class-wcpdf-export.php:98 includes/class-wcpdf-export.php:103
55
+ #: includes/class-wcpdf-export.php:108
56
+ msgid "You do not have sufficient permissions to access this page."
57
+ msgstr ""
58
+
59
+ #: includes/class-wcpdf-export.php:115 includes/class-wcpdf-export.php:175
60
+ msgid "invoice"
61
+ msgid_plural "invoices"
62
+ msgstr[0] "factuur"
63
+ msgstr[1] "facturen"
64
+
65
+ #: includes/class-wcpdf-export.php:117
66
+ msgid "packing-slip"
67
+ msgid_plural "packing-slips"
68
+ msgstr[0] "pakbon"
69
+ msgstr[1] "pakbonnen"
70
+
71
+ #: includes/class-wcpdf-settings.php:36 includes/class-wcpdf-settings.php:37
72
+ #: includes/class-wcpdf-writepanels.php:105
73
+ #: includes/class-wcpdf-writepanels.php:106
74
+ msgid "PDF Invoices"
75
+ msgstr "PDF facturen"
76
+
77
+ #: includes/class-wcpdf-settings.php:60
78
+ msgid "Settings"
79
+ msgstr "Instellingen"
80
+
81
+ #: includes/class-wcpdf-settings.php:75
82
+ msgid "..."
83
+ msgstr ""
84
+
85
+ #: includes/class-wcpdf-settings.php:86
86
+ msgid "WooCommerce PDF Invoices"
87
+ msgstr "WooCommerce PDF Facturen"
88
+
89
+ #: includes/class-wcpdf-settings.php:88
90
+ msgid "General"
91
+ msgstr "Algemeen"
92
+
93
+ #: includes/class-wcpdf-settings.php:89
94
+ msgid "Template"
95
+ msgstr "Sjabloon"
96
+
97
+ #: includes/class-wcpdf-settings.php:142
98
+ msgid "General settings"
99
+ msgstr "Algemene instellingen"
100
+
101
+ #: includes/class-wcpdf-settings.php:149
102
+ msgid "How do you want to view the PDF?"
103
+ msgstr "Hoe wil je de PDF bekijken?"
104
+
105
+ #: includes/class-wcpdf-settings.php:157
106
+ msgid "Download the PDF"
107
+ msgstr "Download de PDF"
108
+
109
+ #: includes/class-wcpdf-settings.php:158
110
+ msgid "Open the PDF in a new browser tab/window"
111
+ msgstr "Open de PDF in een nieuwe browser tab/venster"
112
+
113
+ #: includes/class-wcpdf-settings.php:168
114
+ msgid "Email invoice (attach to order confirmation or invoice email)"
115
+ msgstr "Email factuur (voeg toe aan orderbevestigings-email of factuur email)"
116
+
117
+ #: includes/class-wcpdf-settings.php:175
118
+ #, php-format
119
+ msgid ""
120
+ "It looks like the temp folder (<code>%s</code>) is not writable, check the "
121
+ "permissions for this folder! Without having write access to this folder, the "
122
+ "plugin will not be able to email invoices."
123
+ msgstr ""
124
+ "Het lijkt erop dat de temp folder (<code>%s</code>) niet schrijfbaar is, "
125
+ "controleer de rechten op deze folder! Zonder schrijfrechten kan de plugin "
126
+ "geen facturen emailen."
127
+
128
+ #: includes/class-wcpdf-settings.php:197
129
+ msgid "PDF Template settings"
130
+ msgstr "PDF Template instellingen"
131
+
132
+ #: includes/class-wcpdf-settings.php:204
133
+ msgid "Choose a template"
134
+ msgstr "Kies een sjabloon"
135
+
136
+ #: includes/class-wcpdf-settings.php:212
137
+ msgid ""
138
+ "Want to use your own template? Copy the files from <code>woocommerce-pdf-"
139
+ "invoices-packing-slips/templates/pdf/Simple/</code> to <code>yourtheme/"
140
+ "woocommerce/pdf/yourtemplate/</code> to customize them"
141
+ msgstr ""
142
+ "Wil je je eigen sjabloon gebruiken? Kopieer de bestanden van "
143
+ "<code>woocommerce-pdf-invoices-packing-slips/templates/pdf/Simple/</code> "
144
+ "naar <code>jouwtheme/woocommerce/pdf/jouwtemplate/</code> en pas ze aan naar "
145
+ "je wensen."
146
+
147
+ #: includes/class-wcpdf-settings.php:218
148
+ msgid "Paper size"
149
+ msgstr "Papier formaat"
150
+
151
+ #: includes/class-wcpdf-settings.php:226
152
+ msgid "A4"
153
+ msgstr "A4"
154
+
155
+ #: includes/class-wcpdf-settings.php:227
156
+ msgid "Letter"
157
+ msgstr "Letter (US)"
158
+
159
+ #: includes/class-wcpdf-settings.php:234
160
+ msgid "Shop header/logo"
161
+ msgstr "Shop header/logo"
162
+
163
+ #: includes/class-wcpdf-settings.php:241
164
+ msgid "Select or upload your invoice header/logo"
165
+ msgstr "Selecteer of upload je factuur header/logo"
166
+
167
+ #: includes/class-wcpdf-settings.php:242
168
+ msgid "Set image"
169
+ msgstr "Stel afbeelding in"
170
+
171
+ #: includes/class-wcpdf-settings.php:243
172
+ msgid "Remove image"
173
+ msgstr "Verwijder afbeelding"
174
+
175
+ #: includes/class-wcpdf-settings.php:250
176
+ msgid "Shop Name"
177
+ msgstr "Shop Naam"
178
+
179
+ #: includes/class-wcpdf-settings.php:263
180
+ msgid "Shop Address"
181
+ msgstr "Shop Adres"
182
+
183
+ #: includes/class-wcpdf-settings.php:295
184
+ msgid "Footer: terms & conditions, policies, etc."
185
+ msgstr "Voettekst: algemene voorwaarden, retourenbeleid, etc."
186
+
187
+ #: includes/class-wcpdf-settings.php:311
188
+ msgid "Extra template fields"
189
+ msgstr "Extra sjabloonvelden"
190
+
191
+ #: includes/class-wcpdf-settings.php:318
192
+ msgid "Extra field 1"
193
+ msgstr "Extra veld 1"
194
+
195
+ #: includes/class-wcpdf-settings.php:327
196
+ msgid "This is footer column 1 in the <i>Modern (Premium)</i> template"
197
+ msgstr "Dit is voettekst kolom 1 in het <i>Modern (Premium)</i> sjabloon"
198
+
199
+ #: includes/class-wcpdf-settings.php:333
200
+ msgid "Extra field 2"
201
+ msgstr "Extra veld 2"
202
+
203
+ #: includes/class-wcpdf-settings.php:342
204
+ msgid "This is footer column 2 in the <i>Modern (Premium)</i> template"
205
+ msgstr "Dit is voettekst kolom 2 in het <i>Modern (Premium)</i> sjabloon"
206
+
207
+ #: includes/class-wcpdf-settings.php:348
208
+ msgid "Extra field 3"
209
+ msgstr "Extra veld 3"
210
+
211
+ #: includes/class-wcpdf-settings.php:357
212
+ msgid "This is footer column 3 in the <i>Modern (Premium)</i> template"
213
+ msgstr "Dit is voettekst kolom 3 in het <i>Modern (Premium)</i> sjabloon"
214
+
215
+ #: includes/class-wcpdf-settings.php:570
216
+ msgid "Image resolution"
217
+ msgstr "Afbeeldings resolutie"
218
+
219
+ #: includes/class-wcpdf-settings.php:600
220
+ msgid ""
221
+ "These are used for the (optional) footer columns in the <em>Modern "
222
+ "(Premium)</em> template, but can also be used for other elements in your "
223
+ "custom template"
224
+ msgstr ""
225
+ "Deze velden worden gebruikt voor de (optionele) voettekst kolommen in het "
226
+ "<em>Modern (Premium)</em> sjabloon, maar kunnen ook gebruikt worden voor "
227
+ "andere elementen in je eigen custom sjabloon."
228
+
229
+ #: includes/class-wcpdf-writepanels.php:80
230
+ msgid "Create PDF"
231
+ msgstr "Maak PDF"
232
+
233
+ #: includes/class-wcpdf-writepanels.php:90
234
+ msgid "PDF invoice"
235
+ msgstr "PDF factuur"
236
+
237
+ #: includes/class-wcpdf-writepanels.php:91
238
+ msgid "PDF Packing Slip"
239
+ msgstr "PDF Pakbon"
240
+
241
+ #: includes/class-wcpdf-writepanels.php:107
242
+ #: includes/class-wcpdf-writepanels.php:108
243
+ msgid "PDF Packing Slips"
244
+ msgstr "PDF Pakbonnen"
245
+
246
+ #: templates/pdf/Simple/invoice.php:9 templates/pdf/Simple/invoice.php:21
247
+ msgid "Invoice"
248
+ msgstr "Factuur"
249
+
250
+ #: templates/pdf/Simple/invoice.php:30
251
+ #: templates/pdf/Simple/packing-slip.php:30
252
+ msgid "Order Date:"
253
+ msgstr "Orderdatum:"
254
+
255
+ #: templates/pdf/Simple/invoice.php:32
256
+ #: templates/pdf/Simple/packing-slip.php:32
257
+ msgid "Order Number:"
258
+ msgstr "Ordernummer:"
259
+
260
+ #: templates/pdf/Simple/invoice.php:34
261
+ msgid "Payment Method:"
262
+ msgstr "Betaalmethode:"
263
+
264
+ #: templates/pdf/Simple/invoice.php:47
265
+ #: templates/pdf/Simple/packing-slip.php:45
266
+ msgid "Product"
267
+ msgstr "Product"
268
+
269
+ #: templates/pdf/Simple/invoice.php:48
270
+ #: templates/pdf/Simple/packing-slip.php:46
271
+ msgid "Quantity"
272
+ msgstr "Hoeveelheid"
273
+
274
+ #: templates/pdf/Simple/invoice.php:49
275
+ msgid "Price"
276
+ msgstr "Prijs"
277
+
278
+ #: templates/pdf/Simple/invoice.php:57
279
+ #: templates/pdf/Simple/packing-slip.php:54
280
+ msgid "SKU:"
281
+ msgstr "SKU:"
282
+
283
+ #: templates/pdf/Simple/invoice.php:58
284
+ #: templates/pdf/Simple/packing-slip.php:55
285
+ msgid "Weight:"
286
+ msgstr "Gewicht:"
287
+
288
+ #: templates/pdf/Simple/invoice.php:91
289
+ #: templates/pdf/Simple/packing-slip.php:69
290
+ msgid "Customer Notes"
291
+ msgstr "Opmerking klant:"
292
+
293
+ #: templates/pdf/Simple/packing-slip.php:9
294
+ #: templates/pdf/Simple/packing-slip.php:21
295
+ msgid "Packing Slip"
296
+ msgstr "Pakbon"
297
+
298
+ #~ msgid "SKU"
299
+ #~ msgstr "Artikelnummer"
300
+
301
+ #~ msgid "Description"
302
+ #~ msgstr "Omschrijving"
303
+
304
+ #~ msgid "VAT"
305
+ #~ msgstr "BTW"
306
+
307
+ #~ msgid "Order number"
308
+ #~ msgstr "Factuurnummer"
309
+
310
+ #~ msgid "Date"
311
+ #~ msgstr "Datum"
312
+
313
+ #~ msgid "Shipping method"
314
+ #~ msgstr "Verzendmethode"
315
+
316
+ #~ msgid "Payment method"
317
+ #~ msgstr "Betaalmethode"
318
+
319
+ #~ msgid "Email"
320
+ #~ msgstr "Email"
321
+
322
+ #~ msgid "Phone"
323
+ #~ msgstr "Telefoonnummer"
324
+
325
+ #~ msgid "Recipient"
326
+ #~ msgstr "Ontvanger"
327
+
328
+ #~ msgid "Totals"
329
+ #~ msgstr "Totalen"
330
+
331
+ #~ msgid "Terms & conditions"
332
+ #~ msgstr "Algemene voorwaarden"
333
+
334
+ #~ msgid "Footer imprint"
335
+ #~ msgstr "Voetnoot"
336
+
337
+ #~ msgid "View your shopping cart"
338
+ #~ msgstr "Bekijk uw winkelwagen"
339
+
340
+ #~ msgid "%d item"
341
+ #~ msgid_plural "%d items"
342
+ #~ msgstr[0] "%d artikel"
343
+ #~ msgstr[1] "%d artikelen"
344
+
345
+ #~ msgid "your cart is currently empty"
346
+ #~ msgstr "Uw winkelwagen is leeg"
347
+
348
+ #~ msgid ""
349
+ #~ "WooCommerce Menu Cart depends on <a href=\"%s\">WooCommerce</a> to work!"
350
+ #~ msgstr ""
351
+ #~ "WooCommerce Menu Cart heeft <a href=\"%s\">WooCommerce</a> om te kunnen "
352
+ #~ "functioneren!"
353
+
354
+ #~ msgid "Display Flyout."
355
+ #~ msgstr "Geef flyout weer."
356
+
357
+ #~ msgid "Set the name of the menu you want to display"
358
+ #~ msgstr ""
359
+ #~ "Voer de naam in van het menu waarin je het winkelwagentje wilt weergeven"
360
+
361
+ #~ msgid ""
362
+ #~ "Let's keep this simple! Just check the boxes next to the features you "
363
+ #~ "want."
364
+ #~ msgstr ""
365
+ #~ "Super eenvoudig: geef simpelweg de vakjes naast de features zoals jij ze "
366
+ #~ "wilt gebruiken een vinkje."
367
+
368
+ #~ msgid "Save Changes"
369
+ #~ msgstr "Opslaan"
lib/dompdf/.gitattributes ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.json text
2
+ *.xml text
3
+ *.php text
4
+ *.LGPL text
5
+ *.md text
6
+ *.skel text
7
+ *.css text
8
+ *.inc text
9
+ *.js text
10
+ *.html text
11
+ *.txt text
12
+ *.svg text
13
+
lib/dompdf/.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ .DS_Store
2
+ composer.lock
3
+ vendor
4
+ .idea
5
+ .project
6
+ lib/fonts/*.afm.php
7
+ lib/fonts/*.ufm.php
8
+ lib/fonts/log.htm
lib/dompdf/.gitmodules ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ [submodule "lib/php-font-lib"]
2
+ path = lib/php-font-lib
3
+ url = https://github.com/PhenX/php-font-lib.git
lib/dompdf/CONTRIBUTING.md ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # How to contribute
2
+
3
+ - [Getting help](#getting-help)
4
+ - [Submitting bug reports](#submitting-bug-reports)
5
+ - [Contributing code](#contributing-code)
6
+
7
+ ## Getting help
8
+
9
+ Community discussion, questions, and informal bug reporting is done on the
10
+ [dompdf Google group](http://groups.google.com/group/dompdf).
11
+
12
+ ## Submitting bug reports
13
+
14
+ The preferred way to report bugs is to use the
15
+ [GitHub issue tracker](http://github.com/dompdf/dompdf/issues). Before
16
+ reporting a bug, read these pointers.
17
+
18
+ **Please search inside the bug tracker to see if the bug you found is not already reported.**
19
+
20
+ **Note:** The issue tracker is for *bugs* and *feature requests*, not requests for help.
21
+ Questions should be asked on the
22
+ [dompdf Google group](http://groups.google.com/group/dompdf) instead.
23
+
24
+ ### Reporting bugs effectively
25
+
26
+ - dompdf is maintained by volunteers. They don't owe you anything, so be
27
+ polite. Reports with an indignant or belligerent tone tend to be moved to the
28
+ bottom of the pile.
29
+
30
+ - Include information about **the PHP version on which the problem occurred**. Even
31
+ if you tested several PHP version on different servers, and the problem occurred
32
+ in all of them, mention this fact in the bug report.
33
+ Also include the operating system it's installed on. PHP configuration can also help,
34
+ and server error logs (like Apache logs)
35
+
36
+ - Mention which release of dompdf you're using (the zip, the master branch, etc).
37
+ Preferably, try also with the current development snapshot, to ensure the
38
+ problem has not already been fixed.
39
+
40
+ - Mention very precisely what went wrong. "X is broken" is not a good bug
41
+ report. What did you expect to happen? What happened instead? Describe the
42
+ exact steps a maintainer has to take to make the problem occur. We can not
43
+ fix something that we can not observe.
44
+
45
+ - If the problem can not be reproduced in any of the demos included in the
46
+ dompdf distribution, please provide an HTML document that demonstrates
47
+ the problem. There are a few options to show us your code:
48
+ - [JS Fiddle](http://jsfiddle.net/)
49
+ - [dompdf debug helper](http://eclecticgeek.com/dompdf/debug.php) (provided by @bsweeney)
50
+ - Include the HTML/CSS inside the bug report, with
51
+ [code highlighting](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#wiki-code).
52
+
53
+ ## Contributing code
54
+
55
+ - Make sure you have a [GitHub Account](https://github.com/signup/free)
56
+ - Fork [dompdf](https://github.com/dompdf/dompdf/)
57
+ ([how to fork a repo](https://help.github.com/articles/fork-a-repo))
58
+ - Make your changes
59
+ - Add a simple test file in `www/test/`, with a comprehensive name.
60
+ - Submit a pull request
61
+ ([how to create a pull request](https://help.github.com/articles/fork-a-repo))
62
+
63
+ ### Coding standards
64
+
65
+ - 2 spaces per indentation level, no tabs.
66
+ - spaces inside `if` like this:
67
+ ```php
68
+ if ( $foo == "bar" ) {
69
+ //
70
+ }
71
+ ```
72
+ - booleans in lowercase
73
+ - opening braces *always* on the same line
lib/dompdf/LICENSE.LGPL ADDED
@@ -0,0 +1,456 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 2.1, February 1999
3
+
4
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
5
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ [This is the first released version of the Lesser GPL. It also counts
10
+ as the successor of the GNU Library Public License, version 2, hence
11
+ the version number 2.1.]
12
+
13
+ Preamble
14
+
15
+ The licenses for most software are designed to take away your
16
+ freedom to share and change it. By contrast, the GNU General Public
17
+ Licenses are intended to guarantee your freedom to share and change
18
+ free software--to make sure the software is free for all its users.
19
+
20
+ This license, the Lesser General Public License, applies to some
21
+ specially designated software packages--typically libraries--of the
22
+ Free Software Foundation and other authors who decide to use it. You
23
+ can use it too, but we suggest you first think carefully about whether
24
+ this license or the ordinary General Public License is the better
25
+ strategy to use in any particular case, based on the explanations below.
26
+
27
+ When we speak of free software, we are referring to freedom of use,
28
+ not price. Our General Public Licenses are designed to make sure that
29
+ you have the freedom to distribute copies of free software (and charge
30
+ for this service if you wish); that you receive source code or can get
31
+ it if you want it; that you can change the software and use pieces of
32
+ it in new free programs; and that you are informed that you can do
33
+ these things.
34
+
35
+ To protect your rights, we need to make restrictions that forbid
36
+ distributors to deny you these rights or to ask you to surrender these
37
+ rights. These restrictions translate to certain responsibilities for
38
+ you if you distribute copies of the library or if you modify it.
39
+
40
+ For example, if you distribute copies of the library, whether gratis
41
+ or for a fee, you must give the recipients all the rights that we gave
42
+ you. You must make sure that they, too, receive or can get the source
43
+ code. If you link other code with the library, you must provide
44
+ complete object files to the recipients, so that they can relink them
45
+ with the library after making changes to the library and recompiling
46
+ it. And you must show them these terms so they know their rights.
47
+
48
+ We protect your rights with a two-step method: (1) we copyright the
49
+ library, and (2) we offer you this license, which gives you legal
50
+ permission to copy, distribute and/or modify the library.
51
+
52
+ To protect each distributor, we want to make it very clear that
53
+ there is no warranty for the free library. Also, if the library is
54
+ modified by someone else and passed on, the recipients should know
55
+ that what they have is not the original version, so that the original
56
+ author's reputation will not be affected by problems that might be
57
+ introduced by others.
58
+
59
+ Finally, software patents pose a constant threat to the existence of
60
+ any free program. We wish to make sure that a company cannot
61
+ effectively restrict the users of a free program by obtaining a
62
+ restrictive license from a patent holder. Therefore, we insist that
63
+ any patent license obtained for a version of the library must be
64
+ consistent with the full freedom of use specified in this license.
65
+
66
+ Most GNU software, including some libraries, is covered by the
67
+ ordinary GNU General Public License. This license, the GNU Lesser
68
+ General Public License, applies to certain designated libraries, and
69
+ is quite different from the ordinary General Public License. We use
70
+ this license for certain libraries in order to permit linking those
71
+ libraries into non-free programs.
72
+
73
+ When a program is linked with a library, whether statically or using
74
+ a shared library, the combination of the two is legally speaking a
75
+ combined work, a derivative of the original library. The ordinary
76
+ General Public License therefore permits such linking only if the
77
+ entire combination fits its criteria of freedom. The Lesser General
78
+ Public License permits more lax criteria for linking other code with
79
+ the library.
80
+
81
+ We call this license the "Lesser" General Public License because it
82
+ does Less to protect the user's freedom than the ordinary General
83
+ Public License. It also provides other free software developers Less
84
+ of an advantage over competing non-free programs. These disadvantages
85
+ are the reason we use the ordinary General Public License for many
86
+ libraries. However, the Lesser license provides advantages in certain
87
+ special circumstances.
88
+
89
+ For example, on rare occasions, there may be a special need to
90
+ encourage the widest possible use of a certain library, so that it becomes
91
+ a de-facto standard. To achieve this, non-free programs must be
92
+ allowed to use the library. A more frequent case is that a free
93
+ library does the same job as widely used non-free libraries. In this
94
+ case, there is little to gain by limiting the free library to free
95
+ software only, so we use the Lesser General Public License.
96
+
97
+ In other cases, permission to use a particular library in non-free
98
+ programs enables a greater number of people to use a large body of
99
+ free software. For example, permission to use the GNU C Library in
100
+ non-free programs enables many more people to use the whole GNU
101
+ operating system, as well as its variant, the GNU/Linux operating
102
+ system.
103
+
104
+ Although the Lesser General Public License is Less protective of the
105
+ users' freedom, it does ensure that the user of a program that is
106
+ linked with the Library has the freedom and the wherewithal to run
107
+ that program using a modified version of the Library.
108
+
109
+ The precise terms and conditions for copying, distribution and
110
+ modification follow. Pay close attention to the difference between a
111
+ "work based on the library" and a "work that uses the library". The
112
+ former contains code derived from the library, whereas the latter must
113
+ be combined with the library in order to run.
114
+
115
+ GNU LESSER GENERAL PUBLIC LICENSE
116
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
117
+
118
+ 0. This License Agreement applies to any software library or other
119
+ program which contains a notice placed by the copyright holder or
120
+ other authorized party saying it may be distributed under the terms of
121
+ this Lesser General Public License (also called "this License").
122
+ Each licensee is addressed as "you".
123
+
124
+ A "library" means a collection of software functions and/or data
125
+ prepared so as to be conveniently linked with application programs
126
+ (which use some of those functions and data) to form executables.
127
+
128
+ The "Library", below, refers to any such software library or work
129
+ which has been distributed under these terms. A "work based on the
130
+ Library" means either the Library or any derivative work under
131
+ copyright law: that is to say, a work containing the Library or a
132
+ portion of it, either verbatim or with modifications and/or translated
133
+ straightforwardly into another language. (Hereinafter, translation is
134
+ included without limitation in the term "modification".)
135
+
136
+ "Source code" for a work means the preferred form of the work for
137
+ making modifications to it. For a library, complete source code means
138
+ all the source code for all modules it contains, plus any associated
139
+ interface definition files, plus the scripts used to control compilation
140
+ and installation of the library.
141
+
142
+ Activities other than copying, distribution and modification are not
143
+ covered by this License; they are outside its scope. The act of
144
+ running a program using the Library is not restricted, and output from
145
+ such a program is covered only if its contents constitute a work based
146
+ on the Library (independent of the use of the Library in a tool for
147
+ writing it). Whether that is true depends on what the Library does
148
+ and what the program that uses the Library does.
149
+
150
+ 1. You may copy and distribute verbatim copies of the Library's
151
+ complete source code as you receive it, in any medium, provided that
152
+ you conspicuously and appropriately publish on each copy an
153
+ appropriate copyright notice and disclaimer of warranty; keep intact
154
+ all the notices that refer to this License and to the absence of any
155
+ warranty; and distribute a copy of this License along with the
156
+ Library.
157
+
158
+ You may charge a fee for the physical act of transferring a copy,
159
+ and you may at your option offer warranty protection in exchange for a
160
+ fee.
161
+
162
+ 2. You may modify your copy or copies of the Library or any portion
163
+ of it, thus forming a work based on the Library, and copy and
164
+ distribute such modifications or work under the terms of Section 1
165
+ above, provided that you also meet all of these conditions:
166
+
167
+ a) The modified work must itself be a software library.
168
+
169
+ b) You must cause the files modified to carry prominent notices
170
+ stating that you changed the files and the date of any change.
171
+
172
+ c) You must cause the whole of the work to be licensed at no
173
+ charge to all third parties under the terms of this License.
174
+
175
+ d) If a facility in the modified Library refers to a function or a
176
+ table of data to be supplied by an application program that uses
177
+ the facility, other than as an argument passed when the facility
178
+ is invoked, then you must make a good faith effort to ensure that,
179
+ in the event an application does not supply such function or
180
+ table, the facility still operates, and performs whatever part of
181
+ its purpose remains meaningful.
182
+
183
+ (For example, a function in a library to compute square roots has
184
+ a purpose that is entirely well-defined independent of the
185
+ application. Therefore, Subsection 2d requires that any
186
+ application-supplied function or table used by this function must
187
+ be optional: if the application does not supply it, the square
188
+ root function must still compute square roots.)
189
+
190
+ These requirements apply to the modified work as a whole. If
191
+ identifiable sections of that work are not derived from the Library,
192
+ and can be reasonably considered independent and separate works in
193
+ themselves, then this License, and its terms, do not apply to those
194
+ sections when you distribute them as separate works. But when you
195
+ distribute the same sections as part of a whole which is a work based
196
+ on the Library, the distribution of the whole must be on the terms of
197
+ this License, whose permissions for other licensees extend to the
198
+ entire whole, and thus to each and every part regardless of who wrote
199
+ it.
200
+
201
+ Thus, it is not the intent of this section to claim rights or contest
202
+ your rights to work written entirely by you; rather, the intent is to
203
+ exercise the right to control the distribution of derivative or
204
+ collective works based on the Library.
205
+
206
+ In addition, mere aggregation of another work not based on the Library
207
+ with the Library (or with a work based on the Library) on a volume of
208
+ a storage or distribution medium does not bring the other work under
209
+ the scope of this License.
210
+
211
+ 3. You may opt to apply the terms of the ordinary GNU General Public
212
+ License instead of this License to a given copy of the Library. To do
213
+ this, you must alter all the notices that refer to this License, so
214
+ that they refer to the ordinary GNU General Public License, version 2,
215
+ instead of to this License. (If a newer version than version 2 of the
216
+ ordinary GNU General Public License has appeared, then you can specify
217
+ that version instead if you wish.) Do not make any other change in
218
+ these notices.
219
+
220
+ Once this change is made in a given copy, it is irreversible for
221
+ that copy, so the ordinary GNU General Public License applies to all
222
+ subsequent copies and derivative works made from that copy.
223
+
224
+ This option is useful when you wish to copy part of the code of
225
+ the Library into a program that is not a library.
226
+
227
+ 4. You may copy and distribute the Library (or a portion or
228
+ derivative of it, under Section 2) in object code or executable form
229
+ under the terms of Sections 1 and 2 above provided that you accompany
230
+ it with the complete corresponding machine-readable source code, which
231
+ must be distributed under the terms of Sections 1 and 2 above on a
232
+ medium customarily used for software interchange.
233
+
234
+ If distribution of object code is made by offering access to copy
235
+ from a designated place, then offering equivalent access to copy the
236
+ source code from the same place satisfies the requirement to
237
+ distribute the source code, even though third parties are not
238
+ compelled to copy the source along with the object code.
239
+
240
+ 5. A program that contains no derivative of any portion of the
241
+ Library, but is designed to work with the Library by being compiled or
242
+ linked with it, is called a "work that uses the Library". Such a
243
+ work, in isolation, is not a derivative work of the Library, and
244
+ therefore falls outside the scope of this License.
245
+
246
+ However, linking a "work that uses the Library" with the Library
247
+ creates an executable that is a derivative of the Library (because it
248
+ contains portions of the Library), rather than a "work that uses the
249
+ library". The executable is therefore covered by this License.
250
+ Section 6 states terms for distribution of such executables.
251
+
252
+ When a "work that uses the Library" uses material from a header file
253
+ that is part of the Library, the object code for the work may be a
254
+ derivative work of the Library even though the source code is not.
255
+ Whether this is true is especially significant if the work can be
256
+ linked without the Library, or if the work is itself a library. The
257
+ threshold for this to be true is not precisely defined by law.
258
+
259
+ If such an object file uses only numerical parameters, data
260
+ structure layouts and accessors, and small macros and small inline
261
+ functions (ten lines or less in length), then the use of the object
262
+ file is unrestricted, regardless of whether it is legally a derivative
263
+ work. (Executables containing this object code plus portions of the
264
+ Library will still fall under Section 6.)
265
+
266
+ Otherwise, if the work is a derivative of the Library, you may
267
+ distribute the object code for the work under the terms of Section 6.
268
+ Any executables containing that work also fall under Section 6,
269
+ whether or not they are linked directly with the Library itself.
270
+
271
+ 6. As an exception to the Sections above, you may also combine or
272
+ link a "work that uses the Library" with the Library to produce a
273
+ work containing portions of the Library, and distribute that work
274
+ under terms of your choice, provided that the terms permit
275
+ modification of the work for the customer's own use and reverse
276
+ engineering for debugging such modifications.
277
+
278
+ You must give prominent notice with each copy of the work that the
279
+ Library is used in it and that the Library and its use are covered by
280
+ this License. You must supply a copy of this License. If the work
281
+ during execution displays copyright notices, you must include the
282
+ copyright notice for the Library among them, as well as a reference
283
+ directing the user to the copy of this License. Also, you must do one
284
+ of these things:
285
+
286
+ a) Accompany the work with the complete corresponding
287
+ machine-readable source code for the Library including whatever
288
+ changes were used in the work (which must be distributed under
289
+ Sections 1 and 2 above); and, if the work is an executable linked
290
+ with the Library, with the complete machine-readable "work that
291
+ uses the Library", as object code and/or source code, so that the
292
+ user can modify the Library and then relink to produce a modified
293
+ executable containing the modified Library. (It is understood
294
+ that the user who changes the contents of definitions files in the
295
+ Library will not necessarily be able to recompile the application
296
+ to use the modified definitions.)
297
+
298
+ b) Use a suitable shared library mechanism for linking with the
299
+ Library. A suitable mechanism is one that (1) uses at run time a
300
+ copy of the library already present on the user's computer system,
301
+ rather than copying library functions into the executable, and (2)
302
+ will operate properly with a modified version of the library, if
303
+ the user installs one, as long as the modified version is
304
+ interface-compatible with the version that the work was made with.
305
+
306
+ c) Accompany the work with a written offer, valid for at
307
+ least three years, to give the same user the materials
308
+ specified in Subsection 6a, above, for a charge no more
309
+ than the cost of performing this distribution.
310
+
311
+ d) If distribution of the work is made by offering access to copy
312
+ from a designated place, offer equivalent access to copy the above
313
+ specified materials from the same place.
314
+
315
+ e) Verify that the user has already received a copy of these
316
+ materials or that you have already sent this user a copy.
317
+
318
+ For an executable, the required form of the "work that uses the
319
+ Library" must include any data and utility programs needed for
320
+ reproducing the executable from it. However, as a special exception,
321
+ the materials to be distributed need not include anything that is
322
+ normally distributed (in either source or binary form) with the major
323
+ components (compiler, kernel, and so on) of the operating system on
324
+ which the executable runs, unless that component itself accompanies
325
+ the executable.
326
+
327
+ It may happen that this requirement contradicts the license
328
+ restrictions of other proprietary libraries that do not normally
329
+ accompany the operating system. Such a contradiction means you cannot
330
+ use both them and the Library together in an executable that you
331
+ distribute.
332
+
333
+ 7. You may place library facilities that are a work based on the
334
+ Library side-by-side in a single library together with other library
335
+ facilities not covered by this License, and distribute such a combined
336
+ library, provided that the separate distribution of the work based on
337
+ the Library and of the other library facilities is otherwise
338
+ permitted, and provided that you do these two things:
339
+
340
+ a) Accompany the combined library with a copy of the same work
341
+ based on the Library, uncombined with any other library
342
+ facilities. This must be distributed under the terms of the
343
+ Sections above.
344
+
345
+ b) Give prominent notice with the combined library of the fact
346
+ that part of it is a work based on the Library, and explaining
347
+ where to find the accompanying uncombined form of the same work.
348
+
349
+ 8. You may not copy, modify, sublicense, link with, or distribute
350
+ the Library except as expressly provided under this License. Any
351
+ attempt otherwise to copy, modify, sublicense, link with, or
352
+ distribute the Library is void, and will automatically terminate your
353
+ rights under this License. However, parties who have received copies,
354
+ or rights, from you under this License will not have their licenses
355
+ terminated so long as such parties remain in full compliance.
356
+
357
+ 9. You are not required to accept this License, since you have not
358
+ signed it. However, nothing else grants you permission to modify or
359
+ distribute the Library or its derivative works. These actions are
360
+ prohibited by law if you do not accept this License. Therefore, by
361
+ modifying or distributing the Library (or any work based on the
362
+ Library), you indicate your acceptance of this License to do so, and
363
+ all its terms and conditions for copying, distributing or modifying
364
+ the Library or works based on it.
365
+
366
+ 10. Each time you redistribute the Library (or any work based on the
367
+ Library), the recipient automatically receives a license from the
368
+ original licensor to copy, distribute, link with or modify the Library
369
+ subject to these terms and conditions. You may not impose any further
370
+ restrictions on the recipients' exercise of the rights granted herein.
371
+ You are not responsible for enforcing compliance by third parties with
372
+ this License.
373
+
374
+ 11. If, as a consequence of a court judgment or allegation of patent
375
+ infringement or for any other reason (not limited to patent issues),
376
+ conditions are imposed on you (whether by court order, agreement or
377
+ otherwise) that contradict the conditions of this License, they do not
378
+ excuse you from the conditions of this License. If you cannot
379
+ distribute so as to satisfy simultaneously your obligations under this
380
+ License and any other pertinent obligations, then as a consequence you
381
+ may not distribute the Library at all. For example, if a patent
382
+ license would not permit royalty-free redistribution of the Library by
383
+ all those who receive copies directly or indirectly through you, then
384
+ the only way you could satisfy both it and this License would be to
385
+ refrain entirely from distribution of the Library.
386
+
387
+ If any portion of this section is held invalid or unenforceable under any
388
+ particular circumstance, the balance of the section is intended to apply,
389
+ and the section as a whole is intended to apply in other circumstances.
390
+
391
+ It is not the purpose of this section to induce you to infringe any
392
+ patents or other property right claims or to contest validity of any
393
+ such claims; this section has the sole purpose of protecting the
394
+ integrity of the free software distribution system which is
395
+ implemented by public license practices. Many people have made
396
+ generous contributions to the wide range of software distributed
397
+ through that system in reliance on consistent application of that
398
+ system; it is up to the author/donor to decide if he or she is willing
399
+ to distribute software through any other system and a licensee cannot
400
+ impose that choice.
401
+
402
+ This section is intended to make thoroughly clear what is believed to
403
+ be a consequence of the rest of this License.
404
+
405
+ 12. If the distribution and/or use of the Library is restricted in
406
+ certain countries either by patents or by copyrighted interfaces, the
407
+ original copyright holder who places the Library under this License may add
408
+ an explicit geographical distribution limitation excluding those countries,
409
+ so that distribution is permitted only in or among countries not thus
410
+ excluded. In such case, this License incorporates the limitation as if
411
+ written in the body of this License.
412
+
413
+ 13. The Free Software Foundation may publish revised and/or new
414
+ versions of the Lesser General Public License from time to time.
415
+ Such new versions will be similar in spirit to the present version,
416
+ but may differ in detail to address new problems or concerns.
417
+
418
+ Each version is given a distinguishing version number. If the Library
419
+ specifies a version number of this License which applies to it and
420
+ "any later version", you have the option of following the terms and
421
+ conditions either of that version or of any later version published by
422
+ the Free Software Foundation. If the Library does not specify a
423
+ license version number, you may choose any version ever published by
424
+ the Free Software Foundation.
425
+
426
+ 14. If you wish to incorporate parts of the Library into other free
427
+ programs whose distribution conditions are incompatible with these,
428
+ write to the author to ask for permission. For software which is
429
+ copyrighted by the Free Software Foundation, write to the Free
430
+ Software Foundation; we sometimes make exceptions for this. Our
431
+ decision will be guided by the two goals of preserving the free status
432
+ of all derivatives of our free software and of promoting the sharing
433
+ and reuse of software generally.
434
+
435
+ NO WARRANTY
436
+
437
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
438
+ WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
439
+ EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
440
+ OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
441
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
442
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
443
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
444
+ LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
445
+ THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
446
+
447
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
448
+ WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
449
+ AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
450
+ FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
451
+ CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
452
+ LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
453
+ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
454
+ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
455
+ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
456
+ DAMAGES.
lib/dompdf/README.md ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ **dompdf is an HTML to PDF converter**. At its heart, dompdf is (mostly)
2
+ [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant HTML
3
+ layout and rendering engine written in PHP. It is a style-driven renderer: it will
4
+ download and read external stylesheets, inline style tags, and the style attributes
5
+ of individual HTML elements. It also supports most presentational HTML attributes.
6
+
7
+ ----
8
+
9
+ **Check out the [Demo](http://pxd.me/dompdf/www/examples.php) and ask any question on
10
+ [StackOverflow](http://stackoverflow.com/questions/tagged/dompdf) or on the
11
+ [Google Groups](http://groups.google.com/group/dompdf)**
12
+
13
+ ----
14
+
15
+ [![Follow us on Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf)
16
+ [![Follow us on Google+](https://ssl.gstatic.com/images/icons/gplus-32.png)](https://plus.google.com/108710008521858993320?prsrc=3)
17
+
18
+ Features
19
+ ========
20
+ * handles most CSS 2.1 and a few CSS3 properties, including @import, @media & @page rules
21
+ * supports most presentational HTML 4.0 attributes
22
+ * supports external stylesheets, either local or through http/ftp (via fopen-wrappers)
23
+ * supports complex tables, including row & column spans, separate & collapsed border models, individual cell styling
24
+ * image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
25
+ * no dependencies on external PDF libraries, thanks to the R&OS PDF class
26
+ * inline PHP support
27
+
28
+ Requirements
29
+ ============
30
+ * PHP 5.0+ (5.3 recommended)
31
+ * MBString extension
32
+ * DOM extension (bundled with PHP 5)
33
+ * Some fonts. PDFs internally support Helvetica, Times-Roman, Courier & Zapf-Dingbats, but if you wish to use other fonts you will need to install some fonts. dompdf supports the same fonts as the underlying R&OS PDF class: Type 1 (.pfb with the corresponding .afm) and TrueType (.ttf). The [DejaVu TrueType fonts](http://dejavu-fonts.org) are already installed for you and provide decent Unicode support. See the font installation instructions for more information on how to use fonts.
34
+
35
+ Easy Installation
36
+ ============
37
+ Install with git
38
+ ---
39
+ From the command line switch to the directory where dompdf will reside and run the following commands:
40
+ ```sh
41
+ git clone https://github.com/dompdf/dompdf.git
42
+ git submodule init
43
+ git submodule update
44
+ ```
45
+
46
+ Install with composer
47
+ ---
48
+ To install with Composer, simply add the requirement to your `composer.json` file:
49
+
50
+ ```json
51
+ {
52
+ "require" : {
53
+ "dompdf/dompdf" : "dev-master"
54
+ }
55
+ }
56
+ ```
57
+
58
+ And run Composer to update your dependencies:
59
+
60
+ ```bash
61
+ $ curl -sS http://getcomposer.org/installer | php
62
+ $ php composer.phar update
63
+ ```
64
+
65
+ Before you can use the Composer installation of DOMPDF in your application you must disable the default auto-loader and include the configuration file:
66
+
67
+ ```php
68
+ // somewhere early in your project's loading, require the Composer autoloader
69
+ // see: http://getcomposer.org/doc/00-intro.md
70
+ require 'vendor/autoload.php';
71
+
72
+ // disable DOMPDF's internal autoloader if you are using Composer
73
+ define('DOMPDF_ENABLE_AUTOLOAD', false);
74
+
75
+ // include DOMPDF's default configuration
76
+ require_once '/path/to/vendor/dompdf/dompdf/dompdf_config.inc.php';
77
+ ```
78
+
79
+ Download and install
80
+ ---
81
+ Download an archive of dompdf and extract it into the directory where dompdf will reside
82
+ * You can download stable copies of dompdf from https://github.com/dompdf/dompdf/tags
83
+ * Or download a nightly (the latest, unreleased code) from http://eclecticgeek.com/dompdf
84
+
85
+ Limitations (Known Issues)
86
+ ==========================
87
+ * not particularly tolerant to poorly-formed HTML input (using Tidy first may help).
88
+ * large files or large tables can take a while to render
89
+ * CSS float is not supported (but is in the works).
90
+ * If you find this project useful, please consider making a donation.
91
+
92
+ (Any funds donated will be used to help further development on this project.)
93
+ [![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf)
lib/dompdf/composer.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "dompdf/dompdf",
3
+ "type": "library",
4
+ "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
5
+ "homepage": "https://github.com/dompdf/dompdf",
6
+ "license": "LGPL",
7
+ "authors": [
8
+ {
9
+ "name": "Fabien Ménager",
10
+ "email": "fabien.menager@gmail.com"
11
+ },
12
+ {
13
+ "name": "Brian Sweeney",
14
+ "email": "eclecticgeek@gmail.com"
15
+ }
16
+ ],
17
+ "autoload": {
18
+ "classmap": ["include/"]
19
+ },
20
+ "require": {
21
+ "phenx/php-font-lib": "0.2.*"
22
+ }
23
+ }
lib/dompdf/dompdf.php ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Command line utility to use dompdf.
4
+ * Can also be used with HTTP GET parameters
5
+ *
6
+ * @package dompdf
7
+ * @link http://dompdf.github.com/
8
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
9
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
10
+ */
11
+
12
+ /**
13
+ * Display command line usage
14
+ */
15
+ function dompdf_usage() {
16
+ $default_paper_size = DOMPDF_DEFAULT_PAPER_SIZE;
17
+
18
+ echo <<<EOD
19
+
20
+ Usage: {$_SERVER["argv"][0]} [options] html_file
21
+
22
+ html_file can be a filename, a url if fopen_wrappers are enabled, or the '-' character to read from standard input.
23
+
24
+ Options:
25
+ -h Show this message
26
+ -l List available paper sizes
27
+ -p size Paper size; something like 'letter', 'A4', 'legal', etc.
28
+ The default is '$default_paper_size'
29
+ -o orientation Either 'portrait' or 'landscape'. Default is 'portrait'
30
+ -b path Set the 'document root' of the html_file.
31
+ Relative urls (for stylesheets) are resolved using this directory.
32
+ Default is the directory of html_file.
33
+ -f file The output filename. Default is the input [html_file].pdf
34
+ -v Verbose: display html parsing warnings and file not found errors.
35
+ -d Very verbose: display oodles of debugging output: every frame
36
+ in the tree printed to stdout.
37
+ -t Comma separated list of debugging types (page-break,reflow,split)
38
+
39
+ EOD;
40
+ exit;
41
+ }
42
+
43
+ /**
44
+ * Parses command line options
45
+ *
46
+ * @return array The command line options
47
+ */
48
+ function getoptions() {
49
+
50
+ $opts = array();
51
+
52
+ if ( $_SERVER["argc"] == 1 )
53
+ return $opts;
54
+
55
+ $i = 1;
56
+ while ($i < $_SERVER["argc"]) {
57
+
58
+ switch ($_SERVER["argv"][$i]) {
59
+
60
+ case "--help":
61
+ case "-h":
62
+ $opts["h"] = true;
63
+ $i++;
64
+ break;
65
+
66
+ case "-l":
67
+ $opts["l"] = true;
68
+ $i++;
69
+ break;
70
+
71
+ case "-p":
72
+ if ( !isset($_SERVER["argv"][$i+1]) )
73
+ die("-p switch requires a size parameter\n");
74
+ $opts["p"] = $_SERVER["argv"][$i+1];
75
+ $i += 2;
76
+ break;
77
+
78
+ case "-o":
79
+ if ( !isset($_SERVER["argv"][$i+1]) )
80
+ die("-o switch requires an orientation parameter\n");
81
+ $opts["o"] = $_SERVER["argv"][$i+1];
82
+ $i += 2;
83
+ break;
84
+
85
+ case "-b":
86
+ if ( !isset($_SERVER["argv"][$i+1]) )
87
+ die("-b switch requires a path parameter\n");
88
+ $opts["b"] = $_SERVER["argv"][$i+1];
89
+ $i += 2;
90
+ break;
91
+
92
+ case "-f":
93
+ if ( !isset($_SERVER["argv"][$i+1]) )
94
+ die("-f switch requires a filename parameter\n");
95
+ $opts["f"] = $_SERVER["argv"][$i+1];
96
+ $i += 2;
97
+ break;
98
+
99
+ case "-v":
100
+ $opts["v"] = true;
101
+ $i++;
102
+ break;
103
+
104
+ case "-d":
105
+ $opts["d"] = true;
106
+ $i++;
107
+ break;
108
+
109
+ case "-t":
110
+ if ( !isset($_SERVER['argv'][$i + 1]) )
111
+ die("-t switch requires a comma separated list of types\n");
112
+ $opts["t"] = $_SERVER['argv'][$i+1];
113
+ $i += 2;
114
+ break;
115
+
116
+ default:
117
+ $opts["filename"] = $_SERVER["argv"][$i];
118
+ $i++;
119
+ break;
120
+ }
121
+
122
+ }
123
+ return $opts;
124
+ }
125
+
126
+ require_once("dompdf_config.inc.php");
127
+ global $_dompdf_show_warnings, $_dompdf_debug, $_DOMPDF_DEBUG_TYPES;
128
+
129
+ $sapi = php_sapi_name();
130
+ $options = array();
131
+
132
+ switch ( $sapi ) {
133
+
134
+ case "cli":
135
+
136
+ $opts = getoptions();
137
+
138
+ if ( isset($opts["h"]) || (!isset($opts["filename"]) && !isset($opts["l"])) ) {
139
+ dompdf_usage();
140
+ exit;
141
+ }
142
+
143
+ if ( isset($opts["l"]) ) {
144
+ echo "\nUnderstood paper sizes:\n";
145
+
146
+ foreach (array_keys(CPDF_Adapter::$PAPER_SIZES) as $size)
147
+ echo " " . mb_strtoupper($size) . "\n";
148
+ exit;
149
+ }
150
+ $file = $opts["filename"];
151
+
152
+ if ( isset($opts["p"]) )
153
+ $paper = $opts["p"];
154
+ else
155
+ $paper = DOMPDF_DEFAULT_PAPER_SIZE;
156
+
157
+ if ( isset($opts["o"]) )
158
+ $orientation = $opts["o"];
159
+ else
160
+ $orientation = "portrait";
161
+
162
+ if ( isset($opts["b"]) )
163
+ $base_path = $opts["b"];
164
+
165
+ if ( isset($opts["f"]) )
166
+ $outfile = $opts["f"];
167
+ else {
168
+ if ( $file === "-" )
169
+ $outfile = "dompdf_out.pdf";
170
+ else
171
+ $outfile = str_ireplace(array(".html", ".htm", ".php"), "", $file) . ".pdf";
172
+ }
173
+
174
+ if ( isset($opts["v"]) )
175
+ $_dompdf_show_warnings = true;
176
+
177
+ if ( isset($opts["d"]) ) {
178
+ $_dompdf_show_warnings = true;
179
+ $_dompdf_debug = true;
180
+ }
181
+
182
+ if ( isset($opts['t']) ) {
183
+ $arr = split(',',$opts['t']);
184
+ $types = array();
185
+ foreach ($arr as $type)
186
+ $types[ trim($type) ] = 1;
187
+ $_DOMPDF_DEBUG_TYPES = $types;
188
+ }
189
+
190
+ $save_file = true;
191
+
192
+ break;
193
+
194
+ default:
195
+
196
+ if ( isset($_GET["input_file"]) )
197
+ $file = rawurldecode($_GET["input_file"]);
198
+ else
199
+ throw new DOMPDF_Exception("An input file is required (i.e. input_file _GET variable).");
200
+
201
+ if ( isset($_GET["paper"]) )
202
+ $paper = rawurldecode($_GET["paper"]);
203
+ else
204
+ $paper = DOMPDF_DEFAULT_PAPER_SIZE;
205
+
206
+ if ( isset($_GET["orientation"]) )
207
+ $orientation = rawurldecode($_GET["orientation"]);
208
+ else
209
+ $orientation = "portrait";
210
+
211
+ if ( isset($_GET["base_path"]) ) {
212
+ $base_path = rawurldecode($_GET["base_path"]);
213
+ $file = $base_path . $file; # Set the input file
214
+ }
215
+
216
+ if ( isset($_GET["options"]) ) {
217
+ $options = $_GET["options"];
218
+ }
219
+
220
+ $file_parts = explode_url($file);
221
+
222
+ /* Check to see if the input file is local and, if so, that the base path falls within that specified by DOMDPF_CHROOT */
223
+ if(($file_parts['protocol'] == '' || $file_parts['protocol'] === 'file://')) {
224
+ $file = realpath($file);
225
+ if ( strpos($file, DOMPDF_CHROOT) !== 0 ) {
226
+ throw new DOMPDF_Exception("Permission denied on $file. The file could not be found under the directory specified by DOMPDF_CHROOT.");
227
+ }
228
+ }
229
+
230
+ $outfile = "dompdf_out.pdf"; # Don't allow them to set the output file
231
+ $save_file = false; # Don't save the file
232
+
233
+ break;
234
+ }
235
+
236
+ $dompdf = new DOMPDF();
237
+
238
+ if ( $file === "-" ) {
239
+ $str = "";
240
+ while ( !feof(STDIN) )
241
+ $str .= fread(STDIN, 4096);
242
+
243
+ $dompdf->load_html($str);
244
+
245
+ } else
246
+ $dompdf->load_html_file($file);
247
+
248
+ if ( isset($base_path) ) {
249
+ $dompdf->set_base_path($base_path);
250
+ }
251
+
252
+ $dompdf->set_paper($paper, $orientation);
253
+
254
+ $dompdf->render();
255
+
256
+ if ( $_dompdf_show_warnings ) {
257
+ global $_dompdf_warnings;
258
+ foreach ($_dompdf_warnings as $msg)
259
+ echo $msg . "\n";
260
+ echo $dompdf->get_canvas()->get_cpdf()->messages;
261
+ flush();
262
+ }
263
+
264
+ if ( $save_file ) {
265
+ // if ( !is_writable($outfile) )
266
+ // throw new DOMPDF_Exception("'$outfile' is not writable.");
267
+ if ( strtolower(DOMPDF_PDF_BACKEND) === "gd" )
268
+ $outfile = str_replace(".pdf", ".png", $outfile);
269
+
270
+ list($proto, $host, $path, $file) = explode_url($outfile);
271
+ if ( $proto != "" ) // i.e. not file://
272
+ $outfile = $file; // just save it locally, FIXME? could save it like wget: ./host/basepath/file
273
+
274
+ $outfile = realpath(dirname($outfile)) . DIRECTORY_SEPARATOR . basename($outfile);
275
+
276
+ if ( strpos($outfile, DOMPDF_CHROOT) !== 0 )
277
+ throw new DOMPDF_Exception("Permission denied.");
278
+
279
+ file_put_contents($outfile, $dompdf->output( array("compress" => 0) ));
280
+ exit(0);
281
+ }
282
+
283
+ if ( !headers_sent() ) {
284
+ $dompdf->stream($outfile, $options);
285
+ }
lib/dompdf/dompdf_config.custom.inc.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //define("DOMPDF_TEMP_DIR", "/tmp");
3
+ //define("DOMPDF_CHROOT", DOMPDF_DIR);
4
+ //define("DOMPDF_FONT_DIR", DOMPDF_DIR."/lib/fonts/");
5
+ //define("DOMPDF_FONT_CACHE", DOMPDF_DIR."/lib/fonts/");
6
+ //define("DOMPDF_UNICODE_ENABLED", true);
7
+ //define("DOMPDF_PDF_BACKEND", "PDFLib");
8
+ //define("DOMPDF_DEFAULT_MEDIA_TYPE", "print");
9
+ //define("DOMPDF_DEFAULT_PAPER_SIZE", "letter");
10
+ //define("DOMPDF_DEFAULT_FONT", "serif");
11
+ //define("DOMPDF_DPI", 72);
12
+ //define("DOMPDF_ENABLE_PHP", true);
13
+ //define("DOMPDF_ENABLE_REMOTE", true);
14
+ //define("DOMPDF_ENABLE_CSS_FLOAT", true);
15
+ //define("DOMPDF_ENABLE_JAVASCRIPT", false);
16
+ //define("DEBUGPNG", true);
17
+ //define("DEBUGKEEPTEMP", true);
18
+ //define("DEBUGCSS", true);
19
+ //define("DEBUG_LAYOUT", true);
20
+ //define("DEBUG_LAYOUT_LINES", false);
21
+ //define("DEBUG_LAYOUT_BLOCKS", false);
22
+ //define("DEBUG_LAYOUT_INLINE", false);
23
+ //define("DOMPDF_FONT_HEIGHT_RATIO", 1.0);
24
+ //define("DEBUG_LAYOUT_PADDINGBOX", false);
25
+ //define("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm");
26
+ //define("DOMPDF_ENABLE_HTML5PARSER", true);
27
+ //define("DOMPDF_ENABLE_FONTSUBSETTING", true);
28
+
29
+ // DOMPDF authentication
30
+ //define("DOMPDF_ADMIN_USERNAME", "user");
31
+ //define("DOMPDF_ADMIN_PASSWORD", "password");
lib/dompdf/dompdf_config.inc.php ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ //error_reporting(E_STRICT | E_ALL | E_DEPRECATED);
12
+ //ini_set("display_errors", 1);
13
+
14
+ PHP_VERSION >= 5.0 or die("DOMPDF requires PHP 5.0+");
15
+
16
+ /**
17
+ * The root of your DOMPDF installation
18
+ */
19
+ define("DOMPDF_DIR", str_replace(DIRECTORY_SEPARATOR, '/', realpath(dirname(__FILE__))));
20
+
21
+ /**
22
+ * The location of the DOMPDF include directory
23
+ */
24
+ define("DOMPDF_INC_DIR", DOMPDF_DIR . "/include");
25
+
26
+ /**
27
+ * The location of the DOMPDF lib directory
28
+ */
29
+ define("DOMPDF_LIB_DIR", DOMPDF_DIR . "/lib");
30
+
31
+ /**
32
+ * Some installations don't have $_SERVER['DOCUMENT_ROOT']
33
+ * http://fyneworks.blogspot.com/2007/08/php-documentroot-in-iis-windows-servers.html
34
+ */
35
+ if( !isset($_SERVER['DOCUMENT_ROOT']) ) {
36
+ $path = "";
37
+
38
+ if ( isset($_SERVER['SCRIPT_FILENAME']) )
39
+ $path = $_SERVER['SCRIPT_FILENAME'];
40
+ elseif ( isset($_SERVER['PATH_TRANSLATED']) )
41
+ $path = str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']);
42
+
43
+ $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($path, 0, 0-strlen($_SERVER['PHP_SELF'])));
44
+ }
45
+
46
+ /** Include the custom config file if it exists */
47
+ if ( file_exists(DOMPDF_DIR . "/dompdf_config.custom.inc.php") ){
48
+ require_once(DOMPDF_DIR . "/dompdf_config.custom.inc.php");
49
+ }
50
+
51
+ //FIXME: Some function definitions rely on the constants defined by DOMPDF. However, might this location prove problematic?
52
+ require_once(DOMPDF_INC_DIR . "/functions.inc.php");
53
+
54
+ /**
55
+ * Username and password used by the configuration utility in www/
56
+ */
57
+ def("DOMPDF_ADMIN_USERNAME", "user");
58
+ def("DOMPDF_ADMIN_PASSWORD", "wpovernight");
59
+
60
+ /**
61
+ * The location of the DOMPDF font directory
62
+ *
63
+ * The location of the directory where DOMPDF will store fonts and font metrics
64
+ * Note: This directory must exist and be writable by the webserver process.
65
+ * *Please note the trailing slash.*
66
+ *
67
+ * Notes regarding fonts:
68
+ * Additional .afm font metrics can be added by executing load_font.php from command line.
69
+ *
70
+ * Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
71
+ * be embedded in the pdf file or the PDF may not display correctly. This can significantly
72
+ * increase file size unless font subsetting is enabled. Before embedding a font please
73
+ * review your rights under the font license.
74
+ *
75
+ * Any font specification in the source HTML is translated to the closest font available
76
+ * in the font directory.
77
+ *
78
+ * The pdf standard "Base 14 fonts" are:
79
+ * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
80
+ * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
81
+ * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
82
+ * Symbol, ZapfDingbats.
83
+ */
84
+ def("DOMPDF_FONT_DIR", DOMPDF_DIR . "/lib/fonts/");
85
+
86
+ /**
87
+ * The location of the DOMPDF font cache directory
88
+ *
89
+ * This directory contains the cached font metrics for the fonts used by DOMPDF.
90
+ * This directory can be the same as DOMPDF_FONT_DIR
91
+ *
92
+ * Note: This directory must exist and be writable by the webserver process.
93
+ */
94
+ def("DOMPDF_FONT_CACHE", DOMPDF_FONT_DIR);
95
+
96
+ /**
97
+ * The location of a temporary directory.
98
+ *
99
+ * The directory specified must be writeable by the webserver process.
100
+ * The temporary directory is required to download remote images and when
101
+ * using the PFDLib back end.
102
+ */
103
+ def("DOMPDF_TEMP_DIR", sys_get_temp_dir());
104
+
105
+ /**
106
+ * ==== IMPORTANT ====
107
+ *
108
+ * dompdf's "chroot": Prevents dompdf from accessing system files or other
109
+ * files on the webserver. All local files opened by dompdf must be in a
110
+ * subdirectory of this directory. DO NOT set it to '/' since this could
111
+ * allow an attacker to use dompdf to read any files on the server. This
112
+ * should be an absolute path.
113
+ * This is only checked on command line call by dompdf.php, but not by
114
+ * direct class use like:
115
+ * $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
116
+ */
117
+ def("DOMPDF_CHROOT", realpath(DOMPDF_DIR));
118
+
119
+ /**
120
+ * Whether to use Unicode fonts or not.
121
+ *
122
+ * When set to true the PDF backend must be set to "CPDF" and fonts must be
123
+ * loaded via load_font.php.
124
+ *
125
+ * When enabled, dompdf can support all Unicode glyphs. Any glyphs used in a
126
+ * document must be present in your fonts, however.
127
+ */
128
+ def("DOMPDF_UNICODE_ENABLED", true);
129
+
130
+ /**
131
+ * Whether to make font subsetting or not.
132
+ */
133
+ def("DOMPDF_ENABLE_FONTSUBSETTING", false);
134
+
135
+ /**
136
+ * The PDF rendering backend to use
137
+ *
138
+ * Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
139
+ * 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
140
+ * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link
141
+ * Canvas_Factory} ultimately determines which rendering class to instantiate
142
+ * based on this setting.
143
+ *
144
+ * Both PDFLib & CPDF rendering backends provide sufficient rendering
145
+ * capabilities for dompdf, however additional features (e.g. object,
146
+ * image and font support, etc.) differ between backends. Please see
147
+ * {@link PDFLib_Adapter} for more information on the PDFLib backend
148
+ * and {@link CPDF_Adapter} and lib/class.pdf.php for more information
149
+ * on CPDF. Also see the documentation for each backend at the links
150
+ * below.
151
+ *
152
+ * The GD rendering backend is a little different than PDFLib and
153
+ * CPDF. Several features of CPDF and PDFLib are not supported or do
154
+ * not make any sense when creating image files. For example,
155
+ * multiple pages are not supported, nor are PDF 'objects'. Have a
156
+ * look at {@link GD_Adapter} for more information. GD support is new
157
+ * and experimental, so use it at your own risk.
158
+ *
159
+ * @link http://www.pdflib.com
160
+ * @link http://www.ros.co.nz/pdf
161
+ * @link http://www.php.net/image
162
+ */
163
+ def("DOMPDF_PDF_BACKEND", "CPDF");
164
+
165
+ /**
166
+ * PDFlib license key
167
+ *
168
+ * If you are using a licensed, commercial version of PDFlib, specify
169
+ * your license key here. If you are using PDFlib-Lite or are evaluating
170
+ * the commercial version of PDFlib, comment out this setting.
171
+ *
172
+ * @link http://www.pdflib.com
173
+ *
174
+ * If pdflib present in web server and auto or selected explicitely above,
175
+ * a real license code must exist!
176
+ */
177
+ //def("DOMPDF_PDFLIB_LICENSE", "your license key here");
178
+
179
+ /**
180
+ * html target media view which should be rendered into pdf.
181
+ * List of types and parsing rules for future extensions:
182
+ * http://www.w3.org/TR/REC-html40/types.html
183
+ * screen, tty, tv, projection, handheld, print, braille, aural, all
184
+ * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
185
+ * Note, even though the generated pdf file is intended for print output,
186
+ * the desired content might be different (e.g. screen or projection view of html file).
187
+ * Therefore allow specification of content here.
188
+ */
189
+ def("DOMPDF_DEFAULT_MEDIA_TYPE", "screen");
190
+
191
+ /**
192
+ * The default paper size.
193
+ *
194
+ * North America standard is "letter"; other countries generally "a4"
195
+ *
196
+ * @see CPDF_Adapter::PAPER_SIZES for valid sizes
197
+ */
198
+ def("DOMPDF_DEFAULT_PAPER_SIZE", "a4");
199
+
200
+ /**
201
+ * The default font family
202
+ *
203
+ * Used if no suitable fonts can be found. This must exist in the font folder.
204
+ * @var string
205
+ */
206
+ def("DOMPDF_DEFAULT_FONT", "sans-serif");
207
+
208
+ /**
209
+ * Image DPI setting
210
+ *
211
+ * This setting determines the default DPI setting for images and fonts. The
212
+ * DPI may be overridden for inline images by explictly setting the
213
+ * image's width & height style attributes (i.e. if the image's native
214
+ * width is 600 pixels and you specify the image's width as 72 points,
215
+ * the image will have a DPI of 600 in the rendered PDF. The DPI of
216
+ * background images can not be overridden and is controlled entirely
217
+ * via this parameter.
218
+ *
219
+ * For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
220
+ * If a size in html is given as px (or without unit as image size),
221
+ * this tells the corresponding size in pt.
222
+ * This adjusts the relative sizes to be similar to the rendering of the
223
+ * html page in a reference browser.
224
+ *
225
+ * In pdf, always 1 pt = 1/72 inch
226
+ *
227
+ * Rendering resolution of various browsers in px per inch:
228
+ * Windows Firefox and Internet Explorer:
229
+ * SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
230
+ * Linux Firefox:
231
+ * about:config *resolution: Default:96
232
+ * (xorg screen dimension in mm and Desktop font dpi settings are ignored)
233
+ *
234
+ * Take care about extra font/image zoom factor of browser.
235
+ *
236
+ * In images, <img> size in pixel attribute, img css style, are overriding
237
+ * the real image dimension in px for rendering.
238
+ *
239
+ * @var int
240
+ */
241
+ def("DOMPDF_DPI", 96);
242
+
243
+ /**
244
+ * Enable inline PHP
245
+ *
246
+ * If this setting is set to true then DOMPDF will automatically evaluate
247
+ * inline PHP contained within <script type="text/php"> ... </script> tags.
248
+ *
249
+ * Enabling this for documents you do not trust (e.g. arbitrary remote html
250
+ * pages) is a security risk. Set this option to false if you wish to process
251
+ * untrusted documents.
252
+ *
253
+ * @var bool
254
+ */
255
+ def("DOMPDF_ENABLE_PHP", false);
256
+
257
+ /**
258
+ * Enable inline Javascript
259
+ *
260
+ * If this setting is set to true then DOMPDF will automatically insert
261
+ * JavaScript code contained within <script type="text/javascript"> ... </script> tags.
262
+ *
263
+ * @var bool
264
+ */
265
+ def("DOMPDF_ENABLE_JAVASCRIPT", true);
266
+
267
+ /**
268
+ * Enable remote file access
269
+ *
270
+ * If this setting is set to true, DOMPDF will access remote sites for
271
+ * images and CSS files as required.
272
+ * This is required for part of test case www/test/image_variants.html through www/examples.php
273
+ *
274
+ * Attention!
275
+ * This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and
276
+ * allowing remote access to dompdf.php or on allowing remote html code to be passed to
277
+ * $dompdf = new DOMPDF(); $dompdf->load_html(...);
278
+ * This allows anonymous users to download legally doubtful internet content which on
279
+ * tracing back appears to being downloaded by your server, or allows malicious php code
280
+ * in remote html pages to be executed by your server with your account privileges.
281
+ *
282
+ * @var bool
283
+ */
284
+ def("DOMPDF_ENABLE_REMOTE", true);
285
+
286
+ /**
287
+ * The debug output log
288
+ * @var string
289
+ */
290
+ def("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm");
291
+
292
+ /**
293
+ * A ratio applied to the fonts height to be more like browsers' line height
294
+ */
295
+ def("DOMPDF_FONT_HEIGHT_RATIO", 1.1);
296
+
297
+ /**
298
+ * Enable CSS float
299
+ *
300
+ * Allows people to disabled CSS float support
301
+ * @var bool
302
+ */
303
+ def("DOMPDF_ENABLE_CSS_FLOAT", false);
304
+
305
+ /**
306
+ * Enable the built in DOMPDF autoloader
307
+ *
308
+ * @var bool
309
+ */
310
+ def("DOMPDF_ENABLE_AUTOLOAD", true);
311
+
312
+ /**
313
+ * Prepend the DOMPDF autoload function the spl_autoload stack
314
+ *
315
+ * @var bool
316
+ */
317
+ def("DOMPDF_AUTOLOAD_PREPEND", false);
318
+
319
+ /**
320
+ * Use the more-than-experimental HTML5 Lib parser
321
+ */
322
+ def("DOMPDF_ENABLE_HTML5PARSER", false);
323
+ require_once(DOMPDF_LIB_DIR . "/html5lib/Parser.php");
324
+
325
+ // ### End of user-configurable options ###
326
+
327
+ /**
328
+ * Load autoloader
329
+ */
330
+ if (DOMPDF_ENABLE_AUTOLOAD) {
331
+ require_once(DOMPDF_INC_DIR . "/autoload.inc.php");
332
+ require_once(DOMPDF_LIB_DIR . "/php-font-lib/classes/font.cls.php");
333
+ }
334
+
335
+ /**
336
+ * Ensure that PHP is working with text internally using UTF8 character encoding.
337
+ */
338
+ mb_internal_encoding('UTF-8');
339
+
340
+ /**
341
+ * Global array of warnings generated by DomDocument parser and
342
+ * stylesheet class
343
+ *
344
+ * @var array
345
+ */
346
+ global $_dompdf_warnings;
347
+ $_dompdf_warnings = array();
348
+
349
+ /**
350
+ * If true, $_dompdf_warnings is dumped on script termination when using
351
+ * dompdf/dompdf.php or after rendering when using the DOMPDF class.
352
+ * When using the class, setting this value to true will prevent you from
353
+ * streaming the PDF.
354
+ *
355
+ * @var bool
356
+ */
357
+ global $_dompdf_show_warnings;
358
+ $_dompdf_show_warnings = false;
359
+
360
+ /**
361
+ * If true, the entire tree is dumped to stdout in dompdf.cls.php.
362
+ * Setting this value to true will prevent you from streaming the PDF.
363
+ *
364
+ * @var bool
365
+ */
366
+ global $_dompdf_debug;
367
+ $_dompdf_debug = false;
368
+
369
+ /**
370
+ * Array of enabled debug message types
371
+ *
372
+ * @var array
373
+ */
374
+ global $_DOMPDF_DEBUG_TYPES;
375
+ $_DOMPDF_DEBUG_TYPES = array(); //array("page-break" => 1);
376
+
377
+ /* Optionally enable different classes of debug output before the pdf content.
378
+ * Visible if displaying pdf as text,
379
+ * E.g. on repeated display of same pdf in browser when pdf is not taken out of
380
+ * the browser cache and the premature output prevents setting of the mime type.
381
+ */
382
+ def('DEBUGPNG', false);
383
+ def('DEBUGKEEPTEMP', false);
384
+ def('DEBUGCSS', false);
385
+
386
+ /* Layout debugging. Will display rectangles around different block levels.
387
+ * Visible in the PDF itself.
388
+ */
389
+ def('DEBUG_LAYOUT', false);
390
+ def('DEBUG_LAYOUT_LINES', true);
391
+ def('DEBUG_LAYOUT_BLOCKS', true);
392
+ def('DEBUG_LAYOUT_INLINE', true);
393
+ def('DEBUG_LAYOUT_PADDINGBOX', true);
lib/dompdf/include/absolute_positioner.cls.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions absolutely positioned frames
11
+ */
12
+ class Absolute_Positioner extends Positioner {
13
+
14
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
15
+
16
+ function position() {
17
+
18
+ $frame = $this->_frame;
19
+ $style = $frame->get_style();
20
+
21
+ $p = $frame->find_positionned_parent();
22
+
23
+ list($x, $y, $w, $h) = $frame->get_containing_block();
24
+
25
+ $top = $style->length_in_pt($style->top, $h);
26
+ $right = $style->length_in_pt($style->right, $w);
27
+ $bottom = $style->length_in_pt($style->bottom, $h);
28
+ $left = $style->length_in_pt($style->left, $w);
29
+
30
+ if ( $p && !($left === "auto" && $right === "auto") ) {
31
+ // Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top)
32
+ list($x, $y, $w, $h) = $p->get_padding_box();
33
+ }
34
+
35
+ list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height());
36
+
37
+ $orig_style = $this->_frame->get_original_style();
38
+ $orig_width = $orig_style->width;
39
+ $orig_height = $orig_style->height;
40
+
41
+ /****************************
42
+
43
+ Width auto:
44
+ ____________| left=auto | left=fixed |
45
+ right=auto | A | B |
46
+ right=fixed | C | D |
47
+
48
+ Width fixed:
49
+ ____________| left=auto | left=fixed |
50
+ right=auto | E | F |
51
+ right=fixed | G | H |
52
+
53
+ *****************************/
54
+
55
+ if ( $left === "auto" ) {
56
+ if ( $right === "auto" ) {
57
+ // A or E - Keep the frame at the same position
58
+ $x = $x + $frame->find_block_parent()->get_current_line_box()->w;
59
+ }
60
+ else {
61
+ if ( $orig_width === "auto" ) {
62
+ // C
63
+ $x += $w - $width - $right;
64
+ }
65
+ else {
66
+ // G
67
+ $x += $w - $width - $right;
68
+ }
69
+ }
70
+ }
71
+ else {
72
+ if ( $right === "auto" ) {
73
+ // B or F
74
+ $x += $left;
75
+ }
76
+ else {
77
+ if ( $orig_width === "auto" ) {
78
+ // D - TODO change width
79
+ $x += $left;
80
+ }
81
+ else {
82
+ // H - Everything is fixed: left + width win
83
+ $x += $left;
84
+ }
85
+ }
86
+ }
87
+
88
+ // The same vertically
89
+ if ( $top === "auto" ) {
90
+ if ( $bottom === "auto" ) {
91
+ // A or E - Keep the frame at the same position
92
+ $y = $frame->find_block_parent()->get_current_line_box()->y;
93
+ }
94
+ else {
95
+ if ( $orig_height === "auto" ) {
96
+ // C
97
+ $y += $h - $height - $bottom;
98
+ }
99
+ else {
100
+ // G
101
+ $y += $h - $height - $bottom;
102
+ }
103
+ }
104
+ }
105
+ else {
106
+ if ( $bottom === "auto" ) {
107
+ // B or F
108
+ $y += $top;
109
+ }
110
+ else {
111
+ if ( $orig_height === "auto" ) {
112
+ // D - TODO change height
113
+ $y += $top;
114
+ }
115
+ else {
116
+ // H - Everything is fixed: top + height win
117
+ $y += $top;
118
+ }
119
+ }
120
+ }
121
+
122
+ $frame->set_position($x, $y);
123
+
124
+ }
125
+ }
lib/dompdf/include/abstract_renderer.cls.php ADDED
@@ -0,0 +1,759 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * Base renderer class
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ abstract class Abstract_Renderer {
18
+
19
+ /**
20
+ * Rendering backend
21
+ *
22
+ * @var Canvas
23
+ */
24
+ protected $_canvas;
25
+
26
+ /**
27
+ * Current dompdf instance
28
+ *
29
+ * @var DOMPDF
30
+ */
31
+ protected $_dompdf;
32
+
33
+ /**
34
+ * Class constructor
35
+ *
36
+ * @param DOMPDF $dompdf The current dompdf instance
37
+ */
38
+ function __construct(DOMPDF $dompdf) {
39
+ $this->_dompdf = $dompdf;
40
+ $this->_canvas = $dompdf->get_canvas();
41
+ }
42
+
43
+ /**
44
+ * Render a frame.
45
+ *
46
+ * Specialized in child classes
47
+ *
48
+ * @param Frame $frame The frame to render
49
+ */
50
+ abstract function render(Frame $frame);
51
+
52
+ //........................................................................
53
+
54
+ /**
55
+ * Render a background image over a rectangular area
56
+ *
57
+ * @param string $url The background image to load
58
+ * @param float $x The left edge of the rectangular area
59
+ * @param float $y The top edge of the rectangular area
60
+ * @param float $width The width of the rectangular area
61
+ * @param float $height The height of the rectangular area
62
+ * @param Style $style The associated Style object
63
+ *
64
+ * @throws Exception
65
+ */
66
+ protected function _background_image($url, $x, $y, $width, $height, $style) {
67
+ if ( !function_exists("imagecreatetruecolor") ) {
68
+ throw new Exception("The PHP GD extension is required, but is not installed.");
69
+ }
70
+
71
+ $sheet = $style->get_stylesheet();
72
+
73
+ // Skip degenerate cases
74
+ if ( $width == 0 || $height == 0 ) {
75
+ return;
76
+ }
77
+
78
+ $box_width = $width;
79
+ $box_height = $height;
80
+
81
+ //debugpng
82
+ if (DEBUGPNG) print '[_background_image '.$url.']';
83
+
84
+ list($img, $type, /*$msg*/) = Image_Cache::resolve_url(
85
+ $url,
86
+ $sheet->get_protocol(),
87
+ $sheet->get_host(),
88
+ $sheet->get_base_path(),
89
+ $this->_dompdf
90
+ );
91
+
92
+ // Bail if the image is no good
93
+ if ( Image_Cache::is_broken($img) ) {
94
+ return;
95
+ }
96
+
97
+ //Try to optimize away reading and composing of same background multiple times
98
+ //Postponing read with imagecreatefrom ...()
99
+ //final composition parameters and name not known yet
100
+ //Therefore read dimension directly from file, instead of creating gd object first.
101
+ //$img_w = imagesx($src); $img_h = imagesy($src);
102
+
103
+ list($img_w, $img_h) = dompdf_getimagesize($img);
104
+ if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) {
105
+ return;
106
+ }
107
+
108
+ $repeat = $style->background_repeat;
109
+ $dpi = $this->_dompdf->get_option("dpi");
110
+
111
+ //Increase background resolution and dependent box size according to image resolution to be placed in
112
+ //Then image can be copied in without resize
113
+ $bg_width = round((float)($width * $dpi) / 72);
114
+ $bg_height = round((float)($height * $dpi) / 72);
115
+
116
+ //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel
117
+
118
+ list($bg_x, $bg_y) = $style->background_position;
119
+
120
+ if ( is_percent($bg_x) ) {
121
+ // The point $bg_x % from the left edge of the image is placed
122
+ // $bg_x % from the left edge of the background rectangle
123
+ $p = ((float)$bg_x)/100.0;
124
+ $x1 = $p * $img_w;
125
+ $x2 = $p * $bg_width;
126
+
127
+ $bg_x = $x2 - $x1;
128
+ }
129
+ else {
130
+ $bg_x = (float)($style->length_in_pt($bg_x)*$dpi) / 72;
131
+ }
132
+
133
+ $bg_x = round($bg_x + $style->length_in_pt($style->border_left_width)*$dpi / 72);
134
+
135
+ if ( is_percent($bg_y) ) {
136
+ // The point $bg_y % from the left edge of the image is placed
137
+ // $bg_y % from the left edge of the background rectangle
138
+ $p = ((float)$bg_y)/100.0;
139
+ $y1 = $p * $img_h;
140
+ $y2 = $p * $bg_height;
141
+
142
+ $bg_y = $y2 - $y1;
143
+ }
144
+ else {
145
+ $bg_y = (float)($style->length_in_pt($bg_y)*$dpi) / 72;
146
+ }
147
+
148
+ $bg_y = round($bg_y + $style->length_in_pt($style->border_top_width)*$dpi / 72);
149
+
150
+ //clip background to the image area on partial repeat. Nothing to do if img off area
151
+ //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area
152
+ //On no repeat with positive offset: move size/start to have offset==0
153
+ //Handle x/y Dimensions separately
154
+
155
+ if ( $repeat !== "repeat" && $repeat !== "repeat-x" ) {
156
+ //No repeat x
157
+ if ($bg_x < 0) {
158
+ $bg_width = $img_w + $bg_x;
159
+ }
160
+ else {
161
+ $x += ($bg_x * 72)/$dpi;
162
+ $bg_width = $bg_width - $bg_x;
163
+ if ($bg_width > $img_w) {
164
+ $bg_width = $img_w;
165
+ }
166
+ $bg_x = 0;
167
+ }
168
+
169
+ if ($bg_width <= 0) {
170
+ return;
171
+ }
172
+
173
+ $width = (float)($bg_width * 72)/$dpi;
174
+ }
175
+ else {
176
+ //repeat x
177
+ if ($bg_x < 0) {
178
+ $bg_x = - ((-$bg_x) % $img_w);
179
+ }
180
+ else {
181
+ $bg_x = $bg_x % $img_w;
182
+ if ($bg_x > 0) {
183
+ $bg_x -= $img_w;
184
+ }
185
+ }
186
+ }
187
+
188
+ if ( $repeat !== "repeat" && $repeat !== "repeat-y" ) {
189
+ //no repeat y
190
+ if ($bg_y < 0) {
191
+ $bg_height = $img_h + $bg_y;
192
+ }
193
+ else {
194
+ $y += ($bg_y * 72)/$dpi;
195
+ $bg_height = $bg_height - $bg_y;
196
+ if ($bg_height > $img_h) {
197
+ $bg_height = $img_h;
198
+ }
199
+ $bg_y = 0;
200
+ }
201
+ if ($bg_height <= 0) {
202
+ return;
203
+ }
204
+ $height = (float)($bg_height * 72)/$dpi;
205
+ }
206
+ else {
207
+ //repeat y
208
+ if ($bg_y < 0) {
209
+ $bg_y = - ((-$bg_y) % $img_h);
210
+ }
211
+ else {
212
+ $bg_y = $bg_y % $img_h;
213
+ if ($bg_y > 0) {
214
+ $bg_y -= $img_h;
215
+ }
216
+ }
217
+ }
218
+
219
+ //Optimization, if repeat has no effect
220
+ if ( $repeat === "repeat" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height ) {
221
+ $repeat = "repeat-x";
222
+ }
223
+
224
+ if ( $repeat === "repeat" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width ) {
225
+ $repeat = "repeat-y";
226
+ }
227
+
228
+ if ( ($repeat === "repeat-x" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width) ||
229
+ ($repeat === "repeat-y" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height) ) {
230
+ $repeat = "no-repeat";
231
+ }
232
+
233
+ //Use filename as indicator only
234
+ //different names for different variants to have different copies in the pdf
235
+ //This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color)
236
+ //Note: Here, bg_* are the start values, not end values after going through the tile loops!
237
+
238
+ $filedummy = $img;
239
+
240
+ $is_png = false;
241
+ $filedummy .= '_'.$bg_width.'_'.$bg_height.'_'.$bg_x.'_'.$bg_y.'_'.$repeat;
242
+
243
+ //Optimization to avoid multiple times rendering the same image.
244
+ //If check functions are existing and identical image already cached,
245
+ //then skip creation of duplicate, because it is not needed by addImagePng
246
+ if ( $this->_canvas instanceof CPDF_Adapter &&
247
+ $this->_canvas->get_cpdf()->image_iscached($filedummy) ) {
248
+ $bg = null;
249
+ }
250
+
251
+ else {
252
+
253
+ // Create a new image to fit over the background rectangle
254
+ $bg = imagecreatetruecolor($bg_width, $bg_height);
255
+
256
+ switch (strtolower($type)) {
257
+ case IMAGETYPE_PNG:
258
+ $is_png = true;
259
+ imagesavealpha($bg, true);
260
+ imagealphablending($bg, false);
261
+ $src = imagecreatefrompng($img);
262
+ break;
263
+
264
+ case IMAGETYPE_JPEG:
265
+ $src = imagecreatefromjpeg($img);
266
+ break;
267
+
268
+ case IMAGETYPE_GIF:
269
+ $src = imagecreatefromgif($img);
270
+ break;
271
+
272
+ case IMAGETYPE_BMP:
273
+ $src = imagecreatefrombmp($img);
274
+ break;
275
+
276
+ default:
277
+ return; // Unsupported image type
278
+ }
279
+
280
+ if ( $src == null ) {
281
+ return;
282
+ }
283
+
284
+ //Background color if box is not relevant here
285
+ //Non transparent image: box clipped to real size. Background non relevant.
286
+ //Transparent image: The image controls the transparency and lets shine through whatever background.
287
+ //However on transparent image preset the composed image with the transparency color,
288
+ //to keep the transparency when copying over the non transparent parts of the tiles.
289
+ $ti = imagecolortransparent($src);
290
+
291
+ if ( $ti >= 0 ) {
292
+ $tc = imagecolorsforindex($src, $ti);
293
+ $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']);
294
+ imagefill($bg, 0, 0, $ti);
295
+ imagecolortransparent($bg, $ti);
296
+ }
297
+
298
+ //This has only an effect for the non repeatable dimension.
299
+ //compute start of src and dest coordinates of the single copy
300
+ if ( $bg_x < 0 ) {
301
+ $dst_x = 0;
302
+ $src_x = -$bg_x;
303
+ }
304
+ else {
305
+ $src_x = 0;
306
+ $dst_x = $bg_x;
307
+ }
308
+
309
+ if ( $bg_y < 0 ) {
310
+ $dst_y = 0;
311
+ $src_y = -$bg_y;
312
+ }
313
+ else {
314
+ $src_y = 0;
315
+ $dst_y = $bg_y;
316
+ }
317
+
318
+ //For historical reasons exchange meanings of variables:
319
+ //start_* will be the start values, while bg_* will be the temporary start values in the loops
320
+ $start_x = $bg_x;
321
+ $start_y = $bg_y;
322
+
323
+ // Copy regions from the source image to the background
324
+ if ( $repeat === "no-repeat" ) {
325
+
326
+ // Simply place the image on the background
327
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h);
328
+
329
+ }
330
+ else if ( $repeat === "repeat-x" ) {
331
+
332
+ for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
333
+ if ( $bg_x < 0 ) {
334
+ $dst_x = 0;
335
+ $src_x = -$bg_x;
336
+ $w = $img_w + $bg_x;
337
+ }
338
+ else {
339
+ $dst_x = $bg_x;
340
+ $src_x = 0;
341
+ $w = $img_w;
342
+ }
343
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h);
344
+ }
345
+
346
+ }
347
+ else if ( $repeat === "repeat-y" ) {
348
+
349
+ for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
350
+ if ( $bg_y < 0 ) {
351
+ $dst_y = 0;
352
+ $src_y = -$bg_y;
353
+ $h = $img_h + $bg_y;
354
+ }
355
+ else {
356
+ $dst_y = $bg_y;
357
+ $src_y = 0;
358
+ $h = $img_h;
359
+ }
360
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h);
361
+
362
+ }
363
+
364
+ }
365
+ else if ( $repeat === "repeat" ) {
366
+
367
+ for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
368
+ for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
369
+
370
+ if ( $bg_x < 0 ) {
371
+ $dst_x = 0;
372
+ $src_x = -$bg_x;
373
+ $w = $img_w + $bg_x;
374
+ }
375
+ else {
376
+ $dst_x = $bg_x;
377
+ $src_x = 0;
378
+ $w = $img_w;
379
+ }
380
+
381
+ if ( $bg_y < 0 ) {
382
+ $dst_y = 0;
383
+ $src_y = -$bg_y;
384
+ $h = $img_h + $bg_y;
385
+ }
386
+ else {
387
+ $dst_y = $bg_y;
388
+ $src_y = 0;
389
+ $h = $img_h;
390
+ }
391
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h);
392
+ }
393
+ }
394
+ }
395
+
396
+ else {
397
+ print 'Unknown repeat!';
398
+ }
399
+
400
+ imagedestroy($src);
401
+
402
+ } /* End optimize away creation of duplicates */
403
+
404
+ $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height);
405
+
406
+ //img: image url string
407
+ //img_w, img_h: original image size in px
408
+ //width, height: box size in pt
409
+ //bg_width, bg_height: box size in px
410
+ //x, y: left/top edge of box on page in pt
411
+ //start_x, start_y: placement of image relative to pattern
412
+ //$repeat: repeat mode
413
+ //$bg: GD object of result image
414
+ //$src: GD object of original image
415
+ //When using cpdf and optimization to direct png creation from gd object is available,
416
+ //don't create temp file, but place gd object directly into the pdf
417
+ if ( !$is_png && $this->_canvas instanceof CPDF_Adapter ) {
418
+ // Note: CPDF_Adapter image converts y position
419
+ $this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg);
420
+ }
421
+
422
+ else {
423
+ $tmp_dir = $this->_dompdf->get_option("temp_dir");
424
+ $tmp_name = tempnam($tmp_dir, "bg_dompdf_img_");
425
+ @unlink($tmp_name);
426
+ $tmp_file = "$tmp_name.png";
427
+
428
+ //debugpng
429
+ if (DEBUGPNG) print '[_background_image '.$tmp_file.']';
430
+
431
+ imagepng($bg, $tmp_file);
432
+ $this->_canvas->image($tmp_file, $x, $y, $width, $height);
433
+ imagedestroy($bg);
434
+
435
+ //debugpng
436
+ if (DEBUGPNG) print '[_background_image unlink '.$tmp_file.']';
437
+
438
+ if (!DEBUGKEEPTEMP) {
439
+ unlink($tmp_file);
440
+ }
441
+ }
442
+
443
+ $this->_canvas->clipping_end();
444
+ }
445
+
446
+ protected function _get_dash_pattern($style, $width) {
447
+ $pattern = array();
448
+
449
+ switch ($style) {
450
+ default:
451
+ /*case "solid":
452
+ case "double":
453
+ case "groove":
454
+ case "inset":
455
+ case "outset":
456
+ case "ridge":*/
457
+ case "none": break;
458
+
459
+ case "dotted":
460
+ if ( $width <= 1 )
461
+ $pattern = array($width, $width*2);
462
+ else
463
+ $pattern = array($width);
464
+ break;
465
+
466
+ case "dashed":
467
+ $pattern = array(3 * $width);
468
+ break;
469
+ }
470
+
471
+ return $pattern;
472
+ }
473
+
474
+ protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
475
+ return;
476
+ }
477
+
478
+ protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
479
+ return;
480
+ }
481
+
482
+ // Border rendering functions
483
+
484
+ protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
485
+ $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2);
486
+ }
487
+
488
+
489
+ protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
490
+ $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2);
491
+ }
492
+
493
+
494
+ protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
495
+ // TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't.
496
+ if ( $corner_style !== "bevel" || $r1 > 0 || $r2 > 0 ) {
497
+ // do it the simple way
498
+ $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2);
499
+ return;
500
+ }
501
+
502
+ list($top, $right, $bottom, $left) = $widths;
503
+
504
+ // All this polygon business is for beveled corners...
505
+ switch ($side) {
506
+ case "top":
507
+ $points = array($x, $y,
508
+ $x + $length, $y,
509
+ $x + $length - $right, $y + $top,
510
+ $x + $left, $y + $top);
511
+ $this->_canvas->polygon($points, $color, null, null, true);
512
+ break;
513
+
514
+ case "bottom":
515
+ $points = array($x, $y,
516
+ $x + $length, $y,
517
+ $x + $length - $right, $y - $bottom,
518
+ $x + $left, $y - $bottom);
519
+ $this->_canvas->polygon($points, $color, null, null, true);
520
+ break;
521
+
522
+ case "left":
523
+ $points = array($x, $y,
524
+ $x, $y + $length,
525
+ $x + $left, $y + $length - $bottom,
526
+ $x + $left, $y + $top);
527
+ $this->_canvas->polygon($points, $color, null, null, true);
528
+ break;
529
+
530
+ case "right":
531
+ $points = array($x, $y,
532
+ $x, $y + $length,
533
+ $x - $right, $y + $length - $bottom,
534
+ $x - $right, $y + $top);
535
+ $this->_canvas->polygon($points, $color, null, null, true);
536
+ break;
537
+
538
+ default:
539
+ return;
540
+ }
541
+ }
542
+
543
+ protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) {
544
+ switch ($side) {
545
+
546
+ case "top":
547
+ $r1 -= $left * $ratio;
548
+ $r2 -= $right * $ratio;
549
+ $x += $left * $ratio;
550
+ $y += $top * $ratio;
551
+ $length -= $left * $ratio + $right * $ratio;
552
+ break;
553
+
554
+ case "bottom":
555
+ $r1 -= $right * $ratio;
556
+ $r2 -= $left * $ratio;
557
+ $x += $left * $ratio;
558
+ $y -= $bottom * $ratio;
559
+ $length -= $left * $ratio + $right * $ratio;
560
+ break;
561
+
562
+ case "left":
563
+ $r1 -= $top * $ratio;
564
+ $r2 -= $bottom * $ratio;
565
+ $x += $left * $ratio;
566
+ $y += $top * $ratio;
567
+ $length -= $top * $ratio + $bottom * $ratio;
568
+ break;
569
+
570
+ case "right":
571
+ $r1 -= $bottom * $ratio;
572
+ $r2 -= $top * $ratio;
573
+ $x -= $right * $ratio;
574
+ $y += $top * $ratio;
575
+ $length -= $top * $ratio + $bottom * $ratio;
576
+ break;
577
+
578
+ default:
579
+ return;
580
+
581
+ }
582
+ }
583
+
584
+ protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
585
+ list($top, $right, $bottom, $left) = $widths;
586
+
587
+ $third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3);
588
+
589
+ // draw the outer border
590
+ $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
591
+
592
+ $this->_apply_ratio($side, 2/3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
593
+
594
+ $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
595
+ }
596
+
597
+ protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
598
+ list($top, $right, $bottom, $left) = $widths;
599
+
600
+ $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
601
+
602
+ $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
603
+
604
+ $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
605
+
606
+ $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
607
+
608
+ }
609
+
610
+ protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
611
+ list($top, $right, $bottom, $left) = $widths;
612
+
613
+ $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
614
+
615
+ $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
616
+
617
+ $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
618
+
619
+ $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
620
+
621
+ }
622
+
623
+ protected function _tint($c) {
624
+ if ( !is_numeric($c) )
625
+ return $c;
626
+
627
+ return min(1, $c + 0.16);
628
+ }
629
+
630
+ protected function _shade($c) {
631
+ if ( !is_numeric($c) )
632
+ return $c;
633
+
634
+ return max(0, $c - 0.33);
635
+ }
636
+
637
+ protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
638
+ switch ($side) {
639
+ case "top":
640
+ case "left":
641
+ $shade = array_map(array($this, "_shade"), $color);
642
+ $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
643
+ break;
644
+
645
+ case "bottom":
646
+ case "right":
647
+ $tint = array_map(array($this, "_tint"), $color);
648
+ $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
649
+ break;
650
+
651
+ default:
652
+ return;
653
+ }
654
+ }
655
+
656
+ protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
657
+ switch ($side) {
658
+ case "top":
659
+ case "left":
660
+ $tint = array_map(array($this, "_tint"), $color);
661
+ $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
662
+ break;
663
+
664
+ case "bottom":
665
+ case "right":
666
+ $shade = array_map(array($this, "_shade"), $color);
667
+ $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
668
+ break;
669
+
670
+ default:
671
+ return;
672
+ }
673
+ }
674
+ // Draws a solid, dotted, or dashed line, observing the border radius
675
+ protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) {
676
+ list($top, $right, $bottom, $left) = $widths;
677
+
678
+ $width = $$side;
679
+ $pattern = $this->_get_dash_pattern($pattern_name, $width);
680
+
681
+ $half_width = $width/2;
682
+ $r1 -= $half_width;
683
+ $r2 -= $half_width;
684
+ $adjust = $r1/80;
685
+ $length -= $width;
686
+
687
+ switch ($side) {
688
+ case "top":
689
+ $x += $half_width;
690
+ $y += $half_width;
691
+
692
+ if ( $r1 > 0 ) {
693
+ $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90-$adjust, 135+$adjust, $color, $width, $pattern);
694
+ }
695
+
696
+ $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
697
+
698
+ if ( $r2 > 0 ) {
699
+ $this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45-$adjust, 90+$adjust, $color, $width, $pattern);
700
+ }
701
+ break;
702
+
703
+ case "bottom":
704
+ $x += $half_width;
705
+ $y -= $half_width;
706
+
707
+ if ( $r1 > 0 ) {
708
+ $this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225-$adjust, 270+$adjust, $color, $width, $pattern);
709
+ }
710
+
711
+ $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
712
+
713
+ if ( $r2 > 0 ) {
714
+ $this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270-$adjust, 315+$adjust, $color, $width, $pattern);
715
+ }
716
+ break;
717
+
718
+ case "left":
719
+ $y += $half_width;
720
+ $x += $half_width;
721
+
722
+ if ( $r1 > 0 ) {
723
+ $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135-$adjust, 180+$adjust, $color, $width, $pattern);
724
+ }
725
+
726
+ $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
727
+
728
+ if ( $r2 > 0 ) {
729
+ $this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180-$adjust, 225+$adjust, $color, $width, $pattern);
730
+ }
731
+ break;
732
+
733
+ case "right":
734
+ $y += $half_width;
735
+ $x -= $half_width;
736
+
737
+ if ( $r1 > 0 ) {
738
+ $this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0-$adjust, 45+$adjust, $color, $width, $pattern);
739
+ }
740
+
741
+ $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
742
+
743
+ if ( $r2 > 0 ) {
744
+ $this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315-$adjust, 360+$adjust, $color, $width, $pattern);
745
+ }
746
+ break;
747
+ }
748
+ }
749
+
750
+ protected function _set_opacity($opacity) {
751
+ if ( is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0 ) {
752
+ $this->_canvas->set_opacity( $opacity );
753
+ }
754
+ }
755
+
756
+ protected function _debug_layout($box, $color = "red", $style = array()) {
757
+ $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], CSS_Color::parse($color), 0.1, $style);
758
+ }
759
+ }
lib/dompdf/include/attribute_translator.cls.php ADDED
@@ -0,0 +1,592 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Translates HTML 4.0 attributes into CSS rules
12
+ *
13
+ * @package dompdf
14
+ */
15
+ class Attribute_Translator {
16
+ static $_style_attr = "_html_style_attribute";
17
+
18
+ // Munged data originally from
19
+ // http://www.w3.org/TR/REC-html40/index/attributes.html
20
+ // http://www.cs.tut.fi/~jkorpela/html2css.html
21
+ static private $__ATTRIBUTE_LOOKUP = array(
22
+ //'caption' => array ( 'align' => '', ),
23
+ 'img' => array(
24
+ 'align' => array(
25
+ 'bottom' => 'vertical-align: baseline;',
26
+ 'middle' => 'vertical-align: middle;',
27
+ 'top' => 'vertical-align: top;',
28
+ 'left' => 'float: left;',
29
+ 'right' => 'float: right;'
30
+ ),
31
+ 'border' => 'border: %0.2F px solid;',
32
+ 'height' => 'height: %s px;',
33
+ 'hspace' => 'padding-left: %1$0.2F px; padding-right: %1$0.2F px;',
34
+ 'vspace' => 'padding-top: %1$0.2F px; padding-bottom: %1$0.2F px;',
35
+ 'width' => 'width: %s px;',
36
+ ),
37
+ 'table' => array(
38
+ 'align' => array(
39
+ 'left' => 'margin-left: 0; margin-right: auto;',
40
+ 'center' => 'margin-left: auto; margin-right: auto;',
41
+ 'right' => 'margin-left: auto; margin-right: 0;'
42
+ ),
43
+ 'bgcolor' => 'background-color: %s;',
44
+ 'border' => '!set_table_border',
45
+ 'cellpadding' => '!set_table_cellpadding',//'border-spacing: %0.2F; border-collapse: separate;',
46
+ 'cellspacing' => '!set_table_cellspacing',
47
+ 'frame' => array(
48
+ 'void' => 'border-style: none;',
49
+ 'above' => 'border-top-style: solid;',
50
+ 'below' => 'border-bottom-style: solid;',
51
+ 'hsides' => 'border-left-style: solid; border-right-style: solid;',
52
+ 'vsides' => 'border-top-style: solid; border-bottom-style: solid;',
53
+ 'lhs' => 'border-left-style: solid;',
54
+ 'rhs' => 'border-right-style: solid;',
55
+ 'box' => 'border-style: solid;',
56
+ 'border' => 'border-style: solid;'
57
+ ),
58
+ 'rules' => '!set_table_rules',
59
+ 'width' => 'width: %s;',
60
+ ),
61
+ 'hr' => array(
62
+ 'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly
63
+ 'noshade' => 'border-style: solid;',
64
+ 'size' => '!set_hr_size', //'border-width: %0.2F px;',
65
+ 'width' => 'width: %s;',
66
+ ),
67
+ 'div' => array(
68
+ 'align' => 'text-align: %s;',
69
+ ),
70
+ 'h1' => array(
71
+ 'align' => 'text-align: %s;',
72
+ ),
73
+ 'h2' => array(
74
+ 'align' => 'text-align: %s;',
75
+ ),
76
+ 'h3' => array(
77
+ 'align' => 'text-align: %s;',
78
+ ),
79
+ 'h4' => array(
80
+ 'align' => 'text-align: %s;',
81
+ ),
82
+ 'h5' => array(
83
+ 'align' => 'text-align: %s;',
84
+ ),
85
+ 'h6' => array(
86
+ 'align' => 'text-align: %s;',
87
+ ),
88
+ 'p' => array(
89
+ 'align' => 'text-align: %s;',
90
+ ),
91
+ // 'col' => array(
92
+ // 'align' => '',
93
+ // 'valign' => '',
94
+ // ),
95
+ // 'colgroup' => array(
96
+ // 'align' => '',
97
+ // 'valign' => '',
98
+ // ),
99
+ 'tbody' => array(
100
+ 'align' => '!set_table_row_align',
101
+ 'valign' => '!set_table_row_valign',
102
+ ),
103
+ 'td' => array(
104
+ 'align' => 'text-align: %s;',
105
+ 'bgcolor' => '!set_background_color',
106
+ 'height' => 'height: %s;',
107
+ 'nowrap' => 'white-space: nowrap;',
108
+ 'valign' => 'vertical-align: %s;',
109
+ 'width' => 'width: %s;',
110
+ ),
111
+ 'tfoot' => array(
112
+ 'align' => '!set_table_row_align',
113
+ 'valign' => '!set_table_row_valign',
114
+ ),
115
+ 'th' => array(
116
+ 'align' => 'text-align: %s;',
117
+ 'bgcolor' => '!set_background_color',
118
+ 'height' => 'height: %s;',
119
+ 'nowrap' => 'white-space: nowrap;',
120
+ 'valign' => 'vertical-align: %s;',
121
+ 'width' => 'width: %s;',
122
+ ),
123
+ 'thead' => array(
124
+ 'align' => '!set_table_row_align',
125
+ 'valign' => '!set_table_row_valign',
126
+ ),
127
+ 'tr' => array(
128
+ 'align' => '!set_table_row_align',
129
+ 'bgcolor' => '!set_table_row_bgcolor',
130
+ 'valign' => '!set_table_row_valign',
131
+ ),
132
+ 'body' => array(
133
+ 'background' => 'background-image: url(%s);',
134
+ 'bgcolor' => '!set_background_color',
135
+ 'link' => '!set_body_link',
136
+ 'text' => '!set_color',
137
+ ),
138
+ 'br' => array(
139
+ 'clear' => 'clear: %s;',
140
+ ),
141
+ 'basefont' => array(
142
+ 'color' => '!set_color',
143
+ 'face' => 'font-family: %s;',
144
+ 'size' => '!set_basefont_size',
145
+ ),
146
+ 'font' => array(
147
+ 'color' => '!set_color',
148
+ 'face' => 'font-family: %s;',
149
+ 'size' => '!set_font_size',
150
+ ),
151
+ 'dir' => array(
152
+ 'compact' => 'margin: 0.5em 0;',
153
+ ),
154
+ 'dl' => array(
155
+ 'compact' => 'margin: 0.5em 0;',
156
+ ),
157
+ 'menu' => array(
158
+ 'compact' => 'margin: 0.5em 0;',
159
+ ),
160
+ 'ol' => array(
161
+ 'compact' => 'margin: 0.5em 0;',
162
+ 'start' => 'counter-reset: -dompdf-default-counter %d;',
163
+ 'type' => 'list-style-type: %s;',
164
+ ),
165
+ 'ul' => array(
166
+ 'compact' => 'margin: 0.5em 0;',
167
+ 'type' => 'list-style-type: %s;',
168
+ ),
169
+ 'li' => array(
170
+ 'type' => 'list-style-type: %s;',
171
+ 'value' => 'counter-reset: -dompdf-default-counter %d;',
172
+ ),
173
+ 'pre' => array(
174
+ 'width' => 'width: %s;',
175
+ ),
176
+ );
177
+
178
+ static protected $_last_basefont_size = 3;
179
+ static protected $_font_size_lookup = array(
180
+ // For basefont support
181
+ -3 => "4pt",
182
+ -2 => "5pt",
183
+ -1 => "6pt",
184
+ 0 => "7pt",
185
+
186
+ 1 => "8pt",
187
+ 2 => "10pt",
188
+ 3 => "12pt",
189
+ 4 => "14pt",
190
+ 5 => "18pt",
191
+ 6 => "24pt",
192
+ 7 => "34pt",
193
+
194
+ // For basefont support
195
+ 8 => "48pt",
196
+ 9 => "44pt",
197
+ 10 => "52pt",
198
+ 11 => "60pt",
199
+ );
200
+
201
+ /**
202
+ * @param Frame $frame
203
+ */
204
+ static function translate_attributes(Frame $frame) {
205
+ $node = $frame->get_node();
206
+ $tag = $node->nodeName;
207
+
208
+ if ( !isset(self::$__ATTRIBUTE_LOOKUP[$tag]) ) {
209
+ return;
210
+ }
211
+
212
+ $valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag];
213
+ $attrs = $node->attributes;
214
+ $style = rtrim($node->getAttribute(self::$_style_attr), "; ");
215
+ if ( $style != "" ) {
216
+ $style .= ";";
217
+ }
218
+
219
+ foreach ($attrs as $attr => $attr_node ) {
220
+ if ( !isset($valid_attrs[$attr]) ) {
221
+ continue;
222
+ }
223
+
224
+ $value = $attr_node->value;
225
+
226
+ $target = $valid_attrs[$attr];
227
+
228
+ // Look up $value in $target, if $target is an array:
229
+ if ( is_array($target) ) {
230
+ if ( isset($target[$value]) ) {
231
+ $style .= " " . self::_resolve_target($node, $target[$value], $value);
232
+ }
233
+ }
234
+ else {
235
+ // otherwise use target directly
236
+ $style .= " " . self::_resolve_target($node, $target, $value);
237
+ }
238
+ }
239
+
240
+ if ( !is_null($style) ) {
241
+ $style = ltrim($style);
242
+ $node->setAttribute(self::$_style_attr, $style);
243
+ }
244
+
245
+ }
246
+
247
+ /**
248
+ * @param DOMNode $node
249
+ * @param string $target
250
+ * @param string $value
251
+ *
252
+ * @return string
253
+ */
254
+ static protected function _resolve_target(DOMNode $node, $target, $value) {
255
+ if ( $target[0] === "!" ) {
256
+ // Function call
257
+ $func = "_" . mb_substr($target, 1);
258
+ return self::$func($node, $value);
259
+ }
260
+
261
+ return $value ? sprintf($target, $value) : "";
262
+ }
263
+
264
+ /**
265
+ * @param DOMElement $node
266
+ * @param string $new_style
267
+ */
268
+ static function append_style(DOMElement $node, $new_style) {
269
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
270
+ $style .= $new_style;
271
+ $style = ltrim($style, ";");
272
+ $node->setAttribute(self::$_style_attr, $style);
273
+ }
274
+
275
+ /**
276
+ * @param DOMNode $node
277
+ *
278
+ * @return DOMNodeList|DOMElement[]
279
+ */
280
+ static protected function get_cell_list(DOMNode $node) {
281
+ $xpath = new DOMXpath($node->ownerDocument);
282
+
283
+ switch($node->nodeName) {
284
+ default:
285
+ case "table":
286
+ $query = "tr/td | thead/tr/td | tbody/tr/td | tfoot/tr/td | tr/th | thead/tr/th | tbody/tr/th | tfoot/tr/th";
287
+ break;
288
+
289
+ case "tbody":
290
+ case "tfoot":
291
+ case "thead":
292
+ $query = "tr/td | tr/th";
293
+ break;
294
+
295
+ case "tr":
296
+ $query = "td | th";
297
+ break;
298
+ }
299
+
300
+ return $xpath->query($query, $node);
301
+ }
302
+
303
+ /**
304
+ * @param string $value
305
+ *
306
+ * @return string
307
+ */
308
+ static protected function _get_valid_color($value) {
309
+ if ( preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches) ) {
310
+ $value = "#$matches[1]";
311
+ }
312
+
313
+ return $value;
314
+ }
315
+
316
+ /**
317
+ * @param DOMElement $node
318
+ * @param string $value
319
+ *
320
+ * @return string
321
+ */
322
+ static protected function _set_color(DOMElement $node, $value) {
323
+ $value = self::_get_valid_color($value);
324
+ return "color: $value;";
325
+ }
326
+
327
+ /**
328
+ * @param DOMElement $node
329
+ * @param string $value
330
+ *
331
+ * @return string
332
+ */
333
+ static protected function _set_background_color(DOMElement $node, $value) {
334
+ $value = self::_get_valid_color($value);
335
+ return "background-color: $value;";
336
+ }
337
+
338
+ /**
339
+ * @param DOMElement $node
340
+ * @param string $value
341
+ *
342
+ * @return null
343
+ */
344
+ static protected function _set_table_cellpadding(DOMElement $node, $value) {
345
+ $cell_list = self::get_cell_list($node);
346
+
347
+ foreach ($cell_list as $cell) {
348
+ self::append_style($cell, "; padding: {$value}px;");
349
+ }
350
+
351
+ return null;
352
+ }
353
+
354
+ /**
355
+ * @param DOMElement $node
356
+ * @param string $value
357
+ *
358
+ * @return string
359
+ */
360
+ static protected function _set_table_border(DOMElement $node, $value) {
361
+ $cell_list = self::get_cell_list($node);
362
+
363
+ foreach ($cell_list as $cell) {
364
+ $style = rtrim($cell->getAttribute(self::$_style_attr));
365
+ $style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;";
366
+ $style = ltrim($style, ";");
367
+ $cell->setAttribute(self::$_style_attr, $style);
368
+ }
369
+
370
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
371
+ $style .= "; border-width: $value" . "px; ";
372
+ return ltrim($style, "; ");
373
+ }
374
+
375
+ /**
376
+ * @param DOMElement $node
377
+ * @param string $value
378
+ *
379
+ * @return string
380
+ */
381
+ static protected function _set_table_cellspacing(DOMElement $node, $value) {
382
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
383
+
384
+ if ( $value == 0 ) {
385
+ $style .= "; border-collapse: collapse;";
386
+ }
387
+ else {
388
+ $style .= "; border-spacing: {$value}px; border-collapse: separate;";
389
+ }
390
+
391
+ return ltrim($style, ";");
392
+ }
393
+
394
+ /**
395
+ * @param DOMElement $node
396
+ * @param string $value
397
+ *
398
+ * @return null|string
399
+ */
400
+ static protected function _set_table_rules(DOMElement $node, $value) {
401
+ $new_style = "; border-collapse: collapse;";
402
+
403
+ switch ($value) {
404
+ case "none":
405
+ $new_style .= "border-style: none;";
406
+ break;
407
+
408
+ case "groups":
409
+ // FIXME: unsupported
410
+ return null;
411
+
412
+ case "rows":
413
+ $new_style .= "border-style: solid none solid none; border-width: 1px; ";
414
+ break;
415
+
416
+ case "cols":
417
+ $new_style .= "border-style: none solid none solid; border-width: 1px; ";
418
+ break;
419
+
420
+ case "all":
421
+ $new_style .= "border-style: solid; border-width: 1px; ";
422
+ break;
423
+
424
+ default:
425
+ // Invalid value
426
+ return null;
427
+ }
428
+
429
+ $cell_list = self::get_cell_list($node);
430
+
431
+ foreach ($cell_list as $cell) {
432
+ $style = $cell->getAttribute(self::$_style_attr);
433
+ $style .= $new_style;
434
+ $cell->setAttribute(self::$_style_attr, $style);
435
+ }
436
+
437
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
438
+ $style .= "; border-collapse: collapse; ";
439
+
440
+ return ltrim($style, "; ");
441
+ }
442
+
443
+ /**
444
+ * @param DOMElement $node
445
+ * @param string $value
446
+ *
447
+ * @return string
448
+ */
449
+ static protected function _set_hr_size(DOMElement $node, $value) {
450
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
451
+ $style .= "; border-width: ".max(0, $value-2)."; ";
452
+ return ltrim($style, "; ");
453
+ }
454
+
455
+ /**
456
+ * @param DOMElement $node
457
+ * @param string $value
458
+ *
459
+ * @return null|string
460
+ */
461
+ static protected function _set_hr_align(DOMElement $node, $value) {
462
+ $style = rtrim($node->getAttribute(self::$_style_attr),";");
463
+ $width = $node->getAttribute("width");
464
+
465
+ if ( $width == "" ) {
466
+ $width = "100%";
467
+ }
468
+
469
+ $remainder = 100 - (double)rtrim($width, "% ");
470
+
471
+ switch ($value) {
472
+ case "left":
473
+ $style .= "; margin-right: $remainder %;";
474
+ break;
475
+
476
+ case "right":
477
+ $style .= "; margin-left: $remainder %;";
478
+ break;
479
+
480
+ case "center":
481
+ $style .= "; margin-left: auto; margin-right: auto;";
482
+ break;
483
+
484
+ default:
485
+ return null;
486
+ }
487
+
488
+ return ltrim($style, "; ");
489
+ }
490
+
491
+ /**
492
+ * @param DOMElement $node
493
+ * @param string $value
494
+ *
495
+ * @return null
496
+ */
497
+ static protected function _set_table_row_align(DOMElement $node, $value) {
498
+ $cell_list = self::get_cell_list($node);
499
+
500
+ foreach ($cell_list as $cell) {
501
+ self::append_style($cell, "; text-align: $value;");
502
+ }
503
+
504
+ return null;
505
+ }
506
+
507
+ /**
508
+ * @param DOMElement $node
509
+ * @param string $value
510
+ *
511
+ * @return null
512
+ */
513
+ static protected function _set_table_row_valign(DOMElement $node, $value) {
514
+ $cell_list = self::get_cell_list($node);
515
+
516
+ foreach ($cell_list as $cell) {
517
+ self::append_style($cell, "; vertical-align: $value;");
518
+ }
519
+
520
+ return null;
521
+ }
522
+
523
+ /**
524
+ * @param DOMElement $node
525
+ * @param string $value
526
+ *
527
+ * @return null
528
+ */
529
+ static protected function _set_table_row_bgcolor(DOMElement $node, $value) {
530
+ $cell_list = self::get_cell_list($node);
531
+ $value = self::_get_valid_color($value);
532
+
533
+ foreach ($cell_list as $cell) {
534
+ self::append_style($cell, "; background-color: $value;");
535
+ }
536
+
537
+ return null;
538
+ }
539
+
540
+ /**
541
+ * @param DOMElement $node
542
+ * @param string $value
543
+ *
544
+ * @return null
545
+ */
546
+ static protected function _set_body_link(DOMElement $node, $value) {
547
+ $a_list = $node->getElementsByTagName("a");
548
+ $value = self::_get_valid_color($value);
549
+
550
+ foreach ($a_list as $a) {
551
+ self::append_style($a, "; color: $value;");
552
+ }
553
+
554
+ return null;
555
+ }
556
+
557
+ /**
558
+ * @param DOMElement $node
559
+ * @param string $value
560
+ *
561
+ * @return null
562
+ */
563
+ static protected function _set_basefont_size(DOMElement $node, $value) {
564
+ // FIXME: ? we don't actually set the font size of anything here, just
565
+ // the base size for later modification by <font> tags.
566
+ self::$_last_basefont_size = $value;
567
+ return null;
568
+ }
569
+
570
+ /**
571
+ * @param DOMElement $node
572
+ * @param string $value
573
+ *
574
+ * @return string
575
+ */
576
+ static protected function _set_font_size(DOMElement $node, $value) {
577
+ $style = $node->getAttribute(self::$_style_attr);
578
+
579
+ if ( $value[0] === "-" || $value[0] === "+" ) {
580
+ $value = self::$_last_basefont_size + (int)$value;
581
+ }
582
+
583
+ if ( isset(self::$_font_size_lookup[$value]) ) {
584
+ $style .= "; font-size: " . self::$_font_size_lookup[$value] . ";";
585
+ }
586
+ else {
587
+ $style .= "; font-size: $value;";
588
+ }
589
+
590
+ return ltrim($style, "; ");
591
+ }
592
+ }
lib/dompdf/include/autoload.inc.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * DOMPDF autoload function
12
+ *
13
+ * If you have an existing autoload function, add a call to this function
14
+ * from your existing __autoload() implementation.
15
+ *
16
+ * @param string $class
17
+ */
18
+ function DOMPDF_autoload($class) {
19
+ $filename = DOMPDF_INC_DIR . "/" . mb_strtolower($class) . ".cls.php";
20
+
21
+ if ( is_file($filename) ) {
22
+ include_once $filename;
23
+ }
24
+ }
25
+
26
+ // If SPL autoload functions are available (PHP >= 5.1.2)
27
+ if ( function_exists("spl_autoload_register") ) {
28
+ $autoload = "DOMPDF_autoload";
29
+ $funcs = spl_autoload_functions();
30
+
31
+ // No functions currently in the stack.
32
+ if ( !DOMPDF_AUTOLOAD_PREPEND || $funcs === false ) {
33
+ spl_autoload_register($autoload);
34
+ }
35
+
36
+ // If PHP >= 5.3 the $prepend argument is available
37
+ else if ( PHP_VERSION_ID >= 50300 ) {
38
+ spl_autoload_register($autoload, true, true);
39
+ }
40
+
41
+ else {
42
+ // Unregister existing autoloaders...
43
+ $compat = (PHP_VERSION_ID <= 50102 && PHP_VERSION_ID >= 50100);
44
+
45
+ foreach ($funcs as $func) {
46
+ if (is_array($func)) {
47
+ // :TRICKY: There are some compatibility issues and some
48
+ // places where we need to error out
49
+ $reflector = new ReflectionMethod($func[0], $func[1]);
50
+ if (!$reflector->isStatic()) {
51
+ throw new Exception('This function is not compatible with non-static object methods due to PHP Bug #44144.');
52
+ }
53
+
54
+ // Suprisingly, spl_autoload_register supports the
55
+ // Class::staticMethod callback format, although call_user_func doesn't
56
+ if ($compat) $func = implode('::', $func);
57
+ }
58
+
59
+ spl_autoload_unregister($func);
60
+ }
61
+
62
+ // Register the new one, thus putting it at the front of the stack...
63
+ spl_autoload_register($autoload);
64
+
65
+ // Now, go back and re-register all of our old ones.
66
+ foreach ($funcs as $func) {
67
+ spl_autoload_register($func);
68
+ }
69
+
70
+ // Be polite and ensure that userland autoload gets retained
71
+ if ( function_exists("__autoload") ) {
72
+ spl_autoload_register("__autoload");
73
+ }
74
+ }
75
+ }
76
+
77
+ else if ( !function_exists("__autoload") ) {
78
+ /**
79
+ * Default __autoload() function
80
+ *
81
+ * @param string $class
82
+ */
83
+ function __autoload($class) {
84
+ DOMPDF_autoload($class);
85
+ }
86
+ }
lib/dompdf/include/block_frame_decorator.cls.php ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates frames for block layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Block_Frame_Decorator extends Frame_Decorator {
16
+ /**
17
+ * Current line index
18
+ *
19
+ * @var int
20
+ */
21
+ protected $_cl;
22
+
23
+ /**
24
+ * The block's line boxes
25
+ *
26
+ * @var Line_Box[]
27
+ */
28
+ protected $_line_boxes;
29
+
30
+ function __construct(Frame $frame, DOMPDF $dompdf) {
31
+ parent::__construct($frame, $dompdf);
32
+
33
+ $this->_line_boxes = array(new Line_Box($this));
34
+ $this->_cl = 0;
35
+ }
36
+
37
+ function reset() {
38
+ parent::reset();
39
+
40
+ $this->_line_boxes = array(new Line_Box($this));
41
+ $this->_cl = 0;
42
+ }
43
+
44
+ /**
45
+ * @return Line_Box
46
+ */
47
+ function get_current_line_box() {
48
+ return $this->_line_boxes[$this->_cl];
49
+ }
50
+
51
+ /**
52
+ * @return integer
53
+ */
54
+ function get_current_line_number() {
55
+ return $this->_cl;
56
+ }
57
+
58
+ /**
59
+ * @return Line_Box[]
60
+ */
61
+ function get_line_boxes() {
62
+ return $this->_line_boxes;
63
+ }
64
+
65
+ /**
66
+ * @param integer $i
67
+ */
68
+ function clear_line($i) {
69
+ if ( isset($this->_line_boxes[$i]) ) {
70
+ unset($this->_line_boxes[$i]);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * @param Frame $frame
76
+ */
77
+ function add_frame_to_line(Frame $frame) {
78
+ if ( !$frame->is_in_flow() ) {
79
+ return;
80
+ }
81
+
82
+ $style = $frame->get_style();
83
+
84
+ $frame->set_containing_line($this->_line_boxes[$this->_cl]);
85
+
86
+ /*
87
+ // Adds a new line after a block, only if certain conditions are met
88
+ if ((($frame instanceof Inline_Frame_Decorator && $frame->get_node()->nodeName !== "br") ||
89
+ $frame instanceof Text_Frame_Decorator && trim($frame->get_text())) &&
90
+ ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" &&
91
+ $this->_line_boxes[$this->_cl]->w > 0 )) {
92
+
93
+ $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
94
+ $this->add_line();
95
+
96
+ // Add each child of the inline frame to the line individually
97
+ foreach ($frame->get_children() as $child)
98
+ $this->add_frame_to_line( $child );
99
+ }
100
+ else*/
101
+
102
+ // Handle inline frames (which are effectively wrappers)
103
+ if ( $frame instanceof Inline_Frame_Decorator ) {
104
+
105
+ // Handle line breaks
106
+ if ( $frame->get_node()->nodeName === "br" ) {
107
+ $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
108
+ $this->add_line(true);
109
+ }
110
+
111
+ return;
112
+ }
113
+
114
+ // Trim leading text if this is an empty line. Kinda a hack to put it here,
115
+ // but what can you do...
116
+ if ( $this->get_current_line_box()->w == 0 &&
117
+ $frame->is_text_node() &&
118
+ !$frame->is_pre() ) {
119
+
120
+ $frame->set_text( ltrim($frame->get_text()) );
121
+ $frame->recalculate_width();
122
+ }
123
+
124
+ $w = $frame->get_margin_width();
125
+
126
+ if ( $w == 0 ) {
127
+ return;
128
+ }
129
+
130
+ // Debugging code:
131
+ /*
132
+ pre_r("\n<h3>Adding frame to line:</h3>");
133
+
134
+ // pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")");
135
+ // pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");
136
+ if ( $frame->is_text_node() )
137
+ pre_r('"'.$frame->get_node()->nodeValue.'"');
138
+
139
+ pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w);
140
+ pre_r("Frame: " . get_class($frame));
141
+ pre_r("Frame width: " . $w);
142
+ pre_r("Frame height: " . $frame->get_margin_height());
143
+ pre_r("Containing block width: " . $this->get_containing_block("w"));
144
+ */
145
+ // End debugging
146
+
147
+ $line = $this->_line_boxes[$this->_cl];
148
+ if ( $line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) {
149
+ $this->add_line();
150
+ }
151
+
152
+ $frame->position();
153
+
154
+ $current_line = $this->_line_boxes[$this->_cl];
155
+ $current_line->add_frame($frame);
156
+
157
+ if ( $frame->is_text_node() ) {
158
+ $current_line->wc += count(preg_split("/\s+/", trim($frame->get_text())));
159
+ }
160
+
161
+ $this->increase_line_width($w);
162
+
163
+ $this->maximize_line_height($frame->get_margin_height(), $frame);
164
+ }
165
+
166
+ function remove_frames_from_line(Frame $frame) {
167
+ // Search backwards through the lines for $frame
168
+ $i = $this->_cl;
169
+ $j = null;
170
+
171
+ while ($i >= 0) {
172
+ if ( ($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false ) {
173
+ break;
174
+ }
175
+
176
+ $i--;
177
+ }
178
+
179
+ if ( $j === false ) {
180
+ return;
181
+ }
182
+
183
+ // Remove $frame and all frames that follow
184
+ while ($j < count($this->_line_boxes[$i]->get_frames())) {
185
+ $frames = $this->_line_boxes[$i]->get_frames();
186
+ $f = $frames[$j];
187
+ $frames[$j] = null;
188
+ unset($frames[$j]);
189
+ $j++;
190
+ $this->_line_boxes[$i]->w -= $f->get_margin_width();
191
+ }
192
+
193
+ // Recalculate the height of the line
194
+ $h = 0;
195
+ foreach ($this->_line_boxes[$i]->get_frames() as $f) {
196
+ $h = max( $h, $f->get_margin_height() );
197
+ }
198
+
199
+ $this->_line_boxes[$i]->h = $h;
200
+
201
+ // Remove all lines that follow
202
+ while ($this->_cl > $i) {
203
+ $this->_line_boxes[ $this->_cl ] = null;
204
+ unset($this->_line_boxes[ $this->_cl ]);
205
+ $this->_cl--;
206
+ }
207
+ }
208
+
209
+ function increase_line_width($w) {
210
+ $this->_line_boxes[ $this->_cl ]->w += $w;
211
+ }
212
+
213
+ function maximize_line_height($val, Frame $frame) {
214
+ if ( $val > $this->_line_boxes[ $this->_cl ]->h ) {
215
+ $this->_line_boxes[ $this->_cl ]->tallest_frame = $frame;
216
+ $this->_line_boxes[ $this->_cl ]->h = $val;
217
+ }
218
+ }
219
+
220
+ function add_line($br = false) {
221
+
222
+ // if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 ||
223
+ // return;
224
+
225
+ $this->_line_boxes[$this->_cl]->br = $br;
226
+ $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h;
227
+
228
+ $new_line = new Line_Box($this, $y);
229
+
230
+ $this->_line_boxes[ ++$this->_cl ] = $new_line;
231
+ }
232
+
233
+ //........................................................................
234
+ }
lib/dompdf/include/block_frame_reflower.cls.php ADDED
@@ -0,0 +1,805 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Reflows block frames
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Block_Frame_Reflower extends Frame_Reflower {
17
+ // Minimum line width to justify, as fraction of available width
18
+ const MIN_JUSTIFY_WIDTH = 0.80;
19
+
20
+ /**
21
+ * @var Block_Frame_Decorator
22
+ */
23
+ protected $_frame;
24
+
25
+ function __construct(Block_Frame_Decorator $frame) { parent::__construct($frame); }
26
+
27
+ /**
28
+ * Calculate the ideal used value for the width property as per:
29
+ * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
30
+ *
31
+ * @param float $width
32
+ * @return array
33
+ */
34
+ protected function _calculate_width($width) {
35
+ $frame = $this->_frame;
36
+ $style = $frame->get_style();
37
+ $w = $frame->get_containing_block("w");
38
+
39
+ if ( $style->position === "fixed" ) {
40
+ $w = $frame->get_parent()->get_containing_block("w");
41
+ }
42
+
43
+ $rm = $style->length_in_pt($style->margin_right, $w);
44
+ $lm = $style->length_in_pt($style->margin_left, $w);
45
+
46
+ $left = $style->length_in_pt($style->left, $w);
47
+ $right = $style->length_in_pt($style->right, $w);
48
+
49
+ // Handle 'auto' values
50
+ $dims = array($style->border_left_width,
51
+ $style->border_right_width,
52
+ $style->padding_left,
53
+ $style->padding_right,
54
+ $width !== "auto" ? $width : 0,
55
+ $rm !== "auto" ? $rm : 0,
56
+ $lm !== "auto" ? $lm : 0);
57
+
58
+ // absolutely positioned boxes take the 'left' and 'right' properties into account
59
+ if ( $frame->is_absolute() ) {
60
+ $absolute = true;
61
+ $dims[] = $left !== "auto" ? $left : 0;
62
+ $dims[] = $right !== "auto" ? $right : 0;
63
+ }
64
+ else {
65
+ $absolute = false;
66
+ }
67
+
68
+ $sum = $style->length_in_pt($dims, $w);
69
+
70
+ // Compare to the containing block
71
+ $diff = $w - $sum;
72
+
73
+ if ( $diff > 0 ) {
74
+
75
+ if ( $absolute ) {
76
+
77
+ // resolve auto properties: see
78
+ // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
79
+
80
+ if ( $width === "auto" && $left === "auto" && $right === "auto" ) {
81
+
82
+ if ( $lm === "auto" ) $lm = 0;
83
+ if ( $rm === "auto" ) $rm = 0;
84
+
85
+ // Technically, the width should be "shrink-to-fit" i.e. based on the
86
+ // preferred width of the content... a little too costly here as a
87
+ // special case. Just get the width to take up the slack:
88
+ $left = 0;
89
+ $right = 0;
90
+ $width = $diff;
91
+ }
92
+ else if ( $width === "auto" ) {
93
+
94
+ if ( $lm === "auto" ) $lm = 0;
95
+ if ( $rm === "auto" ) $rm = 0;
96
+ if ( $left === "auto" ) $left = 0;
97
+ if ( $right === "auto" ) $right = 0;
98
+
99
+ $width = $diff;
100
+ }
101
+ else if ( $left === "auto" ) {
102
+
103
+ if ( $lm === "auto" ) $lm = 0;
104
+ if ( $rm === "auto" ) $rm = 0;
105
+ if ( $right === "auto" ) $right = 0;
106
+
107
+ $left = $diff;
108
+ }
109
+ else if ( $right === "auto" ) {
110
+
111
+ if ( $lm === "auto" ) $lm = 0;
112
+ if ( $rm === "auto" ) $rm = 0;
113
+
114
+ $right = $diff;
115
+ }
116
+
117
+ }
118
+ else {
119
+
120
+ // Find auto properties and get them to take up the slack
121
+ if ( $width === "auto" ) {
122
+ $width = $diff;
123
+ }
124
+ else if ( $lm === "auto" && $rm === "auto" ) {
125
+ $lm = $rm = round($diff / 2);
126
+ }
127
+ else if ( $lm === "auto" ) {
128
+ $lm = $diff;
129
+ }
130
+ else if ( $rm === "auto" ) {
131
+ $rm = $diff;
132
+ }
133
+ }
134
+
135
+ }
136
+ else if ($diff < 0) {
137
+
138
+ // We are over constrained--set margin-right to the difference
139
+ $rm = $diff;
140
+
141
+ }
142
+
143
+ return array(
144
+ "width" => $width,
145
+ "margin_left" => $lm,
146
+ "margin_right" => $rm,
147
+ "left" => $left,
148
+ "right" => $right,
149
+ );
150
+ }
151
+
152
+ /**
153
+ * Call the above function, but resolve max/min widths
154
+ *
155
+ * @throws DOMPDF_Exception
156
+ * @return array
157
+ */
158
+ protected function _calculate_restricted_width() {
159
+ $frame = $this->_frame;
160
+ $style = $frame->get_style();
161
+ $cb = $frame->get_containing_block();
162
+
163
+ if ( $style->position === "fixed" ) {
164
+ $cb = $frame->get_root()->get_containing_block();
165
+ }
166
+
167
+ //if ( $style->position === "absolute" )
168
+ // $cb = $frame->find_positionned_parent()->get_containing_block();
169
+
170
+ if ( !isset($cb["w"]) ) {
171
+ throw new DOMPDF_Exception("Box property calculation requires containing block width");
172
+ }
173
+
174
+ // Treat width 100% as auto
175
+ if ( $style->width === "100%" ) {
176
+ $width = "auto";
177
+ }
178
+ else {
179
+ $width = $style->length_in_pt($style->width, $cb["w"]);
180
+ }
181
+
182
+ extract($this->_calculate_width($width));
183
+
184
+ // Handle min/max width
185
+ $min_width = $style->length_in_pt($style->min_width, $cb["w"]);
186
+ $max_width = $style->length_in_pt($style->max_width, $cb["w"]);
187
+
188
+ if ( $max_width !== "none" && $min_width > $max_width ) {
189
+ list($max_width, $min_width) = array($min_width, $max_width);
190
+ }
191
+
192
+ if ( $max_width !== "none" && $width > $max_width ) {
193
+ extract($this->_calculate_width($max_width));
194
+ }
195
+
196
+ if ( $width < $min_width ) {
197
+ extract($this->_calculate_width($min_width));
198
+ }
199
+
200
+ return array($width, $margin_left, $margin_right, $left, $right);
201
+ }
202
+
203
+ /**
204
+ * Determine the unrestricted height of content within the block
205
+ * not by adding each line's height, but by getting the last line's position.
206
+ * This because lines could have been pushed lower by a clearing element.
207
+ *
208
+ * @return float
209
+ */
210
+ protected function _calculate_content_height() {
211
+ $lines = $this->_frame->get_line_boxes();
212
+ $height = 0;
213
+
214
+ foreach ($lines as $line) {
215
+ $height += $line->h;
216
+ }
217
+
218
+ /*
219
+ $first_line = reset($lines);
220
+ $last_line = end($lines);
221
+ $height2 = $last_line->y + $last_line->h - $first_line->y;
222
+ */
223
+
224
+ return $height;
225
+ }
226
+
227
+ /**
228
+ * Determine the frame's restricted height
229
+ *
230
+ * @return array
231
+ */
232
+ protected function _calculate_restricted_height() {
233
+ $frame = $this->_frame;
234
+ $style = $frame->get_style();
235
+ $content_height = $this->_calculate_content_height();
236
+ $cb = $frame->get_containing_block();
237
+
238
+ $height = $style->length_in_pt($style->height, $cb["h"]);
239
+
240
+ $top = $style->length_in_pt($style->top, $cb["h"]);
241
+ $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
242
+
243
+ $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]);
244
+ $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
245
+
246
+ if ( $frame->is_absolute() ) {
247
+
248
+ // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
249
+
250
+ $dims = array($top !== "auto" ? $top : 0,
251
+ $style->margin_top !== "auto" ? $style->margin_top : 0,
252
+ $style->padding_top,
253
+ $style->border_top_width,
254
+ $height !== "auto" ? $height : 0,
255
+ $style->border_bottom_width,
256
+ $style->padding_bottom,
257
+ $style->margin_bottom !== "auto" ? $style->margin_bottom : 0,
258
+ $bottom !== "auto" ? $bottom : 0);
259
+
260
+ $sum = $style->length_in_pt($dims, $cb["h"]);
261
+
262
+ $diff = $cb["h"] - $sum;
263
+
264
+ if ( $diff > 0 ) {
265
+
266
+ if ( $height === "auto" && $top === "auto" && $bottom === "auto" ) {
267
+
268
+ if ( $margin_top === "auto" ) $margin_top = 0;
269
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
270
+
271
+ $height = $diff;
272
+ }
273
+ else if ( $height === "auto" && $top === "auto" ) {
274
+
275
+ if ( $margin_top === "auto" ) $margin_top = 0;
276
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
277
+
278
+ $height = $content_height;
279
+ $top = $diff - $content_height;
280
+ }
281
+ else if ( $height === "auto" && $bottom === "auto" ) {
282
+
283
+ if ( $margin_top === "auto" ) $margin_top = 0;
284
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
285
+
286
+ $height = $content_height;
287
+ $bottom = $diff - $content_height;
288
+ }
289
+ else if ( $top === "auto" && $bottom === "auto" ) {
290
+
291
+ if ( $margin_top === "auto" ) $margin_top = 0;
292
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
293
+
294
+ $bottom = $diff;
295
+ }
296
+ else if ( $top === "auto" ) {
297
+
298
+ if ( $margin_top === "auto" ) $margin_top = 0;
299
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
300
+
301
+ $top = $diff;
302
+ }
303
+ else if ( $height === "auto" ) {
304
+
305
+ if ( $margin_top === "auto" ) $margin_top = 0;
306
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
307
+
308
+ $height = $diff;
309
+ }
310
+ else if ( $bottom === "auto" ) {
311
+
312
+ if ( $margin_top === "auto" ) $margin_top = 0;
313
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
314
+
315
+ $bottom = $diff;
316
+ }
317
+ else {
318
+
319
+ if ( $style->overflow === "visible" ) {
320
+ // set all autos to zero
321
+ if ( $margin_top === "auto" ) $margin_top = 0;
322
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
323
+ if ( $top === "auto" ) $top = 0;
324
+ if ( $bottom === "auto" ) $bottom = 0;
325
+ if ( $height === "auto" ) $height = $content_height;
326
+ }
327
+
328
+ // FIXME: overflow hidden
329
+ }
330
+
331
+ }
332
+
333
+ }
334
+ else {
335
+
336
+ // Expand the height if overflow is visible
337
+ if ( $height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) {
338
+ $height = $content_height;
339
+ }
340
+
341
+ // FIXME: this should probably be moved to a seperate function as per
342
+ // _calculate_restricted_width
343
+
344
+ // Only handle min/max height if the height is independent of the frame's content
345
+ if ( !($style->overflow === "visible" ||
346
+ ($style->overflow === "hidden" && $height === "auto")) ) {
347
+
348
+ $min_height = $style->min_height;
349
+ $max_height = $style->max_height;
350
+
351
+ if ( isset($cb["h"]) ) {
352
+ $min_height = $style->length_in_pt($min_height, $cb["h"]);
353
+ $max_height = $style->length_in_pt($max_height, $cb["h"]);
354
+
355
+ }
356
+ else if ( isset($cb["w"]) ) {
357
+
358
+ if ( mb_strpos($min_height, "%") !== false ) {
359
+ $min_height = 0;
360
+ }
361
+ else {
362
+ $min_height = $style->length_in_pt($min_height, $cb["w"]);
363
+ }
364
+
365
+ if ( mb_strpos($max_height, "%") !== false ) {
366
+ $max_height = "none";
367
+ }
368
+ else {
369
+ $max_height = $style->length_in_pt($max_height, $cb["w"]);
370
+ }
371
+ }
372
+
373
+ if ( $max_height !== "none" && $min_height > $max_height ) {
374
+ // Swap 'em
375
+ list($max_height, $min_height) = array($min_height, $max_height);
376
+ }
377
+
378
+ if ( $max_height !== "none" && $height > $max_height ) {
379
+ $height = $max_height;
380
+ }
381
+
382
+ if ( $height < $min_height ) {
383
+ $height = $min_height;
384
+ }
385
+ }
386
+
387
+ }
388
+
389
+ return array($height, $margin_top, $margin_bottom, $top, $bottom);
390
+
391
+ }
392
+
393
+ /**
394
+ * Adjust the justification of each of our lines.
395
+ * http://www.w3.org/TR/CSS21/text.html#propdef-text-align
396
+ */
397
+ protected function _text_align() {
398
+ $style = $this->_frame->get_style();
399
+ $w = $this->_frame->get_containing_block("w");
400
+ $width = $style->length_in_pt($style->width, $w);
401
+
402
+ switch ($style->text_align) {
403
+ default:
404
+ case "left":
405
+ foreach ($this->_frame->get_line_boxes() as $line) {
406
+ if ( !$line->left ) {
407
+ continue;
408
+ }
409
+
410
+ foreach($line->get_frames() as $frame) {
411
+ if ( $frame instanceof Block_Frame_Decorator) {
412
+ continue;
413
+ }
414
+ $frame->set_position( $frame->get_position("x") + $line->left );
415
+ }
416
+ }
417
+ return;
418
+
419
+ case "right":
420
+ foreach ($this->_frame->get_line_boxes() as $line) {
421
+ // Move each child over by $dx
422
+ $dx = $width - $line->w - $line->right;
423
+
424
+ foreach($line->get_frames() as $frame) {
425
+ // Block frames are not aligned by text-align
426
+ if ($frame instanceof Block_Frame_Decorator) {
427
+ continue;
428
+ }
429
+
430
+ $frame->set_position( $frame->get_position("x") + $dx );
431
+ }
432
+ }
433
+ break;
434
+
435
+
436
+ case "justify":
437
+ // We justify all lines except the last one
438
+ $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards)
439
+ array_pop($lines);
440
+
441
+ foreach($lines as $i => $line) {
442
+ if ( $line->br ) {
443
+ unset($lines[$i]);
444
+ }
445
+ }
446
+
447
+ // One space character's width. Will be used to get a more accurate spacing
448
+ $space_width = Font_Metrics::get_text_width(" ", $style->font_family, $style->font_size);
449
+
450
+ foreach ($lines as $line) {
451
+ if ( $line->left ) {
452
+ foreach ( $line->get_frames() as $frame ) {
453
+ if ( !$frame instanceof Text_Frame_Decorator ) {
454
+ continue;
455
+ }
456
+
457
+ $frame->set_position( $frame->get_position("x") + $line->left );
458
+ }
459
+ }
460
+
461
+ // Only set the spacing if the line is long enough. This is really
462
+ // just an aesthetic choice ;)
463
+ //if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) {
464
+
465
+ // Set the spacing for each child
466
+ if ( $line->wc > 1 ) {
467
+ $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1);
468
+ }
469
+ else {
470
+ $spacing = 0;
471
+ }
472
+
473
+ $dx = 0;
474
+ foreach($line->get_frames() as $frame) {
475
+ if ( !$frame instanceof Text_Frame_Decorator ) {
476
+ continue;
477
+ }
478
+
479
+ $text = $frame->get_text();
480
+ $spaces = mb_substr_count($text, " ");
481
+
482
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
483
+ $_spacing = $spacing + $char_spacing;
484
+
485
+ $frame->set_position( $frame->get_position("x") + $dx );
486
+ $frame->set_text_spacing($_spacing);
487
+
488
+ $dx += $spaces * $_spacing;
489
+ }
490
+
491
+ // The line (should) now occupy the entire width
492
+ $line->w = $width;
493
+
494
+ //}
495
+ }
496
+ break;
497
+
498
+ case "center":
499
+ case "centre":
500
+ foreach ($this->_frame->get_line_boxes() as $line) {
501
+ // Centre each line by moving each frame in the line by:
502
+ $dx = ($width + $line->left - $line->w - $line->right ) / 2;
503
+
504
+ foreach ($line->get_frames() as $frame) {
505
+ // Block frames are not aligned by text-align
506
+ if ($frame instanceof Block_Frame_Decorator) {
507
+ continue;
508
+ }
509
+
510
+ $frame->set_position( $frame->get_position("x") + $dx );
511
+ }
512
+ }
513
+ break;
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Align inline children vertically.
519
+ * Aligns each child vertically after each line is reflowed
520
+ */
521
+ function vertical_align() {
522
+
523
+ $canvas = null;
524
+
525
+ foreach ( $this->_frame->get_line_boxes() as $line ) {
526
+
527
+ $height = $line->h;
528
+
529
+ foreach ( $line->get_frames() as $frame ) {
530
+ $style = $frame->get_style();
531
+
532
+ if ( $style->display !== "inline" ) {
533
+ continue;
534
+ }
535
+
536
+ $align = $frame->get_parent()->get_style()->vertical_align;
537
+
538
+ if ( !isset($canvas) ) {
539
+ $canvas = $frame->get_root()->get_dompdf()->get_canvas();
540
+ }
541
+
542
+ $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size);
543
+ $y_offset = 0;
544
+
545
+ switch ($align) {
546
+ case "baseline":
547
+ $y_offset = $height*0.8 - $baseline; // The 0.8 ratio is arbitrary until we find it's meaning
548
+ break;
549
+
550
+ case "middle":
551
+ $y_offset = ($height*0.8 - $baseline) / 2;
552
+ break;
553
+
554
+ case "sub":
555
+ $y_offset = 0.3 * $height;
556
+ break;
557
+
558
+ case "super":
559
+ $y_offset = -0.2 * $height;
560
+ break;
561
+
562
+ case "text-top":
563
+ case "top": // Not strictly accurate, but good enough for now
564
+ break;
565
+
566
+ case "text-bottom":
567
+ case "bottom":
568
+ $y_offset = $height*0.8 - $baseline;
569
+ break;
570
+ }
571
+
572
+ if ( $y_offset ) {
573
+ $frame->move(0, $y_offset);
574
+ }
575
+ }
576
+ }
577
+ }
578
+
579
+ /**
580
+ * @param Frame $child
581
+ */
582
+ function process_clear(Frame $child){
583
+ $enable_css_float = $this->get_dompdf()->get_option("enable_css_float");
584
+ if ( !$enable_css_float ) {
585
+ return;
586
+ }
587
+
588
+ $child_style = $child->get_style();
589
+ $root = $this->_frame->get_root();
590
+
591
+ // Handle "clear"
592
+ if ( $child_style->clear !== "none" ) {
593
+ $lowest_y = $root->get_lowest_float_offset($child);
594
+
595
+ // If a float is still applying, we handle it
596
+ if ( $lowest_y ) {
597
+ if ( $child->is_in_flow() ) {
598
+ $line_box = $this->_frame->get_current_line_box();
599
+ $line_box->y = $lowest_y + $child->get_margin_height();
600
+ $line_box->left = 0;
601
+ $line_box->right = 0;
602
+ }
603
+
604
+ $child->move(0, $lowest_y - $child->get_position("y"));
605
+ }
606
+ }
607
+ }
608
+
609
+ /**
610
+ * @param Frame $child
611
+ * @param float $cb_x
612
+ * @param float $cb_w
613
+ */
614
+ function process_float(Frame $child, $cb_x, $cb_w){
615
+ $enable_css_float = $this->_frame->get_dompdf()->get_option("enable_css_float");
616
+ if ( !$enable_css_float ) {
617
+ return;
618
+ }
619
+
620
+ $child_style = $child->get_style();
621
+ $root = $this->_frame->get_root();
622
+
623
+ // Handle "float"
624
+ if ( $child_style->float !== "none" ) {
625
+ $root->add_floating_frame($child);
626
+
627
+ // Remove next frame's beginning whitespace
628
+ $next = $child->get_next_sibling();
629
+ if ( $next && $next instanceof Text_Frame_Decorator) {
630
+ $next->set_text(ltrim($next->get_text()));
631
+ }
632
+
633
+ $line_box = $this->_frame->get_current_line_box();
634
+ list($old_x, $old_y) = $child->get_position();
635
+
636
+ $float_x = $cb_x;
637
+ $float_y = $old_y;
638
+ $float_w = $child->get_margin_width();
639
+
640
+ if ( $child_style->clear === "none" ) {
641
+ switch( $child_style->float ) {
642
+ case "left":
643
+ $float_x += $line_box->left;
644
+ break;
645
+ case "right":
646
+ $float_x += ($cb_w - $line_box->right - $float_w);
647
+ break;
648
+ }
649
+ }
650
+ else {
651
+ if ( $child_style->float === "right" ) {
652
+ $float_x += ($cb_w - $float_w);
653
+ }
654
+ }
655
+
656
+ if ( $cb_w < $float_x + $float_w - $old_x ) {
657
+ // TODO handle when floating elements don't fit
658
+ }
659
+
660
+ $line_box->get_float_offsets();
661
+
662
+ if ( $child->_float_next_line ) {
663
+ $float_y += $line_box->h;
664
+ }
665
+
666
+ $child->set_position($float_x, $float_y);
667
+ $child->move($float_x - $old_x, $float_y - $old_y, true);
668
+ }
669
+ }
670
+
671
+ /**
672
+ * @param Frame_Decorator $block
673
+ */
674
+ function reflow(Block_Frame_Decorator $block = null) {
675
+
676
+ // Check if a page break is forced
677
+ $page = $this->_frame->get_root();
678
+ $page->check_forced_page_break($this->_frame);
679
+
680
+ // Bail if the page is full
681
+ if ( $page->is_full() ) {
682
+ return;
683
+ }
684
+
685
+ // Generated content
686
+ $this->_set_content();
687
+
688
+ // Collapse margins if required
689
+ $this->_collapse_margins();
690
+
691
+ $style = $this->_frame->get_style();
692
+ $cb = $this->_frame->get_containing_block();
693
+
694
+ if ( $style->position === "fixed" ) {
695
+ $cb = $this->_frame->get_root()->get_containing_block();
696
+ }
697
+
698
+ // Determine the constraints imposed by this frame: calculate the width
699
+ // of the content area:
700
+ list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width();
701
+
702
+ // Store the calculated properties
703
+ $style->width = $w . "pt";
704
+ $style->margin_left = $left_margin."pt";
705
+ $style->margin_right = $right_margin."pt";
706
+ $style->left = $left ."pt";
707
+ $style->right = $right . "pt";
708
+
709
+ // Update the position
710
+ $this->_frame->position();
711
+ list($x, $y) = $this->_frame->get_position();
712
+
713
+ // Adjust the first line based on the text-indent property
714
+ $indent = $style->length_in_pt($style->text_indent, $cb["w"]);
715
+ $this->_frame->increase_line_width($indent);
716
+
717
+ // Determine the content edge
718
+ $top = $style->length_in_pt(array($style->margin_top,
719
+ $style->padding_top,
720
+ $style->border_top_width), $cb["h"]);
721
+
722
+ $bottom = $style->length_in_pt(array($style->border_bottom_width,
723
+ $style->margin_bottom,
724
+ $style->padding_bottom), $cb["h"]);
725
+
726
+ $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width,
727
+ $style->padding_left), $cb["w"]);
728
+
729
+ $cb_y = $y + $top;
730
+
731
+ $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
732
+
733
+ // Set the y position of the first line in this block
734
+ $line_box = $this->_frame->get_current_line_box();
735
+ $line_box->y = $cb_y;
736
+ $line_box->get_float_offsets();
737
+
738
+ // Set the containing blocks and reflow each child
739
+ foreach ( $this->_frame->get_children() as $child ) {
740
+
741
+ // Bail out if the page is full
742
+ if ( $page->is_full() ) {
743
+ break;
744
+ }
745
+
746
+ $child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
747
+
748
+ $this->process_clear($child);
749
+
750
+ $child->reflow($this->_frame);
751
+
752
+ // Don't add the child to the line if a page break has occurred
753
+ if ( $page->check_page_break($child) ) {
754
+ break;
755
+ }
756
+
757
+ $this->process_float($child, $cb_x, $w);
758
+ }
759
+
760
+ // Determine our height
761
+ list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
762
+ $style->height = $height;
763
+ $style->margin_top = $margin_top;
764
+ $style->margin_bottom = $margin_bottom;
765
+ $style->top = $top;
766
+ $style->bottom = $bottom;
767
+
768
+ $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto"));
769
+
770
+ // Absolute positioning measurement
771
+ if ( $needs_reposition ) {
772
+ $orig_style = $this->_frame->get_original_style();
773
+ if ( $orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto") ) {
774
+ $width = 0;
775
+ foreach ($this->_frame->get_line_boxes() as $line) {
776
+ $width = max($line->w, $width);
777
+ }
778
+ $style->width = $width;
779
+ }
780
+
781
+ $style->left = $orig_style->left;
782
+ $style->right = $orig_style->right;
783
+ }
784
+
785
+ $this->_text_align();
786
+ $this->vertical_align();
787
+
788
+ // Absolute positioning
789
+ if ( $needs_reposition ) {
790
+ list($x, $y) = $this->_frame->get_position();
791
+ $this->_frame->position();
792
+ list($new_x, $new_y) = $this->_frame->get_position();
793
+ $this->_frame->move($new_x-$x, $new_y-$y, true);
794
+ }
795
+
796
+ if ( $block && $this->_frame->is_in_flow() ) {
797
+ $block->add_frame_to_line($this->_frame);
798
+
799
+ // May be inline-block
800
+ if ( $style->display === "block" ) {
801
+ $block->add_line();
802
+ }
803
+ }
804
+ }
805
+ }
lib/dompdf/include/block_positioner.cls.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions block frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Block_Positioner extends Positioner {
16
+
17
+
18
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
19
+
20
+ //........................................................................
21
+
22
+ function position() {
23
+ $frame = $this->_frame;
24
+ $style = $frame->get_style();
25
+ $cb = $frame->get_containing_block();
26
+ $p = $frame->find_block_parent();
27
+
28
+ if ( $p ) {
29
+ $float = $style->float;
30
+
31
+ $enable_css_float = $frame->get_dompdf()->get_option("enable_css_float");
32
+ if ( !$enable_css_float || !$float || $float === "none" ) {
33
+ $p->add_line(true);
34
+ }
35
+ $y = $p->get_current_line_box()->y;
36
+
37
+ }
38
+ else {
39
+ $y = $cb["y"];
40
+ }
41
+
42
+ $x = $cb["x"];
43
+
44
+ // Relative positionning
45
+ if ( $style->position === "relative" ) {
46
+ $top = $style->length_in_pt($style->top, $cb["h"]);
47
+ //$right = $style->length_in_pt($style->right, $cb["w"]);
48
+ //$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
49
+ $left = $style->length_in_pt($style->left, $cb["w"]);
50
+
51
+ $x += $left;
52
+ $y += $top;
53
+ }
54
+
55
+ $frame->set_position($x, $y);
56
+ }
57
+ }
lib/dompdf/include/block_renderer.cls.php ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Renders block frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Block_Renderer extends Abstract_Renderer {
16
+
17
+ //........................................................................
18
+
19
+ function render(Frame $frame) {
20
+ $style = $frame->get_style();
21
+ $node = $frame->get_node();
22
+
23
+ list($x, $y, $w, $h) = $frame->get_border_box();
24
+
25
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
26
+
27
+ if ( $node->nodeName === "body" ) {
28
+ $h = $frame->get_containing_block("h") - $style->length_in_pt(array(
29
+ $style->margin_top,
30
+ $style->border_top_width,
31
+ $style->border_bottom_width,
32
+ $style->margin_bottom),
33
+ $style->width);
34
+ }
35
+
36
+ // Handle anchors & links
37
+ if ( $node->nodeName === "a" && $href = $node->getAttribute("href") ) {
38
+ $this->_canvas->add_link($href, $x, $y, $w, $h);
39
+ }
40
+
41
+ // Draw our background, border and content
42
+ list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
43
+
44
+ if ( $tl + $tr + $br + $bl > 0 ) {
45
+ $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
46
+ }
47
+
48
+ if ( ($bg = $style->background_color) !== "transparent" ) {
49
+ $this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg );
50
+ }
51
+
52
+ if ( ($url = $style->background_image) && $url !== "none" ) {
53
+ $this->_background_image($url, $x, $y, $w, $h, $style);
54
+ }
55
+
56
+ if ( $tl + $tr + $br + $bl > 0 ) {
57
+ $this->_canvas->clipping_end();
58
+ }
59
+
60
+ $border_box = array($x, $y, $w, $h);
61
+ $this->_render_border($frame, $border_box);
62
+ $this->_render_outline($frame, $border_box);
63
+
64
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
65
+ $this->_debug_layout($frame->get_border_box(), "red");
66
+ if (DEBUG_LAYOUT_PADDINGBOX) {
67
+ $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
68
+ }
69
+ }
70
+
71
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) {
72
+ foreach ($frame->get_decorator()->get_line_boxes() as $line) {
73
+ $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
74
+ }
75
+ }
76
+ }
77
+
78
+ protected function _render_border(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") {
79
+ $style = $frame->get_style();
80
+ $bp = $style->get_border_properties();
81
+
82
+ if ( empty($border_box) ) {
83
+ $border_box = $frame->get_border_box();
84
+ }
85
+
86
+ // find the radius
87
+ $radius = $style->get_computed_border_radius($border_box[2], $border_box[3]); // w, h
88
+
89
+ // Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle
90
+ if (
91
+ in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) &&
92
+ $bp["top"] == $bp["right"] &&
93
+ $bp["right"] == $bp["bottom"] &&
94
+ $bp["bottom"] == $bp["left"] &&
95
+ array_sum($radius) == 0
96
+ ) {
97
+ $props = $bp["top"];
98
+ if ( $props["color"] === "transparent" || $props["width"] <= 0 ) return;
99
+
100
+ list($x, $y, $w, $h) = $border_box;
101
+ $width = $style->length_in_pt($props["width"]);
102
+ $pattern = $this->_get_dash_pattern($props["style"], $width);
103
+ $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width, $pattern);
104
+ return;
105
+ }
106
+
107
+ // Do it the long way
108
+ $widths = array($style->length_in_pt($bp["top"]["width"]),
109
+ $style->length_in_pt($bp["right"]["width"]),
110
+ $style->length_in_pt($bp["bottom"]["width"]),
111
+ $style->length_in_pt($bp["left"]["width"]));
112
+
113
+ foreach ($bp as $side => $props) {
114
+ list($x, $y, $w, $h) = $border_box;
115
+ $length = 0;
116
+ $r1 = 0;
117
+ $r2 = 0;
118
+
119
+ if ( !$props["style"] ||
120
+ $props["style"] === "none" ||
121
+ $props["width"] <= 0 ||
122
+ $props["color"] == "transparent" )
123
+ continue;
124
+
125
+ switch($side) {
126
+ case "top":
127
+ $length = $w;
128
+ $r1 = $radius["top-left"];
129
+ $r2 = $radius["top-right"];
130
+ break;
131
+
132
+ case "bottom":
133
+ $length = $w;
134
+ $y += $h;
135
+ $r1 = $radius["bottom-left"];
136
+ $r2 = $radius["bottom-right"];
137
+ break;
138
+
139
+ case "left":
140
+ $length = $h;
141
+ $r1 = $radius["top-left"];
142
+ $r2 = $radius["bottom-left"];
143
+ break;
144
+
145
+ case "right":
146
+ $length = $h;
147
+ $x += $w;
148
+ $r1 = $radius["top-right"];
149
+ $r2 = $radius["bottom-right"];
150
+ break;
151
+ default:
152
+ break;
153
+ }
154
+ $method = "_border_" . $props["style"];
155
+
156
+ // draw rounded corners
157
+ $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2);
158
+ }
159
+ }
160
+
161
+ protected function _render_outline(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") {
162
+ $style = $frame->get_style();
163
+
164
+ $props = array(
165
+ "width" => $style->outline_width,
166
+ "style" => $style->outline_style,
167
+ "color" => $style->outline_color,
168
+ );
169
+
170
+ if ( !$props["style"] || $props["style"] === "none" || $props["width"] <= 0 )
171
+ return;
172
+
173
+ if ( empty($border_box) ) {
174
+ $border_box = $frame->get_border_box();
175
+ }
176
+
177
+ $offset = $style->length_in_pt($props["width"]);
178
+ $pattern = $this->_get_dash_pattern($props["style"], $offset);
179
+
180
+ // If the outline style is "solid" we'd better draw a rectangle
181
+ if ( in_array($props["style"], array("solid", "dashed", "dotted")) ) {
182
+ $border_box[0] -= $offset / 2;
183
+ $border_box[1] -= $offset / 2;
184
+ $border_box[2] += $offset;
185
+ $border_box[3] += $offset;
186
+
187
+ list($x, $y, $w, $h) = $border_box;
188
+ $this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern);
189
+ return;
190
+ }
191
+
192
+ $border_box[0] -= $offset;
193
+ $border_box[1] -= $offset;
194
+ $border_box[2] += $offset * 2;
195
+ $border_box[3] += $offset * 2;
196
+
197
+ $method = "_border_" . $props["style"];
198
+ $widths = array_fill(0, 4, $props["width"]);
199
+ $sides = array("top", "right", "left", "bottom");
200
+ $length = 0;
201
+
202
+ foreach ($sides as $side) {
203
+ list($x, $y, $w, $h) = $border_box;
204
+
205
+ switch($side) {
206
+ case "top":
207
+ $length = $w;
208
+ break;
209
+
210
+ case "bottom":
211
+ $length = $w;
212
+ $y += $h;
213
+ break;
214
+
215
+ case "left":
216
+ $length = $h;
217
+ break;
218
+
219
+ case "right":
220
+ $length = $h;
221
+ $x += $w;
222
+ break;
223
+ default:
224
+ break;
225
+ }
226
+
227
+ $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style);
228
+ }
229
+ }
230
+ }
lib/dompdf/include/cached_pdf_decorator.cls.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Caching canvas implementation
11
+ *
12
+ * Each rendered page is serialized and stored in the {@link Page_Cache}.
13
+ * This is useful for static forms/pages that do not need to be re-rendered
14
+ * all the time.
15
+ *
16
+ * This class decorates normal CPDF_Adapters. It is currently completely
17
+ * experimental.
18
+ *
19
+ * @access private
20
+ * @package dompdf
21
+ */
22
+ class Cached_PDF_Decorator extends CPDF_Adapter implements Canvas {
23
+ /**
24
+ * @var CPDF_Adapter
25
+ */
26
+ protected $_pdf;
27
+ protected $_cache_id;
28
+ protected $_current_page_id;
29
+ protected $_fonts; // fonts used in this document
30
+
31
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
32
+ $this->_fonts = array();
33
+ }
34
+
35
+ /**
36
+ * Must be called after constructor
37
+ *
38
+ * @param int $cache_id
39
+ * @param CPDF_Adapter $pdf
40
+ */
41
+ function init($cache_id, CPDF_Adapter $pdf) {
42
+ $this->_cache_id = $cache_id;
43
+ $this->_pdf = $pdf;
44
+ $this->_current_page_id = $this->_pdf->open_object();
45
+ }
46
+
47
+ //........................................................................
48
+
49
+ function get_cpdf() { return $this->_pdf->get_cpdf(); }
50
+
51
+ function open_object() { $this->_pdf->open_object(); }
52
+ function reopen_object($object) { $this->_pdf->reopen_object($object); }
53
+
54
+ function close_object() { $this->_pdf->close_object(); }
55
+
56
+ function add_object($object, $where = 'all') { $this->_pdf->add_object($object, $where); }
57
+
58
+ function serialize_object($id) { $this->_pdf->serialize_object($id); }
59
+
60
+ function reopen_serialized_object($obj) { $this->_pdf->reopen_serialized_object($obj); }
61
+
62
+ //........................................................................
63
+
64
+ function get_width() { return $this->_pdf->get_width(); }
65
+ function get_height() { return $this->_pdf->get_height(); }
66
+ function get_page_number() { return $this->_pdf->get_page_number(); }
67
+ function get_page_count() { return $this->_pdf->get_page_count(); }
68
+
69
+ function set_page_number($num) { $this->_pdf->set_page_number($num); }
70
+ function set_page_count($count) { $this->_pdf->set_page_count($count); }
71
+
72
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
73
+ $this->_pdf->line($x1, $y1, $x2, $y2, $color, $width, $style);
74
+ }
75
+
76
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
77
+ $this->_pdf->rectangle($x1, $y1, $w, $h, $color, $width, $style);
78
+ }
79
+
80
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
81
+ $this->_pdf->filled_rectangle($x1, $y1, $w, $h, $color);
82
+ }
83
+
84
+ function polygon($points, $color, $width = null, $style = array(), $fill = false) {
85
+ $this->_pdf->polygon($points, $color, $width, $style, $fill);
86
+ }
87
+
88
+ function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
89
+ $this->_pdf->circle($x, $y, $r1, $color, $width, $style, $fill);
90
+ }
91
+
92
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
93
+ $this->_pdf->image($img_url, $x, $y, $w, $h, $resolution);
94
+ }
95
+
96
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
97
+ $this->_fonts[$font] = true;
98
+ $this->_pdf->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
99
+ }
100
+
101
+ function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
102
+
103
+ // We want to remove this from cached pages since it may not be correct
104
+ $this->_pdf->close_object();
105
+ $this->_pdf->page_text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
106
+ $this->_pdf->reopen_object($this->_current_page_id);
107
+ }
108
+
109
+ function page_script($script, $type = 'text/php') {
110
+
111
+ // We want to remove this from cached pages since it may not be correct
112
+ $this->_pdf->close_object();
113
+ $this->_pdf->page_script($script, $type);
114
+ $this->_pdf->reopen_object($this->_current_page_id);
115
+ }
116
+
117
+ function new_page() {
118
+ $this->_pdf->close_object();
119
+
120
+ // Add the object to the current page
121
+ $this->_pdf->add_object($this->_current_page_id, "add");
122
+ $this->_pdf->new_page();
123
+
124
+ Page_Cache::store_page($this->_cache_id,
125
+ $this->_pdf->get_page_number() - 1,
126
+ $this->_pdf->serialize_object($this->_current_page_id));
127
+
128
+ $this->_current_page_id = $this->_pdf->open_object();
129
+ return $this->_current_page_id;
130
+ }
131
+
132
+ function stream($filename, $options = null) {
133
+ // Store the last page in the page cache
134
+ if ( !is_null($this->_current_page_id) ) {
135
+ $this->_pdf->close_object();
136
+ $this->_pdf->add_object($this->_current_page_id, "add");
137
+ Page_Cache::store_page($this->_cache_id,
138
+ $this->_pdf->get_page_number(),
139
+ $this->_pdf->serialize_object($this->_current_page_id));
140
+ Page_Cache::store_fonts($this->_cache_id, $this->_fonts);
141
+ $this->_current_page_id = null;
142
+ }
143
+
144
+ $this->_pdf->stream($filename);
145
+
146
+ }
147
+
148
+ function output($options = null) {
149
+ // Store the last page in the page cache
150
+ if ( !is_null($this->_current_page_id) ) {
151
+ $this->_pdf->close_object();
152
+ $this->_pdf->add_object($this->_current_page_id, "add");
153
+ Page_Cache::store_page($this->_cache_id,
154
+ $this->_pdf->get_page_number(),
155
+ $this->_pdf->serialize_object($this->_current_page_id));
156
+ $this->_current_page_id = null;
157
+ }
158
+
159
+ return $this->_pdf->output();
160
+ }
161
+
162
+ function get_messages() { return $this->_pdf->get_messages(); }
163
+
164
+ }
lib/dompdf/include/canvas.cls.php ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Main rendering interface
12
+ *
13
+ * Currently {@link CPDF_Adapter}, {@link PDFLib_Adapter}, {@link TCPDF_Adapter}, and {@link GD_Adapter}
14
+ * implement this interface.
15
+ *
16
+ * Implementations should measure x and y increasing to the left and down,
17
+ * respectively, with the origin in the top left corner. Implementations
18
+ * are free to use a unit other than points for length, but I can't
19
+ * guarantee that the results will look any good.
20
+ *
21
+ * @package dompdf
22
+ */
23
+ interface Canvas {
24
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf);
25
+
26
+ /**
27
+ * @return DOMPDF
28
+ */
29
+ function get_dompdf();
30
+
31
+ /**
32
+ * Returns the current page number
33
+ *
34
+ * @return int
35
+ */
36
+ function get_page_number();
37
+
38
+ /**
39
+ * Returns the total number of pages
40
+ *
41
+ * @return int
42
+ */
43
+ function get_page_count();
44
+
45
+ /**
46
+ * Sets the total number of pages
47
+ *
48
+ * @param int $count
49
+ */
50
+ function set_page_count($count);
51
+
52
+ /**
53
+ * Draws a line from x1,y1 to x2,y2
54
+ *
55
+ * See {@link Style::munge_color()} for the format of the color array.
56
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
57
+ * $style parameter (aka dash).
58
+ *
59
+ * @param float $x1
60
+ * @param float $y1
61
+ * @param float $x2
62
+ * @param float $y2
63
+ * @param array $color
64
+ * @param float $width
65
+ * @param array $style
66
+ */
67
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = null);
68
+
69
+ /**
70
+ * Draws a rectangle at x1,y1 with width w and height h
71
+ *
72
+ * See {@link Style::munge_color()} for the format of the color array.
73
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
74
+ * parameter (aka dash)
75
+ *
76
+ * @param float $x1
77
+ * @param float $y1
78
+ * @param float $w
79
+ * @param float $h
80
+ * @param array $color
81
+ * @param float $width
82
+ * @param array $style
83
+ */
84
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = null);
85
+
86
+ /**
87
+ * Draws a filled rectangle at x1,y1 with width w and height h
88
+ *
89
+ * See {@link Style::munge_color()} for the format of the color array.
90
+ *
91
+ * @param float $x1
92
+ * @param float $y1
93
+ * @param float $w
94
+ * @param float $h
95
+ * @param array $color
96
+ */
97
+ function filled_rectangle($x1, $y1, $w, $h, $color);
98
+
99
+ /**
100
+ * Starts a clipping rectangle at x1,y1 with width w and height h
101
+ *
102
+ * @param float $x1
103
+ * @param float $y1
104
+ * @param float $w
105
+ * @param float $h
106
+ */
107
+ function clipping_rectangle($x1, $y1, $w, $h);
108
+
109
+ /**
110
+ * Starts a rounded clipping rectangle at x1,y1 with width w and height h
111
+ *
112
+ * @param float $x1
113
+ * @param float $y1
114
+ * @param float $w
115
+ * @param float $h
116
+ * @param float $tl
117
+ * @param float $tr
118
+ * @param float $br
119
+ * @param float $bl
120
+ *
121
+ * @return
122
+ */
123
+ function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl);
124
+
125
+ /**
126
+ * Ends the last clipping shape
127
+ */
128
+ function clipping_end();
129
+
130
+ /**
131
+ * Save current state
132
+ */
133
+ function save();
134
+
135
+ /**
136
+ * Restore last state
137
+ */
138
+ function restore();
139
+
140
+ /**
141
+ * Rotate
142
+ */
143
+ function rotate($angle, $x, $y);
144
+
145
+ /**
146
+ * Skew
147
+ */
148
+ function skew($angle_x, $angle_y, $x, $y);
149
+
150
+ /**
151
+ * Scale
152
+ */
153
+ function scale($s_x, $s_y, $x, $y);
154
+
155
+ /**
156
+ * Translate
157
+ */
158
+ function translate($t_x, $t_y);
159
+
160
+ /**
161
+ * Transform
162
+ */
163
+ function transform($a, $b, $c, $d, $e, $f);
164
+
165
+ /**
166
+ * Draws a polygon
167
+ *
168
+ * The polygon is formed by joining all the points stored in the $points
169
+ * array. $points has the following structure:
170
+ * <code>
171
+ * array(0 => x1,
172
+ * 1 => y1,
173
+ * 2 => x2,
174
+ * 3 => y2,
175
+ * ...
176
+ * );
177
+ * </code>
178
+ *
179
+ * See {@link Style::munge_color()} for the format of the color array.
180
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
181
+ * parameter (aka dash)
182
+ *
183
+ * @param array $points
184
+ * @param array $color
185
+ * @param float $width
186
+ * @param array $style
187
+ * @param bool $fill Fills the polygon if true
188
+ */
189
+ function polygon($points, $color, $width = null, $style = null, $fill = false);
190
+
191
+ /**
192
+ * Draws a circle at $x,$y with radius $r
193
+ *
194
+ * See {@link Style::munge_color()} for the format of the color array.
195
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
196
+ * parameter (aka dash)
197
+ *
198
+ * @param float $x
199
+ * @param float $y
200
+ * @param float $r
201
+ * @param array $color
202
+ * @param float $width
203
+ * @param array $style
204
+ * @param bool $fill Fills the circle if true
205
+ */
206
+ function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false);
207
+
208
+ /**
209
+ * Add an image to the pdf.
210
+ *
211
+ * The image is placed at the specified x and y coordinates with the
212
+ * given width and height.
213
+ *
214
+ * @param string $img_url the path to the image
215
+ * @param float $x x position
216
+ * @param float $y y position
217
+ * @param int $w width (in pixels)
218
+ * @param int $h height (in pixels)
219
+ * @param string $resolution The resolution of the image
220
+ */
221
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal");
222
+
223
+ /**
224
+ * Add an arc to the PDF
225
+ * See {@link Style::munge_color()} for the format of the color array.
226
+ *
227
+ * @param float $x X coordinate of the arc
228
+ * @param float $y Y coordinate of the arc
229
+ * @param float $r1 Radius 1
230
+ * @param float $r2 Radius 2
231
+ * @param float $astart Start angle in degrees
232
+ * @param float $aend End angle in degrees
233
+ * @param array $color Color
234
+ * @param float $width
235
+ * @param array $style
236
+ *
237
+ * @return void
238
+ */
239
+ function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array());
240
+
241
+ /**
242
+ * Writes text at the specified x and y coordinates
243
+ * See {@link Style::munge_color()} for the format of the color array.
244
+ *
245
+ * @param float $x
246
+ * @param float $y
247
+ * @param string $text the text to write
248
+ * @param string $font the font file to use
249
+ * @param float $size the font size, in points
250
+ * @param array $color
251
+ * @param float $word_space word spacing adjustment
252
+ * @param float $char_space char spacing adjustment
253
+ * @param float $angle angle
254
+ *
255
+ * @return void
256
+ */
257
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
258
+
259
+ /**
260
+ * Add a named destination (similar to <a name="foo">...</a> in html)
261
+ *
262
+ * @param string $anchorname The name of the named destination
263
+ */
264
+ function add_named_dest($anchorname);
265
+
266
+ /**
267
+ * Add a link to the pdf
268
+ *
269
+ * @param string $url The url to link to
270
+ * @param float $x The x position of the link
271
+ * @param float $y The y position of the link
272
+ * @param float $width The width of the link
273
+ * @param float $height The height of the link
274
+ *
275
+ * @return void
276
+ */
277
+ function add_link($url, $x, $y, $width, $height);
278
+
279
+ /**
280
+ * Add meta information to the pdf
281
+ *
282
+ * @param string $name Label of the value (Creator, Producer, etc.)
283
+ * @param string $value The text to set
284
+ */
285
+ function add_info($name, $value);
286
+
287
+ /**
288
+ * Calculates text size, in points
289
+ *
290
+ * @param string $text the text to be sized
291
+ * @param string $font the desired font
292
+ * @param float $size the desired font size
293
+ * @param float $word_spacing word spacing, if any
294
+ * @param float $char_spacing
295
+ *
296
+ * @return float
297
+ */
298
+ function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0);
299
+
300
+ /**
301
+ * Calculates font height, in points
302
+ *
303
+ * @param string $font
304
+ * @param float $size
305
+ *
306
+ * @return float
307
+ */
308
+ function get_font_height($font, $size);
309
+
310
+ /**
311
+ * Calculates font baseline, in points
312
+ *
313
+ * @param string $font
314
+ * @param float $size
315
+ *
316
+ * @return float
317
+ */
318
+ function get_font_baseline($font, $size);
319
+
320
+ /**
321
+ * Returns the font x-height, in points
322
+ *
323
+ * @param string $font
324
+ * @param float $size
325
+ *
326
+ * @return float
327
+ */
328
+ //function get_font_x_height($font, $size);
329
+
330
+ /**
331
+ * Sets the opacity
332
+ *
333
+ * @param float $opacity
334
+ * @param string $mode
335
+ */
336
+ function set_opacity($opacity, $mode = "Normal");
337
+
338
+ /**
339
+ * Sets the default view
340
+ *
341
+ * @param string $view
342
+ * 'XYZ' left, top, zoom
343
+ * 'Fit'
344
+ * 'FitH' top
345
+ * 'FitV' left
346
+ * 'FitR' left,bottom,right
347
+ * 'FitB'
348
+ * 'FitBH' top
349
+ * 'FitBV' left
350
+ * @param array $options
351
+ *
352
+ * @return void
353
+ */
354
+ function set_default_view($view, $options = array());
355
+
356
+ /**
357
+ * @param string $script
358
+ *
359
+ * @return void
360
+ */
361
+ function javascript($script);
362
+
363
+ /**
364
+ * Starts a new page
365
+ *
366
+ * Subsequent drawing operations will appear on the new page.
367
+ */
368
+ function new_page();
369
+
370
+ /**
371
+ * Streams the PDF directly to the browser
372
+ *
373
+ * @param string $filename the name of the PDF file
374
+ * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
375
+ */
376
+ function stream($filename, $options = null);
377
+
378
+ /**
379
+ * Returns the PDF as a string
380
+ *
381
+ * @param array $options associative array: 'compress' => 1 or 0
382
+ * @return string
383
+ */
384
+ function output($options = null);
385
+ }
lib/dompdf/include/canvas_factory.cls.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Create canvas instances
11
+ *
12
+ * The canvas factory creates canvas instances based on the
13
+ * availability of rendering backends and config options.
14
+ *
15
+ * @package dompdf
16
+ */
17
+ class Canvas_Factory {
18
+
19
+ /**
20
+ * Constructor is private: this is a static class
21
+ */
22
+ private function __construct() { }
23
+
24
+ /**
25
+ * @param DOMPDF $dompdf
26
+ * @param string|array $paper
27
+ * @param string $orientation
28
+ * @param string $class
29
+ *
30
+ * @return Canvas
31
+ */
32
+ static function get_instance(DOMPDF $dompdf, $paper = null, $orientation = null, $class = null) {
33
+
34
+ $backend = strtolower(DOMPDF_PDF_BACKEND);
35
+
36
+ if ( isset($class) && class_exists($class, false) ) {
37
+ $class .= "_Adapter";
38
+ }
39
+
40
+ else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "pdflib" ) &&
41
+ class_exists("PDFLib", false) ) {
42
+ $class = "PDFLib_Adapter";
43
+ }
44
+
45
+ // FIXME The TCPDF adapter is not ready yet
46
+ //else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "cpdf") )
47
+ // $class = "CPDF_Adapter";
48
+
49
+ else if ( $backend === "tcpdf" ) {
50
+ $class = "TCPDF_Adapter";
51
+ }
52
+
53
+ else if ( $backend === "gd" ) {
54
+ $class = "GD_Adapter";
55
+ }
56
+
57
+ else {
58
+ $class = "CPDF_Adapter";
59
+ }
60
+
61
+ return new $class($paper, $orientation, $dompdf);
62
+ }
63
+ }
lib/dompdf/include/cellmap.cls.php ADDED
@@ -0,0 +1,790 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Maps table cells to the table grid.
11
+ *
12
+ * This class resolves borders in tables with collapsed borders and helps
13
+ * place row & column spanned table cells.
14
+ *
15
+ * @access private
16
+ * @package dompdf
17
+ */
18
+ class Cellmap {
19
+
20
+ /**
21
+ * Border style weight lookup for collapsed border resolution.
22
+ *
23
+ * @var array
24
+ */
25
+ static protected $_BORDER_STYLE_SCORE = array(
26
+ "inset" => 1,
27
+ "groove" => 2,
28
+ "outset" => 3,
29
+ "ridge" => 4,
30
+ "dotted" => 5,
31
+ "dashed" => 6,
32
+ "solid" => 7,
33
+ "double" => 8,
34
+ "hidden" => 9,
35
+ "none" => 0,
36
+ );
37
+
38
+ /**
39
+ * The table object this cellmap is attached to.
40
+ *
41
+ * @var Table_Frame_Decorator
42
+ */
43
+ protected $_table;
44
+
45
+ /**
46
+ * The total number of rows in the table
47
+ *
48
+ * @var int
49
+ */
50
+ protected $_num_rows;
51
+
52
+ /**
53
+ * The total number of columns in the table
54
+ *
55
+ * @var int
56
+ */
57
+ protected $_num_cols;
58
+
59
+ /**
60
+ * 2D array mapping <row,column> to frames
61
+ *
62
+ * @var Frame[][]
63
+ */
64
+ protected $_cells;
65
+
66
+ /**
67
+ * 1D array of column dimensions
68
+ *
69
+ * @var array
70
+ */
71
+ protected $_columns;
72
+
73
+ /**
74
+ * 1D array of row dimensions
75
+ *
76
+ * @var array
77
+ */
78
+ protected $_rows;
79
+
80
+ /**
81
+ * 2D array of border specs
82
+ *
83
+ * @var array
84
+ */
85
+ protected $_borders;
86
+
87
+ /**
88
+ * 1D Array mapping frames to (multiple) <row, col> pairs, keyed on frame_id.
89
+ *
90
+ * @var Frame[]
91
+ */
92
+ protected $_frames;
93
+
94
+ /**
95
+ * Current column when adding cells, 0-based
96
+ *
97
+ * @var int
98
+ */
99
+ private $__col;
100
+
101
+ /**
102
+ * Current row when adding cells, 0-based
103
+ *
104
+ * @var int
105
+ */
106
+ private $__row;
107
+
108
+ /**
109
+ * Tells wether the columns' width can be modified
110
+ *
111
+ * @var bool
112
+ */
113
+ private $_columns_locked = false;
114
+
115
+ /**
116
+ * Tells wether the table has table-layout:fixed
117
+ *
118
+ * @var bool
119
+ */
120
+ private $_fixed_layout = false;
121
+
122
+ //........................................................................
123
+
124
+ function __construct(Table_Frame_Decorator $table) {
125
+ $this->_table = $table;
126
+ $this->reset();
127
+ }
128
+
129
+ function __destruct() {
130
+ clear_object($this);
131
+ }
132
+ //........................................................................
133
+
134
+ function reset() {
135
+ $this->_num_rows = 0;
136
+ $this->_num_cols = 0;
137
+
138
+ $this->_cells = array();
139
+ $this->_frames = array();
140
+
141
+ if ( !$this->_columns_locked ) {
142
+ $this->_columns = array();
143
+ }
144
+
145
+ $this->_rows = array();
146
+
147
+ $this->_borders = array();
148
+
149
+ $this->__col = $this->__row = 0;
150
+ }
151
+
152
+ //........................................................................
153
+
154
+ function lock_columns() {
155
+ $this->_columns_locked = true;
156
+ }
157
+
158
+ function is_columns_locked() {
159
+ return $this->_columns_locked;
160
+ }
161
+
162
+ function set_layout_fixed($fixed) {
163
+ $this->_fixed_layout = $fixed;
164
+ }
165
+
166
+ function is_layout_fixed() {
167
+ return $this->_fixed_layout;
168
+ }
169
+
170
+ function get_num_rows() { return $this->_num_rows; }
171
+ function get_num_cols() { return $this->_num_cols; }
172
+
173
+ function &get_columns() {
174
+ return $this->_columns;
175
+ }
176
+
177
+ function set_columns($columns) {
178
+ $this->_columns = $columns;
179
+ }
180
+
181
+ function &get_column($i) {
182
+ if ( !isset($this->_columns[$i]) ) {
183
+ $this->_columns[$i] = array(
184
+ "x" => 0,
185
+ "min-width" => 0,
186
+ "max-width" => 0,
187
+ "used-width" => null,
188
+ "absolute" => 0,
189
+ "percent" => 0,
190
+ "auto" => true,
191
+ );
192
+ }
193
+
194
+ return $this->_columns[$i];
195
+ }
196
+
197
+ function &get_rows() {
198
+ return $this->_rows;
199
+ }
200
+
201
+ function &get_row($j) {
202
+ if ( !isset($this->_rows[$j]) ) {
203
+ $this->_rows[$j] = array(
204
+ "y" => 0,
205
+ "first-column" => 0,
206
+ "height" => null,
207
+ );
208
+ }
209
+
210
+ return $this->_rows[$j];
211
+ }
212
+
213
+ function get_border($i, $j, $h_v, $prop = null) {
214
+ if ( !isset($this->_borders[$i][$j][$h_v]) ) {
215
+ $this->_borders[$i][$j][$h_v] = array(
216
+ "width" => 0,
217
+ "style" => "solid",
218
+ "color" => "black",
219
+ );
220
+ }
221
+
222
+ if ( isset($prop) ) {
223
+ return $this->_borders[$i][$j][$h_v][$prop];
224
+ }
225
+
226
+ return $this->_borders[$i][$j][$h_v];
227
+ }
228
+
229
+ function get_border_properties($i, $j) {
230
+ return array(
231
+ "top" => $this->get_border($i, $j, "horizontal"),
232
+ "right" => $this->get_border($i, $j+1, "vertical"),
233
+ "bottom" => $this->get_border($i+1, $j, "horizontal"),
234
+ "left" => $this->get_border($i, $j, "vertical"),
235
+ );
236
+ }
237
+
238
+ //........................................................................
239
+
240
+ function get_spanned_cells(Frame $frame) {
241
+ $key = $frame->get_id();
242
+
243
+ if ( !isset($this->_frames[$key]) ) {
244
+ throw new DOMPDF_Exception("Frame not found in cellmap");
245
+ }
246
+
247
+ return $this->_frames[$key];
248
+
249
+ }
250
+
251
+ function frame_exists_in_cellmap(Frame $frame) {
252
+ $key = $frame->get_id();
253
+ return isset($this->_frames[$key]);
254
+ }
255
+
256
+ function get_frame_position(Frame $frame) {
257
+ global $_dompdf_warnings;
258
+
259
+ $key = $frame->get_id();
260
+
261
+ if ( !isset($this->_frames[$key]) ) {
262
+ throw new DOMPDF_Exception("Frame not found in cellmap");
263
+ }
264
+
265
+ $col = $this->_frames[$key]["columns"][0];
266
+ $row = $this->_frames[$key]["rows"][0];
267
+
268
+ if ( !isset($this->_columns[$col])) {
269
+ $_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs.";
270
+ $x = 0;
271
+ }
272
+ else {
273
+ $x = $this->_columns[$col]["x"];
274
+ }
275
+
276
+ if ( !isset($this->_rows[$row])) {
277
+ $_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs.";
278
+ $y = 0;
279
+ }
280
+ else {
281
+ $y = $this->_rows[$row]["y"];
282
+ }
283
+
284
+ return array($x, $y, "x" => $x, "y" => $y);
285
+ }
286
+
287
+ function get_frame_width(Frame $frame) {
288
+ $key = $frame->get_id();
289
+
290
+ if ( !isset($this->_frames[$key]) ) {
291
+ throw new DOMPDF_Exception("Frame not found in cellmap");
292
+ }
293
+
294
+ $cols = $this->_frames[$key]["columns"];
295
+ $w = 0;
296
+ foreach ($cols as $i) {
297
+ $w += $this->_columns[$i]["used-width"];
298
+ }
299
+
300
+ return $w;
301
+ }
302
+
303
+ function get_frame_height(Frame $frame) {
304
+ $key = $frame->get_id();
305
+
306
+ if ( !isset($this->_frames[$key]) ) {
307
+ throw new DOMPDF_Exception("Frame not found in cellmap");
308
+ }
309
+
310
+ $rows = $this->_frames[$key]["rows"];
311
+ $h = 0;
312
+ foreach ($rows as $i) {
313
+ if ( !isset($this->_rows[$i]) ) {
314
+ throw new Exception("The row #$i could not be found, please file an issue in the tracker with the HTML code");
315
+ }
316
+
317
+ $h += $this->_rows[$i]["height"];
318
+ }
319
+
320
+ return $h;
321
+ }
322
+
323
+
324
+ //........................................................................
325
+
326
+ function set_column_width($j, $width) {
327
+ if ( $this->_columns_locked ) {
328
+ return;
329
+ }
330
+
331
+ $col =& $this->get_column($j);
332
+ $col["used-width"] = $width;
333
+ $next_col =& $this->get_column($j+1);
334
+ $next_col["x"] = $next_col["x"] + $width;
335
+ }
336
+
337
+ function set_row_height($i, $height) {
338
+ $row =& $this->get_row($i);
339
+
340
+ if ( $row["height"] !== null && $height <= $row["height"] ) {
341
+ return;
342
+ }
343
+
344
+ $row["height"] = $height;
345
+ $next_row =& $this->get_row($i+1);
346
+ $next_row["y"] = $row["y"] + $height;
347
+
348
+ }
349
+
350
+ //........................................................................
351
+
352
+
353
+ protected function _resolve_border($i, $j, $h_v, $border_spec) {
354
+ $n_width = $border_spec["width"];
355
+ $n_style = $border_spec["style"];
356
+
357
+ if ( !isset($this->_borders[$i][$j][$h_v]) ) {
358
+ $this->_borders[$i][$j][$h_v] = $border_spec;
359
+ return $this->_borders[$i][$j][$h_v]["width"];
360
+ }
361
+
362
+ $border = &$this->_borders[$i][$j][$h_v];
363
+
364
+ $o_width = $border["width"];
365
+ $o_style = $border["style"];
366
+
367
+ if ( ($n_style === "hidden" ||
368
+ $n_width > $o_width ||
369
+ $o_style === "none")
370
+
371
+ or
372
+
373
+ ($o_width == $n_width &&
374
+ in_array($n_style, self::$_BORDER_STYLE_SCORE) &&
375
+ self::$_BORDER_STYLE_SCORE[ $n_style ] > self::$_BORDER_STYLE_SCORE[ $o_style ]) ) {
376
+ $border = $border_spec;
377
+ }
378
+
379
+ return $border["width"];
380
+ }
381
+
382
+ //........................................................................
383
+
384
+ function add_frame(Frame $frame) {
385
+
386
+ $style = $frame->get_style();
387
+ $display = $style->display;
388
+
389
+ $collapse = $this->_table->get_style()->border_collapse == "collapse";
390
+
391
+ // Recursively add the frames within tables, table-row-groups and table-rows
392
+ if ( $display === "table-row" ||
393
+ $display === "table" ||
394
+ $display === "inline-table" ||
395
+ in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
396
+
397
+ $start_row = $this->__row;
398
+ foreach ( $frame->get_children() as $child ) {
399
+ $this->add_frame( $child );
400
+ }
401
+
402
+ if ( $display === "table-row" ) {
403
+ $this->add_row();
404
+ }
405
+
406
+ $num_rows = $this->__row - $start_row - 1;
407
+ $key = $frame->get_id();
408
+
409
+ // Row groups always span across the entire table
410
+ $this->_frames[$key]["columns"] = range(0,max(0,$this->_num_cols-1));
411
+ $this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1));
412
+ $this->_frames[$key]["frame"] = $frame;
413
+
414
+ if ( $display !== "table-row" && $collapse ) {
415
+
416
+ $bp = $style->get_border_properties();
417
+
418
+ // Resolve the borders
419
+ for ( $i = 0; $i < $num_rows+1; $i++) {
420
+ $this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
421
+ $this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
422
+ }
423
+
424
+ for ( $j = 0; $j < $this->_num_cols; $j++) {
425
+ $this->_resolve_border($start_row, $j, "horizontal", $bp["top"]);
426
+ $this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
427
+ }
428
+ }
429
+
430
+
431
+ return;
432
+ }
433
+
434
+ $node = $frame->get_node();
435
+
436
+ // Determine where this cell is going
437
+ $colspan = $node->getAttribute("colspan");
438
+ $rowspan = $node->getAttribute("rowspan");
439
+
440
+ if ( !$colspan ) {
441
+ $colspan = 1;
442
+ $node->setAttribute("colspan",1);
443
+ }
444
+
445
+ if ( !$rowspan ) {
446
+ $rowspan = 1;
447
+ $node->setAttribute("rowspan",1);
448
+ }
449
+ $key = $frame->get_id();
450
+
451
+ $bp = $style->get_border_properties();
452
+
453
+
454
+ // Add the frame to the cellmap
455
+ $max_left = $max_right = 0;
456
+
457
+ // Find the next available column (fix by Ciro Mondueri)
458
+ $ac = $this->__col;
459
+ while ( isset($this->_cells[$this->__row][$ac]) ) {
460
+ $ac++;
461
+ }
462
+
463
+ $this->__col = $ac;
464
+
465
+ // Rows:
466
+ for ( $i = 0; $i < $rowspan; $i++ ) {
467
+ $row = $this->__row + $i;
468
+
469
+ $this->_frames[$key]["rows"][] = $row;
470
+
471
+ for ( $j = 0; $j < $colspan; $j++) {
472
+ $this->_cells[$row][$this->__col + $j] = $frame;
473
+ }
474
+
475
+ if ( $collapse ) {
476
+ // Resolve vertical borders
477
+ $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
478
+ $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
479
+ }
480
+ }
481
+
482
+ $max_top = $max_bottom = 0;
483
+
484
+ // Columns:
485
+ for ( $j = 0; $j < $colspan; $j++ ) {
486
+ $col = $this->__col + $j;
487
+ $this->_frames[$key]["columns"][] = $col;
488
+
489
+ if ( $collapse ) {
490
+ // Resolve horizontal borders
491
+ $max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"]));
492
+ $max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]));
493
+ }
494
+ }
495
+
496
+ $this->_frames[$key]["frame"] = $frame;
497
+
498
+ // Handle seperated border model
499
+ if ( !$collapse ) {
500
+ list($h, $v) = $this->_table->get_style()->border_spacing;
501
+
502
+ // Border spacing is effectively a margin between cells
503
+ $v = $style->length_in_pt($v) / 2;
504
+ $h = $style->length_in_pt($h) / 2;
505
+ $style->margin = "$v $h";
506
+
507
+ // The additional 1/2 width gets added to the table proper
508
+ }
509
+ else {
510
+ // Drop the frame's actual border
511
+ $style->border_left_width = $max_left / 2;
512
+ $style->border_right_width = $max_right / 2;
513
+ $style->border_top_width = $max_top / 2;
514
+ $style->border_bottom_width = $max_bottom / 2;
515
+ $style->margin = "none";
516
+ }
517
+
518
+ if ( !$this->_columns_locked ) {
519
+ // Resolve the frame's width
520
+ if ( $this->_fixed_layout ) {
521
+ list($frame_min, $frame_max) = array(0, 10e-10);
522
+ }
523
+ else {
524
+ list($frame_min, $frame_max) = $frame->get_min_max_width();
525
+ }
526
+
527
+ $width = $style->width;
528
+
529
+ $val = null;
530
+ if ( is_percent($width) ) {
531
+ $var = "percent";
532
+ $val = (float)rtrim($width, "% ") / $colspan;
533
+ }
534
+ else if ( $width !== "auto" ) {
535
+ $var = "absolute";
536
+ $val = $style->length_in_pt($frame_min) / $colspan;
537
+ }
538
+
539
+ $min = 0;
540
+ $max = 0;
541
+ for ( $cs = 0; $cs < $colspan; $cs++ ) {
542
+
543
+ // Resolve the frame's width(s) with other cells
544
+ $col =& $this->get_column( $this->__col + $cs );
545
+
546
+ // Note: $var is either 'percent' or 'absolute'. We compare the
547
+ // requested percentage or absolute values with the existing widths
548
+ // and adjust accordingly.
549
+ if ( isset($var) && $val > $col[$var] ) {
550
+ $col[$var] = $val;
551
+ $col["auto"] = false;
552
+ }
553
+
554
+ $min += $col["min-width"];
555
+ $max += $col["max-width"];
556
+ }
557
+
558
+ if ( $frame_min > $min ) {
559
+ // The frame needs more space. Expand each sub-column
560
+ // FIXME try to avoid putting this dummy value when table-layout:fixed
561
+ $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min) / $colspan);
562
+ for ($c = 0; $c < $colspan; $c++) {
563
+ $col =& $this->get_column($this->__col + $c);
564
+ $col["min-width"] += $inc;
565
+ }
566
+ }
567
+
568
+ if ( $frame_max > $max ) {
569
+ // FIXME try to avoid putting this dummy value when table-layout:fixed
570
+ $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_max - $max) / $colspan);
571
+ for ($c = 0; $c < $colspan; $c++) {
572
+ $col =& $this->get_column($this->__col + $c);
573
+ $col["max-width"] += $inc;
574
+ }
575
+ }
576
+ }
577
+
578
+ $this->__col += $colspan;
579
+ if ( $this->__col > $this->_num_cols )
580
+ $this->_num_cols = $this->__col;
581
+
582
+ }
583
+
584
+ //........................................................................
585
+
586
+ function add_row() {
587
+
588
+ $this->__row++;
589
+ $this->_num_rows++;
590
+
591
+ // Find the next available column
592
+ $i = 0;
593
+ while ( isset($this->_cells[$this->__row][$i]) ) {
594
+ $i++;
595
+ }
596
+
597
+ $this->__col = $i;
598
+
599
+ }
600
+
601
+ //........................................................................
602
+
603
+ /**
604
+ * Remove a row from the cellmap.
605
+ *
606
+ * @param Frame
607
+ */
608
+ function remove_row(Frame $row) {
609
+
610
+ $key = $row->get_id();
611
+ if ( !isset($this->_frames[$key]) ) {
612
+ return; // Presumably this row has alredy been removed
613
+ }
614
+
615
+ $this->_row = $this->_num_rows--;
616
+
617
+ $rows = $this->_frames[$key]["rows"];
618
+ $columns = $this->_frames[$key]["columns"];
619
+
620
+ // Remove all frames from this row
621
+ foreach ( $rows as $r ) {
622
+ foreach ( $columns as $c ) {
623
+ if ( isset($this->_cells[$r][$c]) ) {
624
+ $id = $this->_cells[$r][$c]->get_id();
625
+
626
+ $this->_frames[$id] = null;
627
+ unset($this->_frames[$id]);
628
+
629
+ $this->_cells[$r][$c] = null;
630
+ unset($this->_cells[$r][$c]);
631
+ }
632
+ }
633
+
634
+ $this->_rows[$r] = null;
635
+ unset($this->_rows[$r]);
636
+ }
637
+
638
+ $this->_frames[$key] = null;
639
+ unset($this->_frames[$key]);
640
+
641
+ }
642
+
643
+ /**
644
+ * Remove a row group from the cellmap.
645
+ *
646
+ * @param Frame $group The group to remove
647
+ */
648
+ function remove_row_group(Frame $group) {
649
+
650
+ $key = $group->get_id();
651
+ if ( !isset($this->_frames[$key]) ) {
652
+ return; // Presumably this row has alredy been removed
653
+ }
654
+
655
+ $iter = $group->get_first_child();
656
+ while ($iter) {
657
+ $this->remove_row($iter);
658
+ $iter = $iter->get_next_sibling();
659
+ }
660
+
661
+ $this->_frames[$key] = null;
662
+ unset($this->_frames[$key]);
663
+ }
664
+
665
+ /**
666
+ * Update a row group after rows have been removed
667
+ *
668
+ * @param Frame $group The group to update
669
+ * @param Frame $last_row The last row in the row group
670
+ */
671
+ function update_row_group(Frame $group, Frame $last_row) {
672
+
673
+ $g_key = $group->get_id();
674
+ $r_key = $last_row->get_id();
675
+
676
+ $r_rows = $this->_frames[$r_key]["rows"];
677
+ $this->_frames[$g_key]["rows"] = range( $this->_frames[$g_key]["rows"][0], end($r_rows) );
678
+
679
+ }
680
+
681
+ //........................................................................
682
+
683
+ function assign_x_positions() {
684
+ // Pre-condition: widths must be resolved and assigned to columns and
685
+ // column[0]["x"] must be set.
686
+
687
+ if ( $this->_columns_locked ) {
688
+ return;
689
+ }
690
+
691
+ $x = $this->_columns[0]["x"];
692
+ foreach ( array_keys($this->_columns) as $j ) {
693
+ $this->_columns[$j]["x"] = $x;
694
+ $x += $this->_columns[$j]["used-width"];
695
+ }
696
+
697
+ }
698
+
699
+ function assign_frame_heights() {
700
+ // Pre-condition: widths and heights of each column & row must be
701
+ // calcluated
702
+
703
+ foreach ( $this->_frames as $arr ) {
704
+ $frame = $arr["frame"];
705
+
706
+ $h = 0;
707
+ foreach( $arr["rows"] as $row ) {
708
+ if ( !isset($this->_rows[$row]) ) {
709
+ // The row has been removed because of a page split, so skip it.
710
+ continue;
711
+ }
712
+
713
+ $h += $this->_rows[$row]["height"];
714
+ }
715
+
716
+ if ( $frame instanceof Table_Cell_Frame_Decorator ) {
717
+ $frame->set_cell_height($h);
718
+ }
719
+ else {
720
+ $frame->get_style()->height = $h;
721
+ }
722
+ }
723
+
724
+ }
725
+
726
+ //........................................................................
727
+
728
+ /**
729
+ * Re-adjust frame height if the table height is larger than its content
730
+ */
731
+ function set_frame_heights($table_height, $content_height) {
732
+
733
+
734
+ // Distribute the increased height proportionally amongst each row
735
+ foreach ( $this->_frames as $arr ) {
736
+ $frame = $arr["frame"];
737
+
738
+ $h = 0;
739
+ foreach ($arr["rows"] as $row ) {
740
+ if ( !isset($this->_rows[$row]) ) {
741
+ continue;
742
+ }
743
+
744
+ $h += $this->_rows[$row]["height"];
745
+ }
746
+
747
+ if ( $content_height > 0 ) {
748
+ $new_height = ($h / $content_height) * $table_height;
749
+ }
750
+ else {
751
+ $new_height = 0;
752
+ }
753
+
754
+ if ( $frame instanceof Table_Cell_Frame_Decorator ) {
755
+ $frame->set_cell_height($new_height);
756
+ }
757
+ else {
758
+ $frame->get_style()->height = $new_height;
759
+ }
760
+ }
761
+
762
+ }
763
+
764
+ //........................................................................
765
+
766
+ // Used for debugging:
767
+ function __toString() {
768
+ $str = "";
769
+ $str .= "Columns:<br/>";
770
+ $str .= pre_r($this->_columns, true);
771
+ $str .= "Rows:<br/>";
772
+ $str .= pre_r($this->_rows, true);
773
+
774
+ $str .= "Frames:<br/>";
775
+ $arr = array();
776
+ foreach ( $this->_frames as $key => $val ) {
777
+ $arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]);
778
+ }
779
+
780
+ $str .= pre_r($arr, true);
781
+
782
+ if ( php_sapi_name() == "cli" ) {
783
+ $str = strip_tags(str_replace(array("<br/>","<b>","</b>"),
784
+ array("\n",chr(27)."[01;33m", chr(27)."[0m"),
785
+ $str));
786
+ }
787
+
788
+ return $str;
789
+ }
790
+ }
lib/dompdf/include/cpdf_adapter.cls.php ADDED
@@ -0,0 +1,877 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Orion Richardson <orionr@yahoo.com>
7
+ * @author Helmut Tischer <htischer@weihenstephan.org>
8
+ * @author Fabien Ménager <fabien.menager@gmail.com>
9
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
10
+ */
11
+
12
+ // FIXME: Need to sanity check inputs to this class
13
+ require_once(DOMPDF_LIB_DIR . "/class.pdf.php");
14
+
15
+ /**
16
+ * PDF rendering interface
17
+ *
18
+ * CPDF_Adapter provides a simple stateless interface to the stateful one
19
+ * provided by the Cpdf class.
20
+ *
21
+ * Unless otherwise mentioned, all dimensions are in points (1/72 in). The
22
+ * coordinate origin is in the top left corner, and y values increase
23
+ * downwards.
24
+ *
25
+ * See {@link http://www.ros.co.nz/pdf/} for more complete documentation
26
+ * on the underlying {@link Cpdf} class.
27
+ *
28
+ * @package dompdf
29
+ */
30
+ class CPDF_Adapter implements Canvas {
31
+
32
+ /**
33
+ * Dimensions of paper sizes in points
34
+ *
35
+ * @var array;
36
+ */
37
+ static $PAPER_SIZES = array(
38
+ "4a0" => array(0,0,4767.87,6740.79),
39
+ "2a0" => array(0,0,3370.39,4767.87),
40
+ "a0" => array(0,0,2383.94,3370.39),
41
+ "a1" => array(0,0,1683.78,2383.94),
42
+ "a2" => array(0,0,1190.55,1683.78),
43
+ "a3" => array(0,0,841.89,1190.55),
44
+ "a4" => array(0,0,595.28,841.89),
45
+ "a5" => array(0,0,419.53,595.28),
46
+ "a6" => array(0,0,297.64,419.53),
47
+ "a7" => array(0,0,209.76,297.64),
48
+ "a8" => array(0,0,147.40,209.76),
49
+ "a9" => array(0,0,104.88,147.40),
50
+ "a10" => array(0,0,73.70,104.88),
51
+ "b0" => array(0,0,2834.65,4008.19),
52
+ "b1" => array(0,0,2004.09,2834.65),
53
+ "b2" => array(0,0,1417.32,2004.09),
54
+ "b3" => array(0,0,1000.63,1417.32),
55
+ "b4" => array(0,0,708.66,1000.63),
56
+ "b5" => array(0,0,498.90,708.66),
57
+ "b6" => array(0,0,354.33,498.90),
58
+ "b7" => array(0,0,249.45,354.33),
59
+ "b8" => array(0,0,175.75,249.45),
60
+ "b9" => array(0,0,124.72,175.75),
61
+ "b10" => array(0,0,87.87,124.72),
62
+ "c0" => array(0,0,2599.37,3676.54),
63
+ "c1" => array(0,0,1836.85,2599.37),
64
+ "c2" => array(0,0,1298.27,1836.85),
65
+ "c3" => array(0,0,918.43,1298.27),
66
+ "c4" => array(0,0,649.13,918.43),
67
+ "c5" => array(0,0,459.21,649.13),
68
+ "c6" => array(0,0,323.15,459.21),
69
+ "c7" => array(0,0,229.61,323.15),
70
+ "c8" => array(0,0,161.57,229.61),
71
+ "c9" => array(0,0,113.39,161.57),
72
+ "c10" => array(0,0,79.37,113.39),
73
+ "ra0" => array(0,0,2437.80,3458.27),
74
+ "ra1" => array(0,0,1729.13,2437.80),
75
+ "ra2" => array(0,0,1218.90,1729.13),
76
+ "ra3" => array(0,0,864.57,1218.90),
77
+ "ra4" => array(0,0,609.45,864.57),
78
+ "sra0" => array(0,0,2551.18,3628.35),
79
+ "sra1" => array(0,0,1814.17,2551.18),
80
+ "sra2" => array(0,0,1275.59,1814.17),
81
+ "sra3" => array(0,0,907.09,1275.59),
82
+ "sra4" => array(0,0,637.80,907.09),
83
+ "letter" => array(0,0,612.00,792.00),
84
+ "legal" => array(0,0,612.00,1008.00),
85
+ "ledger" => array(0,0,1224.00, 792.00),
86
+ "tabloid" => array(0,0,792.00, 1224.00),
87
+ "executive" => array(0,0,521.86,756.00),
88
+ "folio" => array(0,0,612.00,936.00),
89
+ "commercial #10 envelope" => array(0,0,684,297),
90
+ "catalog #10 1/2 envelope" => array(0,0,648,864),
91
+ "8.5x11" => array(0,0,612.00,792.00),
92
+ "8.5x14" => array(0,0,612.00,1008.0),
93
+ "11x17" => array(0,0,792.00, 1224.00),
94
+ );
95
+
96
+ /**
97
+ * The DOMPDF object
98
+ *
99
+ * @var DOMPDF
100
+ */
101
+ private $_dompdf;
102
+
103
+ /**
104
+ * Instance of Cpdf class
105
+ *
106
+ * @var Cpdf
107
+ */
108
+ private $_pdf;
109
+
110
+ /**
111
+ * PDF width, in points
112
+ *
113
+ * @var float
114
+ */
115
+ private $_width;
116
+
117
+ /**
118
+ * PDF height, in points
119
+ *
120
+ * @var float;
121
+ */
122
+ private $_height;
123
+
124
+ /**
125
+ * Current page number
126
+ *
127
+ * @var int
128
+ */
129
+ private $_page_number;
130
+
131
+ /**
132
+ * Total number of pages
133
+ *
134
+ * @var int
135
+ */
136
+ private $_page_count;
137
+
138
+ /**
139
+ * Text to display on every page
140
+ *
141
+ * @var array
142
+ */
143
+ private $_page_text;
144
+
145
+ /**
146
+ * Array of pages for accesing after rendering is initially complete
147
+ *
148
+ * @var array
149
+ */
150
+ private $_pages;
151
+
152
+ /**
153
+ * Array of temporary cached images to be deleted when processing is complete
154
+ *
155
+ * @var array
156
+ */
157
+ private $_image_cache;
158
+
159
+ /**
160
+ * Class constructor
161
+ *
162
+ * @param mixed $paper The size of paper to use in this PDF ({@link CPDF_Adapter::$PAPER_SIZES})
163
+ * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
164
+ * @param DOMPDF $dompdf The DOMPDF instance
165
+ */
166
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
167
+ if ( is_array($paper) ) {
168
+ $size = $paper;
169
+ }
170
+ else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) {
171
+ $size = self::$PAPER_SIZES[mb_strtolower($paper)];
172
+ }
173
+ else {
174
+ $size = self::$PAPER_SIZES["letter"];
175
+ }
176
+
177
+ if ( mb_strtolower($orientation) === "landscape" ) {
178
+ list($size[2], $size[3]) = array($size[3], $size[2]);
179
+ }
180
+
181
+ $this->_dompdf = $dompdf;
182
+
183
+ $this->_pdf = new Cpdf(
184
+ $size,
185
+ $dompdf->get_option("enable_unicode"),
186
+ $dompdf->get_option("font_cache"),
187
+ $dompdf->get_option("temp_dir")
188
+ );
189
+
190
+ $this->_pdf->addInfo("Creator", "DOMPDF");
191
+ $time = substr_replace(date('YmdHisO'), '\'', -2, 0).'\'';
192
+ $this->_pdf->addInfo("CreationDate", "D:$time");
193
+ $this->_pdf->addInfo("ModDate", "D:$time");
194
+
195
+ $this->_width = $size[2] - $size[0];
196
+ $this->_height= $size[3] - $size[1];
197
+
198
+ $this->_page_number = $this->_page_count = 1;
199
+ $this->_page_text = array();
200
+
201
+ $this->_pages = array($this->_pdf->getFirstPageId());
202
+
203
+ $this->_image_cache = array();
204
+ }
205
+
206
+ function get_dompdf(){
207
+ return $this->_dompdf;
208
+ }
209
+
210
+ /**
211
+ * Class destructor
212
+ *
213
+ * Deletes all temporary image files
214
+ */
215
+ function __destruct() {
216
+ foreach ($this->_image_cache as $img) {
217
+ // The file might be already deleted by 3rd party tmp cleaner,
218
+ // the file might not have been created at all
219
+ // (if image outputting commands failed)
220
+ // or because the destructor was called twice accidentally.
221
+ if (!file_exists($img)) {
222
+ continue;
223
+ }
224
+
225
+ if (DEBUGPNG) print '[__destruct unlink '.$img.']';
226
+ if (!DEBUGKEEPTEMP) unlink($img);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Returns the Cpdf instance
232
+ *
233
+ * @return Cpdf
234
+ */
235
+ function get_cpdf() {
236
+ return $this->_pdf;
237
+ }
238
+
239
+ /**
240
+ * Add meta information to the PDF
241
+ *
242
+ * @param string $label label of the value (Creator, Producer, etc.)
243
+ * @param string $value the text to set
244
+ */
245
+ function add_info($label, $value) {
246
+ $this->_pdf->addInfo($label, $value);
247
+ }
248
+
249
+ /**
250
+ * Opens a new 'object'
251
+ *
252
+ * While an object is open, all drawing actions are recored in the object,
253
+ * as opposed to being drawn on the current page. Objects can be added
254
+ * later to a specific page or to several pages.
255
+ *
256
+ * The return value is an integer ID for the new object.
257
+ *
258
+ * @see CPDF_Adapter::close_object()
259
+ * @see CPDF_Adapter::add_object()
260
+ *
261
+ * @return int
262
+ */
263
+ function open_object() {
264
+ $ret = $this->_pdf->openObject();
265
+ $this->_pdf->saveState();
266
+ return $ret;
267
+ }
268
+
269
+ /**
270
+ * Reopens an existing 'object'
271
+ *
272
+ * @see CPDF_Adapter::open_object()
273
+ * @param int $object the ID of a previously opened object
274
+ */
275
+ function reopen_object($object) {
276
+ $this->_pdf->reopenObject($object);
277
+ $this->_pdf->saveState();
278
+ }
279
+
280
+ /**
281
+ * Closes the current 'object'
282
+ *
283
+ * @see CPDF_Adapter::open_object()
284
+ */
285
+ function close_object() {
286
+ $this->_pdf->restoreState();
287
+ $this->_pdf->closeObject();
288
+ }
289
+
290
+ /**
291
+ * Adds a specified 'object' to the document
292
+ *
293
+ * $object int specifying an object created with {@link
294
+ * CPDF_Adapter::open_object()}. $where can be one of:
295
+ * - 'add' add to current page only
296
+ * - 'all' add to every page from the current one onwards
297
+ * - 'odd' add to all odd numbered pages from now on
298
+ * - 'even' add to all even numbered pages from now on
299
+ * - 'next' add the object to the next page only
300
+ * - 'nextodd' add to all odd numbered pages from the next one
301
+ * - 'nexteven' add to all even numbered pages from the next one
302
+ *
303
+ * @see Cpdf::addObject()
304
+ *
305
+ * @param int $object
306
+ * @param string $where
307
+ */
308
+ function add_object($object, $where = 'all') {
309
+ $this->_pdf->addObject($object, $where);
310
+ }
311
+
312
+ /**
313
+ * Stops the specified 'object' from appearing in the document.
314
+ *
315
+ * The object will stop being displayed on the page following the current
316
+ * one.
317
+ *
318
+ * @param int $object
319
+ */
320
+ function stop_object($object) {
321
+ $this->_pdf->stopObject($object);
322
+ }
323
+
324
+ /**
325
+ * @access private
326
+ */
327
+ function serialize_object($id) {
328
+ // Serialize the pdf object's current state for retrieval later
329
+ return $this->_pdf->serializeObject($id);
330
+ }
331
+
332
+ /**
333
+ * @access private
334
+ */
335
+ function reopen_serialized_object($obj) {
336
+ return $this->_pdf->restoreSerializedObject($obj);
337
+ }
338
+
339
+ //........................................................................
340
+
341
+ /**
342
+ * Returns the PDF's width in points
343
+ * @return float
344
+ */
345
+ function get_width() { return $this->_width; }
346
+
347
+ /**
348
+ * Returns the PDF's height in points
349
+ * @return float
350
+ */
351
+ function get_height() { return $this->_height; }
352
+
353
+ /**
354
+ * Returns the current page number
355
+ * @return int
356
+ */
357
+ function get_page_number() { return $this->_page_number; }
358
+
359
+ /**
360
+ * Returns the total number of pages in the document
361
+ * @return int
362
+ */
363
+ function get_page_count() { return $this->_page_count; }
364
+
365
+ /**
366
+ * Sets the current page number
367
+ *
368
+ * @param int $num
369
+ */
370
+ function set_page_number($num) { $this->_page_number = $num; }
371
+
372
+ /**
373
+ * Sets the page count
374
+ *
375
+ * @param int $count
376
+ */
377
+ function set_page_count($count) { $this->_page_count = $count; }
378
+
379
+ /**
380
+ * Sets the stroke color
381
+ *
382
+ * See {@link Style::set_color()} for the format of the color array.
383
+ * @param array $color
384
+ */
385
+ protected function _set_stroke_color($color) {
386
+ $this->_pdf->setStrokeColor($color);
387
+ }
388
+
389
+ /**
390
+ * Sets the fill colour
391
+ *
392
+ * See {@link Style::set_color()} for the format of the colour array.
393
+ * @param array $color
394
+ */
395
+ protected function _set_fill_color($color) {
396
+ $this->_pdf->setColor($color);
397
+ }
398
+
399
+ /**
400
+ * Sets line transparency
401
+ * @see Cpdf::setLineTransparency()
402
+ *
403
+ * Valid blend modes are (case-sensitive):
404
+ *
405
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
406
+ * ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
407
+ * Exclusion
408
+ *
409
+ * @param string $mode the blending mode to use
410
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
411
+ */
412
+ protected function _set_line_transparency($mode, $opacity) {
413
+ $this->_pdf->setLineTransparency($mode, $opacity);
414
+ }
415
+
416
+ /**
417
+ * Sets fill transparency
418
+ * @see Cpdf::setFillTransparency()
419
+ *
420
+ * Valid blend modes are (case-sensitive):
421
+ *
422
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
423
+ * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
424
+ * Exclusion
425
+ *
426
+ * @param string $mode the blending mode to use
427
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
428
+ */
429
+ protected function _set_fill_transparency($mode, $opacity) {
430
+ $this->_pdf->setFillTransparency($mode, $opacity);
431
+ }
432
+
433
+ /**
434
+ * Sets the line style
435
+ *
436
+ * @see Cpdf::setLineStyle()
437
+ *
438
+ * @param float $width
439
+ * @param string $cap
440
+ * @param string $join
441
+ * @param array $dash
442
+ */
443
+ protected function _set_line_style($width, $cap, $join, $dash) {
444
+ $this->_pdf->setLineStyle($width, $cap, $join, $dash);
445
+ }
446
+
447
+ /**
448
+ * Sets the opacity
449
+ *
450
+ * @param $opacity
451
+ * @param $mode
452
+ */
453
+ function set_opacity($opacity, $mode = "Normal") {
454
+ $this->_set_line_transparency($mode, $opacity);
455
+ $this->_set_fill_transparency($mode, $opacity);
456
+ }
457
+
458
+ function set_default_view($view, $options = array()) {
459
+ array_unshift($options, $view);
460
+ call_user_func_array(array($this->_pdf, "openHere"), $options);
461
+ }
462
+
463
+ /**
464
+ * Remaps y coords from 4th to 1st quadrant
465
+ *
466
+ * @param float $y
467
+ * @return float
468
+ */
469
+ protected function y($y) {
470
+ return $this->_height - $y;
471
+ }
472
+
473
+ // Canvas implementation
474
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
475
+ $this->_set_stroke_color($color);
476
+ $this->_set_line_style($width, "butt", "", $style);
477
+
478
+ $this->_pdf->line($x1, $this->y($y1),
479
+ $x2, $this->y($y2));
480
+ }
481
+
482
+ function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
483
+ $this->_set_stroke_color($color);
484
+ $this->_set_line_style($width, "butt", "", $style);
485
+
486
+ $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
487
+ }
488
+
489
+ //........................................................................
490
+
491
+ /**
492
+ * Convert a GIF or BMP image to a PNG image
493
+ *
494
+ * @param string $image_url
495
+ * @param integer $type
496
+ *
497
+ * @throws DOMPDF_Exception
498
+ * @return string The url of the newly converted image
499
+ */
500
+ protected function _convert_gif_bmp_to_png($image_url, $type) {
501
+ $image_type = Image_Cache::type_to_ext($type);
502
+ $func_name = "imagecreatefrom$image_type";
503
+
504
+ if ( !function_exists($func_name) ) {
505
+ throw new DOMPDF_Exception("Function $func_name() not found. Cannot convert $image_type image: $image_url. Please install the image PHP extension.");
506
+ }
507
+
508
+ set_error_handler("record_warnings");
509
+ $im = $func_name($image_url);
510
+
511
+ if ( $im ) {
512
+ imageinterlace($im, false);
513
+
514
+ $tmp_dir = $this->_dompdf->get_option("temp_dir");
515
+ $tmp_name = tempnam($tmp_dir, "{$image_type}dompdf_img_");
516
+ @unlink($tmp_name);
517
+ $filename = "$tmp_name.png";
518
+ $this->_image_cache[] = $filename;
519
+
520
+ imagepng($im, $filename);
521
+ imagedestroy($im);
522
+ }
523
+ else {
524
+ $filename = Image_Cache::$broken_image;
525
+ }
526
+
527
+ restore_error_handler();
528
+
529
+ return $filename;
530
+ }
531
+
532
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
533
+ $this->_set_stroke_color($color);
534
+ $this->_set_line_style($width, "butt", "", $style);
535
+ $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
536
+ }
537
+
538
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
539
+ $this->_set_fill_color($color);
540
+ $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
541
+ }
542
+
543
+ function clipping_rectangle($x1, $y1, $w, $h) {
544
+ $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
545
+ }
546
+
547
+ function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
548
+ $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
549
+ }
550
+
551
+ function clipping_end() {
552
+ $this->_pdf->clippingEnd();
553
+ }
554
+
555
+ function save() {
556
+ $this->_pdf->saveState();
557
+ }
558
+
559
+ function restore() {
560
+ $this->_pdf->restoreState();
561
+ }
562
+
563
+ function rotate($angle, $x, $y) {
564
+ $this->_pdf->rotate($angle, $x, $y);
565
+ }
566
+
567
+ function skew($angle_x, $angle_y, $x, $y) {
568
+ $this->_pdf->skew($angle_x, $angle_y, $x, $y);
569
+ }
570
+
571
+ function scale($s_x, $s_y, $x, $y) {
572
+ $this->_pdf->scale($s_x, $s_y, $x, $y);
573
+ }
574
+
575
+ function translate($t_x, $t_y) {
576
+ $this->_pdf->translate($t_x, $t_y);
577
+ }
578
+
579
+ function transform($a, $b, $c, $d, $e, $f) {
580
+ $this->_pdf->transform(array($a, $b, $c, $d, $e, $f));
581
+ }
582
+
583
+ function polygon($points, $color, $width = null, $style = array(), $fill = false) {
584
+ $this->_set_fill_color($color);
585
+ $this->_set_stroke_color($color);
586
+
587
+ // Adjust y values
588
+ for ( $i = 1; $i < count($points); $i += 2) {
589
+ $points[$i] = $this->y($points[$i]);
590
+ }
591
+
592
+ $this->_pdf->polygon($points, count($points) / 2, $fill);
593
+ }
594
+
595
+ function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
596
+ $this->_set_fill_color($color);
597
+ $this->_set_stroke_color($color);
598
+
599
+ if ( !$fill && isset($width) ) {
600
+ $this->_set_line_style($width, "round", "round", $style);
601
+ }
602
+
603
+ $this->_pdf->ellipse($x, $this->y($y), $r1, 0, 0, 8, 0, 360, 1, $fill);
604
+ }
605
+
606
+ function image($img, $x, $y, $w, $h, $resolution = "normal") {
607
+ list($width, $height, $type) = dompdf_getimagesize($img);
608
+
609
+ $debug_png = $this->_dompdf->get_option("debug_png");
610
+
611
+ if ($debug_png) print "[image:$img|$width|$height|$type]";
612
+
613
+ switch ($type) {
614
+ case IMAGETYPE_JPEG:
615
+ if ($debug_png) print '!!!jpg!!!';
616
+ $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
617
+ break;
618
+
619
+ case IMAGETYPE_GIF:
620
+ case IMAGETYPE_BMP:
621
+ if ($debug_png) print '!!!bmp or gif!!!';
622
+ // @todo use cache for BMP and GIF
623
+ $img = $this->_convert_gif_bmp_to_png($img, $type);
624
+
625
+ case IMAGETYPE_PNG:
626
+ if ($debug_png) print '!!!png!!!';
627
+
628
+ $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
629
+ break;
630
+
631
+ default:
632
+ if ($debug_png) print '!!!unknown!!!';
633
+ }
634
+ }
635
+
636
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
637
+ $pdf = $this->_pdf;
638
+
639
+ $pdf->setColor($color);
640
+
641
+ $font .= ".afm";
642
+ $pdf->selectFont($font);
643
+
644
+ //Font_Metrics::get_font_height($font, $size) ==
645
+ //$this->get_font_height($font, $size) ==
646
+ //$this->_pdf->selectFont($font),$this->_pdf->getFontHeight($size)
647
+ //- FontBBoxheight+FontHeightOffset, scaled to $size, in pt
648
+ //$this->_pdf->getFontDescender($size)
649
+ //- Descender scaled to size
650
+ //
651
+ //$this->_pdf->fonts[$this->_pdf->currentFont] sizes:
652
+ //['FontBBox'][0] left, ['FontBBox'][1] bottom, ['FontBBox'][2] right, ['FontBBox'][3] top
653
+ //Maximum extent of all glyphs of the font from the baseline point
654
+ //['Ascender'] maximum height above baseline except accents
655
+ //['Descender'] maximum depth below baseline, negative number means below baseline
656
+ //['FontHeightOffset'] manual enhancement of .afm files to trim windows fonts. currently not used.
657
+ //Values are in 1/1000 pt for a font size of 1 pt
658
+ //
659
+ //['FontBBox'][1] should be close to ['Descender']
660
+ //['FontBBox'][3] should be close to ['Ascender']+Accents
661
+ //in practice, FontBBox values are a little bigger
662
+ //
663
+ //The text position is referenced to the baseline, not to the lower corner of the FontBBox,
664
+ //for what the left,top corner is given.
665
+ //FontBBox spans also the background box for the text.
666
+ //If the lower corner would be used as reference point, the Descents of the glyphs would
667
+ //hang over the background box border.
668
+ //Therefore compensate only the extent above the Baseline.
669
+ //
670
+ //print '<pre>['.$font.','.$size.','.$pdf->getFontHeight($size).','.$pdf->getFontDescender($size).','.$pdf->fonts[$pdf->currentFont]['FontBBox'][3].','.$pdf->fonts[$pdf->currentFont]['FontBBox'][1].','.$pdf->fonts[$pdf->currentFont]['FontHeightOffset'].','.$pdf->fonts[$pdf->currentFont]['Ascender'].','.$pdf->fonts[$pdf->currentFont]['Descender'].']</pre>';
671
+ //
672
+ //$pdf->addText($x, $this->y($y) - ($pdf->fonts[$pdf->currentFont]['FontBBox'][3]*$size)/1000, $size, $text, $angle, $word_space, $char_space);
673
+ $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
674
+ }
675
+
676
+ //........................................................................
677
+
678
+ function javascript($code) {
679
+ $this->_pdf->addJavascript($code);
680
+ }
681
+
682
+ //........................................................................
683
+
684
+ /**
685
+ * Add a named destination (similar to <a name="foo">...</a> in html)
686
+ *
687
+ * @param string $anchorname The name of the named destination
688
+ */
689
+ function add_named_dest($anchorname) {
690
+ $this->_pdf->addDestination($anchorname, "Fit");
691
+ }
692
+
693
+ //........................................................................
694
+
695
+ /**
696
+ * Add a link to the pdf
697
+ *
698
+ * @param string $url The url to link to
699
+ * @param float $x The x position of the link
700
+ * @param float $y The y position of the link
701
+ * @param float $width The width of the link
702
+ * @param float $height The height of the link
703
+ */
704
+ function add_link($url, $x, $y, $width, $height) {
705
+
706
+ $y = $this->y($y) - $height;
707
+
708
+ if ( strpos($url, '#') === 0 ) {
709
+ // Local link
710
+ $name = substr($url,1);
711
+ if ( $name ) {
712
+ $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
713
+ }
714
+
715
+ }
716
+ else {
717
+ $this->_pdf->addLink(rawurldecode($url), $x, $y, $x + $width, $y + $height);
718
+ }
719
+ }
720
+
721
+ function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) {
722
+ $this->_pdf->selectFont($font);
723
+
724
+ $unicode = $this->_dompdf->get_option("enable_unicode");
725
+ if (!$unicode) {
726
+ $text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
727
+ }
728
+
729
+ return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
730
+ }
731
+
732
+ function register_string_subset($font, $string) {
733
+ $this->_pdf->registerText($font, $string);
734
+ }
735
+
736
+ function get_font_height($font, $size) {
737
+ $this->_pdf->selectFont($font);
738
+
739
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
740
+ return $this->_pdf->getFontHeight($size) * $ratio;
741
+ }
742
+
743
+ /*function get_font_x_height($font, $size) {
744
+ $this->_pdf->selectFont($font);
745
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
746
+ return $this->_pdf->getFontXHeight($size) * $ratio;
747
+ }*/
748
+
749
+ function get_font_baseline($font, $size) {
750
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
751
+ return $this->get_font_height($font, $size) / $ratio;
752
+ }
753
+
754
+ /**
755
+ * Writes text at the specified x and y coordinates on every page
756
+ *
757
+ * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
758
+ * with their current values.
759
+ *
760
+ * See {@link Style::munge_color()} for the format of the colour array.
761
+ *
762
+ * @param float $x
763
+ * @param float $y
764
+ * @param string $text the text to write
765
+ * @param string $font the font file to use
766
+ * @param float $size the font size, in points
767
+ * @param array $color
768
+ * @param float $word_space word spacing adjustment
769
+ * @param float $char_space char spacing adjustment
770
+ * @param float $angle angle to write the text at, measured CW starting from the x-axis
771
+ */
772
+ function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
773
+ $_t = "text";
774
+ $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
775
+ }
776
+
777
+ /**
778
+ * Processes a script on every page
779
+ *
780
+ * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
781
+ *
782
+ * This function can be used to add page numbers to all pages
783
+ * after the first one, for example.
784
+ *
785
+ * @param string $code the script code
786
+ * @param string $type the language type for script
787
+ */
788
+ function page_script($code, $type = "text/php") {
789
+ $_t = "script";
790
+ $this->_page_text[] = compact("_t", "code", "type");
791
+ }
792
+
793
+ function new_page() {
794
+ $this->_page_number++;
795
+ $this->_page_count++;
796
+
797
+ $ret = $this->_pdf->newPage();
798
+ $this->_pages[] = $ret;
799
+ return $ret;
800
+ }
801
+
802
+ /**
803
+ * Add text to each page after rendering is complete
804
+ */
805
+ protected function _add_page_text() {
806
+
807
+ if ( !count($this->_page_text) ) {
808
+ return;
809
+ }
810
+
811
+ $page_number = 1;
812
+ $eval = null;
813
+
814
+ foreach ($this->_pages as $pid) {
815
+ $this->reopen_object($pid);
816
+
817
+ foreach ($this->_page_text as $pt) {
818
+ extract($pt);
819
+
820
+ switch ($_t) {
821
+ case "text":
822
+ $text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"),
823
+ array($page_number, $this->_page_count), $text);
824
+ $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
825
+ break;
826
+
827
+ case "script":
828
+ if ( !$eval ) {
829
+ $eval = new PHP_Evaluator($this);
830
+ }
831
+ $eval->evaluate($code, array('PAGE_NUM' => $page_number, 'PAGE_COUNT' => $this->_page_count));
832
+ break;
833
+ }
834
+ }
835
+
836
+ $this->close_object();
837
+ $page_number++;
838
+ }
839
+ }
840
+
841
+ /**
842
+ * Streams the PDF directly to the browser
843
+ *
844
+ * @param string $filename the name of the PDF file
845
+ * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
846
+ */
847
+ function stream($filename, $options = null) {
848
+ // Add page text
849
+ $this->_add_page_text();
850
+
851
+ $options["Content-Disposition"] = $filename;
852
+ $this->_pdf->stream($options);
853
+ }
854
+
855
+ /**
856
+ * Returns the PDF as a string
857
+ *
858
+ * @param array $options Output options
859
+ * @return string
860
+ */
861
+ function output($options = null) {
862
+ $this->_add_page_text();
863
+
864
+ $debug = isset($options["compress"]) && $options["compress"] != 1;
865
+
866
+ return $this->_pdf->output($debug);
867
+ }
868
+
869
+ /**
870
+ * Returns logging messages generated by the Cpdf class
871
+ *
872
+ * @return string
873
+ */
874
+ function get_messages() {
875
+ return $this->_pdf->messages;
876
+ }
877
+ }
lib/dompdf/include/css_color.cls.php ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ class CSS_Color {
11
+ static $cssColorNames = array(
12
+ "aliceblue" => "F0F8FF",
13
+ "antiquewhite" => "FAEBD7",
14
+ "aqua" => "00FFFF",
15
+ "aquamarine" => "7FFFD4",
16
+ "azure" => "F0FFFF",
17
+ "beige" => "F5F5DC",
18
+ "bisque" => "FFE4C4",
19
+ "black" => "000000",
20
+ "blanchedalmond" => "FFEBCD",
21
+ "blue" => "0000FF",
22
+ "blueviolet" => "8A2BE2",
23
+ "brown" => "A52A2A",
24
+ "burlywood" => "DEB887",
25
+ "cadetblue" => "5F9EA0",
26
+ "chartreuse" => "7FFF00",
27
+ "chocolate" => "D2691E",
28
+ "coral" => "FF7F50",
29
+ "cornflowerblue" => "6495ED",
30
+ "cornsilk" => "FFF8DC",
31
+ "crimson" => "DC143C",
32
+ "cyan" => "00FFFF",
33
+ "darkblue" => "00008B",
34
+ "darkcyan" => "008B8B",
35
+ "darkgoldenrod" => "B8860B",
36
+ "darkgray" => "A9A9A9",
37
+ "darkgreen" => "006400",
38
+ "darkgrey" => "A9A9A9",
39
+ "darkkhaki" => "BDB76B",
40
+ "darkmagenta" => "8B008B",
41
+ "darkolivegreen" => "556B2F",
42
+ "darkorange" => "FF8C00",
43
+ "darkorchid" => "9932CC",
44
+ "darkred" => "8B0000",
45
+ "darksalmon" => "E9967A",
46
+ "darkseagreen" => "8FBC8F",
47
+ "darkslateblue" => "483D8B",
48
+ "darkslategray" => "2F4F4F",
49
+ "darkslategrey" => "2F4F4F",
50
+ "darkturquoise" => "00CED1",
51
+ "darkviolet" => "9400D3",
52
+ "deeppink" => "FF1493",
53
+ "deepskyblue" => "00BFFF",
54
+ "dimgray" => "696969",
55
+ "dimgrey" => "696969",
56
+ "dodgerblue" => "1E90FF",
57
+ "firebrick" => "B22222",
58
+ "floralwhite" => "FFFAF0",
59
+ "forestgreen" => "228B22",
60
+ "fuchsia" => "FF00FF",
61
+ "gainsboro" => "DCDCDC",
62
+ "ghostwhite" => "F8F8FF",
63
+ "gold" => "FFD700",
64
+ "goldenrod" => "DAA520",
65
+ "gray" => "808080",
66
+ "green" => "008000",
67
+ "greenyellow" => "ADFF2F",
68
+ "grey" => "808080",
69
+ "honeydew" => "F0FFF0",
70
+ "hotpink" => "FF69B4",
71
+ "indianred" => "CD5C5C",
72
+ "indigo" => "4B0082",
73
+ "ivory" => "FFFFF0",
74
+ "khaki" => "F0E68C",
75
+ "lavender" => "E6E6FA",
76
+ "lavenderblush" => "FFF0F5",
77
+ "lawngreen" => "7CFC00",
78
+ "lemonchiffon" => "FFFACD",
79
+ "lightblue" => "ADD8E6",
80
+ "lightcoral" => "F08080",
81
+ "lightcyan" => "E0FFFF",
82
+ "lightgoldenrodyellow" => "FAFAD2",
83
+ "lightgray" => "D3D3D3",
84
+ "lightgreen" => "90EE90",
85
+ "lightgrey" => "D3D3D3",
86
+ "lightpink" => "FFB6C1",
87
+ "lightsalmon" => "FFA07A",
88
+ "lightseagreen" => "20B2AA",
89
+ "lightskyblue" => "87CEFA",
90
+ "lightslategray" => "778899",
91
+ "lightslategrey" => "778899",
92
+ "lightsteelblue" => "B0C4DE",
93
+ "lightyellow" => "FFFFE0",
94
+ "lime" => "00FF00",
95
+ "limegreen" => "32CD32",
96
+ "linen" => "FAF0E6",
97
+ "magenta" => "FF00FF",
98
+ "maroon" => "800000",
99
+ "mediumaquamarine" => "66CDAA",
100
+ "mediumblue" => "0000CD",
101
+ "mediumorchid" => "BA55D3",
102
+ "mediumpurple" => "9370DB",
103
+ "mediumseagreen" => "3CB371",
104
+ "mediumslateblue" => "7B68EE",
105
+ "mediumspringgreen" => "00FA9A",
106
+ "mediumturquoise" => "48D1CC",
107
+ "mediumvioletred" => "C71585",
108
+ "midnightblue" => "191970",
109
+ "mintcream" => "F5FFFA",
110
+ "mistyrose" => "FFE4E1",
111
+ "moccasin" => "FFE4B5",
112
+ "navajowhite" => "FFDEAD",
113
+ "navy" => "000080",
114
+ "oldlace" => "FDF5E6",
115
+ "olive" => "808000",
116
+ "olivedrab" => "6B8E23",
117
+ "orange" => "FFA500",
118
+ "orangered" => "FF4500",
119
+ "orchid" => "DA70D6",
120
+ "palegoldenrod" => "EEE8AA",
121
+ "palegreen" => "98FB98",
122
+ "paleturquoise" => "AFEEEE",
123
+ "palevioletred" => "DB7093",
124
+ "papayawhip" => "FFEFD5",
125
+ "peachpuff" => "FFDAB9",
126
+ "peru" => "CD853F",
127
+ "pink" => "FFC0CB",
128
+ "plum" => "DDA0DD",
129
+ "powderblue" => "B0E0E6",
130
+ "purple" => "800080",
131
+ "red" => "FF0000",
132
+ "rosybrown" => "BC8F8F",
133
+ "royalblue" => "4169E1",
134
+ "saddlebrown" => "8B4513",
135
+ "salmon" => "FA8072",
136
+ "sandybrown" => "F4A460",
137
+ "seagreen" => "2E8B57",
138
+ "seashell" => "FFF5EE",
139
+ "sienna" => "A0522D",
140
+ "silver" => "C0C0C0",
141
+ "skyblue" => "87CEEB",
142
+ "slateblue" => "6A5ACD",
143
+ "slategray" => "708090",
144
+ "slategrey" => "708090",
145
+ "snow" => "FFFAFA",
146
+ "springgreen" => "00FF7F",
147
+ "steelblue" => "4682B4",
148
+ "tan" => "D2B48C",
149
+ "teal" => "008080",
150
+ "thistle" => "D8BFD8",
151
+ "tomato" => "FF6347",
152
+ "turquoise" => "40E0D0",
153
+ "violet" => "EE82EE",
154
+ "wheat" => "F5DEB3",
155
+ "white" => "FFFFFF",
156
+ "whitesmoke" => "F5F5F5",
157
+ "yellow" => "FFFF00",
158
+ "yellowgreen" => "9ACD32",
159
+ );
160
+
161
+ static function parse($color) {
162
+ if ( is_array($color) ) {
163
+ // Assume the array has the right format...
164
+ // FIXME: should/could verify this.
165
+ return $color;
166
+ }
167
+
168
+ static $cache = array();
169
+
170
+ $color = strtolower($color);
171
+
172
+ if ( isset($cache[$color]) ) {
173
+ return $cache[$color];
174
+ }
175
+
176
+ if ( in_array($color, array("transparent", "inherit")) ) {
177
+ return $cache[$color] = $color;
178
+ }
179
+
180
+ if ( isset(self::$cssColorNames[$color]) ) {
181
+ return $cache[$color] = self::getArray(self::$cssColorNames[$color]);
182
+ }
183
+
184
+ $length = mb_strlen($color);
185
+
186
+ // #rgb format
187
+ if ( $length == 4 && $color[0] === "#" ) {
188
+ return $cache[$color] = self::getArray($color[1].$color[1].$color[2].$color[2].$color[3].$color[3]);
189
+ }
190
+
191
+ // #rrggbb format
192
+ else if ( $length == 7 && $color[0] === "#" ) {
193
+ return $cache[$color] = self::getArray(mb_substr($color, 1, 6));
194
+ }
195
+
196
+ // rgb( r,g,b ) format
197
+ else if ( mb_strpos($color, "rgb") !== false ) {
198
+ $i = mb_strpos($color, "(");
199
+ $j = mb_strpos($color, ")");
200
+
201
+ // Bad color value
202
+ if ( $i === false || $j === false ) {
203
+ return null;
204
+ }
205
+
206
+ $triplet = explode(",", mb_substr($color, $i+1, $j-$i-1));
207
+
208
+ if ( count($triplet) != 3 ) {
209
+ return null;
210
+ }
211
+
212
+ foreach (array_keys($triplet) as $c) {
213
+ $triplet[$c] = trim($triplet[$c]);
214
+
215
+ if ( $triplet[$c][mb_strlen($triplet[$c]) - 1] === "%" ) {
216
+ $triplet[$c] = round($triplet[$c] * 2.55);
217
+ }
218
+ }
219
+
220
+ return $cache[$color] = self::getArray(vsprintf("%02X%02X%02X", $triplet));
221
+
222
+ }
223
+
224
+ // cmyk( c,m,y,k ) format
225
+ // http://www.w3.org/TR/css3-gcpm/#cmyk-colors
226
+ else if ( mb_strpos($color, "cmyk") !== false ) {
227
+ $i = mb_strpos($color, "(");
228
+ $j = mb_strpos($color, ")");
229
+
230
+ // Bad color value
231
+ if ( $i === false || $j === false ) {
232
+ return null;
233
+ }
234
+
235
+ $values = explode(",", mb_substr($color, $i+1, $j-$i-1));
236
+
237
+ if ( count($values) != 4 ) {
238
+ return null;
239
+ }
240
+
241
+ foreach ($values as &$c) {
242
+ $c = floatval(trim($c));
243
+ if ($c > 1.0) $c = 1.0;
244
+ if ($c < 0.0) $c = 0.0;
245
+ }
246
+
247
+ return $cache[$color] = self::getArray($values);
248
+ }
249
+
250
+ return null;
251
+ }
252
+
253
+ static function getArray($color) {
254
+ $c = array(null, null, null, null, "hex" => null);
255
+
256
+ if (is_array($color)) {
257
+ $c = $color;
258
+ $c["c"] = $c[0];
259
+ $c["m"] = $c[1];
260
+ $c["y"] = $c[2];
261
+ $c["k"] = $c[3];
262
+ $c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])";
263
+ }
264
+ else {
265
+ $c[0] = hexdec(mb_substr($color, 0, 2)) / 0xff;
266
+ $c[1] = hexdec(mb_substr($color, 2, 2)) / 0xff;
267
+ $c[2] = hexdec(mb_substr($color, 4, 2)) / 0xff;
268
+ $c["r"] = $c[0];
269
+ $c["g"] = $c[1];
270
+ $c["b"] = $c[2];
271
+ $c["hex"] = "#$color";
272
+ }
273
+
274
+ return $c;
275
+ }
276
+ }
lib/dompdf/include/dompdf.cls.php ADDED
@@ -0,0 +1,1062 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * DOMPDF - PHP5 HTML to PDF renderer
12
+ *
13
+ * DOMPDF loads HTML and does its best to render it as a PDF. It gets its
14
+ * name from the new DomDocument PHP5 extension. Source HTML is first
15
+ * parsed by a DomDocument object. DOMPDF takes the resulting DOM tree and
16
+ * attaches a {@link Frame} object to each node. {@link Frame} objects store
17
+ * positioning and layout information and each has a reference to a {@link
18
+ * Style} object.
19
+ *
20
+ * Style information is loaded and parsed (see {@link Stylesheet}) and is
21
+ * applied to the frames in the tree by using XPath. CSS selectors are
22
+ * converted into XPath queries, and the computed {@link Style} objects are
23
+ * applied to the {@link Frame}s.
24
+ *
25
+ * {@link Frame}s are then decorated (in the design pattern sense of the
26
+ * word) based on their CSS display property ({@link
27
+ * http://www.w3.org/TR/CSS21/visuren.html#propdef-display}).
28
+ * Frame_Decorators augment the basic {@link Frame} class by adding
29
+ * additional properties and methods specific to the particular type of
30
+ * {@link Frame}. For example, in the CSS layout model, block frames
31
+ * (display: block;) contain line boxes that are usually filled with text or
32
+ * other inline frames. The Block_Frame_Decorator therefore adds a $lines
33
+ * property as well as methods to add {@link Frame}s to lines and to add
34
+ * additional lines. {@link Frame}s also are attached to specific
35
+ * Positioner and {@link Frame_Reflower} objects that contain the
36
+ * positioining and layout algorithm for a specific type of frame,
37
+ * respectively. This is an application of the Strategy pattern.
38
+ *
39
+ * Layout, or reflow, proceeds recursively (post-order) starting at the root
40
+ * of the document. Space constraints (containing block width & height) are
41
+ * pushed down, and resolved positions and sizes bubble up. Thus, every
42
+ * {@link Frame} in the document tree is traversed once (except for tables
43
+ * which use a two-pass layout algorithm). If you are interested in the
44
+ * details, see the reflow() method of the Reflower classes.
45
+ *
46
+ * Rendering is relatively straightforward once layout is complete. {@link
47
+ * Frame}s are rendered using an adapted {@link Cpdf} class, originally
48
+ * written by Wayne Munro, http://www.ros.co.nz/pdf/. (Some performance
49
+ * related changes have been made to the original {@link Cpdf} class, and
50
+ * the {@link CPDF_Adapter} class provides a simple, stateless interface to
51
+ * PDF generation.) PDFLib support has now also been added, via the {@link
52
+ * PDFLib_Adapter}.
53
+ *
54
+ *
55
+ * @package dompdf
56
+ */
57
+ class DOMPDF {
58
+
59
+ /**
60
+ * DomDocument representing the HTML document
61
+ *
62
+ * @var DOMDocument
63
+ */
64
+ protected $_xml;
65
+
66
+ /**
67
+ * Frame_Tree derived from the DOM tree
68
+ *
69
+ * @var Frame_Tree
70
+ */
71
+ protected $_tree;
72
+
73
+ /**
74
+ * Stylesheet for the document
75
+ *
76
+ * @var Stylesheet
77
+ */
78
+ protected $_css;
79
+
80
+ /**
81
+ * Actual PDF renderer
82
+ *
83
+ * @var Canvas
84
+ */
85
+ protected $_pdf;
86
+
87
+ /**
88
+ * Desired paper size ('letter', 'legal', 'A4', etc.)
89
+ *
90
+ * @var string
91
+ */
92
+ protected $_paper_size;
93
+
94
+ /**
95
+ * Paper orientation ('portrait' or 'landscape')
96
+ *
97
+ * @var string
98
+ */
99
+ protected $_paper_orientation;
100
+
101
+ /**
102
+ * Callbacks on new page and new element
103
+ *
104
+ * @var array
105
+ */
106
+ protected $_callbacks;
107
+
108
+ /**
109
+ * Experimental caching capability
110
+ *
111
+ * @var string
112
+ */
113
+ private $_cache_id;
114
+
115
+ /**
116
+ * Base hostname
117
+ *
118
+ * Used for relative paths/urls
119
+ * @var string
120
+ */
121
+ protected $_base_host;
122
+
123
+ /**
124
+ * Absolute base path
125
+ *
126
+ * Used for relative paths/urls
127
+ * @var string
128
+ */
129
+ protected $_base_path;
130
+
131
+ /**
132
+ * Protcol used to request file (file://, http://, etc)
133
+ *
134
+ * @var string
135
+ */
136
+ protected $_protocol;
137
+
138
+ /**
139
+ * HTTP context created with stream_context_create()
140
+ * Will be used for file_get_contents
141
+ *
142
+ * @var resource
143
+ */
144
+ protected $_http_context;
145
+
146
+ /**
147
+ * Timestamp of the script start time
148
+ *
149
+ * @var int
150
+ */
151
+ private $_start_time = null;
152
+
153
+ /**
154
+ * The system's locale
155
+ *
156
+ * @var string
157
+ */
158
+ private $_system_locale = null;
159
+
160
+ /**
161
+ * Tells if the system's locale is the C standard one
162
+ *
163
+ * @var bool
164
+ */
165
+ private $_locale_standard = false;
166
+
167
+ /**
168
+ * The default view of the PDF in the viewer
169
+ *
170
+ * @var string
171
+ */
172
+ private $_default_view = "Fit";
173
+
174
+ /**
175
+ * The default view options of the PDF in the viewer
176
+ *
177
+ * @var array
178
+ */
179
+ private $_default_view_options = array();
180
+
181
+ /**
182
+ * Tells wether the DOM document is in quirksmode (experimental)
183
+ *
184
+ * @var bool
185
+ */
186
+ private $_quirksmode = false;
187
+
188
+ /**
189
+ * The list of built-in fonts
190
+ *
191
+ * @var array
192
+ */
193
+ public static $native_fonts = array(
194
+ "courier", "courier-bold", "courier-oblique", "courier-boldoblique",
195
+ "helvetica", "helvetica-bold", "helvetica-oblique", "helvetica-boldoblique",
196
+ "times-roman", "times-bold", "times-italic", "times-bolditalic",
197
+ "symbol", "zapfdinbats"
198
+ );
199
+
200
+ private $_options = array(
201
+ // Directories
202
+ "temp_dir" => DOMPDF_TEMP_DIR,
203
+ "font_dir" => DOMPDF_FONT_DIR,
204
+ "font_cache" => DOMPDF_FONT_CACHE,
205
+ "chroot" => DOMPDF_CHROOT,
206
+ "log_output_file" => DOMPDF_LOG_OUTPUT_FILE,
207
+
208
+ // Rendering
209
+ "default_media_type" => DOMPDF_DEFAULT_MEDIA_TYPE,
210
+ "default_paper_size" => DOMPDF_DEFAULT_PAPER_SIZE,
211
+ "default_font" => DOMPDF_DEFAULT_FONT,
212
+ "dpi" => DOMPDF_DPI,
213
+ "font_height_ratio" => DOMPDF_FONT_HEIGHT_RATIO,
214
+
215
+ // Features
216
+ "enable_unicode" => DOMPDF_UNICODE_ENABLED,
217
+ "enable_php" => DOMPDF_ENABLE_PHP,
218
+ "enable_remote" => DOMPDF_ENABLE_REMOTE,
219
+ "enable_css_float" => DOMPDF_ENABLE_CSS_FLOAT,
220
+ "enable_javascript" => DOMPDF_ENABLE_JAVASCRIPT,
221
+ "enable_html5_parser" => DOMPDF_ENABLE_HTML5PARSER,
222
+ "enable_font_subsetting" => DOMPDF_ENABLE_FONTSUBSETTING,
223
+
224
+ // Debug
225
+ "debug_png" => DEBUGPNG,
226
+ "debug_keep_temp" => DEBUGKEEPTEMP,
227
+ "debug_css" => DEBUGCSS,
228
+ "debug_layout" => DEBUG_LAYOUT,
229
+ "debug_layout_lines" => DEBUG_LAYOUT_LINES,
230
+ "debug_layout_blocks" => DEBUG_LAYOUT_BLOCKS,
231
+ "debug_layout_inline" => DEBUG_LAYOUT_INLINE,
232
+ "debug_layout_padding_box" => DEBUG_LAYOUT_PADDINGBOX,
233
+
234
+ // Admin
235
+ "admin_username" => DOMPDF_ADMIN_USERNAME,
236
+ "admin_password" => DOMPDF_ADMIN_PASSWORD,
237
+ );
238
+
239
+ /**
240
+ * Class constructor
241
+ */
242
+ function __construct() {
243
+ $this->_locale_standard = sprintf('%.1f', 1.0) == '1.0';
244
+
245
+ $this->save_locale();
246
+
247
+ $this->_messages = array();
248
+ $this->_css = new Stylesheet($this);
249
+ $this->_pdf = null;
250
+ $this->_paper_size = DOMPDF_DEFAULT_PAPER_SIZE;
251
+ $this->_paper_orientation = "portrait";
252
+ $this->_base_protocol = "";
253
+ $this->_base_host = "";
254
+ $this->_base_path = "";
255
+ $this->_http_context = null;
256
+ $this->_callbacks = array();
257
+ $this->_cache_id = null;
258
+
259
+ $this->restore_locale();
260
+ }
261
+
262
+ /**
263
+ * Class destructor
264
+ */
265
+ function __destruct() {
266
+ clear_object($this);
267
+ }
268
+
269
+ /**
270
+ * Get the dompdf option value
271
+ *
272
+ * @param string $key
273
+ *
274
+ * @return mixed
275
+ * @throws DOMPDF_Exception
276
+ */
277
+ function get_option($key) {
278
+ if ( !array_key_exists($key, $this->_options) ) {
279
+ throw new DOMPDF_Exception("Option '$key' doesn't exist");
280
+ }
281
+
282
+ return $this->_options[$key];
283
+ }
284
+
285
+ /**
286
+ * @param string $key
287
+ * @param mixed $value
288
+ *
289
+ * @throws DOMPDF_Exception
290
+ */
291
+ function set_option($key, $value) {
292
+ if ( !array_key_exists($key, $this->_options) ) {
293
+ throw new DOMPDF_Exception("Option '$key' doesn't exist");
294
+ }
295
+
296
+ $this->_options[$key] = $value;
297
+ }
298
+
299
+ /**
300
+ * @param array $options
301
+ */
302
+ function set_options(array $options) {
303
+ foreach ($options as $key => $value) {
304
+ $this->set_option($key, $value);
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Save the system's locale configuration and
310
+ * set the right value for numeric formatting
311
+ */
312
+ private function save_locale() {
313
+ if ( $this->_locale_standard ) {
314
+ return;
315
+ }
316
+
317
+ $this->_system_locale = setlocale(LC_NUMERIC, "0");
318
+ setlocale(LC_NUMERIC, "C");
319
+ }
320
+
321
+ /**
322
+ * Restore the system's locale configuration
323
+ */
324
+ private function restore_locale() {
325
+ if ( $this->_locale_standard ) {
326
+ return;
327
+ }
328
+
329
+ setlocale(LC_NUMERIC, $this->_system_locale);
330
+ }
331
+
332
+ /**
333
+ * Returns the underlying {@link Frame_Tree} object
334
+ *
335
+ * @return Frame_Tree
336
+ */
337
+ function get_tree() {
338
+ return $this->_tree;
339
+ }
340
+
341
+ /**
342
+ * Sets the protocol to use
343
+ * FIXME validate these
344
+ *
345
+ * @param string $proto
346
+ */
347
+ function set_protocol($proto) {
348
+ $this->_protocol = $proto;
349
+ }
350
+
351
+ /**
352
+ * Sets the base hostname
353
+ *
354
+ * @param string $host
355
+ */
356
+ function set_host($host) {
357
+ $this->_base_host = $host;
358
+ }
359
+
360
+ /**
361
+ * Sets the base path
362
+ *
363
+ * @param string $path
364
+ */
365
+ function set_base_path($path) {
366
+ $this->_base_path = $path;
367
+ }
368
+
369
+ /**
370
+ * Sets the HTTP context
371
+ *
372
+ * @param resource $http_context
373
+ */
374
+ function set_http_context($http_context) {
375
+ $this->_http_context = $http_context;
376
+ }
377
+
378
+ /**
379
+ * Sets the default view
380
+ *
381
+ * @param string $default_view The default document view
382
+ * @param array $options The view's options
383
+ */
384
+ function set_default_view($default_view, $options) {
385
+ $this->_default_view = $default_view;
386
+ $this->_default_view_options = $options;
387
+ }
388
+
389
+ /**
390
+ * Returns the protocol in use
391
+ *
392
+ * @return string
393
+ */
394
+ function get_protocol() {
395
+ return $this->_protocol;
396
+ }
397
+
398
+ /**
399
+ * Returns the base hostname
400
+ *
401
+ * @return string
402
+ */
403
+ function get_host() {
404
+ return $this->_base_host;
405
+ }
406
+
407
+ /**
408
+ * Returns the base path
409
+ *
410
+ * @return string
411
+ */
412
+ function get_base_path() {
413
+ return $this->_base_path;
414
+ }
415
+
416
+ /**
417
+ * Returns the HTTP context
418
+ *
419
+ * @return resource
420
+ */
421
+ function get_http_context() {
422
+ return $this->_http_context;
423
+ }
424
+
425
+ /**
426
+ * Return the underlying Canvas instance (e.g. CPDF_Adapter, GD_Adapter)
427
+ *
428
+ * @return Canvas
429
+ */
430
+ function get_canvas() {
431
+ return $this->_pdf;
432
+ }
433
+
434
+ /**
435
+ * Returns the callbacks array
436
+ *
437
+ * @return array
438
+ */
439
+ function get_callbacks() {
440
+ return $this->_callbacks;
441
+ }
442
+
443
+ /**
444
+ * Returns the stylesheet
445
+ *
446
+ * @return Stylesheet
447
+ */
448
+ function get_css() {
449
+ return $this->_css;
450
+ }
451
+
452
+ /**
453
+ * @return DOMDocument
454
+ */
455
+ function get_dom() {
456
+ return $this->_xml;
457
+ }
458
+
459
+ /**
460
+ * Loads an HTML file
461
+ * Parse errors are stored in the global array _dompdf_warnings.
462
+ *
463
+ * @param string $file a filename or url to load
464
+ *
465
+ * @throws DOMPDF_Exception
466
+ */
467
+ function load_html_file($file) {
468
+ $this->save_locale();
469
+
470
+ // Store parsing warnings as messages (this is to prevent output to the
471
+ // browser if the html is ugly and the dom extension complains,
472
+ // preventing the pdf from being streamed.)
473
+ if ( !$this->_protocol && !$this->_base_host && !$this->_base_path ) {
474
+ list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($file);
475
+ }
476
+
477
+ if ( !$this->get_option("enable_remote") && ($this->_protocol != "" && $this->_protocol !== "file://" ) ) {
478
+ throw new DOMPDF_Exception("Remote file requested, but DOMPDF_ENABLE_REMOTE is false.");
479
+ }
480
+
481
+ if ($this->_protocol == "" || $this->_protocol === "file://") {
482
+
483
+ // Get the full path to $file, returns false if the file doesn't exist
484
+ $realfile = realpath($file);
485
+ if ( !$realfile ) {
486
+ throw new DOMPDF_Exception("File '$file' not found.");
487
+ }
488
+
489
+ $chroot = $this->get_option("chroot");
490
+ if ( strpos($realfile, $chroot) !== 0 ) {
491
+ throw new DOMPDF_Exception("Permission denied on $file. The file could not be found under the directory specified by DOMPDF_CHROOT.");
492
+ }
493
+
494
+ // Exclude dot files (e.g. .htaccess)
495
+ if ( substr(basename($realfile), 0, 1) === "." ) {
496
+ throw new DOMPDF_Exception("Permission denied on $file.");
497
+ }
498
+
499
+ $file = $realfile;
500
+ }
501
+
502
+ $contents = file_get_contents($file, null, $this->_http_context);
503
+ $encoding = null;
504
+
505
+ // See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/
506
+ if ( isset($http_response_header) ) {
507
+ foreach($http_response_header as $_header) {
508
+ if ( preg_match("@Content-Type:\s*[\w/]+;\s*?charset=([^\s]+)@i", $_header, $matches) ) {
509
+ $encoding = strtoupper($matches[1]);
510
+ break;
511
+ }
512
+ }
513
+ }
514
+
515
+ $this->restore_locale();
516
+
517
+ $this->load_html($contents, $encoding);
518
+ }
519
+
520
+ /**
521
+ * Loads an HTML string
522
+ * Parse errors are stored in the global array _dompdf_warnings.
523
+ * @todo use the $encoding variable
524
+ *
525
+ * @param string $str HTML text to load
526
+ * @param string $encoding Not used yet
527
+ */
528
+ function load_html($str, $encoding = null) {
529
+ $this->save_locale();
530
+
531
+ // FIXME: Determine character encoding, switch to UTF8, update meta tag. Need better http/file stream encoding detection, currently relies on text or meta tag.
532
+ mb_detect_order('auto');
533
+
534
+ if (mb_detect_encoding($str) !== 'UTF-8') {
535
+ $metatags = array(
536
+ '@<meta\s+http-equiv="Content-Type"\s+content="(?:[\w/]+)(?:;\s*?charset=([^\s"]+))?@i',
537
+ '@<meta\s+content="(?:[\w/]+)(?:;\s*?charset=([^\s"]+))"?\s+http-equiv="Content-Type"@i',
538
+ '@<meta [^>]*charset\s*=\s*["\']?\s*([^"\' ]+)@i',
539
+ );
540
+
541
+ foreach($metatags as $metatag) {
542
+ if (preg_match($metatag, $str, $matches)) break;
543
+ }
544
+
545
+ if (mb_detect_encoding($str) == '') {
546
+ if (isset($matches[1])) {
547
+ $encoding = strtoupper($matches[1]);
548
+ }
549
+ else {
550
+ $encoding = 'UTF-8';
551
+ }
552
+ }
553
+ else {
554
+ if ( isset($matches[1]) ) {
555
+ $encoding = strtoupper($matches[1]);
556
+ }
557
+ else {
558
+ $encoding = 'auto';
559
+ }
560
+ }
561
+
562
+ if ( $encoding !== 'UTF-8' ) {
563
+ $str = mb_convert_encoding($str, 'UTF-8', $encoding);
564
+ }
565
+
566
+ if ( isset($matches[1]) ) {
567
+ $str = preg_replace('/charset=([^\s"]+)/i', 'charset=UTF-8', $str);
568
+ }
569
+ else {
570
+ $str = str_replace('<head>', '<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">', $str);
571
+ }
572
+ }
573
+ else {
574
+ $encoding = 'UTF-8';
575
+ }
576
+
577
+ // remove BOM mark from UTF-8, it's treated as document text by DOMDocument
578
+ // FIXME: roll this into the encoding detection using UTF-8/16/32 BOM (http://us2.php.net/manual/en/function.mb-detect-encoding.php#91051)?
579
+ if ( substr($str, 0, 3) == chr(0xEF).chr(0xBB).chr(0xBF) ) {
580
+ $str = substr($str, 3);
581
+ }
582
+
583
+ // Parse embedded php, first-pass
584
+ if ( $this->get_option("enable_php") ) {
585
+ ob_start();
586
+ eval("?" . ">$str");
587
+ $str = ob_get_clean();
588
+ }
589
+
590
+ // if the document contains non utf-8 with a utf-8 meta tag chars and was
591
+ // detected as utf-8 by mbstring, problems could happen.
592
+ // http://devzone.zend.com/article/8855
593
+ if ( $encoding !== 'UTF-8' ) {
594
+ $re = '/<meta ([^>]*)((?:charset=[^"\' ]+)([^>]*)|(?:charset=["\'][^"\' ]+["\']))([^>]*)>/i';
595
+ $str = preg_replace($re, '<meta $1$3>', $str);
596
+ }
597
+
598
+ // Store parsing warnings as messages
599
+ set_error_handler("record_warnings");
600
+
601
+ // @todo Take the quirksmode into account
602
+ // http://hsivonen.iki.fi/doctype/
603
+ // https://developer.mozilla.org/en/mozilla's_quirks_mode
604
+ $quirksmode = false;
605
+
606
+ if ( $this->get_option("enable_html5_parser") ) {
607
+ $tokenizer = new HTML5_Tokenizer($str);
608
+ $tokenizer->parse();
609
+ $doc = $tokenizer->save();
610
+
611
+ // Remove #text children nodes in nodes that shouldn't have
612
+ $tag_names = array("html", "table", "tbody", "thead", "tfoot", "tr");
613
+ foreach($tag_names as $tag_name) {
614
+ $nodes = $doc->getElementsByTagName($tag_name);
615
+
616
+ foreach($nodes as $node) {
617
+ self::remove_text_nodes($node);
618
+ }
619
+ }
620
+
621
+ $quirksmode = ($tokenizer->getTree()->getQuirksMode() > HTML5_TreeBuilder::NO_QUIRKS);
622
+ }
623
+ else {
624
+ $doc = new DOMDocument();
625
+ $doc->preserveWhiteSpace = true;
626
+ $doc->loadHTML($str);
627
+
628
+ // If some text is before the doctype, we are in quirksmode
629
+ if ( preg_match("/^(.+)<!doctype/i", ltrim($str), $matches) ) {
630
+ $quirksmode = true;
631
+ }
632
+ // If no doctype is provided, we are in quirksmode
633
+ elseif ( !preg_match("/^<!doctype/i", ltrim($str), $matches) ) {
634
+ $quirksmode = true;
635
+ }
636
+ else {
637
+ // HTML5 <!DOCTYPE html>
638
+ if ( !$doc->doctype->publicId && !$doc->doctype->systemId ) {
639
+ $quirksmode = false;
640
+ }
641
+
642
+ // not XHTML
643
+ if ( !preg_match("/xhtml/i", $doc->doctype->publicId) ) {
644
+ $quirksmode = true;
645
+ }
646
+ }
647
+ }
648
+
649
+ $this->_xml = $doc;
650
+ $this->_quirksmode = $quirksmode;
651
+
652
+ $this->_tree = new Frame_Tree($this->_xml);
653
+
654
+ restore_error_handler();
655
+
656
+ $this->restore_locale();
657
+ }
658
+
659
+ static function remove_text_nodes(DOMNode $node) {
660
+ $children = array();
661
+ for ($i = 0; $i < $node->childNodes->length; $i++) {
662
+ $child = $node->childNodes->item($i);
663
+ if ( $child->nodeName === "#text" ) {
664
+ $children[] = $child;
665
+ }
666
+ }
667
+
668
+ foreach($children as $child) {
669
+ $node->removeChild($child);
670
+ }
671
+ }
672
+
673
+ /**
674
+ * Builds the {@link Frame_Tree}, loads any CSS and applies the styles to
675
+ * the {@link Frame_Tree}
676
+ */
677
+ protected function _process_html() {
678
+ $this->_tree->build_tree();
679
+
680
+ $this->_css->load_css_file(Stylesheet::DEFAULT_STYLESHEET, Stylesheet::ORIG_UA);
681
+
682
+ $acceptedmedia = Stylesheet::$ACCEPTED_GENERIC_MEDIA_TYPES;
683
+ $acceptedmedia[] = $this->get_option("default_media_type");
684
+
685
+ // <base href="" />
686
+ $base_nodes = $this->_xml->getElementsByTagName("base");
687
+ if ( $base_nodes->length && ($href = $base_nodes->item(0)->getAttribute("href")) ) {
688
+ list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($href);
689
+ }
690
+
691
+ // Set the base path of the Stylesheet to that of the file being processed
692
+ $this->_css->set_protocol($this->_protocol);
693
+ $this->_css->set_host($this->_base_host);
694
+ $this->_css->set_base_path($this->_base_path);
695
+
696
+ // Get all the stylesheets so that they are processed in document order
697
+ $xpath = new DOMXPath($this->_xml);
698
+ $stylesheets = $xpath->query("//*[name() = 'link' or name() = 'style']");
699
+
700
+ foreach($stylesheets as $tag) {
701
+ switch (strtolower($tag->nodeName)) {
702
+ // load <link rel="STYLESHEET" ... /> tags
703
+ case "link":
704
+ if ( mb_strtolower(stripos($tag->getAttribute("rel"), "stylesheet") !== false) || // may be "appendix stylesheet"
705
+ mb_strtolower($tag->getAttribute("type")) === "text/css" ) {
706
+ //Check if the css file is for an accepted media type
707
+ //media not given then always valid
708
+ $formedialist = preg_split("/[\s\n,]/", $tag->getAttribute("media"),-1, PREG_SPLIT_NO_EMPTY);
709
+ if ( count($formedialist) > 0 ) {
710
+ $accept = false;
711
+ foreach ( $formedialist as $type ) {
712
+ if ( in_array(mb_strtolower(trim($type)), $acceptedmedia) ) {
713
+ $accept = true;
714
+ break;
715
+ }
716
+ }
717
+
718
+ if (!$accept) {
719
+ //found at least one mediatype, but none of the accepted ones
720
+ //Skip this css file.
721
+ continue;
722
+ }
723
+ }
724
+
725
+ $url = $tag->getAttribute("href");
726
+ $url = build_url($this->_protocol, $this->_base_host, $this->_base_path, $url);
727
+
728
+ $this->_css->load_css_file($url, Stylesheet::ORIG_AUTHOR);
729
+ }
730
+ break;
731
+
732
+ // load <style> tags
733
+ case "style":
734
+ // Accept all <style> tags by default (note this is contrary to W3C
735
+ // HTML 4.0 spec:
736
+ // http://www.w3.org/TR/REC-html40/present/styles.html#adef-media
737
+ // which states that the default media type is 'screen'
738
+ if ( $tag->hasAttributes() &&
739
+ ($media = $tag->getAttribute("media")) &&
740
+ !in_array($media, $acceptedmedia) ) {
741
+ continue;
742
+ }
743
+
744
+ $css = "";
745
+ if ( $tag->hasChildNodes() ) {
746
+ $child = $tag->firstChild;
747
+ while ( $child ) {
748
+ $css .= $child->nodeValue; // Handle <style><!-- blah --></style>
749
+ $child = $child->nextSibling;
750
+ }
751
+ }
752
+ else {
753
+ $css = $tag->nodeValue;
754
+ }
755
+
756
+ $this->_css->load_css($css);
757
+ break;
758
+ }
759
+ }
760
+ }
761
+
762
+ /**
763
+ * Sets the paper size & orientation
764
+ *
765
+ * @param string $size 'letter', 'legal', 'A4', etc. {@link CPDF_Adapter::$PAPER_SIZES}
766
+ * @param string $orientation 'portrait' or 'landscape'
767
+ */
768
+ function set_paper($size, $orientation = "portrait") {
769
+ $this->_paper_size = $size;
770
+ $this->_paper_orientation = $orientation;
771
+ }
772
+
773
+ /**
774
+ * Enable experimental caching capability
775
+ * @access private
776
+ */
777
+ function enable_caching($cache_id) {
778
+ $this->_cache_id = $cache_id;
779
+ }
780
+
781
+ /**
782
+ * Sets callbacks for events like rendering of pages and elements.
783
+ * The callbacks array contains arrays with 'event' set to 'begin_page',
784
+ * 'end_page', 'begin_frame', or 'end_frame' and 'f' set to a function or
785
+ * object plus method to be called.
786
+ *
787
+ * The function 'f' must take an array as argument, which contains info
788
+ * about the event.
789
+ *
790
+ * @param array $callbacks the set of callbacks to set
791
+ */
792
+ function set_callbacks($callbacks) {
793
+ if (is_array($callbacks)) {
794
+ $this->_callbacks = array();
795
+ foreach ($callbacks as $c) {
796
+ if (is_array($c) && isset($c['event']) && isset($c['f'])) {
797
+ $event = $c['event'];
798
+ $f = $c['f'];
799
+ if (is_callable($f) && is_string($event)) {
800
+ $this->_callbacks[$event][] = $f;
801
+ }
802
+ }
803
+ }
804
+ }
805
+ }
806
+
807
+ /**
808
+ * Get the quirks mode
809
+ *
810
+ * @return boolean true if quirks mode is active
811
+ */
812
+ function get_quirksmode(){
813
+ return $this->_quirksmode;
814
+ }
815
+
816
+ function parse_default_view($value) {
817
+ $valid = array("XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV");
818
+
819
+ $options = preg_split("/\s*,\s*/", trim($value));
820
+ $default_view = array_shift($options);
821
+
822
+ if ( !in_array($default_view, $valid) ) {
823
+ return false;
824
+ }
825
+
826
+ $this->set_default_view($default_view, $options);
827
+ return true;
828
+ }
829
+
830
+ /**
831
+ * Renders the HTML to PDF
832
+ */
833
+ function render() {
834
+ $this->save_locale();
835
+
836
+ $log_output_file = $this->get_option("log_output_file");
837
+ if ( $log_output_file ) {
838
+ if ( !file_exists($log_output_file) && is_writable(dirname($log_output_file)) ) {
839
+ touch($log_output_file);
840
+ }
841
+
842
+ $this->_start_time = microtime(true);
843
+ ob_start();
844
+ }
845
+
846
+ //enable_mem_profile();
847
+
848
+ $this->_process_html();
849
+
850
+ $this->_css->apply_styles($this->_tree);
851
+
852
+ // @page style rules : size, margins
853
+ $page_styles = $this->_css->get_page_styles();
854
+
855
+ $base_page_style = $page_styles["base"];
856
+ unset($page_styles["base"]);
857
+
858
+ foreach($page_styles as $_page_style) {
859
+ $_page_style->inherit($base_page_style);
860
+ }
861
+
862
+ if ( is_array($base_page_style->size) ) {
863
+ $this->set_paper(array(0, 0, $base_page_style->size[0], $base_page_style->size[1]));
864
+ }
865
+
866
+ $this->_pdf = Canvas_Factory::get_instance($this, $this->_paper_size, $this->_paper_orientation);
867
+ Font_Metrics::init($this->_pdf);
868
+
869
+ if ( $this->get_option("enable_font_subsetting") && $this->_pdf instanceof CPDF_Adapter ) {
870
+ foreach ($this->_tree->get_frames() as $frame) {
871
+ $style = $frame->get_style();
872
+ $node = $frame->get_node();
873
+
874
+ // Handle text nodes
875
+ if ( $node->nodeName === "#text" ) {
876
+ $this->_pdf->register_string_subset($style->font_family, $node->nodeValue);
877
+ continue;
878
+ }
879
+
880
+ // Handle generated content (list items)
881
+ if ( $style->display === "list-item" ) {
882
+ $chars = List_Bullet_Renderer::get_counter_chars($style->list_style_type);
883
+ $this->_pdf->register_string_subset($style->font_family, $chars);
884
+ continue;
885
+ }
886
+
887
+ // TODO Handle other generated content (pseudo elements)
888
+ }
889
+ }
890
+
891
+ $root = null;
892
+
893
+ foreach ($this->_tree->get_frames() as $frame) {
894
+ // Set up the root frame
895
+ if ( is_null($root) ) {
896
+ $root = Frame_Factory::decorate_root( $this->_tree->get_root(), $this );
897
+ continue;
898
+ }
899
+
900
+ // Create the appropriate decorators, reflowers & positioners.
901
+ Frame_Factory::decorate_frame($frame, $this, $root);
902
+ }
903
+
904
+ // Add meta information
905
+ $title = $this->_xml->getElementsByTagName("title");
906
+ if ( $title->length ) {
907
+ $this->_pdf->add_info("Title", trim($title->item(0)->nodeValue));
908
+ }
909
+
910
+ $metas = $this->_xml->getElementsByTagName("meta");
911
+ $labels = array(
912
+ "author" => "Author",
913
+ "keywords" => "Keywords",
914
+ "description" => "Subject",
915
+ );
916
+ foreach($metas as $meta) {
917
+ $name = mb_strtolower($meta->getAttribute("name"));
918
+ $value = trim($meta->getAttribute("content"));
919
+
920
+ if ( isset($labels[$name]) ) {
921
+ $this->_pdf->add_info($labels[$name], $value);
922
+ continue;
923
+ }
924
+
925
+ if ( $name === "dompdf.view" && $this->parse_default_view($value) ) {
926
+ $this->_pdf->set_default_view($this->_default_view, $this->_default_view_options);
927
+ }
928
+ }
929
+
930
+ $root->set_containing_block(0, 0, $this->_pdf->get_width(), $this->_pdf->get_height());
931
+ $root->set_renderer(new Renderer($this));
932
+
933
+ // This is where the magic happens:
934
+ $root->reflow();
935
+
936
+ // Clean up cached images
937
+ Image_Cache::clear();
938
+
939
+ global $_dompdf_warnings, $_dompdf_show_warnings;
940
+ if ( $_dompdf_show_warnings ) {
941
+ echo '<b>DOMPDF Warnings</b><br><pre>';
942
+ foreach ($_dompdf_warnings as $msg) {
943
+ echo $msg . "\n";
944
+ }
945
+ echo $this->get_canvas()->get_cpdf()->messages;
946
+ echo '</pre>';
947
+ flush();
948
+ }
949
+
950
+ $this->restore_locale();
951
+ }
952
+
953
+ /**
954
+ * Add meta information to the PDF after rendering
955
+ */
956
+ function add_info($label, $value) {
957
+ if ( !is_null($this->_pdf) ) {
958
+ $this->_pdf->add_info($label, $value);
959
+ }
960
+ }
961
+
962
+ /**
963
+ * Writes the output buffer in the log file
964
+ *
965
+ * @return void
966
+ */
967
+ private function write_log() {
968
+ $log_output_file = $this->get_option("log_output_file");
969
+ if ( !$log_output_file || !is_writable($log_output_file) ) {
970
+ return;
971
+ }
972
+
973
+ $frames = Frame::$ID_COUNTER;
974
+ $memory = DOMPDF_memory_usage() / 1024;
975
+ $time = (microtime(true) - $this->_start_time) * 1000;
976
+
977
+ $out = sprintf(
978
+ "<span style='color: #000' title='Frames'>%6d</span>".
979
+ "<span style='color: #009' title='Memory'>%10.2f KB</span>".
980
+ "<span style='color: #900' title='Time'>%10.2f ms</span>".
981
+ "<span title='Quirksmode'> ".
982
+ ($this->_quirksmode ? "<span style='color: #d00'> ON</span>" : "<span style='color: #0d0'>OFF</span>").
983
+ "</span><br />", $frames, $memory, $time);
984
+
985
+ $out .= ob_get_clean();
986
+
987
+ $log_output_file = $this->get_option("log_output_file");
988
+ file_put_contents($log_output_file, $out);
989
+ }
990
+
991
+ /**
992
+ * Streams the PDF to the client
993
+ *
994
+ * The file will open a download dialog by default. The options
995
+ * parameter controls the output. Accepted options are:
996
+ *
997
+ * 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this
998
+ * header is not included, off by default this header seems to
999
+ * have caused some problems despite the fact that it is supposed
1000
+ * to solve them, so I am leaving it off by default.
1001
+ *
1002
+ * 'compress' = > 1 or 0 - apply content stream compression, this is
1003
+ * on (1) by default
1004
+ *
1005
+ * 'Attachment' => 1 or 0 - if 1, force the browser to open a
1006
+ * download dialog, on (1) by default
1007
+ *
1008
+ * @param string $filename the name of the streamed file
1009
+ * @param array $options header options (see above)
1010
+ */
1011
+ function stream($filename, $options = null) {
1012
+ $this->save_locale();
1013
+
1014
+ $this->write_log();
1015
+
1016
+ if ( !is_null($this->_pdf) ) {
1017
+ $this->_pdf->stream($filename, $options);
1018
+ }
1019
+
1020
+ $this->restore_locale();
1021
+ }
1022
+
1023
+ /**
1024
+ * Returns the PDF as a string
1025
+ *
1026
+ * The file will open a download dialog by default. The options
1027
+ * parameter controls the output. Accepted options are:
1028
+ *
1029
+ *
1030
+ * 'compress' = > 1 or 0 - apply content stream compression, this is
1031
+ * on (1) by default
1032
+ *
1033
+ *
1034
+ * @param array $options options (see above)
1035
+ *
1036
+ * @return string
1037
+ */
1038
+ function output($options = null) {
1039
+ $this->save_locale();
1040
+
1041
+ $this->write_log();
1042
+
1043
+ if ( is_null($this->_pdf) ) {
1044
+ return null;
1045
+ }
1046
+
1047
+ $output = $this->_pdf->output( $options );
1048
+
1049
+ $this->restore_locale();
1050
+
1051
+ return $output;
1052
+ }
1053
+
1054
+ /**
1055
+ * Returns the underlying HTML document as a string
1056
+ *
1057
+ * @return string
1058
+ */
1059
+ function output_html() {
1060
+ return $this->_xml->saveHTML();
1061
+ }
1062
+ }
lib/dompdf/include/dompdf_exception.cls.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Standard exception thrown by DOMPDF classes
11
+ *
12
+ * @package dompdf
13
+ */
14
+ class DOMPDF_Exception extends Exception {
15
+
16
+ /**
17
+ * Class constructor
18
+ *
19
+ * @param string $message Error message
20
+ * @param int $code Error code
21
+ */
22
+ function __construct($message = null, $code = 0) {
23
+ parent::__construct($message, $code);
24
+ }
25
+
26
+ }
lib/dompdf/include/dompdf_image_exception.cls.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Image exception thrown by DOMPDF
11
+ *
12
+ * @package dompdf
13
+ */
14
+ class DOMPDF_Image_Exception extends DOMPDF_Exception {
15
+
16
+ /**
17
+ * Class constructor
18
+ *
19
+ * @param string $message Error message
20
+ * @param int $code Error code
21
+ */
22
+ function __construct($message = null, $code = 0) {
23
+ parent::__construct($message, $code);
24
+ }
25
+
26
+ }
lib/dompdf/include/file.skel ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author ...
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
lib/dompdf/include/fixed_positioner.cls.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Positions fixely positioned frames
12
+ */
13
+ class Fixed_Positioner extends Positioner {
14
+
15
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
16
+
17
+ function position() {
18
+
19
+ $frame = $this->_frame;
20
+ $style = $frame->get_original_style();
21
+ $root = $frame->get_root();
22
+ $initialcb = $root->get_containing_block();
23
+ $initialcb_style = $root->get_style();
24
+
25
+ $p = $frame->find_block_parent();
26
+ if ( $p ) {
27
+ $p->add_line();
28
+ }
29
+
30
+ // Compute the margins of the @page style
31
+ $margin_top = $initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]);
32
+ $margin_right = $initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]);
33
+ $margin_bottom = $initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]);
34
+ $margin_left = $initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]);
35
+
36
+ // The needed computed style of the element
37
+ $height = $style->length_in_pt($style->height, $initialcb["h"]);
38
+ $width = $style->length_in_pt($style->width, $initialcb["w"]);
39
+
40
+ $top = $style->length_in_pt($style->top, $initialcb["h"]);
41
+ $right = $style->length_in_pt($style->right, $initialcb["w"]);
42
+ $bottom = $style->length_in_pt($style->bottom, $initialcb["h"]);
43
+ $left = $style->length_in_pt($style->left, $initialcb["w"]);
44
+
45
+ $y = $margin_top;
46
+ if ( isset($top) ) {
47
+ $y = $top + $margin_top;
48
+ if ( $top === "auto" ) {
49
+ $y = $margin_top;
50
+ if ( isset($bottom) && $bottom !== "auto" ) {
51
+ $y = $initialcb["h"] - $bottom - $margin_bottom;
52
+ $margin_height = $this->_frame->get_margin_height();
53
+ if ( $margin_height !== "auto" ) {
54
+ $y -= $margin_height;
55
+ }
56
+ else {
57
+ $y -= $height;
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ $x = $margin_left;
64
+ if ( isset($left) ) {
65
+ $x = $left + $margin_left;
66
+ if ( $left === "auto" ) {
67
+ $x = $margin_left;
68
+ if ( isset($right) && $right !== "auto" ) {
69
+ $x = $initialcb["w"] - $right - $margin_right;
70
+ $margin_width = $this->_frame->get_margin_width();
71
+ if ( $margin_width !== "auto" ) {
72
+ $x -= $margin_width;
73
+ }
74
+ else {
75
+ $x -= $width;
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ $frame->set_position($x, $y);
82
+
83
+ $children = $frame->get_children();
84
+ foreach($children as $child) {
85
+ $child->set_position($x, $y);
86
+ }
87
+ }
88
+ }
lib/dompdf/include/font_metrics.cls.php ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ require_once DOMPDF_LIB_DIR . "/class.pdf.php";
12
+
13
+ /**
14
+ * Name of the font cache file
15
+ *
16
+ * This file must be writable by the webserver process only to update it
17
+ * with save_font_families() after adding the .afm file references of a new font family
18
+ * with Font_Metrics::save_font_families().
19
+ * This is typically done only from command line with load_font.php on converting
20
+ * ttf fonts to ufm with php-font-lib.
21
+ *
22
+ * Declared here because PHP5 prevents constants from being declared with expressions
23
+ */
24
+ define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache.php");
25
+
26
+ /**
27
+ * The font metrics class
28
+ *
29
+ * This class provides information about fonts and text. It can resolve
30
+ * font names into actual installed font files, as well as determine the
31
+ * size of text in a particular font and size.
32
+ *
33
+ * @static
34
+ * @package dompdf
35
+ */
36
+ class Font_Metrics {
37
+
38
+ /**
39
+ * @see __DOMPDF_FONT_CACHE_FILE
40
+ */
41
+ const CACHE_FILE = __DOMPDF_FONT_CACHE_FILE;
42
+
43
+ /**
44
+ * Underlying {@link Canvas} object to perform text size calculations
45
+ *
46
+ * @var Canvas
47
+ */
48
+ static protected $_pdf = null;
49
+
50
+ /**
51
+ * Array of font family names to font files
52
+ *
53
+ * Usually cached by the {@link load_font.php} script
54
+ *
55
+ * @var array
56
+ */
57
+ static protected $_font_lookup = array();
58
+
59
+
60
+ /**
61
+ * Class initialization
62
+ *
63
+ */
64
+ static function init(Canvas $canvas = null) {
65
+ if (!self::$_pdf) {
66
+ if (!$canvas) {
67
+ $canvas = Canvas_Factory::get_instance(new DOMPDF());
68
+ }
69
+
70
+ self::$_pdf = $canvas;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Calculates text size, in points
76
+ *
77
+ * @param string $text the text to be sized
78
+ * @param string $font the desired font
79
+ * @param float $size the desired font size
80
+ * @param float $word_spacing
81
+ * @param float $char_spacing
82
+ *
83
+ * @internal param float $spacing word spacing, if any
84
+ * @return float
85
+ */
86
+ static function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
87
+ //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
88
+
89
+ // @todo Make sure this cache is efficient before enabling it
90
+ static $cache = array();
91
+
92
+ if ( $text === "" ) {
93
+ return 0;
94
+ }
95
+
96
+ // Don't cache long strings
97
+ $use_cache = !isset($text[50]); // Faster than strlen
98
+
99
+ $key = "$font/$size/$word_spacing/$char_spacing";
100
+
101
+ if ( $use_cache && isset($cache[$key][$text]) ) {
102
+ return $cache[$key]["$text"];
103
+ }
104
+
105
+ $width = self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
106
+
107
+ if ( $use_cache ) {
108
+ $cache[$key][$text] = $width;
109
+ }
110
+
111
+ return $width;
112
+ }
113
+
114
+ /**
115
+ * Calculates font height
116
+ *
117
+ * @param string $font
118
+ * @param float $size
119
+ * @return float
120
+ */
121
+ static function get_font_height($font, $size) {
122
+ return self::$_pdf->get_font_height($font, $size);
123
+ }
124
+
125
+ /**
126
+ * Resolves a font family & subtype into an actual font file
127
+ * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If
128
+ * the particular font family has no suitable font file, the default font
129
+ * ({@link DOMPDF_DEFAULT_FONT}) is used. The font file returned
130
+ * is the absolute pathname to the font file on the system.
131
+ *
132
+ * @param string $family_raw
133
+ * @param string $subtype_raw
134
+ *
135
+ * @return string
136
+ */
137
+ static function get_font($family_raw, $subtype_raw = "normal") {
138
+ static $cache = array();
139
+
140
+ if ( isset($cache[$family_raw][$subtype_raw]) ) {
141
+ return $cache[$family_raw][$subtype_raw];
142
+ }
143
+
144
+ /* Allow calling for various fonts in search path. Therefore not immediately
145
+ * return replacement on non match.
146
+ * Only when called with NULL try replacement.
147
+ * When this is also missing there is really trouble.
148
+ * If only the subtype fails, nevertheless return failure.
149
+ * Only on checking the fallback font, check various subtypes on same font.
150
+ */
151
+
152
+ $subtype = strtolower($subtype_raw);
153
+
154
+ if ( $family_raw ) {
155
+ $family = str_replace( array("'", '"'), "", strtolower($family_raw));
156
+
157
+ if ( isset(self::$_font_lookup[$family][$subtype]) ) {
158
+ return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
159
+ }
160
+
161
+ return null;
162
+ }
163
+
164
+ $family = "serif";
165
+
166
+ if ( isset(self::$_font_lookup[$family][$subtype]) ) {
167
+ return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
168
+ }
169
+
170
+ if ( !isset(self::$_font_lookup[$family]) ) {
171
+ return null;
172
+ }
173
+
174
+ $family = self::$_font_lookup[$family];
175
+
176
+ foreach ( $family as $sub => $font ) {
177
+ if (strpos($subtype, $sub) !== false) {
178
+ return $cache[$family_raw][$subtype_raw] = $font;
179
+ }
180
+ }
181
+
182
+ if ($subtype !== "normal") {
183
+ foreach ( $family as $sub => $font ) {
184
+ if ($sub !== "normal") {
185
+ return $cache[$family_raw][$subtype_raw] = $font;
186
+ }
187
+ }
188
+ }
189
+
190
+ $subtype = "normal";
191
+
192
+ if ( isset($family[$subtype]) ) {
193
+ return $cache[$family_raw][$subtype_raw] = $family[$subtype];
194
+ }
195
+
196
+ return null;
197
+ }
198
+
199
+ static function get_family($family) {
200
+ $family = str_replace( array("'", '"'), "", mb_strtolower($family));
201
+
202
+ if ( isset(self::$_font_lookup[$family]) ) {
203
+ return self::$_font_lookup[$family];
204
+ }
205
+
206
+ return null;
207
+ }
208
+
209
+ /**
210
+ * Saves the stored font family cache
211
+ *
212
+ * The name and location of the cache file are determined by {@link
213
+ * Font_Metrics::CACHE_FILE}. This file should be writable by the
214
+ * webserver process.
215
+ *
216
+ * @see Font_Metrics::load_font_families()
217
+ */
218
+ static function save_font_families() {
219
+ // replace the path to the DOMPDF font directories with the corresponding constants (allows for more portability)
220
+ $cache_data = var_export(self::$_font_lookup, true);
221
+ $cache_data = str_replace('\''.DOMPDF_FONT_DIR , 'DOMPDF_FONT_DIR . \'' , $cache_data);
222
+ $cache_data = str_replace('\''.DOMPDF_DIR , 'DOMPDF_DIR . \'' , $cache_data);
223
+ $cache_data = "<"."?php return $cache_data ?".">";
224
+ file_put_contents(self::CACHE_FILE, $cache_data);
225
+ }
226
+
227
+ /**
228
+ * Loads the stored font family cache
229
+ *
230
+ * @see save_font_families()
231
+ */
232
+ static function load_font_families() {
233
+ $dist_fonts = require_once DOMPDF_DIR . "/lib/fonts/dompdf_font_family_cache.dist.php";
234
+
235
+ // FIXME: temporary step for font cache created before the font cache fix
236
+ if ( is_readable( DOMPDF_FONT_DIR . "dompdf_font_family_cache" ) ) {
237
+ $old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache";
238
+ // If the font family cache is still in the old format
239
+ if ( $old_fonts === 1 ) {
240
+ $cache_data = file_get_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache");
241
+ file_put_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache", "<"."?php return $cache_data ?".">");
242
+ $old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache";
243
+ }
244
+ $dist_fonts += $old_fonts;
245
+ }
246
+
247
+ if ( !is_readable(self::CACHE_FILE) ) {
248
+ self::$_font_lookup = $dist_fonts;
249
+ return;
250
+ }
251
+
252
+ self::$_font_lookup = require_once self::CACHE_FILE;
253
+
254
+ // If the font family cache is still in the old format
255
+ if ( self::$_font_lookup === 1 ) {
256
+ $cache_data = file_get_contents(self::CACHE_FILE);
257
+ file_put_contents(self::CACHE_FILE, "<"."?php return $cache_data ?".">");
258
+ self::$_font_lookup = require_once self::CACHE_FILE;
259
+ }
260
+
261
+ // Merge provided fonts
262
+ self::$_font_lookup += $dist_fonts;
263
+ }
264
+
265
+ static function get_type($type) {
266
+ if (preg_match("/bold/i", $type)) {
267
+ if (preg_match("/italic|oblique/i", $type)) {
268
+ $type = "bold_italic";
269
+ }
270
+ else {
271
+ $type = "bold";
272
+ }
273
+ }
274
+ elseif (preg_match("/italic|oblique/i", $type)) {
275
+ $type = "italic";
276
+ }
277
+ else {
278
+ $type = "normal";
279
+ }
280
+
281
+ return $type;
282
+ }
283
+
284
+ static function install_fonts($files) {
285
+ $names = array();
286
+
287
+ foreach($files as $file) {
288
+ $font = Font::load($file);
289
+ $records = $font->getData("name", "records");
290
+ $type = self::get_type($records[2]);
291
+ $names[mb_strtolower($records[1])][$type] = $file;
292
+ }
293
+
294
+ return $names;
295
+ }
296
+
297
+ static function get_system_fonts() {
298
+ $files = glob("/usr/share/fonts/truetype/*.ttf") +
299
+ glob("/usr/share/fonts/truetype/*/*.ttf") +
300
+ glob("/usr/share/fonts/truetype/*/*/*.ttf") +
301
+ glob("C:\\Windows\\fonts\\*.ttf") +
302
+ glob("C:\\WinNT\\fonts\\*.ttf") +
303
+ glob("/mnt/c_drive/WINDOWS/Fonts/");
304
+
305
+ return self::install_fonts($files);
306
+ }
307
+
308
+ /**
309
+ * Returns the current font lookup table
310
+ *
311
+ * @return array
312
+ */
313
+ static function get_font_families() {
314
+ return self::$_font_lookup;
315
+ }
316
+
317
+ static function set_font_family($fontname, $entry) {
318
+ self::$_font_lookup[mb_strtolower($fontname)] = $entry;
319
+ }
320
+
321
+ static function register_font($style, $remote_file) {
322
+ $fontname = mb_strtolower($style["family"]);
323
+ $families = Font_Metrics::get_font_families();
324
+
325
+ $entry = array();
326
+ if ( isset($families[$fontname]) ) {
327
+ $entry = $families[$fontname];
328
+ }
329
+
330
+ $local_file = DOMPDF_FONT_DIR . md5($remote_file);
331
+ $cache_entry = $local_file;
332
+ $local_file .= ".ttf";
333
+
334
+ $style_string = Font_Metrics::get_type("{$style['weight']} {$style['style']}");
335
+
336
+ if ( !isset($entry[$style_string]) ) {
337
+ $entry[$style_string] = $cache_entry;
338
+
339
+ Font_Metrics::set_font_family($fontname, $entry);
340
+
341
+ // Download the remote file
342
+ if ( !is_file($local_file) ) {
343
+ file_put_contents($local_file, file_get_contents($remote_file));
344
+ }
345
+
346
+ $font = Font::load($local_file);
347
+
348
+ if (!$font) {
349
+ return false;
350
+ }
351
+
352
+ $font->parse();
353
+ $font->saveAdobeFontMetrics("$cache_entry.ufm");
354
+
355
+ // Save the changes
356
+ Font_Metrics::save_font_families();
357
+ }
358
+
359
+ return true;
360
+ }
361
+ }
362
+
363
+ Font_Metrics::load_font_families();
lib/dompdf/include/frame.cls.php ADDED
@@ -0,0 +1,1191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * The main Frame class
11
+ *
12
+ * This class represents a single HTML element. This class stores
13
+ * positioning information as well as containing block location and
14
+ * dimensions. Style information for the element is stored in a {@link
15
+ * Style} object. Tree structure is maintained via the parent & children
16
+ * links.
17
+ *
18
+ * @access protected
19
+ * @package dompdf
20
+ */
21
+ class Frame {
22
+
23
+ /**
24
+ * The DOMElement or DOMText object this frame represents
25
+ *
26
+ * @var DOMElement|DOMText
27
+ */
28
+ protected $_node;
29
+
30
+ /**
31
+ * Unique identifier for this frame. Used to reference this frame
32
+ * via the node.
33
+ *
34
+ * @var string
35
+ */
36
+ protected $_id;
37
+
38
+ /**
39
+ * Unique id counter
40
+ */
41
+ static /*protected*/ $ID_COUNTER = 0;
42
+
43
+ /**
44
+ * This frame's calculated style
45
+ *
46
+ * @var Style
47
+ */
48
+ protected $_style;
49
+
50
+ /**
51
+ * This frame's original style. Needed for cases where frames are
52
+ * split across pages.
53
+ *
54
+ * @var Style
55
+ */
56
+ protected $_original_style;
57
+
58
+ /**
59
+ * This frame's parent in the document tree.
60
+ *
61
+ * @var Frame
62
+ */
63
+ protected $_parent;
64
+
65
+ /**
66
+ * This frame's children
67
+ *
68
+ * @var Frame[]
69
+ */
70
+ protected $_frame_list;
71
+
72
+ /**
73
+ * This frame's first child. All children are handled as a
74
+ * doubly-linked list.
75
+ *
76
+ * @var Frame
77
+ */
78
+ protected $_first_child;
79
+
80
+ /**
81
+ * This frame's last child.
82
+ *
83
+ * @var Frame
84
+ */
85
+ protected $_last_child;
86
+
87
+ /**
88
+ * This frame's previous sibling in the document tree.
89
+ *
90
+ * @var Frame
91
+ */
92
+ protected $_prev_sibling;
93
+
94
+ /**
95
+ * This frame's next sibling in the document tree.
96
+ *
97
+ * @var Frame
98
+ */
99
+ protected $_next_sibling;
100
+
101
+ /**
102
+ * This frame's containing block (used in layout): array(x, y, w, h)
103
+ *
104
+ * @var float[]
105
+ */
106
+ protected $_containing_block;
107
+
108
+ /**
109
+ * Position on the page of the top-left corner of the margin box of
110
+ * this frame: array(x,y)
111
+ *
112
+ * @var float[]
113
+ */
114
+ protected $_position;
115
+
116
+ /**
117
+ * Absolute opacity of this frame
118
+ *
119
+ * @var float
120
+ */
121
+ protected $_opacity;
122
+
123
+ /**
124
+ * This frame's decorator
125
+ *
126
+ * @var Frame_Decorator
127
+ */
128
+ protected $_decorator;
129
+
130
+ /**
131
+ * This frame's containing line box
132
+ *
133
+ * @var Line_Box
134
+ */
135
+ protected $_containing_line;
136
+
137
+ protected $_is_cache = array();
138
+
139
+ /**
140
+ * Tells wether the frame was already pushed to the next page
141
+ *
142
+ * @var bool
143
+ */
144
+ public $_already_pushed = false;
145
+
146
+ public $_float_next_line = false;
147
+
148
+ /**
149
+ * Tells wether the frame was split
150
+ *
151
+ * @var bool
152
+ */
153
+ public $_splitted;
154
+
155
+ static $_ws_state = self::WS_SPACE;
156
+
157
+ const WS_TEXT = 1;
158
+ const WS_SPACE = 2;
159
+
160
+ /**
161
+ * Class destructor
162
+ */
163
+ function __destruct() {
164
+ clear_object($this);
165
+ }
166
+
167
+ /**
168
+ * Class constructor
169
+ *
170
+ * @param DOMNode $node the DOMNode this frame represents
171
+ */
172
+ function __construct(DOMNode $node) {
173
+ $this->_node = $node;
174
+
175
+ $this->_parent = null;
176
+ $this->_first_child = null;
177
+ $this->_last_child = null;
178
+ $this->_prev_sibling = $this->_next_sibling = null;
179
+
180
+ $this->_style = null;
181
+ $this->_original_style = null;
182
+
183
+ $this->_containing_block = array(
184
+ "x" => null,
185
+ "y" => null,
186
+ "w" => null,
187
+ "h" => null,
188
+ );
189
+
190
+ $this->_containing_block[0] =& $this->_containing_block["x"];
191
+ $this->_containing_block[1] =& $this->_containing_block["y"];
192
+ $this->_containing_block[2] =& $this->_containing_block["w"];
193
+ $this->_containing_block[3] =& $this->_containing_block["h"];
194
+
195
+ $this->_position = array(
196
+ "x" => null,
197
+ "y" => null,
198
+ );
199
+
200
+ $this->_position[0] =& $this->_position["x"];
201
+ $this->_position[1] =& $this->_position["y"];
202
+
203
+ $this->_opacity = 1.0;
204
+ $this->_decorator = null;
205
+
206
+ $this->set_id( self::$ID_COUNTER++ );
207
+ }
208
+
209
+ // WIP : preprocessing to remove all the unused whitespace
210
+ protected function ws_trim(){
211
+ if ( $this->ws_keep() ) {
212
+ return;
213
+ }
214
+
215
+ switch(self::$_ws_state) {
216
+ case self::WS_SPACE:
217
+ $node = $this->_node;
218
+
219
+ if ( $node->nodeName === "#text" ) {
220
+ $node->nodeValue = preg_replace("/[ \t\r\n\f]+/u", " ", $node->nodeValue);
221
+
222
+ // starts with a whitespace
223
+ if ( isset($node->nodeValue[0]) && $node->nodeValue[0] === " " ) {
224
+ $node->nodeValue = ltrim($node->nodeValue);
225
+ }
226
+
227
+ // if not empty
228
+ if ( $node->nodeValue !== "" ) {
229
+ // change the current state (text)
230
+ self::$_ws_state = self::WS_TEXT;
231
+
232
+ // ends with a whitespace
233
+ if ( preg_match("/[ \t\r\n\f]+$/u", $node->nodeValue) ) {
234
+ $node->nodeValue = ltrim($node->nodeValue);
235
+ }
236
+ }
237
+ }
238
+ break;
239
+
240
+ case self::WS_TEXT:
241
+ }
242
+ }
243
+
244
+ protected function ws_keep(){
245
+ $whitespace = $this->get_style()->white_space;
246
+ return in_array($whitespace, array("pre", "pre-wrap", "pre-line"));
247
+ }
248
+
249
+ protected function ws_is_text(){
250
+ $node = $this->get_node();
251
+
252
+ if ($node->nodeName === "img") {
253
+ return true;
254
+ }
255
+
256
+ if ( !$this->is_in_flow() ) {
257
+ return false;
258
+ }
259
+
260
+ if ($this->is_text_node()) {
261
+ return trim($node->nodeValue) !== "";
262
+ }
263
+
264
+ return true;
265
+ }
266
+
267
+ /**
268
+ * "Destructor": forcibly free all references held by this frame
269
+ *
270
+ * @param bool $recursive if true, call dispose on all children
271
+ */
272
+ function dispose($recursive = false) {
273
+
274
+ if ( $recursive ) {
275
+ while ( $child = $this->_first_child ) {
276
+ $child->dispose(true);
277
+ }
278
+ }
279
+
280
+ // Remove this frame from the tree
281
+ if ( $this->_prev_sibling ) {
282
+ $this->_prev_sibling->_next_sibling = $this->_next_sibling;
283
+ }
284
+
285
+ if ( $this->_next_sibling ) {
286
+ $this->_next_sibling->_prev_sibling = $this->_prev_sibling;
287
+ }
288
+
289
+ if ( $this->_parent && $this->_parent->_first_child === $this ) {
290
+ $this->_parent->_first_child = $this->_next_sibling;
291
+ }
292
+
293
+ if ( $this->_parent && $this->_parent->_last_child === $this ) {
294
+ $this->_parent->_last_child = $this->_prev_sibling;
295
+ }
296
+
297
+ if ( $this->_parent ) {
298
+ $this->_parent->get_node()->removeChild($this->_node);
299
+ }
300
+
301
+ $this->_style->dispose();
302
+ $this->_style = null;
303
+ unset($this->_style);
304
+
305
+ $this->_original_style->dispose();
306
+ $this->_original_style = null;
307
+ unset($this->_original_style);
308
+
309
+ }
310
+
311
+ // Re-initialize the frame
312
+ function reset() {
313
+ $this->_position["x"] = null;
314
+ $this->_position["y"] = null;
315
+
316
+ $this->_containing_block["x"] = null;
317
+ $this->_containing_block["y"] = null;
318
+ $this->_containing_block["w"] = null;
319
+ $this->_containing_block["h"] = null;
320
+
321
+ $this->_style = null;
322
+ unset($this->_style);
323
+ $this->_style = clone $this->_original_style;
324
+ }
325
+
326
+ //........................................................................
327
+
328
+ /**
329
+ * @return DOMElement|DOMText
330
+ */
331
+ function get_node() {
332
+ return $this->_node;
333
+ }
334
+
335
+ /**
336
+ * @return string
337
+ */
338
+ function get_id() {
339
+ return $this->_id;
340
+ }
341
+
342
+ /**
343
+ * @return Style
344
+ */
345
+ function get_style() {
346
+ return $this->_style;
347
+ }
348
+
349
+ /**
350
+ * @return Style
351
+ */
352
+ function get_original_style() {
353
+ return $this->_original_style;
354
+ }
355
+
356
+ /**
357
+ * @return Frame
358
+ */
359
+ function get_parent() {
360
+ return $this->_parent;
361
+ }
362
+
363
+ /**
364
+ * @return Frame_Decorator
365
+ */
366
+ function get_decorator() {
367
+ return $this->_decorator;
368
+ }
369
+
370
+ /**
371
+ * @return Frame
372
+ */
373
+ function get_first_child() {
374
+ return $this->_first_child;
375
+ }
376
+
377
+ /**
378
+ * @return Frame
379
+ */
380
+ function get_last_child() {
381
+ return $this->_last_child;
382
+ }
383
+
384
+ /**
385
+ * @return Frame
386
+ */
387
+ function get_prev_sibling() {
388
+ return $this->_prev_sibling;
389
+ }
390
+
391
+ /**
392
+ * @return Frame
393
+ */
394
+ function get_next_sibling() {
395
+ return $this->_next_sibling;
396
+ }
397
+
398
+ /**
399
+ * @return FrameList|Frame[]
400
+ */
401
+ function get_children() {
402
+ if ( isset($this->_frame_list) ) {
403
+ return $this->_frame_list;
404
+ }
405
+
406
+ $this->_frame_list = new FrameList($this);
407
+ return $this->_frame_list;
408
+ }
409
+
410
+ // Layout property accessors
411
+
412
+ /**
413
+ * Containing block dimensions
414
+ *
415
+ * @param $i string The key of the wanted containing block's dimension (x, y, x, h)
416
+ *
417
+ * @return float[]|float
418
+ */
419
+ function get_containing_block($i = null) {
420
+ if ( isset($i) ) {
421
+ return $this->_containing_block[$i];
422
+ }
423
+ return $this->_containing_block;
424
+ }
425
+
426
+ /**
427
+ * Block position
428
+ *
429
+ * @param $i string The key of the wanted position value (x, y)
430
+ *
431
+ * @return array|float
432
+ */
433
+ function get_position($i = null) {
434
+ if ( isset($i) ) {
435
+ return $this->_position[$i];
436
+ }
437
+ return $this->_position;
438
+ }
439
+
440
+ //........................................................................
441
+
442
+ /**
443
+ * Return the height of the margin box of the frame, in pt. Meaningless
444
+ * unless the height has been calculated properly.
445
+ *
446
+ * @return float
447
+ */
448
+ function get_margin_height() {
449
+ $style = $this->_style;
450
+
451
+ return $style->length_in_pt(array(
452
+ $style->height,
453
+ $style->margin_top,
454
+ $style->margin_bottom,
455
+ $style->border_top_width,
456
+ $style->border_bottom_width,
457
+ $style->padding_top,
458
+ $style->padding_bottom
459
+ ), $this->_containing_block["h"]);
460
+ }
461
+
462
+ /**
463
+ * Return the width of the margin box of the frame, in pt. Meaningless
464
+ * unless the width has been calculated properly.
465
+ *
466
+ * @return float
467
+ */
468
+ function get_margin_width() {
469
+ $style = $this->_style;
470
+
471
+ return $style->length_in_pt(array(
472
+ $style->width,
473
+ $style->margin_left,
474
+ $style->margin_right,
475
+ $style->border_left_width,
476
+ $style->border_right_width,
477
+ $style->padding_left,
478
+ $style->padding_right
479
+ ), $this->_containing_block["w"]);
480
+ }
481
+
482
+ function get_break_margins(){
483
+ $style = $this->_style;
484
+
485
+ return $style->length_in_pt(array(
486
+ //$style->height,
487
+ $style->margin_top,
488
+ $style->margin_bottom,
489
+ $style->border_top_width,
490
+ $style->border_bottom_width,
491
+ $style->padding_top,
492
+ $style->padding_bottom
493
+ ), $this->_containing_block["h"]);
494
+ }
495
+
496
+ /**
497
+ * Return the padding box (x,y,w,h) of the frame
498
+ *
499
+ * @return array
500
+ */
501
+ function get_padding_box() {
502
+ $style = $this->_style;
503
+ $cb = $this->_containing_block;
504
+
505
+ $x = $this->_position["x"] +
506
+ $style->length_in_pt(array($style->margin_left,
507
+ $style->border_left_width),
508
+ $cb["w"]);
509
+
510
+ $y = $this->_position["y"] +
511
+ $style->length_in_pt(array($style->margin_top,
512
+ $style->border_top_width),
513
+ $cb["h"]);
514
+
515
+ $w = $style->length_in_pt(array($style->padding_left,
516
+ $style->width,
517
+ $style->padding_right),
518
+ $cb["w"]);
519
+
520
+ $h = $style->length_in_pt(array($style->padding_top,
521
+ $style->height,
522
+ $style->padding_bottom),
523
+ $cb["h"]);
524
+
525
+ return array(0 => $x, "x" => $x,
526
+ 1 => $y, "y" => $y,
527
+ 2 => $w, "w" => $w,
528
+ 3 => $h, "h" => $h);
529
+ }
530
+
531
+ /**
532
+ * Return the border box of the frame
533
+ *
534
+ * @return array
535
+ */
536
+ function get_border_box() {
537
+ $style = $this->_style;
538
+ $cb = $this->_containing_block;
539
+
540
+ $x = $this->_position["x"] + $style->length_in_pt($style->margin_left, $cb["w"]);
541
+
542
+ $y = $this->_position["y"] + $style->length_in_pt($style->margin_top, $cb["h"]);
543
+
544
+ $w = $style->length_in_pt(array($style->border_left_width,
545
+ $style->padding_left,
546
+ $style->width,
547
+ $style->padding_right,
548
+ $style->border_right_width),
549
+ $cb["w"]);
550
+
551
+ $h = $style->length_in_pt(array($style->border_top_width,
552
+ $style->padding_top,
553
+ $style->height,
554
+ $style->padding_bottom,
555
+ $style->border_bottom_width),
556
+ $cb["h"]);
557
+
558
+ return array(0 => $x, "x" => $x,
559
+ 1 => $y, "y" => $y,
560
+ 2 => $w, "w" => $w,
561
+ 3 => $h, "h" => $h);
562
+ }
563
+
564
+ function get_opacity($opacity = null) {
565
+ if ( $opacity !== null ) {
566
+ $this->set_opacity($opacity);
567
+ }
568
+ return $this->_opacity;
569
+ }
570
+
571
+ /**
572
+ * @return Line_Box
573
+ */
574
+ function &get_containing_line() {
575
+ return $this->_containing_line;
576
+ }
577
+
578
+ //........................................................................
579
+
580
+ // Set methods
581
+ function set_id($id) {
582
+ $this->_id = $id;
583
+
584
+ // We can only set attributes of DOMElement objects (nodeType == 1).
585
+ // Since these are the only objects that we can assign CSS rules to,
586
+ // this shortcoming is okay.
587
+ if ( $this->_node->nodeType == XML_ELEMENT_NODE ) {
588
+ $this->_node->setAttribute("frame_id", $id);
589
+ }
590
+ }
591
+
592
+ function set_style(Style $style) {
593
+ if ( is_null($this->_style) ) {
594
+ $this->_original_style = clone $style;
595
+ }
596
+
597
+ //$style->set_frame($this);
598
+ $this->_style = $style;
599
+ }
600
+
601
+ function set_decorator(Frame_Decorator $decorator) {
602
+ $this->_decorator = $decorator;
603
+ }
604
+
605
+ function set_containing_block($x = null, $y = null, $w = null, $h = null) {
606
+ if ( is_array($x) ){
607
+ foreach($x as $key => $val){
608
+ $$key = $val;
609
+ }
610
+ }
611
+
612
+ if (is_numeric($x)) {
613
+ $this->_containing_block["x"] = $x;
614
+ }
615
+
616
+ if (is_numeric($y)) {
617
+ $this->_containing_block["y"] = $y;
618
+ }
619
+
620
+ if (is_numeric($w)) {
621
+ $this->_containing_block["w"] = $w;
622
+ }
623
+
624
+ if (is_numeric($h)) {
625
+ $this->_containing_block["h"] = $h;
626
+ }
627
+ }
628
+
629
+ function set_position($x = null, $y = null) {
630
+ if ( is_array($x) ) {
631
+ list($x, $y) = array($x["x"], $x["y"]);
632
+ }
633
+
634
+ if ( is_numeric($x) ) {
635
+ $this->_position["x"] = $x;
636
+ }
637
+
638
+ if ( is_numeric($y) ) {
639
+ $this->_position["y"] = $y;
640
+ }
641
+ }
642
+
643
+ function set_opacity($opacity) {
644
+ $parent = $this->get_parent();
645
+ $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0);
646
+ $this->_opacity = $base_opacity * $opacity;
647
+ }
648
+
649
+ function set_containing_line(Line_Box $line) {
650
+ $this->_containing_line = $line;
651
+ }
652
+
653
+ //........................................................................
654
+
655
+ /**
656
+ * Tells if the frame is a text node
657
+ * @return bool
658
+ */
659
+ function is_text_node() {
660
+ if ( isset($this->_is_cache["text_node"]) ) {
661
+ return $this->_is_cache["text_node"];
662
+ }
663
+
664
+ return $this->_is_cache["text_node"] = ($this->get_node()->nodeName === "#text");
665
+ }
666
+
667
+ function is_positionned() {
668
+ if ( isset($this->_is_cache["positionned"]) ) {
669
+ return $this->_is_cache["positionned"];
670
+ }
671
+
672
+ $position = $this->get_style()->position;
673
+
674
+ return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES);
675
+ }
676
+
677
+ function is_absolute() {
678
+ if ( isset($this->_is_cache["absolute"]) ) {
679
+ return $this->_is_cache["absolute"];
680
+ }
681
+
682
+ $position = $this->get_style()->position;
683
+
684
+ return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed");
685
+ }
686
+
687
+ function is_block() {
688
+ if ( isset($this->_is_cache["block"]) ) {
689
+ return $this->_is_cache["block"];
690
+ }
691
+
692
+ return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES);
693
+ }
694
+
695
+ function is_in_flow() {
696
+ if ( isset($this->_is_cache["in_flow"]) ) {
697
+ return $this->_is_cache["in_flow"];
698
+ }
699
+
700
+ $enable_css_float = $this->get_style()->get_stylesheet()->get_dompdf()->get_option("enable_css_float");
701
+ return $this->_is_cache["in_flow"] = !($enable_css_float && $this->get_style()->float !== "none" || $this->is_absolute());
702
+ }
703
+
704
+ function is_pre(){
705
+ if ( isset($this->_is_cache["pre"]) ) {
706
+ return $this->_is_cache["pre"];
707
+ }
708
+
709
+ $white_space = $this->get_style()->white_space;
710
+
711
+ return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap"));
712
+ }
713
+
714
+ function is_table(){
715
+ if ( isset($this->_is_cache["table"]) ) {
716
+ return $this->_is_cache["table"];
717
+ }
718
+
719
+ $display = $this->get_style()->display;
720
+
721
+ return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES);
722
+ }
723
+
724
+
725
+ /**
726
+ * Inserts a new child at the beginning of the Frame
727
+ *
728
+ * @param $child Frame The new Frame to insert
729
+ * @param $update_node boolean Whether or not to update the DOM
730
+ */
731
+ function prepend_child(Frame $child, $update_node = true) {
732
+ if ( $update_node ) {
733
+ $this->_node->insertBefore($child->_node, $this->_first_child ? $this->_first_child->_node : null);
734
+ }
735
+
736
+ // Remove the child from its parent
737
+ if ( $child->_parent ) {
738
+ $child->_parent->remove_child($child, false);
739
+ }
740
+
741
+ $child->_parent = $this;
742
+ $child->_prev_sibling = null;
743
+
744
+ // Handle the first child
745
+ if ( !$this->_first_child ) {
746
+ $this->_first_child = $child;
747
+ $this->_last_child = $child;
748
+ $child->_next_sibling = null;
749
+ }
750
+ else {
751
+ $this->_first_child->_prev_sibling = $child;
752
+ $child->_next_sibling = $this->_first_child;
753
+ $this->_first_child = $child;
754
+ }
755
+ }
756
+
757
+ /**
758
+ * Inserts a new child at the end of the Frame
759
+ *
760
+ * @param $child Frame The new Frame to insert
761
+ * @param $update_node boolean Whether or not to update the DOM
762
+ */
763
+ function append_child(Frame $child, $update_node = true) {
764
+ if ( $update_node ) {
765
+ $this->_node->appendChild($child->_node);
766
+ }
767
+
768
+ // Remove the child from its parent
769
+ if ( $child->_parent ) {
770
+ $child->_parent->remove_child($child, false);
771
+ }
772
+
773
+ $child->_parent = $this;
774
+ $child->_next_sibling = null;
775
+
776
+ // Handle the first child
777
+ if ( !$this->_last_child ) {
778
+ $this->_first_child = $child;
779
+ $this->_last_child = $child;
780
+ $child->_prev_sibling = null;
781
+ }
782
+ else {
783
+ $this->_last_child->_next_sibling = $child;
784
+ $child->_prev_sibling = $this->_last_child;
785
+ $this->_last_child = $child;
786
+ }
787
+ }
788
+
789
+ /**
790
+ * Inserts a new child immediately before the specified frame
791
+ *
792
+ * @param $new_child Frame The new Frame to insert
793
+ * @param $ref Frame The Frame after the new Frame
794
+ * @param $update_node boolean Whether or not to update the DOM
795
+ *
796
+ * @throws DOMPDF_Exception
797
+ */
798
+ function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) {
799
+ if ( $ref === $this->_first_child ) {
800
+ $this->prepend_child($new_child, $update_node);
801
+ return;
802
+ }
803
+
804
+ if ( is_null($ref) ) {
805
+ $this->append_child($new_child, $update_node);
806
+ return;
807
+ }
808
+
809
+ if ( $ref->_parent !== $this ) {
810
+ throw new DOMPDF_Exception("Reference child is not a child of this node.");
811
+ }
812
+
813
+ // Update the node
814
+ if ( $update_node ) {
815
+ $this->_node->insertBefore($new_child->_node, $ref->_node);
816
+ }
817
+
818
+ // Remove the child from its parent
819
+ if ( $new_child->_parent ) {
820
+ $new_child->_parent->remove_child($new_child, false);
821
+ }
822
+
823
+ $new_child->_parent = $this;
824
+ $new_child->_next_sibling = $ref;
825
+ $new_child->_prev_sibling = $ref->_prev_sibling;
826
+
827
+ if ( $ref->_prev_sibling ) {
828
+ $ref->_prev_sibling->_next_sibling = $new_child;
829
+ }
830
+
831
+ $ref->_prev_sibling = $new_child;
832
+ }
833
+
834
+ /**
835
+ * Inserts a new child immediately after the specified frame
836
+ *
837
+ * @param $new_child Frame The new Frame to insert
838
+ * @param $ref Frame The Frame before the new Frame
839
+ * @param $update_node boolean Whether or not to update the DOM
840
+ *
841
+ * @throws DOMPDF_Exception
842
+ */
843
+ function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) {
844
+ if ( $ref === $this->_last_child ) {
845
+ $this->append_child($new_child, $update_node);
846
+ return;
847
+ }
848
+
849
+ if ( is_null($ref) ) {
850
+ $this->prepend_child($new_child, $update_node);
851
+ return;
852
+ }
853
+
854
+ if ( $ref->_parent !== $this ) {
855
+ throw new DOMPDF_Exception("Reference child is not a child of this node.");
856
+ }
857
+
858
+ // Update the node
859
+ if ( $update_node ) {
860
+ if ( $ref->_next_sibling ) {
861
+ $next_node = $ref->_next_sibling->_node;
862
+ $this->_node->insertBefore($new_child->_node, $next_node);
863
+ }
864
+ else {
865
+ $new_child->_node = $this->_node->appendChild($new_child->_node);
866
+ }
867
+ }
868
+
869
+ // Remove the child from its parent
870
+ if ( $new_child->_parent ) {
871
+ $new_child->_parent->remove_child($new_child, false);
872
+ }
873
+
874
+ $new_child->_parent = $this;
875
+ $new_child->_prev_sibling = $ref;
876
+ $new_child->_next_sibling = $ref->_next_sibling;
877
+
878
+ if ( $ref->_next_sibling ) {
879
+ $ref->_next_sibling->_prev_sibling = $new_child;
880
+ }
881
+
882
+ $ref->_next_sibling = $new_child;
883
+ }
884
+
885
+
886
+ /**
887
+ * Remove a child frame
888
+ *
889
+ * @param Frame $child
890
+ * @param boolean $update_node Whether or not to remove the DOM node
891
+ *
892
+ * @throws DOMPDF_Exception
893
+ * @return Frame The removed child frame
894
+ */
895
+ function remove_child(Frame $child, $update_node = true) {
896
+ if ( $child->_parent !== $this ) {
897
+ throw new DOMPDF_Exception("Child not found in this frame");
898
+ }
899
+
900
+ if ( $update_node ) {
901
+ $this->_node->removeChild($child->_node);
902
+ }
903
+
904
+ if ( $child === $this->_first_child ) {
905
+ $this->_first_child = $child->_next_sibling;
906
+ }
907
+
908
+ if ( $child === $this->_last_child ) {
909
+ $this->_last_child = $child->_prev_sibling;
910
+ }
911
+
912
+ if ( $child->_prev_sibling ) {
913
+ $child->_prev_sibling->_next_sibling = $child->_next_sibling;
914
+ }
915
+
916
+ if ( $child->_next_sibling ) {
917
+ $child->_next_sibling->_prev_sibling = $child->_prev_sibling;
918
+ }
919
+
920
+ $child->_next_sibling = null;
921
+ $child->_prev_sibling = null;
922
+ $child->_parent = null;
923
+ return $child;
924
+ }
925
+
926
+ //........................................................................
927
+
928
+ // Debugging function:
929
+ function __toString() {
930
+ // Skip empty text frames
931
+ // if ( $this->is_text_node() &&
932
+ // preg_replace("/\s/", "", $this->_node->data) === "" )
933
+ // return "";
934
+
935
+
936
+ $str = "<b>" . $this->_node->nodeName . ":</b><br/>";
937
+ //$str .= spl_object_hash($this->_node) . "<br/>";
938
+ $str .= "Id: " .$this->get_id() . "<br/>";
939
+ $str .= "Class: " .get_class($this) . "<br/>";
940
+
941
+ if ( $this->is_text_node() ) {
942
+ $tmp = htmlspecialchars($this->_node->nodeValue);
943
+ $str .= "<pre>'" . mb_substr($tmp,0,70) .
944
+ (mb_strlen($tmp) > 70 ? "..." : "") . "'</pre>";
945
+ }
946
+ elseif ( $css_class = $this->_node->getAttribute("class") ) {
947
+ $str .= "CSS class: '$css_class'<br/>";
948
+ }
949
+
950
+ if ( $this->_parent ) {
951
+ $str .= "\nParent:" . $this->_parent->_node->nodeName .
952
+ " (" . spl_object_hash($this->_parent->_node) . ") " .
953
+ "<br/>";
954
+ }
955
+
956
+ if ( $this->_prev_sibling ) {
957
+ $str .= "Prev: " . $this->_prev_sibling->_node->nodeName .
958
+ " (" . spl_object_hash($this->_prev_sibling->_node) . ") " .
959
+ "<br/>";
960
+ }
961
+
962
+ if ( $this->_next_sibling ) {
963
+ $str .= "Next: " . $this->_next_sibling->_node->nodeName .
964
+ " (" . spl_object_hash($this->_next_sibling->_node) . ") " .
965
+ "<br/>";
966
+ }
967
+
968
+ $d = $this->get_decorator();
969
+ while ($d && $d != $d->get_decorator()) {
970
+ $str .= "Decorator: " . get_class($d) . "<br/>";
971
+ $d = $d->get_decorator();
972
+ }
973
+
974
+ $str .= "Position: " . pre_r($this->_position, true);
975
+ $str .= "\nContaining block: " . pre_r($this->_containing_block, true);
976
+ $str .= "\nMargin width: " . pre_r($this->get_margin_width(), true);
977
+ $str .= "\nMargin height: " . pre_r($this->get_margin_height(), true);
978
+
979
+ $str .= "\nStyle: <pre>". $this->_style->__toString() . "</pre>";
980
+
981
+ if ( $this->_decorator instanceof Block_Frame_Decorator ) {
982
+ $str .= "Lines:<pre>";
983
+ foreach ($this->_decorator->get_line_boxes() as $line) {
984
+ foreach ($line->get_frames() as $frame) {
985
+ if ($frame instanceof Text_Frame_Decorator) {
986
+ $str .= "\ntext: ";
987
+ $str .= "'". htmlspecialchars($frame->get_text()) ."'";
988
+ }
989
+ else {
990
+ $str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")";
991
+ }
992
+ }
993
+
994
+ $str .=
995
+ "\ny => " . $line->y . "\n" .
996
+ "w => " . $line->w . "\n" .
997
+ "h => " . $line->h . "\n" .
998
+ "left => " . $line->left . "\n" .
999
+ "right => " . $line->right . "\n";
1000
+ }
1001
+ $str .= "</pre>";
1002
+ }
1003
+
1004
+ $str .= "\n";
1005
+ if ( php_sapi_name() === "cli" ) {
1006
+ $str = strip_tags(str_replace(array("<br/>","<b>","</b>"),
1007
+ array("\n","",""),
1008
+ $str));
1009
+ }
1010
+
1011
+ return $str;
1012
+ }
1013
+ }
1014
+
1015
+ //------------------------------------------------------------------------
1016
+
1017
+ /**
1018
+ * Linked-list IteratorAggregate
1019
+ *
1020
+ * @access private
1021
+ * @package dompdf
1022
+ */
1023
+ class FrameList implements IteratorAggregate {
1024
+ protected $_frame;
1025
+
1026
+ function __construct($frame) { $this->_frame = $frame; }
1027
+ function getIterator() { return new FrameListIterator($this->_frame); }
1028
+ }
1029
+
1030
+ /**
1031
+ * Linked-list Iterator
1032
+ *
1033
+ * Returns children in order and allows for list to change during iteration,
1034
+ * provided the changes occur to or after the current element
1035
+ *
1036
+ * @access private
1037
+ * @package dompdf
1038
+ */
1039
+ class FrameListIterator implements Iterator {
1040
+
1041
+ /**
1042
+ * @var Frame
1043
+ */
1044
+ protected $_parent;
1045
+
1046
+ /**
1047
+ * @var Frame
1048
+ */
1049
+ protected $_cur;
1050
+
1051
+ /**
1052
+ * @var int
1053
+ */
1054
+ protected $_num;
1055
+
1056
+ function __construct(Frame $frame) {
1057
+ $this->_parent = $frame;
1058
+ $this->_cur = $frame->get_first_child();
1059
+ $this->_num = 0;
1060
+ }
1061
+
1062
+ function rewind() {
1063
+ $this->_cur = $this->_parent->get_first_child();
1064
+ $this->_num = 0;
1065
+ }
1066
+
1067
+ /**
1068
+ * @return bool
1069
+ */
1070
+ function valid() {
1071
+ return isset($this->_cur);// && ($this->_cur->get_prev_sibling() === $this->_prev);
1072
+ }
1073
+
1074
+ function key() { return $this->_num; }
1075
+
1076
+ /**
1077
+ * @return Frame
1078
+ */
1079
+ function current() { return $this->_cur; }
1080
+
1081
+ /**
1082
+ * @return Frame
1083
+ */
1084
+ function next() {
1085
+
1086
+ $ret = $this->_cur;
1087
+ if ( !$ret ) {
1088
+ return null;
1089
+ }
1090
+
1091
+ $this->_cur = $this->_cur->get_next_sibling();
1092
+ $this->_num++;
1093
+ return $ret;
1094
+ }
1095
+ }
1096
+
1097
+ //------------------------------------------------------------------------
1098
+
1099
+ /**
1100
+ * Pre-order IteratorAggregate
1101
+ *
1102
+ * @access private
1103
+ * @package dompdf
1104
+ */
1105
+ class FrameTreeList implements IteratorAggregate {
1106
+ /**
1107
+ * @var Frame
1108
+ */
1109
+ protected $_root;
1110
+
1111
+ function __construct(Frame $root) { $this->_root = $root; }
1112
+
1113
+ /**
1114
+ * @return FrameTreeIterator
1115
+ */
1116
+ function getIterator() { return new FrameTreeIterator($this->_root); }
1117
+ }
1118
+
1119
+ /**
1120
+ * Pre-order Iterator
1121
+ *
1122
+ * Returns frames in preorder traversal order (parent then children)
1123
+ *
1124
+ * @access private
1125
+ * @package dompdf
1126
+ */
1127
+ class FrameTreeIterator implements Iterator {
1128
+ /**
1129
+ * @var Frame
1130
+ */
1131
+ protected $_root;
1132
+ protected $_stack = array();
1133
+
1134
+ /**
1135
+ * @var int
1136
+ */
1137
+ protected $_num;
1138
+
1139
+ function __construct(Frame $root) {
1140
+ $this->_stack[] = $this->_root = $root;
1141
+ $this->_num = 0;
1142
+ }
1143
+
1144
+ function rewind() {
1145
+ $this->_stack = array($this->_root);
1146
+ $this->_num = 0;
1147
+ }
1148
+
1149
+ /**
1150
+ * @return bool
1151
+ */
1152
+ function valid() {
1153
+ return count($this->_stack) > 0;
1154
+ }
1155
+
1156
+ /**
1157
+ * @return int
1158
+ */
1159
+ function key() {
1160
+ return $this->_num;
1161
+ }
1162
+
1163
+ /**
1164
+ * @return Frame
1165
+ */
1166
+ function current() {
1167
+ return end($this->_stack);
1168
+ }
1169
+
1170
+ /**
1171
+ * @return Frame
1172
+ */
1173
+ function next() {
1174
+ $b = end($this->_stack);
1175
+
1176
+ // Pop last element
1177
+ unset($this->_stack[ key($this->_stack) ]);
1178
+ $this->_num++;
1179
+
1180
+ // Push all children onto the stack in reverse order
1181
+ if ( $c = $b->get_last_child() ) {
1182
+ $this->_stack[] = $c;
1183
+ while ( $c = $c->get_prev_sibling() ) {
1184
+ $this->_stack[] = $c;
1185
+ }
1186
+ }
1187
+
1188
+ return $b;
1189
+ }
1190
+ }
1191
+
lib/dompdf/include/frame_decorator.cls.php ADDED
@@ -0,0 +1,686 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Base Frame_Decorator class
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ abstract class Frame_Decorator extends Frame {
16
+ const DEFAULT_COUNTER = "-dompdf-default-counter";
17
+
18
+ public $_counters = array(); // array([id] => counter_value) (for generated content)
19
+
20
+ /**
21
+ * The root node of the DOM tree
22
+ *
23
+ * @var Frame
24
+ */
25
+ protected $_root;
26
+
27
+ /**
28
+ * The decorated frame
29
+ *
30
+ * @var Frame
31
+ */
32
+ protected $_frame;
33
+
34
+ /**
35
+ * Positioner object used to position this frame (Strategy pattern)
36
+ *
37
+ * @var Positioner
38
+ */
39
+ protected $_positioner;
40
+
41
+ /**
42
+ * Reflower object used to calculate frame dimensions (Strategy pattern)
43
+ *
44
+ * @var Frame_Reflower
45
+ */
46
+ protected $_reflower;
47
+
48
+ /**
49
+ * Reference to the current dompdf instance
50
+ *
51
+ * @var DOMPDF
52
+ */
53
+ protected $_dompdf;
54
+
55
+ /**
56
+ * First block parent
57
+ *
58
+ * @var Block_Frame_Decorator
59
+ */
60
+ private $_block_parent;
61
+
62
+ /**
63
+ * First positionned parent (position: relative | absolute | fixed)
64
+ *
65
+ * @var Frame_Decorator
66
+ */
67
+ private $_positionned_parent;
68
+
69
+ /**
70
+ * Class constructor
71
+ *
72
+ * @param Frame $frame The decoration target
73
+ * @param DOMPDF $dompdf The DOMPDF object
74
+ */
75
+ function __construct(Frame $frame, DOMPDF $dompdf) {
76
+ $this->_frame = $frame;
77
+ $this->_root = null;
78
+ $this->_dompdf = $dompdf;
79
+ $frame->set_decorator($this);
80
+ }
81
+
82
+ /**
83
+ * "Destructor": foribly free all references held by this object
84
+ *
85
+ * @param bool $recursive if true, call dispose on all children
86
+ */
87
+ function dispose($recursive = false) {
88
+ if ( $recursive ) {
89
+ while ( $child = $this->get_first_child() ) {
90
+ $child->dispose(true);
91
+ }
92
+ }
93
+
94
+ $this->_root = null;
95
+ unset($this->_root);
96
+
97
+ $this->_frame->dispose(true);
98
+ $this->_frame = null;
99
+ unset($this->_frame);
100
+
101
+ $this->_positioner = null;
102
+ unset($this->_positioner);
103
+
104
+ $this->_reflower = null;
105
+ unset($this->_reflower);
106
+ }
107
+
108
+ /**
109
+ * Return a copy of this frame with $node as its node
110
+ *
111
+ * @param DOMNode $node
112
+ *
113
+ * @return Frame
114
+ */
115
+ function copy(DOMNode $node) {
116
+ $frame = new Frame($node);
117
+ $frame->set_style(clone $this->_frame->get_original_style());
118
+
119
+ return Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
120
+ }
121
+
122
+ /**
123
+ * Create a deep copy: copy this node and all children
124
+ *
125
+ * @return Frame
126
+ */
127
+ function deep_copy() {
128
+ $frame = new Frame($this->get_node()->cloneNode());
129
+ $frame->set_style(clone $this->_frame->get_original_style());
130
+
131
+ $deco = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
132
+
133
+ foreach ($this->get_children() as $child) {
134
+ $deco->append_child($child->deep_copy());
135
+ }
136
+
137
+ return $deco;
138
+ }
139
+
140
+ /**
141
+ * Delegate calls to decorated frame object
142
+ */
143
+ function reset() {
144
+ $this->_frame->reset();
145
+
146
+ $this->_counters = array();
147
+
148
+ // Reset all children
149
+ foreach ($this->get_children() as $child) {
150
+ $child->reset();
151
+ }
152
+ }
153
+
154
+ // Getters -----------
155
+ function get_id() {
156
+ return $this->_frame->get_id();
157
+ }
158
+
159
+ /**
160
+ * @return Frame
161
+ */
162
+ function get_frame() {
163
+ return $this->_frame;
164
+ }
165
+
166
+ /**
167
+ * @return DOMElement|DOMText
168
+ */
169
+ function get_node() {
170
+ return $this->_frame->get_node();
171
+ }
172
+
173
+ /**
174
+ * @return Style
175
+ */
176
+ function get_style() {
177
+ return $this->_frame->get_style();
178
+ }
179
+
180
+ /**
181
+ * @return Style
182
+ */
183
+ function get_original_style() {
184
+ return $this->_frame->get_original_style();
185
+ }
186
+
187
+ /**
188
+ * @param integer $i
189
+ *
190
+ * @return array|float
191
+ */
192
+ function get_containing_block($i = null) {
193
+ return $this->_frame->get_containing_block($i);
194
+ }
195
+
196
+ /**
197
+ * @param integer $i
198
+ *
199
+ * @return array|float
200
+ */
201
+ function get_position($i = null) {
202
+ return $this->_frame->get_position($i);
203
+ }
204
+
205
+ /**
206
+ * @return DOMPDF
207
+ */
208
+ function get_dompdf() {
209
+ return $this->_dompdf;
210
+ }
211
+
212
+ /**
213
+ * @return float
214
+ */
215
+ function get_margin_height() {
216
+ return $this->_frame->get_margin_height();
217
+ }
218
+
219
+ /**
220
+ * @return float
221
+ */
222
+ function get_margin_width() {
223
+ return $this->_frame->get_margin_width();
224
+ }
225
+
226
+ /**
227
+ * @return array
228
+ */
229
+ function get_padding_box() {
230
+ return $this->_frame->get_padding_box();
231
+ }
232
+
233
+ /**
234
+ * @return array
235
+ */
236
+ function get_border_box() {
237
+ return $this->_frame->get_border_box();
238
+ }
239
+
240
+ /**
241
+ * @param integer $id
242
+ */
243
+ function set_id($id) {
244
+ $this->_frame->set_id($id);
245
+ }
246
+
247
+ /**
248
+ * @param Style $style
249
+ */
250
+ function set_style(Style $style) {
251
+ $this->_frame->set_style($style);
252
+ }
253
+
254
+ /**
255
+ * @param float $x
256
+ * @param float $y
257
+ * @param float $w
258
+ * @param float $h
259
+ */
260
+ function set_containing_block($x = null, $y = null, $w = null, $h = null) {
261
+ $this->_frame->set_containing_block($x, $y, $w, $h);
262
+ }
263
+
264
+ /**
265
+ * @param float $x
266
+ * @param float $y
267
+ */
268
+ function set_position($x = null, $y = null) {
269
+ $this->_frame->set_position($x, $y);
270
+ }
271
+
272
+ /**
273
+ * @return string
274
+ */
275
+ function __toString() {
276
+ return $this->_frame->__toString();
277
+ }
278
+
279
+ /**
280
+ * @param Frame $child
281
+ * @param bool $update_node
282
+ */
283
+ function prepend_child(Frame $child, $update_node = true) {
284
+ while ( $child instanceof Frame_Decorator ) {
285
+ $child = $child->_frame;
286
+ }
287
+
288
+ $this->_frame->prepend_child($child, $update_node);
289
+ }
290
+
291
+ /**
292
+ * @param Frame $child
293
+ * @param bool $update_node
294
+ */
295
+ function append_child(Frame $child, $update_node = true) {
296
+ while ( $child instanceof Frame_Decorator ) {
297
+ $child = $child->_frame;
298
+ }
299
+
300
+ $this->_frame->append_child($child, $update_node);
301
+ }
302
+
303
+ /**
304
+ * @param Frame $new_child
305
+ * @param Frame $ref
306
+ * @param bool $update_node
307
+ */
308
+ function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) {
309
+ while ( $new_child instanceof Frame_Decorator ) {
310
+ $new_child = $new_child->_frame;
311
+ }
312
+
313
+ if ( $ref instanceof Frame_Decorator ) {
314
+ $ref = $ref->_frame;
315
+ }
316
+
317
+ $this->_frame->insert_child_before($new_child, $ref, $update_node);
318
+ }
319
+
320
+ /**
321
+ * @param Frame $new_child
322
+ * @param Frame $ref
323
+ * @param bool $update_node
324
+ */
325
+ function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) {
326
+ while ( $new_child instanceof Frame_Decorator ) {
327
+ $new_child = $new_child->_frame;
328
+ }
329
+
330
+ while ( $ref instanceof Frame_Decorator ) {
331
+ $ref = $ref->_frame;
332
+ }
333
+
334
+ $this->_frame->insert_child_after($new_child, $ref, $update_node);
335
+ }
336
+
337
+ /**
338
+ * @param Frame $child
339
+ * @param bool $update_node
340
+ *
341
+ * @return Frame
342
+ */
343
+ function remove_child(Frame $child, $update_node = true) {
344
+ while ( $child instanceof Frame_Decorator ) {
345
+ $child = $child->_frame;
346
+ }
347
+
348
+ return $this->_frame->remove_child($child, $update_node);
349
+ }
350
+
351
+ /**
352
+ * @return Frame_Decorator
353
+ */
354
+ function get_parent() {
355
+ $p = $this->_frame->get_parent();
356
+ if ( $p && $deco = $p->get_decorator() ) {
357
+ while ( $tmp = $deco->get_decorator() ) {
358
+ $deco = $tmp;
359
+ }
360
+
361
+ return $deco;
362
+ }
363
+ else if ( $p ) {
364
+ return $p;
365
+ }
366
+
367
+ return null;
368
+ }
369
+
370
+ /**
371
+ * @return Frame_Decorator
372
+ */
373
+ function get_first_child() {
374
+ $c = $this->_frame->get_first_child();
375
+ if ( $c && $deco = $c->get_decorator() ) {
376
+ while ( $tmp = $deco->get_decorator() ) {
377
+ $deco = $tmp;
378
+ }
379
+
380
+ return $deco;
381
+ }
382
+ else if ( $c ) {
383
+ return $c;
384
+ }
385
+
386
+ return null;
387
+ }
388
+
389
+ /**
390
+ * @return Frame_Decorator
391
+ */
392
+ function get_last_child() {
393
+ $c = $this->_frame->get_last_child();
394
+ if ( $c && $deco = $c->get_decorator() ) {
395
+ while ( $tmp = $deco->get_decorator() ) {
396
+ $deco = $tmp;
397
+ }
398
+
399
+ return $deco;
400
+ }
401
+ else if ( $c ) {
402
+ return $c;
403
+ }
404
+
405
+ return null;
406
+ }
407
+
408
+ /**
409
+ * @return Frame_Decorator
410
+ */
411
+ function get_prev_sibling() {
412
+ $s = $this->_frame->get_prev_sibling();
413
+ if ( $s && $deco = $s->get_decorator() ) {
414
+ while ( $tmp = $deco->get_decorator() ) {
415
+ $deco = $tmp;
416
+ }
417
+ return $deco;
418
+ }
419
+ else if ( $s ) {
420
+ return $s;
421
+ }
422
+
423
+ return null;
424
+ }
425
+
426
+ /**
427
+ * @return Frame_Decorator
428
+ */
429
+ function get_next_sibling() {
430
+ $s = $this->_frame->get_next_sibling();
431
+ if ( $s && $deco = $s->get_decorator() ) {
432
+ while ( $tmp = $deco->get_decorator() ) {
433
+ $deco = $tmp;
434
+ }
435
+
436
+ return $deco;
437
+ }
438
+ else if ( $s ) {
439
+ return $s;
440
+ }
441
+
442
+ return null;
443
+ }
444
+
445
+ /**
446
+ * @return FrameTreeList
447
+ */
448
+ function get_subtree() {
449
+ return new FrameTreeList($this);
450
+ }
451
+
452
+ function set_positioner(Positioner $posn) {
453
+ $this->_positioner = $posn;
454
+ if ( $this->_frame instanceof Frame_Decorator ) {
455
+ $this->_frame->set_positioner($posn);
456
+ }
457
+ }
458
+
459
+ function set_reflower(Frame_Reflower $reflower) {
460
+ $this->_reflower = $reflower;
461
+ if ( $this->_frame instanceof Frame_Decorator ) {
462
+ $this->_frame->set_reflower( $reflower );
463
+ }
464
+ }
465
+
466
+ /**
467
+ * @return Frame_Reflower
468
+ */
469
+ function get_reflower() {
470
+ return $this->_reflower;
471
+ }
472
+
473
+ /**
474
+ * @param Frame $root
475
+ */
476
+ function set_root(Frame $root) {
477
+ $this->_root = $root;
478
+
479
+ if ( $this->_frame instanceof Frame_Decorator ) {
480
+ $this->_frame->set_root($root);
481
+ }
482
+ }
483
+
484
+ /**
485
+ * @return Page_Frame_Decorator
486
+ */
487
+ function get_root() {
488
+ return $this->_root;
489
+ }
490
+
491
+ /**
492
+ * @return Block_Frame_Decorator
493
+ */
494
+ function find_block_parent() {
495
+ // Find our nearest block level parent
496
+ $p = $this->get_parent();
497
+
498
+ while ( $p ) {
499
+ if ( $p->is_block() ) {
500
+ break;
501
+ }
502
+
503
+ $p = $p->get_parent();
504
+ }
505
+
506
+ return $this->_block_parent = $p;
507
+ }
508
+
509
+ /**
510
+ * @return Frame_Decorator
511
+ */
512
+ function find_positionned_parent() {
513
+ // Find our nearest relative positionned parent
514
+ $p = $this->get_parent();
515
+ while ( $p ) {
516
+ if ( $p->is_positionned() ) {
517
+ break;
518
+ }
519
+
520
+ $p = $p->get_parent();
521
+ }
522
+
523
+ if ( !$p ) {
524
+ $p = $this->_root->get_first_child(); // <body>
525
+ }
526
+
527
+ return $this->_positionned_parent = $p;
528
+ }
529
+
530
+ /**
531
+ * split this frame at $child.
532
+ * The current frame is cloned and $child and all children following
533
+ * $child are added to the clone. The clone is then passed to the
534
+ * current frame's parent->split() method.
535
+ *
536
+ * @param Frame $child
537
+ * @param boolean $force_pagebreak
538
+ *
539
+ * @throws DOMPDF_Exception
540
+ * @return void
541
+ */
542
+ function split(Frame $child = null, $force_pagebreak = false) {
543
+ if ( is_null( $child ) ) {
544
+ $this->get_parent()->split($this, $force_pagebreak);
545
+ return;
546
+ }
547
+
548
+ if ( $child->get_parent() !== $this ) {
549
+ throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
550
+ }
551
+
552
+ $node = $this->_frame->get_node();
553
+
554
+ $split = $this->copy( $node->cloneNode() );
555
+ $split->reset();
556
+ $split->get_original_style()->text_indent = 0;
557
+ $split->_splitted = true;
558
+
559
+ // The body's properties must be kept
560
+ if ( $node->nodeName !== "body" ) {
561
+ // Style reset on the first and second parts
562
+ $style = $this->_frame->get_style();
563
+ $style->margin_bottom = 0;
564
+ $style->padding_bottom = 0;
565
+ $style->border_bottom = 0;
566
+
567
+ // second
568
+ $orig_style = $split->get_original_style();
569
+ $orig_style->text_indent = 0;
570
+ $orig_style->margin_top = 0;
571
+ $orig_style->padding_top = 0;
572
+ $orig_style->border_top = 0;
573
+ }
574
+
575
+ $this->get_parent()->insert_child_after($split, $this);
576
+
577
+ // Add $frame and all following siblings to the new split node
578
+ $iter = $child;
579
+ while ($iter) {
580
+ $frame = $iter;
581
+ $iter = $iter->get_next_sibling();
582
+ $frame->reset();
583
+ $split->append_child($frame);
584
+ }
585
+
586
+ $this->get_parent()->split($split, $force_pagebreak);
587
+ }
588
+
589
+ function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) {
590
+ $this->get_parent()->_counters[$id] = intval($value);
591
+ }
592
+
593
+ function increment_counters($counters) {
594
+ foreach($counters as $id => $increment) {
595
+ $this->increment_counter($id, intval($increment));
596
+ }
597
+ }
598
+
599
+ function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) {
600
+ $counter_frame = $this->lookup_counter_frame($id);
601
+
602
+ if ( $counter_frame ) {
603
+ if ( !isset($counter_frame->_counters[$id]) ) {
604
+ $counter_frame->_counters[$id] = 0;
605
+ }
606
+
607
+ $counter_frame->_counters[$id] += $increment;
608
+ }
609
+ }
610
+
611
+ function lookup_counter_frame($id = self::DEFAULT_COUNTER) {
612
+ $f = $this->get_parent();
613
+
614
+ while( $f ) {
615
+ if( isset($f->_counters[$id]) ) {
616
+ return $f;
617
+ }
618
+ $fp = $f->get_parent();
619
+
620
+ if ( !$fp ) {
621
+ return $f;
622
+ }
623
+
624
+ $f = $fp;
625
+ }
626
+ }
627
+
628
+ // TODO: What version is the best : this one or the one in List_Bullet_Renderer ?
629
+ function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") {
630
+ $type = mb_strtolower($type);
631
+
632
+ if ( !isset($this->_counters[$id]) ) {
633
+ $this->_counters[$id] = 0;
634
+ }
635
+
636
+ $value = $this->_counters[$id];
637
+
638
+ switch ($type) {
639
+ default:
640
+ case "decimal":
641
+ return $value;
642
+
643
+ case "decimal-leading-zero":
644
+ return str_pad($value, 2, "0");
645
+
646
+ case "lower-roman":
647
+ return dec2roman($value);
648
+
649
+ case "upper-roman":
650
+ return mb_strtoupper(dec2roman($value));
651
+
652
+ case "lower-latin":
653
+ case "lower-alpha":
654
+ return chr( ($value % 26) + ord('a') - 1);
655
+
656
+ case "upper-latin":
657
+ case "upper-alpha":
658
+ return chr( ($value % 26) + ord('A') - 1);
659
+
660
+ case "lower-greek":
661
+ return unichr($value + 944);
662
+
663
+ case "upper-greek":
664
+ return unichr($value + 912);
665
+ }
666
+ }
667
+
668
+ final function position() {
669
+ $this->_positioner->position();
670
+ }
671
+
672
+ final function move($offset_x, $offset_y, $ignore_self = false) {
673
+ $this->_positioner->move($offset_x, $offset_y, $ignore_self);
674
+ }
675
+
676
+ final function reflow(Block_Frame_Decorator $block = null) {
677
+ // Uncomment this to see the frames before they're laid out, instead of
678
+ // during rendering.
679
+ //echo $this->_frame; flush();
680
+ $this->_reflower->reflow($block);
681
+ }
682
+
683
+ final function get_min_max_width() {
684
+ return $this->_reflower->get_min_max_width();
685
+ }
686
+ }
lib/dompdf/include/frame_factory.cls.php ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Contains frame decorating logic
11
+ *
12
+ * This class is responsible for assigning the correct {@link Frame_Decorator},
13
+ * {@link Positioner}, and {@link Frame_Reflower} objects to {@link Frame}
14
+ * objects. This is determined primarily by the Frame's display type, but
15
+ * also by the Frame's node's type (e.g. DomElement vs. #text)
16
+ *
17
+ * @access private
18
+ * @package dompdf
19
+ */
20
+ class Frame_Factory {
21
+
22
+ /**
23
+ * Decorate the root Frame
24
+ *
25
+ * @param $root Frame The frame to decorate
26
+ * @param $dompdf DOMPDF The dompdf instance
27
+ * @return Page_Frame_Decorator
28
+ */
29
+ static function decorate_root(Frame $root, DOMPDF $dompdf) {
30
+ $frame = new Page_Frame_Decorator($root, $dompdf);
31
+ $frame->set_reflower( new Page_Frame_Reflower($frame) );
32
+ $root->set_decorator($frame);
33
+ return $frame;
34
+ }
35
+
36
+ /**
37
+ * Decorate a Frame
38
+ *
39
+ * @param Frame $frame The frame to decorate
40
+ * @param DOMPDF $dompdf The dompdf instance
41
+ * @param Frame $root The frame to decorate
42
+ *
43
+ * @throws DOMPDF_Exception
44
+ * @return Frame_Decorator
45
+ * FIXME: this is admittedly a little smelly...
46
+ */
47
+ static function decorate_frame(Frame $frame, DOMPDF $dompdf, Frame $root = null) {
48
+ if ( is_null($dompdf) ) {
49
+ throw new DOMPDF_Exception("The DOMPDF argument is required");
50
+ }
51
+
52
+ $style = $frame->get_style();
53
+
54
+ // Floating (and more generally out-of-flow) elements are blocks
55
+ // http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/
56
+ if ( !$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) {
57
+ $style->display = "block";
58
+ }
59
+
60
+ $display = $style->display;
61
+
62
+ switch ($display) {
63
+
64
+ case "block":
65
+ $positioner = "Block";
66
+ $decorator = "Block";
67
+ $reflower = "Block";
68
+ break;
69
+
70
+ case "inline-block":
71
+ $positioner = "Inline";
72
+ $decorator = "Block";
73
+ $reflower = "Block";
74
+ break;
75
+
76
+ case "inline":
77
+ $positioner = "Inline";
78
+ if ( $frame->is_text_node() ) {
79
+ $decorator = "Text";
80
+ $reflower = "Text";
81
+ }
82
+ else {
83
+ $enable_css_float = $dompdf->get_option("enable_css_float");
84
+ if ( $enable_css_float && $style->float !== "none" ) {
85
+ $decorator = "Block";
86
+ $reflower = "Block";
87
+ }
88
+ else {
89
+ $decorator = "Inline";
90
+ $reflower = "Inline";
91
+ }
92
+ }
93
+ break;
94
+
95
+ case "table":
96
+ $positioner = "Block";
97
+ $decorator = "Table";
98
+ $reflower = "Table";
99
+ break;
100
+
101
+ case "inline-table":
102
+ $positioner = "Inline";
103
+ $decorator = "Table";
104
+ $reflower = "Table";
105
+ break;
106
+
107
+ case "table-row-group":
108
+ case "table-header-group":
109
+ case "table-footer-group":
110
+ $positioner = "Null";
111
+ $decorator = "Table_Row_Group";
112
+ $reflower = "Table_Row_Group";
113
+ break;
114
+
115
+ case "table-row":
116
+ $positioner = "Null";
117
+ $decorator = "Table_Row";
118
+ $reflower = "Table_Row";
119
+ break;
120
+
121
+ case "table-cell":
122
+ $positioner = "Table_Cell";
123
+ $decorator = "Table_Cell";
124
+ $reflower = "Table_Cell";
125
+ break;
126
+
127
+ case "list-item":
128
+ $positioner = "Block";
129
+ $decorator = "Block";
130
+ $reflower = "Block";
131
+ break;
132
+
133
+ case "-dompdf-list-bullet":
134
+ if ( $style->list_style_position === "inside" ) {
135
+ $positioner = "Inline";
136
+ }
137
+ else {
138
+ $positioner = "List_Bullet";
139
+ }
140
+
141
+ if ( $style->list_style_image !== "none" ) {
142
+ $decorator = "List_Bullet_Image";
143
+ }
144
+ else {
145
+ $decorator = "List_Bullet";
146
+ }
147
+
148
+ $reflower = "List_Bullet";
149
+ break;
150
+
151
+ case "-dompdf-image":
152
+ $positioner = "Inline";
153
+ $decorator = "Image";
154
+ $reflower = "Image";
155
+ break;
156
+
157
+ case "-dompdf-br":
158
+ $positioner = "Inline";
159
+ $decorator = "Inline";
160
+ $reflower = "Inline";
161
+ break;
162
+
163
+ default:
164
+ // FIXME: should throw some sort of warning or something?
165
+ case "none":
166
+ if ( $style->_dompdf_keep !== "yes" ) {
167
+ // Remove the node and the frame
168
+ $frame->get_parent()->remove_child($frame);
169
+ return;
170
+ }
171
+
172
+ $positioner = "Null";
173
+ $decorator = "Null";
174
+ $reflower = "Null";
175
+ break;
176
+ }
177
+
178
+ // Handle CSS position
179
+ $position = $style->position;
180
+
181
+ if ( $position === "absolute" ) {
182
+ $positioner = "Absolute";
183
+ }
184
+ else if ( $position === "fixed" ) {
185
+ $positioner = "Fixed";
186
+ }
187
+
188
+ $node = $frame->get_node();
189
+
190
+ // Handle nodeName
191
+ if ( $node->nodeName === "img" ) {
192
+ $style->display = "-dompdf-image";
193
+ $decorator = "Image";
194
+ $reflower = "Image";
195
+ }
196
+
197
+ $positioner .= "_Positioner";
198
+ $decorator .= "_Frame_Decorator";
199
+ $reflower .= "_Frame_Reflower";
200
+
201
+ $deco = new $decorator($frame, $dompdf);
202
+
203
+ $deco->set_positioner( new $positioner($deco) );
204
+ $deco->set_reflower( new $reflower($deco) );
205
+
206
+ if ( $root ) {
207
+ $deco->set_root($root);
208
+ }
209
+
210
+ if ( $display === "list-item" ) {
211
+ // Insert a list-bullet frame
212
+ $xml = $dompdf->get_dom();
213
+ $bullet_node = $xml->createElement("bullet"); // arbitrary choice
214
+ $b_f = new Frame($bullet_node);
215
+
216
+ $node = $frame->get_node();
217
+ $parent_node = $node->parentNode;
218
+
219
+ if ( $parent_node ) {
220
+ if ( !$parent_node->hasAttribute("dompdf-children-count") ) {
221
+ $xpath = new DOMXPath($xml);
222
+ $count = $xpath->query("li", $parent_node)->length;
223
+ $parent_node->setAttribute("dompdf-children-count", $count);
224
+ }
225
+
226
+ if ( is_numeric($node->getAttribute("value")) ) {
227
+ $index = intval($node->getAttribute("value"));
228
+ }
229
+ else {
230
+ if ( !$parent_node->hasAttribute("dompdf-counter") ) {
231
+ $index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1);
232
+ }
233
+ else {
234
+ $index = $parent_node->getAttribute("dompdf-counter")+1;
235
+ }
236
+ }
237
+
238
+ $parent_node->setAttribute("dompdf-counter", $index);
239
+ $bullet_node->setAttribute("dompdf-counter", $index);
240
+ }
241
+
242
+ $new_style = $dompdf->get_css()->create_style();
243
+ $new_style->display = "-dompdf-list-bullet";
244
+ $new_style->inherit($style);
245
+ $b_f->set_style($new_style);
246
+
247
+ $deco->prepend_child( Frame_Factory::decorate_frame($b_f, $dompdf, $root) );
248
+ }
249
+
250
+ return $deco;
251
+ }
252
+ }
lib/dompdf/include/frame_reflower.cls.php ADDED
@@ -0,0 +1,453 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Base reflower class
11
+ *
12
+ * Reflower objects are responsible for determining the width and height of
13
+ * individual frames. They also create line and page breaks as necessary.
14
+ *
15
+ * @access private
16
+ * @package dompdf
17
+ */
18
+ abstract class Frame_Reflower {
19
+
20
+ /**
21
+ * Frame for this reflower
22
+ *
23
+ * @var Frame
24
+ */
25
+ protected $_frame;
26
+
27
+ /**
28
+ * Cached min/max size
29
+ *
30
+ * @var array
31
+ */
32
+ protected $_min_max_cache;
33
+
34
+ function __construct(Frame $frame) {
35
+ $this->_frame = $frame;
36
+ $this->_min_max_cache = null;
37
+ }
38
+
39
+ function dispose() {
40
+ clear_object($this);
41
+ }
42
+
43
+ /**
44
+ * @return DOMPDF
45
+ */
46
+ function get_dompdf() {
47
+ return $this->_frame->get_dompdf();
48
+ }
49
+
50
+ /**
51
+ * Collapse frames margins
52
+ * http://www.w3.org/TR/CSS2/box.html#collapsing-margins
53
+ */
54
+ protected function _collapse_margins() {
55
+ $frame = $this->_frame;
56
+ $cb = $frame->get_containing_block();
57
+ $style = $frame->get_style();
58
+
59
+ if ( !$frame->is_in_flow() ) {
60
+ return;
61
+ }
62
+
63
+ $t = $style->length_in_pt($style->margin_top, $cb["h"]);
64
+ $b = $style->length_in_pt($style->margin_bottom, $cb["h"]);
65
+
66
+ // Handle 'auto' values
67
+ if ( $t === "auto" ) {
68
+ $style->margin_top = "0pt";
69
+ $t = 0;
70
+ }
71
+
72
+ if ( $b === "auto" ) {
73
+ $style->margin_bottom = "0pt";
74
+ $b = 0;
75
+ }
76
+
77
+ // Collapse vertical margins:
78
+ $n = $frame->get_next_sibling();
79
+ if ( $n && !$n->is_block() ) {
80
+ while ( $n = $n->get_next_sibling() ) {
81
+ if ( $n->is_block() ) {
82
+ break;
83
+ }
84
+
85
+ if ( !$n->get_first_child() ) {
86
+ $n = null;
87
+ break;
88
+ }
89
+ }
90
+ }
91
+
92
+ if ( $n ) {
93
+ $n_style = $n->get_style();
94
+ $b = max($b, $n_style->length_in_pt($n_style->margin_top, $cb["h"]));
95
+ $n_style->margin_top = "0pt";
96
+ $style->margin_bottom = $b."pt";
97
+ }
98
+
99
+ // Collapse our first child's margin
100
+ /*$f = $this->_frame->get_first_child();
101
+ if ( $f && !$f->is_block() ) {
102
+ while ( $f = $f->get_next_sibling() ) {
103
+ if ( $f->is_block() ) {
104
+ break;
105
+ }
106
+
107
+ if ( !$f->get_first_child() ) {
108
+ $f = null;
109
+ break;
110
+ }
111
+ }
112
+ }
113
+
114
+ // Margin are collapsed only between block elements
115
+ if ( $f ) {
116
+ $f_style = $f->get_style();
117
+ $t = max($t, $f_style->length_in_pt($f_style->margin_top, $cb["h"]));
118
+ $style->margin_top = $t."pt";
119
+ $f_style->margin_bottom = "0pt";
120
+ }*/
121
+ }
122
+
123
+ //........................................................................
124
+
125
+ abstract function reflow(Block_Frame_Decorator $block = null);
126
+
127
+ //........................................................................
128
+
129
+ // Required for table layout: Returns an array(0 => min, 1 => max, "min"
130
+ // => min, "max" => max) of the minimum and maximum widths of this frame.
131
+ // This provides a basic implementation. Child classes should override
132
+ // this if necessary.
133
+ function get_min_max_width() {
134
+ if ( !is_null($this->_min_max_cache) ) {
135
+ return $this->_min_max_cache;
136
+ }
137
+
138
+ $style = $this->_frame->get_style();
139
+
140
+ // Account for margins & padding
141
+ $dims = array($style->padding_left,
142
+ $style->padding_right,
143
+ $style->border_left_width,
144
+ $style->border_right_width,
145
+ $style->margin_left,
146
+ $style->margin_right);
147
+
148
+ $cb_w = $this->_frame->get_containing_block("w");
149
+ $delta = $style->length_in_pt($dims, $cb_w);
150
+
151
+ // Handle degenerate case
152
+ if ( !$this->_frame->get_first_child() ) {
153
+ return $this->_min_max_cache = array(
154
+ $delta, $delta,
155
+ "min" => $delta,
156
+ "max" => $delta,
157
+ );
158
+ }
159
+
160
+ $low = array();
161
+ $high = array();
162
+
163
+ for ( $iter = $this->_frame->get_children()->getIterator();
164
+ $iter->valid();
165
+ $iter->next() ) {
166
+
167
+ $inline_min = 0;
168
+ $inline_max = 0;
169
+
170
+ // Add all adjacent inline widths together to calculate max width
171
+ while ( $iter->valid() && in_array( $iter->current()->get_style()->display, Style::$INLINE_TYPES ) ) {
172
+
173
+ $child = $iter->current();
174
+
175
+ $minmax = $child->get_min_max_width();
176
+
177
+ if ( in_array( $iter->current()->get_style()->white_space, array("pre", "nowrap") ) ) {
178
+ $inline_min += $minmax["min"];
179
+ }
180
+ else {
181
+ $low[] = $minmax["min"];
182
+ }
183
+
184
+ $inline_max += $minmax["max"];
185
+ $iter->next();
186
+
187
+ }
188
+
189
+ if ( $inline_max > 0 ) $high[] = $inline_max;
190
+ if ( $inline_min > 0 ) $low[] = $inline_min;
191
+
192
+ if ( $iter->valid() ) {
193
+ list($low[], $high[]) = $iter->current()->get_min_max_width();
194
+ continue;
195
+ }
196
+
197
+ }
198
+ $min = count($low) ? max($low) : 0;
199
+ $max = count($high) ? max($high) : 0;
200
+
201
+ // Use specified width if it is greater than the minimum defined by the
202
+ // content. If the width is a percentage ignore it for now.
203
+ $width = $style->width;
204
+ if ( $width !== "auto" && !is_percent($width) ) {
205
+ $width = $style->length_in_pt($width, $cb_w);
206
+ if ( $min < $width ) $min = $width;
207
+ if ( $max < $width ) $max = $width;
208
+ }
209
+
210
+ $min += $delta;
211
+ $max += $delta;
212
+ return $this->_min_max_cache = array($min, $max, "min"=>$min, "max"=>$max);
213
+ }
214
+
215
+ /**
216
+ * Parses a CSS string containing quotes and escaped hex characters
217
+ *
218
+ * @param $string string The CSS string to parse
219
+ * @param $single_trim
220
+ * @return string
221
+ */
222
+ protected function _parse_string($string, $single_trim = false) {
223
+ if ( $single_trim ) {
224
+ $string = preg_replace('/^[\"\']/', "", $string);
225
+ $string = preg_replace('/[\"\']$/', "", $string);
226
+ }
227
+ else {
228
+ $string = trim($string, "'\"");
229
+ }
230
+
231
+ $string = str_replace(array("\\\n",'\\"',"\\'"),
232
+ array("",'"',"'"), $string);
233
+
234
+ // Convert escaped hex characters into ascii characters (e.g. \A => newline)
235
+ $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/",
236
+ create_function('$matches',
237
+ 'return unichr(hexdec($matches[1]));'),
238
+ $string);
239
+ return $string;
240
+ }
241
+
242
+ /**
243
+ * Parses a CSS "quotes" property
244
+ *
245
+ * @return array|null An array of pairs of quotes
246
+ */
247
+ protected function _parse_quotes() {
248
+
249
+ // Matches quote types
250
+ $re = '/(\'[^\']*\')|(\"[^\"]*\")/';
251
+
252
+ $quotes = $this->_frame->get_style()->quotes;
253
+
254
+ // split on spaces, except within quotes
255
+ if ( !preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER) ) {
256
+ return null;
257
+ }
258
+
259
+ $quotes_array = array();
260
+ foreach($matches as &$_quote){
261
+ $quotes_array[] = $this->_parse_string($_quote[0], true);
262
+ }
263
+
264
+ if ( empty($quotes_array) ) {
265
+ $quotes_array = array('"', '"');
266
+ }
267
+
268
+ return array_chunk($quotes_array, 2);
269
+ }
270
+
271
+ /**
272
+ * Parses the CSS "content" property
273
+ *
274
+ * @return string|null The resulting string
275
+ */
276
+ protected function _parse_content() {
277
+
278
+ // Matches generated content
279
+ $re = "/\n".
280
+ "\s(counters?\\([^)]*\\))|\n".
281
+ "\A(counters?\\([^)]*\\))|\n".
282
+ "\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\3|\n".
283
+ "\A([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\5|\n" .
284
+ "\s([^\s\"']+)|\n" .
285
+ "\A([^\s\"']+)\n".
286
+ "/xi";
287
+
288
+ $content = $this->_frame->get_style()->content;
289
+
290
+ $quotes = $this->_parse_quotes();
291
+
292
+ // split on spaces, except within quotes
293
+ if ( !preg_match_all($re, $content, $matches, PREG_SET_ORDER) ) {
294
+ return null;
295
+ }
296
+
297
+ $text = "";
298
+
299
+ foreach ($matches as $match) {
300
+
301
+ if ( isset($match[2]) && $match[2] !== "" ) {
302
+ $match[1] = $match[2];
303
+ }
304
+
305
+ if ( isset($match[6]) && $match[6] !== "" ) {
306
+ $match[4] = $match[6];
307
+ }
308
+
309
+ if ( isset($match[8]) && $match[8] !== "" ) {
310
+ $match[7] = $match[8];
311
+ }
312
+
313
+ if ( isset($match[1]) && $match[1] !== "" ) {
314
+
315
+ // counters?(...)
316
+ $match[1] = mb_strtolower(trim($match[1]));
317
+
318
+ // Handle counter() references:
319
+ // http://www.w3.org/TR/CSS21/generate.html#content
320
+
321
+ $i = mb_strpos($match[1], ")");
322
+ if ( $i === false ) {
323
+ continue;
324
+ }
325
+
326
+ preg_match( '/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]+)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i' , $match[1] , $args );
327
+ $counter_id = $args[3];
328
+ if ( strtolower( $args[1] ) == 'counter' ) {
329
+ // counter(name [,style])
330
+ if ( isset( $args[5] ) ) {
331
+ $type = trim( $args[1] );
332
+ }
333
+ else {
334
+ $type = null;
335
+ }
336
+
337
+ $p = $this->_frame->lookup_counter_frame( $counter_id );
338
+
339
+ $text .= $p->counter_value($counter_id, $type);
340
+
341
+ }
342
+ else if ( strtolower( $args[1] ) == 'counters' ) {
343
+ // counters(name, string [,style])
344
+ if ( isset($args[5]) ) {
345
+ $string = $this->_parse_string( $args[5] );
346
+ }
347
+ else {
348
+ $string = "";
349
+ }
350
+
351
+ if ( isset( $args[7] ) ) {
352
+ $type = trim( $args[7] );
353
+ }
354
+ else {
355
+ $type = null;
356
+ }
357
+
358
+ $p = $this->_frame->lookup_counter_frame($counter_id);
359
+ $tmp = array();
360
+ while ($p) {
361
+ // We only want to use the counter values when they actually increment the counter,
362
+ // elements that reset the counter, but do not increment it, are skipped.
363
+ // FIXME: Is this the best method of determining that an element's counter value should be displayed?
364
+ if ( array_key_exists( $counter_id , $p->_counters ) && $p->get_frame()->get_style()->counter_reset == 'none' ) {
365
+ array_unshift( $tmp , $p->counter_value($counter_id, $type) );
366
+ }
367
+ $p = $p->lookup_counter_frame($counter_id);
368
+
369
+ }
370
+ $text .= implode( $string , $tmp );
371
+
372
+ }
373
+ else {
374
+ // countertops?
375
+ continue;
376
+ }
377
+
378
+ }
379
+ else if ( isset($match[4]) && $match[4] !== "" ) {
380
+ // String match
381
+ $text .= $this->_parse_string($match[4]);
382
+ }
383
+ else if ( isset($match[7]) && $match[7] !== "" ) {
384
+ // Directive match
385
+
386
+ if ( $match[7] === "open-quote" ) {
387
+ // FIXME: do something here
388
+ $text .= $quotes[0][0];
389
+ }
390
+ else if ( $match[7] === "close-quote" ) {
391
+ // FIXME: do something else here
392
+ $text .= $quotes[0][1];
393
+ }
394
+ else if ( $match[7] === "no-open-quote" ) {
395
+ // FIXME:
396
+ }
397
+ else if ( $match[7] === "no-close-quote" ) {
398
+ // FIXME:
399
+ }
400
+ else if ( mb_strpos($match[7],"attr(") === 0 ) {
401
+
402
+ $i = mb_strpos($match[7],")");
403
+ if ( $i === false ) {
404
+ continue;
405
+ }
406
+
407
+ $attr = mb_substr($match[7], 5, $i - 5);
408
+ if ( $attr == "" ) {
409
+ continue;
410
+ }
411
+
412
+ $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
413
+ }
414
+ else {
415
+ continue;
416
+ }
417
+ }
418
+ }
419
+
420
+ return $text;
421
+ }
422
+
423
+ /**
424
+ * Sets the generated content of a generated frame
425
+ */
426
+ protected function _set_content(){
427
+ $frame = $this->_frame;
428
+ $style = $frame->get_style();
429
+
430
+ if ( $style->counter_reset && ($reset = $style->counter_reset) !== "none" ) {
431
+ $vars = preg_split('/\s+/', trim($reset), 2);
432
+ $frame->reset_counter($vars[0], isset($vars[1]) ? $vars[1] : 0);
433
+ }
434
+
435
+ if ( $style->counter_increment && ($increment = $style->counter_increment) !== "none" ) {
436
+ $frame->increment_counters($increment);
437
+ }
438
+
439
+ if ( $style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated" ) {
440
+ $content = $this->_parse_content();
441
+ $node = $frame->get_node()->ownerDocument->createTextNode($content);
442
+
443
+ $new_style = $style->get_stylesheet()->create_style();
444
+ $new_style->inherit($style);
445
+
446
+ $new_frame = new Frame($node);
447
+ $new_frame->set_style($new_style);
448
+
449
+ Frame_Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root());
450
+ $frame->append_child($new_frame);
451
+ }
452
+ }
453
+ }
lib/dompdf/include/frame_tree.cls.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Represents an entire document as a tree of frames
11
+ *
12
+ * The Frame_Tree consists of {@link Frame} objects each tied to specific
13
+ * DOMNode objects in a specific DomDocument. The Frame_Tree has the same
14
+ * structure as the DomDocument, but adds additional capabalities for
15
+ * styling and layout.
16
+ *
17
+ * @package dompdf
18
+ * @access protected
19
+ */
20
+ class Frame_Tree {
21
+
22
+ /**
23
+ * Tags to ignore while parsing the tree
24
+ *
25
+ * @var array
26
+ */
27
+ static protected $_HIDDEN_TAGS = array("area", "base", "basefont", "head", "style",
28
+ "meta", "title", "colgroup",
29
+ "noembed", "noscript", "param", "#comment");
30
+ /**
31
+ * The main DomDocument
32
+ *
33
+ * @see http://ca2.php.net/manual/en/ref.dom.php
34
+ * @var DomDocument
35
+ */
36
+ protected $_dom;
37
+
38
+ /**
39
+ * The root node of the FrameTree.
40
+ *
41
+ * @var Frame
42
+ */
43
+ protected $_root;
44
+
45
+ /**
46
+ * Subtrees of absolutely positioned elements
47
+ *
48
+ * @var array of Frames
49
+ */
50
+ protected $_absolute_frames;
51
+
52
+ /**
53
+ * A mapping of {@link Frame} objects to DOMNode objects
54
+ *
55
+ * @var array
56
+ */
57
+ protected $_registry;
58
+
59
+
60
+ /**
61
+ * Class constructor
62
+ *
63
+ * @param DomDocument $dom the main DomDocument object representing the current html document
64
+ */
65
+ function __construct(DomDocument $dom) {
66
+ $this->_dom = $dom;
67
+ $this->_root = null;
68
+ $this->_registry = array();
69
+ }
70
+
71
+ function __destruct() {
72
+ clear_object($this);
73
+ }
74
+
75
+ /**
76
+ * Returns the DomDocument object representing the curent html document
77
+ *
78
+ * @return DOMDocument
79
+ */
80
+ function get_dom() {
81
+ return $this->_dom;
82
+ }
83
+
84
+ /**
85
+ * Returns the root frame of the tree
86
+ *
87
+ * @return Page_Frame_Decorator
88
+ */
89
+ function get_root() {
90
+ return $this->_root;
91
+ }
92
+
93
+ /**
94
+ * Returns a specific frame given its id
95
+ *
96
+ * @param string $id
97
+ * @return Frame
98
+ */
99
+ function get_frame($id) {
100
+ return isset($this->_registry[$id]) ? $this->_registry[$id] : null;
101
+ }
102
+
103
+ /**
104
+ * Returns a post-order iterator for all frames in the tree
105
+ *
106
+ * @return FrameTreeList|Frame[]
107
+ */
108
+ function get_frames() {
109
+ return new FrameTreeList($this->_root);
110
+ }
111
+
112
+ /**
113
+ * Builds the tree
114
+ */
115
+ function build_tree() {
116
+ $html = $this->_dom->getElementsByTagName("html")->item(0);
117
+ if ( is_null($html) ) {
118
+ $html = $this->_dom->firstChild;
119
+ }
120
+
121
+ if ( is_null($html) ) {
122
+ throw new DOMPDF_Exception("Requested HTML document contains no data.");
123
+ }
124
+
125
+ $this->fix_tables();
126
+
127
+ $this->_root = $this->_build_tree_r($html);
128
+ }
129
+
130
+ /**
131
+ * Adds missing TBODYs around TR
132
+ */
133
+ protected function fix_tables(){
134
+ $xp = new DOMXPath($this->_dom);
135
+
136
+ // Move table caption before the table
137
+ // FIXME find a better way to deal with it...
138
+ $captions = $xp->query("//table/caption");
139
+ foreach($captions as $caption) {
140
+ $table = $caption->parentNode;
141
+ $table->parentNode->insertBefore($caption, $table);
142
+ }
143
+
144
+ $rows = $xp->query("//table/tr");
145
+ foreach($rows as $row) {
146
+ $tbody = $this->_dom->createElement("tbody");
147
+ $tbody = $row->parentNode->insertBefore($tbody, $row);
148
+ $tbody->appendChild($row);
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Recursively adds {@link Frame} objects to the tree
154
+ *
155
+ * Recursively build a tree of Frame objects based on a dom tree.
156
+ * No layout information is calculated at this time, although the
157
+ * tree may be adjusted (i.e. nodes and frames for generated content
158
+ * and images may be created).
159
+ *
160
+ * @param DOMNode $node the current DOMNode being considered
161
+ * @return Frame
162
+ */
163
+ protected function _build_tree_r(DOMNode $node) {
164
+
165
+ $frame = new Frame($node);
166
+ $id = $frame->get_id();
167
+ $this->_registry[ $id ] = $frame;
168
+
169
+ if ( !$node->hasChildNodes() ) {
170
+ return $frame;
171
+ }
172
+
173
+ // Fixes 'cannot access undefined property for object with
174
+ // overloaded access', fix by Stefan radulian
175
+ // <stefan.radulian@symbion.at>
176
+ //foreach ($node->childNodes as $child) {
177
+
178
+ // Store the children in an array so that the tree can be modified
179
+ $children = array();
180
+ for ($i = 0; $i < $node->childNodes->length; $i++) {
181
+ $children[] = $node->childNodes->item($i);
182
+ }
183
+
184
+ foreach ($children as $child) {
185
+ $node_name = mb_strtolower($child->nodeName);
186
+
187
+ // Skip non-displaying nodes
188
+ if ( in_array($node_name, self::$_HIDDEN_TAGS) ) {
189
+ if ( $node_name !== "head" && $node_name !== "style" ) {
190
+ $child->parentNode->removeChild($child);
191
+ }
192
+
193
+ continue;
194
+ }
195
+
196
+ // Skip empty text nodes
197
+ if ( $node_name === "#text" && $child->nodeValue == "" ) {
198
+ $child->parentNode->removeChild($child);
199
+ continue;
200
+ }
201
+
202
+ // Skip empty image nodes
203
+ if ( $node_name === "img" && $child->getAttribute("src") == "" ) {
204
+ $child->parentNode->removeChild($child);
205
+ continue;
206
+ }
207
+
208
+ $frame->append_child($this->_build_tree_r($child), false);
209
+ }
210
+
211
+ return $frame;
212
+ }
213
+
214
+ public function insert_node(DOMNode $node, DOMNode $new_node, $pos) {
215
+ if ( $pos === "after" || !$node->firstChild ) {
216
+ $node->appendChild($new_node);
217
+ }
218
+ else {
219
+ $node->insertBefore($new_node, $node->firstChild);
220
+ }
221
+
222
+ $this->_build_tree_r($new_node);
223
+
224
+ $frame_id = $new_node->getAttribute("frame_id");
225
+ $frame = $this->get_frame($frame_id);
226
+
227
+ $parent_id = $node->getAttribute("frame_id");
228
+ $parent = $this->get_frame($parent_id);
229
+
230
+ if ( $pos === "before" ) {
231
+ $parent->prepend_child($frame, false);
232
+ }
233
+ else {
234
+ $parent->append_child($frame, false);
235
+ }
236
+
237
+ return $frame_id;
238
+ }
239
+ }
lib/dompdf/include/functions.inc.php ADDED
@@ -0,0 +1,1026 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ if ( !defined('PHP_VERSION_ID') ) {
12
+ $version = explode('.', PHP_VERSION);
13
+ define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
14
+ }
15
+
16
+ /**
17
+ * Defined a constant if not already defined
18
+ *
19
+ * @param string $name The constant name
20
+ * @param mixed $value The value
21
+ */
22
+ function def($name, $value = true) {
23
+ if ( !defined($name) ) {
24
+ define($name, $value);
25
+ }
26
+ }
27
+
28
+ if ( !function_exists("pre_r") ) {
29
+ /**
30
+ * print_r wrapper for html/cli output
31
+ *
32
+ * Wraps print_r() output in < pre > tags if the current sapi is not 'cli'.
33
+ * Returns the output string instead of displaying it if $return is true.
34
+ *
35
+ * @param mixed $mixed variable or expression to display
36
+ * @param bool $return
37
+ *
38
+ * @return string
39
+ */
40
+ function pre_r($mixed, $return = false) {
41
+ if ( $return ) {
42
+ return "<pre>" . print_r($mixed, true) . "</pre>";
43
+ }
44
+
45
+ if ( php_sapi_name() !== "cli" ) {
46
+ echo "<pre>";
47
+ }
48
+
49
+ print_r($mixed);
50
+
51
+ if ( php_sapi_name() !== "cli" ) {
52
+ echo "</pre>";
53
+ }
54
+ else {
55
+ echo "\n";
56
+ }
57
+
58
+ flush();
59
+
60
+ }
61
+ }
62
+
63
+ if ( !function_exists("pre_var_dump") ) {
64
+ /**
65
+ * var_dump wrapper for html/cli output
66
+ *
67
+ * Wraps var_dump() output in < pre > tags if the current sapi is not 'cli'.
68
+ *
69
+ * @param mixed $mixed variable or expression to display.
70
+ */
71
+ function pre_var_dump($mixed) {
72
+ if ( php_sapi_name() !== "cli" ) {
73
+ echo "<pre>";
74
+ }
75
+
76
+ var_dump($mixed);
77
+
78
+ if ( php_sapi_name() !== "cli" ) {
79
+ echo "</pre>";
80
+ }
81
+ }
82
+ }
83
+
84
+ if ( !function_exists("d") ) {
85
+ /**
86
+ * generic debug function
87
+ *
88
+ * Takes everything and does its best to give a good debug output
89
+ *
90
+ * @param mixed $mixed variable or expression to display.
91
+ */
92
+ function d($mixed) {
93
+ if ( php_sapi_name() !== "cli" ) {
94
+ echo "<pre>";
95
+ }
96
+
97
+ // line
98
+ if ( $mixed instanceof Line_Box ) {
99
+ echo $mixed;
100
+ }
101
+
102
+ // other
103
+ else {
104
+ var_export($mixed);
105
+ }
106
+
107
+ if ( php_sapi_name() !== "cli" ) {
108
+ echo "</pre>";
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * builds a full url given a protocol, hostname, base path and url
115
+ *
116
+ * @param string $protocol
117
+ * @param string $host
118
+ * @param string $base_path
119
+ * @param string $url
120
+ * @return string
121
+ *
122
+ * Initially the trailing slash of $base_path was optional, and conditionally appended.
123
+ * However on dynamically created sites, where the page is given as url parameter,
124
+ * the base path might not end with an url.
125
+ * Therefore do not append a slash, and **require** the $base_url to ending in a slash
126
+ * when needed.
127
+ * Vice versa, on using the local file system path of a file, make sure that the slash
128
+ * is appended (o.k. also for Windows)
129
+ */
130
+ function build_url($protocol, $host, $base_path, $url) {
131
+ if ( strlen($url) == 0 ) {
132
+ //return $protocol . $host . rtrim($base_path, "/\\") . "/";
133
+ return $protocol . $host . $base_path;
134
+ }
135
+
136
+ // Is the url already fully qualified or a Data URI?
137
+ if ( mb_strpos($url, "://") !== false || mb_strpos($url, "data:") === 0 ) {
138
+ return $url;
139
+ }
140
+
141
+ $ret = $protocol;
142
+
143
+ if ( !in_array(mb_strtolower($protocol), array("http://", "https://", "ftp://", "ftps://")) ) {
144
+ //On Windows local file, an abs path can begin also with a '\' or a drive letter and colon
145
+ //drive: followed by a relative path would be a drive specific default folder.
146
+ //not known in php app code, treat as abs path
147
+ //($url[1] !== ':' || ($url[2]!=='\\' && $url[2]!=='/'))
148
+ if ( $url[0] !== '/' && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || ($url[0] !== '\\' && $url[1] !== ':')) ) {
149
+ // For rel path and local acess we ignore the host, and run the path through realpath()
150
+ $ret .= realpath($base_path).'/';
151
+ }
152
+ $ret .= $url;
153
+ $ret = preg_replace('/\?(.*)$/', "", $ret);
154
+ return $ret;
155
+ }
156
+
157
+ //remote urls with backslash in html/css are not really correct, but lets be genereous
158
+ if ( $url[0] === '/' || $url[0] === '\\' ) {
159
+ // Absolute path
160
+ $ret .= $host . $url;
161
+ }
162
+ else {
163
+ // Relative path
164
+ //$base_path = $base_path !== "" ? rtrim($base_path, "/\\") . "/" : "";
165
+ $ret .= $host . $base_path . $url;
166
+ }
167
+
168
+ return $ret;
169
+
170
+ }
171
+
172
+ /**
173
+ * parse a full url or pathname and return an array(protocol, host, path,
174
+ * file + query + fragment)
175
+ *
176
+ * @param string $url
177
+ * @return array
178
+ */
179
+ function explode_url($url) {
180
+ $protocol = "";
181
+ $host = "";
182
+ $path = "";
183
+ $file = "";
184
+
185
+ $arr = parse_url($url);
186
+
187
+ // Exclude windows drive letters...
188
+ if ( isset($arr["scheme"]) && $arr["scheme"] !== "file" && strlen($arr["scheme"]) > 1 ) {
189
+ $protocol = $arr["scheme"] . "://";
190
+
191
+ if ( isset($arr["user"]) ) {
192
+ $host .= $arr["user"];
193
+
194
+ if ( isset($arr["pass"]) ) {
195
+ $host .= ":" . $arr["pass"];
196
+ }
197
+
198
+ $host .= "@";
199
+ }
200
+
201
+ if ( isset($arr["host"]) ) {
202
+ $host .= $arr["host"];
203
+ }
204
+
205
+ if ( isset($arr["port"]) ) {
206
+ $host .= ":" . $arr["port"];
207
+ }
208
+
209
+ if ( isset($arr["path"]) && $arr["path"] !== "" ) {
210
+ // Do we have a trailing slash?
211
+ if ( $arr["path"][ mb_strlen($arr["path"]) - 1 ] === "/" ) {
212
+ $path = $arr["path"];
213
+ $file = "";
214
+ }
215
+ else {
216
+ $path = rtrim(dirname($arr["path"]), '/\\') . "/";
217
+ $file = basename($arr["path"]);
218
+ }
219
+ }
220
+
221
+ if ( isset($arr["query"]) ) {
222
+ $file .= "?" . $arr["query"];
223
+ }
224
+
225
+ if ( isset($arr["fragment"]) ) {
226
+ $file .= "#" . $arr["fragment"];
227
+ }
228
+
229
+ }
230
+ else {
231
+
232
+ $i = mb_strpos($url, "file://");
233
+ if ( $i !== false ) {
234
+ $url = mb_substr($url, $i + 7);
235
+ }
236
+
237
+ $protocol = ""; // "file://"; ? why doesn't this work... It's because of
238
+ // network filenames like //COMPU/SHARENAME
239
+
240
+ $host = ""; // localhost, really
241
+ $file = basename($url);
242
+
243
+ $path = dirname($url);
244
+
245
+ // Check that the path exists
246
+ if ( $path !== false ) {
247
+ $path .= '/';
248
+
249
+ }
250
+ else {
251
+ // generate a url to access the file if no real path found.
252
+ $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
253
+
254
+ $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n");
255
+
256
+ if ( substr($arr["path"], 0, 1) === '/' ) {
257
+ $path = dirname($arr["path"]);
258
+ }
259
+ else {
260
+ $path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"];
261
+ }
262
+ }
263
+ }
264
+
265
+ $ret = array($protocol, $host, $path, $file,
266
+ "protocol" => $protocol,
267
+ "host" => $host,
268
+ "path" => $path,
269
+ "file" => $file);
270
+ return $ret;
271
+ }
272
+
273
+ /**
274
+ * Converts decimal numbers to roman numerals
275
+ *
276
+ * @param int $num
277
+ *
278
+ * @throws DOMPDF_Exception
279
+ * @return string
280
+ */
281
+ function dec2roman($num) {
282
+
283
+ static $ones = array("", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix");
284
+ static $tens = array("", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc");
285
+ static $hund = array("", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm");
286
+ static $thou = array("", "m", "mm", "mmm");
287
+
288
+ if ( !is_numeric($num) ) {
289
+ throw new DOMPDF_Exception("dec2roman() requires a numeric argument.");
290
+ }
291
+
292
+ if ( $num > 4000 || $num < 0 ) {
293
+ return "(out of range)";
294
+ }
295
+
296
+ $num = strrev((string)$num);
297
+
298
+ $ret = "";
299
+ switch (mb_strlen($num)) {
300
+ case 4: $ret .= $thou[$num[3]];
301
+ case 3: $ret .= $hund[$num[2]];
302
+ case 2: $ret .= $tens[$num[1]];
303
+ case 1: $ret .= $ones[$num[0]];
304
+ default: break;
305
+ }
306
+
307
+ return $ret;
308
+ }
309
+
310
+ /**
311
+ * Determines whether $value is a percentage or not
312
+ *
313
+ * @param float $value
314
+ *
315
+ * @return bool
316
+ */
317
+ function is_percent($value) {
318
+ return false !== mb_strpos($value, "%");
319
+ }
320
+
321
+ /**
322
+ * Parses a data URI scheme
323
+ * http://en.wikipedia.org/wiki/Data_URI_scheme
324
+ *
325
+ * @param string $data_uri The data URI to parse
326
+ *
327
+ * @return array The result with charset, mime type and decoded data
328
+ */
329
+ function parse_data_uri($data_uri) {
330
+ if (!preg_match('/^data:(?P<mime>[a-z0-9\/+-.]+)(;charset=(?P<charset>[a-z0-9-])+)?(?P<base64>;base64)?\,(?P<data>.*)?/i', $data_uri, $match)) {
331
+ return false;
332
+ }
333
+
334
+ $match['data'] = rawurldecode($match['data']);
335
+ $result = array(
336
+ 'charset' => $match['charset'] ? $match['charset'] : 'US-ASCII',
337
+ 'mime' => $match['mime'] ? $match['mime'] : 'text/plain',
338
+ 'data' => $match['base64'] ? base64_decode($match['data']) : $match['data'],
339
+ );
340
+
341
+ return $result;
342
+ }
343
+
344
+ /**
345
+ * mb_string compatibility
346
+ */
347
+ if ( !function_exists("mb_strlen") ) {
348
+ define('MB_OVERLOAD_MAIL', 1);
349
+ define('MB_OVERLOAD_STRING', 2);
350
+ define('MB_OVERLOAD_REGEX', 4);
351
+ define('MB_CASE_UPPER', 0);
352
+ define('MB_CASE_LOWER', 1);
353
+ define('MB_CASE_TITLE', 2);
354
+
355
+ function mb_convert_encoding($data, $to_encoding, $from_encoding = 'UTF-8') {
356
+ if (str_replace('-', '', strtolower($to_encoding)) === 'utf8') {
357
+ return utf8_encode($data);
358
+ }
359
+
360
+ return utf8_decode($data);
361
+ }
362
+
363
+ function mb_detect_encoding($data, $encoding_list = array('iso-8859-1'), $strict = false) {
364
+ return 'iso-8859-1';
365
+ }
366
+
367
+ function mb_detect_order($encoding_list = array('iso-8859-1')) {
368
+ return 'iso-8859-1';
369
+ }
370
+
371
+ function mb_internal_encoding($encoding = null) {
372
+ if (isset($encoding)) {
373
+ return true;
374
+ }
375
+
376
+ return 'iso-8859-1';
377
+ }
378
+
379
+ function mb_strlen($str, $encoding = 'iso-8859-1') {
380
+ switch (str_replace('-', '', strtolower($encoding))) {
381
+ case "utf8": return strlen(utf8_encode($str));
382
+ case "8bit": return strlen($str);
383
+ default: return strlen(utf8_decode($str));
384
+ }
385
+ }
386
+
387
+ function mb_strpos($haystack, $needle, $offset = 0) {
388
+ return strpos($haystack, $needle, $offset);
389
+ }
390
+
391
+ function mb_strrpos($haystack, $needle, $offset = 0) {
392
+ return strrpos($haystack, $needle, $offset);
393
+ }
394
+
395
+ function mb_strtolower( $str ) {
396
+ return strtolower($str);
397
+ }
398
+
399
+ function mb_strtoupper( $str ) {
400
+ return strtoupper($str);
401
+ }
402
+
403
+ function mb_substr($string, $start, $length = null, $encoding = 'iso-8859-1') {
404
+ if ( is_null($length) ) {
405
+ return substr($string, $start);
406
+ }
407
+
408
+ return substr($string, $start, $length);
409
+ }
410
+
411
+ function mb_substr_count($haystack, $needle, $encoding = 'iso-8859-1') {
412
+ return substr_count($haystack, $needle);
413
+ }
414
+
415
+ function mb_encode_numericentity($str, $convmap, $encoding) {
416
+ return htmlspecialchars($str);
417
+ }
418
+
419
+ function mb_convert_case($str, $mode = MB_CASE_UPPER, $encoding = array()) {
420
+ switch($mode) {
421
+ case MB_CASE_UPPER: return mb_strtoupper($str);
422
+ case MB_CASE_LOWER: return mb_strtolower($str);
423
+ case MB_CASE_TITLE: return ucwords(mb_strtolower($str));
424
+ default: return $str;
425
+ }
426
+ }
427
+
428
+ function mb_list_encodings() {
429
+ return array(
430
+ "ISO-8859-1",
431
+ "UTF-8",
432
+ "8bit",
433
+ );
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Decoder for RLE8 compression in windows bitmaps
439
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
440
+ *
441
+ * @param string $str Data to decode
442
+ * @param integer $width Image width
443
+ *
444
+ * @return string
445
+ */
446
+ function rle8_decode ($str, $width){
447
+ $lineWidth = $width + (3 - ($width-1) % 4);
448
+ $out = '';
449
+ $cnt = strlen($str);
450
+
451
+ for ($i = 0; $i <$cnt; $i++) {
452
+ $o = ord($str[$i]);
453
+ switch ($o){
454
+ case 0: # ESCAPE
455
+ $i++;
456
+ switch (ord($str[$i])){
457
+ case 0: # NEW LINE
458
+ $padCnt = $lineWidth - strlen($out)%$lineWidth;
459
+ if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
460
+ break;
461
+ case 1: # END OF FILE
462
+ $padCnt = $lineWidth - strlen($out)%$lineWidth;
463
+ if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
464
+ break 3;
465
+ case 2: # DELTA
466
+ $i += 2;
467
+ break;
468
+ default: # ABSOLUTE MODE
469
+ $num = ord($str[$i]);
470
+ for ($j = 0; $j < $num; $j++)
471
+ $out .= $str[++$i];
472
+ if ($num % 2) $i++;
473
+ }
474
+ break;
475
+ default:
476
+ $out .= str_repeat($str[++$i], $o);
477
+ }
478
+ }
479
+ return $out;
480
+ }
481
+
482
+ /**
483
+ * Decoder for RLE4 compression in windows bitmaps
484
+ * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
485
+ *
486
+ * @param string $str Data to decode
487
+ * @param integer $width Image width
488
+ *
489
+ * @return string
490
+ */
491
+ function rle4_decode ($str, $width) {
492
+ $w = floor($width/2) + ($width % 2);
493
+ $lineWidth = $w + (3 - ( ($width-1) / 2) % 4);
494
+ $pixels = array();
495
+ $cnt = strlen($str);
496
+ $c = 0;
497
+
498
+ for ($i = 0; $i < $cnt; $i++) {
499
+ $o = ord($str[$i]);
500
+ switch ($o) {
501
+ case 0: # ESCAPE
502
+ $i++;
503
+ switch (ord($str[$i])){
504
+ case 0: # NEW LINE
505
+ while (count($pixels)%$lineWidth != 0) {
506
+ $pixels[] = 0;
507
+ }
508
+ break;
509
+ case 1: # END OF FILE
510
+ while (count($pixels)%$lineWidth != 0) {
511
+ $pixels[] = 0;
512
+ }
513
+ break 3;
514
+ case 2: # DELTA
515
+ $i += 2;
516
+ break;
517
+ default: # ABSOLUTE MODE
518
+ $num = ord($str[$i]);
519
+ for ($j = 0; $j < $num; $j++) {
520
+ if ($j%2 == 0) {
521
+ $c = ord($str[++$i]);
522
+ $pixels[] = ($c & 240)>>4;
523
+ }
524
+ else {
525
+ $pixels[] = $c & 15;
526
+ }
527
+ }
528
+
529
+ if ($num % 2 == 0) {
530
+ $i++;
531
+ }
532
+ }
533
+ break;
534
+ default:
535
+ $c = ord($str[++$i]);
536
+ for ($j = 0; $j < $o; $j++) {
537
+ $pixels[] = ($j%2==0 ? ($c & 240)>>4 : $c & 15);
538
+ }
539
+ }
540
+ }
541
+
542
+ $out = '';
543
+ if (count($pixels)%2) {
544
+ $pixels[] = 0;
545
+ }
546
+
547
+ $cnt = count($pixels)/2;
548
+
549
+ for ($i = 0; $i < $cnt; $i++) {
550
+ $out .= chr(16*$pixels[2*$i] + $pixels[2*$i+1]);
551
+ }
552
+
553
+ return $out;
554
+ }
555
+
556
+ if ( !function_exists("imagecreatefrombmp") ) {
557
+
558
+ /**
559
+ * Credit goes to mgutt
560
+ * http://www.programmierer-forum.de/function-imagecreatefrombmp-welche-variante-laeuft-t143137.htm
561
+ * Modified by Fabien Menager to support RGB555 BMP format
562
+ */
563
+ function imagecreatefrombmp($filename) {
564
+ if (!function_exists("imagecreatetruecolor")) {
565
+ trigger_error("The PHP GD extension is required, but is not installed.", E_ERROR);
566
+ return false;
567
+ }
568
+
569
+ // version 1.00
570
+ if (!($fh = fopen($filename, 'rb'))) {
571
+ trigger_error('imagecreatefrombmp: Can not open ' . $filename, E_USER_WARNING);
572
+ return false;
573
+ }
574
+
575
+ $bytes_read = 0;
576
+
577
+ // read file header
578
+ $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14));
579
+
580
+ // check for bitmap
581
+ if ($meta['type'] != 19778) {
582
+ trigger_error('imagecreatefrombmp: ' . $filename . ' is not a bitmap!', E_USER_WARNING);
583
+ return false;
584
+ }
585
+
586
+ // read image header
587
+ $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40));
588
+ $bytes_read += 40;
589
+
590
+ // read additional bitfield header
591
+ if ($meta['compression'] == 3) {
592
+ $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12));
593
+ $bytes_read += 12;
594
+ }
595
+
596
+ // set bytes and padding
597
+ $meta['bytes'] = $meta['bits'] / 8;
598
+ $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4)- floor($meta['width'] * $meta['bytes'] / 4)));
599
+ if ($meta['decal'] == 4) {
600
+ $meta['decal'] = 0;
601
+ }
602
+
603
+ // obtain imagesize
604
+ if ($meta['imagesize'] < 1) {
605
+ $meta['imagesize'] = $meta['filesize'] - $meta['offset'];
606
+ // in rare cases filesize is equal to offset so we need to read physical size
607
+ if ($meta['imagesize'] < 1) {
608
+ $meta['imagesize'] = @filesize($filename) - $meta['offset'];
609
+ if ($meta['imagesize'] < 1) {
610
+ trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING);
611
+ return false;
612
+ }
613
+ }
614
+ }
615
+
616
+ // calculate colors
617
+ $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
618
+
619
+ // read color palette
620
+ $palette = array();
621
+ if ($meta['bits'] < 16) {
622
+ $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
623
+ // in rare cases the color value is signed
624
+ if ($palette[1] < 0) {
625
+ foreach ($palette as $i => $color) {
626
+ $palette[$i] = $color + 16777216;
627
+ }
628
+ }
629
+ }
630
+
631
+ // ignore extra bitmap headers
632
+ if ($meta['headersize'] > $bytes_read) {
633
+ fread($fh, $meta['headersize'] - $bytes_read);
634
+ }
635
+
636
+ // create gd image
637
+ $im = imagecreatetruecolor($meta['width'], $meta['height']);
638
+ $data = fread($fh, $meta['imagesize']);
639
+
640
+ // uncompress data
641
+ switch ($meta['compression']) {
642
+ case 1: $data = rle8_decode($data, $meta['width']); break;
643
+ case 2: $data = rle4_decode($data, $meta['width']); break;
644
+ }
645
+
646
+ $p = 0;
647
+ $vide = chr(0);
648
+ $y = $meta['height'] - 1;
649
+ $error = 'imagecreatefrombmp: ' . $filename . ' has not enough data!';
650
+
651
+ // loop through the image data beginning with the lower left corner
652
+ while ($y >= 0) {
653
+ $x = 0;
654
+ while ($x < $meta['width']) {
655
+ switch ($meta['bits']) {
656
+ case 32:
657
+ case 24:
658
+ if (!($part = substr($data, $p, 3 /*$meta['bytes']*/))) {
659
+ trigger_error($error, E_USER_WARNING);
660
+ return $im;
661
+ }
662
+ $color = unpack('V', $part . $vide);
663
+ break;
664
+ case 16:
665
+ if (!($part = substr($data, $p, 2 /*$meta['bytes']*/))) {
666
+ trigger_error($error, E_USER_WARNING);
667
+ return $im;
668
+ }
669
+ $color = unpack('v', $part);
670
+
671
+ if (empty($meta['rMask']) || $meta['rMask'] != 0xf800) {
672
+ $color[1] = (($color[1] & 0x7c00) >> 7) * 65536 + (($color[1] & 0x03e0) >> 2) * 256 + (($color[1] & 0x001f) << 3); // 555
673
+ }
674
+ else {
675
+ $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); // 565
676
+ }
677
+ break;
678
+ case 8:
679
+ $color = unpack('n', $vide . substr($data, $p, 1));
680
+ $color[1] = $palette[ $color[1] + 1 ];
681
+ break;
682
+ case 4:
683
+ $color = unpack('n', $vide . substr($data, floor($p), 1));
684
+ $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
685
+ $color[1] = $palette[ $color[1] + 1 ];
686
+ break;
687
+ case 1:
688
+ $color = unpack('n', $vide . substr($data, floor($p), 1));
689
+ switch (($p * 8) % 8) {
690
+ case 0: $color[1] = $color[1] >> 7; break;
691
+ case 1: $color[1] = ($color[1] & 0x40) >> 6; break;
692
+ case 2: $color[1] = ($color[1] & 0x20) >> 5; break;
693
+ case 3: $color[1] = ($color[1] & 0x10) >> 4; break;
694
+ case 4: $color[1] = ($color[1] & 0x8 ) >> 3; break;
695
+ case 5: $color[1] = ($color[1] & 0x4 ) >> 2; break;
696
+ case 6: $color[1] = ($color[1] & 0x2 ) >> 1; break;
697
+ case 7: $color[1] = ($color[1] & 0x1 ); break;
698
+ }
699
+ $color[1] = $palette[ $color[1] + 1 ];
700
+ break;
701
+ default:
702
+ trigger_error('imagecreatefrombmp: ' . $filename . ' has ' . $meta['bits'] . ' bits and this is not supported!', E_USER_WARNING);
703
+ return false;
704
+ }
705
+ imagesetpixel($im, $x, $y, $color[1]);
706
+ $x++;
707
+ $p += $meta['bytes'];
708
+ }
709
+ $y--;
710
+ $p += $meta['decal'];
711
+ }
712
+ fclose($fh);
713
+ return $im;
714
+ }
715
+ }
716
+
717
+ /**
718
+ * getimagesize doesn't give a good size for 32bit BMP image v5
719
+ *
720
+ * @param string $filename
721
+ * @return array The same format as getimagesize($filename)
722
+ */
723
+ function dompdf_getimagesize($filename) {
724
+ static $cache = array();
725
+
726
+ if ( isset($cache[$filename]) ) {
727
+ return $cache[$filename];
728
+ }
729
+
730
+ list($width, $height, $type) = getimagesize($filename);
731
+
732
+ if ( $width == null || $height == null ) {
733
+ $data = file_get_contents($filename, null, null, 0, 26);
734
+
735
+ if ( substr($data, 0, 2) === "BM" ) {
736
+ $meta = unpack('vtype/Vfilesize/Vreserved/Voffset/Vheadersize/Vwidth/Vheight', $data);
737
+ $width = (int)$meta['width'];
738
+ $height = (int)$meta['height'];
739
+ $type = IMAGETYPE_BMP;
740
+ }
741
+ }
742
+
743
+ return $cache[$filename] = array($width, $height, $type);
744
+ }
745
+
746
+ /**
747
+ * Converts a CMYK color to RGB
748
+ *
749
+ * @param float|float[] $c
750
+ * @param float $m
751
+ * @param float $y
752
+ * @param float $k
753
+ *
754
+ * @return float[]
755
+ */
756
+ function cmyk_to_rgb($c, $m = null, $y = null, $k = null) {
757
+ if (is_array($c)) {
758
+ list($c, $m, $y, $k) = $c;
759
+ }
760
+
761
+ $c *= 255;
762
+ $m *= 255;
763
+ $y *= 255;
764
+ $k *= 255;
765
+
766
+ $r = (1 - round(2.55 * ($c+$k))) ;
767
+ $g = (1 - round(2.55 * ($m+$k))) ;
768
+ $b = (1 - round(2.55 * ($y+$k))) ;
769
+
770
+ if ($r < 0) $r = 0;
771
+ if ($g < 0) $g = 0;
772
+ if ($b < 0) $b = 0;
773
+
774
+ return array(
775
+ $r, $g, $b,
776
+ "r" => $r, "g" => $g, "b" => $b
777
+ );
778
+ }
779
+
780
+ function unichr($c) {
781
+ if ($c <= 0x7F) {
782
+ return chr($c);
783
+ }
784
+ else if ($c <= 0x7FF) {
785
+ return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F);
786
+ }
787
+ else if ($c <= 0xFFFF) {
788
+ return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F)
789
+ . chr(0x80 | $c & 0x3F);
790
+ }
791
+ else if ($c <= 0x10FFFF) {
792
+ return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F)
793
+ . chr(0x80 | $c >> 6 & 0x3F)
794
+ . chr(0x80 | $c & 0x3F);
795
+ }
796
+ return false;
797
+ }
798
+
799
+ if ( !function_exists("date_default_timezone_get") ) {
800
+ function date_default_timezone_get() {
801
+ return "";
802
+ }
803
+
804
+ function date_default_timezone_set($timezone_identifier) {
805
+ return true;
806
+ }
807
+ }
808
+
809
+ /**
810
+ * Stores warnings in an array for display later
811
+ * This function allows warnings generated by the DomDocument parser
812
+ * and CSS loader ({@link Stylesheet}) to be captured and displayed
813
+ * later. Without this function, errors are displayed immediately and
814
+ * PDF streaming is impossible.
815
+ * @see http://www.php.net/manual/en/function.set-error_handler.php
816
+ *
817
+ * @param int $errno
818
+ * @param string $errstr
819
+ * @param string $errfile
820
+ * @param string $errline
821
+ *
822
+ * @throws DOMPDF_Exception
823
+ */
824
+ function record_warnings($errno, $errstr, $errfile, $errline) {
825
+
826
+ // Not a warning or notice
827
+ if ( !($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING )) ) {
828
+ throw new DOMPDF_Exception($errstr . " $errno");
829
+ }
830
+
831
+ global $_dompdf_warnings;
832
+ global $_dompdf_show_warnings;
833
+
834
+ if ( $_dompdf_show_warnings ) {
835
+ echo $errstr . "\n";
836
+ }
837
+
838
+ $_dompdf_warnings[] = $errstr;
839
+ }
840
+
841
+ /**
842
+ * Print a useful backtrace
843
+ */
844
+ function bt() {
845
+ if ( php_sapi_name() !== "cli") {
846
+ echo "<pre>";
847
+ }
848
+
849
+ $bt = debug_backtrace();
850
+
851
+ array_shift($bt); // remove actual bt() call
852
+ echo "\n";
853
+
854
+ $i = 0;
855
+ foreach ($bt as $call) {
856
+ $file = basename($call["file"]) . " (" . $call["line"] . ")";
857
+ if ( isset($call["class"]) ) {
858
+ $func = $call["class"] . "->" . $call["function"] . "()";
859
+ }
860
+ else {
861
+ $func = $call["function"] . "()";
862
+ }
863
+
864
+ echo "#" . str_pad($i, 2, " ", STR_PAD_RIGHT) . ": " . str_pad($file.":", 42) . " $func\n";
865
+ $i++;
866
+ }
867
+ echo "\n";
868
+
869
+ if ( php_sapi_name() !== "cli") {
870
+ echo "</pre>";
871
+ }
872
+ }
873
+
874
+ /**
875
+ * Print debug messages
876
+ *
877
+ * @param string $type The type of debug messages to print
878
+ * @param string $msg The message to show
879
+ */
880
+ function dompdf_debug($type, $msg) {
881
+ global $_DOMPDF_DEBUG_TYPES, $_dompdf_show_warnings, $_dompdf_debug;
882
+ if ( isset($_DOMPDF_DEBUG_TYPES[$type]) && ($_dompdf_show_warnings || $_dompdf_debug) ) {
883
+ $arr = debug_backtrace();
884
+
885
+ echo basename($arr[0]["file"]) . " (" . $arr[0]["line"] ."): " . $arr[1]["function"] . ": ";
886
+ pre_r($msg);
887
+ }
888
+ }
889
+
890
+ if ( !function_exists("print_memusage") ) {
891
+ /**
892
+ * Dump memory usage
893
+ */
894
+ function print_memusage() {
895
+ global $memusage;
896
+ echo "Memory Usage\n";
897
+ $prev = 0;
898
+ $initial = reset($memusage);
899
+ echo str_pad("Initial:", 40) . $initial . "\n\n";
900
+
901
+ foreach ($memusage as $key=>$mem) {
902
+ $mem -= $initial;
903
+ echo str_pad("$key:" , 40);
904
+ echo str_pad("$mem", 12) . "(diff: " . ($mem - $prev) . ")\n";
905
+ $prev = $mem;
906
+ }
907
+
908
+ echo "\n" . str_pad("Total:", 40) . memory_get_usage() . "\n";
909
+ }
910
+ }
911
+
912
+ if ( !function_exists("enable_mem_profile") ) {
913
+ /**
914
+ * Initialize memory profiling code
915
+ */
916
+ function enable_mem_profile() {
917
+ global $memusage;
918
+ $memusage = array("Startup" => memory_get_usage());
919
+ register_shutdown_function("print_memusage");
920
+ }
921
+ }
922
+
923
+ if ( !function_exists("mark_memusage") ) {
924
+ /**
925
+ * Record the current memory usage
926
+ *
927
+ * @param string $location a meaningful location
928
+ */
929
+ function mark_memusage($location) {
930
+ global $memusage;
931
+ if ( isset($memusage) ) {
932
+ $memusage[$location] = memory_get_usage();
933
+ }
934
+ }
935
+ }
936
+
937
+ if ( !function_exists('sys_get_temp_dir')) {
938
+ /**
939
+ * Find the current system temporary directory
940
+ *
941
+ * @link http://us.php.net/manual/en/function.sys-get-temp-dir.php#85261
942
+ */
943
+ function sys_get_temp_dir() {
944
+ if (!empty($_ENV['TMP'])) {
945
+ return realpath($_ENV['TMP']);
946
+ }
947
+
948
+ if (!empty($_ENV['TMPDIR'])) {
949
+ return realpath( $_ENV['TMPDIR']);
950
+ }
951
+
952
+ if (!empty($_ENV['TEMP'])) {
953
+ return realpath( $_ENV['TEMP']);
954
+ }
955
+
956
+ $tempfile=tempnam(uniqid(rand(), true), '');
957
+ if (file_exists($tempfile)) {
958
+ unlink($tempfile);
959
+ return realpath(dirname($tempfile));
960
+ }
961
+ }
962
+ }
963
+
964
+ if ( function_exists("memory_get_peak_usage") ) {
965
+ function DOMPDF_memory_usage(){
966
+ return memory_get_peak_usage(true);
967
+ }
968
+ }
969
+ else if ( function_exists("memory_get_usage") ) {
970
+ function DOMPDF_memory_usage(){
971
+ return memory_get_usage(true);
972
+ }
973
+ }
974
+ else {
975
+ function DOMPDF_memory_usage(){
976
+ return "N/A";
977
+ }
978
+ }
979
+
980
+ if ( function_exists("curl_init") ) {
981
+ function DOMPDF_fetch_url($url, &$headers = null) {
982
+ $ch = curl_init($url);
983
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
984
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
985
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
986
+ curl_setopt($ch, CURLOPT_HEADER, true);
987
+
988
+ $data = curl_exec($ch);
989
+ $raw_headers = substr($data, 0, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
990
+ $headers = preg_split("/[\n\r]+/", trim($raw_headers));
991
+ $data = substr($data, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
992
+ curl_close($ch);
993
+
994
+ return $data;
995
+ }
996
+ }
997
+ else {
998
+ function DOMPDF_fetch_url($url, &$headers = null) {
999
+ $data = file_get_contents($url);
1000
+ $headers = $http_response_header;
1001
+
1002
+ return $data;
1003
+ }
1004
+ }
1005
+
1006
+ /**
1007
+ * Affect null to the unused objects
1008
+ * @param mixed $object
1009
+ */
1010
+ if ( PHP_VERSION_ID < 50300 ) {
1011
+ function clear_object(&$object) {
1012
+ if ( is_object($object) ) {
1013
+ foreach ($object as &$value) {
1014
+ clear_object($value);
1015
+ }
1016
+ }
1017
+
1018
+ $object = null;
1019
+ unset($object);
1020
+ }
1021
+ }
1022
+ else {
1023
+ function clear_object(&$object) {
1024
+ // void
1025
+ }
1026
+ }
lib/dompdf/include/gd_adapter.cls.php ADDED
@@ -0,0 +1,840 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Image rendering interface
12
+ *
13
+ * Renders to an image format supported by GD (jpeg, gif, png, xpm).
14
+ * Not super-useful day-to-day but handy nonetheless
15
+ *
16
+ * @package dompdf
17
+ */
18
+ class GD_Adapter implements Canvas {
19
+ /**
20
+ * @var DOMPDF
21
+ */
22
+ private $_dompdf;
23
+
24
+ /**
25
+ * Resource handle for the image
26
+ *
27
+ * @var resource
28
+ */
29
+ private $_img;
30
+
31
+ /**
32
+ * Image width in pixels
33
+ *
34
+ * @var int
35
+ */
36
+ private $_width;
37
+
38
+ /**
39
+ * Image height in pixels
40
+ *
41
+ * @var int
42
+ */
43
+ private $_height;
44
+
45
+ /**
46
+ * Current page number
47
+ *
48
+ * @var int
49
+ */
50
+ private $_page_number;
51
+
52
+ /**
53
+ * Total number of pages
54
+ *
55
+ * @var int
56
+ */
57
+ private $_page_count;
58
+
59
+ /**
60
+ * Image antialias factor
61
+ *
62
+ * @var float
63
+ */
64
+ private $_aa_factor;
65
+
66
+ /**
67
+ * Allocated colors
68
+ *
69
+ * @var array
70
+ */
71
+ private $_colors;
72
+
73
+ /**
74
+ * Background color
75
+ *
76
+ * @var int
77
+ */
78
+ private $_bg_color;
79
+
80
+ /**
81
+ * Class constructor
82
+ *
83
+ * @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc.
84
+ * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
85
+ * @param DOMPDF $dompdf
86
+ * @param float $aa_factor Anti-aliasing factor, 1 for no AA
87
+ * @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
88
+ */
89
+ function __construct($size, $orientation = "portrait", DOMPDF $dompdf, $aa_factor = 1.0, $bg_color = array(1,1,1,0) ) {
90
+
91
+ if ( !is_array($size) ) {
92
+ $size = strtolower($size);
93
+
94
+ if ( isset(CPDF_Adapter::$PAPER_SIZES[$size]) ) {
95
+ $size = CPDF_Adapter::$PAPER_SIZES[$size];
96
+ }
97
+ else {
98
+ $size = CPDF_Adapter::$PAPER_SIZES["letter"];
99
+ }
100
+ }
101
+
102
+ if ( strtolower($orientation) === "landscape" ) {
103
+ list($size[2],$size[3]) = array($size[3],$size[2]);
104
+ }
105
+
106
+ $this->_dompdf = $dompdf;
107
+
108
+ if ( $aa_factor < 1 ) {
109
+ $aa_factor = 1;
110
+ }
111
+
112
+ $this->_aa_factor = $aa_factor;
113
+
114
+ $size[2] *= $aa_factor;
115
+ $size[3] *= $aa_factor;
116
+
117
+ $this->_width = $size[2] - $size[0];
118
+ $this->_height = $size[3] - $size[1];
119
+
120
+ $this->_img = imagecreatetruecolor($this->_width, $this->_height);
121
+
122
+ if ( is_null($bg_color) || !is_array($bg_color) ) {
123
+ // Pure white bg
124
+ $bg_color = array(1,1,1,0);
125
+ }
126
+
127
+ $this->_bg_color = $this->_allocate_color($bg_color);
128
+ imagealphablending($this->_img, true);
129
+ imagesavealpha($this->_img, true);
130
+ imagefill($this->_img, 0, 0, $this->_bg_color);
131
+
132
+ }
133
+
134
+ function get_dompdf(){
135
+ return $this->_dompdf;
136
+ }
137
+
138
+ /**
139
+ * Return the GF image resource
140
+ *
141
+ * @return resource
142
+ */
143
+ function get_image() { return $this->_img; }
144
+
145
+ /**
146
+ * Return the image's width in pixels
147
+ *
148
+ * @return float
149
+ */
150
+ function get_width() { return $this->_width / $this->_aa_factor; }
151
+
152
+ /**
153
+ * Return the image's height in pixels
154
+ *
155
+ * @return float
156
+ */
157
+ function get_height() { return $this->_height / $this->_aa_factor; }
158
+
159
+ /**
160
+ * Returns the current page number
161
+ * @return int
162
+ */
163
+ function get_page_number() { return $this->_page_number; }
164
+
165
+ /**
166
+ * Returns the total number of pages in the document
167
+ * @return int
168
+ */
169
+ function get_page_count() { return $this->_page_count; }
170
+
171
+ /**
172
+ * Sets the current page number
173
+ *
174
+ * @param int $num
175
+ */
176
+ function set_page_number($num) { $this->_page_number = $num; }
177
+
178
+ /**
179
+ * Sets the page count
180
+ *
181
+ * @param int $count
182
+ */
183
+ function set_page_count($count) { $this->_page_count = $count; }
184
+
185
+ /**
186
+ * Sets the opacity
187
+ *
188
+ * @param $opacity
189
+ * @param $mode
190
+ */
191
+ function set_opacity($opacity, $mode = "Normal") {
192
+ // FIXME
193
+ }
194
+
195
+ /**
196
+ * Allocate a new color. Allocate with GD as needed and store
197
+ * previously allocated colors in $this->_colors.
198
+ *
199
+ * @param array $color The new current color
200
+ * @return int The allocated color
201
+ */
202
+ private function _allocate_color($color) {
203
+
204
+ if ( isset($color["c"]) ) {
205
+ $color = cmyk_to_rgb($color);
206
+ }
207
+
208
+ // Full opacity if no alpha set
209
+ if ( !isset($color[3]) )
210
+ $color[3] = 0;
211
+
212
+ list($r,$g,$b,$a) = $color;
213
+
214
+ $r *= 255;
215
+ $g *= 255;
216
+ $b *= 255;
217
+ $a *= 127;
218
+
219
+ // Clip values
220
+ $r = $r > 255 ? 255 : $r;
221
+ $g = $g > 255 ? 255 : $g;
222
+ $b = $b > 255 ? 255 : $b;
223
+ $a = $a > 127 ? 127 : $a;
224
+
225
+ $r = $r < 0 ? 0 : $r;
226
+ $g = $g < 0 ? 0 : $g;
227
+ $b = $b < 0 ? 0 : $b;
228
+ $a = $a < 0 ? 0 : $a;
229
+
230
+ $key = sprintf("#%02X%02X%02X%02X", $r, $g, $b, $a);
231
+
232
+ if ( isset($this->_colors[$key]) )
233
+ return $this->_colors[$key];
234
+
235
+ if ( $a != 0 )
236
+ $this->_colors[$key] = imagecolorallocatealpha($this->_img, $r, $g, $b, $a);
237
+ else
238
+ $this->_colors[$key] = imagecolorallocate($this->_img, $r, $g, $b);
239
+
240
+ return $this->_colors[$key];
241
+
242
+ }
243
+
244
+ /**
245
+ * Draws a line from x1,y1 to x2,y2
246
+ *
247
+ * See {@link Style::munge_color()} for the format of the color array.
248
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
249
+ * $style parameter (aka dash).
250
+ *
251
+ * @param float $x1
252
+ * @param float $y1
253
+ * @param float $x2
254
+ * @param float $y2
255
+ * @param array $color
256
+ * @param float $width
257
+ * @param array $style
258
+ */
259
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
260
+
261
+ // Scale by the AA factor
262
+ $x1 *= $this->_aa_factor;
263
+ $y1 *= $this->_aa_factor;
264
+ $x2 *= $this->_aa_factor;
265
+ $y2 *= $this->_aa_factor;
266
+ $width *= $this->_aa_factor;
267
+
268
+ $c = $this->_allocate_color($color);
269
+
270
+ // Convert the style array if required
271
+ if ( !is_null($style) ) {
272
+ $gd_style = array();
273
+
274
+ if ( count($style) == 1 ) {
275
+ for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
276
+ $gd_style[] = $c;
277
+ }
278
+
279
+ for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
280
+ $gd_style[] = $this->_bg_color;
281
+ }
282
+
283
+ } else {
284
+
285
+ $i = 0;
286
+ foreach ($style as $length) {
287
+
288
+ if ( $i % 2 == 0 ) {
289
+ // 'On' pattern
290
+ for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
291
+ $gd_style[] = $c;
292
+
293
+ } else {
294
+ // Off pattern
295
+ for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
296
+ $gd_style[] = $this->_bg_color;
297
+
298
+ }
299
+ $i++;
300
+ }
301
+ }
302
+
303
+ imagesetstyle($this->_img, $gd_style);
304
+ $c = IMG_COLOR_STYLED;
305
+ }
306
+
307
+ imagesetthickness($this->_img, $width);
308
+
309
+ imageline($this->_img, $x1, $y1, $x2, $y2, $c);
310
+
311
+ }
312
+
313
+ function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
314
+ // @todo
315
+ }
316
+
317
+ /**
318
+ * Draws a rectangle at x1,y1 with width w and height h
319
+ *
320
+ * See {@link Style::munge_color()} for the format of the color array.
321
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
322
+ * parameter (aka dash)
323
+ *
324
+ * @param float $x1
325
+ * @param float $y1
326
+ * @param float $w
327
+ * @param float $h
328
+ * @param array $color
329
+ * @param float $width
330
+ * @param array $style
331
+ */
332
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
333
+
334
+ // Scale by the AA factor
335
+ $x1 *= $this->_aa_factor;
336
+ $y1 *= $this->_aa_factor;
337
+ $w *= $this->_aa_factor;
338
+ $h *= $this->_aa_factor;
339
+
340
+ $c = $this->_allocate_color($color);
341
+
342
+ // Convert the style array if required
343
+ if ( !is_null($style) ) {
344
+ $gd_style = array();
345
+
346
+ foreach ($style as $length) {
347
+ for ($i = 0; $i < $length; $i++) {
348
+ $gd_style[] = $c;
349
+ }
350
+ }
351
+
352
+ imagesetstyle($this->_img, $gd_style);
353
+ $c = IMG_COLOR_STYLED;
354
+ }
355
+
356
+ imagesetthickness($this->_img, $width);
357
+
358
+ imagerectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
359
+
360
+ }
361
+
362
+ /**
363
+ * Draws a filled rectangle at x1,y1 with width w and height h
364
+ *
365
+ * See {@link Style::munge_color()} for the format of the color array.
366
+ *
367
+ * @param float $x1
368
+ * @param float $y1
369
+ * @param float $w
370
+ * @param float $h
371
+ * @param array $color
372
+ */
373
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
374
+
375
+ // Scale by the AA factor
376
+ $x1 *= $this->_aa_factor;
377
+ $y1 *= $this->_aa_factor;
378
+ $w *= $this->_aa_factor;
379
+ $h *= $this->_aa_factor;
380
+
381
+ $c = $this->_allocate_color($color);
382
+
383
+ imagefilledrectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
384
+
385
+ }
386
+
387
+ /**
388
+ * Starts a clipping rectangle at x1,y1 with width w and height h
389
+ *
390
+ * @param float $x1
391
+ * @param float $y1
392
+ * @param float $w
393
+ * @param float $h
394
+ */
395
+ function clipping_rectangle($x1, $y1, $w, $h) {
396
+ // @todo
397
+ }
398
+
399
+ function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
400
+ // @todo
401
+ }
402
+
403
+ /**
404
+ * Ends the last clipping shape
405
+ */
406
+ function clipping_end() {
407
+ // @todo
408
+ }
409
+
410
+ function save() {
411
+ // @todo
412
+ }
413
+
414
+ function restore() {
415
+ // @todo
416
+ }
417
+
418
+ function rotate($angle, $x, $y) {
419
+ // @todo
420
+ }
421
+
422
+ function skew($angle_x, $angle_y, $x, $y) {
423
+ // @todo
424
+ }
425
+
426
+ function scale($s_x, $s_y, $x, $y) {
427
+ // @todo
428
+ }
429
+
430
+ function translate($t_x, $t_y) {
431
+ // @todo
432
+ }
433
+
434
+ function transform($a, $b, $c, $d, $e, $f) {
435
+ // @todo
436
+ }
437
+
438
+ /**
439
+ * Draws a polygon
440
+ *
441
+ * The polygon is formed by joining all the points stored in the $points
442
+ * array. $points has the following structure:
443
+ * <code>
444
+ * array(0 => x1,
445
+ * 1 => y1,
446
+ * 2 => x2,
447
+ * 3 => y2,
448
+ * ...
449
+ * );
450
+ * </code>
451
+ *
452
+ * See {@link Style::munge_color()} for the format of the color array.
453
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
454
+ * parameter (aka dash)
455
+ *
456
+ * @param array $points
457
+ * @param array $color
458
+ * @param float $width
459
+ * @param array $style
460
+ * @param bool $fill Fills the polygon if true
461
+ */
462
+ function polygon($points, $color, $width = null, $style = null, $fill = false) {
463
+
464
+ // Scale each point by the AA factor
465
+ foreach (array_keys($points) as $i)
466
+ $points[$i] *= $this->_aa_factor;
467
+
468
+ $c = $this->_allocate_color($color);
469
+
470
+ // Convert the style array if required
471
+ if ( !is_null($style) && !$fill ) {
472
+ $gd_style = array();
473
+
474
+ foreach ($style as $length) {
475
+ for ($i = 0; $i < $length; $i++) {
476
+ $gd_style[] = $c;
477
+ }
478
+ }
479
+
480
+ imagesetstyle($this->_img, $gd_style);
481
+ $c = IMG_COLOR_STYLED;
482
+ }
483
+
484
+ imagesetthickness($this->_img, $width);
485
+
486
+ if ( $fill )
487
+ imagefilledpolygon($this->_img, $points, count($points) / 2, $c);
488
+ else
489
+ imagepolygon($this->_img, $points, count($points) / 2, $c);
490
+
491
+ }
492
+
493
+ /**
494
+ * Draws a circle at $x,$y with radius $r
495
+ *
496
+ * See {@link Style::munge_color()} for the format of the color array.
497
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
498
+ * parameter (aka dash)
499
+ *
500
+ * @param float $x
501
+ * @param float $y
502
+ * @param float $r
503
+ * @param array $color
504
+ * @param float $width
505
+ * @param array $style
506
+ * @param bool $fill Fills the circle if true
507
+ */
508
+ function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
509
+
510
+ // Scale by the AA factor
511
+ $x *= $this->_aa_factor;
512
+ $y *= $this->_aa_factor;
513
+ $r *= $this->_aa_factor;
514
+
515
+ $c = $this->_allocate_color($color);
516
+
517
+ // Convert the style array if required
518
+ if ( !is_null($style) && !$fill ) {
519
+ $gd_style = array();
520
+
521
+ foreach ($style as $length) {
522
+ for ($i = 0; $i < $length; $i++) {
523
+ $gd_style[] = $c;
524
+ }
525
+ }
526
+
527
+ imagesetstyle($this->_img, $gd_style);
528
+ $c = IMG_COLOR_STYLED;
529
+ }
530
+
531
+ imagesetthickness($this->_img, $width);
532
+
533
+ if ( $fill )
534
+ imagefilledellipse($this->_img, $x, $y, $r, $r, $c);
535
+ else
536
+ imageellipse($this->_img, $x, $y, $r, $r, $c);
537
+
538
+ }
539
+
540
+ /**
541
+ * Add an image to the pdf.
542
+ * The image is placed at the specified x and y coordinates with the
543
+ * given width and height.
544
+ *
545
+ * @param string $img_url the path to the image
546
+ * @param float $x x position
547
+ * @param float $y y position
548
+ * @param int $w width (in pixels)
549
+ * @param int $h height (in pixels)
550
+ * @param string $resolution
551
+ *
552
+ * @return void
553
+ * @internal param string $img_type the type (e.g. extension) of the image
554
+ */
555
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
556
+ $img_type = Image_Cache::detect_type($img_url);
557
+ $img_ext = Image_Cache::type_to_ext($img_type);
558
+
559
+ if ( !$img_ext ) {
560
+ return;
561
+ }
562
+
563
+ $func = "imagecreatefrom$img_ext";
564
+ $src = @$func($img_url);
565
+
566
+ if ( !$src ) {
567
+ return; // Probably should add to $_dompdf_errors or whatever here
568
+ }
569
+
570
+ // Scale by the AA factor
571
+ $x *= $this->_aa_factor;
572
+ $y *= $this->_aa_factor;
573
+
574
+ $w *= $this->_aa_factor;
575
+ $h *= $this->_aa_factor;
576
+
577
+ $img_w = imagesx($src);
578
+ $img_h = imagesy($src);
579
+
580
+ imagecopyresampled($this->_img, $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
581
+
582
+ }
583
+
584
+ /**
585
+ * Writes text at the specified x and y coordinates
586
+ * See {@link Style::munge_color()} for the format of the color array.
587
+ *
588
+ * @param float $x
589
+ * @param float $y
590
+ * @param string $text the text to write
591
+ * @param string $font the font file to use
592
+ * @param float $size the font size, in points
593
+ * @param array $color
594
+ * @param float $word_spacing word spacing adjustment
595
+ * @param float $char_spacing
596
+ * @param float $angle Text angle
597
+ *
598
+ * @return void
599
+ */
600
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0) {
601
+
602
+ // Scale by the AA factor
603
+ $x *= $this->_aa_factor;
604
+ $y *= $this->_aa_factor;
605
+ $size *= $this->_aa_factor;
606
+
607
+ $h = $this->get_font_height($font, $size);
608
+ $c = $this->_allocate_color($color);
609
+
610
+ $text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8');
611
+
612
+ $font = $this->get_ttf_file($font);
613
+
614
+ // FIXME: word spacing
615
+ @imagettftext($this->_img, $size, $angle, $x, $y + $h, $c, $font, $text);
616
+
617
+ }
618
+
619
+ function javascript($code) {
620
+ // Not implemented
621
+ }
622
+
623
+ /**
624
+ * Add a named destination (similar to <a name="foo">...</a> in html)
625
+ *
626
+ * @param string $anchorname The name of the named destination
627
+ */
628
+ function add_named_dest($anchorname) {
629
+ // Not implemented
630
+ }
631
+
632
+ /**
633
+ * Add a link to the pdf
634
+ *
635
+ * @param string $url The url to link to
636
+ * @param float $x The x position of the link
637
+ * @param float $y The y position of the link
638
+ * @param float $width The width of the link
639
+ * @param float $height The height of the link
640
+ */
641
+ function add_link($url, $x, $y, $width, $height) {
642
+ // Not implemented
643
+ }
644
+
645
+ /**
646
+ * Add meta information to the PDF
647
+ *
648
+ * @param string $label label of the value (Creator, Producer, etc.)
649
+ * @param string $value the text to set
650
+ */
651
+ function add_info($label, $value) {
652
+ // N/A
653
+ }
654
+
655
+ function set_default_view($view, $options = array()) {
656
+ // N/A
657
+ }
658
+
659
+ /**
660
+ * Calculates text size, in points
661
+ *
662
+ * @param string $text the text to be sized
663
+ * @param string $font the desired font
664
+ * @param float $size the desired font size
665
+ * @param float $word_spacing word spacing, if any
666
+ * @param float $char_spacing char spacing, if any
667
+ *
668
+ * @return float
669
+ */
670
+ function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
671
+ $font = $this->get_ttf_file($font);
672
+
673
+ $text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8');
674
+
675
+ // FIXME: word spacing
676
+ list($x1,,$x2) = @imagettfbbox($size, 0, $font, $text);
677
+ return $x2 - $x1;
678
+ }
679
+
680
+ function get_ttf_file($font) {
681
+ if ( strpos($font, '.ttf') === false )
682
+ $font .= ".ttf";
683
+
684
+ /*$filename = substr(strtolower(basename($font)), 0, -4);
685
+
686
+ if ( in_array($filename, DOMPDF::$native_fonts) ) {
687
+ return "arial.ttf";
688
+ }*/
689
+
690
+ return $font;
691
+ }
692
+
693
+ /**
694
+ * Calculates font height, in points
695
+ *
696
+ * @param string $font
697
+ * @param float $size
698
+ * @return float
699
+ */
700
+ function get_font_height($font, $size) {
701
+ $font = $this->get_ttf_file($font);
702
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
703
+
704
+ // FIXME: word spacing
705
+ list(,$y2,,,,$y1) = imagettfbbox($size, 0, $font, "MXjpqytfhl"); // Test string with ascenders, descenders and caps
706
+ return ($y2 - $y1) * $ratio;
707
+ }
708
+
709
+ function get_font_baseline($font, $size) {
710
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
711
+ return $this->get_font_height($font, $size) / $ratio;
712
+ }
713
+
714
+ /**
715
+ * Starts a new page
716
+ *
717
+ * Subsequent drawing operations will appear on the new page.
718
+ */
719
+ function new_page() {
720
+ $this->_page_number++;
721
+ $this->_page_count++;
722
+ }
723
+
724
+ function open_object(){
725
+ // N/A
726
+ }
727
+
728
+ function close_object(){
729
+ // N/A
730
+ }
731
+
732
+ function add_object(){
733
+ // N/A
734
+ }
735
+
736
+ function page_text(){
737
+ // N/A
738
+ }
739
+
740
+ /**
741
+ * Streams the image directly to the browser
742
+ *
743
+ * @param string $filename the name of the image file (ignored)
744
+ * @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
745
+ */
746
+ function stream($filename, $options = null) {
747
+
748
+ // Perform any antialiasing
749
+ if ( $this->_aa_factor != 1 ) {
750
+ $dst_w = $this->_width / $this->_aa_factor;
751
+ $dst_h = $this->_height / $this->_aa_factor;
752
+ $dst = imagecreatetruecolor($dst_w, $dst_h);
753
+ imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
754
+ $dst_w, $dst_h,
755
+ $this->_width, $this->_height);
756
+ } else {
757
+ $dst = $this->_img;
758
+ }
759
+
760
+ if ( !isset($options["type"]) )
761
+ $options["type"] = "png";
762
+
763
+ $type = strtolower($options["type"]);
764
+
765
+ header("Cache-Control: private");
766
+
767
+ switch ($type) {
768
+
769
+ case "jpg":
770
+ case "jpeg":
771
+ if ( !isset($options["quality"]) )
772
+ $options["quality"] = 75;
773
+
774
+ header("Content-type: image/jpeg");
775
+ imagejpeg($dst, '', $options["quality"]);
776
+ break;
777
+
778
+ case "png":
779
+ default:
780
+ header("Content-type: image/png");
781
+ imagepng($dst);
782
+ break;
783
+ }
784
+
785
+ if ( $this->_aa_factor != 1 )
786
+ imagedestroy($dst);
787
+ }
788
+
789
+ /**
790
+ * Returns the PNG as a string
791
+ *
792
+ * @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
793
+ * @return string
794
+ */
795
+ function output($options = null) {
796
+
797
+ if ( $this->_aa_factor != 1 ) {
798
+ $dst_w = $this->_width / $this->_aa_factor;
799
+ $dst_h = $this->_height / $this->_aa_factor;
800
+ $dst = imagecreatetruecolor($dst_w, $dst_h);
801
+ imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
802
+ $dst_w, $dst_h,
803
+ $this->_width, $this->_height);
804
+ } else {
805
+ $dst = $this->_img;
806
+ }
807
+
808
+ if ( !isset($options["type"]) )
809
+ $options["type"] = "png";
810
+
811
+ $type = $options["type"];
812
+
813
+ ob_start();
814
+
815
+ switch ($type) {
816
+
817
+ case "jpg":
818
+ case "jpeg":
819
+ if ( !isset($options["quality"]) )
820
+ $options["quality"] = 75;
821
+
822
+ imagejpeg($dst, '', $options["quality"]);
823
+ break;
824
+
825
+ case "png":
826
+ default:
827
+ imagepng($dst);
828
+ break;
829
+ }
830
+
831
+ $image = ob_get_clean();
832
+
833
+ if ( $this->_aa_factor != 1 )
834
+ imagedestroy($dst);
835
+
836
+ return $image;
837
+ }
838
+
839
+
840
+ }
lib/dompdf/include/image_cache.cls.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * Static class that resolves image urls and downloads and caches
13
+ * remote images if required.
14
+ *
15
+ * @access private
16
+ * @package dompdf
17
+ */
18
+ class Image_Cache {
19
+
20
+ /**
21
+ * Array of downloaded images. Cached so that identical images are
22
+ * not needlessly downloaded.
23
+ *
24
+ * @var array
25
+ */
26
+ static protected $_cache = array();
27
+
28
+ /**
29
+ * The url to the "broken image" used when images can't be loade
30
+ *
31
+ * @var string
32
+ */
33
+ public static $broken_image;
34
+
35
+ /**
36
+ * Resolve and fetch an image for use.
37
+ *
38
+ * @param string $url The url of the image
39
+ * @param string $protocol Default protocol if none specified in $url
40
+ * @param string $host Default host if none specified in $url
41
+ * @param string $base_path Default path if none specified in $url
42
+ * @param DOMPDF $dompdf The DOMPDF instance
43
+ *
44
+ * @throws DOMPDF_Image_Exception
45
+ * @return array An array with two elements: The local path to the image and the image extension
46
+ */
47
+ static function resolve_url($url, $protocol, $host, $base_path, DOMPDF $dompdf) {
48
+ $parsed_url = explode_url($url);
49
+ $message = null;
50
+
51
+ $remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != "");
52
+
53
+ $data_uri = strpos($parsed_url['protocol'], "data:") === 0;
54
+ $full_url = null;
55
+ $enable_remote = $dompdf->get_option("enable_remote");
56
+
57
+ try {
58
+
59
+ // Remote not allowed and is not DataURI
60
+ if ( !$enable_remote && $remote && !$data_uri ) {
61
+ throw new DOMPDF_Image_Exception("DOMPDF_ENABLE_REMOTE is set to FALSE");
62
+ }
63
+
64
+ // Remote allowed or DataURI
65
+ else if ( $enable_remote && $remote || $data_uri ) {
66
+ // Download remote files to a temporary directory
67
+ $full_url = build_url($protocol, $host, $base_path, $url);
68
+
69
+ // From cache
70
+ if ( isset(self::$_cache[$full_url]) ) {
71
+ $resolved_url = self::$_cache[$full_url];
72
+ }
73
+
74
+ // From remote
75
+ else {
76
+ $tmp_dir = $dompdf->get_option("temp_dir");
77
+ $resolved_url = tempnam($tmp_dir, "ca_dompdf_img_");
78
+ $image = "";
79
+
80
+ if ($data_uri) {
81
+ if ($parsed_data_uri = parse_data_uri($url)) {
82
+ $image = $parsed_data_uri['data'];
83
+ }
84
+ }
85
+ else {
86
+ set_error_handler("record_warnings");
87
+ $image = file_get_contents($full_url);
88
+ restore_error_handler();
89
+ }
90
+
91
+ // Image not found or invalid
92
+ if ( strlen($image) == 0 ) {
93
+ $msg = ($data_uri ? "Data-URI could not be parsed" : "Image not found");
94
+ throw new DOMPDF_Image_Exception($msg);
95
+ }
96
+
97
+ // Image found, put in cache and process
98
+ else {
99
+ //e.g. fetch.php?media=url.jpg&cache=1
100
+ //- Image file name might be one of the dynamic parts of the url, don't strip off!
101
+ //- a remote url does not need to have a file extension at all
102
+ //- local cached file does not have a matching file extension
103
+ //Therefore get image type from the content
104
+ file_put_contents($resolved_url, $image);
105
+ }
106
+ }
107
+ }
108
+
109
+ // Not remote, local image
110
+ else {
111
+ $resolved_url = build_url($protocol, $host, $base_path, $url);
112
+ }
113
+
114
+ // Check if the local file is readable
115
+ if ( !is_readable($resolved_url) || !filesize($resolved_url) ) {
116
+ throw new DOMPDF_Image_Exception("Image not readable or empty");
117
+ }
118
+
119
+ // Check is the file is an image
120
+ else {
121
+ list($width, $height, $type) = dompdf_getimagesize($resolved_url);
122
+
123
+ // Known image type
124
+ if ( $width && $height && in_array($type, array(IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP)) ) {
125
+ //Don't put replacement image into cache - otherwise it will be deleted on cache cleanup.
126
+ //Only execute on successful caching of remote image.
127
+ if ( $enable_remote && $remote || $data_uri ) {
128
+ self::$_cache[$full_url] = $resolved_url;
129
+ }
130
+ }
131
+
132
+ // Unknown image type
133
+ else {
134
+ throw new DOMPDF_Image_Exception("Image type unknown");
135
+ }
136
+ }
137
+ }
138
+ catch(DOMPDF_Image_Exception $e) {
139
+ $resolved_url = self::$broken_image;
140
+ $type = IMAGETYPE_PNG;
141
+ $message = $e->getMessage()." \n $url";
142
+ }
143
+
144
+ return array($resolved_url, $type, $message);
145
+ }
146
+
147
+ /**
148
+ * Unlink all cached images (i.e. temporary images either downloaded
149
+ * or converted)
150
+ */
151
+ static function clear() {
152
+ if ( empty(self::$_cache) || DEBUGKEEPTEMP ) return;
153
+
154
+ foreach ( self::$_cache as $file ) {
155
+ if (DEBUGPNG) print "[clear unlink $file]";
156
+ unlink($file);
157
+ }
158
+
159
+ self::$_cache = array();
160
+ }
161
+
162
+ static function detect_type($file) {
163
+ list(, , $type) = dompdf_getimagesize($file);
164
+ return $type;
165
+ }
166
+
167
+ static function type_to_ext($type) {
168
+ $image_types = array(
169
+ IMAGETYPE_GIF => "gif",
170
+ IMAGETYPE_PNG => "png",
171
+ IMAGETYPE_JPEG => "jpeg",
172
+ IMAGETYPE_BMP => "bmp",
173
+ );
174
+
175
+ return (isset($image_types[$type]) ? $image_types[$type] : null);
176
+ }
177
+
178
+ static function is_broken($url) {
179
+ return $url === self::$broken_image;
180
+ }
181
+ }
182
+
183
+ Image_Cache::$broken_image = DOMPDF_LIB_DIR . "/res/broken_image.png";
lib/dompdf/include/image_frame_decorator.cls.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Decorates frames for image layout and rendering
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Image_Frame_Decorator extends Frame_Decorator {
17
+
18
+ /**
19
+ * The path to the image file (note that remote images are
20
+ * downloaded locally to DOMPDF_TEMP_DIR).
21
+ *
22
+ * @var string
23
+ */
24
+ protected $_image_url;
25
+
26
+ /**
27
+ * The image's file error message
28
+ *
29
+ * @var string
30
+ */
31
+ protected $_image_msg;
32
+
33
+ /**
34
+ * Class constructor
35
+ *
36
+ * @param Frame $frame the frame to decorate
37
+ * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls)
38
+ */
39
+ function __construct(Frame $frame, DOMPDF $dompdf) {
40
+ parent::__construct($frame, $dompdf);
41
+ $url = $frame->get_node()->getAttribute("src");
42
+
43
+ $debug_png = $dompdf->get_option("debug_png");
44
+ if ($debug_png) print '[__construct '.$url.']';
45
+
46
+ list($this->_image_url, /*$type*/, $this->_image_msg) = Image_Cache::resolve_url(
47
+ $url,
48
+ $dompdf->get_protocol(),
49
+ $dompdf->get_host(),
50
+ $dompdf->get_base_path(),
51
+ $dompdf
52
+ );
53
+
54
+ if ( Image_Cache::is_broken($this->_image_url) &&
55
+ $alt = $frame->get_node()->getAttribute("alt") ) {
56
+ $style = $frame->get_style();
57
+ $style->width = (4/3)*Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing);
58
+ $style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Return the image's url
64
+ *
65
+ * @return string The url of this image
66
+ */
67
+ function get_image_url() {
68
+ return $this->_image_url;
69
+ }
70
+
71
+ /**
72
+ * Return the image's error message
73
+ *
74
+ * @return string The image's error message
75
+ */
76
+ function get_image_msg() {
77
+ return $this->_image_msg;
78
+ }
79
+
80
+ }
lib/dompdf/include/image_frame_reflower.cls.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Image reflower class
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Image_Frame_Reflower extends Frame_Reflower {
17
+
18
+ function __construct(Image_Frame_Decorator $frame) {
19
+ parent::__construct($frame);
20
+ }
21
+
22
+ function reflow(Block_Frame_Decorator $block = null) {
23
+ $this->_frame->position();
24
+
25
+ //FLOAT
26
+ //$frame = $this->_frame;
27
+ //$page = $frame->get_root();
28
+
29
+ //$enable_css_float = $this->get_dompdf()->get_option("enable_css_float");
30
+ //if ($enable_css_float && $frame->get_style()->float !== "none" ) {
31
+ // $page->add_floating_frame($this);
32
+ //}
33
+ // Set the frame's width
34
+ $this->get_min_max_width();
35
+
36
+ if ( $block ) {
37
+ $block->add_frame_to_line($this->_frame);
38
+ }
39
+ }
40
+
41
+ function get_min_max_width() {
42
+ if (DEBUGPNG) {
43
+ // Determine the image's size. Time consuming. Only when really needed?
44
+ list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
45
+ print "get_min_max_width() ".
46
+ $this->_frame->get_style()->width.' '.
47
+ $this->_frame->get_style()->height.';'.
48
+ $this->_frame->get_parent()->get_style()->width." ".
49
+ $this->_frame->get_parent()->get_style()->height.";".
50
+ $this->_frame->get_parent()->get_parent()->get_style()->width.' '.
51
+ $this->_frame->get_parent()->get_parent()->get_style()->height.';'.
52
+ $img_width. ' '.
53
+ $img_height.'|' ;
54
+ }
55
+
56
+ $style = $this->_frame->get_style();
57
+
58
+ $width_forced = true;
59
+ $height_forced = true;
60
+
61
+ //own style auto or invalid value: use natural size in px
62
+ //own style value: ignore suffix text including unit, use given number as px
63
+ //own style %: walk up parent chain until found available space in pt; fill available space
64
+ //
65
+ //special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto
66
+
67
+ $width = ($style->width > 0 ? $style->width : 0);
68
+ if ( is_percent($width) ) {
69
+ $t = 0.0;
70
+ for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
71
+ $f_style = $f->get_style();
72
+ $t = $f_style->length_in_pt($f_style->width);
73
+ if ($t != 0) {
74
+ break;
75
+ }
76
+ }
77
+ $width = ((float)rtrim($width,"%") * $t)/100; //maybe 0
78
+ } elseif ( !mb_strpos($width, 'pt') ) {
79
+ // Don't set image original size if "%" branch was 0 or size not given.
80
+ // Otherwise aspect changed on %/auto combination for width/height
81
+ // Resample according to px per inch
82
+ // See also List_Bullet_Image_Frame_Decorator::__construct
83
+ $width = $style->length_in_pt($width);
84
+ }
85
+
86
+ $height = ($style->height > 0 ? $style->height : 0);
87
+ if ( is_percent($height) ) {
88
+ $t = 0.0;
89
+ for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
90
+ $f_style = $f->get_style();
91
+ $t = $f_style->length_in_pt($f_style->height);
92
+ if ($t != 0) {
93
+ break;
94
+ }
95
+ }
96
+ $height = ((float)rtrim($height,"%") * $t)/100; //maybe 0
97
+ } elseif ( !mb_strpos($height, 'pt') ) {
98
+ // Don't set image original size if "%" branch was 0 or size not given.
99
+ // Otherwise aspect changed on %/auto combination for width/height
100
+ // Resample according to px per inch
101
+ // See also List_Bullet_Image_Frame_Decorator::__construct
102
+ $height = $style->length_in_pt($height);
103
+ }
104
+
105
+ if ($width == 0 || $height == 0) {
106
+ // Determine the image's size. Time consuming. Only when really needed!
107
+ list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
108
+
109
+ // don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable.
110
+ // Resample according to px per inch
111
+ // See also List_Bullet_Image_Frame_Decorator::__construct
112
+ if ($width == 0 && $height == 0) {
113
+ $dpi = $this->_frame->get_dompdf()->get_option("dpi");
114
+ $width = (float)($img_width * 72) / $dpi;
115
+ $height = (float)($img_height * 72) / $dpi;
116
+ $width_forced = false;
117
+ $height_forced = false;
118
+ } elseif ($height == 0 && $width != 0) {
119
+ $height_forced = false;
120
+ $height = ($width / $img_width) * $img_height; //keep aspect ratio
121
+ } elseif ($width == 0 && $height != 0) {
122
+ $width_forced = false;
123
+ $width = ($height / $img_height) * $img_width; //keep aspect ratio
124
+ }
125
+ }
126
+
127
+ // Handle min/max width/height
128
+ if ( $style->min_width !== "none" ||
129
+ $style->max_width !== "none" ||
130
+ $style->min_height !== "none" ||
131
+ $style->max_height !== "none" ) {
132
+
133
+ list(/*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block();
134
+
135
+ $min_width = $style->length_in_pt($style->min_width, $w);
136
+ $max_width = $style->length_in_pt($style->max_width, $w);
137
+ $min_height = $style->length_in_pt($style->min_height, $h);
138
+ $max_height = $style->length_in_pt($style->max_height, $h);
139
+
140
+ if ( $max_width !== "none" && $width > $max_width ) {
141
+ if ( !$height_forced ) {
142
+ $height *= $max_width / $width;
143
+ }
144
+
145
+ $width = $max_width;
146
+ }
147
+
148
+ if ( $min_width !== "none" && $width < $min_width ) {
149
+ if ( !$height_forced ) {
150
+ $height *= $min_width / $width;
151
+ }
152
+
153
+ $width = $min_width;
154
+ }
155
+
156
+ if ( $max_height !== "none" && $height > $max_height ) {
157
+ if ( !$width_forced ) {
158
+ $width *= $max_height / $height;
159
+ }
160
+
161
+ $height = $max_height;
162
+ }
163
+
164
+ if ( $min_height !== "none" && $height < $min_height ) {
165
+ if ( !$width_forced ) {
166
+ $width *= $min_height / $height;
167
+ }
168
+
169
+ $height = $min_height;
170
+ }
171
+ }
172
+
173
+ if (DEBUGPNG) print $width.' '.$height.';';
174
+
175
+ $style->width = $width . "pt";
176
+ $style->height = $height . "pt";
177
+
178
+ $style->min_width = "none";
179
+ $style->max_width = "none";
180
+ $style->min_height = "none";
181
+ $style->max_height = "none";
182
+
183
+ return array( $width, $width, "min" => $width, "max" => $width);
184
+
185
+ }
186
+ }
lib/dompdf/include/image_renderer.cls.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Image renderer
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Image_Renderer extends Block_Renderer {
17
+
18
+ function render(Frame $frame) {
19
+ // Render background & borders
20
+ $style = $frame->get_style();
21
+ $cb = $frame->get_containing_block();
22
+ list($x, $y, $w, $h) = $frame->get_border_box();
23
+
24
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
25
+
26
+ list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
27
+
28
+ $has_border_radius = $tl + $tr + $br + $bl > 0;
29
+
30
+ if ( $has_border_radius ) {
31
+ $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
32
+ }
33
+
34
+ if ( ($bg = $style->background_color) !== "transparent" ) {
35
+ $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
36
+ }
37
+
38
+ if ( ($url = $style->background_image) && $url !== "none" ) {
39
+ $this->_background_image($url, $x, $y, $w, $h, $style);
40
+ }
41
+
42
+ if ( $has_border_radius ) {
43
+ $this->_canvas->clipping_end();
44
+ }
45
+
46
+ $this->_render_border($frame);
47
+ $this->_render_outline($frame);
48
+
49
+ list($x, $y) = $frame->get_padding_box();
50
+
51
+ $x += $style->length_in_pt($style->padding_left, $cb["w"]);
52
+ $y += $style->length_in_pt($style->padding_top, $cb["h"]);
53
+
54
+ $w = $style->length_in_pt($style->width, $cb["w"]);
55
+ $h = $style->length_in_pt($style->height, $cb["h"]);
56
+
57
+ if ( $has_border_radius ) {
58
+ list($wt, $wr, $wb, $wl) = array(
59
+ $style->border_top_width,
60
+ $style->border_right_width,
61
+ $style->border_bottom_width,
62
+ $style->border_left_width,
63
+ );
64
+
65
+ // we have to get the "inner" radius
66
+ if ( $tl > 0 ) {
67
+ $tl -= ($wt + $wl) / 2;
68
+ }
69
+ if ( $tr > 0 ) {
70
+ $tr -= ($wt + $wr) / 2;
71
+ }
72
+ if ( $br > 0 ) {
73
+ $br -= ($wb + $wr) / 2;
74
+ }
75
+ if ( $bl > 0 ) {
76
+ $bl -= ($wb + $wl) / 2;
77
+ }
78
+
79
+ $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
80
+ }
81
+
82
+ $src = $frame->get_image_url();
83
+ $alt = null;
84
+
85
+ if ( Image_Cache::is_broken($src) &&
86
+ $alt = $frame->get_node()->getAttribute("alt") ) {
87
+ $font = $style->font_family;
88
+ $size = $style->font_size;
89
+ $spacing = $style->word_spacing;
90
+ $this->_canvas->text($x, $y, $alt,
91
+ $font, $size,
92
+ $style->color, $spacing);
93
+ }
94
+ else {
95
+ $this->_canvas->image( $src, $x, $y, $w, $h, $style->image_resolution);
96
+ }
97
+
98
+ if ( $has_border_radius ) {
99
+ $this->_canvas->clipping_end();
100
+ }
101
+
102
+ if ( $msg = $frame->get_image_msg() ) {
103
+ $parts = preg_split("/\s*\n\s*/", $msg);
104
+ $height = 10;
105
+ $_y = $alt ? $y+$h-count($parts)*$height : $y;
106
+
107
+ foreach($parts as $i => $_part) {
108
+ $this->_canvas->text($x, $_y + $i*$height, $_part, "times", $height*0.8, array(0.5, 0.5, 0.5));
109
+ }
110
+ }
111
+
112
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
113
+ $this->_debug_layout($frame->get_border_box(), "blue");
114
+ if (DEBUG_LAYOUT_PADDINGBOX) {
115
+ $this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5));
116
+ }
117
+ }
118
+ }
119
+ }
lib/dompdf/include/inline_frame_decorator.cls.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Decorates frames for inline layout
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Inline_Frame_Decorator extends Frame_Decorator {
17
+
18
+ function __construct(Frame $frame, DOMPDF $dompdf) { parent::__construct($frame, $dompdf); }
19
+
20
+ function split(Frame $frame = null, $force_pagebreak = false) {
21
+
22
+ if ( is_null($frame) ) {
23
+ $this->get_parent()->split($this, $force_pagebreak);
24
+ return;
25
+ }
26
+
27
+ if ( $frame->get_parent() !== $this )
28
+ throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
29
+
30
+ $split = $this->copy( $this->_frame->get_node()->cloneNode() );
31
+ $this->get_parent()->insert_child_after($split, $this);
32
+
33
+ // Unset the current node's right style properties
34
+ $style = $this->_frame->get_style();
35
+ $style->margin_right = 0;
36
+ $style->padding_right = 0;
37
+ $style->border_right_width = 0;
38
+
39
+ // Unset the split node's left style properties since we don't want them
40
+ // to propagate
41
+ $style = $split->get_style();
42
+ $style->margin_left = 0;
43
+ $style->padding_left = 0;
44
+ $style->border_left_width = 0;
45
+
46
+ //On continuation of inline element on next line,
47
+ //don't repeat non-vertically repeatble background images
48
+ //See e.g. in testcase image_variants, long desriptions
49
+ if ( ($url = $style->background_image) && $url !== "none"
50
+ && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y"
51
+ ) {
52
+ $style->background_image = "none";
53
+ }
54
+
55
+ // Add $frame and all following siblings to the new split node
56
+ $iter = $frame;
57
+ while ($iter) {
58
+ $frame = $iter;
59
+ $iter = $iter->get_next_sibling();
60
+ $frame->reset();
61
+ $split->append_child($frame);
62
+ }
63
+
64
+ $page_breaks = array("always", "left", "right");
65
+ $frame_style = $frame->get_style();
66
+ if( $force_pagebreak ||
67
+ in_array($frame_style->page_break_before, $page_breaks) ||
68
+ in_array($frame_style->page_break_after, $page_breaks) ) {
69
+
70
+ $this->get_parent()->split($split, true);
71
+ }
72
+ }
73
+
74
+ }
lib/dompdf/include/inline_frame_reflower.cls.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows inline frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Inline_Frame_Reflower extends Frame_Reflower {
16
+
17
+ function __construct(Frame $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function reflow(Block_Frame_Decorator $block = null) {
22
+ $frame = $this->_frame;
23
+
24
+ // Check if a page break is forced
25
+ $page = $frame->get_root();
26
+ $page->check_forced_page_break($frame);
27
+
28
+ if ( $page->is_full() )
29
+ return;
30
+
31
+ $style = $frame->get_style();
32
+
33
+ // Generated content
34
+ $this->_set_content();
35
+
36
+ $frame->position();
37
+
38
+ $cb = $frame->get_containing_block();
39
+
40
+ // Add our margin, padding & border to the first and last children
41
+ if ( ($f = $frame->get_first_child()) && $f instanceof Text_Frame_Decorator ) {
42
+ $f_style = $f->get_style();
43
+ $f_style->margin_left = $style->margin_left;
44
+ $f_style->padding_left = $style->padding_left;
45
+ $f_style->border_left = $style->border_left;
46
+ }
47
+
48
+ if ( ($l = $frame->get_last_child()) && $l instanceof Text_Frame_Decorator ) {
49
+ $l_style = $l->get_style();
50
+ $l_style->margin_right = $style->margin_right;
51
+ $l_style->padding_right = $style->padding_right;
52
+ $l_style->border_right = $style->border_right;
53
+ }
54
+
55
+ if ( $block ) {
56
+ $block->add_frame_to_line($this->_frame);
57
+ }
58
+
59
+ // Set the containing blocks and reflow each child. The containing
60
+ // block is not changed by line boxes.
61
+ foreach ( $frame->get_children() as $child ) {
62
+ $child->set_containing_block($cb);
63
+ $child->reflow($block);
64
+ }
65
+ }
66
+ }
lib/dompdf/include/inline_positioner.cls.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions inline frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Inline_Positioner extends Positioner {
16
+
17
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function position() {
22
+ /**
23
+ * Find our nearest block level parent and access its lines property.
24
+ * @var Block_Frame_Decorator
25
+ */
26
+ $p = $this->_frame->find_block_parent();
27
+
28
+ // Debugging code:
29
+
30
+ // pre_r("\nPositioning:");
31
+ // pre_r("Me: " . $this->_frame->get_node()->nodeName . " (" . spl_object_hash($this->_frame->get_node()) . ")");
32
+ // pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")");
33
+
34
+ // End debugging
35
+
36
+ if ( !$p )
37
+ throw new DOMPDF_Exception("No block-level parent found. Not good.");
38
+
39
+ $f = $this->_frame;
40
+
41
+ $cb = $f->get_containing_block();
42
+ $line = $p->get_current_line_box();
43
+
44
+ // Skip the page break if in a fixed position element
45
+ $is_fixed = false;
46
+ while($f = $f->get_parent()) {
47
+ if($f->get_style()->position === "fixed") {
48
+ $is_fixed = true;
49
+ break;
50
+ }
51
+ }
52
+
53
+ $f = $this->_frame;
54
+
55
+ if ( !$is_fixed && $f->get_parent() &&
56
+ $f->get_parent() instanceof Inline_Frame_Decorator &&
57
+ $f->is_text_node() ) {
58
+
59
+ $min_max = $f->get_reflower()->get_min_max_width();
60
+
61
+ // If the frame doesn't fit in the current line, a line break occurs
62
+ if ( $min_max["min"] > ($cb["w"] - $line->left - $line->w - $line->right) ) {
63
+ $p->add_line();
64
+ }
65
+ }
66
+
67
+ $f->set_position($cb["x"] + $line->w, $line->y);
68
+
69
+ }
70
+ }
lib/dompdf/include/inline_renderer.cls.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Renders inline frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Inline_Renderer extends Abstract_Renderer {
16
+
17
+ //........................................................................
18
+
19
+ function render(Frame $frame) {
20
+ $style = $frame->get_style();
21
+
22
+ if ( !$frame->get_first_child() )
23
+ return; // No children, no service
24
+
25
+ // Draw the left border if applicable
26
+ $bp = $style->get_border_properties();
27
+ $widths = array($style->length_in_pt($bp["top"]["width"]),
28
+ $style->length_in_pt($bp["right"]["width"]),
29
+ $style->length_in_pt($bp["bottom"]["width"]),
30
+ $style->length_in_pt($bp["left"]["width"]));
31
+
32
+ // Draw the background & border behind each child. To do this we need
33
+ // to figure out just how much space each child takes:
34
+ list($x, $y) = $frame->get_first_child()->get_position();
35
+ $w = null;
36
+ $h = 0;
37
+ // $x += $widths[3];
38
+ // $y += $widths[0];
39
+
40
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
41
+
42
+ $first_row = true;
43
+
44
+ foreach ($frame->get_children() as $child) {
45
+ list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box();
46
+
47
+ if ( !is_null($w) && $child_x < $x + $w ) {
48
+ //This branch seems to be supposed to being called on the first part
49
+ //of an inline html element, and the part after the if clause for the
50
+ //parts after a line break.
51
+ //But because $w initially mostly is 0, and gets updated only on the next
52
+ //round, this seem to be never executed and the common close always.
53
+
54
+ // The next child is on another line. Draw the background &
55
+ // borders on this line.
56
+
57
+ // Background:
58
+ if ( ($bg = $style->background_color) !== "transparent" )
59
+ $this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg);
60
+
61
+ if ( ($url = $style->background_image) && $url !== "none" ) {
62
+ $this->_background_image($url, $x, $y, $w, $h, $style);
63
+ }
64
+
65
+ // If this is the first row, draw the left border
66
+ if ( $first_row ) {
67
+
68
+ if ( $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0 ) {
69
+ $method = "_border_" . $bp["left"]["style"];
70
+ $this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left");
71
+ }
72
+ $first_row = false;
73
+ }
74
+
75
+ // Draw the top & bottom borders
76
+ if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0 ) {
77
+ $method = "_border_" . $bp["top"]["style"];
78
+ $this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top");
79
+ }
80
+
81
+ if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0 ) {
82
+ $method = "_border_" . $bp["bottom"]["style"];
83
+ $this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom");
84
+ }
85
+
86
+ // Handle anchors & links
87
+ $link_node = null;
88
+ if ( $frame->get_node()->nodeName === "a" ) {
89
+ $link_node = $frame->get_node();
90
+ }
91
+ else if ( $frame->get_parent()->get_node()->nodeName === "a" ){
92
+ $link_node = $frame->get_parent()->get_node();
93
+ }
94
+
95
+ if ( $link_node && $href = $link_node->getAttribute("href") ) {
96
+ $this->_canvas->add_link($href, $x, $y, $w, $h);
97
+ }
98
+
99
+ $x = $child_x;
100
+ $y = $child_y;
101
+ $w = $child_w;
102
+ $h = $child_h;
103
+ continue;
104
+ }
105
+
106
+ if ( is_null($w) )
107
+ $w = $child_w;
108
+ else
109
+ $w += $child_w;
110
+
111
+ $h = max($h, $child_h);
112
+
113
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_INLINE) {
114
+ $this->_debug_layout($child->get_border_box(), "blue");
115
+ if (DEBUG_LAYOUT_PADDINGBOX) {
116
+ $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5));
117
+ }
118
+ }
119
+ }
120
+
121
+
122
+ // Handle the last child
123
+ if ( ($bg = $style->background_color) !== "transparent" )
124
+ $this->_canvas->filled_rectangle( $x + $widths[3], $y + $widths[0], $w, $h, $bg);
125
+
126
+ //On continuation lines (after line break) of inline elements, the style got copied.
127
+ //But a non repeatable background image should not be repeated on the next line.
128
+ //But removing the background image above has never an effect, and removing it below
129
+ //removes it always, even on the initial line.
130
+ //Need to handle it elsewhere, e.g. on certain ...clone()... usages.
131
+ // Repeat not given: default is Style::__construct
132
+ // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ...
133
+ //different position? $this->_background_image($url, $x, $y, $w, $h, $style);
134
+ if ( ($url = $style->background_image) && $url !== "none" )
135
+ $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
136
+
137
+ // Add the border widths
138
+ $w += $widths[1] + $widths[3];
139
+ $h += $widths[0] + $widths[2];
140
+
141
+ // make sure the border and background start inside the left margin
142
+ $left_margin = $style->length_in_pt($style->margin_left);
143
+ $x += $left_margin;
144
+
145
+ // If this is the first row, draw the left border too
146
+ if ( $first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0 ) {
147
+ $method = "_border_" . $bp["left"]["style"];
148
+ $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left");
149
+ }
150
+
151
+ // Draw the top & bottom borders
152
+ if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0 ) {
153
+ $method = "_border_" . $bp["top"]["style"];
154
+ $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top");
155
+ }
156
+
157
+ if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0 ) {
158
+ $method = "_border_" . $bp["bottom"]["style"];
159
+ $this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom");
160
+ }
161
+
162
+ // pre_var_dump(get_class($frame->get_next_sibling()));
163
+ // $last_row = get_class($frame->get_next_sibling()) !== 'Inline_Frame_Decorator';
164
+ // Draw the right border if this is the last row
165
+ if ( $bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0 ) {
166
+ $method = "_border_" . $bp["right"]["style"];
167
+ $this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right");
168
+ }
169
+
170
+ // Only two levels of links frames
171
+ $link_node = null;
172
+ if ( $frame->get_node()->nodeName === "a" ) {
173
+ $link_node = $frame->get_node();
174
+
175
+ if ( ($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id")) ) {
176
+ $this->_canvas->add_named_dest($name);
177
+ }
178
+ }
179
+
180
+ if ( $frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a" ){
181
+ $link_node = $frame->get_parent()->get_node();
182
+ }
183
+
184
+ // Handle anchors & links
185
+ if ( $link_node ) {
186
+ if ( $href = $link_node->getAttribute("href") )
187
+ $this->_canvas->add_link($href, $x, $y, $w, $h);
188
+ }
189
+ }
190
+ }
lib/dompdf/include/javascript_embedder.cls.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Fabien Ménager <fabien.menager@gmail.com>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Embeds Javascript into the PDF document
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Javascript_Embedder {
16
+
17
+ /**
18
+ * @var DOMPDF
19
+ */
20
+ protected $_dompdf;
21
+
22
+ function __construct(DOMPDF $dompdf) {
23
+ $this->_dompdf = $dompdf;
24
+ }
25
+
26
+ function insert($script) {
27
+ $this->_dompdf->get_canvas()->javascript($script);
28
+ }
29
+
30
+ function render(Frame $frame) {
31
+ if ( !$this->_dompdf->get_option("enable_javascript") ) {
32
+ return;
33
+ }
34
+
35
+ $this->insert($frame->get_node()->nodeValue);
36
+ }
37
+ }
lib/dompdf/include/line_box.cls.php ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Fabien Ménager <fabien.menager@gmail.com>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * The line box class
11
+ *
12
+ * This class represents a line box
13
+ * http://www.w3.org/TR/CSS2/visuren.html#line-box
14
+ *
15
+ * @access protected
16
+ * @package dompdf
17
+ */
18
+ class Line_Box {
19
+
20
+ /**
21
+ * @var Block_Frame_Decorator
22
+ */
23
+ protected $_block_frame;
24
+
25
+ /**
26
+ * @var Frame[]
27
+ */
28
+ protected $_frames = array();
29
+
30
+ /**
31
+ * @var integer
32
+ */
33
+ public $wc = 0;
34
+
35
+ /**
36
+ * @var float
37
+ */
38
+ public $y = null;
39
+
40
+ /**
41
+ * @var float
42
+ */
43
+ public $w = 0.0;
44
+
45
+ /**
46
+ * @var float
47
+ */
48
+ public $h = 0.0;
49
+
50
+ /**
51
+ * @var float
52
+ */
53
+ public $left = 0.0;
54
+
55
+ /**
56
+ * @var float
57
+ */
58
+ public $right = 0.0;
59
+
60
+ /**
61
+ * @var Frame
62
+ */
63
+ public $tallest_frame = null;
64
+
65
+ /**
66
+ * @var bool[]
67
+ */
68
+ public $floating_blocks = array();
69
+
70
+ /**
71
+ * @var bool
72
+ */
73
+ public $br = false;
74
+
75
+ /**
76
+ * Class constructor
77
+ *
78
+ * @param Block_Frame_Decorator $frame the Block_Frame_Decorator containing this line
79
+ */
80
+ function __construct(Block_Frame_Decorator $frame, $y = 0) {
81
+ $this->_block_frame = $frame;
82
+ $this->_frames = array();
83
+ $this->y = $y;
84
+
85
+ $this->get_float_offsets();
86
+ }
87
+
88
+ /**
89
+ * Returns the floating elements inside the first floating parent
90
+ *
91
+ * @param Page_Frame_Decorator $root
92
+ *
93
+ * @return Frame[]
94
+ */
95
+ function get_floats_inside(Page_Frame_Decorator $root) {
96
+ $floating_frames = $root->get_floating_frames();
97
+
98
+ if ( count($floating_frames) == 0 ) {
99
+ return $floating_frames;
100
+ }
101
+
102
+ // Find nearest floating element
103
+ $p = $this->_block_frame;
104
+ while( $p->get_style()->float === "none" ) {
105
+ $parent = $p->get_parent();
106
+
107
+ if ( !$parent ) {
108
+ break;
109
+ }
110
+
111
+ $p = $parent;
112
+ }
113
+
114
+ if ( $p == $root ) {
115
+ return $floating_frames;
116
+ }
117
+
118
+ $parent = $p;
119
+
120
+ $childs = array();
121
+
122
+ foreach ($floating_frames as $_floating) {
123
+ $p = $_floating->get_parent();
124
+
125
+ while (($p = $p->get_parent()) && $p !== $parent);
126
+
127
+ if ( $p ) {
128
+ $childs[] = $p;
129
+ }
130
+ }
131
+
132
+ return $childs;
133
+ }
134
+
135
+ function get_float_offsets() {
136
+ $enable_css_float = $this->_block_frame->get_dompdf()->get_option("enable_css_float");
137
+ if ( !$enable_css_float ) {
138
+ return;
139
+ }
140
+
141
+ static $anti_infinite_loop = 500; // FIXME smelly hack
142
+
143
+ $reflower = $this->_block_frame->get_reflower();
144
+
145
+ if ( !$reflower ) {
146
+ return;
147
+ }
148
+
149
+ $cb_w = null;
150
+
151
+ $block = $this->_block_frame;
152
+ $root = $block->get_root();
153
+
154
+ if ( !$root ) {
155
+ return;
156
+ }
157
+
158
+ $floating_frames = $this->get_floats_inside($root);
159
+
160
+ foreach ( $floating_frames as $child_key => $floating_frame ) {
161
+ $id = $floating_frame->get_id();
162
+
163
+ if ( isset($this->floating_blocks[$id]) ) {
164
+ continue;
165
+ }
166
+
167
+ $floating_style = $floating_frame->get_style();
168
+ $float = $floating_style->float;
169
+
170
+ $floating_width = $floating_frame->get_margin_width();
171
+
172
+ if (!$cb_w) {
173
+ $cb_w = $floating_frame->get_containing_block("w");
174
+ }
175
+
176
+ $line_w = $this->get_width();
177
+
178
+ if ( !$floating_frame->_float_next_line && ($cb_w <= $line_w + $floating_width) && ($cb_w > $line_w) ) {
179
+ $floating_frame->_float_next_line = true;
180
+ continue;
181
+ }
182
+
183
+ // If the child is still shifted by the floating element
184
+ if ( $anti_infinite_loop-- > 0 &&
185
+ $floating_frame->get_position("y") + $floating_frame->get_margin_height() > $this->y &&
186
+ $block->get_position("x") + $block->get_margin_width() > $floating_frame->get_position("x")
187
+ ) {
188
+ if ( $float === "left" )
189
+ $this->left += $floating_width;
190
+ else
191
+ $this->right += $floating_width;
192
+
193
+ $this->floating_blocks[$id] = true;
194
+ }
195
+
196
+ // else, the floating element won't shift anymore
197
+ else {
198
+ $root->remove_floating_frame($child_key);
199
+ }
200
+ }
201
+ }
202
+
203
+ /**
204
+ * @return float
205
+ */
206
+ function get_width(){
207
+ return $this->left + $this->w + $this->right;
208
+ }
209
+
210
+ /**
211
+ * @return Block_Frame_Decorator
212
+ */
213
+ function get_block_frame() {
214
+ return $this->_block_frame;
215
+ }
216
+
217
+ /**
218
+ * @return Frame[]
219
+ */
220
+ function &get_frames() {
221
+ return $this->_frames;
222
+ }
223
+
224
+ /**
225
+ * @param Frame $frame
226
+ */
227
+ function add_frame(Frame $frame) {
228
+ $this->_frames[] = $frame;
229
+ }
230
+
231
+ function __toString(){
232
+ $props = array("wc", "y", "w", "h", "left", "right", "br");
233
+ $s = "";
234
+ foreach($props as $prop) {
235
+ $s .= "$prop: ".$this->$prop."\n";
236
+ }
237
+ $s .= count($this->_frames)." frames\n";
238
+ return $s;
239
+ }
240
+ /*function __get($prop) {
241
+ if (!isset($this->{"_$prop"})) return;
242
+ return $this->{"_$prop"};
243
+ }*/
244
+ }
245
+
246
+ /*
247
+ class LineBoxList implements Iterator {
248
+ private $_p = 0;
249
+ private $_lines = array();
250
+
251
+ }
252
+ */
lib/dompdf/include/list_bullet_frame_decorator.cls.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Decorates frames for list bullet rendering
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class List_Bullet_Frame_Decorator extends Frame_Decorator {
17
+
18
+ const BULLET_PADDING = 1; // Distance from bullet to text in pt
19
+ // As fraction of font size (including descent). See also DECO_THICKNESS.
20
+ const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04
21
+ const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now.
22
+ const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent.
23
+
24
+ static $BULLET_TYPES = array("disc", "circle", "square");
25
+
26
+ //........................................................................
27
+
28
+ function __construct(Frame $frame, DOMPDF $dompdf) {
29
+ parent::__construct($frame, $dompdf);
30
+ }
31
+
32
+ function get_margin_width() {
33
+ $style = $this->_frame->get_style();
34
+
35
+ // Small hack to prevent extra indenting of list text on list_style_position === "inside"
36
+ // and on suppressed bullet
37
+ if ( $style->list_style_position === "outside" ||
38
+ $style->list_style_type === "none" ) {
39
+ return 0;
40
+ }
41
+
42
+ return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING;
43
+ }
44
+
45
+ //hits only on "inset" lists items, to increase height of box
46
+ function get_margin_height() {
47
+ $style = $this->_frame->get_style();
48
+
49
+ if ( $style->list_style_type === "none" ) {
50
+ return 0;
51
+ }
52
+
53
+ return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING;
54
+ }
55
+
56
+ function get_width() {
57
+ return $this->get_margin_height();
58
+ }
59
+
60
+ function get_height() {
61
+ return $this->get_margin_height();
62
+ }
63
+
64
+ //........................................................................
65
+ }
lib/dompdf/include/list_bullet_frame_reflower.cls.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows list bullets
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class List_Bullet_Frame_Reflower extends Frame_Reflower {
16
+
17
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function reflow(Block_Frame_Decorator $block = null) {
22
+ $style = $this->_frame->get_style();
23
+
24
+ $style->width = $this->_frame->get_width();
25
+ $this->_frame->position();
26
+
27
+ if ( $style->list_style_position === "inside" ) {
28
+ $p = $this->_frame->find_block_parent();
29
+ $p->add_frame_to_line($this->_frame);
30
+ }
31
+
32
+ }
33
+ }
lib/dompdf/include/list_bullet_image_frame_decorator.cls.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Decorates frames for list bullets with custom images
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class List_Bullet_Image_Frame_Decorator extends Frame_Decorator {
17
+
18
+ /**
19
+ * The underlying image frame
20
+ *
21
+ * @var Image_Frame_Decorator
22
+ */
23
+ protected $_img;
24
+
25
+ /**
26
+ * The image's width in pixels
27
+ *
28
+ * @var int
29
+ */
30
+ protected $_width;
31
+
32
+ /**
33
+ * The image's height in pixels
34
+ *
35
+ * @var int
36
+ */
37
+ protected $_height;
38
+
39
+ /**
40
+ * Class constructor
41
+ *
42
+ * @param Frame $frame the bullet frame to decorate
43
+ * @param DOMPDF $dompdf the document's dompdf object
44
+ */
45
+ function __construct(Frame $frame, DOMPDF $dompdf) {
46
+ $style = $frame->get_style();
47
+ $url = $style->list_style_image;
48
+ $frame->get_node()->setAttribute("src", $url);
49
+ $this->_img = new Image_Frame_Decorator($frame, $dompdf);
50
+ parent::__construct($this->_img, $dompdf);
51
+ list($width, $height) = dompdf_getimagesize($this->_img->get_image_url());
52
+
53
+ // Resample the bullet image to be consistent with 'auto' sized images
54
+ // See also Image_Frame_Reflower::get_min_max_width
55
+ // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
56
+ $dpi = $this->_dompdf->get_option("dpi");
57
+ $this->_width = ((float)rtrim($width, "px") * 72) / $dpi;
58
+ $this->_height = ((float)rtrim($height, "px") * 72) / $dpi;
59
+
60
+ //If an image is taller as the containing block/box, the box should be extended.
61
+ //Neighbour elements are overwriting the overlapping image areas.
62
+ //Todo: Where can the box size be extended?
63
+ //Code below has no effect.
64
+ //See block_frame_reflower _calculate_restricted_height
65
+ //See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S.
66
+ //Leave for now
67
+ //if ($style->min_height < $this->_height ) {
68
+ // $style->min_height = $this->_height;
69
+ //}
70
+ //$style->height = "auto";
71
+ }
72
+
73
+ /**
74
+ * Return the bullet's width
75
+ *
76
+ * @return int
77
+ */
78
+ function get_width() {
79
+ //ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator
80
+ //for proper alignment of bullet image and text. Allow image to not fitting on left border.
81
+ //This controls the distance between bullet image and text
82
+ //return $this->_width;
83
+ return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE +
84
+ 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
85
+ }
86
+
87
+ /**
88
+ * Return the bullet's height
89
+ *
90
+ * @return int
91
+ */
92
+ function get_height() {
93
+ //based on image height
94
+ return $this->_height;
95
+ }
96
+
97
+ /**
98
+ * Override get_margin_width
99
+ *
100
+ * @return int
101
+ */
102
+ function get_margin_width() {
103
+ //ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator
104
+ //for proper alignment of bullet image and text. Allow image to not fitting on left border.
105
+ //This controls the extra indentation of text to make room for the bullet image.
106
+ //Here use actual image size, not predefined bullet size
107
+ //return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE +
108
+ // 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
109
+
110
+ // Small hack to prevent indenting of list text
111
+ // Image Might not exist, then position like on list_bullet_frame_decorator fallback to none.
112
+ if ( $this->_frame->get_style()->list_style_position === "outside" ||
113
+ $this->_width == 0)
114
+ return 0;
115
+ //This aligns the "inside" image position with the text.
116
+ //The text starts to the right of the image.
117
+ //Between the image and the text there is an added margin of image width.
118
+ //Where this comes from is unknown.
119
+ //The corresponding List_Bullet_Frame_Decorator sets a smaller margin. bullet size?
120
+ return $this->_width + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
121
+ }
122
+
123
+ /**
124
+ * Override get_margin_height()
125
+ *
126
+ * @return int
127
+ */
128
+ function get_margin_height() {
129
+ //Hits only on "inset" lists items, to increase height of box
130
+ //based on image height
131
+ return $this->_height + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
132
+ }
133
+
134
+ /**
135
+ * Return image url
136
+ *
137
+ * @return string
138
+ */
139
+ function get_image_url() {
140
+ return $this->_img->get_image_url();
141
+ }
142
+
143
+ }
lib/dompdf/include/list_bullet_positioner.cls.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Positions list bullets
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class List_Bullet_Positioner extends Positioner {
17
+
18
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
19
+
20
+ //........................................................................
21
+
22
+ function position() {
23
+
24
+ // Bullets & friends are positioned an absolute distance to the left of
25
+ // the content edge of their parent element
26
+ $cb = $this->_frame->get_containing_block();
27
+
28
+ // Note: this differs from most frames in that we must position
29
+ // ourselves after determining our width
30
+ $x = $cb["x"] - $this->_frame->get_width();
31
+
32
+ $p = $this->_frame->find_block_parent();
33
+
34
+ $y = $p->get_current_line_box()->y;
35
+
36
+ // This is a bit of a hack...
37
+ $n = $this->_frame->get_next_sibling();
38
+ if ( $n ) {
39
+ $style = $n->get_style();
40
+ $line_height = $style->length_in_pt($style->line_height, $style->get_font_size());
41
+ $offset = $style->length_in_pt($line_height, $n->get_containing_block("h")) - $this->_frame->get_height();
42
+ $y += $offset / 2;
43
+ }
44
+
45
+ // Now the position is the left top of the block which should be marked with the bullet.
46
+ // We tried to find out the y of the start of the first text character within the block.
47
+ // But the top margin/padding does not fit, neither from this nor from the next sibling
48
+ // The "bit of a hack" above does not work also.
49
+
50
+ // Instead let's position the bullet vertically centered to the block which should be marked.
51
+ // But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent()
52
+ // the get_containing_block is paper width and the entire list as height.
53
+
54
+ // if ($p) {
55
+ // //$cb = $n->get_containing_block();
56
+ // $cb = $p->get_containing_block();
57
+ // $y += $cb["h"]/2;
58
+ // print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':';
59
+ // }
60
+
61
+ // Todo:
62
+ // For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing
63
+
64
+ /*$style = $p->get_style();
65
+ $font_size = $style->get_font_size();
66
+ $line_height = $style->length_in_pt($style->line_height, $font_size);
67
+ $y += ($line_height - $font_size) / 2; */
68
+
69
+ //Position is x-end y-top of character position of the bullet.
70
+ $this->_frame->set_position($x, $y);
71
+
72
+ }
73
+ }
lib/dompdf/include/list_bullet_renderer.cls.php ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Renders list bullets
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class List_Bullet_Renderer extends Abstract_Renderer {
17
+ static function get_counter_chars($type) {
18
+ static $cache = array();
19
+
20
+ if ( isset($cache[$type]) ) {
21
+ return $cache[$type];
22
+ }
23
+
24
+ $uppercase = false;
25
+ $text = "";
26
+
27
+ switch ($type) {
28
+ case "decimal-leading-zero":
29
+ case "decimal":
30
+ case "1":
31
+ return "0123456789";
32
+
33
+ case "upper-alpha":
34
+ case "upper-latin":
35
+ case "A":
36
+ $uppercase = true;
37
+ case "lower-alpha":
38
+ case "lower-latin":
39
+ case "a":
40
+ $text = "abcdefghijklmnopqrstuvwxyz";
41
+ break;
42
+
43
+ case "upper-roman":
44
+ case "I":
45
+ $uppercase = true;
46
+ case "lower-roman":
47
+ case "i":
48
+ $text = "ivxlcdm";
49
+ break;
50
+
51
+ case "lower-greek":
52
+ for($i = 0; $i < 24; $i++) {
53
+ $text .= unichr($i+944);
54
+ }
55
+ break;
56
+ }
57
+
58
+ if ( $uppercase ) {
59
+ $text = strtoupper($text);
60
+ }
61
+
62
+ return $cache[$type] = "$text.";
63
+ }
64
+
65
+ /**
66
+ * @param integer $n
67
+ * @param string $type
68
+ * @param integer $pad
69
+ *
70
+ * @return string
71
+ */
72
+ private function make_counter($n, $type, $pad = null){
73
+ $n = intval($n);
74
+ $text = "";
75
+ $uppercase = false;
76
+
77
+ switch ($type) {
78
+ case "decimal-leading-zero":
79
+ case "decimal":
80
+ case "1":
81
+ if ($pad)
82
+ $text = str_pad($n, $pad, "0", STR_PAD_LEFT);
83
+ else
84
+ $text = $n;
85
+ break;
86
+
87
+ case "upper-alpha":
88
+ case "upper-latin":
89
+ case "A":
90
+ $uppercase = true;
91
+ case "lower-alpha":
92
+ case "lower-latin":
93
+ case "a":
94
+ $text = chr( ($n % 26) + ord('a') - 1);
95
+ break;
96
+
97
+ case "upper-roman":
98
+ case "I":
99
+ $uppercase = true;
100
+ case "lower-roman":
101
+ case "i":
102
+ $text = dec2roman($n);
103
+ break;
104
+
105
+ case "lower-greek":
106
+ $text = unichr($n + 944);
107
+ break;
108
+ }
109
+
110
+ if ( $uppercase ) {
111
+ $text = strtoupper($text);
112
+ }
113
+
114
+ return "$text.";
115
+ }
116
+
117
+ function render(Frame $frame) {
118
+ $style = $frame->get_style();
119
+ $font_size = $style->get_font_size();
120
+ $line_height = $style->length_in_pt($style->line_height, $frame->get_containing_block("w"));
121
+
122
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
123
+
124
+ $li = $frame->get_parent();
125
+
126
+ // Don't render bullets twice if if was split
127
+ if ($li->_splitted) {
128
+ return;
129
+ }
130
+
131
+ // Handle list-style-image
132
+ // If list style image is requested but missing, fall back to predefined types
133
+ if ( $style->list_style_image !== "none" &&
134
+ !Image_Cache::is_broken($img = $frame->get_image_url())) {
135
+
136
+ list($x,$y) = $frame->get_position();
137
+
138
+ //For expected size and aspect, instead of box size, use image natural size scaled to DPI.
139
+ // Resample the bullet image to be consistent with 'auto' sized images
140
+ // See also Image_Frame_Reflower::get_min_max_width
141
+ // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
142
+ //$w = $frame->get_width();
143
+ //$h = $frame->get_height();
144
+ list($width, $height) = dompdf_getimagesize($img);
145
+ $dpi = $this->_dompdf->get_option("dpi");
146
+ $w = ((float)rtrim($width, "px") * 72) / $dpi;
147
+ $h = ((float)rtrim($height, "px") * 72) / $dpi;
148
+
149
+ $x -= $w;
150
+ $y -= ($line_height - $font_size)/2; //Reverse hinting of list_bullet_positioner
151
+
152
+ $this->_canvas->image( $img, $x, $y, $w, $h);
153
+
154
+ } else {
155
+
156
+ $bullet_style = $style->list_style_type;
157
+
158
+ $fill = false;
159
+
160
+ switch ($bullet_style) {
161
+
162
+ default:
163
+ case "disc":
164
+ $fill = true;
165
+
166
+ case "circle":
167
+ list($x,$y) = $frame->get_position();
168
+ $r = ($font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE /*-List_Bullet_Frame_Decorator::BULLET_THICKNESS*/ ))/2;
169
+ $x -= $font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE/2);
170
+ $y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT))/2;
171
+ $o = $font_size*List_Bullet_Frame_Decorator::BULLET_THICKNESS;
172
+ $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill);
173
+ break;
174
+
175
+ case "square":
176
+ list($x, $y) = $frame->get_position();
177
+ $w = $font_size*List_Bullet_Frame_Decorator::BULLET_SIZE;
178
+ $x -= $w;
179
+ $y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT-List_Bullet_Frame_Decorator::BULLET_SIZE))/2;
180
+ $this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color);
181
+ break;
182
+
183
+ case "decimal-leading-zero":
184
+ case "decimal":
185
+ case "lower-alpha":
186
+ case "lower-latin":
187
+ case "lower-roman":
188
+ case "lower-greek":
189
+ case "upper-alpha":
190
+ case "upper-latin":
191
+ case "upper-roman":
192
+ case "1": // HTML 4.0 compatibility
193
+ case "a":
194
+ case "i":
195
+ case "A":
196
+ case "I":
197
+ $pad = null;
198
+ if ( $bullet_style === "decimal-leading-zero" ) {
199
+ $pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count"));
200
+ }
201
+
202
+ $node = $frame->get_node();
203
+
204
+ if ( !$node->hasAttribute("dompdf-counter") ) {
205
+ return;
206
+ }
207
+
208
+ $index = $node->getAttribute("dompdf-counter");
209
+ $text = $this->make_counter($index, $bullet_style, $pad);
210
+
211
+ if ( trim($text) == "" ) {
212
+ return;
213
+ }
214
+
215
+ $spacing = 0;
216
+ $font_family = $style->font_family;
217
+
218
+ $line = $li->get_containing_line();
219
+ list($x, $y) = array($frame->get_position("x"), $line->y);
220
+
221
+ $x -= Font_Metrics::get_text_width($text, $font_family, $font_size, $spacing);
222
+
223
+ // Take line-height into account
224
+ $line_height = $style->line_height;
225
+ $y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results
226
+
227
+ $this->_canvas->text($x, $y, $text,
228
+ $font_family, $font_size,
229
+ $style->color, $spacing);
230
+
231
+ case "none":
232
+ break;
233
+ }
234
+ }
235
+ }
236
+ }
lib/dompdf/include/null_frame_decorator.cls.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Dummy decorator
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Null_Frame_Decorator extends Frame_Decorator {
16
+
17
+ function __construct(Frame $frame, DOMPDF $dompdf) {
18
+ parent::__construct($frame, $dompdf);
19
+ $style = $this->_frame->get_style();
20
+ $style->width = 0;
21
+ $style->height = 0;
22
+ $style->margin = 0;
23
+ $style->padding = 0;
24
+ }
25
+
26
+ }
lib/dompdf/include/null_frame_reflower.cls.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Dummy reflower
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Null_Frame_Reflower extends Frame_Reflower {
16
+
17
+ function __construct(Frame $frame) { parent::__construct($frame); }
18
+
19
+ function reflow(Block_Frame_Decorator $block = null) { return; }
20
+
21
+ }
lib/dompdf/include/null_positioner.cls.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Dummy positioner
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Null_Positioner extends Positioner {
16
+
17
+ function __construct(Frame_Decorator $frame) {
18
+ parent::__construct($frame);
19
+ }
20
+
21
+ function position() { return; }
22
+
23
+ }
lib/dompdf/include/page_cache.cls.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Caches individual rendered PDF pages
11
+ *
12
+ * Not totally implemented yet. Use at your own risk ;)
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ * @static
17
+ */
18
+ class Page_Cache {
19
+
20
+ const DB_USER = "dompdf_page_cache";
21
+ const DB_PASS = "some meaningful password";
22
+ const DB_NAME = "dompdf_page_cache";
23
+
24
+ static private $__connection = null;
25
+
26
+ static function init() {
27
+ if ( is_null(self::$__connection) ) {
28
+ $con_str = "host=" . DB_HOST .
29
+ " dbname=" . self::DB_NAME .
30
+ " user=" . self::DB_USER .
31
+ " password=" . self::DB_PASS;
32
+
33
+ if ( !self::$__connection = pg_connect($con_str) )
34
+ throw new Exception("Database connection failed.");
35
+ }
36
+ }
37
+
38
+ function __construct() { throw new Exception("Can not create instance of Page_Class. Class is static."); }
39
+
40
+ private static function __query($sql) {
41
+ if ( !($res = pg_query(self::$__connection, $sql)) )
42
+ throw new Exception(pg_last_error(self::$__connection));
43
+ return $res;
44
+ }
45
+
46
+ static function store_page($id, $page_num, $data) {
47
+ $where = "WHERE id='" . pg_escape_string($id) . "' AND ".
48
+ "page_num=". pg_escape_string($page_num);
49
+
50
+ $res = self::__query("SELECT timestamp FROM page_cache ". $where);
51
+
52
+ $row = pg_fetch_assoc($res);
53
+
54
+ if ( $row )
55
+ self::__query("UPDATE page_cache SET data='" . pg_escape_string($data) . "' " . $where);
56
+ else
57
+ self::__query("INSERT INTO page_cache (id, page_num, data) VALUES ('" . pg_escape_string($id) . "', ".
58
+ pg_escape_string($page_num) . ", ".
59
+ "'". pg_escape_string($data) . "')");
60
+
61
+ }
62
+
63
+ static function store_fonts($id, $fonts) {
64
+ self::__query("BEGIN");
65
+ // Update the font information
66
+ self::__query("DELETE FROM page_fonts WHERE id='" . pg_escape_string($id) . "'");
67
+
68
+ foreach (array_keys($fonts) as $font)
69
+ self::__query("INSERT INTO page_fonts (id, font_name) VALUES ('" .
70
+ pg_escape_string($id) . "', '" . pg_escape_string($font) . "')");
71
+ self::__query("COMMIT");
72
+ }
73
+
74
+ // static function retrieve_page($id, $page_num) {
75
+
76
+ // $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ".
77
+ // "page_num=". pg_escape_string($page_num));
78
+
79
+ // $row = pg_fetch_assoc($res);
80
+
81
+ // return pg_unescape_bytea($row["data"]);
82
+
83
+ // }
84
+
85
+ static function get_page_timestamp($id, $page_num) {
86
+ $res = self::__query("SELECT timestamp FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ".
87
+ "page_num=". pg_escape_string($page_num));
88
+
89
+ $row = pg_fetch_assoc($res);
90
+
91
+ return $row["timestamp"];
92
+
93
+ }
94
+
95
+ // Adds the cached document referenced by $id to the provided pdf
96
+ static function insert_cached_document(CPDF_Adapter $pdf, $id, $new_page = true) {
97
+ $res = self::__query("SELECT font_name FROM page_fonts WHERE id='" . pg_escape_string($id) . "'");
98
+
99
+ // Ensure that the fonts needed by the cached document are loaded into
100
+ // the pdf
101
+ while ($row = pg_fetch_assoc($res))
102
+ $pdf->get_cpdf()->selectFont($row["font_name"]);
103
+
104
+ $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "'");
105
+
106
+ if ( $new_page )
107
+ $pdf->new_page();
108
+
109
+ $first = true;
110
+ while ($row = pg_fetch_assoc($res)) {
111
+
112
+ if ( !$first )
113
+ $pdf->new_page();
114
+ else
115
+ $first = false;
116
+
117
+ $page = $pdf->reopen_serialized_object($row["data"]);
118
+ //$pdf->close_object();
119
+ $pdf->add_object($page, "add");
120
+
121
+ }
122
+
123
+ }
124
+ }
125
+
126
+ Page_Cache::init();
lib/dompdf/include/page_frame_decorator.cls.php ADDED
@@ -0,0 +1,592 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates frames for page layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Page_Frame_Decorator extends Frame_Decorator {
16
+
17
+ /**
18
+ * y value of bottom page margin
19
+ *
20
+ * @var float
21
+ */
22
+ protected $_bottom_page_margin;
23
+
24
+ /**
25
+ * Flag indicating page is full.
26
+ *
27
+ * @var bool
28
+ */
29
+ protected $_page_full;
30
+
31
+ /**
32
+ * Number of tables currently being reflowed
33
+ *
34
+ * @var int
35
+ */
36
+ protected $_in_table;
37
+
38
+ /**
39
+ * The pdf renderer
40
+ *
41
+ * @var Renderer
42
+ */
43
+ protected $_renderer;
44
+
45
+ /**
46
+ * This page's floating frames
47
+ *
48
+ * @var array
49
+ */
50
+ protected $_floating_frames = array();
51
+
52
+ //........................................................................
53
+
54
+ /**
55
+ * Class constructor
56
+ *
57
+ * @param Frame $frame the frame to decorate
58
+ * @param DOMPDF $dompdf
59
+ */
60
+ function __construct(Frame $frame, DOMPDF $dompdf) {
61
+ parent::__construct($frame, $dompdf);
62
+ $this->_page_full = false;
63
+ $this->_in_table = 0;
64
+ $this->_bottom_page_margin = null;
65
+ }
66
+
67
+ /**
68
+ * Set the renderer used for this pdf
69
+ *
70
+ * @param Renderer $renderer the renderer to use
71
+ */
72
+ function set_renderer($renderer) {
73
+ $this->_renderer = $renderer;
74
+ }
75
+
76
+ /**
77
+ * Return the renderer used for this pdf
78
+ *
79
+ * @return Renderer
80
+ */
81
+ function get_renderer() {
82
+ return $this->_renderer;
83
+ }
84
+
85
+ /**
86
+ * Set the frame's containing block. Overridden to set $this->_bottom_page_margin.
87
+ *
88
+ * @param float $x
89
+ * @param float $y
90
+ * @param float $w
91
+ * @param float $h
92
+ */
93
+ function set_containing_block($x = null, $y = null, $w = null, $h = null) {
94
+ parent::set_containing_block($x,$y,$w,$h);
95
+ //$w = $this->get_containing_block("w");
96
+ if ( isset($h) )
97
+ $this->_bottom_page_margin = $h; // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w);
98
+ }
99
+
100
+ /**
101
+ * Returns true if the page is full and is no longer accepting frames.
102
+ *
103
+ * @return bool
104
+ */
105
+ function is_full() {
106
+ return $this->_page_full;
107
+ }
108
+
109
+ /**
110
+ * Start a new page by resetting the full flag.
111
+ */
112
+ function next_page() {
113
+ $this->_floating_frames = array();
114
+ $this->_renderer->new_page();
115
+ $this->_page_full = false;
116
+ }
117
+
118
+ /**
119
+ * Indicate to the page that a table is currently being reflowed.
120
+ */
121
+ function table_reflow_start() {
122
+ $this->_in_table++;
123
+ }
124
+
125
+ /**
126
+ * Indicate to the page that table reflow is finished.
127
+ */
128
+ function table_reflow_end() {
129
+ $this->_in_table--;
130
+ }
131
+
132
+ /**
133
+ * Return whether we are currently in a nested table or not
134
+ *
135
+ * @return bool
136
+ */
137
+ function in_nested_table() {
138
+ return $this->_in_table > 1;
139
+ }
140
+
141
+ /**
142
+ * Check if a forced page break is required before $frame. This uses the
143
+ * frame's page_break_before property as well as the preceeding frame's
144
+ * page_break_after property.
145
+ *
146
+ * @link http://www.w3.org/TR/CSS21/page.html#forced
147
+ *
148
+ * @param Frame $frame the frame to check
149
+ * @return bool true if a page break occured
150
+ */
151
+ function check_forced_page_break(Frame $frame) {
152
+
153
+ // Skip check if page is already split
154
+ if ( $this->_page_full )
155
+ return null;
156
+
157
+ $block_types = array("block", "list-item", "table", "inline");
158
+ $page_breaks = array("always", "left", "right");
159
+
160
+ $style = $frame->get_style();
161
+
162
+ if ( !in_array($style->display, $block_types) )
163
+ return false;
164
+
165
+ // Find the previous block-level sibling
166
+ $prev = $frame->get_prev_sibling();
167
+
168
+ while ( $prev && !in_array($prev->get_style()->display, $block_types) )
169
+ $prev = $prev->get_prev_sibling();
170
+
171
+
172
+ if ( in_array($style->page_break_before, $page_breaks) ) {
173
+
174
+ // Prevent cascading splits
175
+ $frame->split(null, true);
176
+ // We have to grab the style again here because split() resets
177
+ // $frame->style to the frame's orignal style.
178
+ $frame->get_style()->page_break_before = "auto";
179
+ $this->_page_full = true;
180
+
181
+ return true;
182
+ }
183
+
184
+ if ( $prev && in_array($prev->get_style()->page_break_after, $page_breaks) ) {
185
+ // Prevent cascading splits
186
+ $frame->split(null, true);
187
+ $prev->get_style()->page_break_after = "auto";
188
+ $this->_page_full = true;
189
+ return true;
190
+ }
191
+
192
+ if( $prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body" ) {
193
+ $prev_last_child = $prev->get_last_child();
194
+ if ( in_array($prev_last_child->get_style()->page_break_after, $page_breaks) ) {
195
+ $frame->split(null, true);
196
+ $prev_last_child->get_style()->page_break_after = "auto";
197
+ $this->_page_full = true;
198
+ return true;
199
+ }
200
+ }
201
+
202
+
203
+ return false;
204
+ }
205
+
206
+ /**
207
+ * Determine if a page break is allowed before $frame
208
+ * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
209
+ *
210
+ * In the normal flow, page breaks can occur at the following places:
211
+ *
212
+ * 1. In the vertical margin between block boxes. When a page
213
+ * break occurs here, the used values of the relevant
214
+ * 'margin-top' and 'margin-bottom' properties are set to '0'.
215
+ * 2. Between line boxes inside a block box.
216
+ *
217
+ * These breaks are subject to the following rules:
218
+ *
219
+ * * Rule A: Breaking at (1) is allowed only if the
220
+ * 'page-break-after' and 'page-break-before' properties of
221
+ * all the elements generating boxes that meet at this margin
222
+ * allow it, which is when at least one of them has the value
223
+ * 'always', 'left', or 'right', or when all of them are
224
+ * 'auto'.
225
+ *
226
+ * * Rule B: However, if all of them are 'auto' and the
227
+ * nearest common ancestor of all the elements has a
228
+ * 'page-break-inside' value of 'avoid', then breaking here is
229
+ * not allowed.
230
+ *
231
+ * * Rule C: Breaking at (2) is allowed only if the number of
232
+ * line boxes between the break and the start of the enclosing
233
+ * block box is the value of 'orphans' or more, and the number
234
+ * of line boxes between the break and the end of the box is
235
+ * the value of 'widows' or more.
236
+ *
237
+ * * Rule D: In addition, breaking at (2) is allowed only if
238
+ * the 'page-break-inside' property is 'auto'.
239
+ *
240
+ * If the above doesn't provide enough break points to keep
241
+ * content from overflowing the page boxes, then rules B and D are
242
+ * dropped in order to find additional breakpoints.
243
+ *
244
+ * If that still does not lead to sufficient break points, rules A
245
+ * and C are dropped as well, to find still more break points.
246
+ *
247
+ * We will also allow breaks between table rows. However, when
248
+ * splitting a table, the table headers should carry over to the
249
+ * next page (but they don't yet).
250
+ *
251
+ * @param Frame $frame the frame to check
252
+ * @return bool true if a break is allowed, false otherwise
253
+ */
254
+ protected function _page_break_allowed(Frame $frame) {
255
+
256
+ $block_types = array("block", "list-item", "table", "-dompdf-image");
257
+ dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName. ")");
258
+ $display = $frame->get_style()->display;
259
+
260
+ // Block Frames (1):
261
+ if ( in_array($display, $block_types) ) {
262
+
263
+ // Avoid breaks within table-cells
264
+ if ( $this->_in_table ) {
265
+ dompdf_debug("page-break", "In table: " . $this->_in_table);
266
+ return false;
267
+ }
268
+
269
+ // Rules A & B
270
+
271
+ if ( $frame->get_style()->page_break_before === "avoid" ) {
272
+ dompdf_debug("page-break", "before: avoid");
273
+ return false;
274
+ }
275
+
276
+ // Find the preceeding block-level sibling
277
+ $prev = $frame->get_prev_sibling();
278
+ while ( $prev && !in_array($prev->get_style()->display, $block_types) )
279
+ $prev = $prev->get_prev_sibling();
280
+
281
+ // Does the previous element allow a page break after?
282
+ if ( $prev && $prev->get_style()->page_break_after === "avoid" ) {
283
+ dompdf_debug("page-break", "after: avoid");
284
+ return false;
285
+ }
286
+
287
+ // If both $prev & $frame have the same parent, check the parent's
288
+ // page_break_inside property.
289
+ $parent = $frame->get_parent();
290
+ if ( $prev && $parent && $parent->get_style()->page_break_inside === "avoid" ) {
291
+ dompdf_debug("page-break", "parent inside: avoid");
292
+ return false;
293
+ }
294
+
295
+ // To prevent cascading page breaks when a top-level element has
296
+ // page-break-inside: avoid, ensure that at least one frame is
297
+ // on the page before splitting.
298
+ if ( $parent->get_node()->nodeName === "body" && !$prev ) {
299
+ // We are the body's first child
300
+ dompdf_debug("page-break", "Body's first child.");
301
+ return false;
302
+ }
303
+
304
+ // If the frame is the first block-level frame, use the value from
305
+ // $frame's parent instead.
306
+ if ( !$prev && $parent )
307
+ return $this->_page_break_allowed( $parent );
308
+
309
+ dompdf_debug("page-break", "block: break allowed");
310
+ return true;
311
+
312
+ }
313
+
314
+ // Inline frames (2):
315
+ else if ( in_array($display, Style::$INLINE_TYPES) ) {
316
+
317
+ // Avoid breaks within table-cells
318
+ if ( $this->_in_table ) {
319
+ dompdf_debug("page-break", "In table: " . $this->_in_table);
320
+ return false;
321
+ }
322
+
323
+ // Rule C
324
+ $block_parent = $frame->find_block_parent();
325
+ if ( count($block_parent->get_line_boxes() ) < $frame->get_style()->orphans ) {
326
+ dompdf_debug("page-break", "orphans");
327
+ return false;
328
+ }
329
+
330
+ // FIXME: Checking widows is tricky without having laid out the
331
+ // remaining line boxes. Just ignore it for now...
332
+
333
+ // Rule D
334
+ $p = $block_parent;
335
+ while ($p) {
336
+ if ( $p->get_style()->page_break_inside === "avoid" ) {
337
+ dompdf_debug("page-break", "parent->inside: avoid");
338
+ return false;
339
+ }
340
+ $p = $p->find_block_parent();
341
+ }
342
+
343
+ // To prevent cascading page breaks when a top-level element has
344
+ // page-break-inside: avoid, ensure that at least one frame with
345
+ // some content is on the page before splitting.
346
+ $prev = $frame->get_prev_sibling();
347
+ while ( $prev && ($prev->is_text_node() && trim($prev->get_node()->nodeValue) == "") )
348
+ $prev = $prev->get_prev_sibling();
349
+
350
+ if ( $block_parent->get_node()->nodeName === "body" && !$prev ) {
351
+ // We are the body's first child
352
+ dompdf_debug("page-break", "Body's first child.");
353
+ return false;
354
+ }
355
+
356
+ // Skip breaks on empty text nodes
357
+ if ( $frame->is_text_node() &&
358
+ $frame->get_node()->nodeValue == "" )
359
+ return false;
360
+
361
+ dompdf_debug("page-break", "inline: break allowed");
362
+ return true;
363
+
364
+ // Table-rows
365
+ } else if ( $display === "table-row" ) {
366
+
367
+ // Simply check if the parent table's page_break_inside property is
368
+ // not 'avoid'
369
+ $p = Table_Frame_Decorator::find_parent_table($frame);
370
+
371
+ while ($p) {
372
+ if ( $p->get_style()->page_break_inside === "avoid" ) {
373
+ dompdf_debug("page-break", "parent->inside: avoid");
374
+ return false;
375
+ }
376
+ $p = $p->find_block_parent();
377
+ }
378
+
379
+ // Avoid breaking after the first row of a table
380
+ if ( $p && $p->get_first_child() === $frame) {
381
+ dompdf_debug("page-break", "table: first-row");
382
+ return false;
383
+ }
384
+
385
+ // If this is a nested table, prevent the page from breaking
386
+ if ( $this->_in_table > 1 ) {
387
+ dompdf_debug("page-break", "table: nested table");
388
+ return false;
389
+ }
390
+
391
+ dompdf_debug("page-break","table-row/row-groups: break allowed");
392
+ return true;
393
+
394
+ } else if ( in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
395
+
396
+ // Disallow breaks at row-groups: only split at row boundaries
397
+ return false;
398
+
399
+ } else {
400
+
401
+ dompdf_debug("page-break", "? " . $frame->get_style()->display . "");
402
+ return false;
403
+ }
404
+
405
+ }
406
+
407
+ /**
408
+ * Check if $frame will fit on the page. If the frame does not fit,
409
+ * the frame tree is modified so that a page break occurs in the
410
+ * correct location.
411
+ *
412
+ * @param Frame $frame the frame to check
413
+ * @return Frame the frame following the page break
414
+ */
415
+ function check_page_break(Frame $frame) {
416
+ // Do not split if we have already or if the frame was already
417
+ // pushed to the next page (prevents infinite loops)
418
+ if ( $this->_page_full || $frame->_already_pushed ) {
419
+ return false;
420
+ }
421
+
422
+ // If the frame is absolute of fixed it shouldn't break
423
+ $p = $frame;
424
+ do {
425
+ if ( $p->is_absolute() )
426
+ return false;
427
+ } while ( $p = $p->get_parent() );
428
+
429
+ $margin_height = $frame->get_margin_height();
430
+
431
+ // FIXME If the row is taller than the page and
432
+ // if it the first of the page, we don't break
433
+ if ( $frame->get_style()->display === "table-row" &&
434
+ !$frame->get_prev_sibling() &&
435
+ $margin_height > $this->get_margin_height() )
436
+ return false;
437
+
438
+ // Determine the frame's maximum y value
439
+ $max_y = $frame->get_position("y") + $margin_height;
440
+
441
+ // If a split is to occur here, then the bottom margins & paddings of all
442
+ // parents of $frame must fit on the page as well:
443
+ $p = $frame->get_parent();
444
+ while ( $p ) {
445
+ $style = $p->get_style();
446
+ $max_y += $style->length_in_pt(array($style->margin_bottom,
447
+ $style->padding_bottom,
448
+ $style->border_bottom_width));
449
+ $p = $p->get_parent();
450
+ }
451
+
452
+
453
+ // Check if $frame flows off the page
454
+ if ( $max_y <= $this->_bottom_page_margin )
455
+ // no: do nothing
456
+ return false;
457
+
458
+ dompdf_debug("page-break", "check_page_break");
459
+ dompdf_debug("page-break", "in_table: " . $this->_in_table);
460
+
461
+ // yes: determine page break location
462
+ $iter = $frame;
463
+ $flg = false;
464
+
465
+ $in_table = $this->_in_table;
466
+
467
+ dompdf_debug("page-break","Starting search");
468
+ while ( $iter ) {
469
+ // echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). "";
470
+ if ( $iter === $this ) {
471
+ dompdf_debug("page-break", "reached root.");
472
+ // We've reached the root in our search. Just split at $frame.
473
+ break;
474
+ }
475
+
476
+ if ( $this->_page_break_allowed($iter) ) {
477
+ dompdf_debug("page-break","break allowed, splitting.");
478
+ $iter->split(null, true);
479
+ $this->_page_full = true;
480
+ $this->_in_table = $in_table;
481
+ $frame->_already_pushed = true;
482
+ return true;
483
+ }
484
+
485
+ if ( !$flg && $next = $iter->get_last_child() ) {
486
+ dompdf_debug("page-break", "following last child.");
487
+
488
+ if ( $next->is_table() )
489
+ $this->_in_table++;
490
+
491
+ $iter = $next;
492
+ continue;
493
+ }
494
+
495
+ if ( $next = $iter->get_prev_sibling() ) {
496
+ dompdf_debug("page-break", "following prev sibling.");
497
+
498
+ if ( $next->is_table() && !$iter->is_table() )
499
+ $this->_in_table++;
500
+
501
+ else if ( !$next->is_table() && $iter->is_table() )
502
+ $this->_in_table--;
503
+
504
+ $iter = $next;
505
+ $flg = false;
506
+ continue;
507
+ }
508
+
509
+ if ( $next = $iter->get_parent() ) {
510
+ dompdf_debug("page-break", "following parent.");
511
+
512
+ if ( $iter->is_table() )
513
+ $this->_in_table--;
514
+
515
+ $iter = $next;
516
+ $flg = true;
517
+ continue;
518
+ }
519
+
520
+ break;
521
+ }
522
+
523
+ $this->_in_table = $in_table;
524
+
525
+ // No valid page break found. Just break at $frame.
526
+ dompdf_debug("page-break", "no valid break found, just splitting.");
527
+
528
+ // If we are in a table, backtrack to the nearest top-level table row
529
+ if ( $this->_in_table ) {
530
+ $iter = $frame;
531
+ while ($iter && $iter->get_style()->display !== "table-row")
532
+ $iter = $iter->get_parent();
533
+
534
+ $iter->split(null, true);
535
+ } else {
536
+ $frame->split(null, true);
537
+ }
538
+
539
+ $this->_page_full = true;
540
+ $frame->_already_pushed = true;
541
+ return true;
542
+ }
543
+
544
+ //........................................................................
545
+
546
+ function split(Frame $frame = null, $force_pagebreak = false) {
547
+ // Do nothing
548
+ }
549
+
550
+ /**
551
+ * Add a floating frame
552
+ *
553
+ * @param Frame $frame
554
+ *
555
+ * @return void
556
+ */
557
+ function add_floating_frame(Frame $frame) {
558
+ array_unshift($this->_floating_frames, $frame);
559
+ }
560
+
561
+ /**
562
+ * @return Frame[]
563
+ */
564
+ function get_floating_frames() {
565
+ return $this->_floating_frames;
566
+ }
567
+
568
+ public function remove_floating_frame($key) {
569
+ unset($this->_floating_frames[$key]);
570
+ }
571
+
572
+ public function get_lowest_float_offset(Frame $child) {
573
+ $style = $child->get_style();
574
+ $side = $style->clear;
575
+ $float = $style->float;
576
+
577
+ $y = 0;
578
+
579
+ foreach($this->_floating_frames as $key => $frame) {
580
+ if ( $side === "both" || $frame->get_style()->float === $side ) {
581
+ $y = max($y, $frame->get_position("y") + $frame->get_margin_height());
582
+
583
+ if ( $float !== "none" ) {
584
+ $this->remove_floating_frame($key);
585
+ }
586
+ }
587
+ }
588
+
589
+ return $y;
590
+ }
591
+
592
+ }
lib/dompdf/include/page_frame_reflower.cls.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Reflows pages
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Page_Frame_Reflower extends Frame_Reflower {
17
+
18
+ /**
19
+ * Cache of the callbacks array
20
+ *
21
+ * @var array
22
+ */
23
+ private $_callbacks;
24
+
25
+ /**
26
+ * Cache of the canvas
27
+ *
28
+ * @var Canvas
29
+ */
30
+ private $_canvas;
31
+
32
+ function __construct(Page_Frame_Decorator $frame) { parent::__construct($frame); }
33
+
34
+ function apply_page_style(Frame $frame, $page_number){
35
+ $style = $frame->get_style();
36
+ $page_styles = $style->get_stylesheet()->get_page_styles();
37
+
38
+ // http://www.w3.org/TR/CSS21/page.html#page-selectors
39
+ if ( count($page_styles) > 1 ) {
40
+ $odd = $page_number % 2 == 1;
41
+ $first = $page_number == 1;
42
+
43
+ $style = clone $page_styles["base"];
44
+
45
+ // FIXME RTL
46
+ if ( $odd && isset($page_styles[":right"]) ) {
47
+ $style->merge($page_styles[":right"]);
48
+ }
49
+
50
+ if ( $odd && isset($page_styles[":odd"]) ) {
51
+ $style->merge($page_styles[":odd"]);
52
+ }
53
+
54
+ // FIXME RTL
55
+ if ( !$odd && isset($page_styles[":left"]) ) {
56
+ $style->merge($page_styles[":left"]);
57
+ }
58
+
59
+ if ( !$odd && isset($page_styles[":even"]) ) {
60
+ $style->merge($page_styles[":even"]);
61
+ }
62
+
63
+ if ( $first && isset($page_styles[":first"]) ) {
64
+ $style->merge($page_styles[":first"]);
65
+ }
66
+
67
+ $frame->set_style($style);
68
+ }
69
+ }
70
+
71
+ //........................................................................
72
+
73
+ /**
74
+ * Paged layout:
75
+ * http://www.w3.org/TR/CSS21/page.html
76
+ */
77
+ function reflow(Block_Frame_Decorator $block = null) {
78
+ $fixed_children = array();
79
+ $prev_child = null;
80
+ $child = $this->_frame->get_first_child();
81
+ $current_page = 0;
82
+
83
+ while ($child) {
84
+ $this->apply_page_style($this->_frame, $current_page + 1);
85
+
86
+ $style = $this->_frame->get_style();
87
+
88
+ // Pages are only concerned with margins
89
+ $cb = $this->_frame->get_containing_block();
90
+ $left = $style->length_in_pt($style->margin_left, $cb["w"]);
91
+ $right = $style->length_in_pt($style->margin_right, $cb["w"]);
92
+ $top = $style->length_in_pt($style->margin_top, $cb["h"]);
93
+ $bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
94
+
95
+ $content_x = $cb["x"] + $left;
96
+ $content_y = $cb["y"] + $top;
97
+ $content_width = $cb["w"] - $left - $right;
98
+ $content_height = $cb["h"] - $top - $bottom;
99
+
100
+ // Only if it's the first page, we save the nodes with a fixed position
101
+ if ($current_page == 0) {
102
+ $children = $child->get_children();
103
+ foreach ($children as $onechild) {
104
+ if ($onechild->get_style()->position === "fixed") {
105
+ $fixed_children[] = $onechild->deep_copy();
106
+ }
107
+ }
108
+ $fixed_children = array_reverse($fixed_children);
109
+ }
110
+
111
+ $child->set_containing_block($content_x, $content_y, $content_width, $content_height);
112
+
113
+ // Check for begin reflow callback
114
+ $this->_check_callbacks("begin_page_reflow", $child);
115
+
116
+ //Insert a copy of each node which have a fixed position
117
+ if ($current_page >= 1) {
118
+ foreach ($fixed_children as $fixed_child) {
119
+ $child->insert_child_before($fixed_child->deep_copy(), $child->get_first_child());
120
+ }
121
+ }
122
+
123
+ $child->reflow();
124
+ $next_child = $child->get_next_sibling();
125
+
126
+ // Check for begin render callback
127
+ $this->_check_callbacks("begin_page_render", $child);
128
+
129
+ // Render the page
130
+ $this->_frame->get_renderer()->render($child);
131
+
132
+ // Check for end render callback
133
+ $this->_check_callbacks("end_page_render", $child);
134
+
135
+ if ( $next_child ) {
136
+ $this->_frame->next_page();
137
+ }
138
+
139
+ // Wait to dispose of all frames on the previous page
140
+ // so callback will have access to them
141
+ if ( $prev_child ) {
142
+ $prev_child->dispose(true);
143
+ }
144
+ $prev_child = $child;
145
+ $child = $next_child;
146
+ $current_page++;
147
+ }
148
+
149
+ // Dispose of previous page if it still exists
150
+ if ( $prev_child ) {
151
+ $prev_child->dispose(true);
152
+ }
153
+ }
154
+
155
+ //........................................................................
156
+
157
+ /**
158
+ * Check for callbacks that need to be performed when a given event
159
+ * gets triggered on a page
160
+ *
161
+ * @param string $event the type of event
162
+ * @param Frame $frame the frame that event is triggered on
163
+ */
164
+ protected function _check_callbacks($event, $frame) {
165
+ if (!isset($this->_callbacks)) {
166
+ $dompdf = $this->_frame->get_dompdf();
167
+ $this->_callbacks = $dompdf->get_callbacks();
168
+ $this->_canvas = $dompdf->get_canvas();
169
+ }
170
+
171
+ if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
172
+ $info = array(0 => $this->_canvas, "canvas" => $this->_canvas,
173
+ 1 => $frame, "frame" => $frame);
174
+ $fs = $this->_callbacks[$event];
175
+ foreach ($fs as $f) {
176
+ if (is_callable($f)) {
177
+ if (is_array($f)) {
178
+ $f[0]->$f[1]($info);
179
+ } else {
180
+ $f($info);
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ }
lib/dompdf/include/pdflib_adapter.cls.php ADDED
@@ -0,0 +1,1085 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * PDF rendering interface
12
+ *
13
+ * PDFLib_Adapter provides a simple, stateless interface to the one
14
+ * provided by PDFLib.
15
+ *
16
+ * Unless otherwise mentioned, all dimensions are in points (1/72 in).
17
+ * The coordinate origin is in the top left corner and y values
18
+ * increase downwards.
19
+ *
20
+ * See {@link http://www.pdflib.com/} for more complete documentation
21
+ * on the underlying PDFlib functions.
22
+ *
23
+ * @package dompdf
24
+ */
25
+ class PDFLib_Adapter implements Canvas {
26
+
27
+ /**
28
+ * Dimensions of paper sizes in points
29
+ *
30
+ * @var array;
31
+ */
32
+ static public $PAPER_SIZES = array(); // Set to CPDF_Adapter::$PAPER_SIZES below.
33
+
34
+ /**
35
+ * Whether to create PDFs in memory or on disk
36
+ *
37
+ * @var bool
38
+ */
39
+ static $IN_MEMORY = true;
40
+
41
+ /**
42
+ * @var DOMPDF
43
+ */
44
+ private $_dompdf;
45
+
46
+ /**
47
+ * Instance of PDFLib class
48
+ *
49
+ * @var PDFlib
50
+ */
51
+ private $_pdf;
52
+
53
+ /**
54
+ * Name of temporary file used for PDFs created on disk
55
+ *
56
+ * @var string
57
+ */
58
+ private $_file;
59
+
60
+ /**
61
+ * PDF width, in points
62
+ *
63
+ * @var float
64
+ */
65
+ private $_width;
66
+
67
+ /**
68
+ * PDF height, in points
69
+ *
70
+ * @var float
71
+ */
72
+ private $_height;
73
+
74
+ /**
75
+ * Last fill color used
76
+ *
77
+ * @var array
78
+ */
79
+ private $_last_fill_color;
80
+
81
+ /**
82
+ * Last stroke color used
83
+ *
84
+ * @var array
85
+ */
86
+ private $_last_stroke_color;
87
+
88
+ /**
89
+ * Cache of image handles
90
+ *
91
+ * @var array
92
+ */
93
+ private $_imgs;
94
+
95
+ /**
96
+ * Cache of font handles
97
+ *
98
+ * @var array
99
+ */
100
+ private $_fonts;
101
+
102
+ /**
103
+ * List of objects (templates) to add to multiple pages
104
+ *
105
+ * @var array
106
+ */
107
+ private $_objs;
108
+
109
+ /**
110
+ * Current page number
111
+ *
112
+ * @var int
113
+ */
114
+ private $_page_number;
115
+
116
+ /**
117
+ * Total number of pages
118
+ *
119
+ * @var int
120
+ */
121
+ private $_page_count;
122
+
123
+ /**
124
+ * Text to display on every page
125
+ *
126
+ * @var array
127
+ */
128
+ private $_page_text;
129
+
130
+ /**
131
+ * Array of pages for accesing after rendering is initially complete
132
+ *
133
+ * @var array
134
+ */
135
+ private $_pages;
136
+
137
+ /**
138
+ * Class constructor
139
+ *
140
+ * @param mixed $paper The size of paper to use either a string (see {@link CPDF_Adapter::$PAPER_SIZES}) or
141
+ * an array(xmin,ymin,xmax,ymax)
142
+ * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
143
+ * @param DOMPDF $dompdf
144
+ */
145
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
146
+ if ( is_array($paper) ) {
147
+ $size = $paper;
148
+ }
149
+ else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) {
150
+ $size = self::$PAPER_SIZES[mb_strtolower($paper)];
151
+ }
152
+ else {
153
+ $size = self::$PAPER_SIZES["letter"];
154
+ }
155
+
156
+ if ( mb_strtolower($orientation) === "landscape" ) {
157
+ list($size[2], $size[3]) = array($size[3], $size[2]);
158
+ }
159
+
160
+ $this->_width = $size[2] - $size[0];
161
+ $this->_height= $size[3] - $size[1];
162
+
163
+ $this->_dompdf = $dompdf;
164
+
165
+ $this->_pdf = new PDFLib();
166
+
167
+ if ( defined("DOMPDF_PDFLIB_LICENSE") )
168
+ $this->_pdf->set_parameter( "license", DOMPDF_PDFLIB_LICENSE);
169
+
170
+ $this->_pdf->set_parameter("textformat", "utf8");
171
+ $this->_pdf->set_parameter("fontwarning", "false");
172
+
173
+ $this->_pdf->set_info("Creator", "DOMPDF");
174
+
175
+ // Silence pedantic warnings about missing TZ settings
176
+ $tz = @date_default_timezone_get();
177
+ date_default_timezone_set("UTC");
178
+ $this->_pdf->set_info("Date", date("Y-m-d"));
179
+ date_default_timezone_set($tz);
180
+
181
+ if ( self::$IN_MEMORY )
182
+ $this->_pdf->begin_document("","");
183
+ else {
184
+ $tmp_dir = $this->_dompdf->get_options("temp_dir");
185
+ $tmp_name = tempnam($tmp_dir, "libdompdf_pdf_");
186
+ @unlink($tmp_name);
187
+ $this->_file = "$tmp_name.pdf";
188
+ $this->_pdf->begin_document($this->_file,"");
189
+ }
190
+
191
+ $this->_pdf->begin_page_ext($this->_width, $this->_height, "");
192
+
193
+ $this->_page_number = $this->_page_count = 1;
194
+ $this->_page_text = array();
195
+
196
+ $this->_imgs = array();
197
+ $this->_fonts = array();
198
+ $this->_objs = array();
199
+
200
+ // Set up font paths
201
+ $families = Font_Metrics::get_font_families();
202
+ foreach ($families as $files) {
203
+ foreach ($files as $file) {
204
+ $face = basename($file);
205
+ $afm = null;
206
+
207
+ // Prefer ttfs to afms
208
+ if ( file_exists("$file.ttf") ) {
209
+ $outline = "$file.ttf";
210
+
211
+ } else if ( file_exists("$file.TTF") ) {
212
+ $outline = "$file.TTF";
213
+
214
+ } else if ( file_exists("$file.pfb") ) {
215
+ $outline = "$file.pfb";
216
+
217
+ if ( file_exists("$file.afm") ) {
218
+ $afm = "$file.afm";
219
+ }
220
+
221
+ } else if ( file_exists("$file.PFB") ) {
222
+ $outline = "$file.PFB";
223
+ if ( file_exists("$file.AFM") ) {
224
+ $afm = "$file.AFM";
225
+ }
226
+ } else {
227
+ continue;
228
+ }
229
+
230
+ $this->_pdf->set_parameter("FontOutline", "\{$face\}=\{$outline\}");
231
+
232
+ if ( !is_null($afm) ) {
233
+ $this->_pdf->set_parameter("FontAFM", "\{$face\}=\{$afm\}");
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ function get_dompdf(){
240
+ return $this->_dompdf;
241
+ }
242
+
243
+ /**
244
+ * Close the pdf
245
+ */
246
+ protected function _close() {
247
+ $this->_place_objects();
248
+
249
+ // Close all pages
250
+ $this->_pdf->suspend_page("");
251
+ for ($p = 1; $p <= $this->_page_count; $p++) {
252
+ $this->_pdf->resume_page("pagenumber=$p");
253
+ $this->_pdf->end_page_ext("");
254
+ }
255
+
256
+ $this->_pdf->end_document("");
257
+ }
258
+
259
+
260
+ /**
261
+ * Returns the PDFLib instance
262
+ *
263
+ * @return PDFLib
264
+ */
265
+ function get_pdflib() {
266
+ return $this->_pdf;
267
+ }
268
+
269
+ /**
270
+ * Add meta information to the PDF
271
+ *
272
+ * @param string $label label of the value (Creator, Producter, etc.)
273
+ * @param string $value the text to set
274
+ */
275
+ function add_info($label, $value) {
276
+ $this->_pdf->set_info($label, $value);
277
+ }
278
+
279
+ /**
280
+ * Opens a new 'object' (template in PDFLib-speak)
281
+ *
282
+ * While an object is open, all drawing actions are recorded to the
283
+ * object instead of being drawn on the current page. Objects can
284
+ * be added later to a specific page or to several pages.
285
+ *
286
+ * The return value is an integer ID for the new object.
287
+ *
288
+ * @see PDFLib_Adapter::close_object()
289
+ * @see PDFLib_Adapter::add_object()
290
+ *
291
+ * @return int
292
+ */
293
+ function open_object() {
294
+ $this->_pdf->suspend_page("");
295
+ $ret = $this->_pdf->begin_template($this->_width, $this->_height);
296
+ $this->_pdf->save();
297
+ $this->_objs[$ret] = array("start_page" => $this->_page_number);
298
+ return $ret;
299
+ }
300
+
301
+ /**
302
+ * Reopen an existing object (NOT IMPLEMENTED)
303
+ * PDFLib does not seem to support reopening templates.
304
+ *
305
+ * @param int $object the ID of a previously opened object
306
+ *
307
+ * @throws DOMPDF_Exception
308
+ * @return void
309
+ */
310
+ function reopen_object($object) {
311
+ throw new DOMPDF_Exception("PDFLib does not support reopening objects.");
312
+ }
313
+
314
+ /**
315
+ * Close the current template
316
+ *
317
+ * @see PDFLib_Adapter::open_object()
318
+ */
319
+ function close_object() {
320
+ $this->_pdf->restore();
321
+ $this->_pdf->end_template();
322
+ $this->_pdf->resume_page("pagenumber=".$this->_page_number);
323
+ }
324
+
325
+ /**
326
+ * Adds the specified object to the document
327
+ *
328
+ * $where can be one of:
329
+ * - 'add' add to current page only
330
+ * - 'all' add to every page from the current one onwards
331
+ * - 'odd' add to all odd numbered pages from now on
332
+ * - 'even' add to all even numbered pages from now on
333
+ * - 'next' add the object to the next page only
334
+ * - 'nextodd' add to all odd numbered pages from the next one
335
+ * - 'nexteven' add to all even numbered pages from the next one
336
+ *
337
+ * @param int $object the object handle returned by open_object()
338
+ * @param string $where
339
+ */
340
+ function add_object($object, $where = 'all') {
341
+
342
+ if ( mb_strpos($where, "next") !== false ) {
343
+ $this->_objs[$object]["start_page"]++;
344
+ $where = str_replace("next", "", $where);
345
+ if ( $where == "" )
346
+ $where = "add";
347
+ }
348
+
349
+ $this->_objs[$object]["where"] = $where;
350
+ }
351
+
352
+ /**
353
+ * Stops the specified template from appearing in the document.
354
+ *
355
+ * The object will stop being displayed on the page following the
356
+ * current one.
357
+ *
358
+ * @param int $object
359
+ */
360
+ function stop_object($object) {
361
+
362
+ if ( !isset($this->_objs[$object]) )
363
+ return;
364
+
365
+ $start = $this->_objs[$object]["start_page"];
366
+ $where = $this->_objs[$object]["where"];
367
+
368
+ // Place the object on this page if required
369
+ if ( $this->_page_number >= $start &&
370
+ (($this->_page_number % 2 == 0 && $where === "even") ||
371
+ ($this->_page_number % 2 == 1 && $where === "odd") ||
372
+ ($where === "all")) ) {
373
+ $this->_pdf->fit_image($object, 0, 0, "");
374
+ }
375
+
376
+ $this->_objs[$object] = null;
377
+ unset($this->_objs[$object]);
378
+ }
379
+
380
+ /**
381
+ * Add all active objects to the current page
382
+ */
383
+ protected function _place_objects() {
384
+
385
+ foreach ( $this->_objs as $obj => $props ) {
386
+ $start = $props["start_page"];
387
+ $where = $props["where"];
388
+
389
+ // Place the object on this page if required
390
+ if ( $this->_page_number >= $start &&
391
+ (($this->_page_number % 2 == 0 && $where === "even") ||
392
+ ($this->_page_number % 2 == 1 && $where === "odd") ||
393
+ ($where === "all")) ) {
394
+ $this->_pdf->fit_image($obj,0,0,"");
395
+ }
396
+ }
397
+
398
+ }
399
+
400
+ function get_width() { return $this->_width; }
401
+
402
+ function get_height() { return $this->_height; }
403
+
404
+ function get_page_number() { return $this->_page_number; }
405
+
406
+ function get_page_count() { return $this->_page_count; }
407
+
408
+ function set_page_number($num) { $this->_page_number = (int)$num; }
409
+
410
+ function set_page_count($count) { $this->_page_count = (int)$count; }
411
+
412
+
413
+ /**
414
+ * Sets the line style
415
+ *
416
+ * @param float $width
417
+ * @param $cap
418
+ * @param string $join
419
+ * @param array $dash
420
+ *
421
+ * @return void
422
+ */
423
+ protected function _set_line_style($width, $cap, $join, $dash) {
424
+
425
+ if ( count($dash) == 1 )
426
+ $dash[] = $dash[0];
427
+
428
+ if ( count($dash) > 1 )
429
+ $this->_pdf->setdashpattern("dasharray={" . implode(" ", $dash) . "}");
430
+ else
431
+ $this->_pdf->setdash(0,0);
432
+
433
+ switch ( $join ) {
434
+ case "miter":
435
+ $this->_pdf->setlinejoin(0);
436
+ break;
437
+
438
+ case "round":
439
+ $this->_pdf->setlinejoin(1);
440
+ break;
441
+
442
+ case "bevel":
443
+ $this->_pdf->setlinejoin(2);
444
+ break;
445
+
446
+ default:
447
+ break;
448
+ }
449
+
450
+ switch ( $cap ) {
451
+ case "butt":
452
+ $this->_pdf->setlinecap(0);
453
+ break;
454
+
455
+ case "round":
456
+ $this->_pdf->setlinecap(1);
457
+ break;
458
+
459
+ case "square":
460
+ $this->_pdf->setlinecap(2);
461
+ break;
462
+
463
+ default:
464
+ break;
465
+ }
466
+
467
+ $this->_pdf->setlinewidth($width);
468
+
469
+ }
470
+
471
+ /**
472
+ * Sets the line color
473
+ *
474
+ * @param array $color array(r,g,b)
475
+ */
476
+ protected function _set_stroke_color($color) {
477
+ if($this->_last_stroke_color == $color)
478
+ return;
479
+
480
+ $this->_last_stroke_color = $color;
481
+
482
+ if (isset($color[3])) {
483
+ $type = "cmyk";
484
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
485
+ }
486
+ elseif (isset($color[2])) {
487
+ $type = "rgb";
488
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
489
+ }
490
+ else {
491
+ $type = "gray";
492
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
493
+ }
494
+
495
+ $this->_pdf->setcolor("stroke", $type, $c1, $c2, $c3, $c4);
496
+ }
497
+
498
+ /**
499
+ * Sets the fill color
500
+ *
501
+ * @param array $color array(r,g,b)
502
+ */
503
+ protected function _set_fill_color($color) {
504
+ if($this->_last_fill_color == $color)
505
+ return;
506
+
507
+ $this->_last_fill_color = $color;
508
+
509
+ if (isset($color[3])) {
510
+ $type = "cmyk";
511
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
512
+ }
513
+ elseif (isset($color[2])) {
514
+ $type = "rgb";
515
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
516
+ }
517
+ else {
518
+ $type = "gray";
519
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
520
+ }
521
+
522
+ $this->_pdf->setcolor("fill", $type, $c1, $c2, $c3, $c4);
523
+ }
524
+
525
+ /**
526
+ * Sets the opacity
527
+ *
528
+ * @param $opacity
529
+ * @param $mode
530
+ */
531
+ function set_opacity($opacity, $mode = "Normal") {
532
+ if ( $mode === "Normal" ) {
533
+ $gstate = $this->_pdf->create_gstate("opacityfill=$opacity opacitystroke=$opacity");
534
+ $this->_pdf->set_gstate($gstate);
535
+ }
536
+ }
537
+
538
+ function set_default_view($view, $options = array()) {
539
+ // TODO
540
+ // http://www.pdflib.com/fileadmin/pdflib/pdf/manuals/PDFlib-8.0.2-API-reference.pdf
541
+ /**
542
+ * fitheight Fit the page height to the window, with the x coordinate left at the left edge of the window.
543
+ * fitrect Fit the rectangle specified by left, bottom, right, and top to the window.
544
+ * fitvisible Fit the visible contents of the page (the ArtBox) to the window.
545
+ * fitvisibleheight Fit the visible contents of the page to the window with the x coordinate left at the left edge of the window.
546
+ * fitvisiblewidth Fit the visible contents of the page to the window with the y coordinate top at the top edge of the window.
547
+ * fitwidth Fit the page width to the window, with the y coordinate top at the top edge of the window.
548
+ * fitwindow Fit the complete page to the window.
549
+ * fixed
550
+ */
551
+ //$this->_pdf->set_parameter("openaction", $view);
552
+ }
553
+
554
+ /**
555
+ * Loads a specific font and stores the corresponding descriptor.
556
+ *
557
+ * @param string $font
558
+ * @param string $encoding
559
+ * @param string $options
560
+ *
561
+ * @return int the font descriptor for the font
562
+ */
563
+ protected function _load_font($font, $encoding = null, $options = "") {
564
+
565
+ // Check if the font is a native PDF font
566
+ // Embed non-native fonts
567
+ $test = strtolower(basename($font));
568
+ if ( in_array($test, DOMPDF::$native_fonts) ) {
569
+ $font = basename($font);
570
+
571
+ } else {
572
+ // Embed non-native fonts
573
+ $options .= " embedding=true";
574
+ }
575
+
576
+ if ( is_null($encoding) ) {
577
+
578
+ // Unicode encoding is only available for the commerical
579
+ // version of PDFlib and not PDFlib-Lite
580
+ if ( defined("DOMPDF_PDFLIB_LICENSE") )
581
+ $encoding = "unicode";
582
+ else
583
+ $encoding = "auto";
584
+
585
+ }
586
+
587
+ $key = "$font:$encoding:$options";
588
+
589
+ if ( isset($this->_fonts[$key]) )
590
+ return $this->_fonts[$key];
591
+
592
+ else {
593
+
594
+ $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options);
595
+ return $this->_fonts[$key];
596
+
597
+ }
598
+
599
+ }
600
+
601
+ /**
602
+ * Remaps y coords from 4th to 1st quadrant
603
+ *
604
+ * @param float $y
605
+ * @return float
606
+ */
607
+ protected function y($y) { return $this->_height - $y; }
608
+
609
+ //........................................................................
610
+
611
+ /**
612
+ * @param float $x1
613
+ * @param float $y1
614
+ * @param float $x2
615
+ * @param float $y2
616
+ * @param array $color
617
+ * @param float $width
618
+ * @param array $style
619
+ */
620
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
621
+ $this->_set_line_style($width, "butt", "", $style);
622
+ $this->_set_stroke_color($color);
623
+
624
+ $y1 = $this->y($y1);
625
+ $y2 = $this->y($y2);
626
+
627
+ $this->_pdf->moveto($x1, $y1);
628
+ $this->_pdf->lineto($x2, $y2);
629
+ $this->_pdf->stroke();
630
+ }
631
+
632
+ function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
633
+ $this->_set_line_style($width, "butt", "", $style);
634
+ $this->_set_stroke_color($color);
635
+
636
+ $y1 = $this->y($y1);
637
+
638
+ $this->_pdf->arc($x1, $y1, $r1, $astart, $aend);
639
+ $this->_pdf->stroke();
640
+ }
641
+
642
+ //........................................................................
643
+
644
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
645
+ $this->_set_stroke_color($color);
646
+ $this->_set_line_style($width, "butt", "", $style);
647
+
648
+ $y1 = $this->y($y1) - $h;
649
+
650
+ $this->_pdf->rect($x1, $y1, $w, $h);
651
+ $this->_pdf->stroke();
652
+ }
653
+
654
+ //........................................................................
655
+
656
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
657
+ $this->_set_fill_color($color);
658
+
659
+ $y1 = $this->y($y1) - $h;
660
+
661
+ $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h));
662
+ $this->_pdf->fill();
663
+ }
664
+
665
+ function clipping_rectangle($x1, $y1, $w, $h) {
666
+ $this->_pdf->save();
667
+
668
+ $y1 = $this->y($y1) - $h;
669
+
670
+ $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h));
671
+ $this->_pdf->clip();
672
+ }
673
+
674
+ function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
675
+ // @todo
676
+ $this->clipping_rectangle($x1, $y1, $w, $h);
677
+ }
678
+
679
+ function clipping_end() {
680
+ $this->_pdf->restore();
681
+ }
682
+
683
+ function save() {
684
+ $this->_pdf->save();
685
+ }
686
+
687
+ function restore() {
688
+ $this->_pdf->restore();
689
+ }
690
+
691
+ function rotate($angle, $x, $y) {
692
+ $pdf = $this->_pdf;
693
+ $pdf->translate($x, $this->_height-$y);
694
+ $pdf->rotate(-$angle);
695
+ $pdf->translate(-$x, -$this->_height+$y);
696
+ }
697
+
698
+ function skew($angle_x, $angle_y, $x, $y) {
699
+ $pdf = $this->_pdf;
700
+ $pdf->translate($x, $this->_height-$y);
701
+ $pdf->skew($angle_y, $angle_x); // Needs to be inverted
702
+ $pdf->translate(-$x, -$this->_height+$y);
703
+ }
704
+
705
+ function scale($s_x, $s_y, $x, $y) {
706
+ $pdf = $this->_pdf;
707
+ $pdf->translate($x, $this->_height-$y);
708
+ $pdf->scale($s_x, $s_y);
709
+ $pdf->translate(-$x, -$this->_height+$y);
710
+ }
711
+
712
+ function translate($t_x, $t_y) {
713
+ $this->_pdf->translate($t_x, -$t_y);
714
+ }
715
+
716
+ function transform($a, $b, $c, $d, $e, $f) {
717
+ $this->_pdf->concat($a, $b, $c, $d, $e, $f);
718
+ }
719
+
720
+ //........................................................................
721
+
722
+ function polygon($points, $color, $width = null, $style = null, $fill = false) {
723
+
724
+ $this->_set_fill_color($color);
725
+ $this->_set_stroke_color($color);
726
+
727
+ if ( !$fill && isset($width) )
728
+ $this->_set_line_style($width, "square", "miter", $style);
729
+
730
+ $y = $this->y(array_pop($points));
731
+ $x = array_pop($points);
732
+ $this->_pdf->moveto($x,$y);
733
+
734
+ while (count($points) > 1) {
735
+ $y = $this->y(array_pop($points));
736
+ $x = array_pop($points);
737
+ $this->_pdf->lineto($x,$y);
738
+ }
739
+
740
+ if ( $fill )
741
+ $this->_pdf->fill();
742
+ else
743
+ $this->_pdf->closepath_stroke();
744
+ }
745
+
746
+ //........................................................................
747
+
748
+ function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
749
+
750
+ $this->_set_fill_color($color);
751
+ $this->_set_stroke_color($color);
752
+
753
+ if ( !$fill && isset($width) )
754
+ $this->_set_line_style($width, "round", "round", $style);
755
+
756
+ $y = $this->y($y);
757
+
758
+ $this->_pdf->circle($x, $y, $r);
759
+
760
+ if ( $fill )
761
+ $this->_pdf->fill();
762
+ else
763
+ $this->_pdf->stroke();
764
+
765
+ }
766
+
767
+ //........................................................................
768
+
769
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
770
+ $w = (int)$w;
771
+ $h = (int)$h;
772
+
773
+ $img_type = Image_Cache::detect_type($img_url);
774
+ $img_ext = Image_Cache::type_to_ext($img_type);
775
+
776
+ if ( !isset($this->_imgs[$img_url]) ) {
777
+ $this->_imgs[$img_url] = $this->_pdf->load_image($img_ext, $img_url, "");
778
+ }
779
+
780
+ $img = $this->_imgs[$img_url];
781
+
782
+ $y = $this->y($y) - $h;
783
+ $this->_pdf->fit_image($img, $x, $y, 'boxsize={'."$w $h".'} fitmethod=entire');
784
+ }
785
+
786
+ //........................................................................
787
+
788
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0, $char_spacing = 0, $angle = 0) {
789
+ $fh = $this->_load_font($font);
790
+
791
+ $this->_pdf->setfont($fh, $size);
792
+ $this->_set_fill_color($color);
793
+
794
+ $y = $this->y($y) - Font_Metrics::get_font_height($font, $size);
795
+
796
+ $word_spacing = (float)$word_spacing;
797
+ $char_spacing = (float)$char_spacing;
798
+ $angle = -(float)$angle;
799
+
800
+ $this->_pdf->fit_textline($text, $x, $y, "rotate=$angle wordspacing=$word_spacing charspacing=$char_spacing ");
801
+
802
+ }
803
+
804
+ //........................................................................
805
+
806
+ function javascript($code) {
807
+ if ( defined("DOMPDF_PDFLIB_LICENSE") ) {
808
+ $this->_pdf->create_action("JavaScript", $code);
809
+ }
810
+ }
811
+
812
+ //........................................................................
813
+
814
+ /**
815
+ * Add a named destination (similar to <a name="foo">...</a> in html)
816
+ *
817
+ * @param string $anchorname The name of the named destination
818
+ */
819
+ function add_named_dest($anchorname) {
820
+ $this->_pdf->add_nameddest($anchorname,"");
821
+ }
822
+
823
+ //........................................................................
824
+
825
+ /**
826
+ * Add a link to the pdf
827
+ *
828
+ * @param string $url The url to link to
829
+ * @param float $x The x position of the link
830
+ * @param float $y The y position of the link
831
+ * @param float $width The width of the link
832
+ * @param float $height The height of the link
833
+ */
834
+ function add_link($url, $x, $y, $width, $height) {
835
+
836
+ $y = $this->y($y) - $height;
837
+ if ( strpos($url, '#') === 0 ) {
838
+ // Local link
839
+ $name = substr($url,1);
840
+ if ( $name )
841
+ $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} destname=". substr($url,1) . " linewidth=0");
842
+ } else {
843
+
844
+ list($proto, $host, $path, $file) = explode_url($url);
845
+
846
+ if ( $proto == "" || $proto === "file://" )
847
+ return; // Local links are not allowed
848
+ $url = build_url($proto, $host, $path, $file);
849
+ $url = '{' . rawurldecode($url) . '}';
850
+
851
+ $action = $this->_pdf->create_action("URI", "url=" . $url);
852
+ $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate=$action} linewidth=0");
853
+ }
854
+ }
855
+
856
+ //........................................................................
857
+
858
+ function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0) {
859
+ $fh = $this->_load_font($font);
860
+
861
+ // Determine the additional width due to extra spacing
862
+ $num_spaces = mb_substr_count($text," ");
863
+ $delta = $word_spacing * $num_spaces;
864
+
865
+ if ( $letter_spacing ) {
866
+ $num_chars = mb_strlen($text);
867
+ $delta += ($num_chars - $num_spaces) * $letter_spacing;
868
+ }
869
+
870
+ return $this->_pdf->stringwidth($text, $fh, $size) + $delta;
871
+ }
872
+
873
+ //........................................................................
874
+
875
+ function get_font_height($font, $size) {
876
+
877
+ $fh = $this->_load_font($font);
878
+
879
+ $this->_pdf->setfont($fh, $size);
880
+
881
+ $asc = $this->_pdf->get_value("ascender", $fh);
882
+ $desc = $this->_pdf->get_value("descender", $fh);
883
+
884
+ // $desc is usually < 0,
885
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
886
+ return $size * ($asc - $desc) * $ratio;
887
+ }
888
+
889
+ function get_font_baseline($font, $size) {
890
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
891
+ return $this->get_font_height($font, $size) / $ratio * 1.1;
892
+ }
893
+
894
+ //........................................................................
895
+
896
+ /**
897
+ * Writes text at the specified x and y coordinates on every page
898
+ *
899
+ * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
900
+ * with their current values.
901
+ *
902
+ * See {@link Style::munge_color()} for the format of the color array.
903
+ *
904
+ * @param float $x
905
+ * @param float $y
906
+ * @param string $text the text to write
907
+ * @param string $font the font file to use
908
+ * @param float $size the font size, in points
909
+ * @param array $color
910
+ * @param float $word_space word spacing adjustment
911
+ * @param float $char_space char spacing adjustment
912
+ * @param float $angle angle to write the text at, measured CW starting from the x-axis
913
+ */
914
+ function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
915
+ $_t = "text";
916
+ $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
917
+ }
918
+
919
+ //........................................................................
920
+
921
+ /**
922
+ * Processes a script on every page
923
+ *
924
+ * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
925
+ *
926
+ * This function can be used to add page numbers to all pages
927
+ * after the first one, for example.
928
+ *
929
+ * @param string $code the script code
930
+ * @param string $type the language type for script
931
+ */
932
+ function page_script($code, $type = "text/php") {
933
+ $_t = "script";
934
+ $this->_page_text[] = compact("_t", "code", "type");
935
+ }
936
+
937
+ //........................................................................
938
+
939
+ function new_page() {
940
+
941
+ // Add objects to the current page
942
+ $this->_place_objects();
943
+
944
+ $this->_pdf->suspend_page("");
945
+ $this->_pdf->begin_page_ext($this->_width, $this->_height, "");
946
+ $this->_page_number = ++$this->_page_count;
947
+
948
+ }
949
+
950
+ //........................................................................
951
+
952
+ /**
953
+ * Add text to each page after rendering is complete
954
+ */
955
+ protected function _add_page_text() {
956
+
957
+ if ( !count($this->_page_text) )
958
+ return;
959
+
960
+ $this->_pdf->suspend_page("");
961
+
962
+ for ($p = 1; $p <= $this->_page_count; $p++) {
963
+ $this->_pdf->resume_page("pagenumber=$p");
964
+
965
+ foreach ($this->_page_text as $pt) {
966
+ extract($pt);
967
+
968
+ switch ($_t) {
969
+
970
+ case "text":
971
+ $text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"),
972
+ array($p, $this->_page_count), $text);
973
+ $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
974
+ break;
975
+
976
+ case "script":
977
+ if (!$eval) {
978
+ $eval = new PHP_Evaluator($this);
979
+ }
980
+ $eval->evaluate($code, array('PAGE_NUM' => $p, 'PAGE_COUNT' => $this->_page_count));
981
+ break;
982
+ }
983
+ }
984
+
985
+ $this->_pdf->suspend_page("");
986
+ }
987
+
988
+ $this->_pdf->resume_page("pagenumber=".$this->_page_number);
989
+ }
990
+
991
+ //........................................................................
992
+
993
+ function stream($filename, $options = null) {
994
+
995
+ // Add page text
996
+ $this->_add_page_text();
997
+
998
+ if ( isset($options["compress"]) && $options["compress"] != 1 )
999
+ $this->_pdf->set_value("compress", 0);
1000
+ else
1001
+ $this->_pdf->set_value("compress", 6);
1002
+
1003
+ $this->_close();
1004
+
1005
+ $data = "";
1006
+
1007
+ if ( self::$IN_MEMORY ) {
1008
+ $data = $this->_pdf->get_buffer();
1009
+ //$size = strlen($data);
1010
+ } else {
1011
+ //$size = filesize($this->_file);
1012
+ }
1013
+
1014
+
1015
+ $filename = str_replace(array("\n","'"),"", $filename);
1016
+ $attach = (isset($options["Attachment"]) && $options["Attachment"]) ? "attachment" : "inline";
1017
+
1018
+ header("Cache-Control: private");
1019
+ header("Content-type: application/pdf");
1020
+ header("Content-Disposition: $attach; filename=\"$filename\"");
1021
+
1022
+ //header("Content-length: " . $size);
1023
+
1024
+ if ( self::$IN_MEMORY )
1025
+ echo $data;
1026
+
1027
+ else {
1028
+
1029
+ // Chunked readfile()
1030
+ $chunk = (1 << 21); // 2 MB
1031
+ $fh = fopen($this->_file, "rb");
1032
+ if ( !$fh )
1033
+ throw new DOMPDF_Exception("Unable to load temporary PDF file: " . $this->_file);
1034
+
1035
+ while ( !feof($fh) )
1036
+ echo fread($fh,$chunk);
1037
+ fclose($fh);
1038
+
1039
+ //debugpng
1040
+ if (DEBUGPNG) print '[pdflib stream unlink '.$this->_file.']';
1041
+ if (!DEBUGKEEPTEMP)
1042
+
1043
+ unlink($this->_file);
1044
+ $this->_file = null;
1045
+ unset($this->_file);
1046
+ }
1047
+
1048
+ flush();
1049
+ }
1050
+
1051
+ //........................................................................
1052
+
1053
+ function output($options = null) {
1054
+
1055
+ // Add page text
1056
+ $this->_add_page_text();
1057
+
1058
+ if ( isset($options["compress"]) && $options["compress"] != 1 )
1059
+ $this->_pdf->set_value("compress", 0);
1060
+ else
1061
+ $this->_pdf->set_value("compress", 6);
1062
+
1063
+ $this->_close();
1064
+
1065
+ if ( self::$IN_MEMORY )
1066
+ $data = $this->_pdf->get_buffer();
1067
+
1068
+ else {
1069
+ $data = file_get_contents($this->_file);
1070
+
1071
+ //debugpng
1072
+ if (DEBUGPNG) print '[pdflib output unlink '.$this->_file.']';
1073
+ if (!DEBUGKEEPTEMP)
1074
+
1075
+ unlink($this->_file);
1076
+ $this->_file = null;
1077
+ unset($this->_file);
1078
+ }
1079
+
1080
+ return $data;
1081
+ }
1082
+ }
1083
+
1084
+ // Workaround for idiotic limitation on statics...
1085
+ PDFLib_Adapter::$PAPER_SIZES = CPDF_Adapter::$PAPER_SIZES;
lib/dompdf/include/php_evaluator.cls.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Executes inline PHP code during the rendering process
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class PHP_Evaluator {
16
+
17
+ /**
18
+ * @var Canvas
19
+ */
20
+ protected $_canvas;
21
+
22
+ function __construct(Canvas $canvas) {
23
+ $this->_canvas = $canvas;
24
+ }
25
+
26
+ function evaluate($code, $vars = array()) {
27
+ if ( !$this->_canvas->get_dompdf()->get_option("enable_php") ) {
28
+ return;
29
+ }
30
+
31
+ // Set up some variables for the inline code
32
+ $pdf = $this->_canvas;
33
+ $PAGE_NUM = $pdf->get_page_number();
34
+ $PAGE_COUNT = $pdf->get_page_count();
35
+
36
+ // Override those variables if passed in
37
+ foreach ($vars as $k => $v) {
38
+ $$k = $v;
39
+ }
40
+
41
+ //$code = html_entity_decode($code); // @todo uncomment this when tested
42
+ eval($code);
43
+ }
44
+
45
+ function render(Frame $frame) {
46
+ $this->evaluate($frame->get_node()->nodeValue);
47
+ }
48
+ }
lib/dompdf/include/positioner.cls.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Base Positioner class
11
+ *
12
+ * Defines postioner interface
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ abstract class Positioner {
18
+
19
+ /**
20
+ * @var Frame_Decorator
21
+ */
22
+ protected $_frame;
23
+
24
+ //........................................................................
25
+
26
+ function __construct(Frame_Decorator $frame) {
27
+ $this->_frame = $frame;
28
+ }
29
+
30
+ /**
31
+ * Class destructor
32
+ */
33
+ function __destruct() {
34
+ clear_object($this);
35
+ }
36
+ //........................................................................
37
+
38
+ abstract function position();
39
+
40
+ function move($offset_x, $offset_y, $ignore_self = false) {
41
+ list($x, $y) = $this->_frame->get_position();
42
+
43
+ if ( !$ignore_self ) {
44
+ $this->_frame->set_position($x + $offset_x, $y + $offset_y);
45
+ }
46
+
47
+ foreach($this->_frame->get_children() as $child) {
48
+ $child->move($offset_x, $offset_y);
49
+ }
50
+ }
51
+ }
lib/dompdf/include/renderer.cls.php ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Concrete renderer
11
+ *
12
+ * Instantiates several specific renderers in order to render any given
13
+ * frame.
14
+ *
15
+ * @access private
16
+ * @package dompdf
17
+ */
18
+ class Renderer extends Abstract_Renderer {
19
+
20
+ /**
21
+ * Array of renderers for specific frame types
22
+ *
23
+ * @var Abstract_Renderer[]
24
+ */
25
+ protected $_renderers;
26
+
27
+ /**
28
+ * Cache of the callbacks array
29
+ *
30
+ * @var array
31
+ */
32
+ private $_callbacks;
33
+
34
+ /**
35
+ * Class destructor
36
+ */
37
+ function __destruct() {
38
+ clear_object($this);
39
+ }
40
+
41
+ /**
42
+ * Advance the canvas to the next page
43
+ */
44
+ function new_page() {
45
+ $this->_canvas->new_page();
46
+ }
47
+
48
+ /**
49
+ * Render frames recursively
50
+ *
51
+ * @param Frame $frame the frame to render
52
+ */
53
+ function render(Frame $frame) {
54
+ global $_dompdf_debug;
55
+
56
+ if ( $_dompdf_debug ) {
57
+ echo $frame;
58
+ flush();
59
+ }
60
+
61
+ $style = $frame->get_style();
62
+
63
+ if ( in_array($style->visibility, array("hidden", "collapse")) ) {
64
+ return;
65
+ }
66
+
67
+ $display = $style->display;
68
+
69
+ // Starts the CSS transformation
70
+ if ( $style->transform && is_array($style->transform) ) {
71
+ $this->_canvas->save();
72
+ list($x, $y) = $frame->get_padding_box();
73
+ $origin = $style->transform_origin;
74
+
75
+ foreach($style->transform as $transform) {
76
+ list($function, $values) = $transform;
77
+ if ( $function === "matrix" ) {
78
+ $function = "transform";
79
+ }
80
+
81
+ $values = array_map("floatval", $values);
82
+ $values[] = $x + $style->length_in_pt($origin[0], $style->width);
83
+ $values[] = $y + $style->length_in_pt($origin[1], $style->height);
84
+
85
+ call_user_func_array(array($this->_canvas, $function), $values);
86
+ }
87
+ }
88
+
89
+ switch ($display) {
90
+
91
+ case "block":
92
+ case "list-item":
93
+ case "inline-block":
94
+ case "table":
95
+ case "inline-table":
96
+ $this->_render_frame("block", $frame);
97
+ break;
98
+
99
+ case "inline":
100
+ if ( $frame->is_text_node() )
101
+ $this->_render_frame("text", $frame);
102
+ else
103
+ $this->_render_frame("inline", $frame);
104
+ break;
105
+
106
+ case "table-cell":
107
+ $this->_render_frame("table-cell", $frame);
108
+ break;
109
+
110
+ case "table-row-group":
111
+ case "table-header-group":
112
+ case "table-footer-group":
113
+ $this->_render_frame("table-row-group", $frame);
114
+ break;
115
+
116
+ case "-dompdf-list-bullet":
117
+ $this->_render_frame("list-bullet", $frame);
118
+ break;
119
+
120
+ case "-dompdf-image":
121
+ $this->_render_frame("image", $frame);
122
+ break;
123
+
124
+ case "none":
125
+ $node = $frame->get_node();
126
+
127
+ if ( $node->nodeName === "script" ) {
128
+ if ( $node->getAttribute("type") === "text/php" ||
129
+ $node->getAttribute("language") === "php" ) {
130
+ // Evaluate embedded php scripts
131
+ $this->_render_frame("php", $frame);
132
+ }
133
+
134
+ elseif ( $node->getAttribute("type") === "text/javascript" ||
135
+ $node->getAttribute("language") === "javascript" ) {
136
+ // Insert JavaScript
137
+ $this->_render_frame("javascript", $frame);
138
+ }
139
+ }
140
+
141
+ // Don't render children, so skip to next iter
142
+ return;
143
+
144
+ default:
145
+ break;
146
+
147
+ }
148
+
149
+ // Starts the overflow: hidden box
150
+ if ( $style->overflow === "hidden" ) {
151
+ list($x, $y, $w, $h) = $frame->get_padding_box();
152
+
153
+ // get border radii
154
+ $style = $frame->get_style();
155
+ list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
156
+
157
+ if ( $tl + $tr + $br + $bl > 0 ) {
158
+ $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
159
+ }
160
+ else {
161
+ $this->_canvas->clipping_rectangle($x, $y, $w, $h);
162
+ }
163
+ }
164
+
165
+ $stack = array();
166
+
167
+ foreach ($frame->get_children() as $child) {
168
+ // < 0 : nagative z-index
169
+ // = 0 : no z-index, no stacking context
170
+ // = 1 : stacking context without z-index
171
+ // > 1 : z-index
172
+ $child_style = $child->get_style();
173
+ $child_z_index = $child_style->z_index;
174
+ $z_index = 0;
175
+
176
+ if ( $child_z_index !== "auto" ) {
177
+ $z_index = intval($child_z_index) + 1;
178
+ }
179
+ elseif ( $child_style->float !== "none" || $child->is_positionned()) {
180
+ $z_index = 1;
181
+ }
182
+
183
+ $stack[$z_index][] = $child;
184
+ }
185
+
186
+ ksort($stack);
187
+
188
+ foreach ($stack as $by_index) {
189
+ foreach($by_index as $child) {
190
+ $this->render($child);
191
+ }
192
+ }
193
+
194
+ // Ends the overflow: hidden box
195
+ if ( $style->overflow === "hidden" ) {
196
+ $this->_canvas->clipping_end();
197
+ }
198
+
199
+ if ( $style->transform && is_array($style->transform) ) {
200
+ $this->_canvas->restore();
201
+ }
202
+
203
+ // Check for end frame callback
204
+ $this->_check_callbacks("end_frame", $frame);
205
+ }
206
+
207
+ /**
208
+ * Check for callbacks that need to be performed when a given event
209
+ * gets triggered on a frame
210
+ *
211
+ * @param string $event the type of event
212
+ * @param Frame $frame the frame that event is triggered on
213
+ */
214
+ protected function _check_callbacks($event, $frame) {
215
+ if (!isset($this->_callbacks)) {
216
+ $this->_callbacks = $this->_dompdf->get_callbacks();
217
+ }
218
+
219
+ if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
220
+ $info = array(0 => $this->_canvas, "canvas" => $this->_canvas,
221
+ 1 => $frame, "frame" => $frame);
222
+ $fs = $this->_callbacks[$event];
223
+ foreach ($fs as $f) {
224
+ if (is_callable($f)) {
225
+ if (is_array($f)) {
226
+ $f[0]->$f[1]($info);
227
+ } else {
228
+ $f($info);
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Render a single frame
237
+ *
238
+ * Creates Renderer objects on demand
239
+ *
240
+ * @param string $type type of renderer to use
241
+ * @param Frame $frame the frame to render
242
+ */
243
+ protected function _render_frame($type, $frame) {
244
+
245
+ if ( !isset($this->_renderers[$type]) ) {
246
+
247
+ switch ($type) {
248
+ case "block":
249
+ $this->_renderers[$type] = new Block_Renderer($this->_dompdf);
250
+ break;
251
+
252
+ case "inline":
253
+ $this->_renderers[$type] = new Inline_Renderer($this->_dompdf);
254
+ break;
255
+
256
+ case "text":
257
+ $this->_renderers[$type] = new Text_Renderer($this->_dompdf);
258
+ break;
259
+
260
+ case "image":
261
+ $this->_renderers[$type] = new Image_Renderer($this->_dompdf);
262
+ break;
263
+
264
+ case "table-cell":
265
+ $this->_renderers[$type] = new Table_Cell_Renderer($this->_dompdf);
266
+ break;
267
+
268
+ case "table-row-group":
269
+ $this->_renderers[$type] = new Table_Row_Group_Renderer($this->_dompdf);
270
+ break;
271
+
272
+ case "list-bullet":
273
+ $this->_renderers[$type] = new List_Bullet_Renderer($this->_dompdf);
274
+ break;
275
+
276
+ case "php":
277
+ $this->_renderers[$type] = new PHP_Evaluator($this->_canvas);
278
+ break;
279
+
280
+ case "javascript":
281
+ $this->_renderers[$type] = new Javascript_Embedder($this->_dompdf);
282
+ break;
283
+
284
+ }
285
+ }
286
+
287
+ $this->_renderers[$type]->render($frame);
288
+
289
+ }
290
+ }
lib/dompdf/include/style.cls.php ADDED
@@ -0,0 +1,2435 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * Represents CSS properties.
13
+ *
14
+ * The Style class is responsible for handling and storing CSS properties.
15
+ * It includes methods to resolve colors and lengths, as well as getters &
16
+ * setters for many CSS properites.
17
+ *
18
+ * Actual CSS parsing is performed in the {@link Stylesheet} class.
19
+ *
20
+ * @package dompdf
21
+ */
22
+ class Style {
23
+
24
+ const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*";
25
+ const CSS_INTEGER = "-?\d+";
26
+
27
+ /**
28
+ * Default font size, in points.
29
+ *
30
+ * @var float
31
+ */
32
+ static $default_font_size = 12;
33
+
34
+ /**
35
+ * Default line height, as a fraction of the font size.
36
+ *
37
+ * @var float
38
+ */
39
+ static $default_line_height = 1.2;
40
+
41
+ /**
42
+ * Default "absolute" font sizes relative to the default font-size
43
+ * http://www.w3.org/TR/css3-fonts/#font-size-the-font-size-property
44
+ * @var array<float>
45
+ */
46
+ static $font_size_keywords = array(
47
+ "xx-small" => 0.6, // 3/5
48
+ "x-small" => 0.75, // 3/4
49
+ "small" => 0.889, // 8/9
50
+ "medium" => 1, // 1
51
+ "large" => 1.2, // 6/5
52
+ "x-large" => 1.5, // 3/2
53
+ "xx-large" => 2.0, // 2/1
54
+ );
55
+
56
+ /**
57
+ * List of all inline types. Should really be a constant.
58
+ *
59
+ * @var array
60
+ */
61
+ static $INLINE_TYPES = array("inline");
62
+
63
+ /**
64
+ * List of all block types. Should really be a constant.
65
+ *
66
+ * @var array
67
+ */
68
+ static $BLOCK_TYPES = array("block", "inline-block", "table-cell", "list-item");
69
+
70
+ /**
71
+ * List of all positionned types. Should really be a constant.
72
+ *
73
+ * @var array
74
+ */
75
+ static $POSITIONNED_TYPES = array("relative", "absolute", "fixed");
76
+
77
+ /**
78
+ * List of all table types. Should really be a constant.
79
+ *
80
+ * @var array;
81
+ */
82
+ static $TABLE_TYPES = array("table", "inline-table");
83
+
84
+ /**
85
+ * List of valid border styles. Should also really be a constant.
86
+ *
87
+ * @var array
88
+ */
89
+ static $BORDER_STYLES = array("none", "hidden", "dotted", "dashed", "solid",
90
+ "double", "groove", "ridge", "inset", "outset");
91
+
92
+ /**
93
+ * Default style values.
94
+ *
95
+ * @link http://www.w3.org/TR/CSS21/propidx.html
96
+ *
97
+ * @var array
98
+ */
99
+ static protected $_defaults = null;
100
+
101
+ /**
102
+ * List of inherited properties
103
+ *
104
+ * @link http://www.w3.org/TR/CSS21/propidx.html
105
+ *
106
+ * @var array
107
+ */
108
+ static protected $_inherited = null;
109
+
110
+ /**
111
+ * Caches method_exists result
112
+ *
113
+ * @var array<bool>
114
+ */
115
+ static protected $_methods_cache = array();
116
+
117
+ /**
118
+ * The stylesheet this style belongs to
119
+ *
120
+ * @see Stylesheet
121
+ * @var Stylesheet
122
+ */
123
+ protected $_stylesheet; // stylesheet this style is attached to
124
+
125
+ /**
126
+ * Main array of all CSS properties & values
127
+ *
128
+ * @var array
129
+ */
130
+ protected $_props;
131
+
132
+ /* var instead of protected would allow access outside of class */
133
+ protected $_important_props;
134
+
135
+ /**
136
+ * Cached property values
137
+ *
138
+ * @var array
139
+ */
140
+ protected $_prop_cache;
141
+
142
+ /**
143
+ * Font size of parent element in document tree. Used for relative font
144
+ * size resolution.
145
+ *
146
+ * @var float
147
+ */
148
+ protected $_parent_font_size; // Font size of parent element
149
+
150
+ protected $_font_family;
151
+
152
+ /**
153
+ * @var Frame
154
+ */
155
+ protected $_frame;
156
+
157
+ /**
158
+ * The origin of the style
159
+ *
160
+ * @var int
161
+ */
162
+ protected $_origin = Stylesheet::ORIG_AUTHOR;
163
+
164
+ // private members
165
+ /**
166
+ * True once the font size is resolved absolutely
167
+ *
168
+ * @var bool
169
+ */
170
+ private $__font_size_calculated; // Cache flag
171
+
172
+ /**
173
+ * The computed border radius
174
+ */
175
+ private $_computed_border_radius = null;
176
+
177
+ /**
178
+ * @var bool
179
+ */
180
+ public $_has_border_radius = false;
181
+
182
+ /**
183
+ * Class constructor
184
+ *
185
+ * @param Stylesheet $stylesheet the stylesheet this Style is associated with.
186
+ * @param int $origin
187
+ */
188
+ function __construct(Stylesheet $stylesheet, $origin = Stylesheet::ORIG_AUTHOR) {
189
+ $this->_props = array();
190
+ $this->_important_props = array();
191
+ $this->_stylesheet = $stylesheet;
192
+ $this->_origin = $origin;
193
+ $this->_parent_font_size = null;
194
+ $this->__font_size_calculated = false;
195
+
196
+ if ( !isset(self::$_defaults) ) {
197
+
198
+ // Shorthand
199
+ $d =& self::$_defaults;
200
+
201
+ // All CSS 2.1 properties, and their default values
202
+ $d["azimuth"] = "center";
203
+ $d["background_attachment"] = "scroll";
204
+ $d["background_color"] = "transparent";
205
+ $d["background_image"] = "none";
206
+ $d["background_image_resolution"] = "normal";
207
+ $d["_dompdf_background_image_resolution"] = $d["background_image_resolution"];
208
+ $d["background_position"] = "0% 0%";
209
+ $d["background_repeat"] = "repeat";
210
+ $d["background"] = "";
211
+ $d["border_collapse"] = "separate";
212
+ $d["border_color"] = "";
213
+ $d["border_spacing"] = "0";
214
+ $d["border_style"] = "";
215
+ $d["border_top"] = "";
216
+ $d["border_right"] = "";
217
+ $d["border_bottom"] = "";
218
+ $d["border_left"] = "";
219
+ $d["border_top_color"] = "";
220
+ $d["border_right_color"] = "";
221
+ $d["border_bottom_color"] = "";
222
+ $d["border_left_color"] = "";
223
+ $d["border_top_style"] = "none";
224
+ $d["border_right_style"] = "none";
225
+ $d["border_bottom_style"] = "none";
226
+ $d["border_left_style"] = "none";
227
+ $d["border_top_width"] = "medium";
228
+ $d["border_right_width"] = "medium";
229
+ $d["border_bottom_width"] = "medium";
230
+ $d["border_left_width"] = "medium";
231
+ $d["border_width"] = "medium";
232
+ $d["border_bottom_left_radius"] = "";
233
+ $d["border_bottom_right_radius"] = "";
234
+ $d["border_top_left_radius"] = "";
235
+ $d["border_top_right_radius"] = "";
236
+ $d["border_radius"] = "";
237
+ $d["border"] = "";
238
+ $d["bottom"] = "auto";
239
+ $d["caption_side"] = "top";
240
+ $d["clear"] = "none";
241
+ $d["clip"] = "auto";
242
+ $d["color"] = "#000000";
243
+ $d["content"] = "normal";
244
+ $d["counter_increment"] = "none";
245
+ $d["counter_reset"] = "none";
246
+ $d["cue_after"] = "none";
247
+ $d["cue_before"] = "none";
248
+ $d["cue"] = "";
249
+ $d["cursor"] = "auto";
250
+ $d["direction"] = "ltr";
251
+ $d["display"] = "inline";
252
+ $d["elevation"] = "level";
253
+ $d["empty_cells"] = "show";
254
+ $d["float"] = "none";
255
+ $d["font_family"] = $stylesheet->get_dompdf()->get_option("default_font");
256
+ $d["font_size"] = "medium";
257
+ $d["font_style"] = "normal";
258
+ $d["font_variant"] = "normal";
259
+ $d["font_weight"] = "normal";
260
+ $d["font"] = "";
261
+ $d["height"] = "auto";
262
+ $d["image_resolution"] = "normal";
263
+ $d["_dompdf_image_resolution"] = $d["image_resolution"];
264
+ $d["_dompdf_keep"] = "";
265
+ $d["left"] = "auto";
266
+ $d["letter_spacing"] = "normal";
267
+ $d["line_height"] = "normal";
268
+ $d["list_style_image"] = "none";
269
+ $d["list_style_position"] = "outside";
270
+ $d["list_style_type"] = "disc";
271
+ $d["list_style"] = "";
272
+ $d["margin_right"] = "0";
273
+ $d["margin_left"] = "0";
274
+ $d["margin_top"] = "0";
275
+ $d["margin_bottom"] = "0";
276
+ $d["margin"] = "";
277
+ $d["max_height"] = "none";
278
+ $d["max_width"] = "none";
279
+ $d["min_height"] = "0";
280
+ $d["min_width"] = "0";
281
+ $d["opacity"] = "1.0"; // CSS3
282
+ $d["orphans"] = "2";
283
+ $d["outline_color"] = ""; // "invert" special color is not supported
284
+ $d["outline_style"] = "none";
285
+ $d["outline_width"] = "medium";
286
+ $d["outline"] = "";
287
+ $d["overflow"] = "visible";
288
+ $d["padding_top"] = "0";
289
+ $d["padding_right"] = "0";
290
+ $d["padding_bottom"] = "0";
291
+ $d["padding_left"] = "0";
292
+ $d["padding"] = "";
293
+ $d["page_break_after"] = "auto";
294
+ $d["page_break_before"] = "auto";
295
+ $d["page_break_inside"] = "auto";
296
+ $d["pause_after"] = "0";
297
+ $d["pause_before"] = "0";
298
+ $d["pause"] = "";
299
+ $d["pitch_range"] = "50";
300
+ $d["pitch"] = "medium";
301
+ $d["play_during"] = "auto";
302
+ $d["position"] = "static";
303
+ $d["quotes"] = "";
304
+ $d["richness"] = "50";
305
+ $d["right"] = "auto";
306
+ $d["size"] = "auto"; // @page
307
+ $d["speak_header"] = "once";
308
+ $d["speak_numeral"] = "continuous";
309
+ $d["speak_punctuation"] = "none";
310
+ $d["speak"] = "normal";
311
+ $d["speech_rate"] = "medium";
312
+ $d["stress"] = "50";
313
+ $d["table_layout"] = "auto";
314
+ $d["text_align"] = "left";
315
+ $d["text_decoration"] = "none";
316
+ $d["text_indent"] = "0";
317
+ $d["text_transform"] = "none";
318
+ $d["top"] = "auto";
319
+ $d["transform"] = "none"; // CSS3
320
+ $d["transform_origin"] = "50% 50%"; // CSS3
321
+ $d["_webkit_transform"] = $d["transform"]; // CSS3
322
+ $d["_webkit_transform_origin"] = $d["transform_origin"]; // CSS3
323
+ $d["unicode_bidi"] = "normal";
324
+ $d["vertical_align"] = "baseline";
325
+ $d["visibility"] = "visible";
326
+ $d["voice_family"] = "";
327
+ $d["volume"] = "medium";
328
+ $d["white_space"] = "normal";
329
+ $d["word_wrap"] = "normal";
330
+ $d["widows"] = "2";
331
+ $d["width"] = "auto";
332
+ $d["word_spacing"] = "normal";
333
+ $d["z_index"] = "auto";
334
+
335
+ // for @font-face
336
+ $d["src"] = "";
337
+ $d["unicode_range"] = "";
338
+
339
+ // Properties that inherit by default
340
+ self::$_inherited = array(
341
+ "azimuth",
342
+ "background_image_resolution",
343
+ "border_collapse",
344
+ "border_spacing",
345
+ "caption_side",
346
+ "color",
347
+ "cursor",
348
+ "direction",
349
+ "elevation",
350
+ "empty_cells",
351
+ "font_family",
352
+ "font_size",
353
+ "font_style",
354
+ "font_variant",
355
+ "font_weight",
356
+ "font",
357
+ "image_resolution",
358
+ "letter_spacing",
359
+ "line_height",
360
+ "list_style_image",
361
+ "list_style_position",
362
+ "list_style_type",
363
+ "list_style",
364
+ "orphans",
365
+ "page_break_inside",
366
+ "pitch_range",
367
+ "pitch",
368
+ "quotes",
369
+ "richness",
370
+ "speak_header",
371
+ "speak_numeral",
372
+ "speak_punctuation",
373
+ "speak",
374
+ "speech_rate",
375
+ "stress",
376
+ "text_align",
377
+ "text_indent",
378
+ "text_transform",
379
+ "visibility",
380
+ "voice_family",
381
+ "volume",
382
+ "white_space",
383
+ "word_wrap",
384
+ "widows",
385
+ "word_spacing",
386
+ );
387
+ }
388
+ }
389
+
390
+ /**
391
+ * "Destructor": forcibly free all references held by this object
392
+ */
393
+ function dispose() {
394
+ clear_object($this);
395
+ }
396
+
397
+ function set_frame(Frame $frame) {
398
+ $this->_frame = $frame;
399
+ }
400
+
401
+ function get_frame() {
402
+ return $this->_frame;
403
+ }
404
+
405
+ function set_origin($origin) {
406
+ $this->_origin = $origin;
407
+ }
408
+
409
+ function get_origin() {
410
+ return $this->_origin;
411
+ }
412
+
413
+ /**
414
+ * returns the {@link Stylesheet} this Style is associated with.
415
+ *
416
+ * @return Stylesheet
417
+ */
418
+ function get_stylesheet() { return $this->_stylesheet; }
419
+
420
+ /**
421
+ * Converts any CSS length value into an absolute length in points.
422
+ *
423
+ * length_in_pt() takes a single length (e.g. '1em') or an array of
424
+ * lengths and returns an absolute length. If an array is passed, then
425
+ * the return value is the sum of all elements.
426
+ *
427
+ * If a reference size is not provided, the default font size is used
428
+ * ({@link Style::$default_font_size}).
429
+ *
430
+ * @param float|array $length the length or array of lengths to resolve
431
+ * @param float $ref_size an absolute reference size to resolve percentage lengths
432
+ * @return float
433
+ */
434
+ function length_in_pt($length, $ref_size = null) {
435
+ static $cache = array();
436
+
437
+ if ( !is_array($length) ) {
438
+ $length = array($length);
439
+ }
440
+
441
+ if ( !isset($ref_size) ) {
442
+ $ref_size = self::$default_font_size;
443
+ }
444
+
445
+ $key = implode("@", $length)."/$ref_size";
446
+
447
+ if ( isset($cache[$key]) ) {
448
+ return $cache[$key];
449
+ }
450
+
451
+ $ret = 0;
452
+ foreach ($length as $l) {
453
+
454
+ if ( $l === "auto" ) {
455
+ return "auto";
456
+ }
457
+
458
+ if ( $l === "none" ) {
459
+ return "none";
460
+ }
461
+
462
+ // Assume numeric values are already in points
463
+ if ( is_numeric($l) ) {
464
+ $ret += $l;
465
+ continue;
466
+ }
467
+
468
+ if ( $l === "normal" ) {
469
+ $ret += $ref_size;
470
+ continue;
471
+ }
472
+
473
+ // Border lengths
474
+ if ( $l === "thin" ) {
475
+ $ret += 0.5;
476
+ continue;
477
+ }
478
+
479
+ if ( $l === "medium" ) {
480
+ $ret += 1.5;
481
+ continue;
482
+ }
483
+
484
+ if ( $l === "thick" ) {
485
+ $ret += 2.5;
486
+ continue;
487
+ }
488
+
489
+ if ( ($i = mb_strpos($l, "px")) !== false ) {
490
+ $dpi = $this->_stylesheet->get_dompdf()->get_option("dpi");
491
+ $ret += ( mb_substr($l, 0, $i) * 72 ) / $dpi;
492
+ continue;
493
+ }
494
+
495
+ if ( ($i = mb_strpos($l, "pt")) !== false ) {
496
+ $ret += (float)mb_substr($l, 0, $i);
497
+ continue;
498
+ }
499
+
500
+ if ( ($i = mb_strpos($l, "%")) !== false ) {
501
+ $ret += (float)mb_substr($l, 0, $i)/100 * $ref_size;
502
+ continue;
503
+ }
504
+
505
+ if ( ($i = mb_strpos($l, "rem")) !== false ) {
506
+ $ret += (float)mb_substr($l, 0, $i) * $this->_stylesheet->get_dompdf()->get_tree()->get_root()->get_style()->font_size;
507
+ continue;
508
+ }
509
+
510
+ if ( ($i = mb_strpos($l, "em")) !== false ) {
511
+ $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size");
512
+ continue;
513
+ }
514
+
515
+ if ( ($i = mb_strpos($l, "cm")) !== false ) {
516
+ $ret += mb_substr($l, 0, $i) * 72 / 2.54;
517
+ continue;
518
+ }
519
+
520
+ if ( ($i = mb_strpos($l, "mm")) !== false ) {
521
+ $ret += mb_substr($l, 0, $i) * 72 / 25.4;
522
+ continue;
523
+ }
524
+
525
+ // FIXME: em:ex ratio?
526
+ if ( ($i = mb_strpos($l, "ex")) !== false ) {
527
+ $ret += mb_substr($l, 0, $i) * $this->__get("font_size") / 2;
528
+ continue;
529
+ }
530
+
531
+ if ( ($i = mb_strpos($l, "in")) !== false ) {
532
+ $ret += (float)mb_substr($l, 0, $i) * 72;
533
+ continue;
534
+ }
535
+
536
+ if ( ($i = mb_strpos($l, "pc")) !== false ) {
537
+ $ret += (float)mb_substr($l, 0, $i) * 12;
538
+ continue;
539
+ }
540
+
541
+ // Bogus value
542
+ $ret += $ref_size;
543
+ }
544
+
545
+ return $cache[$key] = $ret;
546
+ }
547
+
548
+
549
+ /**
550
+ * Set inherited properties in this style using values in $parent
551
+ *
552
+ * @param Style $parent
553
+ *
554
+ * @return Style
555
+ */
556
+ function inherit(Style $parent) {
557
+
558
+ // Set parent font size
559
+ $this->_parent_font_size = $parent->get_font_size();
560
+
561
+ foreach (self::$_inherited as $prop) {
562
+ //inherit the !important property also.
563
+ //if local property is also !important, don't inherit.
564
+ if ( isset($parent->_props[$prop]) &&
565
+ ( !isset($this->_props[$prop]) ||
566
+ ( isset($parent->_important_props[$prop]) && !isset($this->_important_props[$prop]) )
567
+ )
568
+ ) {
569
+ if ( isset($parent->_important_props[$prop]) ) {
570
+ $this->_important_props[$prop] = true;
571
+ }
572
+ //see __set and __get, on all assignments clear cache!
573
+ $this->_prop_cache[$prop] = null;
574
+ $this->_props[$prop] = $parent->_props[$prop];
575
+ }
576
+ }
577
+
578
+ foreach ($this->_props as $prop => $value) {
579
+ if ( $value === "inherit" ) {
580
+ if ( isset($parent->_important_props[$prop]) ) {
581
+ $this->_important_props[$prop] = true;
582
+ }
583
+ //do not assign direct, but
584
+ //implicite assignment through __set, redirect to specialized, get value with __get
585
+ //This is for computing defaults if the parent setting is also missing.
586
+ //Therefore do not directly assign the value without __set
587
+ //set _important_props before that to be able to propagate.
588
+ //see __set and __get, on all assignments clear cache!
589
+ //$this->_prop_cache[$prop] = null;
590
+ //$this->_props[$prop] = $parent->_props[$prop];
591
+ //props_set for more obvious explicite assignment not implemented, because
592
+ //too many implicite uses.
593
+ // $this->props_set($prop, $parent->$prop);
594
+ $this->__set($prop, $parent->__get($prop));
595
+ }
596
+ }
597
+
598
+ return $this;
599
+ }
600
+
601
+ /**
602
+ * Override properties in this style with those in $style
603
+ *
604
+ * @param Style $style
605
+ */
606
+ function merge(Style $style) {
607
+ //treat the !important attribute
608
+ //if old rule has !important attribute, override with new rule only if
609
+ //the new rule is also !important
610
+ foreach($style->_props as $prop => $val ) {
611
+ if (isset($style->_important_props[$prop])) {
612
+ $this->_important_props[$prop] = true;
613
+ //see __set and __get, on all assignments clear cache!
614
+ $this->_prop_cache[$prop] = null;
615
+ $this->_props[$prop] = $val;
616
+ }
617
+ else if ( !isset($this->_important_props[$prop]) ) {
618
+ //see __set and __get, on all assignments clear cache!
619
+ $this->_prop_cache[$prop] = null;
620
+ $this->_props[$prop] = $val;
621
+ }
622
+ }
623
+
624
+ if ( isset($style->_props["font_size"]) ) {
625
+ $this->__font_size_calculated = false;
626
+ }
627
+ }
628
+
629
+ /**
630
+ * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "hex"=>"#rrggbb")
631
+ * based on the provided CSS color value.
632
+ *
633
+ * @param string $color
634
+ * @return array
635
+ */
636
+ function munge_color($color) {
637
+ return CSS_Color::parse($color);
638
+ }
639
+
640
+ /* direct access to _important_props array from outside would work only when declared as
641
+ * 'var $_important_props;' instead of 'protected $_important_props;'
642
+ * Don't call _set/__get on missing attribute. Therefore need a special access.
643
+ * Assume that __set will be also called when this is called, so do not check validity again.
644
+ * Only created, if !important exists -> always set true.
645
+ */
646
+ function important_set($prop) {
647
+ $prop = str_replace("-", "_", $prop);
648
+ $this->_important_props[$prop] = true;
649
+ }
650
+
651
+ function important_get($prop) {
652
+ return isset($this->_important_props[$prop]);
653
+ }
654
+
655
+ /**
656
+ * PHP5 overloaded setter
657
+ *
658
+ * This function along with {@link Style::__get()} permit a user of the
659
+ * Style class to access any (CSS) property using the following syntax:
660
+ * <code>
661
+ * Style->margin_top = "1em";
662
+ * echo (Style->margin_top);
663
+ * </code>
664
+ *
665
+ * __set() automatically calls the provided set function, if one exists,
666
+ * otherwise it sets the property directly. Typically, __set() is not
667
+ * called directly from outside of this class.
668
+ *
669
+ * On each modification clear cache to return accurate setting.
670
+ * Also affects direct settings not using __set
671
+ * For easier finding all assignments, attempted to allowing only explicite assignment:
672
+ * Very many uses, e.g. frame_reflower.cls.php -> for now leave as it is
673
+ * function __set($prop, $val) {
674
+ * throw new DOMPDF_Exception("Implicite replacement of assignment by __set. Not good.");
675
+ * }
676
+ * function props_set($prop, $val) { ... }
677
+ *
678
+ * @param string $prop the property to set
679
+ * @param mixed $val the value of the property
680
+ *
681
+ */
682
+ function __set($prop, $val) {
683
+ $prop = str_replace("-", "_", $prop);
684
+ $this->_prop_cache[$prop] = null;
685
+
686
+ if ( !isset(self::$_defaults[$prop]) ) {
687
+ global $_dompdf_warnings;
688
+ $_dompdf_warnings[] = "'$prop' is not a valid CSS2 property.";
689
+ return;
690
+ }
691
+
692
+ if ( $prop !== "content" && is_string($val) && strlen($val) > 5 && mb_strpos($val, "url") === false ) {
693
+ $val = mb_strtolower(trim(str_replace(array("\n", "\t"), array(" "), $val)));
694
+ $val = preg_replace("/([0-9]+) (pt|px|pc|em|ex|in|cm|mm|%)/S", "\\1\\2", $val);
695
+ }
696
+
697
+ $method = "set_$prop";
698
+
699
+ if ( !isset(self::$_methods_cache[$method]) ) {
700
+ self::$_methods_cache[$method] = method_exists($this, $method);
701
+ }
702
+
703
+ if ( self::$_methods_cache[$method] ) {
704
+ $this->$method($val);
705
+ }
706
+ else {
707
+ $this->_props[$prop] = $val;
708
+ }
709
+ }
710
+
711
+ /**
712
+ * PHP5 overloaded getter
713
+ * Along with {@link Style::__set()} __get() provides access to all CSS
714
+ * properties directly. Typically __get() is not called directly outside
715
+ * of this class.
716
+ * On each modification clear cache to return accurate setting.
717
+ * Also affects direct settings not using __set
718
+ *
719
+ * @param string $prop
720
+ *
721
+ * @throws DOMPDF_Exception
722
+ * @return mixed
723
+ */
724
+ function __get($prop) {
725
+ if ( !isset(self::$_defaults[$prop]) ) {
726
+ throw new DOMPDF_Exception("'$prop' is not a valid CSS2 property.");
727
+ }
728
+
729
+ if ( isset($this->_prop_cache[$prop]) && $this->_prop_cache[$prop] != null ) {
730
+ return $this->_prop_cache[$prop];
731
+ }
732
+
733
+ $method = "get_$prop";
734
+
735
+ // Fall back on defaults if property is not set
736
+ if ( !isset($this->_props[$prop]) ) {
737
+ $this->_props[$prop] = self::$_defaults[$prop];
738
+ }
739
+
740
+ if ( !isset(self::$_methods_cache[$method]) ) {
741
+ self::$_methods_cache[$method] = method_exists($this, $method);
742
+ }
743
+
744
+ if ( self::$_methods_cache[$method] ) {
745
+ return $this->_prop_cache[$prop] = $this->$method();
746
+ }
747
+
748
+ return $this->_prop_cache[$prop] = $this->_props[$prop];
749
+ }
750
+
751
+ function get_font_family_raw(){
752
+ return trim($this->_props["font_family"], " \t\n\r\x0B\"'");
753
+ }
754
+
755
+ /**
756
+ * Getter for the 'font-family' CSS property.
757
+ * Uses the {@link Font_Metrics} class to resolve the font family into an
758
+ * actual font file.
759
+ *
760
+ * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family
761
+ * @throws DOMPDF_Exception
762
+ *
763
+ * @return string
764
+ */
765
+ function get_font_family() {
766
+ if ( isset($this->_font_family) ) {
767
+ return $this->_font_family;
768
+ }
769
+
770
+ $DEBUGCSS=DEBUGCSS; //=DEBUGCSS; Allow override of global setting for ad hoc debug
771
+
772
+ // Select the appropriate font. First determine the subtype, then check
773
+ // the specified font-families for a candidate.
774
+
775
+ // Resolve font-weight
776
+ $weight = $this->__get("font_weight");
777
+
778
+ if ( is_numeric($weight) ) {
779
+ if ( $weight < 600 ) {
780
+ $weight = "normal";
781
+ }
782
+ else {
783
+ $weight = "bold";
784
+ }
785
+ }
786
+ else if ( $weight === "bold" || $weight === "bolder" ) {
787
+ $weight = "bold";
788
+ }
789
+ else {
790
+ $weight = "normal";
791
+ }
792
+
793
+ // Resolve font-style
794
+ $font_style = $this->__get("font_style");
795
+
796
+ if ( $weight === "bold" && ($font_style === "italic" || $font_style === "oblique") ) {
797
+ $subtype = "bold_italic";
798
+ }
799
+ else if ( $weight === "bold" && $font_style !== "italic" && $font_style !== "oblique" ) {
800
+ $subtype = "bold";
801
+ }
802
+ else if ( $weight !== "bold" && ($font_style === "italic" || $font_style === "oblique") ) {
803
+ $subtype = "italic";
804
+ }
805
+ else {
806
+ $subtype = "normal";
807
+ }
808
+
809
+ // Resolve the font family
810
+ if ( $DEBUGCSS ) {
811
+ print "<pre>[get_font_family:";
812
+ print '('.$this->_props["font_family"].'.'.$font_style.'.'.$this->__get("font_weight").'.'.$weight.'.'.$subtype.')';
813
+ }
814
+
815
+ $families = preg_split("/\s*,\s*/", $this->_props["font_family"]);
816
+
817
+ $font = null;
818
+ foreach($families as $family) {
819
+ //remove leading and trailing string delimiters, e.g. on font names with spaces;
820
+ //remove leading and trailing whitespace
821
+ $family = trim($family, " \t\n\r\x0B\"'");
822
+ if ( $DEBUGCSS ) {
823
+ print '('.$family.')';
824
+ }
825
+ $font = Font_Metrics::get_font($family, $subtype);
826
+
827
+ if ( $font ) {
828
+ if ($DEBUGCSS) print '('.$font.")get_font_family]\n</pre>";
829
+ return $this->_font_family = $font;
830
+ }
831
+ }
832
+
833
+ $family = null;
834
+ if ( $DEBUGCSS ) {
835
+ print '(default)';
836
+ }
837
+ $font = Font_Metrics::get_font($family, $subtype);
838
+
839
+ if ( $font ) {
840
+ if ( $DEBUGCSS ) print '('.$font.")get_font_family]\n</pre>";
841
+ return$this->_font_family = $font;
842
+ }
843
+
844
+ throw new DOMPDF_Exception("Unable to find a suitable font replacement for: '" . $this->_props["font_family"] ."'");
845
+
846
+ }
847
+
848
+ /**
849
+ * Returns the resolved font size, in points
850
+ *
851
+ * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
852
+ * @return float
853
+ */
854
+ function get_font_size() {
855
+
856
+ if ( $this->__font_size_calculated ) {
857
+ return $this->_props["font_size"];
858
+ }
859
+
860
+ if ( !isset($this->_props["font_size"]) ) {
861
+ $fs = self::$_defaults["font_size"];
862
+ }
863
+ else {
864
+ $fs = $this->_props["font_size"];
865
+ }
866
+
867
+ if ( !isset($this->_parent_font_size) ) {
868
+ $this->_parent_font_size = self::$default_font_size;
869
+ }
870
+
871
+ switch ($fs) {
872
+ case "xx-small":
873
+ case "x-small":
874
+ case "small":
875
+ case "medium":
876
+ case "large":
877
+ case "x-large":
878
+ case "xx-large":
879
+ $fs = self::$default_font_size * self::$font_size_keywords[$fs];
880
+ break;
881
+
882
+ case "smaller":
883
+ $fs = 8/9 * $this->_parent_font_size;
884
+ break;
885
+
886
+ case "larger":
887
+ $fs = 6/5 * $this->_parent_font_size;
888
+ break;
889
+
890
+ default:
891
+ break;
892
+ }
893
+
894
+ // Ensure relative sizes resolve to something
895
+ if ( ($i = mb_strpos($fs, "em")) !== false ) {
896
+ $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size;
897
+ }
898
+ else if ( ($i = mb_strpos($fs, "ex")) !== false ) {
899
+ $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size;
900
+ }
901
+ else {
902
+ $fs = $this->length_in_pt($fs);
903
+ }
904
+
905
+ //see __set and __get, on all assignments clear cache!
906
+ $this->_prop_cache["font_size"] = null;
907
+ $this->_props["font_size"] = $fs;
908
+ $this->__font_size_calculated = true;
909
+ return $this->_props["font_size"];
910
+
911
+ }
912
+
913
+ /**
914
+ * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
915
+ * @return float
916
+ */
917
+ function get_word_spacing() {
918
+ if ( $this->_props["word_spacing"] === "normal" ) {
919
+ return 0;
920
+ }
921
+
922
+ return $this->_props["word_spacing"];
923
+ }
924
+
925
+ /**
926
+ * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing
927
+ * @return float
928
+ */
929
+ function get_letter_spacing() {
930
+ if ( $this->_props["letter_spacing"] === "normal" ) {
931
+ return 0;
932
+ }
933
+
934
+ return $this->_props["letter_spacing"];
935
+ }
936
+
937
+ /**
938
+ * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
939
+ * @return float
940
+ */
941
+ function get_line_height() {
942
+ $line_height = $this->_props["line_height"];
943
+
944
+ if ( $line_height === "normal" ) {
945
+ return self::$default_line_height * $this->get_font_size();
946
+ }
947
+
948
+ if ( is_numeric($line_height) ) {
949
+ return $this->length_in_pt( $line_height . "em", $this->get_font_size());
950
+ }
951
+
952
+ return $this->length_in_pt( $line_height, $this->_parent_font_size );
953
+ }
954
+
955
+ /**
956
+ * Returns the color as an array
957
+ *
958
+ * The array has the following format:
959
+ * <code>array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb")</code>
960
+ *
961
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
962
+ * @return array
963
+ */
964
+ function get_color() {
965
+ return $this->munge_color( $this->_props["color"] );
966
+ }
967
+
968
+ /**
969
+ * Returns the background color as an array
970
+ *
971
+ * The returned array has the same format as {@link Style::get_color()}
972
+ *
973
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
974
+ * @return array
975
+ */
976
+ function get_background_color() {
977
+ return $this->munge_color( $this->_props["background_color"] );
978
+ }
979
+
980
+ /**
981
+ * Returns the background position as an array
982
+ *
983
+ * The returned array has the following format:
984
+ * <code>array(x,y, "x" => x, "y" => y)</code>
985
+ *
986
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
987
+ * @return array
988
+ */
989
+ function get_background_position() {
990
+ $tmp = explode(" ", $this->_props["background_position"]);
991
+
992
+ switch ($tmp[0]) {
993
+ case "left":
994
+ $x = "0%";
995
+ break;
996
+
997
+ case "right":
998
+ $x = "100%";
999
+ break;
1000
+
1001
+ case "top":
1002
+ $y = "0%";
1003
+ break;
1004
+
1005
+ case "bottom":
1006
+ $y = "100%";
1007
+ break;
1008
+
1009
+ case "center":
1010
+ $x = "50%";
1011
+ $y = "50%";
1012
+ break;
1013
+
1014
+ default:
1015
+ $x = $tmp[0];
1016
+ break;
1017
+ }
1018
+
1019
+ if ( isset($tmp[1]) ) {
1020
+
1021
+ switch ($tmp[1]) {
1022
+ case "left":
1023
+ $x = "0%";
1024
+ break;
1025
+
1026
+ case "right":
1027
+ $x = "100%";
1028
+ break;
1029
+
1030
+ case "top":
1031
+ $y = "0%";
1032
+ break;
1033
+
1034
+ case "bottom":
1035
+ $y = "100%";
1036
+ break;
1037
+
1038
+ case "center":
1039
+ if ( $tmp[0] === "left" || $tmp[0] === "right" || $tmp[0] === "center" ) {
1040
+ $y = "50%";
1041
+ }
1042
+ else {
1043
+ $x = "50%";
1044
+ }
1045
+ break;
1046
+
1047
+ default:
1048
+ $y = $tmp[1];
1049
+ break;
1050
+ }
1051
+
1052
+ }
1053
+ else {
1054
+ $y = "50%";
1055
+ }
1056
+
1057
+ if ( !isset($x) ) {
1058
+ $x = "0%";
1059
+ }
1060
+
1061
+ if ( !isset($y) ) {
1062
+ $y = "0%";
1063
+ }
1064
+
1065
+ return array(
1066
+ 0 => $x, "x" => $x,
1067
+ 1 => $y, "y" => $y,
1068
+ );
1069
+ }
1070
+
1071
+
1072
+ /**
1073
+ * Returns the background as it is currently stored
1074
+ *
1075
+ * (currently anyway only for completeness.
1076
+ * not used for further processing)
1077
+ *
1078
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
1079
+ * @return string
1080
+ */
1081
+ function get_background_attachment() {
1082
+ return $this->_props["background_attachment"];
1083
+ }
1084
+
1085
+
1086
+ /**
1087
+ * Returns the background_repeat as it is currently stored
1088
+ *
1089
+ * (currently anyway only for completeness.
1090
+ * not used for further processing)
1091
+ *
1092
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
1093
+ * @return string
1094
+ */
1095
+ function get_background_repeat() {
1096
+ return $this->_props["background_repeat"];
1097
+ }
1098
+
1099
+
1100
+ /**
1101
+ * Returns the background as it is currently stored
1102
+ *
1103
+ * (currently anyway only for completeness.
1104
+ * not used for further processing, but the individual get_background_xxx)
1105
+ *
1106
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
1107
+ * @return string
1108
+ */
1109
+ function get_background() {
1110
+ return $this->_props["background"];
1111
+ }
1112
+
1113
+
1114
+ /**#@+
1115
+ * Returns the border color as an array
1116
+ *
1117
+ * See {@link Style::get_color()}
1118
+ *
1119
+ * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
1120
+ * @return array
1121
+ */
1122
+ function get_border_top_color() {
1123
+ if ( $this->_props["border_top_color"] === "" ) {
1124
+ //see __set and __get, on all assignments clear cache!
1125
+ $this->_prop_cache["border_top_color"] = null;
1126
+ $this->_props["border_top_color"] = $this->__get("color");
1127
+ }
1128
+
1129
+ return $this->munge_color($this->_props["border_top_color"]);
1130
+ }
1131
+
1132
+ function get_border_right_color() {
1133
+ if ( $this->_props["border_right_color"] === "" ) {
1134
+ //see __set and __get, on all assignments clear cache!
1135
+ $this->_prop_cache["border_right_color"] = null;
1136
+ $this->_props["border_right_color"] = $this->__get("color");
1137
+ }
1138
+
1139
+ return $this->munge_color($this->_props["border_right_color"]);
1140
+ }
1141
+
1142
+ function get_border_bottom_color() {
1143
+ if ( $this->_props["border_bottom_color"] === "" ) {
1144
+ //see __set and __get, on all assignments clear cache!
1145
+ $this->_prop_cache["border_bottom_color"] = null;
1146
+ $this->_props["border_bottom_color"] = $this->__get("color");
1147
+ }
1148
+
1149
+ return $this->munge_color($this->_props["border_bottom_color"]);
1150
+ }
1151
+
1152
+ function get_border_left_color() {
1153
+ if ( $this->_props["border_left_color"] === "" ) {
1154
+ //see __set and __get, on all assignments clear cache!
1155
+ $this->_prop_cache["border_left_color"] = null;
1156
+ $this->_props["border_left_color"] = $this->__get("color");
1157
+ }
1158
+
1159
+ return $this->munge_color($this->_props["border_left_color"]);
1160
+ }
1161
+
1162
+ /**#@-*/
1163
+
1164
+ /**#@+
1165
+ * Returns the border width, as it is currently stored
1166
+ *
1167
+ * @link http://www.w3.org/TR/CSS21/box.html#border-width-properties
1168
+ * @return float|string
1169
+ */
1170
+ function get_border_top_width() {
1171
+ $style = $this->__get("border_top_style");
1172
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_top_width"]) : 0;
1173
+ }
1174
+
1175
+ function get_border_right_width() {
1176
+ $style = $this->__get("border_right_style");
1177
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_right_width"]) : 0;
1178
+ }
1179
+
1180
+ function get_border_bottom_width() {
1181
+ $style = $this->__get("border_bottom_style");
1182
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_bottom_width"]) : 0;
1183
+ }
1184
+
1185
+ function get_border_left_width() {
1186
+ $style = $this->__get("border_left_style");
1187
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_left_width"]) : 0;
1188
+ }
1189
+ /**#@-*/
1190
+
1191
+ /**
1192
+ * Return an array of all border properties.
1193
+ *
1194
+ * The returned array has the following structure:
1195
+ * <code>
1196
+ * array("top" => array("width" => [border-width],
1197
+ * "style" => [border-style],
1198
+ * "color" => [border-color (array)]),
1199
+ * "bottom" ... )
1200
+ * </code>
1201
+ *
1202
+ * @return array
1203
+ */
1204
+ function get_border_properties() {
1205
+ return array(
1206
+ "top" => array(
1207
+ "width" => $this->__get("border_top_width"),
1208
+ "style" => $this->__get("border_top_style"),
1209
+ "color" => $this->__get("border_top_color"),
1210
+ ),
1211
+ "bottom" => array(
1212
+ "width" => $this->__get("border_bottom_width"),
1213
+ "style" => $this->__get("border_bottom_style"),
1214
+ "color" => $this->__get("border_bottom_color"),
1215
+ ),
1216
+ "right" => array(
1217
+ "width" => $this->__get("border_right_width"),
1218
+ "style" => $this->__get("border_right_style"),
1219
+ "color" => $this->__get("border_right_color"),
1220
+ ),
1221
+ "left" => array(
1222
+ "width" => $this->__get("border_left_width"),
1223
+ "style" => $this->__get("border_left_style"),
1224
+ "color" => $this->__get("border_left_color"),
1225
+ ),
1226
+ );
1227
+ }
1228
+
1229
+ /**
1230
+ * Return a single border property
1231
+ *
1232
+ * @param string $side
1233
+ *
1234
+ * @return mixed
1235
+ */
1236
+ protected function _get_border($side) {
1237
+ $color = $this->__get("border_" . $side . "_color");
1238
+
1239
+ return $this->__get("border_" . $side . "_width") . " " .
1240
+ $this->__get("border_" . $side . "_style") . " " . $color["hex"];
1241
+ }
1242
+
1243
+ /**#@+
1244
+ * Return full border properties as a string
1245
+ *
1246
+ * Border properties are returned just as specified in CSS:
1247
+ * <pre>[width] [style] [color]</pre>
1248
+ * e.g. "1px solid blue"
1249
+ *
1250
+ * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
1251
+ * @return string
1252
+ */
1253
+ function get_border_top() {
1254
+ return $this->_get_border("top");
1255
+ }
1256
+
1257
+ function get_border_right() {
1258
+ return $this->_get_border("right");
1259
+ }
1260
+
1261
+ function get_border_bottom() {
1262
+ return $this->_get_border("bottom");
1263
+ }
1264
+
1265
+ function get_border_left() {
1266
+ return $this->_get_border("left");
1267
+ }
1268
+ /**#@-*/
1269
+
1270
+ function get_computed_border_radius($w, $h) {
1271
+ if ( !empty($this->_computed_border_radius) ) {
1272
+ return $this->_computed_border_radius;
1273
+ }
1274
+
1275
+ $rTL = $this->__get("border_top_left_radius");
1276
+ $rTR = $this->__get("border_top_right_radius");
1277
+ $rBL = $this->__get("border_bottom_left_radius");
1278
+ $rBR = $this->__get("border_bottom_right_radius");
1279
+
1280
+ if ( $rTL + $rTR + $rBL + $rBR == 0 ) {
1281
+ return $this->_computed_border_radius = array(
1282
+ 0, 0, 0, 0,
1283
+ "top-left" => 0,
1284
+ "top-right" => 0,
1285
+ "bottom-right" => 0,
1286
+ "bottom-left" => 0,
1287
+ );
1288
+ }
1289
+
1290
+ $t = $this->__get("border_top_width");
1291
+ $r = $this->__get("border_right_width");
1292
+ $b = $this->__get("border_bottom_width");
1293
+ $l = $this->__get("border_left_width");
1294
+
1295
+ $rTL = min($rTL, $h - $rBL - $t/2 - $b/2, $w - $rTR - $l/2 - $r/2);
1296
+ $rTR = min($rTR, $h - $rBR - $t/2 - $b/2, $w - $rTL - $l/2 - $r/2);
1297
+ $rBL = min($rBL, $h - $rTL - $t/2 - $b/2, $w - $rBR - $l/2 - $r/2);
1298
+ $rBR = min($rBR, $h - $rTR - $t/2 - $b/2, $w - $rBL - $l/2 - $r/2);
1299
+
1300
+ return $this->_computed_border_radius = array(
1301
+ $rTL, $rTR, $rBR, $rBL,
1302
+ "top-left" => $rTL,
1303
+ "top-right" => $rTR,
1304
+ "bottom-right" => $rBR,
1305
+ "bottom-left" => $rBL,
1306
+ );
1307
+ }
1308
+ /**#@-*/
1309
+
1310
+
1311
+ /**
1312
+ * Returns the outline color as an array
1313
+ *
1314
+ * See {@link Style::get_color()}
1315
+ *
1316
+ * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
1317
+ * @return array
1318
+ */
1319
+ function get_outline_color() {
1320
+ if ( $this->_props["outline_color"] === "" ) {
1321
+ //see __set and __get, on all assignments clear cache!
1322
+ $this->_prop_cache["outline_color"] = null;
1323
+ $this->_props["outline_color"] = $this->__get("color");
1324
+ }
1325
+
1326
+ return $this->munge_color($this->_props["outline_color"]);
1327
+ }
1328
+
1329
+ /**#@+
1330
+ * Returns the outline width, as it is currently stored
1331
+ * @return float|string
1332
+ */
1333
+ function get_outline_width() {
1334
+ $style = $this->__get("outline_style");
1335
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["outline_width"]) : 0;
1336
+ }
1337
+
1338
+ /**#@+
1339
+ * Return full outline properties as a string
1340
+ *
1341
+ * Outline properties are returned just as specified in CSS:
1342
+ * <pre>[width] [style] [color]</pre>
1343
+ * e.g. "1px solid blue"
1344
+ *
1345
+ * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
1346
+ * @return string
1347
+ */
1348
+ function get_outline() {
1349
+ $color = $this->__get("outline_color");
1350
+ return
1351
+ $this->__get("outline_width") . " " .
1352
+ $this->__get("outline_style") . " " .
1353
+ $color["hex"];
1354
+ }
1355
+ /**#@-*/
1356
+
1357
+ /**
1358
+ * Returns border spacing as an array
1359
+ *
1360
+ * The array has the format (h_space,v_space)
1361
+ *
1362
+ * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
1363
+ * @return array
1364
+ */
1365
+ function get_border_spacing() {
1366
+ $arr = explode(" ", $this->_props["border_spacing"]);
1367
+ if ( count($arr) == 1 ) {
1368
+ $arr[1] = $arr[0];
1369
+ }
1370
+ return $arr;
1371
+ }
1372
+
1373
+ /*==============================*/
1374
+
1375
+ /*
1376
+ !important attribute
1377
+ For basic functionality of the !important attribute with overloading
1378
+ of several styles of an element, changes in inherit(), merge() and _parse_properties()
1379
+ are sufficient [helpers var $_important_props, __construct(), important_set(), important_get()]
1380
+
1381
+ Only for combined attributes extra treatment needed. See below.
1382
+
1383
+ div { border: 1px red; }
1384
+ div { border: solid; } // Not combined! Only one occurence of same style per context
1385
+ //
1386
+ div { border: 1px red; }
1387
+ div a { border: solid; } // Adding to border style ok by inheritance
1388
+ //
1389
+ div { border-style: solid; } // Adding to border style ok because of different styles
1390
+ div { border: 1px red; }
1391
+ //
1392
+ div { border-style: solid; !important} // border: overrides, even though not !important
1393
+ div { border: 1px dashed red; }
1394
+ //
1395
+ div { border: 1px red; !important }
1396
+ div a { border-style: solid; } // Need to override because not set
1397
+
1398
+ Special treatment:
1399
+ At individual property like border-top-width need to check whether overriding value is also !important.
1400
+ Also store the !important condition for later overrides.
1401
+ Since not known who is initiating the override, need to get passed !important as parameter.
1402
+ !important Paramter taken as in the original style in the css file.
1403
+ When property border !important given, do not mark subsets like border_style as important. Only
1404
+ individual properties.
1405
+
1406
+ Note:
1407
+ Setting individual property directly from css with e.g. set_border_top_style() is not needed, because
1408
+ missing set funcions handled by a generic handler __set(), including the !important.
1409
+ Setting individual property of as sub-property is handled below.
1410
+
1411
+ Implementation see at _set_style_side_type()
1412
+ Callers _set_style_sides_type(), _set_style_type, _set_style_type_important()
1413
+
1414
+ Related functionality for background, padding, margin, font, list_style
1415
+ */
1416
+
1417
+ /* Generalized set function for individual attribute of combined style.
1418
+ * With check for !important
1419
+ * Applicable for background, border, padding, margin, font, list_style
1420
+ * Note: $type has a leading underscore (or is empty), the others not.
1421
+ */
1422
+ protected function _set_style_side_type($style, $side, $type, $val, $important) {
1423
+ $prop = $style.'_'.$side.$type;
1424
+
1425
+ if ( !isset($this->_important_props[$prop]) || $important) {
1426
+ //see __set and __get, on all assignments clear cache!
1427
+ $this->_prop_cache[$prop] = null;
1428
+ if ( $important ) {
1429
+ $this->_important_props[$prop] = true;
1430
+ }
1431
+ $this->_props[$prop] = $val;
1432
+ }
1433
+ }
1434
+
1435
+ protected function _set_style_sides_type($style,$top,$right,$bottom,$left,$type,$important) {
1436
+ $this->_set_style_side_type($style,'top',$type,$top,$important);
1437
+ $this->_set_style_side_type($style,'right',$type,$right,$important);
1438
+ $this->_set_style_side_type($style,'bottom',$type,$bottom,$important);
1439
+ $this->_set_style_side_type($style,'left',$type,$left,$important);
1440
+ }
1441
+
1442
+ protected function _set_style_type($style,$type,$val,$important) {
1443
+ $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
1444
+ $arr = explode(" ", $val);
1445
+
1446
+ switch (count($arr)) {
1447
+ case 1: $this->_set_style_sides_type($style,$arr[0],$arr[0],$arr[0],$arr[0],$type,$important); break;
1448
+ case 2: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[0],$arr[1],$type,$important); break;
1449
+ case 3: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[1],$type,$important); break;
1450
+ case 4: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[3],$type,$important); break;
1451
+ }
1452
+
1453
+ //see __set and __get, on all assignments clear cache!
1454
+ $this->_prop_cache[$style.$type] = null;
1455
+ $this->_props[$style.$type] = $val;
1456
+ }
1457
+
1458
+ protected function _set_style_type_important($style,$type,$val) {
1459
+ $this->_set_style_type($style,$type,$val,isset($this->_important_props[$style.$type]));
1460
+ }
1461
+
1462
+ /* Anyway only called if _important matches and is assigned
1463
+ * E.g. _set_style_side_type($style,$side,'',str_replace("none", "0px", $val),isset($this->_important_props[$style.'_'.$side]));
1464
+ */
1465
+ protected function _set_style_side_width_important($style,$side,$val) {
1466
+ //see __set and __get, on all assignments clear cache!
1467
+ $this->_prop_cache[$style.'_'.$side] = null;
1468
+ $this->_props[$style.'_'.$side] = str_replace("none", "0px", $val);
1469
+ }
1470
+
1471
+ protected function _set_style($style,$val,$important) {
1472
+ if ( !isset($this->_important_props[$style]) || $important) {
1473
+ if ( $important ) {
1474
+ $this->_important_props[$style] = true;
1475
+ }
1476
+ //see __set and __get, on all assignments clear cache!
1477
+ $this->_prop_cache[$style] = null;
1478
+ $this->_props[$style] = $val;
1479
+ }
1480
+ }
1481
+
1482
+ protected function _image($val) {
1483
+ $DEBUGCSS=DEBUGCSS;
1484
+ $parsed_url = "none";
1485
+
1486
+ if ( mb_strpos($val, "url") === false ) {
1487
+ $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none
1488
+ }
1489
+ else {
1490
+ $val = preg_replace("/url\(['\"]?([^'\")]+)['\"]?\)/","\\1", trim($val));
1491
+
1492
+ // Resolve the url now in the context of the current stylesheet
1493
+ $parsed_url = explode_url($val);
1494
+ if ( $parsed_url["protocol"] == "" && $this->_stylesheet->get_protocol() == "" ) {
1495
+ if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\' ) {
1496
+ $path = $_SERVER["DOCUMENT_ROOT"].'/';
1497
+ }
1498
+ else {
1499
+ $path = $this->_stylesheet->get_base_path();
1500
+ }
1501
+
1502
+ $path .= $parsed_url["path"] . $parsed_url["file"];
1503
+ $path = realpath($path);
1504
+ // If realpath returns FALSE then specifically state that there is no background image
1505
+ if ( !$path ) {
1506
+ $path = 'none';
1507
+ }
1508
+ }
1509
+ else {
1510
+ $path = build_url($this->_stylesheet->get_protocol(),
1511
+ $this->_stylesheet->get_host(),
1512
+ $this->_stylesheet->get_base_path(),
1513
+ $val);
1514
+ }
1515
+ }
1516
+ if ($DEBUGCSS) {
1517
+ print "<pre>[_image\n";
1518
+ print_r($parsed_url);
1519
+ print $this->_stylesheet->get_protocol()."\n".$this->_stylesheet->get_base_path()."\n".$path."\n";
1520
+ print "_image]</pre>";;
1521
+ }
1522
+ return $path;
1523
+ }
1524
+
1525
+ /*======================*/
1526
+
1527
+ /**
1528
+ * Sets color
1529
+ *
1530
+ * The color parameter can be any valid CSS color value
1531
+ *
1532
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
1533
+ * @param string $color
1534
+ */
1535
+ function set_color($color) {
1536
+ $col = $this->munge_color($color);
1537
+
1538
+ if ( is_null($col) || !isset($col["hex"]) ) {
1539
+ $color = "inherit";
1540
+ }
1541
+ else {
1542
+ $color = $col["hex"];
1543
+ }
1544
+
1545
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1546
+ $this->_prop_cache["color"] = null;
1547
+ $this->_props["color"] = $color;
1548
+ }
1549
+
1550
+ /**
1551
+ * Sets the background color
1552
+ *
1553
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
1554
+ * @param string $color
1555
+ */
1556
+ function set_background_color($color) {
1557
+ $col = $this->munge_color($color);
1558
+
1559
+ if ( is_null($col) ) {
1560
+ return;
1561
+ //$col = self::$_defaults["background_color"];
1562
+ }
1563
+
1564
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1565
+ $this->_prop_cache["background_color"] = null;
1566
+ $this->_props["background_color"] = is_array($col) ? $col["hex"] : $col;
1567
+ }
1568
+
1569
+ /**
1570
+ * Set the background image url
1571
+ * @link http://www.w3.org/TR/CSS21/colors.html#background-properties
1572
+ *
1573
+ * @param string $val
1574
+ */
1575
+ function set_background_image($val) {
1576
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1577
+ $this->_prop_cache["background_image"] = null;
1578
+ $this->_props["background_image"] = $this->_image($val);
1579
+ }
1580
+
1581
+ /**
1582
+ * Sets the background repeat
1583
+ *
1584
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
1585
+ * @param string $val
1586
+ */
1587
+ function set_background_repeat($val) {
1588
+ if ( is_null($val) ) {
1589
+ $val = self::$_defaults["background_repeat"];
1590
+ }
1591
+
1592
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1593
+ $this->_prop_cache["background_repeat"] = null;
1594
+ $this->_props["background_repeat"] = $val;
1595
+ }
1596
+
1597
+ /**
1598
+ * Sets the background attachment
1599
+ *
1600
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
1601
+ * @param string $val
1602
+ */
1603
+ function set_background_attachment($val) {
1604
+ if ( is_null($val) ) {
1605
+ $val = self::$_defaults["background_attachment"];
1606
+ }
1607
+
1608
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1609
+ $this->_prop_cache["background_attachment"] = null;
1610
+ $this->_props["background_attachment"] = $val;
1611
+ }
1612
+
1613
+ /**
1614
+ * Sets the background position
1615
+ *
1616
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
1617
+ * @param string $val
1618
+ */
1619
+ function set_background_position($val) {
1620
+ if ( is_null($val) ) {
1621
+ $val = self::$_defaults["background_position"];
1622
+ }
1623
+
1624
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1625
+ $this->_prop_cache["background_position"] = null;
1626
+ $this->_props["background_position"] = $val;
1627
+ }
1628
+
1629
+ /**
1630
+ * Sets the background - combined options
1631
+ *
1632
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
1633
+ * @param string $val
1634
+ */
1635
+ function set_background($val) {
1636
+ $val = trim($val);
1637
+ $important = isset($this->_important_props["background"]);
1638
+
1639
+ if ( $val === "none" ) {
1640
+ $this->_set_style("background_image", "none", $important);
1641
+ $this->_set_style("background_color", "transparent", $important);
1642
+ }
1643
+ else {
1644
+ $pos = array();
1645
+ $tmp = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
1646
+ $tmp = preg_split("/\s+/", $tmp);
1647
+
1648
+ foreach($tmp as $attr) {
1649
+ if ( mb_substr($attr, 0, 3) === "url" || $attr === "none" ) {
1650
+ $this->_set_style("background_image", $this->_image($attr), $important);
1651
+ }
1652
+ elseif ( $attr === "fixed" || $attr === "scroll" ) {
1653
+ $this->_set_style("background_attachment", $attr, $important);
1654
+ }
1655
+ elseif ( $attr === "repeat" || $attr === "repeat-x" || $attr === "repeat-y" || $attr === "no-repeat" ) {
1656
+ $this->_set_style("background_repeat", $attr, $important);
1657
+ }
1658
+ elseif ( ($col = $this->munge_color($attr)) != null ) {
1659
+ $this->_set_style("background_color", is_array($col) ? $col["hex"] : $col, $important);
1660
+ }
1661
+ else {
1662
+ $pos[] = $attr;
1663
+ }
1664
+ }
1665
+
1666
+ if (count($pos)) {
1667
+ $this->_set_style("background_position", implode(" ", $pos), $important);
1668
+ }
1669
+ }
1670
+
1671
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1672
+ $this->_prop_cache["background"] = null;
1673
+ $this->_props["background"] = $val;
1674
+ }
1675
+
1676
+ /**
1677
+ * Sets the font size
1678
+ *
1679
+ * $size can be any acceptable CSS size
1680
+ *
1681
+ * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
1682
+ * @param string|float $size
1683
+ */
1684
+ function set_font_size($size) {
1685
+ $this->__font_size_calculated = false;
1686
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1687
+ $this->_prop_cache["font_size"] = null;
1688
+ $this->_props["font_size"] = $size;
1689
+ }
1690
+
1691
+ /**
1692
+ * Sets the font style
1693
+ *
1694
+ * combined attributes
1695
+ * set individual attributes also, respecting !important mark
1696
+ * exactly this order, separate by space. Multiple fonts separated by comma:
1697
+ * font-style, font-variant, font-weight, font-size, line-height, font-family
1698
+ *
1699
+ * Other than with border and list, existing partial attributes should
1700
+ * reset when starting here, even when not mentioned.
1701
+ * If individual attribute is !important and explicite or implicite replacement is not,
1702
+ * keep individual attribute
1703
+ *
1704
+ * require whitespace as delimiters for single value attributes
1705
+ * On delimiter "/" treat first as font height, second as line height
1706
+ * treat all remaining at the end of line as font
1707
+ * font-style, font-variant, font-weight, font-size, line-height, font-family
1708
+ *
1709
+ * missing font-size and font-family might be not allowed, but accept it here and
1710
+ * use default (medium size, enpty font name)
1711
+ *
1712
+ * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
1713
+ * @param $val
1714
+ */
1715
+ function set_font($val) {
1716
+ $this->__font_size_calculated = false;
1717
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1718
+ $this->_prop_cache["font"] = null;
1719
+ $this->_props["font"] = $val;
1720
+
1721
+ $important = isset($this->_important_props["font"]);
1722
+
1723
+ if ( preg_match("/^(italic|oblique|normal)\s*(.*)$/i",$val,$match) ) {
1724
+ $this->_set_style("font_style", $match[1], $important);
1725
+ $val = $match[2];
1726
+ }
1727
+ else {
1728
+ $this->_set_style("font_style", self::$_defaults["font_style"], $important);
1729
+ }
1730
+
1731
+ if ( preg_match("/^(small-caps|normal)\s*(.*)$/i",$val,$match) ) {
1732
+ $this->_set_style("font_variant", $match[1], $important);
1733
+ $val = $match[2];
1734
+ }
1735
+ else {
1736
+ $this->_set_style("font_variant", self::$_defaults["font_variant"], $important);
1737
+ }
1738
+
1739
+ //matching numeric value followed by unit -> this is indeed a subsequent font size. Skip!
1740
+ if ( preg_match("/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900|normal)\s*(.*)$/i", $val, $match) &&
1741
+ !preg_match("/^(?:pt|px|pc|em|ex|in|cm|mm|%)/",$match[2])
1742
+ ) {
1743
+ $this->_set_style("font_weight", $match[1], $important);
1744
+ $val = $match[2];
1745
+ }
1746
+ else {
1747
+ $this->_set_style("font_weight", self::$_defaults["font_weight"], $important);
1748
+ }
1749
+
1750
+ if ( preg_match("/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i",$val,$match) ) {
1751
+ $this->_set_style("font_size", $match[1], $important);
1752
+ $val = $match[2];
1753
+ if ( preg_match("/^\/\s*(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i", $val, $match ) ) {
1754
+ $this->_set_style("line_height", $match[1], $important);
1755
+ $val = $match[2];
1756
+ }
1757
+ else {
1758
+ $this->_set_style("line_height", self::$_defaults["line_height"], $important);
1759
+ }
1760
+ }
1761
+ else {
1762
+ $this->_set_style("font_size", self::$_defaults["font_size"], $important);
1763
+ $this->_set_style("line_height", self::$_defaults["line_height"], $important);
1764
+ }
1765
+
1766
+ if( strlen($val) != 0 ) {
1767
+ $this->_set_style("font_family", $val, $important);
1768
+ }
1769
+ else {
1770
+ $this->_set_style("font_family", self::$_defaults["font_family"], $important);
1771
+ }
1772
+ }
1773
+
1774
+ /**#@+
1775
+ * Sets page break properties
1776
+ *
1777
+ * @link http://www.w3.org/TR/CSS21/page.html#page-breaks
1778
+ * @param string $break
1779
+ */
1780
+ function set_page_break_before($break) {
1781
+ if ( $break === "left" || $break === "right" ) {
1782
+ $break = "always";
1783
+ }
1784
+
1785
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1786
+ $this->_prop_cache["page_break_before"] = null;
1787
+ $this->_props["page_break_before"] = $break;
1788
+ }
1789
+
1790
+ function set_page_break_after($break) {
1791
+ if ( $break === "left" || $break === "right" ) {
1792
+ $break = "always";
1793
+ }
1794
+
1795
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1796
+ $this->_prop_cache["page_break_after"] = null;
1797
+ $this->_props["page_break_after"] = $break;
1798
+ }
1799
+ /**#@-*/
1800
+
1801
+ //........................................................................
1802
+
1803
+ /**#@+
1804
+ * Sets the margin size
1805
+ *
1806
+ * @link http://www.w3.org/TR/CSS21/box.html#margin-properties
1807
+ * @param $val
1808
+ */
1809
+ function set_margin_top($val) {
1810
+ $this->_set_style_side_width_important('margin','top',$val);
1811
+ }
1812
+
1813
+ function set_margin_right($val) {
1814
+ $this->_set_style_side_width_important('margin','right',$val);
1815
+ }
1816
+
1817
+ function set_margin_bottom($val) {
1818
+ $this->_set_style_side_width_important('margin','bottom',$val);
1819
+ }
1820
+
1821
+ function set_margin_left($val) {
1822
+ $this->_set_style_side_width_important('margin','left',$val);
1823
+ }
1824
+
1825
+ function set_margin($val) {
1826
+ $val = str_replace("none", "0px", $val);
1827
+ $this->_set_style_type_important('margin','',$val);
1828
+ }
1829
+ /**#@-*/
1830
+
1831
+ /**#@+
1832
+ * Sets the padding size
1833
+ *
1834
+ * @link http://www.w3.org/TR/CSS21/box.html#padding-properties
1835
+ * @param $val
1836
+ */
1837
+ function set_padding_top($val) {
1838
+ $this->_set_style_side_width_important('padding','top',$val);
1839
+ }
1840
+
1841
+ function set_padding_right($val) {
1842
+ $this->_set_style_side_width_important('padding','right',$val);
1843
+ }
1844
+
1845
+ function set_padding_bottom($val) {
1846
+ $this->_set_style_side_width_important('padding','bottom',$val);
1847
+ }
1848
+
1849
+ function set_padding_left($val) {
1850
+ $this->_set_style_side_width_important('padding','left',$val);
1851
+ }
1852
+
1853
+ function set_padding($val) {
1854
+ $val = str_replace("none", "0px", $val);
1855
+ $this->_set_style_type_important('padding','',$val);
1856
+ }
1857
+ /**#@-*/
1858
+
1859
+ /**
1860
+ * Sets a single border
1861
+ *
1862
+ * @param string $side
1863
+ * @param string $border_spec ([width] [style] [color])
1864
+ * @param boolean $important
1865
+ */
1866
+ protected function _set_border($side, $border_spec, $important) {
1867
+ $border_spec = preg_replace("/\s*\,\s*/", ",", $border_spec);
1868
+ //$border_spec = str_replace(",", " ", $border_spec); // Why did we have this ?? rbg(10, 102, 10) > rgb(10 102 10)
1869
+ $arr = explode(" ", $border_spec);
1870
+
1871
+ // FIXME: handle partial values
1872
+
1873
+ //For consistency of individal and combined properties, and with ie8 and firefox3
1874
+ //reset all attributes, even if only partially given
1875
+ $this->_set_style_side_type('border',$side,'_style',self::$_defaults['border_'.$side.'_style'],$important);
1876
+ $this->_set_style_side_type('border',$side,'_width',self::$_defaults['border_'.$side.'_width'],$important);
1877
+ $this->_set_style_side_type('border',$side,'_color',self::$_defaults['border_'.$side.'_color'],$important);
1878
+
1879
+ foreach ($arr as $value) {
1880
+ $value = trim($value);
1881
+ if ( in_array($value, self::$BORDER_STYLES) ) {
1882
+ $this->_set_style_side_type('border',$side,'_style',$value,$important);
1883
+ }
1884
+ else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) {
1885
+ $this->_set_style_side_type('border',$side,'_width',$value,$important);
1886
+ }
1887
+ else {
1888
+ // must be color
1889
+ $this->_set_style_side_type('border',$side,'_color',$value,$important);
1890
+ }
1891
+ }
1892
+
1893
+ //see __set and __get, on all assignments clear cache!
1894
+ $this->_prop_cache['border_'.$side] = null;
1895
+ $this->_props['border_'.$side] = $border_spec;
1896
+ }
1897
+
1898
+ /**
1899
+ * Sets the border styles
1900
+ *
1901
+ * @link http://www.w3.org/TR/CSS21/box.html#border-properties
1902
+ * @param string $val
1903
+ */
1904
+ function set_border_top($val) {
1905
+ $this->_set_border("top", $val, isset($this->_important_props['border_top']));
1906
+ }
1907
+
1908
+ function set_border_right($val) {
1909
+ $this->_set_border("right", $val, isset($this->_important_props['border_right']));
1910
+ }
1911
+
1912
+ function set_border_bottom($val) {
1913
+ $this->_set_border("bottom", $val, isset($this->_important_props['border_bottom']));
1914
+ }
1915
+
1916
+ function set_border_left($val) {
1917
+ $this->_set_border("left", $val, isset($this->_important_props['border_left']));
1918
+ }
1919
+
1920
+ function set_border($val) {
1921
+ $important = isset($this->_important_props["border"]);
1922
+ $this->_set_border("top", $val, $important);
1923
+ $this->_set_border("right", $val, $important);
1924
+ $this->_set_border("bottom", $val, $important);
1925
+ $this->_set_border("left", $val, $important);
1926
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1927
+ $this->_prop_cache["border"] = null;
1928
+ $this->_props["border"] = $val;
1929
+ }
1930
+
1931
+ function set_border_width($val) {
1932
+ $this->_set_style_type_important('border','_width',$val);
1933
+ }
1934
+
1935
+ function set_border_color($val) {
1936
+ $this->_set_style_type_important('border','_color',$val);
1937
+ }
1938
+
1939
+ function set_border_style($val) {
1940
+ $this->_set_style_type_important('border','_style',$val);
1941
+ }
1942
+
1943
+ /**
1944
+ * Sets the border radius size
1945
+ *
1946
+ * http://www.w3.org/TR/css3-background/#corners
1947
+ */
1948
+ function set_border_top_left_radius($val) {
1949
+ $this->_set_border_radius_corner($val, "top_left");
1950
+ }
1951
+
1952
+ function set_border_top_right_radius($val) {
1953
+ $this->_set_border_radius_corner($val, "top_right");
1954
+ }
1955
+
1956
+ function set_border_bottom_left_radius($val) {
1957
+ $this->_set_border_radius_corner($val, "bottom_left");
1958
+ }
1959
+
1960
+ function set_border_bottom_right_radius($val) {
1961
+ $this->_set_border_radius_corner($val, "bottom_right");
1962
+ }
1963
+
1964
+ function set_border_radius($val) {
1965
+ $val = preg_replace("/\s*\,\s*/", ",", $val); // when border-radius has spaces
1966
+ $arr = explode(" ", $val);
1967
+
1968
+ switch (count($arr)) {
1969
+ case 1: $this->_set_border_radii($arr[0],$arr[0],$arr[0],$arr[0]); break;
1970
+ case 2: $this->_set_border_radii($arr[0],$arr[1],$arr[0],$arr[1]); break;
1971
+ case 3: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[1]); break;
1972
+ case 4: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[3]); break;
1973
+ }
1974
+ }
1975
+
1976
+ protected function _set_border_radii($val1, $val2, $val3, $val4) {
1977
+ $this->_set_border_radius_corner($val1, "top_left");
1978
+ $this->_set_border_radius_corner($val2, "top_right");
1979
+ $this->_set_border_radius_corner($val3, "bottom_right");
1980
+ $this->_set_border_radius_corner($val4, "bottom_left");
1981
+ }
1982
+
1983
+ protected function _set_border_radius_corner($val, $corner) {
1984
+ $this->_has_border_radius = true;
1985
+
1986
+ //see __set and __get, on all assignments clear cache!
1987
+ $this->_prop_cache["border_" . $corner . "_radius"] = null;
1988
+
1989
+ $this->_props["border_" . $corner . "_radius"] = $this->length_in_pt($val);
1990
+ }
1991
+
1992
+ /**
1993
+ * Sets the outline styles
1994
+ *
1995
+ * @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines
1996
+ * @param string $val
1997
+ */
1998
+ function set_outline($val) {
1999
+ $important = isset($this->_important_props["outline"]);
2000
+
2001
+ $props = array(
2002
+ "outline_style",
2003
+ "outline_width",
2004
+ "outline_color",
2005
+ );
2006
+
2007
+ foreach($props as $prop) {
2008
+ $_val = self::$_defaults[$prop];
2009
+
2010
+ if ( !isset($this->_important_props[$prop]) || $important) {
2011
+ //see __set and __get, on all assignments clear cache!
2012
+ $this->_prop_cache[$prop] = null;
2013
+ if ( $important ) {
2014
+ $this->_important_props[$prop] = true;
2015
+ }
2016
+ $this->_props[$prop] = $_val;
2017
+ }
2018
+ }
2019
+
2020
+ $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
2021
+ $arr = explode(" ", $val);
2022
+ foreach ($arr as $value) {
2023
+ $value = trim($value);
2024
+
2025
+ if ( in_array($value, self::$BORDER_STYLES) ) {
2026
+ $this->set_outline_style($value);
2027
+ }
2028
+ else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) {
2029
+ $this->set_outline_width($value);
2030
+ }
2031
+ else {
2032
+ // must be color
2033
+ $this->set_outline_color($value);
2034
+ }
2035
+ }
2036
+
2037
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2038
+ $this->_prop_cache["outline"] = null;
2039
+ $this->_props["outline"] = $val;
2040
+ }
2041
+
2042
+ function set_outline_width($val) {
2043
+ $this->_set_style_type_important('outline','_width',$val);
2044
+ }
2045
+
2046
+ function set_outline_color($val) {
2047
+ $this->_set_style_type_important('outline','_color',$val);
2048
+ }
2049
+
2050
+ function set_outline_style($val) {
2051
+ $this->_set_style_type_important('outline','_style',$val);
2052
+ }
2053
+
2054
+ /**
2055
+ * Sets the border spacing
2056
+ *
2057
+ * @link http://www.w3.org/TR/CSS21/box.html#border-properties
2058
+ * @param float $val
2059
+ */
2060
+ function set_border_spacing($val) {
2061
+ $arr = explode(" ", $val);
2062
+
2063
+ if ( count($arr) == 1 ) {
2064
+ $arr[1] = $arr[0];
2065
+ }
2066
+
2067
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2068
+ $this->_prop_cache["border_spacing"] = null;
2069
+ $this->_props["border_spacing"] = "$arr[0] $arr[1]";
2070
+ }
2071
+
2072
+ /**
2073
+ * Sets the list style image
2074
+ *
2075
+ * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
2076
+ * @param $val
2077
+ */
2078
+ function set_list_style_image($val) {
2079
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2080
+ $this->_prop_cache["list_style_image"] = null;
2081
+ $this->_props["list_style_image"] = $this->_image($val);
2082
+ }
2083
+
2084
+ /**
2085
+ * Sets the list style
2086
+ *
2087
+ * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
2088
+ * @param $val
2089
+ */
2090
+ function set_list_style($val) {
2091
+ $important = isset($this->_important_props["list_style"]);
2092
+ $arr = explode(" ", str_replace(",", " ", $val));
2093
+
2094
+ static $types = array(
2095
+ "disc", "circle", "square",
2096
+ "decimal-leading-zero", "decimal", "1",
2097
+ "lower-roman", "upper-roman", "a", "A",
2098
+ "lower-greek",
2099
+ "lower-latin", "upper-latin",
2100
+ "lower-alpha", "upper-alpha",
2101
+ "armenian", "georgian", "hebrew",
2102
+ "cjk-ideographic", "hiragana", "katakana",
2103
+ "hiragana-iroha", "katakana-iroha", "none"
2104
+ );
2105
+
2106
+ static $positions = array("inside", "outside");
2107
+
2108
+ foreach ($arr as $value) {
2109
+ /* http://www.w3.org/TR/CSS21/generate.html#list-style
2110
+ * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none'
2111
+ */
2112
+ if ( $value === "none" ) {
2113
+ $this->_set_style("list_style_type", $value, $important);
2114
+ $this->_set_style("list_style_image", $value, $important);
2115
+ continue;
2116
+ }
2117
+
2118
+ //On setting or merging or inheriting list_style_image as well as list_style_type,
2119
+ //and url exists, then url has precedence, otherwise fall back to list_style_type
2120
+ //Firefox is wrong here (list_style_image gets overwritten on explicite list_style_type)
2121
+ //Internet Explorer 7/8 and dompdf is right.
2122
+
2123
+ if ( mb_substr($value, 0, 3) === "url" ) {
2124
+ $this->_set_style("list_style_image", $this->_image($value), $important);
2125
+ continue;
2126
+ }
2127
+
2128
+ if ( in_array($value, $types) ) {
2129
+ $this->_set_style("list_style_type", $value, $important);
2130
+ }
2131
+ else if ( in_array($value, $positions) ) {
2132
+ $this->_set_style("list_style_position", $value, $important);
2133
+ }
2134
+ }
2135
+
2136
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2137
+ $this->_prop_cache["list_style"] = null;
2138
+ $this->_props["list_style"] = $val;
2139
+ }
2140
+
2141
+ function set_size($val) {
2142
+ $length_re = "/(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))/";
2143
+
2144
+ $val = mb_strtolower($val);
2145
+
2146
+ if ( $val === "auto" ) {
2147
+ return;
2148
+ }
2149
+
2150
+ $parts = preg_split("/\s+/", $val);
2151
+
2152
+ $computed = array();
2153
+ if ( preg_match($length_re, $parts[0]) ) {
2154
+ $computed[] = $this->length_in_pt($parts[0]);
2155
+
2156
+ if ( isset($parts[1]) && preg_match($length_re, $parts[1]) ) {
2157
+ $computed[] = $this->length_in_pt($parts[1]);
2158
+ }
2159
+ else {
2160
+ $computed[] = $computed[0];
2161
+ }
2162
+ }
2163
+ elseif ( isset(CPDF_Adapter::$PAPER_SIZES[$parts[0]]) ) {
2164
+ $computed = array_slice(CPDF_Adapter::$PAPER_SIZES[$parts[0]], 2, 2);
2165
+
2166
+ if ( isset($parts[1]) && $parts[1] === "landscape" ) {
2167
+ $computed = array_reverse($computed);
2168
+ }
2169
+ }
2170
+ else {
2171
+ return;
2172
+ }
2173
+
2174
+ $this->_props["size"] = $computed;
2175
+ }
2176
+
2177
+ /**
2178
+ * Sets the CSS3 transform property
2179
+ *
2180
+ * @link http://www.w3.org/TR/css3-2d-transforms/#transform-property
2181
+ * @param string $val
2182
+ */
2183
+ function set_transform($val) {
2184
+ $number = "\s*([^,\s]+)\s*";
2185
+ $tr_value = "\s*([^,\s]+)\s*";
2186
+ $angle = "\s*([^,\s]+(?:deg|rad)?)\s*";
2187
+
2188
+ if ( !preg_match_all("/[a-z]+\([^\)]+\)/i", $val, $parts, PREG_SET_ORDER) ) {
2189
+ return;
2190
+ }
2191
+
2192
+ $functions = array(
2193
+ //"matrix" => "\($number,$number,$number,$number,$number,$number\)",
2194
+
2195
+ "translate" => "\($tr_value(?:,$tr_value)?\)",
2196
+ "translateX" => "\($tr_value\)",
2197
+ "translateY" => "\($tr_value\)",
2198
+
2199
+ "scale" => "\($number(?:,$number)?\)",
2200
+ "scaleX" => "\($number\)",
2201
+ "scaleY" => "\($number\)",
2202
+
2203
+ "rotate" => "\($angle\)",
2204
+
2205
+ "skew" => "\($angle(?:,$angle)?\)",
2206
+ "skewX" => "\($angle\)",
2207
+ "skewY" => "\($angle\)",
2208
+ );
2209
+
2210
+ $transforms = array();
2211
+
2212
+ foreach($parts as $part) {
2213
+ $t = $part[0];
2214
+
2215
+ foreach($functions as $name => $pattern) {
2216
+ if ( preg_match("/$name\s*$pattern/i", $t, $matches) ) {
2217
+ $values = array_slice($matches, 1);
2218
+
2219
+ switch($name) {
2220
+ // <angle> units
2221
+ case "rotate":
2222
+ case "skew":
2223
+ case "skewX":
2224
+ case "skewY":
2225
+
2226
+ foreach($values as $i => $value) {
2227
+ if ( strpos($value, "rad") ) {
2228
+ $values[$i] = rad2deg(floatval($value));
2229
+ }
2230
+ else {
2231
+ $values[$i] = floatval($value);
2232
+ }
2233
+ }
2234
+
2235
+ switch($name) {
2236
+ case "skew":
2237
+ if ( !isset($values[1]) ) {
2238
+ $values[1] = 0;
2239
+ }
2240
+ break;
2241
+ case "skewX":
2242
+ $name = "skew";
2243
+ $values = array($values[0], 0);
2244
+ break;
2245
+ case "skewY":
2246
+ $name = "skew";
2247
+ $values = array(0, $values[0]);
2248
+ break;
2249
+ }
2250
+ break;
2251
+
2252
+ // <translation-value> units
2253
+ case "translate":
2254
+ $values[0] = $this->length_in_pt($values[0], $this->width);
2255
+
2256
+ if ( isset($values[1]) ) {
2257
+ $values[1] = $this->length_in_pt($values[1], $this->height);
2258
+ }
2259
+ else {
2260
+ $values[1] = 0;
2261
+ }
2262
+ break;
2263
+
2264
+ case "translateX":
2265
+ $name = "translate";
2266
+ $values = array($this->length_in_pt($values[0], $this->width), 0);
2267
+ break;
2268
+
2269
+ case "translateY":
2270
+ $name = "translate";
2271
+ $values = array(0, $this->length_in_pt($values[0], $this->height));
2272
+ break;
2273
+
2274
+ // <number> units
2275
+ case "scale":
2276
+ if ( !isset($values[1]) ) {
2277
+ $values[1] = $values[0];
2278
+ }
2279
+ break;
2280
+
2281
+ case "scaleX":
2282
+ $name = "scale";
2283
+ $values = array($values[0], 1.0);
2284
+ break;
2285
+
2286
+ case "scaleY":
2287
+ $name = "scale";
2288
+ $values = array(1.0, $values[0]);
2289
+ break;
2290
+ }
2291
+
2292
+ $transforms[] = array(
2293
+ $name,
2294
+ $values,
2295
+ );
2296
+ }
2297
+ }
2298
+ }
2299
+
2300
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2301
+ $this->_prop_cache["transform"] = null;
2302
+ $this->_props["transform"] = $transforms;
2303
+ }
2304
+
2305
+ function set__webkit_transform($val) {
2306
+ $this->set_transform($val);
2307
+ }
2308
+
2309
+ function set__webkit_transform_origin($val) {
2310
+ $this->set_transform_origin($val);
2311
+ }
2312
+
2313
+ /**
2314
+ * Sets the CSS3 transform-origin property
2315
+ *
2316
+ * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
2317
+ * @param string $val
2318
+ */
2319
+ function set_transform_origin($val) {
2320
+ $values = preg_split("/\s+/", $val);
2321
+
2322
+ if ( count($values) === 0) {
2323
+ return;
2324
+ }
2325
+
2326
+ foreach($values as &$value) {
2327
+ if ( in_array($value, array("top", "left")) ) {
2328
+ $value = 0;
2329
+ }
2330
+
2331
+ if ( in_array($value, array("bottom", "right")) ) {
2332
+ $value = "100%";
2333
+ }
2334
+ }
2335
+
2336
+ if ( !isset($values[1]) ) {
2337
+ $values[1] = $values[0];
2338
+ }
2339
+
2340
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2341
+ $this->_prop_cache["transform_origin"] = null;
2342
+ $this->_props["transform_origin"] = $values;
2343
+ }
2344
+
2345
+ protected function parse_image_resolution($val) {
2346
+ // If exif data could be get:
2347
+ // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/';
2348
+
2349
+ $re = '/^\s*(\d+|normal|auto)\s*$/';
2350
+
2351
+ if ( !preg_match($re, $val, $matches) ) {
2352
+ return null;
2353
+ }
2354
+
2355
+ return $matches[1];
2356
+ }
2357
+
2358
+ // auto | normal | dpi
2359
+ function set_background_image_resolution($val) {
2360
+ $parsed = $this->parse_image_resolution($val);
2361
+
2362
+ $this->_prop_cache["background_image_resolution"] = null;
2363
+ $this->_props["background_image_resolution"] = $parsed;
2364
+ }
2365
+
2366
+ // auto | normal | dpi
2367
+ function set_image_resolution($val) {
2368
+ $parsed = $this->parse_image_resolution($val);
2369
+
2370
+ $this->_prop_cache["image_resolution"] = null;
2371
+ $this->_props["image_resolution"] = $parsed;
2372
+ }
2373
+
2374
+ function set__dompdf_background_image_resolution($val) {
2375
+ $this->set_background_image_resolution($val);
2376
+ }
2377
+
2378
+ function set__dompdf_image_resolution($val) {
2379
+ $this->set_image_resolution($val);
2380
+ }
2381
+
2382
+ function set_z_index($val) {
2383
+ if ( round($val) != $val && $val !== "auto" ) {
2384
+ return;
2385
+ }
2386
+
2387
+ $this->_prop_cache["z_index"] = null;
2388
+ $this->_props["z_index"] = $val;
2389
+ }
2390
+
2391
+ function set_counter_increment($val) {
2392
+ $val = trim($val);
2393
+ $value = null;
2394
+
2395
+ if ( in_array($val, array("none", "inherit")) ) {
2396
+ $value = $val;
2397
+ }
2398
+ else {
2399
+ if ( preg_match_all("/(".self::CSS_IDENTIFIER.")(?:\s+(".self::CSS_INTEGER."))?/", $val, $matches, PREG_SET_ORDER) ){
2400
+ $value = array();
2401
+ foreach($matches as $match) {
2402
+ $value[$match[1]] = isset($match[2]) ? $match[2] : 1;
2403
+ }
2404
+ }
2405
+ }
2406
+
2407
+ $this->_prop_cache["counter_increment"] = null;
2408
+ $this->_props["counter_increment"] = $value;
2409
+ }
2410
+
2411
+ /**
2412
+ * Generate a string representation of the Style
2413
+ *
2414
+ * This dumps the entire property array into a string via print_r. Useful
2415
+ * for debugging.
2416
+ *
2417
+ * @return string
2418
+ */
2419
+ /*DEBUGCSS print: see below additional debugging util*/
2420
+ function __toString() {
2421
+ return print_r(array_merge(array("parent_font_size" => $this->_parent_font_size),
2422
+ $this->_props), true);
2423
+ }
2424
+
2425
+ /*DEBUGCSS*/ function debug_print() {
2426
+ /*DEBUGCSS*/ print "parent_font_size:".$this->_parent_font_size . ";\n";
2427
+ /*DEBUGCSS*/ foreach($this->_props as $prop => $val ) {
2428
+ /*DEBUGCSS*/ print $prop.':'.$val;
2429
+ /*DEBUGCSS*/ if (isset($this->_important_props[$prop])) {
2430
+ /*DEBUGCSS*/ print '!important';
2431
+ /*DEBUGCSS*/ }
2432
+ /*DEBUGCSS*/ print ";\n";
2433
+ /*DEBUGCSS*/ }
2434
+ /*DEBUGCSS*/ }
2435
+ }
lib/dompdf/include/stylesheet.cls.php ADDED
@@ -0,0 +1,1419 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * The location of the default built-in CSS file.
13
+ * {@link Stylesheet::DEFAULT_STYLESHEET}
14
+ */
15
+ define('__DEFAULT_STYLESHEET', DOMPDF_LIB_DIR . DIRECTORY_SEPARATOR . "res" . DIRECTORY_SEPARATOR . "html.css");
16
+
17
+ /**
18
+ * The master stylesheet class
19
+ *
20
+ * The Stylesheet class is responsible for parsing stylesheets and style
21
+ * tags/attributes. It also acts as a registry of the individual Style
22
+ * objects generated by the current set of loaded CSS files and style
23
+ * elements.
24
+ *
25
+ * @see Style
26
+ * @package dompdf
27
+ */
28
+ class Stylesheet {
29
+
30
+ /**
31
+ * The location of the default built-in CSS file.
32
+ */
33
+ const DEFAULT_STYLESHEET = __DEFAULT_STYLESHEET;
34
+
35
+ /**
36
+ * User agent stylesheet origin
37
+ *
38
+ * @var int
39
+ */
40
+ const ORIG_UA = 1;
41
+
42
+ /**
43
+ * User normal stylesheet origin
44
+ *
45
+ * @var int
46
+ */
47
+ const ORIG_USER = 2;
48
+
49
+ /**
50
+ * Author normal stylesheet origin
51
+ *
52
+ * @var int
53
+ */
54
+ const ORIG_AUTHOR = 3;
55
+
56
+ private static $_stylesheet_origins = array(
57
+ self::ORIG_UA => -0x0FFFFFFF, // user agent style sheets
58
+ self::ORIG_USER => -0x0000FFFF, // user normal style sheets
59
+ self::ORIG_AUTHOR => 0x00000000, // author normal style sheets
60
+ );
61
+
62
+ /**
63
+ * Current dompdf instance
64
+ *
65
+ * @var DOMPDF
66
+ */
67
+ private $_dompdf;
68
+
69
+ /**
70
+ * Array of currently defined styles
71
+ *
72
+ * @var Style[]
73
+ */
74
+ private $_styles;
75
+
76
+ /**
77
+ * Base protocol of the document being parsed
78
+ * Used to handle relative urls.
79
+ *
80
+ * @var string
81
+ */
82
+ private $_protocol;
83
+
84
+ /**
85
+ * Base hostname of the document being parsed
86
+ * Used to handle relative urls.
87
+ *
88
+ * @var string
89
+ */
90
+ private $_base_host;
91
+
92
+ /**
93
+ * Base path of the document being parsed
94
+ * Used to handle relative urls.
95
+ *
96
+ * @var string
97
+ */
98
+ private $_base_path;
99
+
100
+ /**
101
+ * The styles defined by @page rules
102
+ *
103
+ * @var array<Style>
104
+ */
105
+ private $_page_styles;
106
+
107
+ /**
108
+ * List of loaded files, used to prevent recursion
109
+ *
110
+ * @var array
111
+ */
112
+ private $_loaded_files;
113
+
114
+ /**
115
+ * Current stylesheet origin
116
+ *
117
+ * @var int
118
+ */
119
+ private $_current_origin = self::ORIG_UA;
120
+
121
+ /**
122
+ * Accepted CSS media types
123
+ * List of types and parsing rules for future extensions:
124
+ * http://www.w3.org/TR/REC-html40/types.html
125
+ * screen, tty, tv, projection, handheld, print, braille, aural, all
126
+ * The following are non standard extensions for undocumented specific environments.
127
+ * static, visual, bitmap, paged, dompdf
128
+ * Note, even though the generated pdf file is intended for print output,
129
+ * the desired content might be different (e.g. screen or projection view of html file).
130
+ * Therefore allow specification of content by dompdf setting DOMPDF_DEFAULT_MEDIA_TYPE.
131
+ * If given, replace media "print" by DOMPDF_DEFAULT_MEDIA_TYPE.
132
+ * (Previous version $ACCEPTED_MEDIA_TYPES = $ACCEPTED_GENERIC_MEDIA_TYPES + $ACCEPTED_DEFAULT_MEDIA_TYPE)
133
+ */
134
+ static $ACCEPTED_DEFAULT_MEDIA_TYPE = "print";
135
+ static $ACCEPTED_GENERIC_MEDIA_TYPES = array("all", "static", "visual", "bitmap", "paged", "dompdf");
136
+
137
+ /**
138
+ * The class constructor.
139
+ *
140
+ * The base protocol, host & path are initialized to those of
141
+ * the current script.
142
+ */
143
+ function __construct(DOMPDF $dompdf) {
144
+ $this->_dompdf = $dompdf;
145
+ $this->_styles = array();
146
+ $this->_loaded_files = array();
147
+ list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($_SERVER["SCRIPT_FILENAME"]);
148
+ $this->_page_styles = array("base" => null);
149
+ }
150
+
151
+ /**
152
+ * Class destructor
153
+ */
154
+ function __destruct() {
155
+ clear_object($this);
156
+ }
157
+
158
+ /**
159
+ * Set the base protocol
160
+ *
161
+ * @param string $protocol
162
+ */
163
+ function set_protocol($protocol) { $this->_protocol = $protocol; }
164
+
165
+ /**
166
+ * Set the base host
167
+ *
168
+ * @param string $host
169
+ */
170
+ function set_host($host) { $this->_base_host = $host; }
171
+
172
+ /**
173
+ * Set the base path
174
+ *
175
+ * @param string $path
176
+ */
177
+ function set_base_path($path) { $this->_base_path = $path; }
178
+
179
+ /**
180
+ * Return the DOMPDF object
181
+ *
182
+ * @return DOMPDF
183
+ */
184
+ function get_dompdf() { return $this->_dompdf; }
185
+
186
+ /**
187
+ * Return the base protocol for this stylesheet
188
+ *
189
+ * @return string
190
+ */
191
+ function get_protocol() { return $this->_protocol; }
192
+
193
+ /**
194
+ * Return the base host for this stylesheet
195
+ *
196
+ * @return string
197
+ */
198
+ function get_host() { return $this->_base_host; }
199
+
200
+ /**
201
+ * Return the base path for this stylesheet
202
+ *
203
+ * @return string
204
+ */
205
+ function get_base_path() { return $this->_base_path; }
206
+
207
+ /**
208
+ * Return the array of page styles
209
+ *
210
+ * @return Style[]
211
+ */
212
+ function get_page_styles() { return $this->_page_styles; }
213
+
214
+ /**
215
+ * Add a new Style object to the stylesheet
216
+ * add_style() adds a new Style object to the current stylesheet, or
217
+ * merges a new Style with an existing one.
218
+ *
219
+ * @param string $key the Style's selector
220
+ * @param Style $style the Style to be added
221
+ *
222
+ * @throws DOMPDF_Exception
223
+ */
224
+ function add_style($key, Style $style) {
225
+ if ( !is_string($key) ) {
226
+ throw new DOMPDF_Exception("CSS rule must be keyed by a string.");
227
+ }
228
+
229
+ if ( isset($this->_styles[$key]) ) {
230
+ $this->_styles[$key]->merge($style);
231
+ }
232
+ else {
233
+ $this->_styles[$key] = clone $style;
234
+ }
235
+
236
+ $this->_styles[$key]->set_origin( $this->_current_origin );
237
+ }
238
+
239
+ /**
240
+ * lookup a specifc Style object
241
+ *
242
+ * lookup() returns the Style specified by $key, or null if the Style is
243
+ * not found.
244
+ *
245
+ * @param string $key the selector of the requested Style
246
+ * @return Style
247
+ */
248
+ function lookup($key) {
249
+ if ( !isset($this->_styles[$key]) ) {
250
+ return null;
251
+ }
252
+
253
+ return $this->_styles[$key];
254
+ }
255
+
256
+ /**
257
+ * create a new Style object associated with this stylesheet
258
+ *
259
+ * @param Style $parent The style of this style's parent in the DOM tree
260
+ * @return Style
261
+ */
262
+ function create_style(Style $parent = null) {
263
+ return new Style($this, $this->_current_origin);
264
+ }
265
+
266
+ /**
267
+ * load and parse a CSS string
268
+ *
269
+ * @param string $css
270
+ */
271
+ function load_css(&$css) { $this->_parse_css($css); }
272
+
273
+
274
+ /**
275
+ * load and parse a CSS file
276
+ *
277
+ * @param string $file
278
+ * @param int $origin
279
+ */
280
+ function load_css_file($file, $origin = self::ORIG_AUTHOR) {
281
+ if ( $origin ) {
282
+ $this->_current_origin = $origin;
283
+ }
284
+
285
+ // Prevent circular references
286
+ if ( isset($this->_loaded_files[$file]) ) {
287
+ return;
288
+ }
289
+
290
+ $this->_loaded_files[$file] = true;
291
+
292
+ if ( strpos($file, "data:") === 0) {
293
+ $parsed = parse_data_uri($file);
294
+ $css = $parsed["data"];
295
+ }
296
+ else {
297
+ $parsed_url = explode_url($file);
298
+
299
+ list($this->_protocol, $this->_base_host, $this->_base_path, $filename) = $parsed_url;
300
+
301
+ // Fix submitted by Nick Oostveen for aliased directory support:
302
+ if ( $this->_protocol == "" ) {
303
+ $file = $this->_base_path . $filename;
304
+ }
305
+ else {
306
+ $file = build_url($this->_protocol, $this->_base_host, $this->_base_path, $filename);
307
+ }
308
+
309
+ set_error_handler("record_warnings");
310
+ $css = file_get_contents($file, null, $this->_dompdf->get_http_context());
311
+ restore_error_handler();
312
+
313
+ $good_mime_type = true;
314
+
315
+ // See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/
316
+ if ( isset($http_response_header) && !$this->_dompdf->get_quirksmode() ) {
317
+ foreach($http_response_header as $_header) {
318
+ if ( preg_match("@Content-Type:\s*([\w/]+)@i", $_header, $matches) &&
319
+ ($matches[1] !== "text/css") ) {
320
+ $good_mime_type = false;
321
+ }
322
+ }
323
+ }
324
+
325
+ if ( !$good_mime_type || $css == "" ) {
326
+ record_warnings(E_USER_WARNING, "Unable to load css file $file", __FILE__, __LINE__);
327
+ return;
328
+ }
329
+ }
330
+
331
+ $this->_parse_css($css);
332
+ }
333
+
334
+ /**
335
+ * @link http://www.w3.org/TR/CSS21/cascade.html#specificity
336
+ *
337
+ * @param string $selector
338
+ * @param int $origin :
339
+ * - ua: user agent style sheets
340
+ * - un: user normal style sheets
341
+ * - an: author normal style sheets
342
+ * - ai: author important style sheets
343
+ * - ui: user important style sheets
344
+ *
345
+ * @return int
346
+ */
347
+ private function _specificity($selector, $origin = self::ORIG_AUTHOR) {
348
+ // http://www.w3.org/TR/CSS21/cascade.html#specificity
349
+ // ignoring the ":" pseudoclass modifyers
350
+ // also ignored in _css_selector_to_xpath
351
+
352
+ $a = ($selector === "!attr") ? 1 : 0;
353
+
354
+ $b = min(mb_substr_count($selector, "#"), 255);
355
+
356
+ $c = min(mb_substr_count($selector, ".") +
357
+ mb_substr_count($selector, "["), 255);
358
+
359
+ $d = min(mb_substr_count($selector, " ") +
360
+ mb_substr_count($selector, ">") +
361
+ mb_substr_count($selector, "+"), 255);
362
+
363
+ //If a normal element name is at the begining of the string,
364
+ //a leading whitespace might have been removed on whitespace collapsing and removal
365
+ //therefore there might be one whitespace less as selected element names
366
+ //this can lead to a too small specificity
367
+ //see _css_selector_to_xpath
368
+
369
+ if ( !in_array($selector[0], array(" ", ">", ".", "#", "+", ":", "["))/* && $selector !== "*"*/) {
370
+ $d++;
371
+ }
372
+
373
+ if (DEBUGCSS) {
374
+ /*DEBUGCSS*/ print "<pre>\n";
375
+ /*DEBUGCSS*/ printf("_specificity(): 0x%08x \"%s\"\n", ($a << 24) | ($b << 16) | ($c << 8) | ($d), $selector);
376
+ /*DEBUGCSS*/ print "</pre>";
377
+ }
378
+
379
+ return self::$_stylesheet_origins[$origin] + ($a << 24) | ($b << 16) | ($c << 8) | ($d);
380
+ }
381
+
382
+ /**
383
+ * Converts a CSS selector to an XPath query.
384
+ *
385
+ * @param string $selector
386
+ * @param bool $first_pass
387
+ *
388
+ * @throws DOMPDF_Exception
389
+ * @return string
390
+ */
391
+ private function _css_selector_to_xpath($selector, $first_pass = false) {
392
+
393
+ // Collapse white space and strip whitespace around delimiters
394
+ // $search = array("/\\s+/", "/\\s+([.>#+:])\\s+/");
395
+ // $replace = array(" ", "\\1");
396
+ // $selector = preg_replace($search, $replace, trim($selector));
397
+
398
+ // Initial query (non-absolute)
399
+ $query = "//";
400
+
401
+ // Will contain :before and :after if they must be created
402
+ $pseudo_elements = array();
403
+
404
+ // Parse the selector
405
+ //$s = preg_split("/([ :>.#+])/", $selector, -1, PREG_SPLIT_DELIM_CAPTURE);
406
+
407
+ $delimiters = array(" ", ">", ".", "#", "+", ":", "[", "(");
408
+
409
+ // Add an implicit * at the beginning of the selector
410
+ // if it begins with an attribute selector
411
+ if ( $selector[0] === "[" ) {
412
+ $selector = "*$selector";
413
+ }
414
+
415
+ // Add an implicit space at the beginning of the selector if there is no
416
+ // delimiter there already.
417
+ if ( !in_array($selector[0], $delimiters) ) {
418
+ $selector = " $selector";
419
+ }
420
+
421
+ $tok = "";
422
+ $len = mb_strlen($selector);
423
+ $i = 0;
424
+
425
+ while ( $i < $len ) {
426
+
427
+ $s = $selector[$i];
428
+ $i++;
429
+
430
+ // Eat characters up to the next delimiter
431
+ $tok = "";
432
+ $in_attr = false;
433
+
434
+ while ($i < $len) {
435
+ $c = $selector[$i];
436
+ $c_prev = $selector[$i-1];
437
+
438
+ if ( !$in_attr && in_array($c, $delimiters) ) {
439
+ break;
440
+ }
441
+
442
+ if ( $c_prev === "[" ) {
443
+ $in_attr = true;
444
+ }
445
+
446
+ $tok .= $selector[$i++];
447
+
448
+ if ( $in_attr && $c === "]" ) {
449
+ $in_attr = false;
450
+ break;
451
+ }
452
+ }
453
+
454
+ switch ($s) {
455
+
456
+ case " ":
457
+ case ">":
458
+ // All elements matching the next token that are direct children of
459
+ // the current token
460
+ $expr = $s === " " ? "descendant" : "child";
461
+
462
+ if ( mb_substr($query, -1, 1) !== "/" ) {
463
+ $query .= "/";
464
+ }
465
+
466
+ // Tag names are case-insensitive
467
+ $tok = strtolower($tok);
468
+
469
+ if ( !$tok ) {
470
+ $tok = "*";
471
+ }
472
+
473
+ $query .= "$expr::$tok";
474
+ $tok = "";
475
+ break;
476
+
477
+ case ".":
478
+ case "#":
479
+ // All elements matching the current token with a class/id equal to
480
+ // the _next_ token.
481
+
482
+ $attr = $s === "." ? "class" : "id";
483
+
484
+ // empty class/id == *
485
+ if ( mb_substr($query, -1, 1) === "/" ) {
486
+ $query .= "*";
487
+ }
488
+
489
+ // Match multiple classes: $tok contains the current selected
490
+ // class. Search for class attributes with class="$tok",
491
+ // class=".* $tok .*" and class=".* $tok"
492
+
493
+ // This doesn't work because libxml only supports XPath 1.0...
494
+ //$query .= "[matches(@$attr,\"^${tok}\$|^${tok}[ ]+|[ ]+${tok}\$|[ ]+${tok}[ ]+\")]";
495
+
496
+ // Query improvement by Michael Sheakoski <michael@mjsdigital.com>:
497
+ $query .= "[contains(concat(' ', @$attr, ' '), concat(' ', '$tok', ' '))]";
498
+ $tok = "";
499
+ break;
500
+
501
+ case "+":
502
+ // All sibling elements that folow the current token
503
+ if ( mb_substr($query, -1, 1) !== "/" ) {
504
+ $query .= "/";
505
+ }
506
+
507
+ $query .= "following-sibling::$tok";
508
+ $tok = "";
509
+ break;
510
+
511
+ case ":":
512
+ $i2 = $i-strlen($tok)-2; // the char before ":"
513
+ if ( !isset($selector[$i2]) || in_array($selector[$i2], $delimiters) ) {
514
+ $query .= "*";
515
+ }
516
+
517
+ $last = false;
518
+
519
+ // Pseudo-classes
520
+ switch ($tok) {
521
+
522
+ case "first-child":
523
+ $query .= "[1]";
524
+ $tok = "";
525
+ break;
526
+
527
+ case "last-child":
528
+ $query .= "[not(following-sibling::*)]";
529
+ $tok = "";
530
+ break;
531
+
532
+ case "first-of-type":
533
+ $query .= "[position() = 1]";
534
+ $tok = "";
535
+ break;
536
+
537
+ case "last-of-type":
538
+ $query .= "[position() = last()]";
539
+ $tok = "";
540
+ break;
541
+
542
+ // an+b, n, odd, and even
543
+ case "nth-last-of-type":
544
+ case "nth-last-child":
545
+ $last = true;
546
+
547
+ case "nth-of-type":
548
+ case "nth-child":
549
+ $p = $i+1;
550
+ $nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i)-$p));
551
+
552
+ // 1
553
+ if ( preg_match("/^\d+$/", $nth) ) {
554
+ $condition = "position() = $nth";
555
+ }
556
+
557
+ // odd
558
+ elseif ( $nth === "odd" ) {
559
+ $condition = "(position() mod 2) = 1";
560
+ }
561
+
562
+ // even
563
+ elseif ( $nth === "even" ) {
564
+ $condition = "(position() mod 2) = 0";
565
+ }
566
+
567
+ // an+b
568
+ else {
569
+ $condition = $this->_selector_an_plus_b($nth, $last);
570
+ }
571
+
572
+ $query .= "[$condition]";
573
+ $tok = "";
574
+ break;
575
+
576
+ case "link":
577
+ $query .= "[@href]";
578
+ $tok = "";
579
+ break;
580
+
581
+ case "first-line": // TODO
582
+ case "first-letter": // TODO
583
+
584
+ // N/A
585
+ case "active":
586
+ case "hover":
587
+ case "visited":
588
+ $query .= "[false()]";
589
+ $tok = "";
590
+ break;
591
+
592
+ /* Pseudo-elements */
593
+ case "before":
594
+ case "after":
595
+ if ( $first_pass ) {
596
+ $pseudo_elements[$tok] = $tok;
597
+ }
598
+ else {
599
+ $query .= "/*[@$tok]";
600
+ }
601
+
602
+ $tok = "";
603
+ break;
604
+
605
+ case "empty":
606
+ $query .= "[not(*) and not(normalize-space())]";
607
+ $tok = "";
608
+ break;
609
+
610
+ case "disabled":
611
+ case "checked":
612
+ $query .= "[@$tok]";
613
+ $tok = "";
614
+ break;
615
+
616
+ case "enabled":
617
+ $query .= "[not(@disabled)]";
618
+ $tok = "";
619
+ break;
620
+ }
621
+
622
+ break;
623
+
624
+ case "[":
625
+ // Attribute selectors. All with an attribute matching the following token(s)
626
+ $attr_delimiters = array("=", "]", "~", "|", "$", "^", "*");
627
+ $tok_len = mb_strlen($tok);
628
+ $j = 0;
629
+
630
+ $attr = "";
631
+ $op = "";
632
+ $value = "";
633
+
634
+ while ( $j < $tok_len ) {
635
+ if ( in_array($tok[$j], $attr_delimiters) ) {
636
+ break;
637
+ }
638
+ $attr .= $tok[$j++];
639
+ }
640
+
641
+ switch ( $tok[$j] ) {
642
+
643
+ case "~":
644
+ case "|":
645
+ case "$":
646
+ case "^":
647
+ case "*":
648
+ $op .= $tok[$j++];
649
+
650
+ if ( $tok[$j] !== "=" ) {
651
+ throw new DOMPDF_Exception("Invalid CSS selector syntax: invalid attribute selector: $selector");
652
+ }
653
+
654
+ $op .= $tok[$j];
655
+ break;
656
+
657
+ case "=":
658
+ $op = "=";
659
+ break;
660
+
661
+ }
662
+
663
+ // Read the attribute value, if required
664
+ if ( $op != "" ) {
665
+ $j++;
666
+ while ( $j < $tok_len ) {
667
+ if ( $tok[$j] === "]" ) {
668
+ break;
669
+ }
670
+ $value .= $tok[$j++];
671
+ }
672
+ }
673
+
674
+ if ( $attr == "" ) {
675
+ throw new DOMPDF_Exception("Invalid CSS selector syntax: missing attribute name");
676
+ }
677
+
678
+ $value = trim($value, "\"'");
679
+
680
+ switch ( $op ) {
681
+
682
+ case "":
683
+ $query .= "[@$attr]";
684
+ break;
685
+
686
+ case "=":
687
+ $query .= "[@$attr=\"$value\"]";
688
+ break;
689
+
690
+ case "~=":
691
+ // FIXME: this will break if $value contains quoted strings
692
+ // (e.g. [type~="a b c" "d e f"])
693
+ $values = explode(" ", $value);
694
+ $query .= "[";
695
+
696
+ foreach ( $values as $val ) {
697
+ $query .= "@$attr=\"$val\" or ";
698
+ }
699
+
700
+ $query = rtrim($query, " or ") . "]";
701
+ break;
702
+
703
+ case "|=":
704
+ $values = explode("-", $value);
705
+ $query .= "[";
706
+
707
+ foreach ( $values as $val ) {
708
+ $query .= "starts-with(@$attr, \"$val\") or ";
709
+ }
710
+
711
+ $query = rtrim($query, " or ") . "]";
712
+ break;
713
+
714
+ case "$=":
715
+ $query .= "[substring(@$attr, string-length(@$attr)-".(strlen($value) - 1).")=\"$value\"]";
716
+ break;
717
+
718
+ case "^=":
719
+ $query .= "[starts-with(@$attr,\"$value\")]";
720
+ break;
721
+
722
+ case "*=":
723
+ $query .= "[contains(@$attr,\"$value\")]";
724
+ break;
725
+ }
726
+
727
+ break;
728
+ }
729
+ }
730
+ $i++;
731
+
732
+ // case ":":
733
+ // // Pseudo selectors: ignore for now. Partially handled directly
734
+ // // below.
735
+
736
+ // // Skip until the next special character, leaving the token as-is
737
+ // while ( $i < $len ) {
738
+ // if ( in_array($selector[$i], $delimiters) )
739
+ // break;
740
+ // $i++;
741
+ // }
742
+ // break;
743
+
744
+ // default:
745
+ // // Add the character to the token
746
+ // $tok .= $selector[$i++];
747
+ // break;
748
+ // }
749
+
750
+ // }
751
+
752
+
753
+ // Trim the trailing '/' from the query
754
+ if ( mb_strlen($query) > 2 ) {
755
+ $query = rtrim($query, "/");
756
+ }
757
+
758
+ return array("query" => $query, "pseudo_elements" => $pseudo_elements);
759
+ }
760
+
761
+ // https://github.com/tenderlove/nokogiri/blob/master/lib/nokogiri/css/xpath_visitor.rb
762
+ protected function _selector_an_plus_b($expr, $last = false) {
763
+ $expr = preg_replace("/\s/", "", $expr);
764
+ if ( !preg_match("/^(?P<a>-?[0-9]*)?n(?P<b>[-+]?[0-9]+)?$/", $expr, $matches)) {
765
+ return "false()";
766
+ }
767
+
768
+ $a = ((isset($matches["a"]) && $matches["a"] !== "") ? intval($matches["a"]) : 1);
769
+ $b = ((isset($matches["b"]) && $matches["b"] !== "") ? intval($matches["b"]) : 0);
770
+
771
+ $position = ($last ? "(last()-position()+1)" : "position()");
772
+
773
+ if ( $b == 0 ) {
774
+ return "($position mod $a) = 0";
775
+ }
776
+ else {
777
+ $compare = (($a < 0) ? "<=" : ">=");
778
+ $b2 = -$b;
779
+ if ( $b2 >= 0 ) {
780
+ $b2 = "+$b2";
781
+ }
782
+ return "($position $compare $b) and ((($position $b2) mod ".abs($a).") = 0)";
783
+ }
784
+ }
785
+
786
+ /**
787
+ * applies all current styles to a particular document tree
788
+ *
789
+ * apply_styles() applies all currently loaded styles to the provided
790
+ * {@link Frame_Tree}. Aside from parsing CSS, this is the main purpose
791
+ * of this class.
792
+ *
793
+ * @param Frame_Tree $tree
794
+ */
795
+ function apply_styles(Frame_Tree $tree) {
796
+ // Use XPath to select nodes. This would be easier if we could attach
797
+ // Frame objects directly to DOMNodes using the setUserData() method, but
798
+ // we can't do that just yet. Instead, we set a _node attribute_ in
799
+ // Frame->set_id() and use that as a handle on the Frame object via
800
+ // Frame_Tree::$_registry.
801
+
802
+ // We create a scratch array of styles indexed by frame id. Once all
803
+ // styles have been assigned, we order the cached styles by specificity
804
+ // and create a final style object to assign to the frame.
805
+
806
+ // FIXME: this is not particularly robust...
807
+
808
+ $styles = array();
809
+ $xp = new DOMXPath($tree->get_dom());
810
+
811
+ // Add generated content
812
+ foreach ($this->_styles as $selector => $style) {
813
+ if ( strpos($selector, ":before") === false && strpos($selector, ":after") === false ) {
814
+ continue;
815
+ }
816
+
817
+ $query = $this->_css_selector_to_xpath($selector, true);
818
+
819
+ // Retrieve the nodes
820
+ $nodes = @$xp->query($query["query"]);
821
+ if ( $nodes == null ) {
822
+ record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__);
823
+ continue;
824
+ }
825
+
826
+ foreach ($nodes as $node) {
827
+ foreach ($query["pseudo_elements"] as $pos) {
828
+ // Do not add a new pseudo element if another one already matched
829
+ if ( $node->hasAttribute("dompdf_{$pos}_frame_id") ) {
830
+ continue;
831
+ }
832
+
833
+ if (($src = $this->_image($style->content)) !== "none") {
834
+ $new_node = $node->ownerDocument->createElement("img_generated");
835
+ $new_node->setAttribute("src", $src);
836
+ }
837
+ else {
838
+ $new_node = $node->ownerDocument->createElement("dompdf_generated");
839
+ }
840
+
841
+ $new_node->setAttribute($pos, $pos);
842
+
843
+ $new_frame_id = $tree->insert_node($node, $new_node, $pos);
844
+ $node->setAttribute("dompdf_{$pos}_frame_id", $new_frame_id);
845
+ }
846
+ }
847
+ }
848
+
849
+ // Apply all styles in stylesheet
850
+ foreach ($this->_styles as $selector => $style) {
851
+ $query = $this->_css_selector_to_xpath($selector);
852
+
853
+ // Retrieve the nodes
854
+ $nodes = @$xp->query($query["query"]);
855
+ if ( $nodes == null ) {
856
+ record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__);
857
+ continue;
858
+ }
859
+
860
+ foreach ($nodes as $node) {
861
+ // Retrieve the node id
862
+ // Only DOMElements get styles
863
+ if ( $node->nodeType != XML_ELEMENT_NODE ) {
864
+ continue;
865
+ }
866
+
867
+ $id = $node->getAttribute("frame_id");
868
+
869
+ // Assign the current style to the scratch array
870
+ $spec = $this->_specificity($selector);
871
+ $styles[$id][$spec][] = $style;
872
+ }
873
+ }
874
+
875
+ // Now create the styles and assign them to the appropriate frames. (We
876
+ // iterate over the tree using an implicit Frame_Tree iterator.)
877
+ $root_flg = false;
878
+ foreach ($tree->get_frames() as $frame) {
879
+ // pre_r($frame->get_node()->nodeName . ":");
880
+ if ( !$root_flg && $this->_page_styles["base"] ) {
881
+ $style = $this->_page_styles["base"];
882
+ $root_flg = true;
883
+ }
884
+ else {
885
+ $style = $this->create_style();
886
+ }
887
+
888
+ // Find nearest DOMElement parent
889
+ $p = $frame;
890
+ while ( $p = $p->get_parent() ) {
891
+ if ( $p->get_node()->nodeType == XML_ELEMENT_NODE ) {
892
+ break;
893
+ }
894
+ }
895
+
896
+ // Styles can only be applied directly to DOMElements; anonymous
897
+ // frames inherit from their parent
898
+ if ( $frame->get_node()->nodeType != XML_ELEMENT_NODE ) {
899
+ if ( $p ) {
900
+ $style->inherit($p->get_style());
901
+ }
902
+
903
+ $frame->set_style($style);
904
+ continue;
905
+ }
906
+
907
+ $id = $frame->get_id();
908
+
909
+ // Handle HTML 4.0 attributes
910
+ Attribute_Translator::translate_attributes($frame);
911
+ if ( ($str = $frame->get_node()->getAttribute(Attribute_Translator::$_style_attr)) !== "" ) {
912
+ // Lowest specificity
913
+ $styles[$id][1][] = $this->_parse_properties($str);
914
+ }
915
+
916
+ // Locate any additional style attributes
917
+ if ( ($str = $frame->get_node()->getAttribute("style")) !== "" ) {
918
+ // Destroy CSS comments
919
+ $str = preg_replace("'/\*.*?\*/'si", "", $str);
920
+
921
+ $spec = $this->_specificity("!attr");
922
+ $styles[$id][$spec][] = $this->_parse_properties($str);
923
+ }
924
+
925
+ // Grab the applicable styles
926
+ if ( isset($styles[$id]) ) {
927
+
928
+ $applied_styles = $styles[ $frame->get_id() ];
929
+
930
+ // Sort by specificity
931
+ ksort($applied_styles);
932
+
933
+ if (DEBUGCSS) {
934
+ $debug_nodename = $frame->get_node()->nodeName;
935
+ print "<pre>\n[$debug_nodename\n";
936
+ foreach ($applied_styles as $spec => $arr) {
937
+ printf("specificity: 0x%08x\n",$spec);
938
+ foreach ($arr as $s) {
939
+ print "[\n";
940
+ $s->debug_print();
941
+ print "]\n";
942
+ }
943
+ }
944
+ }
945
+
946
+ // Merge the new styles with the inherited styles
947
+ foreach ($applied_styles as $arr) {
948
+ foreach ($arr as $s) {
949
+ $style->merge($s);
950
+ }
951
+ }
952
+ }
953
+
954
+ // Inherit parent's styles if required
955
+ if ( $p ) {
956
+
957
+ if (DEBUGCSS) {
958
+ print "inherit:\n";
959
+ print "[\n";
960
+ $p->get_style()->debug_print();
961
+ print "]\n";
962
+ }
963
+
964
+ $style->inherit( $p->get_style() );
965
+ }
966
+
967
+ if (DEBUGCSS) {
968
+ print "DomElementStyle:\n";
969
+ print "[\n";
970
+ $style->debug_print();
971
+ print "]\n";
972
+ print "/$debug_nodename]\n</pre>";
973
+ }
974
+
975
+ /*DEBUGCSS print: see below different print debugging method
976
+ pre_r($frame->get_node()->nodeName . ":");
977
+ echo "<pre>";
978
+ echo $style;
979
+ echo "</pre>";*/
980
+ $frame->set_style($style);
981
+
982
+ }
983
+
984
+ // We're done! Clean out the registry of all styles since we
985
+ // won't be needing this later.
986
+ foreach ( array_keys($this->_styles) as $key ) {
987
+ $this->_styles[$key] = null;
988
+ unset($this->_styles[$key]);
989
+ }
990
+
991
+ }
992
+
993
+ /**
994
+ * parse a CSS string using a regex parser
995
+ * Called by {@link Stylesheet::parse_css()}
996
+ *
997
+ * @param string $str
998
+ *
999
+ * @throws DOMPDF_Exception
1000
+ */
1001
+ private function _parse_css($str) {
1002
+
1003
+ $str = trim($str);
1004
+
1005
+ // Destroy comments and remove HTML comments
1006
+ $css = preg_replace(array(
1007
+ "'/\*.*?\*/'si",
1008
+ "/^<!--/",
1009
+ "/-->$/"
1010
+ ), "", $str);
1011
+
1012
+ // FIXME: handle '{' within strings, e.g. [attr="string {}"]
1013
+
1014
+ // Something more legible:
1015
+ $re =
1016
+ "/\s* # Skip leading whitespace \n".
1017
+ "( @([^\s{]+)\s*([^{;]*) (?:;|({)) )? # Match @rules followed by ';' or '{' \n".
1018
+ "(?(1) # Only parse sub-sections if we're in an @rule... \n".
1019
+ " (?(4) # ...and if there was a leading '{' \n".
1020
+ " \s*( (?:(?>[^{}]+) ({)? # Parse rulesets and individual @page rules \n".
1021
+ " (?(6) (?>[^}]*) }) \s*)+? \n".
1022
+ " ) \n".
1023
+ " }) # Balancing '}' \n".
1024
+ "| # Branch to match regular rules (not preceeded by '@')\n".
1025
+ "([^{]*{[^}]*})) # Parse normal rulesets\n".
1026
+ "/xs";
1027
+
1028
+ if ( preg_match_all($re, $css, $matches, PREG_SET_ORDER) === false ) {
1029
+ // An error occured
1030
+ throw new DOMPDF_Exception("Error parsing css file: preg_match_all() failed.");
1031
+ }
1032
+
1033
+ // After matching, the array indicies are set as follows:
1034
+ //
1035
+ // [0] => complete text of match
1036
+ // [1] => contains '@import ...;' or '@media {' if applicable
1037
+ // [2] => text following @ for cases where [1] is set
1038
+ // [3] => media types or full text following '@import ...;'
1039
+ // [4] => '{', if present
1040
+ // [5] => rulesets within media rules
1041
+ // [6] => '{', within media rules
1042
+ // [7] => individual rules, outside of media rules
1043
+ //
1044
+ //pre_r($matches);
1045
+ foreach ( $matches as $match ) {
1046
+ $match[2] = trim($match[2]);
1047
+
1048
+ if ( $match[2] !== "" ) {
1049
+ // Handle @rules
1050
+ switch ($match[2]) {
1051
+
1052
+ case "import":
1053
+ $this->_parse_import($match[3]);
1054
+ break;
1055
+
1056
+ case "media":
1057
+ $acceptedmedia = self::$ACCEPTED_GENERIC_MEDIA_TYPES;
1058
+ $acceptedmedia[] = $this->_dompdf->get_option("default_media_type");
1059
+
1060
+ $media = preg_split("/\s*,\s*/", mb_strtolower(trim($match[3])));
1061
+
1062
+ if ( count(array_intersect($acceptedmedia, $media)) ) {
1063
+ $this->_parse_sections($match[5]);
1064
+ }
1065
+ break;
1066
+
1067
+ case "page":
1068
+ //This handles @page to be applied to page oriented media
1069
+ //Note: This has a reduced syntax:
1070
+ //@page { margin:1cm; color:blue; }
1071
+ //Not a sequence of styles like a full.css, but only the properties
1072
+ //of a single style, which is applied to the very first "root" frame before
1073
+ //processing other styles of the frame.
1074
+ //Working properties:
1075
+ // margin (for margin around edge of paper)
1076
+ // font-family (default font of pages)
1077
+ // color (default text color of pages)
1078
+ //Non working properties:
1079
+ // border
1080
+ // padding
1081
+ // background-color
1082
+ //Todo:Reason is unknown
1083
+ //Other properties (like further font or border attributes) not tested.
1084
+ //If a border or background color around each paper sheet is desired,
1085
+ //assign it to the <body> tag, possibly only for the css of the correct media type.
1086
+
1087
+ // If the page has a name, skip the style.
1088
+ $page_selector = trim($match[3]);
1089
+
1090
+ $key = null;
1091
+ switch($page_selector) {
1092
+ case "":
1093
+ $key = "base";
1094
+ break;
1095
+
1096
+ case ":left":
1097
+ case ":right":
1098
+ case ":odd":
1099
+ case ":even":
1100
+ case ":first":
1101
+ $key = $page_selector;
1102
+
1103
+ default: continue;
1104
+ }
1105
+
1106
+ // Store the style for later...
1107
+ if ( empty($this->_page_styles[$key]) ) {
1108
+ $this->_page_styles[$key] = $this->_parse_properties($match[5]);
1109
+ }
1110
+ else {
1111
+ $this->_page_styles[$key]->merge($this->_parse_properties($match[5]));
1112
+ }
1113
+ break;
1114
+
1115
+ case "font-face":
1116
+ $this->_parse_font_face($match[5]);
1117
+ break;
1118
+
1119
+ default:
1120
+ // ignore everything else
1121
+ break;
1122
+ }
1123
+
1124
+ continue;
1125
+ }
1126
+
1127
+ if ( $match[7] !== "" ) {
1128
+ $this->_parse_sections($match[7]);
1129
+ }
1130
+
1131
+ }
1132
+ }
1133
+
1134
+ /* See also style.cls Style::_image(), refactoring?, works also for imported css files */
1135
+ protected function _image($val) {
1136
+ $DEBUGCSS=DEBUGCSS;
1137
+ $parsed_url = "none";
1138
+
1139
+ if ( mb_strpos($val, "url") === false ) {
1140
+ $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none
1141
+ }
1142
+ else {
1143
+ $val = preg_replace("/url\(['\"]?([^'\")]+)['\"]?\)/","\\1", trim($val));
1144
+
1145
+ // Resolve the url now in the context of the current stylesheet
1146
+ $parsed_url = explode_url($val);
1147
+ if ( $parsed_url["protocol"] == "" && $this->get_protocol() == "" ) {
1148
+ if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\' ) {
1149
+ $path = $_SERVER["DOCUMENT_ROOT"].'/';
1150
+ }
1151
+ else {
1152
+ $path = $this->get_base_path();
1153
+ }
1154
+
1155
+ $path .= $parsed_url["path"] . $parsed_url["file"];
1156
+ $path = realpath($path);
1157
+ // If realpath returns FALSE then specifically state that there is no background image
1158
+ // FIXME: Is this causing problems for imported CSS files? There are some './none' references when running the test cases.
1159
+ if (!$path) { $path = 'none'; }
1160
+ }
1161
+ else {
1162
+ $path = build_url($this->get_protocol(),
1163
+ $this->get_host(),
1164
+ $this->get_base_path(),
1165
+ $val);
1166
+ }
1167
+ }
1168
+
1169
+ if ($DEBUGCSS) {
1170
+ print "<pre>[_image\n";
1171
+ print_r($parsed_url);
1172
+ print $this->get_protocol()."\n".$this->get_base_path()."\n".$path."\n";
1173
+ print "_image]</pre>";;
1174
+ }
1175
+
1176
+ return $path;
1177
+ }
1178
+
1179
+ /**
1180
+ * parse @import{} sections
1181
+ *
1182
+ * @param string $url the url of the imported CSS file
1183
+ */
1184
+ private function _parse_import($url) {
1185
+ $arr = preg_split("/[\s\n,]/", $url,-1, PREG_SPLIT_NO_EMPTY);
1186
+ $url = array_shift($arr);
1187
+ $accept = false;
1188
+
1189
+ if ( count($arr) > 0 ) {
1190
+ $acceptedmedia = self::$ACCEPTED_GENERIC_MEDIA_TYPES;
1191
+ $acceptedmedia[] = $this->_dompdf->get_option("default_media_type");
1192
+
1193
+ // @import url media_type [media_type...]
1194
+ foreach ( $arr as $type ) {
1195
+ if ( in_array(mb_strtolower(trim($type)), $acceptedmedia) ) {
1196
+ $accept = true;
1197
+ break;
1198
+ }
1199
+ }
1200
+
1201
+ }
1202
+ else {
1203
+ // unconditional import
1204
+ $accept = true;
1205
+ }
1206
+
1207
+ if ( $accept ) {
1208
+ // Store our current base url properties in case the new url is elsewhere
1209
+ $protocol = $this->_protocol;
1210
+ $host = $this->_base_host;
1211
+ $path = $this->_base_path;
1212
+
1213
+ // $url = str_replace(array('"',"url", "(", ")"), "", $url);
1214
+ // If the protocol is php, assume that we will import using file://
1215
+ // $url = build_url($protocol == "php://" ? "file://" : $protocol, $host, $path, $url);
1216
+ // Above does not work for subfolders and absolute urls.
1217
+ // Todo: As above, do we need to replace php or file to an empty protocol for local files?
1218
+
1219
+ $url = $this->_image($url);
1220
+
1221
+ $this->load_css_file($url);
1222
+
1223
+ // Restore the current base url
1224
+ $this->_protocol = $protocol;
1225
+ $this->_base_host = $host;
1226
+ $this->_base_path = $path;
1227
+ }
1228
+
1229
+ }
1230
+
1231
+ /**
1232
+ * parse @font-face{} sections
1233
+ * http://www.w3.org/TR/css3-fonts/#the-font-face-rule
1234
+ *
1235
+ * @param string $str CSS @font-face rules
1236
+ * @return Style
1237
+ */
1238
+ private function _parse_font_face($str) {
1239
+ $descriptors = $this->_parse_properties($str);
1240
+
1241
+ preg_match_all("/(url|local)\s*\([\"\']?([^\"\'\)]+)[\"\']?\)\s*(format\s*\([\"\']?([^\"\'\)]+)[\"\']?\))?/i", $descriptors->src, $src);
1242
+
1243
+ $sources = array();
1244
+ $valid_sources = array();
1245
+
1246
+ foreach($src[0] as $i => $value) {
1247
+ $source = array(
1248
+ "local" => strtolower($src[1][$i]) === "local",
1249
+ "uri" => $src[2][$i],
1250
+ "format" => $src[4][$i],
1251
+ "path" => build_url($this->_protocol, $this->_base_host, $this->_base_path, $src[2][$i]),
1252
+ );
1253
+
1254
+ if ( !$source["local"] && in_array($source["format"], array("", "woff", "opentype", "truetype")) ) {
1255
+ $valid_sources[] = $source;
1256
+ }
1257
+
1258
+ $sources[] = $source;
1259
+ }
1260
+
1261
+ // No valid sources
1262
+ if ( empty($valid_sources) ) {
1263
+ return;
1264
+ }
1265
+
1266
+ $style = array(
1267
+ "family" => $descriptors->get_font_family_raw(),
1268
+ "weight" => $descriptors->font_weight,
1269
+ "style" => $descriptors->font_style,
1270
+ );
1271
+
1272
+ Font_Metrics::register_font($style, $valid_sources[0]["path"]);
1273
+ }
1274
+
1275
+ /**
1276
+ * parse regular CSS blocks
1277
+ *
1278
+ * _parse_properties() creates a new Style object based on the provided
1279
+ * CSS rules.
1280
+ *
1281
+ * @param string $str CSS rules
1282
+ * @return Style
1283
+ */
1284
+ private function _parse_properties($str) {
1285
+ $properties = preg_split("/;(?=(?:[^\(]*\([^\)]*\))*(?![^\)]*\)))/", $str);
1286
+
1287
+ if (DEBUGCSS) print '[_parse_properties';
1288
+
1289
+ // Create the style
1290
+ $style = new Style($this);
1291
+
1292
+ foreach ($properties as $prop) {
1293
+ // If the $prop contains an url, the regex may be wrong
1294
+ // @todo: fix the regex so that it works everytime
1295
+ /*if (strpos($prop, "url(") === false) {
1296
+ if (preg_match("/([a-z-]+)\s*:\s*[^:]+$/i", $prop, $m))
1297
+ $prop = $m[0];
1298
+ }*/
1299
+ //A css property can have " ! important" appended (whitespace optional)
1300
+ //strip this off to decode core of the property correctly.
1301
+ //Pass on in the style to allow proper handling:
1302
+ //!important properties can only be overridden by other !important ones.
1303
+ //$style->$prop_name = is a shortcut of $style->__set($prop_name,$value);.
1304
+ //If no specific set function available, set _props["prop_name"]
1305
+ //style is always copied completely, or $_props handled separately
1306
+ //Therefore set a _important_props["prop_name"]=true to indicate the modifier
1307
+
1308
+ /* Instead of short code, prefer the typical case with fast code
1309
+ $important = preg_match("/(.*?)!\s*important/",$prop,$match);
1310
+ if ( $important ) {
1311
+ $prop = $match[1];
1312
+ }
1313
+ $prop = trim($prop);
1314
+ */
1315
+ if (DEBUGCSS) print '(';
1316
+
1317
+ $important = false;
1318
+ $prop = trim($prop);
1319
+
1320
+ if ( substr($prop, -9) === 'important' ) {
1321
+ $prop_tmp = rtrim(substr($prop, 0, -9));
1322
+
1323
+ if ( substr($prop_tmp, -1) === '!' ) {
1324
+ $prop = rtrim(substr($prop_tmp, 0, -1));
1325
+ $important = true;
1326
+ }
1327
+ }
1328
+
1329
+ if ( $prop === "" ) {
1330
+ if (DEBUGCSS) print 'empty)';
1331
+ continue;
1332
+ }
1333
+
1334
+ $i = mb_strpos($prop, ":");
1335
+ if ( $i === false ) {
1336
+ if (DEBUGCSS) print 'novalue'.$prop.')';
1337
+ continue;
1338
+ }
1339
+
1340
+ $prop_name = rtrim(mb_strtolower(mb_substr($prop, 0, $i)));
1341
+ $value = ltrim(mb_substr($prop, $i+1));
1342
+ if (DEBUGCSS) print $prop_name.':='.$value.($important?'!IMPORTANT':'').')';
1343
+ //New style, anyway empty
1344
+ //if ($important || !$style->important_get($prop_name) ) {
1345
+ //$style->$prop_name = array($value,$important);
1346
+ //assignment might be replaced by overloading through __set,
1347
+ //and overloaded functions might check _important_props,
1348
+ //therefore set _important_props first.
1349
+ if ($important) {
1350
+ $style->important_set($prop_name);
1351
+ }
1352
+ //For easier debugging, don't use overloading of assignments with __set
1353
+ $style->$prop_name = $value;
1354
+ //$style->props_set($prop_name, $value);
1355
+ }
1356
+ if (DEBUGCSS) print '_parse_properties]';
1357
+
1358
+ return $style;
1359
+ }
1360
+
1361
+ /**
1362
+ * parse selector + rulesets
1363
+ *
1364
+ * @param string $str CSS selectors and rulesets
1365
+ */
1366
+ private function _parse_sections($str) {
1367
+ // Pre-process: collapse all whitespace and strip whitespace around '>',
1368
+ // '.', ':', '+', '#'
1369
+
1370
+ $patterns = array("/[\\s\n]+/", "/\\s+([>.:+#])\\s+/");
1371
+ $replacements = array(" ", "\\1");
1372
+ $str = preg_replace($patterns, $replacements, $str);
1373
+
1374
+ $sections = explode("}", $str);
1375
+ if (DEBUGCSS) print '[_parse_sections';
1376
+ foreach ($sections as $sect) {
1377
+ $i = mb_strpos($sect, "{");
1378
+
1379
+ $selectors = explode(",", mb_substr($sect, 0, $i));
1380
+ if (DEBUGCSS) print '[section';
1381
+ $style = $this->_parse_properties(trim(mb_substr($sect, $i+1)));
1382
+
1383
+ // Assign it to the selected elements
1384
+ foreach ($selectors as $selector) {
1385
+ $selector = trim($selector);
1386
+
1387
+ if ($selector == "") {
1388
+ if (DEBUGCSS) print '#empty#';
1389
+ continue;
1390
+ }
1391
+ if (DEBUGCSS) print '#'.$selector.'#';
1392
+ //if (DEBUGCSS) { if (strpos($selector,'p') !== false) print '!!!p!!!#'; }
1393
+
1394
+ $this->add_style($selector, $style);
1395
+ }
1396
+
1397
+ if (DEBUGCSS) print 'section]';
1398
+ }
1399
+
1400
+ if (DEBUGCSS) print '_parse_sections]';
1401
+ }
1402
+
1403
+ /**
1404
+ * dumps the entire stylesheet as a string
1405
+ *
1406
+ * Generates a string of each selector and associated style in the
1407
+ * Stylesheet. Useful for debugging.
1408
+ *
1409
+ * @return string
1410
+ */
1411
+ function __toString() {
1412
+ $str = "";
1413
+ foreach ($this->_styles as $selector => $style) {
1414
+ $str .= "$selector => " . $style->__toString() . "\n";
1415
+ }
1416
+
1417
+ return $str;
1418
+ }
1419
+ }
lib/dompdf/include/table_cell_frame_decorator.cls.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates table cells for layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Cell_Frame_Decorator extends Block_Frame_Decorator {
16
+
17
+ protected $_resolved_borders;
18
+ protected $_content_height;
19
+
20
+ //........................................................................
21
+
22
+ function __construct(Frame $frame, DOMPDF $dompdf) {
23
+ parent::__construct($frame, $dompdf);
24
+ $this->_resolved_borders = array();
25
+ $this->_content_height = 0;
26
+ }
27
+
28
+ //........................................................................
29
+
30
+ function reset() {
31
+ parent::reset();
32
+ $this->_resolved_borders = array();
33
+ $this->_content_height = 0;
34
+ $this->_frame->reset();
35
+ }
36
+
37
+ function get_content_height() {
38
+ return $this->_content_height;
39
+ }
40
+
41
+ function set_content_height($height) {
42
+ $this->_content_height = $height;
43
+ }
44
+
45
+ function set_cell_height($height) {
46
+ $style = $this->get_style();
47
+ $v_space = $style->length_in_pt(array($style->margin_top,
48
+ $style->padding_top,
49
+ $style->border_top_width,
50
+ $style->border_bottom_width,
51
+ $style->padding_bottom,
52
+ $style->margin_bottom),
53
+ $style->width);
54
+
55
+ $new_height = $height - $v_space;
56
+ $style->height = $new_height;
57
+
58
+ if ( $new_height > $this->_content_height ) {
59
+ $y_offset = 0;
60
+
61
+ // Adjust our vertical alignment
62
+ switch ($style->vertical_align) {
63
+ default:
64
+ case "baseline":
65
+ // FIXME: this isn't right
66
+
67
+ case "top":
68
+ // Don't need to do anything
69
+ return;
70
+
71
+ case "middle":
72
+ $y_offset = ($new_height - $this->_content_height) / 2;
73
+ break;
74
+
75
+ case "bottom":
76
+ $y_offset = $new_height - $this->_content_height;
77
+ break;
78
+ }
79
+
80
+ if ( $y_offset ) {
81
+ // Move our children
82
+ foreach ( $this->get_line_boxes() as $line ) {
83
+ foreach ( $line->get_frames() as $frame )
84
+ $frame->move( 0, $y_offset );
85
+ }
86
+ }
87
+ }
88
+
89
+ }
90
+
91
+ function set_resolved_border($side, $border_spec) {
92
+ $this->_resolved_borders[$side] = $border_spec;
93
+ }
94
+
95
+ //........................................................................
96
+
97
+ function get_resolved_border($side) {
98
+ return $this->_resolved_borders[$side];
99
+ }
100
+
101
+ function get_resolved_borders() { return $this->_resolved_borders; }
102
+ }
lib/dompdf/include/table_cell_frame_reflower.cls.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows table cells
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Cell_Frame_Reflower extends Block_Frame_Reflower {
16
+
17
+ //........................................................................
18
+
19
+ function __construct(Block_Frame_Decorator $frame) {
20
+ parent::__construct($frame);
21
+ }
22
+
23
+ //........................................................................
24
+
25
+ function reflow(Block_Frame_Decorator $block = null) {
26
+
27
+ $style = $this->_frame->get_style();
28
+
29
+ $table = Table_Frame_Decorator::find_parent_table($this->_frame);
30
+ $cellmap = $table->get_cellmap();
31
+
32
+ list($x, $y) = $cellmap->get_frame_position($this->_frame);
33
+ $this->_frame->set_position($x, $y);
34
+
35
+ $cells = $cellmap->get_spanned_cells($this->_frame);
36
+
37
+ $w = 0;
38
+ foreach ( $cells["columns"] as $i ) {
39
+ $col = $cellmap->get_column( $i );
40
+ $w += $col["used-width"];
41
+ }
42
+
43
+ //FIXME?
44
+ $h = $this->_frame->get_containing_block("h");
45
+
46
+ $left_space = $style->length_in_pt(array($style->margin_left,
47
+ $style->padding_left,
48
+ $style->border_left_width),
49
+ $w);
50
+
51
+ $right_space = $style->length_in_pt(array($style->padding_right,
52
+ $style->margin_right,
53
+ $style->border_right_width),
54
+ $w);
55
+
56
+ $top_space = $style->length_in_pt(array($style->margin_top,
57
+ $style->padding_top,
58
+ $style->border_top_width),
59
+ $h);
60
+ $bottom_space = $style->length_in_pt(array($style->margin_bottom,
61
+ $style->padding_bottom,
62
+ $style->border_bottom_width),
63
+ $h);
64
+
65
+ $style->width = $cb_w = $w - $left_space - $right_space;
66
+
67
+ $content_x = $x + $left_space;
68
+ $content_y = $line_y = $y + $top_space;
69
+
70
+ // Adjust the first line based on the text-indent property
71
+ $indent = $style->length_in_pt($style->text_indent, $w);
72
+ $this->_frame->increase_line_width($indent);
73
+
74
+ $page = $this->_frame->get_root();
75
+
76
+ // Set the y position of the first line in the cell
77
+ $line_box = $this->_frame->get_current_line_box();
78
+ $line_box->y = $line_y;
79
+
80
+ // Set the containing blocks and reflow each child
81
+ foreach ( $this->_frame->get_children() as $child ) {
82
+
83
+ if ( $page->is_full() )
84
+ break;
85
+
86
+ $child->set_containing_block($content_x, $content_y, $cb_w, $h);
87
+
88
+ $this->process_clear($child);
89
+
90
+ $child->reflow($this->_frame);
91
+
92
+ $this->process_float($child, $x + $left_space, $w - $right_space - $left_space);
93
+ }
94
+
95
+ // Determine our height
96
+ $style_height = $style->length_in_pt($style->height, $h);
97
+
98
+ $this->_frame->set_content_height($this->_calculate_content_height());
99
+
100
+ $height = max($style_height, $this->_frame->get_content_height());
101
+
102
+ // Let the cellmap know our height
103
+ $cell_height = $height / count($cells["rows"]);
104
+
105
+ if ($style_height <= $height)
106
+ $cell_height += $top_space + $bottom_space;
107
+
108
+ foreach ($cells["rows"] as $i)
109
+ $cellmap->set_row_height($i, $cell_height);
110
+
111
+ $style->height = $height;
112
+
113
+ $this->_text_align();
114
+
115
+ $this->vertical_align();
116
+
117
+ }
118
+
119
+ }
lib/dompdf/include/table_cell_positioner.cls.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions table cells
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Cell_Positioner extends Positioner {
16
+
17
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function position() {
22
+
23
+ $table = Table_Frame_Decorator::find_parent_table($this->_frame);
24
+ $cellmap = $table->get_cellmap();
25
+ $this->_frame->set_position($cellmap->get_frame_position($this->_frame));
26
+
27
+ }
28
+ }
lib/dompdf/include/table_cell_renderer.cls.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Renders table cells
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Cell_Renderer extends Block_Renderer {
16
+
17
+ //........................................................................
18
+
19
+ function render(Frame $frame) {
20
+ $style = $frame->get_style();
21
+
22
+ if ( trim($frame->get_node()->nodeValue) === "" && $style->empty_cells === "hide" ) {
23
+ return;
24
+ }
25
+
26
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
27
+ list($x, $y, $w, $h) = $frame->get_border_box();
28
+
29
+ // Draw our background, border and content
30
+ if ( ($bg = $style->background_color) !== "transparent" ) {
31
+ $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
32
+ }
33
+
34
+ if ( ($url = $style->background_image) && $url !== "none" ) {
35
+ $this->_background_image($url, $x, $y, $w, $h, $style);
36
+ }
37
+
38
+ $table = Table_Frame_Decorator::find_parent_table($frame);
39
+
40
+ if ( $table->get_style()->border_collapse !== "collapse" ) {
41
+ $this->_render_border($frame);
42
+ $this->_render_outline($frame);
43
+ return;
44
+ }
45
+
46
+ // The collapsed case is slightly complicated...
47
+ // @todo Add support for outlines here
48
+
49
+ $cellmap = $table->get_cellmap();
50
+ $cells = $cellmap->get_spanned_cells($frame);
51
+ $num_rows = $cellmap->get_num_rows();
52
+ $num_cols = $cellmap->get_num_cols();
53
+
54
+ // Determine the top row spanned by this cell
55
+ $i = $cells["rows"][0];
56
+ $top_row = $cellmap->get_row($i);
57
+
58
+ // Determine if this cell borders on the bottom of the table. If so,
59
+ // then we draw its bottom border. Otherwise the next row down will
60
+ // draw its top border instead.
61
+ if (in_array( $num_rows - 1, $cells["rows"])) {
62
+ $draw_bottom = true;
63
+ $bottom_row = $cellmap->get_row($num_rows - 1);
64
+ } else
65
+ $draw_bottom = false;
66
+
67
+
68
+ // Draw the horizontal borders
69
+ foreach ( $cells["columns"] as $j ) {
70
+ $bp = $cellmap->get_border_properties($i, $j);
71
+
72
+ $y = $top_row["y"] - $bp["top"]["width"] / 2;
73
+
74
+ $col = $cellmap->get_column($j);
75
+ $x = $col["x"] - $bp["left"]["width"] / 2;
76
+ $w = $col["used-width"] + ($bp["left"]["width"] + $bp["right"]["width"] ) / 2;
77
+
78
+ if ( $bp["top"]["style"] !== "none" && $bp["top"]["width"] > 0 ) {
79
+ $widths = array($bp["top"]["width"],
80
+ $bp["right"]["width"],
81
+ $bp["bottom"]["width"],
82
+ $bp["left"]["width"]);
83
+ $method = "_border_". $bp["top"]["style"];
84
+ $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top", "square");
85
+ }
86
+
87
+ if ( $draw_bottom ) {
88
+ $bp = $cellmap->get_border_properties($num_rows - 1, $j);
89
+ if ( $bp["bottom"]["style"] === "none" || $bp["bottom"]["width"] <= 0 )
90
+ continue;
91
+
92
+ $y = $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2;
93
+
94
+ $widths = array($bp["top"]["width"],
95
+ $bp["right"]["width"],
96
+ $bp["bottom"]["width"],
97
+ $bp["left"]["width"]);
98
+ $method = "_border_". $bp["bottom"]["style"];
99
+ $this->$method($x, $y, $w, $bp["bottom"]["color"], $widths, "bottom", "square");
100
+
101
+ }
102
+ }
103
+
104
+ $j = $cells["columns"][0];
105
+
106
+ $left_col = $cellmap->get_column($j);
107
+
108
+ if (in_array($num_cols - 1, $cells["columns"])) {
109
+ $draw_right = true;
110
+ $right_col = $cellmap->get_column($num_cols - 1);
111
+ } else
112
+ $draw_right = false;
113
+
114
+ // Draw the vertical borders
115
+ foreach ( $cells["rows"] as $i ) {
116
+ $bp = $cellmap->get_border_properties($i, $j);
117
+
118
+ $x = $left_col["x"] - $bp["left"]["width"] / 2;
119
+
120
+ $row = $cellmap->get_row($i);
121
+
122
+ $y = $row["y"] - $bp["top"]["width"] / 2;
123
+ $h = $row["height"] + ($bp["top"]["width"] + $bp["bottom"]["width"])/ 2;
124
+
125
+ if ( $bp["left"]["style"] !== "none" && $bp["left"]["width"] > 0 ) {
126
+
127
+ $widths = array($bp["top"]["width"],
128
+ $bp["right"]["width"],
129
+ $bp["bottom"]["width"],
130
+ $bp["left"]["width"]);
131
+
132
+ $method = "_border_" . $bp["left"]["style"];
133
+ $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left", "square");
134
+ }
135
+
136
+ if ( $draw_right ) {
137
+ $bp = $cellmap->get_border_properties($i, $num_cols - 1);
138
+ if ( $bp["right"]["style"] === "none" || $bp["right"]["width"] <= 0 )
139
+ continue;
140
+
141
+ $x = $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2;
142
+
143
+ $widths = array($bp["top"]["width"],
144
+ $bp["right"]["width"],
145
+ $bp["bottom"]["width"],
146
+ $bp["left"]["width"]);
147
+
148
+ $method = "_border_" . $bp["right"]["style"];
149
+ $this->$method($x, $y, $h, $bp["right"]["color"], $widths, "right", "square");
150
+
151
+ }
152
+ }
153
+
154
+ }
155
+ }
lib/dompdf/include/table_frame_decorator.cls.php ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates Frames for table layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Frame_Decorator extends Frame_Decorator {
16
+ static $VALID_CHILDREN = array("table-row-group",
17
+ "table-row",
18
+ "table-header-group",
19
+ "table-footer-group",
20
+ "table-column",
21
+ "table-column-group",
22
+ "table-caption",
23
+ "table-cell");
24
+
25
+ static $ROW_GROUPS = array('table-row-group',
26
+ 'table-header-group',
27
+ 'table-footer-group');
28
+
29
+ /**
30
+ * The Cellmap object for this table. The cellmap maps table cells
31
+ * to rows and columns, and aids in calculating column widths.
32
+ *
33
+ * @var Cellmap
34
+ */
35
+ protected $_cellmap;
36
+
37
+ /**
38
+ * The minimum width of the table, in pt
39
+ *
40
+ * @var float
41
+ */
42
+ protected $_min_width;
43
+
44
+ /**
45
+ * The maximum width of the table, in pt
46
+ *
47
+ * @var float
48
+ */
49
+ protected $_max_width;
50
+
51
+ /**
52
+ * Table header rows. Each table header is duplicated when a table
53
+ * spans pages.
54
+ *
55
+ * @var array
56
+ */
57
+ protected $_headers;
58
+
59
+ /**
60
+ * Table footer rows. Each table footer is duplicated when a table
61
+ * spans pages.
62
+ *
63
+ * @var array
64
+ */
65
+ protected $_footers;
66
+
67
+ /**
68
+ * Class constructor
69
+ *
70
+ * @param Frame $frame the frame to decorate
71
+ * @param DOMPDF $dompdf
72
+ */
73
+ function __construct(Frame $frame, DOMPDF $dompdf) {
74
+ parent::__construct($frame, $dompdf);
75
+ $this->_cellmap = new Cellmap($this);
76
+
77
+ if ( $frame->get_style()->table_layout === "fixed" ) {
78
+ $this->_cellmap->set_layout_fixed(true);
79
+ }
80
+
81
+ $this->_min_width = null;
82
+ $this->_max_width = null;
83
+ $this->_headers = array();
84
+ $this->_footers = array();
85
+ }
86
+
87
+
88
+ function reset() {
89
+ parent::reset();
90
+ $this->_cellmap->reset();
91
+ $this->_min_width = null;
92
+ $this->_max_width = null;
93
+ $this->_headers = array();
94
+ $this->_footers = array();
95
+ $this->_reflower->reset();
96
+ }
97
+
98
+ //........................................................................
99
+
100
+ /**
101
+ * split the table at $row. $row and all subsequent rows will be
102
+ * added to the clone. This method is overidden in order to remove
103
+ * frames from the cellmap properly.
104
+ *
105
+ * @param Frame $child
106
+ * @param bool $force_pagebreak
107
+ *
108
+ * @return void
109
+ */
110
+ function split(Frame $child = null, $force_pagebreak = false) {
111
+
112
+ if ( is_null($child) ) {
113
+ parent::split();
114
+ return;
115
+ }
116
+
117
+ // If $child is a header or if it is the first non-header row, do
118
+ // not duplicate headers, simply move the table to the next page.
119
+ if ( count($this->_headers) && !in_array($child, $this->_headers, true) &&
120
+ !in_array($child->get_prev_sibling(), $this->_headers, true) ) {
121
+
122
+ $first_header = null;
123
+
124
+ // Insert copies of the table headers before $child
125
+ foreach ($this->_headers as $header) {
126
+
127
+ $new_header = $header->deep_copy();
128
+
129
+ if ( is_null($first_header) )
130
+ $first_header = $new_header;
131
+
132
+ $this->insert_child_before($new_header, $child);
133
+ }
134
+
135
+ parent::split($first_header);
136
+
137
+ } else if ( in_array($child->get_style()->display, self::$ROW_GROUPS) ) {
138
+
139
+ // Individual rows should have already been handled
140
+ parent::split($child);
141
+
142
+ } else {
143
+
144
+ $iter = $child;
145
+
146
+ while ($iter) {
147
+ $this->_cellmap->remove_row($iter);
148
+ $iter = $iter->get_next_sibling();
149
+ }
150
+
151
+ parent::split($child);
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Return a copy of this frame with $node as its node
157
+ *
158
+ * @param DOMNode $node
159
+ * @return Frame
160
+ */
161
+ function copy(DOMNode $node) {
162
+ $deco = parent::copy($node);
163
+
164
+ // In order to keep columns' widths through pages
165
+ $deco->_cellmap->set_columns($this->_cellmap->get_columns());
166
+ $deco->_cellmap->lock_columns();
167
+
168
+ return $deco;
169
+ }
170
+
171
+ /**
172
+ * Static function to locate the parent table of a frame
173
+ *
174
+ * @param Frame $frame
175
+ * @return Table_Frame_Decorator the table that is an ancestor of $frame
176
+ */
177
+ static function find_parent_table(Frame $frame) {
178
+
179
+ while ( $frame = $frame->get_parent() )
180
+ if ( $frame->is_table() )
181
+ break;
182
+
183
+ return $frame;
184
+ }
185
+
186
+ /**
187
+ * Return this table's Cellmap
188
+ *
189
+ * @return Cellmap
190
+ */
191
+ function get_cellmap() { return $this->_cellmap; }
192
+
193
+ /**
194
+ * Return the minimum width of this table
195
+ *
196
+ * @return float
197
+ */
198
+ function get_min_width() { return $this->_min_width; }
199
+
200
+ /**
201
+ * Return the maximum width of this table
202
+ *
203
+ * @return float
204
+ */
205
+ function get_max_width() { return $this->_max_width; }
206
+
207
+ /**
208
+ * Set the minimum width of the table
209
+ *
210
+ * @param float $width the new minimum width
211
+ */
212
+ function set_min_width($width) { $this->_min_width = $width; }
213
+
214
+ /**
215
+ * Set the maximum width of the table
216
+ *
217
+ * @param float $width the new maximum width
218
+ */
219
+ function set_max_width($width) { $this->_max_width = $width; }
220
+
221
+ /**
222
+ * Restructure tree so that the table has the correct structure.
223
+ * Invalid children (i.e. all non-table-rows) are moved below the
224
+ * table.
225
+ */
226
+ function normalise() {
227
+
228
+ // Store frames generated by invalid tags and move them outside the table
229
+ $erroneous_frames = array();
230
+ $anon_row = false;
231
+ $iter = $this->get_first_child();
232
+ while ( $iter ) {
233
+ $child = $iter;
234
+ $iter = $iter->get_next_sibling();
235
+
236
+ $display = $child->get_style()->display;
237
+
238
+ if ( $anon_row ) {
239
+
240
+ if ( $display === "table-row" ) {
241
+ // Add the previous anonymous row
242
+ $this->insert_child_before($table_row, $child);
243
+
244
+ $table_row->normalise();
245
+ $child->normalise();
246
+ $anon_row = false;
247
+ continue;
248
+ }
249
+
250
+ // add the child to the anonymous row
251
+ $table_row->append_child($child);
252
+ continue;
253
+
254
+ } else {
255
+
256
+ if ( $display === "table-row" ) {
257
+ $child->normalise();
258
+ continue;
259
+ }
260
+
261
+ if ( $display === "table-cell" ) {
262
+ // Create an anonymous table row
263
+ $tr = $this->get_node()->ownerDocument->createElement("tr");
264
+
265
+ $frame = new Frame($tr);
266
+
267
+ $css = $this->get_style()->get_stylesheet();
268
+ $style = $css->create_style();
269
+ $style->inherit($this->get_style());
270
+
271
+ // Lookup styles for tr tags. If the user wants styles to work
272
+ // better, they should make the tr explicit... I'm not going to
273
+ // try to guess what they intended.
274
+ if ( $tr_style = $css->lookup("tr") )
275
+ $style->merge($tr_style);
276
+
277
+ // Okay, I have absolutely no idea why I need this clone here, but
278
+ // if it's omitted, php (as of 2004-07-28) segfaults.
279
+ $frame->set_style(clone $style);
280
+ $table_row = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
281
+
282
+ // Add the cell to the row
283
+ $table_row->append_child($child);
284
+
285
+ $anon_row = true;
286
+ continue;
287
+ }
288
+
289
+ if ( !in_array($display, self::$VALID_CHILDREN) ) {
290
+ $erroneous_frames[] = $child;
291
+ continue;
292
+ }
293
+
294
+ // Normalise other table parts (i.e. row groups)
295
+ foreach ($child->get_children() as $grandchild) {
296
+ if ( $grandchild->get_style()->display === "table-row" ) {
297
+ $grandchild->normalise();
298
+ }
299
+ }
300
+
301
+ // Add headers and footers
302
+ if ( $display === "table-header-group" )
303
+ $this->_headers[] = $child;
304
+
305
+ else if ( $display === "table-footer-group" )
306
+ $this->_footers[] = $child;
307
+ }
308
+ }
309
+
310
+ if ( $anon_row ) {
311
+ // Add the row to the table
312
+ $this->_frame->append_child($table_row);
313
+ $table_row->normalise();
314
+ $this->_cellmap->add_row();
315
+ }
316
+
317
+ foreach ($erroneous_frames as $frame)
318
+ $this->move_after($frame);
319
+
320
+ }
321
+
322
+ //........................................................................
323
+
324
+ /**
325
+ * Moves the specified frame and it's corresponding node outside of
326
+ * the table.
327
+ *
328
+ * @param Frame $frame the frame to move
329
+ */
330
+ function move_after(Frame $frame) {
331
+ $this->get_parent()->insert_child_after($frame, $this);
332
+ }
333
+
334
+ }
lib/dompdf/include/table_frame_reflower.cls.php ADDED
@@ -0,0 +1,578 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows tables
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Frame_Reflower extends Frame_Reflower {
16
+ /**
17
+ * Frame for this reflower
18
+ *
19
+ * @var Table_Frame_Decorator
20
+ */
21
+ protected $_frame;
22
+
23
+ /**
24
+ * Cache of results between call to get_min_max_width and assign_widths
25
+ *
26
+ * @var array
27
+ */
28
+ protected $_state;
29
+
30
+ function __construct(Table_Frame_Decorator $frame) {
31
+ $this->_state = null;
32
+ parent::__construct($frame);
33
+ }
34
+
35
+ /**
36
+ * State is held here so it needs to be reset along with the decorator
37
+ */
38
+ function reset() {
39
+ $this->_state = null;
40
+ $this->_min_max_cache = null;
41
+ }
42
+
43
+ //........................................................................
44
+
45
+ protected function _assign_widths() {
46
+ $style = $this->_frame->get_style();
47
+
48
+ // Find the min/max width of the table and sort the columns into
49
+ // absolute/percent/auto arrays
50
+ $min_width = $this->_state["min_width"];
51
+ $max_width = $this->_state["max_width"];
52
+ $percent_used = $this->_state["percent_used"];
53
+ $absolute_used = $this->_state["absolute_used"];
54
+ $auto_min = $this->_state["auto_min"];
55
+
56
+ $absolute =& $this->_state["absolute"];
57
+ $percent =& $this->_state["percent"];
58
+ $auto =& $this->_state["auto"];
59
+
60
+ // Determine the actual width of the table
61
+ $cb = $this->_frame->get_containing_block();
62
+ $columns =& $this->_frame->get_cellmap()->get_columns();
63
+
64
+ $width = $style->width;
65
+
66
+ // Calculate padding & border fudge factor
67
+ $left = $style->margin_left;
68
+ $right = $style->margin_right;
69
+
70
+ $centered = ( $left === "auto" && $right === "auto" );
71
+
72
+ $left = $left === "auto" ? 0 : $style->length_in_pt($left, $cb["w"]);
73
+ $right = $right === "auto" ? 0 : $style->length_in_pt($right, $cb["w"]);
74
+
75
+ $delta = $left + $right;
76
+
77
+ if ( !$centered ) {
78
+ $delta += $style->length_in_pt(array(
79
+ $style->padding_left,
80
+ $style->border_left_width,
81
+ $style->border_right_width,
82
+ $style->padding_right),
83
+ $cb["w"]);
84
+ }
85
+
86
+ $min_table_width = $style->length_in_pt( $style->min_width, $cb["w"] - $delta );
87
+
88
+ // min & max widths already include borders & padding
89
+ $min_width -= $delta;
90
+ $max_width -= $delta;
91
+
92
+ if ( $width !== "auto" ) {
93
+
94
+ $preferred_width = $style->length_in_pt($width, $cb["w"]) - $delta;
95
+
96
+ if ( $preferred_width < $min_table_width )
97
+ $preferred_width = $min_table_width;
98
+
99
+ if ( $preferred_width > $min_width )
100
+ $width = $preferred_width;
101
+ else
102
+ $width = $min_width;
103
+
104
+ } else {
105
+
106
+ if ( $max_width + $delta < $cb["w"] )
107
+ $width = $max_width;
108
+ else if ( $cb["w"] - $delta > $min_width )
109
+ $width = $cb["w"] - $delta;
110
+ else
111
+ $width = $min_width;
112
+
113
+ if ( $width < $min_table_width )
114
+ $width = $min_table_width;
115
+
116
+ }
117
+
118
+ // Store our resolved width
119
+ $style->width = $width;
120
+
121
+ $cellmap = $this->_frame->get_cellmap();
122
+
123
+ if ( $cellmap->is_columns_locked() ) {
124
+ return;
125
+ }
126
+
127
+ // If the whole table fits on the page, then assign each column it's max width
128
+ if ( $width == $max_width ) {
129
+
130
+ foreach (array_keys($columns) as $i)
131
+ $cellmap->set_column_width($i, $columns[$i]["max-width"]);
132
+
133
+ return;
134
+ }
135
+
136
+ // Determine leftover and assign it evenly to all columns
137
+ if ( $width > $min_width ) {
138
+
139
+ // We have four cases to deal with:
140
+ //
141
+ // 1. All columns are auto--no widths have been specified. In this
142
+ // case we distribute extra space across all columns weighted by max-width.
143
+ //
144
+ // 2. Only absolute widths have been specified. In this case we
145
+ // distribute any extra space equally among 'width: auto' columns, or all
146
+ // columns if no auto columns have been specified.
147
+ //
148
+ // 3. Only percentage widths have been specified. In this case we
149
+ // normalize the percentage values and distribute any remaining % to
150
+ // width: auto columns. We then proceed to assign widths as fractions
151
+ // of the table width.
152
+ //
153
+ // 4. Both absolute and percentage widths have been specified.
154
+
155
+ $increment = 0;
156
+
157
+ // Case 1:
158
+ if ( $absolute_used == 0 && $percent_used == 0 ) {
159
+ $increment = $width - $min_width;
160
+
161
+ foreach (array_keys($columns) as $i) {
162
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment * ($columns[$i]["max-width"] / $max_width));
163
+ }
164
+ return;
165
+ }
166
+
167
+
168
+ // Case 2
169
+ if ( $absolute_used > 0 && $percent_used == 0 ) {
170
+
171
+ if ( count($auto) > 0 )
172
+ $increment = ($width - $auto_min - $absolute_used) / count($auto);
173
+
174
+ // Use the absolutely specified width or the increment
175
+ foreach (array_keys($columns) as $i) {
176
+
177
+ if ( $columns[$i]["absolute"] > 0 && count($auto) )
178
+ $cellmap->set_column_width($i, $columns[$i]["min-width"]);
179
+ else if ( count($auto) )
180
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
181
+ else {
182
+ // All absolute columns
183
+ $increment = ($width - $absolute_used) * $columns[$i]["absolute"] / $absolute_used;
184
+
185
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
186
+ }
187
+
188
+ }
189
+ return;
190
+ }
191
+
192
+
193
+ // Case 3:
194
+ if ( $absolute_used == 0 && $percent_used > 0 ) {
195
+
196
+ $scale = null;
197
+ $remaining = null;
198
+
199
+ // Scale percent values if the total percentage is > 100, or if all
200
+ // values are specified as percentages.
201
+ if ( $percent_used > 100 || count($auto) == 0)
202
+ $scale = 100 / $percent_used;
203
+ else
204
+ $scale = 1;
205
+
206
+ // Account for the minimum space used by the unassigned auto columns
207
+ $used_width = $auto_min;
208
+
209
+ foreach ($percent as $i) {
210
+ $columns[$i]["percent"] *= $scale;
211
+
212
+ $slack = $width - $used_width;
213
+
214
+ $w = min($columns[$i]["percent"] * $width/100, $slack);
215
+
216
+ if ( $w < $columns[$i]["min-width"] )
217
+ $w = $columns[$i]["min-width"];
218
+
219
+ $cellmap->set_column_width($i, $w);
220
+ $used_width += $w;
221
+
222
+ }
223
+
224
+ // This works because $used_width includes the min-width of each
225
+ // unassigned column
226
+ if ( count($auto) > 0 ) {
227
+ $increment = ($width - $used_width) / count($auto);
228
+
229
+ foreach ($auto as $i)
230
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
231
+
232
+ }
233
+ return;
234
+ }
235
+
236
+ // Case 4:
237
+
238
+ // First-come, first served
239
+ if ( $absolute_used > 0 && $percent_used > 0 ) {
240
+
241
+ $used_width = $auto_min;
242
+
243
+ foreach ($absolute as $i) {
244
+ $cellmap->set_column_width($i, $columns[$i]["min-width"]);
245
+ $used_width += $columns[$i]["min-width"];
246
+ }
247
+
248
+ // Scale percent values if the total percentage is > 100 or there
249
+ // are no auto values to take up slack
250
+ if ( $percent_used > 100 || count($auto) == 0 )
251
+ $scale = 100 / $percent_used;
252
+ else
253
+ $scale = 1;
254
+
255
+ $remaining_width = $width - $used_width;
256
+
257
+ foreach ($percent as $i) {
258
+ $slack = $remaining_width - $used_width;
259
+
260
+ $columns[$i]["percent"] *= $scale;
261
+ $w = min($columns[$i]["percent"] * $remaining_width / 100, $slack);
262
+
263
+ if ( $w < $columns[$i]["min-width"] )
264
+ $w = $columns[$i]["min-width"];
265
+
266
+ $columns[$i]["used-width"] = $w;
267
+ $used_width += $w;
268
+ }
269
+
270
+ if ( count($auto) > 0 ) {
271
+ $increment = ($width - $used_width) / count($auto);
272
+
273
+ foreach ($auto as $i)
274
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
275
+
276
+ }
277
+
278
+ return;
279
+ }
280
+
281
+
282
+ } else { // we are over constrained
283
+
284
+ // Each column gets its minimum width
285
+ foreach (array_keys($columns) as $i)
286
+ $cellmap->set_column_width($i, $columns[$i]["min-width"]);
287
+
288
+ }
289
+ }
290
+
291
+ //........................................................................
292
+
293
+ // Determine the frame's height based on min/max height
294
+ protected function _calculate_height() {
295
+
296
+ $style = $this->_frame->get_style();
297
+ $height = $style->height;
298
+
299
+ $cellmap = $this->_frame->get_cellmap();
300
+ $cellmap->assign_frame_heights();
301
+ $rows = $cellmap->get_rows();
302
+
303
+ // Determine our content height
304
+ $content_height = 0;
305
+ foreach ( $rows as $r )
306
+ $content_height += $r["height"];
307
+
308
+ $cb = $this->_frame->get_containing_block();
309
+
310
+ if ( !($style->overflow === "visible" ||
311
+ ($style->overflow === "hidden" && $height === "auto")) ) {
312
+
313
+ // Only handle min/max height if the height is independent of the frame's content
314
+
315
+ $min_height = $style->min_height;
316
+ $max_height = $style->max_height;
317
+
318
+ if ( isset($cb["h"]) ) {
319
+ $min_height = $style->length_in_pt($min_height, $cb["h"]);
320
+ $max_height = $style->length_in_pt($max_height, $cb["h"]);
321
+
322
+ } else if ( isset($cb["w"]) ) {
323
+
324
+ if ( mb_strpos($min_height, "%") !== false )
325
+ $min_height = 0;
326
+ else
327
+ $min_height = $style->length_in_pt($min_height, $cb["w"]);
328
+
329
+ if ( mb_strpos($max_height, "%") !== false )
330
+ $max_height = "none";
331
+ else
332
+ $max_height = $style->length_in_pt($max_height, $cb["w"]);
333
+ }
334
+
335
+ if ( $max_height !== "none" && $min_height > $max_height )
336
+ // Swap 'em
337
+ list($max_height, $min_height) = array($min_height, $max_height);
338
+
339
+ if ( $max_height !== "none" && $height > $max_height )
340
+ $height = $max_height;
341
+
342
+ if ( $height < $min_height )
343
+ $height = $min_height;
344
+
345
+ } else {
346
+
347
+ // Use the content height or the height value, whichever is greater
348
+ if ( $height !== "auto" ) {
349
+ $height = $style->length_in_pt($height, $cb["h"]);
350
+
351
+ if ( $height <= $content_height )
352
+ $height = $content_height;
353
+ else
354
+ $cellmap->set_frame_heights($height,$content_height);
355
+
356
+ } else
357
+ $height = $content_height;
358
+
359
+ }
360
+
361
+ return $height;
362
+
363
+ }
364
+ //........................................................................
365
+
366
+ /**
367
+ * @param Block_Frame_Decorator $block
368
+ */
369
+ function reflow(Block_Frame_Decorator $block = null) {
370
+ /**
371
+ * @var Table_Frame_Decorator
372
+ */
373
+ $frame = $this->_frame;
374
+
375
+ // Check if a page break is forced
376
+ $page = $frame->get_root();
377
+ $page->check_forced_page_break($frame);
378
+
379
+ // Bail if the page is full
380
+ if ( $page->is_full() )
381
+ return;
382
+
383
+ // Let the page know that we're reflowing a table so that splits
384
+ // are suppressed (simply setting page-break-inside: avoid won't
385
+ // work because we may have an arbitrary number of block elements
386
+ // inside tds.)
387
+ $page->table_reflow_start();
388
+
389
+ // Collapse vertical margins, if required
390
+ $this->_collapse_margins();
391
+
392
+ $frame->position();
393
+
394
+ // Table layout algorithm:
395
+ // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout
396
+
397
+ if ( is_null($this->_state) )
398
+ $this->get_min_max_width();
399
+
400
+ $cb = $frame->get_containing_block();
401
+ $style = $frame->get_style();
402
+
403
+ // This is slightly inexact, but should be okay. Add half the
404
+ // border-spacing to the table as padding. The other half is added to
405
+ // the cells themselves.
406
+ if ( $style->border_collapse === "separate" ) {
407
+ list($h, $v) = $style->border_spacing;
408
+
409
+ $v = $style->length_in_pt($v) / 2;
410
+ $h = $style->length_in_pt($h) / 2;
411
+
412
+ $style->padding_left = $style->length_in_pt($style->padding_left, $cb["w"]) + $h;
413
+ $style->padding_right = $style->length_in_pt($style->padding_right, $cb["w"]) + $h;
414
+ $style->padding_top = $style->length_in_pt($style->padding_top, $cb["h"]) + $v;
415
+ $style->padding_bottom = $style->length_in_pt($style->padding_bottom, $cb["h"]) + $v;
416
+
417
+ }
418
+
419
+ $this->_assign_widths();
420
+
421
+ // Adjust left & right margins, if they are auto
422
+ $width = $style->width;
423
+ $left = $style->margin_left;
424
+ $right = $style->margin_right;
425
+
426
+ $diff = $cb["w"] - $width;
427
+
428
+ if ( $left === "auto" && $right === "auto" ) {
429
+ if ( $diff < 0 ) {
430
+ $left = 0;
431
+ $right = $diff;
432
+ }
433
+ else {
434
+ $left = $right = $diff / 2;
435
+ }
436
+
437
+ $style->margin_left = "$left pt";
438
+ $style->margin_right = "$right pt";
439
+
440
+ } else {
441
+ if ( $left === "auto" ) {
442
+ $left = $style->length_in_pt($cb["w"] - $right - $width, $cb["w"]);
443
+ }
444
+ if ( $right === "auto" ) {
445
+ $left = $style->length_in_pt($left, $cb["w"]);
446
+ }
447
+ }
448
+
449
+ list($x, $y) = $frame->get_position();
450
+
451
+ // Determine the content edge
452
+ $content_x = $x + $left + $style->length_in_pt(array($style->padding_left,
453
+ $style->border_left_width), $cb["w"]);
454
+ $content_y = $y + $style->length_in_pt(array($style->margin_top,
455
+ $style->border_top_width,
456
+ $style->padding_top), $cb["h"]);
457
+
458
+ if ( isset($cb["h"]) )
459
+ $h = $cb["h"];
460
+ else
461
+ $h = null;
462
+
463
+ $cellmap = $frame->get_cellmap();
464
+ $col =& $cellmap->get_column(0);
465
+ $col["x"] = $content_x;
466
+
467
+ $row =& $cellmap->get_row(0);
468
+ $row["y"] = $content_y;
469
+
470
+ $cellmap->assign_x_positions();
471
+
472
+ // Set the containing block of each child & reflow
473
+ foreach ( $frame->get_children() as $child ) {
474
+
475
+ // Bail if the page is full
476
+ if ( !$page->in_nested_table() && $page->is_full() )
477
+ break;
478
+
479
+ $child->set_containing_block($content_x, $content_y, $width, $h);
480
+ $child->reflow();
481
+
482
+ if ( !$page->in_nested_table() )
483
+ // Check if a split has occured
484
+ $page->check_page_break($child);
485
+
486
+ }
487
+
488
+ // Assign heights to our cells:
489
+ $style->height = $this->_calculate_height();
490
+
491
+ if ( $style->border_collapse === "collapse" ) {
492
+ // Unset our borders because our cells are now using them
493
+ $style->border_style = "none";
494
+ }
495
+
496
+ $page->table_reflow_end();
497
+
498
+ // Debugging:
499
+ //echo ($this->_frame->get_cellmap());
500
+
501
+ if ( $block && $style->float === "none" && $frame->is_in_flow() ) {
502
+ $block->add_frame_to_line($frame);
503
+ $block->add_line();
504
+ }
505
+ }
506
+
507
+ //........................................................................
508
+
509
+ function get_min_max_width() {
510
+
511
+ if ( !is_null($this->_min_max_cache) )
512
+ return $this->_min_max_cache;
513
+
514
+ $style = $this->_frame->get_style();
515
+
516
+ $this->_frame->normalise();
517
+
518
+ // Add the cells to the cellmap (this will calcluate column widths as
519
+ // frames are added)
520
+ $this->_frame->get_cellmap()->add_frame($this->_frame);
521
+
522
+ // Find the min/max width of the table and sort the columns into
523
+ // absolute/percent/auto arrays
524
+ $this->_state = array();
525
+ $this->_state["min_width"] = 0;
526
+ $this->_state["max_width"] = 0;
527
+
528
+ $this->_state["percent_used"] = 0;
529
+ $this->_state["absolute_used"] = 0;
530
+ $this->_state["auto_min"] = 0;
531
+
532
+ $this->_state["absolute"] = array();
533
+ $this->_state["percent"] = array();
534
+ $this->_state["auto"] = array();
535
+
536
+ $columns =& $this->_frame->get_cellmap()->get_columns();
537
+ foreach (array_keys($columns) as $i) {
538
+ $this->_state["min_width"] += $columns[$i]["min-width"];
539
+ $this->_state["max_width"] += $columns[$i]["max-width"];
540
+
541
+ if ( $columns[$i]["absolute"] > 0 ) {
542
+ $this->_state["absolute"][] = $i;
543
+ $this->_state["absolute_used"] += $columns[$i]["absolute"];
544
+
545
+ } else if ( $columns[$i]["percent"] > 0 ) {
546
+ $this->_state["percent"][] = $i;
547
+ $this->_state["percent_used"] += $columns[$i]["percent"];
548
+
549
+ } else {
550
+ $this->_state["auto"][] = $i;
551
+ $this->_state["auto_min"] += $columns[$i]["min-width"];
552
+ }
553
+ }
554
+
555
+ // Account for margins & padding
556
+ $dims = array($style->border_left_width,
557
+ $style->border_right_width,
558
+ $style->padding_left,
559
+ $style->padding_right,
560
+ $style->margin_left,
561
+ $style->margin_right);
562
+
563
+ if ( $style->border_collapse !== "collapse" )
564
+ list($dims[]) = $style->border_spacing;
565
+
566
+ $delta = $style->length_in_pt($dims, $this->_frame->get_containing_block("w"));
567
+
568
+ $this->_state["min_width"] += $delta;
569
+ $this->_state["max_width"] += $delta;
570
+
571
+ return $this->_min_max_cache = array(
572
+ $this->_state["min_width"],
573
+ $this->_state["max_width"],
574
+ "min" => $this->_state["min_width"],
575
+ "max" => $this->_state["max_width"],
576
+ );
577
+ }
578
+ }
lib/dompdf/include/table_row_frame_decorator.cls.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates Frames for table row layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Frame_Decorator extends Frame_Decorator {
16
+
17
+ // protected members
18
+
19
+ function __construct(Frame $frame, DOMPDF $dompdf) {
20
+ parent::__construct($frame, $dompdf);
21
+ }
22
+
23
+ //........................................................................
24
+
25
+ /**
26
+ * Remove all non table-cell frames from this row and move them after
27
+ * the table.
28
+ */
29
+ function normalise() {
30
+
31
+ // Find our table parent
32
+ $p = Table_Frame_Decorator::find_parent_table($this);
33
+
34
+ $erroneous_frames = array();
35
+ foreach ($this->get_children() as $child) {
36
+ $display = $child->get_style()->display;
37
+
38
+ if ( $display !== "table-cell" )
39
+ $erroneous_frames[] = $child;
40
+ }
41
+
42
+ // dump the extra nodes after the table.
43
+ foreach ($erroneous_frames as $frame)
44
+ $p->move_after($frame);
45
+ }
46
+
47
+
48
+ }
lib/dompdf/include/table_row_frame_reflower.cls.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows table rows
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Frame_Reflower extends Frame_Reflower {
16
+
17
+
18
+ function __construct(Table_Row_Frame_Decorator $frame) {
19
+ parent::__construct($frame);
20
+ }
21
+
22
+ //........................................................................
23
+
24
+ function reflow(Block_Frame_Decorator $block = null) {
25
+ $page = $this->_frame->get_root();
26
+
27
+ if ( $page->is_full() )
28
+ return;
29
+
30
+ $this->_frame->position();
31
+ $style = $this->_frame->get_style();
32
+ $cb = $this->_frame->get_containing_block();
33
+
34
+ foreach ($this->_frame->get_children() as $child) {
35
+
36
+ if ( $page->is_full() )
37
+ return;
38
+
39
+ $child->set_containing_block($cb);
40
+ $child->reflow();
41
+
42
+ }
43
+
44
+ if ( $page->is_full() )
45
+ return;
46
+
47
+ $table = Table_Frame_Decorator::find_parent_table($this->_frame);
48
+ $cellmap = $table->get_cellmap();
49
+ $style->width = $cellmap->get_frame_width($this->_frame);
50
+ $style->height = $cellmap->get_frame_height($this->_frame);
51
+
52
+ $this->_frame->set_position($cellmap->get_frame_position($this->_frame));
53
+
54
+ }
55
+
56
+ //........................................................................
57
+
58
+ function get_min_max_width() {
59
+ throw new DOMPDF_Exception("Min/max width is undefined for table rows");
60
+ }
61
+ }
lib/dompdf/include/table_row_group_frame_decorator.cls.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Table row group decorator
11
+ *
12
+ * Overrides split() method for tbody, thead & tfoot elements
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ class Table_Row_Group_Frame_Decorator extends Frame_Decorator {
18
+
19
+ /**
20
+ * Class constructor
21
+ *
22
+ * @param Frame $frame Frame to decorate
23
+ * @param DOMPDF $dompdf Current dompdf instance
24
+ */
25
+ function __construct(Frame $frame, DOMPDF $dompdf) {
26
+ parent::__construct($frame, $dompdf);
27
+ }
28
+
29
+ /**
30
+ * Override split() to remove all child rows and this element from the cellmap
31
+ *
32
+ * @param Frame $child
33
+ * @param bool $force_pagebreak
34
+ *
35
+ * @return void
36
+ */
37
+ function split(Frame $child = null, $force_pagebreak = false) {
38
+
39
+ if ( is_null($child) ) {
40
+ parent::split();
41
+ return;
42
+ }
43
+
44
+ // Remove child & all subsequent rows from the cellmap
45
+ $cellmap = $this->get_parent()->get_cellmap();
46
+ $iter = $child;
47
+
48
+ while ( $iter ) {
49
+ $cellmap->remove_row($iter);
50
+ $iter = $iter->get_next_sibling();
51
+ }
52
+
53
+ // If we are splitting at the first child remove the
54
+ // table-row-group from the cellmap as well
55
+ if ( $child === $this->get_first_child() ) {
56
+ $cellmap->remove_row_group($this);
57
+ parent::split();
58
+ return;
59
+ }
60
+
61
+ $cellmap->update_row_group($this, $child->get_prev_sibling());
62
+ parent::split($child);
63
+
64
+ }
65
+ }
66
+
lib/dompdf/include/table_row_group_frame_reflower.cls.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows table row groups (e.g. tbody tags)
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Group_Frame_Reflower extends Frame_Reflower {
16
+
17
+ function __construct($frame) {
18
+ parent::__construct($frame);
19
+ }
20
+
21
+ function reflow(Block_Frame_Decorator $block = null) {
22
+ $page = $this->_frame->get_root();
23
+
24
+ $style = $this->_frame->get_style();
25
+
26
+ // Our width is equal to the width of our parent table
27
+ $table = Table_Frame_Decorator::find_parent_table($this->_frame);
28
+
29
+ $cb = $this->_frame->get_containing_block();
30
+
31
+ foreach ( $this->_frame->get_children() as $child) {
32
+ // Bail if the page is full
33
+ if ( $page->is_full() )
34
+ return;
35
+
36
+ $child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]);
37
+ $child->reflow();
38
+
39
+ // Check if a split has occured
40
+ $page->check_page_break($child);
41
+
42
+ }
43
+
44
+ if ( $page->is_full() )
45
+ return;
46
+
47
+ $cellmap = $table->get_cellmap();
48
+ $style->width = $cellmap->get_frame_width($this->_frame);
49
+ $style->height = $cellmap->get_frame_height($this->_frame);
50
+
51
+ $this->_frame->set_position($cellmap->get_frame_position($this->_frame));
52
+
53
+ if ( $table->get_style()->border_collapse === "collapse" )
54
+ // Unset our borders because our cells are now using them
55
+ $style->border_style = "none";
56
+
57
+ }
58
+
59
+ }
lib/dompdf/include/table_row_group_renderer.cls.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Renders block frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Group_Renderer extends Block_Renderer {
16
+
17
+ //........................................................................
18
+
19
+ function render(Frame $frame) {
20
+ $style = $frame->get_style();
21
+
22
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
23
+
24
+ $this->_render_border($frame);
25
+ $this->_render_outline($frame);
26
+
27
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
28
+ $this->_debug_layout($frame->get_border_box(), "red");
29
+ if (DEBUG_LAYOUT_PADDINGBOX) {
30
+ $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
31
+ }
32
+ }
33
+
34
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) {
35
+ foreach ($frame->get_decorator()->get_line_boxes() as $line) {
36
+ $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
37
+ }
38
+ }
39
+ }
40
+ }
lib/dompdf/include/table_row_positioner.cls.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions table rows
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Positioner extends Positioner {
16
+
17
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function position() {
22
+
23
+ $cb = $this->_frame->get_containing_block();
24
+ $p = $this->_frame->get_prev_sibling();
25
+
26
+ if ( $p )
27
+ $y = $p->get_position("y") + $p->get_margin_height();
28
+
29
+ else
30
+ $y = $cb["y"];
31
+
32
+ $this->_frame->set_position($cb["x"], $y);
33
+
34
+ }
35
+ }
lib/dompdf/include/tcpdf_adapter.cls.php ADDED
@@ -0,0 +1,628 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ require_once DOMPDF_LIB_DIR . '/tcpdf/tcpdf.php';
10
+
11
+ /**
12
+ * TCPDF PDF Rendering interface
13
+ *
14
+ * TCPDF_Adapter provides a simple, stateless interface to TCPDF.
15
+ *
16
+ * Unless otherwise mentioned, all dimensions are in points (1/72 in).
17
+ * The coordinate origin is in the top left corner and y values
18
+ * increase downwards.
19
+ *
20
+ * See {@link http://tcpdf.sourceforge.net} for more information on
21
+ * the underlying TCPDF class.
22
+ *
23
+ * @package dompdf
24
+ */
25
+ class TCPDF_Adapter implements Canvas {
26
+
27
+ /**
28
+ * Dimensions of paper sizes in points
29
+ *
30
+ * @var array;
31
+ */
32
+ static public $PAPER_SIZES = array(); // Set to CPDF_Adapter::$PAPER_SIZES below.
33
+
34
+ /**
35
+ * @var DOMPDF
36
+ */
37
+ private $_dompdf;
38
+
39
+ /**
40
+ * Instance of the TCPDF class
41
+ *
42
+ * @var TCPDF
43
+ */
44
+ private $_pdf;
45
+
46
+ /**
47
+ * PDF width in points
48
+ *
49
+ * @var float
50
+ */
51
+ private $_width;
52
+
53
+ /**
54
+ * PDF height in points
55
+ *
56
+ * @var float
57
+ */
58
+ private $_height;
59
+
60
+ /**
61
+ * Last fill color used
62
+ *
63
+ * @var array
64
+ */
65
+ private $_last_fill_color;
66
+
67
+ /**
68
+ * Last stroke color used
69
+ *
70
+ * @var array
71
+ */
72
+ private $_last_stroke_color;
73
+
74
+ /**
75
+ * Last line width used
76
+ *
77
+ * @var float
78
+ */
79
+ private $_last_line_width;
80
+
81
+ /**
82
+ * Total number of pages
83
+ *
84
+ * @var int
85
+ */
86
+ private $_page_count;
87
+
88
+ /**
89
+ * Text to display on every page
90
+ *
91
+ * @var array
92
+ */
93
+ private $_page_text;
94
+
95
+ /**
96
+ * Array of pages for accessing after initial rendering is complete
97
+ *
98
+ * @var array
99
+ */
100
+ private $_pages;
101
+
102
+ /**
103
+ * Class constructor
104
+ *
105
+ * @param mixed $paper The size of paper to use either a string (see {@link CPDF_Adapter::$PAPER_SIZES}) or
106
+ * an array(xmin,ymin,xmax,ymax)
107
+ * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
108
+ * @param DOMPDF $dompdf
109
+ */
110
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
111
+
112
+ if ( is_array($paper) )
113
+ $size = $paper;
114
+ else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) )
115
+ $size = self::$PAPER_SIZES[$paper];
116
+ else
117
+ $size = self::$PAPER_SIZES["letter"];
118
+
119
+ if ( mb_strtolower($orientation) === "landscape" ) {
120
+ list($size[2], $size[3]) = array($size[3], $size[2]);
121
+ }
122
+
123
+ $this->_width = $size[2] - $size[0];
124
+ $this->_height = $size[3] - $size[1];
125
+
126
+ $this->_dompdf = $dompdf;
127
+
128
+ $this->_pdf = new TCPDF("P", "pt", array($this->_width, $this->_height));
129
+ $this->_pdf->Setcreator("DOMPDF Converter");
130
+
131
+ $this->_pdf->AddPage();
132
+
133
+ $this->_page_number = $this->_page_count = 1;
134
+ $this->_page_text = array();
135
+
136
+ $this->_last_fill_color = null;
137
+ $this->_last_stroke_color = null;
138
+ $this->_last_line_width = null;
139
+ }
140
+
141
+ function get_dompdf(){
142
+ return $this->_dompdf;
143
+ }
144
+
145
+ /**
146
+ * Remaps y coords from 4th to 1st quadrant
147
+ *
148
+ * @param float $y
149
+ * @return float
150
+ */
151
+ protected function y($y) { return $this->_height - $y; }
152
+
153
+ /**
154
+ * Sets the stroke color
155
+ *
156
+ * @param array $color
157
+ *
158
+ * @return void
159
+ */
160
+ protected function _set_stroke_color($color) {
161
+ $color[0] = round(255 * $color[0]);
162
+ $color[1] = round(255 * $color[1]);
163
+ $color[2] = round(255 * $color[2]);
164
+
165
+ if ( is_null($this->_last_stroke_color) || $color != $this->_last_stroke_color ) {
166
+ $this->_pdf->SetDrawColor($color[0],$color[1],$color[2]);
167
+ $this->_last_stroke_color = $color;
168
+ }
169
+
170
+ }
171
+
172
+ /**
173
+ * Sets the fill color
174
+ *
175
+ * @param array $color
176
+ */
177
+ protected function _set_fill_color($color) {
178
+ $color[0] = round(255 * $color[0]);
179
+ $color[1] = round(255 * $color[1]);
180
+ $color[2] = round(255 * $color[2]);
181
+
182
+ if ( is_null($this->_last_fill_color) || $color != $this->_last_fill_color ) {
183
+ $this->_pdf->SetDrawColor($color[0],$color[1],$color[2]);
184
+ $this->_last_fill_color = $color;
185
+ }
186
+
187
+ }
188
+
189
+ /**
190
+ * Return the TCPDF instance
191
+ *
192
+ * @return TCPDF
193
+ */
194
+ function get_tcpdf() { return $this->_pdf; }
195
+
196
+ /**
197
+ * Returns the current page number
198
+ *
199
+ * @return int
200
+ */
201
+ function get_page_number() {
202
+ return $this->_page_number;
203
+ }
204
+
205
+ /**
206
+ * Returns the total number of pages
207
+ *
208
+ * @return int
209
+ */
210
+ function get_page_count() {
211
+ return $this->_page_count;
212
+ }
213
+
214
+ /**
215
+ * Sets the total number of pages
216
+ *
217
+ * @param int $count
218
+ */
219
+ function set_page_count($count) {
220
+ $this->_page_count = (int)$count;
221
+ }
222
+
223
+ /**
224
+ * Draws a line from x1,y1 to x2,y2
225
+ *
226
+ * See {@link Style::munge_color()} for the format of the color array.
227
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
228
+ * $style parameter (aka dash).
229
+ *
230
+ * @param float $x1
231
+ * @param float $y1
232
+ * @param float $x2
233
+ * @param float $y2
234
+ * @param array $color
235
+ * @param float $width
236
+ * @param array $style
237
+ */
238
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
239
+
240
+ if ( is_null($this->_last_line_width) || $width != $this->_last_line_width ) {
241
+ $this->_pdf->SetLineWidth($width);
242
+ $this->_last_line_width = $width;
243
+ }
244
+
245
+ $this->_set_stroke_color($color);
246
+
247
+ // FIXME: ugh, need to handle different styles here
248
+ $this->_pdf->line($x1, $y1, $x2, $y2);
249
+ }
250
+
251
+ /**
252
+ * Draws a rectangle at x1,y1 with width w and height h
253
+ *
254
+ * See {@link Style::munge_color()} for the format of the color array.
255
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
256
+ * parameter (aka dash)
257
+ *
258
+ * @param float $x1
259
+ * @param float $y1
260
+ * @param float $w
261
+ * @param float $h
262
+ * @param array $color
263
+ * @param float $width
264
+ * @param array $style
265
+ */
266
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
267
+
268
+ if ( is_null($this->_last_line_width) || $width != $this->_last_line_width ) {
269
+ $this->_pdf->SetLineWidth($width);
270
+ $this->_last_line_width = $width;
271
+ }
272
+
273
+ $this->_set_stroke_color($color);
274
+
275
+ // FIXME: ugh, need to handle styles here
276
+ $this->_pdf->rect($x1, $y1, $w, $h);
277
+
278
+ }
279
+
280
+ /**
281
+ * Draws a filled rectangle at x1,y1 with width w and height h
282
+ *
283
+ * See {@link Style::munge_color()} for the format of the color array.
284
+ *
285
+ * @param float $x1
286
+ * @param float $y1
287
+ * @param float $w
288
+ * @param float $h
289
+ * @param array $color
290
+ */
291
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
292
+
293
+ $this->_set_fill_color($color);
294
+
295
+ // FIXME: ugh, need to handle styles here
296
+ $this->_pdf->rect($x1, $y1, $w, $h, "F");
297
+ }
298
+
299
+ /**
300
+ * Draws a polygon
301
+ *
302
+ * The polygon is formed by joining all the points stored in the $points
303
+ * array. $points has the following structure:
304
+ * <code>
305
+ * array(0 => x1,
306
+ * 1 => y1,
307
+ * 2 => x2,
308
+ * 3 => y2,
309
+ * ...
310
+ * );
311
+ * </code>
312
+ *
313
+ * See {@link Style::munge_color()} for the format of the color array.
314
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
315
+ * parameter (aka dash)
316
+ *
317
+ * @param array $points
318
+ * @param array $color
319
+ * @param float $width
320
+ * @param array $style
321
+ * @param bool $fill Fills the polygon if true
322
+ */
323
+ function polygon($points, $color, $width = null, $style = null, $fill = false) {
324
+ // FIXME
325
+ }
326
+
327
+ /**
328
+ * Draws a circle at $x,$y with radius $r
329
+ *
330
+ * See {@link Style::munge_color()} for the format of the color array.
331
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
332
+ * parameter (aka dash)
333
+ *
334
+ * @param float $x
335
+ * @param float $y
336
+ * @param float $r
337
+ * @param array $color
338
+ * @param float $width
339
+ * @param array $style
340
+ * @param bool $fill Fills the circle if true
341
+ */
342
+ function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
343
+ // FIXME
344
+ }
345
+
346
+ /**
347
+ * Add an image to the pdf.
348
+ * The image is placed at the specified x and y coordinates with the
349
+ * given width and height.
350
+ *
351
+ * @param string $img_url the path to the image
352
+ * @param float $x x position
353
+ * @param float $y y position
354
+ * @param int $w width (in pixels)
355
+ * @param int $h height (in pixels)
356
+ * @param string $resolution
357
+ *
358
+ * @return void
359
+ */
360
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
361
+ // FIXME
362
+ }
363
+
364
+ /**
365
+ * Writes text at the specified x and y coordinates
366
+ * See {@link Style::munge_color()} for the format of the color array.
367
+ *
368
+ * @param float $x
369
+ * @param float $y
370
+ * @param string $text the text to write
371
+ * @param string $font the font file to use
372
+ * @param float $size the font size, in points
373
+ * @param array $color
374
+ * @param float $word_space word spacing adjustment
375
+ * @param float $char_space
376
+ * @param float $angle
377
+ *
378
+ * @return void
379
+ */
380
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
381
+ // FIXME
382
+ }
383
+
384
+ function javascript($code) {
385
+ // FIXME
386
+ }
387
+
388
+ /**
389
+ * Add a named destination (similar to <a name="foo">...</a> in html)
390
+ *
391
+ * @param string $anchorname The name of the named destination
392
+ */
393
+ function add_named_dest($anchorname) {
394
+ // FIXME
395
+ }
396
+
397
+ /**
398
+ * Add a link to the pdf
399
+ *
400
+ * @param string $url The url to link to
401
+ * @param float $x The x position of the link
402
+ * @param float $y The y position of the link
403
+ * @param float $width The width of the link
404
+ * @param float $height The height of the link
405
+ */
406
+ function add_link($url, $x, $y, $width, $height) {
407
+ // FIXME
408
+ }
409
+
410
+ /**
411
+ * Add meta information to the PDF
412
+ *
413
+ * @param string $label label of the value (Creator, Producer, etc.)
414
+ * @param string $value the text to set
415
+ */
416
+ function add_info($label, $value) {
417
+ $method = "Set$label";
418
+ if ( in_array("Title", "Author", "Keywords", "Subject") && method_exists($this->_pdf, $method) ) {
419
+ $this->_pdf->$method($value);
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Calculates text size, in points
425
+ *
426
+ * @param string $text the text to be sized
427
+ * @param string $font the desired font
428
+ * @param float $size the desired font size
429
+ * @param float $word_spacing word spacing, if any
430
+ * @param float $char_spacing
431
+ *
432
+ * @return float
433
+ */
434
+ function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
435
+ // FIXME
436
+ }
437
+
438
+ /**
439
+ * Calculates font height, in points
440
+ *
441
+ * @param string $font
442
+ * @param float $size
443
+ * @return float
444
+ */
445
+ function get_font_height($font, $size) {
446
+ // FIXME
447
+ }
448
+
449
+
450
+ /**
451
+ * Starts a new page
452
+ *
453
+ * Subsequent drawing operations will appear on the new page.
454
+ */
455
+ function new_page() {
456
+ // FIXME
457
+ }
458
+
459
+ /**
460
+ * Streams the PDF directly to the browser
461
+ *
462
+ * @param string $filename the name of the PDF file
463
+ * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
464
+ */
465
+ function stream($filename, $options = null) {
466
+ // FIXME
467
+ }
468
+
469
+ /**
470
+ * Returns the PDF as a string
471
+ *
472
+ * @param array $options associative array: 'compress' => 1 or 0
473
+ * @return string
474
+ */
475
+ function output($options = null) {
476
+ // FIXME
477
+ }
478
+
479
+ /**
480
+ * Starts a clipping rectangle at x1,y1 with width w and height h
481
+ *
482
+ * @param float $x1
483
+ * @param float $y1
484
+ * @param float $w
485
+ * @param float $h
486
+ */
487
+ function clipping_rectangle($x1, $y1, $w, $h) {
488
+ // TODO: Implement clipping_rectangle() method.
489
+ }
490
+
491
+ /**
492
+ * Starts a rounded clipping rectangle at x1,y1 with width w and height h
493
+ *
494
+ * @param float $x1
495
+ * @param float $y1
496
+ * @param float $w
497
+ * @param float $h
498
+ * @param float $tl
499
+ * @param float $tr
500
+ * @param float $br
501
+ * @param float $bl
502
+ *
503
+ * @return void
504
+ */
505
+ function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl) {
506
+ // TODO: Implement clipping_roundrectangle() method.
507
+ }
508
+
509
+ /**
510
+ * Ends the last clipping shape
511
+ */
512
+ function clipping_end() {
513
+ // TODO: Implement clipping_end() method.
514
+ }
515
+
516
+ /**
517
+ * Save current state
518
+ */
519
+ function save() {
520
+ // TODO: Implement save() method.
521
+ }
522
+
523
+ /**
524
+ * Restore last state
525
+ */
526
+ function restore() {
527
+ // TODO: Implement restore() method.
528
+ }
529
+
530
+ /**
531
+ * Rotate
532
+ */
533
+ function rotate($angle, $x, $y) {
534
+ // TODO: Implement rotate() method.
535
+ }
536
+
537
+ /**
538
+ * Skew
539
+ */
540
+ function skew($angle_x, $angle_y, $x, $y) {
541
+ // TODO: Implement skew() method.
542
+ }
543
+
544
+ /**
545
+ * Scale
546
+ */
547
+ function scale($s_x, $s_y, $x, $y) {
548
+ // TODO: Implement scale() method.
549
+ }
550
+
551
+ /**
552
+ * Translate
553
+ */
554
+ function translate($t_x, $t_y) {
555
+ // TODO: Implement translate() method.
556
+ }
557
+
558
+ /**
559
+ * Transform
560
+ */
561
+ function transform($a, $b, $c, $d, $e, $f) {
562
+ // TODO: Implement transform() method.
563
+ }
564
+
565
+ /**
566
+ * Add an arc to the PDF
567
+ * See {@link Style::munge_color()} for the format of the color array.
568
+ *
569
+ * @param float $x X coordinate of the arc
570
+ * @param float $y Y coordinate of the arc
571
+ * @param float $r1 Radius 1
572
+ * @param float $r2 Radius 2
573
+ * @param float $astart Start angle in degrees
574
+ * @param float $aend End angle in degrees
575
+ * @param array $color Color
576
+ * @param float $width
577
+ * @param array $style
578
+ *
579
+ * @return void
580
+ */
581
+ function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
582
+ // TODO: Implement arc() method.
583
+ }
584
+
585
+ /**
586
+ * Calculates font baseline, in points
587
+ *
588
+ * @param string $font
589
+ * @param float $size
590
+ *
591
+ * @return float
592
+ */
593
+ function get_font_baseline($font, $size) {
594
+ // TODO: Implement get_font_baseline() method.
595
+ }
596
+
597
+ /**
598
+ * Sets the opacity
599
+ *
600
+ * @param float $opacity
601
+ * @param string $mode
602
+ */
603
+ function set_opacity($opacity, $mode = "Normal") {
604
+ // TODO: Implement set_opacity() method.
605
+ }
606
+
607
+ /**
608
+ * Sets the default view
609
+ *
610
+ * @param string $view
611
+ * 'XYZ' left, top, zoom
612
+ * 'Fit'
613
+ * 'FitH' top
614
+ * 'FitV' left
615
+ * 'FitR' left,bottom,right
616
+ * 'FitB'
617
+ * 'FitBH' top
618
+ * 'FitBV' left
619
+ * @param array $options
620
+ *
621
+ * @return void
622
+ */
623
+ function set_default_view($view, $options = array()) {
624
+ // TODO: Implement set_default_view() method.
625
+ }}
626
+
627
+ // Workaround for idiotic limitation on statics...
628
+ TCPDF_Adapter::$PAPER_SIZES = CPDF_Adapter::$PAPER_SIZES;
lib/dompdf/include/text_frame_decorator.cls.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Brian Sweeney <eclecticgeek@gmail.com>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * Decorates Frame objects for text layout
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ class Text_Frame_Decorator extends Frame_Decorator {
18
+
19
+ // protected members
20
+ protected $_text_spacing;
21
+
22
+ // buggy DOMText::splitText (PHP < 5.2.7)
23
+ public static $_buggy_splittext;
24
+
25
+ function __construct(Frame $frame, DOMPDF $dompdf) {
26
+ if ( !$frame->is_text_node() )
27
+ throw new DOMPDF_Exception("Text_Decorator can only be applied to #text nodes.");
28
+
29
+ parent::__construct($frame, $dompdf);
30
+ $this->_text_spacing = null;
31
+ }
32
+
33
+ //........................................................................
34
+
35
+ function reset() {
36
+ parent::reset();
37
+ $this->_text_spacing = null;
38
+ }
39
+
40
+ //........................................................................
41
+
42
+ // Accessor methods
43
+ function get_text_spacing() { return $this->_text_spacing; }
44
+
45
+ function get_text() {
46
+ // FIXME: this should be in a child class (and is incorrect)
47
+ // if ( $this->_frame->get_style()->content !== "normal" ) {
48
+ // $this->_frame->get_node()->data = $this->_frame->get_style()->content;
49
+ // $this->_frame->get_style()->content = "normal";
50
+ // }
51
+
52
+ // pre_r("---");
53
+ // $style = $this->_frame->get_style();
54
+ // var_dump($text = $this->_frame->get_node()->data);
55
+ // var_dump($asc = utf8_decode($text));
56
+ // for ($i = 0; $i < strlen($asc); $i++)
57
+ // pre_r("$i: " . $asc[$i] . " - " . ord($asc[$i]));
58
+ // pre_r("width: " . Font_Metrics::get_text_width($text, $style->font_family, $style->font_size));
59
+
60
+ return $this->_frame->get_node()->data;
61
+ }
62
+
63
+ //........................................................................
64
+
65
+ // Vertical margins & padding do not apply to text frames
66
+
67
+ // http://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced:
68
+ //
69
+ // The vertical padding, border and margin of an inline, non-replaced box
70
+ // start at the top and bottom of the content area, not the
71
+ // 'line-height'. But only the 'line-height' is used to calculate the
72
+ // height of the line box.
73
+ function get_margin_height() {
74
+ // This function is called in add_frame_to_line() and is used to
75
+ // determine the line height, so we actually want to return the
76
+ // 'line-height' property, not the actual margin box
77
+ $style = $this->get_parent()->get_style();
78
+ $font = $style->font_family;
79
+ $size = $style->font_size;
80
+
81
+ /*
82
+ pre_r('-----');
83
+ pre_r($style->line_height);
84
+ pre_r($style->font_size);
85
+ pre_r(Font_Metrics::get_font_height($font, $size));
86
+ pre_r(($style->line_height / $size) * Font_Metrics::get_font_height($font, $size));
87
+ */
88
+
89
+ return ($style->line_height / $size) * Font_Metrics::get_font_height($font, $size);
90
+
91
+ }
92
+
93
+ function get_padding_box() {
94
+ $pb = $this->_frame->get_padding_box();
95
+ $pb[3] = $pb["h"] = $this->_frame->get_style()->height;
96
+ return $pb;
97
+ }
98
+ //........................................................................
99
+
100
+ // Set method
101
+ function set_text_spacing($spacing) {
102
+ $style = $this->_frame->get_style();
103
+
104
+ $this->_text_spacing = $spacing;
105
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
106
+
107
+ // Re-adjust our width to account for the change in spacing
108
+ $style->width = Font_Metrics::get_text_width($this->get_text(), $style->font_family, $style->font_size, $spacing, $char_spacing);
109
+ }
110
+
111
+ //........................................................................
112
+
113
+ // Recalculate the text width
114
+ function recalculate_width() {
115
+ $style = $this->get_style();
116
+ $text = $this->get_text();
117
+ $size = $style->font_size;
118
+ $font = $style->font_family;
119
+ $word_spacing = $style->length_in_pt($style->word_spacing);
120
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
121
+
122
+ return $style->width = Font_Metrics::get_text_width($text, $font, $size, $word_spacing, $char_spacing);
123
+ }
124
+
125
+ //........................................................................
126
+
127
+ // Text manipulation methods
128
+
129
+ // split the text in this frame at the offset specified. The remaining
130
+ // text is added a sibling frame following this one and is returned.
131
+ function split_text($offset) {
132
+ if ( $offset == 0 )
133
+ return null;
134
+
135
+ if ( self::$_buggy_splittext ) {
136
+ // workaround to solve DOMText::spliText() bug parsing multibyte strings
137
+ $node = $this->_frame->get_node();
138
+ $txt0 = $node->substringData(0, $offset);
139
+ $txt1 = $node->substringData($offset, mb_strlen($node->textContent)-1);
140
+
141
+ $node->replaceData(0, mb_strlen($node->textContent), $txt0);
142
+ $split = $node->parentNode->appendChild(new DOMText($txt1));
143
+ }
144
+ else {
145
+ $split = $this->_frame->get_node()->splitText($offset);
146
+ }
147
+
148
+ $deco = $this->copy($split);
149
+
150
+ $p = $this->get_parent();
151
+ $p->insert_child_after($deco, $this, false);
152
+
153
+ if ( $p instanceof Inline_Frame_Decorator )
154
+ $p->split($deco);
155
+
156
+ return $deco;
157
+ }
158
+
159
+ //........................................................................
160
+
161
+ function delete_text($offset, $count) {
162
+ $this->_frame->get_node()->deleteData($offset, $count);
163
+ }
164
+
165
+ //........................................................................
166
+
167
+ function set_text($text) {
168
+ $this->_frame->get_node()->data = $text;
169
+ }
170
+
171
+ }
172
+
173
+ Text_Frame_Decorator::$_buggy_splittext = PHP_VERSION_ID < 50207;
lib/dompdf/include/text_frame_reflower.cls.php ADDED
@@ -0,0 +1,441 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Reflows text frames.
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Text_Frame_Reflower extends Frame_Reflower {
17
+
18
+ /**
19
+ * @var Block_Frame_Decorator
20
+ */
21
+ protected $_block_parent; // Nearest block-level ancestor
22
+
23
+ /**
24
+ * @var Text_Frame_Decorator
25
+ */
26
+ protected $_frame;
27
+
28
+ public static $_whitespace_pattern = "/[ \t\r\n\f]+/u";
29
+
30
+ function __construct(Text_Frame_Decorator $frame) { parent::__construct($frame); }
31
+
32
+ //........................................................................
33
+
34
+ protected function _collapse_white_space($text) {
35
+ //$text = $this->_frame->get_text();
36
+ // if ( $this->_block_parent->get_current_line_box->w == 0 )
37
+ // $text = ltrim($text, " \n\r\t");
38
+ return preg_replace(self::$_whitespace_pattern, " ", $text);
39
+ }
40
+
41
+ //........................................................................
42
+
43
+ protected function _line_break($text) {
44
+ $style = $this->_frame->get_style();
45
+ $size = $style->font_size;
46
+ $font = $style->font_family;
47
+ $current_line = $this->_block_parent->get_current_line_box();
48
+
49
+ // Determine the available width
50
+ $line_width = $this->_frame->get_containing_block("w");
51
+ $current_line_width = $current_line->left + $current_line->w + $current_line->right;
52
+
53
+ $available_width = $line_width - $current_line_width;
54
+
55
+ // Account for word-spacing
56
+ $word_spacing = $style->length_in_pt($style->word_spacing);
57
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
58
+
59
+ // Determine the frame width including margin, padding & border
60
+ $text_width = Font_Metrics::get_text_width($text, $font, $size, $word_spacing, $char_spacing);
61
+ $mbp_width =
62
+ $style->length_in_pt( array( $style->margin_left,
63
+ $style->border_left_width,
64
+ $style->padding_left,
65
+ $style->padding_right,
66
+ $style->border_right_width,
67
+ $style->margin_right), $line_width );
68
+
69
+ $frame_width = $text_width + $mbp_width;
70
+
71
+ // Debugging:
72
+ // pre_r("Text: '" . htmlspecialchars($text). "'");
73
+ // pre_r("width: " .$frame_width);
74
+ // pre_r("textwidth + delta: $text_width + $mbp_width");
75
+ // pre_r("font-size: $size");
76
+ // pre_r("cb[w]: " .$line_width);
77
+ // pre_r("available width: " . $available_width);
78
+ // pre_r("current line width: " . $current_line_width);
79
+
80
+ // pre_r($words);
81
+
82
+ if ( $frame_width <= $available_width )
83
+ return false;
84
+
85
+ // split the text into words
86
+ $words = preg_split('/([\s-]+)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
87
+ $wc = count($words);
88
+
89
+ // Determine the split point
90
+ $width = 0;
91
+ $str = "";
92
+ reset($words);
93
+
94
+ // @todo support <shy>, <wbr>
95
+ for ($i = 0; $i < $wc; $i += 2) {
96
+ $word = $words[$i] . (isset($words[$i+1]) ? $words[$i+1] : "");
97
+ $word_width = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing);
98
+ if ( $width + $word_width + $mbp_width > $available_width )
99
+ break;
100
+
101
+ $width += $word_width;
102
+ $str .= $word;
103
+ }
104
+
105
+ $break_word = ($style->word_wrap === "break-word");
106
+
107
+ // The first word has overflowed. Force it onto the line
108
+ if ( $current_line_width == 0 && $width == 0 ) {
109
+
110
+ $s = "";
111
+ $last_width = 0;
112
+
113
+ if ( $break_word ) {
114
+ for ( $j = 0; $j < strlen($word); $j++ ) {
115
+ $s .= $word[$j];
116
+ $_width = Font_Metrics::get_text_width($s, $font, $size, $word_spacing, $char_spacing);
117
+ if ($_width > $available_width) {
118
+ break;
119
+ }
120
+
121
+ $last_width = $_width;
122
+ }
123
+ }
124
+
125
+ if ( $break_word && $last_width > 0 ) {
126
+ $width += $last_width;
127
+ $str .= substr($s, 0, -1);
128
+ }
129
+ else {
130
+ $width += $word_width;
131
+ $str .= $word;
132
+ }
133
+ }
134
+
135
+ $offset = mb_strlen($str);
136
+
137
+ // More debugging:
138
+ // pre_var_dump($str);
139
+ // pre_r("Width: ". $width);
140
+ // pre_r("Offset: " . $offset);
141
+
142
+ return $offset;
143
+
144
+ }
145
+
146
+ //........................................................................
147
+
148
+ protected function _newline_break($text) {
149
+
150
+ if ( ($i = mb_strpos($text, "\n")) === false)
151
+ return false;
152
+
153
+ return $i+1;
154
+
155
+ }
156
+
157
+ //........................................................................
158
+
159
+ protected function _layout_line() {
160
+ $frame = $this->_frame;
161
+ $style = $frame->get_style();
162
+ $text = $frame->get_text();
163
+ $size = $style->font_size;
164
+ $font = $style->font_family;
165
+
166
+ // Determine the text height
167
+ $style->height = Font_Metrics::get_font_height( $font, $size );
168
+
169
+ $split = false;
170
+ $add_line = false;
171
+
172
+ // Handle text transform:
173
+ // http://www.w3.org/TR/CSS21/text.html#propdef-text-transform
174
+ switch (strtolower($style->text_transform)) {
175
+ default: break;
176
+ case "capitalize": $text = mb_convert_case($text, MB_CASE_TITLE); break;
177
+ case "uppercase": $text = mb_convert_case($text, MB_CASE_UPPER); break;
178
+ case "lowercase": $text = mb_convert_case($text, MB_CASE_LOWER); break;
179
+ }
180
+
181
+ // Handle white-space property:
182
+ // http://www.w3.org/TR/CSS21/text.html#propdef-white-space
183
+ switch ($style->white_space) {
184
+
185
+ default:
186
+ case "normal":
187
+ $frame->set_text( $text = $this->_collapse_white_space($text) );
188
+ if ( $text == "" )
189
+ break;
190
+
191
+ $split = $this->_line_break($text);
192
+ break;
193
+
194
+ case "pre":
195
+ $split = $this->_newline_break($text);
196
+ $add_line = $split !== false;
197
+ break;
198
+
199
+ case "nowrap":
200
+ $frame->set_text( $text = $this->_collapse_white_space($text) );
201
+ break;
202
+
203
+ case "pre-wrap":
204
+ $split = $this->_newline_break($text);
205
+
206
+ if ( ($tmp = $this->_line_break($text)) !== false ) {
207
+ $add_line = $split < $tmp;
208
+ $split = min($tmp, $split);
209
+ } else
210
+ $add_line = true;
211
+
212
+ break;
213
+
214
+ case "pre-line":
215
+ // Collapse white-space except for \n
216
+ $frame->set_text( $text = preg_replace( "/[ \t]+/u", " ", $text ) );
217
+
218
+ if ( $text == "" )
219
+ break;
220
+
221
+ $split = $this->_newline_break($text);
222
+
223
+ if ( ($tmp = $this->_line_break($text)) !== false ) {
224
+ $add_line = $split < $tmp;
225
+ $split = min($tmp, $split);
226
+ } else
227
+ $add_line = true;
228
+
229
+ break;
230
+
231
+ }
232
+
233
+ // Handle degenerate case
234
+ if ( $text === "" )
235
+ return;
236
+
237
+ if ( $split !== false) {
238
+
239
+ // Handle edge cases
240
+ if ( $split == 0 && $text === " " ) {
241
+ $frame->set_text("");
242
+ return;
243
+ }
244
+
245
+ if ( $split == 0 ) {
246
+
247
+ // Trim newlines from the beginning of the line
248
+ //$this->_frame->set_text(ltrim($text, "\n\r"));
249
+
250
+ $this->_block_parent->add_line();
251
+ $frame->position();
252
+
253
+ // Layout the new line
254
+ $this->_layout_line();
255
+
256
+ }
257
+
258
+ else if ( $split < mb_strlen($frame->get_text()) ) {
259
+ // split the line if required
260
+ $frame->split_text($split);
261
+
262
+ $t = $frame->get_text();
263
+
264
+ // Remove any trailing newlines
265
+ if ( $split > 1 && $t[$split-1] === "\n" && !$frame->is_pre() )
266
+ $frame->set_text( mb_substr($t, 0, -1) );
267
+
268
+ // Do we need to trim spaces on wrapped lines? This might be desired, however, we
269
+ // can't trim the lines here or the layout will be affected if trimming the line
270
+ // leaves enough space to fit the next word in the text stream (because pdf layout
271
+ // is performed elsewhere).
272
+ /*if (!$this->_frame->get_prev_sibling() && !$this->_frame->get_next_sibling()) {
273
+ $t = $this->_frame->get_text();
274
+ $this->_frame->set_text( trim($t) );
275
+ }*/
276
+ }
277
+
278
+ if ( $add_line ) {
279
+ $this->_block_parent->add_line();
280
+ $frame->position();
281
+ }
282
+
283
+ } else {
284
+
285
+ // Remove empty space from start and end of line, but only where there isn't an inline sibling
286
+ // and the parent node isn't an inline element with siblings
287
+ // FIXME: Include non-breaking spaces?
288
+ $t = $frame->get_text();
289
+ $parent = $frame->get_parent();
290
+ $is_inline_frame = get_class($parent) === 'Inline_Frame_Decorator';
291
+
292
+ if ((!$is_inline_frame && !$frame->get_next_sibling())/* ||
293
+ ( $is_inline_frame && !$parent->get_next_sibling())*/) { // fails <b>BOLD <u>UNDERLINED</u></b> becomes <b>BOLD<u>UNDERLINED</u></b>
294
+ $t = rtrim($t);
295
+ }
296
+
297
+ if ((!$is_inline_frame && !$frame->get_prev_sibling())/* ||
298
+ ( $is_inline_frame && !$parent->get_prev_sibling())*/) { // <span><span>A<span>B</span> C</span></span> fails (the whitespace is removed)
299
+ $t = ltrim($t);
300
+ }
301
+
302
+ $frame->set_text( $t );
303
+
304
+ }
305
+
306
+ // Set our new width
307
+ $width = $frame->recalculate_width();
308
+ }
309
+
310
+ //........................................................................
311
+
312
+ function reflow(Block_Frame_Decorator $block = null) {
313
+ $frame = $this->_frame;
314
+ $page = $frame->get_root();
315
+ $page->check_forced_page_break($this->_frame);
316
+
317
+ if ( $page->is_full() )
318
+ return;
319
+
320
+ $this->_block_parent = /*isset($block) ? $block : */$frame->find_block_parent();
321
+
322
+ // Left trim the text if this is the first text on the line and we're
323
+ // collapsing white space
324
+ // if ( $this->_block_parent->get_current_line()->w == 0 &&
325
+ // ($frame->get_style()->white_space !== "pre" ||
326
+ // $frame->get_style()->white_space !== "pre-wrap") ) {
327
+ // $frame->set_text( ltrim( $frame->get_text() ) );
328
+ // }
329
+
330
+ $frame->position();
331
+
332
+ $this->_layout_line();
333
+
334
+ if ( $block ) {
335
+ $block->add_frame_to_line($frame);
336
+ }
337
+ }
338
+
339
+ //........................................................................
340
+
341
+ // Returns an array(0 => min, 1 => max, "min" => min, "max" => max) of the
342
+ // minimum and maximum widths of this frame
343
+ function get_min_max_width() {
344
+ /*if ( !is_null($this->_min_max_cache) )
345
+ return $this->_min_max_cache;*/
346
+ $frame = $this->_frame;
347
+ $style = $frame->get_style();
348
+ $this->_block_parent = $frame->find_block_parent();
349
+ $line_width = $frame->get_containing_block("w");
350
+
351
+ $str = $text = $frame->get_text();
352
+ $size = $style->font_size;
353
+ $font = $style->font_family;
354
+
355
+ $word_spacing = $style->length_in_pt($style->word_spacing);
356
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
357
+
358
+ switch($style->white_space) {
359
+
360
+ default:
361
+ case "normal":
362
+ $str = preg_replace(self::$_whitespace_pattern," ", $str);
363
+ case "pre-wrap":
364
+ case "pre-line":
365
+
366
+ // Find the longest word (i.e. minimum length)
367
+
368
+ // This technique (using arrays & an anonymous function) is actually
369
+ // faster than doing a single-pass character by character scan. Heh,
370
+ // yes I took the time to bench it ;)
371
+ $words = array_flip(preg_split("/[\s-]+/u",$str, -1, PREG_SPLIT_DELIM_CAPTURE));
372
+ /*foreach($words as &$word) {
373
+ $word = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing);
374
+ }*/
375
+ array_walk($words, create_function('&$val,$str',
376
+ '$val = Font_Metrics::get_text_width($str, "'.addslashes($font).'", '.$size.', '.$word_spacing.', '.$char_spacing.');'));
377
+ arsort($words);
378
+ $min = reset($words);
379
+ break;
380
+
381
+ case "pre":
382
+ $lines = array_flip(preg_split("/\n/u", $str));
383
+ /*foreach($words as &$word) {
384
+ $word = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing);
385
+ }*/
386
+ array_walk($lines, create_function('&$val,$str',
387
+ '$val = Font_Metrics::get_text_width($str, "'.addslashes($font).'", '.$size.', '.$word_spacing.', '.$char_spacing.');'));
388
+
389
+ arsort($lines);
390
+ $min = reset($lines);
391
+ break;
392
+
393
+ case "nowrap":
394
+ $min = Font_Metrics::get_text_width($this->_collapse_white_space($str), $font, $size, $word_spacing, $char_spacing);
395
+ break;
396
+
397
+ }
398
+
399
+ switch ($style->white_space) {
400
+
401
+ default:
402
+ case "normal":
403
+ case "nowrap":
404
+ $str = preg_replace(self::$_whitespace_pattern," ", $text);
405
+ break;
406
+
407
+ case "pre-line":
408
+ //XXX: Is this correct?
409
+ $str = preg_replace( "/[ \t]+/u", " ", $text);
410
+
411
+ case "pre-wrap":
412
+ // Find the longest word (i.e. minimum length)
413
+ $lines = array_flip(preg_split("/\n/", $text));
414
+ /*foreach($words as &$word) {
415
+ $word = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing);
416
+ }*/
417
+ array_walk($lines, create_function('&$val,$str',
418
+ '$val = Font_Metrics::get_text_width($str, "'.$font.'", '.$size.', '.$word_spacing.', '.$char_spacing.');'));
419
+ arsort($lines);
420
+ reset($lines);
421
+ $str = key($lines);
422
+ break;
423
+
424
+ }
425
+
426
+ $max = Font_Metrics::get_text_width($str, $font, $size, $word_spacing, $char_spacing);
427
+
428
+ $delta = $style->length_in_pt(array($style->margin_left,
429
+ $style->border_left_width,
430
+ $style->padding_left,
431
+ $style->padding_right,
432
+ $style->border_right_width,
433
+ $style->margin_right), $line_width);
434
+ $min += $delta;
435
+ $max += $delta;
436
+
437
+ return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max);
438
+
439
+ }
440
+
441
+ }
lib/dompdf/include/text_renderer.cls.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://dompdf.github.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * Renders text frames
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ class Text_Renderer extends Abstract_Renderer {
18
+
19
+ const DECO_THICKNESS = 0.02; // Thickness of underline. Screen: 0.08, print: better less, e.g. 0.04
20
+
21
+ //Tweaking if $base and $descent are not accurate.
22
+ //Check method_exists( $this->_canvas, "get_cpdf" )
23
+ //- For cpdf these can and must stay 0, because font metrics are used directly.
24
+ //- For other renderers, if different values are wanted, separate the parameter sets.
25
+ // But $size and $size-$height seem to be accurate enough
26
+ const UNDERLINE_OFFSET = 0.0; // Relative to bottom of text, as fraction of height.
27
+ const OVERLINE_OFFSET = 0.0; // Relative to top of text
28
+ const LINETHROUGH_OFFSET = 0.0; // Relative to centre of text.
29
+ const DECO_EXTENSION = 0.0; // How far to extend lines past either end, in pt
30
+
31
+ //........................................................................
32
+
33
+ /**
34
+ * @param Text_Frame_Decorator $frame
35
+ */
36
+ function render(Frame $frame) {
37
+ $text = $frame->get_text();
38
+ if ( trim($text) === "" )
39
+ return;
40
+
41
+ $style = $frame->get_style();
42
+ list($x, $y) = $frame->get_position();
43
+ $cb = $frame->get_containing_block();
44
+
45
+ if ( ($ml = $style->margin_left) === "auto" || $ml === "none" )
46
+ $ml = 0;
47
+
48
+ if ( ($pl = $style->padding_left) === "auto" || $pl === "none" )
49
+ $pl = 0;
50
+
51
+ if ( ($bl = $style->border_left_width) === "auto" || $bl === "none" )
52
+ $bl = 0;
53
+
54
+ $x += $style->length_in_pt( array($ml, $pl, $bl), $cb["w"] );
55
+
56
+ $font = $style->font_family;
57
+ $size = $frame_font_size = $style->font_size;
58
+ $height = $style->height;
59
+ $word_spacing = $frame->get_text_spacing() + $style->length_in_pt($style->word_spacing);
60
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
61
+ $width = $style->width;
62
+
63
+ /*$text = str_replace(
64
+ array("{PAGE_NUM}"),
65
+ array($this->_canvas->get_page_number()),
66
+ $text
67
+ );*/
68
+
69
+ $this->_canvas->text($x, $y, $text,
70
+ $font, $size,
71
+ $style->color, $word_spacing, $char_spacing);
72
+
73
+ $line = $frame->get_containing_line();
74
+
75
+ // FIXME Instead of using the tallest frame to position,
76
+ // the decoration, the text should be well placed
77
+ if ( false && $line->tallest_frame ) {
78
+ $base_frame = $line->tallest_frame;
79
+ $style = $base_frame->get_style();
80
+ $size = $style->font_size;
81
+ $height = $line->h * ($size / $style->line_height);
82
+ }
83
+
84
+ $line_thickness = $size * self::DECO_THICKNESS;
85
+ $underline_offset = $size * self::UNDERLINE_OFFSET;
86
+ $overline_offset = $size * self::OVERLINE_OFFSET;
87
+ $linethrough_offset = $size * self::LINETHROUGH_OFFSET;
88
+ $underline_position = -0.08;
89
+
90
+ if ( $this->_canvas instanceof CPDF_Adapter ) {
91
+ $cpdf_font = $this->_canvas->get_cpdf()->fonts[$style->font_family];
92
+
93
+ if (isset($cpdf_font["UnderlinePosition"])) {
94
+ $underline_position = $cpdf_font["UnderlinePosition"]/1000;
95
+ }
96
+
97
+ if (isset($cpdf_font["UnderlineThickness"])) {
98
+ $line_thickness = $size * ($cpdf_font["UnderlineThickness"]/1000);
99
+ }
100
+ }
101
+
102
+ $descent = $size * $underline_position;
103
+ $base = $size;
104
+
105
+ // Handle text decoration:
106
+ // http://www.w3.org/TR/CSS21/text.html#propdef-text-decoration
107
+
108
+ // Draw all applicable text-decorations. Start with the root and work our way down.
109
+ $p = $frame;
110
+ $stack = array();
111
+ while ( $p = $p->get_parent() )
112
+ $stack[] = $p;
113
+
114
+ while ( isset($stack[0]) ) {
115
+ $f = array_pop($stack);
116
+
117
+ if ( ($text_deco = $f->get_style()->text_decoration) === "none" )
118
+ continue;
119
+
120
+ $deco_y = $y; //$line->y;
121
+ $color = $f->get_style()->color;
122
+
123
+ switch ($text_deco) {
124
+
125
+ default:
126
+ continue;
127
+
128
+ case "underline":
129
+ $deco_y += $base - $descent + $underline_offset + $line_thickness/2;
130
+ break;
131
+
132
+ case "overline":
133
+ $deco_y += $overline_offset + $line_thickness/2;
134
+ break;
135
+
136
+ case "line-through":
137
+ $deco_y += $base * 0.7 + $linethrough_offset;
138
+ break;
139
+ }
140
+
141
+ $dx = 0;
142
+ $x1 = $x - self::DECO_EXTENSION;
143
+ $x2 = $x + $width + $dx + self::DECO_EXTENSION;
144
+ $this->_canvas->line($x1, $deco_y, $x2, $deco_y, $color, $line_thickness);
145
+ }
146
+
147
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES) {
148
+ $text_width = Font_Metrics::get_text_width($text, $font, $frame_font_size);
149
+ $this->_debug_layout(array($x, $y, $text_width+($line->wc-1)*$word_spacing, $frame_font_size), "orange", array(0.5, 0.5));
150
+ }
151
+ }
152
+ }
lib/dompdf/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php header("Location: www/"); ?>
lib/dompdf/lib/class.pdf.php ADDED
@@ -0,0 +1,4613 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * A PHP class to provide the basic functionality to create a pdf document without
4
+ * any requirement for additional modules.
5
+ *
6
+ * Extended by Orion Richardson to support Unicode / UTF-8 characters using
7
+ * TCPDF and others as a guide.
8
+ *
9
+ * @author Wayne Munro <pdf@ros.co.nz>
10
+ * @author Orion Richardson <orionr@yahoo.com>
11
+ * @author Helmut Tischer <htischer@weihenstephan.org>
12
+ * @author Ryan H. Masten <ryan.masten@gmail.com>
13
+ * @author Brian Sweeney <eclecticgeek@gmail.com>
14
+ * @author Fabien Ménager <fabien.menager@gmail.com>
15
+ * @license Public Domain http://creativecommons.org/licenses/publicdomain/
16
+ * @package Cpdf
17
+ */
18
+ class Cpdf {
19
+
20
+ /**
21
+ * @var integer The current number of pdf objects in the document
22
+ */
23
+ public $numObj = 0;
24
+
25
+ /**
26
+ * @var array This array contains all of the pdf objects, ready for final assembly
27
+ */
28
+ public $objects = array();
29
+
30
+ /**
31
+ * @var integer The objectId (number within the objects array) of the document catalog
32
+ */
33
+ public $catalogId;
34
+
35
+ /**
36
+ * @var array Array carrying information about the fonts that the system currently knows about
37
+ * Used to ensure that a font is not loaded twice, among other things
38
+ */
39
+ public $fonts = array();
40
+
41
+ /**
42
+ * @var string The default font metrics file to use if no other font has been loaded.
43
+ * The path to the directory containing the font metrics should be included
44
+ */
45
+ public $defaultFont = './fonts/Helvetica.afm';
46
+
47
+ /**
48
+ * @string A record of the current font
49
+ */
50
+ public $currentFont = '';
51
+
52
+ /**
53
+ * @var string The current base font
54
+ */
55
+ public $currentBaseFont = '';
56
+
57
+ /**
58
+ * @var integer The number of the current font within the font array
59
+ */
60
+ public $currentFontNum = 0;
61
+
62
+ /**
63
+ * @var integer
64
+ */
65
+ public $currentNode;
66
+
67
+ /**
68
+ * @var integer Object number of the current page
69
+ */
70
+ public $currentPage;
71
+
72
+ /**
73
+ * @var integer Object number of the currently active contents block
74
+ */
75
+ public $currentContents;
76
+
77
+ /**
78
+ * @var integer Number of fonts within the system
79
+ */
80
+ public $numFonts = 0;
81
+
82
+ /**
83
+ * @var integer Number of graphic state resources used
84
+ */
85
+ private $numStates = 0;
86
+
87
+ /**
88
+ * @var array Current color for fill operations, defaults to inactive value,
89
+ * all three components should be between 0 and 1 inclusive when active
90
+ */
91
+ public $currentColor = null;
92
+
93
+ /**
94
+ * @var array Current color for stroke operations (lines etc.)
95
+ */
96
+ public $currentStrokeColor = null;
97
+
98
+ /**
99
+ * @var string Current style that lines are drawn in
100
+ */
101
+ public $currentLineStyle = '';
102
+
103
+ /**
104
+ * @var array Current line transparency (partial graphics state)
105
+ */
106
+ public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
107
+
108
+ /**
109
+ * array Current fill transparency (partial graphics state)
110
+ */
111
+ public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
112
+
113
+ /**
114
+ * @var array An array which is used to save the state of the document, mainly the colors and styles
115
+ * it is used to temporarily change to another state, the change back to what it was before
116
+ */
117
+ public $stateStack = array();
118
+
119
+ /**
120
+ * @var integer Number of elements within the state stack
121
+ */
122
+ public $nStateStack = 0;
123
+
124
+ /**
125
+ * @var integer Number of page objects within the document
126
+ */
127
+ public $numPages = 0;
128
+
129
+ /**
130
+ * @var array Object Id storage stack
131
+ */
132
+ public $stack = array();
133
+
134
+ /**
135
+ * @var integer Number of elements within the object Id storage stack
136
+ */
137
+ public $nStack = 0;
138
+
139
+ /**
140
+ * an array which contains information about the objects which are not firmly attached to pages
141
+ * these have been added with the addObject function
142
+ */
143
+ public $looseObjects = array();
144
+
145
+ /**
146
+ * array contains infomation about how the loose objects are to be added to the document
147
+ */
148
+ public $addLooseObjects = array();
149
+
150
+ /**
151
+ * @var integer The objectId of the information object for the document
152
+ * this contains authorship, title etc.
153
+ */
154
+ public $infoObject = 0;
155
+
156
+ /**
157
+ * @var integer Number of images being tracked within the document
158
+ */
159
+ public $numImages = 0;
160
+
161
+ /**
162
+ * @var array An array containing options about the document
163
+ * it defaults to turning on the compression of the objects
164
+ */
165
+ public $options = array('compression' => true);
166
+
167
+ /**
168
+ * @var integer The objectId of the first page of the document
169
+ */
170
+ public $firstPageId;
171
+
172
+ /**
173
+ * @var float Used to track the last used value of the inter-word spacing, this is so that it is known
174
+ * when the spacing is changed.
175
+ */
176
+ public $wordSpaceAdjust = 0;
177
+
178
+ /**
179
+ * @var float Used to track the last used value of the inter-letter spacing, this is so that it is known
180
+ * when the spacing is changed.
181
+ */
182
+ public $charSpaceAdjust = 0;
183
+
184
+ /**
185
+ * @var integer The object Id of the procset object
186
+ */
187
+ public $procsetObjectId;
188
+
189
+ /**
190
+ * @var array Store the information about the relationship between font families
191
+ * this used so that the code knows which font is the bold version of another font, etc.
192
+ * the value of this array is initialised in the constuctor function.
193
+ */
194
+ public $fontFamilies = array();
195
+
196
+ /**
197
+ * @var string Folder for php serialized formats of font metrics files.
198
+ * If empty string, use same folder as original metrics files.
199
+ * This can be passed in from class creator.
200
+ * If this folder does not exist or is not writable, Cpdf will be **much** slower.
201
+ * Because of potential trouble with php safe mode, folder cannot be created at runtime.
202
+ */
203
+ public $fontcache = '';
204
+
205
+ /**
206
+ * @var integer The version of the font metrics cache file.
207
+ * This value must be manually incremented whenever the internal font data structure is modified.
208
+ */
209
+ public $fontcacheVersion = 6;
210
+
211
+ /**
212
+ * @var string Temporary folder.
213
+ * If empty string, will attempty system tmp folder.
214
+ * This can be passed in from class creator.
215
+ * Only used for conversion of gd images to jpeg images.
216
+ */
217
+ public $tmp = '';
218
+
219
+ /**
220
+ * @var string Track if the current font is bolded or italicised
221
+ */
222
+ public $currentTextState = '';
223
+
224
+ /**
225
+ * @var string Messages are stored here during processing, these can be selected afterwards to give some useful debug information
226
+ */
227
+ public $messages = '';
228
+
229
+ /**
230
+ * @var string The ancryption array for the document encryption is stored here
231
+ */
232
+ public $arc4 = '';
233
+
234
+ /**
235
+ * @var integer The object Id of the encryption information
236
+ */
237
+ public $arc4_objnum = 0;
238
+
239
+ /**
240
+ * @var string The file identifier, used to uniquely identify a pdf document
241
+ */
242
+ public $fileIdentifier = '';
243
+
244
+ /**
245
+ * @var boolean A flag to say if a document is to be encrypted or not
246
+ */
247
+ public $encrypted = false;
248
+
249
+ /**
250
+ * @var string The encryption key for the encryption of all the document content (structure is not encrypted)
251
+ */
252
+ public $encryptionKey = '';
253
+
254
+ /**
255
+ * @var array Array which forms a stack to keep track of nested callback functions
256
+ */
257
+ public $callback = array();
258
+
259
+ /**
260
+ * @var integer The number of callback functions in the callback array
261
+ */
262
+ public $nCallback = 0;
263
+
264
+ /**
265
+ * @var array Store label->id pairs for named destinations, these will be used to replace internal links
266
+ * done this way so that destinations can be defined after the location that links to them
267
+ */
268
+ public $destinations = array();
269
+
270
+ /**
271
+ * @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
272
+ * publiciables within the class, so that the user can rollback at will (from each 'start' command)
273
+ * note that this includes the objects array, so these can be large.
274
+ */
275
+ public $checkpoint = '';
276
+
277
+ /**
278
+ * @var array Table of Image origin filenames and image labels which were already added with o_image().
279
+ * Allows to merge identical images
280
+ */
281
+ public $imagelist = array();
282
+
283
+ /**
284
+ * @var boolean Whether the text passed in should be treated as Unicode or just local character set.
285
+ */
286
+ public $isUnicode = false;
287
+
288
+ /**
289
+ * @var string the JavaScript code of the document
290
+ */
291
+ public $javascript = '';
292
+
293
+ /**
294
+ * @var boolean whether the compression is possible
295
+ */
296
+ protected $compressionReady = false;
297
+
298
+ /**
299
+ * @var array Current page size
300
+ */
301
+ protected $currentPageSize = array("width" => 0, "height" => 0);
302
+
303
+ /**
304
+ * @var array All the chars that will be required in the font subsets
305
+ */
306
+ protected $stringSubsets = array();
307
+
308
+ /**
309
+ * @var string The target internal encoding
310
+ */
311
+ static protected $targetEncoding = 'iso-8859-1';
312
+
313
+ /**
314
+ * @var array The list of the core fonts
315
+ */
316
+ static protected $coreFonts = array(
317
+ 'courier', 'courier-bold', 'courier-oblique', 'courier-boldoblique',
318
+ 'helvetica', 'helvetica-bold', 'helvetica-oblique', 'helvetica-boldoblique',
319
+ 'times-roman', 'times-bold', 'times-italic', 'times-bolditalic',
320
+ 'symbol', 'zapfdingbats'
321
+ );
322
+
323
+ /**
324
+ * Class constructor
325
+ * This will start a new document
326
+ *
327
+ * @param array $pageSize Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
328
+ * @param boolean $isUnicode Whether text will be treated as Unicode or not.
329
+ * @param string $fontcache The font cache folder
330
+ * @param string $tmp The temporary folder
331
+ */
332
+ function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '') {
333
+ $this->isUnicode = $isUnicode;
334
+ $this->fontcache = $fontcache;
335
+ $this->tmp = $tmp;
336
+ $this->newDocument($pageSize);
337
+
338
+ $this->compressionReady = function_exists('gzcompress');
339
+
340
+ if ( in_array('Windows-1252', mb_list_encodings()) ) {
341
+ self::$targetEncoding = 'Windows-1252';
342
+ }
343
+
344
+ // also initialize the font families that are known about already
345
+ $this->setFontFamily('init');
346
+ // $this->fileIdentifier = md5('xxxxxxxx'.time());
347
+ }
348
+
349
+ /**
350
+ * Document object methods (internal use only)
351
+ *
352
+ * There is about one object method for each type of object in the pdf document
353
+ * Each function has the same call list ($id,$action,$options).
354
+ * $id = the object ID of the object, or what it is to be if it is being created
355
+ * $action = a string specifying the action to be performed, though ALL must support:
356
+ * 'new' - create the object with the id $id
357
+ * 'out' - produce the output for the pdf object
358
+ * $options = optional, a string or array containing the various parameters for the object
359
+ *
360
+ * These, in conjunction with the output function are the ONLY way for output to be produced
361
+ * within the pdf 'file'.
362
+ */
363
+
364
+ /**
365
+ * Destination object, used to specify the location for the user to jump to, presently on opening
366
+ */
367
+ protected function o_destination($id, $action, $options = '') {
368
+ if ($action !== 'new') {
369
+ $o = &$this->objects[$id];
370
+ }
371
+
372
+ switch ($action) {
373
+ case 'new':
374
+ $this->objects[$id] = array('t' => 'destination', 'info' => array());
375
+ $tmp = '';
376
+ switch ($options['type']) {
377
+ case 'XYZ':
378
+ case 'FitR':
379
+ $tmp = ' '.$options['p3'].$tmp;
380
+ case 'FitH':
381
+ case 'FitV':
382
+ case 'FitBH':
383
+ case 'FitBV':
384
+ $tmp = ' '.$options['p1'].' '.$options['p2'].$tmp;
385
+ case 'Fit':
386
+ case 'FitB':
387
+ $tmp = $options['type'].$tmp;
388
+ $this->objects[$id]['info']['string'] = $tmp;
389
+ $this->objects[$id]['info']['page'] = $options['page'];
390
+ }
391
+ break;
392
+
393
+ case 'out':
394
+ $tmp = $o['info'];
395
+ $res = "\n$id 0 obj\n".'['.$tmp['page'].' 0 R /'.$tmp['string']."]\nendobj";
396
+ return $res;
397
+ }
398
+ }
399
+
400
+ /**
401
+ * set the viewer preferences
402
+ */
403
+ protected function o_viewerPreferences($id, $action, $options = '') {
404
+ if ($action !== 'new') {
405
+ $o = & $this->objects[$id];
406
+ }
407
+
408
+ switch ($action) {
409
+ case 'new':
410
+ $this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array());
411
+ break;
412
+
413
+ case 'add':
414
+ foreach ($options as $k => $v) {
415
+ switch ($k) {
416
+ case 'HideToolbar':
417
+ case 'HideMenubar':
418
+ case 'HideWindowUI':
419
+ case 'FitWindow':
420
+ case 'CenterWindow':
421
+ case 'NonFullScreenPageMode':
422
+ case 'Direction':
423
+ $o['info'][$k] = $v;
424
+ break;
425
+ }
426
+ }
427
+ break;
428
+
429
+ case 'out':
430
+ $res = "\n$id 0 obj\n<< ";
431
+ foreach ($o['info'] as $k => $v) {
432
+ $res.= "\n/$k $v";
433
+ }
434
+ $res.= "\n>>\n";
435
+ return $res;
436
+ }
437
+ }
438
+
439
+ /**
440
+ * define the document catalog, the overall controller for the document
441
+ */
442
+ protected function o_catalog($id, $action, $options = '') {
443
+ if ($action !== 'new') {
444
+ $o = & $this->objects[$id];
445
+ }
446
+
447
+ switch ($action) {
448
+ case 'new':
449
+ $this->objects[$id] = array('t' => 'catalog', 'info' => array());
450
+ $this->catalogId = $id;
451
+ break;
452
+
453
+ case 'outlines':
454
+ case 'pages':
455
+ case 'openHere':
456
+ case 'javascript':
457
+ $o['info'][$action] = $options;
458
+ break;
459
+
460
+ case 'viewerPreferences':
461
+ if (!isset($o['info']['viewerPreferences'])) {
462
+ $this->numObj++;
463
+ $this->o_viewerPreferences($this->numObj, 'new');
464
+ $o['info']['viewerPreferences'] = $this->numObj;
465
+ }
466
+
467
+ $vp = $o['info']['viewerPreferences'];
468
+ $this->o_viewerPreferences($vp, 'add', $options);
469
+
470
+ break;
471
+
472
+ case 'out':
473
+ $res = "\n$id 0 obj\n<< /Type /Catalog";
474
+
475
+ foreach ($o['info'] as $k => $v) {
476
+ switch ($k) {
477
+ case 'outlines':
478
+ $res.= "\n/Outlines $v 0 R";
479
+ break;
480
+
481
+ case 'pages':
482
+ $res.= "\n/Pages $v 0 R";
483
+ break;
484
+
485
+ case 'viewerPreferences':
486
+ $res.= "\n/ViewerPreferences $v 0 R";
487
+ break;
488
+
489
+ case 'openHere':
490
+ $res.= "\n/OpenAction $v 0 R";
491
+ break;
492
+
493
+ case 'javascript':
494
+ $res.= "\n/Names <</JavaScript $v 0 R>>";
495
+ break;
496
+ }
497
+ }
498
+
499
+ $res.= " >>\nendobj";
500
+ return $res;
501
+ }
502
+ }
503
+
504
+ /**
505
+ * object which is a parent to the pages in the document
506
+ */
507
+ protected function o_pages($id, $action, $options = '') {
508
+ if ($action !== 'new') {
509
+ $o = & $this->objects[$id];
510
+ }
511
+
512
+ switch ($action) {
513
+ case 'new':
514
+ $this->objects[$id] = array('t' => 'pages', 'info' => array());
515
+ $this->o_catalog($this->catalogId, 'pages', $id);
516
+ break;
517
+
518
+ case 'page':
519
+ if (!is_array($options)) {
520
+ // then it will just be the id of the new page
521
+ $o['info']['pages'][] = $options;
522
+ }
523
+ else {
524
+ // then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
525
+ // and pos is either 'before' or 'after', saying where this page will fit.
526
+ if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
527
+ $i = array_search($options['rid'], $o['info']['pages']);
528
+ if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) {
529
+
530
+ // then there is a match
531
+ // make a space
532
+ switch ($options['pos']) {
533
+ case 'before':
534
+ $k = $i;
535
+ break;
536
+
537
+ case 'after':
538
+ $k = $i+1;
539
+ break;
540
+
541
+ default:
542
+ $k = -1;
543
+ break;
544
+ }
545
+
546
+ if ($k >= 0) {
547
+ for ($j = count($o['info']['pages'])-1; $j >= $k; $j--) {
548
+ $o['info']['pages'][$j+1] = $o['info']['pages'][$j];
549
+ }
550
+
551
+ $o['info']['pages'][$k] = $options['id'];
552
+ }
553
+ }
554
+ }
555
+ }
556
+ break;
557
+
558
+ case 'procset':
559
+ $o['info']['procset'] = $options;
560
+ break;
561
+
562
+ case 'mediaBox':
563
+ $o['info']['mediaBox'] = $options;
564
+ // which should be an array of 4 numbers
565
+ $this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
566
+ break;
567
+
568
+ case 'font':
569
+ $o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']);
570
+ break;
571
+
572
+ case 'extGState':
573
+ $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
574
+ break;
575
+
576
+ case 'xObject':
577
+ $o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']);
578
+ break;
579
+
580
+ case 'out':
581
+ if (count($o['info']['pages'])) {
582
+ $res = "\n$id 0 obj\n<< /Type /Pages\n/Kids [";
583
+ foreach ($o['info']['pages'] as $v) {
584
+ $res.= "$v 0 R\n";
585
+ }
586
+
587
+ $res.= "]\n/Count ".count($this->objects[$id]['info']['pages']);
588
+
589
+ if ( (isset($o['info']['fonts']) && count($o['info']['fonts'])) ||
590
+ isset($o['info']['procset']) ||
591
+ (isset($o['info']['extGStates']) && count($o['info']['extGStates']))) {
592
+ $res.= "\n/Resources <<";
593
+
594
+ if (isset($o['info']['procset'])) {
595
+ $res.= "\n/ProcSet ".$o['info']['procset']." 0 R";
596
+ }
597
+
598
+ if (isset($o['info']['fonts']) && count($o['info']['fonts'])) {
599
+ $res.= "\n/Font << ";
600
+ foreach ($o['info']['fonts'] as $finfo) {
601
+ $res.= "\n/F".$finfo['fontNum']." ".$finfo['objNum']." 0 R";
602
+ }
603
+ $res.= "\n>>";
604
+ }
605
+
606
+ if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
607
+ $res.= "\n/XObject << ";
608
+ foreach ($o['info']['xObjects'] as $finfo) {
609
+ $res.= "\n/".$finfo['label']." ".$finfo['objNum']." 0 R";
610
+ }
611
+ $res.= "\n>>";
612
+ }
613
+
614
+ if ( isset($o['info']['extGStates']) && count($o['info']['extGStates'])) {
615
+ $res.= "\n/ExtGState << ";
616
+ foreach ($o['info']['extGStates'] as $gstate) {
617
+ $res.= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R";
618
+ }
619
+ $res.= "\n>>";
620
+ }
621
+
622
+ $res.= "\n>>";
623
+ if (isset($o['info']['mediaBox'])) {
624
+ $tmp = $o['info']['mediaBox'];
625
+ $res.= "\n/MediaBox [".sprintf('%.3F %.3F %.3F %.3F', $tmp[0], $tmp[1], $tmp[2], $tmp[3]) .']';
626
+ }
627
+ }
628
+
629
+ $res.= "\n >>\nendobj";
630
+ }
631
+ else {
632
+ $res = "\n$id 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
633
+ }
634
+
635
+ return $res;
636
+ }
637
+ }
638
+
639
+ /**
640
+ * define the outlines in the doc, empty for now
641
+ */
642
+ protected function o_outlines($id, $action, $options = '') {
643
+ if ($action !== 'new') {
644
+ $o = &$this->objects[$id];
645
+ }
646
+
647
+ switch ($action) {
648
+ case 'new':
649
+ $this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array()));
650
+ $this->o_catalog($this->catalogId, 'outlines', $id);
651
+ break;
652
+
653
+ case 'outline':
654
+ $o['info']['outlines'][] = $options;
655
+ break;
656
+
657
+ case 'out':
658
+ if (count($o['info']['outlines'])) {
659
+ $res = "\n$id 0 obj\n<< /Type /Outlines /Kids [";
660
+ foreach ($o['info']['outlines'] as $v) {
661
+ $res.= "$v 0 R ";
662
+ }
663
+
664
+ $res.= "] /Count ".count($o['info']['outlines']) ." >>\nendobj";
665
+ } else {
666
+ $res = "\n$id 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
667
+ }
668
+
669
+ return $res;
670
+ }
671
+ }
672
+
673
+ /**
674
+ * an object to hold the font description
675
+ */
676
+ protected function o_font($id, $action, $options = '') {
677
+ if ($action !== 'new') {
678
+ $o = &$this->objects[$id];
679
+ }
680
+
681
+ switch ($action) {
682
+ case 'new':
683
+ $this->objects[$id] = array('t' => 'font', 'info' => array('name' => $options['name'], 'fontFileName' => $options['fontFileName'], 'SubType' => 'Type1'));
684
+ $fontNum = $this->numFonts;
685
+ $this->objects[$id]['info']['fontNum'] = $fontNum;
686
+
687
+ // deal with the encoding and the differences
688
+ if (isset($options['differences'])) {
689
+ // then we'll need an encoding dictionary
690
+ $this->numObj++;
691
+ $this->o_fontEncoding($this->numObj, 'new', $options);
692
+ $this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
693
+ }
694
+ else if (isset($options['encoding'])) {
695
+ // we can specify encoding here
696
+ switch ($options['encoding']) {
697
+ case 'WinAnsiEncoding':
698
+ case 'MacRomanEncoding':
699
+ case 'MacExpertEncoding':
700
+ $this->objects[$id]['info']['encoding'] = $options['encoding'];
701
+ break;
702
+
703
+ case 'none':
704
+ break;
705
+
706
+ default:
707
+ $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
708
+ break;
709
+ }
710
+ }
711
+ else {
712
+ $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
713
+ }
714
+
715
+ if ($this->fonts[$options['fontFileName']]['isUnicode']) {
716
+ // For Unicode fonts, we need to incorporate font data into
717
+ // sub-sections that are linked from the primary font section.
718
+ // Look at o_fontGIDtoCID and o_fontDescendentCID functions
719
+ // for more informaiton.
720
+ //
721
+ // All of this code is adapted from the excellent changes made to
722
+ // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
723
+
724
+ $toUnicodeId = ++$this->numObj;
725
+ $this->o_contents($toUnicodeId, 'new', 'raw');
726
+ $this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
727
+
728
+ $stream = <<<EOT
729
+ /CIDInit /ProcSet findresource begin
730
+ 12 dict begin
731
+ begincmap
732
+ /CIDSystemInfo
733
+ <</Registry (Adobe)
734
+ /Ordering (UCS)
735
+ /Supplement 0
736
+ >> def
737
+ /CMapName /Adobe-Identity-UCS def
738
+ /CMapType 2 def
739
+ 1 begincodespacerange
740
+ <0000> <FFFF>
741
+ endcodespacerange
742
+ 1 beginbfrange
743
+ <0000> <FFFF> <0000>
744
+ endbfrange
745
+ endcmap
746
+ CMapName currentdict /CMap defineresource pop
747
+ end
748
+ end
749
+ EOT;
750
+
751
+ $res = "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
752
+ $res .= "stream\n" . $stream . "endstream";
753
+
754
+ $this->objects[$toUnicodeId]['c'] = $res;
755
+
756
+ $cidFontId = ++$this->numObj;
757
+ $this->o_fontDescendentCID($cidFontId, 'new', $options);
758
+ $this->objects[$id]['info']['cidFont'] = $cidFontId;
759
+ }
760
+
761
+ // also tell the pages node about the new font
762
+ $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
763
+ break;
764
+
765
+ case 'add':
766
+ foreach ($options as $k => $v) {
767
+ switch ($k) {
768
+ case 'BaseFont':
769
+ $o['info']['name'] = $v;
770
+ break;
771
+ case 'FirstChar':
772
+ case 'LastChar':
773
+ case 'Widths':
774
+ case 'FontDescriptor':
775
+ case 'SubType':
776
+ $this->addMessage('o_font '.$k." : ".$v);
777
+ $o['info'][$k] = $v;
778
+ break;
779
+ }
780
+ }
781
+
782
+ // pass values down to descendent font
783
+ if (isset($o['info']['cidFont'])) {
784
+ $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
785
+ }
786
+ break;
787
+
788
+ case 'out':
789
+ if ($this->fonts[$this->objects[$id]['info']['fontFileName']]['isUnicode']) {
790
+ // For Unicode fonts, we need to incorporate font data into
791
+ // sub-sections that are linked from the primary font section.
792
+ // Look at o_fontGIDtoCID and o_fontDescendentCID functions
793
+ // for more informaiton.
794
+ //
795
+ // All of this code is adapted from the excellent changes made to
796
+ // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
797
+
798
+ $res = "\n$id 0 obj\n<</Type /Font\n/Subtype /Type0\n";
799
+ $res.= "/BaseFont /".$o['info']['name']."\n";
800
+
801
+ // The horizontal identity mapping for 2-byte CIDs; may be used
802
+ // with CIDFonts using any Registry, Ordering, and Supplement values.
803
+ $res.= "/Encoding /Identity-H\n";
804
+ $res.= "/DescendantFonts [".$o['info']['cidFont']." 0 R]\n";
805
+ $res.= "/ToUnicode ".$o['info']['toUnicode']." 0 R\n";
806
+ $res.= ">>\n";
807
+ $res.= "endobj";
808
+ } else {
809
+ $res = "\n$id 0 obj\n<< /Type /Font\n/Subtype /".$o['info']['SubType']."\n";
810
+ $res.= "/Name /F".$o['info']['fontNum']."\n";
811
+ $res.= "/BaseFont /".$o['info']['name']."\n";
812
+
813
+ if (isset($o['info']['encodingDictionary'])) {
814
+ // then place a reference to the dictionary
815
+ $res.= "/Encoding ".$o['info']['encodingDictionary']." 0 R\n";
816
+ } else if (isset($o['info']['encoding'])) {
817
+ // use the specified encoding
818
+ $res.= "/Encoding /".$o['info']['encoding']."\n";
819
+ }
820
+
821
+ if (isset($o['info']['FirstChar'])) {
822
+ $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
823
+ }
824
+
825
+ if (isset($o['info']['LastChar'])) {
826
+ $res.= "/LastChar ".$o['info']['LastChar']."\n";
827
+ }
828
+
829
+ if (isset($o['info']['Widths'])) {
830
+ $res.= "/Widths ".$o['info']['Widths']." 0 R\n";
831
+ }
832
+
833
+ if (isset($o['info']['FontDescriptor'])) {
834
+ $res.= "/FontDescriptor ".$o['info']['FontDescriptor']." 0 R\n";
835
+ }
836
+
837
+ $res.= ">>\n";
838
+ $res.= "endobj";
839
+ }
840
+
841
+ return $res;
842
+ }
843
+ }
844
+
845
+ /**
846
+ * a font descriptor, needed for including additional fonts
847
+ */
848
+ protected function o_fontDescriptor($id, $action, $options = '') {
849
+ if ($action !== 'new') {
850
+ $o = & $this->objects[$id];
851
+ }
852
+
853
+ switch ($action) {
854
+ case 'new':
855
+ $this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options);
856
+ break;
857
+
858
+ case 'out':
859
+ $res = "\n$id 0 obj\n<< /Type /FontDescriptor\n";
860
+ foreach ($o['info'] as $label => $value) {
861
+ switch ($label) {
862
+ case 'Ascent':
863
+ case 'CapHeight':
864
+ case 'Descent':
865
+ case 'Flags':
866
+ case 'ItalicAngle':
867
+ case 'StemV':
868
+ case 'AvgWidth':
869
+ case 'Leading':
870
+ case 'MaxWidth':
871
+ case 'MissingWidth':
872
+ case 'StemH':
873
+ case 'XHeight':
874
+ case 'CharSet':
875
+ if (mb_strlen($value, '8bit')) {
876
+ $res.= "/$label $value\n";
877
+ }
878
+
879
+ break;
880
+ case 'FontFile':
881
+ case 'FontFile2':
882
+ case 'FontFile3':
883
+ $res.= "/$label $value 0 R\n";
884
+ break;
885
+
886
+ case 'FontBBox':
887
+ $res.= "/$label [$value[0] $value[1] $value[2] $value[3]]\n";
888
+ break;
889
+
890
+ case 'FontName':
891
+ $res.= "/$label /$value\n";
892
+ break;
893
+ }
894
+ }
895
+
896
+ $res.= ">>\nendobj";
897
+
898
+ return $res;
899
+ }
900
+ }
901
+
902
+ /**
903
+ * the font encoding
904
+ */
905
+ protected function o_fontEncoding($id, $action, $options = '') {
906
+ if ($action !== 'new') {
907
+ $o = & $this->objects[$id];
908
+ }
909
+
910
+ switch ($action) {
911
+ case 'new':
912
+ // the options array should contain 'differences' and maybe 'encoding'
913
+ $this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options);
914
+ break;
915
+
916
+ case 'out':
917
+ $res = "\n$id 0 obj\n<< /Type /Encoding\n";
918
+ if (!isset($o['info']['encoding'])) {
919
+ $o['info']['encoding'] = 'WinAnsiEncoding';
920
+ }
921
+
922
+ if ($o['info']['encoding'] !== 'none') {
923
+ $res.= "/BaseEncoding /".$o['info']['encoding']."\n";
924
+ }
925
+
926
+ $res.= "/Differences \n[";
927
+
928
+ $onum = -100;
929
+
930
+ foreach ($o['info']['differences'] as $num => $label) {
931
+ if ($num != $onum+1) {
932
+ // we cannot make use of consecutive numbering
933
+ $res.= "\n$num /$label";
934
+ } else {
935
+ $res.= " /$label";
936
+ }
937
+
938
+ $onum = $num;
939
+ }
940
+
941
+ $res.= "\n]\n>>\nendobj";
942
+ return $res;
943
+ }
944
+ }
945
+
946
+ /**
947
+ * a descendent cid font, needed for unicode fonts
948
+ */
949
+ protected function o_fontDescendentCID($id, $action, $options = '') {
950
+ if ($action !== 'new') {
951
+ $o = & $this->objects[$id];
952
+ }
953
+
954
+ switch ($action) {
955
+ case 'new':
956
+ $this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
957
+
958
+ // we need a CID system info section
959
+ $cidSystemInfoId = ++$this->numObj;
960
+ $this->o_contents($cidSystemInfoId, 'new', 'raw');
961
+ $this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
962
+ $res = "<</Registry (Adobe)\n"; // A string identifying an issuer of character collections
963
+ $res.= "/Ordering (UCS)\n"; // A string that uniquely names a character collection issued by a specific registry
964
+ $res.= "/Supplement 0\n"; // The supplement number of the character collection.
965
+ $res.= ">>";
966
+ $this->objects[$cidSystemInfoId]['c'] = $res;
967
+
968
+ // and a CID to GID map
969
+ $cidToGidMapId = ++$this->numObj;
970
+ $this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
971
+ $this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
972
+ break;
973
+
974
+ case 'add':
975
+ foreach ($options as $k => $v) {
976
+ switch ($k) {
977
+ case 'BaseFont':
978
+ $o['info']['name'] = $v;
979
+ break;
980
+
981
+ case 'FirstChar':
982
+ case 'LastChar':
983
+ case 'MissingWidth':
984
+ case 'FontDescriptor':
985
+ case 'SubType':
986
+ $this->addMessage("o_fontDescendentCID $k : $v");
987
+ $o['info'][$k] = $v;
988
+ break;
989
+ }
990
+ }
991
+
992
+ // pass values down to cid to gid map
993
+ $this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
994
+ break;
995
+
996
+ case 'out':
997
+ $res = "\n$id 0 obj\n";
998
+ $res.= "<</Type /Font\n";
999
+ $res.= "/Subtype /CIDFontType2\n";
1000
+ $res.= "/BaseFont /".$o['info']['name']."\n";
1001
+ $res.= "/CIDSystemInfo ".$o['info']['cidSystemInfo']." 0 R\n";
1002
+ // if (isset($o['info']['FirstChar'])) {
1003
+ // $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
1004
+ // }
1005
+
1006
+ // if (isset($o['info']['LastChar'])) {
1007
+ // $res.= "/LastChar ".$o['info']['LastChar']."\n";
1008
+ // }
1009
+ if (isset($o['info']['FontDescriptor'])) {
1010
+ $res.= "/FontDescriptor ".$o['info']['FontDescriptor']." 0 R\n";
1011
+ }
1012
+
1013
+ if (isset($o['info']['MissingWidth'])) {
1014
+ $res.= "/DW ".$o['info']['MissingWidth']."\n";
1015
+ }
1016
+
1017
+ if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
1018
+ $cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
1019
+ $w = '';
1020
+ foreach ($cid_widths as $cid => $width) {
1021
+ $w .= "$cid [$width] ";
1022
+ }
1023
+ $res.= "/W [$w]\n";
1024
+ }
1025
+
1026
+ $res.= "/CIDToGIDMap ".$o['info']['cidToGidMap']." 0 R\n";
1027
+ $res.= ">>\n";
1028
+ $res.= "endobj";
1029
+
1030
+ return $res;
1031
+ }
1032
+ }
1033
+
1034
+ /**
1035
+ * a font glyph to character map, needed for unicode fonts
1036
+ */
1037
+ protected function o_fontGIDtoCIDMap($id, $action, $options = '') {
1038
+ if ($action !== 'new') {
1039
+ $o = & $this->objects[$id];
1040
+ }
1041
+
1042
+ switch ($action) {
1043
+ case 'new':
1044
+ $this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options);
1045
+ break;
1046
+
1047
+ case 'out':
1048
+ $res = "\n$id 0 obj\n";
1049
+ $fontFileName = $o['info']['fontFileName'];
1050
+ $tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']);
1051
+
1052
+ $compressed = isset($this->fonts[$fontFileName]['CIDtoGID_Compressed']) &&
1053
+ $this->fonts[$fontFileName]['CIDtoGID_Compressed'];
1054
+
1055
+ if (!$compressed && isset($o['raw'])) {
1056
+ $res.= $tmp;
1057
+ } else {
1058
+ $res.= "<<";
1059
+
1060
+ if (!$compressed && $this->compressionReady && $this->options['compression']) {
1061
+ // then implement ZLIB based compression on this content stream
1062
+ $compressed = true;
1063
+ $tmp = gzcompress($tmp, 6);
1064
+ }
1065
+ if ($compressed) {
1066
+ $res.= "\n/Filter /FlateDecode";
1067
+ }
1068
+
1069
+ $res.= "\n/Length ".mb_strlen($tmp, '8bit') .">>\nstream\n$tmp\nendstream";
1070
+ }
1071
+
1072
+ $res.= "\nendobj";
1073
+ return $res;
1074
+ }
1075
+ }
1076
+
1077
+ /**
1078
+ * the document procset, solves some problems with printing to old PS printers
1079
+ */
1080
+ protected function o_procset($id, $action, $options = '') {
1081
+ if ($action !== 'new') {
1082
+ $o = & $this->objects[$id];
1083
+ }
1084
+
1085
+ switch ($action) {
1086
+ case 'new':
1087
+ $this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1));
1088
+ $this->o_pages($this->currentNode, 'procset', $id);
1089
+ $this->procsetObjectId = $id;
1090
+ break;
1091
+
1092
+ case 'add':
1093
+ // this is to add new items to the procset list, despite the fact that this is considered
1094
+ // obselete, the items are required for printing to some postscript printers
1095
+ switch ($options) {
1096
+ case 'ImageB':
1097
+ case 'ImageC':
1098
+ case 'ImageI':
1099
+ $o['info'][$options] = 1;
1100
+ break;
1101
+ }
1102
+ break;
1103
+
1104
+ case 'out':
1105
+ $res = "\n$id 0 obj\n[";
1106
+ foreach ($o['info'] as $label => $val) {
1107
+ $res.= "/$label ";
1108
+ }
1109
+ $res.= "]\nendobj";
1110
+ return $res;
1111
+ }
1112
+ }
1113
+
1114
+ /**
1115
+ * define the document information
1116
+ */
1117
+ protected function o_info($id, $action, $options = '') {
1118
+ if ($action !== 'new') {
1119
+ $o = & $this->objects[$id];
1120
+ }
1121
+
1122
+ switch ($action) {
1123
+ case 'new':
1124
+ $this->infoObject = $id;
1125
+ $date = 'D:'.@date('Ymd');
1126
+ $this->objects[$id] = array('t' => 'info', 'info' => array('Creator' => 'R and OS php pdf writer, http://www.ros.co.nz', 'CreationDate' => $date));
1127
+ break;
1128
+ case 'Title':
1129
+ case 'Author':
1130
+ case 'Subject':
1131
+ case 'Keywords':
1132
+ case 'Creator':
1133
+ case 'Producer':
1134
+ case 'CreationDate':
1135
+ case 'ModDate':
1136
+ case 'Trapped':
1137
+ $o['info'][$action] = $options;
1138
+ break;
1139
+
1140
+ case 'out':
1141
+ if ($this->encrypted) {
1142
+ $this->encryptInit($id);
1143
+ }
1144
+
1145
+ $res = "\n$id 0 obj\n<<\n";
1146
+ foreach ($o['info'] as $k => $v) {
1147
+ $res.= "/$k (";
1148
+
1149
+ if ($this->encrypted) {
1150
+ $v = $this->ARC4($v);
1151
+ }
1152
+
1153
+ // dates must be outputted as-is, without Unicode transformations
1154
+ elseif (!in_array($k, array('CreationDate', 'ModDate'))){
1155
+ $v = $this->filterText($v);
1156
+ }
1157
+
1158
+ $res.= $v;
1159
+ $res.= ")\n";
1160
+ }
1161
+
1162
+ $res.= ">>\nendobj";
1163
+ return $res;
1164
+ }
1165
+ }
1166
+
1167
+ /**
1168
+ * an action object, used to link to URLS initially
1169
+ */
1170
+ protected function o_action($id, $action, $options = '') {
1171
+ if ($action !== 'new') {
1172
+ $o = & $this->objects[$id];
1173
+ }
1174
+
1175
+ switch ($action) {
1176
+ case 'new':
1177
+ if (is_array($options)) {
1178
+ $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']);
1179
+ } else {
1180
+ // then assume a URI action
1181
+ $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI');
1182
+ }
1183
+ break;
1184
+
1185
+ case 'out':
1186
+ if ($this->encrypted) {
1187
+ $this->encryptInit($id);
1188
+ }
1189
+
1190
+ $res = "\n$id 0 obj\n<< /Type /Action";
1191
+ switch ($o['type']) {
1192
+ case 'ilink':
1193
+ if (!isset($this->destinations[(string)$o['info']['label']])) break;
1194
+
1195
+ // there will be an 'label' setting, this is the name of the destination
1196
+ $res.= "\n/S /GoTo\n/D ".$this->destinations[(string)$o['info']['label']]." 0 R";
1197
+ break;
1198
+
1199
+ case 'URI':
1200
+ $res.= "\n/S /URI\n/URI (";
1201
+ if ($this->encrypted) {
1202
+ $res.= $this->filterText($this->ARC4($o['info']), true, false);
1203
+ } else {
1204
+ $res.= $this->filterText($o['info'], true, false);
1205
+ }
1206
+
1207
+ $res.= ")";
1208
+ break;
1209
+ }
1210
+
1211
+ $res.= "\n>>\nendobj";
1212
+ return $res;
1213
+ }
1214
+ }
1215
+
1216
+ /**
1217
+ * an annotation object, this will add an annotation to the current page.
1218
+ * initially will support just link annotations
1219
+ */
1220
+ protected function o_annotation($id, $action, $options = '') {
1221
+ if ($action !== 'new') {
1222
+ $o = & $this->objects[$id];
1223
+ }
1224
+
1225
+ switch ($action) {
1226
+ case 'new':
1227
+ // add the annotation to the current page
1228
+ $pageId = $this->currentPage;
1229
+ $this->o_page($pageId, 'annot', $id);
1230
+
1231
+ // and add the action object which is going to be required
1232
+ switch ($options['type']) {
1233
+ case 'link':
1234
+ $this->objects[$id] = array('t' => 'annotation', 'info' => $options);
1235
+ $this->numObj++;
1236
+ $this->o_action($this->numObj, 'new', $options['url']);
1237
+ $this->objects[$id]['info']['actionId'] = $this->numObj;
1238
+ break;
1239
+
1240
+ case 'ilink':
1241
+ // this is to a named internal link
1242
+ $label = $options['label'];
1243
+ $this->objects[$id] = array('t' => 'annotation', 'info' => $options);
1244
+ $this->numObj++;
1245
+ $this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label));
1246
+ $this->objects[$id]['info']['actionId'] = $this->numObj;
1247
+ break;
1248
+ }
1249
+ break;
1250
+
1251
+ case 'out':
1252
+ $res = "\n$id 0 obj\n<< /Type /Annot";
1253
+ switch ($o['info']['type']) {
1254
+ case 'link':
1255
+ case 'ilink':
1256
+ $res.= "\n/Subtype /Link";
1257
+ break;
1258
+ }
1259
+ $res.= "\n/A ".$o['info']['actionId']." 0 R";
1260
+ $res.= "\n/Border [0 0 0]";
1261
+ $res.= "\n/H /I";
1262
+ $res.= "\n/Rect [ ";
1263
+
1264
+ foreach ($o['info']['rect'] as $v) {
1265
+ $res.= sprintf("%.4F ", $v);
1266
+ }
1267
+
1268
+ $res.= "]";
1269
+ $res.= "\n>>\nendobj";
1270
+ return $res;
1271
+ }
1272
+ }
1273
+
1274
+ /**
1275
+ * a page object, it also creates a contents object to hold its contents
1276
+ */
1277
+ protected function o_page($id, $action, $options = '') {
1278
+ if ($action !== 'new') {
1279
+ $o = & $this->objects[$id];
1280
+ }
1281
+
1282
+ switch ($action) {
1283
+ case 'new':
1284
+ $this->numPages++;
1285
+ $this->objects[$id] = array('t' => 'page', 'info' => array('parent' => $this->currentNode, 'pageNum' => $this->numPages));
1286
+
1287
+ if (is_array($options)) {
1288
+ // then this must be a page insertion, array should contain 'rid','pos'=[before|after]
1289
+ $options['id'] = $id;
1290
+ $this->o_pages($this->currentNode, 'page', $options);
1291
+ } else {
1292
+ $this->o_pages($this->currentNode, 'page', $id);
1293
+ }
1294
+
1295
+ $this->currentPage = $id;
1296
+ //make a contents object to go with this page
1297
+ $this->numObj++;
1298
+ $this->o_contents($this->numObj, 'new', $id);
1299
+ $this->currentContents = $this->numObj;
1300
+ $this->objects[$id]['info']['contents'] = array();
1301
+ $this->objects[$id]['info']['contents'][] = $this->numObj;
1302
+
1303
+ $match = ($this->numPages%2 ? 'odd' : 'even');
1304
+ foreach ($this->addLooseObjects as $oId => $target) {
1305
+ if ($target === 'all' || $match === $target) {
1306
+ $this->objects[$id]['info']['contents'][] = $oId;
1307
+ }
1308
+ }
1309
+ break;
1310
+
1311
+ case 'content':
1312
+ $o['info']['contents'][] = $options;
1313
+ break;
1314
+
1315
+ case 'annot':
1316
+ // add an annotation to this page
1317
+ if (!isset($o['info']['annot'])) {
1318
+ $o['info']['annot'] = array();
1319
+ }
1320
+
1321
+ // $options should contain the id of the annotation dictionary
1322
+ $o['info']['annot'][] = $options;
1323
+ break;
1324
+
1325
+ case 'out':
1326
+ $res = "\n$id 0 obj\n<< /Type /Page";
1327
+ $res.= "\n/Parent ".$o['info']['parent']." 0 R";
1328
+
1329
+ if (isset($o['info']['annot'])) {
1330
+ $res.= "\n/Annots [";
1331
+ foreach ($o['info']['annot'] as $aId) {
1332
+ $res.= " $aId 0 R";
1333
+ }
1334
+ $res.= " ]";
1335
+ }
1336
+
1337
+ $count = count($o['info']['contents']);
1338
+ if ($count == 1) {
1339
+ $res.= "\n/Contents ".$o['info']['contents'][0]." 0 R";
1340
+ } else if ($count > 1) {
1341
+ $res.= "\n/Contents [\n";
1342
+
1343
+ // reverse the page contents so added objects are below normal content
1344
+ //foreach (array_reverse($o['info']['contents']) as $cId) {
1345
+ // Back to normal now that I've got transparency working --Benj
1346
+ foreach ($o['info']['contents'] as $cId) {
1347
+ $res.= "$cId 0 R\n";
1348
+ }
1349
+ $res.= "]";
1350
+ }
1351
+
1352
+ $res.= "\n>>\nendobj";
1353
+ return $res;
1354
+ }
1355
+ }
1356
+
1357
+ /**
1358
+ * the contents objects hold all of the content which appears on pages
1359
+ */
1360
+ protected function o_contents($id, $action, $options = '') {
1361
+ if ($action !== 'new') {
1362
+ $o = & $this->objects[$id];
1363
+ }
1364
+
1365
+ switch ($action) {
1366
+ case 'new':
1367
+ $this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array());
1368
+ if (mb_strlen($options, '8bit') && intval($options)) {
1369
+ // then this contents is the primary for a page
1370
+ $this->objects[$id]['onPage'] = $options;
1371
+ } else if ($options === 'raw') {
1372
+ // then this page contains some other type of system object
1373
+ $this->objects[$id]['raw'] = 1;
1374
+ }
1375
+ break;
1376
+
1377
+ case 'add':
1378
+ // add more options to the decleration
1379
+ foreach ($options as $k => $v) {
1380
+ $o['info'][$k] = $v;
1381
+ }
1382
+
1383
+ case 'out':
1384
+ $tmp = $o['c'];
1385
+ $res = "\n$id 0 obj\n";
1386
+
1387
+ if (isset($this->objects[$id]['raw'])) {
1388
+ $res.= $tmp;
1389
+ } else {
1390
+ $res.= "<<";
1391
+ if ($this->compressionReady && $this->options['compression']) {
1392
+ // then implement ZLIB based compression on this content stream
1393
+ $res.= " /Filter /FlateDecode";
1394
+ $tmp = gzcompress($tmp, 6);
1395
+ }
1396
+
1397
+ if ($this->encrypted) {
1398
+ $this->encryptInit($id);
1399
+ $tmp = $this->ARC4($tmp);
1400
+ }
1401
+
1402
+ foreach ($o['info'] as $k => $v) {
1403
+ $res.= "\n/$k $v";
1404
+ }
1405
+
1406
+ $res.= "\n/Length ".mb_strlen($tmp, '8bit') ." >>\nstream\n$tmp\nendstream";
1407
+ }
1408
+
1409
+ $res.= "\nendobj";
1410
+ return $res;
1411
+ }
1412
+ }
1413
+
1414
+ protected function o_embedjs($id, $action) {
1415
+ if ($action !== 'new') {
1416
+ $o = & $this->objects[$id];
1417
+ }
1418
+
1419
+ switch ($action) {
1420
+ case 'new':
1421
+ $this->objects[$id] = array('t' => 'embedjs', 'info' => array(
1422
+ 'Names' => '[(EmbeddedJS) '.($id+1).' 0 R]'
1423
+ ));
1424
+ break;
1425
+
1426
+ case 'out':
1427
+ $res = "\n$id 0 obj\n<< ";
1428
+ foreach ($o['info'] as $k => $v) {
1429
+ $res.= "\n/$k $v";
1430
+ }
1431
+ $res.= "\n>>\nendobj";
1432
+ return $res;
1433
+ }
1434
+ }
1435
+
1436
+ protected function o_javascript($id, $action, $code = '') {
1437
+ if ($action !== 'new') {
1438
+ $o = & $this->objects[$id];
1439
+ }
1440
+
1441
+ switch ($action) {
1442
+ case 'new':
1443
+ $this->objects[$id] = array('t' => 'javascript', 'info' => array(
1444
+ 'S' => '/JavaScript',
1445
+ 'JS' => '('.$this->filterText($code).')',
1446
+ ));
1447
+ break;
1448
+
1449
+ case 'out':
1450
+ $res = "\n$id 0 obj\n<< ";
1451
+ foreach ($o['info'] as $k => $v) {
1452
+ $res.= "\n/$k $v";
1453
+ }
1454
+ $res.= "\n>>\nendobj";
1455
+ return $res;
1456
+ }
1457
+ }
1458
+
1459
+ /**
1460
+ * an image object, will be an XObject in the document, includes description and data
1461
+ */
1462
+ protected function o_image($id, $action, $options = '') {
1463
+ if ($action !== 'new') {
1464
+ $o = & $this->objects[$id];
1465
+ }
1466
+
1467
+ switch ($action) {
1468
+ case 'new':
1469
+ // make the new object
1470
+ $this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array());
1471
+
1472
+ $info =& $this->objects[$id]['info'];
1473
+
1474
+ $info['Type'] = '/XObject';
1475
+ $info['Subtype'] = '/Image';
1476
+ $info['Width'] = $options['iw'];
1477
+ $info['Height'] = $options['ih'];
1478
+
1479
+ if (isset($options['masked']) && $options['masked']) {
1480
+ $info['SMask'] = ($this->numObj-1).' 0 R';
1481
+ }
1482
+
1483
+ if (!isset($options['type']) || $options['type'] === 'jpg') {
1484
+ if (!isset($options['channels'])) {
1485
+ $options['channels'] = 3;
1486
+ }
1487
+
1488
+ switch ($options['channels']) {
1489
+ case 1: $info['ColorSpace'] = '/DeviceGray'; break;
1490
+ case 4: $info['ColorSpace'] = '/DeviceCMYK'; break;
1491
+ default: $info['ColorSpace'] = '/DeviceRGB'; break;
1492
+ }
1493
+
1494
+ if ($info['ColorSpace'] === '/DeviceCMYK') {
1495
+ $info['Decode'] = '[1 0 1 0 1 0 1 0]';
1496
+ }
1497
+
1498
+ $info['Filter'] = '/DCTDecode';
1499
+ $info['BitsPerComponent'] = 8;
1500
+ }
1501
+
1502
+ else if ($options['type'] === 'png') {
1503
+ $info['Filter'] = '/FlateDecode';
1504
+ $info['DecodeParms'] = '<< /Predictor 15 /Colors '.$options['ncolor'].' /Columns '.$options['iw'].' /BitsPerComponent '.$options['bitsPerComponent'].'>>';
1505
+
1506
+ if ($options['isMask']) {
1507
+ $info['ColorSpace'] = '/DeviceGray';
1508
+ }
1509
+ else {
1510
+ if (mb_strlen($options['pdata'], '8bit')) {
1511
+ $tmp = ' [ /Indexed /DeviceRGB '.(mb_strlen($options['pdata'], '8bit') /3-1) .' ';
1512
+ $this->numObj++;
1513
+ $this->o_contents($this->numObj, 'new');
1514
+ $this->objects[$this->numObj]['c'] = $options['pdata'];
1515
+ $tmp.= $this->numObj.' 0 R';
1516
+ $tmp.= ' ]';
1517
+ $info['ColorSpace'] = $tmp;
1518
+
1519
+ if (isset($options['transparency'])) {
1520
+ $transparency = $options['transparency'];
1521
+ switch ($transparency['type']) {
1522
+ case 'indexed':
1523
+ $tmp = ' [ '.$transparency['data'].' '.$transparency['data'].'] ';
1524
+ $info['Mask'] = $tmp;
1525
+ break;
1526
+
1527
+ case 'color-key':
1528
+ $tmp = ' [ '.
1529
+ $transparency['r'] . ' ' . $transparency['r'] .
1530
+ $transparency['g'] . ' ' . $transparency['g'] .
1531
+ $transparency['b'] . ' ' . $transparency['b'] .
1532
+ ' ] ';
1533
+ $info['Mask'] = $tmp;
1534
+ break;
1535
+ }
1536
+ }
1537
+ } else {
1538
+ if (isset($options['transparency'])) {
1539
+ $transparency = $options['transparency'];
1540
+
1541
+ switch ($transparency['type']) {
1542
+ case 'indexed':
1543
+ $tmp = ' [ '.$transparency['data'].' '.$transparency['data'].'] ';
1544
+ $info['Mask'] = $tmp;
1545
+ break;
1546
+
1547
+ case 'color-key':
1548
+ $tmp = ' [ '.
1549
+ $transparency['r'] . ' ' . $transparency['r'] . ' ' .
1550
+ $transparency['g'] . ' ' . $transparency['g'] . ' ' .
1551
+ $transparency['b'] . ' ' . $transparency['b'] .
1552
+ ' ] ';
1553
+ $info['Mask'] = $tmp;
1554
+ break;
1555
+ }
1556
+ }
1557
+ $info['ColorSpace'] = '/'.$options['color'];
1558
+ }
1559
+ }
1560
+
1561
+ $info['BitsPerComponent'] = $options['bitsPerComponent'];
1562
+ }
1563
+
1564
+ // assign it a place in the named resource dictionary as an external object, according to
1565
+ // the label passed in with it.
1566
+ $this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id));
1567
+
1568
+ // also make sure that we have the right procset object for it.
1569
+ $this->o_procset($this->procsetObjectId, 'add', 'ImageC');
1570
+ break;
1571
+
1572
+ case 'out':
1573
+ $tmp = &$o['data'];
1574
+ $res = "\n$id 0 obj\n<<";
1575
+
1576
+ foreach ($o['info'] as $k => $v) {
1577
+ $res.= "\n/$k $v";
1578
+ }
1579
+
1580
+ if ($this->encrypted) {
1581
+ $this->encryptInit($id);
1582
+ $tmp = $this->ARC4($tmp);
1583
+ }
1584
+
1585
+ $res.= "\n/Length ".mb_strlen($tmp, '8bit') .">>\nstream\n$tmp\nendstream\nendobj";
1586
+
1587
+ return $res;
1588
+ }
1589
+ }
1590
+
1591
+ /**
1592
+ * graphics state object
1593
+ */
1594
+ protected function o_extGState($id, $action, $options = "") {
1595
+ static $valid_params = array("LW", "LC", "LC", "LJ", "ML",
1596
+ "D", "RI", "OP", "op", "OPM",
1597
+ "Font", "BG", "BG2", "UCR",
1598
+ "TR", "TR2", "HT", "FL",
1599
+ "SM", "SA", "BM", "SMask",
1600
+ "CA", "ca", "AIS", "TK");
1601
+
1602
+ if ($action !== "new") {
1603
+ $o = & $this->objects[$id];
1604
+ }
1605
+
1606
+ switch ($action) {
1607
+ case "new":
1608
+ $this->objects[$id] = array('t' => 'extGState', 'info' => $options);
1609
+
1610
+ // Tell the pages about the new resource
1611
+ $this->numStates++;
1612
+ $this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
1613
+ break;
1614
+
1615
+ case "out":
1616
+ $res = "\n$id 0 obj\n<< /Type /ExtGState\n";
1617
+
1618
+ foreach ($o["info"] as $k => $v) {
1619
+ if ( !in_array($k, $valid_params))
1620
+ continue;
1621
+ $res.= "/$k $v\n";
1622
+ }
1623
+
1624
+ $res.= ">>\nendobj";
1625
+ return $res;
1626
+ }
1627
+ }
1628
+
1629
+ /**
1630
+ * encryption object.
1631
+ */
1632
+ protected function o_encryption($id, $action, $options = '') {
1633
+ if ($action !== 'new') {
1634
+ $o = & $this->objects[$id];
1635
+ }
1636
+
1637
+ switch ($action) {
1638
+ case 'new':
1639
+ // make the new object
1640
+ $this->objects[$id] = array('t' => 'encryption', 'info' => $options);
1641
+ $this->arc4_objnum = $id;
1642
+
1643
+ // figure out the additional paramaters required
1644
+ $pad = chr(0x28) .chr(0xBF) .chr(0x4E) .chr(0x5E) .chr(0x4E) .chr(0x75) .chr(0x8A) .chr(0x41)
1645
+ .chr(0x64) .chr(0x00) .chr(0x4E) .chr(0x56) .chr(0xFF) .chr(0xFA) .chr(0x01) .chr(0x08)
1646
+ .chr(0x2E) .chr(0x2E) .chr(0x00) .chr(0xB6) .chr(0xD0) .chr(0x68) .chr(0x3E) .chr(0x80)
1647
+ .chr(0x2F) .chr(0x0C) .chr(0xA9) .chr(0xFE) .chr(0x64) .chr(0x53) .chr(0x69) .chr(0x7A);
1648
+
1649
+ $len = mb_strlen($options['owner'], '8bit');
1650
+
1651
+ if ($len > 32) {
1652
+ $owner = substr($options['owner'], 0, 32);
1653
+ } else if ($len < 32) {
1654
+ $owner = $options['owner'].substr($pad, 0, 32-$len);
1655
+ } else {
1656
+ $owner = $options['owner'];
1657
+ }
1658
+
1659
+ $len = mb_strlen($options['user'], '8bit');
1660
+ if ($len > 32) {
1661
+ $user = substr($options['user'], 0, 32);
1662
+ } else if ($len < 32) {
1663
+ $user = $options['user'].substr($pad, 0, 32-$len);
1664
+ } else {
1665
+ $user = $options['user'];
1666
+ }
1667
+
1668
+ $tmp = $this->md5_16($owner);
1669
+ $okey = substr($tmp, 0, 5);
1670
+ $this->ARC4_init($okey);
1671
+ $ovalue = $this->ARC4($user);
1672
+ $this->objects[$id]['info']['O'] = $ovalue;
1673
+
1674
+ // now make the u value, phew.
1675
+ $tmp = $this->md5_16($user.$ovalue.chr($options['p']) .chr(255) .chr(255) .chr(255) .$this->fileIdentifier);
1676
+
1677
+ $ukey = substr($tmp, 0, 5);
1678
+ $this->ARC4_init($ukey);
1679
+ $this->encryptionKey = $ukey;
1680
+ $this->encrypted = true;
1681
+ $uvalue = $this->ARC4($pad);
1682
+ $this->objects[$id]['info']['U'] = $uvalue;
1683
+ $this->encryptionKey = $ukey;
1684
+ // initialize the arc4 array
1685
+ break;
1686
+
1687
+ case 'out':
1688
+ $res = "\n$id 0 obj\n<<";
1689
+ $res.= "\n/Filter /Standard";
1690
+ $res.= "\n/V 1";
1691
+ $res.= "\n/R 2";
1692
+ $res.= "\n/O (".$this->filterText($o['info']['O'], true, false) .')';
1693
+ $res.= "\n/U (".$this->filterText($o['info']['U'], true, false) .')';
1694
+ // and the p-value needs to be converted to account for the twos-complement approach
1695
+ $o['info']['p'] = (($o['info']['p']^255) +1) *-1;
1696
+ $res.= "\n/P ".($o['info']['p']);
1697
+ $res.= "\n>>\nendobj";
1698
+ return $res;
1699
+ }
1700
+ }
1701
+
1702
+ /**
1703
+ * ARC4 functions
1704
+ * A series of function to implement ARC4 encoding in PHP
1705
+ */
1706
+
1707
+ /**
1708
+ * calculate the 16 byte version of the 128 bit md5 digest of the string
1709
+ */
1710
+ function md5_16($string) {
1711
+ $tmp = md5($string);
1712
+ $out = '';
1713
+ for ($i = 0; $i <= 30; $i = $i+2) {
1714
+ $out.= chr(hexdec(substr($tmp, $i, 2)));
1715
+ }
1716
+ return $out;
1717
+ }
1718
+
1719
+ /**
1720
+ * initialize the encryption for processing a particular object
1721
+ */
1722
+ function encryptInit($id) {
1723
+ $tmp = $this->encryptionKey;
1724
+ $hex = dechex($id);
1725
+ if (mb_strlen($hex, '8bit') < 6) {
1726
+ $hex = substr('000000', 0, 6-mb_strlen($hex, '8bit')) .$hex;
1727
+ }
1728
+ $tmp.= chr(hexdec(substr($hex, 4, 2))) .chr(hexdec(substr($hex, 2, 2))) .chr(hexdec(substr($hex, 0, 2))) .chr(0) .chr(0);
1729
+ $key = $this->md5_16($tmp);
1730
+ $this->ARC4_init(substr($key, 0, 10));
1731
+ }
1732
+
1733
+ /**
1734
+ * initialize the ARC4 encryption
1735
+ */
1736
+ function ARC4_init($key = '') {
1737
+ $this->arc4 = '';
1738
+
1739
+ // setup the control array
1740
+ if (mb_strlen($key, '8bit') == 0) {
1741
+ return;
1742
+ }
1743
+
1744
+ $k = '';
1745
+ while (mb_strlen($k, '8bit') < 256) {
1746
+ $k.= $key;
1747
+ }
1748
+
1749
+ $k = substr($k, 0, 256);
1750
+ for ($i = 0; $i < 256; $i++) {
1751
+ $this->arc4.= chr($i);
1752
+ }
1753
+
1754
+ $j = 0;
1755
+
1756
+ for ($i = 0; $i < 256; $i++) {
1757
+ $t = $this->arc4[$i];
1758
+ $j = ($j + ord($t) + ord($k[$i])) %256;
1759
+ $this->arc4[$i] = $this->arc4[$j];
1760
+ $this->arc4[$j] = $t;
1761
+ }
1762
+ }
1763
+
1764
+ /**
1765
+ * ARC4 encrypt a text string
1766
+ */
1767
+ function ARC4($text) {
1768
+ $len = mb_strlen($text, '8bit');
1769
+ $a = 0;
1770
+ $b = 0;
1771
+ $c = $this->arc4;
1772
+ $out = '';
1773
+ for ($i = 0; $i < $len; $i++) {
1774
+ $a = ($a+1) %256;
1775
+ $t = $c[$a];
1776
+ $b = ($b+ord($t)) %256;
1777
+ $c[$a] = $c[$b];
1778
+ $c[$b] = $t;
1779
+ $k = ord($c[(ord($c[$a]) + ord($c[$b])) %256]);
1780
+ $out.= chr(ord($text[$i]) ^ $k);
1781
+ }
1782
+ return $out;
1783
+ }
1784
+
1785
+ /**
1786
+ * functions which can be called to adjust or add to the document
1787
+ */
1788
+
1789
+ /**
1790
+ * add a link in the document to an external URL
1791
+ */
1792
+ function addLink($url, $x0, $y0, $x1, $y1) {
1793
+ $this->numObj++;
1794
+ $info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1));
1795
+ $this->o_annotation($this->numObj, 'new', $info);
1796
+ }
1797
+
1798
+ /**
1799
+ * add a link in the document to an internal destination (ie. within the document)
1800
+ */
1801
+ function addInternalLink($label, $x0, $y0, $x1, $y1) {
1802
+ $this->numObj++;
1803
+ $info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1));
1804
+ $this->o_annotation($this->numObj, 'new', $info);
1805
+ }
1806
+
1807
+ /**
1808
+ * set the encryption of the document
1809
+ * can be used to turn it on and/or set the passwords which it will have.
1810
+ * also the functions that the user will have are set here, such as print, modify, add
1811
+ */
1812
+ function setEncryption($userPass = '', $ownerPass = '', $pc = array()) {
1813
+ $p = bindec("11000000");
1814
+
1815
+ $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32);
1816
+
1817
+ foreach ($pc as $k => $v) {
1818
+ if ($v && isset($options[$k])) {
1819
+ $p+= $options[$k];
1820
+ } else if (isset($options[$v])) {
1821
+ $p+= $options[$v];
1822
+ }
1823
+ }
1824
+
1825
+ // implement encryption on the document
1826
+ if ($this->arc4_objnum == 0) {
1827
+ // then the block does not exist already, add it.
1828
+ $this->numObj++;
1829
+ if (mb_strlen($ownerPass) == 0) {
1830
+ $ownerPass = $userPass;
1831
+ }
1832
+
1833
+ $this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p));
1834
+ }
1835
+ }
1836
+
1837
+ /**
1838
+ * should be used for internal checks, not implemented as yet
1839
+ */
1840
+ function checkAllHere() {
1841
+ }
1842
+
1843
+ /**
1844
+ * return the pdf stream as a string returned from the function
1845
+ */
1846
+ function output($debug = false) {
1847
+ if ($debug) {
1848
+ // turn compression off
1849
+ $this->options['compression'] = false;
1850
+ }
1851
+
1852
+ if ($this->javascript) {
1853
+ $this->numObj++;
1854
+
1855
+ $js_id = $this->numObj;
1856
+ $this->o_embedjs($js_id, 'new');
1857
+ $this->o_javascript(++$this->numObj, 'new', $this->javascript);
1858
+
1859
+ $id = $this->catalogId;
1860
+
1861
+ $this->o_catalog($id, 'javascript', $js_id);
1862
+ }
1863
+
1864
+ if ($this->arc4_objnum) {
1865
+ $this->ARC4_init($this->encryptionKey);
1866
+ }
1867
+
1868
+ $this->checkAllHere();
1869
+
1870
+ $xref = array();
1871
+ $content = '%PDF-1.3';
1872
+ $pos = mb_strlen($content, '8bit');
1873
+
1874
+ foreach ($this->objects as $k => $v) {
1875
+ $tmp = 'o_'.$v['t'];
1876
+ $cont = $this->$tmp($k, 'out');
1877
+ $content.= $cont;
1878
+ $xref[] = $pos;
1879
+ $pos+= mb_strlen($cont, '8bit');
1880
+ }
1881
+
1882
+ $content.= "\nxref\n0 ".(count($xref) +1) ."\n0000000000 65535 f \n";
1883
+
1884
+ foreach ($xref as $p) {
1885
+ $content.= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n";
1886
+ }
1887
+
1888
+ $content.= "trailer\n<<\n/Size ".(count($xref) +1) ."\n/Root 1 0 R\n/Info $this->infoObject 0 R\n";
1889
+
1890
+ // if encryption has been applied to this document then add the marker for this dictionary
1891
+ if ($this->arc4_objnum > 0) {
1892
+ $content.= "/Encrypt $this->arc4_objnum 0 R\n";
1893
+ }
1894
+
1895
+ if (mb_strlen($this->fileIdentifier, '8bit')) {
1896
+ $content.= "/ID[<$this->fileIdentifier><$this->fileIdentifier>]\n";
1897
+ }
1898
+
1899
+ $content.= ">>\nstartxref\n$pos\n%%EOF\n";
1900
+
1901
+ return $content;
1902
+ }
1903
+
1904
+ /**
1905
+ * intialize a new document
1906
+ * if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
1907
+ * this function is called automatically by the constructor function
1908
+ */
1909
+ private function newDocument($pageSize = array(0, 0, 612, 792)) {
1910
+ $this->numObj = 0;
1911
+ $this->objects = array();
1912
+
1913
+ $this->numObj++;
1914
+ $this->o_catalog($this->numObj, 'new');
1915
+
1916
+ $this->numObj++;
1917
+ $this->o_outlines($this->numObj, 'new');
1918
+
1919
+ $this->numObj++;
1920
+ $this->o_pages($this->numObj, 'new');
1921
+
1922
+ $this->o_pages($this->numObj, 'mediaBox', $pageSize);
1923
+ $this->currentNode = 3;
1924
+
1925
+ $this->numObj++;
1926
+ $this->o_procset($this->numObj, 'new');
1927
+
1928
+ $this->numObj++;
1929
+ $this->o_info($this->numObj, 'new');
1930
+
1931
+ $this->numObj++;
1932
+ $this->o_page($this->numObj, 'new');
1933
+
1934
+ // need to store the first page id as there is no way to get it to the user during
1935
+ // startup
1936
+ $this->firstPageId = $this->currentContents;
1937
+ }
1938
+
1939
+ /**
1940
+ * open the font file and return a php structure containing it.
1941
+ * first check if this one has been done before and saved in a form more suited to php
1942
+ * note that if a php serialized version does not exist it will try and make one, but will
1943
+ * require write access to the directory to do it... it is MUCH faster to have these serialized
1944
+ * files.
1945
+ */
1946
+ private function openFont($font) {
1947
+ // assume that $font contains the path and file but not the extension
1948
+ $pos = strrpos($font, '/');
1949
+
1950
+ if ($pos === false) {
1951
+ $dir = './';
1952
+ $name = $font;
1953
+ } else {
1954
+ $dir = substr($font, 0, $pos+1);
1955
+ $name = substr($font, $pos+1);
1956
+ }
1957
+
1958
+ $fontcache = $this->fontcache;
1959
+ if ($fontcache == '') {
1960
+ $fontcache = $dir;
1961
+ }
1962
+
1963
+ //$name filename without folder and extension of font metrics
1964
+ //$dir folder of font metrics
1965
+ //$fontcache folder of runtime created php serialized version of font metrics.
1966
+ // If this is not given, the same folder as the font metrics will be used.
1967
+ // Storing and reusing serialized versions improves speed much
1968
+
1969
+ $this->addMessage("openFont: $font - $name");
1970
+
1971
+ if ( !$this->isUnicode || in_array(mb_strtolower(basename($name)), self::$coreFonts) ) {
1972
+ $metrics_name = "$name.afm";
1973
+ }
1974
+ else {
1975
+ $metrics_name = "$name.ufm";
1976
+ }
1977
+
1978
+ $cache_name = "$metrics_name.php";
1979
+ $this->addMessage("metrics: $metrics_name, cache: $cache_name");
1980
+
1981
+ if (file_exists($fontcache . $cache_name)) {
1982
+ $this->addMessage("openFont: php file exists $fontcache$cache_name");
1983
+ $this->fonts[$font] = require($fontcache . $cache_name);
1984
+
1985
+ if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) {
1986
+ // if the font file is old, then clear it out and prepare for re-creation
1987
+ $this->addMessage('openFont: clear out, make way for new version.');
1988
+ $this->fonts[$font] = null;
1989
+ unset($this->fonts[$font]);
1990
+ }
1991
+ }
1992
+ else {
1993
+ $old_cache_name = "php_$metrics_name";
1994
+ if (file_exists($fontcache . $old_cache_name)) {
1995
+ $this->addMessage("openFont: php file doesn't exist $fontcache$cache_name, creating it from the old format");
1996
+ $old_cache = file_get_contents($fontcache . $old_cache_name);
1997
+ file_put_contents($fontcache . $cache_name, '<?php return ' . $old_cache . ';');
1998
+ return $this->openFont($font);
1999
+ }
2000
+ }
2001
+
2002
+ if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
2003
+ // then rebuild the php_<font>.afm file from the <font>.afm file
2004
+ $this->addMessage("openFont: build php file from $dir$metrics_name");
2005
+ $data = array();
2006
+
2007
+ // 20 => 'space'
2008
+ $data['codeToName'] = array();
2009
+
2010
+ // Since we're not going to enable Unicode for the core fonts we need to use a font-based
2011
+ // setting for Unicode support rather than a global setting.
2012
+ $data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm');
2013
+
2014
+ $cidtogid = '';
2015
+ if ($data['isUnicode']) {
2016
+ $cidtogid = str_pad('', 256*256*2, "\x00");
2017
+ }
2018
+
2019
+ $file = file($dir . $metrics_name);
2020
+
2021
+ foreach ($file as $rowA) {
2022
+ $row = trim($rowA);
2023
+ $pos = strpos($row, ' ');
2024
+
2025
+ if ($pos) {
2026
+ // then there must be some keyword
2027
+ $key = substr($row, 0, $pos);
2028
+ switch ($key) {
2029
+ case 'FontName':
2030
+ case 'FullName':
2031
+ case 'FamilyName':
2032
+ case 'PostScriptName':
2033
+ case 'Weight':
2034
+ case 'ItalicAngle':
2035
+ case 'IsFixedPitch':
2036
+ case 'CharacterSet':
2037
+ case 'UnderlinePosition':
2038
+ case 'UnderlineThickness':
2039
+ case 'Version':
2040
+ case 'EncodingScheme':
2041
+ case 'CapHeight':
2042
+ case 'XHeight':
2043
+ case 'Ascender':
2044
+ case 'Descender':
2045
+ case 'StdHW':
2046
+ case 'StdVW':
2047
+ case 'StartCharMetrics':
2048
+ case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font. Otherwise it's too big.
2049
+ $data[$key] = trim(substr($row, $pos));
2050
+ break;
2051
+
2052
+ case 'FontBBox':
2053
+ $data[$key] = explode(' ', trim(substr($row, $pos)));
2054
+ break;
2055
+
2056
+ //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
2057
+ case 'C': // Found in AFM files
2058
+ $bits = explode(';', trim($row));
2059
+ $dtmp = array();
2060
+
2061
+ foreach ($bits as $bit) {
2062
+ $bits2 = explode(' ', trim($bit));
2063
+ if (mb_strlen($bits2[0], '8bit') == 0) continue;
2064
+
2065
+ if (count($bits2) > 2) {
2066
+ $dtmp[$bits2[0]] = array();
2067
+ for ($i = 1; $i < count($bits2); $i++) {
2068
+ $dtmp[$bits2[0]][] = $bits2[$i];
2069
+ }
2070
+ } else if (count($bits2) == 2) {
2071
+ $dtmp[$bits2[0]] = $bits2[1];
2072
+ }
2073
+ }
2074
+
2075
+ $c = (int)$dtmp['C'];
2076
+ $n = $dtmp['N'];
2077
+ $width = floatval($dtmp['WX']);
2078
+
2079
+ if ($c >= 0) {
2080
+ if ($c != hexdec($n)) {
2081
+ $data['codeToName'][$c] = $n;
2082
+ }
2083
+ $data['C'][$c] = $width;
2084
+ } else {
2085
+ $data['C'][$n] = $width;
2086
+ }
2087
+
2088
+ if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
2089
+ $data['MissingWidth'] = $width;
2090
+ }
2091
+
2092
+ break;
2093
+
2094
+ // U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
2095
+ case 'U': // Found in UFM files
2096
+ if (!$data['isUnicode']) break;
2097
+
2098
+ $bits = explode(';', trim($row));
2099
+ $dtmp = array();
2100
+
2101
+ foreach ($bits as $bit) {
2102
+ $bits2 = explode(' ', trim($bit));
2103
+ if (mb_strlen($bits2[0], '8bit') === 0) continue;
2104
+
2105
+ if (count($bits2) > 2) {
2106
+ $dtmp[$bits2[0]] = array();
2107
+ for ($i = 1; $i < count($bits2); $i++) {
2108
+ $dtmp[$bits2[0]][] = $bits2[$i];
2109
+ }
2110
+ } else if (count($bits2) == 2) {
2111
+ $dtmp[$bits2[0]] = $bits2[1];
2112
+ }
2113
+ }
2114
+
2115
+ $c = (int)$dtmp['U'];
2116
+ $n = $dtmp['N'];
2117
+ $glyph = $dtmp['G'];
2118
+ $width = floatval($dtmp['WX']);
2119
+
2120
+ if ($c >= 0) {
2121
+ // Set values in CID to GID map
2122
+ if ($c >= 0 && $c < 0xFFFF && $glyph) {
2123
+ $cidtogid[$c*2] = chr($glyph >> 8);
2124
+ $cidtogid[$c*2 + 1] = chr($glyph & 0xFF);
2125
+ }
2126
+
2127
+ if ($c != hexdec($n)) {
2128
+ $data['codeToName'][$c] = $n;
2129
+ }
2130
+ $data['C'][$c] = $width;
2131
+ } else {
2132
+ $data['C'][$n] = $width;
2133
+ }
2134
+
2135
+ if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
2136
+ $data['MissingWidth'] = $width;
2137
+ }
2138
+
2139
+ break;
2140
+
2141
+ case 'KPX':
2142
+ break; // don't include them as they are not used yet
2143
+ //KPX Adieresis yacute -40
2144
+ $bits = explode(' ', trim($row));
2145
+ $data['KPX'][$bits[1]][$bits[2]] = $bits[3];
2146
+ break;
2147
+ }
2148
+ }
2149
+ }
2150
+
2151
+ if ($this->compressionReady && $this->options['compression']) {
2152
+ // then implement ZLIB based compression on CIDtoGID string
2153
+ $data['CIDtoGID_Compressed'] = true;
2154
+ $cidtogid = gzcompress($cidtogid, 6);
2155
+ }
2156
+ $data['CIDtoGID'] = base64_encode($cidtogid);
2157
+ $data['_version_'] = $this->fontcacheVersion;
2158
+ $this->fonts[$font] = $data;
2159
+
2160
+ //Because of potential trouble with php safe mode, expect that the folder already exists.
2161
+ //If not existing, this will hit performance because of missing cached results.
2162
+ if ( is_dir(substr($fontcache, 0, -1)) && is_writable(substr($fontcache, 0, -1)) ) {
2163
+ file_put_contents($fontcache . $cache_name, '<?php return ' . var_export($data, true) . ';');
2164
+ }
2165
+ $data = null;
2166
+ }
2167
+
2168
+ if (!isset($this->fonts[$font])) {
2169
+ $this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
2170
+ }
2171
+
2172
+ //pre_r($this->messages);
2173
+ }
2174
+
2175
+ /**
2176
+ * if the font is not loaded then load it and make the required object
2177
+ * else just make it the current font
2178
+ * the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding'
2179
+ * note that encoding='none' will need to be used for symbolic fonts
2180
+ * and 'differences' => an array of mappings between numbers 0->255 and character names.
2181
+ *
2182
+ */
2183
+ function selectFont($fontName, $encoding = '', $set = true) {
2184
+ $ext = substr($fontName, -4);
2185
+ if ($ext === '.afm' || $ext === '.ufm') {
2186
+ $fontName = substr($fontName, 0, mb_strlen($fontName)-4);
2187
+ }
2188
+
2189
+ if (!isset($this->fonts[$fontName])) {
2190
+ $this->addMessage("selectFont: selecting - $fontName - $encoding, $set");
2191
+
2192
+ // load the file
2193
+ $this->openFont($fontName);
2194
+
2195
+ if (isset($this->fonts[$fontName])) {
2196
+ $this->numObj++;
2197
+ $this->numFonts++;
2198
+
2199
+ $font = &$this->fonts[$fontName];
2200
+
2201
+ //$this->numFonts = md5($fontName);
2202
+ $pos = strrpos($fontName, '/');
2203
+ // $dir = substr($fontName,0,$pos+1);
2204
+ $name = substr($fontName, $pos+1);
2205
+ $options = array('name' => $name, 'fontFileName' => $fontName);
2206
+
2207
+ if (is_array($encoding)) {
2208
+ // then encoding and differences might be set
2209
+ if (isset($encoding['encoding'])) {
2210
+ $options['encoding'] = $encoding['encoding'];
2211
+ }
2212
+
2213
+ if (isset($encoding['differences'])) {
2214
+ $options['differences'] = $encoding['differences'];
2215
+ }
2216
+ } else if (mb_strlen($encoding, '8bit')) {
2217
+ // then perhaps only the encoding has been set
2218
+ $options['encoding'] = $encoding;
2219
+ }
2220
+
2221
+ $fontObj = $this->numObj;
2222
+ $this->o_font($this->numObj, 'new', $options);
2223
+ $font['fontNum'] = $this->numFonts;
2224
+
2225
+ // if this is a '.afm' font, and there is a '.pfa' file to go with it ( as there
2226
+ // should be for all non-basic fonts), then load it into an object and put the
2227
+ // references into the font object
2228
+ $basefile = $fontName;
2229
+
2230
+ $fbtype = '';
2231
+ if (file_exists("$basefile.pfb")) {
2232
+ $fbtype = 'pfb';
2233
+ }
2234
+ else if (file_exists("$basefile.ttf")) {
2235
+ $fbtype = 'ttf';
2236
+ }
2237
+
2238
+ $fbfile = "$basefile.$fbtype";
2239
+
2240
+ // $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
2241
+ // $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
2242
+ $this->addMessage('selectFont: checking for - '.$fbfile);
2243
+
2244
+ // OAR - I don't understand this old check
2245
+ // if (substr($fontName, -4) === '.afm' && strlen($fbtype)) {
2246
+ if ($fbtype) {
2247
+ $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
2248
+ // $fontObj = $this->numObj;
2249
+ $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
2250
+
2251
+ // find the array of font widths, and put that into an object.
2252
+ $firstChar = -1;
2253
+ $lastChar = 0;
2254
+ $widths = array();
2255
+ $cid_widths = array();
2256
+
2257
+ foreach ($font['C'] as $num => $d) {
2258
+ if (intval($num) > 0 || $num == '0') {
2259
+ if (!$font['isUnicode']) {
2260
+ // With Unicode, widths array isn't used
2261
+ if ($lastChar>0 && $num>$lastChar+1) {
2262
+ for ($i = $lastChar+1; $i<$num; $i++) {
2263
+ $widths[] = 0;
2264
+ }
2265
+ }
2266
+ }
2267
+
2268
+ $widths[] = $d;
2269
+
2270
+ if ($font['isUnicode']) {
2271
+ $cid_widths[$num] = $d;
2272
+ }
2273
+
2274
+ if ($firstChar == -1) {
2275
+ $firstChar = $num;
2276
+ }
2277
+
2278
+ $lastChar = $num;
2279
+ }
2280
+ }
2281
+
2282
+ // also need to adjust the widths for the differences array
2283
+ if (isset($options['differences'])) {
2284
+ foreach ($options['differences'] as $charNum => $charName) {
2285
+ if ($charNum > $lastChar) {
2286
+ if (!$font['isUnicode']) {
2287
+ // With Unicode, widths array isn't used
2288
+ for ($i = $lastChar + 1; $i <= $charNum; $i++) {
2289
+ $widths[] = 0;
2290
+ }
2291
+ }
2292
+
2293
+ $lastChar = $charNum;
2294
+ }
2295
+
2296
+ if (isset($font['C'][$charName])) {
2297
+ $widths[$charNum-$firstChar] = $font['C'][$charName];
2298
+ if ($font['isUnicode']) {
2299
+ $cid_widths[$charName] = $font['C'][$charName];
2300
+ }
2301
+ }
2302
+ }
2303
+ }
2304
+
2305
+ if ($font['isUnicode']) {
2306
+ $font['CIDWidths'] = $cid_widths;
2307
+ }
2308
+
2309
+ $this->addMessage('selectFont: FirstChar = '.$firstChar);
2310
+ $this->addMessage('selectFont: LastChar = '.$lastChar);
2311
+
2312
+ $widthid = -1;
2313
+
2314
+ if (!$font['isUnicode']) {
2315
+ // With Unicode, widths array isn't used
2316
+
2317
+ $this->numObj++;
2318
+ $this->o_contents($this->numObj, 'new', 'raw');
2319
+ $this->objects[$this->numObj]['c'].= '['.implode(' ', $widths).']';
2320
+ $widthid = $this->numObj;
2321
+ }
2322
+
2323
+ $missing_width = 500;
2324
+ $stemV = 70;
2325
+
2326
+ if (isset($font['MissingWidth'])) {
2327
+ $missing_width = $font['MissingWidth'];
2328
+ }
2329
+ if (isset($font['StdVW'])) {
2330
+ $stemV = $font['StdVW'];
2331
+ } else if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
2332
+ $stemV = 120;
2333
+ }
2334
+
2335
+ // load the pfb file, and put that into an object too.
2336
+ // note that pdf supports only binary format type 1 font files, though there is a
2337
+ // simple utility to convert them from pfa to pfb.
2338
+ if (!$this->isUnicode || $fbtype !== 'ttf' || empty($this->stringSubsets)) {
2339
+ $data = file_get_contents($fbfile);
2340
+ }
2341
+ else {
2342
+ $this->stringSubsets[$fontName][] = 32; // Force space if not in yet
2343
+
2344
+ $subset = $this->stringSubsets[$fontName];
2345
+ sort($subset);
2346
+
2347
+ // Load font
2348
+ $font_obj = Font::load($fbfile);
2349
+ $font_obj->parse();
2350
+
2351
+ // Define subset
2352
+ $font_obj->setSubset($subset);
2353
+ $font_obj->reduce();
2354
+
2355
+ // Write new font
2356
+ $tmp_name = "$fbfile.tmp.".uniqid();
2357
+ $font_obj->open($tmp_name, Font_Binary_Stream::modeWrite);
2358
+ $font_obj->encode(array("OS/2"));
2359
+ $font_obj->close();
2360
+
2361
+ // Parse the new font to get cid2gid and widths
2362
+ $font_obj = Font::load($tmp_name);
2363
+
2364
+ // Find Unicode char map table
2365
+ $subtable = null;
2366
+ foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
2367
+ if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
2368
+ $subtable = $_subtable;
2369
+ break;
2370
+ }
2371
+ }
2372
+
2373
+ if ($subtable) {
2374
+ $glyphIndexArray = $subtable["glyphIndexArray"];
2375
+ $hmtx = $font_obj->getData("hmtx");
2376
+
2377
+ unset($glyphIndexArray[0xFFFF]);
2378
+
2379
+ $cidtogid = str_pad('', max(array_keys($glyphIndexArray))*2+1, "\x00");
2380
+ $font['CIDWidths'] = array();
2381
+ foreach ($glyphIndexArray as $cid => $gid) {
2382
+ if ($cid >= 0 && $cid < 0xFFFF && $gid) {
2383
+ $cidtogid[$cid*2] = chr($gid >> 8);
2384
+ $cidtogid[$cid*2 + 1] = chr($gid & 0xFF);
2385
+ }
2386
+
2387
+ $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
2388
+ $font['CIDWidths'][$cid] = $width;
2389
+ }
2390
+
2391
+ $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
2392
+ $font['CIDtoGID_Compressed'] = true;
2393
+
2394
+ $data = file_get_contents($tmp_name);
2395
+ }
2396
+ else {
2397
+ $data = file_get_contents($fbfile);
2398
+ }
2399
+
2400
+ $font_obj->close();
2401
+ unlink($tmp_name);
2402
+ }
2403
+
2404
+ // create the font descriptor
2405
+ $this->numObj++;
2406
+ $fontDescriptorId = $this->numObj;
2407
+
2408
+ $this->numObj++;
2409
+ $pfbid = $this->numObj;
2410
+
2411
+ // determine flags (more than a little flakey, hopefully will not matter much)
2412
+ $flags = 0;
2413
+
2414
+ if ($font['ItalicAngle'] != 0) {
2415
+ $flags+= pow(2, 6);
2416
+ }
2417
+
2418
+ if ($font['IsFixedPitch'] === 'true') {
2419
+ $flags+= 1;
2420
+ }
2421
+
2422
+ $flags+= pow(2, 5); // assume non-sybolic
2423
+ $list = array(
2424
+ 'Ascent' => 'Ascender',
2425
+ 'CapHeight' => 'CapHeight',
2426
+ 'MissingWidth' => 'MissingWidth',
2427
+ 'Descent' => 'Descender',
2428
+ 'FontBBox' => 'FontBBox',
2429
+ 'ItalicAngle' => 'ItalicAngle'
2430
+ );
2431
+ $fdopt = array(
2432
+ 'Flags' => $flags,
2433
+ 'FontName' => $adobeFontName,
2434
+ 'StemV' => $stemV
2435
+ );
2436
+
2437
+ foreach ($list as $k => $v) {
2438
+ if (isset($font[$v])) {
2439
+ $fdopt[$k] = $font[$v];
2440
+ }
2441
+ }
2442
+
2443
+ if ($fbtype === 'pfb') {
2444
+ $fdopt['FontFile'] = $pfbid;
2445
+ } else if ($fbtype === 'ttf') {
2446
+ $fdopt['FontFile2'] = $pfbid;
2447
+ }
2448
+
2449
+ $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
2450
+
2451
+ // embed the font program
2452
+ $this->o_contents($this->numObj, 'new');
2453
+ $this->objects[$pfbid]['c'].= $data;
2454
+
2455
+ // determine the cruicial lengths within this file
2456
+ if ($fbtype === 'pfb') {
2457
+ $l1 = strpos($data, 'eexec') +6;
2458
+ $l2 = strpos($data, '00000000') -$l1;
2459
+ $l3 = mb_strlen($data, '8bit') -$l2-$l1;
2460
+ $this->o_contents($this->numObj, 'add', array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3));
2461
+ } else if ($fbtype == 'ttf') {
2462
+ $l1 = mb_strlen($data, '8bit');
2463
+ $this->o_contents($this->numObj, 'add', array('Length1' => $l1));
2464
+ }
2465
+
2466
+ // tell the font object about all this new stuff
2467
+ $tmp = array(
2468
+ 'BaseFont' => $adobeFontName,
2469
+ 'MissingWidth' => $missing_width,
2470
+ 'Widths' => $widthid,
2471
+ 'FirstChar' => $firstChar,
2472
+ 'LastChar' => $lastChar,
2473
+ 'FontDescriptor' => $fontDescriptorId
2474
+ );
2475
+
2476
+ if ($fbtype === 'ttf') {
2477
+ $tmp['SubType'] = 'TrueType';
2478
+ }
2479
+
2480
+ $this->addMessage("adding extra info to font.($fontObj)");
2481
+
2482
+ foreach ($tmp as $fk => $fv) {
2483
+ $this->addMessage("$fk : $fv");
2484
+ }
2485
+
2486
+ $this->o_font($fontObj, 'add', $tmp);
2487
+ } else {
2488
+ $this->addMessage('selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts');
2489
+ }
2490
+
2491
+ // also set the differences here, note that this means that these will take effect only the
2492
+ //first time that a font is selected, else they are ignored
2493
+ if (isset($options['differences'])) {
2494
+ $font['differences'] = $options['differences'];
2495
+ }
2496
+ }
2497
+ }
2498
+
2499
+ if ($set && isset($this->fonts[$fontName])) {
2500
+ // so if for some reason the font was not set in the last one then it will not be selected
2501
+ $this->currentBaseFont = $fontName;
2502
+
2503
+ // the next lines mean that if a new font is selected, then the current text state will be
2504
+ // applied to it as well.
2505
+ $this->currentFont = $this->currentBaseFont;
2506
+ $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
2507
+
2508
+ //$this->setCurrentFont();
2509
+ }
2510
+
2511
+ return $this->currentFontNum;
2512
+ //return $this->numObj;
2513
+ }
2514
+
2515
+ /**
2516
+ * sets up the current font, based on the font families, and the current text state
2517
+ * note that this system is quite flexible, a bold-italic font can be completely different to a
2518
+ * italic-bold font, and even bold-bold will have to be defined within the family to have meaning
2519
+ * This function is to be called whenever the currentTextState is changed, it will update
2520
+ * the currentFont setting to whatever the appropriatte family one is.
2521
+ * If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
2522
+ * This function will change the currentFont to whatever it should be, but will not change the
2523
+ * currentBaseFont.
2524
+ */
2525
+ private function setCurrentFont() {
2526
+ // if (strlen($this->currentBaseFont) == 0){
2527
+ // // then assume an initial font
2528
+ // $this->selectFont($this->defaultFont);
2529
+ // }
2530
+ // $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1);
2531
+ // if (strlen($this->currentTextState)
2532
+ // && isset($this->fontFamilies[$cf])
2533
+ // && isset($this->fontFamilies[$cf][$this->currentTextState])){
2534
+ // // then we are in some state or another
2535
+ // // and this font has a family, and the current setting exists within it
2536
+ // // select the font, then return it
2537
+ // $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState];
2538
+ // $this->selectFont($nf,'',0);
2539
+ // $this->currentFont = $nf;
2540
+ // $this->currentFontNum = $this->fonts[$nf]['fontNum'];
2541
+ // } else {
2542
+ // // the this font must not have the right family member for the current state
2543
+ // // simply assume the base font
2544
+ $this->currentFont = $this->currentBaseFont;
2545
+ $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
2546
+ // }
2547
+ }
2548
+
2549
+ /**
2550
+ * function for the user to find out what the ID is of the first page that was created during
2551
+ * startup - useful if they wish to add something to it later.
2552
+ */
2553
+ function getFirstPageId() {
2554
+ return $this->firstPageId;
2555
+ }
2556
+
2557
+ /**
2558
+ * add content to the currently active object
2559
+ */
2560
+ private function addContent($content) {
2561
+ $this->objects[$this->currentContents]['c'] .= $content;
2562
+ }
2563
+
2564
+ /**
2565
+ * sets the color for fill operations
2566
+ */
2567
+ function setColor($color, $force = false) {
2568
+ $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
2569
+
2570
+ if (!$force && $this->currentColor == $new_color) {
2571
+ return;
2572
+ }
2573
+
2574
+ if (isset($new_color[3])) {
2575
+ $this->currentColor = $new_color;
2576
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColor));
2577
+ }
2578
+
2579
+ else if (isset($new_color[2])) {
2580
+ $this->currentColor = $new_color;
2581
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColor));
2582
+ }
2583
+ }
2584
+
2585
+ /**
2586
+ * sets the color for stroke operations
2587
+ */
2588
+ function setStrokeColor($color, $force = false) {
2589
+ $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
2590
+
2591
+ if (!$force && $this->currentStrokeColor == $new_color) return;
2592
+
2593
+ if (isset($new_color[3])) {
2594
+ $this->currentStrokeColor = $new_color;
2595
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColor));
2596
+ }
2597
+
2598
+ else if (isset($new_color[2])) {
2599
+ $this->currentStrokeColor = $new_color;
2600
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColor));
2601
+ }
2602
+ }
2603
+
2604
+ /**
2605
+ * Set the graphics state for compositions
2606
+ */
2607
+ function setGraphicsState($parameters) {
2608
+ // Create a new graphics state object
2609
+ // FIXME: should actually keep track of states that have already been created...
2610
+ $this->numObj++;
2611
+ $this->o_extGState($this->numObj, 'new', $parameters);
2612
+ $this->addContent("\n/GS$this->numStates gs");
2613
+ }
2614
+
2615
+ /**
2616
+ * Set current blend mode & opacity for lines.
2617
+ *
2618
+ * Valid blend modes are:
2619
+ *
2620
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
2621
+ * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
2622
+ * Exclusion
2623
+ *
2624
+ * @param string $mode the blend mode to use
2625
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
2626
+ */
2627
+ function setLineTransparency($mode, $opacity) {
2628
+ static $blend_modes = array("Normal", "Multiply", "Screen",
2629
+ "Overlay", "Darken", "Lighten",
2630
+ "ColorDogde", "ColorBurn", "HardLight",
2631
+ "SoftLight", "Difference", "Exclusion");
2632
+
2633
+ if ( !in_array($mode, $blend_modes) )
2634
+ $mode = "Normal";
2635
+
2636
+ // Only create a new graphics state if required
2637
+ if ( $mode === $this->currentLineTransparency["mode"] &&
2638
+ $opacity == $this->currentLineTransparency["opacity"] )
2639
+ return;
2640
+
2641
+ $this->currentLineTransparency["mode"] = $mode;
2642
+ $this->currentLineTransparency["opacity"] = $opacity;
2643
+
2644
+ $options = array("BM" => "/$mode",
2645
+ "CA" => (float)$opacity);
2646
+
2647
+ $this->setGraphicsState($options);
2648
+ }
2649
+
2650
+ /**
2651
+ * Set current blend mode & opacity for filled objects.
2652
+ *
2653
+ * Valid blend modes are:
2654
+ *
2655
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
2656
+ * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
2657
+ * Exclusion
2658
+ *
2659
+ * @param string $mode the blend mode to use
2660
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
2661
+ */
2662
+ function setFillTransparency($mode, $opacity) {
2663
+ static $blend_modes = array("Normal", "Multiply", "Screen",
2664
+ "Overlay", "Darken", "Lighten",
2665
+ "ColorDogde", "ColorBurn", "HardLight",
2666
+ "SoftLight", "Difference", "Exclusion");
2667
+
2668
+ if ( !in_array($mode, $blend_modes) ) {
2669
+ $mode = "Normal";
2670
+ }
2671
+
2672
+ if ( $mode === $this->currentFillTransparency["mode"] &&
2673
+ $opacity == $this->currentFillTransparency["opacity"] ) {
2674
+ return;
2675
+ }
2676
+
2677
+ $this->currentFillTransparency["mode"] = $mode;
2678
+ $this->currentFillTransparency["opacity"] = $opacity;
2679
+
2680
+ $options = array(
2681
+ "BM" => "/$mode",
2682
+ "ca" => (float)$opacity,
2683
+ );
2684
+
2685
+ $this->setGraphicsState($options);
2686
+ }
2687
+
2688
+ /**
2689
+ * draw a line from one set of coordinates to another
2690
+ */
2691
+ function line($x1, $y1, $x2, $y2, $stroke = true) {
2692
+ $this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2));
2693
+
2694
+ if ($stroke) {
2695
+ $this->addContent(' S');
2696
+ }
2697
+ }
2698
+
2699
+ /**
2700
+ * draw a bezier curve based on 4 control points
2701
+ */
2702
+ function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) {
2703
+ // in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
2704
+ // as the control points for the curve.
2705
+ $this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3));
2706
+ }
2707
+
2708
+ /**
2709
+ * draw a part of an ellipse
2710
+ */
2711
+ function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8) {
2712
+ $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false);
2713
+ }
2714
+
2715
+ /**
2716
+ * draw a filled ellipse
2717
+ */
2718
+ function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360) {
2719
+ return $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true);
2720
+ }
2721
+
2722
+ /**
2723
+ * draw an ellipse
2724
+ * note that the part and filled ellipse are just special cases of this function
2725
+ *
2726
+ * draws an ellipse in the current line style
2727
+ * centered at $x0,$y0, radii $r1,$r2
2728
+ * if $r2 is not set, then a circle is drawn
2729
+ * from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse.
2730
+ * nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
2731
+ * pretty crappy shape at 2, as we are approximating with bezier curves.
2732
+ */
2733
+ function ellipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360, $close = true, $fill = false, $stroke = true, $incomplete = false) {
2734
+ if ($r1 == 0) {
2735
+ return;
2736
+ }
2737
+
2738
+ if ($r2 == 0) {
2739
+ $r2 = $r1;
2740
+ }
2741
+
2742
+ if ($nSeg < 2) {
2743
+ $nSeg = 2;
2744
+ }
2745
+
2746
+ $astart = deg2rad((float)$astart);
2747
+ $afinish = deg2rad((float)$afinish);
2748
+ $totalAngle = $afinish-$astart;
2749
+
2750
+ $dt = $totalAngle/$nSeg;
2751
+ $dtm = $dt/3;
2752
+
2753
+ if ($angle != 0) {
2754
+ $a = -1*deg2rad((float)$angle);
2755
+
2756
+ $this->addContent(sprintf("\n q %.3F %.3F %.3F %.3F %.3F %.3F cm", cos($a), -sin($a), sin($a), cos($a), $x0, $y0));
2757
+
2758
+ $x0 = 0;
2759
+ $y0 = 0;
2760
+ }
2761
+
2762
+ $t1 = $astart;
2763
+ $a0 = $x0 + $r1*cos($t1);
2764
+ $b0 = $y0 + $r2*sin($t1);
2765
+ $c0 = -$r1 * sin($t1);
2766
+ $d0 = $r2 * cos($t1);
2767
+
2768
+ if (!$incomplete) {
2769
+ $this->addContent(sprintf("\n%.3F %.3F m ", $a0, $b0));
2770
+ }
2771
+
2772
+ for ($i = 1; $i <= $nSeg; $i++) {
2773
+ // draw this bit of the total curve
2774
+ $t1 = $i * $dt + $astart;
2775
+ $a1 = $x0 + $r1 * cos($t1);
2776
+ $b1 = $y0 + $r2 * sin($t1);
2777
+ $c1 = -$r1 * sin($t1);
2778
+ $d1 = $r2 * cos($t1);
2779
+
2780
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F %.3F %.3F c", ($a0+$c0*$dtm), ($b0+$d0*$dtm), ($a1-$c1*$dtm), ($b1-$d1*$dtm), $a1, $b1));
2781
+
2782
+ $a0 = $a1;
2783
+ $b0 = $b1;
2784
+ $c0 = $c1;
2785
+ $d0 = $d1;
2786
+ }
2787
+
2788
+ if (!$incomplete) {
2789
+ if ($fill) {
2790
+ $this->addContent(' f');
2791
+ }
2792
+ else if ($close) {
2793
+ $this->addContent(' s'); // small 's' signifies closing the path as well
2794
+ }
2795
+ else if ($stroke) {
2796
+ $this->addContent(' S');
2797
+ }
2798
+ }
2799
+
2800
+ if ($angle != 0) {
2801
+ $this->addContent(' Q');
2802
+ }
2803
+ }
2804
+
2805
+ /**
2806
+ * this sets the line drawing style.
2807
+ * width, is the thickness of the line in user units
2808
+ * cap is the type of cap to put on the line, values can be 'butt','round','square'
2809
+ * where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the
2810
+ * end of the line.
2811
+ * join can be 'miter', 'round', 'bevel'
2812
+ * dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the
2813
+ * on and off dashes.
2814
+ * (2) represents 2 on, 2 off, 2 on , 2 off ...
2815
+ * (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
2816
+ * phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
2817
+ */
2818
+ function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0) {
2819
+ // this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
2820
+ $string = '';
2821
+
2822
+ if ($width > 0) {
2823
+ $string.= "$width w";
2824
+ }
2825
+
2826
+ $ca = array('butt' => 0, 'round' => 1, 'square' => 2);
2827
+
2828
+ if (isset($ca[$cap])) {
2829
+ $string.= " $ca[$cap] J";
2830
+ }
2831
+
2832
+ $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
2833
+
2834
+ if (isset($ja[$join])) {
2835
+ $string.= " $ja[$join] j";
2836
+ }
2837
+
2838
+ if (is_array($dash)) {
2839
+ $string.= ' [ ' . implode(' ', $dash) . " ] $phase d";
2840
+ }
2841
+
2842
+ $this->currentLineStyle = $string;
2843
+ $this->addContent("\n$string");
2844
+ }
2845
+
2846
+ /**
2847
+ * draw a polygon, the syntax for this is similar to the GD polygon command
2848
+ */
2849
+ function polygon($p, $np, $f = false) {
2850
+ $this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
2851
+
2852
+ for ($i = 2; $i < $np * 2; $i = $i + 2) {
2853
+ $this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i+1]));
2854
+ }
2855
+
2856
+ if ($f) {
2857
+ $this->addContent(' f');
2858
+ } else {
2859
+ $this->addContent(' S');
2860
+ }
2861
+ }
2862
+
2863
+ /**
2864
+ * a filled rectangle, note that it is the width and height of the rectangle which are the secondary paramaters, not
2865
+ * the coordinates of the upper-right corner
2866
+ */
2867
+ function filledRectangle($x1, $y1, $width, $height) {
2868
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height));
2869
+ }
2870
+
2871
+ /**
2872
+ * draw a rectangle, note that it is the width and height of the rectangle which are the secondary paramaters, not
2873
+ * the coordinates of the upper-right corner
2874
+ */
2875
+ function rectangle($x1, $y1, $width, $height) {
2876
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height));
2877
+ }
2878
+
2879
+ /**
2880
+ * save the current graphic state
2881
+ */
2882
+ function save() {
2883
+ // we must reset the color cache or it will keep bad colors after clipping
2884
+ $this->currentColor = null;
2885
+ $this->currentStrokeColor = null;
2886
+ $this->addContent("\nq");
2887
+ }
2888
+
2889
+ /**
2890
+ * restore the last graphic state
2891
+ */
2892
+ function restore() {
2893
+ // we must reset the color cache or it will keep bad colors after clipping
2894
+ $this->currentColor = null;
2895
+ $this->currentStrokeColor = null;
2896
+ $this->addContent("\nQ");
2897
+ }
2898
+
2899
+ /**
2900
+ * draw a clipping rectangle, all the elements added after this will be clipped
2901
+ */
2902
+ function clippingRectangle($x1, $y1, $width, $height) {
2903
+ $this->save();
2904
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height));
2905
+ }
2906
+
2907
+ /**
2908
+ * draw a clipping rounded rectangle, all the elements added after this will be clipped
2909
+ */
2910
+ function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
2911
+ $this->save();
2912
+
2913
+ // start: top edge, left end
2914
+ $this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h));
2915
+
2916
+ // curve: bottom-left corner
2917
+ $this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true);
2918
+
2919
+ // line: right edge, bottom end
2920
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1));
2921
+
2922
+ // curve: bottom-right corner
2923
+ $this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true);
2924
+
2925
+ // line: right edge, top end
2926
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR));
2927
+
2928
+ // curve: bottom-right corner
2929
+ $this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true);
2930
+
2931
+ // line: bottom edge, right end
2932
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h));
2933
+
2934
+ // curve: top-right corner
2935
+ $this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true);
2936
+
2937
+ // line: top edge, left end
2938
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1));
2939
+
2940
+ // Close & clip
2941
+ $this->addContent(" W n");
2942
+ }
2943
+
2944
+ /**
2945
+ * ends the last clipping shape
2946
+ */
2947
+ function clippingEnd() {
2948
+ $this->restore();
2949
+ }
2950
+
2951
+ /**
2952
+ * scale
2953
+ * @param float $s_x scaling factor for width as percent
2954
+ * @param float $s_y scaling factor for height as percent
2955
+ * @param float $x Origin abscisse
2956
+ * @param float $y Origin ordinate
2957
+ */
2958
+ function scale($s_x, $s_y, $x, $y) {
2959
+ $y = $this->currentPageSize["height"] - $y;
2960
+
2961
+ $tm = array(
2962
+ $s_x, 0,
2963
+ 0, $s_y,
2964
+ $x*(1-$s_x), $y*(1-$s_y)
2965
+ );
2966
+
2967
+ $this->transform($tm);
2968
+ }
2969
+
2970
+ /**
2971
+ * translate
2972
+ * @param float $t_x movement to the right
2973
+ * @param float $t_y movement to the bottom
2974
+ */
2975
+ function translate($t_x, $t_y) {
2976
+ $tm = array(
2977
+ 1, 0,
2978
+ 0, 1,
2979
+ $t_x, -$t_y
2980
+ );
2981
+
2982
+ $this->transform($tm);
2983
+ }
2984
+
2985
+ /**
2986
+ * rotate
2987
+ * @param float $angle angle in degrees for counter-clockwise rotation
2988
+ * @param float $x Origin abscisse
2989
+ * @param float $y Origin ordinate
2990
+ */
2991
+ function rotate($angle, $x, $y) {
2992
+ $y = $this->currentPageSize["height"] - $y;
2993
+
2994
+ $a = deg2rad($angle);
2995
+ $cos_a = cos($a);
2996
+ $sin_a = sin($a);
2997
+
2998
+ $tm = array(
2999
+ $cos_a, -$sin_a,
3000
+ $sin_a, $cos_a,
3001
+ $x - $sin_a*$y - $cos_a*$x, $y - $cos_a*$y + $sin_a*$x,
3002
+ );
3003
+
3004
+ $this->transform($tm);
3005
+ }
3006
+
3007
+ /**
3008
+ * skew
3009
+ * @param float $angle_x
3010
+ * @param float $angle_y
3011
+ * @param float $x Origin abscisse
3012
+ * @param float $y Origin ordinate
3013
+ */
3014
+ function skew($angle_x, $angle_y, $x, $y) {
3015
+ $y = $this->currentPageSize["height"] - $y;
3016
+
3017
+ $tan_x = tan(deg2rad($angle_x));
3018
+ $tan_y = tan(deg2rad($angle_y));
3019
+
3020
+ $tm = array(
3021
+ 1, -$tan_y,
3022
+ -$tan_x, 1,
3023
+ $tan_x*$y, $tan_y*$x,
3024
+ );
3025
+
3026
+ $this->transform($tm);
3027
+ }
3028
+
3029
+ /**
3030
+ * apply graphic transformations
3031
+ * @param array $tm transformation matrix
3032
+ */
3033
+ function transform($tm) {
3034
+ $this->addContent(vsprintf("\n %.3F %.3F %.3F %.3F %.3F %.3F cm", $tm));
3035
+ }
3036
+
3037
+ /**
3038
+ * add a new page to the document
3039
+ * this also makes the new page the current active object
3040
+ */
3041
+ function newPage($insert = 0, $id = 0, $pos = 'after') {
3042
+ // if there is a state saved, then go up the stack closing them
3043
+ // then on the new page, re-open them with the right setings
3044
+
3045
+ if ($this->nStateStack) {
3046
+ for ($i = $this->nStateStack; $i >= 1; $i--) {
3047
+ $this->restoreState($i);
3048
+ }
3049
+ }
3050
+
3051
+ $this->numObj++;
3052
+
3053
+ if ($insert) {
3054
+ // the id from the ezPdf class is the id of the contents of the page, not the page object itself
3055
+ // query that object to find the parent
3056
+ $rid = $this->objects[$id]['onPage'];
3057
+ $opt = array('rid' => $rid, 'pos' => $pos);
3058
+ $this->o_page($this->numObj, 'new', $opt);
3059
+ } else {
3060
+ $this->o_page($this->numObj, 'new');
3061
+ }
3062
+
3063
+ // if there is a stack saved, then put that onto the page
3064
+ if ($this->nStateStack) {
3065
+ for ($i = 1; $i <= $this->nStateStack; $i++) {
3066
+ $this->saveState($i);
3067
+ }
3068
+ }
3069
+
3070
+ // and if there has been a stroke or fill color set, then transfer them
3071
+ if (isset($this->currentColor)) {
3072
+ $this->setColor($this->currentColor, true);
3073
+ }
3074
+
3075
+ if (isset($this->currentStrokeColor)) {
3076
+ $this->setStrokeColor($this->currentStrokeColor, true);
3077
+ }
3078
+
3079
+ // if there is a line style set, then put this in too
3080
+ if (mb_strlen($this->currentLineStyle, '8bit')) {
3081
+ $this->addContent("\n$this->currentLineStyle");
3082
+ }
3083
+
3084
+ // the call to the o_page object set currentContents to the present page, so this can be returned as the page id
3085
+ return $this->currentContents;
3086
+ }
3087
+
3088
+ /**
3089
+ * output the pdf code, streaming it to the browser
3090
+ * the relevant headers are set so that hopefully the browser will recognise it
3091
+ */
3092
+ function stream($options = '') {
3093
+ // setting the options allows the adjustment of the headers
3094
+ // values at the moment are:
3095
+ // 'Content-Disposition' => 'filename' - sets the filename, though not too sure how well this will
3096
+ // work as in my trial the browser seems to use the filename of the php file with .pdf on the end
3097
+ // 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this header is not included, off by default
3098
+ // this header seems to have caused some problems despite tha fact that it is supposed to solve
3099
+ // them, so I am leaving it off by default.
3100
+ // 'compress' = > 1 or 0 - apply content stream compression, this is on (1) by default
3101
+ // 'Attachment' => 1 or 0 - if 1, force the browser to open a download dialog
3102
+ if (!is_array($options)) {
3103
+ $options = array();
3104
+ }
3105
+
3106
+ if ( headers_sent()) {
3107
+ die("Unable to stream pdf: headers already sent");
3108
+ }
3109
+
3110
+ $debug = empty($options['compression']);
3111
+ $tmp = ltrim($this->output($debug));
3112
+
3113
+ header("Cache-Control: private");
3114
+ header("Content-type: application/pdf");
3115
+
3116
+ //FIXME: I don't know that this is sufficient for determining content length (i.e. what about transport compression?)
3117
+ header("Content-Length: " . mb_strlen($tmp, '8bit'));
3118
+ $fileName = (isset($options['Content-Disposition']) ? $options['Content-Disposition'] : 'file.pdf');
3119
+
3120
+ if ( !isset($options["Attachment"]))
3121
+ $options["Attachment"] = true;
3122
+
3123
+ $attachment = $options["Attachment"] ? "attachment" : "inline";
3124
+
3125
+ // detect the character encoding of the incoming file
3126
+ $encoding = mb_detect_encoding($fileName);
3127
+ $fallbackfilename = mb_convert_encoding($fileName, "ISO-8859-1", $encoding);
3128
+ $encodedfallbackfilename = rawurlencode($fallbackfilename);
3129
+ $encodedfilename = rawurlencode($fileName);
3130
+
3131
+ header("Content-Disposition: $attachment; filename=". $encodedfallbackfilename ."; filename*=UTF-8''$encodedfilename");
3132
+
3133
+ if (isset($options['Accept-Ranges']) && $options['Accept-Ranges'] == 1) {
3134
+ //FIXME: Is this the correct value ... spec says 1#range-unit
3135
+ header("Accept-Ranges: " . mb_strlen($tmp, '8bit'));
3136
+ }
3137
+
3138
+ echo $tmp;
3139
+ flush();
3140
+ }
3141
+
3142
+ /**
3143
+ * return the height in units of the current font in the given size
3144
+ */
3145
+ function getFontHeight($size) {
3146
+ if (!$this->numFonts) {
3147
+ $this->selectFont($this->defaultFont);
3148
+ }
3149
+
3150
+ $font = $this->fonts[$this->currentFont];
3151
+
3152
+ // for the current font, and the given size, what is the height of the font in user units
3153
+ if ( isset($font['Ascender']) && isset($font['Descender']) ) {
3154
+ $h = $font['Ascender']-$font['Descender'];
3155
+ }
3156
+ else {
3157
+ $h = $font['FontBBox'][3]-$font['FontBBox'][1];
3158
+ }
3159
+
3160
+ // have to adjust by a font offset for Windows fonts. unfortunately it looks like
3161
+ // the bounding box calculations are wrong and I don't know why.
3162
+ if (isset($font['FontHeightOffset'])) {
3163
+ // For CourierNew from Windows this needs to be -646 to match the
3164
+ // Adobe native Courier font.
3165
+ //
3166
+ // For FreeMono from GNU this needs to be -337 to match the
3167
+ // Courier font.
3168
+ //
3169
+ // Both have been added manually to the .afm and .ufm files.
3170
+ $h += (int)$font['FontHeightOffset'];
3171
+ }
3172
+
3173
+ return $size*$h/1000;
3174
+ }
3175
+
3176
+ function getFontXHeight($size) {
3177
+ if (!$this->numFonts) {
3178
+ $this->selectFont($this->defaultFont);
3179
+ }
3180
+
3181
+ $font = $this->fonts[$this->currentFont];
3182
+
3183
+ // for the current font, and the given size, what is the height of the font in user units
3184
+ if ( isset($font['XHeight']) ) {
3185
+ $xh = $font['Ascender']-$font['Descender'];
3186
+ }
3187
+ else {
3188
+ $xh = $this->getFontHeight($size) / 2;
3189
+ }
3190
+
3191
+ return $size*$xh/1000;
3192
+ }
3193
+
3194
+ /**
3195
+ * return the font descender, this will normally return a negative number
3196
+ * if you add this number to the baseline, you get the level of the bottom of the font
3197
+ * it is in the pdf user units
3198
+ */
3199
+ function getFontDescender($size) {
3200
+ // note that this will most likely return a negative value
3201
+ if (!$this->numFonts) {
3202
+ $this->selectFont($this->defaultFont);
3203
+ }
3204
+
3205
+ //$h = $this->fonts[$this->currentFont]['FontBBox'][1];
3206
+ $h = $this->fonts[$this->currentFont]['Descender'];
3207
+
3208
+ return $size*$h/1000;
3209
+ }
3210
+
3211
+ /**
3212
+ * filter the text, this is applied to all text just before being inserted into the pdf document
3213
+ * it escapes the various things that need to be escaped, and so on
3214
+ *
3215
+ * @access private
3216
+ */
3217
+ function filterText($text, $bom = true, $convert_encoding = true) {
3218
+ if (!$this->numFonts) {
3219
+ $this->selectFont($this->defaultFont);
3220
+ }
3221
+
3222
+ if ($convert_encoding) {
3223
+ $cf = $this->currentFont;
3224
+ if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
3225
+ //$text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
3226
+ $text = $this->utf8toUtf16BE($text, $bom);
3227
+ } else {
3228
+ //$text = html_entity_decode($text, ENT_QUOTES);
3229
+ $text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
3230
+ }
3231
+ }
3232
+
3233
+ // the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
3234
+ return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
3235
+ }
3236
+
3237
+ /**
3238
+ * return array containing codepoints (UTF-8 character values) for the
3239
+ * string passed in.
3240
+ *
3241
+ * based on the excellent TCPDF code by Nicola Asuni and the
3242
+ * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
3243
+ *
3244
+ * @access private
3245
+ * @author Orion Richardson
3246
+ * @since January 5, 2008
3247
+ * @param string $text UTF-8 string to process
3248
+ * @return array UTF-8 codepoints array for the string
3249
+ */
3250
+ function utf8toCodePointsArray(&$text) {
3251
+ $length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
3252
+ $unicode = array(); // array containing unicode values
3253
+ $bytes = array(); // array containing single character byte sequences
3254
+ $numbytes = 1; // number of octetc needed to represent the UTF-8 character
3255
+
3256
+ for ($i = 0; $i < $length; $i++) {
3257
+ $c = ord($text[$i]); // get one string character at time
3258
+ if (count($bytes) === 0) { // get starting octect
3259
+ if ($c <= 0x7F) {
3260
+ $unicode[] = $c; // use the character "as is" because is ASCII
3261
+ $numbytes = 1;
3262
+ } elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
3263
+ $bytes[] = ($c - 0xC0) << 0x06;
3264
+ $numbytes = 2;
3265
+ } elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
3266
+ $bytes[] = ($c - 0xE0) << 0x0C;
3267
+ $numbytes = 3;
3268
+ } elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
3269
+ $bytes[] = ($c - 0xF0) << 0x12;
3270
+ $numbytes = 4;
3271
+ } else {
3272
+ // use replacement character for other invalid sequences
3273
+ $unicode[] = 0xFFFD;
3274
+ $bytes = array();
3275
+ $numbytes = 1;
3276
+ }
3277
+ } elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
3278
+ $bytes[] = $c - 0x80;
3279
+ if (count($bytes) === $numbytes) {
3280
+ // compose UTF-8 bytes to a single unicode value
3281
+ $c = $bytes[0];
3282
+ for ($j = 1; $j < $numbytes; $j++) {
3283
+ $c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
3284
+ }
3285
+ if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) {
3286
+ // The definition of UTF-8 prohibits encoding character numbers between
3287
+ // U+D800 and U+DFFF, which are reserved for use with the UTF-16
3288
+ // encoding form (as surrogate pairs) and do not directly represent
3289
+ // characters.
3290
+ $unicode[] = 0xFFFD; // use replacement character
3291
+ } else {
3292
+ $unicode[] = $c; // add char to array
3293
+ }
3294
+ // reset data for next char
3295
+ $bytes = array();
3296
+ $numbytes = 1;
3297
+ }
3298
+ } else {
3299
+ // use replacement character for other invalid sequences
3300
+ $unicode[] = 0xFFFD;
3301
+ $bytes = array();
3302
+ $numbytes = 1;
3303
+ }
3304
+ }
3305
+ return $unicode;
3306
+ }
3307
+
3308
+ /**
3309
+ * convert UTF-8 to UTF-16 with an additional byte order marker
3310
+ * at the front if required.
3311
+ *
3312
+ * based on the excellent TCPDF code by Nicola Asuni and the
3313
+ * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
3314
+ *
3315
+ * @access private
3316
+ * @author Orion Richardson
3317
+ * @since January 5, 2008
3318
+ * @param string $text UTF-8 string to process
3319
+ * @param boolean $bom whether to add the byte order marker
3320
+ * @return string UTF-16 result string
3321
+ */
3322
+ function utf8toUtf16BE(&$text, $bom = true) {
3323
+ $cf = $this->currentFont;
3324
+ if (!$this->fonts[$cf]['isUnicode']) return $text;
3325
+ $out = $bom ? "\xFE\xFF" : '';
3326
+
3327
+ $unicode = $this->utf8toCodePointsArray($text);
3328
+ foreach ($unicode as $c) {
3329
+ if ($c === 0xFFFD) {
3330
+ $out .= "\xFF\xFD"; // replacement character
3331
+ } elseif ($c < 0x10000) {
3332
+ $out .= chr($c >> 0x08) . chr($c & 0xFF);
3333
+ } else {
3334
+ $c -= 0x10000;
3335
+ $w1 = 0xD800 | ($c >> 0x10);
3336
+ $w2 = 0xDC00 | ($c & 0x3FF);
3337
+ $out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
3338
+ }
3339
+ }
3340
+ return $out;
3341
+ }
3342
+
3343
+ /**
3344
+ * given a start position and information about how text is to be laid out, calculate where
3345
+ * on the page the text will end
3346
+ */
3347
+ private function getTextPosition($x, $y, $angle, $size, $wa, $text) {
3348
+ // given this information return an array containing x and y for the end position as elements 0 and 1
3349
+ $w = $this->getTextWidth($size, $text);
3350
+
3351
+ // need to adjust for the number of spaces in this text
3352
+ $words = explode(' ', $text);
3353
+ $nspaces = count($words) -1;
3354
+ $w+= $wa*$nspaces;
3355
+ $a = deg2rad((float)$angle);
3356
+
3357
+ return array(cos($a) *$w+$x, -sin($a) *$w+$y);
3358
+ }
3359
+
3360
+ /**
3361
+ * Callback method used by smallCaps
3362
+ *
3363
+ * @param array $matches
3364
+ * @return string
3365
+ */
3366
+ function toUpper($matches) {
3367
+ return mb_strtoupper($matches[0]);
3368
+ }
3369
+
3370
+ function concatMatches($matches) {
3371
+ $str = "";
3372
+ foreach ($matches as $match){
3373
+ $str .= $match[0];
3374
+ }
3375
+ return $str;
3376
+ }
3377
+
3378
+ /**
3379
+ * add text to the document, at a specified location, size and angle on the page
3380
+ */
3381
+ function registerText($font, $text) {
3382
+ if ( !$this->isUnicode || in_array(mb_strtolower(basename($font)), self::$coreFonts) ) {
3383
+ return;
3384
+ }
3385
+
3386
+ if ( !isset($this->stringSubsets[$font]) ) {
3387
+ $this->stringSubsets[$font] = array();
3388
+ }
3389
+
3390
+ $this->stringSubsets[$font] = array_unique(array_merge($this->stringSubsets[$font], $this->utf8toCodePointsArray($text)));
3391
+ }
3392
+
3393
+ /**
3394
+ * add text to the document, at a specified location, size and angle on the page
3395
+ */
3396
+ function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false) {
3397
+ if (!$this->numFonts) {
3398
+ $this->selectFont($this->defaultFont);
3399
+ }
3400
+
3401
+ $text = str_replace(array("\r", "\n"), "", $text);
3402
+
3403
+ if ( $smallCaps ) {
3404
+ preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
3405
+ $lower = $this->concatMatches($matches);
3406
+ d($lower);
3407
+
3408
+ preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
3409
+ $other = $this->concatMatches($matches);
3410
+ d($other);
3411
+
3412
+ //$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
3413
+ }
3414
+
3415
+ // if there are any open callbacks, then they should be called, to show the start of the line
3416
+ if ($this->nCallback > 0) {
3417
+ for ($i = $this->nCallback; $i > 0; $i--) {
3418
+ // call each function
3419
+ $info = array(
3420
+ 'x' => $x,
3421
+ 'y' => $y,
3422
+ 'angle' => $angle,
3423
+ 'status' => 'sol',
3424
+ 'p' => $this->callback[$i]['p'],
3425
+ 'nCallback' => $this->callback[$i]['nCallback'],
3426
+ 'height' => $this->callback[$i]['height'],
3427
+ 'descender' => $this->callback[$i]['descender']
3428
+ );
3429
+
3430
+ $func = $this->callback[$i]['f'];
3431
+ $this->$func($info);
3432
+ }
3433
+ }
3434
+
3435
+ if ($angle == 0) {
3436
+ $this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y));
3437
+ } else {
3438
+ $a = deg2rad((float)$angle);
3439
+ $this->addContent(sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y));
3440
+ }
3441
+
3442
+ if ($wordSpaceAdjust != 0 || $wordSpaceAdjust != $this->wordSpaceAdjust) {
3443
+ $this->wordSpaceAdjust = $wordSpaceAdjust;
3444
+ $this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
3445
+ }
3446
+
3447
+ if ($charSpaceAdjust != 0 || $charSpaceAdjust != $this->charSpaceAdjust) {
3448
+ $this->charSpaceAdjust = $charSpaceAdjust;
3449
+ $this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust));
3450
+ }
3451
+
3452
+ $len = mb_strlen($text);
3453
+ $start = 0;
3454
+
3455
+ if ($start < $len) {
3456
+ $part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start);
3457
+ $place_text = $this->filterText($part, false);
3458
+ // modify unicode text so that extra word spacing is manually implemented (bug #)
3459
+ $cf = $this->currentFont;
3460
+ if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
3461
+ $space_scale = 1000 / $size;
3462
+ //$place_text = str_replace(' ', ') ( ) '.($this->getTextWidth($size, chr(32), $wordSpaceAdjust)*-75).' (', $place_text);
3463
+ $place_text = str_replace(' ', ' ) '.(-round($space_scale*$wordSpaceAdjust)).' (', $place_text);
3464
+ }
3465
+ $this->addContent(" /F$this->currentFontNum ".sprintf('%.1F Tf ', $size));
3466
+ $this->addContent(" [($place_text)] TJ");
3467
+ }
3468
+
3469
+ $this->addContent(' ET');
3470
+
3471
+ // if there are any open callbacks, then they should be called, to show the end of the line
3472
+ if ($this->nCallback > 0) {
3473
+ for ($i = $this->nCallback; $i > 0; $i--) {
3474
+ // call each function
3475
+ $tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
3476
+ $info = array(
3477
+ 'x' => $tmp[0],
3478
+ 'y' => $tmp[1],
3479
+ 'angle' => $angle,
3480
+ 'status' => 'eol',
3481
+ 'p' => $this->callback[$i]['p'],
3482
+ 'nCallback' => $this->callback[$i]['nCallback'],
3483
+ 'height' => $this->callback[$i]['height'],
3484
+ 'descender' => $this->callback[$i]['descender']
3485
+ );
3486
+ $func = $this->callback[$i]['f'];
3487
+ $this->$func($info);
3488
+ }
3489
+ }
3490
+ }
3491
+
3492
+ /**
3493
+ * calculate how wide a given text string will be on a page, at a given size.
3494
+ * this can be called externally, but is alse used by the other class functions
3495
+ */
3496
+ function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0) {
3497
+ static $ord_cache = array();
3498
+
3499
+ // this function should not change any of the settings, though it will need to
3500
+ // track any directives which change during calculation, so copy them at the start
3501
+ // and put them back at the end.
3502
+ $store_currentTextState = $this->currentTextState;
3503
+
3504
+ if (!$this->numFonts) {
3505
+ $this->selectFont($this->defaultFont);
3506
+ }
3507
+
3508
+ $text = str_replace(array("\r", "\n"), "", $text);
3509
+
3510
+ // converts a number or a float to a string so it can get the width
3511
+ $text = "$text";
3512
+
3513
+ // hmm, this is where it all starts to get tricky - use the font information to
3514
+ // calculate the width of each character, add them up and convert to user units
3515
+ $w = 0;
3516
+ $cf = $this->currentFont;
3517
+ $current_font = $this->fonts[$cf];
3518
+ $space_scale = 1000 / $size;
3519
+ $n_spaces = 0;
3520
+
3521
+ if ( $current_font['isUnicode']) {
3522
+ // for Unicode, use the code points array to calculate width rather
3523
+ // than just the string itself
3524
+ $unicode = $this->utf8toCodePointsArray($text);
3525
+
3526
+ foreach ($unicode as $char) {
3527
+ // check if we have to replace character
3528
+ if ( isset($current_font['differences'][$char])) {
3529
+ $char = $current_font['differences'][$char];
3530
+ }
3531
+
3532
+ if ( isset($current_font['C'][$char]) ) {
3533
+ $char_width = $current_font['C'][$char];
3534
+
3535
+ // add the character width
3536
+ $w += $char_width;
3537
+
3538
+ // add additional padding for space
3539
+ if ( isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space' ) { // Space
3540
+ $w += $word_spacing * $space_scale;
3541
+ $n_spaces++;
3542
+ }
3543
+ }
3544
+ }
3545
+
3546
+ // add additionnal char spacing
3547
+ if ( $char_spacing != 0 ) {
3548
+ $w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
3549
+ }
3550
+
3551
+ } else {
3552
+ // If CPDF is in Unicode mode but the current font does not support Unicode we need to convert the character set to Windows-1252
3553
+ if ( $this->isUnicode ) {
3554
+ $text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
3555
+ }
3556
+
3557
+ $len = mb_strlen($text, 'Windows-1252');
3558
+
3559
+ for ($i = 0; $i < $len; $i++) {
3560
+ $c = $text[$i];
3561
+ $char = isset($ord_cache[$c]) ? $ord_cache[$c] : ($ord_cache[$c] = ord($c));
3562
+
3563
+ // check if we have to replace character
3564
+ if ( isset($current_font['differences'][$char])) {
3565
+ $char = $current_font['differences'][$char];
3566
+ }
3567
+
3568
+ if ( isset($current_font['C'][$char]) ) {
3569
+ $char_width = $current_font['C'][$char];
3570
+
3571
+ // add the character width
3572
+ $w += $char_width;
3573
+
3574
+ // add additional padding for space
3575
+ if ( isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space' ) { // Space
3576
+ $w += $word_spacing * $space_scale;
3577
+ $n_spaces++;
3578
+ }
3579
+ }
3580
+ }
3581
+
3582
+ // add additionnal char spacing
3583
+ if ( $char_spacing != 0 ) {
3584
+ $w += $char_spacing * $space_scale * ($len + $n_spaces);
3585
+ }
3586
+ }
3587
+
3588
+ $this->currentTextState = $store_currentTextState;
3589
+ $this->setCurrentFont();
3590
+
3591
+ return $w*$size/1000;
3592
+ }
3593
+
3594
+ /**
3595
+ * this will be called at a new page to return the state to what it was on the
3596
+ * end of the previous page, before the stack was closed down
3597
+ * This is to get around not being able to have open 'q' across pages
3598
+ *
3599
+ */
3600
+ function saveState($pageEnd = 0) {
3601
+ if ($pageEnd) {
3602
+ // this will be called at a new page to return the state to what it was on the
3603
+ // end of the previous page, before the stack was closed down
3604
+ // This is to get around not being able to have open 'q' across pages
3605
+ $opt = $this->stateStack[$pageEnd];
3606
+ // ok to use this as stack starts numbering at 1
3607
+ $this->setColor($opt['col'], true);
3608
+ $this->setStrokeColor($opt['str'], true);
3609
+ $this->addContent("\n".$opt['lin']);
3610
+ // $this->currentLineStyle = $opt['lin'];
3611
+ } else {
3612
+ $this->nStateStack++;
3613
+ $this->stateStack[$this->nStateStack] = array(
3614
+ 'col' => $this->currentColor,
3615
+ 'str' => $this->currentStrokeColor,
3616
+ 'lin' => $this->currentLineStyle
3617
+ );
3618
+ }
3619
+
3620
+ $this->save();
3621
+ }
3622
+
3623
+ /**
3624
+ * restore a previously saved state
3625
+ */
3626
+ function restoreState($pageEnd = 0) {
3627
+ if (!$pageEnd) {
3628
+ $n = $this->nStateStack;
3629
+ $this->currentColor = $this->stateStack[$n]['col'];
3630
+ $this->currentStrokeColor = $this->stateStack[$n]['str'];
3631
+ $this->addContent("\n".$this->stateStack[$n]['lin']);
3632
+ $this->currentLineStyle = $this->stateStack[$n]['lin'];
3633
+ $this->stateStack[$n] = null;
3634
+ unset($this->stateStack[$n]);
3635
+ $this->nStateStack--;
3636
+ }
3637
+
3638
+ $this->restore();
3639
+ }
3640
+
3641
+ /**
3642
+ * make a loose object, the output will go into this object, until it is closed, then will revert to
3643
+ * the current one.
3644
+ * this object will not appear until it is included within a page.
3645
+ * the function will return the object number
3646
+ */
3647
+ function openObject() {
3648
+ $this->nStack++;
3649
+ $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
3650
+ // add a new object of the content type, to hold the data flow
3651
+ $this->numObj++;
3652
+ $this->o_contents($this->numObj, 'new');
3653
+ $this->currentContents = $this->numObj;
3654
+ $this->looseObjects[$this->numObj] = 1;
3655
+
3656
+ return $this->numObj;
3657
+ }
3658
+
3659
+ /**
3660
+ * open an existing object for editing
3661
+ */
3662
+ function reopenObject($id) {
3663
+ $this->nStack++;
3664
+ $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
3665
+ $this->currentContents = $id;
3666
+
3667
+ // also if this object is the primary contents for a page, then set the current page to its parent
3668
+ if (isset($this->objects[$id]['onPage'])) {
3669
+ $this->currentPage = $this->objects[$id]['onPage'];
3670
+ }
3671
+ }
3672
+
3673
+ /**
3674
+ * close an object
3675
+ */
3676
+ function closeObject() {
3677
+ // close the object, as long as there was one open in the first place, which will be indicated by
3678
+ // an objectId on the stack.
3679
+ if ($this->nStack > 0) {
3680
+ $this->currentContents = $this->stack[$this->nStack]['c'];
3681
+ $this->currentPage = $this->stack[$this->nStack]['p'];
3682
+ $this->nStack--;
3683
+ // easier to probably not worry about removing the old entries, they will be overwritten
3684
+ // if there are new ones.
3685
+ }
3686
+ }
3687
+
3688
+ /**
3689
+ * stop an object from appearing on pages from this point on
3690
+ */
3691
+ function stopObject($id) {
3692
+ // if an object has been appearing on pages up to now, then stop it, this page will
3693
+ // be the last one that could contian it.
3694
+ if (isset($this->addLooseObjects[$id])) {
3695
+ $this->addLooseObjects[$id] = '';
3696
+ }
3697
+ }
3698
+
3699
+ /**
3700
+ * after an object has been created, it wil only show if it has been added, using this function.
3701
+ */
3702
+ function addObject($id, $options = 'add') {
3703
+ // add the specified object to the page
3704
+ if (isset($this->looseObjects[$id]) && $this->currentContents != $id) {
3705
+ // then it is a valid object, and it is not being added to itself
3706
+ switch ($options) {
3707
+ case 'all':
3708
+ // then this object is to be added to this page (done in the next block) and
3709
+ // all future new pages.
3710
+ $this->addLooseObjects[$id] = 'all';
3711
+
3712
+ case 'add':
3713
+ if (isset($this->objects[$this->currentContents]['onPage'])) {
3714
+ // then the destination contents is the primary for the page
3715
+ // (though this object is actually added to that page)
3716
+ $this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id);
3717
+ }
3718
+ break;
3719
+
3720
+ case 'even':
3721
+ $this->addLooseObjects[$id] = 'even';
3722
+ $pageObjectId = $this->objects[$this->currentContents]['onPage'];
3723
+ if ($this->objects[$pageObjectId]['info']['pageNum']%2 == 0) {
3724
+ $this->addObject($id);
3725
+ // hacky huh :)
3726
+ }
3727
+ break;
3728
+
3729
+ case 'odd':
3730
+ $this->addLooseObjects[$id] = 'odd';
3731
+ $pageObjectId = $this->objects[$this->currentContents]['onPage'];
3732
+ if ($this->objects[$pageObjectId]['info']['pageNum']%2 == 1) {
3733
+ $this->addObject($id);
3734
+ // hacky huh :)
3735
+ }
3736
+ break;
3737
+
3738
+ case 'next':
3739
+ $this->addLooseObjects[$id] = 'all';
3740
+ break;
3741
+
3742
+ case 'nexteven':
3743
+ $this->addLooseObjects[$id] = 'even';
3744
+ break;
3745
+
3746
+ case 'nextodd':
3747
+ $this->addLooseObjects[$id] = 'odd';
3748
+ break;
3749
+ }
3750
+ }
3751
+ }
3752
+
3753
+ /**
3754
+ * return a storable representation of a specific object
3755
+ */
3756
+ function serializeObject($id) {
3757
+ if ( array_key_exists($id, $this->objects)) {
3758
+ return serialize($this->objects[$id]);
3759
+ }
3760
+ }
3761
+
3762
+ /**
3763
+ * restore an object from its stored representation. returns its new object id.
3764
+ */
3765
+ function restoreSerializedObject($obj) {
3766
+ $obj_id = $this->openObject();
3767
+ $this->objects[$obj_id] = unserialize($obj);
3768
+ $this->closeObject();
3769
+ return $obj_id;
3770
+ }
3771
+
3772
+ /**
3773
+ * add content to the documents info object
3774
+ */
3775
+ function addInfo($label, $value = 0) {
3776
+ // this will only work if the label is one of the valid ones.
3777
+ // modify this so that arrays can be passed as well.
3778
+ // if $label is an array then assume that it is key => value pairs
3779
+ // else assume that they are both scalar, anything else will probably error
3780
+ if (is_array($label)) {
3781
+ foreach ($label as $l => $v) {
3782
+ $this->o_info($this->infoObject, $l, $v);
3783
+ }
3784
+ } else {
3785
+ $this->o_info($this->infoObject, $label, $value);
3786
+ }
3787
+ }
3788
+
3789
+ /**
3790
+ * set the viewer preferences of the document, it is up to the browser to obey these.
3791
+ */
3792
+ function setPreferences($label, $value = 0) {
3793
+ // this will only work if the label is one of the valid ones.
3794
+ if (is_array($label)) {
3795
+ foreach ($label as $l => $v) {
3796
+ $this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
3797
+ }
3798
+ } else {
3799
+ $this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
3800
+ }
3801
+ }
3802
+
3803
+ /**
3804
+ * extract an integer from a position in a byte stream
3805
+ */
3806
+ private function getBytes(&$data, $pos, $num) {
3807
+ // return the integer represented by $num bytes from $pos within $data
3808
+ $ret = 0;
3809
+ for ($i = 0; $i < $num; $i++) {
3810
+ $ret *= 256;
3811
+ $ret += ord($data[$pos+$i]);
3812
+ }
3813
+
3814
+ return $ret;
3815
+ }
3816
+
3817
+ /**
3818
+ * Check if image already added to pdf image directory.
3819
+ * If yes, need not to create again (pass empty data)
3820
+ */
3821
+ function image_iscached($imgname) {
3822
+ return isset($this->imagelist[$imgname]);
3823
+ }
3824
+
3825
+ /**
3826
+ * add a PNG image into the document, from a GD object
3827
+ * this should work with remote files
3828
+ *
3829
+ * @param string $file The PNG file
3830
+ * @param float $x X position
3831
+ * @param float $y Y position
3832
+ * @param float $w Width
3833
+ * @param float $h Height
3834
+ * @param resource $img A GD resource
3835
+ * @param bool $is_mask true if the image is a mask
3836
+ * @param bool $mask true if the image is masked
3837
+ */
3838
+ function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null) {
3839
+ if (!function_exists("imagepng")) {
3840
+ throw new Exception("The PHP GD extension is required, but is not installed.");
3841
+ }
3842
+
3843
+ //if already cached, need not to read again
3844
+ if ( isset($this->imagelist[$file]) ) {
3845
+ $data = null;
3846
+ }
3847
+ else {
3848
+ // Example for transparency handling on new image. Retain for current image
3849
+ // $tIndex = imagecolortransparent($img);
3850
+ // if ($tIndex > 0) {
3851
+ // $tColor = imagecolorsforindex($img, $tIndex);
3852
+ // $new_tIndex = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']);
3853
+ // imagefill($new_img, 0, 0, $new_tIndex);
3854
+ // imagecolortransparent($new_img, $new_tIndex);
3855
+ // }
3856
+ // blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn
3857
+ //imagealphablending($img, true);
3858
+
3859
+ //default, but explicitely set to ensure pdf compatibility
3860
+ imagesavealpha($img, false/*!$is_mask && !$mask*/);
3861
+
3862
+ $error = 0;
3863
+ //DEBUG_IMG_TEMP
3864
+ //debugpng
3865
+ if (DEBUGPNG) print '[addImagePng '.$file.']';
3866
+
3867
+ ob_start();
3868
+ @imagepng($img);
3869
+ $data = ob_get_clean();
3870
+
3871
+ if ($data == '') {
3872
+ $error = 1;
3873
+ $errormsg = 'trouble writing file from GD';
3874
+ //DEBUG_IMG_TEMP
3875
+ //debugpng
3876
+ if (DEBUGPNG) print 'trouble writing file from GD';
3877
+ }
3878
+
3879
+ if ($error) {
3880
+ $this->addMessage('PNG error - ('.$file.') '.$errormsg);
3881
+ return;
3882
+ }
3883
+ } //End isset($this->imagelist[$file]) (png Duplicate removal)
3884
+
3885
+ $this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
3886
+ }
3887
+
3888
+ protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte) {
3889
+ // generate images
3890
+ $img = imagecreatefrompng($file);
3891
+
3892
+ if ($img === false) {
3893
+ return;
3894
+ }
3895
+
3896
+ // FIXME The pixel transformation doesn't work well with 8bit PNGs
3897
+ $eight_bit = ($byte & 4) !== 4;
3898
+
3899
+ $wpx = imagesx($img);
3900
+ $hpx = imagesy($img);
3901
+
3902
+ imagesavealpha($img, false);
3903
+
3904
+ // create temp alpha file
3905
+ $tempfile_alpha = tempnam($this->tmp, "cpdf_img_");
3906
+ @unlink($tempfile_alpha);
3907
+ $tempfile_alpha = "$tempfile_alpha.png";
3908
+
3909
+ // create temp plain file
3910
+ $tempfile_plain = tempnam($this->tmp, "cpdf_img_");
3911
+ @unlink($tempfile_plain);
3912
+ $tempfile_plain = "$tempfile_plain.png";
3913
+
3914
+ $imgalpha = imagecreate($wpx, $hpx);
3915
+ imagesavealpha($imgalpha, false);
3916
+
3917
+ // generate gray scale palette (0 -> 255)
3918
+ for ($c = 0; $c < 256; ++$c) {
3919
+ imagecolorallocate($imgalpha, $c, $c, $c);
3920
+ }
3921
+
3922
+ // Use PECL gmagick + Graphics Magic to process transparent PNG images
3923
+ if (extension_loaded("gmagick")) {
3924
+ $gmagick = new Gmagick($file);
3925
+ $gmagick->setimageformat('png');
3926
+
3927
+ // Get opacity channel (negative of alpha channel)
3928
+ $alpha_channel_neg = clone $gmagick;
3929
+ $alpha_channel_neg->separateimagechannel(Gmagick::CHANNEL_OPACITY);
3930
+
3931
+ // Negate opacity channel
3932
+ $alpha_channel = new Gmagick();
3933
+ $alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png");
3934
+ $alpha_channel->compositeimage($alpha_channel_neg, Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
3935
+ $alpha_channel->separateimagechannel(Gmagick::CHANNEL_RED);
3936
+ $alpha_channel->writeimage($tempfile_alpha);
3937
+
3938
+ // Cast to 8bit+palette
3939
+ $imgalpha_ = imagecreatefrompng($tempfile_alpha);
3940
+ imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
3941
+ imagedestroy($imgalpha_);
3942
+ imagepng($imgalpha, $tempfile_alpha);
3943
+
3944
+ // Make opaque image
3945
+ $color_channels = new Gmagick();
3946
+ $color_channels->newimage($wpx, $hpx, "#FFFFFF", "png");
3947
+ $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYRED, 0, 0);
3948
+ $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYGREEN, 0, 0);
3949
+ $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYBLUE, 0, 0);
3950
+ $color_channels->writeimage($tempfile_plain);
3951
+
3952
+ $imgplain = imagecreatefrompng($tempfile_plain);
3953
+ }
3954
+
3955
+ // Use PECL imagick + ImageMagic to process transparent PNG images
3956
+ elseif (extension_loaded("imagick")) {
3957
+ // Native cloning was added to pecl-imagick in svn commit 263814
3958
+ // the first version containing it was 3.0.1RC1
3959
+ static $imagickClonable = null;
3960
+ if($imagickClonable === null) {
3961
+ $imagickClonable = version_compare(phpversion('imagick'), '3.0.1rc1') > 0;
3962
+ }
3963
+
3964
+ $imagick = new Imagick($file);
3965
+ $imagick->setFormat('png');
3966
+
3967
+ // Get opacity channel (negative of alpha channel)
3968
+ $alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
3969
+ $alpha_channel->separateImageChannel(Imagick::CHANNEL_ALPHA);
3970
+ $alpha_channel->negateImage(true);
3971
+ $alpha_channel->writeImage($tempfile_alpha);
3972
+
3973
+ // Cast to 8bit+palette
3974
+ $imgalpha_ = imagecreatefrompng($tempfile_alpha);
3975
+ imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
3976
+ imagedestroy($imgalpha_);
3977
+ imagepng($imgalpha, $tempfile_alpha);
3978
+
3979
+ // Make opaque image
3980
+ $color_channels = new Imagick();
3981
+ $color_channels->newImage($wpx, $hpx, "#FFFFFF", "png");
3982
+ $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYRED, 0, 0);
3983
+ $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYGREEN, 0, 0);
3984
+ $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYBLUE, 0, 0);
3985
+ $color_channels->writeImage($tempfile_plain);
3986
+
3987
+ $imgplain = imagecreatefrompng($tempfile_plain);
3988
+ }
3989
+ else {
3990
+ // allocated colors cache
3991
+ $allocated_colors = array();
3992
+
3993
+ // extract alpha channel
3994
+ for ($xpx = 0; $xpx < $wpx; ++$xpx) {
3995
+ for ($ypx = 0; $ypx < $hpx; ++$ypx) {
3996
+ $color = imagecolorat($img, $xpx, $ypx);
3997
+ $col = imagecolorsforindex($img, $color);
3998
+ $alpha = $col['alpha'];
3999
+
4000
+ if ($eight_bit) {
4001
+ // with gamma correction
4002
+ $gammacorr = 2.2;
4003
+ $pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255;
4004
+ }
4005
+
4006
+ else {
4007
+ // without gamma correction
4008
+ $pixel = (127 - $alpha) * 2;
4009
+
4010
+ $key = $col['red'].$col['green'].$col['blue'];
4011
+
4012
+ if (!isset($allocated_colors[$key])) {
4013
+ $pixel_img = imagecolorallocate($img, $col['red'], $col['green'], $col['blue']);
4014
+ $allocated_colors[$key] = $pixel_img;
4015
+ }
4016
+ else {
4017
+ $pixel_img = $allocated_colors[$key];
4018
+ }
4019
+
4020
+ imagesetpixel($img, $xpx, $ypx, $pixel_img);
4021
+ }
4022
+
4023
+ imagesetpixel($imgalpha, $xpx, $ypx, $pixel);
4024
+ }
4025
+ }
4026
+
4027
+ // extract image without alpha channel
4028
+ $imgplain = imagecreatetruecolor($wpx, $hpx);
4029
+ imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
4030
+ imagedestroy($img);
4031
+
4032
+ imagepng($imgalpha, $tempfile_alpha);
4033
+ imagepng($imgplain, $tempfile_plain);
4034
+ }
4035
+
4036
+ // embed mask image
4037
+ $this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true);
4038
+ imagedestroy($imgalpha);
4039
+
4040
+ // embed image, masked with previously embedded mask
4041
+ $this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, true);
4042
+ imagedestroy($imgplain);
4043
+
4044
+ // remove temp files
4045
+ unlink($tempfile_alpha);
4046
+ unlink($tempfile_plain);
4047
+ }
4048
+
4049
+ /**
4050
+ * add a PNG image into the document, from a file
4051
+ * this should work with remote files
4052
+ */
4053
+ function addPngFromFile($file, $x, $y, $w = 0, $h = 0) {
4054
+ if (!function_exists("imagecreatefrompng")) {
4055
+ throw new Exception("The PHP GD extension is required, but is not installed.");
4056
+ }
4057
+
4058
+ //if already cached, need not to read again
4059
+ if ( isset($this->imagelist[$file]) ) {
4060
+ $img = null;
4061
+ }
4062
+
4063
+ else {
4064
+ $info = file_get_contents ($file, false, null, 24, 5);
4065
+ $meta = unpack("CbitDepth/CcolorType/CcompressionMethod/CfilterMethod/CinterlaceMethod", $info);
4066
+ $bit_depth = $meta["bitDepth"];
4067
+ $color_type = $meta["colorType"];
4068
+
4069
+ // http://www.w3.org/TR/PNG/#11IHDR
4070
+ // 3 => indexed
4071
+ // 4 => greyscale with alpha
4072
+ // 6 => fullcolor with alpha
4073
+ $is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4);
4074
+
4075
+ if ($is_alpha) { // exclude grayscale alpha
4076
+ return $this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
4077
+ }
4078
+
4079
+ //png files typically contain an alpha channel.
4080
+ //pdf file format or class.pdf does not support alpha blending.
4081
+ //on alpha blended images, more transparent areas have a color near black.
4082
+ //This appears in the result on not storing the alpha channel.
4083
+ //Correct would be the box background image or its parent when transparent.
4084
+ //But this would make the image dependent on the background.
4085
+ //Therefore create an image with white background and copy in
4086
+ //A more natural background than black is white.
4087
+ //Therefore create an empty image with white background and merge the
4088
+ //image in with alpha blending.
4089
+ $imgtmp = @imagecreatefrompng($file);
4090
+ if (!$imgtmp) {
4091
+ return;
4092
+ }
4093
+ $sx = imagesx($imgtmp);
4094
+ $sy = imagesy($imgtmp);
4095
+ $img = imagecreatetruecolor($sx,$sy);
4096
+ imagealphablending($img, true);
4097
+
4098
+ // @todo is it still needed ??
4099
+ $ti = imagecolortransparent($imgtmp);
4100
+ if ($ti >= 0) {
4101
+ $tc = imagecolorsforindex($imgtmp,$ti);
4102
+ $ti = imagecolorallocate($img,$tc['red'],$tc['green'],$tc['blue']);
4103
+ imagefill($img,0,0,$ti);
4104
+ imagecolortransparent($img, $ti);
4105
+ } else {
4106
+ imagefill($img,1,1,imagecolorallocate($img,255,255,255));
4107
+ }
4108
+
4109
+ imagecopy($img,$imgtmp,0,0,0,0,$sx,$sy);
4110
+ imagedestroy($imgtmp);
4111
+ }
4112
+ $this->addImagePng($file, $x, $y, $w, $h, $img);
4113
+
4114
+ if ( $img ) {
4115
+ imagedestroy($img);
4116
+ }
4117
+ }
4118
+
4119
+ /**
4120
+ * add a PNG image into the document, from a memory buffer of the file
4121
+ */
4122
+ function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null) {
4123
+ if ( isset($this->imagelist[$file]) ) {
4124
+ $data = null;
4125
+ $info['width'] = $this->imagelist[$file]['w'];
4126
+ $info['height'] = $this->imagelist[$file]['h'];
4127
+ $label = $this->imagelist[$file]['label'];
4128
+ }
4129
+
4130
+ else {
4131
+ if ($data == null) {
4132
+ $this->addMessage('addPngFromBuf error - data not present!');
4133
+ return;
4134
+ }
4135
+
4136
+ $error = 0;
4137
+
4138
+ if (!$error) {
4139
+ $header = chr(137) .chr(80) .chr(78) .chr(71) .chr(13) .chr(10) .chr(26) .chr(10);
4140
+
4141
+ if (mb_substr($data, 0, 8, '8bit') != $header) {
4142
+ $error = 1;
4143
+
4144
+ if (DEBUGPNG) print '[addPngFromFile this file does not have a valid header '.$file.']';
4145
+
4146
+ $errormsg = 'this file does not have a valid header';
4147
+ }
4148
+ }
4149
+
4150
+ if (!$error) {
4151
+ // set pointer
4152
+ $p = 8;
4153
+ $len = mb_strlen($data, '8bit');
4154
+
4155
+ // cycle through the file, identifying chunks
4156
+ $haveHeader = 0;
4157
+ $info = array();
4158
+ $idata = '';
4159
+ $pdata = '';
4160
+
4161
+ while ($p < $len) {
4162
+ $chunkLen = $this->getBytes($data, $p, 4);
4163
+ $chunkType = mb_substr($data, $p+4, 4, '8bit');
4164
+
4165
+ switch ($chunkType) {
4166
+ case 'IHDR':
4167
+ // this is where all the file information comes from
4168
+ $info['width'] = $this->getBytes($data, $p+8, 4);
4169
+ $info['height'] = $this->getBytes($data, $p+12, 4);
4170
+ $info['bitDepth'] = ord($data[$p+16]);
4171
+ $info['colorType'] = ord($data[$p+17]);
4172
+ $info['compressionMethod'] = ord($data[$p+18]);
4173
+ $info['filterMethod'] = ord($data[$p+19]);
4174
+ $info['interlaceMethod'] = ord($data[$p+20]);
4175
+
4176
+ //print_r($info);
4177
+ $haveHeader = 1;
4178
+ if ($info['compressionMethod'] != 0) {
4179
+ $error = 1;
4180
+
4181
+ //debugpng
4182
+ if (DEBUGPNG) print '[addPngFromFile unsupported compression method '.$file.']';
4183
+
4184
+ $errormsg = 'unsupported compression method';
4185
+ }
4186
+
4187
+ if ($info['filterMethod'] != 0) {
4188
+ $error = 1;
4189
+
4190
+ //debugpng
4191
+ if (DEBUGPNG) print '[addPngFromFile unsupported filter method '.$file.']';
4192
+
4193
+ $errormsg = 'unsupported filter method';
4194
+ }
4195
+ break;
4196
+
4197
+ case 'PLTE':
4198
+ $pdata.= mb_substr($data, $p+8, $chunkLen, '8bit');
4199
+ break;
4200
+
4201
+ case 'IDAT':
4202
+ $idata.= mb_substr($data, $p+8, $chunkLen, '8bit');
4203
+ break;
4204
+
4205
+ case 'tRNS':
4206
+ //this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
4207
+ //print "tRNS found, color type = ".$info['colorType']."\n";
4208
+ $transparency = array();
4209
+
4210
+ switch ($info['colorType']) {
4211
+ // indexed color, rbg
4212
+ case 3:
4213
+ /* corresponding to entries in the plte chunk
4214
+ Alpha for palette index 0: 1 byte
4215
+ Alpha for palette index 1: 1 byte
4216
+ ...etc...
4217
+ */
4218
+ // there will be one entry for each palette entry. up until the last non-opaque entry.
4219
+ // set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
4220
+ $transparency['type'] = 'indexed';
4221
+ $trans = 0;
4222
+
4223
+ for ($i = $chunkLen; $i >= 0; $i--) {
4224
+ if (ord($data[$p+8+$i]) == 0) {
4225
+ $trans = $i;
4226
+ }
4227
+ }
4228
+
4229
+ $transparency['data'] = $trans;
4230
+ break;
4231
+
4232
+ // grayscale
4233
+ case 0:
4234
+ /* corresponding to entries in the plte chunk
4235
+ Gray: 2 bytes, range 0 .. (2^bitdepth)-1
4236
+ */
4237
+ // $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale
4238
+ $transparency['type'] = 'indexed';
4239
+ $transparency['data'] = ord($data[$p+8+1]);
4240
+ break;
4241
+
4242
+ // truecolor
4243
+ case 2:
4244
+ /* corresponding to entries in the plte chunk
4245
+ Red: 2 bytes, range 0 .. (2^bitdepth)-1
4246
+ Green: 2 bytes, range 0 .. (2^bitdepth)-1
4247
+ Blue: 2 bytes, range 0 .. (2^bitdepth)-1
4248
+ */
4249
+ $transparency['r'] = $this->getBytes($data, $p+8, 2);
4250
+ // r from truecolor
4251
+ $transparency['g'] = $this->getBytes($data, $p+10, 2);
4252
+ // g from truecolor
4253
+ $transparency['b'] = $this->getBytes($data, $p+12, 2);
4254
+ // b from truecolor
4255
+
4256
+ $transparency['type'] = 'color-key';
4257
+ break;
4258
+
4259
+ //unsupported transparency type
4260
+ default:
4261
+ if (DEBUGPNG) print '[addPngFromFile unsupported transparency type '.$file.']';
4262
+ break;
4263
+ }
4264
+
4265
+ // KS End new code
4266
+ break;
4267
+
4268
+ default:
4269
+ break;
4270
+ }
4271
+
4272
+ $p += $chunkLen+12;
4273
+ }
4274
+
4275
+ if (!$haveHeader) {
4276
+ $error = 1;
4277
+
4278
+ //debugpng
4279
+ if (DEBUGPNG) print '[addPngFromFile information header is missing '.$file.']';
4280
+
4281
+ $errormsg = 'information header is missing';
4282
+ }
4283
+
4284
+ if (isset($info['interlaceMethod']) && $info['interlaceMethod']) {
4285
+ $error = 1;
4286
+
4287
+ //debugpng
4288
+ if (DEBUGPNG) print '[addPngFromFile no support for interlaced images in pdf '.$file.']';
4289
+
4290
+ $errormsg = 'There appears to be no support for interlaced images in pdf.';
4291
+ }
4292
+ }
4293
+
4294
+ if (!$error && $info['bitDepth'] > 8) {
4295
+ $error = 1;
4296
+
4297
+ //debugpng
4298
+ if (DEBUGPNG) print '[addPngFromFile bit depth of 8 or less is supported '.$file.']';
4299
+
4300
+ $errormsg = 'only bit depth of 8 or less is supported';
4301
+ }
4302
+
4303
+ if (!$error) {
4304
+ switch ($info['colorType']) {
4305
+ case 3:
4306
+ $color = 'DeviceRGB';
4307
+ $ncolor = 1;
4308
+ break;
4309
+
4310
+ case 2:
4311
+ $color = 'DeviceRGB';
4312
+ $ncolor = 3;
4313
+ break;
4314
+
4315
+ case 0:
4316
+ $color = 'DeviceGray';
4317
+ $ncolor = 1;
4318
+ break;
4319
+
4320
+ default:
4321
+ $error = 1;
4322
+
4323
+ //debugpng
4324
+ if (DEBUGPNG) print '[addPngFromFile alpha channel not supported: '.$info['colorType'].' '.$file.']';
4325
+
4326
+ $errormsg = 'transparancey alpha channel not supported, transparency only supported for palette images.';
4327
+ }
4328
+ }
4329
+
4330
+ if ($error) {
4331
+ $this->addMessage('PNG error - ('.$file.') '.$errormsg);
4332
+ return;
4333
+ }
4334
+
4335
+ //print_r($info);
4336
+ // so this image is ok... add it in.
4337
+ $this->numImages++;
4338
+ $im = $this->numImages;
4339
+ $label = "I$im";
4340
+ $this->numObj++;
4341
+
4342
+ // $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
4343
+ $options = array(
4344
+ 'label' => $label,
4345
+ 'data' => $idata,
4346
+ 'bitsPerComponent' => $info['bitDepth'],
4347
+ 'pdata' => $pdata,
4348
+ 'iw' => $info['width'],
4349
+ 'ih' => $info['height'],
4350
+ 'type' => 'png',
4351
+ 'color' => $color,
4352
+ 'ncolor' => $ncolor,
4353
+ 'masked' => $mask,
4354
+ 'isMask' => $is_mask
4355
+ );
4356
+
4357
+ if (isset($transparency)) {
4358
+ $options['transparency'] = $transparency;
4359
+ }
4360
+
4361
+ $this->o_image($this->numObj, 'new', $options);
4362
+ $this->imagelist[$file] = array('label' =>$label, 'w' => $info['width'], 'h' => $info['height']);
4363
+ }
4364
+
4365
+ if ($is_mask) {
4366
+ return;
4367
+ }
4368
+
4369
+ if ($w <= 0 && $h <= 0) {
4370
+ $w = $info['width'];
4371
+ $h = $info['height'];
4372
+ }
4373
+
4374
+ if ($w <= 0) {
4375
+ $w = $h/$info['height']*$info['width'];
4376
+ }
4377
+
4378
+ if ($h <= 0) {
4379
+ $h = $w*$info['height']/$info['width'];
4380
+ }
4381
+
4382
+ $this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ", $w, $h, $x, $y, $label));
4383
+ }
4384
+
4385
+ /**
4386
+ * add a JPEG image into the document, from a file
4387
+ */
4388
+ function addJpegFromFile($img, $x, $y, $w = 0, $h = 0) {
4389
+ // attempt to add a jpeg image straight from a file, using no GD commands
4390
+ // note that this function is unable to operate on a remote file.
4391
+
4392
+ if (!file_exists($img)) {
4393
+ return;
4394
+ }
4395
+
4396
+ if ( $this->image_iscached($img) ) {
4397
+ $data = null;
4398
+ $imageWidth = $this->imagelist[$img]['w'];
4399
+ $imageHeight = $this->imagelist[$img]['h'];
4400
+ $channels = $this->imagelist[$img]['c'];
4401
+ }
4402
+ else {
4403
+ $tmp = getimagesize($img);
4404
+ $imageWidth = $tmp[0];
4405
+ $imageHeight = $tmp[1];
4406
+
4407
+ if ( isset($tmp['channels']) ) {
4408
+ $channels = $tmp['channels'];
4409
+ } else {
4410
+ $channels = 3;
4411
+ }
4412
+
4413
+ $data = file_get_contents($img);
4414
+ }
4415
+
4416
+ if ($w <= 0 && $h <= 0) {
4417
+ $w = $imageWidth;
4418
+ }
4419
+
4420
+ if ($w == 0) {
4421
+ $w = $h/$imageHeight*$imageWidth;
4422
+ }
4423
+
4424
+ if ($h == 0) {
4425
+ $h = $w*$imageHeight/$imageWidth;
4426
+ }
4427
+
4428
+ $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img);
4429
+ }
4430
+
4431
+ /**
4432
+ * common code used by the two JPEG adding functions
4433
+ */
4434
+ private function addJpegImage_common(&$data, $x, $y, $w = 0, $h = 0, $imageWidth, $imageHeight, $channels = 3, $imgname) {
4435
+ if ( $this->image_iscached($imgname) ) {
4436
+ $label = $this->imagelist[$imgname]['label'];
4437
+ //debugpng
4438
+ //if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']';
4439
+
4440
+ } else {
4441
+ if ($data == null) {
4442
+ $this->addMessage('addJpegImage_common error - ('.$imgname.') data not present!');
4443
+ return;
4444
+ }
4445
+
4446
+ // note that this function is not to be called externally
4447
+ // it is just the common code between the GD and the file options
4448
+ $this->numImages++;
4449
+ $im = $this->numImages;
4450
+ $label = "I$im";
4451
+ $this->numObj++;
4452
+
4453
+ $this->o_image($this->numObj, 'new', array(
4454
+ 'label' => $label,
4455
+ 'data' => &$data,
4456
+ 'iw' => $imageWidth,
4457
+ 'ih' => $imageHeight,
4458
+ 'channels' => $channels
4459
+ ));
4460
+
4461
+ $this->imagelist[$imgname] = array('label' =>$label, 'w' => $imageWidth, 'h' => $imageHeight, 'c'=> $channels);
4462
+ }
4463
+
4464
+ $this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
4465
+ }
4466
+
4467
+ /**
4468
+ * specify where the document should open when it first starts
4469
+ */
4470
+ function openHere($style, $a = 0, $b = 0, $c = 0) {
4471
+ // this function will open the document at a specified page, in a specified style
4472
+ // the values for style, and the required paramters are:
4473
+ // 'XYZ' left, top, zoom
4474
+ // 'Fit'
4475
+ // 'FitH' top
4476
+ // 'FitV' left
4477
+ // 'FitR' left,bottom,right
4478
+ // 'FitB'
4479
+ // 'FitBH' top
4480
+ // 'FitBV' left
4481
+ $this->numObj++;
4482
+ $this->o_destination($this->numObj, 'new', array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c));
4483
+ $id = $this->catalogId;
4484
+ $this->o_catalog($id, 'openHere', $this->numObj);
4485
+ }
4486
+
4487
+ /**
4488
+ * Add JavaScript code to the PDF document
4489
+ *
4490
+ * @param string $code
4491
+ * @return void
4492
+ */
4493
+ function addJavascript($code) {
4494
+ $this->javascript .= $code;
4495
+ }
4496
+
4497
+ /**
4498
+ * create a labelled destination within the document
4499
+ */
4500
+ function addDestination($label, $style, $a = 0, $b = 0, $c = 0) {
4501
+ // associates the given label with the destination, it is done this way so that a destination can be specified after
4502
+ // it has been linked to
4503
+ // styles are the same as the 'openHere' function
4504
+ $this->numObj++;
4505
+ $this->o_destination($this->numObj, 'new', array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c));
4506
+ $id = $this->numObj;
4507
+
4508
+ // store the label->idf relationship, note that this means that labels can be used only once
4509
+ $this->destinations["$label"] = $id;
4510
+ }
4511
+
4512
+ /**
4513
+ * define font families, this is used to initialize the font families for the default fonts
4514
+ * and for the user to add new ones for their fonts. The default bahavious can be overridden should
4515
+ * that be desired.
4516
+ */
4517
+ function setFontFamily($family, $options = '') {
4518
+ if (!is_array($options)) {
4519
+ if ($family === 'init') {
4520
+ // set the known family groups
4521
+ // these font families will be used to enable bold and italic markers to be included
4522
+ // within text streams. html forms will be used... <b></b> <i></i>
4523
+ $this->fontFamilies['Helvetica.afm'] =
4524
+ array(
4525
+ 'b' => 'Helvetica-Bold.afm',
4526
+ 'i' => 'Helvetica-Oblique.afm',
4527
+ 'bi' => 'Helvetica-BoldOblique.afm',
4528
+ 'ib' => 'Helvetica-BoldOblique.afm'
4529
+ );
4530
+
4531
+ $this->fontFamilies['Courier.afm'] =
4532
+ array(
4533
+ 'b' => 'Courier-Bold.afm',
4534
+ 'i' => 'Courier-Oblique.afm',
4535
+ 'bi' => 'Courier-BoldOblique.afm',
4536
+ 'ib' => 'Courier-BoldOblique.afm'
4537
+ );
4538
+
4539
+ $this->fontFamilies['Times-Roman.afm'] =
4540
+ array(
4541
+ 'b' => 'Times-Bold.afm',
4542
+ 'i' => 'Times-Italic.afm',
4543
+ 'bi' => 'Times-BoldItalic.afm',
4544
+ 'ib' => 'Times-BoldItalic.afm'
4545
+ );
4546
+ }
4547
+ } else {
4548
+
4549
+ // the user is trying to set a font family
4550
+ // note that this can also be used to set the base ones to something else
4551
+ if (mb_strlen($family)) {
4552
+ $this->fontFamilies[$family] = $options;
4553
+ }
4554
+ }
4555
+ }
4556
+
4557
+ /**
4558
+ * used to add messages for use in debugging
4559
+ */
4560
+ function addMessage($message) {
4561
+ $this->messages.= $message."\n";
4562
+ }
4563
+
4564
+ /**
4565
+ * a few functions which should allow the document to be treated transactionally.
4566
+ */
4567
+ function transaction($action) {
4568
+ switch ($action) {
4569
+ case 'start':
4570
+ // store all the data away into the checkpoint variable
4571
+ $data = get_object_vars($this);
4572
+ $this->checkpoint = $data;
4573
+ unset($data);
4574
+ break;
4575
+
4576
+ case 'commit':
4577
+ if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) {
4578
+ $tmp = $this->checkpoint['checkpoint'];
4579
+ $this->checkpoint = $tmp;
4580
+ unset($tmp);
4581
+ } else {
4582
+ $this->checkpoint = '';
4583
+ }
4584
+ break;
4585
+
4586
+ case 'rewind':
4587
+ // do not destroy the current checkpoint, but move us back to the state then, so that we can try again
4588
+ if (is_array($this->checkpoint)) {
4589
+ // can only abort if were inside a checkpoint
4590
+ $tmp = $this->checkpoint;
4591
+
4592
+ foreach ($tmp as $k => $v) {
4593
+ if ($k !== 'checkpoint') {
4594
+ $this->$k = $v;
4595
+ }
4596
+ }
4597
+ unset($tmp);
4598
+ }
4599
+ break;
4600
+
4601
+ case 'abort':
4602
+ if (is_array($this->checkpoint)) {
4603
+ // can only abort if were inside a checkpoint
4604
+ $tmp = $this->checkpoint;
4605
+ foreach ($tmp as $k => $v) {
4606
+ $this->$k = $v;
4607
+ }
4608
+ unset($tmp);
4609
+ }
4610
+ break;
4611
+ }
4612
+ }
4613
+ }
lib/dompdf/lib/fonts/Courier-Bold.afm ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
3
+ Comment Creation Date: Mon Jun 23 16:28:00 0:00:00
4
+ Comment UniqueID 43048
5
+ Comment VMusage 41139 52164
6
+ FontName Courier-Bold
7
+ FullName Courier Bold
8
+ FamilyName Courier
9
+ Weight Bold
10
+ ItalicAngle 0
11
+ IsFixedPitch true
12
+ CharacterSet ExtendedRoman
13
+ FontBBox -113 -250 749 801
14
+ UnderlinePosition -100
15
+ UnderlineThickness 50
16
+ Version 003.000
17
+ Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
18
+ EncodingScheme WinAnsiEncoding
19
+ CapHeight 562
20
+ XHeight 439
21
+ Ascender 629
22
+ Descender -157
23
+ StdHW 84
24
+ StdVW 106
25
+ StartCharMetrics 317
26
+ C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
27
+ C 160 ; WX 600 ; N space ; B 0 0 0 0 ;
28
+ C 33 ; WX 600 ; N exclam ; B 202 -15 398 572 ;
29
+ C 34 ; WX 600 ; N quotedbl ; B 135 277 465 562 ;
30
+ C 35 ; WX 600 ; N numbersign ; B 56 -45 544 651 ;
31
+ C 36 ; WX 600 ; N dollar ; B 82 -126 519 666 ;
32
+ C 37 ; WX 600 ; N percent ; B 5 -15 595 616 ;
33
+ C 38 ; WX 600 ; N ampersand ; B 36 -15 546 543 ;
34
+ C 146 ; WX 600 ; N quoteright ; B 171 277 423 562 ;
35
+ C 40 ; WX 600 ; N parenleft ; B 219 -102 461 616 ;
36
+ C 41 ; WX 600 ; N parenright ; B 139 -102 381 616 ;
37
+ C 42 ; WX 600 ; N asterisk ; B 91 219 509 601 ;
38
+ C 43 ; WX 600 ; N plus ; B 71 39 529 478 ;
39
+ C 44 ; WX 600 ; N comma ; B 123 -111 393 174 ;
40
+ C 45 ; WX 600 ; N hyphen ; B 100 203 500 313 ;
41
+ C 173 ; WX 600 ; N hyphen ; B 100 203 500 313 ;
42
+ C 46 ; WX 600 ; N period ; B 192 -15 408 171 ;
43
+ C 47 ; WX 600 ; N slash ; B 98 -77 502 626 ;
44
+ C 48 ; WX 600 ; N zero ; B 87 -15 513 616 ;
45
+ C 49 ; WX 600 ; N one ; B 81 0 539 616 ;
46
+ C 50 ; WX 600 ; N two ; B 61 0 499 616 ;
47
+ C 51 ; WX 600 ; N three ; B 63 -15 501 616 ;
48
+ C 52 ; WX 600 ; N four ; B 53 0 507 616 ;
49
+ C 53 ; WX 600 ; N five ; B 70 -15 521 601 ;
50
+ C 54 ; WX 600 ; N six ; B 90 -15 521 616 ;
51
+ C 55 ; WX 600 ; N seven ; B 55 0 494 601 ;
52
+ C 56 ; WX 600 ; N eight ; B 83 -15 517 616 ;
53
+ C 57 ; WX 600 ; N nine ; B 79 -15 510 616 ;
54
+ C 58 ; WX 600 ; N colon ; B 191 -15 407 425 ;
55
+ C 59 ; WX 600 ; N semicolon ; B 123 -111 408 425 ;
56
+ C 60 ; WX 600 ; N less ; B 66 15 523 501 ;
57
+ C 61 ; WX 600 ; N equal ; B 71 118 529 398 ;
58
+ C 62 ; WX 600 ; N greater ; B 77 15 534 501 ;
59
+ C 63 ; WX 600 ; N question ; B 98 -14 501 580 ;
60
+ C 64 ; WX 600 ; N at ; B 16 -15 584 616 ;
61
+ C 65 ; WX 600 ; N A ; B -9 0 609 562 ;
62
+ C 66 ; WX 600 ; N B ; B 30 0 573 562 ;
63
+ C 67 ; WX 600 ; N C ; B 22 -18 560 580 ;
64
+ C 68 ; WX 600 ; N D ; B 30 0 594 562 ;
65
+ C 69 ; WX 600 ; N E ; B 25 0 560 562 ;
66
+ C 70 ; WX 600 ; N F ; B 39 0 570 562 ;
67
+ C 71 ; WX 600 ; N G ; B 22 -18 594 580 ;
68
+ C 72 ; WX 600 ; N H ; B 20 0 580 562 ;
69
+ C 73 ; WX 600 ; N I ; B 77 0 523 562 ;
70
+ C 74 ; WX 600 ; N J ; B 37 -18 601 562 ;
71
+ C 75 ; WX 600 ; N K ; B 21 0 599 562 ;
72
+ C 76 ; WX 600 ; N L ; B 39 0 578 562 ;
73
+ C 77 ; WX 600 ; N M ; B -2 0 602 562 ;
74
+ C 78 ; WX 600 ; N N ; B 8 -12 610 562 ;
75
+ C 79 ; WX 600 ; N O ; B 22 -18 578 580 ;
76
+ C 80 ; WX 600 ; N P ; B 48 0 559 562 ;
77
+ C 81 ; WX 600 ; N Q ; B 32 -138 578 580 ;
78
+ C 82 ; WX 600 ; N R ; B 24 0 599 562 ;
79
+ C 83 ; WX 600 ; N S ; B 47 -22 553 582 ;
80
+ C 84 ; WX 600 ; N T ; B 21 0 579 562 ;
81
+ C 85 ; WX 600 ; N U ; B 4 -18 596 562 ;
82
+ C 86 ; WX 600 ; N V ; B -13 0 613 562 ;
83
+ C 87 ; WX 600 ; N W ; B -18 0 618 562 ;
84
+ C 88 ; WX 600 ; N X ; B 12 0 588 562 ;
85
+ C 89 ; WX 600 ; N Y ; B 12 0 589 562 ;
86
+ C 90 ; WX 600 ; N Z ; B 62 0 539 562 ;
87
+ C 91 ; WX 600 ; N bracketleft ; B 245 -102 475 616 ;
88
+ C 92 ; WX 600 ; N backslash ; B 99 -77 503 626 ;
89
+ C 93 ; WX 600 ; N bracketright ; B 125 -102 355 616 ;
90
+ C 94 ; WX 600 ; N asciicircum ; B 108 250 492 616 ;
91
+ C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
92
+ C 145 ; WX 600 ; N quoteleft ; B 178 277 428 562 ;
93
+ C 97 ; WX 600 ; N a ; B 35 -15 570 454 ;
94
+ C 98 ; WX 600 ; N b ; B 0 -15 584 626 ;
95
+ C 99 ; WX 600 ; N c ; B 40 -15 545 459 ;
96
+ C 100 ; WX 600 ; N d ; B 20 -15 591 626 ;
97
+ C 101 ; WX 600 ; N e ; B 40 -15 563 454 ;
98
+ C 102 ; WX 600 ; N f ; B 83 0 547 626 ; L i fi ; L l fl ;
99
+ C 103 ; WX 600 ; N g ; B 30 -146 580 454 ;
100
+ C 104 ; WX 600 ; N h ; B 5 0 592 626 ;
101
+ C 105 ; WX 600 ; N i ; B 77 0 523 658 ;
102
+ C 106 ; WX 600 ; N j ; B 63 -146 440 658 ;
103
+ C 107 ; WX 600 ; N k ; B 20 0 585 626 ;
104
+ C 108 ; WX 600 ; N l ; B 77 0 523 626 ;
105
+ C 109 ; WX 600 ; N m ; B -22 0 626 454 ;
106
+ C 110 ; WX 600 ; N n ; B 18 0 592 454 ;
107
+ C 111 ; WX 600 ; N o ; B 30 -15 570 454 ;
108
+ C 112 ; WX 600 ; N p ; B -1 -142 570 454 ;
109
+ C 113 ; WX 600 ; N q ; B 20 -142 591 454 ;
110
+ C 114 ; WX 600 ; N r ; B 47 0 580 454 ;
111
+ C 115 ; WX 600 ; N s ; B 68 -17 535 459 ;
112
+ C 116 ; WX 600 ; N t ; B 47 -15 532 562 ;
113
+ C 117 ; WX 600 ; N u ; B -1 -15 569 439 ;
114
+ C 118 ; WX 600 ; N v ; B -1 0 601 439 ;
115
+ C 119 ; WX 600 ; N w ; B -18 0 618 439 ;
116
+ C 120 ; WX 600 ; N x ; B 6 0 594 439 ;
117
+ C 121 ; WX 600 ; N y ; B -4 -142 601 439 ;
118
+ C 122 ; WX 600 ; N z ; B 81 0 520 439 ;
119
+ C 123 ; WX 600 ; N braceleft ; B 160 -102 464 616 ;
120
+ C 124 ; WX 600 ; N bar ; B 255 -250 345 750 ;
121
+ C 125 ; WX 600 ; N braceright ; B 136 -102 440 616 ;
122
+ C 126 ; WX 600 ; N asciitilde ; B 71 153 530 356 ;
123
+ C 161 ; WX 600 ; N exclamdown ; B 202 -146 398 449 ;
124
+ C 162 ; WX 600 ; N cent ; B 66 -49 518 614 ;
125
+ C 163 ; WX 600 ; N sterling ; B 72 -28 558 611 ;
126
+ C -1 ; WX 600 ; N fraction ; B 25 -60 576 661 ;
127
+ C 165 ; WX 600 ; N yen ; B 10 0 590 562 ;
128
+ C 131 ; WX 600 ; N florin ; B -30 -131 572 616 ;
129
+ C 167 ; WX 600 ; N section ; B 83 -70 517 580 ;
130
+ C 164 ; WX 600 ; N currency ; B 54 49 546 517 ;
131
+ C 39 ; WX 600 ; N quotesingle ; B 227 277 373 562 ;
132
+ C 147 ; WX 600 ; N quotedblleft ; B 71 277 535 562 ;
133
+ C 170 ; WX 600 ; N guillemotleft ; B 8 70 553 446 ;
134
+ C 139 ; WX 600 ; N guilsinglleft ; B 141 70 459 446 ;
135
+ C 155 ; WX 600 ; N guilsinglright ; B 141 70 459 446 ;
136
+ C -1 ; WX 600 ; N fi ; B 12 0 593 626 ;
137
+ C -1 ; WX 600 ; N fl ; B 12 0 593 626 ;
138
+ C 150 ; WX 600 ; N endash ; B 65 203 535 313 ;
139
+ C 134 ; WX 600 ; N dagger ; B 106 -70 494 580 ;
140
+ C 135 ; WX 600 ; N daggerdbl ; B 106 -70 494 580 ;
141
+ C 183 ; WX 600 ; N periodcentered ; B 196 165 404 351 ;
142
+ C 182 ; WX 600 ; N paragraph ; B 6 -70 576 580 ;
143
+ C 149 ; WX 600 ; N bullet ; B 140 132 460 430 ;
144
+ C 130 ; WX 600 ; N quotesinglbase ; B 175 -142 427 143 ;
145
+ C 132 ; WX 600 ; N quotedblbase ; B 65 -142 529 143 ;
146
+ C 148 ; WX 600 ; N quotedblright ; B 61 277 525 562 ;
147
+ C 187 ; WX 600 ; N guillemotright ; B 47 70 592 446 ;
148
+ C 133 ; WX 600 ; N ellipsis ; B 26 -15 574 116 ;
149
+ C 137 ; WX 600 ; N perthousand ; B -113 -15 713 616 ;
150
+ C 191 ; WX 600 ; N questiondown ; B 99 -146 502 449 ;
151
+ C 96 ; WX 600 ; N grave ; B 132 508 395 661 ;
152
+ C 180 ; WX 600 ; N acute ; B 205 508 468 661 ;
153
+ C 136 ; WX 600 ; N circumflex ; B 103 483 497 657 ;
154
+ C 152 ; WX 600 ; N tilde ; B 89 493 512 636 ;
155
+ C 175 ; WX 600 ; N macron ; B 88 505 512 585 ;
156
+ C -1 ; WX 600 ; N breve ; B 83 468 517 631 ;
157
+ C -1 ; WX 600 ; N dotaccent ; B 230 498 370 638 ;
158
+ C 168 ; WX 600 ; N dieresis ; B 128 498 472 638 ;
159
+ C -1 ; WX 600 ; N ring ; B 198 481 402 678 ;
160
+ C 184 ; WX 600 ; N cedilla ; B 205 -206 387 0 ;
161
+ C -1 ; WX 600 ; N hungarumlaut ; B 68 488 588 661 ;
162
+ C -1 ; WX 600 ; N ogonek ; B 169 -199 400 0 ;
163
+ C -1 ; WX 600 ; N caron ; B 103 493 497 667 ;
164
+ C 151 ; WX 600 ; N emdash ; B -10 203 610 313 ;
165
+ C 198 ; WX 600 ; N AE ; B -29 0 602 562 ;
166
+ C 170 ; WX 600 ; N ordfeminine ; B 147 196 453 580 ;
167
+ C -1 ; WX 600 ; N Lslash ; B 39 0 578 562 ;
168
+ C 216 ; WX 600 ; N Oslash ; B 22 -22 578 584 ;
169
+ C 140 ; WX 600 ; N OE ; B -25 0 595 562 ;
170
+ C 186 ; WX 600 ; N ordmasculine ; B 147 196 453 580 ;
171
+ C 230 ; WX 600 ; N ae ; B -4 -15 601 454 ;
172
+ C -1 ; WX 600 ; N dotlessi ; B 77 0 523 439 ;
173
+ C -1 ; WX 600 ; N lslash ; B 77 0 523 626 ;
174
+ C 248 ; WX 600 ; N oslash ; B 30 -24 570 463 ;
175
+ C 156 ; WX 600 ; N oe ; B -18 -15 611 454 ;
176
+ C 223 ; WX 600 ; N germandbls ; B 22 -15 596 626 ;
177
+ C 207 ; WX 600 ; N Idieresis ; B 77 0 523 761 ;
178
+ C 233 ; WX 600 ; N eacute ; B 40 -15 563 661 ;
179
+ C -1 ; WX 600 ; N abreve ; B 35 -15 570 661 ;
180
+ C -1 ; WX 600 ; N uhungarumlaut ; B -1 -15 628 661 ;
181
+ C -1 ; WX 600 ; N ecaron ; B 40 -15 563 667 ;
182
+ C 159 ; WX 600 ; N Ydieresis ; B 12 0 589 761 ;
183
+ C 247 ; WX 600 ; N divide ; B 71 16 529 500 ;
184
+ C 221 ; WX 600 ; N Yacute ; B 12 0 589 784 ;
185
+ C 194 ; WX 600 ; N Acircumflex ; B -9 0 609 780 ;
186
+ C 225 ; WX 600 ; N aacute ; B 35 -15 570 661 ;
187
+ C 219 ; WX 600 ; N Ucircumflex ; B 4 -18 596 780 ;
188
+ C 253 ; WX 600 ; N yacute ; B -4 -142 601 661 ;
189
+ C -1 ; WX 600 ; N scommaaccent ; B 68 -250 535 459 ;
190
+ C 234 ; WX 600 ; N ecircumflex ; B 40 -15 563 657 ;
191
+ C -1 ; WX 600 ; N Uring ; B 4 -18 596 801 ;
192
+ C 220 ; WX 600 ; N Udieresis ; B 4 -18 596 761 ;
193
+ C -1 ; WX 600 ; N aogonek ; B 35 -199 586 454 ;
194
+ C 218 ; WX 600 ; N Uacute ; B 4 -18 596 784 ;
195
+ C -1 ; WX 600 ; N uogonek ; B -1 -199 585 439 ;
196
+ C 203 ; WX 600 ; N Edieresis ; B 25 0 560 761 ;
197
+ C -1 ; WX 600 ; N Dcroat ; B 30 0 594 562 ;
198
+ C -1 ; WX 600 ; N commaaccent ; B 205 -250 397 -57 ;
199
+ C 169 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
200
+ C -1 ; WX 600 ; N Emacron ; B 25 0 560 708 ;
201
+ C -1 ; WX 600 ; N ccaron ; B 40 -15 545 667 ;
202
+ C 229 ; WX 600 ; N aring ; B 35 -15 570 678 ;
203
+ C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 610 562 ;
204
+ C -1 ; WX 600 ; N lacute ; B 77 0 523 801 ;
205
+ C 224 ; WX 600 ; N agrave ; B 35 -15 570 661 ;
206
+ C -1 ; WX 600 ; N Tcommaaccent ; B 21 -250 579 562 ;
207
+ C -1 ; WX 600 ; N Cacute ; B 22 -18 560 784 ;
208
+ C 227 ; WX 600 ; N atilde ; B 35 -15 570 636 ;
209
+ C -1 ; WX 600 ; N Edotaccent ; B 25 0 560 761 ;
210
+ C 154 ; WX 600 ; N scaron ; B 68 -17 535 667 ;
211
+ C -1 ; WX 600 ; N scedilla ; B 68 -206 535 459 ;
212
+ C 237 ; WX 600 ; N iacute ; B 77 0 523 661 ;
213
+ C -1 ; WX 600 ; N lozenge ; B 66 0 534 740 ;
214
+ C -1 ; WX 600 ; N Rcaron ; B 24 0 599 790 ;
215
+ C -1 ; WX 600 ; N Gcommaaccent ; B 22 -250 594 580 ;
216
+ C 251 ; WX 600 ; N ucircumflex ; B -1 -15 569 657 ;
217
+ C 226 ; WX 600 ; N acircumflex ; B 35 -15 570 657 ;
218
+ C -1 ; WX 600 ; N Amacron ; B -9 0 609 708 ;
219
+ C -1 ; WX 600 ; N rcaron ; B 47 0 580 667 ;
220
+ C 231 ; WX 600 ; N ccedilla ; B 40 -206 545 459 ;
221
+ C -1 ; WX 600 ; N Zdotaccent ; B 62 0 539 761 ;
222
+ C 222 ; WX 600 ; N Thorn ; B 48 0 557 562 ;
223
+ C -1 ; WX 600 ; N Omacron ; B 22 -18 578 708 ;
224
+ C -1 ; WX 600 ; N Racute ; B 24 0 599 784 ;
225
+ C -1 ; WX 600 ; N Sacute ; B 47 -22 553 784 ;
226
+ C -1 ; WX 600 ; N dcaron ; B 20 -15 727 626 ;
227
+ C -1 ; WX 600 ; N Umacron ; B 4 -18 596 708 ;
228
+ C -1 ; WX 600 ; N uring ; B -1 -15 569 678 ;
229
+ C 179 ; WX 600 ; N threesuperior ; B 138 222 433 616 ;
230
+ C 210 ; WX 600 ; N Ograve ; B 22 -18 578 784 ;
231
+ C 192 ; WX 600 ; N Agrave ; B -9 0 609 784 ;
232
+ C -1 ; WX 600 ; N Abreve ; B -9 0 609 784 ;
233
+ C 215 ; WX 600 ; N multiply ; B 81 39 520 478 ;
234
+ C 250 ; WX 600 ; N uacute ; B -1 -15 569 661 ;
235
+ C -1 ; WX 600 ; N Tcaron ; B 21 0 579 790 ;
236
+ C -1 ; WX 600 ; N partialdiff ; B 63 -38 537 728 ;
237
+ C 255 ; WX 600 ; N ydieresis ; B -4 -142 601 638 ;
238
+ C -1 ; WX 600 ; N Nacute ; B 8 -12 610 784 ;
239
+ C 238 ; WX 600 ; N icircumflex ; B 73 0 523 657 ;
240
+ C 202 ; WX 600 ; N Ecircumflex ; B 25 0 560 780 ;
241
+ C 228 ; WX 600 ; N adieresis ; B 35 -15 570 638 ;
242
+ C 235 ; WX 600 ; N edieresis ; B 40 -15 563 638 ;
243
+ C -1 ; WX 600 ; N cacute ; B 40 -15 545 661 ;
244
+ C -1 ; WX 600 ; N nacute ; B 18 0 592 661 ;
245
+ C -1 ; WX 600 ; N umacron ; B -1 -15 569 585 ;
246
+ C -1 ; WX 600 ; N Ncaron ; B 8 -12 610 790 ;
247
+ C 205 ; WX 600 ; N Iacute ; B 77 0 523 784 ;
248
+ C 177 ; WX 600 ; N plusminus ; B 71 24 529 515 ;
249
+ C 166 ; WX 600 ; N brokenbar ; B 255 -175 345 675 ;
250
+ C 174 ; WX 600 ; N registered ; B 0 -18 600 580 ;
251
+ C -1 ; WX 600 ; N Gbreve ; B 22 -18 594 784 ;
252
+ C -1 ; WX 600 ; N Idotaccent ; B 77 0 523 761 ;
253
+ C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ;
254
+ C 200 ; WX 600 ; N Egrave ; B 25 0 560 784 ;
255
+ C -1 ; WX 600 ; N racute ; B 47 0 580 661 ;
256
+ C -1 ; WX 600 ; N omacron ; B 30 -15 570 585 ;
257
+ C -1 ; WX 600 ; N Zacute ; B 62 0 539 784 ;
258
+ C 142 ; WX 600 ; N Zcaron ; B 62 0 539 790 ;
259
+ C -1 ; WX 600 ; N greaterequal ; B 26 0 523 696 ;
260
+ C 208 ; WX 600 ; N Eth ; B 30 0 594 562 ;
261
+ C 199 ; WX 600 ; N Ccedilla ; B 22 -206 560 580 ;
262
+ C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 523 626 ;
263
+ C -1 ; WX 600 ; N tcaron ; B 47 -15 532 703 ;
264
+ C -1 ; WX 600 ; N eogonek ; B 40 -199 563 454 ;
265
+ C -1 ; WX 600 ; N Uogonek ; B 4 -199 596 562 ;
266
+ C 193 ; WX 600 ; N Aacute ; B -9 0 609 784 ;
267
+ C 196 ; WX 600 ; N Adieresis ; B -9 0 609 761 ;
268
+ C 232 ; WX 600 ; N egrave ; B 40 -15 563 661 ;
269
+ C -1 ; WX 600 ; N zacute ; B 81 0 520 661 ;
270
+ C -1 ; WX 600 ; N iogonek ; B 77 -199 523 658 ;
271
+ C 211 ; WX 600 ; N Oacute ; B 22 -18 578 784 ;
272
+ C 243 ; WX 600 ; N oacute ; B 30 -15 570 661 ;
273
+ C -1 ; WX 600 ; N amacron ; B 35 -15 570 585 ;
274
+ C -1 ; WX 600 ; N sacute ; B 68 -17 535 661 ;
275
+ C 239 ; WX 600 ; N idieresis ; B 77 0 523 618 ;
276
+ C 212 ; WX 600 ; N Ocircumflex ; B 22 -18 578 780 ;
277
+ C 217 ; WX 600 ; N Ugrave ; B 4 -18 596 784 ;
278
+ C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
279
+ C 254 ; WX 600 ; N thorn ; B -14 -142 570 626 ;
280
+ C 178 ; WX 600 ; N twosuperior ; B 143 230 436 616 ;
281
+ C 214 ; WX 600 ; N Odieresis ; B 22 -18 578 761 ;
282
+ C 181 ; WX 600 ; N mu ; B -1 -142 569 439 ;
283
+ C 236 ; WX 600 ; N igrave ; B 77 0 523 661 ;
284
+ C -1 ; WX 600 ; N ohungarumlaut ; B 30 -15 668 661 ;
285
+ C -1 ; WX 600 ; N Eogonek ; B 25 -199 576 562 ;
286
+ C -1 ; WX 600 ; N dcroat ; B 20 -15 591 626 ;
287
+ C 190 ; WX 600 ; N threequarters ; B -47 -60 648 661 ;
288
+ C -1 ; WX 600 ; N Scedilla ; B 47 -206 553 582 ;
289
+ C -1 ; WX 600 ; N lcaron ; B 77 0 597 626 ;
290
+ C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 599 562 ;
291
+ C -1 ; WX 600 ; N Lacute ; B 39 0 578 784 ;
292
+ C 153 ; WX 600 ; N trademark ; B -9 230 749 562 ;
293
+ C -1 ; WX 600 ; N edotaccent ; B 40 -15 563 638 ;
294
+ C 204 ; WX 600 ; N Igrave ; B 77 0 523 784 ;
295
+ C -1 ; WX 600 ; N Imacron ; B 77 0 523 708 ;
296
+ C -1 ; WX 600 ; N Lcaron ; B 39 0 637 562 ;
297
+ C 189 ; WX 600 ; N onehalf ; B -47 -60 648 661 ;
298
+ C -1 ; WX 600 ; N lessequal ; B 26 0 523 696 ;
299
+ C 244 ; WX 600 ; N ocircumflex ; B 30 -15 570 657 ;
300
+ C 241 ; WX 600 ; N ntilde ; B 18 0 592 636 ;
301
+ C -1 ; WX 600 ; N Uhungarumlaut ; B 4 -18 638 784 ;
302
+ C 201 ; WX 600 ; N Eacute ; B 25 0 560 784 ;
303
+ C -1 ; WX 600 ; N emacron ; B 40 -15 563 585 ;
304
+ C -1 ; WX 600 ; N gbreve ; B 30 -146 580 661 ;
305
+ C 188 ; WX 600 ; N onequarter ; B -56 -60 656 661 ;
306
+ C 138 ; WX 600 ; N Scaron ; B 47 -22 553 790 ;
307
+ C -1 ; WX 600 ; N Scommaaccent ; B 47 -250 553 582 ;
308
+ C -1 ; WX 600 ; N Ohungarumlaut ; B 22 -18 628 784 ;
309
+ C 176 ; WX 600 ; N degree ; B 86 243 474 616 ;
310
+ C 242 ; WX 600 ; N ograve ; B 30 -15 570 661 ;
311
+ C -1 ; WX 600 ; N Ccaron ; B 22 -18 560 790 ;
312
+ C 249 ; WX 600 ; N ugrave ; B -1 -15 569 661 ;
313
+ C -1 ; WX 600 ; N radical ; B -19 -104 473 778 ;
314
+ C -1 ; WX 600 ; N Dcaron ; B 30 0 594 790 ;
315
+ C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 580 454 ;
316
+ C 209 ; WX 600 ; N Ntilde ; B 8 -12 610 759 ;
317
+ C 245 ; WX 600 ; N otilde ; B 30 -15 570 636 ;
318
+ C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 599 562 ;
319
+ C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 578 562 ;
320
+ C 195 ; WX 600 ; N Atilde ; B -9 0 609 759 ;
321
+ C -1 ; WX 600 ; N Aogonek ; B -9 -199 625 562 ;
322
+ C 197 ; WX 600 ; N Aring ; B -9 0 609 801 ;
323
+ C 213 ; WX 600 ; N Otilde ; B 22 -18 578 759 ;
324
+ C -1 ; WX 600 ; N zdotaccent ; B 81 0 520 638 ;
325
+ C -1 ; WX 600 ; N Ecaron ; B 25 0 560 790 ;
326
+ C -1 ; WX 600 ; N Iogonek ; B 77 -199 523 562 ;
327
+ C -1 ; WX 600 ; N kcommaaccent ; B 20 -250 585 626 ;
328
+ C -1 ; WX 600 ; N minus ; B 71 203 529 313 ;
329
+ C 206 ; WX 600 ; N Icircumflex ; B 77 0 523 780 ;
330
+ C -1 ; WX 600 ; N ncaron ; B 18 0 592 667 ;
331
+ C -1 ; WX 600 ; N tcommaaccent ; B 47 -250 532 562 ;
332
+ C 172 ; WX 600 ; N logicalnot ; B 71 103 529 413 ;
333
+ C 246 ; WX 600 ; N odieresis ; B 30 -15 570 638 ;
334
+ C 252 ; WX 600 ; N udieresis ; B -1 -15 569 638 ;
335
+ C -1 ; WX 600 ; N notequal ; B 12 -47 537 563 ;
336
+ C -1 ; WX 600 ; N gcommaaccent ; B 30 -146 580 714 ;
337
+ C 240 ; WX 600 ; N eth ; B 58 -27 543 626 ;
338
+ C 158 ; WX 600 ; N zcaron ; B 81 0 520 667 ;
339
+ C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 592 454 ;
340
+ C 185 ; WX 600 ; N onesuperior ; B 153 230 447 616 ;
341
+ C -1 ; WX 600 ; N imacron ; B 77 0 523 585 ;
342
+ C 128 ; WX 600 ; N Euro ; B 0 0 0 0 ;
343
+ EndCharMetrics
344
+ EndFontMetrics
lib/dompdf/lib/fonts/Courier-BoldOblique.afm ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
3
+ Comment Creation Date: Mon Jun 23 16:28:46 0:00:00
4
+ Comment UniqueID 43049
5
+ Comment VMusage 17529 79244
6
+ FontName Courier-BoldOblique
7
+ FullName Courier Bold Oblique
8
+ FamilyName Courier
9
+ Weight Bold
10
+ ItalicAngle -12
11
+ IsFixedPitch true
12
+ CharacterSet ExtendedRoman
13
+ FontBBox -57 -250 869 801
14
+ UnderlinePosition -100
15
+ UnderlineThickness 50
16
+ Version 3
17
+ Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
18
+ EncodingScheme WinAnsiEncoding
19
+ CapHeight 562
20
+ XHeight 439
21
+ Ascender 629
22
+ Descender -157
23
+ StdHW 84
24
+ StdVW 106
25
+ StartCharMetrics 317
26
+ C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
27
+ C 160 ; WX 600 ; N space ; B 0 0 0 0 ;
28
+ C 33 ; WX 600 ; N exclam ; B 215 -15 495 572 ;
29
+ C 34 ; WX 600 ; N quotedbl ; B 211 277 585 562 ;
30
+ C 35 ; WX 600 ; N numbersign ; B 88 -45 641 651 ;
31
+ C 36 ; WX 600 ; N dollar ; B 87 -126 630 666 ;
32
+ C 37 ; WX 600 ; N percent ; B 101 -15 625 616 ;
33
+ C 38 ; WX 600 ; N ampersand ; B 61 -15 595 543 ;
34
+ C 146 ; WX 600 ; N quoteright ; B 229 277 543 562 ;
35
+ C 40 ; WX 600 ; N parenleft ; B 265 -102 592 616 ;
36
+ C 41 ; WX 600 ; N parenright ; B 117 -102 444 616 ;
37
+ C 42 ; WX 600 ; N asterisk ; B 179 219 598 601 ;
38
+ C 43 ; WX 600 ; N plus ; B 114 39 596 478 ;
39
+ C 44 ; WX 600 ; N comma ; B 99 -111 430 174 ;
40
+ C 45 ; WX 600 ; N hyphen ; B 143 203 567 313 ;
41
+ C 173 ; WX 600 ; N hyphen ; B 143 203 567 313 ;
42
+ C 46 ; WX 600 ; N period ; B 206 -15 427 171 ;
43
+ C 47 ; WX 600 ; N slash ; B 90 -77 626 626 ;
44
+ C 48 ; WX 600 ; N zero ; B 135 -15 593 616 ;
45
+ C 49 ; WX 600 ; N one ; B 93 0 562 616 ;
46
+ C 50 ; WX 600 ; N two ; B 61 0 594 616 ;
47
+ C 51 ; WX 600 ; N three ; B 71 -15 571 616 ;
48
+ C 52 ; WX 600 ; N four ; B 81 0 559 616 ;
49
+ C 53 ; WX 600 ; N five ; B 77 -15 621 601 ;
50
+ C 54 ; WX 600 ; N six ; B 135 -15 652 616 ;
51
+ C 55 ; WX 600 ; N seven ; B 147 0 622 601 ;
52
+ C 56 ; WX 600 ; N eight ; B 115 -15 604 616 ;
53
+ C 57 ; WX 600 ; N nine ; B 75 -15 592 616 ;
54
+ C 58 ; WX 600 ; N colon ; B 205 -15 480 425 ;
55
+ C 59 ; WX 600 ; N semicolon ; B 99 -111 481 425 ;
56
+ C 60 ; WX 600 ; N less ; B 120 15 613 501 ;
57
+ C 61 ; WX 600 ; N equal ; B 96 118 614 398 ;
58
+ C 62 ; WX 600 ; N greater ; B 97 15 589 501 ;
59
+ C 63 ; WX 600 ; N question ; B 183 -14 592 580 ;
60
+ C 64 ; WX 600 ; N at ; B 65 -15 642 616 ;
61
+ C 65 ; WX 600 ; N A ; B -9 0 632 562 ;
62
+ C 66 ; WX 600 ; N B ; B 30 0 630 562 ;
63
+ C 67 ; WX 600 ; N C ; B 74 -18 675 580 ;
64
+ C 68 ; WX 600 ; N D ; B 30 0 664 562 ;
65
+ C 69 ; WX 600 ; N E ; B 25 0 670 562 ;
66
+ C 70 ; WX 600 ; N F ; B 39 0 684 562 ;
67
+ C 71 ; WX 600 ; N G ; B 74 -18 675 580 ;
68
+ C 72 ; WX 600 ; N H ; B 20 0 700 562 ;
69
+ C 73 ; WX 600 ; N I ; B 77 0 643 562 ;
70
+ C 74 ; WX 600 ; N J ; B 58 -18 721 562 ;
71
+ C 75 ; WX 600 ; N K ; B 21 0 692 562 ;
72
+ C 76 ; WX 600 ; N L ; B 39 0 636 562 ;
73
+ C 77 ; WX 600 ; N M ; B -2 0 722 562 ;
74
+ C 78 ; WX 600 ; N N ; B 8 -12 730 562 ;
75
+ C 79 ; WX 600 ; N O ; B 74 -18 645 580 ;
76
+ C 80 ; WX 600 ; N P ; B 48 0 643 562 ;
77
+ C 81 ; WX 600 ; N Q ; B 83 -138 636 580 ;
78
+ C 82 ; WX 600 ; N R ; B 24 0 617 562 ;
79
+ C 83 ; WX 600 ; N S ; B 54 -22 673 582 ;
80
+ C 84 ; WX 600 ; N T ; B 86 0 679 562 ;
81
+ C 85 ; WX 600 ; N U ; B 101 -18 716 562 ;
82
+ C 86 ; WX 600 ; N V ; B 84 0 733 562 ;
83
+ C 87 ; WX 600 ; N W ; B 79 0 738 562 ;
84
+ C 88 ; WX 600 ; N X ; B 12 0 690 562 ;
85
+ C 89 ; WX 600 ; N Y ; B 109 0 709 562 ;
86
+ C 90 ; WX 600 ; N Z ; B 62 0 637 562 ;
87
+ C 91 ; WX 600 ; N bracketleft ; B 223 -102 606 616 ;
88
+ C 92 ; WX 600 ; N backslash ; B 222 -77 496 626 ;
89
+ C 93 ; WX 600 ; N bracketright ; B 103 -102 486 616 ;
90
+ C 94 ; WX 600 ; N asciicircum ; B 171 250 556 616 ;
91
+ C 95 ; WX 600 ; N underscore ; B -27 -125 585 -75 ;
92
+ C 145 ; WX 600 ; N quoteleft ; B 297 277 487 562 ;
93
+ C 97 ; WX 600 ; N a ; B 61 -15 593 454 ;
94
+ C 98 ; WX 600 ; N b ; B 13 -15 636 626 ;
95
+ C 99 ; WX 600 ; N c ; B 81 -15 631 459 ;
96
+ C 100 ; WX 600 ; N d ; B 60 -15 645 626 ;
97
+ C 101 ; WX 600 ; N e ; B 81 -15 605 454 ;
98
+ C 102 ; WX 600 ; N f ; B 83 0 677 626 ; L i fi ; L l fl ;
99
+ C 103 ; WX 600 ; N g ; B 40 -146 674 454 ;
100
+ C 104 ; WX 600 ; N h ; B 18 0 615 626 ;
101
+ C 105 ; WX 600 ; N i ; B 77 0 546 658 ;
102
+ C 106 ; WX 600 ; N j ; B 36 -146 580 658 ;
103
+ C 107 ; WX 600 ; N k ; B 33 0 643 626 ;
104
+ C 108 ; WX 600 ; N l ; B 77 0 546 626 ;
105
+ C 109 ; WX 600 ; N m ; B -22 0 649 454 ;
106
+ C 110 ; WX 600 ; N n ; B 18 0 615 454 ;
107
+ C 111 ; WX 600 ; N o ; B 71 -15 622 454 ;
108
+ C 112 ; WX 600 ; N p ; B -32 -142 622 454 ;
109
+ C 113 ; WX 600 ; N q ; B 60 -142 685 454 ;
110
+ C 114 ; WX 600 ; N r ; B 47 0 655 454 ;
111
+ C 115 ; WX 600 ; N s ; B 66 -17 608 459 ;
112
+ C 116 ; WX 600 ; N t ; B 118 -15 567 562 ;
113
+ C 117 ; WX 600 ; N u ; B 70 -15 592 439 ;
114
+ C 118 ; WX 600 ; N v ; B 70 0 695 439 ;
115
+ C 119 ; WX 600 ; N w ; B 53 0 712 439 ;
116
+ C 120 ; WX 600 ; N x ; B 6 0 671 439 ;
117
+ C 121 ; WX 600 ; N y ; B -21 -142 695 439 ;
118
+ C 122 ; WX 600 ; N z ; B 81 0 614 439 ;
119
+ C 123 ; WX 600 ; N braceleft ; B 203 -102 595 616 ;
120
+ C 124 ; WX 600 ; N bar ; B 201 -250 505 750 ;
121
+ C 125 ; WX 600 ; N braceright ; B 114 -102 506 616 ;
122
+ C 126 ; WX 600 ; N asciitilde ; B 120 153 590 356 ;
123
+ C 161 ; WX 600 ; N exclamdown ; B 196 -146 477 449 ;
124
+ C 162 ; WX 600 ; N cent ; B 121 -49 605 614 ;
125
+ C 163 ; WX 600 ; N sterling ; B 106 -28 650 611 ;
126
+ C -1 ; WX 600 ; N fraction ; B 22 -60 708 661 ;
127
+ C 165 ; WX 600 ; N yen ; B 98 0 710 562 ;
128
+ C 131 ; WX 600 ; N florin ; B -57 -131 702 616 ;
129
+ C 167 ; WX 600 ; N section ; B 74 -70 620 580 ;
130
+ C 164 ; WX 600 ; N currency ; B 77 49 644 517 ;
131
+ C 39 ; WX 600 ; N quotesingle ; B 303 277 493 562 ;
132
+ C 147 ; WX 600 ; N quotedblleft ; B 190 277 594 562 ;
133
+ C 170 ; WX 600 ; N guillemotleft ; B 62 70 639 446 ;
134
+ C 139 ; WX 600 ; N guilsinglleft ; B 195 70 545 446 ;
135
+ C 155 ; WX 600 ; N guilsinglright ; B 165 70 514 446 ;
136
+ C -1 ; WX 600 ; N fi ; B 12 0 644 626 ;
137
+ C -1 ; WX 600 ; N fl ; B 12 0 644 626 ;
138
+ C 150 ; WX 600 ; N endash ; B 108 203 602 313 ;
139
+ C 134 ; WX 600 ; N dagger ; B 175 -70 586 580 ;
140
+ C 135 ; WX 600 ; N daggerdbl ; B 121 -70 587 580 ;
141
+ C 183 ; WX 600 ; N periodcentered ; B 248 165 461 351 ;
142
+ C 182 ; WX 600 ; N paragraph ; B 61 -70 700 580 ;
143
+ C 149 ; WX 600 ; N bullet ; B 196 132 523 430 ;
144
+ C 130 ; WX 600 ; N quotesinglbase ; B 144 -142 458 143 ;
145
+ C 132 ; WX 600 ; N quotedblbase ; B 34 -142 560 143 ;
146
+ C 148 ; WX 600 ; N quotedblright ; B 119 277 645 562 ;
147
+ C 187 ; WX 600 ; N guillemotright ; B 71 70 647 446 ;
148
+ C 133 ; WX 600 ; N ellipsis ; B 35 -15 587 116 ;
149
+ C 137 ; WX 600 ; N perthousand ; B -45 -15 743 616 ;
150
+ C 191 ; WX 600 ; N questiondown ; B 100 -146 509 449 ;
151
+ C 96 ; WX 600 ; N grave ; B 272 508 503 661 ;
152
+ C 180 ; WX 600 ; N acute ; B 312 508 609 661 ;
153
+ C 136 ; WX 600 ; N circumflex ; B 212 483 607 657 ;
154
+ C 152 ; WX 600 ; N tilde ; B 199 493 643 636 ;
155
+ C 175 ; WX 600 ; N macron ; B 195 505 637 585 ;
156
+ C -1 ; WX 600 ; N breve ; B 217 468 652 631 ;
157
+ C -1 ; WX 600 ; N dotaccent ; B 348 498 493 638 ;
158
+ C 168 ; WX 600 ; N dieresis ; B 246 498 595 638 ;
159
+ C -1 ; WX 600 ; N ring ; B 319 481 528 678 ;
160
+ C 184 ; WX 600 ; N cedilla ; B 168 -206 368 0 ;
161
+ C -1 ; WX 600 ; N hungarumlaut ; B 171 488 729 661 ;
162
+ C -1 ; WX 600 ; N ogonek ; B 143 -199 367 0 ;
163
+ C -1 ; WX 600 ; N caron ; B 238 493 633 667 ;
164
+ C 151 ; WX 600 ; N emdash ; B 33 203 677 313 ;
165
+ C 198 ; WX 600 ; N AE ; B -29 0 708 562 ;
166
+ C 170 ; WX 600 ; N ordfeminine ; B 188 196 526 580 ;
167
+ C -1 ; WX 600 ; N Lslash ; B 39 0 636 562 ;
168
+ C 216 ; WX 600 ; N Oslash ; B 48 -22 673 584 ;
169
+ C 140 ; WX 600 ; N OE ; B 26 0 701 562 ;
170
+ C 186 ; WX 600 ; N ordmasculine ; B 188 196 543 580 ;
171
+ C 230 ; WX 600 ; N ae ; B 21 -15 652 454 ;
172
+ C -1 ; WX 600 ; N dotlessi ; B 77 0 546 439 ;
173
+ C -1 ; WX 600 ; N lslash ; B 77 0 587 626 ;
174
+ C 248 ; WX 600 ; N oslash ; B 54 -24 638 463 ;
175
+ C 156 ; WX 600 ; N oe ; B 18 -15 662 454 ;
176
+ C 223 ; WX 600 ; N germandbls ; B 22 -15 629 626 ;
177
+ C 207 ; WX 600 ; N Idieresis ; B 77 0 643 761 ;
178
+ C 233 ; WX 600 ; N eacute ; B 81 -15 609 661 ;
179
+ C -1 ; WX 600 ; N abreve ; B 61 -15 658 661 ;
180
+ C -1 ; WX 600 ; N uhungarumlaut ; B 70 -15 769 661 ;
181
+ C -1 ; WX 600 ; N ecaron ; B 81 -15 633 667 ;
182
+ C 159 ; WX 600 ; N Ydieresis ; B 109 0 709 761 ;
183
+ C 247 ; WX 600 ; N divide ; B 114 16 596 500 ;
184
+ C 221 ; WX 600 ; N Yacute ; B 109 0 709 784 ;
185
+ C 194 ; WX 600 ; N Acircumflex ; B -9 0 632 780 ;
186
+ C 225 ; WX 600 ; N aacute ; B 61 -15 609 661 ;
187
+ C 219 ; WX 600 ; N Ucircumflex ; B 101 -18 716 780 ;
188
+ C 253 ; WX 600 ; N yacute ; B -21 -142 695 661 ;
189
+ C -1 ; WX 600 ; N scommaaccent ; B 66 -250 608 459 ;
190
+ C 234 ; WX 600 ; N ecircumflex ; B 81 -15 607 657 ;
191
+ C -1 ; WX 600 ; N Uring ; B 101 -18 716 801 ;
192
+ C 220 ; WX 600 ; N Udieresis ; B 101 -18 716 761 ;
193
+ C -1 ; WX 600 ; N aogonek ; B 61 -199 593 454 ;
194
+ C 218 ; WX 600 ; N Uacute ; B 101 -18 716 784 ;
195
+ C -1 ; WX 600 ; N uogonek ; B 70 -199 592 439 ;
196
+ C 203 ; WX 600 ; N Edieresis ; B 25 0 670 761 ;
197
+ C -1 ; WX 600 ; N Dcroat ; B 30 0 664 562 ;
198
+ C -1 ; WX 600 ; N commaaccent ; B 151 -250 385 -57 ;
199
+ C 169 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
200
+ C -1 ; WX 600 ; N Emacron ; B 25 0 670 708 ;
201
+ C -1 ; WX 600 ; N ccaron ; B 81 -15 633 667 ;
202
+ C 229 ; WX 600 ; N aring ; B 61 -15 593 678 ;
203
+ C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 730 562 ;
204
+ C -1 ; WX 600 ; N lacute ; B 77 0 639 801 ;
205
+ C 224 ; WX 600 ; N agrave ; B 61 -15 593 661 ;
206
+ C -1 ; WX 600 ; N Tcommaaccent ; B 86 -250 679 562 ;
207
+ C -1 ; WX 600 ; N Cacute ; B 74 -18 675 784 ;
208
+ C 227 ; WX 600 ; N atilde ; B 61 -15 643 636 ;
209
+ C -1 ; WX 600 ; N Edotaccent ; B 25 0 670 761 ;
210
+ C 154 ; WX 600 ; N scaron ; B 66 -17 633 667 ;
211
+ C -1 ; WX 600 ; N scedilla ; B 66 -206 608 459 ;
212
+ C 237 ; WX 600 ; N iacute ; B 77 0 609 661 ;
213
+ C -1 ; WX 600 ; N lozenge ; B 145 0 614 740 ;
214
+ C -1 ; WX 600 ; N Rcaron ; B 24 0 659 790 ;
215
+ C -1 ; WX 600 ; N Gcommaaccent ; B 74 -250 675 580 ;
216
+ C 251 ; WX 600 ; N ucircumflex ; B 70 -15 597 657 ;
217
+ C 226 ; WX 600 ; N acircumflex ; B 61 -15 607 657 ;
218
+ C -1 ; WX 600 ; N Amacron ; B -9 0 633 708 ;
219
+ C -1 ; WX 600 ; N rcaron ; B 47 0 655 667 ;
220
+ C 231 ; WX 600 ; N ccedilla ; B 81 -206 631 459 ;
221
+ C -1 ; WX 600 ; N Zdotaccent ; B 62 0 637 761 ;
222
+ C 222 ; WX 600 ; N Thorn ; B 48 0 620 562 ;
223
+ C -1 ; WX 600 ; N Omacron ; B 74 -18 663 708 ;
224
+ C -1 ; WX 600 ; N Racute ; B 24 0 665 784 ;
225
+ C -1 ; WX 600 ; N Sacute ; B 54 -22 673 784 ;
226
+ C -1 ; WX 600 ; N dcaron ; B 60 -15 861 626 ;
227
+ C -1 ; WX 600 ; N Umacron ; B 101 -18 716 708 ;
228
+ C -1 ; WX 600 ; N uring ; B 70 -15 592 678 ;
229
+ C 179 ; WX 600 ; N threesuperior ; B 193 222 526 616 ;
230
+ C 210 ; WX 600 ; N Ograve ; B 74 -18 645 784 ;
231
+ C 192 ; WX 600 ; N Agrave ; B -9 0 632 784 ;
232
+ C -1 ; WX 600 ; N Abreve ; B -9 0 684 784 ;
233
+ C 215 ; WX 600 ; N multiply ; B 104 39 606 478 ;
234
+ C 250 ; WX 600 ; N uacute ; B 70 -15 599 661 ;
235
+ C -1 ; WX 600 ; N Tcaron ; B 86 0 679 790 ;
236
+ C -1 ; WX 600 ; N partialdiff ; B 91 -38 627 728 ;
237
+ C 255 ; WX 600 ; N ydieresis ; B -21 -142 695 638 ;
238
+ C -1 ; WX 600 ; N Nacute ; B 8 -12 730 784 ;
239
+ C 238 ; WX 600 ; N icircumflex ; B 77 0 577 657 ;
240
+ C 202 ; WX 600 ; N Ecircumflex ; B 25 0 670 780 ;
241
+ C 228 ; WX 600 ; N adieresis ; B 61 -15 595 638 ;
242
+ C 235 ; WX 600 ; N edieresis ; B 81 -15 605 638 ;
243
+ C -1 ; WX 600 ; N cacute ; B 81 -15 649 661 ;
244
+ C -1 ; WX 600 ; N nacute ; B 18 0 639 661 ;
245
+ C -1 ; WX 600 ; N umacron ; B 70 -15 637 585 ;
246
+ C -1 ; WX 600 ; N Ncaron ; B 8 -12 730 790 ;
247
+ C 205 ; WX 600 ; N Iacute ; B 77 0 643 784 ;
248
+ C 177 ; WX 600 ; N plusminus ; B 76 24 614 515 ;
249
+ C 166 ; WX 600 ; N brokenbar ; B 217 -175 489 675 ;
250
+ C 174 ; WX 600 ; N registered ; B 53 -18 667 580 ;
251
+ C -1 ; WX 600 ; N Gbreve ; B 74 -18 684 784 ;
252
+ C -1 ; WX 600 ; N Idotaccent ; B 77 0 643 761 ;
253
+ C -1 ; WX 600 ; N summation ; B 15 -10 672 706 ;
254
+ C 200 ; WX 600 ; N Egrave ; B 25 0 670 784 ;
255
+ C -1 ; WX 600 ; N racute ; B 47 0 655 661 ;
256
+ C -1 ; WX 600 ; N omacron ; B 71 -15 637 585 ;
257
+ C -1 ; WX 600 ; N Zacute ; B 62 0 665 784 ;
258
+ C 142 ; WX 600 ; N Zcaron ; B 62 0 659 790 ;
259
+ C -1 ; WX 600 ; N greaterequal ; B 26 0 627 696 ;
260
+ C 208 ; WX 600 ; N Eth ; B 30 0 664 562 ;
261
+ C 199 ; WX 600 ; N Ccedilla ; B 74 -206 675 580 ;
262
+ C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 546 626 ;
263
+ C -1 ; WX 600 ; N tcaron ; B 118 -15 627 703 ;
264
+ C -1 ; WX 600 ; N eogonek ; B 81 -199 605 454 ;
265
+ C -1 ; WX 600 ; N Uogonek ; B 101 -199 716 562 ;
266
+ C 193 ; WX 600 ; N Aacute ; B -9 0 655 784 ;
267
+ C 196 ; WX 600 ; N Adieresis ; B -9 0 632 761 ;
268
+ C 232 ; WX 600 ; N egrave ; B 81 -15 605 661 ;
269
+ C -1 ; WX 600 ; N zacute ; B 81 0 614 661 ;
270
+ C -1 ; WX 600 ; N iogonek ; B 77 -199 546 658 ;
271
+ C 211 ; WX 600 ; N Oacute ; B 74 -18 645 784 ;
272
+ C 243 ; WX 600 ; N oacute ; B 71 -15 649 661 ;
273
+ C -1 ; WX 600 ; N amacron ; B 61 -15 637 585 ;
274
+ C -1 ; WX 600 ; N sacute ; B 66 -17 609 661 ;
275
+ C 239 ; WX 600 ; N idieresis ; B 77 0 561 618 ;
276
+ C 212 ; WX 600 ; N Ocircumflex ; B 74 -18 645 780 ;
277
+ C 217 ; WX 600 ; N Ugrave ; B 101 -18 716 784 ;
278
+ C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
279
+ C 254 ; WX 600 ; N thorn ; B -32 -142 622 626 ;
280
+ C 178 ; WX 600 ; N twosuperior ; B 191 230 542 616 ;
281
+ C 214 ; WX 600 ; N Odieresis ; B 74 -18 645 761 ;
282
+ C 181 ; WX 600 ; N mu ; B 49 -142 592 439 ;
283
+ C 236 ; WX 600 ; N igrave ; B 77 0 546 661 ;
284
+ C -1 ; WX 600 ; N ohungarumlaut ; B 71 -15 809 661 ;
285
+ C -1 ; WX 600 ; N Eogonek ; B 25 -199 670 562 ;
286
+ C -1 ; WX 600 ; N dcroat ; B 60 -15 712 626 ;
287
+ C 190 ; WX 600 ; N threequarters ; B 8 -60 699 661 ;
288
+ C -1 ; WX 600 ; N Scedilla ; B 54 -206 673 582 ;
289
+ C -1 ; WX 600 ; N lcaron ; B 77 0 731 626 ;
290
+ C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 692 562 ;
291
+ C -1 ; WX 600 ; N Lacute ; B 39 0 636 784 ;
292
+ C 153 ; WX 600 ; N trademark ; B 86 230 869 562 ;
293
+ C -1 ; WX 600 ; N edotaccent ; B 81 -15 605 638 ;
294
+ C 204 ; WX 600 ; N Igrave ; B 77 0 643 784 ;
295
+ C -1 ; WX 600 ; N Imacron ; B 77 0 663 708 ;
296
+ C -1 ; WX 600 ; N Lcaron ; B 39 0 757 562 ;
297
+ C 189 ; WX 600 ; N onehalf ; B 22 -60 716 661 ;
298
+ C -1 ; WX 600 ; N lessequal ; B 26 0 671 696 ;
299
+ C 244 ; WX 600 ; N ocircumflex ; B 71 -15 622 657 ;
300
+ C 241 ; WX 600 ; N ntilde ; B 18 0 643 636 ;
301
+ C -1 ; WX 600 ; N Uhungarumlaut ; B 101 -18 805 784 ;
302
+ C 201 ; WX 600 ; N Eacute ; B 25 0 670 784 ;
303
+ C -1 ; WX 600 ; N emacron ; B 81 -15 637 585 ;
304
+ C -1 ; WX 600 ; N gbreve ; B 40 -146 674 661 ;
305
+ C 188 ; WX 600 ; N onequarter ; B 13 -60 707 661 ;
306
+ C 138 ; WX 600 ; N Scaron ; B 54 -22 689 790 ;
307
+ C -1 ; WX 600 ; N Scommaaccent ; B 54 -250 673 582 ;
308
+ C -1 ; WX 600 ; N Ohungarumlaut ; B 74 -18 795 784 ;
309
+ C 176 ; WX 600 ; N degree ; B 173 243 570 616 ;
310
+ C 242 ; WX 600 ; N ograve ; B 71 -15 622 661 ;
311
+ C -1 ; WX 600 ; N Ccaron ; B 74 -18 689 790 ;
312
+ C 249 ; WX 600 ; N ugrave ; B 70 -15 592 661 ;
313
+ C -1 ; WX 600 ; N radical ; B 67 -104 635 778 ;
314
+ C -1 ; WX 600 ; N Dcaron ; B 30 0 664 790 ;
315
+ C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 655 454 ;
316
+ C 209 ; WX 600 ; N Ntilde ; B 8 -12 730 759 ;
317
+ C 245 ; WX 600 ; N otilde ; B 71 -15 643 636 ;
318
+ C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 617 562 ;
319
+ C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 636 562 ;
320
+ C 195 ; WX 600 ; N Atilde ; B -9 0 669 759 ;
321
+ C -1 ; WX 600 ; N Aogonek ; B -9 -199 632 562 ;
322
+ C 197 ; WX 600 ; N Aring ; B -9 0 632 801 ;
323
+ C 213 ; WX 600 ; N Otilde ; B 74 -18 669 759 ;
324
+ C -1 ; WX 600 ; N zdotaccent ; B 81 0 614 638 ;
325
+ C -1 ; WX 600 ; N Ecaron ; B 25 0 670 790 ;
326
+ C -1 ; WX 600 ; N Iogonek ; B 77 -199 643 562 ;
327
+ C -1 ; WX 600 ; N kcommaaccent ; B 33 -250 643 626 ;
328
+ C -1 ; WX 600 ; N minus ; B 114 203 596 313 ;
329
+ C 206 ; WX 600 ; N Icircumflex ; B 77 0 643 780 ;
330
+ C -1 ; WX 600 ; N ncaron ; B 18 0 633 667 ;
331
+ C -1 ; WX 600 ; N tcommaaccent ; B 118 -250 567 562 ;
332
+ C 172 ; WX 600 ; N logicalnot ; B 135 103 617 413 ;
333
+ C 246 ; WX 600 ; N odieresis ; B 71 -15 622 638 ;
334
+ C 252 ; WX 600 ; N udieresis ; B 70 -15 595 638 ;
335
+ C -1 ; WX 600 ; N notequal ; B 30 -47 626 563 ;
336
+ C -1 ; WX 600 ; N gcommaaccent ; B 40 -146 674 714 ;
337
+ C 240 ; WX 600 ; N eth ; B 93 -27 661 626 ;
338
+ C 158 ; WX 600 ; N zcaron ; B 81 0 643 667 ;
339
+ C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 615 454 ;
340
+ C 185 ; WX 600 ; N onesuperior ; B 212 230 514 616 ;
341
+ C -1 ; WX 600 ; N imacron ; B 77 0 575 585 ;
342
+ C 128 ; WX 600 ; N Euro ; B 0 0 0 0 ;
343
+ EndCharMetrics
344
+ EndFontMetrics
lib/dompdf/lib/fonts/Courier-Oblique.afm ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
3
+ Comment Creation Date: Thu May 0:00:00 17:37:52 1997
4
+ Comment UniqueID 43051
5
+ Comment VMusage 16248 75829
6
+ FontName Courier-Oblique
7
+ FullName Courier Oblique
8
+ FamilyName Courier
9
+ Weight Medium
10
+ ItalicAngle -12
11
+ IsFixedPitch true
12
+ CharacterSet ExtendedRoman
13
+ FontBBox -27 -250 849 805
14
+ UnderlinePosition -100
15
+ UnderlineThickness 50
16
+ Version 003.000
17
+ Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
18
+ EncodingScheme WinAnsiEncoding
19
+ CapHeight 562
20
+ XHeight 426
21
+ Ascender 629
22
+ Descender -157
23
+ StdHW 51
24
+ StdVW 51
25
+ StartCharMetrics 317
26
+ C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
27
+ C 160 ; WX 600 ; N space ; B 0 0 0 0 ;
28
+ C 33 ; WX 600 ; N exclam ; B 243 -15 464 572 ;
29
+ C 34 ; WX 600 ; N quotedbl ; B 273 328 532 562 ;
30
+ C 35 ; WX 600 ; N numbersign ; B 133 -32 596 639 ;
31
+ C 36 ; WX 600 ; N dollar ; B 108 -126 596 662 ;
32
+ C 37 ; WX 600 ; N percent ; B 134 -15 599 622 ;
33
+ C 38 ; WX 600 ; N ampersand ; B 87 -15 580 543 ;
34
+ C 146 ; WX 600 ; N quoteright ; B 283 328 495 562 ;
35
+ C 40 ; WX 600 ; N parenleft ; B 313 -108 572 622 ;
36
+ C 41 ; WX 600 ; N parenright ; B 137 -108 396 622 ;
37
+ C 42 ; WX 600 ; N asterisk ; B 212 257 580 607 ;
38
+ C 43 ; WX 600 ; N plus ; B 129 44 580 470 ;
39
+ C 44 ; WX 600 ; N comma ; B 157 -112 370 122 ;
40
+ C 45 ; WX 600 ; N hyphen ; B 152 231 558 285 ;
41
+ C 173 ; WX 600 ; N hyphen ; B 152 231 558 285 ;
42
+ C 46 ; WX 600 ; N period ; B 238 -15 382 109 ;
43
+ C 47 ; WX 600 ; N slash ; B 112 -80 604 629 ;
44
+ C 48 ; WX 600 ; N zero ; B 154 -15 575 622 ;
45
+ C 49 ; WX 600 ; N one ; B 98 0 515 622 ;
46
+ C 50 ; WX 600 ; N two ; B 70 0 568 622 ;
47
+ C 51 ; WX 600 ; N three ; B 82 -15 538 622 ;
48
+ C 52 ; WX 600 ; N four ; B 108 0 541 622 ;
49
+ C 53 ; WX 600 ; N five ; B 99 -15 589 607 ;
50
+ C 54 ; WX 600 ; N six ; B 155 -15 629 622 ;
51
+ C 55 ; WX 600 ; N seven ; B 182 0 612 607 ;
52
+ C 56 ; WX 600 ; N eight ; B 132 -15 588 622 ;
53
+ C 57 ; WX 600 ; N nine ; B 93 -15 574 622 ;
54
+ C 58 ; WX 600 ; N colon ; B 238 -15 441 385 ;
55
+ C 59 ; WX 600 ; N semicolon ; B 157 -112 441 385 ;
56
+ C 60 ; WX 600 ; N less ; B 96 42 610 472 ;
57
+ C 61 ; WX 600 ; N equal ; B 109 138 600 376 ;
58
+ C 62 ; WX 600 ; N greater ; B 85 42 599 472 ;
59
+ C 63 ; WX 600 ; N question ; B 222 -15 583 572 ;
60
+ C 64 ; WX 600 ; N at ; B 127 -15 582 622 ;
61
+ C 65 ; WX 600 ; N A ; B 3 0 607 562 ;
62
+ C 66 ; WX 600 ; N B ; B 43 0 616 562 ;
63
+ C 67 ; WX 600 ; N C ; B 93 -18 655 580 ;
64
+ C 68 ; WX 600 ; N D ; B 43 0 645 562 ;
65
+ C 69 ; WX 600 ; N E ; B 53 0 660 562 ;
66
+ C 70 ; WX 600 ; N F ; B 53 0 660 562 ;
67
+ C 71 ; WX 600 ; N G ; B 83 -18 645 580 ;
68
+ C 72 ; WX 600 ; N H ; B 32 0 687 562 ;
69
+ C 73 ; WX 600 ; N I ; B 96 0 623 562 ;
70
+ C 74 ; WX 600 ; N J ; B 52 -18 685 562 ;
71
+ C 75 ; WX 600 ; N K ; B 38 0 671 562 ;
72
+ C 76 ; WX 600 ; N L ; B 47 0 607 562 ;
73
+ C 77 ; WX 600 ; N M ; B 4 0 715 562 ;
74
+ C 78 ; WX 600 ; N N ; B 7 -13 712 562 ;
75
+ C 79 ; WX 600 ; N O ; B 94 -18 625 580 ;
76
+ C 80 ; WX 600 ; N P ; B 79 0 644 562 ;
77
+ C 81 ; WX 600 ; N Q ; B 95 -138 625 580 ;
78
+ C 82 ; WX 600 ; N R ; B 38 0 598 562 ;
79
+ C 83 ; WX 600 ; N S ; B 76 -20 650 580 ;
80
+ C 84 ; WX 600 ; N T ; B 108 0 665 562 ;
81
+ C 85 ; WX 600 ; N U ; B 125 -18 702 562 ;
82
+ C 86 ; WX 600 ; N V ; B 105 -13 723 562 ;
83
+ C 87 ; WX 600 ; N W ; B 106 -13 722 562 ;
84
+ C 88 ; WX 600 ; N X ; B 23 0 675 562 ;
85
+ C 89 ; WX 600 ; N Y ; B 133 0 695 562 ;
86
+ C 90 ; WX 600 ; N Z ; B 86 0 610 562 ;
87
+ C 91 ; WX 600 ; N bracketleft ; B 246 -108 574 622 ;
88
+ C 92 ; WX 600 ; N backslash ; B 249 -80 468 629 ;
89
+ C 93 ; WX 600 ; N bracketright ; B 135 -108 463 622 ;
90
+ C 94 ; WX 600 ; N asciicircum ; B 175 354 587 622 ;
91
+ C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ;
92
+ C 145 ; WX 600 ; N quoteleft ; B 343 328 457 562 ;
93
+ C 97 ; WX 600 ; N a ; B 76 -15 569 441 ;
94
+ C 98 ; WX 600 ; N b ; B 29 -15 625 629 ;
95
+ C 99 ; WX 600 ; N c ; B 106 -15 608 441 ;
96
+ C 100 ; WX 600 ; N d ; B 85 -15 640 629 ;
97
+ C 101 ; WX 600 ; N e ; B 106 -15 598 441 ;
98
+ C 102 ; WX 600 ; N f ; B 114 0 662 629 ; L i fi ; L l fl ;
99
+ C 103 ; WX 600 ; N g ; B 61 -157 657 441 ;
100
+ C 104 ; WX 600 ; N h ; B 33 0 592 629 ;
101
+ C 105 ; WX 600 ; N i ; B 95 0 515 657 ;
102
+ C 106 ; WX 600 ; N j ; B 52 -157 550 657 ;
103
+ C 107 ; WX 600 ; N k ; B 58 0 633 629 ;
104
+ C 108 ; WX 600 ; N l ; B 95 0 515 629 ;
105
+ C 109 ; WX 600 ; N m ; B -5 0 615 441 ;
106
+ C 110 ; WX 600 ; N n ; B 26 0 585 441 ;
107
+ C 111 ; WX 600 ; N o ; B 102 -15 588 441 ;
108
+ C 112 ; WX 600 ; N p ; B -24 -157 605 441 ;
109
+ C 113 ; WX 600 ; N q ; B 85 -157 682 441 ;
110
+ C 114 ; WX 600 ; N r ; B 60 0 636 441 ;
111
+ C 115 ; WX 600 ; N s ; B 78 -15 584 441 ;
112
+ C 116 ; WX 600 ; N t ; B 167 -15 561 561 ;
113
+ C 117 ; WX 600 ; N u ; B 101 -15 572 426 ;
114
+ C 118 ; WX 600 ; N v ; B 90 -10 681 426 ;
115
+ C 119 ; WX 600 ; N w ; B 76 -10 695 426 ;
116
+ C 120 ; WX 600 ; N x ; B 20 0 655 426 ;
117
+ C 121 ; WX 600 ; N y ; B -4 -157 683 426 ;
118
+ C 122 ; WX 600 ; N z ; B 99 0 593 426 ;
119
+ C 123 ; WX 600 ; N braceleft ; B 233 -108 569 622 ;
120
+ C 124 ; WX 600 ; N bar ; B 222 -250 485 750 ;
121
+ C 125 ; WX 600 ; N braceright ; B 140 -108 477 622 ;
122
+ C 126 ; WX 600 ; N asciitilde ; B 116 197 600 320 ;
123
+ C 161 ; WX 600 ; N exclamdown ; B 225 -157 445 430 ;
124
+ C 162 ; WX 600 ; N cent ; B 151 -49 588 614 ;
125
+ C 163 ; WX 600 ; N sterling ; B 124 -21 621 611 ;
126
+ C -1 ; WX 600 ; N fraction ; B 84 -57 646 665 ;
127
+ C 165 ; WX 600 ; N yen ; B 120 0 693 562 ;
128
+ C 131 ; WX 600 ; N florin ; B -26 -143 671 622 ;
129
+ C 167 ; WX 600 ; N section ; B 104 -78 590 580 ;
130
+ C 164 ; WX 600 ; N currency ; B 94 58 628 506 ;
131
+ C 39 ; WX 600 ; N quotesingle ; B 345 328 460 562 ;
132
+ C 147 ; WX 600 ; N quotedblleft ; B 262 328 541 562 ;
133
+ C 170 ; WX 600 ; N guillemotleft ; B 92 70 652 446 ;
134
+ C 139 ; WX 600 ; N guilsinglleft ; B 204 70 540 446 ;
135
+ C 155 ; WX 600 ; N guilsinglright ; B 170 70 506 446 ;
136
+ C -1 ; WX 600 ; N fi ; B 3 0 619 629 ;
137
+ C -1 ; WX 600 ; N fl ; B 3 0 619 629 ;
138
+ C 150 ; WX 600 ; N endash ; B 124 231 586 285 ;
139
+ C 134 ; WX 600 ; N dagger ; B 217 -78 546 580 ;
140
+ C 135 ; WX 600 ; N daggerdbl ; B 163 -78 546 580 ;
141
+ C 183 ; WX 600 ; N periodcentered ; B 275 189 434 327 ;
142
+ C 182 ; WX 600 ; N paragraph ; B 100 -78 630 562 ;
143
+ C 149 ; WX 600 ; N bullet ; B 224 130 485 383 ;
144
+ C 130 ; WX 600 ; N quotesinglbase ; B 185 -134 397 100 ;
145
+ C 132 ; WX 600 ; N quotedblbase ; B 115 -134 478 100 ;
146
+ C 148 ; WX 600 ; N quotedblright ; B 213 328 576 562 ;
147
+ C 187 ; WX 600 ; N guillemotright ; B 58 70 618 446 ;
148
+ C 133 ; WX 600 ; N ellipsis ; B 46 -15 575 111 ;
149
+ C 137 ; WX 600 ; N perthousand ; B 59 -15 627 622 ;
150
+ C 191 ; WX 600 ; N questiondown ; B 105 -157 466 430 ;
151
+ C 96 ; WX 600 ; N grave ; B 294 497 484 672 ;
152
+ C 180 ; WX 600 ; N acute ; B 348 497 612 672 ;
153
+ C 136 ; WX 600 ; N circumflex ; B 229 477 581 654 ;
154
+ C 152 ; WX 600 ; N tilde ; B 212 489 629 606 ;
155
+ C 175 ; WX 600 ; N macron ; B 232 525 600 565 ;
156
+ C -1 ; WX 600 ; N breve ; B 279 501 576 609 ;
157
+ C -1 ; WX 600 ; N dotaccent ; B 373 537 478 640 ;
158
+ C 168 ; WX 600 ; N dieresis ; B 272 537 579 640 ;
159
+ C -1 ; WX 600 ; N ring ; B 332 463 500 627 ;
160
+ C 184 ; WX 600 ; N cedilla ; B 197 -151 344 10 ;
161
+ C -1 ; WX 600 ; N hungarumlaut ; B 239 497 683 672 ;
162
+ C -1 ; WX 600 ; N ogonek ; B 189 -172 377 4 ;
163
+ C -1 ; WX 600 ; N caron ; B 262 492 614 669 ;
164
+ C 151 ; WX 600 ; N emdash ; B 49 231 661 285 ;
165
+ C 198 ; WX 600 ; N AE ; B 3 0 655 562 ;
166
+ C 170 ; WX 600 ; N ordfeminine ; B 209 249 512 580 ;
167
+ C -1 ; WX 600 ; N Lslash ; B 47 0 607 562 ;
168
+ C 216 ; WX 600 ; N Oslash ; B 94 -80 625 629 ;
169
+ C 140 ; WX 600 ; N OE ; B 59 0 672 562 ;
170
+ C 186 ; WX 600 ; N ordmasculine ; B 210 249 535 580 ;
171
+ C 230 ; WX 600 ; N ae ; B 41 -15 626 441 ;
172
+ C -1 ; WX 600 ; N dotlessi ; B 95 0 515 426 ;
173
+ C -1 ; WX 600 ; N lslash ; B 95 0 587 629 ;
174
+ C 248 ; WX 600 ; N oslash ; B 102 -80 588 506 ;
175
+ C 156 ; WX 600 ; N oe ; B 54 -15 615 441 ;
176
+ C 223 ; WX 600 ; N germandbls ; B 48 -15 617 629 ;
177
+ C 207 ; WX 600 ; N Idieresis ; B 96 0 623 753 ;
178
+ C 233 ; WX 600 ; N eacute ; B 106 -15 612 672 ;
179
+ C -1 ; WX 600 ; N abreve ; B 76 -15 576 609 ;
180
+ C -1 ; WX 600 ; N uhungarumlaut ; B 101 -15 723 672 ;
181
+ C -1 ; WX 600 ; N ecaron ; B 106 -15 614 669 ;
182
+ C 159 ; WX 600 ; N Ydieresis ; B 133 0 695 753 ;
183
+ C 247 ; WX 600 ; N divide ; B 136 48 573 467 ;
184
+ C 221 ; WX 600 ; N Yacute ; B 133 0 695 805 ;
185
+ C 194 ; WX 600 ; N Acircumflex ; B 3 0 607 787 ;
186
+ C 225 ; WX 600 ; N aacute ; B 76 -15 612 672 ;
187
+ C 219 ; WX 600 ; N Ucircumflex ; B 125 -18 702 787 ;
188
+ C 253 ; WX 600 ; N yacute ; B -4 -157 683 672 ;
189
+ C -1 ; WX 600 ; N scommaaccent ; B 78 -250 584 441 ;
190
+ C 234 ; WX 600 ; N ecircumflex ; B 106 -15 598 654 ;
191
+ C -1 ; WX 600 ; N Uring ; B 125 -18 702 760 ;
192
+ C 220 ; WX 600 ; N Udieresis ; B 125 -18 702 753 ;
193
+ C -1 ; WX 600 ; N aogonek ; B 76 -172 569 441 ;
194
+ C 218 ; WX 600 ; N Uacute ; B 125 -18 702 805 ;
195
+ C -1 ; WX 600 ; N uogonek ; B 101 -172 572 426 ;
196
+ C 203 ; WX 600 ; N Edieresis ; B 53 0 660 753 ;
197
+ C -1 ; WX 600 ; N Dcroat ; B 43 0 645 562 ;
198
+ C -1 ; WX 600 ; N commaaccent ; B 145 -250 323 -58 ;
199
+ C 169 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
200
+ C -1 ; WX 600 ; N Emacron ; B 53 0 660 698 ;
201
+ C -1 ; WX 600 ; N ccaron ; B 106 -15 614 669 ;
202
+ C 229 ; WX 600 ; N aring ; B 76 -15 569 627 ;
203
+ C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 712 562 ;
204
+ C -1 ; WX 600 ; N lacute ; B 95 0 640 805 ;
205
+ C 224 ; WX 600 ; N agrave ; B 76 -15 569 672 ;
206
+ C -1 ; WX 600 ; N Tcommaaccent ; B 108 -250 665 562 ;
207
+ C -1 ; WX 600 ; N Cacute ; B 93 -18 655 805 ;
208
+ C 227 ; WX 600 ; N atilde ; B 76 -15 629 606 ;
209
+ C -1 ; WX 600 ; N Edotaccent ; B 53 0 660 753 ;
210
+ C 154 ; WX 600 ; N scaron ; B 78 -15 614 669 ;
211
+ C -1 ; WX 600 ; N scedilla ; B 78 -151 584 441 ;
212
+ C 237 ; WX 600 ; N iacute ; B 95 0 612 672 ;
213
+ C -1 ; WX 600 ; N lozenge ; B 94 0 519 706 ;
214
+ C -1 ; WX 600 ; N Rcaron ; B 38 0 642 802 ;
215
+ C -1 ; WX 600 ; N Gcommaaccent ; B 83 -250 645 580 ;
216
+ C 251 ; WX 600 ; N ucircumflex ; B 101 -15 572 654 ;
217
+ C 226 ; WX 600 ; N acircumflex ; B 76 -15 581 654 ;
218
+ C -1 ; WX 600 ; N Amacron ; B 3 0 607 698 ;
219
+ C -1 ; WX 600 ; N rcaron ; B 60 0 636 669 ;
220
+ C 231 ; WX 600 ; N ccedilla ; B 106 -151 614 441 ;
221
+ C -1 ; WX 600 ; N Zdotaccent ; B 86 0 610 753 ;
222
+ C 222 ; WX 600 ; N Thorn ; B 79 0 606 562 ;
223
+ C -1 ; WX 600 ; N Omacron ; B 94 -18 628 698 ;
224
+ C -1 ; WX 600 ; N Racute ; B 38 0 670 805 ;
225
+ C -1 ; WX 600 ; N Sacute ; B 76 -20 650 805 ;
226
+ C -1 ; WX 600 ; N dcaron ; B 85 -15 849 629 ;
227
+ C -1 ; WX 600 ; N Umacron ; B 125 -18 702 698 ;
228
+ C -1 ; WX 600 ; N uring ; B 101 -15 572 627 ;
229
+ C 179 ; WX 600 ; N threesuperior ; B 213 240 501 622 ;
230
+ C 210 ; WX 600 ; N Ograve ; B 94 -18 625 805 ;
231
+ C 192 ; WX 600 ; N Agrave ; B 3 0 607 805 ;
232
+ C -1 ; WX 600 ; N Abreve ; B 3 0 607 732 ;
233
+ C 215 ; WX 600 ; N multiply ; B 103 43 607 470 ;
234
+ C 250 ; WX 600 ; N uacute ; B 101 -15 602 672 ;
235
+ C -1 ; WX 600 ; N Tcaron ; B 108 0 665 802 ;
236
+ C -1 ; WX 600 ; N partialdiff ; B 45 -38 546 710 ;
237
+ C 255 ; WX 600 ; N ydieresis ; B -4 -157 683 620 ;
238
+ C -1 ; WX 600 ; N Nacute ; B 7 -13 712 805 ;
239
+ C 238 ; WX 600 ; N icircumflex ; B 95 0 551 654 ;
240
+ C 202 ; WX 600 ; N Ecircumflex ; B 53 0 660 787 ;
241
+ C 228 ; WX 600 ; N adieresis ; B 76 -15 575 620 ;
242
+ C 235 ; WX 600 ; N edieresis ; B 106 -15 598 620 ;
243
+ C -1 ; WX 600 ; N cacute ; B 106 -15 612 672 ;
244
+ C -1 ; WX 600 ; N nacute ; B 26 0 602 672 ;
245
+ C -1 ; WX 600 ; N umacron ; B 101 -15 600 565 ;
246
+ C -1 ; WX 600 ; N Ncaron ; B 7 -13 712 802 ;
247
+ C 205 ; WX 600 ; N Iacute ; B 96 0 640 805 ;
248
+ C 177 ; WX 600 ; N plusminus ; B 96 44 594 558 ;
249
+ C 166 ; WX 600 ; N brokenbar ; B 238 -175 469 675 ;
250
+ C 174 ; WX 600 ; N registered ; B 53 -18 667 580 ;
251
+ C -1 ; WX 600 ; N Gbreve ; B 83 -18 645 732 ;
252
+ C -1 ; WX 600 ; N Idotaccent ; B 96 0 623 753 ;
253
+ C -1 ; WX 600 ; N summation ; B 15 -10 670 706 ;
254
+ C 200 ; WX 600 ; N Egrave ; B 53 0 660 805 ;
255
+ C -1 ; WX 600 ; N racute ; B 60 0 636 672 ;
256
+ C -1 ; WX 600 ; N omacron ; B 102 -15 600 565 ;
257
+ C -1 ; WX 600 ; N Zacute ; B 86 0 670 805 ;
258
+ C 142 ; WX 600 ; N Zcaron ; B 86 0 642 802 ;
259
+ C -1 ; WX 600 ; N greaterequal ; B 98 0 594 710 ;
260
+ C 208 ; WX 600 ; N Eth ; B 43 0 645 562 ;
261
+ C 199 ; WX 600 ; N Ccedilla ; B 93 -151 658 580 ;
262
+ C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 515 629 ;
263
+ C -1 ; WX 600 ; N tcaron ; B 167 -15 587 717 ;
264
+ C -1 ; WX 600 ; N eogonek ; B 106 -172 598 441 ;
265
+ C -1 ; WX 600 ; N Uogonek ; B 124 -172 702 562 ;
266
+ C 193 ; WX 600 ; N Aacute ; B 3 0 660 805 ;
267
+ C 196 ; WX 600 ; N Adieresis ; B 3 0 607 753 ;
268
+ C 232 ; WX 600 ; N egrave ; B 106 -15 598 672 ;
269
+ C -1 ; WX 600 ; N zacute ; B 99 0 612 672 ;
270
+ C -1 ; WX 600 ; N iogonek ; B 95 -172 515 657 ;
271
+ C 211 ; WX 600 ; N Oacute ; B 94 -18 640 805 ;
272
+ C 243 ; WX 600 ; N oacute ; B 102 -15 612 672 ;
273
+ C -1 ; WX 600 ; N amacron ; B 76 -15 600 565 ;
274
+ C -1 ; WX 600 ; N sacute ; B 78 -15 612 672 ;
275
+ C 239 ; WX 600 ; N idieresis ; B 95 0 545 620 ;
276
+ C 212 ; WX 600 ; N Ocircumflex ; B 94 -18 625 787 ;
277
+ C 217 ; WX 600 ; N Ugrave ; B 125 -18 702 805 ;
278
+ C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
279
+ C 254 ; WX 600 ; N thorn ; B -24 -157 605 629 ;
280
+ C 178 ; WX 600 ; N twosuperior ; B 230 249 535 622 ;
281
+ C 214 ; WX 600 ; N Odieresis ; B 94 -18 625 753 ;
282
+ C 181 ; WX 600 ; N mu ; B 72 -157 572 426 ;
283
+ C 236 ; WX 600 ; N igrave ; B 95 0 515 672 ;
284
+ C -1 ; WX 600 ; N ohungarumlaut ; B 102 -15 723 672 ;
285
+ C -1 ; WX 600 ; N Eogonek ; B 53 -172 660 562 ;
286
+ C -1 ; WX 600 ; N dcroat ; B 85 -15 704 629 ;
287
+ C 190 ; WX 600 ; N threequarters ; B 73 -56 659 666 ;
288
+ C -1 ; WX 600 ; N Scedilla ; B 76 -151 650 580 ;
289
+ C -1 ; WX 600 ; N lcaron ; B 95 0 667 629 ;
290
+ C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 671 562 ;
291
+ C -1 ; WX 600 ; N Lacute ; B 47 0 607 805 ;
292
+ C 153 ; WX 600 ; N trademark ; B 75 263 742 562 ;
293
+ C -1 ; WX 600 ; N edotaccent ; B 106 -15 598 620 ;
294
+ C 204 ; WX 600 ; N Igrave ; B 96 0 623 805 ;
295
+ C -1 ; WX 600 ; N Imacron ; B 96 0 628 698 ;
296
+ C -1 ; WX 600 ; N Lcaron ; B 47 0 632 562 ;
297
+ C 189 ; WX 600 ; N onehalf ; B 65 -57 669 665 ;
298
+ C -1 ; WX 600 ; N lessequal ; B 98 0 645 710 ;
299
+ C 244 ; WX 600 ; N ocircumflex ; B 102 -15 588 654 ;
300
+ C 241 ; WX 600 ; N ntilde ; B 26 0 629 606 ;
301
+ C -1 ; WX 600 ; N Uhungarumlaut ; B 125 -18 761 805 ;
302
+ C 201 ; WX 600 ; N Eacute ; B 53 0 670 805 ;
303
+ C -1 ; WX 600 ; N emacron ; B 106 -15 600 565 ;
304
+ C -1 ; WX 600 ; N gbreve ; B 61 -157 657 609 ;
305
+ C 188 ; WX 600 ; N onequarter ; B 65 -57 674 665 ;
306
+ C 138 ; WX 600 ; N Scaron ; B 76 -20 672 802 ;
307
+ C -1 ; WX 600 ; N Scommaaccent ; B 76 -250 650 580 ;
308
+ C -1 ; WX 600 ; N Ohungarumlaut ; B 94 -18 751 805 ;
309
+ C 176 ; WX 600 ; N degree ; B 214 269 576 622 ;
310
+ C 242 ; WX 600 ; N ograve ; B 102 -15 588 672 ;
311
+ C -1 ; WX 600 ; N Ccaron ; B 93 -18 672 802 ;
312
+ C 249 ; WX 600 ; N ugrave ; B 101 -15 572 672 ;
313
+ C -1 ; WX 600 ; N radical ; B 85 -15 765 792 ;
314
+ C -1 ; WX 600 ; N Dcaron ; B 43 0 645 802 ;
315
+ C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 636 441 ;
316
+ C 209 ; WX 600 ; N Ntilde ; B 7 -13 712 729 ;
317
+ C 245 ; WX 600 ; N otilde ; B 102 -15 629 606 ;
318
+ C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 598 562 ;
319
+ C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 607 562 ;
320
+ C 195 ; WX 600 ; N Atilde ; B 3 0 655 729 ;
321
+ C -1 ; WX 600 ; N Aogonek ; B 3 -172 607 562 ;
322
+ C 197 ; WX 600 ; N Aring ; B 3 0 607 750 ;
323
+ C 213 ; WX 600 ; N Otilde ; B 94 -18 655 729 ;
324
+ C -1 ; WX 600 ; N zdotaccent ; B 99 0 593 620 ;
325
+ C -1 ; WX 600 ; N Ecaron ; B 53 0 660 802 ;
326
+ C -1 ; WX 600 ; N Iogonek ; B 96 -172 623 562 ;
327
+ C -1 ; WX 600 ; N kcommaaccent ; B 58 -250 633 629 ;
328
+ C -1 ; WX 600 ; N minus ; B 129 232 580 283 ;
329
+ C 206 ; WX 600 ; N Icircumflex ; B 96 0 623 787 ;
330
+ C -1 ; WX 600 ; N ncaron ; B 26 0 614 669 ;
331
+ C -1 ; WX 600 ; N tcommaaccent ; B 165 -250 561 561 ;
332
+ C 172 ; WX 600 ; N logicalnot ; B 155 108 591 369 ;
333
+ C 246 ; WX 600 ; N odieresis ; B 102 -15 588 620 ;
334
+ C 252 ; WX 600 ; N udieresis ; B 101 -15 575 620 ;
335
+ C -1 ; WX 600 ; N notequal ; B 43 -16 621 529 ;
336
+ C -1 ; WX 600 ; N gcommaaccent ; B 61 -157 657 708 ;
337
+ C 240 ; WX 600 ; N eth ; B 102 -15 639 629 ;
338
+ C 158 ; WX 600 ; N zcaron ; B 99 0 624 669 ;
339
+ C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 585 441 ;
340
+ C 185 ; WX 600 ; N onesuperior ; B 231 249 491 622 ;
341
+ C -1 ; WX 600 ; N imacron ; B 95 0 543 565 ;
342
+ C 128 ; WX 600 ; N Euro ; B 0 0 0 0 ;
343
+ EndCharMetrics
344
+ EndFontMetrics
lib/dompdf/lib/fonts/Courier.afm ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
3
+ Comment Creation Date: Thu May 1 17:27:09 1997
4
+ Comment UniqueID 43050
5
+ Comment VMusage 39754 50779
6
+ FontName Courier
7
+ FullName Courier
8
+ FamilyName Courier
9
+ Weight Medium
10
+ ItalicAngle 0
11
+ IsFixedPitch true
12
+ CharacterSet ExtendedRoman
13
+ FontBBox -23 -250 715 805
14
+ UnderlinePosition -100
15
+ UnderlineThickness 50
16
+ Version 003.000
17
+ Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
18
+ EncodingScheme WinAnsiEncoding
19
+ CapHeight 562
20
+ XHeight 426
21
+ Ascender 629
22
+ Descender -157
23
+ StdHW 51
24
+ StdVW 51
25
+ StartCharMetrics 317
26
+ C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
27
+ C 160 ; WX 600 ; N space ; B 0 0 0 0 ;
28
+ C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ;
29
+ C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ;
30
+ C 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ;
31
+ C 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ;
32
+ C 37 ; WX 600 ; N percent ; B 81 -15 518 622 ;
33
+ C 38 ; WX 600 ; N ampersand ; B 63 -15 538 543 ;
34
+ C 146 ; WX 600 ; N quoteright ; B 213 328 376 562 ;
35
+ C 40 ; WX 600 ; N parenleft ; B 269 -108 440 622 ;
36
+ C 41 ; WX 600 ; N parenright ; B 160 -108 331 622 ;
37
+ C 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ;
38
+ C 43 ; WX 600 ; N plus ; B 80 44 520 470 ;
39
+ C 44 ; WX 600 ; N comma ; B 181 -112 344 122 ;
40
+ C 45 ; WX 600 ; N hyphen ; B 103 231 497 285 ;
41
+ C 173 ; WX 600 ; N hyphen ; B 103 231 497 285 ;
42
+ C 46 ; WX 600 ; N period ; B 229 -15 371 109 ;
43
+ C 47 ; WX 600 ; N slash ; B 125 -80 475 629 ;
44
+ C 48 ; WX 600 ; N zero ; B 106 -15 494 622 ;
45
+ C 49 ; WX 600 ; N one ; B 96 0 505 622 ;
46
+ C 50 ; WX 600 ; N two ; B 70 0 471 622 ;
47
+ C 51 ; WX 600 ; N three ; B 75 -15 466 622 ;
48
+ C 52 ; WX 600 ; N four ; B 78 0 500 622 ;
49
+ C 53 ; WX 600 ; N five ; B 92 -15 497 607 ;
50
+ C 54 ; WX 600 ; N six ; B 111 -15 497 622 ;
51
+ C 55 ; WX 600 ; N seven ; B 82 0 483 607 ;
52
+ C 56 ; WX 600 ; N eight ; B 102 -15 498 622 ;
53
+ C 57 ; WX 600 ; N nine ; B 96 -15 489 622 ;
54
+ C 58 ; WX 600 ; N colon ; B 229 -15 371 385 ;
55
+ C 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ;
56
+ C 60 ; WX 600 ; N less ; B 41 42 519 472 ;
57
+ C 61 ; WX 600 ; N equal ; B 80 138 520 376 ;
58
+ C 62 ; WX 600 ; N greater ; B 66 42 544 472 ;
59
+ C 63 ; WX 600 ; N question ; B 129 -15 492 572 ;
60
+ C 64 ; WX 600 ; N at ; B 77 -15 533 622 ;
61
+ C 65 ; WX 600 ; N A ; B 3 0 597 562 ;
62
+ C 66 ; WX 600 ; N B ; B 43 0 559 562 ;
63
+ C 67 ; WX 600 ; N C ; B 41 -18 540 580 ;
64
+ C 68 ; WX 600 ; N D ; B 43 0 574 562 ;
65
+ C 69 ; WX 600 ; N E ; B 53 0 550 562 ;
66
+ C 70 ; WX 600 ; N F ; B 53 0 545 562 ;
67
+ C 71 ; WX 600 ; N G ; B 31 -18 575 580 ;
68
+ C 72 ; WX 600 ; N H ; B 32 0 568 562 ;
69
+ C 73 ; WX 600 ; N I ; B 96 0 504 562 ;
70
+ C 74 ; WX 600 ; N J ; B 34 -18 566 562 ;
71
+ C 75 ; WX 600 ; N K ; B 38 0 582 562 ;
72
+ C 76 ; WX 600 ; N L ; B 47 0 554 562 ;
73
+ C 77 ; WX 600 ; N M ; B 4 0 596 562 ;
74
+ C 78 ; WX 600 ; N N ; B 7 -13 593 562 ;
75
+ C 79 ; WX 600 ; N O ; B 43 -18 557 580 ;
76
+ C 80 ; WX 600 ; N P ; B 79 0 558 562 ;
77
+ C 81 ; WX 600 ; N Q ; B 43 -138 557 580 ;
78
+ C 82 ; WX 600 ; N R ; B 38 0 588 562 ;
79
+ C 83 ; WX 600 ; N S ; B 72 -20 529 580 ;
80
+ C 84 ; WX 600 ; N T ; B 38 0 563 562 ;
81
+ C 85 ; WX 600 ; N U ; B 17 -18 583 562 ;
82
+ C 86 ; WX 600 ; N V ; B -4 -13 604 562 ;
83
+ C 87 ; WX 600 ; N W ; B -3 -13 603 562 ;
84
+ C 88 ; WX 600 ; N X ; B 23 0 577 562 ;
85
+ C 89 ; WX 600 ; N Y ; B 24 0 576 562 ;
86
+ C 90 ; WX 600 ; N Z ; B 86 0 514 562 ;
87
+ C 91 ; WX 600 ; N bracketleft ; B 269 -108 442 622 ;
88
+ C 92 ; WX 600 ; N backslash ; B 118 -80 482 629 ;
89
+ C 93 ; WX 600 ; N bracketright ; B 158 -108 331 622 ;
90
+ C 94 ; WX 600 ; N asciicircum ; B 94 354 506 622 ;
91
+ C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
92
+ C 145 ; WX 600 ; N quoteleft ; B 224 328 387 562 ;
93
+ C 97 ; WX 600 ; N a ; B 53 -15 559 441 ;
94
+ C 98 ; WX 600 ; N b ; B 14 -15 575 629 ;
95
+ C 99 ; WX 600 ; N c ; B 66 -15 529 441 ;
96
+ C 100 ; WX 600 ; N d ; B 45 -15 591 629 ;
97
+ C 101 ; WX 600 ; N e ; B 66 -15 548 441 ;
98
+ C 102 ; WX 600 ; N f ; B 114 0 531 629 ; L i fi ; L l fl ;
99
+ C 103 ; WX 600 ; N g ; B 45 -157 566 441 ;
100
+ C 104 ; WX 600 ; N h ; B 18 0 582 629 ;
101
+ C 105 ; WX 600 ; N i ; B 95 0 505 657 ;
102
+ C 106 ; WX 600 ; N j ; B 82 -157 410 657 ;
103
+ C 107 ; WX 600 ; N k ; B 43 0 580 629 ;
104
+ C 108 ; WX 600 ; N l ; B 95 0 505 629 ;
105
+ C 109 ; WX 600 ; N m ; B -5 0 605 441 ;
106
+ C 110 ; WX 600 ; N n ; B 26 0 575 441 ;
107
+ C 111 ; WX 600 ; N o ; B 62 -15 538 441 ;
108
+ C 112 ; WX 600 ; N p ; B 9 -157 555 441 ;
109
+ C 113 ; WX 600 ; N q ; B 45 -157 591 441 ;
110
+ C 114 ; WX 600 ; N r ; B 60 0 559 441 ;
111
+ C 115 ; WX 600 ; N s ; B 80 -15 513 441 ;
112
+ C 116 ; WX 600 ; N t ; B 87 -15 530 561 ;
113
+ C 117 ; WX 600 ; N u ; B 21 -15 562 426 ;
114
+ C 118 ; WX 600 ; N v ; B 10 -10 590 426 ;
115
+ C 119 ; WX 600 ; N w ; B -4 -10 604 426 ;
116
+ C 120 ; WX 600 ; N x ; B 20 0 580 426 ;
117
+ C 121 ; WX 600 ; N y ; B 7 -157 592 426 ;
118
+ C 122 ; WX 600 ; N z ; B 99 0 502 426 ;
119
+ C 123 ; WX 600 ; N braceleft ; B 182 -108 437 622 ;
120
+ C 124 ; WX 600 ; N bar ; B 275 -250 326 750 ;
121
+ C 125 ; WX 600 ; N braceright ; B 163 -108 418 622 ;
122
+ C 126 ; WX 600 ; N asciitilde ; B 63 197 540 320 ;
123
+ C 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ;
124
+ C 162 ; WX 600 ; N cent ; B 96 -49 500 614 ;
125
+ C 163 ; WX 600 ; N sterling ; B 84 -21 521 611 ;
126
+ C -1 ; WX 600 ; N fraction ; B 92 -57 509 665 ;
127
+ C 165 ; WX 600 ; N yen ; B 26 0 574 562 ;
128
+ C 131 ; WX 600 ; N florin ; B 4 -143 539 622 ;
129
+ C 167 ; WX 600 ; N section ; B 113 -78 488 580 ;
130
+ C 164 ; WX 600 ; N currency ; B 73 58 527 506 ;
131
+ C 39 ; WX 600 ; N quotesingle ; B 259 328 341 562 ;
132
+ C 147 ; WX 600 ; N quotedblleft ; B 143 328 471 562 ;
133
+ C 170 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ;
134
+ C 139 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ;
135
+ C 155 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ;
136
+ C -1 ; WX 600 ; N fi ; B 3 0 597 629 ;
137
+ C -1 ; WX 600 ; N fl ; B 3 0 597 629 ;
138
+ C 150 ; WX 600 ; N endash ; B 75 231 525 285 ;
139
+ C 134 ; WX 600 ; N dagger ; B 141 -78 459 580 ;
140
+ C 135 ; WX 600 ; N daggerdbl ; B 141 -78 459 580 ;
141
+ C 183 ; WX 600 ; N periodcentered ; B 222 189 378 327 ;
142
+ C 182 ; WX 600 ; N paragraph ; B 50 -78 511 562 ;
143
+ C 149 ; WX 600 ; N bullet ; B 172 130 428 383 ;
144
+ C 130 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ;
145
+ C 132 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ;
146
+ C 148 ; WX 600 ; N quotedblright ; B 143 328 457 562 ;
147
+ C 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ;
148
+ C 133 ; WX 600 ; N ellipsis ; B 37 -15 563 111 ;
149
+ C 137 ; WX 600 ; N perthousand ; B 3 -15 600 622 ;
150
+ C 191 ; WX 600 ; N questiondown ; B 108 -157 471 430 ;
151
+ C 96 ; WX 600 ; N grave ; B 151 497 378 672 ;
152
+ C 180 ; WX 600 ; N acute ; B 242 497 469 672 ;
153
+ C 136 ; WX 600 ; N circumflex ; B 124 477 476 654 ;
154
+ C 152 ; WX 600 ; N tilde ; B 105 489 503 606 ;
155
+ C 175 ; WX 600 ; N macron ; B 120 525 480 565 ;
156
+ C -1 ; WX 600 ; N breve ; B 153 501 447 609 ;
157
+ C -1 ; WX 600 ; N dotaccent ; B 249 537 352 640 ;
158
+ C 168 ; WX 600 ; N dieresis ; B 148 537 453 640 ;
159
+ C -1 ; WX 600 ; N ring ; B 218 463 382 627 ;
160
+ C 184 ; WX 600 ; N cedilla ; B 224 -151 362 10 ;
161
+ C -1 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ;
162
+ C -1 ; WX 600 ; N ogonek ; B 211 -172 407 4 ;
163
+ C -1 ; WX 600 ; N caron ; B 124 492 476 669 ;
164
+ C 151 ; WX 600 ; N emdash ; B 0 231 600 285 ;
165
+ C 198 ; WX 600 ; N AE ; B 3 0 550 562 ;
166
+ C 170 ; WX 600 ; N ordfeminine ; B 156 249 442 580 ;
167
+ C -1 ; WX 600 ; N Lslash ; B 47 0 554 562 ;
168
+ C 216 ; WX 600 ; N Oslash ; B 43 -80 557 629 ;
169
+ C 140 ; WX 600 ; N OE ; B 7 0 567 562 ;
170
+ C 186 ; WX 600 ; N ordmasculine ; B 157 249 443 580 ;
171
+ C 230 ; WX 600 ; N ae ; B 19 -15 570 441 ;
172
+ C -1 ; WX 600 ; N dotlessi ; B 95 0 505 426 ;
173
+ C -1 ; WX 600 ; N lslash ; B 95 0 505 629 ;
174
+ C 248 ; WX 600 ; N oslash ; B 62 -80 538 506 ;
175
+ C 156 ; WX 600 ; N oe ; B 19 -15 559 441 ;
176
+ C 223 ; WX 600 ; N germandbls ; B 48 -15 588 629 ;
177
+ C 207 ; WX 600 ; N Idieresis ; B 96 0 504 753 ;
178
+ C 233 ; WX 600 ; N eacute ; B 66 -15 548 672 ;
179
+ C -1 ; WX 600 ; N abreve ; B 53 -15 559 609 ;
180
+ C -1 ; WX 600 ; N uhungarumlaut ; B 21 -15 580 672 ;
181
+ C -1 ; WX 600 ; N ecaron ; B 66 -15 548 669 ;
182
+ C 159 ; WX 600 ; N Ydieresis ; B 24 0 576 753 ;
183
+ C 247 ; WX 600 ; N divide ; B 87 48 513 467 ;
184
+ C 221 ; WX 600 ; N Yacute ; B 24 0 576 805 ;
185
+ C 194 ; WX 600 ; N Acircumflex ; B 3 0 597 787 ;
186
+ C 225 ; WX 600 ; N aacute ; B 53 -15 559 672 ;
187
+ C 219 ; WX 600 ; N Ucircumflex ; B 17 -18 583 787 ;
188
+ C 253 ; WX 600 ; N yacute ; B 7 -157 592 672 ;
189
+ C -1 ; WX 600 ; N scommaaccent ; B 80 -250 513 441 ;
190
+ C 234 ; WX 600 ; N ecircumflex ; B 66 -15 548 654 ;
191
+ C -1 ; WX 600 ; N Uring ; B 17 -18 583 760 ;
192
+ C 220 ; WX 600 ; N Udieresis ; B 17 -18 583 753 ;
193
+ C -1 ; WX 600 ; N aogonek ; B 53 -172 587 441 ;
194
+ C 218 ; WX 600 ; N Uacute ; B 17 -18 583 805 ;
195
+ C -1 ; WX 600 ; N uogonek ; B 21 -172 590 426 ;
196
+ C 203 ; WX 600 ; N Edieresis ; B 53 0 550 753 ;
197
+ C -1 ; WX 600 ; N Dcroat ; B 30 0 574 562 ;
198
+ C -1 ; WX 600 ; N commaaccent ; B 198 -250 335 -58 ;
199
+ C 169 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
200
+ C -1 ; WX 600 ; N Emacron ; B 53 0 550 698 ;
201
+ C -1 ; WX 600 ; N ccaron ; B 66 -15 529 669 ;
202
+ C 229 ; WX 600 ; N aring ; B 53 -15 559 627 ;
203
+ C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 593 562 ;
204
+ C -1 ; WX 600 ; N lacute ; B 95 0 505 805 ;
205
+ C 224 ; WX 600 ; N agrave ; B 53 -15 559 672 ;
206
+ C -1 ; WX 600 ; N Tcommaaccent ; B 38 -250 563 562 ;
207
+ C -1 ; WX 600 ; N Cacute ; B 41 -18 540 805 ;
208
+ C 227 ; WX 600 ; N atilde ; B 53 -15 559 606 ;
209
+ C -1 ; WX 600 ; N Edotaccent ; B 53 0 550 753 ;
210
+ C 154 ; WX 600 ; N scaron ; B 80 -15 513 669 ;
211
+ C -1 ; WX 600 ; N scedilla ; B 80 -151 513 441 ;
212
+ C 237 ; WX 600 ; N iacute ; B 95 0 505 672 ;
213
+ C -1 ; WX 600 ; N lozenge ; B 18 0 443 706 ;
214
+ C -1 ; WX 600 ; N Rcaron ; B 38 0 588 802 ;
215
+ C -1 ; WX 600 ; N Gcommaaccent ; B 31 -250 575 580 ;
216
+ C 251 ; WX 600 ; N ucircumflex ; B 21 -15 562 654 ;
217
+ C 226 ; WX 600 ; N acircumflex ; B 53 -15 559 654 ;
218
+ C -1 ; WX 600 ; N Amacron ; B 3 0 597 698 ;
219
+ C -1 ; WX 600 ; N rcaron ; B 60 0 559 669 ;
220
+ C 231 ; WX 600 ; N ccedilla ; B 66 -151 529 441 ;
221
+ C -1 ; WX 600 ; N Zdotaccent ; B 86 0 514 753 ;
222
+ C 222 ; WX 600 ; N Thorn ; B 79 0 538 562 ;
223
+ C -1 ; WX 600 ; N Omacron ; B 43 -18 557 698 ;
224
+ C -1 ; WX 600 ; N Racute ; B 38 0 588 805 ;
225
+ C -1 ; WX 600 ; N Sacute ; B 72 -20 529 805 ;
226
+ C -1 ; WX 600 ; N dcaron ; B 45 -15 715 629 ;
227
+ C -1 ; WX 600 ; N Umacron ; B 17 -18 583 698 ;
228
+ C -1 ; WX 600 ; N uring ; B 21 -15 562 627 ;
229
+ C 179 ; WX 600 ; N threesuperior ; B 155 240 406 622 ;
230
+ C 210 ; WX 600 ; N Ograve ; B 43 -18 557 805 ;
231
+ C 192 ; WX 600 ; N Agrave ; B 3 0 597 805 ;
232
+ C -1 ; WX 600 ; N Abreve ; B 3 0 597 732 ;
233
+ C 215 ; WX 600 ; N multiply ; B 87 43 515 470 ;
234
+ C 250 ; WX 600 ; N uacute ; B 21 -15 562 672 ;
235
+ C -1 ; WX 600 ; N Tcaron ; B 38 0 563 802 ;
236
+ C -1 ; WX 600 ; N partialdiff ; B 17 -38 459 710 ;
237
+ C 255 ; WX 600 ; N ydieresis ; B 7 -157 592 620 ;
238
+ C -1 ; WX 600 ; N Nacute ; B 7 -13 593 805 ;
239
+ C 238 ; WX 600 ; N icircumflex ; B 94 0 505 654 ;
240
+ C 202 ; WX 600 ; N Ecircumflex ; B 53 0 550 787 ;
241
+ C 228 ; WX 600 ; N adieresis ; B 53 -15 559 620 ;
242
+ C 235 ; WX 600 ; N edieresis ; B 66 -15 548 620 ;
243
+ C -1 ; WX 600 ; N cacute ; B 66 -15 529 672 ;
244
+ C -1 ; WX 600 ; N nacute ; B 26 0 575 672 ;
245
+ C -1 ; WX 600 ; N umacron ; B 21 -15 562 565 ;
246
+ C -1 ; WX 600 ; N Ncaron ; B 7 -13 593 802 ;
247
+ C 205 ; WX 600 ; N Iacute ; B 96 0 504 805 ;
248
+ C 177 ; WX 600 ; N plusminus ; B 87 44 513 558 ;
249
+ C 166 ; WX 600 ; N brokenbar ; B 275 -175 326 675 ;
250
+ C 174 ; WX 600 ; N registered ; B 0 -18 600 580 ;
251
+ C -1 ; WX 600 ; N Gbreve ; B 31 -18 575 732 ;
252
+ C -1 ; WX 600 ; N Idotaccent ; B 96 0 504 753 ;
253
+ C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;
254
+ C 200 ; WX 600 ; N Egrave ; B 53 0 550 805 ;
255
+ C -1 ; WX 600 ; N racute ; B 60 0 559 672 ;
256
+ C -1 ; WX 600 ; N omacron ; B 62 -15 538 565 ;
257
+ C -1 ; WX 600 ; N Zacute ; B 86 0 514 805 ;
258
+ C 142 ; WX 600 ; N Zcaron ; B 86 0 514 802 ;
259
+ C -1 ; WX 600 ; N greaterequal ; B 98 0 502 710 ;
260
+ C 208 ; WX 600 ; N Eth ; B 30 0 574 562 ;
261
+ C 199 ; WX 600 ; N Ccedilla ; B 41 -151 540 580 ;
262
+ C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 505 629 ;
263
+ C -1 ; WX 600 ; N tcaron ; B 87 -15 530 717 ;
264
+ C -1 ; WX 600 ; N eogonek ; B 66 -172 548 441 ;
265
+ C -1 ; WX 600 ; N Uogonek ; B 17 -172 583 562 ;
266
+ C 193 ; WX 600 ; N Aacute ; B 3 0 597 805 ;
267
+ C 196 ; WX 600 ; N Adieresis ; B 3 0 597 753 ;
268
+ C 232 ; WX 600 ; N egrave ; B 66 -15 548 672 ;
269
+ C -1 ; WX 600 ; N zacute ; B 99 0 502 672 ;
270
+ C -1 ; WX 600 ; N iogonek ; B 95 -172 505 657 ;
271
+ C 211 ; WX 600 ; N Oacute ; B 43 -18 557 805 ;
272
+ C 243 ; WX 600 ; N oacute ; B 62 -15 538 672 ;
273
+ C -1 ; WX 600 ; N amacron ; B 53 -15 559 565 ;
274
+ C -1 ; WX 600 ; N sacute ; B 80 -15 513 672 ;
275
+ C 239 ; WX 600 ; N idieresis ; B 95 0 505 620 ;
276
+ C 212 ; WX 600 ; N Ocircumflex ; B 43 -18 557 787 ;
277
+ C 217 ; WX 600 ; N Ugrave ; B 17 -18 583 805 ;
278
+ C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
279
+ C 254 ; WX 600 ; N thorn ; B -6 -157 555 629 ;
280
+ C 178 ; WX 600 ; N twosuperior ; B 177 249 424 622 ;
281
+ C 214 ; WX 600 ; N Odieresis ; B 43 -18 557 753 ;
282
+ C 181 ; WX 600 ; N mu ; B 21 -157 562 426 ;
283
+ C 236 ; WX 600 ; N igrave ; B 95 0 505 672 ;
284
+ C -1 ; WX 600 ; N ohungarumlaut ; B 62 -15 580 672 ;
285
+ C -1 ; WX 600 ; N Eogonek ; B 53 -172 561 562 ;
286
+ C -1 ; WX 600 ; N dcroat ; B 45 -15 591 629 ;
287
+ C 190 ; WX 600 ; N threequarters ; B 8 -56 593 666 ;
288
+ C -1 ; WX 600 ; N Scedilla ; B 72 -151 529 580 ;
289
+ C -1 ; WX 600 ; N lcaron ; B 95 0 533 629 ;
290
+ C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 582 562 ;
291
+ C -1 ; WX 600 ; N Lacute ; B 47 0 554 805 ;
292
+ C 153 ; WX 600 ; N trademark ; B -23 263 623 562 ;
293
+ C -1 ; WX 600 ; N edotaccent ; B 66 -15 548 620 ;
294
+ C 204 ; WX 600 ; N Igrave ; B 96 0 504 805 ;
295
+ C -1 ; WX 600 ; N Imacron ; B 96 0 504 698 ;
296
+ C -1 ; WX 600 ; N Lcaron ; B 47 0 554 562 ;
297
+ C 189 ; WX 600 ; N onehalf ; B 0 -57 611 665 ;
298
+ C -1 ; WX 600 ; N lessequal ; B 98 0 502 710 ;
299
+ C 244 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ;
300
+ C 241 ; WX 600 ; N ntilde ; B 26 0 575 606 ;
301
+ C -1 ; WX 600 ; N Uhungarumlaut ; B 17 -18 590 805 ;
302
+ C 201 ; WX 600 ; N Eacute ; B 53 0 550 805 ;
303
+ C -1 ; WX 600 ; N emacron ; B 66 -15 548 565 ;
304
+ C -1 ; WX 600 ; N gbreve ; B 45 -157 566 609 ;
305
+ C 188 ; WX 600 ; N onequarter ; B 0 -57 600 665 ;
306
+ C 138 ; WX 600 ; N Scaron ; B 72 -20 529 802 ;
307
+ C -1 ; WX 600 ; N Scommaaccent ; B 72 -250 529 580 ;
308
+ C -1 ; WX 600 ; N Ohungarumlaut ; B 43 -18 580 805 ;
309
+ C 176 ; WX 600 ; N degree ; B 123 269 477 622 ;
310
+ C 242 ; WX 600 ; N ograve ; B 62 -15 538 672 ;
311
+ C -1 ; WX 600 ; N Ccaron ; B 41 -18 540 802 ;
312
+ C 249 ; WX 600 ; N ugrave ; B 21 -15 562 672 ;
313
+ C -1 ; WX 600 ; N radical ; B 3 -15 597 792 ;
314
+ C -1 ; WX 600 ; N Dcaron ; B 43 0 574 802 ;
315
+ C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 559 441 ;
316
+ C 209 ; WX 600 ; N Ntilde ; B 7 -13 593 729 ;
317
+ C 245 ; WX 600 ; N otilde ; B 62 -15 538 606 ;
318
+ C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 588 562 ;
319
+ C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 554 562 ;
320
+ C 195 ; WX 600 ; N Atilde ; B 3 0 597 729 ;
321
+ C -1 ; WX 600 ; N Aogonek ; B 3 -172 608 562 ;
322
+ C 197 ; WX 600 ; N Aring ; B 3 0 597 750 ;
323
+ C 213 ; WX 600 ; N Otilde ; B 43 -18 557 729 ;
324
+ C -1 ; WX 600 ; N zdotaccent ; B 99 0 502 620 ;
325
+ C -1 ; WX 600 ; N Ecaron ; B 53 0 550 802 ;
326
+ C -1 ; WX 600 ; N Iogonek ; B 96 -172 504 562 ;
327
+ C -1 ; WX 600 ; N kcommaaccent ; B 43 -250 580 629 ;
328
+ C -1 ; WX 600 ; N minus ; B 80 232 520 283 ;
329
+ C 206 ; WX 600 ; N Icircumflex ; B 96 0 504 787 ;
330
+ C -1 ; WX 600 ; N ncaron ; B 26 0 575 669 ;
331
+ C -1 ; WX 600 ; N tcommaaccent ; B 87 -250 530 561 ;
332
+ C 172 ; WX 600 ; N logicalnot ; B 87 108 513 369 ;
333
+ C 246 ; WX 600 ; N odieresis ; B 62 -15 538 620 ;
334
+ C 252 ; WX 600 ; N udieresis ; B 21 -15 562 620 ;
335
+ C -1 ; WX 600 ; N notequal ; B 15 -16 540 529 ;
336
+ C -1 ; WX 600 ; N gcommaaccent ; B 45 -157 566 708 ;
337
+ C 240 ; WX 600 ; N eth ; B 62 -15 538 629 ;
338
+ C 158 ; WX 600 ; N zcaron ; B 99 0 502 669 ;
339
+ C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 575 441 ;
340
+ C 185 ; WX 600 ; N onesuperior ; B 172 249 428 622 ;
341
+ C -1 ; WX 600 ; N imacron ; B 95 0 505 565 ;
342
+ C 128 ; WX 600 ; N Euro ; B 0 0 0 0 ;
343
+ EndCharMetrics
344
+ EndFontMetrics
lib/dompdf/lib/fonts/Helvetica-Bold.afm ADDED
@@ -0,0 +1,2829 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.
3
+ Comment Creation Date: Thu May 1 12:43:52 1997
4
+ Comment UniqueID 43052
5
+ Comment VMusage 37169 48194
6
+ FontName Helvetica-Bold
7
+ FullName Helvetica Bold
8
+ FamilyName Helvetica
9
+ Weight Bold
10
+ ItalicAngle 0
11
+ IsFixedPitch false
12
+ CharacterSet ExtendedRoman
13
+ FontBBox -170 -228 1003 962
14
+ UnderlinePosition -100
15
+ UnderlineThickness 50
16
+ Version 002.000
17
+ Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.
18
+ EncodingScheme WinAnsiEncoding
19
+ CapHeight 718
20
+ XHeight 532
21
+ Ascender 718
22
+ Descender -207
23
+ StdHW 118
24
+ StdVW 140
25
+ StartCharMetrics 317
26
+ C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
27
+ C 160 ; WX 278 ; N space ; B 0 0 0 0 ;
28
+ C 33 ; WX 333 ; N exclam ; B 90 0 244 718 ;
29
+ C 34 ; WX 474 ; N quotedbl ; B 98 447 376 718 ;
30
+ C 35 ; WX 556 ; N numbersign ; B 18 0 538 698 ;
31
+ C 36 ; WX 556 ; N dollar ; B 30 -115 523 775 ;
32
+ C 37 ; WX 889 ; N percent ; B 28 -19 861 710 ;
33
+ C 38 ; WX 722 ; N ampersand ; B 54 -19 701 718 ;
34
+ C 146 ; WX 278 ; N quoteright ; B 69 445 209 718 ;
35
+ C 40 ; WX 333 ; N parenleft ; B 35 -208 314 734 ;
36
+ C 41 ; WX 333 ; N parenright ; B 19 -208 298 734 ;
37
+ C 42 ; WX 389 ; N asterisk ; B 27 387 362 718 ;
38
+ C 43 ; WX 584 ; N plus ; B 40 0 544 506 ;
39
+ C 44 ; WX 278 ; N comma ; B 64 -168 214 146 ;
40
+ C 45 ; WX 333 ; N hyphen ; B 27 215 306 345 ;
41
+ C 173 ; WX 333 ; N hyphen ; B 44 232 289 322 ;
42
+ C 46 ; WX 278 ; N period ; B 64 0 214 146 ;
43
+ C 47 ; WX 278 ; N slash ; B -33 -19 311 737 ;
44
+ C 48 ; WX 556 ; N zero ; B 32 -19 524 710 ;
45
+ C 49 ; WX 556 ; N one ; B 69 0 378 710 ;
46
+ C 50 ; WX 556 ; N two ; B 26 0 511 710 ;
47
+ C 51 ; WX 556 ; N three ; B 27 -19 516 710 ;
48
+ C 52 ; WX 556 ; N four ; B 27 0 526 710 ;
49
+ C 53 ; WX 556 ; N five ; B 27 -19 516 698 ;
50
+ C 54 ; WX 556 ; N six ; B 31 -19 520 710 ;
51
+ C 55 ; WX 556 ; N seven ; B 25 0 528 698 ;
52
+ C 56 ; WX 556 ; N eight ; B 32 -19 524 710 ;
53
+ C 57 ; WX 556 ; N nine ; B 30 -19 522 710 ;
54
+ C 58 ; WX 333 ; N colon ; B 92 0 242 512 ;
55
+ C 59 ; WX 333 ; N semicolon ; B 92 -168 242 512 ;
56
+ C 60 ; WX 584 ; N less ; B 38 -8 546 514 ;
57
+ C 61 ; WX 584 ; N equal ; B 40 87 544 419 ;
58
+ C 62 ; WX 584 ; N greater ; B 38 -8 546 514 ;
59
+ C 63 ; WX 611 ; N question ; B 60 0 556 727 ;
60
+ C 64 ; WX 975 ; N at ; B 118 -19 856 737 ;
61
+ C 65 ; WX 722 ; N A ; B 20 0 702 718 ;
62
+ C 66 ; WX 722 ; N B ; B 76 0 669 718 ;
63
+ C 67 ; WX 722 ; N C ; B 44 -19 684 737 ;
64
+ C 68 ; WX 722 ; N D ; B 76 0 685 718 ;
65
+ C 69 ; WX 667 ; N E ; B 76 0 621 718 ;
66
+ C 70 ; WX 611 ; N F ; B 76 0 587 718 ;
67
+ C 71 ; WX 778 ; N G ; B 44 -19 713 737 ;
68
+ C 72 ; WX 722 ; N H ; B 71 0 651 718 ;
69
+ C 73 ; WX 278 ; N I ; B 64 0 214 718 ;
70
+ C 74 ; WX 556 ; N J ; B 22 -18 484 718 ;
71
+ C 75 ; WX 722 ; N K ; B 87 0 722 718 ;
72
+ C 76 ; WX 611 ; N L ; B 76 0 583 718 ;
73
+ C 77 ; WX 833 ; N M ; B 69 0 765 718 ;
74
+ C 78 ; WX 722 ; N N ; B 69 0 654 718 ;
75
+ C 79 ; WX 778 ; N O ; B 44 -19 734 737 ;
76
+ C 80 ; WX 667 ; N P ; B 76 0 627 718 ;
77
+ C 81 ; WX 778 ; N Q ; B 44 -52 737 737 ;
78
+ C 82 ; WX 722 ; N R ; B 76 0 677 718 ;
79
+ C 83 ; WX 667 ; N S ; B 39 -19 629 737 ;
80
+ C 84 ; WX 611 ; N T ; B 14 0 598 718 ;
81
+ C 85 ; WX 722 ; N U ; B 72 -19 651 718 ;
82
+ C 86 ; WX 667 ; N V ; B 19 0 648 718 ;
83
+ C 87 ; WX 944 ; N W ; B 16 0 929 718 ;
84
+ C 88 ; WX 667 ; N X ; B 14 0 653 718 ;
85
+ C 89 ; WX 667 ; N Y ; B 15 0 653 718 ;
86
+ C 90 ; WX 611 ; N Z ; B 25 0 586 718 ;
87
+ C 91 ; WX 333 ; N bracketleft ; B 63 -196 309 722 ;
88
+ C 92 ; WX 278 ; N backslash ; B -33 -19 311 737 ;
89
+ C 93 ; WX 333 ; N bracketright ; B 24 -196 270 722 ;
90
+ C 94 ; WX 584 ; N asciicircum ; B 62 323 522 698 ;
91
+ C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ;
92
+ C 145 ; WX 278 ; N quoteleft ; B 69 454 209 727 ;
93
+ C 97 ; WX 556 ; N a ; B 29 -14 527 546 ;
94
+ C 98 ; WX 611 ; N b ; B 61 -14 578 718 ;
95
+ C 99 ; WX 556 ; N c ; B 34 -14 524 546 ;
96
+ C 100 ; WX 611 ; N d ; B 34 -14 551 718 ;
97
+ C 101 ; WX 556 ; N e ; B 23 -14 528 546 ;
98
+ C 102 ; WX 333 ; N f ; B 10 0 318 727 ; L i fi ; L l fl ;
99
+ C 103 ; WX 611 ; N g ; B 40 -217 553 546 ;
100
+ C 104 ; WX 611 ; N h ; B 65 0 546 718 ;
101
+ C 105 ; WX 278 ; N i ; B 69 0 209 725 ;
102
+ C 106 ; WX 278 ; N j ; B 3 -214 209 725 ;
103
+ C 107 ; WX 556 ; N k ; B 69 0 562 718 ;
104
+ C 108 ; WX 278 ; N l ; B 69 0 209 718 ;
105
+ C 109 ; WX 889 ; N m ; B 64 0 826 546 ;
106
+ C 110 ; WX 611 ; N n ; B 65 0 546 546 ;
107
+ C 111 ; WX 611 ; N o ; B 34 -14 578 546 ;
108
+ C 112 ; WX 611 ; N p ; B 62 -207 578 546 ;
109
+ C 113 ; WX 611 ; N q ; B 34 -207 552 546 ;
110
+ C 114 ; WX 389 ; N r ; B 64 0 373 546 ;
111
+ C 115 ; WX 556 ; N s ; B 30 -14 519 546 ;
112
+ C 116 ; WX 333 ; N t ; B 10 -6 309 676 ;
113
+ C 117 ; WX 611 ; N u ; B 66 -14 545 532 ;
114
+ C 118 ; WX 556 ; N v ; B 13 0 543 532 ;
115
+ C 119 ; WX 778 ; N w ; B 10 0 769 532 ;
116
+ C 120 ; WX 556 ; N x ; B 15 0 541 532 ;
117
+ C 121 ; WX 556 ; N y ; B 10 -214 539 532 ;
118
+ C 122 ; WX 500 ; N z ; B 20 0 480 532 ;
119
+ C 123 ; WX 389 ; N braceleft ; B 48 -196 365 722 ;
120
+ C 124 ; WX 280 ; N bar ; B 84 -225 196 775 ;
121
+ C 125 ; WX 389 ; N braceright ; B 24 -196 341 722 ;
122
+ C 126 ; WX 584 ; N asciitilde ; B 61 163 523 343 ;
123
+ C 161 ; WX 333 ; N exclamdown ; B 90 -186 244 532 ;
124
+ C 162 ; WX 556 ; N cent ; B 34 -118 524 628 ;
125
+ C 163 ; WX 556 ; N sterling ; B 28 -16 541 718 ;
126
+ C -1 ; WX 167 ; N fraction ; B -170 -19 336 710 ;
127
+ C 165 ; WX 556 ; N yen ; B -9 0 565 698 ;
128
+ C 131 ; WX 556 ; N florin ; B -10 -210 516 737 ;
129
+ C 167 ; WX 556 ; N section ; B 34 -184 522 727 ;
130
+ C 164 ; WX 556 ; N currency ; B -3 76 559 636 ;
131
+ C 39 ; WX 238 ; N quotesingle ; B 70 447 168 718 ;
132
+ C 147 ; WX 500 ; N quotedblleft ; B 64 454 436 727 ;
133
+ C 170 ; WX 556 ; N guillemotleft ; B 88 76 468 484 ;
134
+ C 139 ; WX 333 ; N guilsinglleft ; B 83 76 250 484 ;
135
+ C 155 ; WX 333 ; N guilsinglright ; B 83 76 250 484 ;
136
+ C -1 ; WX 611 ; N fi ; B 10 0 542 727 ;
137
+ C -1 ; WX 611 ; N fl ; B 10 0 542 727 ;
138
+ C 150 ; WX 556 ; N endash ; B 0 227 556 333 ;
139
+ C 134 ; WX 556 ; N dagger ; B 36 -171 520 718 ;
140
+ C 135 ; WX 556 ; N daggerdbl ; B 36 -171 520 718 ;
141
+ C 183 ; WX 278 ; N periodcentered ; B 58 172 220 334 ;
142
+ C 182 ; WX 556 ; N paragraph ; B -8 -191 539 700 ;
143
+ C 149 ; WX 350 ; N bullet ; B 10 194 340 524 ;
144
+ C 130 ; WX 278 ; N quotesinglbase ; B 69 -146 209 127 ;
145
+ C 132 ; WX 500 ; N quotedblbase ; B 64 -146 436 127 ;
146
+ C 148 ; WX 500 ; N quotedblright ; B 64 445 436 718 ;
147
+ C 187 ; WX 556 ; N guillemotright ; B 88 76 468 484 ;
148
+ C 133 ; WX 1000 ; N ellipsis ; B 92 0 908 146 ;
149
+ C 137 ; WX 1000 ; N perthousand ; B -3 -19 1003 710 ;
150
+ C 191 ; WX 611 ; N questiondown ; B 55 -195 551 532 ;
151
+ C 96 ; WX 333 ; N grave ; B -23 604 225 750 ;
152
+ C 180 ; WX 333 ; N acute ; B 108 604 356 750 ;
153
+ C 136 ; WX 333 ; N circumflex ; B -10 604 343 750 ;
154
+ C 152 ; WX 333 ; N tilde ; B -17 610 350 737 ;
155
+ C 175 ; WX 333 ; N macron ; B -6 604 339 678 ;
156
+ C -1 ; WX 333 ; N breve ; B -2 604 335 750 ;
157
+ C -1 ; WX 333 ; N dotaccent ; B 104 614 230 729 ;
158
+ C 168 ; WX 333 ; N dieresis ; B 6 614 327 729 ;
159
+ C -1 ; WX 333 ; N ring ; B 59 568 275 776 ;
160
+ C 184 ; WX 333 ; N cedilla ; B 6 -228 245 0 ;
161
+ C -1 ; WX 333 ; N hungarumlaut ; B 9 604 486 750 ;
162
+ C -1 ; WX 333 ; N ogonek ; B 71 -228 304 0 ;
163
+ C -1 ; WX 333 ; N caron ; B -10 604 343 750 ;
164
+ C 151 ; WX 1000 ; N emdash ; B 0 227 1000 333 ;
165
+ C 198 ; WX 1000 ; N AE ; B 5 0 954 718 ;
166
+ C 170 ; WX 370 ; N ordfeminine ; B 22 401 347 737 ;
167
+ C -1 ; WX 611 ; N Lslash ; B -20 0 583 718 ;
168
+ C 216 ; WX 778 ; N Oslash ; B 33 -27 744 745 ;
169
+ C 140 ; WX 1000 ; N OE ; B 37 -19 961 737 ;
170
+ C 186 ; WX 365 ; N ordmasculine ; B 6 401 360 737 ;
171
+ C 230 ; WX 889 ; N ae ; B 29 -14 858 546 ;
172
+ C -1 ; WX 278 ; N dotlessi ; B 69 0 209 532 ;
173
+ C -1 ; WX 278 ; N lslash ; B -18 0 296 718 ;
174
+ C 248 ; WX 611 ; N oslash ; B 22 -29 589 560 ;
175
+ C 156 ; WX 944 ; N oe ; B 34 -14 912 546 ;
176
+ C 223 ; WX 611 ; N germandbls ; B 69 -14 579 731 ;
177
+ C 207 ; WX 278 ; N Idieresis ; B -21 0 300 915 ;
178
+ C 233 ; WX 556 ; N eacute ; B 23 -14 528 750 ;
179
+ C -1 ; WX 556 ; N abreve ; B 29 -14 527 750 ;
180
+ C -1 ; WX 611 ; N uhungarumlaut ; B 66 -14 625 750 ;
181
+ C -1 ; WX 556 ; N ecaron ; B 23 -14 528 750 ;
182
+ C 159 ; WX 667 ; N Ydieresis ; B 15 0 653 915 ;
183
+ C 247 ; WX 584 ; N divide ; B 40 -42 544 548 ;
184
+ C 221 ; WX 667 ; N Yacute ; B 15 0 653 936 ;
185
+ C 194 ; WX 722 ; N Acircumflex ; B 20 0 702 936 ;
186
+ C 225 ; WX 556 ; N aacute ; B 29 -14 527 750 ;
187
+ C 219 ; WX 722 ; N Ucircumflex ; B 72 -19 651 936 ;
188
+ C 253 ; WX 556 ; N yacute ; B 10 -214 539 750 ;
189
+ C -1 ; WX 556 ; N scommaaccent ; B 30 -228 519 546 ;
190
+ C 234 ; WX 556 ; N ecircumflex ; B 23 -14 528 750 ;
191
+ C -1 ; WX 722 ; N Uring ; B 72 -19 651 962 ;
192
+ C 220 ; WX 722 ; N Udieresis ; B 72 -19 651 915 ;
193
+ C -1 ; WX 556 ; N aogonek ; B 29 -224 545 546 ;
194
+ C 218 ; WX 722 ; N Uacute ; B 72 -19 651 936 ;
195
+ C -1 ; WX 611 ; N uogonek ; B 66 -228 545 532 ;
196
+ C 203 ; WX 667 ; N Edieresis ; B 76 0 621 915 ;
197
+ C -1 ; WX 722 ; N Dcroat ; B -5 0 685 718 ;
198
+ C -1 ; WX 250 ; N commaaccent ; B 64 -228 199 -50 ;
199
+ C 169 ; WX 737 ; N copyright ; B -11 -19 749 737 ;
200
+ C -1 ; WX 667 ; N Emacron ; B 76 0 621 864 ;
201
+ C -1 ; WX 556 ; N ccaron ; B 34 -14 524 750 ;
202
+ C 229 ; WX 556 ; N aring ; B 29 -14 527 776 ;
203
+ C -1 ; WX 722 ; N Ncommaaccent ; B 69 -228 654 718 ;
204
+ C -1 ; WX 278 ; N lacute ; B 69 0 329 936 ;
205
+ C 224 ; WX 556 ; N agrave ; B 29 -14 527 750 ;
206
+ C -1 ; WX 611 ; N Tcommaaccent ; B 14 -228 598 718 ;
207
+ C -1 ; WX 722 ; N Cacute ; B 44 -19 684 936 ;
208
+ C 227 ; WX 556 ; N atilde ; B 29 -14 527 737 ;
209
+ C -1 ; WX 667 ; N Edotaccent ; B 76 0 621 915 ;
210
+ C 154 ; WX 556 ; N scaron ; B 30 -14 519 750 ;
211
+ C -1 ; WX 556 ; N scedilla ; B 30 -228 519 546 ;
212
+ C 237 ; WX 278 ; N iacute ; B 69 0 329 750 ;
213
+ C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ;
214
+ C -1 ; WX 722 ; N Rcaron ; B 76 0 677 936 ;
215
+ C -1 ; WX 778 ; N Gcommaaccent ; B 44 -228 713 737 ;
216
+ C 251 ; WX 611 ; N ucircumflex ; B 66 -14 545 750 ;
217
+ C 226 ; WX 556 ; N acircumflex ; B 29 -14 527 750 ;
218
+ C -1 ; WX 722 ; N Amacron ; B 20 0 702 864 ;
219
+ C -1 ; WX 389 ; N rcaron ; B 18 0 373 750 ;
220
+ C 231 ; WX 556 ; N ccedilla ; B 34 -228 524 546 ;
221
+ C -1 ; WX 611 ; N Zdotaccent ; B 25 0 586 915 ;
222
+ C 222 ; WX 667 ; N Thorn ; B 76 0 627 718 ;
223
+ C -1 ; WX 778 ; N Omacron ; B 44 -19 734 864 ;
224
+ C -1 ; WX 722 ; N Racute ; B 76 0 677 936 ;
225
+ C -1 ; WX 667 ; N Sacute ; B 39 -19 629 936 ;
226
+ C -1 ; WX 743 ; N dcaron ; B 34 -14 750 718 ;
227
+ C -1 ; WX 722 ; N Umacron ; B 72 -19 651 864 ;
228
+ C -1 ; WX 611 ; N uring ; B 66 -14 545 776 ;
229
+ C 179 ; WX 333 ; N threesuperior ; B 8 271 326 710 ;
230
+ C 210 ; WX 778 ; N Ograve ; B 44 -19 734 936 ;
231
+ C 192 ; WX 722 ; N Agrave ; B 20 0 702 936 ;
232
+ C -1 ; WX 722 ; N Abreve ; B 20 0 702 936 ;
233
+ C 215 ; WX 584 ; N multiply ; B 40 1 545 505 ;
234
+ C 250 ; WX 611 ; N uacute ; B 66 -14 545 750 ;
235
+ C -1 ; WX 611 ; N Tcaron ; B 14 0 598 936 ;
236
+ C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ;
237
+ C 255 ; WX 556 ; N ydieresis ; B 10 -214 539 729 ;
238
+ C -1 ; WX 722 ; N Nacute ; B 69 0 654 936 ;
239
+ C 238 ; WX 278 ; N icircumflex ; B -37 0 316 750 ;
240
+ C 202 ; WX 667 ; N Ecircumflex ; B 76 0 621 936 ;
241
+ C 228 ; WX 556 ; N adieresis ; B 29 -14 527 729 ;
242
+ C 235 ; WX 556 ; N edieresis ; B 23 -14 528 729 ;
243
+ C -1 ; WX 556 ; N cacute ; B 34 -14 524 750 ;
244
+ C -1 ; WX 611 ; N nacute ; B 65 0 546 750 ;
245
+ C -1 ; WX 611 ; N umacron ; B 66 -14 545 678 ;
246
+ C -1 ; WX 722 ; N Ncaron ; B 69 0 654 936 ;
247
+ C 205 ; WX 278 ; N Iacute ; B 64 0 329 936 ;
248
+ C 177 ; WX 584 ; N plusminus ; B 40 0 544 506 ;
249
+ C 166 ; WX 280 ; N brokenbar ; B 84 -150 196 700 ;
250
+ C 174 ; WX 737 ; N registered ; B -11 -19 748 737 ;
251
+ C -1 ; WX 778 ; N Gbreve ; B 44 -19 713 936 ;
252
+ C -1 ; WX 278 ; N Idotaccent ; B 64 0 214 915 ;
253
+ C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ;
254
+ C 200 ; WX 667 ; N Egrave ; B 76 0 621 936 ;
255
+ C -1 ; WX 389 ; N racute ; B 64 0 384 750 ;
256
+ C -1 ; WX 611 ; N omacron ; B 34 -14 578 678 ;
257
+ C -1 ; WX 611 ; N Zacute ; B 25 0 586 936 ;
258
+ C 142 ; WX 611 ; N Zcaron ; B 25 0 586 936 ;
259
+ C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ;
260
+ C 208 ; WX 722 ; N Eth ; B -5 0 685 718 ;
261
+ C 199 ; WX 722 ; N Ccedilla ; B 44 -228 684 737 ;
262
+ C -1 ; WX 278 ; N lcommaaccent ; B 69 -228 213 718 ;
263
+ C -1 ; WX 389 ; N tcaron ; B 10 -6 421 878 ;
264
+ C -1 ; WX 556 ; N eogonek ; B 23 -228 528 546 ;
265
+ C -1 ; WX 722 ; N Uogonek ; B 72 -228 651 718 ;
266
+ C 193 ; WX 722 ; N Aacute ; B 20 0 702 936 ;
267
+ C 196 ; WX 722 ; N Adieresis ; B 20 0 702 915 ;
268
+ C 232 ; WX 556 ; N egrave ; B 23 -14 528 750 ;
269
+ C -1 ; WX 500 ; N zacute ; B 20 0 480 750 ;
270
+ C -1 ; WX 278 ; N iogonek ; B 16 -224 249 725 ;
271
+ C 211 ; WX 778 ; N Oacute ; B 44 -19 734 936 ;
272
+ C 243 ; WX 611 ; N oacute ; B 34 -14 578 750 ;
273
+ C -1 ; WX 556 ; N amacron ; B 29 -14 527 678 ;
274
+ C -1 ; WX 556 ; N sacute ; B 30 -14 519 750 ;
275
+ C 239 ; WX 278 ; N idieresis ; B -21 0 300 729 ;
276
+ C 212 ; WX 778 ; N Ocircumflex ; B 44 -19 734 936 ;
277
+ C 217 ; WX 722 ; N Ugrave ; B 72 -19 651 936 ;
278
+ C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
279
+ C 254 ; WX 611 ; N thorn ; B 62 -208 578 718 ;
280
+ C 178 ; WX 333 ; N twosuperior ; B 9 283 324 710 ;
281
+ C 214 ; WX 778 ; N Odieresis ; B 44 -19 734 915 ;
282
+ C 181 ; WX 611 ; N mu ; B 66 -207 545 532 ;
283
+ C 236 ; WX 278 ; N igrave ; B -50 0 209 750 ;
284
+ C -1 ; WX 611 ; N ohungarumlaut ; B 34 -14 625 750 ;
285
+ C -1 ; WX 667 ; N Eogonek ; B 76 -224 639 718 ;
286
+ C -1 ; WX 611 ; N dcroat ; B 34 -14 650 718 ;
287
+ C 190 ; WX 834 ; N threequarters ; B 16 -19 799 710 ;
288
+ C -1 ; WX 667 ; N Scedilla ; B 39 -228 629 737 ;
289
+ C -1 ; WX 400 ; N lcaron ; B 69 0 408 718 ;
290
+ C -1 ; WX 722 ; N Kcommaaccent ; B 87 -228 722 718 ;
291
+ C -1 ; WX 611 ; N Lacute ; B 76 0 583 936 ;
292
+ C 153 ; WX 1000 ; N trademark ; B 44 306 956 718 ;
293
+ C -1 ; WX 556 ; N edotaccent ; B 23 -14 528 729 ;
294
+ C 204 ; WX 278 ; N Igrave ; B -50 0 214 936 ;
295
+ C -1 ; WX 278 ; N Imacron ; B -33 0 312 864 ;
296
+ C -1 ; WX 611 ; N Lcaron ; B 76 0 583 718 ;
297
+ C 189 ; WX 834 ; N onehalf ; B 26 -19 794 710 ;
298
+ C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ;
299
+ C 244 ; WX 611 ; N ocircumflex ; B 34 -14 578 750 ;
300
+ C 241 ; WX 611 ; N ntilde ; B 65 0 546 737 ;
301
+ C -1 ; WX 722 ; N Uhungarumlaut ; B 72 -19 681 936 ;
302
+ C 201 ; WX 667 ; N Eacute ; B 76 0 621 936 ;
303
+ C -1 ; WX 556 ; N emacron ; B 23 -14 528 678 ;
304
+ C -1 ; WX 611 ; N gbreve ; B 40 -217 553 750 ;
305
+ C 188 ; WX 834 ; N onequarter ; B 26 -19 766 710 ;
306
+ C 138 ; WX 667 ; N Scaron ; B 39 -19 629 936 ;
307
+ C -1 ; WX 667 ; N Scommaaccent ; B 39 -228 629 737 ;
308
+ C -1 ; WX 778 ; N Ohungarumlaut ; B 44 -19 734 936 ;
309
+ C 176 ; WX 400 ; N degree ; B 57 426 343 712 ;
310
+ C 242 ; WX 611 ; N ograve ; B 34 -14 578 750 ;
311
+ C -1 ; WX 722 ; N Ccaron ; B 44 -19 684 936 ;
312
+ C 249 ; WX 611 ; N ugrave ; B 66 -14 545 750 ;
313
+ C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ;
314
+ C -1 ; WX 722 ; N Dcaron ; B 76 0 685 936 ;
315
+ C -1 ; WX 389 ; N rcommaaccent ; B 64 -228 373 546 ;
316
+ C 209 ; WX 722 ; N Ntilde ; B 69 0 654 923 ;
317
+ C 245 ; WX 611 ; N otilde ; B 34 -14 578 737 ;
318
+ C -1 ; WX 722 ; N Rcommaaccent ; B 76 -228 677 718 ;
319
+ C -1 ; WX 611 ; N Lcommaaccent ; B 76 -228 583 718 ;
320
+ C 195 ; WX 722 ; N Atilde ; B 20 0 702 923 ;
321
+ C -1 ; WX 722 ; N Aogonek ; B 20 -224 742 718 ;
322
+ C 197 ; WX 722 ; N Aring ; B 20 0 702 962 ;
323
+ C 213 ; WX 778 ; N Otilde ; B 44 -19 734 923 ;
324
+ C -1 ; WX 500 ; N zdotaccent ; B 20 0 480 729 ;
325
+ C -1 ; WX 667 ; N Ecaron ; B 76 0 621 936 ;
326
+ C -1 ; WX 278 ; N Iogonek ; B -11 -228 222 718 ;
327
+ C -1 ; WX 556 ; N kcommaaccent ; B 69 -228 562 718 ;
328
+ C -1 ; WX 584 ; N minus ; B 40 197 544 309 ;
329
+ C 206 ; WX 278 ; N Icircumflex ; B -37 0 316 936 ;
330
+ C -1 ; WX 611 ; N ncaron ; B 65 0 546 750 ;
331
+ C -1 ; WX 333 ; N tcommaaccent ; B 10 -228 309 676 ;
332
+ C 172 ; WX 584 ; N logicalnot ; B 40 108 544 419 ;
333
+ C 246 ; WX 611 ; N odieresis ; B 34 -14 578 729 ;
334
+ C 252 ; WX 611 ; N udieresis ; B 66 -14 545 729 ;
335
+ C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ;
336
+ C -1 ; WX 611 ; N gcommaaccent ; B 40 -217 553 850 ;
337
+ C 240 ; WX 611 ; N eth ; B 34 -14 578 737 ;
338
+ C 158 ; WX 500 ; N zcaron ; B 20 0 480 750 ;
339
+ C -1 ; WX 611 ; N ncommaaccent ; B 65 -228 546 546 ;
340
+ C 185 ; WX 333 ; N onesuperior ; B 26 283 237 710 ;
341
+ C -1 ; WX 278 ; N imacron ; B -8 0 285 678 ;
342
+ C 128 ; WX 556 ; N Euro ; B 0 0 0 0 ;
343
+ EndCharMetrics
344
+ StartKernData
345
+ StartKernPairs 2481
346
+ KPX A C -40
347
+ KPX A Cacute -40
348
+ KPX A Ccaron -40
349
+ KPX A Ccedilla -40
350
+ KPX A G -50
351
+ KPX A Gbreve -50
352
+ KPX A Gcommaaccent -50
353
+ KPX A O -40
354
+ KPX A Oacute -40
355
+ KPX A Ocircumflex -40
356
+ KPX A Odieresis -40
357
+ KPX A Ograve -40
358
+ KPX A Ohungarumlaut -40
359
+ KPX A Omacron -40
360
+ KPX A Oslash -40
361
+ KPX A Otilde -40
362
+ KPX A Q -40
363
+ KPX A T -90
364
+ KPX A Tcaron -90
365
+ KPX A Tcommaaccent -90
366
+ KPX A U -50
367
+ KPX A Uacute -50
368
+ KPX A Ucircumflex -50
369
+ KPX A Udieresis -50
370
+ KPX A Ugrave -50
371
+ KPX A Uhungarumlaut -50
372
+ KPX A Umacron -50
373
+ KPX A Uogonek -50
374
+ KPX A Uring -50
375
+ KPX A V -80
376
+ KPX A W -60
377
+ KPX A Y -110
378
+ KPX A Yacute -110
379
+ KPX A Ydieresis -110
380
+ KPX A u -30
381
+ KPX A uacute -30
382
+ KPX A ucircumflex -30
383
+ KPX A udieresis -30
384
+ KPX A ugrave -30
385
+ KPX A uhungarumlaut -30
386
+ KPX A umacron -30
387
+ KPX A uogonek -30
388
+ KPX A uring -30
389
+ KPX A v -40
390
+ KPX A w -30
391
+ KPX A y -30
392
+ KPX A yacute -30
393
+ KPX A ydieresis -30
394
+ KPX Aacute C -40
395
+ KPX Aacute Cacute -40
396
+ KPX Aacute Ccaron -40
397
+ KPX Aacute Ccedilla -40
398
+ KPX Aacute G -50
399
+ KPX Aacute Gbreve -50
400
+ KPX Aacute Gcommaaccent -50
401
+ KPX Aacute O -40
402
+ KPX Aacute Oacute -40
403
+ KPX Aacute Ocircumflex -40
404
+ KPX Aacute Odieresis -40
405
+ KPX Aacute Ograve -40
406
+ KPX Aacute Ohungarumlaut -40
407
+ KPX Aacute Omacron -40
408
+ KPX Aacute Oslash -40
409
+ KPX Aacute Otilde -40
410
+ KPX Aacute Q -40
411
+ KPX Aacute T -90
412
+ KPX Aacute Tcaron -90
413
+ KPX Aacute Tcommaaccent -90
414
+ KPX Aacute U -50
415
+ KPX Aacute Uacute -50
416
+ KPX Aacute Ucircumflex -50
417
+ KPX Aacute Udieresis -50
418
+ KPX Aacute Ugrave -50
419
+ KPX Aacute Uhungarumlaut -50
420
+ KPX Aacute Umacron -50
421
+ KPX Aacute Uogonek -50
422
+ KPX Aacute Uring -50
423
+ KPX Aacute V -80
424
+ KPX Aacute W -60
425
+ KPX Aacute Y -110
426
+ KPX Aacute Yacute -110
427
+ KPX Aacute Ydieresis -110
428
+ KPX Aacute u -30
429
+ KPX Aacute uacute -30
430
+ KPX Aacute ucircumflex -30
431
+ KPX Aacute udieresis -30
432
+ KPX Aacute ugrave -30
433
+ KPX Aacute uhungarumlaut -30
434
+ KPX Aacute umacron -30
435
+ KPX Aacute uogonek -30
436
+ KPX Aacute uring -30
437
+ KPX Aacute v -40
438
+ KPX Aacute w -30
439
+ KPX Aacute y -30
440
+ KPX Aacute yacute -30
441
+ KPX Aacute ydieresis -30
442
+ KPX Abreve C -40
443
+ KPX Abreve Cacute -40
444
+ KPX Abreve Ccaron -40
445
+ KPX Abreve Ccedilla -40
446
+ KPX Abreve G -50
447
+ KPX Abreve Gbreve -50
448
+ KPX Abreve Gcommaaccent -50
449
+ KPX Abreve O -40
450
+ KPX Abreve Oacute -40
451
+ KPX Abreve Ocircumflex -40
452
+ KPX Abreve Odieresis -40
453
+ KPX Abreve Ograve -40
454
+ KPX Abreve Ohungarumlaut -40
455
+ KPX Abreve Omacron -40
456
+ KPX Abreve Oslash -40
457
+ KPX Abreve Otilde -40
458
+ KPX Abreve Q -40
459
+ KPX Abreve T -90
460
+ KPX Abreve Tcaron -90
461
+ KPX Abreve Tcommaaccent -90
462
+ KPX Abreve U -50
463
+ KPX Abreve Uacute -50
464
+ KPX Abreve Ucircumflex -50
465
+ KPX Abreve Udieresis -50
466
+ KPX Abreve Ugrave -50
467
+ KPX Abreve Uhungarumlaut -50
468
+ KPX Abreve Umacron -50
469
+ KPX Abreve Uogonek -50
470
+ KPX Abreve Uring -50
471
+ KPX Abreve V -80
472
+ KPX Abreve W -60
473
+ KPX Abreve Y -110
474
+ KPX Abreve Yacute -110
475
+ KPX Abreve Ydieresis -110
476
+ KPX Abreve u -30
477
+ KPX Abreve uacute -30
478
+ KPX Abreve ucircumflex -30
479
+ KPX Abreve udieresis -30
480
+ KPX Abreve ugrave -30
481
+ KPX Abreve uhungarumlaut -30
482
+ KPX Abreve umacron -30
483
+ KPX Abreve uogonek -30
484
+ KPX Abreve uring -30
485
+ KPX Abreve v -40
486
+ KPX Abreve w -30
487
+ KPX Abreve y -30
488
+ KPX Abreve yacute -30
489
+ KPX Abreve ydieresis -30
490
+ KPX Acircumflex C -40
491
+ KPX Acircumflex Cacute -40
492
+ KPX Acircumflex Ccaron -40
493
+ KPX Acircumflex Ccedilla -40
494
+ KPX Acircumflex G -50
495
+ KPX Acircumflex Gbreve -50
496
+ KPX Acircumflex Gcommaaccent -50
497
+ KPX Acircumflex O -40
498
+ KPX Acircumflex Oacute -40
499
+ KPX Acircumflex Ocircumflex -40
500
+ KPX Acircumflex Odieresis -40
501
+ KPX Acircumflex Ograve -40
502
+ KPX Acircumflex Ohungarumlaut -40
503
+ KPX Acircumflex Omacron -40
504
+ KPX Acircumflex Oslash -40
505
+ KPX Acircumflex Otilde -40
506
+ KPX Acircumflex Q -40
507
+ KPX Acircumflex T -90
508
+ KPX Acircumflex Tcaron -90
509
+ KPX Acircumflex Tcommaaccent -90
510
+ KPX Acircumflex U -50
511
+ KPX Acircumflex Uacute -50
512
+ KPX Acircumflex Ucircumflex -50
513
+ KPX Acircumflex Udieresis -50
514
+ KPX Acircumflex Ugrave -50
515
+ KPX Acircumflex Uhungarumlaut -50
516
+ KPX Acircumflex Umacron -50
517
+ KPX Acircumflex Uogonek -50
518
+ KPX Acircumflex Uring -50
519
+ KPX Acircumflex V -80
520
+ KPX Acircumflex W -60
521
+ KPX Acircumflex Y -110
522
+ KPX Acircumflex Yacute -110
523
+ KPX Acircumflex Ydieresis -110
524
+ KPX Acircumflex u -30
525
+ KPX Acircumflex uacute -30
526
+ KPX Acircumflex ucircumflex -30
527
+ KPX Acircumflex udieresis -30
528
+ KPX Acircumflex ugrave -30
529
+ KPX Acircumflex uhungarumlaut -30
530
+ KPX Acircumflex umacron -30
531
+ KPX Acircumflex uogonek -30
532
+ KPX Acircumflex uring -30
533
+ KPX Acircumflex v -40
534
+ KPX Acircumflex w -30
535
+ KPX Acircumflex y -30
536
+ KPX Acircumflex yacute -30
537
+ KPX Acircumflex ydieresis -30
538
+ KPX Adieresis C -40
539
+ KPX Adieresis Cacute -40
540
+ KPX Adieresis Ccaron -40
541
+ KPX Adieresis Ccedilla -40
542
+ KPX Adieresis G -50
543
+ KPX Adieresis Gbreve -50
544
+ KPX Adieresis Gcommaaccent -50
545
+ KPX Adieresis O -40
546
+ KPX Adieresis Oacute -40
547
+ KPX Adieresis Ocircumflex -40
548
+ KPX Adieresis Odieresis -40
549
+ KPX Adieresis Ograve -40
550
+ KPX Adieresis Ohungarumlaut -40
551
+ KPX Adieresis Omacron -40
552
+ KPX Adieresis Oslash -40
553
+ KPX Adieresis Otilde -40
554
+ KPX Adieresis Q -40
555
+ KPX Adieresis T -90
556
+ KPX Adieresis Tcaron -90
557
+ KPX Adieresis Tcommaaccent -90
558
+ KPX Adieresis U -50
559
+ KPX Adieresis Uacute -50
560
+ KPX Adieresis Ucircumflex -50
561
+ KPX Adieresis Udieresis -50
562
+ KPX Adieresis Ugrave -50
563
+ KPX Adieresis Uhungarumlaut -50
564
+ KPX Adieresis Umacron -50
565
+ KPX Adieresis Uogonek -50
566
+ KPX Adieresis Uring -50
567
+ KPX Adieresis V -80
568
+ KPX Adieresis W -60
569
+ KPX Adieresis Y -110
570
+ KPX Adieresis Yacute -110
571
+ KPX Adieresis Ydieresis -110
572
+ KPX Adieresis u -30
573
+ KPX Adieresis uacute -30
574
+ KPX Adieresis ucircumflex -30
575
+ KPX Adieresis udieresis -30
576
+ KPX Adieresis ugrave -30
577
+ KPX Adieresis uhungarumlaut -30
578
+ KPX Adieresis umacron -30
579
+ KPX Adieresis uogonek -30
580
+ KPX Adieresis uring -30
581
+ KPX Adieresis v -40
582
+ KPX Adieresis w -30
583
+ KPX Adieresis y -30
584
+ KPX Adieresis yacute -30
585
+ KPX Adieresis ydieresis -30
586
+ KPX Agrave C -40
587
+ KPX Agrave Cacute -40
588
+ KPX Agrave Ccaron -40
589
+ KPX Agrave Ccedilla -40
590
+ KPX Agrave G -50
591
+ KPX Agrave Gbreve -50
592
+ KPX Agrave Gcommaaccent -50
593
+ KPX Agrave O -40
594
+ KPX Agrave Oacute -40
595
+ KPX Agrave Ocircumflex -40
596
+ KPX Agrave Odieresis -40
597
+ KPX Agrave Ograve -40
598
+ KPX Agrave Ohungarumlaut -40
599
+ KPX Agrave Omacron -40
600
+ KPX Agrave Oslash -40
601
+ KPX Agrave Otilde -40
602
+ KPX Agrave Q -40
603
+ KPX Agrave T -90
604
+ KPX Agrave Tcaron -90
605
+ KPX Agrave Tcommaaccent -90
606
+ KPX Agrave U -50
607
+ KPX Agrave Uacute -50
608
+ KPX Agrave Ucircumflex -50
609
+ KPX Agrave Udieresis -50
610
+ KPX Agrave Ugrave -50
611
+ KPX Agrave Uhungarumlaut -50
612
+ KPX Agrave Umacron -50
613
+ KPX Agrave Uogonek -50
614
+ KPX Agrave Uring -50
615
+ KPX Agrave V -80
616
+ KPX Agrave W -60
617
+ KPX Agrave Y -110
618
+ KPX Agrave Yacute -110
619
+ KPX Agrave Ydieresis -110
620
+ KPX Agrave u -30
621
+ KPX Agrave uacute -30
622
+ KPX Agrave ucircumflex -30
623
+ KPX Agrave udieresis -30
624
+ KPX Agrave ugrave -30
625
+ KPX Agrave uhungarumlaut -30
626
+ KPX Agrave umacron -30
627
+ KPX Agrave uogonek -30
628
+ KPX Agrave uring -30
629
+ KPX Agrave v -40
630
+ KPX Agrave w -30
631
+ KPX Agrave y -30
632
+ KPX Agrave yacute -30
633
+ KPX Agrave ydieresis -30
634
+ KPX Amacron C -40
635
+ KPX Amacron Cacute -40
636
+ KPX Amacron Ccaron -40
637
+ KPX Amacron Ccedilla -40
638
+ KPX Amacron G -50
639
+ KPX Amacron Gbreve -50
640
+ KPX Amacron Gcommaaccent -50
641
+ KPX Amacron O -40
642
+ KPX Amacron Oacute -40
643
+ KPX Amacron Ocircumflex -40
644
+ KPX Amacron Odieresis -40
645
+ KPX Amacron Ograve -40
646
+ KPX Amacron Ohungarumlaut -40
647
+ KPX Amacron Omacron -40
648
+ KPX Amacron Oslash -40
649
+ KPX Amacron Otilde -40
650
+ KPX Amacron Q -40
651
+ KPX Amacron T -90
652
+ KPX Amacron Tcaron -90
653
+ KPX Amacron Tcommaaccent -90
654
+ KPX Amacron U -50
655
+ KPX Amacron Uacute -50
656
+ KPX Amacron Ucircumflex -50
657
+ KPX Amacron Udieresis -50
658
+ KPX Amacron Ugrave -50
659
+ KPX Amacron Uhungarumlaut -50
660
+ KPX Amacron Umacron -50
661
+ KPX Amacron Uogonek -50
662
+ KPX Amacron Uring -50
663
+ KPX Amacron V -80
664
+ KPX Amacron W -60
665
+ KPX Amacron Y -110
666
+ KPX Amacron Yacute -110
667
+ KPX Amacron Ydieresis -110
668
+ KPX Amacron u -30
669
+ KPX Amacron uacute -30
670
+ KPX Amacron ucircumflex -30
671
+ KPX Amacron udieresis -30
672
+ KPX Amacron ugrave -30
673
+ KPX Amacron uhungarumlaut -30
674
+ KPX Amacron umacron -30
675
+ KPX Amacron uogonek -30
676
+ KPX Amacron uring -30
677
+ KPX Amacron v -40
678
+ KPX Amacron w -30
679
+ KPX Amacron y -30
680
+ KPX Amacron yacute -30
681
+ KPX Amacron ydieresis -30
682
+ KPX Aogonek C -40
683
+ KPX Aogonek Cacute -40
684
+ KPX Aogonek Ccaron -40
685
+ KPX Aogonek Ccedilla -40
686
+ KPX Aogonek G -50
687
+ KPX Aogonek Gbreve -50
688
+ KPX Aogonek Gcommaaccent -50
689
+ KPX Aogonek O -40
690
+ KPX Aogonek Oacute -40
691
+ KPX Aogonek Ocircumflex -40
692
+ KPX Aogonek Odieresis -40
693
+ KPX Aogonek Ograve -40
694
+ KPX Aogonek Ohungarumlaut -40
695
+ KPX Aogonek Omacron -40
696
+ KPX Aogonek Oslash -40
697
+ KPX Aogonek Otilde -40
698
+ KPX Aogonek Q -40
699
+ KPX Aogonek T -90
700
+ KPX Aogonek Tcaron -90
701
+ KPX Aogonek Tcommaaccent -90
702
+ KPX Aogonek U -50
703
+ KPX Aogonek Uacute -50
704
+ KPX Aogonek Ucircumflex -50
705
+ KPX Aogonek Udieresis -50
706
+ KPX Aogonek Ugrave -50
707
+ KPX Aogonek Uhungarumlaut -50
708
+ KPX Aogonek Umacron -50
709
+ KPX Aogonek Uogonek -50
710
+ KPX Aogonek Uring -50
711
+ KPX Aogonek V -80
712
+ KPX Aogonek W -60
713
+ KPX Aogonek Y -110
714
+ KPX Aogonek Yacute -110
715
+ KPX Aogonek Ydieresis -110
716
+ KPX Aogonek u -30
717
+ KPX Aogonek uacute -30
718
+ KPX Aogonek ucircumflex -30
719
+ KPX Aogonek udieresis -30
720
+ KPX Aogonek ugrave -30
721
+ KPX Aogonek uhungarumlaut -30
722
+ KPX Aogonek umacron -30
723
+ KPX Aogonek uogonek -30
724
+ KPX Aogonek uring -30
725
+ KPX Aogonek v -40
726
+ KPX Aogonek w -30
727
+ KPX Aogonek y -30
728
+ KPX Aogonek yacute -30
729
+ KPX Aogonek ydieresis -30
730
+ KPX Aring C -40
731
+ KPX Aring Cacute -40
732
+ KPX Aring Ccaron -40
733
+ KPX Aring Ccedilla -40
734
+ KPX Aring G -50
735
+ KPX Aring Gbreve -50
736
+ KPX Aring Gcommaaccent -50
737
+ KPX Aring O -40
738
+ KPX Aring Oacute -40
739
+ KPX Aring Ocircumflex -40
740
+ KPX Aring Odieresis -40
741
+ KPX Aring Ograve -40
742
+ KPX Aring Ohungarumlaut -40
743
+ KPX Aring Omacron -40
744
+ KPX Aring Oslash -40
745
+ KPX Aring Otilde -40
746
+ KPX Aring Q -40
747
+ KPX Aring T -90
748
+ KPX Aring Tcaron -90
749
+ KPX Aring Tcommaaccent -90
750
+ KPX Aring U -50
751
+ KPX Aring Uacute -50
752
+ KPX Aring Ucircumflex -50
753
+ KPX Aring Udieresis -50
754
+ KPX Aring Ugrave -50
755
+ KPX Aring Uhungarumlaut -50
756
+ KPX Aring Umacron -50
757
+ KPX Aring Uogonek -50
758
+ KPX Aring Uring -50
759
+ KPX Aring V -80
760
+ KPX Aring W -60
761
+ KPX Aring Y -110
762
+ KPX Aring Yacute -110
763
+ KPX Aring Ydieresis -110
764
+ KPX Aring u -30
765
+ KPX Aring uacute -30
766
+ KPX Aring ucircumflex -30
767
+ KPX Aring udieresis -30
768
+ KPX Aring ugrave -30
769
+ KPX Aring uhungarumlaut -30
770
+ KPX Aring umacron -30
771
+ KPX Aring uogonek -30
772
+ KPX Aring uring -30
773
+ KPX Aring v -40
774
+ KPX Aring w -30
775
+ KPX Aring y -30
776
+ KPX Aring yacute -30
777
+ KPX Aring ydieresis -30
778
+ KPX Atilde C -40
779
+ KPX Atilde Cacute -40
780
+ KPX Atilde Ccaron -40
781
+ KPX Atilde Ccedilla -40
782
+ KPX Atilde G -50
783
+ KPX Atilde Gbreve -50
784
+ KPX Atilde Gcommaaccent -50
785
+ KPX Atilde O -40
786
+ KPX Atilde Oacute -40
787
+ KPX Atilde Ocircumflex -40
788
+ KPX Atilde Odieresis -40
789
+ KPX Atilde Ograve -40
790
+ KPX Atilde Ohungarumlaut -40
791
+ KPX Atilde Omacron -40
792
+ KPX Atilde Oslash -40
793
+ KPX Atilde Otilde -40
794
+ KPX Atilde Q -40
795
+ KPX Atilde T -90
796
+ KPX Atilde Tcaron -90
797
+ KPX Atilde Tcommaaccent -90
798
+ KPX Atilde U -50
799
+ KPX Atilde Uacute -50
800
+ KPX Atilde Ucircumflex -50
801
+ KPX Atilde Udieresis -50
802
+ KPX Atilde Ugrave -50
803
+ KPX Atilde Uhungarumlaut -50
804
+ KPX Atilde Umacron -50
805
+ KPX Atilde Uogonek -50
806
+ KPX Atilde Uring -50
807
+ KPX Atilde V -80
808
+ KPX Atilde W -60
809
+ KPX Atilde Y -110
810
+ KPX Atilde Yacute -110
811
+ KPX Atilde Ydieresis -110
812
+ KPX Atilde u -30
813
+ KPX Atilde uacute -30
814
+ KPX Atilde ucircumflex -30
815
+ KPX Atilde udieresis -30
816
+ KPX Atilde ugrave -30
817
+ KPX Atilde uhungarumlaut -30
818
+ KPX Atilde umacron -30
819
+ KPX Atilde uogonek -30
820
+ KPX Atilde uring -30
821
+ KPX Atilde v -40
822
+ KPX Atilde w -30
823
+ KPX Atilde y -30
824
+ KPX Atilde yacute -30
825
+ KPX Atilde ydieresis -30
826
+ KPX B A -30
827
+ KPX B Aacute -30
828
+ KPX B Abreve -30
829
+ KPX B Acircumflex -30
830
+ KPX B Adieresis -30
831
+ KPX B Agrave -30
832
+ KPX B Amacron -30
833
+ KPX B Aogonek -30
834
+ KPX B Aring -30
835
+ KPX B Atilde -30
836
+ KPX B U -10
837
+ KPX B Uacute -10
838
+ KPX B Ucircumflex -10
839
+ KPX B Udieresis -10
840
+ KPX B Ugrave -10
841
+ KPX B Uhungarumlaut -10
842
+ KPX B Umacron -10
843
+ KPX B Uogonek -10
844
+ KPX B Uring -10
845
+ KPX D A -40
846
+ KPX D Aacute -40
847
+ KPX D Abreve -40
848
+ KPX D Acircumflex -40
849
+ KPX D Adieresis -40
850
+ KPX D Agrave -40
851
+ KPX D Amacron -40
852
+ KPX D Aogonek -40
853
+ KPX D Aring -40
854
+ KPX D Atilde -40
855
+ KPX D V -40
856
+ KPX D W -40
857
+ KPX D Y -70
858
+ KPX D Yacute -70
859
+ KPX D Ydieresis -70
860
+ KPX D comma -30
861
+ KPX D period -30
862
+ KPX Dcaron A -40
863
+ KPX Dcaron Aacute -40
864
+ KPX Dcaron Abreve -40
865
+ KPX Dcaron Acircumflex -40
866
+ KPX Dcaron Adieresis -40
867
+ KPX Dcaron Agrave -40
868
+ KPX Dcaron Amacron -40
869
+ KPX Dcaron Aogonek -40
870
+ KPX Dcaron Aring -40
871
+ KPX Dcaron Atilde -40
872
+ KPX Dcaron V -40
873
+ KPX Dcaron W -40
874
+ KPX Dcaron Y -70
875
+ KPX Dcaron Yacute -70
876
+ KPX Dcaron Ydieresis -70
877
+ KPX Dcaron comma -30
878
+ KPX Dcaron period -30
879
+ KPX Dcroat A -40
880
+ KPX Dcroat Aacute -40
881
+ KPX Dcroat Abreve -40
882
+ KPX Dcroat Acircumflex -40
883
+ KPX Dcroat Adieresis -40
884
+ KPX Dcroat Agrave -40
885
+ KPX Dcroat Amacron -40
886
+ KPX Dcroat Aogonek -40
887
+ KPX Dcroat Aring -40
888
+ KPX Dcroat Atilde -40
889
+ KPX Dcroat V -40
890
+ KPX Dcroat W -40
891
+ KPX Dcroat Y -70
892
+ KPX Dcroat Yacute -70
893
+ KPX Dcroat Ydieresis -70
894
+ KPX Dcroat comma -30
895
+ KPX Dcroat period -30
896
+ KPX F A -80
897
+ KPX F Aacute -80
898
+ KPX F Abreve -80
899
+ KPX F Acircumflex -80
900
+ KPX F Adieresis -80
901
+ KPX F Agrave -80
902
+ KPX F Amacron -80
903
+ KPX F Aogonek -80
904
+ KPX F Aring -80
905
+ KPX F Atilde -80
906
+ KPX F a -20
907
+ KPX F aacute -20
908
+ KPX F abreve -20
909
+ KPX F acircumflex -20
910
+ KPX F adieresis -20
911
+ KPX F agrave -20
912
+ KPX F amacron -20
913
+ KPX F aogonek -20
914
+ KPX F aring -20
915
+ KPX F atilde -20
916
+ KPX F comma -100
917
+ KPX F period -100
918
+ KPX J A -20
919
+ KPX J Aacute -20
920
+ KPX J Abreve -20
921
+ KPX J Acircumflex -20
922
+ KPX J Adieresis -20
923
+ KPX J Agrave -20
924
+ KPX J Amacron -20
925
+ KPX J Aogonek -20
926
+ KPX J Aring -20
927
+ KPX J Atilde -20
928
+ KPX J comma -20
929
+ KPX J period -20
930
+ KPX J u -20
931
+ KPX J uacute -20
932
+ KPX J ucircumflex -20
933
+ KPX J udieresis -20
934
+ KPX J ugrave -20
935
+ KPX J uhungarumlaut -20
936
+ KPX J umacron -20
937
+ KPX J uogonek -20
938
+ KPX J uring -20
939
+ KPX K O -30
940
+ KPX K Oacute -30
941
+ KPX K Ocircumflex -30
942
+ KPX K Odieresis -30
943
+ KPX K Ograve -30
944
+ KPX K Ohungarumlaut -30
945
+ KPX K Omacron -30
946
+ KPX K Oslash -30
947
+ KPX K Otilde -30
948
+ KPX K e -15
949
+ KPX K eacute -15
950
+ KPX K ecaron -15
951
+ KPX K ecircumflex -15
952
+ KPX K edieresis -15
953
+ KPX K edotaccent -15
954
+ KPX K egrave -15
955
+ KPX K emacron -15
956
+ KPX K eogonek -15
957
+ KPX K o -35
958
+ KPX K oacute -35
959
+ KPX K ocircumflex -35
960
+ KPX K odieresis -35
961
+ KPX K ograve -35
962
+ KPX K ohungarumlaut -35
963
+ KPX K omacron -35
964
+ KPX K oslash -35
965
+ KPX K otilde -35
966
+ KPX K u -30
967
+ KPX K uacute -30
968
+ KPX K ucircumflex -30
969
+ KPX K udieresis -30
970
+ KPX K ugrave -30
971
+ KPX K uhungarumlaut -30
972
+ KPX K umacron -30
973
+ KPX K uogonek -30
974
+ KPX K uring -30
975
+ KPX K y -40
976
+ KPX K yacute -40
977
+ KPX K ydieresis -40
978
+ KPX Kcommaaccent O -30
979
+ KPX Kcommaaccent Oacute -30
980
+ KPX Kcommaaccent Ocircumflex -30
981
+ KPX Kcommaaccent Odieresis -30
982
+ KPX Kcommaaccent Ograve -30
983
+ KPX Kcommaaccent Ohungarumlaut -30
984
+ KPX Kcommaaccent Omacron -30
985
+ KPX Kcommaaccent Oslash -30
986
+ KPX Kcommaaccent Otilde -30
987
+ KPX Kcommaaccent e -15
988
+ KPX Kcommaaccent eacute -15
989
+ KPX Kcommaaccent ecaron -15
990
+ KPX Kcommaaccent ecircumflex -15
991
+ KPX Kcommaaccent edieresis -15
992
+ KPX Kcommaaccent edotaccent -15
993
+ KPX Kcommaaccent egrave -15
994
+ KPX Kcommaaccent emacron -15
995
+ KPX Kcommaaccent eogonek -15
996
+ KPX Kcommaaccent o -35
997
+ KPX Kcommaaccent oacute -35
998
+ KPX Kcommaaccent ocircumflex -35
999
+ KPX Kcommaaccent odieresis -35
1000
+ KPX Kcommaaccent ograve -35
1001
+ KPX Kcommaaccent ohungarumlaut -35
1002
+ KPX Kcommaaccent omacron -35
1003
+ KPX Kcommaaccent oslash -35
1004
+ KPX Kcommaaccent otilde -35
1005
+ KPX Kcommaaccent u -30
1006
+ KPX Kcommaaccent uacute -30
1007
+ KPX Kcommaaccent ucircumflex -30
1008
+ KPX Kcommaaccent udieresis -30
1009
+ KPX Kcommaaccent ugrave -30
1010
+ KPX Kcommaaccent uhungarumlaut -30
1011
+ KPX Kcommaaccent umacron -30
1012
+ KPX Kcommaaccent uogonek -30
1013
+ KPX Kcommaaccent uring -30
1014
+ KPX Kcommaaccent y -40
1015
+ KPX Kcommaaccent yacute -40
1016
+ KPX Kcommaaccent ydieresis -40
1017
+ KPX L T -90
1018
+ KPX L Tcaron -90
1019
+ KPX L Tcommaaccent -90
1020
+ KPX L V -110
1021
+ KPX L W -80
1022
+ KPX L Y -120
1023
+ KPX L Yacute -120
1024
+ KPX L Ydieresis -120
1025
+ KPX L quotedblright -140
1026
+ KPX L quoteright -140
1027
+ KPX L y -30
1028
+ KPX L yacute -30
1029
+ KPX L ydieresis -30
1030
+ KPX Lacute T -90
1031
+ KPX Lacute Tcaron -90
1032
+ KPX Lacute Tcommaaccent -90
1033
+ KPX Lacute V -110
1034
+ KPX Lacute W -80
1035
+ KPX Lacute Y -120
1036
+ KPX Lacute Yacute -120
1037
+ KPX Lacute Ydieresis -120
1038
+ KPX Lacute quotedblright -140
1039
+ KPX Lacute quoteright -140
1040
+ KPX Lacute y -30
1041
+ KPX Lacute yacute -30
1042
+ KPX Lacute ydieresis -30
1043
+ KPX Lcommaaccent T -90
1044
+ KPX Lcommaaccent Tcaron -90
1045
+ KPX Lcommaaccent Tcommaaccent -90
1046
+ KPX Lcommaaccent V -110
1047
+ KPX Lcommaaccent W -80
1048
+ KPX Lcommaaccent Y -120
1049
+ KPX Lcommaaccent Yacute -120
1050
+ KPX Lcommaaccent Ydieresis -120
1051
+ KPX Lcommaaccent quotedblright -140
1052
+ KPX Lcommaaccent quoteright -140
1053
+ KPX Lcommaaccent y -30
1054
+ KPX Lcommaaccent yacute -30
1055
+ KPX Lcommaaccent ydieresis -30
1056
+ KPX Lslash T -90
1057
+ KPX Lslash Tcaron -90
1058
+ KPX Lslash Tcommaaccent -90
1059
+ KPX Lslash V -110
1060
+ KPX Lslash W -80
1061
+ KPX Lslash Y -120
1062
+ KPX Lslash Yacute -120
1063
+ KPX Lslash Ydieresis -120
1064
+ KPX Lslash quotedblright -140
1065
+ KPX Lslash quoteright -140
1066
+ KPX Lslash y -30
1067
+ KPX Lslash yacute -30
1068
+ KPX Lslash ydieresis -30
1069
+ KPX O A -50
1070
+ KPX O Aacute -50
1071
+ KPX O Abreve -50
1072
+ KPX O Acircumflex -50
1073
+ KPX O Adieresis -50
1074
+ KPX O Agrave -50
1075
+ KPX O Amacron -50
1076
+ KPX O Aogonek -50
1077
+ KPX O Aring -50
1078
+ KPX O Atilde -50
1079
+ KPX O T -40
1080
+ KPX O Tcaron -40
1081
+ KPX O Tcommaaccent -40
1082
+ KPX O V -50
1083
+ KPX O W -50
1084
+ KPX O X -50
1085
+ KPX O Y -70
1086
+ KPX O Yacute -70
1087
+ KPX O Ydieresis -70
1088
+ KPX O comma -40
1089
+ KPX O period -40
1090
+ KPX Oacute A -50
1091
+ KPX Oacute Aacute -50
1092
+ KPX Oacute Abreve -50
1093
+ KPX Oacute Acircumflex -50
1094
+ KPX Oacute Adieresis -50
1095
+ KPX Oacute Agrave -50
1096
+ KPX Oacute Amacron -50
1097
+ KPX Oacute Aogonek -50
1098
+ KPX Oacute Aring -50
1099
+ KPX Oacute Atilde -50
1100
+ KPX Oacute T -40
1101
+ KPX Oacute Tcaron -40
1102
+ KPX Oacute Tcommaaccent -40
1103
+ KPX Oacute V -50
1104
+ KPX Oacute W -50
1105
+ KPX Oacute X -50
1106
+ KPX Oacute Y -70
1107
+ KPX Oacute Yacute -70
1108
+ KPX Oacute Ydieresis -70
1109
+ KPX Oacute comma -40
1110
+ KPX Oacute period -40
1111
+ KPX Ocircumflex A -50
1112
+ KPX Ocircumflex Aacute -50
1113
+ KPX Ocircumflex Abreve -50
1114
+ KPX Ocircumflex Acircumflex -50
1115
+ KPX Ocircumflex Adieresis -50
1116
+ KPX Ocircumflex Agrave -50
1117
+ KPX Ocircumflex Amacron -50
1118
+ KPX Ocircumflex Aogonek -50
1119
+ KPX Ocircumflex Aring -50
1120
+ KPX Ocircumflex Atilde -50
1121
+ KPX Ocircumflex T -40
1122
+ KPX Ocircumflex Tcaron -40
1123
+ KPX Ocircumflex Tcommaaccent -40
1124
+ KPX Ocircumflex V -50
1125
+ KPX Ocircumflex W -50
1126
+ KPX Ocircumflex X -50
1127
+ KPX Ocircumflex Y -70
1128
+ KPX Ocircumflex Yacute -70
1129
+ KPX Ocircumflex Ydieresis -70
1130
+ KPX Ocircumflex comma -40
1131
+ KPX Ocircumflex period -40
1132
+ KPX Odieresis A -50
1133
+ KPX Odieresis Aacute -50
1134
+ KPX Odieresis Abreve -50
1135
+ KPX Odieresis Acircumflex -50
1136
+ KPX Odieresis Adieresis -50
1137
+ KPX Odieresis Agrave -50
1138
+ KPX Odieresis Amacron -50
1139
+ KPX Odieresis Aogonek -50
1140
+ KPX Odieresis Aring -50
1141
+ KPX Odieresis Atilde -50
1142
+ KPX Odieresis T -40
1143
+ KPX Odieresis Tcaron -40
1144
+ KPX Odieresis Tcommaaccent -40
1145
+ KPX Odieresis V -50
1146
+ KPX Odieresis W -50
1147
+ KPX Odieresis X -50
1148
+ KPX Odieresis Y -70
1149
+ KPX Odieresis Yacute -70
1150
+ KPX Odieresis Ydieresis -70
1151
+ KPX Odieresis comma -40
1152
+ KPX Odieresis period -40
1153
+ KPX Ograve A -50
1154
+ KPX Ograve Aacute -50
1155
+ KPX Ograve Abreve -50
1156
+ KPX Ograve Acircumflex -50
1157
+ KPX Ograve Adieresis -50
1158
+ KPX Ograve Agrave -50
1159
+ KPX Ograve Amacron -50
1160
+ KPX Ograve Aogonek -50
1161
+ KPX Ograve Aring -50
1162
+ KPX Ograve Atilde -50
1163
+ KPX Ograve T -40
1164
+ KPX Ograve Tcaron -40
1165
+ KPX Ograve Tcommaaccent -40
1166
+ KPX Ograve V -50
1167
+ KPX Ograve W -50
1168
+ KPX Ograve X -50
1169
+ KPX Ograve Y -70
1170
+ KPX Ograve Yacute -70
1171
+ KPX Ograve Ydieresis -70
1172
+ KPX Ograve comma -40
1173
+ KPX Ograve period -40
1174
+ KPX Ohungarumlaut A -50
1175
+ KPX Ohungarumlaut Aacute -50
1176
+ KPX Ohungarumlaut Abreve -50
1177
+ KPX Ohungarumlaut Acircumflex -50
1178
+ KPX Ohungarumlaut Adieresis -50
1179
+ KPX Ohungarumlaut Agrave -50
1180
+ KPX Ohungarumlaut Amacron -50
1181
+ KPX Ohungarumlaut Aogonek -50
1182
+ KPX Ohungarumlaut Aring -50
1183
+ KPX Ohungarumlaut Atilde -50
1184
+ KPX Ohungarumlaut T -40
1185
+ KPX Ohungarumlaut Tcaron -40
1186
+ KPX Ohungarumlaut Tcommaaccent -40
1187
+ KPX Ohungarumlaut V -50
1188
+ KPX Ohungarumlaut W -50
1189
+ KPX Ohungarumlaut X -50
1190
+ KPX Ohungarumlaut Y -70
1191
+ KPX Ohungarumlaut Yacute -70
1192
+ KPX Ohungarumlaut Ydieresis -70
1193
+ KPX Ohungarumlaut comma -40
1194
+ KPX Ohungarumlaut period -40
1195
+ KPX Omacron A -50
1196
+ KPX Omacron Aacute -50
1197
+ KPX Omacron Abreve -50
1198
+ KPX Omacron Acircumflex -50
1199
+ KPX Omacron Adieresis -50
1200
+ KPX Omacron Agrave -50
1201
+ KPX Omacron Amacron -50
1202
+ KPX Omacron Aogonek -50
1203
+ KPX Omacron Aring -50
1204
+ KPX Omacron Atilde -50
1205
+ KPX Omacron T -40
1206
+ KPX Omacron Tcaron -40
1207
+ KPX Omacron Tcommaaccent -40
1208
+ KPX Omacron V -50
1209
+ KPX Omacron W -50
1210
+ KPX Omacron X -50
1211
+ KPX Omacron Y -70
1212
+ KPX Omacron Yacute -70
1213
+ KPX Omacron Ydieresis -70
1214
+ KPX Omacron comma -40
1215
+ KPX Omacron period -40
1216
+ KPX Oslash A -50
1217
+ KPX Oslash Aacute -50
1218
+ KPX Oslash Abreve -50
1219
+ KPX Oslash Acircumflex -50
1220
+ KPX Oslash Adieresis -50
1221
+ KPX Oslash Agrave -50
1222
+ KPX Oslash Amacron -50
1223
+ KPX Oslash Aogonek -50
1224
+ KPX Oslash Aring -50
1225
+ KPX Oslash Atilde -50
1226
+ KPX Oslash T -40
1227
+ KPX Oslash Tcaron -40
1228
+ KPX Oslash Tcommaaccent -40
1229
+ KPX Oslash V -50
1230
+ KPX Oslash W -50
1231
+ KPX Oslash X -50
1232
+ KPX Oslash Y -70
1233
+ KPX Oslash Yacute -70
1234
+ KPX Oslash Ydieresis -70
1235
+ KPX Oslash comma -40
1236
+ KPX Oslash period -40
1237
+ KPX Otilde A -50
1238
+ KPX Otilde Aacute -50
1239
+ KPX Otilde Abreve -50
1240
+ KPX Otilde Acircumflex -50
1241
+ KPX Otilde Adieresis -50
1242
+ KPX Otilde Agrave -50
1243
+ KPX Otilde Amacron -50
1244
+ KPX Otilde Aogonek -50
1245
+ KPX Otilde Aring -50
1246
+ KPX Otilde Atilde -50
1247
+ KPX Otilde T -40
1248
+ KPX Otilde Tcaron -40
1249
+ KPX Otilde Tcommaaccent -40
1250
+ KPX Otilde V -50
1251
+ KPX Otilde W -50
1252
+ KPX Otilde X -50
1253
+ KPX Otilde Y -70
1254
+ KPX Otilde Yacute -70
1255
+ KPX Otilde Ydieresis -70
1256
+ KPX Otilde comma -40
1257
+ KPX Otilde period -40
1258
+ KPX P A -100
1259
+ KPX P Aacute -100
1260
+ KPX P Abreve -100
1261
+ KPX P Acircumflex -100
1262
+ KPX P Adieresis -100
1263
+ KPX P Agrave -100
1264
+ KPX P Amacron -100
1265
+ KPX P Aogonek -100
1266
+ KPX P Aring -100
1267
+ KPX P Atilde -100
1268
+ KPX P a -30
1269
+ KPX P aacute -30
1270
+ KPX P abreve -30
1271
+ KPX P acircumflex -30
1272
+ KPX P adieresis -30
1273
+ KPX P agrave -30
1274
+ KPX P amacron -30
1275
+ KPX P aogonek -30
1276
+ KPX P aring -30
1277
+ KPX P atilde -30
1278
+ KPX P comma -120
1279
+ KPX P e -30
1280
+ KPX P eacute -30
1281
+ KPX P ecaron -30
1282
+ KPX P ecircumflex -30
1283
+ KPX P edieresis -30
1284
+ KPX P edotaccent -30
1285
+ KPX P egrave -30
1286
+ KPX P emacron -30
1287
+ KPX P eogonek -30
1288
+ KPX P o -40
1289
+ KPX P oacute -40
1290
+ KPX P ocircumflex -40
1291
+ KPX P odieresis -40
1292
+ KPX P ograve -40
1293
+ KPX P ohungarumlaut -40
1294
+ KPX P omacron -40
1295
+ KPX P oslash -40
1296
+ KPX P otilde -40
1297
+ KPX P period -120
1298
+ KPX Q U -10
1299
+ KPX Q Uacute -10
1300
+ KPX Q Ucircumflex -10
1301
+ KPX Q Udieresis -10
1302
+ KPX Q Ugrave -10
1303
+ KPX Q Uhungarumlaut -10
1304
+ KPX Q Umacron -10
1305
+ KPX Q Uogonek -10
1306
+ KPX Q Uring -10
1307
+ KPX Q comma 20
1308
+ KPX Q period 20
1309
+ KPX R O -20
1310
+ KPX R Oacute -20
1311
+ KPX R Ocircumflex -20
1312
+ KPX R Odieresis -20
1313
+ KPX R Ograve -20
1314
+ KPX R Ohungarumlaut -20
1315
+ KPX R Omacron -20
1316
+ KPX R Oslash -20
1317
+ KPX R Otilde -20
1318
+ KPX R T -20
1319
+ KPX R Tcaron -20
1320
+ KPX R Tcommaaccent -20
1321
+ KPX R U -20
1322
+ KPX R Uacute -20
1323
+ KPX R Ucircumflex -20
1324
+ KPX R Udieresis -20
1325
+ KPX R Ugrave -20
1326
+ KPX R Uhungarumlaut -20
1327
+ KPX R Umacron -20
1328
+ KPX R Uogonek -20
1329
+ KPX R Uring -20
1330
+ KPX R V -50
1331
+ KPX R W -40
1332
+ KPX R Y -50
1333
+ KPX R Yacute -50
1334
+ KPX R Ydieresis -50
1335
+ KPX Racute O -20
1336
+ KPX Racute Oacute -20
1337
+ KPX Racute Ocircumflex -20
1338
+ KPX Racute Odieresis -20
1339
+ KPX Racute Ograve -20
1340
+ KPX Racute Ohungarumlaut -20
1341
+ KPX Racute Omacron -20
1342
+ KPX Racute Oslash -20
1343
+ KPX Racute Otilde -20
1344
+ KPX Racute T -20
1345
+ KPX Racute Tcaron -20
1346
+ KPX Racute Tcommaaccent -20
1347
+ KPX Racute U -20
1348
+ KPX Racute Uacute -20
1349
+ KPX Racute Ucircumflex -20
1350
+ KPX Racute Udieresis -20
1351
+ KPX Racute Ugrave -20
1352
+ KPX Racute Uhungarumlaut -20
1353
+ KPX Racute Umacron -20
1354
+ KPX Racute Uogonek -20
1355
+ KPX Racute Uring -20
1356
+ KPX Racute V -50
1357
+ KPX Racute W -40
1358
+ KPX Racute Y -50
1359
+ KPX Racute Yacute -50
1360
+ KPX Racute Ydieresis -50
1361
+ KPX Rcaron O -20
1362
+ KPX Rcaron Oacute -20
1363
+ KPX Rcaron Ocircumflex -20
1364
+ KPX Rcaron Odieresis -20
1365
+ KPX Rcaron Ograve -20
1366
+ KPX Rcaron Ohungarumlaut -20
1367
+ KPX Rcaron Omacron -20
1368
+ KPX Rcaron Oslash -20
1369
+ KPX Rcaron Otilde -20
1370
+ KPX Rcaron T -20
1371
+ KPX Rcaron Tcaron -20
1372
+ KPX Rcaron Tcommaaccent -20
1373
+ KPX Rcaron U -20
1374
+ KPX Rcaron Uacute -20
1375
+ KPX Rcaron Ucircumflex -20
1376
+ KPX Rcaron Udieresis -20
1377
+ KPX Rcaron Ugrave -20
1378
+ KPX Rcaron Uhungarumlaut -20
1379
+ KPX Rcaron Umacron -20
1380
+ KPX Rcaron Uogonek -20
1381
+ KPX Rcaron Uring -20
1382
+ KPX Rcaron V -50
1383
+ KPX Rcaron W -40
1384
+ KPX Rcaron Y -50
1385
+ KPX Rcaron Yacute -50
1386
+ KPX Rcaron Ydieresis -50
1387
+ KPX Rcommaaccent O -20
1388
+ KPX Rcommaaccent Oacute -20
1389
+ KPX Rcommaaccent Ocircumflex -20
1390
+ KPX Rcommaaccent Odieresis -20
1391
+ KPX Rcommaaccent Ograve -20
1392
+ KPX Rcommaaccent Ohungarumlaut -20
1393
+ KPX Rcommaaccent Omacron -20
1394
+ KPX Rcommaaccent Oslash -20
1395
+ KPX Rcommaaccent Otilde -20
1396
+ KPX Rcommaaccent T -20
1397
+ KPX Rcommaaccent Tcaron -20
1398
+ KPX Rcommaaccent Tcommaaccent -20
1399
+ KPX Rcommaaccent U -20
1400
+ KPX Rcommaaccent Uacute -20
1401
+ KPX Rcommaaccent Ucircumflex -20
1402
+ KPX Rcommaaccent Udieresis -20
1403
+ KPX Rcommaaccent Ugrave -20
1404
+ KPX Rcommaaccent Uhungarumlaut -20
1405
+ KPX Rcommaaccent Umacron -20
1406
+ KPX Rcommaaccent Uogonek -20
1407
+ KPX Rcommaaccent Uring -20
1408
+ KPX Rcommaaccent V -50
1409
+ KPX Rcommaaccent W -40
1410
+ KPX Rcommaaccent Y -50
1411
+ KPX Rcommaaccent Yacute -50
1412
+ KPX Rcommaaccent Ydieresis -50
1413
+ KPX T A -90
1414
+ KPX T Aacute -90
1415
+ KPX T Abreve -90
1416
+ KPX T Acircumflex -90
1417
+ KPX T Adieresis -90
1418
+ KPX T Agrave -90
1419
+ KPX T Amacron -90
1420
+ KPX T Aogonek -90
1421
+ KPX T Aring -90
1422
+ KPX T Atilde -90
1423
+ KPX T O -40
1424
+ KPX T Oacute -40
1425
+ KPX T Ocircumflex -40
1426
+ KPX T Odieresis -40
1427
+ KPX T Ograve -40
1428
+ KPX T Ohungarumlaut -40
1429
+ KPX T Omacron -40
1430
+ KPX T Oslash -40
1431
+ KPX T Otilde -40
1432
+ KPX T a -80
1433
+ KPX T aacute -80
1434
+ KPX T abreve -80
1435
+ KPX T acircumflex -80
1436
+ KPX T adieresis -80
1437
+ KPX T agrave -80
1438
+ KPX T amacron -80
1439
+ KPX T aogonek -80
1440
+ KPX T aring -80
1441
+ KPX T atilde -80
1442
+ KPX T colon -40
1443
+ KPX T comma -80
1444
+ KPX T e -60
1445
+ KPX T eacute -60
1446
+ KPX T ecaron -60
1447
+ KPX T ecircumflex -60
1448
+ KPX T edieresis -60
1449
+ KPX T edotaccent -60
1450
+ KPX T egrave -60
1451
+ KPX T emacron -60
1452
+ KPX T eogonek -60
1453
+ KPX T hyphen -120
1454
+ KPX T o -80
1455
+ KPX T oacute -80
1456
+ KPX T ocircumflex -80
1457
+ KPX T odieresis -80
1458
+ KPX T ograve -80
1459
+ KPX T ohungarumlaut -80
1460
+ KPX T omacron -80
1461
+ KPX T oslash -80
1462
+ KPX T otilde -80
1463
+ KPX T period -80
1464
+ KPX T r -80
1465
+ KPX T racute -80
1466
+ KPX T rcommaaccent -80
1467
+ KPX T semicolon -40
1468
+ KPX T u -90
1469
+ KPX T uacute -90
1470
+ KPX T ucircumflex -90
1471
+ KPX T udieresis -90
1472
+ KPX T ugrave -90
1473
+ KPX T uhungarumlaut -90
1474
+ KPX T umacron -90
1475
+ KPX T uogonek -90
1476
+ KPX T uring -90
1477
+ KPX T w -60
1478
+ KPX T y -60
1479
+ KPX T yacute -60
1480
+ KPX T ydieresis -60
1481
+ KPX Tcaron A -90
1482
+ KPX Tcaron Aacute -90
1483
+ KPX Tcaron Abreve -90
1484
+ KPX Tcaron Acircumflex -90
1485
+ KPX Tcaron Adieresis -90
1486
+ KPX Tcaron Agrave -90
1487
+ KPX Tcaron Amacron -90
1488
+ KPX Tcaron Aogonek -90
1489
+ KPX Tcaron Aring -90
1490
+ KPX Tcaron Atilde -90
1491
+ KPX Tcaron O -40
1492
+ KPX Tcaron Oacute -40
1493
+ KPX Tcaron Ocircumflex -40
1494
+ KPX Tcaron Odieresis -40
1495
+ KPX Tcaron Ograve -40
1496
+ KPX Tcaron Ohungarumlaut -40
1497
+ KPX Tcaron Omacron -40
1498
+ KPX Tcaron Oslash -40
1499
+ KPX Tcaron Otilde -40
1500
+ KPX Tcaron a -80
1501
+ KPX Tcaron aacute -80
1502
+ KPX Tcaron abreve -80
1503
+ KPX Tcaron acircumflex -80
1504
+ KPX Tcaron adieresis -80
1505
+ KPX Tcaron agrave -80
1506
+ KPX Tcaron amacron -80
1507
+ KPX Tcaron aogonek -80
1508
+ KPX Tcaron aring -80
1509
+ KPX Tcaron atilde -80
1510
+ KPX Tcaron colon -40
1511
+ KPX Tcaron comma -80
1512
+ KPX Tcaron e -60
1513
+ KPX Tcaron eacute -60
1514
+ KPX Tcaron ecaron -60
1515
+ KPX Tcaron ecircumflex -60
1516
+ KPX Tcaron edieresis -60
1517
+ KPX Tcaron edotaccent -60
1518
+ KPX Tcaron egrave -60
1519
+ KPX Tcaron emacron -60
1520
+ KPX Tcaron eogonek -60
1521
+ KPX Tcaron hyphen -120
1522
+ KPX Tcaron o -80
1523
+ KPX Tcaron oacute -80
1524
+ KPX Tcaron ocircumflex -80
1525
+ KPX Tcaron odieresis -80
1526
+ KPX Tcaron ograve -80
1527
+ KPX Tcaron ohungarumlaut -80
1528
+ KPX Tcaron omacron -80
1529
+ KPX Tcaron oslash -80
1530
+ KPX Tcaron otilde -80
1531
+ KPX Tcaron period -80
1532
+ KPX Tcaron r -80
1533
+ KPX Tcaron racute -80
1534
+ KPX Tcaron rcommaaccent -80
1535
+ KPX Tcaron semicolon -40
1536
+ KPX Tcaron u -90
1537
+ KPX Tcaron uacute -90
1538
+ KPX Tcaron ucircumflex -90
1539
+ KPX Tcaron udieresis -90
1540
+ KPX Tcaron ugrave -90
1541
+ KPX Tcaron uhungarumlaut -90
1542
+ KPX Tcaron umacron -90
1543
+ KPX Tcaron uogonek -90
1544
+ KPX Tcaron uring -90
1545
+ KPX Tcaron w -60
1546
+ KPX Tcaron y -60
1547
+ KPX Tcaron yacute -60
1548
+ KPX Tcaron ydieresis -60
1549
+ KPX Tcommaaccent A -90
1550
+ KPX Tcommaaccent Aacute -90
1551
+ KPX Tcommaaccent Abreve -90
1552
+ KPX Tcommaaccent Acircumflex -90
1553
+ KPX Tcommaaccent Adieresis -90
1554
+ KPX Tcommaaccent Agrave -90
1555
+ KPX Tcommaaccent Amacron -90
1556
+ KPX Tcommaaccent Aogonek -90
1557
+ KPX Tcommaaccent Aring -90
1558
+ KPX Tcommaaccent Atilde -90
1559
+ KPX Tcommaaccent O -40
1560
+ KPX Tcommaaccent Oacute -40
1561
+ KPX Tcommaaccent Ocircumflex -40
1562
+ KPX Tcommaaccent Odieresis -40
1563
+ KPX Tcommaaccent Ograve -40
1564
+ KPX Tcommaaccent Ohungarumlaut -40
1565
+ KPX Tcommaaccent Omacron -40
1566
+ KPX Tcommaaccent Oslash -40
1567
+ KPX Tcommaaccent Otilde -40
1568
+ KPX Tcommaaccent a -80
1569
+ KPX Tcommaaccent aacute -80
1570
+ KPX Tcommaaccent abreve -80
1571
+ KPX Tcommaaccent acircumflex -80
1572
+ KPX Tcommaaccent adieresis -80
1573
+ KPX Tcommaaccent agrave -80
1574
+ KPX Tcommaaccent amacron -80
1575
+ KPX Tcommaaccent aogonek -80
1576
+ KPX Tcommaaccent aring -80
1577
+ KPX Tcommaaccent atilde -80
1578
+ KPX Tcommaaccent colon -40
1579
+ KPX Tcommaaccent comma -80
1580
+ KPX Tcommaaccent e -60
1581
+ KPX Tcommaaccent eacute -60
1582
+ KPX Tcommaaccent ecaron -60
1583
+ KPX Tcommaaccent ecircumflex -60
1584
+ KPX Tcommaaccent edieresis -60
1585
+ KPX Tcommaaccent edotaccent -60
1586
+ KPX Tcommaaccent egrave -60
1587
+ KPX Tcommaaccent emacron -60
1588
+ KPX Tcommaaccent eogonek -60
1589
+ KPX Tcommaaccent hyphen -120
1590
+ KPX Tcommaaccent o -80
1591
+ KPX Tcommaaccent oacute -80
1592
+ KPX Tcommaaccent ocircumflex -80
1593
+ KPX Tcommaaccent odieresis -80
1594
+ KPX Tcommaaccent ograve -80
1595
+ KPX Tcommaaccent ohungarumlaut -80
1596
+ KPX Tcommaaccent omacron -80
1597
+ KPX Tcommaaccent oslash -80
1598
+ KPX Tcommaaccent otilde -80
1599
+ KPX Tcommaaccent period -80
1600
+ KPX Tcommaaccent r -80
1601
+ KPX Tcommaaccent racute -80
1602
+ KPX Tcommaaccent rcommaaccent -80
1603
+ KPX Tcommaaccent semicolon -40
1604
+ KPX Tcommaaccent u -90
1605
+ KPX Tcommaaccent uacute -90
1606
+ KPX Tcommaaccent ucircumflex -90
1607
+ KPX Tcommaaccent udieresis -90
1608
+ KPX Tcommaaccent ugrave -90
1609
+ KPX Tcommaaccent uhungarumlaut -90
1610
+ KPX Tcommaaccent umacron -90
1611
+ KPX Tcommaaccent uogonek -90
1612
+ KPX Tcommaaccent uring -90
1613
+ KPX Tcommaaccent w -60
1614
+ KPX Tcommaaccent y -60
1615
+ KPX Tcommaaccent yacute -60
1616
+ KPX Tcommaaccent ydieresis -60
1617
+ KPX U A -50
1618
+ KPX U Aacute -50
1619
+ KPX U Abreve -50
1620
+ KPX U Acircumflex -50
1621
+ KPX U Adieresis -50
1622
+ KPX U Agrave -50
1623
+ KPX U Amacron -50
1624
+ KPX U Aogonek -50
1625
+ KPX U Aring -50
1626
+ KPX U Atilde -50
1627
+ KPX U comma -30
1628
+ KPX U period -30
1629
+ KPX Uacute A -50
1630
+ KPX Uacute Aacute -50
1631
+ KPX Uacute Abreve -50
1632
+ KPX Uacute Acircumflex -50
1633
+ KPX Uacute Adieresis -50
1634
+ KPX Uacute Agrave -50
1635
+ KPX Uacute Amacron -50
1636
+ KPX Uacute Aogonek -50
1637
+ KPX Uacute Aring -50
1638
+ KPX Uacute Atilde -50
1639
+ KPX Uacute comma -30
1640
+ KPX Uacute period -30
1641
+ KPX Ucircumflex A -50
1642
+ KPX Ucircumflex Aacute -50
1643
+ KPX Ucircumflex Abreve -50
1644
+ KPX Ucircumflex Acircumflex -50
1645
+ KPX Ucircumflex Adieresis -50
1646
+ KPX Ucircumflex Agrave -50
1647
+ KPX Ucircumflex Amacron -50
1648
+ KPX Ucircumflex Aogonek -50
1649
+ KPX Ucircumflex Aring -50
1650
+ KPX Ucircumflex Atilde -50
1651
+ KPX Ucircumflex comma -30
1652
+ KPX Ucircumflex period -30
1653
+ KPX Udieresis A -50
1654
+ KPX Udieresis Aacute -50
1655
+ KPX Udieresis Abreve -50
1656
+ KPX Udieresis Acircumflex -50
1657
+ KPX Udieresis Adieresis -50
1658
+ KPX Udieresis Agrave -50
1659
+ KPX Udieresis Amacron -50
1660
+ KPX Udieresis Aogonek -50
1661
+ KPX Udieresis Aring -50
1662
+ KPX Udieresis Atilde -50
1663
+ KPX Udieresis comma -30
1664
+ KPX Udieresis period -30
1665
+ KPX Ugrave A -50
1666
+ KPX Ugrave Aacute -50
1667
+ KPX Ugrave Abreve -50
1668
+ KPX Ugrave Acircumflex -50
1669
+ KPX Ugrave Adieresis -50
1670
+ KPX Ugrave Agrave -50
1671
+ KPX Ugrave Amacron -50
1672
+ KPX Ugrave Aogonek -50
1673
+ KPX Ugrave Aring -50
1674
+ KPX Ugrave Atilde -50
1675
+ KPX Ugrave comma -30
1676
+ KPX Ugrave period -30
1677
+ KPX Uhungarumlaut A -50
1678
+ KPX Uhungarumlaut Aacute -50
1679
+ KPX Uhungarumlaut Abreve -50
1680
+ KPX Uhungarumlaut Acircumflex -50
1681
+ KPX Uhungarumlaut Adieresis -50
1682
+ KPX Uhungarumlaut Agrave -50
1683
+ KPX Uhungarumlaut Amacron -50
1684
+ KPX Uhungarumlaut Aogonek -50
1685
+ KPX Uhungarumlaut Aring -50
1686
+ KPX Uhungarumlaut Atilde -50
1687
+ KPX Uhungarumlaut comma -30
1688
+ KPX Uhungarumlaut period -30
1689
+ KPX Umacron A -50
1690
+ KPX Umacron Aacute -50
1691
+ KPX Umacron Abreve -50
1692
+ KPX Umacron Acircumflex -50
1693
+ KPX Umacron Adieresis -50
1694
+ KPX Umacron Agrave -50
1695
+ KPX Umacron Amacron -50
1696
+ KPX Umacron Aogonek -50
1697
+ KPX Umacron Aring -50
1698
+ KPX Umacron Atilde -50
1699
+ KPX Umacron comma -30
1700
+ KPX Umacron period -30
1701
+ KPX Uogonek A -50
1702
+ KPX Uogonek Aacute -50
1703
+ KPX Uogonek Abreve -50
1704
+ KPX Uogonek Acircumflex -50
1705
+ KPX Uogonek Adieresis -50
1706
+ KPX Uogonek Agrave -50
1707
+ KPX Uogonek Amacron -50
1708
+ KPX Uogonek Aogonek -50
1709
+ KPX Uogonek Aring -50
1710
+ KPX Uogonek Atilde -50
1711
+ KPX Uogonek comma -30
1712
+ KPX Uogonek period -30
1713
+ KPX Uring A -50
1714
+ KPX Uring Aacute -50
1715
+ KPX Uring Abreve -50
1716
+ KPX Uring Acircumflex -50
1717
+ KPX Uring Adieresis -50
1718
+ KPX Uring Agrave -50
1719
+ KPX Uring Amacron -50
1720
+ KPX Uring Aogonek -50
1721
+ KPX Uring Aring -50
1722
+ KPX Uring Atilde -50
1723
+ KPX Uring comma -30
1724
+ KPX Uring period -30
1725
+ KPX V A -80
1726
+ KPX V Aacute -80
1727
+ KPX V Abreve -80
1728
+ KPX V Acircumflex -80
1729
+ KPX V Adieresis -80
1730
+ KPX V Agrave -80
1731
+ KPX V Amacron -80
1732
+ KPX V Aogonek -80
1733
+ KPX V Aring -80
1734
+ KPX V Atilde -80
1735
+ KPX V G -50
1736
+ KPX V Gbreve -50
1737
+ KPX V Gcommaaccent -50
1738
+ KPX V O -50
1739
+ KPX V Oacute -50
1740
+ KPX V Ocircumflex -50
1741
+ KPX V Odieresis -50
1742
+ KPX V Ograve -50
1743
+ KPX V Ohungarumlaut -50
1744
+ KPX V Omacron -50
1745
+ KPX V Oslash -50
1746
+ KPX V Otilde -50
1747
+ KPX V a -60
1748
+ KPX V aacute -60
1749
+ KPX V abreve -60
1750
+ KPX V acircumflex -60
1751
+ KPX V adieresis -60
1752
+ KPX V agrave -60
1753
+ KPX V amacron -60
1754
+ KPX V aogonek -60
1755
+ KPX V aring -60
1756
+ KPX V atilde -60
1757
+ KPX V colon -40
1758
+ KPX V comma -120
1759
+ KPX V e -50
1760
+ KPX V eacute -50
1761
+ KPX V ecaron -50
1762
+ KPX V ecircumflex -50
1763
+ KPX V edieresis -50
1764
+ KPX V edotaccent -50
1765
+ KPX V egrave -50
1766
+ KPX V emacron -50
1767
+ KPX V eogonek -50
1768
+ KPX V hyphen -80
1769
+ KPX V o -90
1770
+ KPX V oacute -90
1771
+ KPX V ocircumflex -90
1772
+ KPX V odieresis -90
1773
+ KPX V ograve -90
1774
+ KPX V ohungarumlaut -90
1775
+ KPX V omacron -90
1776
+ KPX V oslash -90
1777
+ KPX V otilde -90
1778
+ KPX V period -120
1779
+ KPX V semicolon -40
1780
+ KPX V u -60
1781
+ KPX V uacute -60
1782
+ KPX V ucircumflex -60
1783
+ KPX V udieresis -60
1784
+ KPX V ugrave -60
1785
+ KPX V uhungarumlaut -60
1786
+ KPX V umacron -60
1787
+ KPX V uogonek -60
1788
+ KPX V uring -60
1789
+ KPX W A -60
1790
+ KPX W Aacute -60
1791
+ KPX W Abreve -60
1792
+ KPX W Acircumflex -60
1793
+ KPX W Adieresis -60
1794
+ KPX W Agrave -60
1795
+ KPX W Amacron -60
1796
+ KPX W Aogonek -60
1797
+ KPX W Aring -60
1798
+ KPX W Atilde -60
1799
+ KPX W O -20
1800
+ KPX W Oacute -20
1801
+ KPX W Ocircumflex -20
1802
+ KPX W Odieresis -20
1803
+ KPX W Ograve -20
1804
+ KPX W Ohungarumlaut -20
1805
+ KPX W Omacron -20
1806
+ KPX W Oslash -20
1807
+ KPX W Otilde -20
1808
+ KPX W a -40
1809
+ KPX W aacute -40
1810
+ KPX W abreve -40
1811
+ KPX W acircumflex -40
1812
+ KPX W adieresis -40
1813
+ KPX W agrave -40
1814
+ KPX W amacron -40
1815
+ KPX W aogonek -40
1816
+ KPX W aring -40
1817
+ KPX W atilde -40
1818
+ KPX W colon -10
1819
+ KPX W comma -80
1820
+ KPX W e -35
1821
+ KPX W eacute -35
1822
+ KPX W ecaron -35
1823
+ KPX W ecircumflex -35
1824
+ KPX W edieresis -35
1825
+ KPX W edotaccent -35
1826
+ KPX W egrave -35
1827
+ KPX W emacron -35
1828
+ KPX W eogonek -35
1829
+ KPX W hyphen -40
1830
+ KPX W o -60
1831
+ KPX W oacute -60
1832
+ KPX W ocircumflex -60
1833
+ KPX W odieresis -60
1834
+ KPX W ograve -60
1835
+ KPX W ohungarumlaut -60
1836
+ KPX W omacron -60
1837
+ KPX W oslash -60
1838
+ KPX W otilde -60
1839
+ KPX W period -80
1840
+ KPX W semicolon -10
1841
+ KPX W u -45
1842
+ KPX W uacute -45
1843
+ KPX W ucircumflex -45
1844
+ KPX W udieresis -45
1845
+ KPX W ugrave -45
1846
+ KPX W uhungarumlaut -45
1847
+ KPX W umacron -45
1848
+ KPX W uogonek -45
1849
+ KPX W uring -45
1850
+ KPX W y -20
1851
+ KPX W yacute -20
1852
+ KPX W ydieresis -20
1853
+ KPX Y A -110
1854
+ KPX Y Aacute -110
1855
+ KPX Y Abreve -110
1856
+ KPX Y Acircumflex -110
1857
+ KPX Y Adieresis -110
1858
+ KPX Y Agrave -110
1859
+ KPX Y Amacron -110
1860
+ KPX Y Aogonek -110
1861
+ KPX Y Aring -110
1862
+ KPX Y Atilde -110
1863
+ KPX Y O -70
1864
+ KPX Y Oacute -70
1865
+ KPX Y Ocircumflex -70
1866
+ KPX Y Odieresis -70
1867
+ KPX Y Ograve -70
1868
+ KPX Y Ohungarumlaut -70
1869
+ KPX Y Omacron -70
1870
+ KPX Y Oslash -70
1871
+ KPX Y Otilde -70
1872
+ KPX Y a -90
1873
+ KPX Y aacute -90
1874
+ KPX Y abreve -90
1875
+ KPX Y acircumflex -90
1876
+ KPX Y adieresis -90
1877
+ KPX Y agrave -90
1878
+ KPX Y amacron -90
1879
+ KPX Y aogonek -90
1880
+ KPX Y aring -90
1881
+ KPX Y atilde -90
1882
+ KPX Y colon -50
1883
+ KPX Y comma -100
1884
+ KPX Y e -80
1885
+ KPX Y eacute -80
1886
+ KPX Y ecaron -80
1887
+ KPX Y ecircumflex -80
1888
+ KPX Y edieresis -80
1889
+ KPX Y edotaccent -80
1890
+ KPX Y egrave -80
1891
+ KPX Y emacron -80
1892
+ KPX Y eogonek -80
1893
+ KPX Y o -100
1894
+ KPX Y oacute -100
1895
+ KPX Y ocircumflex -100
1896
+ KPX Y odieresis -100
1897
+ KPX Y ograve -100
1898
+ KPX Y ohungarumlaut -100
1899
+ KPX Y omacron -100
1900
+ KPX Y oslash -100
1901
+ KPX Y otilde -100
1902
+ KPX Y period -100
1903
+ KPX Y semicolon -50
1904
+ KPX Y u -100
1905
+ KPX Y uacute -100
1906
+ KPX Y ucircumflex -100
1907
+ KPX Y udieresis -100
1908
+ KPX Y ugrave -100
1909
+ KPX Y uhungarumlaut -100
1910
+ KPX Y umacron -100
1911
+ KPX Y uogonek -100
1912
+ KPX Y uring -100
1913
+ KPX Yacute A -110
1914
+ KPX Yacute Aacute -110
1915
+ KPX Yacute Abreve -110
1916
+ KPX Yacute Acircumflex -110
1917
+ KPX Yacute Adieresis -110
1918
+ KPX Yacute Agrave -110
1919
+ KPX Yacute Amacron -110
1920
+ KPX Yacute Aogonek -110
1921
+ KPX Yacute Aring -110
1922
+ KPX Yacute Atilde -110
1923
+ KPX Yacute O -70
1924
+ KPX Yacute Oacute -70
1925
+ KPX Yacute Ocircumflex -70
1926
+ KPX Yacute Odieresis -70
1927
+ KPX Yacute Ograve -70
1928
+ KPX Yacute Ohungarumlaut -70
1929
+ KPX Yacute Omacron -70
1930
+ KPX Yacute Oslash -70
1931
+ KPX Yacute Otilde -70
1932
+ KPX Yacute a -90
1933
+ KPX Yacute aacute -90
1934
+ KPX Yacute abreve -90
1935
+ KPX Yacute acircumflex -90
1936
+ KPX Yacute adieresis -90
1937
+ KPX Yacute agrave -90
1938
+ KPX Yacute amacron -90
1939
+ KPX Yacute aogonek -90
1940
+ KPX Yacute aring -90
1941
+ KPX Yacute atilde -90
1942
+ KPX Yacute colon -50
1943
+ KPX Yacute comma -100
1944
+ KPX Yacute e -80
1945
+ KPX Yacute eacute -80
1946
+ KPX Yacute ecaron -80
1947
+ KPX Yacute ecircumflex -80
1948
+ KPX Yacute edieresis -80
1949
+ KPX Yacute edotaccent -80
1950
+ KPX Yacute egrave -80
1951
+ KPX Yacute emacron -80
1952
+ KPX Yacute eogonek -80
1953
+ KPX Yacute o -100
1954
+ KPX Yacute oacute -100
1955
+ KPX Yacute ocircumflex -100
1956
+ KPX Yacute odieresis -100
1957
+ KPX Yacute ograve -100
1958
+ KPX Yacute ohungarumlaut -100
1959
+ KPX Yacute omacron -100
1960
+ KPX Yacute oslash -100
1961
+ KPX Yacute otilde -100
1962
+ KPX Yacute period -100
1963
+ KPX Yacute semicolon -50
1964
+ KPX Yacute u -100
1965
+ KPX Yacute uacute -100
1966
+ KPX Yacute ucircumflex -100
1967
+ KPX Yacute udieresis -100
1968
+ KPX Yacute ugrave -100
1969
+ KPX Yacute uhungarumlaut -100
1970
+ KPX Yacute umacron -100
1971
+ KPX Yacute uogonek -100
1972
+ KPX Yacute uring -100
1973
+ KPX Ydieresis A -110
1974
+ KPX Ydieresis Aacute -110
1975
+ KPX Ydieresis Abreve -110
1976
+ KPX Ydieresis Acircumflex -110
1977
+ KPX Ydieresis Adieresis -110
1978
+ KPX Ydieresis Agrave -110
1979
+ KPX Ydieresis Amacron -110
1980
+ KPX Ydieresis Aogonek -110
1981
+ KPX Ydieresis Aring -110
1982
+ KPX Ydieresis Atilde -110
1983
+ KPX Ydieresis O -70
1984
+ KPX Ydieresis Oacute -70
1985
+ KPX Ydieresis Ocircumflex -70
1986
+ KPX Ydieresis Odieresis -70
1987
+ KPX Ydieresis Ograve -70
1988
+ KPX Ydieresis Ohungarumlaut -70
1989
+ KPX Ydieresis Omacron -70
1990
+ KPX Ydieresis Oslash -70
1991
+ KPX Ydieresis Otilde -70
1992
+ KPX Ydieresis a -90
1993
+ KPX Ydieresis aacute -90
1994
+ KPX Ydieresis abreve -90
1995
+ KPX Ydieresis acircumflex -90
1996
+ KPX Ydieresis adieresis -90
1997
+ KPX Ydieresis agrave -90
1998
+ KPX Ydieresis amacron -90
1999
+ KPX Ydieresis aogonek -90
2000
+ KPX Ydieresis aring -90
2001
+ KPX Ydieresis atilde -90
2002
+ KPX Ydieresis colon -50
2003
+ KPX Ydieresis comma -100
2004
+ KPX Ydieresis e -80
2005
+ KPX Ydieresis eacute -80
2006
+ KPX Ydieresis ecaron -80
2007
+ KPX Ydieresis ecircumflex -80
2008
+ KPX Ydieresis edieresis -80
2009
+ KPX Ydieresis edotaccent -80
2010
+ KPX Ydieresis egrave -80
2011
+ KPX Ydieresis emacron -80
2012
+ KPX Ydieresis eogonek -80
2013
+ KPX Ydieresis o -100
2014
+ KPX Ydieresis oacute -100
2015
+ KPX Ydieresis ocircumflex -100
2016
+ KPX Ydieresis odieresis -100
2017
+ KPX Ydieresis ograve -100
2018
+ KPX Ydieresis ohungarumlaut -100
2019
+ KPX Ydieresis omacron -100
2020
+ KPX Ydieresis oslash -100
2021
+ KPX Ydieresis otilde -100
2022
+ KPX Ydieresis period -100
2023
+ KPX Ydieresis semicolon -50
2024
+ KPX Ydieresis u -100
2025
+ KPX Ydieresis uacute -100
2026
+ KPX Ydieresis ucircumflex -100
2027
+ KPX Ydieresis udieresis -100
2028
+ KPX Ydieresis ugrave -100
2029
+ KPX Ydieresis uhungarumlaut -100
2030
+ KPX Ydieresis umacron -100
2031
+ KPX Ydieresis uogonek -100
2032
+ KPX Ydieresis uring -100
2033
+ KPX a g -10
2034
+ KPX a gbreve -10
2035
+ KPX a gcommaaccent -10
2036
+ KPX a v -15
2037
+ KPX a w -15
2038
+ KPX a y -20
2039
+ KPX a yacute -20
2040
+ KPX a ydieresis -20
2041
+ KPX aacute g -10
2042
+ KPX aacute gbreve -10
2043
+ KPX aacute gcommaaccent -10
2044
+ KPX aacute v -15
2045
+ KPX aacute w -15
2046
+ KPX aacute y -20
2047
+ KPX aacute yacute -20
2048
+ KPX aacute ydieresis -20
2049
+ KPX abreve g -10
2050
+ KPX abreve gbreve -10
2051
+ KPX abreve gcommaaccent -10
2052
+ KPX abreve v -15
2053
+ KPX abreve w -15
2054
+ KPX abreve y -20
2055
+ KPX abreve yacute -20
2056
+ KPX abreve ydieresis -20
2057
+ KPX acircumflex g -10
2058
+ KPX acircumflex gbreve -10
2059
+ KPX acircumflex gcommaaccent -10
2060
+ KPX acircumflex v -15
2061
+ KPX acircumflex w -15
2062
+ KPX acircumflex y -20
2063
+ KPX acircumflex yacute -20
2064
+ KPX acircumflex ydieresis -20
2065
+ KPX adieresis g -10
2066
+ KPX adieresis gbreve -10
2067
+ KPX adieresis gcommaaccent -10
2068
+ KPX adieresis v -15
2069
+ KPX adieresis w -15
2070
+ KPX adieresis y -20
2071
+ KPX adieresis yacute -20
2072
+ KPX adieresis ydieresis -20
2073
+ KPX agrave g -10
2074
+ KPX agrave gbreve -10
2075
+ KPX agrave gcommaaccent -10
2076
+ KPX agrave v -15
2077
+ KPX agrave w -15
2078
+ KPX agrave y -20
2079
+ KPX agrave yacute -20
2080
+ KPX agrave ydieresis -20
2081
+ KPX amacron g -10
2082
+ KPX amacron gbreve -10
2083
+ KPX amacron gcommaaccent -10
2084
+ KPX amacron v -15
2085
+ KPX amacron w -15
2086
+ KPX amacron y -20
2087
+ KPX amacron yacute -20
2088
+ KPX amacron ydieresis -20
2089
+ KPX aogonek g -10
2090
+ KPX aogonek gbreve -10
2091
+ KPX aogonek gcommaaccent -10
2092
+ KPX aogonek v -15
2093
+ KPX aogonek w -15
2094
+ KPX aogonek y -20
2095
+ KPX aogonek yacute -20
2096
+ KPX aogonek ydieresis -20
2097
+ KPX aring g -10
2098
+ KPX aring gbreve -10
2099
+ KPX aring gcommaaccent -10
2100
+ KPX aring v -15
2101
+ KPX aring w -15
2102
+ KPX aring y -20
2103
+ KPX aring yacute -20
2104
+ KPX aring ydieresis -20
2105
+ KPX atilde g -10
2106
+ KPX atilde gbreve -10
2107
+ KPX atilde gcommaaccent -10
2108
+ KPX atilde v -15
2109
+ KPX atilde w -15
2110
+ KPX atilde y -20
2111
+ KPX atilde yacute -20
2112
+ KPX atilde ydieresis -20
2113
+ KPX b l -10
2114
+ KPX b lacute -10
2115
+ KPX b lcommaaccent -10
2116
+ KPX b lslash -10
2117
+ KPX b u -20
2118
+ KPX b uacute -20
2119
+ KPX b ucircumflex -20
2120
+ KPX b udieresis -20
2121
+ KPX b ugrave -20
2122
+ KPX b uhungarumlaut -20
2123
+ KPX b umacron -20
2124
+ KPX b uogonek -20
2125
+ KPX b uring -20
2126
+ KPX b v -20
2127
+ KPX b y -20
2128
+ KPX b yacute -20
2129
+ KPX b ydieresis -20
2130
+ KPX c h -10
2131
+ KPX c k -20
2132
+ KPX c kcommaaccent -20
2133
+ KPX c l -20
2134
+ KPX c lacute -20
2135
+ KPX c lcommaaccent -20
2136
+ KPX c lslash -20
2137
+ KPX c y -10
2138
+ KPX c yacute -10
2139
+ KPX c ydieresis -10
2140
+ KPX cacute h -10
2141
+ KPX cacute k -20
2142
+ KPX cacute kcommaaccent -20
2143
+ KPX cacute l -20
2144
+ KPX cacute lacute -20
2145
+ KPX cacute lcommaaccent -20
2146
+ KPX cacute lslash -20
2147
+ KPX cacute y -10
2148
+ KPX cacute yacute -10
2149
+ KPX cacute ydieresis -10
2150
+ KPX ccaron h -10
2151
+ KPX ccaron k -20
2152
+ KPX ccaron kcommaaccent -20
2153
+ KPX ccaron l -20
2154
+ KPX ccaron lacute -20
2155
+ KPX ccaron lcommaaccent -20
2156
+ KPX ccaron lslash -20
2157
+ KPX ccaron y -10
2158
+ KPX ccaron yacute -10
2159
+ KPX ccaron ydieresis -10
2160
+ KPX ccedilla h -10
2161
+ KPX ccedilla k -20
2162
+ KPX ccedilla kcommaaccent -20
2163
+ KPX ccedilla l -20
2164
+ KPX ccedilla lacute -20
2165
+ KPX ccedilla lcommaaccent -20
2166
+ KPX ccedilla lslash -20
2167
+ KPX ccedilla y -10
2168
+ KPX ccedilla yacute -10
2169
+ KPX ccedilla ydieresis -10
2170
+ KPX colon space -40
2171
+ KPX comma quotedblright -120
2172
+ KPX comma quoteright -120
2173
+ KPX comma space -40
2174
+ KPX d d -10
2175
+ KPX d dcroat -10
2176
+ KPX d v -15
2177
+ KPX d w -15
2178
+ KPX d y -15
2179
+ KPX d yacute -15
2180
+ KPX d ydieresis -15
2181
+ KPX dcroat d -10
2182
+ KPX dcroat dcroat -10
2183
+ KPX dcroat v -15
2184
+ KPX dcroat w -15
2185
+ KPX dcroat y -15
2186
+ KPX dcroat yacute -15
2187
+ KPX dcroat ydieresis -15
2188
+ KPX e comma 10
2189
+ KPX e period 20
2190
+ KPX e v -15
2191
+ KPX e w -15
2192
+ KPX e x -15
2193
+ KPX e y -15
2194
+ KPX e yacute -15
2195
+ KPX e ydieresis -15
2196
+ KPX eacute comma 10
2197
+ KPX eacute period 20
2198
+ KP