Version Description
- Feature: WooCommerce Order Status & Actions Manager emails compatibility
- Fix: Better url fallback for images stored in cloud
- Update: dompdf library updated to 0.8.2 - DOMDocument parser set to default again
Download this release
Release Info
Developer | pomegranate |
Plugin | WooCommerce PDF Invoices & Packing Slips |
Version | 2.1.0 |
Comparing to | |
See all releases |
Code changes from version 2.0.15 to 2.1.0
- composer.json +5 -6
- composer.lock +203 -213
- includes/class-wcpdf-main.php +1 -1
- includes/class-wcpdf-pdf-maker.php +1 -1
- includes/class-wcpdf-settings-debug.php +3 -3
- includes/compatibility/class-wcpdf-compatibility-third-party-plugins.php +162 -143
- includes/documents/abstract-wcpdf-order-document-methods.php +9 -1
- includes/views/wcpdf-extensions.php +1 -1
- readme.txt +8 -5
- vendor/autoload.php +7 -7
- vendor/composer/ClassLoader.php +445 -413
- vendor/composer/LICENSE +21 -21
- vendor/composer/autoload_real.php +52 -52
- vendor/composer/installed.json +193 -187
- vendor/dompdf/dompdf/CONTRIBUTING.md +65 -65
- vendor/dompdf/dompdf/README.md +211 -207
- vendor/dompdf/dompdf/VERSION +1 -1
- vendor/dompdf/dompdf/autoload.inc.php +0 -28
- vendor/dompdf/dompdf/composer.json +44 -44
- vendor/dompdf/dompdf/lib/Cpdf.php +5640 -5566
- vendor/dompdf/dompdf/lib/html5lib/Data.php +123 -114
- vendor/dompdf/dompdf/lib/html5lib/Tokenizer.php +2470 -2468
- vendor/dompdf/dompdf/lib/html5lib/TreeBuilder.php +1875 -3959
composer.json
CHANGED
@@ -1,7 +1,6 @@
|
|
1 |
-
{
|
2 |
-
"require": {
|
3 |
-
"php": ">=5.3.0",
|
4 |
-
"dompdf/dompdf": "*"
|
5 |
-
|
6 |
-
}
|
7 |
}
|
1 |
+
{
|
2 |
+
"require": {
|
3 |
+
"php": ">=5.3.0",
|
4 |
+
"dompdf/dompdf": "*"
|
5 |
+
}
|
|
|
6 |
}
|
composer.lock
CHANGED
@@ -1,213 +1,203 @@
|
|
1 |
-
{
|
2 |
-
"_readme": [
|
3 |
-
"This file locks the dependencies of your project to a known state",
|
4 |
-
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
5 |
-
"This file is @generated automatically"
|
6 |
-
],
|
7 |
-
"hash": "
|
8 |
-
"
|
9 |
-
|
10 |
-
|
11 |
-
"
|
12 |
-
"
|
13 |
-
|
14 |
-
"
|
15 |
-
"
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
"
|
20 |
-
"
|
21 |
-
"
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
"ext-
|
26 |
-
"ext-
|
27 |
-
"
|
28 |
-
"phenx/php-
|
29 |
-
"
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
"
|
34 |
-
|
35 |
-
|
36 |
-
"
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
"
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
"
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
"
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
"
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
"
|
69 |
-
"
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
"
|
74 |
-
"
|
75 |
-
|
76 |
-
"
|
77 |
-
"
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
"
|
82 |
-
"
|
83 |
-
"
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
"
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
"
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
"
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
"
|
106 |
-
"
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
"
|
111 |
-
"
|
112 |
-
|
113 |
-
"
|
114 |
-
"
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
"
|
119 |
-
"
|
120 |
-
"
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
"
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
"
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
"
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
"
|
146 |
-
"
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
"
|
151 |
-
"
|
152 |
-
|
153 |
-
"
|
154 |
-
"
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
"
|
159 |
-
"
|
160 |
-
"
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
"
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
"
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
"
|
185 |
-
"
|
186 |
-
|
187 |
-
"
|
188 |
-
"
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
],
|
194 |
-
"
|
195 |
-
"
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
]
|
203 |
-
|
204 |
-
"stability-flags": {
|
205 |
-
"phenx/php-svg-lib": 20
|
206 |
-
},
|
207 |
-
"prefer-stable": false,
|
208 |
-
"prefer-lowest": false,
|
209 |
-
"platform": {
|
210 |
-
"php": ">=5.3.0"
|
211 |
-
},
|
212 |
-
"platform-dev": []
|
213 |
-
}
|
1 |
+
{
|
2 |
+
"_readme": [
|
3 |
+
"This file locks the dependencies of your project to a known state",
|
4 |
+
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
5 |
+
"This file is @generated automatically"
|
6 |
+
],
|
7 |
+
"content-hash": "7e8e737c6fffa1c43aefc1018cab381f",
|
8 |
+
"packages": [
|
9 |
+
{
|
10 |
+
"name": "dompdf/dompdf",
|
11 |
+
"version": "v0.8.2",
|
12 |
+
"source": {
|
13 |
+
"type": "git",
|
14 |
+
"url": "https://github.com/dompdf/dompdf.git",
|
15 |
+
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6"
|
16 |
+
},
|
17 |
+
"dist": {
|
18 |
+
"type": "zip",
|
19 |
+
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
20 |
+
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
21 |
+
"shasum": ""
|
22 |
+
},
|
23 |
+
"require": {
|
24 |
+
"ext-dom": "*",
|
25 |
+
"ext-gd": "*",
|
26 |
+
"ext-mbstring": "*",
|
27 |
+
"phenx/php-font-lib": "0.5.*",
|
28 |
+
"phenx/php-svg-lib": "0.3.*",
|
29 |
+
"php": ">=5.4.0"
|
30 |
+
},
|
31 |
+
"require-dev": {
|
32 |
+
"phpunit/phpunit": "4.8.*",
|
33 |
+
"squizlabs/php_codesniffer": "2.*"
|
34 |
+
},
|
35 |
+
"type": "library",
|
36 |
+
"extra": {
|
37 |
+
"branch-alias": {
|
38 |
+
"dev-develop": "0.7-dev"
|
39 |
+
}
|
40 |
+
},
|
41 |
+
"autoload": {
|
42 |
+
"psr-4": {
|
43 |
+
"Dompdf\\": "src/"
|
44 |
+
},
|
45 |
+
"classmap": [
|
46 |
+
"lib/"
|
47 |
+
]
|
48 |
+
},
|
49 |
+
"notification-url": "https://packagist.org/downloads/",
|
50 |
+
"license": [
|
51 |
+
"LGPL-2.1"
|
52 |
+
],
|
53 |
+
"authors": [
|
54 |
+
{
|
55 |
+
"name": "Fabien Ménager",
|
56 |
+
"email": "fabien.menager@gmail.com"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"name": "Brian Sweeney",
|
60 |
+
"email": "eclecticgeek@gmail.com"
|
61 |
+
},
|
62 |
+
{
|
63 |
+
"name": "Gabriel Bull",
|
64 |
+
"email": "me@gabrielbull.com"
|
65 |
+
}
|
66 |
+
],
|
67 |
+
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
68 |
+
"homepage": "https://github.com/dompdf/dompdf",
|
69 |
+
"time": "2017-11-26T14:49:08+00:00"
|
70 |
+
},
|
71 |
+
{
|
72 |
+
"name": "phenx/php-font-lib",
|
73 |
+
"version": "0.5.1",
|
74 |
+
"source": {
|
75 |
+
"type": "git",
|
76 |
+
"url": "https://github.com/PhenX/php-font-lib.git",
|
77 |
+
"reference": "760148820110a1ae0936e5cc35851e25a938bc97"
|
78 |
+
},
|
79 |
+
"dist": {
|
80 |
+
"type": "zip",
|
81 |
+
"url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/760148820110a1ae0936e5cc35851e25a938bc97",
|
82 |
+
"reference": "760148820110a1ae0936e5cc35851e25a938bc97",
|
83 |
+
"shasum": ""
|
84 |
+
},
|
85 |
+
"require-dev": {
|
86 |
+
"phpunit/phpunit": "^4.8"
|
87 |
+
},
|
88 |
+
"type": "library",
|
89 |
+
"autoload": {
|
90 |
+
"psr-4": {
|
91 |
+
"FontLib\\": "src/FontLib"
|
92 |
+
}
|
93 |
+
},
|
94 |
+
"notification-url": "https://packagist.org/downloads/",
|
95 |
+
"license": [
|
96 |
+
"LGPL-3.0"
|
97 |
+
],
|
98 |
+
"authors": [
|
99 |
+
{
|
100 |
+
"name": "Fabien Ménager",
|
101 |
+
"email": "fabien.menager@gmail.com"
|
102 |
+
}
|
103 |
+
],
|
104 |
+
"description": "A library to read, parse, export and make subsets of different types of font files.",
|
105 |
+
"homepage": "https://github.com/PhenX/php-font-lib",
|
106 |
+
"time": "2017-09-13T16:14:37+00:00"
|
107 |
+
},
|
108 |
+
{
|
109 |
+
"name": "phenx/php-svg-lib",
|
110 |
+
"version": "v0.3",
|
111 |
+
"source": {
|
112 |
+
"type": "git",
|
113 |
+
"url": "https://github.com/PhenX/php-svg-lib.git",
|
114 |
+
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa"
|
115 |
+
},
|
116 |
+
"dist": {
|
117 |
+
"type": "zip",
|
118 |
+
"url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
119 |
+
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
120 |
+
"shasum": ""
|
121 |
+
},
|
122 |
+
"require": {
|
123 |
+
"sabberworm/php-css-parser": "8.1.*"
|
124 |
+
},
|
125 |
+
"require-dev": {
|
126 |
+
"phpunit/phpunit": "~5.0"
|
127 |
+
},
|
128 |
+
"type": "library",
|
129 |
+
"autoload": {
|
130 |
+
"psr-0": {
|
131 |
+
"Svg\\": "src/"
|
132 |
+
}
|
133 |
+
},
|
134 |
+
"notification-url": "https://packagist.org/downloads/",
|
135 |
+
"license": [
|
136 |
+
"LGPL-3.0"
|
137 |
+
],
|
138 |
+
"authors": [
|
139 |
+
{
|
140 |
+
"name": "Fabien Ménager",
|
141 |
+
"email": "fabien.menager@gmail.com"
|
142 |
+
}
|
143 |
+
],
|
144 |
+
"description": "A library to read, parse and export to PDF SVG files.",
|
145 |
+
"homepage": "https://github.com/PhenX/php-svg-lib",
|
146 |
+
"time": "2017-05-24T10:07:27+00:00"
|
147 |
+
},
|
148 |
+
{
|
149 |
+
"name": "sabberworm/php-css-parser",
|
150 |
+
"version": "8.1.0",
|
151 |
+
"source": {
|
152 |
+
"type": "git",
|
153 |
+
"url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
|
154 |
+
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef"
|
155 |
+
},
|
156 |
+
"dist": {
|
157 |
+
"type": "zip",
|
158 |
+
"url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/850cbbcbe7fbb155387a151ea562897a67e242ef",
|
159 |
+
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef",
|
160 |
+
"shasum": ""
|
161 |
+
},
|
162 |
+
"require": {
|
163 |
+
"php": ">=5.3.2"
|
164 |
+
},
|
165 |
+
"require-dev": {
|
166 |
+
"phpunit/phpunit": "*"
|
167 |
+
},
|
168 |
+
"type": "library",
|
169 |
+
"autoload": {
|
170 |
+
"psr-0": {
|
171 |
+
"Sabberworm\\CSS": "lib/"
|
172 |
+
}
|
173 |
+
},
|
174 |
+
"notification-url": "https://packagist.org/downloads/",
|
175 |
+
"license": [
|
176 |
+
"MIT"
|
177 |
+
],
|
178 |
+
"authors": [
|
179 |
+
{
|
180 |
+
"name": "Raphael Schweikert"
|
181 |
+
}
|
182 |
+
],
|
183 |
+
"description": "Parser for CSS Files written in PHP",
|
184 |
+
"homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
185 |
+
"keywords": [
|
186 |
+
"css",
|
187 |
+
"parser",
|
188 |
+
"stylesheet"
|
189 |
+
],
|
190 |
+
"time": "2016-07-19T19:14:21+00:00"
|
191 |
+
}
|
192 |
+
],
|
193 |
+
"packages-dev": [],
|
194 |
+
"aliases": [],
|
195 |
+
"minimum-stability": "stable",
|
196 |
+
"stability-flags": [],
|
197 |
+
"prefer-stable": false,
|
198 |
+
"prefer-lowest": false,
|
199 |
+
"platform": {
|
200 |
+
"php": ">=5.3.0"
|
201 |
+
},
|
202 |
+
"platform-dev": []
|
203 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/class-wcpdf-main.php
CHANGED
@@ -446,7 +446,7 @@ class Main {
|
|
446 |
* Use currency symbol font (when enabled in options)
|
447 |
*/
|
448 |
public function use_currency_font ( $document_type, $document ) {
|
449 |
-
add_filter( 'woocommerce_currency_symbol', array( $this, 'wrap_currency_symbol' ),
|
450 |
add_action( 'wpo_wcpdf_custom_styles', array($this, 'currency_symbol_font_styles' ) );
|
451 |
}
|
452 |
|
446 |
* Use currency symbol font (when enabled in options)
|
447 |
*/
|
448 |
public function use_currency_font ( $document_type, $document ) {
|
449 |
+
add_filter( 'woocommerce_currency_symbol', array( $this, 'wrap_currency_symbol' ), 999, 2);
|
450 |
add_action( 'wpo_wcpdf_custom_styles', array($this, 'currency_symbol_font_styles' ) );
|
451 |
}
|
452 |
|
includes/class-wcpdf-pdf-maker.php
CHANGED
@@ -42,7 +42,7 @@ class PDF_Maker {
|
|
42 |
'isRemoteEnabled' => true,
|
43 |
'isFontSubsettingEnabled' => $this->settings['font_subsetting'],
|
44 |
// HTML5 parser requires iconv
|
45 |
-
'isHtml5ParserEnabled' => ( isset(WPO_WCPDF()->settings->debug_settings['
|
46 |
) ) );
|
47 |
|
48 |
// instantiate and use the dompdf class
|
42 |
'isRemoteEnabled' => true,
|
43 |
'isFontSubsettingEnabled' => $this->settings['font_subsetting'],
|
44 |
// HTML5 parser requires iconv
|
45 |
+
'isHtml5ParserEnabled' => ( isset(WPO_WCPDF()->settings->debug_settings['use_html5_parser']) && extension_loaded('iconv') ) ? true : false,
|
46 |
) ) );
|
47 |
|
48 |
// instantiate and use the dompdf class
|
includes/class-wcpdf-settings-debug.php
CHANGED
@@ -172,13 +172,13 @@ class Settings_Debug {
|
|
172 |
),
|
173 |
array(
|
174 |
'type' => 'setting',
|
175 |
-
'id' => '
|
176 |
-
'title' => __( 'Use
|
177 |
'callback' => 'checkbox',
|
178 |
'section' => 'debug_settings',
|
179 |
'args' => array(
|
180 |
'option_name' => $option_name,
|
181 |
-
'id' => '
|
182 |
)
|
183 |
),
|
184 |
);
|
172 |
),
|
173 |
array(
|
174 |
'type' => 'setting',
|
175 |
+
'id' => 'use_html5_parser',
|
176 |
+
'title' => __( 'Use alternative HTML5 parser to parse HTML', 'woocommerce-pdf-invoices-packing-slips' ),
|
177 |
'callback' => 'checkbox',
|
178 |
'section' => 'debug_settings',
|
179 |
'args' => array(
|
180 |
'option_name' => $option_name,
|
181 |
+
'id' => 'use_html5_parser',
|
182 |
)
|
183 |
),
|
184 |
);
|
includes/compatibility/class-wcpdf-compatibility-third-party-plugins.php
CHANGED
@@ -1,143 +1,162 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices\Compatibility;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
-
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
-
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_DateTime;
|
8 |
-
|
9 |
-
defined( 'ABSPATH' ) or exit;
|
10 |
-
|
11 |
-
if ( ! class_exists( '\\WPO\\WC\\PDF_Invoices\\Compatibility\\Third_Party_Plugins' ) ) :
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Third party plugin compatibility class.
|
15 |
-
*
|
16 |
-
* @since 2.0
|
17 |
-
*/
|
18 |
-
class Third_Party_Plugins {
|
19 |
-
function __construct() {
|
20 |
-
// WooCommerce Subscriptions compatibility
|
21 |
-
if ( class_exists('WC_Subscriptions') ) {
|
22 |
-
if ( version_compare( \WC_Subscriptions::$version, '2.0', '<' ) ) {
|
23 |
-
add_action( 'woocommerce_subscriptions_renewal_order_created', array( $this, 'woocommerce_subscriptions_renewal_order_created' ), 10, 4 );
|
24 |
-
} else {
|
25 |
-
add_action( 'wcs_renewal_order_created', array( $this, 'wcs_renewal_order_created' ), 10, 2 );
|
26 |
-
}
|
27 |
-
}
|
28 |
-
|
29 |
-
// WooCommerce Product Bundles compatibility (add row classes)
|
30 |
-
if ( class_exists('WC_Bundles') ) {
|
31 |
-
add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_product_bundles_classes' ), 10, 4 );
|
32 |
-
}
|
33 |
-
|
34 |
-
// WooCommerce Chained Products compatibility (add row classes)
|
35 |
-
if ( class_exists('SA_WC_Chained_Products') ) {
|
36 |
-
add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_chained_product_class' ), 10, 4 );
|
37 |
-
}
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
}
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices\Compatibility;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
+
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
+
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_DateTime;
|
8 |
+
|
9 |
+
defined( 'ABSPATH' ) or exit;
|
10 |
+
|
11 |
+
if ( ! class_exists( '\\WPO\\WC\\PDF_Invoices\\Compatibility\\Third_Party_Plugins' ) ) :
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Third party plugin compatibility class.
|
15 |
+
*
|
16 |
+
* @since 2.0
|
17 |
+
*/
|
18 |
+
class Third_Party_Plugins {
|
19 |
+
function __construct() {
|
20 |
+
// WooCommerce Subscriptions compatibility
|
21 |
+
if ( class_exists('WC_Subscriptions') ) {
|
22 |
+
if ( version_compare( \WC_Subscriptions::$version, '2.0', '<' ) ) {
|
23 |
+
add_action( 'woocommerce_subscriptions_renewal_order_created', array( $this, 'woocommerce_subscriptions_renewal_order_created' ), 10, 4 );
|
24 |
+
} else {
|
25 |
+
add_action( 'wcs_renewal_order_created', array( $this, 'wcs_renewal_order_created' ), 10, 2 );
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
// WooCommerce Product Bundles compatibility (add row classes)
|
30 |
+
if ( class_exists('WC_Bundles') ) {
|
31 |
+
add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_product_bundles_classes' ), 10, 4 );
|
32 |
+
}
|
33 |
+
|
34 |
+
// WooCommerce Chained Products compatibility (add row classes)
|
35 |
+
if ( class_exists('SA_WC_Chained_Products') ) {
|
36 |
+
add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_chained_product_class' ), 10, 4 );
|
37 |
+
}
|
38 |
+
|
39 |
+
// WooCommerce Order Status & Actions Manager emails compatibility
|
40 |
+
if (class_exists('WC_Custom_Status')) {
|
41 |
+
add_filter( 'wpo_wcpdf_wc_emails', array( $this, 'wc_order_status_actions_emails' ), 10, 1 );
|
42 |
+
}
|
43 |
+
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Reset invoice data for WooCommerce subscription renewal orders
|
48 |
+
* https://wordpress.org/support/topic/subscription-renewal-duplicate-invoice-number?replies=6#post-6138110
|
49 |
+
*/
|
50 |
+
public function woocommerce_subscriptions_renewal_order_created ( $renewal_order, $original_order, $product_id, $new_order_role ) {
|
51 |
+
$this->reset_invoice_data( $renewal_order );
|
52 |
+
return $renewal_order;
|
53 |
+
}
|
54 |
+
|
55 |
+
public function wcs_renewal_order_created ( $renewal_order, $subscription ) {
|
56 |
+
$this->reset_invoice_data( $renewal_order );
|
57 |
+
return $renewal_order;
|
58 |
+
}
|
59 |
+
|
60 |
+
public function reset_invoice_data ( $order ) {
|
61 |
+
if ( ! is_object( $order ) ) {
|
62 |
+
$order = wc_get_order( $order );
|
63 |
+
}
|
64 |
+
// delete invoice number, invoice date & invoice exists meta
|
65 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_number' );
|
66 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_number_data' );
|
67 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_formatted_invoice_number' );
|
68 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_date' );
|
69 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_exists' );
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* WooCommerce Product Bundles
|
74 |
+
* @param string $classes CSS classes for item row (tr)
|
75 |
+
* @param string $document_type PDF Document type
|
76 |
+
* @param object $order WC_Order order
|
77 |
+
* @param int $item_id WooCommerce Item ID
|
78 |
+
*/
|
79 |
+
public function add_product_bundles_classes ( $classes, $document_type, $order, $item_id = '' ) {
|
80 |
+
if ( empty($item_id) ) {
|
81 |
+
// get item id from classes (backwards compatibility fix)
|
82 |
+
$class_array = explode(' ', $classes);
|
83 |
+
foreach ($class_array as $class) {
|
84 |
+
if (is_numeric($class)) {
|
85 |
+
$item_id = $class;
|
86 |
+
break;
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
// if still empty, we lost the item id somewhere :(
|
91 |
+
if (empty($item_id)) {
|
92 |
+
return $classes;
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
if ( $bundled_by = WCX_Order::get_item_meta( $order, $item_id, '_bundled_by', true ) ) {
|
97 |
+
$classes = $classes . ' bundled-item';
|
98 |
+
|
99 |
+
// check bundled item visibility
|
100 |
+
if ( $hidden = WCX_Order::get_item_meta( $order, $item_id, '_bundled_item_hidden', true ) ) {
|
101 |
+
$classes = $classes . ' hidden';
|
102 |
+
}
|
103 |
+
|
104 |
+
return $classes;
|
105 |
+
} elseif ( $bundled_items = WCX_Order::get_item_meta( $order, $item_id, '_bundled_items', true ) ) {
|
106 |
+
return $classes . ' product-bundle';
|
107 |
+
}
|
108 |
+
|
109 |
+
return $classes;
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* WooCommerce Chanined Products
|
114 |
+
* @param string $classes CSS classes for item row (tr)
|
115 |
+
* @param string $document_type PDF Document type
|
116 |
+
* @param object $order WC_Order order
|
117 |
+
* @param int $item_id WooCommerce Item ID
|
118 |
+
*/
|
119 |
+
public function add_chained_product_class ( $classes, $document_type, $order, $item_id = '' ) {
|
120 |
+
if ( empty($item_id) ) {
|
121 |
+
// get item id from classes (backwards compatibility fix)
|
122 |
+
$class_array = explode(' ', $classes);
|
123 |
+
foreach ($class_array as $class) {
|
124 |
+
if (is_numeric($class)) {
|
125 |
+
$item_id = $class;
|
126 |
+
break;
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
// if still empty, we lost the item id somewhere :(
|
131 |
+
if (empty($item_id)) {
|
132 |
+
return $classes;
|
133 |
+
}
|
134 |
+
}
|
135 |
+
|
136 |
+
if ( $chained_product_of = WCX_Order::get_item_meta( $order, $item_id, '_chained_product_of', true ) ) {
|
137 |
+
return $classes . ' chained-product';
|
138 |
+
}
|
139 |
+
|
140 |
+
return $classes;
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* WooCommerce Order Status & Actions Manager emails compatibility
|
145 |
+
*/
|
146 |
+
public function wc_order_status_actions_emails ( $emails ) {
|
147 |
+
// get list of custom statuses from WooCommerce Custom Order Status & Actions
|
148 |
+
// status slug => status name
|
149 |
+
$custom_statuses = WC_Custom_Status::get_status_list_names();
|
150 |
+
// append _email to slug (=email_id) and add to emails list
|
151 |
+
foreach ($custom_statuses as $status_slug => $status_name) {
|
152 |
+
$emails[$status_slug.'_email'] = $status_name;
|
153 |
+
}
|
154 |
+
return $emails;
|
155 |
+
}
|
156 |
+
|
157 |
+
}
|
158 |
+
|
159 |
+
|
160 |
+
endif; // Class exists check
|
161 |
+
|
162 |
+
return new Third_Party_Plugins();
|
includes/documents/abstract-wcpdf-order-document-methods.php
CHANGED
@@ -641,9 +641,17 @@ abstract class Order_Document_Methods extends Order_Document {
|
|
641 |
}
|
642 |
|
643 |
// Thumbnail (full img tag)
|
644 |
-
if (apply_filters('wpo_wcpdf_use_path', true) && file_exists($thumbnail_path)) {
|
645 |
// load img with server path by default
|
646 |
$thumbnail = sprintf('<img width="90" height="90" src="%s" class="attachment-shop_thumbnail wp-post-image">', $thumbnail_path );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
647 |
} else {
|
648 |
// load img with http url when filtered
|
649 |
$thumbnail = $thumbnail_img_tag_url;
|
641 |
}
|
642 |
|
643 |
// Thumbnail (full img tag)
|
644 |
+
if ( apply_filters('wpo_wcpdf_use_path', true) && file_exists($thumbnail_path) ) {
|
645 |
// load img with server path by default
|
646 |
$thumbnail = sprintf('<img width="90" height="90" src="%s" class="attachment-shop_thumbnail wp-post-image">', $thumbnail_path );
|
647 |
+
} elseif ( apply_filters('wpo_wcpdf_use_path', true) && !file_exists($thumbnail_path) ) {
|
648 |
+
// should use paths but file not found, replace // with http(s):// for dompdf compatibility
|
649 |
+
if ( substr( $thumbnail_url, 0, 2 ) === "//" ) {
|
650 |
+
$prefix = is_ssl() ? 'https://' : 'http://';
|
651 |
+
$https_thumbnail_url = $prefix . ltrim( $thumbnail_url, '/' );
|
652 |
+
}
|
653 |
+
$thumbnail_img_tag_url = str_replace($thumbnail_url, $https_thumbnail_url, $thumbnail_img_tag_url);
|
654 |
+
$thumbnail = $thumbnail_img_tag_url;
|
655 |
} else {
|
656 |
// load img with http url when filtered
|
657 |
$thumbnail = $thumbnail_img_tag_url;
|
includes/views/wcpdf-extensions.php
CHANGED
@@ -105,7 +105,7 @@ jQuery(document).ready(function() {
|
|
105 |
<?php } ?>
|
106 |
|
107 |
<?php
|
108 |
-
if (!class_exists('WooCommerce_PDF_IPS_Templates') && !$no_pro) {
|
109 |
$template_link = '<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/" target="_blank">wpovernight.com</a>';
|
110 |
$email_link = '<a href="mailto:support@wpovernight.com">support@wpovernight.com</a>'
|
111 |
?>
|
105 |
<?php } ?>
|
106 |
|
107 |
<?php
|
108 |
+
if (!class_exists('WooCommerce_PDF_IPS_Templates') && !class_exists('WPO_WCPDF_Templates') && !$no_pro) {
|
109 |
$template_link = '<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/" target="_blank">wpovernight.com</a>';
|
110 |
$email_link = '<a href="mailto:support@wpovernight.com">support@wpovernight.com</a>'
|
111 |
?>
|
readme.txt
CHANGED
@@ -3,9 +3,9 @@ Contributors: pomegranate
|
|
3 |
Donate link: https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/
|
4 |
Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice, packing slip, export, email, bulk, automatic
|
5 |
Requires at least: 3.5
|
6 |
-
Tested up to: 4.
|
7 |
Requires PHP: 5.3
|
8 |
-
Stable tag: 2.0
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
@@ -103,7 +103,10 @@ There's a setting on the Status tab of the settings page that allows you to togg
|
|
103 |
|
104 |
== Changelog ==
|
105 |
|
106 |
-
|
|
|
|
|
|
|
107 |
|
108 |
= 2.0.15 =
|
109 |
* Fix: Prevent saving invoice number/date from order details page when not edited
|
@@ -208,5 +211,5 @@ There's a setting on the Status tab of the settings page that allows you to togg
|
|
208 |
|
209 |
== Upgrade Notice ==
|
210 |
|
211 |
-
= 2.0
|
212 |
-
|
3 |
Donate link: https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/
|
4 |
Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice, packing slip, export, email, bulk, automatic
|
5 |
Requires at least: 3.5
|
6 |
+
Tested up to: 4.9
|
7 |
Requires PHP: 5.3
|
8 |
+
Stable tag: 2.1.0
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
103 |
|
104 |
== Changelog ==
|
105 |
|
106 |
+
= 2.1.0 =
|
107 |
+
* Feature: WooCommerce Order Status & Actions Manager emails compatibility
|
108 |
+
* Fix: Better url fallback for images stored in cloud
|
109 |
+
* Update: dompdf library updated to 0.8.2 - DOMDocument parser set to default again
|
110 |
|
111 |
= 2.0.15 =
|
112 |
* Fix: Prevent saving invoice number/date from order details page when not edited
|
211 |
|
212 |
== Upgrade Notice ==
|
213 |
|
214 |
+
= 2.0 =
|
215 |
+
2.0 is a BIG update! Make a full site backup before upgrading!
|
vendor/autoload.php
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// autoload.php @generated by Composer
|
4 |
-
|
5 |
-
require_once __DIR__ . '/composer
|
6 |
-
|
7 |
-
return ComposerAutoloaderInit50d65aade31e72e54f6b791559872608::getLoader();
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload.php @generated by Composer
|
4 |
+
|
5 |
+
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
+
|
7 |
+
return ComposerAutoloaderInit50d65aade31e72e54f6b791559872608::getLoader();
|
vendor/composer/ClassLoader.php
CHANGED
@@ -1,413 +1,445 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/*
|
4 |
-
* This file is part of Composer.
|
5 |
-
*
|
6 |
-
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
-
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
-
*
|
9 |
-
* For the full copyright and license information, please view the LICENSE
|
10 |
-
* file that was distributed with this source code.
|
11 |
-
*/
|
12 |
-
|
13 |
-
namespace Composer\Autoload;
|
14 |
-
|
15 |
-
/**
|
16 |
-
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
17 |
-
*
|
18 |
-
* $loader = new \Composer\Autoload\ClassLoader();
|
19 |
-
*
|
20 |
-
* // register classes with namespaces
|
21 |
-
* $loader->add('Symfony\Component', __DIR__.'/component');
|
22 |
-
* $loader->add('Symfony', __DIR__.'/framework');
|
23 |
-
*
|
24 |
-
* // activate the autoloader
|
25 |
-
* $loader->register();
|
26 |
-
*
|
27 |
-
* // to enable searching the include path (eg. for PEAR packages)
|
28 |
-
* $loader->setUseIncludePath(true);
|
29 |
-
*
|
30 |
-
* In this example, if you try to use a class in the Symfony\Component
|
31 |
-
* namespace or one of its children (Symfony\Component\Console for instance),
|
32 |
-
* the autoloader will first look for the class under the component/
|
33 |
-
* directory, and it will then fallback to the framework/ directory if not
|
34 |
-
* found before giving up.
|
35 |
-
*
|
36 |
-
* This class is loosely based on the Symfony UniversalClassLoader.
|
37 |
-
*
|
38 |
-
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
-
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
-
* @see http://www.php-fig.org/psr/psr-0/
|
41 |
-
* @see http://www.php-fig.org/psr/psr-4/
|
42 |
-
*/
|
43 |
-
class ClassLoader
|
44 |
-
{
|
45 |
-
// PSR-4
|
46 |
-
private $prefixLengthsPsr4 = array();
|
47 |
-
private $prefixDirsPsr4 = array();
|
48 |
-
private $fallbackDirsPsr4 = array();
|
49 |
-
|
50 |
-
// PSR-0
|
51 |
-
private $prefixesPsr0 = array();
|
52 |
-
private $fallbackDirsPsr0 = array();
|
53 |
-
|
54 |
-
private $useIncludePath = false;
|
55 |
-
private $classMap = array();
|
56 |
-
|
57 |
-
private $
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
*
|
103 |
-
*
|
104 |
-
*
|
105 |
-
* @param
|
106 |
-
* @param
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
$
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
$
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
*
|
148 |
-
*
|
149 |
-
*
|
150 |
-
* @param
|
151 |
-
* @param
|
152 |
-
*
|
153 |
-
*
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
$
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
$this->
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
$
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
*
|
196 |
-
*
|
197 |
-
*
|
198 |
-
* @param
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
*
|
212 |
-
*
|
213 |
-
*
|
214 |
-
* @param
|
215 |
-
*
|
216 |
-
*
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
$this->
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
*
|
235 |
-
*
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
*
|
245 |
-
*
|
246 |
-
*
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
*
|
256 |
-
*
|
257 |
-
*
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
*
|
267 |
-
*
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
*
|
277 |
-
*
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
*
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
}
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
if (
|
382 |
-
foreach ($
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
{
|
412 |
-
|
413 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Composer.
|
5 |
+
*
|
6 |
+
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
+
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Composer\Autoload;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
17 |
+
*
|
18 |
+
* $loader = new \Composer\Autoload\ClassLoader();
|
19 |
+
*
|
20 |
+
* // register classes with namespaces
|
21 |
+
* $loader->add('Symfony\Component', __DIR__.'/component');
|
22 |
+
* $loader->add('Symfony', __DIR__.'/framework');
|
23 |
+
*
|
24 |
+
* // activate the autoloader
|
25 |
+
* $loader->register();
|
26 |
+
*
|
27 |
+
* // to enable searching the include path (eg. for PEAR packages)
|
28 |
+
* $loader->setUseIncludePath(true);
|
29 |
+
*
|
30 |
+
* In this example, if you try to use a class in the Symfony\Component
|
31 |
+
* namespace or one of its children (Symfony\Component\Console for instance),
|
32 |
+
* the autoloader will first look for the class under the component/
|
33 |
+
* directory, and it will then fallback to the framework/ directory if not
|
34 |
+
* found before giving up.
|
35 |
+
*
|
36 |
+
* This class is loosely based on the Symfony UniversalClassLoader.
|
37 |
+
*
|
38 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
+
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
+
* @see http://www.php-fig.org/psr/psr-0/
|
41 |
+
* @see http://www.php-fig.org/psr/psr-4/
|
42 |
+
*/
|
43 |
+
class ClassLoader
|
44 |
+
{
|
45 |
+
// PSR-4
|
46 |
+
private $prefixLengthsPsr4 = array();
|
47 |
+
private $prefixDirsPsr4 = array();
|
48 |
+
private $fallbackDirsPsr4 = array();
|
49 |
+
|
50 |
+
// PSR-0
|
51 |
+
private $prefixesPsr0 = array();
|
52 |
+
private $fallbackDirsPsr0 = array();
|
53 |
+
|
54 |
+
private $useIncludePath = false;
|
55 |
+
private $classMap = array();
|
56 |
+
private $classMapAuthoritative = false;
|
57 |
+
private $missingClasses = array();
|
58 |
+
private $apcuPrefix;
|
59 |
+
|
60 |
+
public function getPrefixes()
|
61 |
+
{
|
62 |
+
if (!empty($this->prefixesPsr0)) {
|
63 |
+
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
64 |
+
}
|
65 |
+
|
66 |
+
return array();
|
67 |
+
}
|
68 |
+
|
69 |
+
public function getPrefixesPsr4()
|
70 |
+
{
|
71 |
+
return $this->prefixDirsPsr4;
|
72 |
+
}
|
73 |
+
|
74 |
+
public function getFallbackDirs()
|
75 |
+
{
|
76 |
+
return $this->fallbackDirsPsr0;
|
77 |
+
}
|
78 |
+
|
79 |
+
public function getFallbackDirsPsr4()
|
80 |
+
{
|
81 |
+
return $this->fallbackDirsPsr4;
|
82 |
+
}
|
83 |
+
|
84 |
+
public function getClassMap()
|
85 |
+
{
|
86 |
+
return $this->classMap;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @param array $classMap Class to filename map
|
91 |
+
*/
|
92 |
+
public function addClassMap(array $classMap)
|
93 |
+
{
|
94 |
+
if ($this->classMap) {
|
95 |
+
$this->classMap = array_merge($this->classMap, $classMap);
|
96 |
+
} else {
|
97 |
+
$this->classMap = $classMap;
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Registers a set of PSR-0 directories for a given prefix, either
|
103 |
+
* appending or prepending to the ones previously set for this prefix.
|
104 |
+
*
|
105 |
+
* @param string $prefix The prefix
|
106 |
+
* @param array|string $paths The PSR-0 root directories
|
107 |
+
* @param bool $prepend Whether to prepend the directories
|
108 |
+
*/
|
109 |
+
public function add($prefix, $paths, $prepend = false)
|
110 |
+
{
|
111 |
+
if (!$prefix) {
|
112 |
+
if ($prepend) {
|
113 |
+
$this->fallbackDirsPsr0 = array_merge(
|
114 |
+
(array) $paths,
|
115 |
+
$this->fallbackDirsPsr0
|
116 |
+
);
|
117 |
+
} else {
|
118 |
+
$this->fallbackDirsPsr0 = array_merge(
|
119 |
+
$this->fallbackDirsPsr0,
|
120 |
+
(array) $paths
|
121 |
+
);
|
122 |
+
}
|
123 |
+
|
124 |
+
return;
|
125 |
+
}
|
126 |
+
|
127 |
+
$first = $prefix[0];
|
128 |
+
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
129 |
+
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
130 |
+
|
131 |
+
return;
|
132 |
+
}
|
133 |
+
if ($prepend) {
|
134 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
135 |
+
(array) $paths,
|
136 |
+
$this->prefixesPsr0[$first][$prefix]
|
137 |
+
);
|
138 |
+
} else {
|
139 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
140 |
+
$this->prefixesPsr0[$first][$prefix],
|
141 |
+
(array) $paths
|
142 |
+
);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Registers a set of PSR-4 directories for a given namespace, either
|
148 |
+
* appending or prepending to the ones previously set for this namespace.
|
149 |
+
*
|
150 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
151 |
+
* @param array|string $paths The PSR-4 base directories
|
152 |
+
* @param bool $prepend Whether to prepend the directories
|
153 |
+
*
|
154 |
+
* @throws \InvalidArgumentException
|
155 |
+
*/
|
156 |
+
public function addPsr4($prefix, $paths, $prepend = false)
|
157 |
+
{
|
158 |
+
if (!$prefix) {
|
159 |
+
// Register directories for the root namespace.
|
160 |
+
if ($prepend) {
|
161 |
+
$this->fallbackDirsPsr4 = array_merge(
|
162 |
+
(array) $paths,
|
163 |
+
$this->fallbackDirsPsr4
|
164 |
+
);
|
165 |
+
} else {
|
166 |
+
$this->fallbackDirsPsr4 = array_merge(
|
167 |
+
$this->fallbackDirsPsr4,
|
168 |
+
(array) $paths
|
169 |
+
);
|
170 |
+
}
|
171 |
+
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
172 |
+
// Register directories for a new namespace.
|
173 |
+
$length = strlen($prefix);
|
174 |
+
if ('\\' !== $prefix[$length - 1]) {
|
175 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
176 |
+
}
|
177 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
178 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
179 |
+
} elseif ($prepend) {
|
180 |
+
// Prepend directories for an already registered namespace.
|
181 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
182 |
+
(array) $paths,
|
183 |
+
$this->prefixDirsPsr4[$prefix]
|
184 |
+
);
|
185 |
+
} else {
|
186 |
+
// Append directories for an already registered namespace.
|
187 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
188 |
+
$this->prefixDirsPsr4[$prefix],
|
189 |
+
(array) $paths
|
190 |
+
);
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Registers a set of PSR-0 directories for a given prefix,
|
196 |
+
* replacing any others previously set for this prefix.
|
197 |
+
*
|
198 |
+
* @param string $prefix The prefix
|
199 |
+
* @param array|string $paths The PSR-0 base directories
|
200 |
+
*/
|
201 |
+
public function set($prefix, $paths)
|
202 |
+
{
|
203 |
+
if (!$prefix) {
|
204 |
+
$this->fallbackDirsPsr0 = (array) $paths;
|
205 |
+
} else {
|
206 |
+
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Registers a set of PSR-4 directories for a given namespace,
|
212 |
+
* replacing any others previously set for this namespace.
|
213 |
+
*
|
214 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
215 |
+
* @param array|string $paths The PSR-4 base directories
|
216 |
+
*
|
217 |
+
* @throws \InvalidArgumentException
|
218 |
+
*/
|
219 |
+
public function setPsr4($prefix, $paths)
|
220 |
+
{
|
221 |
+
if (!$prefix) {
|
222 |
+
$this->fallbackDirsPsr4 = (array) $paths;
|
223 |
+
} else {
|
224 |
+
$length = strlen($prefix);
|
225 |
+
if ('\\' !== $prefix[$length - 1]) {
|
226 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
227 |
+
}
|
228 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
229 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Turns on searching the include path for class files.
|
235 |
+
*
|
236 |
+
* @param bool $useIncludePath
|
237 |
+
*/
|
238 |
+
public function setUseIncludePath($useIncludePath)
|
239 |
+
{
|
240 |
+
$this->useIncludePath = $useIncludePath;
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Can be used to check if the autoloader uses the include path to check
|
245 |
+
* for classes.
|
246 |
+
*
|
247 |
+
* @return bool
|
248 |
+
*/
|
249 |
+
public function getUseIncludePath()
|
250 |
+
{
|
251 |
+
return $this->useIncludePath;
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Turns off searching the prefix and fallback directories for classes
|
256 |
+
* that have not been registered with the class map.
|
257 |
+
*
|
258 |
+
* @param bool $classMapAuthoritative
|
259 |
+
*/
|
260 |
+
public function setClassMapAuthoritative($classMapAuthoritative)
|
261 |
+
{
|
262 |
+
$this->classMapAuthoritative = $classMapAuthoritative;
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Should class lookup fail if not found in the current class map?
|
267 |
+
*
|
268 |
+
* @return bool
|
269 |
+
*/
|
270 |
+
public function isClassMapAuthoritative()
|
271 |
+
{
|
272 |
+
return $this->classMapAuthoritative;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
277 |
+
*
|
278 |
+
* @param string|null $apcuPrefix
|
279 |
+
*/
|
280 |
+
public function setApcuPrefix($apcuPrefix)
|
281 |
+
{
|
282 |
+
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* The APCu prefix in use, or null if APCu caching is not enabled.
|
287 |
+
*
|
288 |
+
* @return string|null
|
289 |
+
*/
|
290 |
+
public function getApcuPrefix()
|
291 |
+
{
|
292 |
+
return $this->apcuPrefix;
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Registers this instance as an autoloader.
|
297 |
+
*
|
298 |
+
* @param bool $prepend Whether to prepend the autoloader or not
|
299 |
+
*/
|
300 |
+
public function register($prepend = false)
|
301 |
+
{
|
302 |
+
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
303 |
+
}
|
304 |
+
|
305 |
+
/**
|
306 |
+
* Unregisters this instance as an autoloader.
|
307 |
+
*/
|
308 |
+
public function unregister()
|
309 |
+
{
|
310 |
+
spl_autoload_unregister(array($this, 'loadClass'));
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Loads the given class or interface.
|
315 |
+
*
|
316 |
+
* @param string $class The name of the class
|
317 |
+
* @return bool|null True if loaded, null otherwise
|
318 |
+
*/
|
319 |
+
public function loadClass($class)
|
320 |
+
{
|
321 |
+
if ($file = $this->findFile($class)) {
|
322 |
+
includeFile($file);
|
323 |
+
|
324 |
+
return true;
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
/**
|
329 |
+
* Finds the path to the file where the class is defined.
|
330 |
+
*
|
331 |
+
* @param string $class The name of the class
|
332 |
+
*
|
333 |
+
* @return string|false The path if found, false otherwise
|
334 |
+
*/
|
335 |
+
public function findFile($class)
|
336 |
+
{
|
337 |
+
// class map lookup
|
338 |
+
if (isset($this->classMap[$class])) {
|
339 |
+
return $this->classMap[$class];
|
340 |
+
}
|
341 |
+
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
342 |
+
return false;
|
343 |
+
}
|
344 |
+
if (null !== $this->apcuPrefix) {
|
345 |
+
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
346 |
+
if ($hit) {
|
347 |
+
return $file;
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
$file = $this->findFileWithExtension($class, '.php');
|
352 |
+
|
353 |
+
// Search for Hack files if we are running on HHVM
|
354 |
+
if (false === $file && defined('HHVM_VERSION')) {
|
355 |
+
$file = $this->findFileWithExtension($class, '.hh');
|
356 |
+
}
|
357 |
+
|
358 |
+
if (null !== $this->apcuPrefix) {
|
359 |
+
apcu_add($this->apcuPrefix.$class, $file);
|
360 |
+
}
|
361 |
+
|
362 |
+
if (false === $file) {
|
363 |
+
// Remember that this class does not exist.
|
364 |
+
$this->missingClasses[$class] = true;
|
365 |
+
}
|
366 |
+
|
367 |
+
return $file;
|
368 |
+
}
|
369 |
+
|
370 |
+
private function findFileWithExtension($class, $ext)
|
371 |
+
{
|
372 |
+
// PSR-4 lookup
|
373 |
+
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
374 |
+
|
375 |
+
$first = $class[0];
|
376 |
+
if (isset($this->prefixLengthsPsr4[$first])) {
|
377 |
+
$subPath = $class;
|
378 |
+
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
379 |
+
$subPath = substr($subPath, 0, $lastPos);
|
380 |
+
$search = $subPath.'\\';
|
381 |
+
if (isset($this->prefixDirsPsr4[$search])) {
|
382 |
+
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
383 |
+
$length = $this->prefixLengthsPsr4[$first][$search];
|
384 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
385 |
+
return $file;
|
386 |
+
}
|
387 |
+
}
|
388 |
+
}
|
389 |
+
}
|
390 |
+
}
|
391 |
+
|
392 |
+
// PSR-4 fallback dirs
|
393 |
+
foreach ($this->fallbackDirsPsr4 as $dir) {
|
394 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
395 |
+
return $file;
|
396 |
+
}
|
397 |
+
}
|
398 |
+
|
399 |
+
// PSR-0 lookup
|
400 |
+
if (false !== $pos = strrpos($class, '\\')) {
|
401 |
+
// namespaced class name
|
402 |
+
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
403 |
+
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
404 |
+
} else {
|
405 |
+
// PEAR-like class name
|
406 |
+
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
407 |
+
}
|
408 |
+
|
409 |
+
if (isset($this->prefixesPsr0[$first])) {
|
410 |
+
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
411 |
+
if (0 === strpos($class, $prefix)) {
|
412 |
+
foreach ($dirs as $dir) {
|
413 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
414 |
+
return $file;
|
415 |
+
}
|
416 |
+
}
|
417 |
+
}
|
418 |
+
}
|
419 |
+
}
|
420 |
+
|
421 |
+
// PSR-0 fallback dirs
|
422 |
+
foreach ($this->fallbackDirsPsr0 as $dir) {
|
423 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
424 |
+
return $file;
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
// PSR-0 include paths.
|
429 |
+
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
430 |
+
return $file;
|
431 |
+
}
|
432 |
+
|
433 |
+
return false;
|
434 |
+
}
|
435 |
+
}
|
436 |
+
|
437 |
+
/**
|
438 |
+
* Scope isolated include.
|
439 |
+
*
|
440 |
+
* Prevents access to $this/self from included files.
|
441 |
+
*/
|
442 |
+
function includeFile($file)
|
443 |
+
{
|
444 |
+
include $file;
|
445 |
+
}
|
vendor/composer/LICENSE
CHANGED
@@ -1,21 +1,21 @@
|
|
1 |
-
|
2 |
-
Copyright (c)
|
3 |
-
|
4 |
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5 |
-
of this software and associated documentation files (the "Software"), to deal
|
6 |
-
in the Software without restriction, including without limitation the rights
|
7 |
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8 |
-
copies of the Software, and to permit persons to whom the Software is furnished
|
9 |
-
to do so, subject to the following conditions:
|
10 |
-
|
11 |
-
The above copyright notice and this permission notice shall be included in all
|
12 |
-
copies or substantial portions of the Software.
|
13 |
-
|
14 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15 |
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16 |
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17 |
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18 |
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19 |
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20 |
-
THE SOFTWARE.
|
21 |
-
|
1 |
+
|
2 |
+
Copyright (c) Nils Adermann, Jordi Boggiano
|
3 |
+
|
4 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5 |
+
of this software and associated documentation files (the "Software"), to deal
|
6 |
+
in the Software without restriction, including without limitation the rights
|
7 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8 |
+
copies of the Software, and to permit persons to whom the Software is furnished
|
9 |
+
to do so, subject to the following conditions:
|
10 |
+
|
11 |
+
The above copyright notice and this permission notice shall be included in all
|
12 |
+
copies or substantial portions of the Software.
|
13 |
+
|
14 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20 |
+
THE SOFTWARE.
|
21 |
+
|
vendor/composer/autoload_real.php
CHANGED
@@ -1,52 +1,52 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// autoload_real.php @generated by Composer
|
4 |
-
|
5 |
-
class ComposerAutoloaderInit50d65aade31e72e54f6b791559872608
|
6 |
-
{
|
7 |
-
private static $loader;
|
8 |
-
|
9 |
-
public static function loadClassLoader($class)
|
10 |
-
{
|
11 |
-
if ('Composer\Autoload\ClassLoader' === $class) {
|
12 |
-
require __DIR__ . '/ClassLoader.php';
|
13 |
-
}
|
14 |
-
}
|
15 |
-
|
16 |
-
public static function getLoader()
|
17 |
-
{
|
18 |
-
if (null !== self::$loader) {
|
19 |
-
return self::$loader;
|
20 |
-
}
|
21 |
-
|
22 |
-
spl_autoload_register(array('ComposerAutoloaderInit50d65aade31e72e54f6b791559872608', 'loadClassLoader'), true, true);
|
23 |
-
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
-
spl_autoload_unregister(array('ComposerAutoloaderInit50d65aade31e72e54f6b791559872608', 'loadClassLoader'));
|
25 |
-
|
26 |
-
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
|
27 |
-
if ($useStaticLoader) {
|
28 |
-
require_once __DIR__ . '/autoload_static.php';
|
29 |
-
|
30 |
-
call_user_func(\Composer\Autoload\ComposerStaticInit50d65aade31e72e54f6b791559872608::getInitializer($loader));
|
31 |
-
} else {
|
32 |
-
$map = require __DIR__ . '/autoload_namespaces.php';
|
33 |
-
foreach ($map as $namespace => $path) {
|
34 |
-
$loader->set($namespace, $path);
|
35 |
-
}
|
36 |
-
|
37 |
-
$map = require __DIR__ . '/autoload_psr4.php';
|
38 |
-
foreach ($map as $namespace => $path) {
|
39 |
-
$loader->setPsr4($namespace, $path);
|
40 |
-
}
|
41 |
-
|
42 |
-
$classMap = require __DIR__ . '/autoload_classmap.php';
|
43 |
-
if ($classMap) {
|
44 |
-
$loader->addClassMap($classMap);
|
45 |
-
}
|
46 |
-
}
|
47 |
-
|
48 |
-
$loader->register(true);
|
49 |
-
|
50 |
-
return $loader;
|
51 |
-
}
|
52 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_real.php @generated by Composer
|
4 |
+
|
5 |
+
class ComposerAutoloaderInit50d65aade31e72e54f6b791559872608
|
6 |
+
{
|
7 |
+
private static $loader;
|
8 |
+
|
9 |
+
public static function loadClassLoader($class)
|
10 |
+
{
|
11 |
+
if ('Composer\Autoload\ClassLoader' === $class) {
|
12 |
+
require __DIR__ . '/ClassLoader.php';
|
13 |
+
}
|
14 |
+
}
|
15 |
+
|
16 |
+
public static function getLoader()
|
17 |
+
{
|
18 |
+
if (null !== self::$loader) {
|
19 |
+
return self::$loader;
|
20 |
+
}
|
21 |
+
|
22 |
+
spl_autoload_register(array('ComposerAutoloaderInit50d65aade31e72e54f6b791559872608', 'loadClassLoader'), true, true);
|
23 |
+
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit50d65aade31e72e54f6b791559872608', 'loadClassLoader'));
|
25 |
+
|
26 |
+
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
27 |
+
if ($useStaticLoader) {
|
28 |
+
require_once __DIR__ . '/autoload_static.php';
|
29 |
+
|
30 |
+
call_user_func(\Composer\Autoload\ComposerStaticInit50d65aade31e72e54f6b791559872608::getInitializer($loader));
|
31 |
+
} else {
|
32 |
+
$map = require __DIR__ . '/autoload_namespaces.php';
|
33 |
+
foreach ($map as $namespace => $path) {
|
34 |
+
$loader->set($namespace, $path);
|
35 |
+
}
|
36 |
+
|
37 |
+
$map = require __DIR__ . '/autoload_psr4.php';
|
38 |
+
foreach ($map as $namespace => $path) {
|
39 |
+
$loader->setPsr4($namespace, $path);
|
40 |
+
}
|
41 |
+
|
42 |
+
$classMap = require __DIR__ . '/autoload_classmap.php';
|
43 |
+
if ($classMap) {
|
44 |
+
$loader->addClassMap($classMap);
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
$loader->register(true);
|
49 |
+
|
50 |
+
return $loader;
|
51 |
+
}
|
52 |
+
}
|
vendor/composer/installed.json
CHANGED
@@ -1,187 +1,193 @@
|
|
1 |
-
[
|
2 |
-
{
|
3 |
-
"name": "sabberworm/php-css-parser",
|
4 |
-
"version": "
|
5 |
-
"version_normalized": "
|
6 |
-
"source": {
|
7 |
-
"type": "git",
|
8 |
-
"url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
|
9 |
-
"reference": "
|
10 |
-
},
|
11 |
-
"dist": {
|
12 |
-
"type": "zip",
|
13 |
-
"url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/
|
14 |
-
"reference": "
|
15 |
-
"shasum": ""
|
16 |
-
},
|
17 |
-
"require": {
|
18 |
-
"php": ">=5.3.2"
|
19 |
-
},
|
20 |
-
"
|
21 |
-
|
22 |
-
|
23 |
-
"
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
"
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
"
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
"
|
56 |
-
|
57 |
-
|
58 |
-
"
|
59 |
-
|
60 |
-
|
61 |
-
"
|
62 |
-
},
|
63 |
-
"
|
64 |
-
|
65 |
-
|
66 |
-
"
|
67 |
-
"
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
"
|
72 |
-
"
|
73 |
-
"
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
],
|
81 |
-
"
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
"
|
88 |
-
"
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
"
|
94 |
-
|
95 |
-
"
|
96 |
-
"
|
97 |
-
"
|
98 |
-
},
|
99 |
-
"
|
100 |
-
"
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
"
|
106 |
-
"
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
"
|
111 |
-
"
|
112 |
-
"
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
],
|
120 |
-
"
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
"
|
127 |
-
"
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
"
|
133 |
-
|
134 |
-
"
|
135 |
-
"
|
136 |
-
"
|
137 |
-
},
|
138 |
-
"
|
139 |
-
"
|
140 |
-
"
|
141 |
-
"
|
142 |
-
"
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
"
|
148 |
-
"
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
"
|
153 |
-
"
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
"
|
158 |
-
"
|
159 |
-
"
|
160 |
-
"
|
161 |
-
}
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
"
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"name": "sabberworm/php-css-parser",
|
4 |
+
"version": "8.1.0",
|
5 |
+
"version_normalized": "8.1.0.0",
|
6 |
+
"source": {
|
7 |
+
"type": "git",
|
8 |
+
"url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
|
9 |
+
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef"
|
10 |
+
},
|
11 |
+
"dist": {
|
12 |
+
"type": "zip",
|
13 |
+
"url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/850cbbcbe7fbb155387a151ea562897a67e242ef",
|
14 |
+
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef",
|
15 |
+
"shasum": ""
|
16 |
+
},
|
17 |
+
"require": {
|
18 |
+
"php": ">=5.3.2"
|
19 |
+
},
|
20 |
+
"require-dev": {
|
21 |
+
"phpunit/phpunit": "*"
|
22 |
+
},
|
23 |
+
"time": "2016-07-19T19:14:21+00:00",
|
24 |
+
"type": "library",
|
25 |
+
"installation-source": "dist",
|
26 |
+
"autoload": {
|
27 |
+
"psr-0": {
|
28 |
+
"Sabberworm\\CSS": "lib/"
|
29 |
+
}
|
30 |
+
},
|
31 |
+
"notification-url": "https://packagist.org/downloads/",
|
32 |
+
"license": [
|
33 |
+
"MIT"
|
34 |
+
],
|
35 |
+
"authors": [
|
36 |
+
{
|
37 |
+
"name": "Raphael Schweikert"
|
38 |
+
}
|
39 |
+
],
|
40 |
+
"description": "Parser for CSS Files written in PHP",
|
41 |
+
"homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
42 |
+
"keywords": [
|
43 |
+
"css",
|
44 |
+
"parser",
|
45 |
+
"stylesheet"
|
46 |
+
]
|
47 |
+
},
|
48 |
+
{
|
49 |
+
"name": "phenx/php-svg-lib",
|
50 |
+
"version": "v0.3",
|
51 |
+
"version_normalized": "0.3.0.0",
|
52 |
+
"source": {
|
53 |
+
"type": "git",
|
54 |
+
"url": "https://github.com/PhenX/php-svg-lib.git",
|
55 |
+
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa"
|
56 |
+
},
|
57 |
+
"dist": {
|
58 |
+
"type": "zip",
|
59 |
+
"url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
60 |
+
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
61 |
+
"shasum": ""
|
62 |
+
},
|
63 |
+
"require": {
|
64 |
+
"sabberworm/php-css-parser": "8.1.*"
|
65 |
+
},
|
66 |
+
"require-dev": {
|
67 |
+
"phpunit/phpunit": "~5.0"
|
68 |
+
},
|
69 |
+
"time": "2017-05-24T10:07:27+00:00",
|
70 |
+
"type": "library",
|
71 |
+
"installation-source": "dist",
|
72 |
+
"autoload": {
|
73 |
+
"psr-0": {
|
74 |
+
"Svg\\": "src/"
|
75 |
+
}
|
76 |
+
},
|
77 |
+
"notification-url": "https://packagist.org/downloads/",
|
78 |
+
"license": [
|
79 |
+
"LGPL-3.0"
|
80 |
+
],
|
81 |
+
"authors": [
|
82 |
+
{
|
83 |
+
"name": "Fabien Ménager",
|
84 |
+
"email": "fabien.menager@gmail.com"
|
85 |
+
}
|
86 |
+
],
|
87 |
+
"description": "A library to read, parse and export to PDF SVG files.",
|
88 |
+
"homepage": "https://github.com/PhenX/php-svg-lib"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"name": "phenx/php-font-lib",
|
92 |
+
"version": "0.5.1",
|
93 |
+
"version_normalized": "0.5.1.0",
|
94 |
+
"source": {
|
95 |
+
"type": "git",
|
96 |
+
"url": "https://github.com/PhenX/php-font-lib.git",
|
97 |
+
"reference": "760148820110a1ae0936e5cc35851e25a938bc97"
|
98 |
+
},
|
99 |
+
"dist": {
|
100 |
+
"type": "zip",
|
101 |
+
"url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/760148820110a1ae0936e5cc35851e25a938bc97",
|
102 |
+
"reference": "760148820110a1ae0936e5cc35851e25a938bc97",
|
103 |
+
"shasum": ""
|
104 |
+
},
|
105 |
+
"require-dev": {
|
106 |
+
"phpunit/phpunit": "^4.8"
|
107 |
+
},
|
108 |
+
"time": "2017-09-13T16:14:37+00:00",
|
109 |
+
"type": "library",
|
110 |
+
"installation-source": "dist",
|
111 |
+
"autoload": {
|
112 |
+
"psr-4": {
|
113 |
+
"FontLib\\": "src/FontLib"
|
114 |
+
}
|
115 |
+
},
|
116 |
+
"notification-url": "https://packagist.org/downloads/",
|
117 |
+
"license": [
|
118 |
+
"LGPL-3.0"
|
119 |
+
],
|
120 |
+
"authors": [
|
121 |
+
{
|
122 |
+
"name": "Fabien Ménager",
|
123 |
+
"email": "fabien.menager@gmail.com"
|
124 |
+
}
|
125 |
+
],
|
126 |
+
"description": "A library to read, parse, export and make subsets of different types of font files.",
|
127 |
+
"homepage": "https://github.com/PhenX/php-font-lib"
|
128 |
+
},
|
129 |
+
{
|
130 |
+
"name": "dompdf/dompdf",
|
131 |
+
"version": "v0.8.2",
|
132 |
+
"version_normalized": "0.8.2.0",
|
133 |
+
"source": {
|
134 |
+
"type": "git",
|
135 |
+
"url": "https://github.com/dompdf/dompdf.git",
|
136 |
+
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6"
|
137 |
+
},
|
138 |
+
"dist": {
|
139 |
+
"type": "zip",
|
140 |
+
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
141 |
+
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
142 |
+
"shasum": ""
|
143 |
+
},
|
144 |
+
"require": {
|
145 |
+
"ext-dom": "*",
|
146 |
+
"ext-gd": "*",
|
147 |
+
"ext-mbstring": "*",
|
148 |
+
"phenx/php-font-lib": "0.5.*",
|
149 |
+
"phenx/php-svg-lib": "0.3.*",
|
150 |
+
"php": ">=5.4.0"
|
151 |
+
},
|
152 |
+
"require-dev": {
|
153 |
+
"phpunit/phpunit": "4.8.*",
|
154 |
+
"squizlabs/php_codesniffer": "2.*"
|
155 |
+
},
|
156 |
+
"time": "2017-11-26T14:49:08+00:00",
|
157 |
+
"type": "library",
|
158 |
+
"extra": {
|
159 |
+
"branch-alias": {
|
160 |
+
"dev-develop": "0.7-dev"
|
161 |
+
}
|
162 |
+
},
|
163 |
+
"installation-source": "dist",
|
164 |
+
"autoload": {
|
165 |
+
"psr-4": {
|
166 |
+
"Dompdf\\": "src/"
|
167 |
+
},
|
168 |
+
"classmap": [
|
169 |
+
"lib/"
|
170 |
+
]
|
171 |
+
},
|
172 |
+
"notification-url": "https://packagist.org/downloads/",
|
173 |
+
"license": [
|
174 |
+
"LGPL-2.1"
|
175 |
+
],
|
176 |
+
"authors": [
|
177 |
+
{
|
178 |
+
"name": "Fabien Ménager",
|
179 |
+
"email": "fabien.menager@gmail.com"
|
180 |
+
},
|
181 |
+
{
|
182 |
+
"name": "Brian Sweeney",
|
183 |
+
"email": "eclecticgeek@gmail.com"
|
184 |
+
},
|
185 |
+
{
|
186 |
+
"name": "Gabriel Bull",
|
187 |
+
"email": "me@gabrielbull.com"
|
188 |
+
}
|
189 |
+
],
|
190 |
+
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
191 |
+
"homepage": "https://github.com/dompdf/dompdf"
|
192 |
+
}
|
193 |
+
]
|
vendor/dompdf/dompdf/CONTRIBUTING.md
CHANGED
@@ -1,65 +1,65 @@
|
|
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). You may also
|
11 |
-
seek help on
|
12 |
-
[StackOverflow](http://stackoverflow.com/questions/tagged/dompdf).
|
13 |
-
|
14 |
-
## Submitting bug reports
|
15 |
-
|
16 |
-
The preferred way to report bugs is to use the
|
17 |
-
[GitHub issue tracker](http://github.com/dompdf/dompdf/issues). Before
|
18 |
-
reporting a bug, read these pointers.
|
19 |
-
|
20 |
-
**Please search inside the bug tracker to see if the bug you found is not already reported.**
|
21 |
-
|
22 |
-
**Note:** The issue tracker is for *bugs* and *feature requests*, not requests for help.
|
23 |
-
Questions should be asked on the
|
24 |
-
[dompdf Google group](http://groups.google.com/group/dompdf) instead.
|
25 |
-
|
26 |
-
### Reporting bugs effectively
|
27 |
-
|
28 |
-
- dompdf is maintained by volunteers. They don't owe you anything, so be
|
29 |
-
polite. Reports with an indignant or belligerent tone tend to be moved to the
|
30 |
-
bottom of the pile.
|
31 |
-
|
32 |
-
- Include information about **the PHP version on which the problem occurred**. Even
|
33 |
-
if you tested several PHP version on different servers, and the problem occurred
|
34 |
-
in all of them, mention this fact in the bug report.
|
35 |
-
Also include the operating system it's installed on. PHP configuration can also help,
|
36 |
-
and server error logs (like Apache logs)
|
37 |
-
|
38 |
-
- Mention which release of dompdf you're using (the zip, the master branch, etc).
|
39 |
-
Preferably, try also with the current development snapshot, to ensure the
|
40 |
-
problem has not already been fixed.
|
41 |
-
|
42 |
-
- Mention very precisely what went wrong. "X is broken" is not a good bug
|
43 |
-
report. What did you expect to happen? What happened instead? Describe the
|
44 |
-
exact steps a maintainer has to take to make the problem occur. We can not
|
45 |
-
fix something that we can not observe.
|
46 |
-
|
47 |
-
- If the problem can not be reproduced in any of the demos included in the
|
48 |
-
dompdf distribution, please provide an HTML document that demonstrates
|
49 |
-
the problem. There are a few options to show us your code:
|
50 |
-
- [JS Fiddle](http://jsfiddle.net/)
|
51 |
-
- [dompdf debug helper](http://eclecticgeek.com/dompdf/debug.php) (provided by @bsweeney)
|
52 |
-
- Include the HTML/CSS inside the bug report, with
|
53 |
-
[code highlighting](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#wiki-code).
|
54 |
-
|
55 |
-
## Contributing code
|
56 |
-
|
57 |
-
- Make sure you have a [GitHub Account](https://github.com/signup/free)
|
58 |
-
- Fork [dompdf](https://github.com/dompdf/dompdf/)
|
59 |
-
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
|
60 |
-
- *Make your changes on the
|
61 |
-
the master branch if you are attempting to address an urgent bug in the released code.
|
62 |
-
- Add a simple test file in `www/test/`, with a comprehensive name.
|
63 |
-
- Add a unit test in the ``test/Dompdf/Tests/`` directory.
|
64 |
-
- Submit a pull request
|
65 |
-
([how to create a pull request](https://help.github.com/articles/fork-a-repo))
|
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). You may also
|
11 |
+
seek help on
|
12 |
+
[StackOverflow](http://stackoverflow.com/questions/tagged/dompdf).
|
13 |
+
|
14 |
+
## Submitting bug reports
|
15 |
+
|
16 |
+
The preferred way to report bugs is to use the
|
17 |
+
[GitHub issue tracker](http://github.com/dompdf/dompdf/issues). Before
|
18 |
+
reporting a bug, read these pointers.
|
19 |
+
|
20 |
+
**Please search inside the bug tracker to see if the bug you found is not already reported.**
|
21 |
+
|
22 |
+
**Note:** The issue tracker is for *bugs* and *feature requests*, not requests for help.
|
23 |
+
Questions should be asked on the
|
24 |
+
[dompdf Google group](http://groups.google.com/group/dompdf) instead.
|
25 |
+
|
26 |
+
### Reporting bugs effectively
|
27 |
+
|
28 |
+
- dompdf is maintained by volunteers. They don't owe you anything, so be
|
29 |
+
polite. Reports with an indignant or belligerent tone tend to be moved to the
|
30 |
+
bottom of the pile.
|
31 |
+
|
32 |
+
- Include information about **the PHP version on which the problem occurred**. Even
|
33 |
+
if you tested several PHP version on different servers, and the problem occurred
|
34 |
+
in all of them, mention this fact in the bug report.
|
35 |
+
Also include the operating system it's installed on. PHP configuration can also help,
|
36 |
+
and server error logs (like Apache logs)
|
37 |
+
|
38 |
+
- Mention which release of dompdf you're using (the zip, the master branch, etc).
|
39 |
+
Preferably, try also with the current development snapshot, to ensure the
|
40 |
+
problem has not already been fixed.
|
41 |
+
|
42 |
+
- Mention very precisely what went wrong. "X is broken" is not a good bug
|
43 |
+
report. What did you expect to happen? What happened instead? Describe the
|
44 |
+
exact steps a maintainer has to take to make the problem occur. We can not
|
45 |
+
fix something that we can not observe.
|
46 |
+
|
47 |
+
- If the problem can not be reproduced in any of the demos included in the
|
48 |
+
dompdf distribution, please provide an HTML document that demonstrates
|
49 |
+
the problem. There are a few options to show us your code:
|
50 |
+
- [JS Fiddle](http://jsfiddle.net/)
|
51 |
+
- [dompdf debug helper](http://eclecticgeek.com/dompdf/debug.php) (provided by @bsweeney)
|
52 |
+
- Include the HTML/CSS inside the bug report, with
|
53 |
+
[code highlighting](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#wiki-code).
|
54 |
+
|
55 |
+
## Contributing code
|
56 |
+
|
57 |
+
- Make sure you have a [GitHub Account](https://github.com/signup/free)
|
58 |
+
- Fork [dompdf](https://github.com/dompdf/dompdf/)
|
59 |
+
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
|
60 |
+
- *Make your changes on the `develop` branch* or the most appropriate feature branch. Please only patch
|
61 |
+
the master branch if you are attempting to address an urgent bug in the released code.
|
62 |
+
- Add a simple test file in `www/test/`, with a comprehensive name.
|
63 |
+
- Add a unit test in the ``test/Dompdf/Tests/`` directory.
|
64 |
+
- Submit a pull request
|
65 |
+
([how to create a pull request](https://help.github.com/articles/fork-a-repo))
|
vendor/dompdf/dompdf/README.md
CHANGED
@@ -1,207 +1,211 @@
|
|
1 |
-
Dompdf
|
2 |
-
======
|
3 |
-
|
4 |
-
[![Build Status](https://travis-ci.org/dompdf/dompdf.png?branch=master)](https://travis-ci.org/dompdf/dompdf)
|
5 |
-
[![Latest Stable Version](https://poser.pugx.org/dompdf/dompdf/v/stable.png)](https://packagist.org/packages/dompdf/dompdf)
|
6 |
-
[![Total Downloads](https://poser.pugx.org/dompdf/dompdf/downloads.png)](https://packagist.org/packages/dompdf/dompdf)
|
7 |
-
[![Latest Unstable Version](https://poser.pugx.org/dompdf/dompdf/v/unstable.png)](https://packagist.org/packages/dompdf/dompdf)
|
8 |
-
[![License](https://poser.pugx.org/dompdf/dompdf/license.png)](https://packagist.org/packages/dompdf/dompdf)
|
9 |
-
|
10 |
-
**Dompdf is an HTML to PDF converter**
|
11 |
-
|
12 |
-
At its heart, dompdf is (mostly) a [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant
|
13 |
-
HTML layout and rendering engine written in PHP. It is a style-driven renderer:
|
14 |
-
it will download and read external stylesheets, inline style tags, and the style
|
15 |
-
attributes of individual HTML elements. It also supports most presentational
|
16 |
-
HTML attributes.
|
17 |
-
|
18 |
-
*This document applies to the latest stable code which may not reflect the current
|
19 |
-
release. For released code please
|
20 |
-
[navigate to the appropriate tag](https://github.com/dompdf/dompdf/tags).*
|
21 |
-
|
22 |
-
----
|
23 |
-
|
24 |
-
**Check out the [demo](
|
25 |
-
question on [StackOverflow](http://stackoverflow.com/questions/tagged/dompdf) or
|
26 |
-
on the [Google Groups](http://groups.google.com/group/dompdf).**
|
27 |
-
|
28 |
-
Follow us on [![Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf) or
|
29 |
-
[![Follow us on Google+](https://ssl.gstatic.com/images/icons/gplus-16.png)](https://plus.google.com/108710008521858993320?prsrc=3).
|
30 |
-
|
31 |
-
---
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
## Features
|
36 |
-
|
37 |
-
* Handles most CSS 2.1 and a few CSS3 properties, including @import, @media &
|
38 |
-
@page rules
|
39 |
-
* Supports most presentational HTML 4.0 attributes
|
40 |
-
* Supports external stylesheets, either local or through http/ftp (via
|
41 |
-
fopen-wrappers)
|
42 |
-
* Supports complex tables, including row & column spans, separate & collapsed
|
43 |
-
border models, individual cell styling
|
44 |
-
* Image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
|
45 |
-
* No dependencies on external PDF libraries, thanks to the R&OS PDF class
|
46 |
-
* Inline PHP support
|
47 |
-
* Basic SVG support
|
48 |
-
|
49 |
-
## Requirements
|
50 |
-
|
51 |
-
* PHP version 5.
|
52 |
-
* DOM extension
|
53 |
-
* GD extension
|
54 |
-
* MBString extension
|
55 |
-
* php-font-lib
|
56 |
-
* php-svg-lib
|
57 |
-
|
58 |
-
### Recommendations
|
59 |
-
|
60 |
-
* OPcache (OPcache, XCache, APC, etc.): improves performance
|
61 |
-
* IMagick or GMagick extension: improves image processing performance
|
62 |
-
|
63 |
-
Visit the wiki for more information:
|
64 |
-
https://github.com/dompdf/dompdf/wiki/Requirements
|
65 |
-
|
66 |
-
## About Fonts & Character Encoding
|
67 |
-
|
68 |
-
PDF documents internally support the following fonts: Helvetica, Times-Roman,
|
69 |
-
Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI
|
70 |
-
encoding. In order for a PDF to display characters that are not available in
|
71 |
-
Windows ANSI you must supply an external font. Dompdf will embed any referenced
|
72 |
-
font in the PDF so long as it has been pre-loaded or is accessible to dompdf and
|
73 |
-
reference in CSS @font-face rules. See the
|
74 |
-
[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding)
|
75 |
-
for more information on how to use fonts.
|
76 |
-
|
77 |
-
The [DejaVu TrueType fonts](http://dejavu-fonts.org) have been pre-installed
|
78 |
-
to give dompdf decent Unicode character coverage by default. To use the DejaVu
|
79 |
-
fonts reference the font in your stylesheet, e.g. `body { font-family: DejaVu
|
80 |
-
Sans; }` (for DejaVu Sans). The following DejaVu 2.34 fonts are available:
|
81 |
-
DejaVu Sans, DejaVu Serif, and DejaVu Sans Mono.
|
82 |
-
|
83 |
-
## Easy Installation
|
84 |
-
|
85 |
-
### Install with composer
|
86 |
-
|
87 |
-
To install with [Composer](https://getcomposer.org/), simply require the
|
88 |
-
latest version of this package.
|
89 |
-
|
90 |
-
```bash
|
91 |
-
composer require dompdf/dompdf
|
92 |
-
```
|
93 |
-
|
94 |
-
Make sure that the autoload file from Composer is loaded.
|
95 |
-
|
96 |
-
```php
|
97 |
-
// somewhere early in your project's loading, require the Composer autoloader
|
98 |
-
// see: http://getcomposer.org/doc/00-intro.md
|
99 |
-
require 'vendor/autoload.php';
|
100 |
-
|
101 |
-
```
|
102 |
-
|
103 |
-
### Download and install
|
104 |
-
|
105 |
-
Download an archive of dompdf and extract it into the directory where dompdf
|
106 |
-
will reside
|
107 |
-
* You can download stable copies of dompdf from
|
108 |
-
https://github.com/dompdf/dompdf/releases
|
109 |
-
* Or download a nightly (the latest, unreleased code) from
|
110 |
-
http://eclecticgeek.com/dompdf
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
the
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
require_once 'dompdf/
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
//
|
158 |
-
$dompdf
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
```
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
|
|
|
|
|
|
|
1 |
+
Dompdf
|
2 |
+
======
|
3 |
+
|
4 |
+
[![Build Status](https://travis-ci.org/dompdf/dompdf.png?branch=master)](https://travis-ci.org/dompdf/dompdf)
|
5 |
+
[![Latest Stable Version](https://poser.pugx.org/dompdf/dompdf/v/stable.png)](https://packagist.org/packages/dompdf/dompdf)
|
6 |
+
[![Total Downloads](https://poser.pugx.org/dompdf/dompdf/downloads.png)](https://packagist.org/packages/dompdf/dompdf)
|
7 |
+
[![Latest Unstable Version](https://poser.pugx.org/dompdf/dompdf/v/unstable.png)](https://packagist.org/packages/dompdf/dompdf)
|
8 |
+
[![License](https://poser.pugx.org/dompdf/dompdf/license.png)](https://packagist.org/packages/dompdf/dompdf)
|
9 |
+
|
10 |
+
**Dompdf is an HTML to PDF converter**
|
11 |
+
|
12 |
+
At its heart, dompdf is (mostly) a [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant
|
13 |
+
HTML layout and rendering engine written in PHP. It is a style-driven renderer:
|
14 |
+
it will download and read external stylesheets, inline style tags, and the style
|
15 |
+
attributes of individual HTML elements. It also supports most presentational
|
16 |
+
HTML attributes.
|
17 |
+
|
18 |
+
*This document applies to the latest stable code which may not reflect the current
|
19 |
+
release. For released code please
|
20 |
+
[navigate to the appropriate tag](https://github.com/dompdf/dompdf/tags).*
|
21 |
+
|
22 |
+
----
|
23 |
+
|
24 |
+
**Check out the [demo](https://dompdf.net/examples.php) and ask any
|
25 |
+
question on [StackOverflow](http://stackoverflow.com/questions/tagged/dompdf) or
|
26 |
+
on the [Google Groups](http://groups.google.com/group/dompdf).**
|
27 |
+
|
28 |
+
Follow us on [![Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf) or
|
29 |
+
[![Follow us on Google+](https://ssl.gstatic.com/images/icons/gplus-16.png)](https://plus.google.com/108710008521858993320?prsrc=3).
|
30 |
+
|
31 |
+
---
|
32 |
+
|
33 |
+
|
34 |
+
|
35 |
+
## Features
|
36 |
+
|
37 |
+
* Handles most CSS 2.1 and a few CSS3 properties, including @import, @media &
|
38 |
+
@page rules
|
39 |
+
* Supports most presentational HTML 4.0 attributes
|
40 |
+
* Supports external stylesheets, either local or through http/ftp (via
|
41 |
+
fopen-wrappers)
|
42 |
+
* Supports complex tables, including row & column spans, separate & collapsed
|
43 |
+
border models, individual cell styling
|
44 |
+
* Image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
|
45 |
+
* No dependencies on external PDF libraries, thanks to the R&OS PDF class
|
46 |
+
* Inline PHP support
|
47 |
+
* Basic SVG support
|
48 |
+
|
49 |
+
## Requirements
|
50 |
+
|
51 |
+
* PHP version 5.4.0 or higher
|
52 |
+
* DOM extension
|
53 |
+
* GD extension
|
54 |
+
* MBString extension
|
55 |
+
* php-font-lib
|
56 |
+
* php-svg-lib
|
57 |
+
|
58 |
+
### Recommendations
|
59 |
+
|
60 |
+
* OPcache (OPcache, XCache, APC, etc.): improves performance
|
61 |
+
* IMagick or GMagick extension: improves image processing performance
|
62 |
+
|
63 |
+
Visit the wiki for more information:
|
64 |
+
https://github.com/dompdf/dompdf/wiki/Requirements
|
65 |
+
|
66 |
+
## About Fonts & Character Encoding
|
67 |
+
|
68 |
+
PDF documents internally support the following fonts: Helvetica, Times-Roman,
|
69 |
+
Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI
|
70 |
+
encoding. In order for a PDF to display characters that are not available in
|
71 |
+
Windows ANSI you must supply an external font. Dompdf will embed any referenced
|
72 |
+
font in the PDF so long as it has been pre-loaded or is accessible to dompdf and
|
73 |
+
reference in CSS @font-face rules. See the
|
74 |
+
[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding)
|
75 |
+
for more information on how to use fonts.
|
76 |
+
|
77 |
+
The [DejaVu TrueType fonts](http://dejavu-fonts.org) have been pre-installed
|
78 |
+
to give dompdf decent Unicode character coverage by default. To use the DejaVu
|
79 |
+
fonts reference the font in your stylesheet, e.g. `body { font-family: DejaVu
|
80 |
+
Sans; }` (for DejaVu Sans). The following DejaVu 2.34 fonts are available:
|
81 |
+
DejaVu Sans, DejaVu Serif, and DejaVu Sans Mono.
|
82 |
+
|
83 |
+
## Easy Installation
|
84 |
+
|
85 |
+
### Install with composer
|
86 |
+
|
87 |
+
To install with [Composer](https://getcomposer.org/), simply require the
|
88 |
+
latest version of this package.
|
89 |
+
|
90 |
+
```bash
|
91 |
+
composer require dompdf/dompdf
|
92 |
+
```
|
93 |
+
|
94 |
+
Make sure that the autoload file from Composer is loaded.
|
95 |
+
|
96 |
+
```php
|
97 |
+
// somewhere early in your project's loading, require the Composer autoloader
|
98 |
+
// see: http://getcomposer.org/doc/00-intro.md
|
99 |
+
require 'vendor/autoload.php';
|
100 |
+
|
101 |
+
```
|
102 |
+
|
103 |
+
### Download and install
|
104 |
+
|
105 |
+
Download an archive of dompdf and extract it into the directory where dompdf
|
106 |
+
will reside
|
107 |
+
* You can download stable copies of dompdf from
|
108 |
+
https://github.com/dompdf/dompdf/releases
|
109 |
+
* Or download a nightly (the latest, unreleased code) from
|
110 |
+
http://eclecticgeek.com/dompdf
|
111 |
+
|
112 |
+
Use the packaged release autoloader to load dompdf, libraries,
|
113 |
+
and helper functions in your PHP:
|
114 |
+
|
115 |
+
```php
|
116 |
+
// include autoloader
|
117 |
+
require_once 'dompdf/autoload.inc.php';
|
118 |
+
```
|
119 |
+
|
120 |
+
### Install with git
|
121 |
+
|
122 |
+
From the command line, switch to the directory where dompdf will reside and run
|
123 |
+
the following commands:
|
124 |
+
|
125 |
+
```sh
|
126 |
+
git clone https://github.com/dompdf/dompdf.git
|
127 |
+
cd dompdf
|
128 |
+
|
129 |
+
git clone https://github.com/PhenX/php-font-lib.git lib/php-font-lib
|
130 |
+
cd lib/php-font-lib
|
131 |
+
git checkout 0.5.1
|
132 |
+
cd ..
|
133 |
+
|
134 |
+
git clone https://github.com/PhenX/php-svg-lib.git php-svg-lib
|
135 |
+
cd php-svg-lib
|
136 |
+
git checkout v0.3
|
137 |
+
```
|
138 |
+
|
139 |
+
Require dompdf, libraries, and helper functions in your PHP:
|
140 |
+
|
141 |
+
```php
|
142 |
+
require_once 'dompdf/lib/html5lib/Parser.php';
|
143 |
+
require_once 'dompdf/lib/php-font-lib/src/FontLib/Autoloader.php';
|
144 |
+
require_once 'dompdf/lib/php-svg-lib/src/autoload.php';
|
145 |
+
require_once 'dompdf/src/Autoloader.php';
|
146 |
+
Dompdf\Autoloader::register();
|
147 |
+
```
|
148 |
+
|
149 |
+
## Quick Start
|
150 |
+
|
151 |
+
Just pass your HTML in to dompdf and stream the output:
|
152 |
+
|
153 |
+
```php
|
154 |
+
// reference the Dompdf namespace
|
155 |
+
use Dompdf\Dompdf;
|
156 |
+
|
157 |
+
// instantiate and use the dompdf class
|
158 |
+
$dompdf = new Dompdf();
|
159 |
+
$dompdf->loadHtml('hello world');
|
160 |
+
|
161 |
+
// (Optional) Setup the paper size and orientation
|
162 |
+
$dompdf->setPaper('A4', 'landscape');
|
163 |
+
|
164 |
+
// Render the HTML as PDF
|
165 |
+
$dompdf->render();
|
166 |
+
|
167 |
+
// Output the generated PDF to Browser
|
168 |
+
$dompdf->stream();
|
169 |
+
```
|
170 |
+
|
171 |
+
### Setting Options
|
172 |
+
|
173 |
+
Set options during dompdf instantiation:
|
174 |
+
|
175 |
+
```php
|
176 |
+
use Dompdf\Dompdf;
|
177 |
+
use Dompdf\Options;
|
178 |
+
|
179 |
+
$options = new Options();
|
180 |
+
$options->set('defaultFont', 'Courier');
|
181 |
+
$dompdf = new Dompdf($options);
|
182 |
+
```
|
183 |
+
|
184 |
+
or at run time
|
185 |
+
|
186 |
+
```php
|
187 |
+
use Dompdf\Dompdf;
|
188 |
+
|
189 |
+
$dompdf = new Dompdf();
|
190 |
+
$dompdf->set_option('defaultFont', 'Courier');
|
191 |
+
```
|
192 |
+
|
193 |
+
See [Dompdf\Options](src/Options.php) for a list of available options.
|
194 |
+
|
195 |
+
|
196 |
+
## Limitations (Known Issues)
|
197 |
+
|
198 |
+
* Dompdf is not particularly tolerant to poorly-formed HTML input. To avoid
|
199 |
+
any unexpected rendering issues you should either enable the built-in HTML5
|
200 |
+
parser at runtime (`$dompdf->set_option('isHtml5ParserEnabled', true);`)
|
201 |
+
or run your HTML through a HTML validator/cleaner (such as
|
202 |
+
[Tidy](http://tidy.sourceforge.net) or the
|
203 |
+
[W3C Markup Validation Service](http://validator.w3.org)).
|
204 |
+
* Large files or large tables can take a while to render.
|
205 |
+
* CSS float is in development and may not produce the desired result
|
206 |
+
|
207 |
+
---
|
208 |
+
|
209 |
+
[![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf)
|
210 |
+
|
211 |
+
*If you find this project useful, please consider making a donation. Any funds donated will be used to help further development on this project.)*
|
vendor/dompdf/dompdf/VERSION
CHANGED
@@ -1 +1 @@
|
|
1 |
-
<
|
1 |
+
<5113accd>
|
vendor/dompdf/dompdf/autoload.inc.php
DELETED
@@ -1,28 +0,0 @@
|
|
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 |
-
|
19 |
-
require_once __DIR__ . '/lib/html5lib/Parser.php';
|
20 |
-
require_once __DIR__ . '/lib/php-font-lib/src/FontLib/Autoloader.php';
|
21 |
-
require_once __DIR__ . '/lib/php-svg-lib/src/autoload.php';
|
22 |
-
|
23 |
-
/*
|
24 |
-
* New PHP 5.3.0 namespaced autoloader
|
25 |
-
*/
|
26 |
-
require_once __DIR__ . '/src/Autoloader.php';
|
27 |
-
|
28 |
-
Dompdf\Autoloader::register();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/dompdf/dompdf/composer.json
CHANGED
@@ -1,44 +1,44 @@
|
|
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-2.1",
|
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 |
-
"name": "Gabriel Bull",
|
18 |
-
"email": "me@gabrielbull.com"
|
19 |
-
}
|
20 |
-
],
|
21 |
-
"autoload": {
|
22 |
-
"psr-4" : {
|
23 |
-
"Dompdf\\" : "src/"
|
24 |
-
},
|
25 |
-
"classmap" : ["lib/"]
|
26 |
-
},
|
27 |
-
"require": {
|
28 |
-
"php": ">=5.
|
29 |
-
"ext-gd": "*",
|
30 |
-
"ext-dom": "*",
|
31 |
-
"ext-mbstring": "*",
|
32 |
-
"phenx/php-font-lib": "0.5.*",
|
33 |
-
"phenx/php-svg-lib": "0.
|
34 |
-
},
|
35 |
-
"require-dev": {
|
36 |
-
"phpunit/phpunit": "4.8.*",
|
37 |
-
"squizlabs/php_codesniffer": "2.*"
|
38 |
-
},
|
39 |
-
"extra": {
|
40 |
-
"branch-alias": {
|
41 |
-
"dev-develop": "0.7-dev"
|
42 |
-
}
|
43 |
-
}
|
44 |
-
}
|
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-2.1",
|
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 |
+
"name": "Gabriel Bull",
|
18 |
+
"email": "me@gabrielbull.com"
|
19 |
+
}
|
20 |
+
],
|
21 |
+
"autoload": {
|
22 |
+
"psr-4" : {
|
23 |
+
"Dompdf\\" : "src/"
|
24 |
+
},
|
25 |
+
"classmap" : ["lib/"]
|
26 |
+
},
|
27 |
+
"require": {
|
28 |
+
"php": ">=5.4.0",
|
29 |
+
"ext-gd": "*",
|
30 |
+
"ext-dom": "*",
|
31 |
+
"ext-mbstring": "*",
|
32 |
+
"phenx/php-font-lib": "0.5.*",
|
33 |
+
"phenx/php-svg-lib": "0.3.*"
|
34 |
+
},
|
35 |
+
"require-dev": {
|
36 |
+
"phpunit/phpunit": "4.8.*",
|
37 |
+
"squizlabs/php_codesniffer": "2.*"
|
38 |
+
},
|
39 |
+
"extra": {
|
40 |
+
"branch-alias": {
|
41 |
+
"dev-develop": "0.7-dev"
|
42 |
+
}
|
43 |
+
}
|
44 |
+
}
|
vendor/dompdf/dompdf/lib/Cpdf.php
CHANGED
@@ -1,5566 +1,5640 @@
|
|
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 |
-
use FontLib\Font;
|
19 |
-
use FontLib\BinaryStream;
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
*
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
*
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
*
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
*
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
*
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
*
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
*
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
*
|
194 |
-
*
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
*
|
201 |
-
*
|
202 |
-
*
|
203 |
-
*
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
*
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
*
|
216 |
-
*
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
*
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
*
|
274 |
-
*
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
*
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
'courier',
|
320 |
-
'courier-
|
321 |
-
'courier-
|
322 |
-
'
|
323 |
-
'helvetica',
|
324 |
-
'helvetica-
|
325 |
-
'helvetica-
|
326 |
-
'
|
327 |
-
'times-
|
328 |
-
'times-
|
329 |
-
'times-
|
330 |
-
'
|
331 |
-
'
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
*
|
337 |
-
*
|
338 |
-
*
|
339 |
-
* @param
|
340 |
-
* @param
|
341 |
-
* @param string $
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
$this->
|
347 |
-
$this->
|
348 |
-
$this->
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
*
|
364 |
-
*
|
365 |
-
*
|
366 |
-
*
|
367 |
-
*
|
368 |
-
*
|
369 |
-
*
|
370 |
-
*
|
371 |
-
*
|
372 |
-
*
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
*
|
379 |
-
*
|
380 |
-
* @param $
|
381 |
-
* @
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
$
|
390 |
-
|
391 |
-
|
392 |
-
case '
|
393 |
-
|
394 |
-
case '
|
395 |
-
|
396 |
-
case '
|
397 |
-
|
398 |
-
case '
|
399 |
-
|
400 |
-
case '
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
$
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
$
|
412 |
-
|
413 |
-
|
414 |
-
$res
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
*
|
424 |
-
*
|
425 |
-
* @param $
|
426 |
-
* @
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
$
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
case '
|
444 |
-
case '
|
445 |
-
case '
|
446 |
-
case '
|
447 |
-
case '
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
case '
|
460 |
-
case '
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
$
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
$v =
|
514 |
-
} elseif (
|
515 |
-
$v = (
|
516 |
-
} elseif (
|
517 |
-
$v =
|
518 |
-
}
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
$res
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
*
|
533 |
-
*
|
534 |
-
* @param $
|
535 |
-
* @
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
case '
|
552 |
-
case '
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
$this->numObj
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
$
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
$res
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
*
|
607 |
-
*
|
608 |
-
* @param $
|
609 |
-
* @
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
$
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
$
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
$
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
$tmp[
|
735 |
-
$tmp[
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
$res
|
743 |
-
}
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
*
|
755 |
-
*
|
756 |
-
* @param $
|
757 |
-
* @
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
$res
|
785 |
-
}
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
*
|
797 |
-
*
|
798 |
-
* @param $
|
799 |
-
* @
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
'
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
$this->numObj
|
826 |
-
|
827 |
-
$
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
case '
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
}
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
//
|
852 |
-
//
|
853 |
-
//
|
854 |
-
//
|
855 |
-
//
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
$
|
860 |
-
|
861 |
-
$
|
862 |
-
|
863 |
-
$
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
|
888 |
-
|
889 |
-
$this->
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
$res
|
945 |
-
$res .= "
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
|
966 |
-
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
|
1003 |
-
|
1004 |
-
$res = "\n$id 0 obj\n
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
-
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
1049 |
-
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
1068 |
-
|
1069 |
-
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
|
1074 |
-
|
1075 |
-
|
1076 |
-
|
1077 |
-
|
1078 |
-
|
1079 |
-
|
1080 |
-
|
1081 |
-
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
-
|
1087 |
-
|
1088 |
-
|
1089 |
-
|
1090 |
-
|
1091 |
-
|
1092 |
-
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
-
|
1105 |
-
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
1109 |
-
|
1110 |
-
|
1111 |
-
|
1112 |
-
|
1113 |
-
|
1114 |
-
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
1120 |
-
|
1121 |
-
|
1122 |
-
|
1123 |
-
|
1124 |
-
$res
|
1125 |
-
|
1126 |
-
$res
|
1127 |
-
|
1128 |
-
|
1129 |
-
|
1130 |
-
|
1131 |
-
|
1132 |
-
|
1133 |
-
|
1134 |
-
|
1135 |
-
|
1136 |
-
|
1137 |
-
|
1138 |
-
|
1139 |
-
|
1140 |
-
|
1141 |
-
|
1142 |
-
|
1143 |
-
|
1144 |
-
|
1145 |
-
|
1146 |
-
|
1147 |
-
|
1148 |
-
|
1149 |
-
|
1150 |
-
|
1151 |
-
|
1152 |
-
|
1153 |
-
|
1154 |
-
|
1155 |
-
|
1156 |
-
|
1157 |
-
|
1158 |
-
|
1159 |
-
|
1160 |
-
|
1161 |
-
|
1162 |
-
$
|
1163 |
-
|
1164 |
-
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
|
1169 |
-
|
1170 |
-
|
1171 |
-
|
1172 |
-
|
1173 |
-
|
1174 |
-
|
1175 |
-
|
1176 |
-
|
1177 |
-
}
|
1178 |
-
|
1179 |
-
|
1180 |
-
|
1181 |
-
|
1182 |
-
|
1183 |
-
|
1184 |
-
|
1185 |
-
|
1186 |
-
|
1187 |
-
|
1188 |
-
$res .= "/
|
1189 |
-
$
|
1190 |
-
$res
|
1191 |
-
|
1192 |
-
|
1193 |
-
|
1194 |
-
|
1195 |
-
|
1196 |
-
|
1197 |
-
|
1198 |
-
|
1199 |
-
|
1200 |
-
|
1201 |
-
|
1202 |
-
|
1203 |
-
|
1204 |
-
|
1205 |
-
|
1206 |
-
|
1207 |
-
|
1208 |
-
|
1209 |
-
|
1210 |
-
|
1211 |
-
|
1212 |
-
|
1213 |
-
|
1214 |
-
$
|
1215 |
-
|
1216 |
-
|
1217 |
-
|
1218 |
-
|
1219 |
-
|
1220 |
-
|
1221 |
-
|
1222 |
-
|
1223 |
-
|
1224 |
-
|
1225 |
-
|
1226 |
-
|
1227 |
-
|
1228 |
-
|
1229 |
-
|
1230 |
-
|
1231 |
-
|
1232 |
-
|
1233 |
-
|
1234 |
-
|
1235 |
-
|
1236 |
-
|
1237 |
-
|
1238 |
-
|
1239 |
-
|
1240 |
-
|
1241 |
-
|
1242 |
-
$
|
1243 |
-
|
1244 |
-
|
1245 |
-
|
1246 |
-
|
1247 |
-
|
1248 |
-
|
1249 |
-
|
1250 |
-
|
1251 |
-
|
1252 |
-
|
1253 |
-
|
1254 |
-
|
1255 |
-
|
1256 |
-
|
1257 |
-
|
1258 |
-
|
1259 |
-
|
1260 |
-
|
1261 |
-
|
1262 |
-
|
1263 |
-
|
1264 |
-
|
1265 |
-
|
1266 |
-
|
1267 |
-
|
1268 |
-
|
1269 |
-
|
1270 |
-
|
1271 |
-
|
1272 |
-
|
1273 |
-
|
1274 |
-
|
1275 |
-
|
1276 |
-
|
1277 |
-
|
1278 |
-
|
1279 |
-
|
1280 |
-
|
1281 |
-
|
1282 |
-
|
1283 |
-
|
1284 |
-
|
1285 |
-
|
1286 |
-
|
1287 |
-
|
1288 |
-
$
|
1289 |
-
|
1290 |
-
|
1291 |
-
|
1292 |
-
|
1293 |
-
|
1294 |
-
|
1295 |
-
|
1296 |
-
|
1297 |
-
|
1298 |
-
|
1299 |
-
|
1300 |
-
|
1301 |
-
|
1302 |
-
|
1303 |
-
|
1304 |
-
|
1305 |
-
|
1306 |
-
|
1307 |
-
|
1308 |
-
|
1309 |
-
|
1310 |
-
|
1311 |
-
|
1312 |
-
|
1313 |
-
|
1314 |
-
|
1315 |
-
|
1316 |
-
|
1317 |
-
|
1318 |
-
|
1319 |
-
|
1320 |
-
|
1321 |
-
|
1322 |
-
|
1323 |
-
|
1324 |
-
|
1325 |
-
|
1326 |
-
|
1327 |
-
|
1328 |
-
|
1329 |
-
|
1330 |
-
|
1331 |
-
|
1332 |
-
|
1333 |
-
|
1334 |
-
|
1335 |
-
|
1336 |
-
|
1337 |
-
|
1338 |
-
|
1339 |
-
$
|
1340 |
-
|
1341 |
-
|
1342 |
-
|
1343 |
-
|
1344 |
-
|
1345 |
-
|
1346 |
-
|
1347 |
-
|
1348 |
-
|
1349 |
-
|
1350 |
-
|
1351 |
-
|
1352 |
-
|
1353 |
-
|
1354 |
-
|
1355 |
-
|
1356 |
-
|
1357 |
-
|
1358 |
-
|
1359 |
-
|
1360 |
-
|
1361 |
-
|
1362 |
-
|
1363 |
-
|
1364 |
-
|
1365 |
-
|
1366 |
-
|
1367 |
-
|
1368 |
-
|
1369 |
-
|
1370 |
-
|
1371 |
-
|
1372 |
-
|
1373 |
-
|
1374 |
-
|
1375 |
-
|
1376 |
-
|
1377 |
-
|
1378 |
-
|
1379 |
-
|
1380 |
-
|
1381 |
-
|
1382 |
-
|
1383 |
-
|
1384 |
-
|
1385 |
-
|
1386 |
-
|
1387 |
-
|
1388 |
-
|
1389 |
-
|
1390 |
-
|
1391 |
-
|
1392 |
-
|
1393 |
-
|
1394 |
-
|
1395 |
-
|
1396 |
-
|
1397 |
-
|
1398 |
-
|
1399 |
-
|
1400 |
-
|
1401 |
-
|
1402 |
-
|
1403 |
-
|
1404 |
-
|
1405 |
-
|
1406 |
-
|
1407 |
-
|
1408 |
-
|
1409 |
-
|
1410 |
-
|
1411 |
-
|
1412 |
-
|
1413 |
-
|
1414 |
-
|
1415 |
-
|
1416 |
-
|
1417 |
-
|
1418 |
-
|
1419 |
-
|
1420 |
-
|
1421 |
-
|
1422 |
-
|
1423 |
-
|
1424 |
-
|
1425 |
-
|
1426 |
-
|
1427 |
-
|
1428 |
-
|
1429 |
-
|
1430 |
-
|
1431 |
-
|
1432 |
-
|
1433 |
-
|
1434 |
-
|
1435 |
-
|
1436 |
-
|
1437 |
-
|
1438 |
-
|
1439 |
-
|
1440 |
-
|
1441 |
-
|
1442 |
-
|
1443 |
-
|
1444 |
-
|
1445 |
-
|
1446 |
-
|
1447 |
-
|
1448 |
-
|
1449 |
-
|
1450 |
-
|
1451 |
-
|
1452 |
-
|
1453 |
-
|
1454 |
-
|
1455 |
-
|
1456 |
-
|
1457 |
-
|
1458 |
-
|
1459 |
-
|
1460 |
-
|
1461 |
-
|
1462 |
-
|
1463 |
-
|
1464 |
-
$res = "\n$id 0 obj\n<< /Type /
|
1465 |
-
switch ($o['
|
1466 |
-
case '
|
1467 |
-
|
1468 |
-
|
1469 |
-
|
1470 |
-
|
1471 |
-
|
1472 |
-
|
1473 |
-
|
1474 |
-
|
1475 |
-
|
1476 |
-
|
1477 |
-
|
1478 |
-
|
1479 |
-
|
1480 |
-
|
1481 |
-
|
1482 |
-
|
1483 |
-
|
1484 |
-
|
1485 |
-
|
1486 |
-
|
1487 |
-
|
1488 |
-
|
1489 |
-
|
1490 |
-
|
1491 |
-
|
1492 |
-
|
1493 |
-
|
1494 |
-
|
1495 |
-
|
1496 |
-
|
1497 |
-
|
1498 |
-
|
1499 |
-
|
1500 |
-
|
1501 |
-
|
1502 |
-
|
1503 |
-
|
1504 |
-
|
1505 |
-
|
1506 |
-
|
1507 |
-
|
1508 |
-
|
1509 |
-
|
1510 |
-
|
1511 |
-
|
1512 |
-
|
1513 |
-
|
1514 |
-
|
1515 |
-
|
1516 |
-
|
1517 |
-
|
1518 |
-
|
1519 |
-
|
1520 |
-
|
1521 |
-
|
1522 |
-
|
1523 |
-
|
1524 |
-
|
1525 |
-
|
1526 |
-
|
1527 |
-
|
1528 |
-
|
1529 |
-
|
1530 |
-
|
1531 |
-
|
1532 |
-
|
1533 |
-
|
1534 |
-
|
1535 |
-
|
1536 |
-
|
1537 |
-
|
1538 |
-
|
1539 |
-
|
1540 |
-
|
1541 |
-
|
1542 |
-
|
1543 |
-
|
1544 |
-
|
1545 |
-
|
1546 |
-
|
1547 |
-
|
1548 |
-
|
1549 |
-
|
1550 |
-
|
1551 |
-
|
1552 |
-
|
1553 |
-
|
1554 |
-
$res
|
1555 |
-
|
1556 |
-
|
1557 |
-
|
1558 |
-
|
1559 |
-
|
1560 |
-
|
1561 |
-
|
1562 |
-
|
1563 |
-
|
1564 |
-
|
1565 |
-
|
1566 |
-
|
1567 |
-
|
1568 |
-
|
1569 |
-
|
1570 |
-
|
1571 |
-
|
1572 |
-
|
1573 |
-
|
1574 |
-
|
1575 |
-
|
1576 |
-
|
1577 |
-
|
1578 |
-
|
1579 |
-
|
1580 |
-
|
1581 |
-
|
1582 |
-
|
1583 |
-
|
1584 |
-
|
1585 |
-
|
1586 |
-
|
1587 |
-
|
1588 |
-
|
1589 |
-
|
1590 |
-
|
1591 |
-
|
1592 |
-
|
1593 |
-
|
1594 |
-
|
1595 |
-
|
1596 |
-
|
1597 |
-
|
1598 |
-
|
1599 |
-
|
1600 |
-
|
1601 |
-
|
1602 |
-
|
1603 |
-
|
1604 |
-
|
1605 |
-
|
1606 |
-
|
1607 |
-
|
1608 |
-
|
1609 |
-
|
1610 |
-
|
1611 |
-
|
1612 |
-
|
1613 |
-
|
1614 |
-
|
1615 |
-
|
1616 |
-
|
1617 |
-
|
1618 |
-
|
1619 |
-
$
|
1620 |
-
}
|
1621 |
-
|
1622 |
-
|
1623 |
-
|
1624 |
-
|
1625 |
-
|
1626 |
-
|
1627 |
-
|
1628 |
-
|
1629 |
-
|
1630 |
-
|
1631 |
-
|
1632 |
-
|
1633 |
-
|
1634 |
-
|
1635 |
-
|
1636 |
-
|
1637 |
-
|
1638 |
-
|
1639 |
-
|
1640 |
-
|
1641 |
-
$res .= "
|
1642 |
-
|
1643 |
-
|
1644 |
-
|
1645 |
-
|
1646 |
-
|
1647 |
-
|
1648 |
-
|
1649 |
-
|
1650 |
-
|
1651 |
-
|
1652 |
-
|
1653 |
-
|
1654 |
-
|
1655 |
-
|
1656 |
-
|
1657 |
-
|
1658 |
-
|
1659 |
-
|
1660 |
-
|
1661 |
-
|
1662 |
-
|
1663 |
-
|
1664 |
-
|
1665 |
-
|
1666 |
-
|
1667 |
-
|
1668 |
-
|
1669 |
-
|
1670 |
-
|
1671 |
-
|
1672 |
-
|
1673 |
-
|
1674 |
-
|
1675 |
-
|
1676 |
-
|
1677 |
-
|
1678 |
-
|
1679 |
-
|
1680 |
-
|
1681 |
-
|
1682 |
-
|
1683 |
-
|
1684 |
-
|
1685 |
-
|
1686 |
-
|
1687 |
-
|
1688 |
-
|
1689 |
-
|
1690 |
-
|
1691 |
-
|
1692 |
-
|
1693 |
-
|
1694 |
-
|
1695 |
-
|
1696 |
-
|
1697 |
-
|
1698 |
-
|
1699 |
-
|
1700 |
-
|
1701 |
-
|
1702 |
-
|
1703 |
-
|
1704 |
-
|
1705 |
-
|
1706 |
-
|
1707 |
-
|
1708 |
-
|
1709 |
-
$
|
1710 |
-
|
1711 |
-
|
1712 |
-
|
1713 |
-
|
1714 |
-
|
1715 |
-
|
1716 |
-
|
1717 |
-
|
1718 |
-
|
1719 |
-
|
1720 |
-
|
1721 |
-
|
1722 |
-
|
1723 |
-
|
1724 |
-
|
1725 |
-
|
1726 |
-
|
1727 |
-
|
1728 |
-
|
1729 |
-
|
1730 |
-
|
1731 |
-
|
1732 |
-
|
1733 |
-
|
1734 |
-
|
1735 |
-
|
1736 |
-
|
1737 |
-
|
1738 |
-
|
1739 |
-
|
1740 |
-
|
1741 |
-
|
1742 |
-
|
1743 |
-
|
1744 |
-
|
1745 |
-
|
1746 |
-
|
1747 |
-
|
1748 |
-
|
1749 |
-
|
1750 |
-
$
|
1751 |
-
|
1752 |
-
|
1753 |
-
|
1754 |
-
|
1755 |
-
|
1756 |
-
|
1757 |
-
|
1758 |
-
|
1759 |
-
|
1760 |
-
|
1761 |
-
|
1762 |
-
|
1763 |
-
|
1764 |
-
|
1765 |
-
|
1766 |
-
|
1767 |
-
|
1768 |
-
|
1769 |
-
|
1770 |
-
|
1771 |
-
|
1772 |
-
|
1773 |
-
|
1774 |
-
|
1775 |
-
|
1776 |
-
|
1777 |
-
|
1778 |
-
|
1779 |
-
|
1780 |
-
|
1781 |
-
|
1782 |
-
|
1783 |
-
|
1784 |
-
|
1785 |
-
|
1786 |
-
|
1787 |
-
|
1788 |
-
|
1789 |
-
|
1790 |
-
|
1791 |
-
|
1792 |
-
|
1793 |
-
|
1794 |
-
|
1795 |
-
|
1796 |
-
|
1797 |
-
|
1798 |
-
|
1799 |
-
|
1800 |
-
|
1801 |
-
|
1802 |
-
|
1803 |
-
|
1804 |
-
|
1805 |
-
|
1806 |
-
|
1807 |
-
|
1808 |
-
|
1809 |
-
|
1810 |
-
|
1811 |
-
|
1812 |
-
|
1813 |
-
|
1814 |
-
|
1815 |
-
|
1816 |
-
|
1817 |
-
|
1818 |
-
|
1819 |
-
|
1820 |
-
|
1821 |
-
|
1822 |
-
|
1823 |
-
|
1824 |
-
|
1825 |
-
|
1826 |
-
|
1827 |
-
|
1828 |
-
|
1829 |
-
|
1830 |
-
|
1831 |
-
|
1832 |
-
|
1833 |
-
|
1834 |
-
|
1835 |
-
|
1836 |
-
|
1837 |
-
|
1838 |
-
|
1839 |
-
|
1840 |
-
|
1841 |
-
|
1842 |
-
|
1843 |
-
|
1844 |
-
|
1845 |
-
|
1846 |
-
|
1847 |
-
|
1848 |
-
|
1849 |
-
|
1850 |
-
|
1851 |
-
|
1852 |
-
|
1853 |
-
|
1854 |
-
|
1855 |
-
|
1856 |
-
|
1857 |
-
|
1858 |
-
|
1859 |
-
|
1860 |
-
|
1861 |
-
|
1862 |
-
|
1863 |
-
|
1864 |
-
|
1865 |
-
|
1866 |
-
|
1867 |
-
|
1868 |
-
|
1869 |
-
|
1870 |
-
|
1871 |
-
|
1872 |
-
|
1873 |
-
|
1874 |
-
|
1875 |
-
|
1876 |
-
|
1877 |
-
|
1878 |
-
|
1879 |
-
|
1880 |
-
|
1881 |
-
|
1882 |
-
|
1883 |
-
|
1884 |
-
|
1885 |
-
|
1886 |
-
|
1887 |
-
|
1888 |
-
|
1889 |
-
|
1890 |
-
|
1891 |
-
|
1892 |
-
|
1893 |
-
|
1894 |
-
|
1895 |
-
|
1896 |
-
|
1897 |
-
|
1898 |
-
|
1899 |
-
|
1900 |
-
|
1901 |
-
|
1902 |
-
|
1903 |
-
|
1904 |
-
|
1905 |
-
|
1906 |
-
|
1907 |
-
|
1908 |
-
|
1909 |
-
|
1910 |
-
|
1911 |
-
|
1912 |
-
|
1913 |
-
|
1914 |
-
|
1915 |
-
|
1916 |
-
|
1917 |
-
|
1918 |
-
//
|
1919 |
-
|
1920 |
-
$this->o_pages($this->currentNode, '
|
1921 |
-
|
1922 |
-
|
1923 |
-
|
1924 |
-
|
1925 |
-
|
1926 |
-
|
1927 |
-
|
1928 |
-
|
1929 |
-
|
1930 |
-
|
1931 |
-
|
1932 |
-
|
1933 |
-
|
1934 |
-
|
1935 |
-
|
1936 |
-
|
1937 |
-
|
1938 |
-
|
1939 |
-
|
1940 |
-
|
1941 |
-
|
1942 |
-
|
1943 |
-
|
1944 |
-
|
1945 |
-
|
1946 |
-
|
1947 |
-
|
1948 |
-
|
1949 |
-
|
1950 |
-
|
1951 |
-
|
1952 |
-
|
1953 |
-
|
1954 |
-
|
1955 |
-
|
1956 |
-
|
1957 |
-
|
1958 |
-
|
1959 |
-
|
1960 |
-
|
1961 |
-
|
1962 |
-
|
1963 |
-
|
1964 |
-
|
1965 |
-
|
1966 |
-
|
1967 |
-
|
1968 |
-
|
1969 |
-
|
1970 |
-
|
1971 |
-
|
1972 |
-
|
1973 |
-
|
1974 |
-
|
1975 |
-
|
1976 |
-
|
1977 |
-
|
1978 |
-
|
1979 |
-
|
1980 |
-
|
1981 |
-
|
1982 |
-
|
1983 |
-
|
1984 |
-
|
1985 |
-
|
1986 |
-
|
1987 |
-
|
1988 |
-
|
1989 |
-
$this->
|
1990 |
-
|
1991 |
-
|
1992 |
-
|
1993 |
-
|
1994 |
-
|
1995 |
-
|
1996 |
-
|
1997 |
-
|
1998 |
-
$
|
1999 |
-
|
2000 |
-
$
|
2001 |
-
|
2002 |
-
|
2003 |
-
|
2004 |
-
|
2005 |
-
|
2006 |
-
|
2007 |
-
|
2008 |
-
|
2009 |
-
|
2010 |
-
|
2011 |
-
|
2012 |
-
|
2013 |
-
|
2014 |
-
|
2015 |
-
|
2016 |
-
|
2017 |
-
|
2018 |
-
|
2019 |
-
|
2020 |
-
|
2021 |
-
|
2022 |
-
|
2023 |
-
|
2024 |
-
|
2025 |
-
|
2026 |
-
|
2027 |
-
|
2028 |
-
|
2029 |
-
|
2030 |
-
|
2031 |
-
|
2032 |
-
|
2033 |
-
|
2034 |
-
|
2035 |
-
|
2036 |
-
|
2037 |
-
|
2038 |
-
|
2039 |
-
|
2040 |
-
|
2041 |
-
|
2042 |
-
|
2043 |
-
|
2044 |
-
|
2045 |
-
|
2046 |
-
|
2047 |
-
|
2048 |
-
|
2049 |
-
|
2050 |
-
|
2051 |
-
|
2052 |
-
|
2053 |
-
|
2054 |
-
|
2055 |
-
|
2056 |
-
|
2057 |
-
|
2058 |
-
|
2059 |
-
|
2060 |
-
|
2061 |
-
|
2062 |
-
|
2063 |
-
|
2064 |
-
|
2065 |
-
|
2066 |
-
|
2067 |
-
|
2068 |
-
|
2069 |
-
|
2070 |
-
|
2071 |
-
|
2072 |
-
|
2073 |
-
|
2074 |
-
|
2075 |
-
|
2076 |
-
|
2077 |
-
|
2078 |
-
|
2079 |
-
|
2080 |
-
|
2081 |
-
|
2082 |
-
|
2083 |
-
|
2084 |
-
|
2085 |
-
|
2086 |
-
|
2087 |
-
|
2088 |
-
|
2089 |
-
|
2090 |
-
|
2091 |
-
|
2092 |
-
|
2093 |
-
|
2094 |
-
|
2095 |
-
|
2096 |
-
|
2097 |
-
|
2098 |
-
|
2099 |
-
|
2100 |
-
|
2101 |
-
|
2102 |
-
|
2103 |
-
|
2104 |
-
|
2105 |
-
|
2106 |
-
|
2107 |
-
|
2108 |
-
|
2109 |
-
|
2110 |
-
*
|
2111 |
-
|
2112 |
-
|
2113 |
-
|
2114 |
-
|
2115 |
-
|
2116 |
-
|
2117 |
-
$
|
2118 |
-
$out = '';
|
2119 |
-
for ($i = 0; $i
|
2120 |
-
$
|
2121 |
-
|
2122 |
-
|
2123 |
-
|
2124 |
-
|
2125 |
-
|
2126 |
-
|
2127 |
-
|
2128 |
-
|
2129 |
-
|
2130 |
-
|
2131 |
-
|
2132 |
-
|
2133 |
-
|
2134 |
-
|
2135 |
-
|
2136 |
-
|
2137 |
-
|
2138 |
-
|
2139 |
-
|
2140 |
-
|
2141 |
-
|
2142 |
-
|
2143 |
-
|
2144 |
-
|
2145 |
-
|
2146 |
-
|
2147 |
-
|
2148 |
-
|
2149 |
-
|
2150 |
-
|
2151 |
-
|
2152 |
-
|
2153 |
-
|
2154 |
-
|
2155 |
-
|
2156 |
-
|
2157 |
-
|
2158 |
-
|
2159 |
-
|
2160 |
-
|
2161 |
-
|
2162 |
-
|
2163 |
-
$
|
2164 |
-
|
2165 |
-
|
2166 |
-
|
2167 |
-
|
2168 |
-
|
2169 |
-
|
2170 |
-
|
2171 |
-
|
2172 |
-
|
2173 |
-
|
2174 |
-
|
2175 |
-
|
2176 |
-
|
2177 |
-
|
2178 |
-
|
2179 |
-
|
2180 |
-
|
2181 |
-
|
2182 |
-
|
2183 |
-
|
2184 |
-
|
2185 |
-
|
2186 |
-
|
2187 |
-
|
2188 |
-
|
2189 |
-
|
2190 |
-
|
2191 |
-
|
2192 |
-
|
2193 |
-
|
2194 |
-
|
2195 |
-
|
2196 |
-
$
|
2197 |
-
|
2198 |
-
|
2199 |
-
|
2200 |
-
|
2201 |
-
$
|
2202 |
-
|
2203 |
-
|
2204 |
-
|
2205 |
-
|
2206 |
-
|
2207 |
-
|
2208 |
-
|
2209 |
-
|
2210 |
-
|
2211 |
-
|
2212 |
-
/**
|
2213 |
-
*
|
2214 |
-
*
|
2215 |
-
* @param
|
2216 |
-
* @
|
2217 |
-
|
2218 |
-
|
2219 |
-
|
2220 |
-
|
2221 |
-
|
2222 |
-
|
2223 |
-
|
2224 |
-
|
2225 |
-
|
2226 |
-
|
2227 |
-
|
2228 |
-
|
2229 |
-
|
2230 |
-
|
2231 |
-
|
2232 |
-
|
2233 |
-
|
2234 |
-
|
2235 |
-
|
2236 |
-
|
2237 |
-
|
2238 |
-
|
2239 |
-
|
2240 |
-
|
2241 |
-
$this->
|
2242 |
-
|
2243 |
-
|
2244 |
-
|
2245 |
-
|
2246 |
-
|
2247 |
-
|
2248 |
-
|
2249 |
-
|
2250 |
-
|
2251 |
-
|
2252 |
-
|
2253 |
-
|
2254 |
-
|
2255 |
-
$
|
2256 |
-
|
2257 |
-
|
2258 |
-
|
2259 |
-
|
2260 |
-
|
2261 |
-
|
2262 |
-
|
2263 |
-
|
2264 |
-
|
2265 |
-
|
2266 |
-
|
2267 |
-
|
2268 |
-
|
2269 |
-
|
2270 |
-
|
2271 |
-
|
2272 |
-
|
2273 |
-
|
2274 |
-
|
2275 |
-
|
2276 |
-
|
2277 |
-
|
2278 |
-
|
2279 |
-
|
2280 |
-
|
2281 |
-
|
2282 |
-
*
|
2283 |
-
|
2284 |
-
|
2285 |
-
|
2286 |
-
|
2287 |
-
|
2288 |
-
|
2289 |
-
|
2290 |
-
|
2291 |
-
|
2292 |
-
|
2293 |
-
|
2294 |
-
|
2295 |
-
|
2296 |
-
|
2297 |
-
|
2298 |
-
|
2299 |
-
|
2300 |
-
|
2301 |
-
|
2302 |
-
|
2303 |
-
|
2304 |
-
|
2305 |
-
|
2306 |
-
|
2307 |
-
|
2308 |
-
|
2309 |
-
|
2310 |
-
|
2311 |
-
|
2312 |
-
|
2313 |
-
|
2314 |
-
|
2315 |
-
|
2316 |
-
|
2317 |
-
|
2318 |
-
|
2319 |
-
|
2320 |
-
|
2321 |
-
|
2322 |
-
|
2323 |
-
|
2324 |
-
|
2325 |
-
|
2326 |
-
|
2327 |
-
|
2328 |
-
|
2329 |
-
|
2330 |
-
|
2331 |
-
|
2332 |
-
|
2333 |
-
|
2334 |
-
|
2335 |
-
|
2336 |
-
|
2337 |
-
|
2338 |
-
|
2339 |
-
|
2340 |
-
|
2341 |
-
|
2342 |
-
|
2343 |
-
|
2344 |
-
|
2345 |
-
|
2346 |
-
|
2347 |
-
|
2348 |
-
|
2349 |
-
|
2350 |
-
|
2351 |
-
|
2352 |
-
|
2353 |
-
|
2354 |
-
|
2355 |
-
|
2356 |
-
|
2357 |
-
|
2358 |
-
|
2359 |
-
|
2360 |
-
|
2361 |
-
|
2362 |
-
|
2363 |
-
|
2364 |
-
|
2365 |
-
|
2366 |
-
|
2367 |
-
|
2368 |
-
|
2369 |
-
|
2370 |
-
|
2371 |
-
|
2372 |
-
|
2373 |
-
|
2374 |
-
|
2375 |
-
|
2376 |
-
|
2377 |
-
|
2378 |
-
|
2379 |
-
|
2380 |
-
|
2381 |
-
|
2382 |
-
|
2383 |
-
|
2384 |
-
|
2385 |
-
|
2386 |
-
|
2387 |
-
|
2388 |
-
|
2389 |
-
|
2390 |
-
|
2391 |
-
|
2392 |
-
|
2393 |
-
|
2394 |
-
|
2395 |
-
|
2396 |
-
|
2397 |
-
|
2398 |
-
|
2399 |
-
|
2400 |
-
|
2401 |
-
|
2402 |
-
|
2403 |
-
|
2404 |
-
|
2405 |
-
|
2406 |
-
|
2407 |
-
|
2408 |
-
|
2409 |
-
|
2410 |
-
|
2411 |
-
|
2412 |
-
|
2413 |
-
|
2414 |
-
|
2415 |
-
|
2416 |
-
|
2417 |
-
|
2418 |
-
|
2419 |
-
|
2420 |
-
|
2421 |
-
|
2422 |
-
|
2423 |
-
|
2424 |
-
|
2425 |
-
|
2426 |
-
|
2427 |
-
|
2428 |
-
|
2429 |
-
|
2430 |
-
|
2431 |
-
|
2432 |
-
|
2433 |
-
|
2434 |
-
|
2435 |
-
|
2436 |
-
|
2437 |
-
|
2438 |
-
|
2439 |
-
|
2440 |
-
|
2441 |
-
|
2442 |
-
|
2443 |
-
|
2444 |
-
|
2445 |
-
|
2446 |
-
|
2447 |
-
|
2448 |
-
|
2449 |
-
|
2450 |
-
|
2451 |
-
|
2452 |
-
|
2453 |
-
|
2454 |
-
|
2455 |
-
|
2456 |
-
|
2457 |
-
|
2458 |
-
|
2459 |
-
|
2460 |
-
|
2461 |
-
|
2462 |
-
|
2463 |
-
|
2464 |
-
|
2465 |
-
|
2466 |
-
|
2467 |
-
|
2468 |
-
|
2469 |
-
|
2470 |
-
|
2471 |
-
|
2472 |
-
|
2473 |
-
|
2474 |
-
|
2475 |
-
|
2476 |
-
|
2477 |
-
|
2478 |
-
|
2479 |
-
|
2480 |
-
|
2481 |
-
|
2482 |
-
|
2483 |
-
|
2484 |
-
|
2485 |
-
|
2486 |
-
|
2487 |
-
|
2488 |
-
|
2489 |
-
|
2490 |
-
|
2491 |
-
|
2492 |
-
|
2493 |
-
|
2494 |
-
|
2495 |
-
|
2496 |
-
|
2497 |
-
|
2498 |
-
|
2499 |
-
|
2500 |
-
|
2501 |
-
|
2502 |
-
|
2503 |
-
|
2504 |
-
|
2505 |
-
|
2506 |
-
|
2507 |
-
|
2508 |
-
|
2509 |
-
|
2510 |
-
|
2511 |
-
|
2512 |
-
|
2513 |
-
|
2514 |
-
|
2515 |
-
|
2516 |
-
|
2517 |
-
|
2518 |
-
|
2519 |
-
|
2520 |
-
|
2521 |
-
|
2522 |
-
|
2523 |
-
$
|
2524 |
-
|
2525 |
-
|
2526 |
-
|
2527 |
-
|
2528 |
-
|
2529 |
-
|
2530 |
-
|
2531 |
-
|
2532 |
-
|
2533 |
-
|
2534 |
-
|
2535 |
-
|
2536 |
-
|
2537 |
-
|
2538 |
-
|
2539 |
-
|
2540 |
-
|
2541 |
-
|
2542 |
-
|
2543 |
-
|
2544 |
-
|
2545 |
-
|
2546 |
-
|
2547 |
-
|
2548 |
-
|
2549 |
-
|
2550 |
-
|
2551 |
-
|
2552 |
-
|
2553 |
-
|
2554 |
-
|
2555 |
-
|
2556 |
-
|
2557 |
-
|
2558 |
-
|
2559 |
-
|
2560 |
-
|
2561 |
-
|
2562 |
-
|
2563 |
-
|
2564 |
-
|
2565 |
-
|
2566 |
-
|
2567 |
-
|
2568 |
-
|
2569 |
-
|
2570 |
-
|
2571 |
-
|
2572 |
-
|
2573 |
-
|
2574 |
-
|
2575 |
-
|
2576 |
-
|
2577 |
-
|
2578 |
-
|
2579 |
-
|
2580 |
-
|
2581 |
-
|
2582 |
-
|
2583 |
-
|
2584 |
-
|
2585 |
-
|
2586 |
-
|
2587 |
-
|
2588 |
-
|
2589 |
-
|
2590 |
-
|
2591 |
-
|
2592 |
-
|
2593 |
-
|
2594 |
-
|
2595 |
-
|
2596 |
-
|
2597 |
-
|
2598 |
-
|
2599 |
-
|
2600 |
-
|
2601 |
-
|
2602 |
-
|
2603 |
-
|
2604 |
-
|
2605 |
-
|
2606 |
-
|
2607 |
-
|
2608 |
-
|
2609 |
-
|
2610 |
-
|
2611 |
-
|
2612 |
-
|
2613 |
-
|
2614 |
-
|
2615 |
-
|
2616 |
-
|
2617 |
-
|
2618 |
-
|
2619 |
-
|
2620 |
-
|
2621 |
-
|
2622 |
-
|
2623 |
-
|
2624 |
-
|
2625 |
-
|
2626 |
-
|
2627 |
-
|
2628 |
-
|
2629 |
-
|
2630 |
-
|
2631 |
-
|
2632 |
-
|
2633 |
-
|
2634 |
-
|
2635 |
-
|
2636 |
-
|
2637 |
-
|
2638 |
-
|
2639 |
-
|
2640 |
-
|
2641 |
-
|
2642 |
-
|
2643 |
-
|
2644 |
-
|
2645 |
-
|
2646 |
-
|
2647 |
-
|
2648 |
-
|
2649 |
-
|
2650 |
-
|
2651 |
-
|
2652 |
-
|
2653 |
-
|
2654 |
-
|
2655 |
-
|
2656 |
-
|
2657 |
-
|
2658 |
-
|
2659 |
-
|
2660 |
-
|
2661 |
-
|
2662 |
-
|
2663 |
-
|
2664 |
-
|
2665 |
-
|
2666 |
-
|
2667 |
-
|
2668 |
-
|
2669 |
-
|
2670 |
-
|
2671 |
-
|
2672 |
-
|
2673 |
-
|
2674 |
-
|
2675 |
-
|
2676 |
-
|
2677 |
-
|
2678 |
-
|
2679 |
-
|
2680 |
-
|
2681 |
-
|
2682 |
-
|
2683 |
-
|
2684 |
-
|
2685 |
-
|
2686 |
-
|
2687 |
-
|
2688 |
-
|
2689 |
-
|
2690 |
-
|
2691 |
-
|
2692 |
-
|
2693 |
-
|
2694 |
-
|
2695 |
-
|
2696 |
-
|
2697 |
-
|
2698 |
-
|
2699 |
-
|
2700 |
-
|
2701 |
-
|
2702 |
-
|
2703 |
-
|
2704 |
-
|
2705 |
-
|
2706 |
-
|
2707 |
-
|
2708 |
-
|
2709 |
-
|
2710 |
-
|
2711 |
-
|
2712 |
-
|
2713 |
-
|
2714 |
-
|
2715 |
-
|
2716 |
-
|
2717 |
-
|
2718 |
-
|
2719 |
-
|
2720 |
-
|
2721 |
-
|
2722 |
-
|
2723 |
-
|
2724 |
-
|
2725 |
-
|
2726 |
-
|
2727 |
-
|
2728 |
-
|
2729 |
-
|
2730 |
-
|
2731 |
-
|
2732 |
-
|
2733 |
-
|
2734 |
-
|
2735 |
-
|
2736 |
-
|
2737 |
-
$
|
2738 |
-
|
2739 |
-
|
2740 |
-
|
2741 |
-
|
2742 |
-
|
2743 |
-
|
2744 |
-
|
2745 |
-
|
2746 |
-
|
2747 |
-
|
2748 |
-
|
2749 |
-
|
2750 |
-
|
2751 |
-
|
2752 |
-
|
2753 |
-
|
2754 |
-
|
2755 |
-
|
2756 |
-
|
2757 |
-
|
2758 |
-
|
2759 |
-
|
2760 |
-
|
2761 |
-
|
2762 |
-
|
2763 |
-
foreach ($
|
2764 |
-
if ($
|
2765 |
-
|
2766 |
-
|
2767 |
-
|
2768 |
-
|
2769 |
-
|
2770 |
-
|
2771 |
-
|
2772 |
-
|
2773 |
-
|
2774 |
-
|
2775 |
-
|
2776 |
-
|
2777 |
-
|
2778 |
-
|
2779 |
-
|
2780 |
-
|
2781 |
-
|
2782 |
-
|
2783 |
-
|
2784 |
-
|
2785 |
-
|
2786 |
-
|
2787 |
-
|
2788 |
-
|
2789 |
-
|
2790 |
-
|
2791 |
-
|
2792 |
-
|
2793 |
-
|
2794 |
-
|
2795 |
-
|
2796 |
-
$
|
2797 |
-
|
2798 |
-
|
2799 |
-
|
2800 |
-
|
2801 |
-
|
2802 |
-
$
|
2803 |
-
|
2804 |
-
|
2805 |
-
$
|
2806 |
-
|
2807 |
-
|
2808 |
-
$
|
2809 |
-
|
2810 |
-
|
2811 |
-
$
|
2812 |
-
|
2813 |
-
|
2814 |
-
|
2815 |
-
|
2816 |
-
|
2817 |
-
|
2818 |
-
|
2819 |
-
|
2820 |
-
|
2821 |
-
|
2822 |
-
|
2823 |
-
|
2824 |
-
|
2825 |
-
|
2826 |
-
|
2827 |
-
|
2828 |
-
|
2829 |
-
|
2830 |
-
|
2831 |
-
|
2832 |
-
|
2833 |
-
|
2834 |
-
|
2835 |
-
|
2836 |
-
|
2837 |
-
|
2838 |
-
|
2839 |
-
|
2840 |
-
$
|
2841 |
-
|
2842 |
-
|
2843 |
-
|
2844 |
-
|
2845 |
-
|
2846 |
-
|
2847 |
-
|
2848 |
-
|
2849 |
-
|
2850 |
-
|
2851 |
-
|
2852 |
-
|
2853 |
-
|
2854 |
-
|
2855 |
-
|
2856 |
-
|
2857 |
-
|
2858 |
-
|
2859 |
-
|
2860 |
-
|
2861 |
-
|
2862 |
-
|
2863 |
-
|
2864 |
-
|
2865 |
-
|
2866 |
-
|
2867 |
-
|
2868 |
-
|
2869 |
-
|
2870 |
-
|
2871 |
-
|
2872 |
-
|
2873 |
-
|
2874 |
-
|
2875 |
-
|
2876 |
-
|
2877 |
-
|
2878 |
-
|
2879 |
-
|
2880 |
-
|
2881 |
-
|
2882 |
-
|
2883 |
-
|
2884 |
-
|
2885 |
-
|
2886 |
-
$
|
2887 |
-
|
2888 |
-
$this->
|
2889 |
-
|
2890 |
-
|
2891 |
-
|
2892 |
-
|
2893 |
-
|
2894 |
-
|
2895 |
-
|
2896 |
-
|
2897 |
-
|
2898 |
-
|
2899 |
-
|
2900 |
-
|
2901 |
-
|
2902 |
-
|
2903 |
-
|
2904 |
-
|
2905 |
-
|
2906 |
-
|
2907 |
-
|
2908 |
-
|
2909 |
-
|
2910 |
-
|
2911 |
-
|
2912 |
-
|
2913 |
-
|
2914 |
-
|
2915 |
-
|
2916 |
-
|
2917 |
-
|
2918 |
-
|
2919 |
-
|
2920 |
-
|
2921 |
-
|
2922 |
-
|
2923 |
-
|
2924 |
-
|
2925 |
-
|
2926 |
-
|
2927 |
-
|
2928 |
-
|
2929 |
-
|
2930 |
-
|
2931 |
-
|
2932 |
-
|
2933 |
-
|
2934 |
-
|
2935 |
-
|
2936 |
-
|
2937 |
-
|
2938 |
-
|
2939 |
-
|
2940 |
-
|
2941 |
-
|
2942 |
-
|
2943 |
-
|
2944 |
-
|
2945 |
-
|
2946 |
-
|
2947 |
-
|
2948 |
-
|
2949 |
-
|
2950 |
-
|
2951 |
-
|
2952 |
-
|
2953 |
-
|
2954 |
-
|
2955 |
-
|
2956 |
-
|
2957 |
-
|
2958 |
-
|
2959 |
-
|
2960 |
-
|
2961 |
-
|
2962 |
-
|
2963 |
-
|
2964 |
-
|
2965 |
-
|
2966 |
-
|
2967 |
-
|
2968 |
-
|
2969 |
-
|
2970 |
-
|
2971 |
-
|
2972 |
-
|
2973 |
-
|
2974 |
-
|
2975 |
-
|
2976 |
-
|
2977 |
-
|
2978 |
-
|
2979 |
-
|
2980 |
-
|
2981 |
-
|
2982 |
-
|
2983 |
-
|
2984 |
-
|
2985 |
-
|
2986 |
-
|
2987 |
-
|
2988 |
-
|
2989 |
-
|
2990 |
-
|
2991 |
-
|
2992 |
-
|
2993 |
-
|
2994 |
-
|
2995 |
-
}
|
2996 |
-
|
2997 |
-
|
2998 |
-
|
2999 |
-
|
3000 |
-
|
3001 |
-
|
3002 |
-
|
3003 |
-
|
3004 |
-
|
3005 |
-
|
3006 |
-
|
3007 |
-
|
3008 |
-
|
3009 |
-
|
3010 |
-
|
3011 |
-
|
3012 |
-
|
3013 |
-
|
3014 |
-
|
3015 |
-
|
3016 |
-
|
3017 |
-
|
3018 |
-
|
3019 |
-
|
3020 |
-
|
3021 |
-
|
3022 |
-
|
3023 |
-
|
3024 |
-
|
3025 |
-
|
3026 |
-
|
3027 |
-
|
3028 |
-
|
3029 |
-
|
3030 |
-
|
3031 |
-
|
3032 |
-
|
3033 |
-
|
3034 |
-
|
3035 |
-
|
3036 |
-
|
3037 |
-
|
3038 |
-
*
|
3039 |
-
*
|
3040 |
-
* @
|
3041 |
-
*/
|
3042 |
-
function
|
3043 |
-
{
|
3044 |
-
|
3045 |
-
|
3046 |
-
|
3047 |
-
|
3048 |
-
|
3049 |
-
|
3050 |
-
|
3051 |
-
|
3052 |
-
|
3053 |
-
|
3054 |
-
|
3055 |
-
|
3056 |
-
|
3057 |
-
|
3058 |
-
*
|
3059 |
-
*
|
3060 |
-
*
|
3061 |
-
*
|
3062 |
-
|
3063 |
-
|
3064 |
-
|
3065 |
-
|
3066 |
-
|
3067 |
-
|
3068 |
-
|
3069 |
-
|
3070 |
-
|
3071 |
-
|
3072 |
-
|
3073 |
-
"
|
3074 |
-
|
3075 |
-
|
3076 |
-
|
3077 |
-
|
3078 |
-
|
3079 |
-
|
3080 |
-
|
3081 |
-
|
3082 |
-
|
3083 |
-
|
3084 |
-
|
3085 |
-
|
3086 |
-
|
3087 |
-
|
3088 |
-
|
3089 |
-
|
3090 |
-
|
3091 |
-
|
3092 |
-
|
3093 |
-
|
3094 |
-
|
3095 |
-
|
3096 |
-
|
3097 |
-
|
3098 |
-
|
3099 |
-
|
3100 |
-
|
3101 |
-
|
3102 |
-
|
3103 |
-
|
3104 |
-
|
3105 |
-
|
3106 |
-
|
3107 |
-
|
3108 |
-
|
3109 |
-
|
3110 |
-
|
3111 |
-
|
3112 |
-
|
3113 |
-
|
3114 |
-
|
3115 |
-
|
3116 |
-
|
3117 |
-
|
3118 |
-
|
3119 |
-
|
3120 |
-
|
3121 |
-
|
3122 |
-
|
3123 |
-
|
3124 |
-
|
3125 |
-
|
3126 |
-
|
3127 |
-
|
3128 |
-
|
3129 |
-
|
3130 |
-
|
3131 |
-
|
3132 |
-
|
3133 |
-
|
3134 |
-
|
3135 |
-
|
3136 |
-
|
3137 |
-
|
3138 |
-
|
3139 |
-
|
3140 |
-
|
3141 |
-
|
3142 |
-
|
3143 |
-
|
3144 |
-
|
3145 |
-
|
3146 |
-
|
3147 |
-
|
3148 |
-
|
3149 |
-
|
3150 |
-
|
3151 |
-
|
3152 |
-
$
|
3153 |
-
|
3154 |
-
|
3155 |
-
|
3156 |
-
|
3157 |
-
|
3158 |
-
|
3159 |
-
|
3160 |
-
|
3161 |
-
|
3162 |
-
|
3163 |
-
|
3164 |
-
|
3165 |
-
|
3166 |
-
|
3167 |
-
|
3168 |
-
|
3169 |
-
|
3170 |
-
|
3171 |
-
|
3172 |
-
|
3173 |
-
|
3174 |
-
|
3175 |
-
|
3176 |
-
|
3177 |
-
|
3178 |
-
|
3179 |
-
|
3180 |
-
|
3181 |
-
|
3182 |
-
|
3183 |
-
|
3184 |
-
|
3185 |
-
|
3186 |
-
|
3187 |
-
|
3188 |
-
|
3189 |
-
|
3190 |
-
|
3191 |
-
|
3192 |
-
|
3193 |
-
|
3194 |
-
|
3195 |
-
*
|
3196 |
-
*
|
3197 |
-
*
|
3198 |
-
* @param $
|
3199 |
-
* @param $
|
3200 |
-
|
3201 |
-
|
3202 |
-
|
3203 |
-
|
3204 |
-
|
3205 |
-
|
3206 |
-
|
3207 |
-
|
3208 |
-
|
3209 |
-
|
3210 |
-
|
3211 |
-
|
3212 |
-
|
3213 |
-
|
3214 |
-
|
3215 |
-
|
3216 |
-
|
3217 |
-
|
3218 |
-
|
3219 |
-
|
3220 |
-
|
3221 |
-
|
3222 |
-
|
3223 |
-
|
3224 |
-
|
3225 |
-
|
3226 |
-
|
3227 |
-
|
3228 |
-
|
3229 |
-
|
3230 |
-
|
3231 |
-
|
3232 |
-
|
3233 |
-
|
3234 |
-
|
3235 |
-
|
3236 |
-
|
3237 |
-
|
3238 |
-
|
3239 |
-
|
3240 |
-
|
3241 |
-
|
3242 |
-
|
3243 |
-
|
3244 |
-
|
3245 |
-
|
3246 |
-
|
3247 |
-
|
3248 |
-
|
3249 |
-
|
3250 |
-
|
3251 |
-
|
3252 |
-
|
3253 |
-
|
3254 |
-
|
3255 |
-
|
3256 |
-
|
3257 |
-
|
3258 |
-
|
3259 |
-
|
3260 |
-
|
3261 |
-
|
3262 |
-
|
3263 |
-
|
3264 |
-
|
3265 |
-
|
3266 |
-
|
3267 |
-
|
3268 |
-
|
3269 |
-
|
3270 |
-
|
3271 |
-
|
3272 |
-
|
3273 |
-
|
3274 |
-
|
3275 |
-
|
3276 |
-
|
3277 |
-
|
3278 |
-
|
3279 |
-
*
|
3280 |
-
*
|
3281 |
-
*
|
3282 |
-
* @param $
|
3283 |
-
* @param $
|
3284 |
-
* @param $
|
3285 |
-
* @param
|
3286 |
-
* @param int $
|
3287 |
-
* @param int $
|
3288 |
-
* @param int $
|
3289 |
-
|
3290 |
-
|
3291 |
-
|
3292 |
-
|
3293 |
-
|
3294 |
-
|
3295 |
-
|
3296 |
-
|
3297 |
-
|
3298 |
-
|
3299 |
-
|
3300 |
-
|
3301 |
-
|
3302 |
-
|
3303 |
-
|
3304 |
-
|
3305 |
-
|
3306 |
-
|
3307 |
-
|
3308 |
-
|
3309 |
-
|
3310 |
-
|
3311 |
-
|
3312 |
-
|
3313 |
-
|
3314 |
-
|
3315 |
-
|
3316 |
-
|
3317 |
-
|
3318 |
-
|
3319 |
-
|
3320 |
-
|
3321 |
-
|
3322 |
-
|
3323 |
-
|
3324 |
-
|
3325 |
-
|
3326 |
-
|
3327 |
-
|
3328 |
-
|
3329 |
-
|
3330 |
-
|
3331 |
-
|
3332 |
-
|
3333 |
-
|
3334 |
-
|
3335 |
-
|
3336 |
-
|
3337 |
-
|
3338 |
-
|
3339 |
-
|
3340 |
-
|
3341 |
-
|
3342 |
-
$
|
3343 |
-
|
3344 |
-
|
3345 |
-
|
3346 |
-
|
3347 |
-
|
3348 |
-
|
3349 |
-
|
3350 |
-
|
3351 |
-
|
3352 |
-
|
3353 |
-
|
3354 |
-
|
3355 |
-
|
3356 |
-
|
3357 |
-
|
3358 |
-
|
3359 |
-
|
3360 |
-
|
3361 |
-
|
3362 |
-
|
3363 |
-
|
3364 |
-
|
3365 |
-
|
3366 |
-
|
3367 |
-
|
3368 |
-
|
3369 |
-
|
3370 |
-
|
3371 |
-
|
3372 |
-
|
3373 |
-
|
3374 |
-
|
3375 |
-
|
3376 |
-
|
3377 |
-
|
3378 |
-
|
3379 |
-
|
3380 |
-
|
3381 |
-
|
3382 |
-
|
3383 |
-
|
3384 |
-
|
3385 |
-
|
3386 |
-
|
3387 |
-
|
3388 |
-
|
3389 |
-
|
3390 |
-
|
3391 |
-
|
3392 |
-
|
3393 |
-
|
3394 |
-
|
3395 |
-
|
3396 |
-
|
3397 |
-
|
3398 |
-
|
3399 |
-
|
3400 |
-
|
3401 |
-
|
3402 |
-
|
3403 |
-
|
3404 |
-
|
3405 |
-
|
3406 |
-
|
3407 |
-
|
3408 |
-
|
3409 |
-
|
3410 |
-
|
3411 |
-
|
3412 |
-
|
3413 |
-
|
3414 |
-
|
3415 |
-
|
3416 |
-
$
|
3417 |
-
|
3418 |
-
|
3419 |
-
|
3420 |
-
|
3421 |
-
|
3422 |
-
|
3423 |
-
|
3424 |
-
|
3425 |
-
|
3426 |
-
|
3427 |
-
|
3428 |
-
|
3429 |
-
|
3430 |
-
|
3431 |
-
|
3432 |
-
|
3433 |
-
|
3434 |
-
|
3435 |
-
|
3436 |
-
|
3437 |
-
|
3438 |
-
|
3439 |
-
|
3440 |
-
|
3441 |
-
|
3442 |
-
|
3443 |
-
|
3444 |
-
|
3445 |
-
|
3446 |
-
|
3447 |
-
|
3448 |
-
|
3449 |
-
|
3450 |
-
|
3451 |
-
|
3452 |
-
|
3453 |
-
|
3454 |
-
|
3455 |
-
|
3456 |
-
|
3457 |
-
|
3458 |
-
|
3459 |
-
|
3460 |
-
$
|
3461 |
-
|
3462 |
-
|
3463 |
-
|
3464 |
-
|
3465 |
-
|
3466 |
-
|
3467 |
-
|
3468 |
-
|
3469 |
-
|
3470 |
-
|
3471 |
-
|
3472 |
-
|
3473 |
-
|
3474 |
-
|
3475 |
-
|
3476 |
-
|
3477 |
-
|
3478 |
-
|
3479 |
-
|
3480 |
-
|
3481 |
-
|
3482 |
-
|
3483 |
-
|
3484 |
-
|
3485 |
-
|
3486 |
-
|
3487 |
-
|
3488 |
-
|
3489 |
-
|
3490 |
-
|
3491 |
-
|
3492 |
-
|
3493 |
-
*
|
3494 |
-
*
|
3495 |
-
*
|
3496 |
-
*
|
3497 |
-
*
|
3498 |
-
* @param $width
|
3499 |
-
* @param $
|
3500 |
-
|
3501 |
-
|
3502 |
-
|
3503 |
-
|
3504 |
-
|
3505 |
-
|
3506 |
-
|
3507 |
-
|
3508 |
-
|
3509 |
-
|
3510 |
-
|
3511 |
-
|
3512 |
-
|
3513 |
-
$
|
3514 |
-
|
3515 |
-
|
3516 |
-
|
3517 |
-
|
3518 |
-
|
3519 |
-
|
3520 |
-
|
3521 |
-
|
3522 |
-
|
3523 |
-
|
3524 |
-
|
3525 |
-
|
3526 |
-
|
3527 |
-
|
3528 |
-
|
3529 |
-
$this->
|
3530 |
-
|
3531 |
-
|
3532 |
-
|
3533 |
-
|
3534 |
-
|
3535 |
-
|
3536 |
-
|
3537 |
-
|
3538 |
-
|
3539 |
-
|
3540 |
-
|
3541 |
-
|
3542 |
-
|
3543 |
-
|
3544 |
-
|
3545 |
-
|
3546 |
-
|
3547 |
-
|
3548 |
-
|
3549 |
-
|
3550 |
-
|
3551 |
-
|
3552 |
-
|
3553 |
-
|
3554 |
-
|
3555 |
-
|
3556 |
-
|
3557 |
-
|
3558 |
-
*
|
3559 |
-
*
|
3560 |
-
* @param $
|
3561 |
-
* @param $
|
3562 |
-
* @param $
|
3563 |
-
|
3564 |
-
|
3565 |
-
|
3566 |
-
|
3567 |
-
|
3568 |
-
|
3569 |
-
|
3570 |
-
|
3571 |
-
|
3572 |
-
|
3573 |
-
|
3574 |
-
|
3575 |
-
|
3576 |
-
|
3577 |
-
|
3578 |
-
|
3579 |
-
|
3580 |
-
$this->
|
3581 |
-
|
3582 |
-
|
3583 |
-
|
3584 |
-
|
3585 |
-
|
3586 |
-
|
3587 |
-
|
3588 |
-
|
3589 |
-
|
3590 |
-
|
3591 |
-
|
3592 |
-
|
3593 |
-
|
3594 |
-
|
3595 |
-
|
3596 |
-
|
3597 |
-
|
3598 |
-
|
3599 |
-
|
3600 |
-
|
3601 |
-
|
3602 |
-
|
3603 |
-
|
3604 |
-
$this->addContent("
|
3605 |
-
}
|
3606 |
-
|
3607 |
-
|
3608 |
-
|
3609 |
-
|
3610 |
-
|
3611 |
-
|
3612 |
-
|
3613 |
-
|
3614 |
-
|
3615 |
-
|
3616 |
-
|
3617 |
-
|
3618 |
-
|
3619 |
-
|
3620 |
-
|
3621 |
-
|
3622 |
-
|
3623 |
-
|
3624 |
-
|
3625 |
-
|
3626 |
-
|
3627 |
-
|
3628 |
-
|
3629 |
-
|
3630 |
-
|
3631 |
-
|
3632 |
-
|
3633 |
-
|
3634 |
-
|
3635 |
-
|
3636 |
-
|
3637 |
-
|
3638 |
-
|
3639 |
-
|
3640 |
-
*
|
3641 |
-
|
3642 |
-
|
3643 |
-
|
3644 |
-
|
3645 |
-
|
3646 |
-
|
3647 |
-
|
3648 |
-
|
3649 |
-
|
3650 |
-
|
3651 |
-
|
3652 |
-
|
3653 |
-
|
3654 |
-
|
3655 |
-
|
3656 |
-
|
3657 |
-
|
3658 |
-
|
3659 |
-
|
3660 |
-
|
3661 |
-
|
3662 |
-
|
3663 |
-
|
3664 |
-
|
3665 |
-
|
3666 |
-
|
3667 |
-
|
3668 |
-
$
|
3669 |
-
|
3670 |
-
|
3671 |
-
$
|
3672 |
-
|
3673 |
-
|
3674 |
-
$
|
3675 |
-
|
3676 |
-
|
3677 |
-
|
3678 |
-
|
3679 |
-
|
3680 |
-
|
3681 |
-
|
3682 |
-
|
3683 |
-
$this->
|
3684 |
-
|
3685 |
-
|
3686 |
-
|
3687 |
-
|
3688 |
-
|
3689 |
-
|
3690 |
-
|
3691 |
-
|
3692 |
-
|
3693 |
-
|
3694 |
-
|
3695 |
-
|
3696 |
-
|
3697 |
-
|
3698 |
-
|
3699 |
-
|
3700 |
-
|
3701 |
-
|
3702 |
-
|
3703 |
-
|
3704 |
-
|
3705 |
-
|
3706 |
-
|
3707 |
-
|
3708 |
-
|
3709 |
-
|
3710 |
-
|
3711 |
-
|
3712 |
-
|
3713 |
-
|
3714 |
-
|
3715 |
-
|
3716 |
-
|
3717 |
-
|
3718 |
-
|
3719 |
-
|
3720 |
-
|
3721 |
-
|
3722 |
-
|
3723 |
-
|
3724 |
-
|
3725 |
-
|
3726 |
-
|
3727 |
-
|
3728 |
-
|
3729 |
-
|
3730 |
-
|
3731 |
-
|
3732 |
-
|
3733 |
-
|
3734 |
-
|
3735 |
-
|
3736 |
-
|
3737 |
-
|
3738 |
-
|
3739 |
-
|
3740 |
-
|
3741 |
-
|
3742 |
-
|
3743 |
-
|
3744 |
-
|
3745 |
-
|
3746 |
-
|
3747 |
-
|
3748 |
-
|
3749 |
-
|
3750 |
-
|
3751 |
-
|
3752 |
-
|
3753 |
-
|
3754 |
-
|
3755 |
-
|
3756 |
-
|
3757 |
-
|
3758 |
-
|
3759 |
-
|
3760 |
-
|
3761 |
-
|
3762 |
-
|
3763 |
-
|
3764 |
-
|
3765 |
-
|
3766 |
-
|
3767 |
-
|
3768 |
-
$
|
3769 |
-
|
3770 |
-
|
3771 |
-
|
3772 |
-
|
3773 |
-
|
3774 |
-
|
3775 |
-
|
3776 |
-
|
3777 |
-
|
3778 |
-
|
3779 |
-
|
3780 |
-
|
3781 |
-
*
|
3782 |
-
*
|
3783 |
-
*
|
3784 |
-
|
3785 |
-
|
3786 |
-
|
3787 |
-
|
3788 |
-
|
3789 |
-
|
3790 |
-
|
3791 |
-
|
3792 |
-
|
3793 |
-
|
3794 |
-
|
3795 |
-
|
3796 |
-
|
3797 |
-
|
3798 |
-
$
|
3799 |
-
|
3800 |
-
|
3801 |
-
|
3802 |
-
|
3803 |
-
|
3804 |
-
|
3805 |
-
|
3806 |
-
|
3807 |
-
|
3808 |
-
|
3809 |
-
|
3810 |
-
|
3811 |
-
|
3812 |
-
|
3813 |
-
|
3814 |
-
|
3815 |
-
|
3816 |
-
|
3817 |
-
|
3818 |
-
|
3819 |
-
|
3820 |
-
|
3821 |
-
|
3822 |
-
|
3823 |
-
|
3824 |
-
|
3825 |
-
|
3826 |
-
|
3827 |
-
|
3828 |
-
|
3829 |
-
|
3830 |
-
|
3831 |
-
|
3832 |
-
|
3833 |
-
|
3834 |
-
|
3835 |
-
|
3836 |
-
|
3837 |
-
|
3838 |
-
|
3839 |
-
|
3840 |
-
|
3841 |
-
|
3842 |
-
|
3843 |
-
|
3844 |
-
|
3845 |
-
|
3846 |
-
|
3847 |
-
|
3848 |
-
|
3849 |
-
|
3850 |
-
|
3851 |
-
|
3852 |
-
|
3853 |
-
|
3854 |
-
|
3855 |
-
|
3856 |
-
|
3857 |
-
|
3858 |
-
if (isset($
|
3859 |
-
$
|
3860 |
-
}
|
3861 |
-
|
3862 |
-
|
3863 |
-
|
3864 |
-
|
3865 |
-
|
3866 |
-
|
3867 |
-
|
3868 |
-
|
3869 |
-
|
3870 |
-
|
3871 |
-
|
3872 |
-
|
3873 |
-
|
3874 |
-
|
3875 |
-
|
3876 |
-
|
3877 |
-
|
3878 |
-
|
3879 |
-
|
3880 |
-
|
3881 |
-
|
3882 |
-
|
3883 |
-
|
3884 |
-
|
3885 |
-
|
3886 |
-
|
3887 |
-
|
3888 |
-
|
3889 |
-
|
3890 |
-
|
3891 |
-
|
3892 |
-
|
3893 |
-
|
3894 |
-
|
3895 |
-
|
3896 |
-
|
3897 |
-
|
3898 |
-
|
3899 |
-
|
3900 |
-
|
3901 |
-
|
3902 |
-
|
3903 |
-
|
3904 |
-
|
3905 |
-
|
3906 |
-
|
3907 |
-
|
3908 |
-
|
3909 |
-
|
3910 |
-
|
3911 |
-
|
3912 |
-
|
3913 |
-
|
3914 |
-
|
3915 |
-
|
3916 |
-
|
3917 |
-
|
3918 |
-
|
3919 |
-
|
3920 |
-
|
3921 |
-
|
3922 |
-
|
3923 |
-
|
3924 |
-
|
3925 |
-
|
3926 |
-
|
3927 |
-
|
3928 |
-
|
3929 |
-
|
3930 |
-
|
3931 |
-
|
3932 |
-
|
3933 |
-
|
3934 |
-
|
3935 |
-
|
3936 |
-
|
3937 |
-
|
3938 |
-
|
3939 |
-
|
3940 |
-
|
3941 |
-
|
3942 |
-
|
3943 |
-
|
3944 |
-
|
3945 |
-
|
3946 |
-
|
3947 |
-
|
3948 |
-
|
3949 |
-
|
3950 |
-
|
3951 |
-
|
3952 |
-
|
3953 |
-
|
3954 |
-
|
3955 |
-
|
3956 |
-
|
3957 |
-
|
3958 |
-
|
3959 |
-
|
3960 |
-
|
3961 |
-
|
3962 |
-
|
3963 |
-
|
3964 |
-
|
3965 |
-
|
3966 |
-
|
3967 |
-
|
3968 |
-
|
3969 |
-
|
3970 |
-
|
3971 |
-
|
3972 |
-
|
3973 |
-
|
3974 |
-
|
3975 |
-
|
3976 |
-
|
3977 |
-
|
3978 |
-
|
3979 |
-
|
3980 |
-
|
3981 |
-
|
3982 |
-
|
3983 |
-
|
3984 |
-
|
3985 |
-
|
3986 |
-
|
3987 |
-
|
3988 |
-
|
3989 |
-
|
3990 |
-
|
3991 |
-
|
3992 |
-
|
3993 |
-
|
3994 |
-
|
3995 |
-
|
3996 |
-
|
3997 |
-
|
3998 |
-
|
3999 |
-
|
4000 |
-
|
4001 |
-
|
4002 |
-
|
4003 |
-
|
4004 |
-
|
4005 |
-
|
4006 |
-
|
4007 |
-
|
4008 |
-
|
4009 |
-
|
4010 |
-
|
4011 |
-
|
4012 |
-
|
4013 |
-
|
4014 |
-
|
4015 |
-
|
4016 |
-
|
4017 |
-
|
4018 |
-
|
4019 |
-
|
4020 |
-
|
4021 |
-
|
4022 |
-
|
4023 |
-
|
4024 |
-
|
4025 |
-
|
4026 |
-
|
4027 |
-
|
4028 |
-
|
4029 |
-
|
4030 |
-
*
|
4031 |
-
*
|
4032 |
-
*
|
4033 |
-
*
|
4034 |
-
*
|
4035 |
-
*
|
4036 |
-
*
|
4037 |
-
* @
|
4038 |
-
|
4039 |
-
|
4040 |
-
|
4041 |
-
|
4042 |
-
|
4043 |
-
|
4044 |
-
|
4045 |
-
|
4046 |
-
|
4047 |
-
|
4048 |
-
|
4049 |
-
|
4050 |
-
|
4051 |
-
|
4052 |
-
|
4053 |
-
|
4054 |
-
|
4055 |
-
|
4056 |
-
|
4057 |
-
|
4058 |
-
|
4059 |
-
|
4060 |
-
|
4061 |
-
|
4062 |
-
|
4063 |
-
|
4064 |
-
|
4065 |
-
|
4066 |
-
|
4067 |
-
|
4068 |
-
|
4069 |
-
|
4070 |
-
|
4071 |
-
|
4072 |
-
|
4073 |
-
|
4074 |
-
|
4075 |
-
|
4076 |
-
|
4077 |
-
|
4078 |
-
|
4079 |
-
|
4080 |
-
|
4081 |
-
|
4082 |
-
|
4083 |
-
|
4084 |
-
|
4085 |
-
|
4086 |
-
|
4087 |
-
|
4088 |
-
|
4089 |
-
|
4090 |
-
|
4091 |
-
|
4092 |
-
|
4093 |
-
|
4094 |
-
|
4095 |
-
|
4096 |
-
|
4097 |
-
|
4098 |
-
|
4099 |
-
|
4100 |
-
*
|
4101 |
-
*
|
4102 |
-
|
4103 |
-
|
4104 |
-
|
4105 |
-
|
4106 |
-
|
4107 |
-
|
4108 |
-
|
4109 |
-
|
4110 |
-
|
4111 |
-
|
4112 |
-
|
4113 |
-
|
4114 |
-
|
4115 |
-
|
4116 |
-
|
4117 |
-
|
4118 |
-
|
4119 |
-
|
4120 |
-
|
4121 |
-
|
4122 |
-
|
4123 |
-
|
4124 |
-
|
4125 |
-
|
4126 |
-
|
4127 |
-
|
4128 |
-
|
4129 |
-
|
4130 |
-
|
4131 |
-
|
4132 |
-
|
4133 |
-
|
4134 |
-
|
4135 |
-
|
4136 |
-
|
4137 |
-
|
4138 |
-
|
4139 |
-
|
4140 |
-
*
|
4141 |
-
*
|
4142 |
-
* @param $
|
4143 |
-
* @param $
|
4144 |
-
* @param $
|
4145 |
-
* @param $text
|
4146 |
-
* @
|
4147 |
-
|
4148 |
-
|
4149 |
-
|
4150 |
-
|
4151 |
-
|
4152 |
-
|
4153 |
-
|
4154 |
-
|
4155 |
-
|
4156 |
-
|
4157 |
-
$
|
4158 |
-
|
4159 |
-
|
4160 |
-
|
4161 |
-
|
4162 |
-
|
4163 |
-
|
4164 |
-
|
4165 |
-
|
4166 |
-
|
4167 |
-
|
4168 |
-
|
4169 |
-
|
4170 |
-
|
4171 |
-
|
4172 |
-
|
4173 |
-
|
4174 |
-
|
4175 |
-
|
4176 |
-
|
4177 |
-
|
4178 |
-
|
4179 |
-
|
4180 |
-
|
4181 |
-
|
4182 |
-
|
4183 |
-
|
4184 |
-
|
4185 |
-
|
4186 |
-
|
4187 |
-
|
4188 |
-
|
4189 |
-
|
4190 |
-
|
4191 |
-
|
4192 |
-
|
4193 |
-
|
4194 |
-
|
4195 |
-
|
4196 |
-
|
4197 |
-
);
|
4198 |
-
}
|
4199 |
-
|
4200 |
-
|
4201 |
-
$this->
|
4202 |
-
|
4203 |
-
|
4204 |
-
|
4205 |
-
|
4206 |
-
|
4207 |
-
|
4208 |
-
|
4209 |
-
|
4210 |
-
|
4211 |
-
|
4212 |
-
|
4213 |
-
|
4214 |
-
|
4215 |
-
|
4216 |
-
|
4217 |
-
|
4218 |
-
|
4219 |
-
|
4220 |
-
$this->
|
4221 |
-
|
4222 |
-
|
4223 |
-
|
4224 |
-
|
4225 |
-
|
4226 |
-
|
4227 |
-
|
4228 |
-
|
4229 |
-
|
4230 |
-
|
4231 |
-
|
4232 |
-
|
4233 |
-
|
4234 |
-
|
4235 |
-
|
4236 |
-
|
4237 |
-
|
4238 |
-
|
4239 |
-
|
4240 |
-
|
4241 |
-
|
4242 |
-
'
|
4243 |
-
'
|
4244 |
-
'
|
4245 |
-
'
|
4246 |
-
'
|
4247 |
-
'
|
4248 |
-
|
4249 |
-
|
4250 |
-
|
4251 |
-
|
4252 |
-
|
4253 |
-
|
4254 |
-
|
4255 |
-
|
4256 |
-
|
4257 |
-
|
4258 |
-
|
4259 |
-
|
4260 |
-
|
4261 |
-
|
4262 |
-
|
4263 |
-
|
4264 |
-
|
4265 |
-
|
4266 |
-
|
4267 |
-
|
4268 |
-
|
4269 |
-
|
4270 |
-
|
4271 |
-
|
4272 |
-
|
4273 |
-
|
4274 |
-
|
4275 |
-
|
4276 |
-
|
4277 |
-
|
4278 |
-
|
4279 |
-
|
4280 |
-
|
4281 |
-
|
4282 |
-
|
4283 |
-
|
4284 |
-
|
4285 |
-
|
4286 |
-
|
4287 |
-
|
4288 |
-
|
4289 |
-
|
4290 |
-
|
4291 |
-
|
4292 |
-
|
4293 |
-
|
4294 |
-
|
4295 |
-
|
4296 |
-
|
4297 |
-
|
4298 |
-
|
4299 |
-
|
4300 |
-
|
4301 |
-
|
4302 |
-
|
4303 |
-
|
4304 |
-
|
4305 |
-
|
4306 |
-
|
4307 |
-
|
4308 |
-
|
4309 |
-
|
4310 |
-
|
4311 |
-
|
4312 |
-
|
4313 |
-
|
4314 |
-
|
4315 |
-
|
4316 |
-
|
4317 |
-
|
4318 |
-
|
4319 |
-
|
4320 |
-
|
4321 |
-
|
4322 |
-
|
4323 |
-
|
4324 |
-
|
4325 |
-
|
4326 |
-
|
4327 |
-
|
4328 |
-
|
4329 |
-
|
4330 |
-
|
4331 |
-
|
4332 |
-
|
4333 |
-
|
4334 |
-
|
4335 |
-
|
4336 |
-
|
4337 |
-
|
4338 |
-
|
4339 |
-
|
4340 |
-
|
4341 |
-
|
4342 |
-
|
4343 |
-
|
4344 |
-
|
4345 |
-
|
4346 |
-
|
4347 |
-
|
4348 |
-
|
4349 |
-
|
4350 |
-
|
4351 |
-
|
4352 |
-
|
4353 |
-
|
4354 |
-
|
4355 |
-
|
4356 |
-
|
4357 |
-
|
4358 |
-
|
4359 |
-
|
4360 |
-
|
4361 |
-
|
4362 |
-
|
4363 |
-
|
4364 |
-
|
4365 |
-
|
4366 |
-
|
4367 |
-
|
4368 |
-
|
4369 |
-
|
4370 |
-
|
4371 |
-
|
4372 |
-
|
4373 |
-
|
4374 |
-
|
4375 |
-
|
4376 |
-
|
4377 |
-
|
4378 |
-
|
4379 |
-
|
4380 |
-
|
4381 |
-
|
4382 |
-
//
|
4383 |
-
|
4384 |
-
|
4385 |
-
|
4386 |
-
|
4387 |
-
|
4388 |
-
|
4389 |
-
)
|
4390 |
-
|
4391 |
-
|
4392 |
-
|
4393 |
-
|
4394 |
-
|
4395 |
-
|
4396 |
-
|
4397 |
-
|
4398 |
-
|
4399 |
-
|
4400 |
-
|
4401 |
-
|
4402 |
-
|
4403 |
-
|
4404 |
-
|
4405 |
-
|
4406 |
-
|
4407 |
-
|
4408 |
-
|
4409 |
-
|
4410 |
-
|
4411 |
-
|
4412 |
-
|
4413 |
-
|
4414 |
-
|
4415 |
-
|
4416 |
-
|
4417 |
-
|
4418 |
-
|
4419 |
-
|
4420 |
-
|
4421 |
-
|
4422 |
-
|
4423 |
-
|
4424 |
-
|
4425 |
-
|
4426 |
-
|
4427 |
-
$
|
4428 |
-
|
4429 |
-
|
4430 |
-
|
4431 |
-
|
4432 |
-
|
4433 |
-
|
4434 |
-
|
4435 |
-
|
4436 |
-
|
4437 |
-
|
4438 |
-
|
4439 |
-
|
4440 |
-
|
4441 |
-
|
4442 |
-
|
4443 |
-
|
4444 |
-
|
4445 |
-
|
4446 |
-
|
4447 |
-
|
4448 |
-
|
4449 |
-
|
4450 |
-
$this->
|
4451 |
-
|
4452 |
-
|
4453 |
-
|
4454 |
-
|
4455 |
-
|
4456 |
-
|
4457 |
-
|
4458 |
-
|
4459 |
-
|
4460 |
-
|
4461 |
-
|
4462 |
-
|
4463 |
-
|
4464 |
-
|
4465 |
-
|
4466 |
-
|
4467 |
-
|
4468 |
-
|
4469 |
-
|
4470 |
-
|
4471 |
-
|
4472 |
-
|
4473 |
-
|
4474 |
-
|
4475 |
-
|
4476 |
-
|
4477 |
-
|
4478 |
-
|
4479 |
-
|
4480 |
-
|
4481 |
-
|
4482 |
-
|
4483 |
-
|
4484 |
-
|
4485 |
-
*
|
4486 |
-
*
|
4487 |
-
*
|
4488 |
-
* @
|
4489 |
-
*/
|
4490 |
-
function
|
4491 |
-
{
|
4492 |
-
|
4493 |
-
|
4494 |
-
|
4495 |
-
|
4496 |
-
|
4497 |
-
|
4498 |
-
|
4499 |
-
|
4500 |
-
|
4501 |
-
|
4502 |
-
|
4503 |
-
|
4504 |
-
|
4505 |
-
|
4506 |
-
|
4507 |
-
|
4508 |
-
|
4509 |
-
|
4510 |
-
|
4511 |
-
|
4512 |
-
|
4513 |
-
|
4514 |
-
|
4515 |
-
|
4516 |
-
|
4517 |
-
|
4518 |
-
|
4519 |
-
|
4520 |
-
|
4521 |
-
|
4522 |
-
|
4523 |
-
|
4524 |
-
|
4525 |
-
|
4526 |
-
|
4527 |
-
|
4528 |
-
|
4529 |
-
|
4530 |
-
|
4531 |
-
|
4532 |
-
|
4533 |
-
|
4534 |
-
|
4535 |
-
|
4536 |
-
|
4537 |
-
|
4538 |
-
|
4539 |
-
|
4540 |
-
|
4541 |
-
|
4542 |
-
|
4543 |
-
|
4544 |
-
|
4545 |
-
|
4546 |
-
|
4547 |
-
|
4548 |
-
|
4549 |
-
|
4550 |
-
|
4551 |
-
|
4552 |
-
|
4553 |
-
|
4554 |
-
|
4555 |
-
|
4556 |
-
|
4557 |
-
|
4558 |
-
|
4559 |
-
|
4560 |
-
|
4561 |
-
|
4562 |
-
|
4563 |
-
|
4564 |
-
|
4565 |
-
|
4566 |
-
|
4567 |
-
|
4568 |
-
|
4569 |
-
|
4570 |
-
|
4571 |
-
|
4572 |
-
|
4573 |
-
|
4574 |
-
|
4575 |
-
|
4576 |
-
|
4577 |
-
|
4578 |
-
|
4579 |
-
|
4580 |
-
|
4581 |
-
|
4582 |
-
|
4583 |
-
|
4584 |
-
|
4585 |
-
|
4586 |
-
|
4587 |
-
|
4588 |
-
|
4589 |
-
|
4590 |
-
|
4591 |
-
|
4592 |
-
|
4593 |
-
|
4594 |
-
|
4595 |
-
|
4596 |
-
|
4597 |
-
|
4598 |
-
|
4599 |
-
|
4600 |
-
|
4601 |
-
|
4602 |
-
|
4603 |
-
|
4604 |
-
|
4605 |
-
|
4606 |
-
|
4607 |
-
|
4608 |
-
|
4609 |
-
|
4610 |
-
|
4611 |
-
|
4612 |
-
*
|
4613 |
-
|
4614 |
-
|
4615 |
-
|
4616 |
-
|
4617 |
-
|
4618 |
-
|
4619 |
-
|
4620 |
-
|
4621 |
-
|
4622 |
-
|
4623 |
-
|
4624 |
-
|
4625 |
-
|
4626 |
-
|
4627 |
-
|
4628 |
-
|
4629 |
-
|
4630 |
-
|
4631 |
-
|
4632 |
-
|
4633 |
-
|
4634 |
-
|
4635 |
-
|
4636 |
-
|
4637 |
-
|
4638 |
-
|
4639 |
-
|
4640 |
-
|
4641 |
-
|
4642 |
-
|
4643 |
-
|
4644 |
-
|
4645 |
-
|
4646 |
-
|
4647 |
-
|
4648 |
-
|
4649 |
-
|
4650 |
-
|
4651 |
-
|
4652 |
-
|
4653 |
-
|
4654 |
-
|
4655 |
-
|
4656 |
-
|
4657 |
-
|
4658 |
-
|
4659 |
-
|
4660 |
-
|
4661 |
-
|
4662 |
-
|
4663 |
-
|
4664 |
-
|
4665 |
-
|
4666 |
-
|
4667 |
-
|
4668 |
-
|
4669 |
-
|
4670 |
-
|
4671 |
-
|
4672 |
-
|
4673 |
-
|
4674 |
-
|
4675 |
-
|
4676 |
-
|
4677 |
-
|
4678 |
-
|
4679 |
-
|
4680 |
-
|
4681 |
-
|
4682 |
-
|
4683 |
-
|
4684 |
-
|
4685 |
-
|
4686 |
-
|
4687 |
-
|
4688 |
-
|
4689 |
-
|
4690 |
-
$
|
4691 |
-
|
4692 |
-
|
4693 |
-
|
4694 |
-
|
4695 |
-
|
4696 |
-
|
4697 |
-
|
4698 |
-
|
4699 |
-
|
4700 |
-
|
4701 |
-
|
4702 |
-
|
4703 |
-
|
4704 |
-
|
4705 |
-
|
4706 |
-
|
4707 |
-
|
4708 |
-
|
4709 |
-
|
4710 |
-
|
4711 |
-
|
4712 |
-
|
4713 |
-
* @param $file
|
4714 |
-
* @param $x
|
4715 |
-
* @param $y
|
4716 |
-
* @param $w
|
4717 |
-
* @param $h
|
4718 |
-
* @param $
|
4719 |
-
|
4720 |
-
|
4721 |
-
|
4722 |
-
|
4723 |
-
|
4724 |
-
|
4725 |
-
if (
|
4726 |
-
|
4727 |
-
}
|
4728 |
-
|
4729 |
-
//
|
4730 |
-
|
4731 |
-
|
4732 |
-
|
4733 |
-
|
4734 |
-
|
4735 |
-
|
4736 |
-
|
4737 |
-
|
4738 |
-
|
4739 |
-
|
4740 |
-
|
4741 |
-
|
4742 |
-
|
4743 |
-
|
4744 |
-
|
4745 |
-
|
4746 |
-
|
4747 |
-
|
4748 |
-
|
4749 |
-
|
4750 |
-
|
4751 |
-
|
4752 |
-
|
4753 |
-
|
4754 |
-
|
4755 |
-
|
4756 |
-
|
4757 |
-
|
4758 |
-
$
|
4759 |
-
|
4760 |
-
|
4761 |
-
|
4762 |
-
|
4763 |
-
|
4764 |
-
|
4765 |
-
|
4766 |
-
|
4767 |
-
|
4768 |
-
$
|
4769 |
-
|
4770 |
-
|
4771 |
-
|
4772 |
-
|
4773 |
-
|
4774 |
-
|
4775 |
-
|
4776 |
-
|
4777 |
-
|
4778 |
-
|
4779 |
-
|
4780 |
-
|
4781 |
-
|
4782 |
-
|
4783 |
-
|
4784 |
-
|
4785 |
-
|
4786 |
-
|
4787 |
-
|
4788 |
-
|
4789 |
-
|
4790 |
-
|
4791 |
-
|
4792 |
-
|
4793 |
-
|
4794 |
-
|
4795 |
-
|
4796 |
-
|
4797 |
-
|
4798 |
-
|
4799 |
-
|
4800 |
-
|
4801 |
-
|
4802 |
-
|
4803 |
-
|
4804 |
-
|
4805 |
-
|
4806 |
-
|
4807 |
-
|
4808 |
-
|
4809 |
-
|
4810 |
-
|
4811 |
-
|
4812 |
-
|
4813 |
-
|
4814 |
-
|
4815 |
-
|
4816 |
-
|
4817 |
-
|
4818 |
-
|
4819 |
-
|
4820 |
-
|
4821 |
-
|
4822 |
-
|
4823 |
-
|
4824 |
-
|
4825 |
-
|
4826 |
-
|
4827 |
-
|
4828 |
-
|
4829 |
-
|
4830 |
-
|
4831 |
-
|
4832 |
-
|
4833 |
-
|
4834 |
-
|
4835 |
-
|
4836 |
-
|
4837 |
-
|
4838 |
-
|
4839 |
-
|
4840 |
-
|
4841 |
-
|
4842 |
-
|
4843 |
-
|
4844 |
-
|
4845 |
-
|
4846 |
-
|
4847 |
-
|
4848 |
-
|
4849 |
-
|
4850 |
-
|
4851 |
-
|
4852 |
-
|
4853 |
-
|
4854 |
-
|
4855 |
-
//
|
4856 |
-
|
4857 |
-
|
4858 |
-
|
4859 |
-
|
4860 |
-
|
4861 |
-
|
4862 |
-
|
4863 |
-
|
4864 |
-
|
4865 |
-
|
4866 |
-
|
4867 |
-
|
4868 |
-
|
4869 |
-
|
4870 |
-
|
4871 |
-
|
4872 |
-
|
4873 |
-
|
4874 |
-
|
4875 |
-
|
4876 |
-
|
4877 |
-
|
4878 |
-
|
4879 |
-
|
4880 |
-
|
4881 |
-
|
4882 |
-
|
4883 |
-
|
4884 |
-
|
4885 |
-
|
4886 |
-
|
4887 |
-
|
4888 |
-
|
4889 |
-
|
4890 |
-
|
4891 |
-
|
4892 |
-
|
4893 |
-
|
4894 |
-
|
4895 |
-
|
4896 |
-
|
4897 |
-
|
4898 |
-
|
4899 |
-
|
4900 |
-
|
4901 |
-
|
4902 |
-
|
4903 |
-
|
4904 |
-
|
4905 |
-
|
4906 |
-
|
4907 |
-
|
4908 |
-
|
4909 |
-
|
4910 |
-
|
4911 |
-
|
4912 |
-
|
4913 |
-
|
4914 |
-
|
4915 |
-
|
4916 |
-
|
4917 |
-
|
4918 |
-
|
4919 |
-
|
4920 |
-
|
4921 |
-
|
4922 |
-
|
4923 |
-
|
4924 |
-
|
4925 |
-
|
4926 |
-
|
4927 |
-
|
4928 |
-
|
4929 |
-
|
4930 |
-
|
4931 |
-
|
4932 |
-
|
4933 |
-
|
4934 |
-
|
4935 |
-
|
4936 |
-
|
4937 |
-
|
4938 |
-
|
4939 |
-
|
4940 |
-
|
4941 |
-
|
4942 |
-
|
4943 |
-
|
4944 |
-
|
4945 |
-
|
4946 |
-
|
4947 |
-
|
4948 |
-
|
4949 |
-
|
4950 |
-
|
4951 |
-
|
4952 |
-
|
4953 |
-
|
4954 |
-
|
4955 |
-
*
|
4956 |
-
*
|
4957 |
-
*
|
4958 |
-
* @param $
|
4959 |
-
* @param $
|
4960 |
-
* @
|
4961 |
-
|
4962 |
-
|
4963 |
-
|
4964 |
-
|
4965 |
-
|
4966 |
-
|
4967 |
-
|
4968 |
-
|
4969 |
-
|
4970 |
-
|
4971 |
-
|
4972 |
-
|
4973 |
-
|
4974 |
-
|
4975 |
-
|
4976 |
-
|
4977 |
-
|
4978 |
-
|
4979 |
-
|
4980 |
-
|
4981 |
-
|
4982 |
-
|
4983 |
-
|
4984 |
-
|
4985 |
-
|
4986 |
-
|
4987 |
-
|
4988 |
-
|
4989 |
-
|
4990 |
-
|
4991 |
-
|
4992 |
-
|
4993 |
-
|
4994 |
-
|
4995 |
-
|
4996 |
-
|
4997 |
-
|
4998 |
-
$
|
4999 |
-
|
5000 |
-
|
5001 |
-
|
5002 |
-
|
5003 |
-
|
5004 |
-
|
5005 |
-
|
5006 |
-
|
5007 |
-
|
5008 |
-
|
5009 |
-
|
5010 |
-
|
5011 |
-
|
5012 |
-
|
5013 |
-
|
5014 |
-
|
5015 |
-
|
5016 |
-
|
5017 |
-
|
5018 |
-
|
5019 |
-
|
5020 |
-
|
5021 |
-
|
5022 |
-
|
5023 |
-
|
5024 |
-
|
5025 |
-
|
5026 |
-
|
5027 |
-
|
5028 |
-
|
5029 |
-
|
5030 |
-
|
5031 |
-
|
5032 |
-
|
5033 |
-
|
5034 |
-
|
5035 |
-
|
5036 |
-
|
5037 |
-
|
5038 |
-
|
5039 |
-
|
5040 |
-
|
5041 |
-
|
5042 |
-
|
5043 |
-
|
5044 |
-
|
5045 |
-
|
5046 |
-
|
5047 |
-
|
5048 |
-
|
5049 |
-
|
5050 |
-
|
5051 |
-
|
5052 |
-
|
5053 |
-
|
5054 |
-
|
5055 |
-
|
5056 |
-
|
5057 |
-
|
5058 |
-
|
5059 |
-
|
5060 |
-
|
5061 |
-
|
5062 |
-
|
5063 |
-
|
5064 |
-
|
5065 |
-
|
5066 |
-
|
5067 |
-
|
5068 |
-
|
5069 |
-
|
5070 |
-
|
5071 |
-
|
5072 |
-
|
5073 |
-
|
5074 |
-
|
5075 |
-
|
5076 |
-
|
5077 |
-
|
5078 |
-
|
5079 |
-
|
5080 |
-
|
5081 |
-
|
5082 |
-
|
5083 |
-
|
5084 |
-
|
5085 |
-
|
5086 |
-
|
5087 |
-
|
5088 |
-
|
5089 |
-
|
5090 |
-
|
5091 |
-
|
5092 |
-
|
5093 |
-
|
5094 |
-
|
5095 |
-
|
5096 |
-
|
5097 |
-
|
5098 |
-
|
5099 |
-
|
5100 |
-
|
5101 |
-
|
5102 |
-
|
5103 |
-
|
5104 |
-
|
5105 |
-
|
5106 |
-
|
5107 |
-
|
5108 |
-
|
5109 |
-
|
5110 |
-
|
5111 |
-
|
5112 |
-
|
5113 |
-
|
5114 |
-
|
5115 |
-
|
5116 |
-
|
5117 |
-
|
5118 |
-
|
5119 |
-
|
5120 |
-
|
5121 |
-
|
5122 |
-
|
5123 |
-
|
5124 |
-
|
5125 |
-
|
5126 |
-
|
5127 |
-
|
5128 |
-
|
5129 |
-
|
5130 |
-
|
5131 |
-
|
5132 |
-
|
5133 |
-
|
5134 |
-
|
5135 |
-
|
5136 |
-
|
5137 |
-
|
5138 |
-
|
5139 |
-
|
5140 |
-
|
5141 |
-
|
5142 |
-
|
5143 |
-
|
5144 |
-
|
5145 |
-
|
5146 |
-
|
5147 |
-
|
5148 |
-
|
5149 |
-
|
5150 |
-
|
5151 |
-
|
5152 |
-
|
5153 |
-
|
5154 |
-
|
5155 |
-
|
5156 |
-
|
5157 |
-
|
5158 |
-
|
5159 |
-
|
5160 |
-
|
5161 |
-
|
5162 |
-
|
5163 |
-
|
5164 |
-
|
5165 |
-
|
5166 |
-
|
5167 |
-
|
5168 |
-
|
5169 |
-
|
5170 |
-
|
5171 |
-
|
5172 |
-
|
5173 |
-
|
5174 |
-
|
5175 |
-
|
5176 |
-
|
5177 |
-
|
5178 |
-
|
5179 |
-
|
5180 |
-
|
5181 |
-
|
5182 |
-
|
5183 |
-
|
5184 |
-
|
5185 |
-
|
5186 |
-
|
5187 |
-
|
5188 |
-
|
5189 |
-
|
5190 |
-
|
5191 |
-
|
5192 |
-
|
5193 |
-
|
5194 |
-
|
5195 |
-
|
5196 |
-
|
5197 |
-
|
5198 |
-
|
5199 |
-
|
5200 |
-
|
5201 |
-
|
5202 |
-
|
5203 |
-
|
5204 |
-
|
5205 |
-
|
5206 |
-
|
5207 |
-
|
5208 |
-
|
5209 |
-
|
5210 |
-
|
5211 |
-
|
5212 |
-
|
5213 |
-
|
5214 |
-
|
5215 |
-
|
5216 |
-
|
5217 |
-
|
5218 |
-
|
5219 |
-
|
5220 |
-
|
5221 |
-
|
5222 |
-
|
5223 |
-
|
5224 |
-
|
5225 |
-
|
5226 |
-
|
5227 |
-
|
5228 |
-
|
5229 |
-
|
5230 |
-
|
5231 |
-
|
5232 |
-
|
5233 |
-
|
5234 |
-
|
5235 |
-
|
5236 |
-
|
5237 |
-
|
5238 |
-
'
|
5239 |
-
|
5240 |
-
|
5241 |
-
|
5242 |
-
|
5243 |
-
|
5244 |
-
|
5245 |
-
|
5246 |
-
|
5247 |
-
|
5248 |
-
|
5249 |
-
|
5250 |
-
|
5251 |
-
|
5252 |
-
|
5253 |
-
|
5254 |
-
|
5255 |
-
|
5256 |
-
|
5257 |
-
|
5258 |
-
|
5259 |
-
|
5260 |
-
|
5261 |
-
|
5262 |
-
|
5263 |
-
|
5264 |
-
|
5265 |
-
|
5266 |
-
|
5267 |
-
|
5268 |
-
|
5269 |
-
|
5270 |
-
|
5271 |
-
|
5272 |
-
|
5273 |
-
|
5274 |
-
|
5275 |
-
|
5276 |
-
|
5277 |
-
|
5278 |
-
|
5279 |
-
|
5280 |
-
|
5281 |
-
|
5282 |
-
|
5283 |
-
|
5284 |
-
|
5285 |
-
|
5286 |
-
|
5287 |
-
|
5288 |
-
|
5289 |
-
|
5290 |
-
|
5291 |
-
|
5292 |
-
|
5293 |
-
|
5294 |
-
|
5295 |
-
|
5296 |
-
|
5297 |
-
|
5298 |
-
$
|
5299 |
-
|
5300 |
-
|
5301 |
-
|
5302 |
-
|
5303 |
-
|
5304 |
-
|
5305 |
-
|
5306 |
-
|
5307 |
-
|
5308 |
-
|
5309 |
-
|
5310 |
-
|
5311 |
-
|
5312 |
-
|
5313 |
-
|
5314 |
-
|
5315 |
-
|
5316 |
-
|
5317 |
-
|
5318 |
-
|
5319 |
-
|
5320 |
-
|
5321 |
-
|
5322 |
-
|
5323 |
-
|
5324 |
-
|
5325 |
-
|
5326 |
-
|
5327 |
-
|
5328 |
-
|
5329 |
-
|
5330 |
-
|
5331 |
-
|
5332 |
-
|
5333 |
-
|
5334 |
-
|
5335 |
-
|
5336 |
-
|
5337 |
-
|
5338 |
-
|
5339 |
-
$
|
5340 |
-
|
5341 |
-
|
5342 |
-
|
5343 |
-
$
|
5344 |
-
|
5345 |
-
|
5346 |
-
|
5347 |
-
|
5348 |
-
|
5349 |
-
|
5350 |
-
|
5351 |
-
|
5352 |
-
|
5353 |
-
|
5354 |
-
|
5355 |
-
|
5356 |
-
|
5357 |
-
|
5358 |
-
|
5359 |
-
|
5360 |
-
|
5361 |
-
|
5362 |
-
|
5363 |
-
|
5364 |
-
|
5365 |
-
|
5366 |
-
$this->
|
5367 |
-
|
5368 |
-
|
5369 |
-
|
5370 |
-
|
5371 |
-
|
5372 |
-
|
5373 |
-
|
5374 |
-
|
5375 |
-
|
5376 |
-
|
5377 |
-
|
5378 |
-
|
5379 |
-
|
5380 |
-
|
5381 |
-
|
5382 |
-
|
5383 |
-
|
5384 |
-
|
5385 |
-
|
5386 |
-
|
5387 |
-
|
5388 |
-
|
5389 |
-
|
5390 |
-
|
5391 |
-
|
5392 |
-
|
5393 |
-
|
5394 |
-
|
5395 |
-
|
5396 |
-
|
5397 |
-
|
5398 |
-
|
5399 |
-
|
5400 |
-
|
5401 |
-
|
5402 |
-
|
5403 |
-
|
5404 |
-
|
5405 |
-
|
5406 |
-
|
5407 |
-
|
5408 |
-
|
5409 |
-
|
5410 |
-
|
5411 |
-
|
5412 |
-
|
5413 |
-
|
5414 |
-
|
5415 |
-
$
|
5416 |
-
$
|
5417 |
-
|
5418 |
-
|
5419 |
-
|
5420 |
-
|
5421 |
-
|
5422 |
-
|
5423 |
-
|
5424 |
-
|
5425 |
-
|
5426 |
-
|
5427 |
-
|
5428 |
-
|
5429 |
-
|
5430 |
-
|
5431 |
-
|
5432 |
-
|
5433 |
-
|
5434 |
-
|
5435 |
-
|
5436 |
-
|
5437 |
-
|
5438 |
-
|
5439 |
-
|
5440 |
-
|
5441 |
-
|
5442 |
-
|
5443 |
-
|
5444 |
-
|
5445 |
-
|
5446 |
-
|
5447 |
-
|
5448 |
-
|
5449 |
-
|
5450 |
-
|
5451 |
-
|
5452 |
-
|
5453 |
-
|
5454 |
-
|
5455 |
-
|
5456 |
-
|
5457 |
-
|
5458 |
-
|
5459 |
-
|
5460 |
-
|
5461 |
-
|
5462 |
-
|
5463 |
-
|
5464 |
-
|
5465 |
-
|
5466 |
-
|
5467 |
-
|
5468 |
-
|
5469 |
-
|
5470 |
-
|
5471 |
-
|
5472 |
-
|
5473 |
-
|
5474 |
-
|
5475 |
-
|
5476 |
-
|
5477 |
-
|
5478 |
-
|
5479 |
-
|
5480 |
-
|
5481 |
-
|
5482 |
-
|
5483 |
-
|
5484 |
-
|
5485 |
-
|
5486 |
-
|
5487 |
-
|
5488 |
-
|
5489 |
-
|
5490 |
-
|
5491 |
-
|
5492 |
-
|
5493 |
-
|
5494 |
-
|
5495 |
-
|
5496 |
-
|
5497 |
-
|
5498 |
-
|
5499 |
-
|
5500 |
-
|
5501 |
-
|
5502 |
-
|
5503 |
-
|
5504 |
-
|
5505 |
-
*
|
5506 |
-
*
|
5507 |
-
* @param $
|
5508 |
-
|
5509 |
-
|
5510 |
-
|
5511 |
-
|
5512 |
-
|
5513 |
-
|
5514 |
-
|
5515 |
-
|
5516 |
-
|
5517 |
-
|
5518 |
-
|
5519 |
-
|
5520 |
-
|
5521 |
-
|
5522 |
-
|
5523 |
-
|
5524 |
-
|
5525 |
-
|
5526 |
-
|
5527 |
-
|
5528 |
-
|
5529 |
-
|
5530 |
-
|
5531 |
-
|
5532 |
-
|
5533 |
-
|
5534 |
-
|
5535 |
-
|
5536 |
-
|
5537 |
-
|
5538 |
-
|
5539 |
-
|
5540 |
-
|
5541 |
-
|
5542 |
-
|
5543 |
-
|
5544 |
-
|
5545 |
-
|
5546 |
-
|
5547 |
-
|
5548 |
-
|
5549 |
-
|
5550 |
-
|
5551 |
-
|
5552 |
-
|
5553 |
-
|
5554 |
-
|
5555 |
-
|
5556 |
-
|
5557 |
-
|
5558 |
-
|
5559 |
-
|
5560 |
-
|
5561 |
-
|
5562 |
-
|
5563 |
-
|
5564 |
-
|
5565 |
-
|
5566 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
use FontLib\Font;
|
19 |
+
use FontLib\BinaryStream;
|
20 |
+
|
21 |
+
class Cpdf
|
22 |
+
{
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var integer The current number of pdf objects in the document
|
26 |
+
*/
|
27 |
+
public $numObj = 0;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var array This array contains all of the pdf objects, ready for final assembly
|
31 |
+
*/
|
32 |
+
public $objects = array();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @var integer The objectId (number within the objects array) of the document catalog
|
36 |
+
*/
|
37 |
+
public $catalogId;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @var array Array carrying information about the fonts that the system currently knows about
|
41 |
+
* Used to ensure that a font is not loaded twice, among other things
|
42 |
+
*/
|
43 |
+
public $fonts = array();
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @var string The default font metrics file to use if no other font has been loaded.
|
47 |
+
* The path to the directory containing the font metrics should be included
|
48 |
+
*/
|
49 |
+
public $defaultFont = './fonts/Helvetica.afm';
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @string A record of the current font
|
53 |
+
*/
|
54 |
+
public $currentFont = '';
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @var string The current base font
|
58 |
+
*/
|
59 |
+
public $currentBaseFont = '';
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @var integer The number of the current font within the font array
|
63 |
+
*/
|
64 |
+
public $currentFontNum = 0;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @var integer
|
68 |
+
*/
|
69 |
+
public $currentNode;
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @var integer Object number of the current page
|
73 |
+
*/
|
74 |
+
public $currentPage;
|
75 |
+
|
76 |
+
/**
|
77 |
+
* @var integer Object number of the currently active contents block
|
78 |
+
*/
|
79 |
+
public $currentContents;
|
80 |
+
|
81 |
+
/**
|
82 |
+
* @var integer Number of fonts within the system
|
83 |
+
*/
|
84 |
+
public $numFonts = 0;
|
85 |
+
|
86 |
+
/**
|
87 |
+
* @var integer Number of graphic state resources used
|
88 |
+
*/
|
89 |
+
private $numStates = 0;
|
90 |
+
|
91 |
+
/**
|
92 |
+
* @var array Number of graphic state resources used
|
93 |
+
*/
|
94 |
+
private $gstates = array();
|
95 |
+
|
96 |
+
/**
|
97 |
+
* @var array Current color for fill operations, defaults to inactive value,
|
98 |
+
* all three components should be between 0 and 1 inclusive when active
|
99 |
+
*/
|
100 |
+
public $currentColor = null;
|
101 |
+
|
102 |
+
/**
|
103 |
+
* @var array Current color for stroke operations (lines etc.)
|
104 |
+
*/
|
105 |
+
public $currentStrokeColor = null;
|
106 |
+
|
107 |
+
/**
|
108 |
+
* @var string Fill rule (nonzero or evenodd)
|
109 |
+
*/
|
110 |
+
public $fillRule = "nonzero";
|
111 |
+
|
112 |
+
/**
|
113 |
+
* @var string Current style that lines are drawn in
|
114 |
+
*/
|
115 |
+
public $currentLineStyle = '';
|
116 |
+
|
117 |
+
/**
|
118 |
+
* @var array Current line transparency (partial graphics state)
|
119 |
+
*/
|
120 |
+
public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
121 |
+
|
122 |
+
/**
|
123 |
+
* array Current fill transparency (partial graphics state)
|
124 |
+
*/
|
125 |
+
public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
126 |
+
|
127 |
+
/**
|
128 |
+
* @var array An array which is used to save the state of the document, mainly the colors and styles
|
129 |
+
* it is used to temporarily change to another state, then change back to what it was before
|
130 |
+
*/
|
131 |
+
public $stateStack = array();
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @var integer Number of elements within the state stack
|
135 |
+
*/
|
136 |
+
public $nStateStack = 0;
|
137 |
+
|
138 |
+
/**
|
139 |
+
* @var integer Number of page objects within the document
|
140 |
+
*/
|
141 |
+
public $numPages = 0;
|
142 |
+
|
143 |
+
/**
|
144 |
+
* @var array Object Id storage stack
|
145 |
+
*/
|
146 |
+
public $stack = array();
|
147 |
+
|
148 |
+
/**
|
149 |
+
* @var integer Number of elements within the object Id storage stack
|
150 |
+
*/
|
151 |
+
public $nStack = 0;
|
152 |
+
|
153 |
+
/**
|
154 |
+
* an array which contains information about the objects which are not firmly attached to pages
|
155 |
+
* these have been added with the addObject function
|
156 |
+
*/
|
157 |
+
public $looseObjects = array();
|
158 |
+
|
159 |
+
/**
|
160 |
+
* array contains information about how the loose objects are to be added to the document
|
161 |
+
*/
|
162 |
+
public $addLooseObjects = array();
|
163 |
+
|
164 |
+
/**
|
165 |
+
* @var integer The objectId of the information object for the document
|
166 |
+
* this contains authorship, title etc.
|
167 |
+
*/
|
168 |
+
public $infoObject = 0;
|
169 |
+
|
170 |
+
/**
|
171 |
+
* @var integer Number of images being tracked within the document
|
172 |
+
*/
|
173 |
+
public $numImages = 0;
|
174 |
+
|
175 |
+
/**
|
176 |
+
* @var array An array containing options about the document
|
177 |
+
* it defaults to turning on the compression of the objects
|
178 |
+
*/
|
179 |
+
public $options = array('compression' => true);
|
180 |
+
|
181 |
+
/**
|
182 |
+
* @var integer The objectId of the first page of the document
|
183 |
+
*/
|
184 |
+
public $firstPageId;
|
185 |
+
|
186 |
+
/**
|
187 |
+
* @var integer The object Id of the procset object
|
188 |
+
*/
|
189 |
+
public $procsetObjectId;
|
190 |
+
|
191 |
+
/**
|
192 |
+
* @var array Store the information about the relationship between font families
|
193 |
+
* this used so that the code knows which font is the bold version of another font, etc.
|
194 |
+
* the value of this array is initialised in the constructor function.
|
195 |
+
*/
|
196 |
+
public $fontFamilies = array();
|
197 |
+
|
198 |
+
/**
|
199 |
+
* @var string Folder for php serialized formats of font metrics files.
|
200 |
+
* If empty string, use same folder as original metrics files.
|
201 |
+
* This can be passed in from class creator.
|
202 |
+
* If this folder does not exist or is not writable, Cpdf will be **much** slower.
|
203 |
+
* Because of potential trouble with php safe mode, folder cannot be created at runtime.
|
204 |
+
*/
|
205 |
+
public $fontcache = '';
|
206 |
+
|
207 |
+
/**
|
208 |
+
* @var integer The version of the font metrics cache file.
|
209 |
+
* This value must be manually incremented whenever the internal font data structure is modified.
|
210 |
+
*/
|
211 |
+
public $fontcacheVersion = 6;
|
212 |
+
|
213 |
+
/**
|
214 |
+
* @var string Temporary folder.
|
215 |
+
* If empty string, will attempt system tmp folder.
|
216 |
+
* This can be passed in from class creator.
|
217 |
+
*/
|
218 |
+
public $tmp = '';
|
219 |
+
|
220 |
+
/**
|
221 |
+
* @var string Track if the current font is bolded or italicised
|
222 |
+
*/
|
223 |
+
public $currentTextState = '';
|
224 |
+
|
225 |
+
/**
|
226 |
+
* @var string Messages are stored here during processing, these can be selected afterwards to give some useful debug information
|
227 |
+
*/
|
228 |
+
public $messages = '';
|
229 |
+
|
230 |
+
/**
|
231 |
+
* @var string The encryption array for the document encryption is stored here
|
232 |
+
*/
|
233 |
+
public $arc4 = '';
|
234 |
+
|
235 |
+
/**
|
236 |
+
* @var integer The object Id of the encryption information
|
237 |
+
*/
|
238 |
+
public $arc4_objnum = 0;
|
239 |
+
|
240 |
+
/**
|
241 |
+
* @var string The file identifier, used to uniquely identify a pdf document
|
242 |
+
*/
|
243 |
+
public $fileIdentifier = '';
|
244 |
+
|
245 |
+
/**
|
246 |
+
* @var boolean A flag to say if a document is to be encrypted or not
|
247 |
+
*/
|
248 |
+
public $encrypted = false;
|
249 |
+
|
250 |
+
/**
|
251 |
+
* @var string The encryption key for the encryption of all the document content (structure is not encrypted)
|
252 |
+
*/
|
253 |
+
public $encryptionKey = '';
|
254 |
+
|
255 |
+
/**
|
256 |
+
* @var array Array which forms a stack to keep track of nested callback functions
|
257 |
+
*/
|
258 |
+
public $callback = array();
|
259 |
+
|
260 |
+
/**
|
261 |
+
* @var integer The number of callback functions in the callback array
|
262 |
+
*/
|
263 |
+
public $nCallback = 0;
|
264 |
+
|
265 |
+
/**
|
266 |
+
* @var array Store label->id pairs for named destinations, these will be used to replace internal links
|
267 |
+
* done this way so that destinations can be defined after the location that links to them
|
268 |
+
*/
|
269 |
+
public $destinations = array();
|
270 |
+
|
271 |
+
/**
|
272 |
+
* @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
|
273 |
+
* publiciables within the class, so that the user can rollback at will (from each 'start' command)
|
274 |
+
* note that this includes the objects array, so these can be large.
|
275 |
+
*/
|
276 |
+
public $checkpoint = '';
|
277 |
+
|
278 |
+
/**
|
279 |
+
* @var array Table of Image origin filenames and image labels which were already added with o_image().
|
280 |
+
* Allows to merge identical images
|
281 |
+
*/
|
282 |
+
public $imagelist = array();
|
283 |
+
|
284 |
+
/**
|
285 |
+
* @var boolean Whether the text passed in should be treated as Unicode or just local character set.
|
286 |
+
*/
|
287 |
+
public $isUnicode = false;
|
288 |
+
|
289 |
+
/**
|
290 |
+
* @var string the JavaScript code of the document
|
291 |
+
*/
|
292 |
+
public $javascript = '';
|
293 |
+
|
294 |
+
/**
|
295 |
+
* @var boolean whether the compression is possible
|
296 |
+
*/
|
297 |
+
protected $compressionReady = false;
|
298 |
+
|
299 |
+
/**
|
300 |
+
* @var array Current page size
|
301 |
+
*/
|
302 |
+
protected $currentPageSize = array("width" => 0, "height" => 0);
|
303 |
+
|
304 |
+
/**
|
305 |
+
* @var array All the chars that will be required in the font subsets
|
306 |
+
*/
|
307 |
+
protected $stringSubsets = array();
|
308 |
+
|
309 |
+
/**
|
310 |
+
* @var string The target internal encoding
|
311 |
+
*/
|
312 |
+
static protected $targetEncoding = 'Windows-1252';
|
313 |
+
|
314 |
+
/**
|
315 |
+
* @var array The list of the core fonts
|
316 |
+
*/
|
317 |
+
static protected $coreFonts = array(
|
318 |
+
'courier',
|
319 |
+
'courier-bold',
|
320 |
+
'courier-oblique',
|
321 |
+
'courier-boldoblique',
|
322 |
+
'helvetica',
|
323 |
+
'helvetica-bold',
|
324 |
+
'helvetica-oblique',
|
325 |
+
'helvetica-boldoblique',
|
326 |
+
'times-roman',
|
327 |
+
'times-bold',
|
328 |
+
'times-italic',
|
329 |
+
'times-bolditalic',
|
330 |
+
'symbol',
|
331 |
+
'zapfdingbats'
|
332 |
+
);
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Class constructor
|
336 |
+
* This will start a new document
|
337 |
+
*
|
338 |
+
* @param array $pageSize Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
|
339 |
+
* @param boolean $isUnicode Whether text will be treated as Unicode or not.
|
340 |
+
* @param string $fontcache The font cache folder
|
341 |
+
* @param string $tmp The temporary folder
|
342 |
+
*/
|
343 |
+
function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '')
|
344 |
+
{
|
345 |
+
$this->isUnicode = $isUnicode;
|
346 |
+
$this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\");
|
347 |
+
$this->tmp = ($tmp !== '' ? $tmp : sys_get_temp_dir());
|
348 |
+
$this->newDocument($pageSize);
|
349 |
+
|
350 |
+
$this->compressionReady = function_exists('gzcompress');
|
351 |
+
|
352 |
+
if (in_array('Windows-1252', mb_list_encodings())) {
|
353 |
+
self::$targetEncoding = 'Windows-1252';
|
354 |
+
}
|
355 |
+
|
356 |
+
// also initialize the font families that are known about already
|
357 |
+
$this->setFontFamily('init');
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* Document object methods (internal use only)
|
362 |
+
*
|
363 |
+
* There is about one object method for each type of object in the pdf document
|
364 |
+
* Each function has the same call list ($id,$action,$options).
|
365 |
+
* $id = the object ID of the object, or what it is to be if it is being created
|
366 |
+
* $action = a string specifying the action to be performed, though ALL must support:
|
367 |
+
* 'new' - create the object with the id $id
|
368 |
+
* 'out' - produce the output for the pdf object
|
369 |
+
* $options = optional, a string or array containing the various parameters for the object
|
370 |
+
*
|
371 |
+
* These, in conjunction with the output function are the ONLY way for output to be produced
|
372 |
+
* within the pdf 'file'.
|
373 |
+
*/
|
374 |
+
|
375 |
+
/**
|
376 |
+
* Destination object, used to specify the location for the user to jump to, presently on opening
|
377 |
+
*
|
378 |
+
* @param $id
|
379 |
+
* @param $action
|
380 |
+
* @param string $options
|
381 |
+
* @return string|null
|
382 |
+
*/
|
383 |
+
protected function o_destination($id, $action, $options = '')
|
384 |
+
{
|
385 |
+
switch ($action) {
|
386 |
+
case 'new':
|
387 |
+
$this->objects[$id] = array('t' => 'destination', 'info' => array());
|
388 |
+
$tmp = '';
|
389 |
+
switch ($options['type']) {
|
390 |
+
case 'XYZ':
|
391 |
+
/** @noinspection PhpMissingBreakStatementInspection */
|
392 |
+
case 'FitR':
|
393 |
+
$tmp = ' ' . $options['p3'] . $tmp;
|
394 |
+
case 'FitH':
|
395 |
+
case 'FitV':
|
396 |
+
case 'FitBH':
|
397 |
+
/** @noinspection PhpMissingBreakStatementInspection */
|
398 |
+
case 'FitBV':
|
399 |
+
$tmp = ' ' . $options['p1'] . ' ' . $options['p2'] . $tmp;
|
400 |
+
case 'Fit':
|
401 |
+
case 'FitB':
|
402 |
+
$tmp = $options['type'] . $tmp;
|
403 |
+
$this->objects[$id]['info']['string'] = $tmp;
|
404 |
+
$this->objects[$id]['info']['page'] = $options['page'];
|
405 |
+
}
|
406 |
+
break;
|
407 |
+
|
408 |
+
case 'out':
|
409 |
+
$o = &$this->objects[$id];
|
410 |
+
|
411 |
+
$tmp = $o['info'];
|
412 |
+
$res = "\n$id 0 obj\n" . '[' . $tmp['page'] . ' 0 R /' . $tmp['string'] . "]\nendobj";
|
413 |
+
|
414 |
+
return $res;
|
415 |
+
}
|
416 |
+
|
417 |
+
return null;
|
418 |
+
}
|
419 |
+
|
420 |
+
/**
|
421 |
+
* set the viewer preferences
|
422 |
+
*
|
423 |
+
* @param $id
|
424 |
+
* @param $action
|
425 |
+
* @param string|array $options
|
426 |
+
* @return string|null
|
427 |
+
*/
|
428 |
+
protected function o_viewerPreferences($id, $action, $options = '')
|
429 |
+
{
|
430 |
+
switch ($action) {
|
431 |
+
case 'new':
|
432 |
+
$this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array());
|
433 |
+
break;
|
434 |
+
|
435 |
+
case 'add':
|
436 |
+
$o = &$this->objects[$id];
|
437 |
+
|
438 |
+
foreach ($options as $k => $v) {
|
439 |
+
switch ($k) {
|
440 |
+
// Boolean keys
|
441 |
+
case 'HideToolbar':
|
442 |
+
case 'HideMenubar':
|
443 |
+
case 'HideWindowUI':
|
444 |
+
case 'FitWindow':
|
445 |
+
case 'CenterWindow':
|
446 |
+
case 'DisplayDocTitle':
|
447 |
+
case 'PickTrayByPDFSize':
|
448 |
+
$o['info'][$k] = (bool)$v;
|
449 |
+
break;
|
450 |
+
|
451 |
+
// Integer keys
|
452 |
+
case 'NumCopies':
|
453 |
+
$o['info'][$k] = (int)$v;
|
454 |
+
break;
|
455 |
+
|
456 |
+
// Name keys
|
457 |
+
case 'ViewArea':
|
458 |
+
case 'ViewClip':
|
459 |
+
case 'PrintClip':
|
460 |
+
case 'PrintArea':
|
461 |
+
$o['info'][$k] = (string)$v;
|
462 |
+
break;
|
463 |
+
|
464 |
+
// Named with limited valid values
|
465 |
+
case 'NonFullScreenPageMode':
|
466 |
+
if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) {
|
467 |
+
continue;
|
468 |
+
}
|
469 |
+
$o['info'][$k] = $v;
|
470 |
+
break;
|
471 |
+
|
472 |
+
case 'Direction':
|
473 |
+
if (!in_array($v, array('L2R', 'R2L'))) {
|
474 |
+
continue;
|
475 |
+
}
|
476 |
+
$o['info'][$k] = $v;
|
477 |
+
break;
|
478 |
+
|
479 |
+
case 'PrintScaling':
|
480 |
+
if (!in_array($v, array('None', 'AppDefault'))) {
|
481 |
+
continue;
|
482 |
+
}
|
483 |
+
$o['info'][$k] = $v;
|
484 |
+
break;
|
485 |
+
|
486 |
+
case 'Duplex':
|
487 |
+
if (!in_array($v, array('None', 'AppDefault'))) {
|
488 |
+
continue;
|
489 |
+
}
|
490 |
+
$o['info'][$k] = $v;
|
491 |
+
break;
|
492 |
+
|
493 |
+
// Integer array
|
494 |
+
case 'PrintPageRange':
|
495 |
+
// Cast to integer array
|
496 |
+
foreach ($v as $vK => $vV) {
|
497 |
+
$v[$vK] = (int)$vV;
|
498 |
+
}
|
499 |
+
$o['info'][$k] = array_values($v);
|
500 |
+
break;
|
501 |
+
}
|
502 |
+
}
|
503 |
+
break;
|
504 |
+
|
505 |
+
case 'out':
|
506 |
+
$o = &$this->objects[$id];
|
507 |
+
$res = "\n$id 0 obj\n<< ";
|
508 |
+
|
509 |
+
foreach ($o['info'] as $k => $v) {
|
510 |
+
if (is_string($v)) {
|
511 |
+
$v = '/' . $v;
|
512 |
+
} elseif (is_int($v)) {
|
513 |
+
$v = (string) $v;
|
514 |
+
} elseif (is_bool($v)) {
|
515 |
+
$v = ($v ? 'true' : 'false');
|
516 |
+
} elseif (is_array($v)) {
|
517 |
+
$v = '[' . implode(' ', $v) . ']';
|
518 |
+
}
|
519 |
+
$res .= "\n/$k $v";
|
520 |
+
}
|
521 |
+
$res .= "\n>>\n";
|
522 |
+
|
523 |
+
return $res;
|
524 |
+
}
|
525 |
+
|
526 |
+
return null;
|
527 |
+
}
|
528 |
+
|
529 |
+
/**
|
530 |
+
* define the document catalog, the overall controller for the document
|
531 |
+
*
|
532 |
+
* @param $id
|
533 |
+
* @param $action
|
534 |
+
* @param string|array $options
|
535 |
+
* @return string|null
|
536 |
+
*/
|
537 |
+
protected function o_catalog($id, $action, $options = '')
|
538 |
+
{
|
539 |
+
if ($action !== 'new') {
|
540 |
+
$o = &$this->objects[$id];
|
541 |
+
}
|
542 |
+
|
543 |
+
switch ($action) {
|
544 |
+
case 'new':
|
545 |
+
$this->objects[$id] = array('t' => 'catalog', 'info' => array());
|
546 |
+
$this->catalogId = $id;
|
547 |
+
break;
|
548 |
+
|
549 |
+
case 'outlines':
|
550 |
+
case 'pages':
|
551 |
+
case 'openHere':
|
552 |
+
case 'javascript':
|
553 |
+
$o['info'][$action] = $options;
|
554 |
+
break;
|
555 |
+
|
556 |
+
case 'viewerPreferences':
|
557 |
+
if (!isset($o['info']['viewerPreferences'])) {
|
558 |
+
$this->numObj++;
|
559 |
+
$this->o_viewerPreferences($this->numObj, 'new');
|
560 |
+
$o['info']['viewerPreferences'] = $this->numObj;
|
561 |
+
}
|
562 |
+
|
563 |
+
$vp = $o['info']['viewerPreferences'];
|
564 |
+
$this->o_viewerPreferences($vp, 'add', $options);
|
565 |
+
|
566 |
+
break;
|
567 |
+
|
568 |
+
case 'out':
|
569 |
+
$res = "\n$id 0 obj\n<< /Type /Catalog";
|
570 |
+
|
571 |
+
foreach ($o['info'] as $k => $v) {
|
572 |
+
switch ($k) {
|
573 |
+
case 'outlines':
|
574 |
+
$res .= "\n/Outlines $v 0 R";
|
575 |
+
break;
|
576 |
+
|
577 |
+
case 'pages':
|
578 |
+
$res .= "\n/Pages $v 0 R";
|
579 |
+
break;
|
580 |
+
|
581 |
+
case 'viewerPreferences':
|
582 |
+
$res .= "\n/ViewerPreferences $v 0 R";
|
583 |
+
break;
|
584 |
+
|
585 |
+
case 'openHere':
|
586 |
+
$res .= "\n/OpenAction $v 0 R";
|
587 |
+
break;
|
588 |
+
|
589 |
+
case 'javascript':
|
590 |
+
$res .= "\n/Names <</JavaScript $v 0 R>>";
|
591 |
+
break;
|
592 |
+
}
|
593 |
+
}
|
594 |
+
|
595 |
+
$res .= " >>\nendobj";
|
596 |
+
|
597 |
+
return $res;
|
598 |
+
}
|
599 |
+
|
600 |
+
return null;
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* object which is a parent to the pages in the document
|
605 |
+
*
|
606 |
+
* @param $id
|
607 |
+
* @param $action
|
608 |
+
* @param string $options
|
609 |
+
* @return string|null
|
610 |
+
*/
|
611 |
+
protected function o_pages($id, $action, $options = '')
|
612 |
+
{
|
613 |
+
if ($action !== 'new') {
|
614 |
+
$o = &$this->objects[$id];
|
615 |
+
}
|
616 |
+
|
617 |
+
switch ($action) {
|
618 |
+
case 'new':
|
619 |
+
$this->objects[$id] = array('t' => 'pages', 'info' => array());
|
620 |
+
$this->o_catalog($this->catalogId, 'pages', $id);
|
621 |
+
break;
|
622 |
+
|
623 |
+
case 'page':
|
624 |
+
if (!is_array($options)) {
|
625 |
+
// then it will just be the id of the new page
|
626 |
+
$o['info']['pages'][] = $options;
|
627 |
+
} else {
|
628 |
+
// then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
|
629 |
+
// and pos is either 'before' or 'after', saying where this page will fit.
|
630 |
+
if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
|
631 |
+
$i = array_search($options['rid'], $o['info']['pages']);
|
632 |
+
if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) {
|
633 |
+
|
634 |
+
// then there is a match
|
635 |
+
// make a space
|
636 |
+
switch ($options['pos']) {
|
637 |
+
case 'before':
|
638 |
+
$k = $i;
|
639 |
+
break;
|
640 |
+
|
641 |
+
case 'after':
|
642 |
+
$k = $i + 1;
|
643 |
+
break;
|
644 |
+
|
645 |
+
default:
|
646 |
+
$k = -1;
|
647 |
+
break;
|
648 |
+
}
|
649 |
+
|
650 |
+
if ($k >= 0) {
|
651 |
+
for ($j = count($o['info']['pages']) - 1; $j >= $k; $j--) {
|
652 |
+
$o['info']['pages'][$j + 1] = $o['info']['pages'][$j];
|
653 |
+
}
|
654 |
+
|
655 |
+
$o['info']['pages'][$k] = $options['id'];
|
656 |
+
}
|
657 |
+
}
|
658 |
+
}
|
659 |
+
}
|
660 |
+
break;
|
661 |
+
|
662 |
+
case 'procset':
|
663 |
+
$o['info']['procset'] = $options;
|
664 |
+
break;
|
665 |
+
|
666 |
+
case 'mediaBox':
|
667 |
+
$o['info']['mediaBox'] = $options;
|
668 |
+
// which should be an array of 4 numbers
|
669 |
+
$this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
|
670 |
+
break;
|
671 |
+
|
672 |
+
case 'font':
|
673 |
+
$o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']);
|
674 |
+
break;
|
675 |
+
|
676 |
+
case 'extGState':
|
677 |
+
$o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
|
678 |
+
break;
|
679 |
+
|
680 |
+
case 'xObject':
|
681 |
+
$o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']);
|
682 |
+
break;
|
683 |
+
|
684 |
+
case 'out':
|
685 |
+
if (count($o['info']['pages'])) {
|
686 |
+
$res = "\n$id 0 obj\n<< /Type /Pages\n/Kids [";
|
687 |
+
foreach ($o['info']['pages'] as $v) {
|
688 |
+
$res .= "$v 0 R\n";
|
689 |
+
}
|
690 |
+
|
691 |
+
$res .= "]\n/Count " . count($this->objects[$id]['info']['pages']);
|
692 |
+
|
693 |
+
if ((isset($o['info']['fonts']) && count($o['info']['fonts'])) ||
|
694 |
+
isset($o['info']['procset']) ||
|
695 |
+
(isset($o['info']['extGStates']) && count($o['info']['extGStates']))
|
696 |
+
) {
|
697 |
+
$res .= "\n/Resources <<";
|
698 |
+
|
699 |
+
if (isset($o['info']['procset'])) {
|
700 |
+
$res .= "\n/ProcSet " . $o['info']['procset'] . " 0 R";
|
701 |
+
}
|
702 |
+
|
703 |
+
if (isset($o['info']['fonts']) && count($o['info']['fonts'])) {
|
704 |
+
$res .= "\n/Font << ";
|
705 |
+
foreach ($o['info']['fonts'] as $finfo) {
|
706 |
+
$res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R";
|
707 |
+
}
|
708 |
+
$res .= "\n>>";
|
709 |
+
}
|
710 |
+
|
711 |
+
if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
|
712 |
+
$res .= "\n/XObject << ";
|
713 |
+
foreach ($o['info']['xObjects'] as $finfo) {
|
714 |
+
$res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
|
715 |
+
}
|
716 |
+
$res .= "\n>>";
|
717 |
+
}
|
718 |
+
|
719 |
+
if (isset($o['info']['extGStates']) && count($o['info']['extGStates'])) {
|
720 |
+
$res .= "\n/ExtGState << ";
|
721 |
+
foreach ($o['info']['extGStates'] as $gstate) {
|
722 |
+
$res .= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R";
|
723 |
+
}
|
724 |
+
$res .= "\n>>";
|
725 |
+
}
|
726 |
+
|
727 |
+
$res .= "\n>>";
|
728 |
+
if (isset($o['info']['mediaBox'])) {
|
729 |
+
$tmp = $o['info']['mediaBox'];
|
730 |
+
$res .= "\n/MediaBox [" . sprintf(
|
731 |
+
'%.3F %.3F %.3F %.3F',
|
732 |
+
$tmp[0],
|
733 |
+
$tmp[1],
|
734 |
+
$tmp[2],
|
735 |
+
$tmp[3]
|
736 |
+
) . ']';
|
737 |
+
}
|
738 |
+
}
|
739 |
+
|
740 |
+
$res .= "\n >>\nendobj";
|
741 |
+
} else {
|
742 |
+
$res = "\n$id 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
|
743 |
+
}
|
744 |
+
|
745 |
+
return $res;
|
746 |
+
}
|
747 |
+
|
748 |
+
return null;
|
749 |
+
}
|
750 |
+
|
751 |
+
/**
|
752 |
+
* define the outlines in the doc, empty for now
|
753 |
+
*
|
754 |
+
* @param $id
|
755 |
+
* @param $action
|
756 |
+
* @param string $options
|
757 |
+
* @return string|null
|
758 |
+
*/
|
759 |
+
protected function o_outlines($id, $action, $options = '')
|
760 |
+
{
|
761 |
+
if ($action !== 'new') {
|
762 |
+
$o = &$this->objects[$id];
|
763 |
+
}
|
764 |
+
|
765 |
+
switch ($action) {
|
766 |
+
case 'new':
|
767 |
+
$this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array()));
|
768 |
+
$this->o_catalog($this->catalogId, 'outlines', $id);
|
769 |
+
break;
|
770 |
+
|
771 |
+
case 'outline':
|
772 |
+
$o['info']['outlines'][] = $options;
|
773 |
+
break;
|
774 |
+
|
775 |
+
case 'out':
|
776 |
+
if (count($o['info']['outlines'])) {
|
777 |
+
$res = "\n$id 0 obj\n<< /Type /Outlines /Kids [";
|
778 |
+
foreach ($o['info']['outlines'] as $v) {
|
779 |
+
$res .= "$v 0 R ";
|
780 |
+
}
|
781 |
+
|
782 |
+
$res .= "] /Count " . count($o['info']['outlines']) . " >>\nendobj";
|
783 |
+
} else {
|
784 |
+
$res = "\n$id 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
|
785 |
+
}
|
786 |
+
|
787 |
+
return $res;
|
788 |
+
}
|
789 |
+
|
790 |
+
return null;
|
791 |
+
}
|
792 |
+
|
793 |
+
/**
|
794 |
+
* an object to hold the font description
|
795 |
+
*
|
796 |
+
* @param $id
|
797 |
+
* @param $action
|
798 |
+
* @param string|array $options
|
799 |
+
* @return string|null
|
800 |
+
*/
|
801 |
+
protected function o_font($id, $action, $options = '')
|
802 |
+
{
|
803 |
+
if ($action !== 'new') {
|
804 |
+
$o = &$this->objects[$id];
|
805 |
+
}
|
806 |
+
|
807 |
+
switch ($action) {
|
808 |
+
case 'new':
|
809 |
+
$this->objects[$id] = array(
|
810 |
+
't' => 'font',
|
811 |
+
'info' => array(
|
812 |
+
'name' => $options['name'],
|
813 |
+
'fontFileName' => $options['fontFileName'],
|
814 |
+
'SubType' => 'Type1'
|
815 |
+
)
|
816 |
+
);
|
817 |
+
$fontNum = $this->numFonts;
|
818 |
+
$this->objects[$id]['info']['fontNum'] = $fontNum;
|
819 |
+
|
820 |
+
// deal with the encoding and the differences
|
821 |
+
if (isset($options['differences'])) {
|
822 |
+
// then we'll need an encoding dictionary
|
823 |
+
$this->numObj++;
|
824 |
+
$this->o_fontEncoding($this->numObj, 'new', $options);
|
825 |
+
$this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
|
826 |
+
} else {
|
827 |
+
if (isset($options['encoding'])) {
|
828 |
+
// we can specify encoding here
|
829 |
+
switch ($options['encoding']) {
|
830 |
+
case 'WinAnsiEncoding':
|
831 |
+
case 'MacRomanEncoding':
|
832 |
+
case 'MacExpertEncoding':
|
833 |
+
$this->objects[$id]['info']['encoding'] = $options['encoding'];
|
834 |
+
break;
|
835 |
+
|
836 |
+
case 'none':
|
837 |
+
break;
|
838 |
+
|
839 |
+
default:
|
840 |
+
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
841 |
+
break;
|
842 |
+
}
|
843 |
+
} else {
|
844 |
+
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
845 |
+
}
|
846 |
+
}
|
847 |
+
|
848 |
+
if ($this->fonts[$options['fontFileName']]['isUnicode']) {
|
849 |
+
// For Unicode fonts, we need to incorporate font data into
|
850 |
+
// sub-sections that are linked from the primary font section.
|
851 |
+
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
852 |
+
// for more information.
|
853 |
+
//
|
854 |
+
// All of this code is adapted from the excellent changes made to
|
855 |
+
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
856 |
+
|
857 |
+
$toUnicodeId = ++$this->numObj;
|
858 |
+
$this->o_toUnicode($toUnicodeId, 'new');
|
859 |
+
$this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
|
860 |
+
|
861 |
+
$cidFontId = ++$this->numObj;
|
862 |
+
$this->o_fontDescendentCID($cidFontId, 'new', $options);
|
863 |
+
$this->objects[$id]['info']['cidFont'] = $cidFontId;
|
864 |
+
}
|
865 |
+
|
866 |
+
// also tell the pages node about the new font
|
867 |
+
$this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
|
868 |
+
break;
|
869 |
+
|
870 |
+
case 'add':
|
871 |
+
foreach ($options as $k => $v) {
|
872 |
+
switch ($k) {
|
873 |
+
case 'BaseFont':
|
874 |
+
$o['info']['name'] = $v;
|
875 |
+
break;
|
876 |
+
case 'FirstChar':
|
877 |
+
case 'LastChar':
|
878 |
+
case 'Widths':
|
879 |
+
case 'FontDescriptor':
|
880 |
+
case 'SubType':
|
881 |
+
$this->addMessage('o_font ' . $k . " : " . $v);
|
882 |
+
$o['info'][$k] = $v;
|
883 |
+
break;
|
884 |
+
}
|
885 |
+
}
|
886 |
+
|
887 |
+
// pass values down to descendent font
|
888 |
+
if (isset($o['info']['cidFont'])) {
|
889 |
+
$this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
|
890 |
+
}
|
891 |
+
break;
|
892 |
+
|
893 |
+
case 'out':
|
894 |
+
if ($this->fonts[$this->objects[$id]['info']['fontFileName']]['isUnicode']) {
|
895 |
+
// For Unicode fonts, we need to incorporate font data into
|
896 |
+
// sub-sections that are linked from the primary font section.
|
897 |
+
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
898 |
+
// for more information.
|
899 |
+
//
|
900 |
+
// All of this code is adapted from the excellent changes made to
|
901 |
+
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
902 |
+
|
903 |
+
$res = "\n$id 0 obj\n<</Type /Font\n/Subtype /Type0\n";
|
904 |
+
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
905 |
+
|
906 |
+
// The horizontal identity mapping for 2-byte CIDs; may be used
|
907 |
+
// with CIDFonts using any Registry, Ordering, and Supplement values.
|
908 |
+
$res .= "/Encoding /Identity-H\n";
|
909 |
+
$res .= "/DescendantFonts [" . $o['info']['cidFont'] . " 0 R]\n";
|
910 |
+
$res .= "/ToUnicode " . $o['info']['toUnicode'] . " 0 R\n";
|
911 |
+
$res .= ">>\n";
|
912 |
+
$res .= "endobj";
|
913 |
+
} else {
|
914 |
+
$res = "\n$id 0 obj\n<< /Type /Font\n/Subtype /" . $o['info']['SubType'] . "\n";
|
915 |
+
$res .= "/Name /F" . $o['info']['fontNum'] . "\n";
|
916 |
+
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
917 |
+
|
918 |
+
if (isset($o['info']['encodingDictionary'])) {
|
919 |
+
// then place a reference to the dictionary
|
920 |
+
$res .= "/Encoding " . $o['info']['encodingDictionary'] . " 0 R\n";
|
921 |
+
} else {
|
922 |
+
if (isset($o['info']['encoding'])) {
|
923 |
+
// use the specified encoding
|
924 |
+
$res .= "/Encoding /" . $o['info']['encoding'] . "\n";
|
925 |
+
}
|
926 |
+
}
|
927 |
+
|
928 |
+
if (isset($o['info']['FirstChar'])) {
|
929 |
+
$res .= "/FirstChar " . $o['info']['FirstChar'] . "\n";
|
930 |
+
}
|
931 |
+
|
932 |
+
if (isset($o['info']['LastChar'])) {
|
933 |
+
$res .= "/LastChar " . $o['info']['LastChar'] . "\n";
|
934 |
+
}
|
935 |
+
|
936 |
+
if (isset($o['info']['Widths'])) {
|
937 |
+
$res .= "/Widths " . $o['info']['Widths'] . " 0 R\n";
|
938 |
+
}
|
939 |
+
|
940 |
+
if (isset($o['info']['FontDescriptor'])) {
|
941 |
+
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
942 |
+
}
|
943 |
+
|
944 |
+
$res .= ">>\n";
|
945 |
+
$res .= "endobj";
|
946 |
+
}
|
947 |
+
|
948 |
+
return $res;
|
949 |
+
}
|
950 |
+
|
951 |
+
return null;
|
952 |
+
}
|
953 |
+
|
954 |
+
/**
|
955 |
+
* A toUnicode section, needed for unicode fonts
|
956 |
+
*
|
957 |
+
* @param $id
|
958 |
+
* @param $action
|
959 |
+
* @return null|string
|
960 |
+
*/
|
961 |
+
protected function o_toUnicode($id, $action)
|
962 |
+
{
|
963 |
+
switch ($action) {
|
964 |
+
case 'new':
|
965 |
+
$this->objects[$id] = array(
|
966 |
+
't' => 'toUnicode'
|
967 |
+
);
|
968 |
+
break;
|
969 |
+
case 'add':
|
970 |
+
break;
|
971 |
+
case 'out':
|
972 |
+
$ordering = '(UCS)';
|
973 |
+
$registry = '(Adobe)';
|
974 |
+
|
975 |
+
if ($this->encrypted) {
|
976 |
+
$this->encryptInit($id);
|
977 |
+
$ordering = $this->ARC4($ordering);
|
978 |
+
$registry = $this->ARC4($registry);
|
979 |
+
}
|
980 |
+
|
981 |
+
$stream = <<<EOT
|
982 |
+
/CIDInit /ProcSet findresource begin
|
983 |
+
12 dict begin
|
984 |
+
begincmap
|
985 |
+
/CIDSystemInfo
|
986 |
+
<</Registry $registry
|
987 |
+
/Ordering $ordering
|
988 |
+
/Supplement 0
|
989 |
+
>> def
|
990 |
+
/CMapName /Adobe-Identity-UCS def
|
991 |
+
/CMapType 2 def
|
992 |
+
1 begincodespacerange
|
993 |
+
<0000> <FFFF>
|
994 |
+
endcodespacerange
|
995 |
+
1 beginbfrange
|
996 |
+
<0000> <FFFF> <0000>
|
997 |
+
endbfrange
|
998 |
+
endcmap
|
999 |
+
CMapName currentdict /CMap defineresource pop
|
1000 |
+
end
|
1001 |
+
end
|
1002 |
+
EOT;
|
1003 |
+
|
1004 |
+
$res = "\n$id 0 obj\n";
|
1005 |
+
$res .= "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
|
1006 |
+
$res .= "stream\n" . $stream . "\nendstream" . "\nendobj";;
|
1007 |
+
|
1008 |
+
return $res;
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
return null;
|
1012 |
+
}
|
1013 |
+
|
1014 |
+
/**
|
1015 |
+
* a font descriptor, needed for including additional fonts
|
1016 |
+
*
|
1017 |
+
* @param $id
|
1018 |
+
* @param $action
|
1019 |
+
* @param string $options
|
1020 |
+
* @return null|string
|
1021 |
+
*/
|
1022 |
+
protected function o_fontDescriptor($id, $action, $options = '')
|
1023 |
+
{
|
1024 |
+
if ($action !== 'new') {
|
1025 |
+
$o = &$this->objects[$id];
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
switch ($action) {
|
1029 |
+
case 'new':
|
1030 |
+
$this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options);
|
1031 |
+
break;
|
1032 |
+
|
1033 |
+
case 'out':
|
1034 |
+
$res = "\n$id 0 obj\n<< /Type /FontDescriptor\n";
|
1035 |
+
foreach ($o['info'] as $label => $value) {
|
1036 |
+
switch ($label) {
|
1037 |
+
case 'Ascent':
|
1038 |
+
case 'CapHeight':
|
1039 |
+
case 'Descent':
|
1040 |
+
case 'Flags':
|
1041 |
+
case 'ItalicAngle':
|
1042 |
+
case 'StemV':
|
1043 |
+
case 'AvgWidth':
|
1044 |
+
case 'Leading':
|
1045 |
+
case 'MaxWidth':
|
1046 |
+
case 'MissingWidth':
|
1047 |
+
case 'StemH':
|
1048 |
+
case 'XHeight':
|
1049 |
+
case 'CharSet':
|
1050 |
+
if (mb_strlen($value, '8bit')) {
|
1051 |
+
$res .= "/$label $value\n";
|
1052 |
+
}
|
1053 |
+
|
1054 |
+
break;
|
1055 |
+
case 'FontFile':
|
1056 |
+
case 'FontFile2':
|
1057 |
+
case 'FontFile3':
|
1058 |
+
$res .= "/$label $value 0 R\n";
|
1059 |
+
break;
|
1060 |
+
|
1061 |
+
case 'FontBBox':
|
1062 |
+
$res .= "/$label [$value[0] $value[1] $value[2] $value[3]]\n";
|
1063 |
+
break;
|
1064 |
+
|
1065 |
+
case 'FontName':
|
1066 |
+
$res .= "/$label /$value\n";
|
1067 |
+
break;
|
1068 |
+
}
|
1069 |
+
}
|
1070 |
+
|
1071 |
+
$res .= ">>\nendobj";
|
1072 |
+
|
1073 |
+
return $res;
|
1074 |
+
}
|
1075 |
+
|
1076 |
+
return null;
|
1077 |
+
}
|
1078 |
+
|
1079 |
+
/**
|
1080 |
+
* the font encoding
|
1081 |
+
*
|
1082 |
+
* @param $id
|
1083 |
+
* @param $action
|
1084 |
+
* @param string $options
|
1085 |
+
* @return null|string
|
1086 |
+
*/
|
1087 |
+
protected function o_fontEncoding($id, $action, $options = '')
|
1088 |
+
{
|
1089 |
+
if ($action !== 'new') {
|
1090 |
+
$o = &$this->objects[$id];
|
1091 |
+
}
|
1092 |
+
|
1093 |
+
switch ($action) {
|
1094 |
+
case 'new':
|
1095 |
+
// the options array should contain 'differences' and maybe 'encoding'
|
1096 |
+
$this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options);
|
1097 |
+
break;
|
1098 |
+
|
1099 |
+
case 'out':
|
1100 |
+
$res = "\n$id 0 obj\n<< /Type /Encoding\n";
|
1101 |
+
if (!isset($o['info']['encoding'])) {
|
1102 |
+
$o['info']['encoding'] = 'WinAnsiEncoding';
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
if ($o['info']['encoding'] !== 'none') {
|
1106 |
+
$res .= "/BaseEncoding /" . $o['info']['encoding'] . "\n";
|
1107 |
+
}
|
1108 |
+
|
1109 |
+
$res .= "/Differences \n[";
|
1110 |
+
|
1111 |
+
$onum = -100;
|
1112 |
+
|
1113 |
+
foreach ($o['info']['differences'] as $num => $label) {
|
1114 |
+
if ($num != $onum + 1) {
|
1115 |
+
// we cannot make use of consecutive numbering
|
1116 |
+
$res .= "\n$num /$label";
|
1117 |
+
} else {
|
1118 |
+
$res .= " /$label";
|
1119 |
+
}
|
1120 |
+
|
1121 |
+
$onum = $num;
|
1122 |
+
}
|
1123 |
+
|
1124 |
+
$res .= "\n]\n>>\nendobj";
|
1125 |
+
|
1126 |
+
return $res;
|
1127 |
+
}
|
1128 |
+
|
1129 |
+
return null;
|
1130 |
+
}
|
1131 |
+
|
1132 |
+
/**
|
1133 |
+
* a descendent cid font, needed for unicode fonts
|
1134 |
+
*
|
1135 |
+
* @param $id
|
1136 |
+
* @param $action
|
1137 |
+
* @param string|array $options
|
1138 |
+
* @return null|string
|
1139 |
+
*/
|
1140 |
+
protected function o_fontDescendentCID($id, $action, $options = '')
|
1141 |
+
{
|
1142 |
+
if ($action !== 'new') {
|
1143 |
+
$o = &$this->objects[$id];
|
1144 |
+
}
|
1145 |
+
|
1146 |
+
switch ($action) {
|
1147 |
+
case 'new':
|
1148 |
+
$this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
|
1149 |
+
|
1150 |
+
// we need a CID system info section
|
1151 |
+
$cidSystemInfoId = ++$this->numObj;
|
1152 |
+
$this->o_cidSystemInfo($cidSystemInfoId, 'new');
|
1153 |
+
$this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
|
1154 |
+
|
1155 |
+
// and a CID to GID map
|
1156 |
+
$cidToGidMapId = ++$this->numObj;
|
1157 |
+
$this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
|
1158 |
+
$this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
|
1159 |
+
break;
|
1160 |
+
|
1161 |
+
case 'add':
|
1162 |
+
foreach ($options as $k => $v) {
|
1163 |
+
switch ($k) {
|
1164 |
+
case 'BaseFont':
|
1165 |
+
$o['info']['name'] = $v;
|
1166 |
+
break;
|
1167 |
+
|
1168 |
+
case 'FirstChar':
|
1169 |
+
case 'LastChar':
|
1170 |
+
case 'MissingWidth':
|
1171 |
+
case 'FontDescriptor':
|
1172 |
+
case 'SubType':
|
1173 |
+
$this->addMessage("o_fontDescendentCID $k : $v");
|
1174 |
+
$o['info'][$k] = $v;
|
1175 |
+
break;
|
1176 |
+
}
|
1177 |
+
}
|
1178 |
+
|
1179 |
+
// pass values down to cid to gid map
|
1180 |
+
$this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
|
1181 |
+
break;
|
1182 |
+
|
1183 |
+
case 'out':
|
1184 |
+
$res = "\n$id 0 obj\n";
|
1185 |
+
$res .= "<</Type /Font\n";
|
1186 |
+
$res .= "/Subtype /CIDFontType2\n";
|
1187 |
+
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
1188 |
+
$res .= "/CIDSystemInfo " . $o['info']['cidSystemInfo'] . " 0 R\n";
|
1189 |
+
// if (isset($o['info']['FirstChar'])) {
|
1190 |
+
// $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
|
1191 |
+
// }
|
1192 |
+
|
1193 |
+
// if (isset($o['info']['LastChar'])) {
|
1194 |
+
// $res.= "/LastChar ".$o['info']['LastChar']."\n";
|
1195 |
+
// }
|
1196 |
+
if (isset($o['info']['FontDescriptor'])) {
|
1197 |
+
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
1198 |
+
}
|
1199 |
+
|
1200 |
+
if (isset($o['info']['MissingWidth'])) {
|
1201 |
+
$res .= "/DW " . $o['info']['MissingWidth'] . "\n";
|
1202 |
+
}
|
1203 |
+
|
1204 |
+
if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
|
1205 |
+
$cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
|
1206 |
+
$w = '';
|
1207 |
+
foreach ($cid_widths as $cid => $width) {
|
1208 |
+
$w .= "$cid [$width] ";
|
1209 |
+
}
|
1210 |
+
$res .= "/W [$w]\n";
|
1211 |
+
}
|
1212 |
+
|
1213 |
+
$res .= "/CIDToGIDMap " . $o['info']['cidToGidMap'] . " 0 R\n";
|
1214 |
+
$res .= ">>\n";
|
1215 |
+
$res .= "endobj";
|
1216 |
+
|
1217 |
+
return $res;
|
1218 |
+
}
|
1219 |
+
|
1220 |
+
return null;
|
1221 |
+
}
|
1222 |
+
|
1223 |
+
/**
|
1224 |
+
* CID system info section, needed for unicode fonts
|
1225 |
+
*
|
1226 |
+
* @param $id
|
1227 |
+
* @param $action
|
1228 |
+
* @return null|string
|
1229 |
+
*/
|
1230 |
+
protected function o_cidSystemInfo($id, $action)
|
1231 |
+
{
|
1232 |
+
switch ($action) {
|
1233 |
+
case 'new':
|
1234 |
+
$this->objects[$id] = array(
|
1235 |
+
't' => 'cidSystemInfo'
|
1236 |
+
);
|
1237 |
+
break;
|
1238 |
+
case 'add':
|
1239 |
+
break;
|
1240 |
+
case 'out':
|
1241 |
+
$ordering = '(UCS)';
|
1242 |
+
$registry = '(Adobe)';
|
1243 |
+
|
1244 |
+
if ($this->encrypted) {
|
1245 |
+
$this->encryptInit($id);
|
1246 |
+
$ordering = $this->ARC4($ordering);
|
1247 |
+
$registry = $this->ARC4($registry);
|
1248 |
+
}
|
1249 |
+
|
1250 |
+
|
1251 |
+
$res = "\n$id 0 obj\n";
|
1252 |
+
|
1253 |
+
$res .= '<</Registry ' . $registry . "\n"; // A string identifying an issuer of character collections
|
1254 |
+
$res .= '/Ordering ' . $ordering . "\n"; // A string that uniquely names a character collection issued by a specific registry
|
1255 |
+
$res .= "/Supplement 0\n"; // The supplement number of the character collection.
|
1256 |
+
$res .= ">>";
|
1257 |
+
|
1258 |
+
$res .= "\nendobj";;
|
1259 |
+
|
1260 |
+
return $res;
|
1261 |
+
}
|
1262 |
+
|
1263 |
+
return null;
|
1264 |
+
}
|
1265 |
+
|
1266 |
+
/**
|
1267 |
+
* a font glyph to character map, needed for unicode fonts
|
1268 |
+
*
|
1269 |
+
* @param $id
|
1270 |
+
* @param $action
|
1271 |
+
* @param string $options
|
1272 |
+
* @return null|string
|
1273 |
+
*/
|
1274 |
+
protected function o_fontGIDtoCIDMap($id, $action, $options = '')
|
1275 |
+
{
|
1276 |
+
if ($action !== 'new') {
|
1277 |
+
$o = &$this->objects[$id];
|
1278 |
+
}
|
1279 |
+
|
1280 |
+
switch ($action) {
|
1281 |
+
case 'new':
|
1282 |
+
$this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options);
|
1283 |
+
break;
|
1284 |
+
|
1285 |
+
case 'out':
|
1286 |
+
$res = "\n$id 0 obj\n";
|
1287 |
+
$fontFileName = $o['info']['fontFileName'];
|
1288 |
+
$tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']);
|
1289 |
+
|
1290 |
+
$compressed = isset($this->fonts[$fontFileName]['CIDtoGID_Compressed']) &&
|
1291 |
+
$this->fonts[$fontFileName]['CIDtoGID_Compressed'];
|
1292 |
+
|
1293 |
+
if (!$compressed && isset($o['raw'])) {
|
1294 |
+
$res .= $tmp;
|
1295 |
+
} else {
|
1296 |
+
$res .= "<<";
|
1297 |
+
|
1298 |
+
if (!$compressed && $this->compressionReady && $this->options['compression']) {
|
1299 |
+
// then implement ZLIB based compression on this content stream
|
1300 |
+
$compressed = true;
|
1301 |
+
$tmp = gzcompress($tmp, 6);
|
1302 |
+
}
|
1303 |
+
if ($compressed) {
|
1304 |
+
$res .= "\n/Filter /FlateDecode";
|
1305 |
+
}
|
1306 |
+
|
1307 |
+
if ($this->encrypted) {
|
1308 |
+
$this->encryptInit($id);
|
1309 |
+
$tmp = $this->ARC4($tmp);
|
1310 |
+
}
|
1311 |
+
|
1312 |
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream";
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
$res .= "\nendobj";
|
1316 |
+
|
1317 |
+
return $res;
|
1318 |
+
}
|
1319 |
+
|
1320 |
+
return null;
|
1321 |
+
}
|
1322 |
+
|
1323 |
+
/**
|
1324 |
+
* the document procset, solves some problems with printing to old PS printers
|
1325 |
+
*
|
1326 |
+
* @param $id
|
1327 |
+
* @param $action
|
1328 |
+
* @param string $options
|
1329 |
+
* @return null|string
|
1330 |
+
*/
|
1331 |
+
protected function o_procset($id, $action, $options = '')
|
1332 |
+
{
|
1333 |
+
if ($action !== 'new') {
|
1334 |
+
$o = &$this->objects[$id];
|
1335 |
+
}
|
1336 |
+
|
1337 |
+
switch ($action) {
|
1338 |
+
case 'new':
|
1339 |
+
$this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1));
|
1340 |
+
$this->o_pages($this->currentNode, 'procset', $id);
|
1341 |
+
$this->procsetObjectId = $id;
|
1342 |
+
break;
|
1343 |
+
|
1344 |
+
case 'add':
|
1345 |
+
// this is to add new items to the procset list, despite the fact that this is considered
|
1346 |
+
// obsolete, the items are required for printing to some postscript printers
|
1347 |
+
switch ($options) {
|
1348 |
+
case 'ImageB':
|
1349 |
+
case 'ImageC':
|
1350 |
+
case 'ImageI':
|
1351 |
+
$o['info'][$options] = 1;
|
1352 |
+
break;
|
1353 |
+
}
|
1354 |
+
break;
|
1355 |
+
|
1356 |
+
case 'out':
|
1357 |
+
$res = "\n$id 0 obj\n[";
|
1358 |
+
foreach ($o['info'] as $label => $val) {
|
1359 |
+
$res .= "/$label ";
|
1360 |
+
}
|
1361 |
+
$res .= "]\nendobj";
|
1362 |
+
|
1363 |
+
return $res;
|
1364 |
+
}
|
1365 |
+
|
1366 |
+
return null;
|
1367 |
+
}
|
1368 |
+
|
1369 |
+
/**
|
1370 |
+
* define the document information
|
1371 |
+
*
|
1372 |
+
* @param $id
|
1373 |
+
* @param $action
|
1374 |
+
* @param string $options
|
1375 |
+
* @return null|string
|
1376 |
+
*/
|
1377 |
+
protected function o_info($id, $action, $options = '')
|
1378 |
+
{
|
1379 |
+
switch ($action) {
|
1380 |
+
case 'new':
|
1381 |
+
$this->infoObject = $id;
|
1382 |
+
$date = 'D:' . @date('Ymd');
|
1383 |
+
$this->objects[$id] = array(
|
1384 |
+
't' => 'info',
|
1385 |
+
'info' => array(
|
1386 |
+
'Producer' => 'CPDF (dompdf)',
|
1387 |
+
'CreationDate' => $date
|
1388 |
+
)
|
1389 |
+
);
|
1390 |
+
break;
|
1391 |
+
case 'Title':
|
1392 |
+
case 'Author':
|
1393 |
+
case 'Subject':
|
1394 |
+
case 'Keywords':
|
1395 |
+
case 'Creator':
|
1396 |
+
case 'Producer':
|
1397 |
+
case 'CreationDate':
|
1398 |
+
case 'ModDate':
|
1399 |
+
case 'Trapped':
|
1400 |
+
$this->objects[$id]['info'][$action] = $options;
|
1401 |
+
break;
|
1402 |
+
|
1403 |
+
case 'out':
|
1404 |
+
$encrypted = $this->encrypted;
|
1405 |
+
if ($encrypted) {
|
1406 |
+
$this->encryptInit($id);
|
1407 |
+
}
|
1408 |
+
|
1409 |
+
$res = "\n$id 0 obj\n<<\n";
|
1410 |
+
$o = &$this->objects[$id];
|
1411 |
+
foreach ($o['info'] as $k => $v) {
|
1412 |
+
$res .= "/$k (";
|
1413 |
+
|
1414 |
+
// dates must be outputted as-is, without Unicode transformations
|
1415 |
+
if ($k !== 'CreationDate' && $k !== 'ModDate') {
|
1416 |
+
$v = $this->filterText($v, true, false);
|
1417 |
+
}
|
1418 |
+
|
1419 |
+
if ($encrypted) {
|
1420 |
+
$v = $this->ARC4($v);
|
1421 |
+
}
|
1422 |
+
|
1423 |
+
$res .= $v;
|
1424 |
+
$res .= ")\n";
|
1425 |
+
}
|
1426 |
+
|
1427 |
+
$res .= ">>\nendobj";
|
1428 |
+
|
1429 |
+
return $res;
|
1430 |
+
}
|
1431 |
+
|
1432 |
+
return null;
|
1433 |
+
}
|
1434 |
+
|
1435 |
+
/**
|
1436 |
+
* an action object, used to link to URLS initially
|
1437 |
+
*
|
1438 |
+
* @param $id
|
1439 |
+
* @param $action
|
1440 |
+
* @param string $options
|
1441 |
+
* @return null|string
|
1442 |
+
*/
|
1443 |
+
protected function o_action($id, $action, $options = '')
|
1444 |
+
{
|
1445 |
+
if ($action !== 'new') {
|
1446 |
+
$o = &$this->objects[$id];
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
switch ($action) {
|
1450 |
+
case 'new':
|
1451 |
+
if (is_array($options)) {
|
1452 |
+
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']);
|
1453 |
+
} else {
|
1454 |
+
// then assume a URI action
|
1455 |
+
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI');
|
1456 |
+
}
|
1457 |
+
break;
|
1458 |
+
|
1459 |
+
case 'out':
|
1460 |
+
if ($this->encrypted) {
|
1461 |
+
$this->encryptInit($id);
|
1462 |
+
}
|
1463 |
+
|
1464 |
+
$res = "\n$id 0 obj\n<< /Type /Action";
|
1465 |
+
switch ($o['type']) {
|
1466 |
+
case 'ilink':
|
1467 |
+
if (!isset($this->destinations[(string)$o['info']['label']])) {
|
1468 |
+
break;
|
1469 |
+
}
|
1470 |
+
|
1471 |
+
// there will be an 'label' setting, this is the name of the destination
|
1472 |
+
$res .= "\n/S /GoTo\n/D " . $this->destinations[(string)$o['info']['label']] . " 0 R";
|
1473 |
+
break;
|
1474 |
+
|
1475 |
+
case 'URI':
|
1476 |
+
$res .= "\n/S /URI\n/URI (";
|
1477 |
+
if ($this->encrypted) {
|
1478 |
+
$res .= $this->filterText($this->ARC4($o['info']), false, false);
|
1479 |
+
} else {
|
1480 |
+
$res .= $this->filterText($o['info'], false, false);
|
1481 |
+
}
|
1482 |
+
|
1483 |
+
$res .= ")";
|
1484 |
+
break;
|
1485 |
+
}
|
1486 |
+
|
1487 |
+
$res .= "\n>>\nendobj";
|
1488 |
+
|
1489 |
+
return $res;
|
1490 |
+
}
|
1491 |
+
|
1492 |
+
return null;
|
1493 |
+
}
|
1494 |
+
|
1495 |
+
/**
|
1496 |
+
* an annotation object, this will add an annotation to the current page.
|
1497 |
+
* initially will support just link annotations
|
1498 |
+
*
|
1499 |
+
* @param $id
|
1500 |
+
* @param $action
|
1501 |
+
* @param string $options
|
1502 |
+
* @return null|string
|
1503 |
+
*/
|
1504 |
+
protected function o_annotation($id, $action, $options = '')
|
1505 |
+
{
|
1506 |
+
if ($action !== 'new') {
|
1507 |
+
$o = &$this->objects[$id];
|
1508 |
+
}
|
1509 |
+
|
1510 |
+
switch ($action) {
|
1511 |
+
case 'new':
|
1512 |
+
// add the annotation to the current page
|
1513 |
+
$pageId = $this->currentPage;
|
1514 |
+
$this->o_page($pageId, 'annot', $id);
|
1515 |
+
|
1516 |
+
// and add the action object which is going to be required
|
1517 |
+
switch ($options['type']) {
|
1518 |
+
case 'link':
|
1519 |
+
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1520 |
+
$this->numObj++;
|
1521 |
+
$this->o_action($this->numObj, 'new', $options['url']);
|
1522 |
+
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1523 |
+
break;
|
1524 |
+
|
1525 |
+
case 'ilink':
|
1526 |
+
// this is to a named internal link
|
1527 |
+
$label = $options['label'];
|
1528 |
+
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1529 |
+
$this->numObj++;
|
1530 |
+
$this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label));
|
1531 |
+
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1532 |
+
break;
|
1533 |
+
}
|
1534 |
+
break;
|
1535 |
+
|
1536 |
+
case 'out':
|
1537 |
+
$res = "\n$id 0 obj\n<< /Type /Annot";
|
1538 |
+
switch ($o['info']['type']) {
|
1539 |
+
case 'link':
|
1540 |
+
case 'ilink':
|
1541 |
+
$res .= "\n/Subtype /Link";
|
1542 |
+
break;
|
1543 |
+
}
|
1544 |
+
$res .= "\n/A " . $o['info']['actionId'] . " 0 R";
|
1545 |
+
$res .= "\n/Border [0 0 0]";
|
1546 |
+
$res .= "\n/H /I";
|
1547 |
+
$res .= "\n/Rect [ ";
|
1548 |
+
|
1549 |
+
foreach ($o['info']['rect'] as $v) {
|
1550 |
+
$res .= sprintf("%.4F ", $v);
|
1551 |
+
}
|
1552 |
+
|
1553 |
+
$res .= "]";
|
1554 |
+
$res .= "\n>>\nendobj";
|
1555 |
+
|
1556 |
+
return $res;
|
1557 |
+
}
|
1558 |
+
|
1559 |
+
return null;
|
1560 |
+
}
|
1561 |
+
|
1562 |
+
/**
|
1563 |
+
* a page object, it also creates a contents object to hold its contents
|
1564 |
+
*
|
1565 |
+
* @param $id
|
1566 |
+
* @param $action
|
1567 |
+
* @param string $options
|
1568 |
+
* @return null|string
|
1569 |
+
*/
|
1570 |
+
protected function o_page($id, $action, $options = '')
|
1571 |
+
{
|
1572 |
+
if ($action !== 'new') {
|
1573 |
+
$o = &$this->objects[$id];
|
1574 |
+
}
|
1575 |
+
|
1576 |
+
switch ($action) {
|
1577 |
+
case 'new':
|
1578 |
+
$this->numPages++;
|
1579 |
+
$this->objects[$id] = array(
|
1580 |
+
't' => 'page',
|
1581 |
+
'info' => array(
|
1582 |
+
'parent' => $this->currentNode,
|
1583 |
+
'pageNum' => $this->numPages,
|
1584 |
+
'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox']
|
1585 |
+
)
|
1586 |
+
);
|
1587 |
+
|
1588 |
+
if (is_array($options)) {
|
1589 |
+
// then this must be a page insertion, array should contain 'rid','pos'=[before|after]
|
1590 |
+
$options['id'] = $id;
|
1591 |
+
$this->o_pages($this->currentNode, 'page', $options);
|
1592 |
+
} else {
|
1593 |
+
$this->o_pages($this->currentNode, 'page', $id);
|
1594 |
+
}
|
1595 |
+
|
1596 |
+
$this->currentPage = $id;
|
1597 |
+
//make a contents object to go with this page
|
1598 |
+
$this->numObj++;
|
1599 |
+
$this->o_contents($this->numObj, 'new', $id);
|
1600 |
+
$this->currentContents = $this->numObj;
|
1601 |
+
$this->objects[$id]['info']['contents'] = array();
|
1602 |
+
$this->objects[$id]['info']['contents'][] = $this->numObj;
|
1603 |
+
|
1604 |
+
$match = ($this->numPages % 2 ? 'odd' : 'even');
|
1605 |
+
foreach ($this->addLooseObjects as $oId => $target) {
|
1606 |
+
if ($target === 'all' || $match === $target) {
|
1607 |
+
$this->objects[$id]['info']['contents'][] = $oId;
|
1608 |
+
}
|
1609 |
+
}
|
1610 |
+
break;
|
1611 |
+
|
1612 |
+
case 'content':
|
1613 |
+
$o['info']['contents'][] = $options;
|
1614 |
+
break;
|
1615 |
+
|
1616 |
+
case 'annot':
|
1617 |
+
// add an annotation to this page
|
1618 |
+
if (!isset($o['info']['annot'])) {
|
1619 |
+
$o['info']['annot'] = array();
|
1620 |
+
}
|
1621 |
+
|
1622 |
+
// $options should contain the id of the annotation dictionary
|
1623 |
+
$o['info']['annot'][] = $options;
|
1624 |
+
break;
|
1625 |
+
|
1626 |
+
case 'out':
|
1627 |
+
$res = "\n$id 0 obj\n<< /Type /Page";
|
1628 |
+
if (isset($o['info']['mediaBox'])) {
|
1629 |
+
$tmp = $o['info']['mediaBox'];
|
1630 |
+
$res .= "\n/MediaBox [" . sprintf(
|
1631 |
+
'%.3F %.3F %.3F %.3F',
|
1632 |
+
$tmp[0],
|
1633 |
+
$tmp[1],
|
1634 |
+
$tmp[2],
|
1635 |
+
$tmp[3]
|
1636 |
+
) . ']';
|
1637 |
+
}
|
1638 |
+
$res .= "\n/Parent " . $o['info']['parent'] . " 0 R";
|
1639 |
+
|
1640 |
+
if (isset($o['info']['annot'])) {
|
1641 |
+
$res .= "\n/Annots [";
|
1642 |
+
foreach ($o['info']['annot'] as $aId) {
|
1643 |
+
$res .= " $aId 0 R";
|
1644 |
+
}
|
1645 |
+
$res .= " ]";
|
1646 |
+
}
|
1647 |
+
|
1648 |
+
$count = count($o['info']['contents']);
|
1649 |
+
if ($count == 1) {
|
1650 |
+
$res .= "\n/Contents " . $o['info']['contents'][0] . " 0 R";
|
1651 |
+
} else {
|
1652 |
+
if ($count > 1) {
|
1653 |
+
$res .= "\n/Contents [\n";
|
1654 |
+
|
1655 |
+
// reverse the page contents so added objects are below normal content
|
1656 |
+
//foreach (array_reverse($o['info']['contents']) as $cId) {
|
1657 |
+
// Back to normal now that I've got transparency working --Benj
|
1658 |
+
foreach ($o['info']['contents'] as $cId) {
|
1659 |
+
$res .= "$cId 0 R\n";
|
1660 |
+
}
|
1661 |
+
$res .= "]";
|
1662 |
+
}
|
1663 |
+
}
|
1664 |
+
|
1665 |
+
$res .= "\n>>\nendobj";
|
1666 |
+
|
1667 |
+
return $res;
|
1668 |
+
}
|
1669 |
+
|
1670 |
+
return null;
|
1671 |
+
}
|
1672 |
+
|
1673 |
+
/**
|
1674 |
+
* the contents objects hold all of the content which appears on pages
|
1675 |
+
*
|
1676 |
+
* @param $id
|
1677 |
+
* @param $action
|
1678 |
+
* @param string|array $options
|
1679 |
+
* @return null|string
|
1680 |
+
*/
|
1681 |
+
protected function o_contents($id, $action, $options = '')
|
1682 |
+
{
|
1683 |
+
if ($action !== 'new') {
|
1684 |
+
$o = &$this->objects[$id];
|
1685 |
+
}
|
1686 |
+
|
1687 |
+
switch ($action) {
|
1688 |
+
case 'new':
|
1689 |
+
$this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array());
|
1690 |
+
if (mb_strlen($options, '8bit') && intval($options)) {
|
1691 |
+
// then this contents is the primary for a page
|
1692 |
+
$this->objects[$id]['onPage'] = $options;
|
1693 |
+
} else {
|
1694 |
+
if ($options === 'raw') {
|
1695 |
+
// then this page contains some other type of system object
|
1696 |
+
$this->objects[$id]['raw'] = 1;
|
1697 |
+
}
|
1698 |
+
}
|
1699 |
+
break;
|
1700 |
+
|
1701 |
+
case 'add':
|
1702 |
+
// add more options to the declaration
|
1703 |
+
foreach ($options as $k => $v) {
|
1704 |
+
$o['info'][$k] = $v;
|
1705 |
+
}
|
1706 |
+
|
1707 |
+
case 'out':
|
1708 |
+
$tmp = $o['c'];
|
1709 |
+
$res = "\n$id 0 obj\n";
|
1710 |
+
|
1711 |
+
if (isset($this->objects[$id]['raw'])) {
|
1712 |
+
$res .= $tmp;
|
1713 |
+
} else {
|
1714 |
+
$res .= "<<";
|
1715 |
+
if ($this->compressionReady && $this->options['compression']) {
|
1716 |
+
// then implement ZLIB based compression on this content stream
|
1717 |
+
$res .= " /Filter /FlateDecode";
|
1718 |
+
$tmp = gzcompress($tmp, 6);
|
1719 |
+
}
|
1720 |
+
|
1721 |
+
if ($this->encrypted) {
|
1722 |
+
$this->encryptInit($id);
|
1723 |
+
$tmp = $this->ARC4($tmp);
|
1724 |
+
}
|
1725 |
+
|
1726 |
+
foreach ($o['info'] as $k => $v) {
|
1727 |
+
$res .= "\n/$k $v";
|
1728 |
+
}
|
1729 |
+
|
1730 |
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . " >>\nstream\n$tmp\nendstream";
|
1731 |
+
}
|
1732 |
+
|
1733 |
+
$res .= "\nendobj";
|
1734 |
+
|
1735 |
+
return $res;
|
1736 |
+
}
|
1737 |
+
|
1738 |
+
return null;
|
1739 |
+
}
|
1740 |
+
|
1741 |
+
/**
|
1742 |
+
* @param $id
|
1743 |
+
* @param $action
|
1744 |
+
* @return string|null
|
1745 |
+
*/
|
1746 |
+
protected function o_embedjs($id, $action)
|
1747 |
+
{
|
1748 |
+
switch ($action) {
|
1749 |
+
case 'new':
|
1750 |
+
$this->objects[$id] = array(
|
1751 |
+
't' => 'embedjs',
|
1752 |
+
'info' => array(
|
1753 |
+
'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]'
|
1754 |
+
)
|
1755 |
+
);
|
1756 |
+
break;
|
1757 |
+
|
1758 |
+
case 'out':
|
1759 |
+
$o = &$this->objects[$id];
|
1760 |
+
$res = "\n$id 0 obj\n<< ";
|
1761 |
+
foreach ($o['info'] as $k => $v) {
|
1762 |
+
$res .= "\n/$k $v";
|
1763 |
+
}
|
1764 |
+
$res .= "\n>>\nendobj";
|
1765 |
+
|
1766 |
+
return $res;
|
1767 |
+
}
|
1768 |
+
|
1769 |
+
return null;
|
1770 |
+
}
|
1771 |
+
|
1772 |
+
/**
|
1773 |
+
* @param $id
|
1774 |
+
* @param $action
|
1775 |
+
* @param string $code
|
1776 |
+
* @return null|string
|
1777 |
+
*/
|
1778 |
+
protected function o_javascript($id, $action, $code = '')
|
1779 |
+
{
|
1780 |
+
switch ($action) {
|
1781 |
+
case 'new':
|
1782 |
+
$this->objects[$id] = array(
|
1783 |
+
't' => 'javascript',
|
1784 |
+
'info' => array(
|
1785 |
+
'S' => '/JavaScript',
|
1786 |
+
'JS' => '(' . $this->filterText($code, true, false) . ')',
|
1787 |
+
)
|
1788 |
+
);
|
1789 |
+
break;
|
1790 |
+
|
1791 |
+
case 'out':
|
1792 |
+
$o = &$this->objects[$id];
|
1793 |
+
$res = "\n$id 0 obj\n<< ";
|
1794 |
+
|
1795 |
+
foreach ($o['info'] as $k => $v) {
|
1796 |
+
$res .= "\n/$k $v";
|
1797 |
+
}
|
1798 |
+
$res .= "\n>>\nendobj";
|
1799 |
+
|
1800 |
+
return $res;
|
1801 |
+
}
|
1802 |
+
|
1803 |
+
return null;
|
1804 |
+
}
|
1805 |
+
|
1806 |
+
/**
|
1807 |
+
* an image object, will be an XObject in the document, includes description and data
|
1808 |
+
*
|
1809 |
+
* @param $id
|
1810 |
+
* @param $action
|
1811 |
+
* @param string $options
|
1812 |
+
* @return null|string
|
1813 |
+
*/
|
1814 |
+
protected function o_image($id, $action, $options = '')
|
1815 |
+
{
|
1816 |
+
switch ($action) {
|
1817 |
+
case 'new':
|
1818 |
+
// make the new object
|
1819 |
+
$this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array());
|
1820 |
+
|
1821 |
+
$info =& $this->objects[$id]['info'];
|
1822 |
+
|
1823 |
+
$info['Type'] = '/XObject';
|
1824 |
+
$info['Subtype'] = '/Image';
|
1825 |
+
$info['Width'] = $options['iw'];
|
1826 |
+
$info['Height'] = $options['ih'];
|
1827 |
+
|
1828 |
+
if (isset($options['masked']) && $options['masked']) {
|
1829 |
+
$info['SMask'] = ($this->numObj - 1) . ' 0 R';
|
1830 |
+
}
|
1831 |
+
|
1832 |
+
if (!isset($options['type']) || $options['type'] === 'jpg') {
|
1833 |
+
if (!isset($options['channels'])) {
|
1834 |
+
$options['channels'] = 3;
|
1835 |
+
}
|
1836 |
+
|
1837 |
+
switch ($options['channels']) {
|
1838 |
+
case 1:
|
1839 |
+
$info['ColorSpace'] = '/DeviceGray';
|
1840 |
+
break;
|
1841 |
+
case 4:
|
1842 |
+
$info['ColorSpace'] = '/DeviceCMYK';
|
1843 |
+
break;
|
1844 |
+
default:
|
1845 |
+
$info['ColorSpace'] = '/DeviceRGB';
|
1846 |
+
break;
|
1847 |
+
}
|
1848 |
+
|
1849 |
+
if ($info['ColorSpace'] === '/DeviceCMYK') {
|
1850 |
+
$info['Decode'] = '[1 0 1 0 1 0 1 0]';
|
1851 |
+
}
|
1852 |
+
|
1853 |
+
$info['Filter'] = '/DCTDecode';
|
1854 |
+
$info['BitsPerComponent'] = 8;
|
1855 |
+
} else {
|
1856 |
+
if ($options['type'] === 'png') {
|
1857 |
+
$info['Filter'] = '/FlateDecode';
|
1858 |
+
$info['DecodeParms'] = '<< /Predictor 15 /Colors ' . $options['ncolor'] . ' /Columns ' . $options['iw'] . ' /BitsPerComponent ' . $options['bitsPerComponent'] . '>>';
|
1859 |
+
|
1860 |
+
if ($options['isMask']) {
|
1861 |
+
$info['ColorSpace'] = '/DeviceGray';
|
1862 |
+
} else {
|
1863 |
+
if (mb_strlen($options['pdata'], '8bit')) {
|
1864 |
+
$tmp = ' [ /Indexed /DeviceRGB ' . (mb_strlen($options['pdata'], '8bit') / 3 - 1) . ' ';
|
1865 |
+
$this->numObj++;
|
1866 |
+
$this->o_contents($this->numObj, 'new');
|
1867 |
+
$this->objects[$this->numObj]['c'] = $options['pdata'];
|
1868 |
+
$tmp .= $this->numObj . ' 0 R';
|
1869 |
+
$tmp .= ' ]';
|
1870 |
+
$info['ColorSpace'] = $tmp;
|
1871 |
+
|
1872 |
+
if (isset($options['transparency'])) {
|
1873 |
+
$transparency = $options['transparency'];
|
1874 |
+
switch ($transparency['type']) {
|
1875 |
+
case 'indexed':
|
1876 |
+
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1877 |
+
$info['Mask'] = $tmp;
|
1878 |
+
break;
|
1879 |
+
|
1880 |
+
case 'color-key':
|
1881 |
+
$tmp = ' [ ' .
|
1882 |
+
$transparency['r'] . ' ' . $transparency['r'] .
|
1883 |
+
$transparency['g'] . ' ' . $transparency['g'] .
|
1884 |
+
$transparency['b'] . ' ' . $transparency['b'] .
|
1885 |
+
' ] ';
|
1886 |
+
$info['Mask'] = $tmp;
|
1887 |
+
break;
|
1888 |
+
}
|
1889 |
+
}
|
1890 |
+
} else {
|
1891 |
+
if (isset($options['transparency'])) {
|
1892 |
+
$transparency = $options['transparency'];
|
1893 |
+
|
1894 |
+
switch ($transparency['type']) {
|
1895 |
+
case 'indexed':
|
1896 |
+
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1897 |
+
$info['Mask'] = $tmp;
|
1898 |
+
break;
|
1899 |
+
|
1900 |
+
case 'color-key':
|
1901 |
+
$tmp = ' [ ' .
|
1902 |
+
$transparency['r'] . ' ' . $transparency['r'] . ' ' .
|
1903 |
+
$transparency['g'] . ' ' . $transparency['g'] . ' ' .
|
1904 |
+
$transparency['b'] . ' ' . $transparency['b'] .
|
1905 |
+
' ] ';
|
1906 |
+
$info['Mask'] = $tmp;
|
1907 |
+
break;
|
1908 |
+
}
|
1909 |
+
}
|
1910 |
+
$info['ColorSpace'] = '/' . $options['color'];
|
1911 |
+
}
|
1912 |
+
}
|
1913 |
+
|
1914 |
+
$info['BitsPerComponent'] = $options['bitsPerComponent'];
|
1915 |
+
}
|
1916 |
+
}
|
1917 |
+
|
1918 |
+
// assign it a place in the named resource dictionary as an external object, according to
|
1919 |
+
// the label passed in with it.
|
1920 |
+
$this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id));
|
1921 |
+
|
1922 |
+
// also make sure that we have the right procset object for it.
|
1923 |
+
$this->o_procset($this->procsetObjectId, 'add', 'ImageC');
|
1924 |
+
break;
|
1925 |
+
|
1926 |
+
case 'out':
|
1927 |
+
$o = &$this->objects[$id];
|
1928 |
+
$tmp = &$o['data'];
|
1929 |
+
$res = "\n$id 0 obj\n<<";
|
1930 |
+
|
1931 |
+
foreach ($o['info'] as $k => $v) {
|
1932 |
+
$res .= "\n/$k $v";
|
1933 |
+
}
|
1934 |
+
|
1935 |
+
if ($this->encrypted) {
|
1936 |
+
$this->encryptInit($id);
|
1937 |
+
$tmp = $this->ARC4($tmp);
|
1938 |
+
}
|
1939 |
+
|
1940 |
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream\nendobj";
|
1941 |
+
|
1942 |
+
return $res;
|
1943 |
+
}
|
1944 |
+
|
1945 |
+
return null;
|
1946 |
+
}
|
1947 |
+
|
1948 |
+
/**
|
1949 |
+
* graphics state object
|
1950 |
+
*
|
1951 |
+
* @param $id
|
1952 |
+
* @param $action
|
1953 |
+
* @param string $options
|
1954 |
+
* @return null|string
|
1955 |
+
*/
|
1956 |
+
protected function o_extGState($id, $action, $options = "")
|
1957 |
+
{
|
1958 |
+
static $valid_params = array(
|
1959 |
+
"LW",
|
1960 |
+
"LC",
|
1961 |
+
"LC",
|
1962 |
+
"LJ",
|
1963 |
+
"ML",
|
1964 |
+
"D",
|
1965 |
+
"RI",
|
1966 |
+
"OP",
|
1967 |
+
"op",
|
1968 |
+
"OPM",
|
1969 |
+
"Font",
|
1970 |
+
"BG",
|
1971 |
+
"BG2",
|
1972 |
+
"UCR",
|
1973 |
+
"TR",
|
1974 |
+
"TR2",
|
1975 |
+
"HT",
|
1976 |
+
"FL",
|
1977 |
+
"SM",
|
1978 |
+
"SA",
|
1979 |
+
"BM",
|
1980 |
+
"SMask",
|
1981 |
+
"CA",
|
1982 |
+
"ca",
|
1983 |
+
"AIS",
|
1984 |
+
"TK"
|
1985 |
+
);
|
1986 |
+
|
1987 |
+
switch ($action) {
|
1988 |
+
case "new":
|
1989 |
+
$this->objects[$id] = array('t' => 'extGState', 'info' => $options);
|
1990 |
+
|
1991 |
+
// Tell the pages about the new resource
|
1992 |
+
$this->numStates++;
|
1993 |
+
$this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
|
1994 |
+
break;
|
1995 |
+
|
1996 |
+
case "out":
|
1997 |
+
$o = &$this->objects[$id];
|
1998 |
+
$res = "\n$id 0 obj\n<< /Type /ExtGState\n";
|
1999 |
+
|
2000 |
+
foreach ($o["info"] as $k => $v) {
|
2001 |
+
if (!in_array($k, $valid_params)) {
|
2002 |
+
continue;
|
2003 |
+
}
|
2004 |
+
$res .= "/$k $v\n";
|
2005 |
+
}
|
2006 |
+
|
2007 |
+
$res .= ">>\nendobj";
|
2008 |
+
|
2009 |
+
return $res;
|
2010 |
+
}
|
2011 |
+
|
2012 |
+
return null;
|
2013 |
+
}
|
2014 |
+
|
2015 |
+
/**
|
2016 |
+
* encryption object.
|
2017 |
+
*
|
2018 |
+
* @param $id
|
2019 |
+
* @param $action
|
2020 |
+
* @param string $options
|
2021 |
+
* @return string|null
|
2022 |
+
*/
|
2023 |
+
protected function o_encryption($id, $action, $options = '')
|
2024 |
+
{
|
2025 |
+
switch ($action) {
|
2026 |
+
case 'new':
|
2027 |
+
// make the new object
|
2028 |
+
$this->objects[$id] = array('t' => 'encryption', 'info' => $options);
|
2029 |
+
$this->arc4_objnum = $id;
|
2030 |
+
break;
|
2031 |
+
|
2032 |
+
case 'keys':
|
2033 |
+
// figure out the additional parameters required
|
2034 |
+
$pad = chr(0x28) . chr(0xBF) . chr(0x4E) . chr(0x5E) . chr(0x4E) . chr(0x75) . chr(0x8A) . chr(0x41)
|
2035 |
+
. chr(0x64) . chr(0x00) . chr(0x4E) . chr(0x56) . chr(0xFF) . chr(0xFA) . chr(0x01) . chr(0x08)
|
2036 |
+
. chr(0x2E) . chr(0x2E) . chr(0x00) . chr(0xB6) . chr(0xD0) . chr(0x68) . chr(0x3E) . chr(0x80)
|
2037 |
+
. chr(0x2F) . chr(0x0C) . chr(0xA9) . chr(0xFE) . chr(0x64) . chr(0x53) . chr(0x69) . chr(0x7A);
|
2038 |
+
|
2039 |
+
$info = $this->objects[$id]['info'];
|
2040 |
+
|
2041 |
+
$len = mb_strlen($info['owner'], '8bit');
|
2042 |
+
|
2043 |
+
if ($len > 32) {
|
2044 |
+
$owner = substr($info['owner'], 0, 32);
|
2045 |
+
} else {
|
2046 |
+
if ($len < 32) {
|
2047 |
+
$owner = $info['owner'] . substr($pad, 0, 32 - $len);
|
2048 |
+
} else {
|
2049 |
+
$owner = $info['owner'];
|
2050 |
+
}
|
2051 |
+
}
|
2052 |
+
|
2053 |
+
$len = mb_strlen($info['user'], '8bit');
|
2054 |
+
if ($len > 32) {
|
2055 |
+
$user = substr($info['user'], 0, 32);
|
2056 |
+
} else {
|
2057 |
+
if ($len < 32) {
|
2058 |
+
$user = $info['user'] . substr($pad, 0, 32 - $len);
|
2059 |
+
} else {
|
2060 |
+
$user = $info['user'];
|
2061 |
+
}
|
2062 |
+
}
|
2063 |
+
|
2064 |
+
$tmp = $this->md5_16($owner);
|
2065 |
+
$okey = substr($tmp, 0, 5);
|
2066 |
+
$this->ARC4_init($okey);
|
2067 |
+
$ovalue = $this->ARC4($user);
|
2068 |
+
$this->objects[$id]['info']['O'] = $ovalue;
|
2069 |
+
|
2070 |
+
// now make the u value, phew.
|
2071 |
+
$tmp = $this->md5_16(
|
2072 |
+
$user . $ovalue . chr($info['p']) . chr(255) . chr(255) . chr(255) . hex2bin($this->fileIdentifier)
|
2073 |
+
);
|
2074 |
+
|
2075 |
+
$ukey = substr($tmp, 0, 5);
|
2076 |
+
$this->ARC4_init($ukey);
|
2077 |
+
$this->encryptionKey = $ukey;
|
2078 |
+
$this->encrypted = true;
|
2079 |
+
$uvalue = $this->ARC4($pad);
|
2080 |
+
$this->objects[$id]['info']['U'] = $uvalue;
|
2081 |
+
// initialize the arc4 array
|
2082 |
+
break;
|
2083 |
+
|
2084 |
+
case 'out':
|
2085 |
+
$o = &$this->objects[$id];
|
2086 |
+
|
2087 |
+
$res = "\n$id 0 obj\n<<";
|
2088 |
+
$res .= "\n/Filter /Standard";
|
2089 |
+
$res .= "\n/V 1";
|
2090 |
+
$res .= "\n/R 2";
|
2091 |
+
$res .= "\n/O (" . $this->filterText($o['info']['O'], false, false) . ')';
|
2092 |
+
$res .= "\n/U (" . $this->filterText($o['info']['U'], false, false) . ')';
|
2093 |
+
// and the p-value needs to be converted to account for the twos-complement approach
|
2094 |
+
$o['info']['p'] = (($o['info']['p'] ^ 255) + 1) * -1;
|
2095 |
+
$res .= "\n/P " . ($o['info']['p']);
|
2096 |
+
$res .= "\n>>\nendobj";
|
2097 |
+
|
2098 |
+
return $res;
|
2099 |
+
}
|
2100 |
+
|
2101 |
+
return null;
|
2102 |
+
}
|
2103 |
+
|
2104 |
+
/**
|
2105 |
+
* ARC4 functions
|
2106 |
+
* A series of function to implement ARC4 encoding in PHP
|
2107 |
+
*/
|
2108 |
+
|
2109 |
+
/**
|
2110 |
+
* calculate the 16 byte version of the 128 bit md5 digest of the string
|
2111 |
+
*
|
2112 |
+
* @param $string
|
2113 |
+
* @return string
|
2114 |
+
*/
|
2115 |
+
function md5_16($string)
|
2116 |
+
{
|
2117 |
+
$tmp = md5($string);
|
2118 |
+
$out = '';
|
2119 |
+
for ($i = 0; $i <= 30; $i = $i + 2) {
|
2120 |
+
$out .= chr(hexdec(substr($tmp, $i, 2)));
|
2121 |
+
}
|
2122 |
+
|
2123 |
+
return $out;
|
2124 |
+
}
|
2125 |
+
|
2126 |
+
/**
|
2127 |
+
* initialize the encryption for processing a particular object
|
2128 |
+
*
|
2129 |
+
* @param $id
|
2130 |
+
*/
|
2131 |
+
function encryptInit($id)
|
2132 |
+
{
|
2133 |
+
$tmp = $this->encryptionKey;
|
2134 |
+
$hex = dechex($id);
|
2135 |
+
if (mb_strlen($hex, '8bit') < 6) {
|
2136 |
+
$hex = substr('000000', 0, 6 - mb_strlen($hex, '8bit')) . $hex;
|
2137 |
+
}
|
2138 |
+
$tmp .= chr(hexdec(substr($hex, 4, 2)))
|
2139 |
+
. chr(hexdec(substr($hex, 2, 2)))
|
2140 |
+
. chr(hexdec(substr($hex, 0, 2)))
|
2141 |
+
. chr(0)
|
2142 |
+
. chr(0)
|
2143 |
+
;
|
2144 |
+
$key = $this->md5_16($tmp);
|
2145 |
+
$this->ARC4_init(substr($key, 0, 10));
|
2146 |
+
}
|
2147 |
+
|
2148 |
+
/**
|
2149 |
+
* initialize the ARC4 encryption
|
2150 |
+
*
|
2151 |
+
* @param string $key
|
2152 |
+
*/
|
2153 |
+
function ARC4_init($key = '')
|
2154 |
+
{
|
2155 |
+
$this->arc4 = '';
|
2156 |
+
|
2157 |
+
// setup the control array
|
2158 |
+
if (mb_strlen($key, '8bit') == 0) {
|
2159 |
+
return;
|
2160 |
+
}
|
2161 |
+
|
2162 |
+
$k = '';
|
2163 |
+
while (mb_strlen($k, '8bit') < 256) {
|
2164 |
+
$k .= $key;
|
2165 |
+
}
|
2166 |
+
|
2167 |
+
$k = substr($k, 0, 256);
|
2168 |
+
for ($i = 0; $i < 256; $i++) {
|
2169 |
+
$this->arc4 .= chr($i);
|
2170 |
+
}
|
2171 |
+
|
2172 |
+
$j = 0;
|
2173 |
+
|
2174 |
+
for ($i = 0; $i < 256; $i++) {
|
2175 |
+
$t = $this->arc4[$i];
|
2176 |
+
$j = ($j + ord($t) + ord($k[$i])) % 256;
|
2177 |
+
$this->arc4[$i] = $this->arc4[$j];
|
2178 |
+
$this->arc4[$j] = $t;
|
2179 |
+
}
|
2180 |
+
}
|
2181 |
+
|
2182 |
+
/**
|
2183 |
+
* ARC4 encrypt a text string
|
2184 |
+
*
|
2185 |
+
* @param $text
|
2186 |
+
* @return string
|
2187 |
+
*/
|
2188 |
+
function ARC4($text)
|
2189 |
+
{
|
2190 |
+
$len = mb_strlen($text, '8bit');
|
2191 |
+
$a = 0;
|
2192 |
+
$b = 0;
|
2193 |
+
$c = $this->arc4;
|
2194 |
+
$out = '';
|
2195 |
+
for ($i = 0; $i < $len; $i++) {
|
2196 |
+
$a = ($a + 1) % 256;
|
2197 |
+
$t = $c[$a];
|
2198 |
+
$b = ($b + ord($t)) % 256;
|
2199 |
+
$c[$a] = $c[$b];
|
2200 |
+
$c[$b] = $t;
|
2201 |
+
$k = ord($c[(ord($c[$a]) + ord($c[$b])) % 256]);
|
2202 |
+
$out .= chr(ord($text[$i]) ^ $k);
|
2203 |
+
}
|
2204 |
+
|
2205 |
+
return $out;
|
2206 |
+
}
|
2207 |
+
|
2208 |
+
/**
|
2209 |
+
* functions which can be called to adjust or add to the document
|
2210 |
+
*/
|
2211 |
+
|
2212 |
+
/**
|
2213 |
+
* add a link in the document to an external URL
|
2214 |
+
*
|
2215 |
+
* @param $url
|
2216 |
+
* @param $x0
|
2217 |
+
* @param $y0
|
2218 |
+
* @param $x1
|
2219 |
+
* @param $y1
|
2220 |
+
*/
|
2221 |
+
function addLink($url, $x0, $y0, $x1, $y1)
|
2222 |
+
{
|
2223 |
+
$this->numObj++;
|
2224 |
+
$info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1));
|
2225 |
+
$this->o_annotation($this->numObj, 'new', $info);
|
2226 |
+
}
|
2227 |
+
|
2228 |
+
/**
|
2229 |
+
* add a link in the document to an internal destination (ie. within the document)
|
2230 |
+
*
|
2231 |
+
* @param $label
|
2232 |
+
* @param $x0
|
2233 |
+
* @param $y0
|
2234 |
+
* @param $x1
|
2235 |
+
* @param $y1
|
2236 |
+
*/
|
2237 |
+
function addInternalLink($label, $x0, $y0, $x1, $y1)
|
2238 |
+
{
|
2239 |
+
$this->numObj++;
|
2240 |
+
$info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1));
|
2241 |
+
$this->o_annotation($this->numObj, 'new', $info);
|
2242 |
+
}
|
2243 |
+
|
2244 |
+
/**
|
2245 |
+
* set the encryption of the document
|
2246 |
+
* can be used to turn it on and/or set the passwords which it will have.
|
2247 |
+
* also the functions that the user will have are set here, such as print, modify, add
|
2248 |
+
*
|
2249 |
+
* @param string $userPass
|
2250 |
+
* @param string $ownerPass
|
2251 |
+
* @param array $pc
|
2252 |
+
*/
|
2253 |
+
function setEncryption($userPass = '', $ownerPass = '', $pc = array())
|
2254 |
+
{
|
2255 |
+
$p = bindec("11000000");
|
2256 |
+
|
2257 |
+
$options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32);
|
2258 |
+
|
2259 |
+
foreach ($pc as $k => $v) {
|
2260 |
+
if ($v && isset($options[$k])) {
|
2261 |
+
$p += $options[$k];
|
2262 |
+
} else {
|
2263 |
+
if (isset($options[$v])) {
|
2264 |
+
$p += $options[$v];
|
2265 |
+
}
|
2266 |
+
}
|
2267 |
+
}
|
2268 |
+
|
2269 |
+
// implement encryption on the document
|
2270 |
+
if ($this->arc4_objnum == 0) {
|
2271 |
+
// then the block does not exist already, add it.
|
2272 |
+
$this->numObj++;
|
2273 |
+
if (mb_strlen($ownerPass) == 0) {
|
2274 |
+
$ownerPass = $userPass;
|
2275 |
+
}
|
2276 |
+
|
2277 |
+
$this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p));
|
2278 |
+
}
|
2279 |
+
}
|
2280 |
+
|
2281 |
+
/**
|
2282 |
+
* should be used for internal checks, not implemented as yet
|
2283 |
+
*/
|
2284 |
+
function checkAllHere()
|
2285 |
+
{
|
2286 |
+
}
|
2287 |
+
|
2288 |
+
/**
|
2289 |
+
* return the pdf stream as a string returned from the function
|
2290 |
+
*
|
2291 |
+
* @param bool $debug
|
2292 |
+
* @return string
|
2293 |
+
*/
|
2294 |
+
function output($debug = false)
|
2295 |
+
{
|
2296 |
+
if ($debug) {
|
2297 |
+
// turn compression off
|
2298 |
+
$this->options['compression'] = false;
|
2299 |
+
}
|
2300 |
+
|
2301 |
+
if ($this->javascript) {
|
2302 |
+
$this->numObj++;
|
2303 |
+
|
2304 |
+
$js_id = $this->numObj;
|
2305 |
+
$this->o_embedjs($js_id, 'new');
|
2306 |
+
$this->o_javascript(++$this->numObj, 'new', $this->javascript);
|
2307 |
+
|
2308 |
+
$id = $this->catalogId;
|
2309 |
+
|
2310 |
+
$this->o_catalog($id, 'javascript', $js_id);
|
2311 |
+
}
|
2312 |
+
|
2313 |
+
if ($this->fileIdentifier === '') {
|
2314 |
+
$tmp = implode('', $this->objects[$this->infoObject]['info']);
|
2315 |
+
$this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand());
|
2316 |
+
}
|
2317 |
+
|
2318 |
+
if ($this->arc4_objnum) {
|
2319 |
+
$this->o_encryption($this->arc4_objnum, 'keys');
|
2320 |
+
$this->ARC4_init($this->encryptionKey);
|
2321 |
+
}
|
2322 |
+
|
2323 |
+
$this->checkAllHere();
|
2324 |
+
|
2325 |
+
$xref = array();
|
2326 |
+
$content = '%PDF-1.3';
|
2327 |
+
$pos = mb_strlen($content, '8bit');
|
2328 |
+
|
2329 |
+
foreach ($this->objects as $k => $v) {
|
2330 |
+
$tmp = 'o_' . $v['t'];
|
2331 |
+
$cont = $this->$tmp($k, 'out');
|
2332 |
+
$content .= $cont;
|
2333 |
+
$xref[] = $pos + 1; //+1 to account for \n at the start of each object
|
2334 |
+
$pos += mb_strlen($cont, '8bit');
|
2335 |
+
}
|
2336 |
+
|
2337 |
+
$content .= "\nxref\n0 " . (count($xref) + 1) . "\n0000000000 65535 f \n";
|
2338 |
+
|
2339 |
+
foreach ($xref as $p) {
|
2340 |
+
$content .= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n";
|
2341 |
+
}
|
2342 |
+
|
2343 |
+
$content .= "trailer\n<<\n" .
|
2344 |
+
'/Size ' . (count($xref) + 1) . "\n" .
|
2345 |
+
'/Root 1 0 R' . "\n" .
|
2346 |
+
'/Info ' . $this->infoObject . " 0 R\n"
|
2347 |
+
;
|
2348 |
+
|
2349 |
+
// if encryption has been applied to this document then add the marker for this dictionary
|
2350 |
+
if ($this->arc4_objnum > 0) {
|
2351 |
+
$content .= '/Encrypt ' . $this->arc4_objnum . " 0 R\n";
|
2352 |
+
}
|
2353 |
+
|
2354 |
+
$content .= '/ID[<' . $this->fileIdentifier . '><' . $this->fileIdentifier . ">]\n";
|
2355 |
+
|
2356 |
+
// account for \n added at start of xref table
|
2357 |
+
$pos++;
|
2358 |
+
|
2359 |
+
$content .= ">>\nstartxref\n$pos\n%%EOF\n";
|
2360 |
+
|
2361 |
+
return $content;
|
2362 |
+
}
|
2363 |
+
|
2364 |
+
/**
|
2365 |
+
* initialize a new document
|
2366 |
+
* if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
|
2367 |
+
* this function is called automatically by the constructor function
|
2368 |
+
*
|
2369 |
+
* @param array $pageSize
|
2370 |
+
*/
|
2371 |
+
private function newDocument($pageSize = array(0, 0, 612, 792))
|
2372 |
+
{
|
2373 |
+
$this->numObj = 0;
|
2374 |
+
$this->objects = array();
|
2375 |
+
|
2376 |
+
$this->numObj++;
|
2377 |
+
$this->o_catalog($this->numObj, 'new');
|
2378 |
+
|
2379 |
+
$this->numObj++;
|
2380 |
+
$this->o_outlines($this->numObj, 'new');
|
2381 |
+
|
2382 |
+
$this->numObj++;
|
2383 |
+
$this->o_pages($this->numObj, 'new');
|
2384 |
+
|
2385 |
+
$this->o_pages($this->numObj, 'mediaBox', $pageSize);
|
2386 |
+
$this->currentNode = 3;
|
2387 |
+
|
2388 |
+
$this->numObj++;
|
2389 |
+
$this->o_procset($this->numObj, 'new');
|
2390 |
+
|
2391 |
+
$this->numObj++;
|
2392 |
+
$this->o_info($this->numObj, 'new');
|
2393 |
+
|
2394 |
+
$this->numObj++;
|
2395 |
+
$this->o_page($this->numObj, 'new');
|
2396 |
+
|
2397 |
+
// need to store the first page id as there is no way to get it to the user during
|
2398 |
+
// startup
|
2399 |
+
$this->firstPageId = $this->currentContents;
|
2400 |
+
}
|
2401 |
+
|
2402 |
+
/**
|
2403 |
+
* open the font file and return a php structure containing it.
|
2404 |
+
* first check if this one has been done before and saved in a form more suited to php
|
2405 |
+
* note that if a php serialized version does not exist it will try and make one, but will
|
2406 |
+
* require write access to the directory to do it... it is MUCH faster to have these serialized
|
2407 |
+
* files.
|
2408 |
+
*
|
2409 |
+
* @param $font
|
2410 |
+
*/
|
2411 |
+
private function openFont($font)
|
2412 |
+
{
|
2413 |
+
// assume that $font contains the path and file but not the extension
|
2414 |
+
$name = basename($font);
|
2415 |
+
$dir = dirname($font) . '/';
|
2416 |
+
|
2417 |
+
$fontcache = $this->fontcache;
|
2418 |
+
if ($fontcache == '') {
|
2419 |
+
$fontcache = rtrim($dir, DIRECTORY_SEPARATOR."/\\");
|
2420 |
+
}
|
2421 |
+
|
2422 |
+
//$name filename without folder and extension of font metrics
|
2423 |
+
//$dir folder of font metrics
|
2424 |
+
//$fontcache folder of runtime created php serialized version of font metrics.
|
2425 |
+
// If this is not given, the same folder as the font metrics will be used.
|
2426 |
+
// Storing and reusing serialized versions improves speed much
|
2427 |
+
|
2428 |
+
$this->addMessage("openFont: $font - $name");
|
2429 |
+
|
2430 |
+
if (!$this->isUnicode || in_array(mb_strtolower(basename($name)), self::$coreFonts)) {
|
2431 |
+
$metrics_name = "$name.afm";
|
2432 |
+
} else {
|
2433 |
+
$metrics_name = "$name.ufm";
|
2434 |
+
}
|
2435 |
+
|
2436 |
+
$cache_name = "$metrics_name.php";
|
2437 |
+
$this->addMessage("metrics: $metrics_name, cache: $cache_name");
|
2438 |
+
|
2439 |
+
if (file_exists($fontcache . '/' . $cache_name)) {
|
2440 |
+
$this->addMessage("openFont: php file exists $fontcache/$cache_name");
|
2441 |
+
$this->fonts[$font] = require($fontcache . '/' . $cache_name);
|
2442 |
+
|
2443 |
+
if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) {
|
2444 |
+
// if the font file is old, then clear it out and prepare for re-creation
|
2445 |
+
$this->addMessage('openFont: clear out, make way for new version.');
|
2446 |
+
$this->fonts[$font] = null;
|
2447 |
+
unset($this->fonts[$font]);
|
2448 |
+
}
|
2449 |
+
} else {
|
2450 |
+
$old_cache_name = "php_$metrics_name";
|
2451 |
+
if (file_exists($fontcache . '/' . $old_cache_name)) {
|
2452 |
+
$this->addMessage(
|
2453 |
+
"openFont: php file doesn't exist $fontcache/$cache_name, creating it from the old format"
|
2454 |
+
);
|
2455 |
+
$old_cache = file_get_contents($fontcache . '/' . $old_cache_name);
|
2456 |
+
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . $old_cache . ';');
|
2457 |
+
|
2458 |
+
$this->openFont($font);
|
2459 |
+
return;
|
2460 |
+
}
|
2461 |
+
}
|
2462 |
+
|
2463 |
+
if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
|
2464 |
+
// then rebuild the php_<font>.afm file from the <font>.afm file
|
2465 |
+
$this->addMessage("openFont: build php file from $dir$metrics_name");
|
2466 |
+
$data = array();
|
2467 |
+
|
2468 |
+
// 20 => 'space'
|
2469 |
+
$data['codeToName'] = array();
|
2470 |
+
|
2471 |
+
// Since we're not going to enable Unicode for the core fonts we need to use a font-based
|
2472 |
+
// setting for Unicode support rather than a global setting.
|
2473 |
+
$data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm');
|
2474 |
+
|
2475 |
+
$cidtogid = '';
|
2476 |
+
if ($data['isUnicode']) {
|
2477 |
+
$cidtogid = str_pad('', 256 * 256 * 2, "\x00");
|
2478 |
+
}
|
2479 |
+
|
2480 |
+
$file = file($dir . $metrics_name);
|
2481 |
+
|
2482 |
+
foreach ($file as $rowA) {
|
2483 |
+
$row = trim($rowA);
|
2484 |
+
$pos = strpos($row, ' ');
|
2485 |
+
|
2486 |
+
if ($pos) {
|
2487 |
+
// then there must be some keyword
|
2488 |
+
$key = substr($row, 0, $pos);
|
2489 |
+
switch ($key) {
|
2490 |
+
case 'FontName':
|
2491 |
+
case 'FullName':
|
2492 |
+
case 'FamilyName':
|
2493 |
+
case 'PostScriptName':
|
2494 |
+
case 'Weight':
|
2495 |
+
case 'ItalicAngle':
|
2496 |
+
case 'IsFixedPitch':
|
2497 |
+
case 'CharacterSet':
|
2498 |
+
case 'UnderlinePosition':
|
2499 |
+
case 'UnderlineThickness':
|
2500 |
+
case 'Version':
|
2501 |
+
case 'EncodingScheme':
|
2502 |
+
case 'CapHeight':
|
2503 |
+
case 'XHeight':
|
2504 |
+
case 'Ascender':
|
2505 |
+
case 'Descender':
|
2506 |
+
case 'StdHW':
|
2507 |
+
case 'StdVW':
|
2508 |
+
case 'StartCharMetrics':
|
2509 |
+
case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font. Otherwise it's too big.
|
2510 |
+
$data[$key] = trim(substr($row, $pos));
|
2511 |
+
break;
|
2512 |
+
|
2513 |
+
case 'FontBBox':
|
2514 |
+
$data[$key] = explode(' ', trim(substr($row, $pos)));
|
2515 |
+
break;
|
2516 |
+
|
2517 |
+
//C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
|
2518 |
+
case 'C': // Found in AFM files
|
2519 |
+
$bits = explode(';', trim($row));
|
2520 |
+
$dtmp = array();
|
2521 |
+
|
2522 |
+
foreach ($bits as $bit) {
|
2523 |
+
$bits2 = explode(' ', trim($bit));
|
2524 |
+
if (mb_strlen($bits2[0], '8bit') == 0) {
|
2525 |
+
continue;
|
2526 |
+
}
|
2527 |
+
|
2528 |
+
if (count($bits2) > 2) {
|
2529 |
+
$dtmp[$bits2[0]] = array();
|
2530 |
+
for ($i = 1; $i < count($bits2); $i++) {
|
2531 |
+
$dtmp[$bits2[0]][] = $bits2[$i];
|
2532 |
+
}
|
2533 |
+
} else {
|
2534 |
+
if (count($bits2) == 2) {
|
2535 |
+
$dtmp[$bits2[0]] = $bits2[1];
|
2536 |
+
}
|
2537 |
+
}
|
2538 |
+
}
|
2539 |
+
|
2540 |
+
$c = (int)$dtmp['C'];
|
2541 |
+
$n = $dtmp['N'];
|
2542 |
+
$width = floatval($dtmp['WX']);
|
2543 |
+
|
2544 |
+
if ($c >= 0) {
|
2545 |
+
if ($c != hexdec($n)) {
|
2546 |
+
$data['codeToName'][$c] = $n;
|
2547 |
+
}
|
2548 |
+
$data['C'][$c] = $width;
|
2549 |
+
} else {
|
2550 |
+
$data['C'][$n] = $width;
|
2551 |
+
}
|
2552 |
+
|
2553 |
+
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2554 |
+
$data['MissingWidth'] = $width;
|
2555 |
+
}
|
2556 |
+
|
2557 |
+
break;
|
2558 |
+
|
2559 |
+
// U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
|
2560 |
+
case 'U': // Found in UFM files
|
2561 |
+
if (!$data['isUnicode']) {
|
2562 |
+
break;
|
2563 |
+
}
|
2564 |
+
|
2565 |
+
$bits = explode(';', trim($row));
|
2566 |
+
$dtmp = array();
|
2567 |
+
|
2568 |
+
foreach ($bits as $bit) {
|
2569 |
+
$bits2 = explode(' ', trim($bit));
|
2570 |
+
if (mb_strlen($bits2[0], '8bit') === 0) {
|
2571 |
+
continue;
|
2572 |
+
}
|
2573 |
+
|
2574 |
+
if (count($bits2) > 2) {
|
2575 |
+
$dtmp[$bits2[0]] = array();
|
2576 |
+
for ($i = 1; $i < count($bits2); $i++) {
|
2577 |
+
$dtmp[$bits2[0]][] = $bits2[$i];
|
2578 |
+
}
|
2579 |
+
} else {
|
2580 |
+
if (count($bits2) == 2) {
|
2581 |
+
$dtmp[$bits2[0]] = $bits2[1];
|
2582 |
+
}
|
2583 |
+
}
|
2584 |
+
}
|
2585 |
+
|
2586 |
+
$c = (int)$dtmp['U'];
|
2587 |
+
$n = $dtmp['N'];
|
2588 |
+
$glyph = $dtmp['G'];
|
2589 |
+
$width = floatval($dtmp['WX']);
|
2590 |
+
|
2591 |
+
if ($c >= 0) {
|
2592 |
+
// Set values in CID to GID map
|
2593 |
+
if ($c >= 0 && $c < 0xFFFF && $glyph) {
|
2594 |
+
$cidtogid[$c * 2] = chr($glyph >> 8);
|
2595 |
+
$cidtogid[$c * 2 + 1] = chr($glyph & 0xFF);
|
2596 |
+
}
|
2597 |
+
|
2598 |
+
if ($c != hexdec($n)) {
|
2599 |
+
$data['codeToName'][$c] = $n;
|
2600 |
+
}
|
2601 |
+
$data['C'][$c] = $width;
|
2602 |
+
} else {
|
2603 |
+
$data['C'][$n] = $width;
|
2604 |
+
}
|
2605 |
+
|
2606 |
+
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2607 |
+
$data['MissingWidth'] = $width;
|
2608 |
+
}
|
2609 |
+
|
2610 |
+
break;
|
2611 |
+
|
2612 |
+
case 'KPX':
|
2613 |
+
break; // don't include them as they are not used yet
|
2614 |
+
//KPX Adieresis yacute -40
|
2615 |
+
/*$bits = explode(' ', trim($row));
|
2616 |
+
$data['KPX'][$bits[1]][$bits[2]] = $bits[3];
|
2617 |
+
break;*/
|
2618 |
+
}
|
2619 |
+
}
|
2620 |
+
}
|
2621 |
+
|
2622 |
+
if ($this->compressionReady && $this->options['compression']) {
|
2623 |
+
// then implement ZLIB based compression on CIDtoGID string
|
2624 |
+
$data['CIDtoGID_Compressed'] = true;
|
2625 |
+
$cidtogid = gzcompress($cidtogid, 6);
|
2626 |
+
}
|
2627 |
+
$data['CIDtoGID'] = base64_encode($cidtogid);
|
2628 |
+
$data['_version_'] = $this->fontcacheVersion;
|
2629 |
+
$this->fonts[$font] = $data;
|
2630 |
+
|
2631 |
+
//Because of potential trouble with php safe mode, expect that the folder already exists.
|
2632 |
+
//If not existing, this will hit performance because of missing cached results.
|
2633 |
+
if (is_dir($fontcache) && is_writable($fontcache)) {
|
2634 |
+
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . var_export($data, true) . ';');
|
2635 |
+
}
|
2636 |
+
$data = null;
|
2637 |
+
}
|
2638 |
+
|
2639 |
+
if (!isset($this->fonts[$font])) {
|
2640 |
+
$this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
|
2641 |
+
}
|
2642 |
+
|
2643 |
+
//pre_r($this->messages);
|
2644 |
+
}
|
2645 |
+
|
2646 |
+
/**
|
2647 |
+
* if the font is not loaded then load it and make the required object
|
2648 |
+
* else just make it the current font
|
2649 |
+
* the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding'
|
2650 |
+
* note that encoding='none' will need to be used for symbolic fonts
|
2651 |
+
* and 'differences' => an array of mappings between numbers 0->255 and character names.
|
2652 |
+
*
|
2653 |
+
* @param $fontName
|
2654 |
+
* @param string $encoding
|
2655 |
+
* @param bool $set
|
2656 |
+
* @return int
|
2657 |
+
*/
|
2658 |
+
function selectFont($fontName, $encoding = '', $set = true)
|
2659 |
+
{
|
2660 |
+
$ext = substr($fontName, -4);
|
2661 |
+
if ($ext === '.afm' || $ext === '.ufm') {
|
2662 |
+
$fontName = substr($fontName, 0, mb_strlen($fontName) - 4);
|
2663 |
+
}
|
2664 |
+
|
2665 |
+
if (!isset($this->fonts[$fontName])) {
|
2666 |
+
$this->addMessage("selectFont: selecting - $fontName - $encoding, $set");
|
2667 |
+
|
2668 |
+
// load the file
|
2669 |
+
$this->openFont($fontName);
|
2670 |
+
|
2671 |
+
if (isset($this->fonts[$fontName])) {
|
2672 |
+
$this->numObj++;
|
2673 |
+
$this->numFonts++;
|
2674 |
+
|
2675 |
+
$font = &$this->fonts[$fontName];
|
2676 |
+
|
2677 |
+
$name = basename($fontName);
|
2678 |
+
$dir = dirname($fontName) . '/';
|
2679 |
+
$options = array('name' => $name, 'fontFileName' => $fontName);
|
2680 |
+
|
2681 |
+
if (is_array($encoding)) {
|
2682 |
+
// then encoding and differences might be set
|
2683 |
+
if (isset($encoding['encoding'])) {
|
2684 |
+
$options['encoding'] = $encoding['encoding'];
|
2685 |
+
}
|
2686 |
+
|
2687 |
+
if (isset($encoding['differences'])) {
|
2688 |
+
$options['differences'] = $encoding['differences'];
|
2689 |
+
}
|
2690 |
+
} else {
|
2691 |
+
if (mb_strlen($encoding, '8bit')) {
|
2692 |
+
// then perhaps only the encoding has been set
|
2693 |
+
$options['encoding'] = $encoding;
|
2694 |
+
}
|
2695 |
+
}
|
2696 |
+
|
2697 |
+
$fontObj = $this->numObj;
|
2698 |
+
$this->o_font($this->numObj, 'new', $options);
|
2699 |
+
$font['fontNum'] = $this->numFonts;
|
2700 |
+
|
2701 |
+
// if this is a '.afm' font, and there is a '.pfa' file to go with it (as there
|
2702 |
+
// should be for all non-basic fonts), then load it into an object and put the
|
2703 |
+
// references into the font object
|
2704 |
+
$basefile = $fontName;
|
2705 |
+
|
2706 |
+
$fbtype = '';
|
2707 |
+
if (file_exists("$basefile.ttf")) {
|
2708 |
+
$fbtype = 'ttf';
|
2709 |
+
} elseif (file_exists("$basefile.TTF")) {
|
2710 |
+
$fbtype = 'TTF';
|
2711 |
+
} elseif (file_exists("$basefile.pfb")) {
|
2712 |
+
$fbtype = 'pfb';
|
2713 |
+
} elseif (file_exists("$basefile.PFB")) {
|
2714 |
+
$fbtype = 'PFB';
|
2715 |
+
}
|
2716 |
+
|
2717 |
+
$fbfile = "$basefile.$fbtype";
|
2718 |
+
|
2719 |
+
// $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
|
2720 |
+
// $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
|
2721 |
+
$this->addMessage('selectFont: checking for - ' . $fbfile);
|
2722 |
+
|
2723 |
+
// OAR - I don't understand this old check
|
2724 |
+
// if (substr($fontName, -4) === '.afm' && strlen($fbtype)) {
|
2725 |
+
if ($fbtype) {
|
2726 |
+
$adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
|
2727 |
+
// $fontObj = $this->numObj;
|
2728 |
+
$this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
|
2729 |
+
|
2730 |
+
// find the array of font widths, and put that into an object.
|
2731 |
+
$firstChar = -1;
|
2732 |
+
$lastChar = 0;
|
2733 |
+
$widths = array();
|
2734 |
+
$cid_widths = array();
|
2735 |
+
|
2736 |
+
foreach ($font['C'] as $num => $d) {
|
2737 |
+
if (intval($num) > 0 || $num == '0') {
|
2738 |
+
if (!$font['isUnicode']) {
|
2739 |
+
// With Unicode, widths array isn't used
|
2740 |
+
if ($lastChar > 0 && $num > $lastChar + 1) {
|
2741 |
+
for ($i = $lastChar + 1; $i < $num; $i++) {
|
2742 |
+
$widths[] = 0;
|
2743 |
+
}
|
2744 |
+
}
|
2745 |
+
}
|
2746 |
+
|
2747 |
+
$widths[] = $d;
|
2748 |
+
|
2749 |
+
if ($font['isUnicode']) {
|
2750 |
+
$cid_widths[$num] = $d;
|
2751 |
+
}
|
2752 |
+
|
2753 |
+
if ($firstChar == -1) {
|
2754 |
+
$firstChar = $num;
|
2755 |
+
}
|
2756 |
+
|
2757 |
+
$lastChar = $num;
|
2758 |
+
}
|
2759 |
+
}
|
2760 |
+
|
2761 |
+
// also need to adjust the widths for the differences array
|
2762 |
+
if (isset($options['differences'])) {
|
2763 |
+
foreach ($options['differences'] as $charNum => $charName) {
|
2764 |
+
if ($charNum > $lastChar) {
|
2765 |
+
if (!$font['isUnicode']) {
|
2766 |
+
// With Unicode, widths array isn't used
|
2767 |
+
for ($i = $lastChar + 1; $i <= $charNum; $i++) {
|
2768 |
+
$widths[] = 0;
|
2769 |
+
}
|
2770 |
+
}
|
2771 |
+
|
2772 |
+
$lastChar = $charNum;
|
2773 |
+
}
|
2774 |
+
|
2775 |
+
if (isset($font['C'][$charName])) {
|
2776 |
+
$widths[$charNum - $firstChar] = $font['C'][$charName];
|
2777 |
+
if ($font['isUnicode']) {
|
2778 |
+
$cid_widths[$charName] = $font['C'][$charName];
|
2779 |
+
}
|
2780 |
+
}
|
2781 |
+
}
|
2782 |
+
}
|
2783 |
+
|
2784 |
+
if ($font['isUnicode']) {
|
2785 |
+
$font['CIDWidths'] = $cid_widths;
|
2786 |
+
}
|
2787 |
+
|
2788 |
+
$this->addMessage('selectFont: FirstChar = ' . $firstChar);
|
2789 |
+
$this->addMessage('selectFont: LastChar = ' . $lastChar);
|
2790 |
+
|
2791 |
+
$widthid = -1;
|
2792 |
+
|
2793 |
+
if (!$font['isUnicode']) {
|
2794 |
+
// With Unicode, widths array isn't used
|
2795 |
+
|
2796 |
+
$this->numObj++;
|
2797 |
+
$this->o_contents($this->numObj, 'new', 'raw');
|
2798 |
+
$this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
|
2799 |
+
$widthid = $this->numObj;
|
2800 |
+
}
|
2801 |
+
|
2802 |
+
$missing_width = 500;
|
2803 |
+
$stemV = 70;
|
2804 |
+
|
2805 |
+
if (isset($font['MissingWidth'])) {
|
2806 |
+
$missing_width = $font['MissingWidth'];
|
2807 |
+
}
|
2808 |
+
if (isset($font['StdVW'])) {
|
2809 |
+
$stemV = $font['StdVW'];
|
2810 |
+
} else {
|
2811 |
+
if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
|
2812 |
+
$stemV = 120;
|
2813 |
+
}
|
2814 |
+
}
|
2815 |
+
|
2816 |
+
// load the pfb file, and put that into an object too.
|
2817 |
+
// note that pdf supports only binary format type 1 font files, though there is a
|
2818 |
+
// simple utility to convert them from pfa to pfb.
|
2819 |
+
// FIXME: should we move font subset creation to CPDF::output? See notes in issue #750.
|
2820 |
+
if (!$this->isUnicode || strtolower($fbtype) !== 'ttf' || empty($this->stringSubsets)) {
|
2821 |
+
$data = file_get_contents($fbfile);
|
2822 |
+
} else {
|
2823 |
+
$this->stringSubsets[$fontName][] = 32; // Force space if not in yet
|
2824 |
+
|
2825 |
+
$subset = $this->stringSubsets[$fontName];
|
2826 |
+
sort($subset);
|
2827 |
+
|
2828 |
+
// Load font
|
2829 |
+
$font_obj = Font::load($fbfile);
|
2830 |
+
$font_obj->parse();
|
2831 |
+
|
2832 |
+
// Define subset
|
2833 |
+
$font_obj->setSubset($subset);
|
2834 |
+
$font_obj->reduce();
|
2835 |
+
|
2836 |
+
// Write new font
|
2837 |
+
$tmp_name = $this->tmp . "/" . basename($fbfile) . ".tmp." . uniqid();
|
2838 |
+
$font_obj->open($tmp_name, BinaryStream::modeWrite);
|
2839 |
+
$font_obj->encode(array("OS/2"));
|
2840 |
+
$font_obj->close();
|
2841 |
+
|
2842 |
+
// Parse the new font to get cid2gid and widths
|
2843 |
+
$font_obj = Font::load($tmp_name);
|
2844 |
+
|
2845 |
+
// Find Unicode char map table
|
2846 |
+
$subtable = null;
|
2847 |
+
foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
|
2848 |
+
if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
|
2849 |
+
$subtable = $_subtable;
|
2850 |
+
break;
|
2851 |
+
}
|
2852 |
+
}
|
2853 |
+
|
2854 |
+
if ($subtable) {
|
2855 |
+
$glyphIndexArray = $subtable["glyphIndexArray"];
|
2856 |
+
$hmtx = $font_obj->getData("hmtx");
|
2857 |
+
|
2858 |
+
unset($glyphIndexArray[0xFFFF]);
|
2859 |
+
|
2860 |
+
$cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00");
|
2861 |
+
$font['CIDWidths'] = array();
|
2862 |
+
foreach ($glyphIndexArray as $cid => $gid) {
|
2863 |
+
if ($cid >= 0 && $cid < 0xFFFF && $gid) {
|
2864 |
+
$cidtogid[$cid * 2] = chr($gid >> 8);
|
2865 |
+
$cidtogid[$cid * 2 + 1] = chr($gid & 0xFF);
|
2866 |
+
}
|
2867 |
+
|
2868 |
+
$width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
|
2869 |
+
$font['CIDWidths'][$cid] = $width;
|
2870 |
+
}
|
2871 |
+
|
2872 |
+
$font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
|
2873 |
+
$font['CIDtoGID_Compressed'] = true;
|
2874 |
+
|
2875 |
+
$data = file_get_contents($tmp_name);
|
2876 |
+
} else {
|
2877 |
+
$data = file_get_contents($fbfile);
|
2878 |
+
}
|
2879 |
+
|
2880 |
+
$font_obj->close();
|
2881 |
+
unlink($tmp_name);
|
2882 |
+
}
|
2883 |
+
|
2884 |
+
// create the font descriptor
|
2885 |
+
$this->numObj++;
|
2886 |
+
$fontDescriptorId = $this->numObj;
|
2887 |
+
|
2888 |
+
$this->numObj++;
|
2889 |
+
$pfbid = $this->numObj;
|
2890 |
+
|
2891 |
+
// determine flags (more than a little flakey, hopefully will not matter much)
|
2892 |
+
$flags = 0;
|
2893 |
+
|
2894 |
+
if ($font['ItalicAngle'] != 0) {
|
2895 |
+
$flags += pow(2, 6);
|
2896 |
+
}
|
2897 |
+
|
2898 |
+
if ($font['IsFixedPitch'] === 'true') {
|
2899 |
+
$flags += 1;
|
2900 |
+
}
|
2901 |
+
|
2902 |
+
$flags += pow(2, 5); // assume non-sybolic
|
2903 |
+
$list = array(
|
2904 |
+
'Ascent' => 'Ascender',
|
2905 |
+
'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight'
|
2906 |
+
'MissingWidth' => 'MissingWidth',
|
2907 |
+
'Descent' => 'Descender',
|
2908 |
+
'FontBBox' => 'FontBBox',
|
2909 |
+
'ItalicAngle' => 'ItalicAngle'
|
2910 |
+
);
|
2911 |
+
$fdopt = array(
|
2912 |
+
'Flags' => $flags,
|
2913 |
+
'FontName' => $adobeFontName,
|
2914 |
+
'StemV' => $stemV
|
2915 |
+
);
|
2916 |
+
|
2917 |
+
foreach ($list as $k => $v) {
|
2918 |
+
if (isset($font[$v])) {
|
2919 |
+
$fdopt[$k] = $font[$v];
|
2920 |
+
}
|
2921 |
+
}
|
2922 |
+
|
2923 |
+
if (strtolower($fbtype) === 'pfb') {
|
2924 |
+
$fdopt['FontFile'] = $pfbid;
|
2925 |
+
} elseif (strtolower($fbtype) === 'ttf') {
|
2926 |
+
$fdopt['FontFile2'] = $pfbid;
|
2927 |
+
}
|
2928 |
+
|
2929 |
+
$this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
|
2930 |
+
|
2931 |
+
// embed the font program
|
2932 |
+
$this->o_contents($this->numObj, 'new');
|
2933 |
+
$this->objects[$pfbid]['c'] .= $data;
|
2934 |
+
|
2935 |
+
// determine the cruicial lengths within this file
|
2936 |
+
if (strtolower($fbtype) === 'pfb') {
|
2937 |
+
$l1 = strpos($data, 'eexec') + 6;
|
2938 |
+
$l2 = strpos($data, '00000000') - $l1;
|
2939 |
+
$l3 = mb_strlen($data, '8bit') - $l2 - $l1;
|
2940 |
+
$this->o_contents(
|
2941 |
+
$this->numObj,
|
2942 |
+
'add',
|
2943 |
+
array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)
|
2944 |
+
);
|
2945 |
+
} elseif (strtolower($fbtype) == 'ttf') {
|
2946 |
+
$l1 = mb_strlen($data, '8bit');
|
2947 |
+
$this->o_contents($this->numObj, 'add', array('Length1' => $l1));
|
2948 |
+
}
|
2949 |
+
|
2950 |
+
// tell the font object about all this new stuff
|
2951 |
+
$tmp = array(
|
2952 |
+
'BaseFont' => $adobeFontName,
|
2953 |
+
'MissingWidth' => $missing_width,
|
2954 |
+
'Widths' => $widthid,
|
2955 |
+
'FirstChar' => $firstChar,
|
2956 |
+
'LastChar' => $lastChar,
|
2957 |
+
'FontDescriptor' => $fontDescriptorId
|
2958 |
+
);
|
2959 |
+
|
2960 |
+
if (strtolower($fbtype) === 'ttf') {
|
2961 |
+
$tmp['SubType'] = 'TrueType';
|
2962 |
+
}
|
2963 |
+
|
2964 |
+
$this->addMessage("adding extra info to font.($fontObj)");
|
2965 |
+
|
2966 |
+
foreach ($tmp as $fk => $fv) {
|
2967 |
+
$this->addMessage("$fk : $fv");
|
2968 |
+
}
|
2969 |
+
|
2970 |
+
$this->o_font($fontObj, 'add', $tmp);
|
2971 |
+
} else {
|
2972 |
+
$this->addMessage(
|
2973 |
+
'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
|
2974 |
+
);
|
2975 |
+
}
|
2976 |
+
|
2977 |
+
// also set the differences here, note that this means that these will take effect only the
|
2978 |
+
//first time that a font is selected, else they are ignored
|
2979 |
+
if (isset($options['differences'])) {
|
2980 |
+
$font['differences'] = $options['differences'];
|
2981 |
+
}
|
2982 |
+
}
|
2983 |
+
}
|
2984 |
+
|
2985 |
+
if ($set && isset($this->fonts[$fontName])) {
|
2986 |
+
// so if for some reason the font was not set in the last one then it will not be selected
|
2987 |
+
$this->currentBaseFont = $fontName;
|
2988 |
+
|
2989 |
+
// the next lines mean that if a new font is selected, then the current text state will be
|
2990 |
+
// applied to it as well.
|
2991 |
+
$this->currentFont = $this->currentBaseFont;
|
2992 |
+
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
2993 |
+
|
2994 |
+
//$this->setCurrentFont();
|
2995 |
+
}
|
2996 |
+
|
2997 |
+
return $this->currentFontNum;
|
2998 |
+
//return $this->numObj;
|
2999 |
+
}
|
3000 |
+
|
3001 |
+
/**
|
3002 |
+
* sets up the current font, based on the font families, and the current text state
|
3003 |
+
* note that this system is quite flexible, a bold-italic font can be completely different to a
|
3004 |
+
* italic-bold font, and even bold-bold will have to be defined within the family to have meaning
|
3005 |
+
* This function is to be called whenever the currentTextState is changed, it will update
|
3006 |
+
* the currentFont setting to whatever the appropriate family one is.
|
3007 |
+
* If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
|
3008 |
+
* This function will change the currentFont to whatever it should be, but will not change the
|
3009 |
+
* currentBaseFont.
|
3010 |
+
*/
|
3011 |
+
private function setCurrentFont()
|
3012 |
+
{
|
3013 |
+
// if (strlen($this->currentBaseFont) == 0){
|
3014 |
+
// // then assume an initial font
|
3015 |
+
// $this->selectFont($this->defaultFont);
|
3016 |
+
// }
|
3017 |
+
// $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1);
|
3018 |
+
// if (strlen($this->currentTextState)
|
3019 |
+
// && isset($this->fontFamilies[$cf])
|
3020 |
+
// && isset($this->fontFamilies[$cf][$this->currentTextState])){
|
3021 |
+
// // then we are in some state or another
|
3022 |
+
// // and this font has a family, and the current setting exists within it
|
3023 |
+
// // select the font, then return it
|
3024 |
+
// $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState];
|
3025 |
+
// $this->selectFont($nf,'',0);
|
3026 |
+
// $this->currentFont = $nf;
|
3027 |
+
// $this->currentFontNum = $this->fonts[$nf]['fontNum'];
|
3028 |
+
// } else {
|
3029 |
+
// // the this font must not have the right family member for the current state
|
3030 |
+
// // simply assume the base font
|
3031 |
+
$this->currentFont = $this->currentBaseFont;
|
3032 |
+
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
3033 |
+
// }
|
3034 |
+
}
|
3035 |
+
|
3036 |
+
/**
|
3037 |
+
* function for the user to find out what the ID is of the first page that was created during
|
3038 |
+
* startup - useful if they wish to add something to it later.
|
3039 |
+
*
|
3040 |
+
* @return int
|
3041 |
+
*/
|
3042 |
+
function getFirstPageId()
|
3043 |
+
{
|
3044 |
+
return $this->firstPageId;
|
3045 |
+
}
|
3046 |
+
|
3047 |
+
/**
|
3048 |
+
* add content to the currently active object
|
3049 |
+
*
|
3050 |
+
* @param $content
|
3051 |
+
*/
|
3052 |
+
private function addContent($content)
|
3053 |
+
{
|
3054 |
+
$this->objects[$this->currentContents]['c'] .= $content;
|
3055 |
+
}
|
3056 |
+
|
3057 |
+
/**
|
3058 |
+
* sets the color for fill operations
|
3059 |
+
*
|
3060 |
+
* @param $color
|
3061 |
+
* @param bool $force
|
3062 |
+
*/
|
3063 |
+
function setColor($color, $force = false)
|
3064 |
+
{
|
3065 |
+
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3066 |
+
|
3067 |
+
if (!$force && $this->currentColor == $new_color) {
|
3068 |
+
return;
|
3069 |
+
}
|
3070 |
+
|
3071 |
+
if (isset($new_color[3])) {
|
3072 |
+
$this->currentColor = $new_color;
|
3073 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColor));
|
3074 |
+
} else {
|
3075 |
+
if (isset($new_color[2])) {
|
3076 |
+
$this->currentColor = $new_color;
|
3077 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColor));
|
3078 |
+
}
|
3079 |
+
}
|
3080 |
+
}
|
3081 |
+
|
3082 |
+
/**
|
3083 |
+
* sets the color for fill operations
|
3084 |
+
*
|
3085 |
+
* @param $fillRule
|
3086 |
+
*/
|
3087 |
+
function setFillRule($fillRule)
|
3088 |
+
{
|
3089 |
+
if (!in_array($fillRule, array("nonzero", "evenodd"))) {
|
3090 |
+
return;
|
3091 |
+
}
|
3092 |
+
|
3093 |
+
$this->fillRule = $fillRule;
|
3094 |
+
}
|
3095 |
+
|
3096 |
+
/**
|
3097 |
+
* sets the color for stroke operations
|
3098 |
+
*
|
3099 |
+
* @param $color
|
3100 |
+
* @param bool $force
|
3101 |
+
*/
|
3102 |
+
function setStrokeColor($color, $force = false)
|
3103 |
+
{
|
3104 |
+
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3105 |
+
|
3106 |
+
if (!$force && $this->currentStrokeColor == $new_color) {
|
3107 |
+
return;
|
3108 |
+
}
|
3109 |
+
|
3110 |
+
if (isset($new_color[3])) {
|
3111 |
+
$this->currentStrokeColor = $new_color;
|
3112 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColor));
|
3113 |
+
} else {
|
3114 |
+
if (isset($new_color[2])) {
|
3115 |
+
$this->currentStrokeColor = $new_color;
|
3116 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColor));
|
3117 |
+
}
|
3118 |
+
}
|
3119 |
+
}
|
3120 |
+
|
3121 |
+
/**
|
3122 |
+
* Set the graphics state for compositions
|
3123 |
+
*
|
3124 |
+
* @param $parameters
|
3125 |
+
*/
|
3126 |
+
function setGraphicsState($parameters)
|
3127 |
+
{
|
3128 |
+
// Create a new graphics state object if necessary
|
3129 |
+
if (($gstate = array_search($parameters, $this->gstates)) === false) {
|
3130 |
+
$this->numObj++;
|
3131 |
+
$this->o_extGState($this->numObj, 'new', $parameters);
|
3132 |
+
$gstate = $this->numStates;
|
3133 |
+
$this->gstates[$gstate] = $parameters;
|
3134 |
+
}
|
3135 |
+
$this->addContent("\n/GS$gstate gs");
|
3136 |
+
}
|
3137 |
+
|
3138 |
+
/**
|
3139 |
+
* Set current blend mode & opacity for lines.
|
3140 |
+
*
|
3141 |
+
* Valid blend modes are:
|
3142 |
+
*
|
3143 |
+
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3144 |
+
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3145 |
+
* Exclusion
|
3146 |
+
*
|
3147 |
+
* @param string $mode the blend mode to use
|
3148 |
+
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3149 |
+
*/
|
3150 |
+
function setLineTransparency($mode, $opacity)
|
3151 |
+
{
|
3152 |
+
static $blend_modes = array(
|
3153 |
+
"Normal",
|
3154 |
+
"Multiply",
|
3155 |
+
"Screen",
|
3156 |
+
"Overlay",
|
3157 |
+
"Darken",
|
3158 |
+
"Lighten",
|
3159 |
+
"ColorDogde",
|
3160 |
+
"ColorBurn",
|
3161 |
+
"HardLight",
|
3162 |
+
"SoftLight",
|
3163 |
+
"Difference",
|
3164 |
+
"Exclusion"
|
3165 |
+
);
|
3166 |
+
|
3167 |
+
if (!in_array($mode, $blend_modes)) {
|
3168 |
+
$mode = "Normal";
|
3169 |
+
}
|
3170 |
+
|
3171 |
+
// Only create a new graphics state if required
|
3172 |
+
if ($mode === $this->currentLineTransparency["mode"] &&
|
3173 |
+
$opacity == $this->currentLineTransparency["opacity"]
|
3174 |
+
) {
|
3175 |
+
return;
|
3176 |
+
}
|
3177 |
+
|
3178 |
+
$this->currentLineTransparency["mode"] = $mode;
|
3179 |
+
$this->currentLineTransparency["opacity"] = $opacity;
|
3180 |
+
|
3181 |
+
$options = array(
|
3182 |
+
"BM" => "/$mode",
|
3183 |
+
"CA" => (float)$opacity
|
3184 |
+
);
|
3185 |
+
|
3186 |
+
$this->setGraphicsState($options);
|
3187 |
+
}
|
3188 |
+
|
3189 |
+
/**
|
3190 |
+
* Set current blend mode & opacity for filled objects.
|
3191 |
+
*
|
3192 |
+
* Valid blend modes are:
|
3193 |
+
*
|
3194 |
+
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3195 |
+
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3196 |
+
* Exclusion
|
3197 |
+
*
|
3198 |
+
* @param string $mode the blend mode to use
|
3199 |
+
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3200 |
+
*/
|
3201 |
+
function setFillTransparency($mode, $opacity)
|
3202 |
+
{
|
3203 |
+
static $blend_modes = array(
|
3204 |
+
"Normal",
|
3205 |
+
"Multiply",
|
3206 |
+
"Screen",
|
3207 |
+
"Overlay",
|
3208 |
+
"Darken",
|
3209 |
+
"Lighten",
|
3210 |
+
"ColorDogde",
|
3211 |
+
"ColorBurn",
|
3212 |
+
"HardLight",
|
3213 |
+
"SoftLight",
|
3214 |
+
"Difference",
|
3215 |
+
"Exclusion"
|
3216 |
+
);
|
3217 |
+
|
3218 |
+
if (!in_array($mode, $blend_modes)) {
|
3219 |
+
$mode = "Normal";
|
3220 |
+
}
|
3221 |
+
|
3222 |
+
if ($mode === $this->currentFillTransparency["mode"] &&
|
3223 |
+
$opacity == $this->currentFillTransparency["opacity"]
|
3224 |
+
) {
|
3225 |
+
return;
|
3226 |
+
}
|
3227 |
+
|
3228 |
+
$this->currentFillTransparency["mode"] = $mode;
|
3229 |
+
$this->currentFillTransparency["opacity"] = $opacity;
|
3230 |
+
|
3231 |
+
$options = array(
|
3232 |
+
"BM" => "/$mode",
|
3233 |
+
"ca" => (float)$opacity,
|
3234 |
+
);
|
3235 |
+
|
3236 |
+
$this->setGraphicsState($options);
|
3237 |
+
}
|
3238 |
+
|
3239 |
+
/**
|
3240 |
+
* draw a line from one set of coordinates to another
|
3241 |
+
*
|
3242 |
+
* @param $x1
|
3243 |
+
* @param $y1
|
3244 |
+
* @param $x2
|
3245 |
+
* @param $y2
|
3246 |
+
* @param bool $stroke
|
3247 |
+
*/
|
3248 |
+
function line($x1, $y1, $x2, $y2, $stroke = true)
|
3249 |
+
{
|
3250 |
+
$this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2));
|
3251 |
+
|
3252 |
+
if ($stroke) {
|
3253 |
+
$this->addContent(' S');
|
3254 |
+
}
|
3255 |
+
}
|
3256 |
+
|
3257 |
+
/**
|
3258 |
+
* draw a bezier curve based on 4 control points
|
3259 |
+
*
|
3260 |
+
* @param $x0
|
3261 |
+
* @param $y0
|
3262 |
+
* @param $x1
|
3263 |
+
* @param $y1
|
3264 |
+
* @param $x2
|
3265 |
+
* @param $y2
|
3266 |
+
* @param $x3
|
3267 |
+
* @param $y3
|
3268 |
+
*/
|
3269 |
+
function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3270 |
+
{
|
3271 |
+
// in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
|
3272 |
+
// as the control points for the curve.
|
3273 |
+
$this->addContent(
|
3274 |
+
sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3275 |
+
);
|
3276 |
+
}
|
3277 |
+
|
3278 |
+
/**
|
3279 |
+
* draw a part of an ellipse
|
3280 |
+
*
|
3281 |
+
* @param $x0
|
3282 |
+
* @param $y0
|
3283 |
+
* @param $astart
|
3284 |
+
* @param $afinish
|
3285 |
+
* @param $r1
|
3286 |
+
* @param int $r2
|
3287 |
+
* @param int $angle
|
3288 |
+
* @param int $nSeg
|
3289 |
+
*/
|
3290 |
+
function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
|
3291 |
+
{
|
3292 |
+
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false);
|
3293 |
+
}
|
3294 |
+
|
3295 |
+
/**
|
3296 |
+
* draw a filled ellipse
|
3297 |
+
*
|
3298 |
+
* @param $x0
|
3299 |
+
* @param $y0
|
3300 |
+
* @param $r1
|
3301 |
+
* @param int $r2
|
3302 |
+
* @param int $angle
|
3303 |
+
* @param int $nSeg
|
3304 |
+
* @param int $astart
|
3305 |
+
* @param int $afinish
|
3306 |
+
*/
|
3307 |
+
function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
|
3308 |
+
{
|
3309 |
+
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true);
|
3310 |
+
}
|
3311 |
+
|
3312 |
+
/**
|
3313 |
+
* @param $x
|
3314 |
+
* @param $y
|
3315 |
+
*/
|
3316 |
+
function lineTo($x, $y)
|
3317 |
+
{
|
3318 |
+
$this->addContent(sprintf("\n%.3F %.3F l", $x, $y));
|
3319 |
+
}
|
3320 |
+
|
3321 |
+
/**
|
3322 |
+
* @param $x
|
3323 |
+
* @param $y
|
3324 |
+
*/
|
3325 |
+
function moveTo($x, $y)
|
3326 |
+
{
|
3327 |
+
$this->addContent(sprintf("\n%.3F %.3F m", $x, $y));
|
3328 |
+
}
|
3329 |
+
|
3330 |
+
/**
|
3331 |
+
* draw a bezier curve based on 4 control points
|
3332 |
+
*
|
3333 |
+
* @param $x1
|
3334 |
+
* @param $y1
|
3335 |
+
* @param $x2
|
3336 |
+
* @param $y2
|
3337 |
+
* @param $x3
|
3338 |
+
* @param $y3
|
3339 |
+
*/
|
3340 |
+
function curveTo($x1, $y1, $x2, $y2, $x3, $y3)
|
3341 |
+
{
|
3342 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F %.3F %.3F c", $x1, $y1, $x2, $y2, $x3, $y3));
|
3343 |
+
}
|
3344 |
+
|
3345 |
+
/**
|
3346 |
+
* draw a bezier curve based on 4 control points
|
3347 |
+
*/ function quadTo($cpx, $cpy, $x, $y)
|
3348 |
+
{
|
3349 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F v", $cpx, $cpy, $x, $y));
|
3350 |
+
}
|
3351 |
+
|
3352 |
+
function closePath()
|
3353 |
+
{
|
3354 |
+
$this->addContent(' h');
|
3355 |
+
}
|
3356 |
+
|
3357 |
+
function endPath()
|
3358 |
+
{
|
3359 |
+
$this->addContent(' n');
|
3360 |
+
}
|
3361 |
+
|
3362 |
+
/**
|
3363 |
+
* draw an ellipse
|
3364 |
+
* note that the part and filled ellipse are just special cases of this function
|
3365 |
+
*
|
3366 |
+
* draws an ellipse in the current line style
|
3367 |
+
* centered at $x0,$y0, radii $r1,$r2
|
3368 |
+
* if $r2 is not set, then a circle is drawn
|
3369 |
+
* from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse.
|
3370 |
+
* nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
|
3371 |
+
* pretty crappy shape at 2, as we are approximating with bezier curves.
|
3372 |
+
*
|
3373 |
+
* @param $x0
|
3374 |
+
* @param $y0
|
3375 |
+
* @param $r1
|
3376 |
+
* @param int $r2
|
3377 |
+
* @param int $angle
|
3378 |
+
* @param int $nSeg
|
3379 |
+
* @param int $astart
|
3380 |
+
* @param int $afinish
|
3381 |
+
* @param bool $close
|
3382 |
+
* @param bool $fill
|
3383 |
+
* @param bool $stroke
|
3384 |
+
* @param bool $incomplete
|
3385 |
+
*/
|
3386 |
+
function ellipse(
|
3387 |
+
$x0,
|
3388 |
+
$y0,
|
3389 |
+
$r1,
|
3390 |
+
$r2 = 0,
|
3391 |
+
$angle = 0,
|
3392 |
+
$nSeg = 8,
|
3393 |
+
$astart = 0,
|
3394 |
+
$afinish = 360,
|
3395 |
+
$close = true,
|
3396 |
+
$fill = false,
|
3397 |
+
$stroke = true,
|
3398 |
+
$incomplete = false
|
3399 |
+
) {
|
3400 |
+
if ($r1 == 0) {
|
3401 |
+
return;
|
3402 |
+
}
|
3403 |
+
|
3404 |
+
if ($r2 == 0) {
|
3405 |
+
$r2 = $r1;
|
3406 |
+
}
|
3407 |
+
|
3408 |
+
if ($nSeg < 2) {
|
3409 |
+
$nSeg = 2;
|
3410 |
+
}
|
3411 |
+
|
3412 |
+
$astart = deg2rad((float)$astart);
|
3413 |
+
$afinish = deg2rad((float)$afinish);
|
3414 |
+
$totalAngle = $afinish - $astart;
|
3415 |
+
|
3416 |
+
$dt = $totalAngle / $nSeg;
|
3417 |
+
$dtm = $dt / 3;
|
3418 |
+
|
3419 |
+
if ($angle != 0) {
|
3420 |
+
$a = -1 * deg2rad((float)$angle);
|
3421 |
+
|
3422 |
+
$this->addContent(
|
3423 |
+
sprintf("\n q %.3F %.3F %.3F %.3F %.3F %.3F cm", cos($a), -sin($a), sin($a), cos($a), $x0, $y0)
|
3424 |
+
);
|
3425 |
+
|
3426 |
+
$x0 = 0;
|
3427 |
+
$y0 = 0;
|
3428 |
+
}
|
3429 |
+
|
3430 |
+
$t1 = $astart;
|
3431 |
+
$a0 = $x0 + $r1 * cos($t1);
|
3432 |
+
$b0 = $y0 + $r2 * sin($t1);
|
3433 |
+
$c0 = -$r1 * sin($t1);
|
3434 |
+
$d0 = $r2 * cos($t1);
|
3435 |
+
|
3436 |
+
if (!$incomplete) {
|
3437 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $a0, $b0));
|
3438 |
+
}
|
3439 |
+
|
3440 |
+
for ($i = 1; $i <= $nSeg; $i++) {
|
3441 |
+
// draw this bit of the total curve
|
3442 |
+
$t1 = $i * $dt + $astart;
|
3443 |
+
$a1 = $x0 + $r1 * cos($t1);
|
3444 |
+
$b1 = $y0 + $r2 * sin($t1);
|
3445 |
+
$c1 = -$r1 * sin($t1);
|
3446 |
+
$d1 = $r2 * cos($t1);
|
3447 |
+
|
3448 |
+
$this->addContent(
|
3449 |
+
sprintf(
|
3450 |
+
"\n%.3F %.3F %.3F %.3F %.3F %.3F c",
|
3451 |
+
($a0 + $c0 * $dtm),
|
3452 |
+
($b0 + $d0 * $dtm),
|
3453 |
+
($a1 - $c1 * $dtm),
|
3454 |
+
($b1 - $d1 * $dtm),
|
3455 |
+
$a1,
|
3456 |
+
$b1
|
3457 |
+
)
|
3458 |
+
);
|
3459 |
+
|
3460 |
+
$a0 = $a1;
|
3461 |
+
$b0 = $b1;
|
3462 |
+
$c0 = $c1;
|
3463 |
+
$d0 = $d1;
|
3464 |
+
}
|
3465 |
+
|
3466 |
+
if (!$incomplete) {
|
3467 |
+
if ($fill) {
|
3468 |
+
$this->addContent(' f');
|
3469 |
+
}
|
3470 |
+
|
3471 |
+
if ($stroke) {
|
3472 |
+
if ($close) {
|
3473 |
+
$this->addContent(' s'); // small 's' signifies closing the path as well
|
3474 |
+
} else {
|
3475 |
+
$this->addContent(' S');
|
3476 |
+
}
|
3477 |
+
}
|
3478 |
+
}
|
3479 |
+
|
3480 |
+
if ($angle != 0) {
|
3481 |
+
$this->addContent(' Q');
|
3482 |
+
}
|
3483 |
+
}
|
3484 |
+
|
3485 |
+
/**
|
3486 |
+
* this sets the line drawing style.
|
3487 |
+
* width, is the thickness of the line in user units
|
3488 |
+
* cap is the type of cap to put on the line, values can be 'butt','round','square'
|
3489 |
+
* where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the
|
3490 |
+
* end of the line.
|
3491 |
+
* join can be 'miter', 'round', 'bevel'
|
3492 |
+
* dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the
|
3493 |
+
* on and off dashes.
|
3494 |
+
* (2) represents 2 on, 2 off, 2 on , 2 off ...
|
3495 |
+
* (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
|
3496 |
+
* phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
|
3497 |
+
*
|
3498 |
+
* @param int $width
|
3499 |
+
* @param string $cap
|
3500 |
+
* @param string $join
|
3501 |
+
* @param string $dash
|
3502 |
+
* @param int $phase
|
3503 |
+
*/
|
3504 |
+
function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
|
3505 |
+
{
|
3506 |
+
// this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
|
3507 |
+
$string = '';
|
3508 |
+
|
3509 |
+
if ($width > 0) {
|
3510 |
+
$string .= "$width w";
|
3511 |
+
}
|
3512 |
+
|
3513 |
+
$ca = array('butt' => 0, 'round' => 1, 'square' => 2);
|
3514 |
+
|
3515 |
+
if (isset($ca[$cap])) {
|
3516 |
+
$string .= " $ca[$cap] J";
|
3517 |
+
}
|
3518 |
+
|
3519 |
+
$ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
|
3520 |
+
|
3521 |
+
if (isset($ja[$join])) {
|
3522 |
+
$string .= " $ja[$join] j";
|
3523 |
+
}
|
3524 |
+
|
3525 |
+
if (is_array($dash)) {
|
3526 |
+
$string .= ' [ ' . implode(' ', $dash) . " ] $phase d";
|
3527 |
+
}
|
3528 |
+
|
3529 |
+
$this->currentLineStyle = $string;
|
3530 |
+
$this->addContent("\n$string");
|
3531 |
+
}
|
3532 |
+
|
3533 |
+
/**
|
3534 |
+
* draw a polygon, the syntax for this is similar to the GD polygon command
|
3535 |
+
*
|
3536 |
+
* @param $p
|
3537 |
+
* @param $np
|
3538 |
+
* @param bool $f
|
3539 |
+
*/
|
3540 |
+
function polygon($p, $np, $f = false)
|
3541 |
+
{
|
3542 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
|
3543 |
+
|
3544 |
+
for ($i = 2; $i < $np * 2; $i = $i + 2) {
|
3545 |
+
$this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
|
3546 |
+
}
|
3547 |
+
|
3548 |
+
if ($f) {
|
3549 |
+
$this->addContent(' f');
|
3550 |
+
} else {
|
3551 |
+
$this->addContent(' S');
|
3552 |
+
}
|
3553 |
+
}
|
3554 |
+
|
3555 |
+
/**
|
3556 |
+
* a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3557 |
+
* the coordinates of the upper-right corner
|
3558 |
+
*
|
3559 |
+
* @param $x1
|
3560 |
+
* @param $y1
|
3561 |
+
* @param $width
|
3562 |
+
* @param $height
|
3563 |
+
*/
|
3564 |
+
function filledRectangle($x1, $y1, $width, $height)
|
3565 |
+
{
|
3566 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height));
|
3567 |
+
}
|
3568 |
+
|
3569 |
+
/**
|
3570 |
+
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3571 |
+
* the coordinates of the upper-right corner
|
3572 |
+
*
|
3573 |
+
* @param $x1
|
3574 |
+
* @param $y1
|
3575 |
+
* @param $width
|
3576 |
+
* @param $height
|
3577 |
+
*/
|
3578 |
+
function rectangle($x1, $y1, $width, $height)
|
3579 |
+
{
|
3580 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height));
|
3581 |
+
}
|
3582 |
+
|
3583 |
+
/**
|
3584 |
+
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3585 |
+
* the coordinates of the upper-right corner
|
3586 |
+
*
|
3587 |
+
* @param $x1
|
3588 |
+
* @param $y1
|
3589 |
+
* @param $width
|
3590 |
+
* @param $height
|
3591 |
+
*/
|
3592 |
+
function rect($x1, $y1, $width, $height)
|
3593 |
+
{
|
3594 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height));
|
3595 |
+
}
|
3596 |
+
|
3597 |
+
function stroke()
|
3598 |
+
{
|
3599 |
+
$this->addContent("\nS");
|
3600 |
+
}
|
3601 |
+
|
3602 |
+
function fill()
|
3603 |
+
{
|
3604 |
+
$this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3605 |
+
}
|
3606 |
+
|
3607 |
+
function fillStroke()
|
3608 |
+
{
|
3609 |
+
$this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3610 |
+
}
|
3611 |
+
|
3612 |
+
/**
|
3613 |
+
* save the current graphic state
|
3614 |
+
*/
|
3615 |
+
function save()
|
3616 |
+
{
|
3617 |
+
// we must reset the color cache or it will keep bad colors after clipping
|
3618 |
+
$this->currentColor = null;
|
3619 |
+
$this->currentStrokeColor = null;
|
3620 |
+
$this->addContent("\nq");
|
3621 |
+
}
|
3622 |
+
|
3623 |
+
/**
|
3624 |
+
* restore the last graphic state
|
3625 |
+
*/
|
3626 |
+
function restore()
|
3627 |
+
{
|
3628 |
+
// we must reset the color cache or it will keep bad colors after clipping
|
3629 |
+
$this->currentColor = null;
|
3630 |
+
$this->currentStrokeColor = null;
|
3631 |
+
$this->addContent("\nQ");
|
3632 |
+
}
|
3633 |
+
|
3634 |
+
/**
|
3635 |
+
* draw a clipping rectangle, all the elements added after this will be clipped
|
3636 |
+
*
|
3637 |
+
* @param $x1
|
3638 |
+
* @param $y1
|
3639 |
+
* @param $width
|
3640 |
+
* @param $height
|
3641 |
+
*/
|
3642 |
+
function clippingRectangle($x1, $y1, $width, $height)
|
3643 |
+
{
|
3644 |
+
$this->save();
|
3645 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height));
|
3646 |
+
}
|
3647 |
+
|
3648 |
+
/**
|
3649 |
+
* draw a clipping rounded rectangle, all the elements added after this will be clipped
|
3650 |
+
*
|
3651 |
+
* @param $x1
|
3652 |
+
* @param $y1
|
3653 |
+
* @param $w
|
3654 |
+
* @param $h
|
3655 |
+
* @param $rTL
|
3656 |
+
* @param $rTR
|
3657 |
+
* @param $rBR
|
3658 |
+
* @param $rBL
|
3659 |
+
*/
|
3660 |
+
function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
3661 |
+
{
|
3662 |
+
$this->save();
|
3663 |
+
|
3664 |
+
// start: top edge, left end
|
3665 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h));
|
3666 |
+
|
3667 |
+
// line: bottom edge, left end
|
3668 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1, $y1 + $rBL));
|
3669 |
+
|
3670 |
+
// curve: bottom-left corner
|
3671 |
+
$this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true);
|
3672 |
+
|
3673 |
+
// line: right edge, bottom end
|
3674 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1));
|
3675 |
+
|
3676 |
+
// curve: bottom-right corner
|
3677 |
+
$this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true);
|
3678 |
+
|
3679 |
+
// line: right edge, top end
|
3680 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR));
|
3681 |
+
|
3682 |
+
// curve: bottom-right corner
|
3683 |
+
$this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true);
|
3684 |
+
|
3685 |
+
// line: bottom edge, right end
|
3686 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h));
|
3687 |
+
|
3688 |
+
// curve: top-right corner
|
3689 |
+
$this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true);
|
3690 |
+
|
3691 |
+
// line: top edge, left end
|
3692 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1));
|
3693 |
+
|
3694 |
+
// Close & clip
|
3695 |
+
$this->addContent(" W n");
|
3696 |
+
}
|
3697 |
+
|
3698 |
+
/**
|
3699 |
+
* ends the last clipping shape
|
3700 |
+
*/
|
3701 |
+
function clippingEnd()
|
3702 |
+
{
|
3703 |
+
$this->restore();
|
3704 |
+
}
|
3705 |
+
|
3706 |
+
/**
|
3707 |
+
* scale
|
3708 |
+
*
|
3709 |
+
* @param float $s_x scaling factor for width as percent
|
3710 |
+
* @param float $s_y scaling factor for height as percent
|
3711 |
+
* @param float $x Origin abscissa
|
3712 |
+
* @param float $y Origin ordinate
|
3713 |
+
*/
|
3714 |
+
function scale($s_x, $s_y, $x, $y)
|
3715 |
+
{
|
3716 |
+
$y = $this->currentPageSize["height"] - $y;
|
3717 |
+
|
3718 |
+
$tm = array(
|
3719 |
+
$s_x,
|
3720 |
+
0,
|
3721 |
+
0,
|
3722 |
+
$s_y,
|
3723 |
+
$x * (1 - $s_x),
|
3724 |
+
$y * (1 - $s_y)
|
3725 |
+
);
|
3726 |
+
|
3727 |
+
$this->transform($tm);
|
3728 |
+
}
|
3729 |
+
|
3730 |
+
/**
|
3731 |
+
* translate
|
3732 |
+
*
|
3733 |
+
* @param float $t_x movement to the right
|
3734 |
+
* @param float $t_y movement to the bottom
|
3735 |
+
*/
|
3736 |
+
function translate($t_x, $t_y)
|
3737 |
+
{
|
3738 |
+
$tm = array(
|
3739 |
+
1,
|
3740 |
+
0,
|
3741 |
+
0,
|
3742 |
+
1,
|
3743 |
+
$t_x,
|
3744 |
+
-$t_y
|
3745 |
+
);
|
3746 |
+
|
3747 |
+
$this->transform($tm);
|
3748 |
+
}
|
3749 |
+
|
3750 |
+
/**
|
3751 |
+
* rotate
|
3752 |
+
*
|
3753 |
+
* @param float $angle angle in degrees for counter-clockwise rotation
|
3754 |
+
* @param float $x Origin abscissa
|
3755 |
+
* @param float $y Origin ordinate
|
3756 |
+
*/
|
3757 |
+
function rotate($angle, $x, $y)
|
3758 |
+
{
|
3759 |
+
$y = $this->currentPageSize["height"] - $y;
|
3760 |
+
|
3761 |
+
$a = deg2rad($angle);
|
3762 |
+
$cos_a = cos($a);
|
3763 |
+
$sin_a = sin($a);
|
3764 |
+
|
3765 |
+
$tm = array(
|
3766 |
+
$cos_a,
|
3767 |
+
-$sin_a,
|
3768 |
+
$sin_a,
|
3769 |
+
$cos_a,
|
3770 |
+
$x - $sin_a * $y - $cos_a * $x,
|
3771 |
+
$y - $cos_a * $y + $sin_a * $x,
|
3772 |
+
);
|
3773 |
+
|
3774 |
+
$this->transform($tm);
|
3775 |
+
}
|
3776 |
+
|
3777 |
+
/**
|
3778 |
+
* skew
|
3779 |
+
*
|
3780 |
+
* @param float $angle_x
|
3781 |
+
* @param float $angle_y
|
3782 |
+
* @param float $x Origin abscissa
|
3783 |
+
* @param float $y Origin ordinate
|
3784 |
+
*/
|
3785 |
+
function skew($angle_x, $angle_y, $x, $y)
|
3786 |
+
{
|
3787 |
+
$y = $this->currentPageSize["height"] - $y;
|
3788 |
+
|
3789 |
+
$tan_x = tan(deg2rad($angle_x));
|
3790 |
+
$tan_y = tan(deg2rad($angle_y));
|
3791 |
+
|
3792 |
+
$tm = array(
|
3793 |
+
1,
|
3794 |
+
-$tan_y,
|
3795 |
+
-$tan_x,
|
3796 |
+
1,
|
3797 |
+
$tan_x * $y,
|
3798 |
+
$tan_y * $x,
|
3799 |
+
);
|
3800 |
+
|
3801 |
+
$this->transform($tm);
|
3802 |
+
}
|
3803 |
+
|
3804 |
+
/**
|
3805 |
+
* apply graphic transformations
|
3806 |
+
*
|
3807 |
+
* @param array $tm transformation matrix
|
3808 |
+
*/
|
3809 |
+
function transform($tm)
|
3810 |
+
{
|
3811 |
+
$this->addContent(vsprintf("\n %.3F %.3F %.3F %.3F %.3F %.3F cm", $tm));
|
3812 |
+
}
|
3813 |
+
|
3814 |
+
/**
|
3815 |
+
* add a new page to the document
|
3816 |
+
* this also makes the new page the current active object
|
3817 |
+
*
|
3818 |
+
* @param int $insert
|
3819 |
+
* @param int $id
|
3820 |
+
* @param string $pos
|
3821 |
+
* @return int
|
3822 |
+
*/
|
3823 |
+
function newPage($insert = 0, $id = 0, $pos = 'after')
|
3824 |
+
{
|
3825 |
+
// if there is a state saved, then go up the stack closing them
|
3826 |
+
// then on the new page, re-open them with the right setings
|
3827 |
+
|
3828 |
+
if ($this->nStateStack) {
|
3829 |
+
for ($i = $this->nStateStack; $i >= 1; $i--) {
|
3830 |
+
$this->restoreState($i);
|
3831 |
+
}
|
3832 |
+
}
|
3833 |
+
|
3834 |
+
$this->numObj++;
|
3835 |
+
|
3836 |
+
if ($insert) {
|
3837 |
+
// the id from the ezPdf class is the id of the contents of the page, not the page object itself
|
3838 |
+
// query that object to find the parent
|
3839 |
+
$rid = $this->objects[$id]['onPage'];
|
3840 |
+
$opt = array('rid' => $rid, 'pos' => $pos);
|
3841 |
+
$this->o_page($this->numObj, 'new', $opt);
|
3842 |
+
} else {
|
3843 |
+
$this->o_page($this->numObj, 'new');
|
3844 |
+
}
|
3845 |
+
|
3846 |
+
// if there is a stack saved, then put that onto the page
|
3847 |
+
if ($this->nStateStack) {
|
3848 |
+
for ($i = 1; $i <= $this->nStateStack; $i++) {
|
3849 |
+
$this->saveState($i);
|
3850 |
+
}
|
3851 |
+
}
|
3852 |
+
|
3853 |
+
// and if there has been a stroke or fill color set, then transfer them
|
3854 |
+
if (isset($this->currentColor)) {
|
3855 |
+
$this->setColor($this->currentColor, true);
|
3856 |
+
}
|
3857 |
+
|
3858 |
+
if (isset($this->currentStrokeColor)) {
|
3859 |
+
$this->setStrokeColor($this->currentStrokeColor, true);
|
3860 |
+
}
|
3861 |
+
|
3862 |
+
// if there is a line style set, then put this in too
|
3863 |
+
if (mb_strlen($this->currentLineStyle, '8bit')) {
|
3864 |
+
$this->addContent("\n$this->currentLineStyle");
|
3865 |
+
}
|
3866 |
+
|
3867 |
+
// the call to the o_page object set currentContents to the present page, so this can be returned as the page id
|
3868 |
+
return $this->currentContents;
|
3869 |
+
}
|
3870 |
+
|
3871 |
+
/**
|
3872 |
+
* Streams the PDF to the client.
|
3873 |
+
*
|
3874 |
+
* @param string $filename The filename to present to the client.
|
3875 |
+
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
|
3876 |
+
*/
|
3877 |
+
function stream($filename = "document.pdf", $options = array())
|
3878 |
+
{
|
3879 |
+
if (headers_sent()) {
|
3880 |
+
die("Unable to stream pdf: headers already sent");
|
3881 |
+
}
|
3882 |
+
|
3883 |
+
if (!isset($options["compress"])) $options["compress"] = true;
|
3884 |
+
if (!isset($options["Attachment"])) $options["Attachment"] = true;
|
3885 |
+
|
3886 |
+
$debug = !$options['compress'];
|
3887 |
+
$tmp = ltrim($this->output($debug));
|
3888 |
+
|
3889 |
+
header("Cache-Control: private");
|
3890 |
+
header("Content-Type: application/pdf");
|
3891 |
+
header("Content-Length: " . mb_strlen($tmp, "8bit"));
|
3892 |
+
|
3893 |
+
$filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
|
3894 |
+
$attachment = $options["Attachment"] ? "attachment" : "inline";
|
3895 |
+
|
3896 |
+
$encoding = mb_detect_encoding($filename);
|
3897 |
+
$fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding);
|
3898 |
+
$fallbackfilename = str_replace("\"", "", $fallbackfilename);
|
3899 |
+
$encodedfilename = rawurlencode($filename);
|
3900 |
+
|
3901 |
+
$contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\"";
|
3902 |
+
if ($fallbackfilename !== $filename) {
|
3903 |
+
$contentDisposition .= "; filename*=UTF-8''$encodedfilename";
|
3904 |
+
}
|
3905 |
+
header($contentDisposition);
|
3906 |
+
|
3907 |
+
echo $tmp;
|
3908 |
+
flush();
|
3909 |
+
}
|
3910 |
+
|
3911 |
+
/**
|
3912 |
+
* return the height in units of the current font in the given size
|
3913 |
+
*
|
3914 |
+
* @param $size
|
3915 |
+
* @return float|int
|
3916 |
+
*/
|
3917 |
+
function getFontHeight($size)
|
3918 |
+
{
|
3919 |
+
if (!$this->numFonts) {
|
3920 |
+
$this->selectFont($this->defaultFont);
|
3921 |
+
}
|
3922 |
+
|
3923 |
+
$font = $this->fonts[$this->currentFont];
|
3924 |
+
|
3925 |
+
// for the current font, and the given size, what is the height of the font in user units
|
3926 |
+
if (isset($font['Ascender']) && isset($font['Descender'])) {
|
3927 |
+
$h = $font['Ascender'] - $font['Descender'];
|
3928 |
+
} else {
|
3929 |
+
$h = $font['FontBBox'][3] - $font['FontBBox'][1];
|
3930 |
+
}
|
3931 |
+
|
3932 |
+
// have to adjust by a font offset for Windows fonts. unfortunately it looks like
|
3933 |
+
// the bounding box calculations are wrong and I don't know why.
|
3934 |
+
if (isset($font['FontHeightOffset'])) {
|
3935 |
+
// For CourierNew from Windows this needs to be -646 to match the
|
3936 |
+
// Adobe native Courier font.
|
3937 |
+
//
|
3938 |
+
// For FreeMono from GNU this needs to be -337 to match the
|
3939 |
+
// Courier font.
|
3940 |
+
//
|
3941 |
+
// Both have been added manually to the .afm and .ufm files.
|
3942 |
+
$h += (int)$font['FontHeightOffset'];
|
3943 |
+
}
|
3944 |
+
|
3945 |
+
return $size * $h / 1000;
|
3946 |
+
}
|
3947 |
+
|
3948 |
+
/**
|
3949 |
+
* @param $size
|
3950 |
+
* @return float|int
|
3951 |
+
*/
|
3952 |
+
function getFontXHeight($size)
|
3953 |
+
{
|
3954 |
+
if (!$this->numFonts) {
|
3955 |
+
$this->selectFont($this->defaultFont);
|
3956 |
+
}
|
3957 |
+
|
3958 |
+
$font = $this->fonts[$this->currentFont];
|
3959 |
+
|
3960 |
+
// for the current font, and the given size, what is the height of the font in user units
|
3961 |
+
if (isset($font['XHeight'])) {
|
3962 |
+
$xh = $font['Ascender'] - $font['Descender'];
|
3963 |
+
} else {
|
3964 |
+
$xh = $this->getFontHeight($size) / 2;
|
3965 |
+
}
|
3966 |
+
|
3967 |
+
return $size * $xh / 1000;
|
3968 |
+
}
|
3969 |
+
|
3970 |
+
/**
|
3971 |
+
* return the font descender, this will normally return a negative number
|
3972 |
+
* if you add this number to the baseline, you get the level of the bottom of the font
|
3973 |
+
* it is in the pdf user units
|
3974 |
+
*
|
3975 |
+
* @param $size
|
3976 |
+
* @return float|int
|
3977 |
+
*/
|
3978 |
+
function getFontDescender($size)
|
3979 |
+
{
|
3980 |
+
// note that this will most likely return a negative value
|
3981 |
+
if (!$this->numFonts) {
|
3982 |
+
$this->selectFont($this->defaultFont);
|
3983 |
+
}
|
3984 |
+
|
3985 |
+
//$h = $this->fonts[$this->currentFont]['FontBBox'][1];
|
3986 |
+
$h = $this->fonts[$this->currentFont]['Descender'];
|
3987 |
+
|
3988 |
+
return $size * $h / 1000;
|
3989 |
+
}
|
3990 |
+
|
3991 |
+
/**
|
3992 |
+
* filter the text, this is applied to all text just before being inserted into the pdf document
|
3993 |
+
* it escapes the various things that need to be escaped, and so on
|
3994 |
+
*
|
3995 |
+
* @access private
|
3996 |
+
*
|
3997 |
+
* @param $text
|
3998 |
+
* @param bool $bom
|
3999 |
+
* @param bool $convert_encoding
|
4000 |
+
* @return string
|
4001 |
+
*/
|
4002 |
+
function filterText($text, $bom = true, $convert_encoding = true)
|
4003 |
+
{
|
4004 |
+
if (!$this->numFonts) {
|
4005 |
+
$this->selectFont($this->defaultFont);
|
4006 |
+
}
|
4007 |
+
|
4008 |
+
if ($convert_encoding) {
|
4009 |
+
$cf = $this->currentFont;
|
4010 |
+
if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
|
4011 |
+
$text = $this->utf8toUtf16BE($text, $bom);
|
4012 |
+
} else {
|
4013 |
+
//$text = html_entity_decode($text, ENT_QUOTES);
|
4014 |
+
$text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
|
4015 |
+
}
|
4016 |
+
} else if ($bom) {
|
4017 |
+
$text = $this->utf8toUtf16BE($text, $bom);
|
4018 |
+
}
|
4019 |
+
|
4020 |
+
// the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
|
4021 |
+
return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
|
4022 |
+
}
|
4023 |
+
|
4024 |
+
/**
|
4025 |
+
* return array containing codepoints (UTF-8 character values) for the
|
4026 |
+
* string passed in.
|
4027 |
+
*
|
4028 |
+
* based on the excellent TCPDF code by Nicola Asuni and the
|
4029 |
+
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4030 |
+
*
|
4031 |
+
* @access private
|
4032 |
+
* @author Orion Richardson
|
4033 |
+
* @since January 5, 2008
|
4034 |
+
*
|
4035 |
+
* @param string $text UTF-8 string to process
|
4036 |
+
*
|
4037 |
+
* @return array UTF-8 codepoints array for the string
|
4038 |
+
*/
|
4039 |
+
function utf8toCodePointsArray(&$text)
|
4040 |
+
{
|
4041 |
+
$length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
|
4042 |
+
$unicode = array(); // array containing unicode values
|
4043 |
+
$bytes = array(); // array containing single character byte sequences
|
4044 |
+
$numbytes = 1; // number of octets needed to represent the UTF-8 character
|
4045 |
+
|
4046 |
+
for ($i = 0; $i < $length; $i++) {
|
4047 |
+
$c = ord($text[$i]); // get one string character at time
|
4048 |
+
if (count($bytes) === 0) { // get starting octect
|
4049 |
+
if ($c <= 0x7F) {
|
4050 |
+
$unicode[] = $c; // use the character "as is" because is ASCII
|
4051 |
+
$numbytes = 1;
|
4052 |
+
} elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
|
4053 |
+
$bytes[] = ($c - 0xC0) << 0x06;
|
4054 |
+
$numbytes = 2;
|
4055 |
+
} elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
|
4056 |
+
$bytes[] = ($c - 0xE0) << 0x0C;
|
4057 |
+
$numbytes = 3;
|
4058 |
+
} elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
|
4059 |
+
$bytes[] = ($c - 0xF0) << 0x12;
|
4060 |
+
$numbytes = 4;
|
4061 |
+
} else {
|
4062 |
+
// use replacement character for other invalid sequences
|
4063 |
+
$unicode[] = 0xFFFD;
|
4064 |
+
$bytes = array();
|
4065 |
+
$numbytes = 1;
|
4066 |
+
}
|
4067 |
+
} elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
|
4068 |
+
$bytes[] = $c - 0x80;
|
4069 |
+
if (count($bytes) === $numbytes) {
|
4070 |
+
// compose UTF-8 bytes to a single unicode value
|
4071 |
+
$c = $bytes[0];
|
4072 |
+
for ($j = 1; $j < $numbytes; $j++) {
|
4073 |
+
$c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
|
4074 |
+
}
|
4075 |
+
if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) {
|
4076 |
+
// The definition of UTF-8 prohibits encoding character numbers between
|
4077 |
+
// U+D800 and U+DFFF, which are reserved for use with the UTF-16
|
4078 |
+
// encoding form (as surrogate pairs) and do not directly represent
|
4079 |
+
// characters.
|
4080 |
+
$unicode[] = 0xFFFD; // use replacement character
|
4081 |
+
} else {
|
4082 |
+
$unicode[] = $c; // add char to array
|
4083 |
+
}
|
4084 |
+
// reset data for next char
|
4085 |
+
$bytes = array();
|
4086 |
+
$numbytes = 1;
|
4087 |
+
}
|
4088 |
+
} else {
|
4089 |
+
// use replacement character for other invalid sequences
|
4090 |
+
$unicode[] = 0xFFFD;
|
4091 |
+
$bytes = array();
|
4092 |
+
$numbytes = 1;
|
4093 |
+
}
|
4094 |
+
}
|
4095 |
+
|
4096 |
+
return $unicode;
|
4097 |
+
}
|
4098 |
+
|
4099 |
+
/**
|
4100 |
+
* convert UTF-8 to UTF-16 with an additional byte order marker
|
4101 |
+
* at the front if required.
|
4102 |
+
*
|
4103 |
+
* based on the excellent TCPDF code by Nicola Asuni and the
|
4104 |
+
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4105 |
+
*
|
4106 |
+
* @access private
|
4107 |
+
* @author Orion Richardson
|
4108 |
+
* @since January 5, 2008
|
4109 |
+
*
|
4110 |
+
* @param string $text UTF-8 string to process
|
4111 |
+
* @param boolean $bom whether to add the byte order marker
|
4112 |
+
*
|
4113 |
+
* @return string UTF-16 result string
|
4114 |
+
*/
|
4115 |
+
function utf8toUtf16BE(&$text, $bom = true)
|
4116 |
+
{
|
4117 |
+
$out = $bom ? "\xFE\xFF" : '';
|
4118 |
+
|
4119 |
+
$unicode = $this->utf8toCodePointsArray($text);
|
4120 |
+
foreach ($unicode as $c) {
|
4121 |
+
if ($c === 0xFFFD) {
|
4122 |
+
$out .= "\xFF\xFD"; // replacement character
|
4123 |
+
} elseif ($c < 0x10000) {
|
4124 |
+
$out .= chr($c >> 0x08) . chr($c & 0xFF);
|
4125 |
+
} else {
|
4126 |
+
$c -= 0x10000;
|
4127 |
+
$w1 = 0xD800 | ($c >> 0x10);
|
4128 |
+
$w2 = 0xDC00 | ($c & 0x3FF);
|
4129 |
+
$out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
|
4130 |
+
}
|
4131 |
+
}
|
4132 |
+
|
4133 |
+
return $out;
|
4134 |
+
}
|
4135 |
+
|
4136 |
+
/**
|
4137 |
+
* given a start position and information about how text is to be laid out, calculate where
|
4138 |
+
* on the page the text will end
|
4139 |
+
*
|
4140 |
+
* @param $x
|
4141 |
+
* @param $y
|
4142 |
+
* @param $angle
|
4143 |
+
* @param $size
|
4144 |
+
* @param $wa
|
4145 |
+
* @param $text
|
4146 |
+
* @return array
|
4147 |
+
*/
|
4148 |
+
private function getTextPosition($x, $y, $angle, $size, $wa, $text)
|
4149 |
+
{
|
4150 |
+
// given this information return an array containing x and y for the end position as elements 0 and 1
|
4151 |
+
$w = $this->getTextWidth($size, $text);
|
4152 |
+
|
4153 |
+
// need to adjust for the number of spaces in this text
|
4154 |
+
$words = explode(' ', $text);
|
4155 |
+
$nspaces = count($words) - 1;
|
4156 |
+
$w += $wa * $nspaces;
|
4157 |
+
$a = deg2rad((float)$angle);
|
4158 |
+
|
4159 |
+
return array(cos($a) * $w + $x, -sin($a) * $w + $y);
|
4160 |
+
}
|
4161 |
+
|
4162 |
+
/**
|
4163 |
+
* Callback method used by smallCaps
|
4164 |
+
*
|
4165 |
+
* @param array $matches
|
4166 |
+
*
|
4167 |
+
* @return string
|
4168 |
+
*/
|
4169 |
+
function toUpper($matches)
|
4170 |
+
{
|
4171 |
+
return mb_strtoupper($matches[0]);
|
4172 |
+
}
|
4173 |
+
|
4174 |
+
function concatMatches($matches)
|
4175 |
+
{
|
4176 |
+
$str = "";
|
4177 |
+
foreach ($matches as $match) {
|
4178 |
+
$str .= $match[0];
|
4179 |
+
}
|
4180 |
+
|
4181 |
+
return $str;
|
4182 |
+
}
|
4183 |
+
|
4184 |
+
/**
|
4185 |
+
* register text for font subsetting
|
4186 |
+
*
|
4187 |
+
* @param $font
|
4188 |
+
* @param $text
|
4189 |
+
*/
|
4190 |
+
function registerText($font, $text)
|
4191 |
+
{
|
4192 |
+
if (!$this->isUnicode || in_array(mb_strtolower(basename($font)), self::$coreFonts)) {
|
4193 |
+
return;
|
4194 |
+
}
|
4195 |
+
|
4196 |
+
if (!isset($this->stringSubsets[$font])) {
|
4197 |
+
$this->stringSubsets[$font] = array();
|
4198 |
+
}
|
4199 |
+
|
4200 |
+
$this->stringSubsets[$font] = array_unique(
|
4201 |
+
array_merge($this->stringSubsets[$font], $this->utf8toCodePointsArray($text))
|
4202 |
+
);
|
4203 |
+
}
|
4204 |
+
|
4205 |
+
/**
|
4206 |
+
* add text to the document, at a specified location, size and angle on the page
|
4207 |
+
*
|
4208 |
+
* @param $x
|
4209 |
+
* @param $y
|
4210 |
+
* @param $size
|
4211 |
+
* @param $text
|
4212 |
+
* @param int $angle
|
4213 |
+
* @param int $wordSpaceAdjust
|
4214 |
+
* @param int $charSpaceAdjust
|
4215 |
+
* @param bool $smallCaps
|
4216 |
+
*/
|
4217 |
+
function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false)
|
4218 |
+
{
|
4219 |
+
if (!$this->numFonts) {
|
4220 |
+
$this->selectFont($this->defaultFont);
|
4221 |
+
}
|
4222 |
+
|
4223 |
+
$text = str_replace(array("\r", "\n"), "", $text);
|
4224 |
+
|
4225 |
+
if ($smallCaps) {
|
4226 |
+
preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4227 |
+
$lower = $this->concatMatches($matches);
|
4228 |
+
d($lower);
|
4229 |
+
|
4230 |
+
preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4231 |
+
$other = $this->concatMatches($matches);
|
4232 |
+
d($other);
|
4233 |
+
|
4234 |
+
//$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
|
4235 |
+
}
|
4236 |
+
|
4237 |
+
// if there are any open callbacks, then they should be called, to show the start of the line
|
4238 |
+
if ($this->nCallback > 0) {
|
4239 |
+
for ($i = $this->nCallback; $i > 0; $i--) {
|
4240 |
+
// call each function
|
4241 |
+
$info = array(
|
4242 |
+
'x' => $x,
|
4243 |
+
'y' => $y,
|
4244 |
+
'angle' => $angle,
|
4245 |
+
'status' => 'sol',
|
4246 |
+
'p' => $this->callback[$i]['p'],
|
4247 |
+
'nCallback' => $this->callback[$i]['nCallback'],
|
4248 |
+
'height' => $this->callback[$i]['height'],
|
4249 |
+
'descender' => $this->callback[$i]['descender']
|
4250 |
+
);
|
4251 |
+
|
4252 |
+
$func = $this->callback[$i]['f'];
|
4253 |
+
$this->$func($info);
|
4254 |
+
}
|
4255 |
+
}
|
4256 |
+
|
4257 |
+
if ($angle == 0) {
|
4258 |
+
$this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y));
|
4259 |
+
} else {
|
4260 |
+
$a = deg2rad((float)$angle);
|
4261 |
+
$this->addContent(
|
4262 |
+
sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y)
|
4263 |
+
);
|
4264 |
+
}
|
4265 |
+
|
4266 |
+
if ($wordSpaceAdjust != 0) {
|
4267 |
+
$this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
|
4268 |
+
}
|
4269 |
+
|
4270 |
+
if ($charSpaceAdjust != 0) {
|
4271 |
+
$this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust));
|
4272 |
+
}
|
4273 |
+
|
4274 |
+
$len = mb_strlen($text);
|
4275 |
+
$start = 0;
|
4276 |
+
|
4277 |
+
if ($start < $len) {
|
4278 |
+
$part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start);
|
4279 |
+
$place_text = $this->filterText($part, false);
|
4280 |
+
// modify unicode text so that extra word spacing is manually implemented (bug #)
|
4281 |
+
$cf = $this->currentFont;
|
4282 |
+
if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
|
4283 |
+
$space_scale = 1000 / $size;
|
4284 |
+
$place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text);
|
4285 |
+
}
|
4286 |
+
$this->addContent(" /F$this->currentFontNum " . sprintf('%.1F Tf ', $size));
|
4287 |
+
$this->addContent(" [($place_text)] TJ");
|
4288 |
+
}
|
4289 |
+
|
4290 |
+
if ($wordSpaceAdjust != 0) {
|
4291 |
+
$this->addContent(sprintf(" %.3F Tw", 0));
|
4292 |
+
}
|
4293 |
+
|
4294 |
+
if ($charSpaceAdjust != 0) {
|
4295 |
+
$this->addContent(sprintf(" %.3F Tc", 0));
|
4296 |
+
}
|
4297 |
+
|
4298 |
+
$this->addContent(' ET');
|
4299 |
+
|
4300 |
+
// if there are any open callbacks, then they should be called, to show the end of the line
|
4301 |
+
if ($this->nCallback > 0) {
|
4302 |
+
for ($i = $this->nCallback; $i > 0; $i--) {
|
4303 |
+
// call each function
|
4304 |
+
$tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
|
4305 |
+
$info = array(
|
4306 |
+
'x' => $tmp[0],
|
4307 |
+
'y' => $tmp[1],
|
4308 |
+
'angle' => $angle,
|
4309 |
+
'status' => 'eol',
|
4310 |
+
'p' => $this->callback[$i]['p'],
|
4311 |
+
'nCallback' => $this->callback[$i]['nCallback'],
|
4312 |
+
'height' => $this->callback[$i]['height'],
|
4313 |
+
'descender' => $this->callback[$i]['descender']
|
4314 |
+
);
|
4315 |
+
$func = $this->callback[$i]['f'];
|
4316 |
+
$this->$func($info);
|
4317 |
+
}
|
4318 |
+
}
|
4319 |
+
}
|
4320 |
+
|
4321 |
+
/**
|
4322 |
+
* calculate how wide a given text string will be on a page, at a given size.
|
4323 |
+
* this can be called externally, but is also used by the other class functions
|
4324 |
+
*
|
4325 |
+
* @param $size
|
4326 |
+
* @param $text
|
4327 |
+
* @param int $word_spacing
|
4328 |
+
* @param int $char_spacing
|
4329 |
+
* @return float|int
|
4330 |
+
*/
|
4331 |
+
function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0)
|
4332 |
+
{
|
4333 |
+
static $ord_cache = array();
|
4334 |
+
|
4335 |
+
// this function should not change any of the settings, though it will need to
|
4336 |
+
// track any directives which change during calculation, so copy them at the start
|
4337 |
+
// and put them back at the end.
|
4338 |
+
$store_currentTextState = $this->currentTextState;
|
4339 |
+
|
4340 |
+
if (!$this->numFonts) {
|
4341 |
+
$this->selectFont($this->defaultFont);
|
4342 |
+
}
|
4343 |
+
|
4344 |
+
$text = str_replace(array("\r", "\n"), "", $text);
|
4345 |
+
|
4346 |
+
// converts a number or a float to a string so it can get the width
|
4347 |
+
$text = "$text";
|
4348 |
+
|
4349 |
+
// hmm, this is where it all starts to get tricky - use the font information to
|
4350 |
+
// calculate the width of each character, add them up and convert to user units
|
4351 |
+
$w = 0;
|
4352 |
+
$cf = $this->currentFont;
|
4353 |
+
$current_font = $this->fonts[$cf];
|
4354 |
+
$space_scale = 1000 / ($size > 0 ? $size : 1);
|
4355 |
+
$n_spaces = 0;
|
4356 |
+
|
4357 |
+
if ($current_font['isUnicode']) {
|
4358 |
+
// for Unicode, use the code points array to calculate width rather
|
4359 |
+
// than just the string itself
|
4360 |
+
$unicode = $this->utf8toCodePointsArray($text);
|
4361 |
+
|
4362 |
+
foreach ($unicode as $char) {
|
4363 |
+
// check if we have to replace character
|
4364 |
+
if (isset($current_font['differences'][$char])) {
|
4365 |
+
$char = $current_font['differences'][$char];
|
4366 |
+
}
|
4367 |
+
|
4368 |
+
if (isset($current_font['C'][$char])) {
|
4369 |
+
$char_width = $current_font['C'][$char];
|
4370 |
+
|
4371 |
+
// add the character width
|
4372 |
+
$w += $char_width;
|
4373 |
+
|
4374 |
+
// add additional padding for space
|
4375 |
+
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4376 |
+
$w += $word_spacing * $space_scale;
|
4377 |
+
$n_spaces++;
|
4378 |
+
}
|
4379 |
+
}
|
4380 |
+
}
|
4381 |
+
|
4382 |
+
// add additional char spacing
|
4383 |
+
if ($char_spacing != 0) {
|
4384 |
+
$w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
|
4385 |
+
}
|
4386 |
+
|
4387 |
+
} else {
|
4388 |
+
// If CPDF is in Unicode mode but the current font does not support Unicode we need to convert the character set to Windows-1252
|
4389 |
+
if ($this->isUnicode) {
|
4390 |
+
$text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
|
4391 |
+
}
|
4392 |
+
|
4393 |
+
$len = mb_strlen($text, 'Windows-1252');
|
4394 |
+
|
4395 |
+
for ($i = 0; $i < $len; $i++) {
|
4396 |
+
$c = $text[$i];
|
4397 |
+
$char = isset($ord_cache[$c]) ? $ord_cache[$c] : ($ord_cache[$c] = ord($c));
|
4398 |
+
|
4399 |
+
// check if we have to replace character
|
4400 |
+
if (isset($current_font['differences'][$char])) {
|
4401 |
+
$char = $current_font['differences'][$char];
|
4402 |
+
}
|
4403 |
+
|
4404 |
+
if (isset($current_font['C'][$char])) {
|
4405 |
+
$char_width = $current_font['C'][$char];
|
4406 |
+
|
4407 |
+
// add the character width
|
4408 |
+
$w += $char_width;
|
4409 |
+
|
4410 |
+
// add additional padding for space
|
4411 |
+
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4412 |
+
$w += $word_spacing * $space_scale;
|
4413 |
+
$n_spaces++;
|
4414 |
+
}
|
4415 |
+
}
|
4416 |
+
}
|
4417 |
+
|
4418 |
+
// add additional char spacing
|
4419 |
+
if ($char_spacing != 0) {
|
4420 |
+
$w += $char_spacing * $space_scale * ($len + $n_spaces);
|
4421 |
+
}
|
4422 |
+
}
|
4423 |
+
|
4424 |
+
$this->currentTextState = $store_currentTextState;
|
4425 |
+
$this->setCurrentFont();
|
4426 |
+
|
4427 |
+
return $w * $size / 1000;
|
4428 |
+
}
|
4429 |
+
|
4430 |
+
/**
|
4431 |
+
* this will be called at a new page to return the state to what it was on the
|
4432 |
+
* end of the previous page, before the stack was closed down
|
4433 |
+
* This is to get around not being able to have open 'q' across pages
|
4434 |
+
*
|
4435 |
+
* @param int $pageEnd
|
4436 |
+
*/
|
4437 |
+
function saveState($pageEnd = 0)
|
4438 |
+
{
|
4439 |
+
if ($pageEnd) {
|
4440 |
+
// this will be called at a new page to return the state to what it was on the
|
4441 |
+
// end of the previous page, before the stack was closed down
|
4442 |
+
// This is to get around not being able to have open 'q' across pages
|
4443 |
+
$opt = $this->stateStack[$pageEnd];
|
4444 |
+
// ok to use this as stack starts numbering at 1
|
4445 |
+
$this->setColor($opt['col'], true);
|
4446 |
+
$this->setStrokeColor($opt['str'], true);
|
4447 |
+
$this->addContent("\n" . $opt['lin']);
|
4448 |
+
// $this->currentLineStyle = $opt['lin'];
|
4449 |
+
} else {
|
4450 |
+
$this->nStateStack++;
|
4451 |
+
$this->stateStack[$this->nStateStack] = array(
|
4452 |
+
'col' => $this->currentColor,
|
4453 |
+
'str' => $this->currentStrokeColor,
|
4454 |
+
'lin' => $this->currentLineStyle
|
4455 |
+
);
|
4456 |
+
}
|
4457 |
+
|
4458 |
+
$this->save();
|
4459 |
+
}
|
4460 |
+
|
4461 |
+
/**
|
4462 |
+
* restore a previously saved state
|
4463 |
+
*
|
4464 |
+
* @param int $pageEnd
|
4465 |
+
*/
|
4466 |
+
function restoreState($pageEnd = 0)
|
4467 |
+
{
|
4468 |
+
if (!$pageEnd) {
|
4469 |
+
$n = $this->nStateStack;
|
4470 |
+
$this->currentColor = $this->stateStack[$n]['col'];
|
4471 |
+
$this->currentStrokeColor = $this->stateStack[$n]['str'];
|
4472 |
+
$this->addContent("\n" . $this->stateStack[$n]['lin']);
|
4473 |
+
$this->currentLineStyle = $this->stateStack[$n]['lin'];
|
4474 |
+
$this->stateStack[$n] = null;
|
4475 |
+
unset($this->stateStack[$n]);
|
4476 |
+
$this->nStateStack--;
|
4477 |
+
}
|
4478 |
+
|
4479 |
+
$this->restore();
|
4480 |
+
}
|
4481 |
+
|
4482 |
+
/**
|
4483 |
+
* make a loose object, the output will go into this object, until it is closed, then will revert to
|
4484 |
+
* the current one.
|
4485 |
+
* this object will not appear until it is included within a page.
|
4486 |
+
* the function will return the object number
|
4487 |
+
*
|
4488 |
+
* @return int
|
4489 |
+
*/
|
4490 |
+
function openObject()
|
4491 |
+
{
|
4492 |
+
$this->nStack++;
|
4493 |
+
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4494 |
+
// add a new object of the content type, to hold the data flow
|
4495 |
+
$this->numObj++;
|
4496 |
+
$this->o_contents($this->numObj, 'new');
|
4497 |
+
$this->currentContents = $this->numObj;
|
4498 |
+
$this->looseObjects[$this->numObj] = 1;
|
4499 |
+
|
4500 |
+
return $this->numObj;
|
4501 |
+
}
|
4502 |
+
|
4503 |
+
/**
|
4504 |
+
* open an existing object for editing
|
4505 |
+
*
|
4506 |
+
* @param $id
|
4507 |
+
*/
|
4508 |
+
function reopenObject($id)
|
4509 |
+
{
|
4510 |
+
$this->nStack++;
|
4511 |
+
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4512 |
+
$this->currentContents = $id;
|
4513 |
+
|
4514 |
+
// also if this object is the primary contents for a page, then set the current page to its parent
|
4515 |
+
if (isset($this->objects[$id]['onPage'])) {
|
4516 |
+
$this->currentPage = $this->objects[$id]['onPage'];
|
4517 |
+
}
|
4518 |
+
}
|
4519 |
+
|
4520 |
+
/**
|
4521 |
+
* close an object
|
4522 |
+
*/
|
4523 |
+
function closeObject()
|
4524 |
+
{
|
4525 |
+
// close the object, as long as there was one open in the first place, which will be indicated by
|
4526 |
+
// an objectId on the stack.
|
4527 |
+
if ($this->nStack > 0) {
|
4528 |
+
$this->currentContents = $this->stack[$this->nStack]['c'];
|
4529 |
+
$this->currentPage = $this->stack[$this->nStack]['p'];
|
4530 |
+
$this->nStack--;
|
4531 |
+
// easier to probably not worry about removing the old entries, they will be overwritten
|
4532 |
+
// if there are new ones.
|
4533 |
+
}
|
4534 |
+
}
|
4535 |
+
|
4536 |
+
/**
|
4537 |
+
* stop an object from appearing on pages from this point on
|
4538 |
+
*
|
4539 |
+
* @param $id
|
4540 |
+
*/
|
4541 |
+
function stopObject($id)
|
4542 |
+
{
|
4543 |
+
// if an object has been appearing on pages up to now, then stop it, this page will
|
4544 |
+
// be the last one that could contain it.
|
4545 |
+
if (isset($this->addLooseObjects[$id])) {
|
4546 |
+
$this->addLooseObjects[$id] = '';
|
4547 |
+
}
|
4548 |
+
}
|
4549 |
+
|
4550 |
+
/**
|
4551 |
+
* after an object has been created, it wil only show if it has been added, using this function.
|
4552 |
+
*
|
4553 |
+
* @param $id
|
4554 |
+
* @param string $options
|
4555 |
+
*/
|
4556 |
+
function addObject($id, $options = 'add')
|
4557 |
+
{
|
4558 |
+
// add the specified object to the page
|
4559 |
+
if (isset($this->looseObjects[$id]) && $this->currentContents != $id) {
|
4560 |
+
// then it is a valid object, and it is not being added to itself
|
4561 |
+
switch ($options) {
|
4562 |
+
case 'all':
|
4563 |
+
// then this object is to be added to this page (done in the next block) and
|
4564 |
+
// all future new pages.
|
4565 |
+
$this->addLooseObjects[$id] = 'all';
|
4566 |
+
|
4567 |
+
case 'add':
|
4568 |
+
if (isset($this->objects[$this->currentContents]['onPage'])) {
|
4569 |
+
// then the destination contents is the primary for the page
|
4570 |
+
// (though this object is actually added to that page)
|
4571 |
+
$this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id);
|
4572 |
+
}
|
4573 |
+
break;
|
4574 |
+
|
4575 |
+
case 'even':
|
4576 |
+
$this->addLooseObjects[$id] = 'even';
|
4577 |
+
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4578 |
+
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 0) {
|
4579 |
+
$this->addObject($id);
|
4580 |
+
// hacky huh :)
|
4581 |
+
}
|
4582 |
+
break;
|
4583 |
+
|
4584 |
+
case 'odd':
|
4585 |
+
$this->addLooseObjects[$id] = 'odd';
|
4586 |
+
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4587 |
+
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 1) {
|
4588 |
+
$this->addObject($id);
|
4589 |
+
// hacky huh :)
|
4590 |
+
}
|
4591 |
+
break;
|
4592 |
+
|
4593 |
+
case 'next':
|
4594 |
+
$this->addLooseObjects[$id] = 'all';
|
4595 |
+
break;
|
4596 |
+
|
4597 |
+
case 'nexteven':
|
4598 |
+
$this->addLooseObjects[$id] = 'even';
|
4599 |
+
break;
|
4600 |
+
|
4601 |
+
case 'nextodd':
|
4602 |
+
$this->addLooseObjects[$id] = 'odd';
|
4603 |
+
break;
|
4604 |
+
}
|
4605 |
+
}
|
4606 |
+
}
|
4607 |
+
|
4608 |
+
/**
|
4609 |
+
* return a storable representation of a specific object
|
4610 |
+
*
|
4611 |
+
* @param $id
|
4612 |
+
* @return string|null
|
4613 |
+
*/
|
4614 |
+
function serializeObject($id)
|
4615 |
+
{
|
4616 |
+
if (array_key_exists($id, $this->objects)) {
|
4617 |
+
return serialize($this->objects[$id]);
|
4618 |
+
}
|
4619 |
+
|
4620 |
+
return null;
|
4621 |
+
}
|
4622 |
+
|
4623 |
+
/**
|
4624 |
+
* restore an object from its stored representation. returns its new object id.
|
4625 |
+
*
|
4626 |
+
* @param $obj
|
4627 |
+
* @return int
|
4628 |
+
*/
|
4629 |
+
function restoreSerializedObject($obj)
|
4630 |
+
{
|
4631 |
+
$obj_id = $this->openObject();
|
4632 |
+
$this->objects[$obj_id] = unserialize($obj);
|
4633 |
+
$this->closeObject();
|
4634 |
+
|
4635 |
+
return $obj_id;
|
4636 |
+
}
|
4637 |
+
|
4638 |
+
/**
|
4639 |
+
* add content to the documents info object
|
4640 |
+
*
|
4641 |
+
* @param $label
|
4642 |
+
* @param int $value
|
4643 |
+
*/
|
4644 |
+
function addInfo($label, $value = 0)
|
4645 |
+
{
|
4646 |
+
// this will only work if the label is one of the valid ones.
|
4647 |
+
// modify this so that arrays can be passed as well.
|
4648 |
+
// if $label is an array then assume that it is key => value pairs
|
4649 |
+
// else assume that they are both scalar, anything else will probably error
|
4650 |
+
if (is_array($label)) {
|
4651 |
+
foreach ($label as $l => $v) {
|
4652 |
+
$this->o_info($this->infoObject, $l, $v);
|
4653 |
+
}
|
4654 |
+
} else {
|
4655 |
+
$this->o_info($this->infoObject, $label, $value);
|
4656 |
+
}
|
4657 |
+
}
|
4658 |
+
|
4659 |
+
/**
|
4660 |
+
* set the viewer preferences of the document, it is up to the browser to obey these.
|
4661 |
+
*
|
4662 |
+
* @param $label
|
4663 |
+
* @param int $value
|
4664 |
+
*/
|
4665 |
+
function setPreferences($label, $value = 0)
|
4666 |
+
{
|
4667 |
+
// this will only work if the label is one of the valid ones.
|
4668 |
+
if (is_array($label)) {
|
4669 |
+
foreach ($label as $l => $v) {
|
4670 |
+
$this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
|
4671 |
+
}
|
4672 |
+
} else {
|
4673 |
+
$this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
|
4674 |
+
}
|
4675 |
+
}
|
4676 |
+
|
4677 |
+
/**
|
4678 |
+
* extract an integer from a position in a byte stream
|
4679 |
+
*
|
4680 |
+
* @param $data
|
4681 |
+
* @param $pos
|
4682 |
+
* @param $num
|
4683 |
+
* @return int
|
4684 |
+
*/
|
4685 |
+
private function getBytes(&$data, $pos, $num)
|
4686 |
+
{
|
4687 |
+
// return the integer represented by $num bytes from $pos within $data
|
4688 |
+
$ret = 0;
|
4689 |
+
for ($i = 0; $i < $num; $i++) {
|
4690 |
+
$ret *= 256;
|
4691 |
+
$ret += ord($data[$pos + $i]);
|
4692 |
+
}
|
4693 |
+
|
4694 |
+
return $ret;
|
4695 |
+
}
|
4696 |
+
|
4697 |
+
/**
|
4698 |
+
* Check if image already added to pdf image directory.
|
4699 |
+
* If yes, need not to create again (pass empty data)
|
4700 |
+
*
|
4701 |
+
* @param $imgname
|
4702 |
+
* @return bool
|
4703 |
+
*/
|
4704 |
+
function image_iscached($imgname)
|
4705 |
+
{
|
4706 |
+
return isset($this->imagelist[$imgname]);
|
4707 |
+
}
|
4708 |
+
|
4709 |
+
/**
|
4710 |
+
* add a PNG image into the document, from a GD object
|
4711 |
+
* this should work with remote files
|
4712 |
+
*
|
4713 |
+
* @param string $file The PNG file
|
4714 |
+
* @param float $x X position
|
4715 |
+
* @param float $y Y position
|
4716 |
+
* @param float $w Width
|
4717 |
+
* @param float $h Height
|
4718 |
+
* @param resource $img A GD resource
|
4719 |
+
* @param bool $is_mask true if the image is a mask
|
4720 |
+
* @param bool $mask true if the image is masked
|
4721 |
+
* @throws Exception
|
4722 |
+
*/
|
4723 |
+
function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null)
|
4724 |
+
{
|
4725 |
+
if (!function_exists("imagepng")) {
|
4726 |
+
throw new \Exception("The PHP GD extension is required, but is not installed.");
|
4727 |
+
}
|
4728 |
+
|
4729 |
+
//if already cached, need not to read again
|
4730 |
+
if (isset($this->imagelist[$file])) {
|
4731 |
+
$data = null;
|
4732 |
+
} else {
|
4733 |
+
// Example for transparency handling on new image. Retain for current image
|
4734 |
+
// $tIndex = imagecolortransparent($img);
|
4735 |
+
// if ($tIndex > 0) {
|
4736 |
+
// $tColor = imagecolorsforindex($img, $tIndex);
|
4737 |
+
// $new_tIndex = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']);
|
4738 |
+
// imagefill($new_img, 0, 0, $new_tIndex);
|
4739 |
+
// imagecolortransparent($new_img, $new_tIndex);
|
4740 |
+
// }
|
4741 |
+
// blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn
|
4742 |
+
//imagealphablending($img, true);
|
4743 |
+
|
4744 |
+
//default, but explicitely set to ensure pdf compatibility
|
4745 |
+
imagesavealpha($img, false/*!$is_mask && !$mask*/);
|
4746 |
+
|
4747 |
+
$error = 0;
|
4748 |
+
//DEBUG_IMG_TEMP
|
4749 |
+
//debugpng
|
4750 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4751 |
+
print '[addImagePng ' . $file . ']';
|
4752 |
+
}
|
4753 |
+
|
4754 |
+
ob_start();
|
4755 |
+
@imagepng($img);
|
4756 |
+
$data = ob_get_clean();
|
4757 |
+
|
4758 |
+
if ($data == '') {
|
4759 |
+
$error = 1;
|
4760 |
+
$errormsg = 'trouble writing file from GD';
|
4761 |
+
//DEBUG_IMG_TEMP
|
4762 |
+
//debugpng
|
4763 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4764 |
+
print 'trouble writing file from GD';
|
4765 |
+
}
|
4766 |
+
}
|
4767 |
+
|
4768 |
+
if ($error) {
|
4769 |
+
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
|
4770 |
+
|
4771 |
+
return;
|
4772 |
+
}
|
4773 |
+
} //End isset($this->imagelist[$file]) (png Duplicate removal)
|
4774 |
+
|
4775 |
+
$this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
|
4776 |
+
}
|
4777 |
+
|
4778 |
+
/**
|
4779 |
+
* @param $file
|
4780 |
+
* @param $x
|
4781 |
+
* @param $y
|
4782 |
+
* @param $w
|
4783 |
+
* @param $h
|
4784 |
+
* @param $byte
|
4785 |
+
*/
|
4786 |
+
protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte)
|
4787 |
+
{
|
4788 |
+
// generate images
|
4789 |
+
$img = imagecreatefrompng($file);
|
4790 |
+
|
4791 |
+
if ($img === false) {
|
4792 |
+
return;
|
4793 |
+
}
|
4794 |
+
|
4795 |
+
// FIXME The pixel transformation doesn't work well with 8bit PNGs
|
4796 |
+
$eight_bit = ($byte & 4) !== 4;
|
4797 |
+
|
4798 |
+
$wpx = imagesx($img);
|
4799 |
+
$hpx = imagesy($img);
|
4800 |
+
|
4801 |
+
imagesavealpha($img, false);
|
4802 |
+
|
4803 |
+
// create temp alpha file
|
4804 |
+
$tempfile_alpha = tempnam($this->tmp, "cpdf_img_");
|
4805 |
+
@unlink($tempfile_alpha);
|
4806 |
+
$tempfile_alpha = "$tempfile_alpha.png";
|
4807 |
+
|
4808 |
+
// create temp plain file
|
4809 |
+
$tempfile_plain = tempnam($this->tmp, "cpdf_img_");
|
4810 |
+
@unlink($tempfile_plain);
|
4811 |
+
$tempfile_plain = "$tempfile_plain.png";
|
4812 |
+
|
4813 |
+
$imgalpha = imagecreate($wpx, $hpx);
|
4814 |
+
imagesavealpha($imgalpha, false);
|
4815 |
+
|
4816 |
+
// generate gray scale palette (0 -> 255)
|
4817 |
+
for ($c = 0; $c < 256; ++$c) {
|
4818 |
+
imagecolorallocate($imgalpha, $c, $c, $c);
|
4819 |
+
}
|
4820 |
+
|
4821 |
+
// Use PECL gmagick + Graphics Magic to process transparent PNG images
|
4822 |
+
if (extension_loaded("gmagick")) {
|
4823 |
+
$gmagick = new \Gmagick($file);
|
4824 |
+
$gmagick->setimageformat('png');
|
4825 |
+
|
4826 |
+
// Get opacity channel (negative of alpha channel)
|
4827 |
+
$alpha_channel_neg = clone $gmagick;
|
4828 |
+
$alpha_channel_neg->separateimagechannel(\Gmagick::CHANNEL_OPACITY);
|
4829 |
+
|
4830 |
+
// Negate opacity channel
|
4831 |
+
$alpha_channel = new \Gmagick();
|
4832 |
+
$alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4833 |
+
$alpha_channel->compositeimage($alpha_channel_neg, \Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
|
4834 |
+
$alpha_channel->separateimagechannel(\Gmagick::CHANNEL_RED);
|
4835 |
+
$alpha_channel->writeimage($tempfile_alpha);
|
4836 |
+
|
4837 |
+
// Cast to 8bit+palette
|
4838 |
+
$imgalpha_ = imagecreatefrompng($tempfile_alpha);
|
4839 |
+
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
|
4840 |
+
imagedestroy($imgalpha_);
|
4841 |
+
imagepng($imgalpha, $tempfile_alpha);
|
4842 |
+
|
4843 |
+
// Make opaque image
|
4844 |
+
$color_channels = new \Gmagick();
|
4845 |
+
$color_channels->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4846 |
+
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYRED, 0, 0);
|
4847 |
+
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYGREEN, 0, 0);
|
4848 |
+
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYBLUE, 0, 0);
|
4849 |
+
$color_channels->writeimage($tempfile_plain);
|
4850 |
+
|
4851 |
+
$imgplain = imagecreatefrompng($tempfile_plain);
|
4852 |
+
}
|
4853 |
+
// Use PECL imagick + ImageMagic to process transparent PNG images
|
4854 |
+
elseif (extension_loaded("imagick")) {
|
4855 |
+
// Native cloning was added to pecl-imagick in svn commit 263814
|
4856 |
+
// the first version containing it was 3.0.1RC1
|
4857 |
+
static $imagickClonable = null;
|
4858 |
+
if ($imagickClonable === null) {
|
4859 |
+
$imagickClonable = version_compare(Imagick::IMAGICK_EXTVER, '3.0.1rc1') > 0;
|
4860 |
+
}
|
4861 |
+
|
4862 |
+
$imagick = new \Imagick($file);
|
4863 |
+
$imagick->setFormat('png');
|
4864 |
+
|
4865 |
+
// Get opacity channel (negative of alpha channel)
|
4866 |
+
if ($imagick->getImageAlphaChannel() !== 0) {
|
4867 |
+
$alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
|
4868 |
+
$alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA);
|
4869 |
+
$alpha_channel->negateImage(true);
|
4870 |
+
$alpha_channel->writeImage($tempfile_alpha);
|
4871 |
+
|
4872 |
+
// Cast to 8bit+palette
|
4873 |
+
$imgalpha_ = imagecreatefrompng($tempfile_alpha);
|
4874 |
+
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
|
4875 |
+
imagedestroy($imgalpha_);
|
4876 |
+
imagepng($imgalpha, $tempfile_alpha);
|
4877 |
+
} else {
|
4878 |
+
$tempfile_alpha = null;
|
4879 |
+
}
|
4880 |
+
|
4881 |
+
// Make opaque image
|
4882 |
+
$color_channels = new \Imagick();
|
4883 |
+
$color_channels->newImage($wpx, $hpx, "#FFFFFF", "png");
|
4884 |
+
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYRED, 0, 0);
|
4885 |
+
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYGREEN, 0, 0);
|
4886 |
+
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYBLUE, 0, 0);
|
4887 |
+
$color_channels->writeImage($tempfile_plain);
|
4888 |
+
|
4889 |
+
$imgplain = imagecreatefrompng($tempfile_plain);
|
4890 |
+
} else {
|
4891 |
+
// allocated colors cache
|
4892 |
+
$allocated_colors = array();
|
4893 |
+
|
4894 |
+
// extract alpha channel
|
4895 |
+
for ($xpx = 0; $xpx < $wpx; ++$xpx) {
|
4896 |
+
for ($ypx = 0; $ypx < $hpx; ++$ypx) {
|
4897 |
+
$color = imagecolorat($img, $xpx, $ypx);
|
4898 |
+
$col = imagecolorsforindex($img, $color);
|
4899 |
+
$alpha = $col['alpha'];
|
4900 |
+
|
4901 |
+
if ($eight_bit) {
|
4902 |
+
// with gamma correction
|
4903 |
+
$gammacorr = 2.2;
|
4904 |
+
$pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255;
|
4905 |
+
} else {
|
4906 |
+
// without gamma correction
|
4907 |
+
$pixel = (127 - $alpha) * 2;
|
4908 |
+
|
4909 |
+
$key = $col['red'] . $col['green'] . $col['blue'];
|
4910 |
+
|
4911 |
+
if (!isset($allocated_colors[$key])) {
|
4912 |
+
$pixel_img = imagecolorallocate($img, $col['red'], $col['green'], $col['blue']);
|
4913 |
+
$allocated_colors[$key] = $pixel_img;
|
4914 |
+
} else {
|
4915 |
+
$pixel_img = $allocated_colors[$key];
|
4916 |
+
}
|
4917 |
+
|
4918 |
+
imagesetpixel($img, $xpx, $ypx, $pixel_img);
|
4919 |
+
}
|
4920 |
+
|
4921 |
+
imagesetpixel($imgalpha, $xpx, $ypx, $pixel);
|
4922 |
+
}
|
4923 |
+
}
|
4924 |
+
|
4925 |
+
// extract image without alpha channel
|
4926 |
+
$imgplain = imagecreatetruecolor($wpx, $hpx);
|
4927 |
+
imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
|
4928 |
+
imagedestroy($img);
|
4929 |
+
|
4930 |
+
imagepng($imgalpha, $tempfile_alpha);
|
4931 |
+
imagepng($imgplain, $tempfile_plain);
|
4932 |
+
}
|
4933 |
+
|
4934 |
+
// embed mask image
|
4935 |
+
if ($tempfile_alpha) {
|
4936 |
+
$this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true);
|
4937 |
+
imagedestroy($imgalpha);
|
4938 |
+
}
|
4939 |
+
|
4940 |
+
// embed image, masked with previously embedded mask
|
4941 |
+
$this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, ($tempfile_alpha !== null));
|
4942 |
+
imagedestroy($imgplain);
|
4943 |
+
|
4944 |
+
// remove temp files
|
4945 |
+
if ($tempfile_alpha) {
|
4946 |
+
unlink($tempfile_alpha);
|
4947 |
+
}
|
4948 |
+
unlink($tempfile_plain);
|
4949 |
+
}
|
4950 |
+
|
4951 |
+
/**
|
4952 |
+
* add a PNG image into the document, from a file
|
4953 |
+
* this should work with remote files
|
4954 |
+
*
|
4955 |
+
* @param $file
|
4956 |
+
* @param $x
|
4957 |
+
* @param $y
|
4958 |
+
* @param int $w
|
4959 |
+
* @param int $h
|
4960 |
+
* @throws Exception
|
4961 |
+
*/
|
4962 |
+
function addPngFromFile($file, $x, $y, $w = 0, $h = 0)
|
4963 |
+
{
|
4964 |
+
if (!function_exists("imagecreatefrompng")) {
|
4965 |
+
throw new \Exception("The PHP GD extension is required, but is not installed.");
|
4966 |
+
}
|
4967 |
+
|
4968 |
+
//if already cached, need not to read again
|
4969 |
+
if (isset($this->imagelist[$file])) {
|
4970 |
+
$img = null;
|
4971 |
+
} else {
|
4972 |
+
$info = file_get_contents($file, false, null, 24, 5);
|
4973 |
+
$meta = unpack("CbitDepth/CcolorType/CcompressionMethod/CfilterMethod/CinterlaceMethod", $info);
|
4974 |
+
$bit_depth = $meta["bitDepth"];
|
4975 |
+
$color_type = $meta["colorType"];
|
4976 |
+
|
4977 |
+
// http://www.w3.org/TR/PNG/#11IHDR
|
4978 |
+
// 3 => indexed
|
4979 |
+
// 4 => greyscale with alpha
|
4980 |
+
// 6 => fullcolor with alpha
|
4981 |
+
$is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4);
|
4982 |
+
|
4983 |
+
if ($is_alpha) { // exclude grayscale alpha
|
4984 |
+
$this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
|
4985 |
+
return;
|
4986 |
+
}
|
4987 |
+
|
4988 |
+
//png files typically contain an alpha channel.
|
4989 |
+
//pdf file format or class.pdf does not support alpha blending.
|
4990 |
+
//on alpha blended images, more transparent areas have a color near black.
|
4991 |
+
//This appears in the result on not storing the alpha channel.
|
4992 |
+
//Correct would be the box background image or its parent when transparent.
|
4993 |
+
//But this would make the image dependent on the background.
|
4994 |
+
//Therefore create an image with white background and copy in
|
4995 |
+
//A more natural background than black is white.
|
4996 |
+
//Therefore create an empty image with white background and merge the
|
4997 |
+
//image in with alpha blending.
|
4998 |
+
$imgtmp = @imagecreatefrompng($file);
|
4999 |
+
if (!$imgtmp) {
|
5000 |
+
return;
|
5001 |
+
}
|
5002 |
+
$sx = imagesx($imgtmp);
|
5003 |
+
$sy = imagesy($imgtmp);
|
5004 |
+
$img = imagecreatetruecolor($sx, $sy);
|
5005 |
+
imagealphablending($img, true);
|
5006 |
+
|
5007 |
+
// @todo is it still needed ??
|
5008 |
+
$ti = imagecolortransparent($imgtmp);
|
5009 |
+
if ($ti >= 0) {
|
5010 |
+
$tc = imagecolorsforindex($imgtmp, $ti);
|
5011 |
+
$ti = imagecolorallocate($img, $tc['red'], $tc['green'], $tc['blue']);
|
5012 |
+
imagefill($img, 0, 0, $ti);
|
5013 |
+
imagecolortransparent($img, $ti);
|
5014 |
+
} else {
|
5015 |
+
imagefill($img, 1, 1, imagecolorallocate($img, 255, 255, 255));
|
5016 |
+
}
|
5017 |
+
|
5018 |
+
imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy);
|
5019 |
+
imagedestroy($imgtmp);
|
5020 |
+
}
|
5021 |
+
$this->addImagePng($file, $x, $y, $w, $h, $img);
|
5022 |
+
|
5023 |
+
if ($img) {
|
5024 |
+
imagedestroy($img);
|
5025 |
+
}
|
5026 |
+
}
|
5027 |
+
|
5028 |
+
/**
|
5029 |
+
* add a PNG image into the document, from a file
|
5030 |
+
* this should work with remote files
|
5031 |
+
*
|
5032 |
+
* @param $file
|
5033 |
+
* @param $x
|
5034 |
+
* @param $y
|
5035 |
+
* @param int $w
|
5036 |
+
* @param int $h
|
5037 |
+
*/
|
5038 |
+
function addSvgFromFile($file, $x, $y, $w = 0, $h = 0)
|
5039 |
+
{
|
5040 |
+
$doc = new \Svg\Document();
|
5041 |
+
$doc->loadFile($file);
|
5042 |
+
$dimensions = $doc->getDimensions();
|
5043 |
+
|
5044 |
+
$this->save();
|
5045 |
+
|
5046 |
+
$this->transform(array($w / $dimensions["width"], 0, 0, $h / $dimensions["height"], $x, $y));
|
5047 |
+
|
5048 |
+
$surface = new \Svg\Surface\SurfaceCpdf($doc, $this);
|
5049 |
+
$doc->render($surface);
|
5050 |
+
|
5051 |
+
$this->restore();
|
5052 |
+
}
|
5053 |
+
|
5054 |
+
/**
|
5055 |
+
* add a PNG image into the document, from a memory buffer of the file
|
5056 |
+
*
|
5057 |
+
* @param $file
|
5058 |
+
* @param $x
|
5059 |
+
* @param $y
|
5060 |
+
* @param float $w
|
5061 |
+
* @param float $h
|
5062 |
+
* @param $data
|
5063 |
+
* @param bool $is_mask
|
5064 |
+
* @param null $mask
|
5065 |
+
*/
|
5066 |
+
function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null)
|
5067 |
+
{
|
5068 |
+
if (isset($this->imagelist[$file])) {
|
5069 |
+
$data = null;
|
5070 |
+
$info['width'] = $this->imagelist[$file]['w'];
|
5071 |
+
$info['height'] = $this->imagelist[$file]['h'];
|
5072 |
+
$label = $this->imagelist[$file]['label'];
|
5073 |
+
} else {
|
5074 |
+
if ($data == null) {
|
5075 |
+
$this->addMessage('addPngFromBuf error - data not present!');
|
5076 |
+
|
5077 |
+
return;
|
5078 |
+
}
|
5079 |
+
|
5080 |
+
$error = 0;
|
5081 |
+
|
5082 |
+
if (!$error) {
|
5083 |
+
$header = chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10);
|
5084 |
+
|
5085 |
+
if (mb_substr($data, 0, 8, '8bit') != $header) {
|
5086 |
+
$error = 1;
|
5087 |
+
|
5088 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5089 |
+
print '[addPngFromFile this file does not have a valid header ' . $file . ']';
|
5090 |
+
}
|
5091 |
+
|
5092 |
+
$errormsg = 'this file does not have a valid header';
|
5093 |
+
}
|
5094 |
+
}
|
5095 |
+
|
5096 |
+
if (!$error) {
|
5097 |
+
// set pointer
|
5098 |
+
$p = 8;
|
5099 |
+
$len = mb_strlen($data, '8bit');
|
5100 |
+
|
5101 |
+
// cycle through the file, identifying chunks
|
5102 |
+
$haveHeader = 0;
|
5103 |
+
$info = array();
|
5104 |
+
$idata = '';
|
5105 |
+
$pdata = '';
|
5106 |
+
|
5107 |
+
while ($p < $len) {
|
5108 |
+
$chunkLen = $this->getBytes($data, $p, 4);
|
5109 |
+
$chunkType = mb_substr($data, $p + 4, 4, '8bit');
|
5110 |
+
|
5111 |
+
switch ($chunkType) {
|
5112 |
+
case 'IHDR':
|
5113 |
+
// this is where all the file information comes from
|
5114 |
+
$info['width'] = $this->getBytes($data, $p + 8, 4);
|
5115 |
+
$info['height'] = $this->getBytes($data, $p + 12, 4);
|
5116 |
+
$info['bitDepth'] = ord($data[$p + 16]);
|
5117 |
+
$info['colorType'] = ord($data[$p + 17]);
|
5118 |
+
$info['compressionMethod'] = ord($data[$p + 18]);
|
5119 |
+
$info['filterMethod'] = ord($data[$p + 19]);
|
5120 |
+
$info['interlaceMethod'] = ord($data[$p + 20]);
|
5121 |
+
|
5122 |
+
//print_r($info);
|
5123 |
+
$haveHeader = 1;
|
5124 |
+
if ($info['compressionMethod'] != 0) {
|
5125 |
+
$error = 1;
|
5126 |
+
|
5127 |
+
//debugpng
|
5128 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5129 |
+
print '[addPngFromFile unsupported compression method ' . $file . ']';
|
5130 |
+
}
|
5131 |
+
|
5132 |
+
$errormsg = 'unsupported compression method';
|
5133 |
+
}
|
5134 |
+
|
5135 |
+
if ($info['filterMethod'] != 0) {
|
5136 |
+
$error = 1;
|
5137 |
+
|
5138 |
+
//debugpng
|
5139 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5140 |
+
print '[addPngFromFile unsupported filter method ' . $file . ']';
|
5141 |
+
}
|
5142 |
+
|
5143 |
+
$errormsg = 'unsupported filter method';
|
5144 |
+
}
|
5145 |
+
break;
|
5146 |
+
|
5147 |
+
case 'PLTE':
|
5148 |
+
$pdata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
|
5149 |
+
break;
|
5150 |
+
|
5151 |
+
case 'IDAT':
|
5152 |
+
$idata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
|
5153 |
+
break;
|
5154 |
+
|
5155 |
+
case 'tRNS':
|
5156 |
+
//this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
|
5157 |
+
//print "tRNS found, color type = ".$info['colorType']."\n";
|
5158 |
+
$transparency = array();
|
5159 |
+
|
5160 |
+
switch ($info['colorType']) {
|
5161 |
+
// indexed color, rbg
|
5162 |
+
case 3:
|
5163 |
+
/* corresponding to entries in the plte chunk
|
5164 |
+
Alpha for palette index 0: 1 byte
|
5165 |
+
Alpha for palette index 1: 1 byte
|
5166 |
+
...etc...
|
5167 |
+
*/
|
5168 |
+
// there will be one entry for each palette entry. up until the last non-opaque entry.
|
5169 |
+
// set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
|
5170 |
+
$transparency['type'] = 'indexed';
|
5171 |
+
$trans = 0;
|
5172 |
+
|
5173 |
+
for ($i = $chunkLen; $i >= 0; $i--) {
|
5174 |
+
if (ord($data[$p + 8 + $i]) == 0) {
|
5175 |
+
$trans = $i;
|
5176 |
+
}
|
5177 |
+
}
|
5178 |
+
|
5179 |
+
$transparency['data'] = $trans;
|
5180 |
+
break;
|
5181 |
+
|
5182 |
+
// grayscale
|
5183 |
+
case 0:
|
5184 |
+
/* corresponding to entries in the plte chunk
|
5185 |
+
Gray: 2 bytes, range 0 .. (2^bitdepth)-1
|
5186 |
+
*/
|
5187 |
+
// $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale
|
5188 |
+
$transparency['type'] = 'indexed';
|
5189 |
+
$transparency['data'] = ord($data[$p + 8 + 1]);
|
5190 |
+
break;
|
5191 |
+
|
5192 |
+
// truecolor
|
5193 |
+
case 2:
|
5194 |
+
/* corresponding to entries in the plte chunk
|
5195 |
+
Red: 2 bytes, range 0 .. (2^bitdepth)-1
|
5196 |
+
Green: 2 bytes, range 0 .. (2^bitdepth)-1
|
5197 |
+
Blue: 2 bytes, range 0 .. (2^bitdepth)-1
|
5198 |
+
*/
|
5199 |
+
$transparency['r'] = $this->getBytes($data, $p + 8, 2);
|
5200 |
+
// r from truecolor
|
5201 |
+
$transparency['g'] = $this->getBytes($data, $p + 10, 2);
|
5202 |
+
// g from truecolor
|
5203 |
+
$transparency['b'] = $this->getBytes($data, $p + 12, 2);
|
5204 |
+
// b from truecolor
|
5205 |
+
|
5206 |
+
$transparency['type'] = 'color-key';
|
5207 |
+
break;
|
5208 |
+
|
5209 |
+
//unsupported transparency type
|
5210 |
+
default:
|
5211 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5212 |
+
print '[addPngFromFile unsupported transparency type ' . $file . ']';
|
5213 |
+
}
|
5214 |
+
break;
|
5215 |
+
}
|
5216 |
+
|
5217 |
+
// KS End new code
|
5218 |
+
break;
|
5219 |
+
|
5220 |
+
default:
|
5221 |
+
break;
|
5222 |
+
}
|
5223 |
+
|
5224 |
+
$p += $chunkLen + 12;
|
5225 |
+
}
|
5226 |
+
|
5227 |
+
if (!$haveHeader) {
|
5228 |
+
$error = 1;
|
5229 |
+
|
5230 |
+
//debugpng
|
5231 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5232 |
+
print '[addPngFromFile information header is missing ' . $file . ']';
|
5233 |
+
}
|
5234 |
+
|
5235 |
+
$errormsg = 'information header is missing';
|
5236 |
+
}
|
5237 |
+
|
5238 |
+
if (isset($info['interlaceMethod']) && $info['interlaceMethod']) {
|
5239 |
+
$error = 1;
|
5240 |
+
|
5241 |
+
//debugpng
|
5242 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5243 |
+
print '[addPngFromFile no support for interlaced images in pdf ' . $file . ']';
|
5244 |
+
}
|
5245 |
+
|
5246 |
+
$errormsg = 'There appears to be no support for interlaced images in pdf.';
|
5247 |
+
}
|
5248 |
+
}
|
5249 |
+
|
5250 |
+
if (!$error && $info['bitDepth'] > 8) {
|
5251 |
+
$error = 1;
|
5252 |
+
|
5253 |
+
//debugpng
|
5254 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5255 |
+
print '[addPngFromFile bit depth of 8 or less is supported ' . $file . ']';
|
5256 |
+
}
|
5257 |
+
|
5258 |
+
$errormsg = 'only bit depth of 8 or less is supported';
|
5259 |
+
}
|
5260 |
+
|
5261 |
+
if (!$error) {
|
5262 |
+
switch ($info['colorType']) {
|
5263 |
+
case 3:
|
5264 |
+
$color = 'DeviceRGB';
|
5265 |
+
$ncolor = 1;
|
5266 |
+
break;
|
5267 |
+
|
5268 |
+
case 2:
|
5269 |
+
$color = 'DeviceRGB';
|
5270 |
+
$ncolor = 3;
|
5271 |
+
break;
|
5272 |
+
|
5273 |
+
case 0:
|
5274 |
+
$color = 'DeviceGray';
|
5275 |
+
$ncolor = 1;
|
5276 |
+
break;
|
5277 |
+
|
5278 |
+
default:
|
5279 |
+
$error = 1;
|
5280 |
+
|
5281 |
+
//debugpng
|
5282 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5283 |
+
print '[addPngFromFile alpha channel not supported: ' . $info['colorType'] . ' ' . $file . ']';
|
5284 |
+
}
|
5285 |
+
|
5286 |
+
$errormsg = 'transparency alpha channel not supported, transparency only supported for palette images.';
|
5287 |
+
}
|
5288 |
+
}
|
5289 |
+
|
5290 |
+
if ($error) {
|
5291 |
+
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
|
5292 |
+
|
5293 |
+
return;
|
5294 |
+
}
|
5295 |
+
|
5296 |
+
//print_r($info);
|
5297 |
+
// so this image is ok... add it in.
|
5298 |
+
$this->numImages++;
|
5299 |
+
$im = $this->numImages;
|
5300 |
+
$label = "I$im";
|
5301 |
+
$this->numObj++;
|
5302 |
+
|
5303 |
+
// $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
|
5304 |
+
$options = array(
|
5305 |
+
'label' => $label,
|
5306 |
+
'data' => $idata,
|
5307 |
+
'bitsPerComponent' => $info['bitDepth'],
|
5308 |
+
'pdata' => $pdata,
|
5309 |
+
'iw' => $info['width'],
|
5310 |
+
'ih' => $info['height'],
|
5311 |
+
'type' => 'png',
|
5312 |
+
'color' => $color,
|
5313 |
+
'ncolor' => $ncolor,
|
5314 |
+
'masked' => $mask,
|
5315 |
+
'isMask' => $is_mask
|
5316 |
+
);
|
5317 |
+
|
5318 |
+
if (isset($transparency)) {
|
5319 |
+
$options['transparency'] = $transparency;
|
5320 |
+
}
|
5321 |
+
|
5322 |
+
$this->o_image($this->numObj, 'new', $options);
|
5323 |
+
$this->imagelist[$file] = array('label' => $label, 'w' => $info['width'], 'h' => $info['height']);
|
5324 |
+
}
|
5325 |
+
|
5326 |
+
if ($is_mask) {
|
5327 |
+
return;
|
5328 |
+
}
|
5329 |
+
|
5330 |
+
if ($w <= 0 && $h <= 0) {
|
5331 |
+
$w = $info['width'];
|
5332 |
+
$h = $info['height'];
|
5333 |
+
}
|
5334 |
+
|
5335 |
+
if ($w <= 0) {
|
5336 |
+
$w = $h / $info['height'] * $info['width'];
|
5337 |
+
}
|
5338 |
+
|
5339 |
+
if ($h <= 0) {
|
5340 |
+
$h = $w * $info['height'] / $info['width'];
|
5341 |
+
}
|
5342 |
+
|
5343 |
+
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ", $w, $h, $x, $y, $label));
|
5344 |
+
}
|
5345 |
+
|
5346 |
+
/**
|
5347 |
+
* add a JPEG image into the document, from a file
|
5348 |
+
*
|
5349 |
+
* @param $img
|
5350 |
+
* @param $x
|
5351 |
+
* @param $y
|
5352 |
+
* @param int $w
|
5353 |
+
* @param int $h
|
5354 |
+
*/
|
5355 |
+
function addJpegFromFile($img, $x, $y, $w = 0, $h = 0)
|
5356 |
+
{
|
5357 |
+
// attempt to add a jpeg image straight from a file, using no GD commands
|
5358 |
+
// note that this function is unable to operate on a remote file.
|
5359 |
+
|
5360 |
+
if (!file_exists($img)) {
|
5361 |
+
return;
|
5362 |
+
}
|
5363 |
+
|
5364 |
+
if ($this->image_iscached($img)) {
|
5365 |
+
$data = null;
|
5366 |
+
$imageWidth = $this->imagelist[$img]['w'];
|
5367 |
+
$imageHeight = $this->imagelist[$img]['h'];
|
5368 |
+
$channels = $this->imagelist[$img]['c'];
|
5369 |
+
} else {
|
5370 |
+
$tmp = getimagesize($img);
|
5371 |
+
$imageWidth = $tmp[0];
|
5372 |
+
$imageHeight = $tmp[1];
|
5373 |
+
|
5374 |
+
if (isset($tmp['channels'])) {
|
5375 |
+
$channels = $tmp['channels'];
|
5376 |
+
} else {
|
5377 |
+
$channels = 3;
|
5378 |
+
}
|
5379 |
+
|
5380 |
+
$data = file_get_contents($img);
|
5381 |
+
}
|
5382 |
+
|
5383 |
+
if ($w <= 0 && $h <= 0) {
|
5384 |
+
$w = $imageWidth;
|
5385 |
+
}
|
5386 |
+
|
5387 |
+
if ($w == 0) {
|
5388 |
+
$w = $h / $imageHeight * $imageWidth;
|
5389 |
+
}
|
5390 |
+
|
5391 |
+
if ($h == 0) {
|
5392 |
+
$h = $w * $imageHeight / $imageWidth;
|
5393 |
+
}
|
5394 |
+
|
5395 |
+
$this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img);
|
5396 |
+
}
|
5397 |
+
|
5398 |
+
/**
|
5399 |
+
* common code used by the two JPEG adding functions
|
5400 |
+
* @param $data
|
5401 |
+
* @param $x
|
5402 |
+
* @param $y
|
5403 |
+
* @param int $w
|
5404 |
+
* @param int $h
|
5405 |
+
* @param $imageWidth
|
5406 |
+
* @param $imageHeight
|
5407 |
+
* @param int $channels
|
5408 |
+
* @param $imgname
|
5409 |
+
*/
|
5410 |
+
private function addJpegImage_common(
|
5411 |
+
&$data,
|
5412 |
+
$x,
|
5413 |
+
$y,
|
5414 |
+
$w = 0,
|
5415 |
+
$h = 0,
|
5416 |
+
$imageWidth,
|
5417 |
+
$imageHeight,
|
5418 |
+
$channels = 3,
|
5419 |
+
$imgname
|
5420 |
+
) {
|
5421 |
+
if ($this->image_iscached($imgname)) {
|
5422 |
+
$label = $this->imagelist[$imgname]['label'];
|
5423 |
+
//debugpng
|
5424 |
+
//if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']';
|
5425 |
+
|
5426 |
+
} else {
|
5427 |
+
if ($data == null) {
|
5428 |
+
$this->addMessage('addJpegImage_common error - (' . $imgname . ') data not present!');
|
5429 |
+
|
5430 |
+
return;
|
5431 |
+
}
|
5432 |
+
|
5433 |
+
// note that this function is not to be called externally
|
5434 |
+
// it is just the common code between the GD and the file options
|
5435 |
+
$this->numImages++;
|
5436 |
+
$im = $this->numImages;
|
5437 |
+
$label = "I$im";
|
5438 |
+
$this->numObj++;
|
5439 |
+
|
5440 |
+
$this->o_image(
|
5441 |
+
$this->numObj,
|
5442 |
+
'new',
|
5443 |
+
array(
|
5444 |
+
'label' => $label,
|
5445 |
+
'data' => &$data,
|
5446 |
+
'iw' => $imageWidth,
|
5447 |
+
'ih' => $imageHeight,
|
5448 |
+
'channels' => $channels
|
5449 |
+
)
|
5450 |
+
);
|
5451 |
+
|
5452 |
+
$this->imagelist[$imgname] = array(
|
5453 |
+
'label' => $label,
|
5454 |
+
'w' => $imageWidth,
|
5455 |
+
'h' => $imageHeight,
|
5456 |
+
'c' => $channels
|
5457 |
+
);
|
5458 |
+
}
|
5459 |
+
|
5460 |
+
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
|
5461 |
+
}
|
5462 |
+
|
5463 |
+
/**
|
5464 |
+
* specify where the document should open when it first starts
|
5465 |
+
*
|
5466 |
+
* @param $style
|
5467 |
+
* @param int $a
|
5468 |
+
* @param int $b
|
5469 |
+
* @param int $c
|
5470 |
+
*/
|
5471 |
+
function openHere($style, $a = 0, $b = 0, $c = 0)
|
5472 |
+
{
|
5473 |
+
// this function will open the document at a specified page, in a specified style
|
5474 |
+
// the values for style, and the required parameters are:
|
5475 |
+
// 'XYZ' left, top, zoom
|
5476 |
+
// 'Fit'
|
5477 |
+
// 'FitH' top
|
5478 |
+
// 'FitV' left
|
5479 |
+
// 'FitR' left,bottom,right
|
5480 |
+
// 'FitB'
|
5481 |
+
// 'FitBH' top
|
5482 |
+
// 'FitBV' left
|
5483 |
+
$this->numObj++;
|
5484 |
+
$this->o_destination(
|
5485 |
+
$this->numObj,
|
5486 |
+
'new',
|
5487 |
+
array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
|
5488 |
+
);
|
5489 |
+
$id = $this->catalogId;
|
5490 |
+
$this->o_catalog($id, 'openHere', $this->numObj);
|
5491 |
+
}
|
5492 |
+
|
5493 |
+
/**
|
5494 |
+
* Add JavaScript code to the PDF document
|
5495 |
+
*
|
5496 |
+
* @param string $code
|
5497 |
+
*/
|
5498 |
+
function addJavascript($code)
|
5499 |
+
{
|
5500 |
+
$this->javascript .= $code;
|
5501 |
+
}
|
5502 |
+
|
5503 |
+
/**
|
5504 |
+
* create a labelled destination within the document
|
5505 |
+
*
|
5506 |
+
* @param $label
|
5507 |
+
* @param $style
|
5508 |
+
* @param int $a
|
5509 |
+
* @param int $b
|
5510 |
+
* @param int $c
|
5511 |
+
*/
|
5512 |
+
function addDestination($label, $style, $a = 0, $b = 0, $c = 0)
|
5513 |
+
{
|
5514 |
+
// associates the given label with the destination, it is done this way so that a destination can be specified after
|
5515 |
+
// it has been linked to
|
5516 |
+
// styles are the same as the 'openHere' function
|
5517 |
+
$this->numObj++;
|
5518 |
+
$this->o_destination(
|
5519 |
+
$this->numObj,
|
5520 |
+
'new',
|
5521 |
+
array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
|
5522 |
+
);
|
5523 |
+
$id = $this->numObj;
|
5524 |
+
|
5525 |
+
// store the label->idf relationship, note that this means that labels can be used only once
|
5526 |
+
$this->destinations["$label"] = $id;
|
5527 |
+
}
|
5528 |
+
|
5529 |
+
/**
|
5530 |
+
* define font families, this is used to initialize the font families for the default fonts
|
5531 |
+
* and for the user to add new ones for their fonts. The default bahavious can be overridden should
|
5532 |
+
* that be desired.
|
5533 |
+
*
|
5534 |
+
* @param $family
|
5535 |
+
* @param string $options
|
5536 |
+
*/
|
5537 |
+
function setFontFamily($family, $options = '')
|
5538 |
+
{
|
5539 |
+
if (!is_array($options)) {
|
5540 |
+
if ($family === 'init') {
|
5541 |
+
// set the known family groups
|
5542 |
+
// these font families will be used to enable bold and italic markers to be included
|
5543 |
+
// within text streams. html forms will be used... <b></b> <i></i>
|
5544 |
+
$this->fontFamilies['Helvetica.afm'] =
|
5545 |
+
array(
|
5546 |
+
'b' => 'Helvetica-Bold.afm',
|
5547 |
+
'i' => 'Helvetica-Oblique.afm',
|
5548 |
+
'bi' => 'Helvetica-BoldOblique.afm',
|
5549 |
+
'ib' => 'Helvetica-BoldOblique.afm'
|
5550 |
+
);
|
5551 |
+
|
5552 |
+
$this->fontFamilies['Courier.afm'] =
|
5553 |
+
array(
|
5554 |
+
'b' => 'Courier-Bold.afm',
|
5555 |
+
'i' => 'Courier-Oblique.afm',
|
5556 |
+
'bi' => 'Courier-BoldOblique.afm',
|
5557 |
+
'ib' => 'Courier-BoldOblique.afm'
|
5558 |
+
);
|
5559 |
+
|
5560 |
+
$this->fontFamilies['Times-Roman.afm'] =
|
5561 |
+
array(
|
5562 |
+
'b' => 'Times-Bold.afm',
|
5563 |
+
'i' => 'Times-Italic.afm',
|
5564 |
+
'bi' => 'Times-BoldItalic.afm',
|
5565 |
+
'ib' => 'Times-BoldItalic.afm'
|
5566 |
+
);
|
5567 |
+
}
|
5568 |
+
} else {
|
5569 |
+
|
5570 |
+
// the user is trying to set a font family
|
5571 |
+
// note that this can also be used to set the base ones to something else
|
5572 |
+
if (mb_strlen($family)) {
|
5573 |
+
$this->fontFamilies[$family] = $options;
|
5574 |
+
}
|
5575 |
+
}
|
5576 |
+
}
|
5577 |
+
|
5578 |
+
/**
|
5579 |
+
* used to add messages for use in debugging
|
5580 |
+
*
|
5581 |
+
* @param $message
|
5582 |
+
*/
|
5583 |
+
function addMessage($message)
|
5584 |
+
{
|
5585 |
+
$this->messages .= $message . "\n";
|
5586 |
+
}
|
5587 |
+
|
5588 |
+
/**
|
5589 |
+
* a few functions which should allow the document to be treated transactionally.
|
5590 |
+
*
|
5591 |
+
* @param $action
|
5592 |
+
*/
|
5593 |
+
function transaction($action)
|
5594 |
+
{
|
5595 |
+
switch ($action) {
|
5596 |
+
case 'start':
|
5597 |
+
// store all the data away into the checkpoint variable
|
5598 |
+
$data = get_object_vars($this);
|
5599 |
+
$this->checkpoint = $data;
|
5600 |
+
unset($data);
|
5601 |
+
break;
|
5602 |
+
|
5603 |
+
case 'commit':
|
5604 |
+
if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) {
|
5605 |
+
$tmp = $this->checkpoint['checkpoint'];
|
5606 |
+
$this->checkpoint = $tmp;
|
5607 |
+
unset($tmp);
|
5608 |
+
} else {
|
5609 |
+
$this->checkpoint = '';
|
5610 |
+
}
|
5611 |
+
break;
|
5612 |
+
|
5613 |
+
case 'rewind':
|
5614 |
+
// do not destroy the current checkpoint, but move us back to the state then, so that we can try again
|
5615 |
+
if (is_array($this->checkpoint)) {
|
5616 |
+
// can only abort if were inside a checkpoint
|
5617 |
+
$tmp = $this->checkpoint;
|
5618 |
+
|
5619 |
+
foreach ($tmp as $k => $v) {
|
5620 |
+
if ($k !== 'checkpoint') {
|
5621 |
+
$this->$k = $v;
|
5622 |
+
}
|
5623 |
+
}
|
5624 |
+
unset($tmp);
|
5625 |
+
}
|
5626 |
+
break;
|
5627 |
+
|
5628 |
+
case 'abort':
|
5629 |
+
if (is_array($this->checkpoint)) {
|
5630 |
+
// can only abort if were inside a checkpoint
|
5631 |
+
$tmp = $this->checkpoint;
|
5632 |
+
foreach ($tmp as $k => $v) {
|
5633 |
+
$this->$k = $v;
|
5634 |
+
}
|
5635 |
+
unset($tmp);
|
5636 |
+
}
|
5637 |
+
break;
|
5638 |
+
}
|
5639 |
+
}
|
5640 |
+
}
|
vendor/dompdf/dompdf/lib/html5lib/Data.php
CHANGED
@@ -1,114 +1,123 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// warning: this file is encoded in UTF-8!
|
4 |
-
|
5 |
-
class HTML5_Data
|
6 |
-
{
|
7 |
-
|
8 |
-
// at some point this should be moved to a .ser file. Another
|
9 |
-
// possible optimization is to give UTF-8 bytes, not Unicode
|
10 |
-
// codepoints
|
11 |
-
// XXX: Not quite sure why it's named this; this is
|
12 |
-
// actually the numeric entity dereference table.
|
13 |
-
protected static $realCodepointTable = array(
|
14 |
-
0x00 => 0xFFFD, // REPLACEMENT CHARACTER
|
15 |
-
0x0D => 0x000A, // LINE FEED (LF)
|
16 |
-
0x80 => 0x20AC, // EURO SIGN ('€')
|
17 |
-
0x81 => 0x0081, // <control>
|
18 |
-
0x82 => 0x201A, // SINGLE LOW-9 QUOTATION MARK ('‚')
|
19 |
-
0x83 => 0x0192, // LATIN SMALL LETTER F WITH HOOK ('ƒ')
|
20 |
-
0x84 => 0x201E, // DOUBLE LOW-9 QUOTATION MARK ('„')
|
21 |
-
0x85 => 0x2026, // HORIZONTAL ELLIPSIS ('…')
|
22 |
-
0x86 => 0x2020, // DAGGER ('†')
|
23 |
-
0x87 => 0x2021, // DOUBLE DAGGER ('‡')
|
24 |
-
0x88 => 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT ('ˆ')
|
25 |
-
0x89 => 0x2030, // PER MILLE SIGN ('‰')
|
26 |
-
0x8A => 0x0160, // LATIN CAPITAL LETTER S WITH CARON ('Š')
|
27 |
-
0x8B => 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK ('‹')
|
28 |
-
0x8C => 0x0152, // LATIN CAPITAL LIGATURE OE ('Œ')
|
29 |
-
0x8D => 0x008D, // <control>
|
30 |
-
0x8E => 0x017D, // LATIN CAPITAL LETTER Z WITH CARON ('Ž')
|
31 |
-
0x8F => 0x008F, // <control>
|
32 |
-
0x90 => 0x0090, // <control>
|
33 |
-
0x91 => 0x2018, // LEFT SINGLE QUOTATION MARK ('‘')
|
34 |
-
0x92 => 0x2019, // RIGHT SINGLE QUOTATION MARK ('’')
|
35 |
-
0x93 => 0x201C, // LEFT DOUBLE QUOTATION MARK ('“')
|
36 |
-
0x94 => 0x201D, // RIGHT DOUBLE QUOTATION MARK ('”')
|
37 |
-
0x95 => 0x2022, // BULLET ('•')
|
38 |
-
0x96 => 0x2013, // EN DASH ('–')
|
39 |
-
0x97 => 0x2014, // EM DASH ('—')
|
40 |
-
0x98 => 0x02DC, // SMALL TILDE ('˜')
|
41 |
-
0x99 => 0x2122, // TRADE MARK SIGN ('™')
|
42 |
-
0x9A => 0x0161, // LATIN SMALL LETTER S WITH CARON ('š')
|
43 |
-
0x9B => 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK ('›')
|
44 |
-
0x9C => 0x0153, // LATIN SMALL LIGATURE OE ('œ')
|
45 |
-
0x9D => 0x009D, // <control>
|
46 |
-
0x9E => 0x017E, // LATIN SMALL LETTER Z WITH CARON ('ž')
|
47 |
-
0x9F => 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS ('Ÿ')
|
48 |
-
);
|
49 |
-
|
50 |
-
protected static $namedCharacterReferences;
|
51 |
-
|
52 |
-
protected static $namedCharacterReferenceMaxLength;
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Returns the "real" Unicode codepoint of a malformed character
|
56 |
-
* reference.
|
57 |
-
*/
|
58 |
-
public static function getRealCodepoint($ref) {
|
59 |
-
if (!isset(self::$realCodepointTable[$ref]))
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
//
|
91 |
-
$x =
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
$
|
101 |
-
}
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// warning: this file is encoded in UTF-8!
|
4 |
+
|
5 |
+
class HTML5_Data
|
6 |
+
{
|
7 |
+
|
8 |
+
// at some point this should be moved to a .ser file. Another
|
9 |
+
// possible optimization is to give UTF-8 bytes, not Unicode
|
10 |
+
// codepoints
|
11 |
+
// XXX: Not quite sure why it's named this; this is
|
12 |
+
// actually the numeric entity dereference table.
|
13 |
+
protected static $realCodepointTable = array(
|
14 |
+
0x00 => 0xFFFD, // REPLACEMENT CHARACTER
|
15 |
+
0x0D => 0x000A, // LINE FEED (LF)
|
16 |
+
0x80 => 0x20AC, // EURO SIGN ('€')
|
17 |
+
0x81 => 0x0081, // <control>
|
18 |
+
0x82 => 0x201A, // SINGLE LOW-9 QUOTATION MARK ('‚')
|
19 |
+
0x83 => 0x0192, // LATIN SMALL LETTER F WITH HOOK ('ƒ')
|
20 |
+
0x84 => 0x201E, // DOUBLE LOW-9 QUOTATION MARK ('„')
|
21 |
+
0x85 => 0x2026, // HORIZONTAL ELLIPSIS ('…')
|
22 |
+
0x86 => 0x2020, // DAGGER ('†')
|
23 |
+
0x87 => 0x2021, // DOUBLE DAGGER ('‡')
|
24 |
+
0x88 => 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT ('ˆ')
|
25 |
+
0x89 => 0x2030, // PER MILLE SIGN ('‰')
|
26 |
+
0x8A => 0x0160, // LATIN CAPITAL LETTER S WITH CARON ('Š')
|
27 |
+
0x8B => 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK ('‹')
|
28 |
+
0x8C => 0x0152, // LATIN CAPITAL LIGATURE OE ('Œ')
|
29 |
+
0x8D => 0x008D, // <control>
|
30 |
+
0x8E => 0x017D, // LATIN CAPITAL LETTER Z WITH CARON ('Ž')
|
31 |
+
0x8F => 0x008F, // <control>
|
32 |
+
0x90 => 0x0090, // <control>
|
33 |
+
0x91 => 0x2018, // LEFT SINGLE QUOTATION MARK ('‘')
|
34 |
+
0x92 => 0x2019, // RIGHT SINGLE QUOTATION MARK ('’')
|
35 |
+
0x93 => 0x201C, // LEFT DOUBLE QUOTATION MARK ('“')
|
36 |
+
0x94 => 0x201D, // RIGHT DOUBLE QUOTATION MARK ('”')
|
37 |
+
0x95 => 0x2022, // BULLET ('•')
|
38 |
+
0x96 => 0x2013, // EN DASH ('–')
|
39 |
+
0x97 => 0x2014, // EM DASH ('—')
|
40 |
+
0x98 => 0x02DC, // SMALL TILDE ('˜')
|
41 |
+
0x99 => 0x2122, // TRADE MARK SIGN ('™')
|
42 |
+
0x9A => 0x0161, // LATIN SMALL LETTER S WITH CARON ('š')
|
43 |
+
0x9B => 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK ('›')
|
44 |
+
0x9C => 0x0153, // LATIN SMALL LIGATURE OE ('œ')
|
45 |
+
0x9D => 0x009D, // <control>
|
46 |
+
0x9E => 0x017E, // LATIN SMALL LETTER Z WITH CARON ('ž')
|
47 |
+
0x9F => 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS ('Ÿ')
|
48 |
+
);
|
49 |
+
|
50 |
+
protected static $namedCharacterReferences;
|
51 |
+
|
52 |
+
protected static $namedCharacterReferenceMaxLength;
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Returns the "real" Unicode codepoint of a malformed character
|
56 |
+
* reference.
|
57 |
+
*/
|
58 |
+
public static function getRealCodepoint($ref) {
|
59 |
+
if (!isset(self::$realCodepointTable[$ref])) {
|
60 |
+
return false;
|
61 |
+
} else {
|
62 |
+
return self::$realCodepointTable[$ref];
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
public static function getNamedCharacterReferences() {
|
67 |
+
if (!self::$namedCharacterReferences) {
|
68 |
+
self::$namedCharacterReferences = unserialize(
|
69 |
+
file_get_contents(dirname(__FILE__) . '/named-character-references.ser'));
|
70 |
+
}
|
71 |
+
return self::$namedCharacterReferences;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Converts a Unicode codepoint to sequence of UTF-8 bytes.
|
76 |
+
* @note Shamelessly stolen from HTML Purifier, which is also
|
77 |
+
* shamelessly stolen from Feyd (which is in public domain).
|
78 |
+
*/
|
79 |
+
public static function utf8chr($code) {
|
80 |
+
/* We don't care: we live dangerously
|
81 |
+
* if($code > 0x10FFFF or $code < 0x0 or
|
82 |
+
($code >= 0xD800 and $code <= 0xDFFF) ) {
|
83 |
+
// bits are set outside the "valid" range as defined
|
84 |
+
// by UNICODE 4.1.0
|
85 |
+
return "\xEF\xBF\xBD";
|
86 |
+
}*/
|
87 |
+
|
88 |
+
$y = $z = $w = 0;
|
89 |
+
if ($code < 0x80) {
|
90 |
+
// regular ASCII character
|
91 |
+
$x = $code;
|
92 |
+
} else {
|
93 |
+
// set up bits for UTF-8
|
94 |
+
$x = ($code & 0x3F) | 0x80;
|
95 |
+
if ($code < 0x800) {
|
96 |
+
$y = (($code & 0x7FF) >> 6) | 0xC0;
|
97 |
+
} else {
|
98 |
+
$y = (($code & 0xFC0) >> 6) | 0x80;
|
99 |
+
if ($code < 0x10000) {
|
100 |
+
$z = (($code >> 12) & 0x0F) | 0xE0;
|
101 |
+
} else {
|
102 |
+
$z = (($code >> 12) & 0x3F) | 0x80;
|
103 |
+
$w = (($code >> 18) & 0x07) | 0xF0;
|
104 |
+
}
|
105 |
+
}
|
106 |
+
}
|
107 |
+
// set up the actual character
|
108 |
+
$ret = '';
|
109 |
+
if ($w) {
|
110 |
+
$ret .= chr($w);
|
111 |
+
}
|
112 |
+
if ($z) {
|
113 |
+
$ret .= chr($z);
|
114 |
+
}
|
115 |
+
if ($y) {
|
116 |
+
$ret .= chr($y);
|
117 |
+
}
|
118 |
+
$ret .= chr($x);
|
119 |
+
|
120 |
+
return $ret;
|
121 |
+
}
|
122 |
+
|
123 |
+
}
|
vendor/dompdf/dompdf/lib/html5lib/Tokenizer.php
CHANGED
@@ -1,2468 +1,2470 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/*
|
4 |
-
|
5 |
-
Copyright 2007 Jeroen van der Meer <http://jero.net/>
|
6 |
-
Copyright 2008 Edward Z. Yang <http://htmlpurifier.org/>
|
7 |
-
Copyright 2009 Geoffrey Sneddon <http://gsnedders.com/>
|
8 |
-
|
9 |
-
Permission is hereby granted, free of charge, to any person obtaining a
|
10 |
-
copy of this software and associated documentation files (the
|
11 |
-
"Software"), to deal in the Software without restriction, including
|
12 |
-
without limitation the rights to use, copy, modify, merge, publish,
|
13 |
-
distribute, sublicense, and/or sell copies of the Software, and to
|
14 |
-
permit persons to whom the Software is furnished to do so, subject to
|
15 |
-
the following conditions:
|
16 |
-
|
17 |
-
The above copyright notice and this permission notice shall be included
|
18 |
-
in all copies or substantial portions of the Software.
|
19 |
-
|
20 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
21 |
-
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22 |
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
23 |
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
24 |
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
25 |
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
26 |
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27 |
-
|
28 |
-
*/
|
29 |
-
|
30 |
-
// Some conventions:
|
31 |
-
// /* */ indicates verbatim text from the HTML 5 specification
|
32 |
-
// // indicates regular comments
|
33 |
-
|
34 |
-
// all flags are in hyphenated form
|
35 |
-
|
36 |
-
class HTML5_Tokenizer {
|
37 |
-
/**
|
38 |
-
* @var HTML5_InputStream
|
39 |
-
*
|
40 |
-
* Points to an InputStream object.
|
41 |
-
*/
|
42 |
-
protected $stream;
|
43 |
-
|
44 |
-
/**
|
45 |
-
* @var HTML5_TreeBuilder
|
46 |
-
*
|
47 |
-
* Tree builder that the tokenizer emits token to.
|
48 |
-
*/
|
49 |
-
private $tree;
|
50 |
-
|
51 |
-
/**
|
52 |
-
* @var int
|
53 |
-
*
|
54 |
-
* Current content model we are parsing as.
|
55 |
-
*/
|
56 |
-
protected $content_model;
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Current token that is being built, but not yet emitted. Also
|
60 |
-
* is the last token emitted, if applicable.
|
61 |
-
*/
|
62 |
-
protected $token;
|
63 |
-
|
64 |
-
// These are constants describing the content model
|
65 |
-
const PCDATA = 0;
|
66 |
-
const RCDATA = 1;
|
67 |
-
const CDATA = 2;
|
68 |
-
const PLAINTEXT = 3;
|
69 |
-
|
70 |
-
// These are constants describing tokens
|
71 |
-
// XXX should probably be moved somewhere else, probably the
|
72 |
-
// HTML5 class.
|
73 |
-
const DOCTYPE = 0;
|
74 |
-
const STARTTAG = 1;
|
75 |
-
const ENDTAG = 2;
|
76 |
-
const COMMENT = 3;
|
77 |
-
const CHARACTER = 4;
|
78 |
-
const SPACECHARACTER = 5;
|
79 |
-
const EOF = 6;
|
80 |
-
const PARSEERROR = 7;
|
81 |
-
|
82 |
-
// These are constants representing bunches of characters.
|
83 |
-
const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
84 |
-
const UPPER_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
85 |
-
const LOWER_ALPHA = 'abcdefghijklmnopqrstuvwxyz';
|
86 |
-
const DIGIT = '0123456789';
|
87 |
-
const HEX = '0123456789ABCDEFabcdef';
|
88 |
-
const WHITESPACE = "\t\n\x0c ";
|
89 |
-
|
90 |
-
/**
|
91 |
-
* @param $data | Data to parse
|
92 |
-
* @param HTML5_TreeBuilder|null $builder
|
93 |
-
*/
|
94 |
-
public function __construct($data, $builder = null) {
|
95 |
-
$this->stream = new HTML5_InputStream($data);
|
96 |
-
if (!$builder) {
|
97 |
-
$this->tree = new HTML5_TreeBuilder;
|
98 |
-
} else {
|
99 |
-
$this->tree = $builder;
|
100 |
-
}
|
101 |
-
$this->content_model = self::PCDATA;
|
102 |
-
}
|
103 |
-
|
104 |
-
/**
|
105 |
-
* @param null $context
|
106 |
-
*/
|
107 |
-
public function parseFragment($context = null) {
|
108 |
-
$this->tree->setupContext($context);
|
109 |
-
if ($this->tree->content_model) {
|
110 |
-
$this->content_model = $this->tree->content_model;
|
111 |
-
$this->tree->content_model = null;
|
112 |
-
}
|
113 |
-
$this->parse();
|
114 |
-
}
|
115 |
-
|
116 |
-
// XXX maybe convert this into an iterator? regardless, this function
|
117 |
-
// and the save function should go into a Parser facade of some sort
|
118 |
-
/**
|
119 |
-
* Performs the actual parsing of the document.
|
120 |
-
*/
|
121 |
-
public function parse() {
|
122 |
-
// Current state
|
123 |
-
$state = 'data';
|
124 |
-
// This is used to avoid having to have look-behind in the data state.
|
125 |
-
$lastFourChars = '';
|
126 |
-
/**
|
127 |
-
* Escape flag as specified by the HTML5 specification: "used to
|
128 |
-
* control the behavior of the tokeniser. It is either true or
|
129 |
-
* false, and initially must be set to the false state."
|
130 |
-
*/
|
131 |
-
$escape = false;
|
132 |
-
//echo "\n\n";
|
133 |
-
while($state !== null) {
|
134 |
-
|
135 |
-
/*echo $state . ' ';
|
136 |
-
switch ($this->content_model) {
|
137 |
-
case self::PCDATA: echo 'PCDATA'; break;
|
138 |
-
case self::RCDATA: echo 'RCDATA'; break;
|
139 |
-
case self::CDATA: echo 'CDATA'; break;
|
140 |
-
case self::PLAINTEXT: echo 'PLAINTEXT'; break;
|
141 |
-
}
|
142 |
-
if ($escape) echo " escape";
|
143 |
-
echo "\n";*/
|
144 |
-
|
145 |
-
switch($state) {
|
146 |
-
case 'data':
|
147 |
-
|
148 |
-
/* Consume the next input character */
|
149 |
-
$char = $this->stream->char();
|
150 |
-
$lastFourChars .= $char;
|
151 |
-
if (strlen($lastFourChars) > 4)
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
the
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
$
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
//
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
$chars = $this->stream->
|
291 |
-
}
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
$
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
$
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
'
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
'
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
U+003E GREATER-THAN SIGN
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
$this->stream->
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
* U+
|
471 |
-
* U+
|
472 |
-
* U+
|
473 |
-
* U+
|
474 |
-
*
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
//
|
482 |
-
|
483 |
-
$
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
//
|
493 |
-
|
494 |
-
//
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
U+
|
582 |
-
U+
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
the
|
602 |
-
|
603 |
-
|
604 |
-
$this->
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
$this->
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
U+
|
638 |
-
U+
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
U+
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
U+
|
714 |
-
U+
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
the
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
$
|
743 |
-
|
744 |
-
|
745 |
-
$
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
$
|
777 |
-
|
778 |
-
|
779 |
-
$
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
attribute
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
U+
|
801 |
-
U+
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
U+
|
881 |
-
U+
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
|
888 |
-
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
$
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
|
966 |
-
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
$
|
977 |
-
|
978 |
-
|
979 |
-
$
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
|
1003 |
-
|
1004 |
-
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
$
|
1015 |
-
|
1016 |
-
|
1017 |
-
$
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
U+
|
1029 |
-
U+
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
-
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
1049 |
-
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
U+
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
1068 |
-
|
1069 |
-
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
|
1074 |
-
|
1075 |
-
$
|
1076 |
-
|
1077 |
-
|
1078 |
-
$
|
1079 |
-
|
1080 |
-
|
1081 |
-
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
-
|
1087 |
-
|
1088 |
-
|
1089 |
-
|
1090 |
-
U+
|
1091 |
-
|
1092 |
-
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
-
|
1105 |
-
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
1109 |
-
|
1110 |
-
|
1111 |
-
|
1112 |
-
|
1113 |
-
|
1114 |
-
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
1120 |
-
|
1121 |
-
|
1122 |
-
|
1123 |
-
|
1124 |
-
|
1125 |
-
|
1126 |
-
|
1127 |
-
|
1128 |
-
|
1129 |
-
|
1130 |
-
|
1131 |
-
|
1132 |
-
|
1133 |
-
|
1134 |
-
|
1135 |
-
|
1136 |
-
|
1137 |
-
|
1138 |
-
|
1139 |
-
$
|
1140 |
-
|
1141 |
-
|
1142 |
-
|
1143 |
-
|
1144 |
-
|
1145 |
-
|
1146 |
-
|
1147 |
-
|
1148 |
-
|
1149 |
-
|
1150 |
-
|
1151 |
-
|
1152 |
-
|
1153 |
-
|
1154 |
-
|
1155 |
-
|
1156 |
-
|
1157 |
-
|
1158 |
-
|
1159 |
-
|
1160 |
-
|
1161 |
-
|
1162 |
-
|
1163 |
-
|
1164 |
-
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
|
1169 |
-
|
1170 |
-
|
1171 |
-
|
1172 |
-
the
|
1173 |
-
|
1174 |
-
|
1175 |
-
|
1176 |
-
$this->
|
1177 |
-
|
1178 |
-
|
1179 |
-
|
1180 |
-
|
1181 |
-
|
1182 |
-
|
1183 |
-
|
1184 |
-
|
1185 |
-
|
1186 |
-
|
1187 |
-
|
1188 |
-
|
1189 |
-
|
1190 |
-
|
1191 |
-
|
1192 |
-
|
1193 |
-
|
1194 |
-
|
1195 |
-
|
1196 |
-
|
1197 |
-
|
1198 |
-
|
1199 |
-
|
1200 |
-
|
1201 |
-
|
1202 |
-
|
1203 |
-
|
1204 |
-
|
1205 |
-
|
1206 |
-
|
1207 |
-
|
1208 |
-
|
1209 |
-
|
1210 |
-
|
1211 |
-
|
1212 |
-
|
1213 |
-
|
1214 |
-
|
1215 |
-
CDATA
|
1216 |
-
|
1217 |
-
|
1218 |
-
|
1219 |
-
|
1220 |
-
|
1221 |
-
|
1222 |
-
|
1223 |
-
|
1224 |
-
|
1225 |
-
|
1226 |
-
|
1227 |
-
|
1228 |
-
|
1229 |
-
|
1230 |
-
|
1231 |
-
|
1232 |
-
|
1233 |
-
|
1234 |
-
|
1235 |
-
|
1236 |
-
|
1237 |
-
|
1238 |
-
|
1239 |
-
|
1240 |
-
|
1241 |
-
|
1242 |
-
|
1243 |
-
|
1244 |
-
|
1245 |
-
|
1246 |
-
|
1247 |
-
|
1248 |
-
|
1249 |
-
|
1250 |
-
|
1251 |
-
|
1252 |
-
|
1253 |
-
|
1254 |
-
|
1255 |
-
EOF
|
1256 |
-
|
1257 |
-
|
1258 |
-
|
1259 |
-
|
1260 |
-
|
1261 |
-
|
1262 |
-
$
|
1263 |
-
|
1264 |
-
|
1265 |
-
|
1266 |
-
|
1267 |
-
|
1268 |
-
|
1269 |
-
|
1270 |
-
|
1271 |
-
|
1272 |
-
|
1273 |
-
|
1274 |
-
|
1275 |
-
|
1276 |
-
|
1277 |
-
|
1278 |
-
|
1279 |
-
|
1280 |
-
|
1281 |
-
|
1282 |
-
|
1283 |
-
|
1284 |
-
|
1285 |
-
|
1286 |
-
|
1287 |
-
|
1288 |
-
|
1289 |
-
|
1290 |
-
|
1291 |
-
|
1292 |
-
|
1293 |
-
|
1294 |
-
|
1295 |
-
|
1296 |
-
|
1297 |
-
|
1298 |
-
$
|
1299 |
-
|
1300 |
-
$
|
1301 |
-
|
1302 |
-
|
1303 |
-
|
1304 |
-
|
1305 |
-
|
1306 |
-
|
1307 |
-
|
1308 |
-
|
1309 |
-
|
1310 |
-
|
1311 |
-
|
1312 |
-
|
1313 |
-
|
1314 |
-
|
1315 |
-
|
1316 |
-
|
1317 |
-
|
1318 |
-
|
1319 |
-
|
1320 |
-
|
1321 |
-
|
1322 |
-
|
1323 |
-
|
1324 |
-
$
|
1325 |
-
|
1326 |
-
|
1327 |
-
|
1328 |
-
|
1329 |
-
|
1330 |
-
|
1331 |
-
|
1332 |
-
$this->
|
1333 |
-
|
1334 |
-
|
1335 |
-
|
1336 |
-
|
1337 |
-
|
1338 |
-
|
1339 |
-
|
1340 |
-
|
1341 |
-
|
1342 |
-
|
1343 |
-
|
1344 |
-
|
1345 |
-
|
1346 |
-
|
1347 |
-
|
1348 |
-
|
1349 |
-
|
1350 |
-
|
1351 |
-
|
1352 |
-
|
1353 |
-
|
1354 |
-
|
1355 |
-
$
|
1356 |
-
|
1357 |
-
|
1358 |
-
|
1359 |
-
|
1360 |
-
|
1361 |
-
|
1362 |
-
|
1363 |
-
|
1364 |
-
|
1365 |
-
|
1366 |
-
|
1367 |
-
|
1368 |
-
|
1369 |
-
|
1370 |
-
|
1371 |
-
|
1372 |
-
|
1373 |
-
|
1374 |
-
|
1375 |
-
|
1376 |
-
|
1377 |
-
|
1378 |
-
|
1379 |
-
|
1380 |
-
|
1381 |
-
|
1382 |
-
|
1383 |
-
|
1384 |
-
|
1385 |
-
|
1386 |
-
|
1387 |
-
|
1388 |
-
|
1389 |
-
|
1390 |
-
|
1391 |
-
|
1392 |
-
|
1393 |
-
|
1394 |
-
|
1395 |
-
|
1396 |
-
|
1397 |
-
|
1398 |
-
|
1399 |
-
|
1400 |
-
|
1401 |
-
|
1402 |
-
|
1403 |
-
|
1404 |
-
|
1405 |
-
EOF
|
1406 |
-
|
1407 |
-
|
1408 |
-
|
1409 |
-
|
1410 |
-
|
1411 |
-
|
1412 |
-
$
|
1413 |
-
|
1414 |
-
|
1415 |
-
|
1416 |
-
|
1417 |
-
|
1418 |
-
|
1419 |
-
|
1420 |
-
|
1421 |
-
|
1422 |
-
|
1423 |
-
|
1424 |
-
|
1425 |
-
|
1426 |
-
|
1427 |
-
|
1428 |
-
|
1429 |
-
|
1430 |
-
|
1431 |
-
|
1432 |
-
|
1433 |
-
|
1434 |
-
$
|
1435 |
-
|
1436 |
-
|
1437 |
-
$
|
1438 |
-
|
1439 |
-
|
1440 |
-
|
1441 |
-
|
1442 |
-
|
1443 |
-
$
|
1444 |
-
|
1445 |
-
$
|
1446 |
-
|
1447 |
-
|
1448 |
-
|
1449 |
-
|
1450 |
-
|
1451 |
-
|
1452 |
-
|
1453 |
-
|
1454 |
-
|
1455 |
-
|
1456 |
-
$state = '
|
1457 |
-
} elseif ($char ===
|
1458 |
-
$
|
1459 |
-
} elseif ($char ===
|
1460 |
-
$this->
|
1461 |
-
|
1462 |
-
|
1463 |
-
|
1464 |
-
|
1465 |
-
|
1466 |
-
$
|
1467 |
-
|
1468 |
-
$
|
1469 |
-
|
1470 |
-
|
1471 |
-
|
1472 |
-
|
1473 |
-
|
1474 |
-
|
1475 |
-
|
1476 |
-
|
1477 |
-
|
1478 |
-
|
1479 |
-
|
1480 |
-
|
1481 |
-
U+
|
1482 |
-
|
1483 |
-
|
1484 |
-
|
1485 |
-
|
1486 |
-
|
1487 |
-
|
1488 |
-
|
1489 |
-
|
1490 |
-
|
1491 |
-
|
1492 |
-
|
1493 |
-
|
1494 |
-
|
1495 |
-
|
1496 |
-
|
1497 |
-
'
|
1498 |
-
'
|
1499 |
-
|
1500 |
-
|
1501 |
-
|
1502 |
-
|
1503 |
-
|
1504 |
-
|
1505 |
-
|
1506 |
-
|
1507 |
-
|
1508 |
-
|
1509 |
-
|
1510 |
-
|
1511 |
-
|
1512 |
-
|
1513 |
-
|
1514 |
-
|
1515 |
-
|
1516 |
-
|
1517 |
-
|
1518 |
-
|
1519 |
-
|
1520 |
-
|
1521 |
-
|
1522 |
-
|
1523 |
-
|
1524 |
-
U+
|
1525 |
-
|
1526 |
-
|
1527 |
-
|
1528 |
-
|
1529 |
-
|
1530 |
-
|
1531 |
-
|
1532 |
-
|
1533 |
-
|
1534 |
-
|
1535 |
-
|
1536 |
-
|
1537 |
-
|
1538 |
-
|
1539 |
-
'
|
1540 |
-
'
|
1541 |
-
|
1542 |
-
|
1543 |
-
|
1544 |
-
|
1545 |
-
|
1546 |
-
|
1547 |
-
|
1548 |
-
|
1549 |
-
|
1550 |
-
|
1551 |
-
|
1552 |
-
|
1553 |
-
|
1554 |
-
'
|
1555 |
-
|
1556 |
-
|
1557 |
-
|
1558 |
-
|
1559 |
-
|
1560 |
-
|
1561 |
-
|
1562 |
-
|
1563 |
-
|
1564 |
-
|
1565 |
-
|
1566 |
-
|
1567 |
-
|
1568 |
-
|
1569 |
-
|
1570 |
-
|
1571 |
-
'
|
1572 |
-
'
|
1573 |
-
|
1574 |
-
|
1575 |
-
|
1576 |
-
|
1577 |
-
|
1578 |
-
|
1579 |
-
|
1580 |
-
|
1581 |
-
|
1582 |
-
|
1583 |
-
|
1584 |
-
|
1585 |
-
'
|
1586 |
-
|
1587 |
-
|
1588 |
-
|
1589 |
-
|
1590 |
-
|
1591 |
-
|
1592 |
-
|
1593 |
-
|
1594 |
-
|
1595 |
-
|
1596 |
-
|
1597 |
-
|
1598 |
-
|
1599 |
-
|
1600 |
-
U+
|
1601 |
-
|
1602 |
-
|
1603 |
-
|
1604 |
-
|
1605 |
-
|
1606 |
-
|
1607 |
-
|
1608 |
-
|
1609 |
-
|
1610 |
-
|
1611 |
-
|
1612 |
-
|
1613 |
-
|
1614 |
-
|
1615 |
-
|
1616 |
-
|
1617 |
-
|
1618 |
-
|
1619 |
-
|
1620 |
-
|
1621 |
-
|
1622 |
-
|
1623 |
-
|
1624 |
-
|
1625 |
-
|
1626 |
-
|
1627 |
-
|
1628 |
-
$this->
|
1629 |
-
$
|
1630 |
-
|
1631 |
-
|
1632 |
-
|
1633 |
-
|
1634 |
-
|
1635 |
-
|
1636 |
-
|
1637 |
-
|
1638 |
-
|
1639 |
-
|
1640 |
-
//
|
1641 |
-
|
1642 |
-
|
1643 |
-
|
1644 |
-
|
1645 |
-
|
1646 |
-
|
1647 |
-
|
1648 |
-
|
1649 |
-
|
1650 |
-
|
1651 |
-
|
1652 |
-
|
1653 |
-
|
1654 |
-
U+
|
1655 |
-
|
1656 |
-
|
1657 |
-
|
1658 |
-
|
1659 |
-
|
1660 |
-
|
1661 |
-
|
1662 |
-
|
1663 |
-
|
1664 |
-
|
1665 |
-
|
1666 |
-
|
1667 |
-
|
1668 |
-
|
1669 |
-
|
1670 |
-
|
1671 |
-
|
1672 |
-
|
1673 |
-
|
1674 |
-
$this->
|
1675 |
-
$
|
1676 |
-
|
1677 |
-
|
1678 |
-
|
1679 |
-
|
1680 |
-
|
1681 |
-
|
1682 |
-
|
1683 |
-
|
1684 |
-
|
1685 |
-
|
1686 |
-
|
1687 |
-
|
1688 |
-
|
1689 |
-
|
1690 |
-
|
1691 |
-
|
1692 |
-
|
1693 |
-
|
1694 |
-
|
1695 |
-
|
1696 |
-
|
1697 |
-
|
1698 |
-
|
1699 |
-
|
1700 |
-
|
1701 |
-
|
1702 |
-
|
1703 |
-
|
1704 |
-
|
1705 |
-
$
|
1706 |
-
|
1707 |
-
|
1708 |
-
|
1709 |
-
|
1710 |
-
|
1711 |
-
|
1712 |
-
|
1713 |
-
|
1714 |
-
|
1715 |
-
|
1716 |
-
|
1717 |
-
|
1718 |
-
U+
|
1719 |
-
|
1720 |
-
|
1721 |
-
|
1722 |
-
|
1723 |
-
|
1724 |
-
identifier
|
1725 |
-
|
1726 |
-
|
1727 |
-
|
1728 |
-
|
1729 |
-
|
1730 |
-
|
1731 |
-
identifier
|
1732 |
-
|
1733 |
-
|
1734 |
-
|
1735 |
-
|
1736 |
-
|
1737 |
-
|
1738 |
-
|
1739 |
-
|
1740 |
-
|
1741 |
-
|
1742 |
-
|
1743 |
-
$
|
1744 |
-
|
1745 |
-
|
1746 |
-
|
1747 |
-
|
1748 |
-
|
1749 |
-
|
1750 |
-
|
1751 |
-
|
1752 |
-
|
1753 |
-
|
1754 |
-
$this->
|
1755 |
-
$
|
1756 |
-
|
1757 |
-
|
1758 |
-
|
1759 |
-
|
1760 |
-
|
1761 |
-
|
1762 |
-
|
1763 |
-
|
1764 |
-
|
1765 |
-
|
1766 |
-
|
1767 |
-
|
1768 |
-
|
1769 |
-
|
1770 |
-
|
1771 |
-
|
1772 |
-
|
1773 |
-
|
1774 |
-
|
1775 |
-
|
1776 |
-
|
1777 |
-
|
1778 |
-
|
1779 |
-
|
1780 |
-
|
1781 |
-
|
1782 |
-
|
1783 |
-
|
1784 |
-
|
1785 |
-
|
1786 |
-
$
|
1787 |
-
|
1788 |
-
|
1789 |
-
|
1790 |
-
|
1791 |
-
|
1792 |
-
|
1793 |
-
|
1794 |
-
|
1795 |
-
|
1796 |
-
|
1797 |
-
|
1798 |
-
$this->
|
1799 |
-
$
|
1800 |
-
|
1801 |
-
|
1802 |
-
|
1803 |
-
|
1804 |
-
|
1805 |
-
|
1806 |
-
|
1807 |
-
|
1808 |
-
|
1809 |
-
|
1810 |
-
|
1811 |
-
|
1812 |
-
|
1813 |
-
|
1814 |
-
|
1815 |
-
|
1816 |
-
|
1817 |
-
|
1818 |
-
|
1819 |
-
|
1820 |
-
|
1821 |
-
|
1822 |
-
|
1823 |
-
|
1824 |
-
|
1825 |
-
|
1826 |
-
|
1827 |
-
$
|
1828 |
-
|
1829 |
-
|
1830 |
-
|
1831 |
-
|
1832 |
-
|
1833 |
-
|
1834 |
-
|
1835 |
-
|
1836 |
-
|
1837 |
-
|
1838 |
-
|
1839 |
-
$this->
|
1840 |
-
$
|
1841 |
-
|
1842 |
-
|
1843 |
-
|
1844 |
-
|
1845 |
-
|
1846 |
-
|
1847 |
-
|
1848 |
-
|
1849 |
-
|
1850 |
-
|
1851 |
-
|
1852 |
-
|
1853 |
-
|
1854 |
-
|
1855 |
-
|
1856 |
-
|
1857 |
-
|
1858 |
-
U+
|
1859 |
-
|
1860 |
-
|
1861 |
-
|
1862 |
-
|
1863 |
-
|
1864 |
-
system identifier
|
1865 |
-
|
1866 |
-
|
1867 |
-
|
1868 |
-
|
1869 |
-
|
1870 |
-
|
1871 |
-
system identifier
|
1872 |
-
|
1873 |
-
|
1874 |
-
|
1875 |
-
|
1876 |
-
|
1877 |
-
|
1878 |
-
|
1879 |
-
|
1880 |
-
|
1881 |
-
|
1882 |
-
|
1883 |
-
|
1884 |
-
|
1885 |
-
|
1886 |
-
|
1887 |
-
|
1888 |
-
|
1889 |
-
$this->
|
1890 |
-
$
|
1891 |
-
|
1892 |
-
|
1893 |
-
|
1894 |
-
|
1895 |
-
|
1896 |
-
|
1897 |
-
|
1898 |
-
|
1899 |
-
|
1900 |
-
|
1901 |
-
|
1902 |
-
|
1903 |
-
|
1904 |
-
|
1905 |
-
|
1906 |
-
|
1907 |
-
|
1908 |
-
|
1909 |
-
|
1910 |
-
|
1911 |
-
|
1912 |
-
U+
|
1913 |
-
|
1914 |
-
|
1915 |
-
|
1916 |
-
|
1917 |
-
|
1918 |
-
identifier
|
1919 |
-
|
1920 |
-
|
1921 |
-
|
1922 |
-
|
1923 |
-
|
1924 |
-
|
1925 |
-
identifier
|
1926 |
-
|
1927 |
-
|
1928 |
-
|
1929 |
-
|
1930 |
-
|
1931 |
-
|
1932 |
-
|
1933 |
-
|
1934 |
-
|
1935 |
-
|
1936 |
-
|
1937 |
-
$
|
1938 |
-
|
1939 |
-
|
1940 |
-
|
1941 |
-
|
1942 |
-
|
1943 |
-
|
1944 |
-
|
1945 |
-
|
1946 |
-
|
1947 |
-
|
1948 |
-
$this->
|
1949 |
-
$
|
1950 |
-
|
1951 |
-
|
1952 |
-
|
1953 |
-
|
1954 |
-
|
1955 |
-
|
1956 |
-
|
1957 |
-
|
1958 |
-
|
1959 |
-
|
1960 |
-
|
1961 |
-
|
1962 |
-
|
1963 |
-
|
1964 |
-
|
1965 |
-
|
1966 |
-
|
1967 |
-
|
1968 |
-
|
1969 |
-
|
1970 |
-
|
1971 |
-
|
1972 |
-
|
1973 |
-
|
1974 |
-
|
1975 |
-
|
1976 |
-
|
1977 |
-
|
1978 |
-
|
1979 |
-
|
1980 |
-
$
|
1981 |
-
|
1982 |
-
|
1983 |
-
|
1984 |
-
|
1985 |
-
|
1986 |
-
|
1987 |
-
|
1988 |
-
|
1989 |
-
|
1990 |
-
|
1991 |
-
|
1992 |
-
$this->
|
1993 |
-
$
|
1994 |
-
|
1995 |
-
|
1996 |
-
|
1997 |
-
|
1998 |
-
|
1999 |
-
|
2000 |
-
|
2001 |
-
|
2002 |
-
|
2003 |
-
|
2004 |
-
|
2005 |
-
|
2006 |
-
|
2007 |
-
|
2008 |
-
|
2009 |
-
|
2010 |
-
|
2011 |
-
|
2012 |
-
|
2013 |
-
|
2014 |
-
|
2015 |
-
|
2016 |
-
|
2017 |
-
|
2018 |
-
|
2019 |
-
|
2020 |
-
|
2021 |
-
$
|
2022 |
-
|
2023 |
-
|
2024 |
-
|
2025 |
-
|
2026 |
-
|
2027 |
-
|
2028 |
-
|
2029 |
-
|
2030 |
-
|
2031 |
-
|
2032 |
-
|
2033 |
-
$this->
|
2034 |
-
$
|
2035 |
-
|
2036 |
-
|
2037 |
-
|
2038 |
-
|
2039 |
-
|
2040 |
-
|
2041 |
-
|
2042 |
-
|
2043 |
-
|
2044 |
-
|
2045 |
-
|
2046 |
-
|
2047 |
-
|
2048 |
-
|
2049 |
-
|
2050 |
-
|
2051 |
-
|
2052 |
-
U+
|
2053 |
-
|
2054 |
-
|
2055 |
-
|
2056 |
-
|
2057 |
-
|
2058 |
-
|
2059 |
-
|
2060 |
-
|
2061 |
-
|
2062 |
-
|
2063 |
-
|
2064 |
-
|
2065 |
-
|
2066 |
-
|
2067 |
-
|
2068 |
-
|
2069 |
-
$this->
|
2070 |
-
$
|
2071 |
-
|
2072 |
-
|
2073 |
-
|
2074 |
-
|
2075 |
-
|
2076 |
-
|
2077 |
-
|
2078 |
-
|
2079 |
-
|
2080 |
-
|
2081 |
-
|
2082 |
-
|
2083 |
-
|
2084 |
-
|
2085 |
-
|
2086 |
-
|
2087 |
-
|
2088 |
-
|
2089 |
-
|
2090 |
-
|
2091 |
-
|
2092 |
-
|
2093 |
-
|
2094 |
-
|
2095 |
-
|
2096 |
-
|
2097 |
-
|
2098 |
-
|
2099 |
-
|
2100 |
-
$
|
2101 |
-
|
2102 |
-
|
2103 |
-
|
2104 |
-
|
2105 |
-
|
2106 |
-
|
2107 |
-
|
2108 |
-
|
2109 |
-
|
2110 |
-
|
2111 |
-
|
2112 |
-
|
2113 |
-
|
2114 |
-
|
2115 |
-
|
2116 |
-
*
|
2117 |
-
|
2118 |
-
|
2119 |
-
|
2120 |
-
|
2121 |
-
|
2122 |
-
|
2123 |
-
|
2124 |
-
|
2125 |
-
|
2126 |
-
|
2127 |
-
|
2128 |
-
|
2129 |
-
|
2130 |
-
|
2131 |
-
|
2132 |
-
|
2133 |
-
|
2134 |
-
*
|
2135 |
-
|
2136 |
-
|
2137 |
-
|
2138 |
-
|
2139 |
-
|
2140 |
-
|
2141 |
-
|
2142 |
-
|
2143 |
-
* @
|
2144 |
-
|
2145 |
-
|
2146 |
-
|
2147 |
-
|
2148 |
-
//
|
2149 |
-
|
2150 |
-
//
|
2151 |
-
|
2152 |
-
|
2153 |
-
|
2154 |
-
|
2155 |
-
|
2156 |
-
|
2157 |
-
|
2158 |
-
|
2159 |
-
|
2160 |
-
|
2161 |
-
|
2162 |
-
|
2163 |
-
$chars[0] === "\
|
2164 |
-
$chars[0] === "\
|
2165 |
-
$chars[0] ===
|
2166 |
-
$chars[0] ===
|
2167 |
-
$chars ===
|
2168 |
-
$chars[0] ===
|
2169 |
-
|
2170 |
-
|
2171 |
-
|
2172 |
-
|
2173 |
-
U+
|
2174 |
-
U+
|
2175 |
-
U+
|
2176 |
-
|
2177 |
-
|
2178 |
-
|
2179 |
-
|
2180 |
-
|
2181 |
-
|
2182 |
-
|
2183 |
-
|
2184 |
-
|
2185 |
-
|
2186 |
-
/*
|
2187 |
-
|
2188 |
-
|
2189 |
-
|
2190 |
-
|
2191 |
-
|
2192 |
-
/*
|
2193 |
-
|
2194 |
-
/*
|
2195 |
-
|
2196 |
-
|
2197 |
-
|
2198 |
-
|
2199 |
-
|
2200 |
-
|
2201 |
-
|
2202 |
-
|
2203 |
-
|
2204 |
-
|
2205 |
-
|
2206 |
-
|
2207 |
-
|
2208 |
-
|
2209 |
-
|
2210 |
-
|
2211 |
-
|
2212 |
-
|
2213 |
-
|
2214 |
-
|
2215 |
-
|
2216 |
-
|
2217 |
-
|
2218 |
-
|
2219 |
-
|
2220 |
-
|
2221 |
-
|
2222 |
-
|
2223 |
-
|
2224 |
-
|
2225 |
-
|
2226 |
-
|
2227 |
-
|
2228 |
-
|
2229 |
-
|
2230 |
-
|
2231 |
-
|
2232 |
-
|
2233 |
-
if
|
2234 |
-
|
2235 |
-
|
2236 |
-
|
2237 |
-
|
2238 |
-
|
2239 |
-
|
2240 |
-
|
2241 |
-
|
2242 |
-
|
2243 |
-
|
2244 |
-
|
2245 |
-
|
2246 |
-
|
2247 |
-
|
2248 |
-
|
2249 |
-
|
2250 |
-
|
2251 |
-
|
2252 |
-
|
2253 |
-
|
2254 |
-
|
2255 |
-
|
2256 |
-
|
2257 |
-
|
2258 |
-
|
2259 |
-
|
2260 |
-
|
2261 |
-
|
2262 |
-
|
2263 |
-
|
2264 |
-
|
2265 |
-
|
2266 |
-
|
2267 |
-
|
2268 |
-
|
2269 |
-
|
2270 |
-
|
2271 |
-
|
2272 |
-
*
|
2273 |
-
*
|
2274 |
-
*
|
2275 |
-
*
|
2276 |
-
*
|
2277 |
-
*
|
2278 |
-
*
|
2279 |
-
*
|
2280 |
-
|
2281 |
-
|
2282 |
-
|
2283 |
-
|
2284 |
-
$codepoint >=
|
2285 |
-
$codepoint
|
2286 |
-
$codepoint >=
|
2287 |
-
$codepoint >=
|
2288 |
-
|
2289 |
-
$codepoint
|
2290 |
-
|
2291 |
-
$
|
2292 |
-
|
2293 |
-
|
2294 |
-
|
2295 |
-
|
2296 |
-
|
2297 |
-
|
2298 |
-
|
2299 |
-
|
2300 |
-
|
2301 |
-
|
2302 |
-
/*
|
2303 |
-
|
2304 |
-
|
2305 |
-
|
2306 |
-
|
2307 |
-
|
2308 |
-
|
2309 |
-
|
2310 |
-
|
2311 |
-
|
2312 |
-
|
2313 |
-
//
|
2314 |
-
$
|
2315 |
-
|
2316 |
-
|
2317 |
-
|
2318 |
-
|
2319 |
-
|
2320 |
-
|
2321 |
-
|
2322 |
-
|
2323 |
-
|
2324 |
-
|
2325 |
-
|
2326 |
-
|
2327 |
-
//
|
2328 |
-
//
|
2329 |
-
|
2330 |
-
|
2331 |
-
|
2332 |
-
|
2333 |
-
|
2334 |
-
|
2335 |
-
|
2336 |
-
|
2337 |
-
|
2338 |
-
|
2339 |
-
|
2340 |
-
|
2341 |
-
|
2342 |
-
|
2343 |
-
|
2344 |
-
|
2345 |
-
|
2346 |
-
|
2347 |
-
|
2348 |
-
|
2349 |
-
|
2350 |
-
|
2351 |
-
|
2352 |
-
|
2353 |
-
|
2354 |
-
|
2355 |
-
|
2356 |
-
|
2357 |
-
|
2358 |
-
|
2359 |
-
|
2360 |
-
|
2361 |
-
|
2362 |
-
|
2363 |
-
|
2364 |
-
|
2365 |
-
|
2366 |
-
|
2367 |
-
|
2368 |
-
|
2369 |
-
$next = $
|
2370 |
-
|
2371 |
-
|
2372 |
-
|
2373 |
-
|
2374 |
-
|
2375 |
-
'
|
2376 |
-
|
2377 |
-
|
2378 |
-
|
2379 |
-
|
2380 |
-
|
2381 |
-
|
2382 |
-
|
2383 |
-
|
2384 |
-
|
2385 |
-
|
2386 |
-
|
2387 |
-
|
2388 |
-
|
2389 |
-
|
2390 |
-
|
2391 |
-
|
2392 |
-
|
2393 |
-
|
2394 |
-
|
2395 |
-
|
2396 |
-
|
2397 |
-
|
2398 |
-
|
2399 |
-
|
2400 |
-
|
2401 |
-
|
2402 |
-
|
2403 |
-
|
2404 |
-
|
2405 |
-
|
2406 |
-
|
2407 |
-
|
2408 |
-
|
2409 |
-
|
2410 |
-
|
2411 |
-
|
2412 |
-
|
2413 |
-
|
2414 |
-
*
|
2415 |
-
*
|
2416 |
-
* @param
|
2417 |
-
|
2418 |
-
|
2419 |
-
|
2420 |
-
|
2421 |
-
|
2422 |
-
|
2423 |
-
|
2424 |
-
|
2425 |
-
|
2426 |
-
|
2427 |
-
|
2428 |
-
|
2429 |
-
|
2430 |
-
|
2431 |
-
|
2432 |
-
|
2433 |
-
|
2434 |
-
|
2435 |
-
|
2436 |
-
|
2437 |
-
|
2438 |
-
|
2439 |
-
|
2440 |
-
|
2441 |
-
|
2442 |
-
|
2443 |
-
|
2444 |
-
|
2445 |
-
|
2446 |
-
|
2447 |
-
|
2448 |
-
|
2449 |
-
|
2450 |
-
}
|
2451 |
-
|
2452 |
-
|
2453 |
-
|
2454 |
-
|
2455 |
-
|
2456 |
-
|
2457 |
-
|
2458 |
-
|
2459 |
-
|
2460 |
-
|
2461 |
-
|
2462 |
-
|
2463 |
-
|
2464 |
-
|
2465 |
-
}
|
2466 |
-
|
2467 |
-
}
|
2468 |
-
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
|
5 |
+
Copyright 2007 Jeroen van der Meer <http://jero.net/>
|
6 |
+
Copyright 2008 Edward Z. Yang <http://htmlpurifier.org/>
|
7 |
+
Copyright 2009 Geoffrey Sneddon <http://gsnedders.com/>
|
8 |
+
|
9 |
+
Permission is hereby granted, free of charge, to any person obtaining a
|
10 |
+
copy of this software and associated documentation files (the
|
11 |
+
"Software"), to deal in the Software without restriction, including
|
12 |
+
without limitation the rights to use, copy, modify, merge, publish,
|
13 |
+
distribute, sublicense, and/or sell copies of the Software, and to
|
14 |
+
permit persons to whom the Software is furnished to do so, subject to
|
15 |
+
the following conditions:
|
16 |
+
|
17 |
+
The above copyright notice and this permission notice shall be included
|
18 |
+
in all copies or substantial portions of the Software.
|
19 |
+
|
20 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
21 |
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
23 |
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
24 |
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
25 |
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
26 |
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27 |
+
|
28 |
+
*/
|
29 |
+
|
30 |
+
// Some conventions:
|
31 |
+
// /* */ indicates verbatim text from the HTML 5 specification
|
32 |
+
// // indicates regular comments
|
33 |
+
|
34 |
+
// all flags are in hyphenated form
|
35 |
+
|
36 |
+
class HTML5_Tokenizer {
|
37 |
+
/**
|
38 |
+
* @var HTML5_InputStream
|
39 |
+
*
|
40 |
+
* Points to an InputStream object.
|
41 |
+
*/
|
42 |
+
protected $stream;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @var HTML5_TreeBuilder
|
46 |
+
*
|
47 |
+
* Tree builder that the tokenizer emits token to.
|
48 |
+
*/
|
49 |
+
private $tree;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @var int
|
53 |
+
*
|
54 |
+
* Current content model we are parsing as.
|
55 |
+
*/
|
56 |
+
protected $content_model;
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Current token that is being built, but not yet emitted. Also
|
60 |
+
* is the last token emitted, if applicable.
|
61 |
+
*/
|
62 |
+
protected $token;
|
63 |
+
|
64 |
+
// These are constants describing the content model
|
65 |
+
const PCDATA = 0;
|
66 |
+
const RCDATA = 1;
|
67 |
+
const CDATA = 2;
|
68 |
+
const PLAINTEXT = 3;
|
69 |
+
|
70 |
+
// These are constants describing tokens
|
71 |
+
// XXX should probably be moved somewhere else, probably the
|
72 |
+
// HTML5 class.
|
73 |
+
const DOCTYPE = 0;
|
74 |
+
const STARTTAG = 1;
|
75 |
+
const ENDTAG = 2;
|
76 |
+
const COMMENT = 3;
|
77 |
+
const CHARACTER = 4;
|
78 |
+
const SPACECHARACTER = 5;
|
79 |
+
const EOF = 6;
|
80 |
+
const PARSEERROR = 7;
|
81 |
+
|
82 |
+
// These are constants representing bunches of characters.
|
83 |
+
const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
84 |
+
const UPPER_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
85 |
+
const LOWER_ALPHA = 'abcdefghijklmnopqrstuvwxyz';
|
86 |
+
const DIGIT = '0123456789';
|
87 |
+
const HEX = '0123456789ABCDEFabcdef';
|
88 |
+
const WHITESPACE = "\t\n\x0c ";
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @param $data | Data to parse
|
92 |
+
* @param HTML5_TreeBuilder|null $builder
|
93 |
+
*/
|
94 |
+
public function __construct($data, $builder = null) {
|
95 |
+
$this->stream = new HTML5_InputStream($data);
|
96 |
+
if (!$builder) {
|
97 |
+
$this->tree = new HTML5_TreeBuilder;
|
98 |
+
} else {
|
99 |
+
$this->tree = $builder;
|
100 |
+
}
|
101 |
+
$this->content_model = self::PCDATA;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* @param null $context
|
106 |
+
*/
|
107 |
+
public function parseFragment($context = null) {
|
108 |
+
$this->tree->setupContext($context);
|
109 |
+
if ($this->tree->content_model) {
|
110 |
+
$this->content_model = $this->tree->content_model;
|
111 |
+
$this->tree->content_model = null;
|
112 |
+
}
|
113 |
+
$this->parse();
|
114 |
+
}
|
115 |
+
|
116 |
+
// XXX maybe convert this into an iterator? regardless, this function
|
117 |
+
// and the save function should go into a Parser facade of some sort
|
118 |
+
/**
|
119 |
+
* Performs the actual parsing of the document.
|
120 |
+
*/
|
121 |
+
public function parse() {
|
122 |
+
// Current state
|
123 |
+
$state = 'data';
|
124 |
+
// This is used to avoid having to have look-behind in the data state.
|
125 |
+
$lastFourChars = '';
|
126 |
+
/**
|
127 |
+
* Escape flag as specified by the HTML5 specification: "used to
|
128 |
+
* control the behavior of the tokeniser. It is either true or
|
129 |
+
* false, and initially must be set to the false state."
|
130 |
+
*/
|
131 |
+
$escape = false;
|
132 |
+
//echo "\n\n";
|
133 |
+
while($state !== null) {
|
134 |
+
|
135 |
+
/*echo $state . ' ';
|
136 |
+
switch ($this->content_model) {
|
137 |
+
case self::PCDATA: echo 'PCDATA'; break;
|
138 |
+
case self::RCDATA: echo 'RCDATA'; break;
|
139 |
+
case self::CDATA: echo 'CDATA'; break;
|
140 |
+
case self::PLAINTEXT: echo 'PLAINTEXT'; break;
|
141 |
+
}
|
142 |
+
if ($escape) echo " escape";
|
143 |
+
echo "\n";*/
|
144 |
+
|
145 |
+
switch($state) {
|
146 |
+
case 'data':
|
147 |
+
|
148 |
+
/* Consume the next input character */
|
149 |
+
$char = $this->stream->char();
|
150 |
+
$lastFourChars .= $char;
|
151 |
+
if (strlen($lastFourChars) > 4) {
|
152 |
+
$lastFourChars = substr($lastFourChars, -4);
|
153 |
+
}
|
154 |
+
|
155 |
+
// see below for meaning
|
156 |
+
$hyp_cond =
|
157 |
+
!$escape &&
|
158 |
+
(
|
159 |
+
$this->content_model === self::RCDATA ||
|
160 |
+
$this->content_model === self::CDATA
|
161 |
+
);
|
162 |
+
$amp_cond =
|
163 |
+
!$escape &&
|
164 |
+
(
|
165 |
+
$this->content_model === self::PCDATA ||
|
166 |
+
$this->content_model === self::RCDATA
|
167 |
+
);
|
168 |
+
$lt_cond =
|
169 |
+
$this->content_model === self::PCDATA ||
|
170 |
+
(
|
171 |
+
(
|
172 |
+
$this->content_model === self::RCDATA ||
|
173 |
+
$this->content_model === self::CDATA
|
174 |
+
) &&
|
175 |
+
!$escape
|
176 |
+
);
|
177 |
+
$gt_cond =
|
178 |
+
$escape &&
|
179 |
+
(
|
180 |
+
$this->content_model === self::RCDATA ||
|
181 |
+
$this->content_model === self::CDATA
|
182 |
+
);
|
183 |
+
|
184 |
+
if ($char === '&' && $amp_cond === true) {
|
185 |
+
/* U+0026 AMPERSAND (&)
|
186 |
+
When the content model flag is set to one of the PCDATA or RCDATA
|
187 |
+
states and the escape flag is false: switch to the
|
188 |
+
character reference data state. Otherwise: treat it as per
|
189 |
+
the "anything else" entry below. */
|
190 |
+
$state = 'character reference data';
|
191 |
+
|
192 |
+
} elseif (
|
193 |
+
$char === '-' &&
|
194 |
+
$hyp_cond === true &&
|
195 |
+
$lastFourChars === '<!--'
|
196 |
+
) {
|
197 |
+
/*
|
198 |
+
U+002D HYPHEN-MINUS (-)
|
199 |
+
If the content model flag is set to either the RCDATA state or
|
200 |
+
the CDATA state, and the escape flag is false, and there are at
|
201 |
+
least three characters before this one in the input stream, and the
|
202 |
+
last four characters in the input stream, including this one, are
|
203 |
+
U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS,
|
204 |
+
and U+002D HYPHEN-MINUS ("<!--"), then set the escape flag to true. */
|
205 |
+
$escape = true;
|
206 |
+
|
207 |
+
/* In any case, emit the input character as a character token. Stay
|
208 |
+
in the data state. */
|
209 |
+
$this->emitToken(array(
|
210 |
+
'type' => self::CHARACTER,
|
211 |
+
'data' => '-'
|
212 |
+
));
|
213 |
+
// We do the "any case" part as part of "anything else".
|
214 |
+
|
215 |
+
/* U+003C LESS-THAN SIGN (<) */
|
216 |
+
} elseif ($char === '<' && $lt_cond === true) {
|
217 |
+
/* When the content model flag is set to the PCDATA state: switch
|
218 |
+
to the tag open state.
|
219 |
+
|
220 |
+
When the content model flag is set to either the RCDATA state or
|
221 |
+
the CDATA state and the escape flag is false: switch to the tag
|
222 |
+
open state.
|
223 |
+
|
224 |
+
Otherwise: treat it as per the "anything else" entry below. */
|
225 |
+
$state = 'tag open';
|
226 |
+
|
227 |
+
/* U+003E GREATER-THAN SIGN (>) */
|
228 |
+
} elseif (
|
229 |
+
$char === '>' &&
|
230 |
+
$gt_cond === true &&
|
231 |
+
substr($lastFourChars, 1) === '-->'
|
232 |
+
) {
|
233 |
+
/* If the content model flag is set to either the RCDATA state or
|
234 |
+
the CDATA state, and the escape flag is true, and the last three
|
235 |
+
characters in the input stream including this one are U+002D
|
236 |
+
HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN ("-->"),
|
237 |
+
set the escape flag to false. */
|
238 |
+
$escape = false;
|
239 |
+
|
240 |
+
/* In any case, emit the input character as a character token.
|
241 |
+
Stay in the data state. */
|
242 |
+
$this->emitToken(array(
|
243 |
+
'type' => self::CHARACTER,
|
244 |
+
'data' => '>'
|
245 |
+
));
|
246 |
+
// We do the "any case" part as part of "anything else".
|
247 |
+
|
248 |
+
} elseif ($char === false) {
|
249 |
+
/* EOF
|
250 |
+
Emit an end-of-file token. */
|
251 |
+
$state = null;
|
252 |
+
$this->tree->emitToken(array(
|
253 |
+
'type' => self::EOF
|
254 |
+
));
|
255 |
+
|
256 |
+
} elseif ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
257 |
+
// Directly after emitting a token you switch back to the "data
|
258 |
+
// state". At that point spaceCharacters are important so they are
|
259 |
+
// emitted separately.
|
260 |
+
$chars = $this->stream->charsWhile(self::WHITESPACE);
|
261 |
+
$this->emitToken(array(
|
262 |
+
'type' => self::SPACECHARACTER,
|
263 |
+
'data' => $char . $chars
|
264 |
+
));
|
265 |
+
$lastFourChars .= $chars;
|
266 |
+
if (strlen($lastFourChars) > 4) {
|
267 |
+
$lastFourChars = substr($lastFourChars, -4);
|
268 |
+
}
|
269 |
+
} else {
|
270 |
+
/* Anything else
|
271 |
+
THIS IS AN OPTIMIZATION: Get as many character that
|
272 |
+
otherwise would also be treated as a character token and emit it
|
273 |
+
as a single character token. Stay in the data state. */
|
274 |
+
|
275 |
+
$mask = '';
|
276 |
+
if ($hyp_cond === true) {
|
277 |
+
$mask .= '-';
|
278 |
+
}
|
279 |
+
if ($amp_cond === true) {
|
280 |
+
$mask .= '&';
|
281 |
+
}
|
282 |
+
if ($lt_cond === true) {
|
283 |
+
$mask .= '<';
|
284 |
+
}
|
285 |
+
if ($gt_cond === true) {
|
286 |
+
$mask .= '>';
|
287 |
+
}
|
288 |
+
|
289 |
+
if ($mask === '') {
|
290 |
+
$chars = $this->stream->remainingChars();
|
291 |
+
} else {
|
292 |
+
$chars = $this->stream->charsUntil($mask);
|
293 |
+
}
|
294 |
+
|
295 |
+
$this->emitToken(array(
|
296 |
+
'type' => self::CHARACTER,
|
297 |
+
'data' => $char . $chars
|
298 |
+
));
|
299 |
+
|
300 |
+
$lastFourChars .= $chars;
|
301 |
+
if (strlen($lastFourChars) > 4) {
|
302 |
+
$lastFourChars = substr($lastFourChars, -4);
|
303 |
+
}
|
304 |
+
|
305 |
+
$state = 'data';
|
306 |
+
}
|
307 |
+
break;
|
308 |
+
|
309 |
+
case 'character reference data':
|
310 |
+
/* (This cannot happen if the content model flag
|
311 |
+
is set to the CDATA state.) */
|
312 |
+
|
313 |
+
/* Attempt to consume a character reference, with no
|
314 |
+
additional allowed character. */
|
315 |
+
$entity = $this->consumeCharacterReference();
|
316 |
+
|
317 |
+
/* If nothing is returned, emit a U+0026 AMPERSAND
|
318 |
+
character token. Otherwise, emit the character token that
|
319 |
+
was returned. */
|
320 |
+
// This is all done when consuming the character reference.
|
321 |
+
$this->emitToken(array(
|
322 |
+
'type' => self::CHARACTER,
|
323 |
+
'data' => $entity
|
324 |
+
));
|
325 |
+
|
326 |
+
/* Finally, switch to the data state. */
|
327 |
+
$state = 'data';
|
328 |
+
break;
|
329 |
+
|
330 |
+
case 'tag open':
|
331 |
+
$char = $this->stream->char();
|
332 |
+
|
333 |
+
switch ($this->content_model) {
|
334 |
+
case self::RCDATA:
|
335 |
+
case self::CDATA:
|
336 |
+
/* Consume the next input character. If it is a
|
337 |
+
U+002F SOLIDUS (/) character, switch to the close
|
338 |
+
tag open state. Otherwise, emit a U+003C LESS-THAN
|
339 |
+
SIGN character token and reconsume the current input
|
340 |
+
character in the data state. */
|
341 |
+
// We consumed above.
|
342 |
+
|
343 |
+
if ($char === '/') {
|
344 |
+
$state = 'close tag open';
|
345 |
+
} else {
|
346 |
+
$this->emitToken(array(
|
347 |
+
'type' => self::CHARACTER,
|
348 |
+
'data' => '<'
|
349 |
+
));
|
350 |
+
|
351 |
+
$this->stream->unget();
|
352 |
+
|
353 |
+
$state = 'data';
|
354 |
+
}
|
355 |
+
break;
|
356 |
+
|
357 |
+
case self::PCDATA:
|
358 |
+
/* If the content model flag is set to the PCDATA state
|
359 |
+
Consume the next input character: */
|
360 |
+
// We consumed above.
|
361 |
+
|
362 |
+
if ($char === '!') {
|
363 |
+
/* U+0021 EXCLAMATION MARK (!)
|
364 |
+
Switch to the markup declaration open state. */
|
365 |
+
$state = 'markup declaration open';
|
366 |
+
|
367 |
+
} elseif ($char === '/') {
|
368 |
+
/* U+002F SOLIDUS (/)
|
369 |
+
Switch to the close tag open state. */
|
370 |
+
$state = 'close tag open';
|
371 |
+
|
372 |
+
} elseif ('A' <= $char && $char <= 'Z') {
|
373 |
+
/* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
|
374 |
+
Create a new start tag token, set its tag name to the lowercase
|
375 |
+
version of the input character (add 0x0020 to the character's code
|
376 |
+
point), then switch to the tag name state. (Don't emit the token
|
377 |
+
yet; further details will be filled in before it is emitted.) */
|
378 |
+
$this->token = array(
|
379 |
+
'name' => strtolower($char),
|
380 |
+
'type' => self::STARTTAG,
|
381 |
+
'attr' => array()
|
382 |
+
);
|
383 |
+
|
384 |
+
$state = 'tag name';
|
385 |
+
|
386 |
+
} elseif ('a' <= $char && $char <= 'z') {
|
387 |
+
/* U+0061 LATIN SMALL LETTER A through to U+007A LATIN SMALL LETTER Z
|
388 |
+
Create a new start tag token, set its tag name to the input
|
389 |
+
character, then switch to the tag name state. (Don't emit
|
390 |
+
the token yet; further details will be filled in before it
|
391 |
+
is emitted.) */
|
392 |
+
$this->token = array(
|
393 |
+
'name' => $char,
|
394 |
+
'type' => self::STARTTAG,
|
395 |
+
'attr' => array()
|
396 |
+
);
|
397 |
+
|
398 |
+
$state = 'tag name';
|
399 |
+
|
400 |
+
} elseif ($char === '>') {
|
401 |
+
/* U+003E GREATER-THAN SIGN (>)
|
402 |
+
Parse error. Emit a U+003C LESS-THAN SIGN character token and a
|
403 |
+
U+003E GREATER-THAN SIGN character token. Switch to the data state. */
|
404 |
+
$this->emitToken(array(
|
405 |
+
'type' => self::PARSEERROR,
|
406 |
+
'data' => 'expected-tag-name-but-got-right-bracket'
|
407 |
+
));
|
408 |
+
$this->emitToken(array(
|
409 |
+
'type' => self::CHARACTER,
|
410 |
+
'data' => '<>'
|
411 |
+
));
|
412 |
+
|
413 |
+
$state = 'data';
|
414 |
+
|
415 |
+
} elseif ($char === '?') {
|
416 |
+
/* U+003F QUESTION MARK (?)
|
417 |
+
Parse error. Switch to the bogus comment state. */
|
418 |
+
$this->emitToken(array(
|
419 |
+
'type' => self::PARSEERROR,
|
420 |
+
'data' => 'expected-tag-name-but-got-question-mark'
|
421 |
+
));
|
422 |
+
$this->token = array(
|
423 |
+
'data' => '?',
|
424 |
+
'type' => self::COMMENT
|
425 |
+
);
|
426 |
+
$state = 'bogus comment';
|
427 |
+
|
428 |
+
} else {
|
429 |
+
/* Anything else
|
430 |
+
Parse error. Emit a U+003C LESS-THAN SIGN character token and
|
431 |
+
reconsume the current input character in the data state. */
|
432 |
+
$this->emitToken(array(
|
433 |
+
'type' => self::PARSEERROR,
|
434 |
+
'data' => 'expected-tag-name'
|
435 |
+
));
|
436 |
+
$this->emitToken(array(
|
437 |
+
'type' => self::CHARACTER,
|
438 |
+
'data' => '<'
|
439 |
+
));
|
440 |
+
|
441 |
+
$state = 'data';
|
442 |
+
$this->stream->unget();
|
443 |
+
}
|
444 |
+
break;
|
445 |
+
}
|
446 |
+
break;
|
447 |
+
|
448 |
+
case 'close tag open':
|
449 |
+
if (
|
450 |
+
$this->content_model === self::RCDATA ||
|
451 |
+
$this->content_model === self::CDATA
|
452 |
+
) {
|
453 |
+
/* If the content model flag is set to the RCDATA or CDATA
|
454 |
+
states... */
|
455 |
+
$name = strtolower($this->stream->charsWhile(self::ALPHA));
|
456 |
+
$following = $this->stream->char();
|
457 |
+
$this->stream->unget();
|
458 |
+
if (
|
459 |
+
!$this->token ||
|
460 |
+
$this->token['name'] !== $name ||
|
461 |
+
$this->token['name'] === $name && !in_array($following, array("\x09", "\x0A", "\x0C", "\x20", "\x3E", "\x2F", false))
|
462 |
+
) {
|
463 |
+
/* if no start tag token has ever been emitted by this instance
|
464 |
+
of the tokenizer (fragment case), or, if the next few
|
465 |
+
characters do not match the tag name of the last start tag
|
466 |
+
token emitted (compared in an ASCII case-insensitive manner),
|
467 |
+
or if they do but they are not immediately followed by one of
|
468 |
+
the following characters:
|
469 |
+
|
470 |
+
* U+0009 CHARACTER TABULATION
|
471 |
+
* U+000A LINE FEED (LF)
|
472 |
+
* U+000C FORM FEED (FF)
|
473 |
+
* U+0020 SPACE
|
474 |
+
* U+003E GREATER-THAN SIGN (>)
|
475 |
+
* U+002F SOLIDUS (/)
|
476 |
+
* EOF
|
477 |
+
|
478 |
+
...then emit a U+003C LESS-THAN SIGN character token, a
|
479 |
+
U+002F SOLIDUS character token, and switch to the data
|
480 |
+
state to process the next input character. */
|
481 |
+
// XXX: Probably ought to replace in_array with $following === x ||...
|
482 |
+
|
483 |
+
// We also need to emit $name now we've consumed that, as we
|
484 |
+
// know it'll just be emitted as a character token.
|
485 |
+
$this->emitToken(array(
|
486 |
+
'type' => self::CHARACTER,
|
487 |
+
'data' => '</' . $name
|
488 |
+
));
|
489 |
+
|
490 |
+
$state = 'data';
|
491 |
+
} else {
|
492 |
+
// This matches what would happen if we actually did the
|
493 |
+
// otherwise below (but we can't because we've consumed too
|
494 |
+
// much).
|
495 |
+
|
496 |
+
// Start the end tag token with the name we already have.
|
497 |
+
$this->token = array(
|
498 |
+
'name' => $name,
|
499 |
+
'type' => self::ENDTAG
|
500 |
+
);
|
501 |
+
|
502 |
+
// Change to tag name state.
|
503 |
+
$state = 'tag name';
|
504 |
+
}
|
505 |
+
} elseif ($this->content_model === self::PCDATA) {
|
506 |
+
/* Otherwise, if the content model flag is set to the PCDATA
|
507 |
+
state [...]: */
|
508 |
+
$char = $this->stream->char();
|
509 |
+
|
510 |
+
if ('A' <= $char && $char <= 'Z') {
|
511 |
+
/* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
|
512 |
+
Create a new end tag token, set its tag name to the lowercase version
|
513 |
+
of the input character (add 0x0020 to the character's code point), then
|
514 |
+
switch to the tag name state. (Don't emit the token yet; further details
|
515 |
+
will be filled in before it is emitted.) */
|
516 |
+
$this->token = array(
|
517 |
+
'name' => strtolower($char),
|
518 |
+
'type' => self::ENDTAG
|
519 |
+
);
|
520 |
+
|
521 |
+
$state = 'tag name';
|
522 |
+
|
523 |
+
} elseif ('a' <= $char && $char <= 'z') {
|
524 |
+
/* U+0061 LATIN SMALL LETTER A through to U+007A LATIN SMALL LETTER Z
|
525 |
+
Create a new end tag token, set its tag name to the
|
526 |
+
input character, then switch to the tag name state.
|
527 |
+
(Don't emit the token yet; further details will be
|
528 |
+
filled in before it is emitted.) */
|
529 |
+
$this->token = array(
|
530 |
+
'name' => $char,
|
531 |
+
'type' => self::ENDTAG
|
532 |
+
);
|
533 |
+
|
534 |
+
$state = 'tag name';
|
535 |
+
|
536 |
+
} elseif ($char === '>') {
|
537 |
+
/* U+003E GREATER-THAN SIGN (>)
|
538 |
+
Parse error. Switch to the data state. */
|
539 |
+
$this->emitToken(array(
|
540 |
+
'type' => self::PARSEERROR,
|
541 |
+
'data' => 'expected-closing-tag-but-got-right-bracket'
|
542 |
+
));
|
543 |
+
$state = 'data';
|
544 |
+
|
545 |
+
} elseif ($char === false) {
|
546 |
+
/* EOF
|
547 |
+
Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F
|
548 |
+
SOLIDUS character token. Reconsume the EOF character in the data state. */
|
549 |
+
$this->emitToken(array(
|
550 |
+
'type' => self::PARSEERROR,
|
551 |
+
'data' => 'expected-closing-tag-but-got-eof'
|
552 |
+
));
|
553 |
+
$this->emitToken(array(
|
554 |
+
'type' => self::CHARACTER,
|
555 |
+
'data' => '</'
|
556 |
+
));
|
557 |
+
|
558 |
+
$this->stream->unget();
|
559 |
+
$state = 'data';
|
560 |
+
|
561 |
+
} else {
|
562 |
+
/* Parse error. Switch to the bogus comment state. */
|
563 |
+
$this->emitToken(array(
|
564 |
+
'type' => self::PARSEERROR,
|
565 |
+
'data' => 'expected-closing-tag-but-got-char'
|
566 |
+
));
|
567 |
+
$this->token = array(
|
568 |
+
'data' => $char,
|
569 |
+
'type' => self::COMMENT
|
570 |
+
);
|
571 |
+
$state = 'bogus comment';
|
572 |
+
}
|
573 |
+
}
|
574 |
+
break;
|
575 |
+
|
576 |
+
case 'tag name':
|
577 |
+
/* Consume the next input character: */
|
578 |
+
$char = $this->stream->char();
|
579 |
+
|
580 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
581 |
+
/* U+0009 CHARACTER TABULATION
|
582 |
+
U+000A LINE FEED (LF)
|
583 |
+
U+000C FORM FEED (FF)
|
584 |
+
U+0020 SPACE
|
585 |
+
Switch to the before attribute name state. */
|
586 |
+
$state = 'before attribute name';
|
587 |
+
|
588 |
+
} elseif ($char === '/') {
|
589 |
+
/* U+002F SOLIDUS (/)
|
590 |
+
Switch to the self-closing start tag state. */
|
591 |
+
$state = 'self-closing start tag';
|
592 |
+
|
593 |
+
} elseif ($char === '>') {
|
594 |
+
/* U+003E GREATER-THAN SIGN (>)
|
595 |
+
Emit the current tag token. Switch to the data state. */
|
596 |
+
$this->emitToken($this->token);
|
597 |
+
$state = 'data';
|
598 |
+
|
599 |
+
} elseif ('A' <= $char && $char <= 'Z') {
|
600 |
+
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
601 |
+
Append the lowercase version of the current input
|
602 |
+
character (add 0x0020 to the character's code point) to
|
603 |
+
the current tag token's tag name. Stay in the tag name state. */
|
604 |
+
$chars = $this->stream->charsWhile(self::UPPER_ALPHA);
|
605 |
+
|
606 |
+
$this->token['name'] .= strtolower($char . $chars);
|
607 |
+
$state = 'tag name';
|
608 |
+
|
609 |
+
} elseif ($char === false) {
|
610 |
+
/* EOF
|
611 |
+
Parse error. Reconsume the EOF character in the data state. */
|
612 |
+
$this->emitToken(array(
|
613 |
+
'type' => self::PARSEERROR,
|
614 |
+
'data' => 'eof-in-tag-name'
|
615 |
+
));
|
616 |
+
|
617 |
+
$this->stream->unget();
|
618 |
+
$state = 'data';
|
619 |
+
|
620 |
+
} else {
|
621 |
+
/* Anything else
|
622 |
+
Append the current input character to the current tag token's tag name.
|
623 |
+
Stay in the tag name state. */
|
624 |
+
$chars = $this->stream->charsUntil("\t\n\x0C />" . self::UPPER_ALPHA);
|
625 |
+
|
626 |
+
$this->token['name'] .= $char . $chars;
|
627 |
+
$state = 'tag name';
|
628 |
+
}
|
629 |
+
break;
|
630 |
+
|
631 |
+
case 'before attribute name':
|
632 |
+
/* Consume the next input character: */
|
633 |
+
$char = $this->stream->char();
|
634 |
+
|
635 |
+
// this conditional is optimized, check bottom
|
636 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
637 |
+
/* U+0009 CHARACTER TABULATION
|
638 |
+
U+000A LINE FEED (LF)
|
639 |
+
U+000C FORM FEED (FF)
|
640 |
+
U+0020 SPACE
|
641 |
+
Stay in the before attribute name state. */
|
642 |
+
$state = 'before attribute name';
|
643 |
+
|
644 |
+
} elseif ($char === '/') {
|
645 |
+
/* U+002F SOLIDUS (/)
|
646 |
+
Switch to the self-closing start tag state. */
|
647 |
+
$state = 'self-closing start tag';
|
648 |
+
|
649 |
+
} elseif ($char === '>') {
|
650 |
+
/* U+003E GREATER-THAN SIGN (>)
|
651 |
+
Emit the current tag token. Switch to the data state. */
|
652 |
+
$this->emitToken($this->token);
|
653 |
+
$state = 'data';
|
654 |
+
|
655 |
+
} elseif ('A' <= $char && $char <= 'Z') {
|
656 |
+
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
657 |
+
Start a new attribute in the current tag token. Set that
|
658 |
+
attribute's name to the lowercase version of the current
|
659 |
+
input character (add 0x0020 to the character's code
|
660 |
+
point), and its value to the empty string. Switch to the
|
661 |
+
attribute name state.*/
|
662 |
+
$this->token['attr'][] = array(
|
663 |
+
'name' => strtolower($char),
|
664 |
+
'value' => ''
|
665 |
+
);
|
666 |
+
|
667 |
+
$state = 'attribute name';
|
668 |
+
|
669 |
+
} elseif ($char === false) {
|
670 |
+
/* EOF
|
671 |
+
Parse error. Reconsume the EOF character in the data state. */
|
672 |
+
$this->emitToken(array(
|
673 |
+
'type' => self::PARSEERROR,
|
674 |
+
'data' => 'expected-attribute-name-but-got-eof'
|
675 |
+
));
|
676 |
+
|
677 |
+
$this->stream->unget();
|
678 |
+
$state = 'data';
|
679 |
+
|
680 |
+
} else {
|
681 |
+
/* U+0022 QUOTATION MARK (")
|
682 |
+
U+0027 APOSTROPHE (')
|
683 |
+
U+003C LESS-THAN SIGN (<)
|
684 |
+
U+003D EQUALS SIGN (=)
|
685 |
+
Parse error. Treat it as per the "anything else" entry
|
686 |
+
below. */
|
687 |
+
if ($char === '"' || $char === "'" || $char === '<' || $char === '=') {
|
688 |
+
$this->emitToken(array(
|
689 |
+
'type' => self::PARSEERROR,
|
690 |
+
'data' => 'invalid-character-in-attribute-name'
|
691 |
+
));
|
692 |
+
}
|
693 |
+
|
694 |
+
/* Anything else
|
695 |
+
Start a new attribute in the current tag token. Set that attribute's
|
696 |
+
name to the current input character, and its value to the empty string.
|
697 |
+
Switch to the attribute name state. */
|
698 |
+
$this->token['attr'][] = array(
|
699 |
+
'name' => $char,
|
700 |
+
'value' => ''
|
701 |
+
);
|
702 |
+
|
703 |
+
$state = 'attribute name';
|
704 |
+
}
|
705 |
+
break;
|
706 |
+
|
707 |
+
case 'attribute name':
|
708 |
+
// Consume the next input character:
|
709 |
+
$char = $this->stream->char();
|
710 |
+
|
711 |
+
// this conditional is optimized, check bottom
|
712 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
713 |
+
/* U+0009 CHARACTER TABULATION
|
714 |
+
U+000A LINE FEED (LF)
|
715 |
+
U+000C FORM FEED (FF)
|
716 |
+
U+0020 SPACE
|
717 |
+
Switch to the after attribute name state. */
|
718 |
+
$state = 'after attribute name';
|
719 |
+
|
720 |
+
} elseif ($char === '/') {
|
721 |
+
/* U+002F SOLIDUS (/)
|
722 |
+
Switch to the self-closing start tag state. */
|
723 |
+
$state = 'self-closing start tag';
|
724 |
+
|
725 |
+
} elseif ($char === '=') {
|
726 |
+
/* U+003D EQUALS SIGN (=)
|
727 |
+
Switch to the before attribute value state. */
|
728 |
+
$state = 'before attribute value';
|
729 |
+
|
730 |
+
} elseif ($char === '>') {
|
731 |
+
/* U+003E GREATER-THAN SIGN (>)
|
732 |
+
Emit the current tag token. Switch to the data state. */
|
733 |
+
$this->emitToken($this->token);
|
734 |
+
$state = 'data';
|
735 |
+
|
736 |
+
} elseif ('A' <= $char && $char <= 'Z') {
|
737 |
+
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
738 |
+
Append the lowercase version of the current input
|
739 |
+
character (add 0x0020 to the character's code point) to
|
740 |
+
the current attribute's name. Stay in the attribute name
|
741 |
+
state. */
|
742 |
+
$chars = $this->stream->charsWhile(self::UPPER_ALPHA);
|
743 |
+
|
744 |
+
$last = count($this->token['attr']) - 1;
|
745 |
+
$this->token['attr'][$last]['name'] .= strtolower($char . $chars);
|
746 |
+
|
747 |
+
$state = 'attribute name';
|
748 |
+
|
749 |
+
} elseif ($char === false) {
|
750 |
+
/* EOF
|
751 |
+
Parse error. Reconsume the EOF character in the data state. */
|
752 |
+
$this->emitToken(array(
|
753 |
+
'type' => self::PARSEERROR,
|
754 |
+
'data' => 'eof-in-attribute-name'
|
755 |
+
));
|
756 |
+
|
757 |
+
$this->stream->unget();
|
758 |
+
$state = 'data';
|
759 |
+
|
760 |
+
} else {
|
761 |
+
/* U+0022 QUOTATION MARK (")
|
762 |
+
U+0027 APOSTROPHE (')
|
763 |
+
U+003C LESS-THAN SIGN (<)
|
764 |
+
Parse error. Treat it as per the "anything else"
|
765 |
+
entry below. */
|
766 |
+
if ($char === '"' || $char === "'" || $char === '<') {
|
767 |
+
$this->emitToken(array(
|
768 |
+
'type' => self::PARSEERROR,
|
769 |
+
'data' => 'invalid-character-in-attribute-name'
|
770 |
+
));
|
771 |
+
}
|
772 |
+
|
773 |
+
/* Anything else
|
774 |
+
Append the current input character to the current attribute's name.
|
775 |
+
Stay in the attribute name state. */
|
776 |
+
$chars = $this->stream->charsUntil("\t\n\x0C /=>\"'" . self::UPPER_ALPHA);
|
777 |
+
|
778 |
+
$last = count($this->token['attr']) - 1;
|
779 |
+
$this->token['attr'][$last]['name'] .= $char . $chars;
|
780 |
+
|
781 |
+
$state = 'attribute name';
|
782 |
+
}
|
783 |
+
|
784 |
+
/* When the user agent leaves the attribute name state
|
785 |
+
(and before emitting the tag token, if appropriate), the
|
786 |
+
complete attribute's name must be compared to the other
|
787 |
+
attributes on the same token; if there is already an
|
788 |
+
attribute on the token with the exact same name, then this
|
789 |
+
is a parse error and the new attribute must be dropped, along
|
790 |
+
with the value that gets associated with it (if any). */
|
791 |
+
// this might be implemented in the emitToken method
|
792 |
+
break;
|
793 |
+
|
794 |
+
case 'after attribute name':
|
795 |
+
// Consume the next input character:
|
796 |
+
$char = $this->stream->char();
|
797 |
+
|
798 |
+
// this is an optimized conditional, check the bottom
|
799 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
800 |
+
/* U+0009 CHARACTER TABULATION
|
801 |
+
U+000A LINE FEED (LF)
|
802 |
+
U+000C FORM FEED (FF)
|
803 |
+
U+0020 SPACE
|
804 |
+
Stay in the after attribute name state. */
|
805 |
+
$state = 'after attribute name';
|
806 |
+
|
807 |
+
} elseif ($char === '/') {
|
808 |
+
/* U+002F SOLIDUS (/)
|
809 |
+
Switch to the self-closing start tag state. */
|
810 |
+
$state = 'self-closing start tag';
|
811 |
+
|
812 |
+
} elseif ($char === '=') {
|
813 |
+
/* U+003D EQUALS SIGN (=)
|
814 |
+
Switch to the before attribute value state. */
|
815 |
+
$state = 'before attribute value';
|
816 |
+
|
817 |
+
} elseif ($char === '>') {
|
818 |
+
/* U+003E GREATER-THAN SIGN (>)
|
819 |
+
Emit the current tag token. Switch to the data state. */
|
820 |
+
$this->emitToken($this->token);
|
821 |
+
$state = 'data';
|
822 |
+
|
823 |
+
} elseif ('A' <= $char && $char <= 'Z') {
|
824 |
+
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
825 |
+
Start a new attribute in the current tag token. Set that
|
826 |
+
attribute's name to the lowercase version of the current
|
827 |
+
input character (add 0x0020 to the character's code
|
828 |
+
point), and its value to the empty string. Switch to the
|
829 |
+
attribute name state. */
|
830 |
+
$this->token['attr'][] = array(
|
831 |
+
'name' => strtolower($char),
|
832 |
+
'value' => ''
|
833 |
+
);
|
834 |
+
|
835 |
+
$state = 'attribute name';
|
836 |
+
|
837 |
+
} elseif ($char === false) {
|
838 |
+
/* EOF
|
839 |
+
Parse error. Reconsume the EOF character in the data state. */
|
840 |
+
$this->emitToken(array(
|
841 |
+
'type' => self::PARSEERROR,
|
842 |
+
'data' => 'expected-end-of-tag-but-got-eof'
|
843 |
+
));
|
844 |
+
|
845 |
+
$this->stream->unget();
|
846 |
+
$state = 'data';
|
847 |
+
|
848 |
+
} else {
|
849 |
+
/* U+0022 QUOTATION MARK (")
|
850 |
+
U+0027 APOSTROPHE (')
|
851 |
+
U+003C LESS-THAN SIGN(<)
|
852 |
+
Parse error. Treat it as per the "anything else"
|
853 |
+
entry below. */
|
854 |
+
if ($char === '"' || $char === "'" || $char === "<") {
|
855 |
+
$this->emitToken(array(
|
856 |
+
'type' => self::PARSEERROR,
|
857 |
+
'data' => 'invalid-character-after-attribute-name'
|
858 |
+
));
|
859 |
+
}
|
860 |
+
|
861 |
+
/* Anything else
|
862 |
+
Start a new attribute in the current tag token. Set that attribute's
|
863 |
+
name to the current input character, and its value to the empty string.
|
864 |
+
Switch to the attribute name state. */
|
865 |
+
$this->token['attr'][] = array(
|
866 |
+
'name' => $char,
|
867 |
+
'value' => ''
|
868 |
+
);
|
869 |
+
|
870 |
+
$state = 'attribute name';
|
871 |
+
}
|
872 |
+
break;
|
873 |
+
|
874 |
+
case 'before attribute value':
|
875 |
+
// Consume the next input character:
|
876 |
+
$char = $this->stream->char();
|
877 |
+
|
878 |
+
// this is an optimized conditional
|
879 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
880 |
+
/* U+0009 CHARACTER TABULATION
|
881 |
+
U+000A LINE FEED (LF)
|
882 |
+
U+000C FORM FEED (FF)
|
883 |
+
U+0020 SPACE
|
884 |
+
Stay in the before attribute value state. */
|
885 |
+
$state = 'before attribute value';
|
886 |
+
|
887 |
+
} elseif ($char === '"') {
|
888 |
+
/* U+0022 QUOTATION MARK (")
|
889 |
+
Switch to the attribute value (double-quoted) state. */
|
890 |
+
$state = 'attribute value (double-quoted)';
|
891 |
+
|
892 |
+
} elseif ($char === '&') {
|
893 |
+
/* U+0026 AMPERSAND (&)
|
894 |
+
Switch to the attribute value (unquoted) state and reconsume
|
895 |
+
this input character. */
|
896 |
+
$this->stream->unget();
|
897 |
+
$state = 'attribute value (unquoted)';
|
898 |
+
|
899 |
+
} elseif ($char === '\'') {
|
900 |
+
/* U+0027 APOSTROPHE (')
|
901 |
+
Switch to the attribute value (single-quoted) state. */
|
902 |
+
$state = 'attribute value (single-quoted)';
|
903 |
+
|
904 |
+
} elseif ($char === '>') {
|
905 |
+
/* U+003E GREATER-THAN SIGN (>)
|
906 |
+
Parse error. Emit the current tag token. Switch to the data state. */
|
907 |
+
$this->emitToken(array(
|
908 |
+
'type' => self::PARSEERROR,
|
909 |
+
'data' => 'expected-attribute-value-but-got-right-bracket'
|
910 |
+
));
|
911 |
+
$this->emitToken($this->token);
|
912 |
+
$state = 'data';
|
913 |
+
|
914 |
+
} elseif ($char === false) {
|
915 |
+
/* EOF
|
916 |
+
Parse error. Reconsume the EOF character in the data state. */
|
917 |
+
$this->emitToken(array(
|
918 |
+
'type' => self::PARSEERROR,
|
919 |
+
'data' => 'expected-attribute-value-but-got-eof'
|
920 |
+
));
|
921 |
+
$this->stream->unget();
|
922 |
+
$state = 'data';
|
923 |
+
|
924 |
+
} else {
|
925 |
+
/* U+003D EQUALS SIGN (=)
|
926 |
+
* U+003C LESS-THAN SIGN (<)
|
927 |
+
Parse error. Treat it as per the "anything else" entry below. */
|
928 |
+
if ($char === '=' || $char === '<') {
|
929 |
+
$this->emitToken(array(
|
930 |
+
'type' => self::PARSEERROR,
|
931 |
+
'data' => 'equals-in-unquoted-attribute-value'
|
932 |
+
));
|
933 |
+
}
|
934 |
+
|
935 |
+
/* Anything else
|
936 |
+
Append the current input character to the current attribute's value.
|
937 |
+
Switch to the attribute value (unquoted) state. */
|
938 |
+
$last = count($this->token['attr']) - 1;
|
939 |
+
$this->token['attr'][$last]['value'] .= $char;
|
940 |
+
|
941 |
+
$state = 'attribute value (unquoted)';
|
942 |
+
}
|
943 |
+
break;
|
944 |
+
|
945 |
+
case 'attribute value (double-quoted)':
|
946 |
+
// Consume the next input character:
|
947 |
+
$char = $this->stream->char();
|
948 |
+
|
949 |
+
if ($char === '"') {
|
950 |
+
/* U+0022 QUOTATION MARK (")
|
951 |
+
Switch to the after attribute value (quoted) state. */
|
952 |
+
$state = 'after attribute value (quoted)';
|
953 |
+
|
954 |
+
} elseif ($char === '&') {
|
955 |
+
/* U+0026 AMPERSAND (&)
|
956 |
+
Switch to the character reference in attribute value
|
957 |
+
state, with the additional allowed character
|
958 |
+
being U+0022 QUOTATION MARK ("). */
|
959 |
+
$this->characterReferenceInAttributeValue('"');
|
960 |
+
|
961 |
+
} elseif ($char === false) {
|
962 |
+
/* EOF
|
963 |
+
Parse error. Reconsume the EOF character in the data state. */
|
964 |
+
$this->emitToken(array(
|
965 |
+
'type' => self::PARSEERROR,
|
966 |
+
'data' => 'eof-in-attribute-value-double-quote'
|
967 |
+
));
|
968 |
+
|
969 |
+
$this->stream->unget();
|
970 |
+
$state = 'data';
|
971 |
+
|
972 |
+
} else {
|
973 |
+
/* Anything else
|
974 |
+
Append the current input character to the current attribute's value.
|
975 |
+
Stay in the attribute value (double-quoted) state. */
|
976 |
+
$chars = $this->stream->charsUntil('"&');
|
977 |
+
|
978 |
+
$last = count($this->token['attr']) - 1;
|
979 |
+
$this->token['attr'][$last]['value'] .= $char . $chars;
|
980 |
+
|
981 |
+
$state = 'attribute value (double-quoted)';
|
982 |
+
}
|
983 |
+
break;
|
984 |
+
|
985 |
+
case 'attribute value (single-quoted)':
|
986 |
+
// Consume the next input character:
|
987 |
+
$char = $this->stream->char();
|
988 |
+
|
989 |
+
if ($char === "'") {
|
990 |
+
/* U+0022 QUOTATION MARK (')
|
991 |
+
Switch to the after attribute value state. */
|
992 |
+
$state = 'after attribute value (quoted)';
|
993 |
+
|
994 |
+
} elseif ($char === '&') {
|
995 |
+
/* U+0026 AMPERSAND (&)
|
996 |
+
Switch to the entity in attribute value state. */
|
997 |
+
$this->characterReferenceInAttributeValue("'");
|
998 |
+
|
999 |
+
} elseif ($char === false) {
|
1000 |
+
/* EOF
|
1001 |
+
Parse error. Reconsume the EOF character in the data state. */
|
1002 |
+
$this->emitToken(array(
|
1003 |
+
'type' => self::PARSEERROR,
|
1004 |
+
'data' => 'eof-in-attribute-value-single-quote'
|
1005 |
+
));
|
1006 |
+
|
1007 |
+
$this->stream->unget();
|
1008 |
+
$state = 'data';
|
1009 |
+
|
1010 |
+
} else {
|
1011 |
+
/* Anything else
|
1012 |
+
Append the current input character to the current attribute's value.
|
1013 |
+
Stay in the attribute value (single-quoted) state. */
|
1014 |
+
$chars = $this->stream->charsUntil("'&");
|
1015 |
+
|
1016 |
+
$last = count($this->token['attr']) - 1;
|
1017 |
+
$this->token['attr'][$last]['value'] .= $char . $chars;
|
1018 |
+
|
1019 |
+
$state = 'attribute value (single-quoted)';
|
1020 |
+
}
|
1021 |
+
break;
|
1022 |
+
|
1023 |
+
case 'attribute value (unquoted)':
|
1024 |
+
// Consume the next input character:
|
1025 |
+
$char = $this->stream->char();
|
1026 |
+
|
1027 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1028 |
+
/* U+0009 CHARACTER TABULATION
|
1029 |
+
U+000A LINE FEED (LF)
|
1030 |
+
U+000C FORM FEED (FF)
|
1031 |
+
U+0020 SPACE
|
1032 |
+
Switch to the before attribute name state. */
|
1033 |
+
$state = 'before attribute name';
|
1034 |
+
|
1035 |
+
} elseif ($char === '&') {
|
1036 |
+
/* U+0026 AMPERSAND (&)
|
1037 |
+
Switch to the entity in attribute value state, with the
|
1038 |
+
additional allowed character being U+003E
|
1039 |
+
GREATER-THAN SIGN (>). */
|
1040 |
+
$this->characterReferenceInAttributeValue('>');
|
1041 |
+
|
1042 |
+
} elseif ($char === '>') {
|
1043 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1044 |
+
Emit the current tag token. Switch to the data state. */
|
1045 |
+
$this->emitToken($this->token);
|
1046 |
+
$state = 'data';
|
1047 |
+
|
1048 |
+
} elseif ($char === false) {
|
1049 |
+
/* EOF
|
1050 |
+
Parse error. Reconsume the EOF character in the data state. */
|
1051 |
+
$this->emitToken(array(
|
1052 |
+
'type' => self::PARSEERROR,
|
1053 |
+
'data' => 'eof-in-attribute-value-no-quotes'
|
1054 |
+
));
|
1055 |
+
$this->stream->unget();
|
1056 |
+
$state = 'data';
|
1057 |
+
|
1058 |
+
} else {
|
1059 |
+
/* U+0022 QUOTATION MARK (")
|
1060 |
+
U+0027 APOSTROPHE (')
|
1061 |
+
U+003C LESS-THAN SIGN (<)
|
1062 |
+
U+003D EQUALS SIGN (=)
|
1063 |
+
Parse error. Treat it as per the "anything else"
|
1064 |
+
entry below. */
|
1065 |
+
if ($char === '"' || $char === "'" || $char === '=' || $char == '<') {
|
1066 |
+
$this->emitToken(array(
|
1067 |
+
'type' => self::PARSEERROR,
|
1068 |
+
'data' => 'unexpected-character-in-unquoted-attribute-value'
|
1069 |
+
));
|
1070 |
+
}
|
1071 |
+
|
1072 |
+
/* Anything else
|
1073 |
+
Append the current input character to the current attribute's value.
|
1074 |
+
Stay in the attribute value (unquoted) state. */
|
1075 |
+
$chars = $this->stream->charsUntil("\t\n\x0c &>\"'=");
|
1076 |
+
|
1077 |
+
$last = count($this->token['attr']) - 1;
|
1078 |
+
$this->token['attr'][$last]['value'] .= $char . $chars;
|
1079 |
+
|
1080 |
+
$state = 'attribute value (unquoted)';
|
1081 |
+
}
|
1082 |
+
break;
|
1083 |
+
|
1084 |
+
case 'after attribute value (quoted)':
|
1085 |
+
/* Consume the next input character: */
|
1086 |
+
$char = $this->stream->char();
|
1087 |
+
|
1088 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1089 |
+
/* U+0009 CHARACTER TABULATION
|
1090 |
+
U+000A LINE FEED (LF)
|
1091 |
+
U+000C FORM FEED (FF)
|
1092 |
+
U+0020 SPACE
|
1093 |
+
Switch to the before attribute name state. */
|
1094 |
+
$state = 'before attribute name';
|
1095 |
+
|
1096 |
+
} elseif ($char === '/') {
|
1097 |
+
/* U+002F SOLIDUS (/)
|
1098 |
+
Switch to the self-closing start tag state. */
|
1099 |
+
$state = 'self-closing start tag';
|
1100 |
+
|
1101 |
+
} elseif ($char === '>') {
|
1102 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1103 |
+
Emit the current tag token. Switch to the data state. */
|
1104 |
+
$this->emitToken($this->token);
|
1105 |
+
$state = 'data';
|
1106 |
+
|
1107 |
+
} elseif ($char === false) {
|
1108 |
+
/* EOF
|
1109 |
+
Parse error. Reconsume the EOF character in the data state. */
|
1110 |
+
$this->emitToken(array(
|
1111 |
+
'type' => self::PARSEERROR,
|
1112 |
+
'data' => 'unexpected-EOF-after-attribute-value'
|
1113 |
+
));
|
1114 |
+
$this->stream->unget();
|
1115 |
+
$state = 'data';
|
1116 |
+
|
1117 |
+
} else {
|
1118 |
+
/* Anything else
|
1119 |
+
Parse error. Reconsume the character in the before attribute
|
1120 |
+
name state. */
|
1121 |
+
$this->emitToken(array(
|
1122 |
+
'type' => self::PARSEERROR,
|
1123 |
+
'data' => 'unexpected-character-after-attribute-value'
|
1124 |
+
));
|
1125 |
+
$this->stream->unget();
|
1126 |
+
$state = 'before attribute name';
|
1127 |
+
}
|
1128 |
+
break;
|
1129 |
+
|
1130 |
+
case 'self-closing start tag':
|
1131 |
+
/* Consume the next input character: */
|
1132 |
+
$char = $this->stream->char();
|
1133 |
+
|
1134 |
+
if ($char === '>') {
|
1135 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1136 |
+
Set the self-closing flag of the current tag token.
|
1137 |
+
Emit the current tag token. Switch to the data state. */
|
1138 |
+
// not sure if this is the name we want
|
1139 |
+
$this->token['self-closing'] = true;
|
1140 |
+
$this->emitToken($this->token);
|
1141 |
+
$state = 'data';
|
1142 |
+
|
1143 |
+
} elseif ($char === false) {
|
1144 |
+
/* EOF
|
1145 |
+
Parse error. Reconsume the EOF character in the data state. */
|
1146 |
+
$this->emitToken(array(
|
1147 |
+
'type' => self::PARSEERROR,
|
1148 |
+
'data' => 'unexpected-eof-after-self-closing'
|
1149 |
+
));
|
1150 |
+
$this->stream->unget();
|
1151 |
+
$state = 'data';
|
1152 |
+
|
1153 |
+
} else {
|
1154 |
+
/* Anything else
|
1155 |
+
Parse error. Reconsume the character in the before attribute name state. */
|
1156 |
+
$this->emitToken(array(
|
1157 |
+
'type' => self::PARSEERROR,
|
1158 |
+
'data' => 'unexpected-character-after-self-closing'
|
1159 |
+
));
|
1160 |
+
$this->stream->unget();
|
1161 |
+
$state = 'before attribute name';
|
1162 |
+
}
|
1163 |
+
break;
|
1164 |
+
|
1165 |
+
case 'bogus comment':
|
1166 |
+
/* (This can only happen if the content model flag is set to the PCDATA state.) */
|
1167 |
+
/* Consume every character up to the first U+003E GREATER-THAN SIGN
|
1168 |
+
character (>) or the end of the file (EOF), whichever comes first. Emit
|
1169 |
+
a comment token whose data is the concatenation of all the characters
|
1170 |
+
starting from and including the character that caused the state machine
|
1171 |
+
to switch into the bogus comment state, up to and including the last
|
1172 |
+
consumed character before the U+003E character, if any, or up to the
|
1173 |
+
end of the file otherwise. (If the comment was started by the end of
|
1174 |
+
the file (EOF), the token is empty.) */
|
1175 |
+
$this->token['data'] .= (string) $this->stream->charsUntil('>');
|
1176 |
+
$this->stream->char();
|
1177 |
+
|
1178 |
+
$this->emitToken($this->token);
|
1179 |
+
|
1180 |
+
/* Switch to the data state. */
|
1181 |
+
$state = 'data';
|
1182 |
+
break;
|
1183 |
+
|
1184 |
+
case 'markup declaration open':
|
1185 |
+
// Consume for below
|
1186 |
+
$hyphens = $this->stream->charsWhile('-', 2);
|
1187 |
+
if ($hyphens === '-') {
|
1188 |
+
$this->stream->unget();
|
1189 |
+
}
|
1190 |
+
if ($hyphens !== '--') {
|
1191 |
+
$alpha = $this->stream->charsWhile(self::ALPHA, 7);
|
1192 |
+
}
|
1193 |
+
|
1194 |
+
/* If the next two characters are both U+002D HYPHEN-MINUS (-)
|
1195 |
+
characters, consume those two characters, create a comment token whose
|
1196 |
+
data is the empty string, and switch to the comment state. */
|
1197 |
+
if ($hyphens === '--') {
|
1198 |
+
$state = 'comment start';
|
1199 |
+
$this->token = array(
|
1200 |
+
'data' => '',
|
1201 |
+
'type' => self::COMMENT
|
1202 |
+
);
|
1203 |
+
|
1204 |
+
/* Otherwise if the next seven characters are a case-insensitive match
|
1205 |
+
for the word "DOCTYPE", then consume those characters and switch to the
|
1206 |
+
DOCTYPE state. */
|
1207 |
+
} elseif (strtoupper($alpha) === 'DOCTYPE') {
|
1208 |
+
$state = 'DOCTYPE';
|
1209 |
+
|
1210 |
+
// XXX not implemented
|
1211 |
+
/* Otherwise, if the insertion mode is "in foreign content"
|
1212 |
+
and the current node is not an element in the HTML namespace
|
1213 |
+
and the next seven characters are an ASCII case-sensitive
|
1214 |
+
match for the string "[CDATA[" (the five uppercase letters
|
1215 |
+
"CDATA" with a U+005B LEFT SQUARE BRACKET character before
|
1216 |
+
and after), then consume those characters and switch to the
|
1217 |
+
CDATA section state (which is unrelated to the content model
|
1218 |
+
flag's CDATA state). */
|
1219 |
+
|
1220 |
+
/* Otherwise, is is a parse error. Switch to the bogus comment state.
|
1221 |
+
The next character that is consumed, if any, is the first character
|
1222 |
+
that will be in the comment. */
|
1223 |
+
} else {
|
1224 |
+
$this->emitToken(array(
|
1225 |
+
'type' => self::PARSEERROR,
|
1226 |
+
'data' => 'expected-dashes-or-doctype'
|
1227 |
+
));
|
1228 |
+
$this->token = array(
|
1229 |
+
'data' => (string) $alpha,
|
1230 |
+
'type' => self::COMMENT
|
1231 |
+
);
|
1232 |
+
$state = 'bogus comment';
|
1233 |
+
}
|
1234 |
+
break;
|
1235 |
+
|
1236 |
+
case 'comment start':
|
1237 |
+
/* Consume the next input character: */
|
1238 |
+
$char = $this->stream->char();
|
1239 |
+
|
1240 |
+
if ($char === '-') {
|
1241 |
+
/* U+002D HYPHEN-MINUS (-)
|
1242 |
+
Switch to the comment start dash state. */
|
1243 |
+
$state = 'comment start dash';
|
1244 |
+
} elseif ($char === '>') {
|
1245 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1246 |
+
Parse error. Emit the comment token. Switch to the
|
1247 |
+
data state. */
|
1248 |
+
$this->emitToken(array(
|
1249 |
+
'type' => self::PARSEERROR,
|
1250 |
+
'data' => 'incorrect-comment'
|
1251 |
+
));
|
1252 |
+
$this->emitToken($this->token);
|
1253 |
+
$state = 'data';
|
1254 |
+
} elseif ($char === false) {
|
1255 |
+
/* EOF
|
1256 |
+
Parse error. Emit the comment token. Reconsume the
|
1257 |
+
EOF character in the data state. */
|
1258 |
+
$this->emitToken(array(
|
1259 |
+
'type' => self::PARSEERROR,
|
1260 |
+
'data' => 'eof-in-comment'
|
1261 |
+
));
|
1262 |
+
$this->emitToken($this->token);
|
1263 |
+
$this->stream->unget();
|
1264 |
+
$state = 'data';
|
1265 |
+
} else {
|
1266 |
+
/* Anything else
|
1267 |
+
Append the input character to the comment token's
|
1268 |
+
data. Switch to the comment state. */
|
1269 |
+
$this->token['data'] .= $char;
|
1270 |
+
$state = 'comment';
|
1271 |
+
}
|
1272 |
+
break;
|
1273 |
+
|
1274 |
+
case 'comment start dash':
|
1275 |
+
/* Consume the next input character: */
|
1276 |
+
$char = $this->stream->char();
|
1277 |
+
if ($char === '-') {
|
1278 |
+
/* U+002D HYPHEN-MINUS (-)
|
1279 |
+
Switch to the comment end state */
|
1280 |
+
$state = 'comment end';
|
1281 |
+
} elseif ($char === '>') {
|
1282 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1283 |
+
Parse error. Emit the comment token. Switch to the
|
1284 |
+
data state. */
|
1285 |
+
$this->emitToken(array(
|
1286 |
+
'type' => self::PARSEERROR,
|
1287 |
+
'data' => 'incorrect-comment'
|
1288 |
+
));
|
1289 |
+
$this->emitToken($this->token);
|
1290 |
+
$state = 'data';
|
1291 |
+
} elseif ($char === false) {
|
1292 |
+
/* Parse error. Emit the comment token. Reconsume the
|
1293 |
+
EOF character in the data state. */
|
1294 |
+
$this->emitToken(array(
|
1295 |
+
'type' => self::PARSEERROR,
|
1296 |
+
'data' => 'eof-in-comment'
|
1297 |
+
));
|
1298 |
+
$this->emitToken($this->token);
|
1299 |
+
$this->stream->unget();
|
1300 |
+
$state = 'data';
|
1301 |
+
} else {
|
1302 |
+
$this->token['data'] .= '-' . $char;
|
1303 |
+
$state = 'comment';
|
1304 |
+
}
|
1305 |
+
break;
|
1306 |
+
|
1307 |
+
case 'comment':
|
1308 |
+
/* Consume the next input character: */
|
1309 |
+
$char = $this->stream->char();
|
1310 |
+
|
1311 |
+
if ($char === '-') {
|
1312 |
+
/* U+002D HYPHEN-MINUS (-)
|
1313 |
+
Switch to the comment end dash state */
|
1314 |
+
$state = 'comment end dash';
|
1315 |
+
|
1316 |
+
} elseif ($char === false) {
|
1317 |
+
/* EOF
|
1318 |
+
Parse error. Emit the comment token. Reconsume the EOF character
|
1319 |
+
in the data state. */
|
1320 |
+
$this->emitToken(array(
|
1321 |
+
'type' => self::PARSEERROR,
|
1322 |
+
'data' => 'eof-in-comment'
|
1323 |
+
));
|
1324 |
+
$this->emitToken($this->token);
|
1325 |
+
$this->stream->unget();
|
1326 |
+
$state = 'data';
|
1327 |
+
|
1328 |
+
} else {
|
1329 |
+
/* Anything else
|
1330 |
+
Append the input character to the comment token's data. Stay in
|
1331 |
+
the comment state. */
|
1332 |
+
$chars = $this->stream->charsUntil('-');
|
1333 |
+
|
1334 |
+
$this->token['data'] .= $char . $chars;
|
1335 |
+
}
|
1336 |
+
break;
|
1337 |
+
|
1338 |
+
case 'comment end dash':
|
1339 |
+
/* Consume the next input character: */
|
1340 |
+
$char = $this->stream->char();
|
1341 |
+
|
1342 |
+
if ($char === '-') {
|
1343 |
+
/* U+002D HYPHEN-MINUS (-)
|
1344 |
+
Switch to the comment end state */
|
1345 |
+
$state = 'comment end';
|
1346 |
+
|
1347 |
+
} elseif ($char === false) {
|
1348 |
+
/* EOF
|
1349 |
+
Parse error. Emit the comment token. Reconsume the EOF character
|
1350 |
+
in the data state. */
|
1351 |
+
$this->emitToken(array(
|
1352 |
+
'type' => self::PARSEERROR,
|
1353 |
+
'data' => 'eof-in-comment-end-dash'
|
1354 |
+
));
|
1355 |
+
$this->emitToken($this->token);
|
1356 |
+
$this->stream->unget();
|
1357 |
+
$state = 'data';
|
1358 |
+
|
1359 |
+
} else {
|
1360 |
+
/* Anything else
|
1361 |
+
Append a U+002D HYPHEN-MINUS (-) character and the input
|
1362 |
+
character to the comment token's data. Switch to the comment state. */
|
1363 |
+
$this->token['data'] .= '-'.$char;
|
1364 |
+
$state = 'comment';
|
1365 |
+
}
|
1366 |
+
break;
|
1367 |
+
|
1368 |
+
case 'comment end':
|
1369 |
+
/* Consume the next input character: */
|
1370 |
+
$char = $this->stream->char();
|
1371 |
+
|
1372 |
+
if ($char === '>') {
|
1373 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1374 |
+
Emit the comment token. Switch to the data state. */
|
1375 |
+
$this->emitToken($this->token);
|
1376 |
+
$state = 'data';
|
1377 |
+
|
1378 |
+
} elseif ($char === '-') {
|
1379 |
+
/* U+002D HYPHEN-MINUS (-)
|
1380 |
+
Parse error. Append a U+002D HYPHEN-MINUS (-) character
|
1381 |
+
to the comment token's data. Stay in the comment end
|
1382 |
+
state. */
|
1383 |
+
$this->emitToken(array(
|
1384 |
+
'type' => self::PARSEERROR,
|
1385 |
+
'data' => 'unexpected-dash-after-double-dash-in-comment'
|
1386 |
+
));
|
1387 |
+
$this->token['data'] .= '-';
|
1388 |
+
|
1389 |
+
} elseif ($char === "\t" || $char === "\n" || $char === "\x0a" || $char === ' ') {
|
1390 |
+
$this->emitToken(array(
|
1391 |
+
'type' => self::PARSEERROR,
|
1392 |
+
'data' => 'unexpected-space-after-double-dash-in-comment'
|
1393 |
+
));
|
1394 |
+
$this->token['data'] .= '--' . $char;
|
1395 |
+
$state = 'comment end space';
|
1396 |
+
|
1397 |
+
} elseif ($char === '!') {
|
1398 |
+
$this->emitToken(array(
|
1399 |
+
'type' => self::PARSEERROR,
|
1400 |
+
'data' => 'unexpected-bang-after-double-dash-in-comment'
|
1401 |
+
));
|
1402 |
+
$state = 'comment end bang';
|
1403 |
+
|
1404 |
+
} elseif ($char === false) {
|
1405 |
+
/* EOF
|
1406 |
+
Parse error. Emit the comment token. Reconsume the
|
1407 |
+
EOF character in the data state. */
|
1408 |
+
$this->emitToken(array(
|
1409 |
+
'type' => self::PARSEERROR,
|
1410 |
+
'data' => 'eof-in-comment-double-dash'
|
1411 |
+
));
|
1412 |
+
$this->emitToken($this->token);
|
1413 |
+
$this->stream->unget();
|
1414 |
+
$state = 'data';
|
1415 |
+
|
1416 |
+
} else {
|
1417 |
+
/* Anything else
|
1418 |
+
Parse error. Append two U+002D HYPHEN-MINUS (-)
|
1419 |
+
characters and the input character to the comment token's
|
1420 |
+
data. Switch to the comment state. */
|
1421 |
+
$this->emitToken(array(
|
1422 |
+
'type' => self::PARSEERROR,
|
1423 |
+
'data' => 'unexpected-char-in-comment'
|
1424 |
+
));
|
1425 |
+
$this->token['data'] .= '--'.$char;
|
1426 |
+
$state = 'comment';
|
1427 |
+
}
|
1428 |
+
break;
|
1429 |
+
|
1430 |
+
case 'comment end bang':
|
1431 |
+
$char = $this->stream->char();
|
1432 |
+
if ($char === '>') {
|
1433 |
+
$this->emitToken($this->token);
|
1434 |
+
$state = 'data';
|
1435 |
+
} elseif ($char === "-") {
|
1436 |
+
$this->token['data'] .= '--!';
|
1437 |
+
$state = 'comment end dash';
|
1438 |
+
} elseif ($char === false) {
|
1439 |
+
$this->emitToken(array(
|
1440 |
+
'type' => self::PARSEERROR,
|
1441 |
+
'data' => 'eof-in-comment-end-bang'
|
1442 |
+
));
|
1443 |
+
$this->emitToken($this->token);
|
1444 |
+
$this->stream->unget();
|
1445 |
+
$state = 'data';
|
1446 |
+
} else {
|
1447 |
+
$this->token['data'] .= '--!' . $char;
|
1448 |
+
$state = 'comment';
|
1449 |
+
}
|
1450 |
+
break;
|
1451 |
+
|
1452 |
+
case 'comment end space':
|
1453 |
+
$char = $this->stream->char();
|
1454 |
+
if ($char === '>') {
|
1455 |
+
$this->emitToken($this->token);
|
1456 |
+
$state = 'data';
|
1457 |
+
} elseif ($char === '-') {
|
1458 |
+
$state = 'comment end dash';
|
1459 |
+
} elseif ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1460 |
+
$this->token['data'] .= $char;
|
1461 |
+
} elseif ($char === false) {
|
1462 |
+
$this->emitToken(array(
|
1463 |
+
'type' => self::PARSEERROR,
|
1464 |
+
'data' => 'unexpected-eof-in-comment-end-space',
|
1465 |
+
));
|
1466 |
+
$this->emitToken($this->token);
|
1467 |
+
$this->stream->unget();
|
1468 |
+
$state = 'data';
|
1469 |
+
} else {
|
1470 |
+
$this->token['data'] .= $char;
|
1471 |
+
$state = 'comment';
|
1472 |
+
}
|
1473 |
+
break;
|
1474 |
+
|
1475 |
+
case 'DOCTYPE':
|
1476 |
+
/* Consume the next input character: */
|
1477 |
+
$char = $this->stream->char();
|
1478 |
+
|
1479 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1480 |
+
/* U+0009 CHARACTER TABULATION
|
1481 |
+
U+000A LINE FEED (LF)
|
1482 |
+
U+000C FORM FEED (FF)
|
1483 |
+
U+0020 SPACE
|
1484 |
+
Switch to the before DOCTYPE name state. */
|
1485 |
+
$state = 'before DOCTYPE name';
|
1486 |
+
|
1487 |
+
} elseif ($char === false) {
|
1488 |
+
/* EOF
|
1489 |
+
Parse error. Create a new DOCTYPE token. Set its
|
1490 |
+
force-quirks flag to on. Emit the token. Reconsume the
|
1491 |
+
EOF character in the data state. */
|
1492 |
+
$this->emitToken(array(
|
1493 |
+
'type' => self::PARSEERROR,
|
1494 |
+
'data' => 'need-space-after-doctype-but-got-eof'
|
1495 |
+
));
|
1496 |
+
$this->emitToken(array(
|
1497 |
+
'name' => '',
|
1498 |
+
'type' => self::DOCTYPE,
|
1499 |
+
'force-quirks' => true,
|
1500 |
+
'error' => true
|
1501 |
+
));
|
1502 |
+
$this->stream->unget();
|
1503 |
+
$state = 'data';
|
1504 |
+
|
1505 |
+
} else {
|
1506 |
+
/* Anything else
|
1507 |
+
Parse error. Reconsume the current character in the
|
1508 |
+
before DOCTYPE name state. */
|
1509 |
+
$this->emitToken(array(
|
1510 |
+
'type' => self::PARSEERROR,
|
1511 |
+
'data' => 'need-space-after-doctype'
|
1512 |
+
));
|
1513 |
+
$this->stream->unget();
|
1514 |
+
$state = 'before DOCTYPE name';
|
1515 |
+
}
|
1516 |
+
break;
|
1517 |
+
|
1518 |
+
case 'before DOCTYPE name':
|
1519 |
+
/* Consume the next input character: */
|
1520 |
+
$char = $this->stream->char();
|
1521 |
+
|
1522 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1523 |
+
/* U+0009 CHARACTER TABULATION
|
1524 |
+
U+000A LINE FEED (LF)
|
1525 |
+
U+000C FORM FEED (FF)
|
1526 |
+
U+0020 SPACE
|
1527 |
+
Stay in the before DOCTYPE name state. */
|
1528 |
+
|
1529 |
+
} elseif ($char === '>') {
|
1530 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1531 |
+
Parse error. Create a new DOCTYPE token. Set its
|
1532 |
+
force-quirks flag to on. Emit the token. Switch to the
|
1533 |
+
data state. */
|
1534 |
+
$this->emitToken(array(
|
1535 |
+
'type' => self::PARSEERROR,
|
1536 |
+
'data' => 'expected-doctype-name-but-got-right-bracket'
|
1537 |
+
));
|
1538 |
+
$this->emitToken(array(
|
1539 |
+
'name' => '',
|
1540 |
+
'type' => self::DOCTYPE,
|
1541 |
+
'force-quirks' => true,
|
1542 |
+
'error' => true
|
1543 |
+
));
|
1544 |
+
|
1545 |
+
$state = 'data';
|
1546 |
+
|
1547 |
+
} elseif ('A' <= $char && $char <= 'Z') {
|
1548 |
+
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
1549 |
+
Create a new DOCTYPE token. Set the token's name to the
|
1550 |
+
lowercase version of the input character (add 0x0020 to
|
1551 |
+
the character's code point). Switch to the DOCTYPE name
|
1552 |
+
state. */
|
1553 |
+
$this->token = array(
|
1554 |
+
'name' => strtolower($char),
|
1555 |
+
'type' => self::DOCTYPE,
|
1556 |
+
'error' => true
|
1557 |
+
);
|
1558 |
+
|
1559 |
+
$state = 'DOCTYPE name';
|
1560 |
+
|
1561 |
+
} elseif ($char === false) {
|
1562 |
+
/* EOF
|
1563 |
+
Parse error. Create a new DOCTYPE token. Set its
|
1564 |
+
force-quirks flag to on. Emit the token. Reconsume the
|
1565 |
+
EOF character in the data state. */
|
1566 |
+
$this->emitToken(array(
|
1567 |
+
'type' => self::PARSEERROR,
|
1568 |
+
'data' => 'expected-doctype-name-but-got-eof'
|
1569 |
+
));
|
1570 |
+
$this->emitToken(array(
|
1571 |
+
'name' => '',
|
1572 |
+
'type' => self::DOCTYPE,
|
1573 |
+
'force-quirks' => true,
|
1574 |
+
'error' => true
|
1575 |
+
));
|
1576 |
+
|
1577 |
+
$this->stream->unget();
|
1578 |
+
$state = 'data';
|
1579 |
+
|
1580 |
+
} else {
|
1581 |
+
/* Anything else
|
1582 |
+
Create a new DOCTYPE token. Set the token's name to the
|
1583 |
+
current input character. Switch to the DOCTYPE name state. */
|
1584 |
+
$this->token = array(
|
1585 |
+
'name' => $char,
|
1586 |
+
'type' => self::DOCTYPE,
|
1587 |
+
'error' => true
|
1588 |
+
);
|
1589 |
+
|
1590 |
+
$state = 'DOCTYPE name';
|
1591 |
+
}
|
1592 |
+
break;
|
1593 |
+
|
1594 |
+
case 'DOCTYPE name':
|
1595 |
+
/* Consume the next input character: */
|
1596 |
+
$char = $this->stream->char();
|
1597 |
+
|
1598 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1599 |
+
/* U+0009 CHARACTER TABULATION
|
1600 |
+
U+000A LINE FEED (LF)
|
1601 |
+
U+000C FORM FEED (FF)
|
1602 |
+
U+0020 SPACE
|
1603 |
+
Switch to the after DOCTYPE name state. */
|
1604 |
+
$state = 'after DOCTYPE name';
|
1605 |
+
|
1606 |
+
} elseif ($char === '>') {
|
1607 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1608 |
+
Emit the current DOCTYPE token. Switch to the data state. */
|
1609 |
+
$this->emitToken($this->token);
|
1610 |
+
$state = 'data';
|
1611 |
+
|
1612 |
+
} elseif ('A' <= $char && $char <= 'Z') {
|
1613 |
+
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
1614 |
+
Append the lowercase version of the input character
|
1615 |
+
(add 0x0020 to the character's code point) to the current
|
1616 |
+
DOCTYPE token's name. Stay in the DOCTYPE name state. */
|
1617 |
+
$this->token['name'] .= strtolower($char);
|
1618 |
+
|
1619 |
+
} elseif ($char === false) {
|
1620 |
+
/* EOF
|
1621 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
1622 |
+
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1623 |
+
character in the data state. */
|
1624 |
+
$this->emitToken(array(
|
1625 |
+
'type' => self::PARSEERROR,
|
1626 |
+
'data' => 'eof-in-doctype-name'
|
1627 |
+
));
|
1628 |
+
$this->token['force-quirks'] = true;
|
1629 |
+
$this->emitToken($this->token);
|
1630 |
+
$this->stream->unget();
|
1631 |
+
$state = 'data';
|
1632 |
+
|
1633 |
+
} else {
|
1634 |
+
/* Anything else
|
1635 |
+
Append the current input character to the current
|
1636 |
+
DOCTYPE token's name. Stay in the DOCTYPE name state. */
|
1637 |
+
$this->token['name'] .= $char;
|
1638 |
+
}
|
1639 |
+
|
1640 |
+
// XXX this is probably some sort of quirks mode designation,
|
1641 |
+
// check tree-builder to be sure. In general 'error' needs
|
1642 |
+
// to be specc'ified, this probably means removing it at the end
|
1643 |
+
$this->token['error'] = ($this->token['name'] === 'HTML')
|
1644 |
+
? false
|
1645 |
+
: true;
|
1646 |
+
break;
|
1647 |
+
|
1648 |
+
case 'after DOCTYPE name':
|
1649 |
+
/* Consume the next input character: */
|
1650 |
+
$char = $this->stream->char();
|
1651 |
+
|
1652 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1653 |
+
/* U+0009 CHARACTER TABULATION
|
1654 |
+
U+000A LINE FEED (LF)
|
1655 |
+
U+000C FORM FEED (FF)
|
1656 |
+
U+0020 SPACE
|
1657 |
+
Stay in the after DOCTYPE name state. */
|
1658 |
+
|
1659 |
+
} elseif ($char === '>') {
|
1660 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1661 |
+
Emit the current DOCTYPE token. Switch to the data state. */
|
1662 |
+
$this->emitToken($this->token);
|
1663 |
+
$state = 'data';
|
1664 |
+
|
1665 |
+
} elseif ($char === false) {
|
1666 |
+
/* EOF
|
1667 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
1668 |
+
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1669 |
+
character in the data state. */
|
1670 |
+
$this->emitToken(array(
|
1671 |
+
'type' => self::PARSEERROR,
|
1672 |
+
'data' => 'eof-in-doctype'
|
1673 |
+
));
|
1674 |
+
$this->token['force-quirks'] = true;
|
1675 |
+
$this->emitToken($this->token);
|
1676 |
+
$this->stream->unget();
|
1677 |
+
$state = 'data';
|
1678 |
+
|
1679 |
+
} else {
|
1680 |
+
/* Anything else */
|
1681 |
+
|
1682 |
+
$nextSix = strtoupper($char . $this->stream->charsWhile(self::ALPHA, 5));
|
1683 |
+
if ($nextSix === 'PUBLIC') {
|
1684 |
+
/* If the next six characters are an ASCII
|
1685 |
+
case-insensitive match for the word "PUBLIC", then
|
1686 |
+
consume those characters and switch to the before
|
1687 |
+
DOCTYPE public identifier state. */
|
1688 |
+
$state = 'before DOCTYPE public identifier';
|
1689 |
+
|
1690 |
+
} elseif ($nextSix === 'SYSTEM') {
|
1691 |
+
/* Otherwise, if the next six characters are an ASCII
|
1692 |
+
case-insensitive match for the word "SYSTEM", then
|
1693 |
+
consume those characters and switch to the before
|
1694 |
+
DOCTYPE system identifier state. */
|
1695 |
+
$state = 'before DOCTYPE system identifier';
|
1696 |
+
|
1697 |
+
} else {
|
1698 |
+
/* Otherwise, this is the parse error. Set the DOCTYPE
|
1699 |
+
token's force-quirks flag to on. Switch to the bogus
|
1700 |
+
DOCTYPE state. */
|
1701 |
+
$this->emitToken(array(
|
1702 |
+
'type' => self::PARSEERROR,
|
1703 |
+
'data' => 'expected-space-or-right-bracket-in-doctype'
|
1704 |
+
));
|
1705 |
+
$this->token['force-quirks'] = true;
|
1706 |
+
$this->token['error'] = true;
|
1707 |
+
$state = 'bogus DOCTYPE';
|
1708 |
+
}
|
1709 |
+
}
|
1710 |
+
break;
|
1711 |
+
|
1712 |
+
case 'before DOCTYPE public identifier':
|
1713 |
+
/* Consume the next input character: */
|
1714 |
+
$char = $this->stream->char();
|
1715 |
+
|
1716 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1717 |
+
/* U+0009 CHARACTER TABULATION
|
1718 |
+
U+000A LINE FEED (LF)
|
1719 |
+
U+000C FORM FEED (FF)
|
1720 |
+
U+0020 SPACE
|
1721 |
+
Stay in the before DOCTYPE public identifier state. */
|
1722 |
+
} elseif ($char === '"') {
|
1723 |
+
/* U+0022 QUOTATION MARK (")
|
1724 |
+
Set the DOCTYPE token's public identifier to the empty
|
1725 |
+
string (not missing), then switch to the DOCTYPE public
|
1726 |
+
identifier (double-quoted) state. */
|
1727 |
+
$this->token['public'] = '';
|
1728 |
+
$state = 'DOCTYPE public identifier (double-quoted)';
|
1729 |
+
} elseif ($char === "'") {
|
1730 |
+
/* U+0027 APOSTROPHE (')
|
1731 |
+
Set the DOCTYPE token's public identifier to the empty
|
1732 |
+
string (not missing), then switch to the DOCTYPE public
|
1733 |
+
identifier (single-quoted) state. */
|
1734 |
+
$this->token['public'] = '';
|
1735 |
+
$state = 'DOCTYPE public identifier (single-quoted)';
|
1736 |
+
} elseif ($char === '>') {
|
1737 |
+
/* Parse error. Set the DOCTYPE token's force-quirks flag
|
1738 |
+
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1739 |
+
$this->emitToken(array(
|
1740 |
+
'type' => self::PARSEERROR,
|
1741 |
+
'data' => 'unexpected-end-of-doctype'
|
1742 |
+
));
|
1743 |
+
$this->token['force-quirks'] = true;
|
1744 |
+
$this->emitToken($this->token);
|
1745 |
+
$state = 'data';
|
1746 |
+
} elseif ($char === false) {
|
1747 |
+
/* Parse error. Set the DOCTYPE token's force-quirks
|
1748 |
+
flag to on. Emit that DOCTYPE token. Reconsume the EOF
|
1749 |
+
character in the data state. */
|
1750 |
+
$this->emitToken(array(
|
1751 |
+
'type' => self::PARSEERROR,
|
1752 |
+
'data' => 'eof-in-doctype'
|
1753 |
+
));
|
1754 |
+
$this->token['force-quirks'] = true;
|
1755 |
+
$this->emitToken($this->token);
|
1756 |
+
$this->stream->unget();
|
1757 |
+
$state = 'data';
|
1758 |
+
} else {
|
1759 |
+
/* Parse error. Set the DOCTYPE token's force-quirks flag
|
1760 |
+
to on. Switch to the bogus DOCTYPE state. */
|
1761 |
+
$this->emitToken(array(
|
1762 |
+
'type' => self::PARSEERROR,
|
1763 |
+
'data' => 'unexpected-char-in-doctype'
|
1764 |
+
));
|
1765 |
+
$this->token['force-quirks'] = true;
|
1766 |
+
$state = 'bogus DOCTYPE';
|
1767 |
+
}
|
1768 |
+
break;
|
1769 |
+
|
1770 |
+
case 'DOCTYPE public identifier (double-quoted)':
|
1771 |
+
/* Consume the next input character: */
|
1772 |
+
$char = $this->stream->char();
|
1773 |
+
|
1774 |
+
if ($char === '"') {
|
1775 |
+
/* U+0022 QUOTATION MARK (")
|
1776 |
+
Switch to the after DOCTYPE public identifier state. */
|
1777 |
+
$state = 'after DOCTYPE public identifier';
|
1778 |
+
} elseif ($char === '>') {
|
1779 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1780 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
1781 |
+
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1782 |
+
$this->emitToken(array(
|
1783 |
+
'type' => self::PARSEERROR,
|
1784 |
+
'data' => 'unexpected-end-of-doctype'
|
1785 |
+
));
|
1786 |
+
$this->token['force-quirks'] = true;
|
1787 |
+
$this->emitToken($this->token);
|
1788 |
+
$state = 'data';
|
1789 |
+
} elseif ($char === false) {
|
1790 |
+
/* EOF
|
1791 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
1792 |
+
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1793 |
+
character in the data state. */
|
1794 |
+
$this->emitToken(array(
|
1795 |
+
'type' => self::PARSEERROR,
|
1796 |
+
'data' => 'eof-in-doctype'
|
1797 |
+
));
|
1798 |
+
$this->token['force-quirks'] = true;
|
1799 |
+
$this->emitToken($this->token);
|
1800 |
+
$this->stream->unget();
|
1801 |
+
$state = 'data';
|
1802 |
+
} else {
|
1803 |
+
/* Anything else
|
1804 |
+
Append the current input character to the current
|
1805 |
+
DOCTYPE token's public identifier. Stay in the DOCTYPE
|
1806 |
+
public identifier (double-quoted) state. */
|
1807 |
+
$this->token['public'] .= $char;
|
1808 |
+
}
|
1809 |
+
break;
|
1810 |
+
|
1811 |
+
case 'DOCTYPE public identifier (single-quoted)':
|
1812 |
+
/* Consume the next input character: */
|
1813 |
+
$char = $this->stream->char();
|
1814 |
+
|
1815 |
+
if ($char === "'") {
|
1816 |
+
/* U+0027 APOSTROPHE (')
|
1817 |
+
Switch to the after DOCTYPE public identifier state. */
|
1818 |
+
$state = 'after DOCTYPE public identifier';
|
1819 |
+
} elseif ($char === '>') {
|
1820 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1821 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
1822 |
+
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1823 |
+
$this->emitToken(array(
|
1824 |
+
'type' => self::PARSEERROR,
|
1825 |
+
'data' => 'unexpected-end-of-doctype'
|
1826 |
+
));
|
1827 |
+
$this->token['force-quirks'] = true;
|
1828 |
+
$this->emitToken($this->token);
|
1829 |
+
$state = 'data';
|
1830 |
+
} elseif ($char === false) {
|
1831 |
+
/* EOF
|
1832 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
1833 |
+
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1834 |
+
character in the data state. */
|
1835 |
+
$this->emitToken(array(
|
1836 |
+
'type' => self::PARSEERROR,
|
1837 |
+
'data' => 'eof-in-doctype'
|
1838 |
+
));
|
1839 |
+
$this->token['force-quirks'] = true;
|
1840 |
+
$this->emitToken($this->token);
|
1841 |
+
$this->stream->unget();
|
1842 |
+
$state = 'data';
|
1843 |
+
} else {
|
1844 |
+
/* Anything else
|
1845 |
+
Append the current input character to the current
|
1846 |
+
DOCTYPE token's public identifier. Stay in the DOCTYPE
|
1847 |
+
public identifier (double-quoted) state. */
|
1848 |
+
$this->token['public'] .= $char;
|
1849 |
+
}
|
1850 |
+
break;
|
1851 |
+
|
1852 |
+
case 'after DOCTYPE public identifier':
|
1853 |
+
/* Consume the next input character: */
|
1854 |
+
$char = $this->stream->char();
|
1855 |
+
|
1856 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1857 |
+
/* U+0009 CHARACTER TABULATION
|
1858 |
+
U+000A LINE FEED (LF)
|
1859 |
+
U+000C FORM FEED (FF)
|
1860 |
+
U+0020 SPACE
|
1861 |
+
Stay in the after DOCTYPE public identifier state. */
|
1862 |
+
} elseif ($char === '"') {
|
1863 |
+
/* U+0022 QUOTATION MARK (")
|
1864 |
+
Set the DOCTYPE token's system identifier to the
|
1865 |
+
empty string (not missing), then switch to the DOCTYPE
|
1866 |
+
system identifier (double-quoted) state. */
|
1867 |
+
$this->token['system'] = '';
|
1868 |
+
$state = 'DOCTYPE system identifier (double-quoted)';
|
1869 |
+
} elseif ($char === "'") {
|
1870 |
+
/* U+0027 APOSTROPHE (')
|
1871 |
+
Set the DOCTYPE token's system identifier to the
|
1872 |
+
empty string (not missing), then switch to the DOCTYPE
|
1873 |
+
system identifier (single-quoted) state. */
|
1874 |
+
$this->token['system'] = '';
|
1875 |
+
$state = 'DOCTYPE system identifier (single-quoted)';
|
1876 |
+
} elseif ($char === '>') {
|
1877 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1878 |
+
Emit the current DOCTYPE token. Switch to the data state. */
|
1879 |
+
$this->emitToken($this->token);
|
1880 |
+
$state = 'data';
|
1881 |
+
} elseif ($char === false) {
|
1882 |
+
/* Parse error. Set the DOCTYPE token's force-quirks
|
1883 |
+
flag to on. Emit that DOCTYPE token. Reconsume the EOF
|
1884 |
+
character in the data state. */
|
1885 |
+
$this->emitToken(array(
|
1886 |
+
'type' => self::PARSEERROR,
|
1887 |
+
'data' => 'eof-in-doctype'
|
1888 |
+
));
|
1889 |
+
$this->token['force-quirks'] = true;
|
1890 |
+
$this->emitToken($this->token);
|
1891 |
+
$this->stream->unget();
|
1892 |
+
$state = 'data';
|
1893 |
+
} else {
|
1894 |
+
/* Anything else
|
1895 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
1896 |
+
to on. Switch to the bogus DOCTYPE state. */
|
1897 |
+
$this->emitToken(array(
|
1898 |
+
'type' => self::PARSEERROR,
|
1899 |
+
'data' => 'unexpected-char-in-doctype'
|
1900 |
+
));
|
1901 |
+
$this->token['force-quirks'] = true;
|
1902 |
+
$state = 'bogus DOCTYPE';
|
1903 |
+
}
|
1904 |
+
break;
|
1905 |
+
|
1906 |
+
case 'before DOCTYPE system identifier':
|
1907 |
+
/* Consume the next input character: */
|
1908 |
+
$char = $this->stream->char();
|
1909 |
+
|
1910 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1911 |
+
/* U+0009 CHARACTER TABULATION
|
1912 |
+
U+000A LINE FEED (LF)
|
1913 |
+
U+000C FORM FEED (FF)
|
1914 |
+
U+0020 SPACE
|
1915 |
+
Stay in the before DOCTYPE system identifier state. */
|
1916 |
+
} elseif ($char === '"') {
|
1917 |
+
/* U+0022 QUOTATION MARK (")
|
1918 |
+
Set the DOCTYPE token's system identifier to the empty
|
1919 |
+
string (not missing), then switch to the DOCTYPE system
|
1920 |
+
identifier (double-quoted) state. */
|
1921 |
+
$this->token['system'] = '';
|
1922 |
+
$state = 'DOCTYPE system identifier (double-quoted)';
|
1923 |
+
} elseif ($char === "'") {
|
1924 |
+
/* U+0027 APOSTROPHE (')
|
1925 |
+
Set the DOCTYPE token's system identifier to the empty
|
1926 |
+
string (not missing), then switch to the DOCTYPE system
|
1927 |
+
identifier (single-quoted) state. */
|
1928 |
+
$this->token['system'] = '';
|
1929 |
+
$state = 'DOCTYPE system identifier (single-quoted)';
|
1930 |
+
} elseif ($char === '>') {
|
1931 |
+
/* Parse error. Set the DOCTYPE token's force-quirks flag
|
1932 |
+
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1933 |
+
$this->emitToken(array(
|
1934 |
+
'type' => self::PARSEERROR,
|
1935 |
+
'data' => 'unexpected-char-in-doctype'
|
1936 |
+
));
|
1937 |
+
$this->token['force-quirks'] = true;
|
1938 |
+
$this->emitToken($this->token);
|
1939 |
+
$state = 'data';
|
1940 |
+
} elseif ($char === false) {
|
1941 |
+
/* Parse error. Set the DOCTYPE token's force-quirks
|
1942 |
+
flag to on. Emit that DOCTYPE token. Reconsume the EOF
|
1943 |
+
character in the data state. */
|
1944 |
+
$this->emitToken(array(
|
1945 |
+
'type' => self::PARSEERROR,
|
1946 |
+
'data' => 'eof-in-doctype'
|
1947 |
+
));
|
1948 |
+
$this->token['force-quirks'] = true;
|
1949 |
+
$this->emitToken($this->token);
|
1950 |
+
$this->stream->unget();
|
1951 |
+
$state = 'data';
|
1952 |
+
} else {
|
1953 |
+
/* Parse error. Set the DOCTYPE token's force-quirks flag
|
1954 |
+
to on. Switch to the bogus DOCTYPE state. */
|
1955 |
+
$this->emitToken(array(
|
1956 |
+
'type' => self::PARSEERROR,
|
1957 |
+
'data' => 'unexpected-char-in-doctype'
|
1958 |
+
));
|
1959 |
+
$this->token['force-quirks'] = true;
|
1960 |
+
$state = 'bogus DOCTYPE';
|
1961 |
+
}
|
1962 |
+
break;
|
1963 |
+
|
1964 |
+
case 'DOCTYPE system identifier (double-quoted)':
|
1965 |
+
/* Consume the next input character: */
|
1966 |
+
$char = $this->stream->char();
|
1967 |
+
|
1968 |
+
if ($char === '"') {
|
1969 |
+
/* U+0022 QUOTATION MARK (")
|
1970 |
+
Switch to the after DOCTYPE system identifier state. */
|
1971 |
+
$state = 'after DOCTYPE system identifier';
|
1972 |
+
} elseif ($char === '>') {
|
1973 |
+
/* U+003E GREATER-THAN SIGN (>)
|
1974 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
1975 |
+
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1976 |
+
$this->emitToken(array(
|
1977 |
+
'type' => self::PARSEERROR,
|
1978 |
+
'data' => 'unexpected-end-of-doctype'
|
1979 |
+
));
|
1980 |
+
$this->token['force-quirks'] = true;
|
1981 |
+
$this->emitToken($this->token);
|
1982 |
+
$state = 'data';
|
1983 |
+
} elseif ($char === false) {
|
1984 |
+
/* EOF
|
1985 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
1986 |
+
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1987 |
+
character in the data state. */
|
1988 |
+
$this->emitToken(array(
|
1989 |
+
'type' => self::PARSEERROR,
|
1990 |
+
'data' => 'eof-in-doctype'
|
1991 |
+
));
|
1992 |
+
$this->token['force-quirks'] = true;
|
1993 |
+
$this->emitToken($this->token);
|
1994 |
+
$this->stream->unget();
|
1995 |
+
$state = 'data';
|
1996 |
+
} else {
|
1997 |
+
/* Anything else
|
1998 |
+
Append the current input character to the current
|
1999 |
+
DOCTYPE token's system identifier. Stay in the DOCTYPE
|
2000 |
+
system identifier (double-quoted) state. */
|
2001 |
+
$this->token['system'] .= $char;
|
2002 |
+
}
|
2003 |
+
break;
|
2004 |
+
|
2005 |
+
case 'DOCTYPE system identifier (single-quoted)':
|
2006 |
+
/* Consume the next input character: */
|
2007 |
+
$char = $this->stream->char();
|
2008 |
+
|
2009 |
+
if ($char === "'") {
|
2010 |
+
/* U+0027 APOSTROPHE (')
|
2011 |
+
Switch to the after DOCTYPE system identifier state. */
|
2012 |
+
$state = 'after DOCTYPE system identifier';
|
2013 |
+
} elseif ($char === '>') {
|
2014 |
+
/* U+003E GREATER-THAN SIGN (>)
|
2015 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
2016 |
+
to on. Emit that DOCTYPE token. Switch to the data state. */
|
2017 |
+
$this->emitToken(array(
|
2018 |
+
'type' => self::PARSEERROR,
|
2019 |
+
'data' => 'unexpected-end-of-doctype'
|
2020 |
+
));
|
2021 |
+
$this->token['force-quirks'] = true;
|
2022 |
+
$this->emitToken($this->token);
|
2023 |
+
$state = 'data';
|
2024 |
+
} elseif ($char === false) {
|
2025 |
+
/* EOF
|
2026 |
+
Parse error. Set the DOCTYPE token's force-quirks flag
|
2027 |
+
to on. Emit that DOCTYPE token. Reconsume the EOF
|
2028 |
+
character in the data state. */
|
2029 |
+
$this->emitToken(array(
|
2030 |
+
'type' => self::PARSEERROR,
|
2031 |
+
'data' => 'eof-in-doctype'
|
2032 |
+
));
|
2033 |
+
$this->token['force-quirks'] = true;
|
2034 |
+
$this->emitToken($this->token);
|
2035 |
+
$this->stream->unget();
|
2036 |
+
$state = 'data';
|
2037 |
+
} else {
|
2038 |
+
/* Anything else
|
2039 |
+
Append the current input character to the current
|
2040 |
+
DOCTYPE token's system identifier. Stay in the DOCTYPE
|
2041 |
+
system identifier (double-quoted) state. */
|
2042 |
+
$this->token['system'] .= $char;
|
2043 |
+
}
|
2044 |
+
break;
|
2045 |
+
|
2046 |
+
case 'after DOCTYPE system identifier':
|
2047 |
+
/* Consume the next input character: */
|
2048 |
+
$char = $this->stream->char();
|
2049 |
+
|
2050 |
+
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
2051 |
+
/* U+0009 CHARACTER TABULATION
|
2052 |
+
U+000A LINE FEED (LF)
|
2053 |
+
U+000C FORM FEED (FF)
|
2054 |
+
U+0020 SPACE
|
2055 |
+
Stay in the after DOCTYPE system identifier state. */
|
2056 |
+
} elseif ($char === '>') {
|
2057 |
+
/* U+003E GREATER-THAN SIGN (>)
|
2058 |
+
Emit the current DOCTYPE token. Switch to the data state. */
|
2059 |
+
$this->emitToken($this->token);
|
2060 |
+
$state = 'data';
|
2061 |
+
} elseif ($char === false) {
|
2062 |
+
/* Parse error. Set the DOCTYPE token's force-quirks
|
2063 |
+
flag to on. Emit that DOCTYPE token. Reconsume the EOF
|
2064 |
+
character in the data state. */
|
2065 |
+
$this->emitToken(array(
|
2066 |
+
'type' => self::PARSEERROR,
|
2067 |
+
'data' => 'eof-in-doctype'
|
2068 |
+
));
|
2069 |
+
$this->token['force-quirks'] = true;
|
2070 |
+
$this->emitToken($this->token);
|
2071 |
+
$this->stream->unget();
|
2072 |
+
$state = 'data';
|
2073 |
+
} else {
|
2074 |
+
/* Anything else
|
2075 |
+
Parse error. Switch to the bogus DOCTYPE state.
|
2076 |
+
(This does not set the DOCTYPE token's force-quirks
|
2077 |
+
flag to on.) */
|
2078 |
+
$this->emitToken(array(
|
2079 |
+
'type' => self::PARSEERROR,
|
2080 |
+
'data' => 'unexpected-char-in-doctype'
|
2081 |
+
));
|
2082 |
+
$state = 'bogus DOCTYPE';
|
2083 |
+
}
|
2084 |
+
break;
|
2085 |
+
|
2086 |
+
case 'bogus DOCTYPE':
|
2087 |
+
/* Consume the next input character: */
|
2088 |
+
$char = $this->stream->char();
|
2089 |
+
|
2090 |
+
if ($char === '>') {
|
2091 |
+
/* U+003E GREATER-THAN SIGN (>)
|
2092 |
+
Emit the DOCTYPE token. Switch to the data state. */
|
2093 |
+
$this->emitToken($this->token);
|
2094 |
+
$state = 'data';
|
2095 |
+
|
2096 |
+
} elseif ($char === false) {
|
2097 |
+
/* EOF
|
2098 |
+
Emit the DOCTYPE token. Reconsume the EOF character in
|
2099 |
+
the data state. */
|
2100 |
+
$this->emitToken($this->token);
|
2101 |
+
$this->stream->unget();
|
2102 |
+
$state = 'data';
|
2103 |
+
|
2104 |
+
} else {
|
2105 |
+
/* Anything else
|
2106 |
+
Stay in the bogus DOCTYPE state. */
|
2107 |
+
}
|
2108 |
+
break;
|
2109 |
+
|
2110 |
+
// case 'cdataSection':
|
2111 |
+
}
|
2112 |
+
}
|
2113 |
+
}
|
2114 |
+
|
2115 |
+
/**
|
2116 |
+
* Returns a serialized representation of the tree.
|
2117 |
+
*
|
2118 |
+
* @return DOMDocument|DOMNodeList
|
2119 |
+
*/
|
2120 |
+
public function save() {
|
2121 |
+
return $this->tree->save();
|
2122 |
+
}
|
2123 |
+
|
2124 |
+
/**
|
2125 |
+
* @return HTML5_TreeBuilder The tree
|
2126 |
+
*/
|
2127 |
+
public function getTree()
|
2128 |
+
{
|
2129 |
+
return $this->tree;
|
2130 |
+
}
|
2131 |
+
|
2132 |
+
|
2133 |
+
/**
|
2134 |
+
* Returns the input stream.
|
2135 |
+
*
|
2136 |
+
* @return HTML5_InputStream
|
2137 |
+
*/
|
2138 |
+
public function stream() {
|
2139 |
+
return $this->stream;
|
2140 |
+
}
|
2141 |
+
|
2142 |
+
/**
|
2143 |
+
* @param bool $allowed
|
2144 |
+
* @param bool $inattr
|
2145 |
+
* @return string
|
2146 |
+
*/
|
2147 |
+
private function consumeCharacterReference($allowed = false, $inattr = false) {
|
2148 |
+
// This goes quite far against spec, and is far closer to the Python
|
2149 |
+
// impl., mainly because we don't do the large unconsuming the spec
|
2150 |
+
// requires.
|
2151 |
+
|
2152 |
+
// All consumed characters.
|
2153 |
+
$chars = $this->stream->char();
|
2154 |
+
|
2155 |
+
/* This section defines how to consume a character
|
2156 |
+
reference. This definition is used when parsing character
|
2157 |
+
references in text and in attributes.
|
2158 |
+
|
2159 |
+
The behavior depends on the identity of the next character
|
2160 |
+
(the one immediately after the U+0026 AMPERSAND character): */
|
2161 |
+
|
2162 |
+
if (
|
2163 |
+
$chars[0] === "\x09" ||
|
2164 |
+
$chars[0] === "\x0A" ||
|
2165 |
+
$chars[0] === "\x0C" ||
|
2166 |
+
$chars[0] === "\x20" ||
|
2167 |
+
$chars[0] === '<' ||
|
2168 |
+
$chars[0] === '&' ||
|
2169 |
+
$chars === false ||
|
2170 |
+
$chars[0] === $allowed
|
2171 |
+
) {
|
2172 |
+
/* U+0009 CHARACTER TABULATION
|
2173 |
+
U+000A LINE FEED (LF)
|
2174 |
+
U+000C FORM FEED (FF)
|
2175 |
+
U+0020 SPACE
|
2176 |
+
U+003C LESS-THAN SIGN
|
2177 |
+
U+0026 AMPERSAND
|
2178 |
+
EOF
|
2179 |
+
The additional allowed character, if there is one
|
2180 |
+
Not a character reference. No characters are consumed,
|
2181 |
+
and nothing is returned. (This is not an error, either.) */
|
2182 |
+
// We already consumed, so unconsume.
|
2183 |
+
$this->stream->unget();
|
2184 |
+
return '&';
|
2185 |
+
} elseif ($chars[0] === '#') {
|
2186 |
+
/* Consume the U+0023 NUMBER SIGN. */
|
2187 |
+
// Um, yeah, we already did that.
|
2188 |
+
/* The behavior further depends on the character after
|
2189 |
+
the U+0023 NUMBER SIGN: */
|
2190 |
+
$chars .= $this->stream->char();
|
2191 |
+
if (isset($chars[1]) && ($chars[1] === 'x' || $chars[1] === 'X')) {
|
2192 |
+
/* U+0078 LATIN SMALL LETTER X
|
2193 |
+
U+0058 LATIN CAPITAL LETTER X */
|
2194 |
+
/* Consume the X. */
|
2195 |
+
// Um, yeah, we already did that.
|
2196 |
+
/* Follow the steps below, but using the range of
|
2197 |
+
characters U+0030 DIGIT ZERO through to U+0039 DIGIT
|
2198 |
+
NINE, U+0061 LATIN SMALL LETTER A through to U+0066
|
2199 |
+
LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER
|
2200 |
+
A, through to U+0046 LATIN CAPITAL LETTER F (in other
|
2201 |
+
words, 0123456789, ABCDEF, abcdef). */
|
2202 |
+
$char_class = self::HEX;
|
2203 |
+
/* When it comes to interpreting the
|
2204 |
+
number, interpret it as a hexadecimal number. */
|
2205 |
+
$hex = true;
|
2206 |
+
} else {
|
2207 |
+
/* Anything else */
|
2208 |
+
// Unconsume because we shouldn't have consumed this.
|
2209 |
+
$chars = $chars[0];
|
2210 |
+
$this->stream->unget();
|
2211 |
+
/* Follow the steps below, but using the range of
|
2212 |
+
characters U+0030 DIGIT ZERO through to U+0039 DIGIT
|
2213 |
+
NINE (i.e. just 0123456789). */
|
2214 |
+
$char_class = self::DIGIT;
|
2215 |
+
/* When it comes to interpreting the number,
|
2216 |
+
interpret it as a decimal number. */
|
2217 |
+
$hex = false;
|
2218 |
+
}
|
2219 |
+
|
2220 |
+
/* Consume as many characters as match the range of characters given above. */
|
2221 |
+
$consumed = $this->stream->charsWhile($char_class);
|
2222 |
+
if ($consumed === '' || $consumed === false) {
|
2223 |
+
/* If no characters match the range, then don't consume
|
2224 |
+
any characters (and unconsume the U+0023 NUMBER SIGN
|
2225 |
+
character and, if appropriate, the X character). This
|
2226 |
+
is a parse error; nothing is returned. */
|
2227 |
+
$this->emitToken(array(
|
2228 |
+
'type' => self::PARSEERROR,
|
2229 |
+
'data' => 'expected-numeric-entity'
|
2230 |
+
));
|
2231 |
+
return '&' . $chars;
|
2232 |
+
} else {
|
2233 |
+
/* Otherwise, if the next character is a U+003B SEMICOLON,
|
2234 |
+
consume that too. If it isn't, there is a parse error. */
|
2235 |
+
if ($this->stream->char() !== ';') {
|
2236 |
+
$this->stream->unget();
|
2237 |
+
$this->emitToken(array(
|
2238 |
+
'type' => self::PARSEERROR,
|
2239 |
+
'data' => 'numeric-entity-without-semicolon'
|
2240 |
+
));
|
2241 |
+
}
|
2242 |
+
|
2243 |
+
/* If one or more characters match the range, then take
|
2244 |
+
them all and interpret the string of characters as a number
|
2245 |
+
(either hexadecimal or decimal as appropriate). */
|
2246 |
+
$codepoint = $hex ? hexdec($consumed) : (int) $consumed;
|
2247 |
+
|
2248 |
+
/* If that number is one of the numbers in the first column
|
2249 |
+
of the following table, then this is a parse error. Find the
|
2250 |
+
row with that number in the first column, and return a
|
2251 |
+
character token for the Unicode character given in the
|
2252 |
+
second column of that row. */
|
2253 |
+
$new_codepoint = HTML5_Data::getRealCodepoint($codepoint);
|
2254 |
+
if ($new_codepoint) {
|
2255 |
+
$this->emitToken(array(
|
2256 |
+
'type' => self::PARSEERROR,
|
2257 |
+
'data' => 'illegal-windows-1252-entity'
|
2258 |
+
));
|
2259 |
+
return HTML5_Data::utf8chr($new_codepoint);
|
2260 |
+
} else {
|
2261 |
+
/* Otherwise, if the number is greater than 0x10FFFF, then
|
2262 |
+
* this is a parse error. Return a U+FFFD REPLACEMENT
|
2263 |
+
* CHARACTER. */
|
2264 |
+
if ($codepoint > 0x10FFFF) {
|
2265 |
+
$this->emitToken(array(
|
2266 |
+
'type' => self::PARSEERROR,
|
2267 |
+
'data' => 'overlong-character-entity' // XXX probably not correct
|
2268 |
+
));
|
2269 |
+
return "\xEF\xBF\xBD";
|
2270 |
+
}
|
2271 |
+
/* Otherwise, return a character token for the Unicode
|
2272 |
+
* character whose code point is that number. If the
|
2273 |
+
* number is in the range 0x0001 to 0x0008, 0x000E to
|
2274 |
+
* 0x001F, 0x007F to 0x009F, 0xD800 to 0xDFFF, 0xFDD0 to
|
2275 |
+
* 0xFDEF, or is one of 0x000B, 0xFFFE, 0xFFFF, 0x1FFFE,
|
2276 |
+
* 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, 0x4FFFE,
|
2277 |
+
* 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
|
2278 |
+
* 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE,
|
2279 |
+
* 0xAFFFF, 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE,
|
2280 |
+
* 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE,
|
2281 |
+
* or 0x10FFFF, then this is a parse error. */
|
2282 |
+
// && has higher precedence than ||
|
2283 |
+
if (
|
2284 |
+
$codepoint >= 0x0000 && $codepoint <= 0x0008 ||
|
2285 |
+
$codepoint === 0x000B ||
|
2286 |
+
$codepoint >= 0x000E && $codepoint <= 0x001F ||
|
2287 |
+
$codepoint >= 0x007F && $codepoint <= 0x009F ||
|
2288 |
+
$codepoint >= 0xD800 && $codepoint <= 0xDFFF ||
|
2289 |
+
$codepoint >= 0xFDD0 && $codepoint <= 0xFDEF ||
|
2290 |
+
($codepoint & 0xFFFE) === 0xFFFE ||
|
2291 |
+
$codepoint == 0x10FFFF || $codepoint == 0x10FFFE
|
2292 |
+
) {
|
2293 |
+
$this->emitToken(array(
|
2294 |
+
'type' => self::PARSEERROR,
|
2295 |
+
'data' => 'illegal-codepoint-for-numeric-entity'
|
2296 |
+
));
|
2297 |
+
}
|
2298 |
+
return HTML5_Data::utf8chr($codepoint);
|
2299 |
+
}
|
2300 |
+
}
|
2301 |
+
} else {
|
2302 |
+
/* Anything else */
|
2303 |
+
|
2304 |
+
/* Consume the maximum number of characters possible,
|
2305 |
+
with the consumed characters matching one of the
|
2306 |
+
identifiers in the first column of the named character
|
2307 |
+
references table (in a case-sensitive manner). */
|
2308 |
+
// What we actually do here is consume as much as we can while it
|
2309 |
+
// matches the start of one of the identifiers in the first column.
|
2310 |
+
|
2311 |
+
$refs = HTML5_Data::getNamedCharacterReferences();
|
2312 |
+
|
2313 |
+
// Get the longest string which is the start of an identifier
|
2314 |
+
// ($chars) as well as the longest identifier which matches ($id)
|
2315 |
+
// and its codepoint ($codepoint).
|
2316 |
+
$codepoint = false;
|
2317 |
+
$char = $chars;
|
2318 |
+
while ($char !== false && isset($refs[$char])) {
|
2319 |
+
$refs = $refs[$char];
|
2320 |
+
if (isset($refs['codepoint'])) {
|
2321 |
+
$id = $chars;
|
2322 |
+
$codepoint = $refs['codepoint'];
|
2323 |
+
}
|
2324 |
+
$chars .= $char = $this->stream->char();
|
2325 |
+
}
|
2326 |
+
|
2327 |
+
// Unconsume the one character we just took which caused the while
|
2328 |
+
// statement to fail. This could be anything and could cause state
|
2329 |
+
// changes (as if it matches the while loop it must be
|
2330 |
+
// alphanumeric so we can just concat it to whatever we get later).
|
2331 |
+
$this->stream->unget();
|
2332 |
+
if ($char !== false) {
|
2333 |
+
$chars = substr($chars, 0, -1);
|
2334 |
+
}
|
2335 |
+
|
2336 |
+
/* If no match can be made, then this is a parse error.
|
2337 |
+
No characters are consumed, and nothing is returned. */
|
2338 |
+
if (!$codepoint) {
|
2339 |
+
$this->emitToken(array(
|
2340 |
+
'type' => self::PARSEERROR,
|
2341 |
+
'data' => 'expected-named-entity'
|
2342 |
+
));
|
2343 |
+
return '&' . $chars;
|
2344 |
+
}
|
2345 |
+
|
2346 |
+
/* If the last character matched is not a U+003B SEMICOLON
|
2347 |
+
(;), there is a parse error. */
|
2348 |
+
$semicolon = true;
|
2349 |
+
if (substr($id, -1) !== ';') {
|
2350 |
+
$this->emitToken(array(
|
2351 |
+
'type' => self::PARSEERROR,
|
2352 |
+
'data' => 'named-entity-without-semicolon'
|
2353 |
+
));
|
2354 |
+
$semicolon = false;
|
2355 |
+
}
|
2356 |
+
|
2357 |
+
/* If the character reference is being consumed as part of
|
2358 |
+
an attribute, and the last character matched is not a
|
2359 |
+
U+003B SEMICOLON (;), and the next character is in the
|
2360 |
+
range U+0030 DIGIT ZERO to U+0039 DIGIT NINE, U+0041
|
2361 |
+
LATIN CAPITAL LETTER A to U+005A LATIN CAPITAL LETTER Z,
|
2362 |
+
or U+0061 LATIN SMALL LETTER A to U+007A LATIN SMALL LETTER Z,
|
2363 |
+
then, for historical reasons, all the characters that were
|
2364 |
+
matched after the U+0026 AMPERSAND (&) must be unconsumed,
|
2365 |
+
and nothing is returned. */
|
2366 |
+
if ($inattr && !$semicolon) {
|
2367 |
+
// The next character is either the next character in $chars or in the stream.
|
2368 |
+
if (strlen($chars) > strlen($id)) {
|
2369 |
+
$next = substr($chars, strlen($id), 1);
|
2370 |
+
} else {
|
2371 |
+
$next = $this->stream->char();
|
2372 |
+
$this->stream->unget();
|
2373 |
+
}
|
2374 |
+
if (
|
2375 |
+
'0' <= $next && $next <= '9' ||
|
2376 |
+
'A' <= $next && $next <= 'Z' ||
|
2377 |
+
'a' <= $next && $next <= 'z'
|
2378 |
+
) {
|
2379 |
+
return '&' . $chars;
|
2380 |
+
}
|
2381 |
+
}
|
2382 |
+
|
2383 |
+
/* Otherwise, return a character token for the character
|
2384 |
+
corresponding to the character reference name (as given
|
2385 |
+
by the second column of the named character references table). */
|
2386 |
+
return HTML5_Data::utf8chr($codepoint) . substr($chars, strlen($id));
|
2387 |
+
}
|
2388 |
+
}
|
2389 |
+
|
2390 |
+
/**
|
2391 |
+
* @param bool $allowed
|
2392 |
+
*/
|
2393 |
+
private function characterReferenceInAttributeValue($allowed = false) {
|
2394 |
+
/* Attempt to consume a character reference. */
|
2395 |
+
$entity = $this->consumeCharacterReference($allowed, true);
|
2396 |
+
|
2397 |
+
/* If nothing is returned, append a U+0026 AMPERSAND
|
2398 |
+
character to the current attribute's value.
|
2399 |
+
|
2400 |
+
Otherwise, append the returned character token to the
|
2401 |
+
current attribute's value. */
|
2402 |
+
$char = (!$entity)
|
2403 |
+
? '&'
|
2404 |
+
: $entity;
|
2405 |
+
|
2406 |
+
$last = count($this->token['attr']) - 1;
|
2407 |
+
$this->token['attr'][$last]['value'] .= $char;
|
2408 |
+
|
2409 |
+
/* Finally, switch back to the attribute value state that you
|
2410 |
+
were in when were switched into this state. */
|
2411 |
+
}
|
2412 |
+
|
2413 |
+
/**
|
2414 |
+
* Emits a token, passing it on to the tree builder.
|
2415 |
+
*
|
2416 |
+
* @param $token
|
2417 |
+
* @param bool $checkStream
|
2418 |
+
* @param bool $dry
|
2419 |
+
*/
|
2420 |
+
protected function emitToken($token, $checkStream = true, $dry = false) {
|
2421 |
+
if ($checkStream === true) {
|
2422 |
+
// Emit errors from input stream.
|
2423 |
+
while ($this->stream->errors) {
|
2424 |
+
$this->emitToken(array_shift($this->stream->errors), false);
|
2425 |
+
}
|
2426 |
+
}
|
2427 |
+
if ($token['type'] === self::ENDTAG && !empty($token['attr'])) {
|
2428 |
+
for ($i = 0; $i < count($token['attr']); $i++) {
|
2429 |
+
$this->emitToken(array(
|
2430 |
+
'type' => self::PARSEERROR,
|
2431 |
+
'data' => 'attributes-in-end-tag'
|
2432 |
+
));
|
2433 |
+
}
|
2434 |
+
}
|
2435 |
+
if ($token['type'] === self::ENDTAG && !empty($token['self-closing'])) {
|
2436 |
+
$this->emitToken(array(
|
2437 |
+
'type' => self::PARSEERROR,
|
2438 |
+
'data' => 'self-closing-flag-on-end-tag',
|
2439 |
+
));
|
2440 |
+
}
|
2441 |
+
if ($token['type'] === self::STARTTAG) {
|
2442 |
+
// This could be changed to actually pass the tree-builder a hash
|
2443 |
+
$hash = array();
|
2444 |
+
foreach ($token['attr'] as $keypair) {
|
2445 |
+
if (isset($hash[$keypair['name']])) {
|
2446 |
+
$this->emitToken(array(
|
2447 |
+
'type' => self::PARSEERROR,
|
2448 |
+
'data' => 'duplicate-attribute',
|
2449 |
+
));
|
2450 |
+
} else {
|
2451 |
+
$hash[$keypair['name']] = $keypair['value'];
|
2452 |
+
}
|
2453 |
+
}
|
2454 |
+
}
|
2455 |
+
|
2456 |
+
if ($dry === false) {
|
2457 |
+
// the current structure of attributes is not a terribly good one
|
2458 |
+
$this->tree->emitToken($token);
|
2459 |
+
}
|
2460 |
+
|
2461 |
+
if ($dry === false && is_int($this->tree->content_model)) {
|
2462 |
+
$this->content_model = $this->tree->content_model;
|
2463 |
+
$this->tree->content_model = null;
|
2464 |
+
|
2465 |
+
} elseif ($token['type'] === self::ENDTAG) {
|
2466 |
+
$this->content_model = self::PCDATA;
|
2467 |
+
}
|
2468 |
+
}
|
2469 |
+
}
|
2470 |
+
|
vendor/dompdf/dompdf/lib/html5lib/TreeBuilder.php
CHANGED
@@ -1,3959 +1,3989 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/*
|
4 |
-
|
5 |
-
Copyright 2007 Jeroen van der Meer <http://jero.net/>
|
6 |
-
Copyright 2009 Edward Z. Yang <edwardzyang@thewritingpot.com>
|
7 |
-
|
8 |
-
Permission is hereby granted, free of charge, to any person obtaining a
|
9 |
-
copy of this software and associated documentation files (the
|
10 |
-
"Software"), to deal in the Software without restriction, including
|
11 |
-
without limitation the rights to use, copy, modify, merge, publish,
|
12 |
-
distribute, sublicense, and/or sell copies of the Software, and to
|
13 |
-
permit persons to whom the Software is furnished to do so, subject to
|
14 |
-
the following conditions:
|
15 |
-
|
16 |
-
The above copyright notice and this permission notice shall be included
|
17 |
-
in all copies or substantial portions of the Software.
|
18 |
-
|
19 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
20 |
-
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21 |
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
22 |
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
23 |
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
24 |
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
25 |
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26 |
-
|
27 |
-
*/
|
28 |
-
|
29 |
-
// Tags for FIX ME!!!: (in order of priority)
|
30 |
-
// XXX - should be fixed NAO!
|
31 |
-
// XERROR - with regards to parse errors
|
32 |
-
// XSCRIPT - with regards to scripting mode
|
33 |
-
// XENCODING - with regards to encoding (for reparsing tests)
|
34 |
-
// XDOM - DOM specific code (tagName is explicitly not marked).
|
35 |
-
// this is not (yet) in helper functions.
|
36 |
-
|
37 |
-
class HTML5_TreeBuilder {
|
38 |
-
public $stack = array();
|
39 |
-
public $content_model;
|
40 |
-
|
41 |
-
private $mode;
|
42 |
-
private $original_mode;
|
43 |
-
private $secondary_mode;
|
44 |
-
private $dom;
|
45 |
-
// Whether or not normal insertion of nodes should actually foster
|
46 |
-
// parent (used in one case in spec)
|
47 |
-
private $foster_parent = false;
|
48 |
-
private $a_formatting = array();
|
49 |
-
|
50 |
-
private $head_pointer = null;
|
51 |
-
private $form_pointer = null;
|
52 |
-
|
53 |
-
private $flag_frameset_ok = true;
|
54 |
-
private $flag_force_quirks = false;
|
55 |
-
private $ignored = false;
|
56 |
-
private $quirks_mode = null;
|
57 |
-
// this gets to 2 when we want to ignore the next lf character, and
|
58 |
-
// is decrement at the beginning of each processed token (this way,
|
59 |
-
// code can check for (bool)$ignore_lf_token, but it phases out
|
60 |
-
// appropriately)
|
61 |
-
private $ignore_lf_token = 0;
|
62 |
-
private $fragment = false;
|
63 |
-
private $root;
|
64 |
-
|
65 |
-
private $scoping = array('applet','button','caption','html','marquee','object','table','td','th', 'svg:foreignObject');
|
66 |
-
private $formatting = array('a','b','big','code','em','font','i','nobr','s','small','strike','strong','tt','u');
|
67 |
-
// dl and ds are speculative
|
68 |
-
private $special = array('address','area','article','aside','base','basefont','bgsound',
|
69 |
-
'blockquote','body','br','center','col','colgroup','command','dc','dd','details','dir','div','dl','ds',
|
70 |
-
'dt','embed','fieldset','figure','footer','form','frame','frameset','h1','h2','h3','h4','h5',
|
71 |
-
'h6','head','header','hgroup','hr','iframe','img','input','isindex','li','link',
|
72 |
-
'listing','menu','meta','nav','noembed','noframes','noscript','ol',
|
73 |
-
'p','param','plaintext','pre','script','select','spacer','style',
|
74 |
-
'tbody','textarea','tfoot','thead','title','tr','ul','wbr');
|
75 |
-
|
76 |
-
private $pendingTableCharacters;
|
77 |
-
private $pendingTableCharactersDirty;
|
78 |
-
|
79 |
-
// Tree construction modes
|
80 |
-
const INITIAL = 0;
|
81 |
-
const BEFORE_HTML = 1;
|
82 |
-
const BEFORE_HEAD = 2;
|
83 |
-
const IN_HEAD = 3;
|
84 |
-
const IN_HEAD_NOSCRIPT = 4;
|
85 |
-
const AFTER_HEAD = 5;
|
86 |
-
const IN_BODY = 6;
|
87 |
-
const IN_CDATA_RCDATA = 7;
|
88 |
-
const IN_TABLE = 8;
|
89 |
-
const IN_TABLE_TEXT = 9;
|
90 |
-
const IN_CAPTION = 10;
|
91 |
-
const IN_COLUMN_GROUP = 11;
|
92 |
-
const IN_TABLE_BODY = 12;
|
93 |
-
const IN_ROW = 13;
|
94 |
-
const IN_CELL = 14;
|
95 |
-
const IN_SELECT = 15;
|
96 |
-
const IN_SELECT_IN_TABLE= 16;
|
97 |
-
const IN_FOREIGN_CONTENT= 17;
|
98 |
-
const AFTER_BODY = 18;
|
99 |
-
const IN_FRAMESET = 19;
|
100 |
-
const AFTER_FRAMESET = 20;
|
101 |
-
const AFTER_AFTER_BODY = 21;
|
102 |
-
const AFTER_AFTER_FRAMESET = 22;
|
103 |
-
|
104 |
-
/**
|
105 |
-
* Converts a magic number to a readable name. Use for debugging.
|
106 |
-
*/
|
107 |
-
private function strConst($number) {
|
108 |
-
static $lookup;
|
109 |
-
if (!$lookup) {
|
110 |
-
$lookup = array();
|
111 |
-
$r = new ReflectionClass('HTML5_TreeBuilder');
|
112 |
-
$consts = $r->getConstants();
|
113 |
-
foreach ($consts as $const => $num) {
|
114 |
-
if (!is_int($num))
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
const
|
125 |
-
const
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
const
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
const
|
139 |
-
const
|
140 |
-
const
|
141 |
-
const
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
const
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
$this->dom
|
156 |
-
|
157 |
-
$this->dom->
|
158 |
-
$this->dom->
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
*
|
169 |
-
*
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
if ($
|
185 |
-
echo
|
186 |
-
|
187 |
-
|
188 |
-
$
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
*
|
215 |
-
*
|
216 |
-
*
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
* set to the
|
223 |
-
*
|
224 |
-
*
|
225 |
-
*
|
226 |
-
*
|
227 |
-
*
|
228 |
-
*
|
229 |
-
*
|
230 |
-
*
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
//
|
240 |
-
//
|
241 |
-
|
242 |
-
//
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
$
|
255 |
-
|
256 |
-
|
257 |
-
"
|
258 |
-
"-//
|
259 |
-
"-//
|
260 |
-
"-//ietf//dtd html 2.0
|
261 |
-
"-//ietf//dtd html 2.0
|
262 |
-
"-//ietf//dtd html 2.0 strict//",
|
263 |
-
"-//ietf//dtd html 2.0//",
|
264 |
-
"-//ietf//dtd html 2.
|
265 |
-
"-//ietf//dtd html
|
266 |
-
"-//ietf//dtd html
|
267 |
-
"-//ietf//dtd html 3.
|
268 |
-
"-//ietf//dtd html 3//",
|
269 |
-
"-//ietf//dtd html
|
270 |
-
"-//ietf//dtd html
|
271 |
-
"-//ietf//dtd html level
|
272 |
-
"-//ietf//dtd html level
|
273 |
-
"-//ietf//dtd html
|
274 |
-
"-//ietf//dtd html
|
275 |
-
"-//ietf//dtd html strict level
|
276 |
-
"-//ietf//dtd html strict level
|
277 |
-
"-//ietf//dtd html strict//",
|
278 |
-
"-//ietf//dtd html//",
|
279 |
-
"-//
|
280 |
-
"-//
|
281 |
-
"-//
|
282 |
-
"-//microsoft//dtd internet explorer 2.0
|
283 |
-
"-//microsoft//dtd internet explorer
|
284 |
-
"-//microsoft//dtd internet explorer
|
285 |
-
"-//microsoft//dtd internet explorer 3.0
|
286 |
-
"-//
|
287 |
-
"-//
|
288 |
-
"-//
|
289 |
-
"-//
|
290 |
-
"-//o'reilly and associates//dtd html
|
291 |
-
"-//
|
292 |
-
"-//
|
293 |
-
"-//
|
294 |
-
"-//
|
295 |
-
"-//
|
296 |
-
"-//
|
297 |
-
"-//w3c//dtd html 3
|
298 |
-
"-//w3c//dtd html 3.2//",
|
299 |
-
"-//w3c//dtd html 3.
|
300 |
-
"-//w3c//dtd html
|
301 |
-
"-//w3c//dtd html
|
302 |
-
"-//w3c//dtd html
|
303 |
-
"-//w3c//dtd html
|
304 |
-
"-//w3c//dtd
|
305 |
-
"-//
|
306 |
-
"-//
|
307 |
-
"-//
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
"html",
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
$
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
$this->
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
$
|
427 |
-
|
428 |
-
$this->
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
$
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
'
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
*
|
507 |
-
*
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
'
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
$token['
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
//
|
563 |
-
|
564 |
-
//
|
565 |
-
// the
|
566 |
-
//
|
567 |
-
//
|
568 |
-
//
|
569 |
-
//
|
570 |
-
//
|
571 |
-
//
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
/*
|
608 |
-
$this->
|
609 |
-
/*
|
610 |
-
$this->
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
$token['
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
} elseif ($token['type'] === HTML5_Tokenizer::
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
($token['type'] === HTML5_Tokenizer::
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
($token['type'] === HTML5_Tokenizer::
|
666 |
-
$token['name']
|
667 |
-
|
668 |
-
|
669 |
-
// parse error
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
/*
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
'
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
*
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
element.
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
*
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
$this->
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
case '
|
866 |
-
case '
|
867 |
-
case '
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
if
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
|
888 |
-
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
*
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
}
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
*
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
if
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
$
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
$
|
966 |
-
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
$
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
/*
|
1000 |
-
|
1001 |
-
|
1002 |
-
if
|
1003 |
-
|
1004 |
-
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
if
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
$this->
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
-
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
1049 |
-
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
1068 |
-
|
1069 |
-
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
|
1074 |
-
|
1075 |
-
|
1076 |
-
|
1077 |
-
|
1078 |
-
|
1079 |
-
|
1080 |
-
|
1081 |
-
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
-
case '
|
1087 |
-
|
1088 |
-
|
1089 |
-
|
1090 |
-
|
1091 |
-
|
1092 |
-
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
-
|
1105 |
-
*
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
1109 |
-
|
1110 |
-
|
1111 |
-
|
1112 |
-
|
1113 |
-
|
1114 |
-
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
1120 |
-
|
1121 |
-
|
1122 |
-
|
1123 |
-
|
1124 |
-
|
1125 |
-
|
1126 |
-
|
1127 |
-
|
1128 |
-
|
1129 |
-
|
1130 |
-
|
1131 |
-
|
1132 |
-
|
1133 |
-
|
1134 |
-
|
1135 |
-
|
1136 |
-
|
1137 |
-
|
1138 |
-
|
1139 |
-
|
1140 |
-
|
1141 |
-
|
1142 |
-
|
1143 |
-
|
1144 |
-
|
1145 |
-
|
1146 |
-
|
1147 |
-
$this->
|
1148 |
-
|
1149 |
-
|
1150 |
-
|
1151 |
-
|
1152 |
-
|
1153 |
-
|
1154 |
-
|
1155 |
-
|
1156 |
-
|
1157 |
-
|
1158 |
-
|
1159 |
-
|
1160 |
-
|
1161 |
-
$this->
|
1162 |
-
|
1163 |
-
|
1164 |
-
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
|
1169 |
-
|
1170 |
-
|
1171 |
-
*
|
1172 |
-
|
1173 |
-
|
1174 |
-
|
1175 |
-
|
1176 |
-
|
1177 |
-
|
1178 |
-
|
1179 |
-
|
1180 |
-
|
1181 |
-
|
1182 |
-
|
1183 |
-
$this->
|
1184 |
-
|
1185 |
-
|
1186 |
-
|
1187 |
-
|
1188 |
-
|
1189 |
-
|
1190 |
-
|
1191 |
-
|
1192 |
-
|
1193 |
-
case '
|
1194 |
-
|
1195 |
-
|
1196 |
-
|
1197 |
-
|
1198 |
-
|
1199 |
-
|
1200 |
-
|
1201 |
-
|
1202 |
-
|
1203 |
-
|
1204 |
-
|
1205 |
-
|
1206 |
-
|
1207 |
-
|
1208 |
-
|
1209 |
-
|
1210 |
-
|
1211 |
-
|
1212 |
-
|
1213 |
-
|
1214 |
-
|
1215 |
-
|
1216 |
-
|
1217 |
-
|
1218 |
-
|
1219 |
-
|
1220 |
-
|
1221 |
-
|
1222 |
-
|
1223 |
-
|
1224 |
-
|
1225 |
-
|
1226 |
-
|
1227 |
-
|
1228 |
-
|
1229 |
-
|
1230 |
-
|
1231 |
-
|
1232 |
-
|
1233 |
-
|
1234 |
-
|
1235 |
-
|
1236 |
-
|
1237 |
-
|
1238 |
-
|
1239 |
-
|
1240 |
-
|
1241 |
-
|
1242 |
-
|
1243 |
-
|
1244 |
-
|
1245 |
-
|
1246 |
-
|
1247 |
-
|
1248 |
-
|
1249 |
-
|
1250 |
-
|
1251 |
-
|
1252 |
-
/*
|
1253 |
-
|
1254 |
-
|
1255 |
-
|
1256 |
-
|
1257 |
-
/*
|
1258 |
-
|
1259 |
-
|
1260 |
-
* the
|
1261 |
-
|
1262 |
-
|
1263 |
-
|
1264 |
-
|
1265 |
-
|
1266 |
-
|
1267 |
-
|
1268 |
-
|
1269 |
-
'
|
1270 |
-
|
1271 |
-
|
1272 |
-
|
1273 |
-
|
1274 |
-
|
1275 |
-
|
1276 |
-
|
1277 |
-
'
|
1278 |
-
|
1279 |
-
|
1280 |
-
|
1281 |
-
|
1282 |
-
|
1283 |
-
|
1284 |
-
|
1285 |
-
'
|
1286 |
-
|
1287 |
-
|
1288 |
-
|
1289 |
-
|
1290 |
-
if
|
1291 |
-
|
1292 |
-
|
1293 |
-
|
1294 |
-
|
1295 |
-
|
1296 |
-
|
1297 |
-
|
1298 |
-
|
1299 |
-
|
1300 |
-
|
1301 |
-
|
1302 |
-
|
1303 |
-
|
1304 |
-
|
1305 |
-
|
1306 |
-
|
1307 |
-
|
1308 |
-
|
1309 |
-
|
1310 |
-
|
1311 |
-
|
1312 |
-
|
1313 |
-
$
|
1314 |
-
|
1315 |
-
|
1316 |
-
'
|
1317 |
-
|
1318 |
-
|
1319 |
-
|
1320 |
-
|
1321 |
-
|
1322 |
-
|
1323 |
-
|
1324 |
-
|
1325 |
-
|
1326 |
-
|
1327 |
-
|
1328 |
-
|
1329 |
-
|
1330 |
-
|
1331 |
-
|
1332 |
-
|
1333 |
-
|
1334 |
-
|
1335 |
-
|
1336 |
-
|
1337 |
-
|
1338 |
-
|
1339 |
-
|
1340 |
-
|
1341 |
-
}
|
1342 |
-
|
1343 |
-
|
1344 |
-
|
1345 |
-
|
1346 |
-
|
1347 |
-
|
1348 |
-
|
1349 |
-
|
1350 |
-
|
1351 |
-
*
|
1352 |
-
*
|
1353 |
-
|
1354 |
-
|
1355 |
-
$this->
|
1356 |
-
|
1357 |
-
$this->
|
1358 |
-
|
1359 |
-
|
1360 |
-
|
1361 |
-
|
1362 |
-
|
1363 |
-
|
1364 |
-
|
1365 |
-
|
1366 |
-
|
1367 |
-
|
1368 |
-
|
1369 |
-
if
|
1370 |
-
|
1371 |
-
|
1372 |
-
|
1373 |
-
|
1374 |
-
|
1375 |
-
|
1376 |
-
|
1377 |
-
|
1378 |
-
|
1379 |
-
$this->
|
1380 |
-
|
1381 |
-
$this->
|
1382 |
-
|
1383 |
-
|
1384 |
-
|
1385 |
-
|
1386 |
-
|
1387 |
-
|
1388 |
-
|
1389 |
-
|
1390 |
-
|
1391 |
-
|
1392 |
-
|
1393 |
-
|
1394 |
-
|
1395 |
-
|
1396 |
-
|
1397 |
-
|
1398 |
-
|
1399 |
-
|
1400 |
-
|
1401 |
-
|
1402 |
-
$this->
|
1403 |
-
|
1404 |
-
|
1405 |
-
|
1406 |
-
|
1407 |
-
*
|
1408 |
-
*
|
1409 |
-
|
1410 |
-
|
1411 |
-
|
1412 |
-
$this->mode === self::
|
1413 |
-
|
1414 |
-
$this->mode
|
1415 |
-
|
1416 |
-
$this->mode = self::
|
1417 |
-
}
|
1418 |
-
|
1419 |
-
|
1420 |
-
|
1421 |
-
|
1422 |
-
|
1423 |
-
|
1424 |
-
|
1425 |
-
|
1426 |
-
|
1427 |
-
|
1428 |
-
|
1429 |
-
|
1430 |
-
|
1431 |
-
|
1432 |
-
|
1433 |
-
|
1434 |
-
|
1435 |
-
*
|
1436 |
-
|
1437 |
-
|
1438 |
-
|
1439 |
-
|
1440 |
-
|
1441 |
-
|
1442 |
-
|
1443 |
-
|
1444 |
-
|
1445 |
-
|
1446 |
-
|
1447 |
-
|
1448 |
-
$
|
1449 |
-
|
1450 |
-
|
1451 |
-
|
1452 |
-
|
1453 |
-
|
1454 |
-
|
1455 |
-
|
1456 |
-
$
|
1457 |
-
$this->
|
1458 |
-
|
1459 |
-
|
1460 |
-
|
1461 |
-
|
1462 |
-
|
1463 |
-
|
1464 |
-
|
1465 |
-
|
1466 |
-
|
1467 |
-
|
1468 |
-
|
1469 |
-
|
1470 |
-
|
1471 |
-
$
|
1472 |
-
$this->
|
1473 |
-
|
1474 |
-
|
1475 |
-
|
1476 |
-
|
1477 |
-
|
1478 |
-
|
1479 |
-
|
1480 |
-
|
1481 |
-
|
1482 |
-
|
1483 |
-
|
1484 |
-
|
1485 |
-
|
1486 |
-
|
1487 |
-
|
1488 |
-
|
1489 |
-
|
1490 |
-
|
1491 |
-
|
1492 |
-
|
1493 |
-
$this->
|
1494 |
-
|
1495 |
-
|
1496 |
-
|
1497 |
-
|
1498 |
-
|
1499 |
-
|
1500 |
-
|
1501 |
-
|
1502 |
-
|
1503 |
-
|
1504 |
-
|
1505 |
-
|
1506 |
-
|
1507 |
-
|
1508 |
-
|
1509 |
-
|
1510 |
-
|
1511 |
-
|
1512 |
-
*
|
1513 |
-
*
|
1514 |
-
* element,
|
1515 |
-
*
|
1516 |
-
*
|
1517 |
-
|
1518 |
-
|
1519 |
-
|
1520 |
-
}
|
1521 |
-
|
1522 |
-
|
1523 |
-
|
1524 |
-
|
1525 |
-
|
1526 |
-
|
1527 |
-
|
1528 |
-
|
1529 |
-
|
1530 |
-
|
1531 |
-
|
1532 |
-
|
1533 |
-
|
1534 |
-
|
1535 |
-
|
1536 |
-
|
1537 |
-
|
1538 |
-
|
1539 |
-
|
1540 |
-
|
1541 |
-
|
1542 |
-
|
1543 |
-
case '
|
1544 |
-
case '
|
1545 |
-
case '
|
1546 |
-
|
1547 |
-
|
1548 |
-
|
1549 |
-
|
1550 |
-
|
1551 |
-
|
1552 |
-
|
1553 |
-
|
1554 |
-
is
|
1555 |
-
|
1556 |
-
|
1557 |
-
|
1558 |
-
|
1559 |
-
|
1560 |
-
with
|
1561 |
-
|
1562 |
-
|
1563 |
-
|
1564 |
-
|
1565 |
-
|
1566 |
-
}
|
1567 |
-
|
1568 |
-
|
1569 |
-
|
1570 |
-
|
1571 |
-
|
1572 |
-
|
1573 |
-
/*
|
1574 |
-
$this->form_pointer
|
1575 |
-
/*
|
1576 |
-
|
1577 |
-
|
1578 |
-
|
1579 |
-
|
1580 |
-
|
1581 |
-
|
1582 |
-
|
1583 |
-
/*
|
1584 |
-
|
1585 |
-
|
1586 |
-
|
1587 |
-
|
1588 |
-
|
1589 |
-
|
1590 |
-
|
1591 |
-
|
1592 |
-
|
1593 |
-
|
1594 |
-
|
1595 |
-
|
1596 |
-
|
1597 |
-
|
1598 |
-
|
1599 |
-
|
1600 |
-
|
1601 |
-
|
1602 |
-
|
1603 |
-
|
1604 |
-
|
1605 |
-
|
1606 |
-
|
1607 |
-
|
1608 |
-
|
1609 |
-
|
1610 |
-
|
1611 |
-
|
1612 |
-
|
1613 |
-
|
1614 |
-
|
1615 |
-
|
1616 |
-
|
1617 |
-
|
1618 |
-
|
1619 |
-
|
1620 |
-
|
1621 |
-
|
1622 |
-
|
1623 |
-
|
1624 |
-
|
1625 |
-
|
1626 |
-
|
1627 |
-
|
1628 |
-
|
1629 |
-
|
1630 |
-
|
1631 |
-
|
1632 |
-
|
1633 |
-
|
1634 |
-
|
1635 |
-
|
1636 |
-
|
1637 |
-
|
1638 |
-
|
1639 |
-
|
1640 |
-
|
1641 |
-
|
1642 |
-
|
1643 |
-
|
1644 |
-
|
1645 |
-
|
1646 |
-
|
1647 |
-
|
1648 |
-
|
1649 |
-
|
1650 |
-
|
1651 |
-
|
1652 |
-
|
1653 |
-
|
1654 |
-
|
1655 |
-
|
1656 |
-
|
1657 |
-
|
1658 |
-
|
1659 |
-
|
1660 |
-
|
1661 |
-
|
1662 |
-
|
1663 |
-
|
1664 |
-
|
1665 |
-
|
1666 |
-
|
1667 |
-
|
1668 |
-
|
1669 |
-
|
1670 |
-
|
1671 |
-
|
1672 |
-
|
1673 |
-
|
1674 |
-
|
1675 |
-
|
1676 |
-
|
1677 |
-
|
1678 |
-
|
1679 |
-
|
1680 |
-
|
1681 |
-
|
1682 |
-
|
1683 |
-
|
1684 |
-
|
1685 |
-
|
1686 |
-
|
1687 |
-
|
1688 |
-
|
1689 |
-
|
1690 |
-
|
1691 |
-
|
1692 |
-
|
1693 |
-
|
1694 |
-
|
1695 |
-
|
1696 |
-
|
1697 |
-
|
1698 |
-
|
1699 |
-
|
1700 |
-
case '
|
1701 |
-
|
1702 |
-
|
1703 |
-
|
1704 |
-
|
1705 |
-
|
1706 |
-
|
1707 |
-
|
1708 |
-
|
1709 |
-
|
1710 |
-
|
1711 |
-
|
1712 |
-
|
1713 |
-
|
1714 |
-
|
1715 |
-
|
1716 |
-
$
|
1717 |
-
|
1718 |
-
|
1719 |
-
|
1720 |
-
|
1721 |
-
|
1722 |
-
|
1723 |
-
is
|
1724 |
-
|
1725 |
-
|
1726 |
-
|
1727 |
-
|
1728 |
-
|
1729 |
-
|
1730 |
-
|
1731 |
-
|
1732 |
-
|
1733 |
-
|
1734 |
-
|
1735 |
-
|
1736 |
-
|
1737 |
-
|
1738 |
-
|
1739 |
-
|
1740 |
-
|
1741 |
-
|
1742 |
-
|
1743 |
-
|
1744 |
-
|
1745 |
-
|
1746 |
-
|
1747 |
-
|
1748 |
-
|
1749 |
-
|
1750 |
-
|
1751 |
-
|
1752 |
-
|
1753 |
-
|
1754 |
-
|
1755 |
-
|
1756 |
-
|
1757 |
-
|
1758 |
-
|
1759 |
-
|
1760 |
-
|
1761 |
-
|
1762 |
-
|
1763 |
-
|
1764 |
-
|
1765 |
-
|
1766 |
-
|
1767 |
-
|
1768 |
-
|
1769 |
-
|
1770 |
-
|
1771 |
-
|
1772 |
-
|
1773 |
-
|
1774 |
-
|
1775 |
-
|
1776 |
-
|
1777 |
-
|
1778 |
-
|
1779 |
-
|
1780 |
-
|
1781 |
-
|
1782 |
-
|
1783 |
-
|
1784 |
-
|
1785 |
-
|
1786 |
-
|
1787 |
-
|
1788 |
-
of
|
1789 |
-
$
|
1790 |
-
|
1791 |
-
/*
|
1792 |
-
|
1793 |
-
|
1794 |
-
|
1795 |
-
|
1796 |
-
|
1797 |
-
|
1798 |
-
|
1799 |
-
|
1800 |
-
|
1801 |
-
|
1802 |
-
|
1803 |
-
|
1804 |
-
|
1805 |
-
to
|
1806 |
-
|
1807 |
-
|
1808 |
-
|
1809 |
-
|
1810 |
-
|
1811 |
-
|
1812 |
-
|
1813 |
-
|
1814 |
-
|
1815 |
-
|
1816 |
-
|
1817 |
-
|
1818 |
-
|
1819 |
-
/* 6.
|
1820 |
-
|
1821 |
-
|
1822 |
-
|
1823 |
-
|
1824 |
-
|
1825 |
-
|
1826 |
-
|
1827 |
-
|
1828 |
-
|
1829 |
-
|
1830 |
-
|
1831 |
-
|
1832 |
-
|
1833 |
-
|
1834 |
-
|
1835 |
-
|
1836 |
-
|
1837 |
-
|
1838 |
-
|
1839 |
-
|
1840 |
-
|
1841 |
-
|
1842 |
-
|
1843 |
-
|
1844 |
-
|
1845 |
-
|
1846 |
-
|
1847 |
-
|
1848 |
-
|
1849 |
-
|
1850 |
-
|
1851 |
-
|
1852 |
-
|
1853 |
-
|
1854 |
-
|
1855 |
-
|
1856 |
-
|
1857 |
-
|
1858 |
-
|
1859 |
-
|
1860 |
-
|
1861 |
-
|
1862 |
-
|
1863 |
-
|
1864 |
-
|
1865 |
-
|
1866 |
-
|
1867 |
-
|
1868 |
-
|
1869 |
-
|
1870 |
-
|
1871 |
-
|
1872 |
-
|
1873 |
-
|
1874 |
-
|
1875 |
-
|
1876 |
-
$common_ancestor->appendChild($last_node);
|
1877 |
-
}
|
1878 |
-
|
1879 |
-
/* 8. Create an element for the token for which the
|
1880 |
-
* formatting element was created. */
|
1881 |
-
// XDOM
|
1882 |
-
$clone = $formatting_element->cloneNode();
|
1883 |
-
|
1884 |
-
/* 9. Take all of the child nodes of the furthest
|
1885 |
-
block and append them to the element created in the
|
1886 |
-
last step. */
|
1887 |
-
// XDOM
|
1888 |
-
while ($furthest_block->hasChildNodes()) {
|
1889 |
-
$child = $furthest_block->firstChild;
|
1890 |
-
$furthest_block->removeChild($child);
|
1891 |
-
$clone->appendChild($child);
|
1892 |
-
}
|
1893 |
-
|
1894 |
-
/* 10. Append that clone to the furthest block. */
|
1895 |
-
// XDOM
|
1896 |
-
$furthest_block->appendChild($clone);
|
1897 |
-
|
1898 |
-
/* 11. Remove the formatting element from the list
|
1899 |
-
of active formatting elements, and insert the new element
|
1900 |
-
into the list of active formatting elements at the
|
1901 |
-
position of the aforementioned bookmark. */
|
1902 |
-
$fe_af_pos = array_search($formatting_element, $this->a_formatting, true);
|
1903 |
-
array_splice($this->a_formatting, $fe_af_pos, 1);
|
1904 |
-
|
1905 |
-
$af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
|
1906 |
-
$af_part2 = array_slice($this->a_formatting, $bookmark);
|
1907 |
-
$this->a_formatting = array_merge($af_part1, array($clone), $af_part2);
|
1908 |
-
|
1909 |
-
/* 12. Remove the formatting element from the stack
|
1910 |
-
of open elements, and insert the new element into the stack
|
1911 |
-
of open elements immediately below the position of the
|
1912 |
-
furthest block in that stack. */
|
1913 |
-
$fe_s_pos = array_search($formatting_element, $this->stack, true);
|
1914 |
-
array_splice($this->stack, $fe_s_pos, 1);
|
1915 |
-
|
1916 |
-
$fb_s_pos = array_search($furthest_block, $this->stack, true);
|
1917 |
-
$s_part1 = array_slice($this->stack, 0, $fb_s_pos + 1);
|
1918 |
-
$s_part2 = array_slice($this->stack, $fb_s_pos + 1);
|
1919 |
-
$this->stack = array_merge($s_part1, array($clone), $s_part2);
|
1920 |
-
|
1921 |
-
/* 13. Jump back to step 1 in this series of steps. */
|
1922 |
-
unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
|
1923 |
-
}
|
1924 |
-
break;
|
1925 |
-
|
1926 |
-
case 'applet': case 'button': case 'marquee': case 'object':
|
1927 |
-
/* If the stack of open elements has an element in scope whose
|
1928 |
-
tag name matches the tag name of the token, then generate implied
|
1929 |
-
tags. */
|
1930 |
-
if ($this->elementInScope($token['name'])) {
|
1931 |
-
$this->generateImpliedEndTags();
|
1932 |
-
|
1933 |
-
/* Now, if the current node is not an element with the same
|
1934 |
-
tag name as the token, then this is a parse error. */
|
1935 |
-
// XERROR: implement logic
|
1936 |
-
|
1937 |
-
/* Pop elements from the stack of open elements until
|
1938 |
-
* an element with the same tag name as the token has
|
1939 |
-
* been popped from the stack. */
|
1940 |
-
do {
|
1941 |
-
$node = array_pop($this->stack);
|
1942 |
-
} while ($node->tagName !== $token['name']);
|
1943 |
-
|
1944 |
-
/* Clear the list of active formatting elements up to the
|
1945 |
-
* last marker. */
|
1946 |
-
$keys = array_keys($this->a_formatting, self::MARKER, true);
|
1947 |
-
$marker = end($keys);
|
1948 |
-
|
1949 |
-
for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
|
1950 |
-
array_pop($this->a_formatting);
|
1951 |
-
}
|
1952 |
-
}
|
1953 |
-
/*else {
|
1954 |
-
// parse error
|
1955 |
-
}*/
|
1956 |
-
break;
|
1957 |
-
|
1958 |
-
case 'br':
|
1959 |
-
// Parse error
|
1960 |
-
$this->emitToken(array(
|
1961 |
-
'name' => 'br',
|
1962 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
1963 |
-
));
|
1964 |
-
break;
|
1965 |
-
|
1966 |
-
/* An end tag token not covered by the previous entries */
|
1967 |
-
default:
|
1968 |
-
for ($n = count($this->stack) - 1; $n >= 0; $n--) {
|
1969 |
-
/* Initialise node to be the current node (the bottommost
|
1970 |
-
node of the stack). */
|
1971 |
-
$node = $this->stack[$n];
|
1972 |
-
|
1973 |
-
/* If node has the same tag name as the end tag token,
|
1974 |
-
then: */
|
1975 |
-
if ($token['name'] === $node->tagName) {
|
1976 |
-
/* Generate implied end tags. */
|
1977 |
-
$this->generateImpliedEndTags();
|
1978 |
-
|
1979 |
-
/* If the tag name of the end tag token does not
|
1980 |
-
match the tag name of the current node, this is a
|
1981 |
-
parse error. */
|
1982 |
-
// XERROR: implement this
|
1983 |
-
|
1984 |
-
/* Pop all the nodes from the current node up to
|
1985 |
-
node, including node, then stop these steps. */
|
1986 |
-
// XSKETCHY
|
1987 |
-
do {
|
1988 |
-
$pop = array_pop($this->stack);
|
1989 |
-
} while ($pop !== $node);
|
1990 |
-
break;
|
1991 |
-
} else {
|
1992 |
-
$category = $this->getElementCategory($node);
|
1993 |
-
|
1994 |
-
if ($category !== self::FORMATTING && $category !== self::PHRASING) {
|
1995 |
-
/* Otherwise, if node is in neither the formatting
|
1996 |
-
category nor the phrasing category, then this is a
|
1997 |
-
parse error. Stop this algorithm. The end tag token
|
1998 |
-
is ignored. */
|
1999 |
-
$this->ignored = true;
|
2000 |
-
break;
|
2001 |
-
// parse error
|
2002 |
-
}
|
2003 |
-
}
|
2004 |
-
/* Set node to the previous entry in the stack of open elements. Loop. */
|
2005 |
-
}
|
2006 |
-
break;
|
2007 |
-
}
|
2008 |
-
break;
|
2009 |
-
}
|
2010 |
-
break;
|
2011 |
-
|
2012 |
-
case self::IN_CDATA_RCDATA:
|
2013 |
-
if (
|
2014 |
-
$token['type'] === HTML5_Tokenizer::CHARACTER ||
|
2015 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER
|
2016 |
-
) {
|
2017 |
-
$this->insertText($token['data']);
|
2018 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
2019 |
-
// parse error
|
2020 |
-
/* If the current node is a script element, mark the script
|
2021 |
-
* element as "already executed". */
|
2022 |
-
// probably not necessary
|
2023 |
-
array_pop($this->stack);
|
2024 |
-
$this->mode = $this->original_mode;
|
2025 |
-
$this->emitToken($token);
|
2026 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'script') {
|
2027 |
-
array_pop($this->stack);
|
2028 |
-
$this->mode = $this->original_mode;
|
2029 |
-
// we're ignoring all of the execution stuff
|
2030 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG) {
|
2031 |
-
array_pop($this->stack);
|
2032 |
-
$this->mode = $this->original_mode;
|
2033 |
-
}
|
2034 |
-
break;
|
2035 |
-
|
2036 |
-
case self::IN_TABLE:
|
2037 |
-
$clear = array('html', 'table');
|
2038 |
-
|
2039 |
-
/* A character token */
|
2040 |
-
if ($token['type'] === HTML5_Tokenizer::CHARACTER ||
|
2041 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2042 |
-
/* Let the pending table character tokens
|
2043 |
-
* be an empty list of tokens. */
|
2044 |
-
$this->pendingTableCharacters = "";
|
2045 |
-
$this->pendingTableCharactersDirty = false;
|
2046 |
-
/* Let the original insertion mode be the current
|
2047 |
-
* insertion mode. */
|
2048 |
-
$this->original_mode = $this->mode;
|
2049 |
-
/* Switch the insertion mode to
|
2050 |
-
* "in table text" and
|
2051 |
-
* reprocess the token. */
|
2052 |
-
$this->mode = self::IN_TABLE_TEXT;
|
2053 |
-
$this->emitToken($token);
|
2054 |
-
|
2055 |
-
/* A comment token */
|
2056 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2057 |
-
/* Append a Comment node to the current node with the data
|
2058 |
-
attribute set to the data given in the comment token. */
|
2059 |
-
$this->insertComment($token['data']);
|
2060 |
-
|
2061 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
2062 |
-
// parse error
|
2063 |
-
|
2064 |
-
/* A start tag whose tag name is "caption" */
|
2065 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2066 |
-
$token['name'] === 'caption') {
|
2067 |
-
/* Clear the stack back to a table context. */
|
2068 |
-
$this->clearStackToTableContext($clear);
|
2069 |
-
|
2070 |
-
/* Insert a marker at the end of the list of active
|
2071 |
-
formatting elements. */
|
2072 |
-
$this->a_formatting[] = self::MARKER;
|
2073 |
-
|
2074 |
-
/* Insert an HTML element for the token, then switch the
|
2075 |
-
insertion mode to "in caption". */
|
2076 |
-
$this->insertElement($token);
|
2077 |
-
$this->mode = self::IN_CAPTION;
|
2078 |
-
|
2079 |
-
/* A start tag whose tag name is "colgroup" */
|
2080 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2081 |
-
$token['name'] === 'colgroup') {
|
2082 |
-
/* Clear the stack back to a table context. */
|
2083 |
-
$this->clearStackToTableContext($clear);
|
2084 |
-
|
2085 |
-
/* Insert an HTML element for the token, then switch the
|
2086 |
-
insertion mode to "in column group". */
|
2087 |
-
$this->insertElement($token);
|
2088 |
-
$this->mode = self::IN_COLUMN_GROUP;
|
2089 |
-
|
2090 |
-
/* A start tag whose tag name is "col" */
|
2091 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2092 |
-
$token['name'] === 'col') {
|
2093 |
-
$this->emitToken(array(
|
2094 |
-
'name' => 'colgroup',
|
2095 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
2096 |
-
'attr' => array()
|
2097 |
-
));
|
2098 |
-
|
2099 |
-
$this->emitToken($token);
|
2100 |
-
|
2101 |
-
/* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
|
2102 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2103 |
-
array('tbody', 'tfoot', 'thead'))) {
|
2104 |
-
/* Clear the stack back to a table context. */
|
2105 |
-
$this->clearStackToTableContext($clear);
|
2106 |
-
|
2107 |
-
/* Insert an HTML element for the token, then switch the insertion
|
2108 |
-
mode to "in table body". */
|
2109 |
-
$this->insertElement($token);
|
2110 |
-
$this->mode = self::IN_TABLE_BODY;
|
2111 |
-
|
2112 |
-
/* A start tag whose tag name is one of: "td", "th", "tr" */
|
2113 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2114 |
-
in_array($token['name'], array('td', 'th', 'tr'))) {
|
2115 |
-
/* Act as if a start tag token with the tag name "tbody" had been
|
2116 |
-
seen, then reprocess the current token. */
|
2117 |
-
$this->emitToken(array(
|
2118 |
-
'name' => 'tbody',
|
2119 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
2120 |
-
'attr' => array()
|
2121 |
-
));
|
2122 |
-
|
2123 |
-
$this->emitToken($token);
|
2124 |
-
|
2125 |
-
/* A start tag whose tag name is "table" */
|
2126 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2127 |
-
$token['name'] === 'table') {
|
2128 |
-
/* Parse error. Act as if an end tag token with the tag name "table"
|
2129 |
-
had been seen, then, if that token wasn't ignored, reprocess the
|
2130 |
-
current token. */
|
2131 |
-
$this->emitToken(array(
|
2132 |
-
'name' => 'table',
|
2133 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2134 |
-
));
|
2135 |
-
|
2136 |
-
if (!$this->ignored) $this->emitToken($token);
|
2137 |
-
|
2138 |
-
/* An end tag whose tag name is "table" */
|
2139 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2140 |
-
$token['name'] === 'table') {
|
2141 |
-
/* If the stack of open elements does not have an element in table
|
2142 |
-
scope with the same tag name as the token, this is a parse error.
|
2143 |
-
Ignore the token. (fragment case) */
|
2144 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2145 |
-
$this->ignored = true;
|
2146 |
-
} else {
|
2147 |
-
do {
|
2148 |
-
$node = array_pop($this->stack);
|
2149 |
-
} while ($node->tagName !== 'table');
|
2150 |
-
|
2151 |
-
/* Reset the insertion mode appropriately. */
|
2152 |
-
$this->resetInsertionMode();
|
2153 |
-
}
|
2154 |
-
|
2155 |
-
/* An end tag whose tag name is one of: "body", "caption", "col",
|
2156 |
-
"colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
|
2157 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2158 |
-
array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td',
|
2159 |
-
'tfoot', 'th', 'thead', 'tr'))) {
|
2160 |
-
// Parse error. Ignore the token.
|
2161 |
-
|
2162 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2163 |
-
($token['name'] === 'style' || $token['name'] === 'script')) {
|
2164 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
2165 |
-
|
2166 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'input' &&
|
2167 |
-
// assignment is intentional
|
2168 |
-
/* If the token does not have an attribute with the name "type", or
|
2169 |
-
* if it does, but that attribute's value is not an ASCII
|
2170 |
-
* case-insensitive match for the string "hidden", then: act as
|
2171 |
-
* described in the "anything else" entry below. */
|
2172 |
-
($type = $this->getAttr($token, 'type')) && strtolower($type) === 'hidden') {
|
2173 |
-
// I.e., if its an input with the type attribute == 'hidden'
|
2174 |
-
/* Otherwise */
|
2175 |
-
// parse error
|
2176 |
-
$this->insertElement($token);
|
2177 |
-
array_pop($this->stack);
|
2178 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
2179 |
-
/* If the current node is not the root html element, then this is a parse error. */
|
2180 |
-
if (end($this->stack)->tagName !== 'html') {
|
2181 |
-
// Note: It can only be the current node in the fragment case.
|
2182 |
-
// parse error
|
2183 |
-
}
|
2184 |
-
/* Stop parsing. */
|
2185 |
-
/* Anything else */
|
2186 |
-
} else {
|
2187 |
-
/* Parse error. Process the token as if the insertion mode was "in
|
2188 |
-
body", with the following exception: */
|
2189 |
-
|
2190 |
-
$old = $this->foster_parent;
|
2191 |
-
$this->foster_parent = true;
|
2192 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2193 |
-
$this->foster_parent = $old;
|
2194 |
-
}
|
2195 |
-
break;
|
2196 |
-
|
2197 |
-
case self::IN_TABLE_TEXT:
|
2198 |
-
/* A character token */
|
2199 |
-
if ($token['type'] === HTML5_Tokenizer::CHARACTER) {
|
2200 |
-
/* Append the character token to the pending table
|
2201 |
-
* character tokens list. */
|
2202 |
-
$this->pendingTableCharacters .= $token['data'];
|
2203 |
-
$this->pendingTableCharactersDirty = true;
|
2204 |
-
} elseif ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2205 |
-
$this->pendingTableCharacters .= $token['data'];
|
2206 |
-
/* Anything else */
|
2207 |
-
} else {
|
2208 |
-
if ($this->pendingTableCharacters !== '' && is_string($this->pendingTableCharacters)) {
|
2209 |
-
/* If any of the tokens in the pending table character tokens list
|
2210 |
-
* are character tokens that are not one of U+0009 CHARACTER
|
2211 |
-
* TABULATION, U+000A LINE FEED (LF), U+000C FORM FEED (FF), or
|
2212 |
-
* U+0020 SPACE, then reprocess those character tokens using the
|
2213 |
-
* rules given in the "anything else" entry in the in table"
|
2214 |
-
* insertion mode.*/
|
2215 |
-
if ($this->pendingTableCharactersDirty) {
|
2216 |
-
/* Parse error. Process the token using the rules for the
|
2217 |
-
* "in body" insertion mode, except that if the current
|
2218 |
-
* node is a table, tbody, tfoot, thead, or tr element,
|
2219 |
-
* then, whenever a node would be inserted into the current
|
2220 |
-
* node, it must instead be foster parented. */
|
2221 |
-
// XERROR
|
2222 |
-
$old = $this->foster_parent;
|
2223 |
-
$this->foster_parent = true;
|
2224 |
-
$text_token = array(
|
2225 |
-
'type' => HTML5_Tokenizer::CHARACTER,
|
2226 |
-
'data' => $this->pendingTableCharacters,
|
2227 |
-
);
|
2228 |
-
$this->processWithRulesFor($text_token, self::IN_BODY);
|
2229 |
-
$this->foster_parent = $old;
|
2230 |
-
|
2231 |
-
/* Otherwise, insert the characters given by the pending table
|
2232 |
-
* character tokens list into the current node. */
|
2233 |
-
} else {
|
2234 |
-
$this->insertText($this->pendingTableCharacters);
|
2235 |
-
}
|
2236 |
-
$this->pendingTableCharacters = null;
|
2237 |
-
$this->pendingTableCharactersNull = null;
|
2238 |
-
}
|
2239 |
-
|
2240 |
-
/* Switch the insertion mode to the original insertion mode and
|
2241 |
-
* reprocess the token.
|
2242 |
-
*/
|
2243 |
-
$this->mode = $this->original_mode;
|
2244 |
-
$this->emitToken($token);
|
2245 |
-
}
|
2246 |
-
break;
|
2247 |
-
|
2248 |
-
case self::IN_CAPTION:
|
2249 |
-
/* An end tag whose tag name is "caption" */
|
2250 |
-
if ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'caption') {
|
2251 |
-
/* If the stack of open elements does not have an element in table
|
2252 |
-
scope with the same tag name as the token, this is a parse error.
|
2253 |
-
Ignore the token. (fragment case) */
|
2254 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2255 |
-
$this->ignored = true;
|
2256 |
-
// Ignore
|
2257 |
-
|
2258 |
-
/* Otherwise: */
|
2259 |
-
} else {
|
2260 |
-
/* Generate implied end tags. */
|
2261 |
-
$this->generateImpliedEndTags();
|
2262 |
-
|
2263 |
-
/* Now, if the current node is not a caption element, then this
|
2264 |
-
is a parse error. */
|
2265 |
-
// XERROR: implement
|
2266 |
-
|
2267 |
-
/* Pop elements from this stack until a caption element has
|
2268 |
-
been popped from the stack. */
|
2269 |
-
do {
|
2270 |
-
$node = array_pop($this->stack);
|
2271 |
-
} while ($node->tagName !== 'caption');
|
2272 |
-
|
2273 |
-
/* Clear the list of active formatting elements up to the last
|
2274 |
-
marker. */
|
2275 |
-
$this->clearTheActiveFormattingElementsUpToTheLastMarker();
|
2276 |
-
|
2277 |
-
/* Switch the insertion mode to "in table". */
|
2278 |
-
$this->mode = self::IN_TABLE;
|
2279 |
-
}
|
2280 |
-
|
2281 |
-
/* A start tag whose tag name is one of: "caption", "col", "colgroup",
|
2282 |
-
"tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag
|
2283 |
-
name is "table" */
|
2284 |
-
} elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2285 |
-
array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
|
2286 |
-
'thead', 'tr'))) || ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2287 |
-
$token['name'] === 'table')) {
|
2288 |
-
/* Parse error. Act as if an end tag with the tag name "caption"
|
2289 |
-
had been seen, then, if that token wasn't ignored, reprocess the
|
2290 |
-
current token. */
|
2291 |
-
$this->emitToken(array(
|
2292 |
-
'name' => 'caption',
|
2293 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2294 |
-
));
|
2295 |
-
|
2296 |
-
if (!$this->ignored) {
|
2297 |
-
$this->emitToken($token);
|
2298 |
-
}
|
2299 |
-
|
2300 |
-
/* An end tag whose tag name is one of: "body", "col", "colgroup",
|
2301 |
-
"html", "tbody", "td", "tfoot", "th", "thead", "tr" */
|
2302 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2303 |
-
array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th',
|
2304 |
-
'thead', 'tr'))) {
|
2305 |
-
// Parse error. Ignore the token.
|
2306 |
-
$this->ignored = true;
|
2307 |
-
} else {
|
2308 |
-
/* Process the token as if the insertion mode was "in body". */
|
2309 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2310 |
-
}
|
2311 |
-
break;
|
2312 |
-
|
2313 |
-
case self::IN_COLUMN_GROUP:
|
2314 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
2315 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
2316 |
-
or U+0020 SPACE */
|
2317 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2318 |
-
/* Append the character to the current node. */
|
2319 |
-
$this->insertText($token['data']);
|
2320 |
-
|
2321 |
-
/* A comment token */
|
2322 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2323 |
-
/* Append a Comment node to the current node with the data
|
2324 |
-
attribute set to the data given in the comment token. */
|
2325 |
-
$this->insertComment($token['data']);
|
2326 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
2327 |
-
// parse error
|
2328 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
2329 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2330 |
-
|
2331 |
-
/* A start tag whose tag name is "col" */
|
2332 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'col') {
|
2333 |
-
/* Insert a col element for the token. Immediately pop the current
|
2334 |
-
node off the stack of open elements. */
|
2335 |
-
$this->insertElement($token);
|
2336 |
-
array_pop($this->stack);
|
2337 |
-
// XERROR: Acknowledge the token's self-closing flag, if it is set.
|
2338 |
-
|
2339 |
-
/* An end tag whose tag name is "colgroup" */
|
2340 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2341 |
-
$token['name'] === 'colgroup') {
|
2342 |
-
/* If the current node is the root html element, then this is a
|
2343 |
-
parse error, ignore the token. (fragment case) */
|
2344 |
-
if (end($this->stack)->tagName === 'html') {
|
2345 |
-
$this->ignored = true;
|
2346 |
-
|
2347 |
-
/* Otherwise, pop the current node (which will be a colgroup
|
2348 |
-
element) from the stack of open elements. Switch the insertion
|
2349 |
-
mode to "in table". */
|
2350 |
-
} else {
|
2351 |
-
array_pop($this->stack);
|
2352 |
-
$this->mode = self::IN_TABLE;
|
2353 |
-
}
|
2354 |
-
|
2355 |
-
/* An end tag whose tag name is "col" */
|
2356 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'col') {
|
2357 |
-
/* Parse error. Ignore the token. */
|
2358 |
-
$this->ignored = true;
|
2359 |
-
|
2360 |
-
/* An end-of-file token */
|
2361 |
-
/* If the current node is the root html element */
|
2362 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF && end($this->stack)->tagName === 'html') {
|
2363 |
-
/* Stop parsing */
|
2364 |
-
|
2365 |
-
/* Anything else */
|
2366 |
-
} else {
|
2367 |
-
/* Act as if an end tag with the tag name "colgroup" had been seen,
|
2368 |
-
and then, if that token wasn't ignored, reprocess the current token. */
|
2369 |
-
$this->emitToken(array(
|
2370 |
-
'name' => 'colgroup',
|
2371 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2372 |
-
));
|
2373 |
-
|
2374 |
-
if (!$this->ignored) $this->emitToken($token);
|
2375 |
-
}
|
2376 |
-
break;
|
2377 |
-
|
2378 |
-
case self::IN_TABLE_BODY:
|
2379 |
-
$clear = array('tbody', 'tfoot', 'thead', 'html');
|
2380 |
-
|
2381 |
-
/* A start tag whose tag name is "tr" */
|
2382 |
-
if ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'tr') {
|
2383 |
-
/* Clear the stack back to a table body context. */
|
2384 |
-
$this->clearStackToTableContext($clear);
|
2385 |
-
|
2386 |
-
/* Insert a tr element for the token, then switch the insertion
|
2387 |
-
mode to "in row". */
|
2388 |
-
$this->insertElement($token);
|
2389 |
-
$this->mode = self::IN_ROW;
|
2390 |
-
|
2391 |
-
/* A start tag whose tag name is one of: "th", "td" */
|
2392 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2393 |
-
($token['name'] === 'th' || $token['name'] === 'td')) {
|
2394 |
-
/* Parse error. Act as if a start tag with the tag name "tr" had
|
2395 |
-
been seen, then reprocess the current token. */
|
2396 |
-
$this->emitToken(array(
|
2397 |
-
'name' => 'tr',
|
2398 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
2399 |
-
'attr' => array()
|
2400 |
-
));
|
2401 |
-
|
2402 |
-
$this->emitToken($token);
|
2403 |
-
|
2404 |
-
/* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
|
2405 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2406 |
-
in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
|
2407 |
-
/* If the stack of open elements does not have an element in table
|
2408 |
-
scope with the same tag name as the token, this is a parse error.
|
2409 |
-
Ignore the token. */
|
2410 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2411 |
-
// Parse error
|
2412 |
-
$this->ignored = true;
|
2413 |
-
|
2414 |
-
/* Otherwise: */
|
2415 |
-
} else {
|
2416 |
-
/* Clear the stack back to a table body context. */
|
2417 |
-
$this->clearStackToTableContext($clear);
|
2418 |
-
|
2419 |
-
/* Pop the current node from the stack of open elements. Switch
|
2420 |
-
the insertion mode to "in table". */
|
2421 |
-
array_pop($this->stack);
|
2422 |
-
$this->mode = self::IN_TABLE;
|
2423 |
-
}
|
2424 |
-
|
2425 |
-
/* A start tag whose tag name is one of: "caption", "col", "colgroup",
|
2426 |
-
"tbody", "tfoot", "thead", or an end tag whose tag name is "table" */
|
2427 |
-
} elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2428 |
-
array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead'))) ||
|
2429 |
-
($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) {
|
2430 |
-
/* If the stack of open elements does not have a tbody, thead, or
|
2431 |
-
tfoot element in table scope, this is a parse error. Ignore the
|
2432 |
-
token. (fragment case) */
|
2433 |
-
if (!$this->elementInScope(array('tbody', 'thead', 'tfoot'), self::SCOPE_TABLE)) {
|
2434 |
-
// parse error
|
2435 |
-
$this->ignored = true;
|
2436 |
-
|
2437 |
-
/* Otherwise: */
|
2438 |
-
} else {
|
2439 |
-
/* Clear the stack back to a table body context. */
|
2440 |
-
$this->clearStackToTableContext($clear);
|
2441 |
-
|
2442 |
-
/* Act as if an end tag with the same tag name as the current
|
2443 |
-
node ("tbody", "tfoot", or "thead") had been seen, then
|
2444 |
-
reprocess the current token. */
|
2445 |
-
$this->emitToken(array(
|
2446 |
-
'name' => end($this->stack)->tagName,
|
2447 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2448 |
-
));
|
2449 |
-
|
2450 |
-
$this->emitToken($token);
|
2451 |
-
}
|
2452 |
-
|
2453 |
-
/* An end tag whose tag name is one of: "body", "caption", "col",
|
2454 |
-
"colgroup", "html", "td", "th", "tr" */
|
2455 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2456 |
-
array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) {
|
2457 |
-
/* Parse error. Ignore the token. */
|
2458 |
-
$this->ignored = true;
|
2459 |
-
|
2460 |
-
/* Anything else */
|
2461 |
-
} else {
|
2462 |
-
/* Process the token as if the insertion mode was "in table". */
|
2463 |
-
$this->processWithRulesFor($token, self::IN_TABLE);
|
2464 |
-
}
|
2465 |
-
break;
|
2466 |
-
|
2467 |
-
case self::IN_ROW:
|
2468 |
-
$clear = array('tr', 'html');
|
2469 |
-
|
2470 |
-
/* A start tag whose tag name is one of: "th", "td" */
|
2471 |
-
if ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2472 |
-
($token['name'] === 'th' || $token['name'] === 'td')) {
|
2473 |
-
/* Clear the stack back to a table row context. */
|
2474 |
-
$this->clearStackToTableContext($clear);
|
2475 |
-
|
2476 |
-
/* Insert an HTML element for the token, then switch the insertion
|
2477 |
-
mode to "in cell". */
|
2478 |
-
$this->insertElement($token);
|
2479 |
-
$this->mode = self::IN_CELL;
|
2480 |
-
|
2481 |
-
/* Insert a marker at the end of the list of active formatting
|
2482 |
-
elements. */
|
2483 |
-
$this->a_formatting[] = self::MARKER;
|
2484 |
-
|
2485 |
-
/* An end tag whose tag name is "tr" */
|
2486 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'tr') {
|
2487 |
-
/* If the stack of open elements does not have an element in table
|
2488 |
-
scope with the same tag name as the token, this is a parse error.
|
2489 |
-
Ignore the token. (fragment case) */
|
2490 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2491 |
-
// Ignore.
|
2492 |
-
$this->ignored = true;
|
2493 |
-
} else {
|
2494 |
-
/* Clear the stack back to a table row context. */
|
2495 |
-
$this->clearStackToTableContext($clear);
|
2496 |
-
|
2497 |
-
/* Pop the current node (which will be a tr element) from the
|
2498 |
-
stack of open elements. Switch the insertion mode to "in table
|
2499 |
-
body". */
|
2500 |
-
array_pop($this->stack);
|
2501 |
-
$this->mode = self::IN_TABLE_BODY;
|
2502 |
-
}
|
2503 |
-
|
2504 |
-
/* A start tag whose tag name is one of: "caption", "col", "colgroup",
|
2505 |
-
"tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */
|
2506 |
-
} elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2507 |
-
array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) ||
|
2508 |
-
($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) {
|
2509 |
-
/* Act as if an end tag with the tag name "tr" had been seen, then,
|
2510 |
-
if that token wasn't ignored, reprocess the current token. */
|
2511 |
-
$this->emitToken(array(
|
2512 |
-
'name' => 'tr',
|
2513 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2514 |
-
));
|
2515 |
-
if (!$this->ignored) $this->emitToken($token);
|
2516 |
-
|
2517 |
-
/* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
|
2518 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2519 |
-
in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
|
2520 |
-
/* If the stack of open elements does not have an element in table
|
2521 |
-
scope with the same tag name as the token, this is a parse error.
|
2522 |
-
Ignore the token. */
|
2523 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2524 |
-
$this->ignored = true;
|
2525 |
-
|
2526 |
-
/* Otherwise: */
|
2527 |
-
} else {
|
2528 |
-
/* Otherwise, act as if an end tag with the tag name "tr" had
|
2529 |
-
been seen, then reprocess the current token. */
|
2530 |
-
$this->emitToken(array(
|
2531 |
-
'name' => 'tr',
|
2532 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2533 |
-
));
|
2534 |
-
|
2535 |
-
$this->emitToken($token);
|
2536 |
-
}
|
2537 |
-
|
2538 |
-
/* An end tag whose tag name is one of: "body", "caption", "col",
|
2539 |
-
"colgroup", "html", "td", "th" */
|
2540 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2541 |
-
array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th'))) {
|
2542 |
-
/* Parse error. Ignore the token. */
|
2543 |
-
$this->ignored = true;
|
2544 |
-
|
2545 |
-
/* Anything else */
|
2546 |
-
} else {
|
2547 |
-
/* Process the token as if the insertion mode was "in table". */
|
2548 |
-
$this->processWithRulesFor($token, self::IN_TABLE);
|
2549 |
-
}
|
2550 |
-
break;
|
2551 |
-
|
2552 |
-
case self::IN_CELL:
|
2553 |
-
/* An end tag whose tag name is one of: "td", "th" */
|
2554 |
-
if ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2555 |
-
($token['name'] === 'td' || $token['name'] === 'th')) {
|
2556 |
-
/* If the stack of open elements does not have an element in table
|
2557 |
-
scope with the same tag name as that of the token, then this is a
|
2558 |
-
parse error and the token must be ignored. */
|
2559 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2560 |
-
$this->ignored = true;
|
2561 |
-
|
2562 |
-
/* Otherwise: */
|
2563 |
-
} else {
|
2564 |
-
/* Generate implied end tags, except for elements with the same
|
2565 |
-
tag name as the token. */
|
2566 |
-
$this->generateImpliedEndTags(array($token['name']));
|
2567 |
-
|
2568 |
-
/* Now, if the current node is not an element with the same tag
|
2569 |
-
name as the token, then this is a parse error. */
|
2570 |
-
// XERROR: Implement parse error code
|
2571 |
-
|
2572 |
-
/* Pop elements from this stack until an element with the same
|
2573 |
-
tag name as the token has been popped from the stack. */
|
2574 |
-
do {
|
2575 |
-
$node = array_pop($this->stack);
|
2576 |
-
} while ($node->tagName !== $token['name']);
|
2577 |
-
|
2578 |
-
/* Clear the list of active formatting elements up to the last
|
2579 |
-
marker. */
|
2580 |
-
$this->clearTheActiveFormattingElementsUpToTheLastMarker();
|
2581 |
-
|
2582 |
-
/* Switch the insertion mode to "in row". (The current node
|
2583 |
-
will be a tr element at this point.) */
|
2584 |
-
$this->mode = self::IN_ROW;
|
2585 |
-
}
|
2586 |
-
|
2587 |
-
/* A start tag whose tag name is one of: "caption", "col", "colgroup",
|
2588 |
-
"tbody", "td", "tfoot", "th", "thead", "tr" */
|
2589 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2590 |
-
array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
|
2591 |
-
'thead', 'tr'))) {
|
2592 |
-
/* If the stack of open elements does not have a td or th element
|
2593 |
-
in table scope, then this is a parse error; ignore the token.
|
2594 |
-
(fragment case) */
|
2595 |
-
if (!$this->elementInScope(array('td', 'th'), self::SCOPE_TABLE)) {
|
2596 |
-
// parse error
|
2597 |
-
$this->ignored = true;
|
2598 |
-
|
2599 |
-
/* Otherwise, close the cell (see below) and reprocess the current
|
2600 |
-
token. */
|
2601 |
-
} else {
|
2602 |
-
$this->closeCell();
|
2603 |
-
$this->emitToken($token);
|
2604 |
-
}
|
2605 |
-
|
2606 |
-
/* An end tag whose tag name is one of: "body", "caption", "col",
|
2607 |
-
"colgroup", "html" */
|
2608 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2609 |
-
array('body', 'caption', 'col', 'colgroup', 'html'))) {
|
2610 |
-
/* Parse error. Ignore the token. */
|
2611 |
-
$this->ignored = true;
|
2612 |
-
|
2613 |
-
/* An end tag whose tag name is one of: "table", "tbody", "tfoot",
|
2614 |
-
"thead", "tr" */
|
2615 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2616 |
-
array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
|
2617 |
-
/* If the stack of open elements does not have a td or th element
|
2618 |
-
in table scope, then this is a parse error; ignore the token.
|
2619 |
-
(innerHTML case) */
|
2620 |
-
if (!$this->elementInScope(array('td', 'th'), self::SCOPE_TABLE)) {
|
2621 |
-
// Parse error
|
2622 |
-
$this->ignored = true;
|
2623 |
-
|
2624 |
-
/* Otherwise, close the cell (see below) and reprocess the current
|
2625 |
-
token. */
|
2626 |
-
} else {
|
2627 |
-
$this->closeCell();
|
2628 |
-
$this->emitToken($token);
|
2629 |
-
}
|
2630 |
-
|
2631 |
-
/* Anything else */
|
2632 |
-
} else {
|
2633 |
-
/* Process the token as if the insertion mode was "in body". */
|
2634 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2635 |
-
}
|
2636 |
-
break;
|
2637 |
-
|
2638 |
-
case self::IN_SELECT:
|
2639 |
-
/* Handle the token as follows: */
|
2640 |
-
|
2641 |
-
/* A character token */
|
2642 |
-
if (
|
2643 |
-
$token['type'] === HTML5_Tokenizer::CHARACTER ||
|
2644 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER
|
2645 |
-
) {
|
2646 |
-
/* Append the token's character to the current node. */
|
2647 |
-
$this->insertText($token['data']);
|
2648 |
-
|
2649 |
-
/* A comment token */
|
2650 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2651 |
-
/* Append a Comment node to the current node with the data
|
2652 |
-
attribute set to the data given in the comment token. */
|
2653 |
-
$this->insertComment($token['data']);
|
2654 |
-
|
2655 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
2656 |
-
// parse error
|
2657 |
-
|
2658 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
2659 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2660 |
-
|
2661 |
-
/* A start tag token whose tag name is "option" */
|
2662 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2663 |
-
$token['name'] === 'option') {
|
2664 |
-
/* If the current node is an option element, act as if an end tag
|
2665 |
-
with the tag name "option" had been seen. */
|
2666 |
-
if (end($this->stack)->tagName === 'option') {
|
2667 |
-
$this->emitToken(array(
|
2668 |
-
'name' => 'option',
|
2669 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2670 |
-
));
|
2671 |
-
}
|
2672 |
-
|
2673 |
-
/* Insert an HTML element for the token. */
|
2674 |
-
$this->insertElement($token);
|
2675 |
-
|
2676 |
-
/* A start tag token whose tag name is "optgroup" */
|
2677 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2678 |
-
$token['name'] === 'optgroup') {
|
2679 |
-
/* If the current node is an option element, act as if an end tag
|
2680 |
-
with the tag name "option" had been seen. */
|
2681 |
-
if (end($this->stack)->tagName === 'option') {
|
2682 |
-
$this->emitToken(array(
|
2683 |
-
'name' => 'option',
|
2684 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2685 |
-
));
|
2686 |
-
}
|
2687 |
-
|
2688 |
-
/* If the current node is an optgroup element, act as if an end tag
|
2689 |
-
with the tag name "optgroup" had been seen. */
|
2690 |
-
if (end($this->stack)->tagName === 'optgroup') {
|
2691 |
-
$this->emitToken(array(
|
2692 |
-
'name' => 'optgroup',
|
2693 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2694 |
-
));
|
2695 |
-
}
|
2696 |
-
|
2697 |
-
/* Insert an HTML element for the token. */
|
2698 |
-
$this->insertElement($token);
|
2699 |
-
|
2700 |
-
/* An end tag token whose tag name is "optgroup" */
|
2701 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2702 |
-
$token['name'] === 'optgroup') {
|
2703 |
-
/* First, if the current node is an option element, and the node
|
2704 |
-
immediately before it in the stack of open elements is an optgroup
|
2705 |
-
element, then act as if an end tag with the tag name "option" had
|
2706 |
-
been seen. */
|
2707 |
-
$elements_in_stack = count($this->stack);
|
2708 |
-
|
2709 |
-
if ($this->stack[$elements_in_stack - 1]->tagName === 'option' &&
|
2710 |
-
$this->stack[$elements_in_stack - 2]->tagName === 'optgroup') {
|
2711 |
-
$this->emitToken(array(
|
2712 |
-
'name' => 'option',
|
2713 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2714 |
-
));
|
2715 |
-
}
|
2716 |
-
|
2717 |
-
/* If the current node is an optgroup element, then pop that node
|
2718 |
-
from the stack of open elements. Otherwise, this is a parse error,
|
2719 |
-
ignore the token. */
|
2720 |
-
if (end($this->stack)->tagName === 'optgroup') {
|
2721 |
-
array_pop($this->stack);
|
2722 |
-
} else {
|
2723 |
-
// parse error
|
2724 |
-
$this->ignored = true;
|
2725 |
-
}
|
2726 |
-
|
2727 |
-
/* An end tag token whose tag name is "option" */
|
2728 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2729 |
-
$token['name'] === 'option') {
|
2730 |
-
/* If the current node is an option element, then pop that node
|
2731 |
-
from the stack of open elements. Otherwise, this is a parse error,
|
2732 |
-
ignore the token. */
|
2733 |
-
if (end($this->stack)->tagName === 'option') {
|
2734 |
-
array_pop($this->stack);
|
2735 |
-
} else {
|
2736 |
-
// parse error
|
2737 |
-
$this->ignored = true;
|
2738 |
-
}
|
2739 |
-
|
2740 |
-
/* An end tag whose tag name is "select" */
|
2741 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2742 |
-
$token['name'] === 'select') {
|
2743 |
-
/* If the stack of open elements does not have an element in table
|
2744 |
-
scope with the same tag name as the token, this is a parse error.
|
2745 |
-
Ignore the token. (fragment case) */
|
2746 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2747 |
-
$this->ignored = true;
|
2748 |
-
// parse error
|
2749 |
-
|
2750 |
-
/* Otherwise: */
|
2751 |
-
} else {
|
2752 |
-
/* Pop elements from the stack of open elements until a select
|
2753 |
-
element has been popped from the stack. */
|
2754 |
-
do {
|
2755 |
-
$node = array_pop($this->stack);
|
2756 |
-
} while ($node->tagName !== 'select');
|
2757 |
-
|
2758 |
-
/* Reset the insertion mode appropriately. */
|
2759 |
-
$this->resetInsertionMode();
|
2760 |
-
}
|
2761 |
-
|
2762 |
-
/* A start tag whose tag name is "select" */
|
2763 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'select') {
|
2764 |
-
/* Parse error. Act as if the token had been an end tag with the
|
2765 |
-
tag name "select" instead. */
|
2766 |
-
$this->emitToken(array(
|
2767 |
-
'name' => 'select',
|
2768 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2769 |
-
));
|
2770 |
-
|
2771 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2772 |
-
($token['name'] === 'input' || $token['name'] === 'keygen' || $token['name'] === 'textarea')) {
|
2773 |
-
// parse error
|
2774 |
-
$this->emitToken(array(
|
2775 |
-
'name' => 'select',
|
2776 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2777 |
-
));
|
2778 |
-
$this->emitToken($token);
|
2779 |
-
|
2780 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') {
|
2781 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
2782 |
-
|
2783 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
2784 |
-
// XERROR: If the current node is not the root html element, then this is a parse error.
|
2785 |
-
/* Stop parsing */
|
2786 |
-
|
2787 |
-
/* Anything else */
|
2788 |
-
} else {
|
2789 |
-
/* Parse error. Ignore the token. */
|
2790 |
-
$this->ignored = true;
|
2791 |
-
}
|
2792 |
-
break;
|
2793 |
-
|
2794 |
-
case self::IN_SELECT_IN_TABLE:
|
2795 |
-
|
2796 |
-
if ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2797 |
-
in_array($token['name'], array('caption', 'table', 'tbody',
|
2798 |
-
'tfoot', 'thead', 'tr', 'td', 'th'))) {
|
2799 |
-
// parse error
|
2800 |
-
$this->emitToken(array(
|
2801 |
-
'name' => 'select',
|
2802 |
-
'type' => HTML5_Tokenizer::ENDTAG,
|
2803 |
-
));
|
2804 |
-
$this->emitToken($token);
|
2805 |
-
|
2806 |
-
/* An end tag whose tag name is one of: "caption", "table", "tbody",
|
2807 |
-
"tfoot", "thead", "tr", "td", "th" */
|
2808 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2809 |
-
in_array($token['name'], array('caption', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'td', 'th'))) {
|
2810 |
-
/* Parse error. */
|
2811 |
-
// parse error
|
2812 |
-
|
2813 |
-
/* If the stack of open elements has an element in table scope with
|
2814 |
-
the same tag name as that of the token, then act as if an end tag
|
2815 |
-
with the tag name "select" had been seen, and reprocess the token.
|
2816 |
-
Otherwise, ignore the token. */
|
2817 |
-
if ($this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2818 |
-
$this->emitToken(array(
|
2819 |
-
'name' => 'select',
|
2820 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2821 |
-
));
|
2822 |
-
|
2823 |
-
$this->emitToken($token);
|
2824 |
-
} else {
|
2825 |
-
$this->ignored = true;
|
2826 |
-
}
|
2827 |
-
} else {
|
2828 |
-
$this->processWithRulesFor($token, self::IN_SELECT);
|
2829 |
-
}
|
2830 |
-
break;
|
2831 |
-
|
2832 |
-
case self::IN_FOREIGN_CONTENT:
|
2833 |
-
if ($token['type'] === HTML5_Tokenizer::CHARACTER ||
|
2834 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2835 |
-
$this->insertText($token['data']);
|
2836 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2837 |
-
$this->insertComment($token['data']);
|
2838 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
2839 |
-
// XERROR: parse error
|
2840 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2841 |
-
$token['name'] === 'script' && end($this->stack)->tagName === 'script' &&
|
2842 |
-
// XDOM
|
2843 |
-
end($this->stack)->namespaceURI === self::NS_SVG) {
|
2844 |
-
array_pop($this->stack);
|
2845 |
-
// a bunch of script running mumbo jumbo
|
2846 |
-
} elseif (
|
2847 |
-
($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2848 |
-
((
|
2849 |
-
$token['name'] !== 'mglyph' &&
|
2850 |
-
$token['name'] !== 'malignmark' &&
|
2851 |
-
// XDOM
|
2852 |
-
end($this->stack)->namespaceURI === self::NS_MATHML &&
|
2853 |
-
in_array(end($this->stack)->tagName, array('mi', 'mo', 'mn', 'ms', 'mtext'))
|
2854 |
-
) ||
|
2855 |
-
(
|
2856 |
-
$token['name'] === 'svg' &&
|
2857 |
-
// XDOM
|
2858 |
-
end($this->stack)->namespaceURI === self::NS_MATHML &&
|
2859 |
-
end($this->stack)->tagName === 'annotation-xml'
|
2860 |
-
) ||
|
2861 |
-
(
|
2862 |
-
// XDOM
|
2863 |
-
end($this->stack)->namespaceURI === self::NS_SVG &&
|
2864 |
-
in_array(end($this->stack)->tagName, array('foreignObject', 'desc', 'title'))
|
2865 |
-
) ||
|
2866 |
-
(
|
2867 |
-
// XSKETCHY && XDOM
|
2868 |
-
end($this->stack)->namespaceURI === self::NS_HTML
|
2869 |
-
))
|
2870 |
-
) || $token['type'] === HTML5_Tokenizer::ENDTAG
|
2871 |
-
) {
|
2872 |
-
$this->processWithRulesFor($token, $this->secondary_mode);
|
2873 |
-
/* If, after doing so, the insertion mode is still "in foreign
|
2874 |
-
* content", but there is no element in scope that has a namespace
|
2875 |
-
* other than the HTML namespace, switch the insertion mode to the
|
2876 |
-
* secondary insertion mode. */
|
2877 |
-
if ($this->mode === self::IN_FOREIGN_CONTENT) {
|
2878 |
-
$found = false;
|
2879 |
-
// this basically duplicates elementInScope()
|
2880 |
-
for ($i = count($this->stack) - 1; $i >= 0; $i--) {
|
2881 |
-
// XDOM
|
2882 |
-
$node = $this->stack[$i];
|
2883 |
-
if ($node->namespaceURI !== self::NS_HTML) {
|
2884 |
-
$found = true;
|
2885 |
-
break;
|
2886 |
-
} elseif (in_array($node->tagName, array('table', 'html',
|
2887 |
-
'applet', 'caption', 'td', 'th', 'button', 'marquee',
|
2888 |
-
'object')) || ($node->tagName === 'foreignObject' &&
|
2889 |
-
$node->namespaceURI === self::NS_SVG)) {
|
2890 |
-
break;
|
2891 |
-
}
|
2892 |
-
}
|
2893 |
-
if (!$found) {
|
2894 |
-
$this->mode = $this->secondary_mode;
|
2895 |
-
}
|
2896 |
-
}
|
2897 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF || (
|
2898 |
-
$token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2899 |
-
(in_array($token['name'], array('b', "big", "blockquote", "body", "br",
|
2900 |
-
"center", "code", "dc", "dd", "div", "dl", "ds", "dt", "em", "embed", "h1", "h2",
|
2901 |
-
"h3", "h4", "h5", "h6", "head", "hr", "i", "img", "li", "listing",
|
2902 |
-
"menu", "meta", "nobr", "ol", "p", "pre", "ruby", "s", "small",
|
2903 |
-
"span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul",
|
2904 |
-
"var")) || ($token['name'] === 'font' && ($this->getAttr($token, 'color') ||
|
2905 |
-
$this->getAttr($token, 'face') || $this->getAttr($token, 'size')))))) {
|
2906 |
-
// XERROR: parse error
|
2907 |
-
do {
|
2908 |
-
$node = array_pop($this->stack);
|
2909 |
-
// XDOM
|
2910 |
-
} while ($node->namespaceURI !== self::NS_HTML);
|
2911 |
-
$this->stack[] = $node;
|
2912 |
-
$this->mode = $this->secondary_mode;
|
2913 |
-
$this->emitToken($token);
|
2914 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG) {
|
2915 |
-
static $svg_lookup = array(
|
2916 |
-
'altglyph' => 'altGlyph',
|
2917 |
-
'altglyphdef' => 'altGlyphDef',
|
2918 |
-
'altglyphitem' => 'altGlyphItem',
|
2919 |
-
'animatecolor' => 'animateColor',
|
2920 |
-
'animatemotion' => 'animateMotion',
|
2921 |
-
'animatetransform' => 'animateTransform',
|
2922 |
-
'clippath' => 'clipPath',
|
2923 |
-
'feblend' => 'feBlend',
|
2924 |
-
'fecolormatrix' => 'feColorMatrix',
|
2925 |
-
'fecomponenttransfer' => 'feComponentTransfer',
|
2926 |
-
'fecomposite' => 'feComposite',
|
2927 |
-
'feconvolvematrix' => 'feConvolveMatrix',
|
2928 |
-
'fediffuselighting' => 'feDiffuseLighting',
|
2929 |
-
'fedisplacementmap' => 'feDisplacementMap',
|
2930 |
-
'fedistantlight' => 'feDistantLight',
|
2931 |
-
'feflood' => 'feFlood',
|
2932 |
-
'fefunca' => 'feFuncA',
|
2933 |
-
'fefuncb' => 'feFuncB',
|
2934 |
-
'fefuncg' => 'feFuncG',
|
2935 |
-
'fefuncr' => 'feFuncR',
|
2936 |
-
'fegaussianblur' => 'feGaussianBlur',
|
2937 |
-
'feimage' => 'feImage',
|
2938 |
-
'femerge' => 'feMerge',
|
2939 |
-
'femergenode' => 'feMergeNode',
|
2940 |
-
'femorphology' => 'feMorphology',
|
2941 |
-
'feoffset' => 'feOffset',
|
2942 |
-
'fepointlight' => 'fePointLight',
|
2943 |
-
'fespecularlighting' => 'feSpecularLighting',
|
2944 |
-
'fespotlight' => 'feSpotLight',
|
2945 |
-
'fetile' => 'feTile',
|
2946 |
-
'feturbulence' => 'feTurbulence',
|
2947 |
-
'foreignobject' => 'foreignObject',
|
2948 |
-
'glyphref' => 'glyphRef',
|
2949 |
-
'lineargradient' => 'linearGradient',
|
2950 |
-
'radialgradient' => 'radialGradient',
|
2951 |
-
'textpath' => 'textPath',
|
2952 |
-
);
|
2953 |
-
// XDOM
|
2954 |
-
$current = end($this->stack);
|
2955 |
-
if ($current->namespaceURI === self::NS_MATHML) {
|
2956 |
-
$token = $this->adjustMathMLAttributes($token);
|
2957 |
-
}
|
2958 |
-
if ($current->namespaceURI === self::NS_SVG &&
|
2959 |
-
isset($svg_lookup[$token['name']])) {
|
2960 |
-
$token['name'] = $svg_lookup[$token['name']];
|
2961 |
-
}
|
2962 |
-
if ($current->namespaceURI === self::NS_SVG) {
|
2963 |
-
$token = $this->adjustSVGAttributes($token);
|
2964 |
-
}
|
2965 |
-
$token = $this->adjustForeignAttributes($token);
|
2966 |
-
$this->insertForeignElement($token, $current->namespaceURI);
|
2967 |
-
if (isset($token['self-closing'])) {
|
2968 |
-
array_pop($this->stack);
|
2969 |
-
// XERROR: acknowledge self-closing flag
|
2970 |
-
}
|
2971 |
-
}
|
2972 |
-
break;
|
2973 |
-
|
2974 |
-
case self::AFTER_BODY:
|
2975 |
-
/* Handle the token as follows: */
|
2976 |
-
|
2977 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
2978 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
2979 |
-
or U+0020 SPACE */
|
2980 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2981 |
-
/* Process the token as it would be processed if the insertion mode
|
2982 |
-
was "in body". */
|
2983 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2984 |
-
|
2985 |
-
/* A comment token */
|
2986 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2987 |
-
/* Append a Comment node to the first element in the stack of open
|
2988 |
-
elements (the html element), with the data attribute set to the
|
2989 |
-
data given in the comment token. */
|
2990 |
-
// XDOM
|
2991 |
-
$comment = $this->dom->createComment($token['data']);
|
2992 |
-
$this->stack[0]->appendChild($comment);
|
2993 |
-
|
2994 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
2995 |
-
// parse error
|
2996 |
-
|
2997 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
2998 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2999 |
-
|
3000 |
-
/* An end tag with the tag name "html" */
|
3001 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'html') {
|
3002 |
-
/* If the parser was originally created as part of the HTML
|
3003 |
-
* fragment parsing algorithm, this is a parse error; ignore
|
3004 |
-
* the token. (fragment case) */
|
3005 |
-
$this->ignored = true;
|
3006 |
-
// XERROR: implement this
|
3007 |
-
|
3008 |
-
$this->mode = self::AFTER_AFTER_BODY;
|
3009 |
-
|
3010 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3011 |
-
/* Stop parsing */
|
3012 |
-
|
3013 |
-
/* Anything else */
|
3014 |
-
} else {
|
3015 |
-
/* Parse error. Set the insertion mode to "in body" and reprocess
|
3016 |
-
the token. */
|
3017 |
-
$this->mode = self::IN_BODY;
|
3018 |
-
$this->emitToken($token);
|
3019 |
-
}
|
3020 |
-
break;
|
3021 |
-
|
3022 |
-
case self::IN_FRAMESET:
|
3023 |
-
/* Handle the token as follows: */
|
3024 |
-
|
3025 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
3026 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
3027 |
-
U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
|
3028 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
3029 |
-
/* Append the character to the current node. */
|
3030 |
-
$this->insertText($token['data']);
|
3031 |
-
|
3032 |
-
/* A comment token */
|
3033 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
3034 |
-
/* Append a Comment node to the current node with the data
|
3035 |
-
attribute set to the data given in the comment token. */
|
3036 |
-
$this->insertComment($token['data']);
|
3037 |
-
|
3038 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
3039 |
-
// parse error
|
3040 |
-
|
3041 |
-
/* A start tag with the tag name "frameset" */
|
3042 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
3043 |
-
$token['name'] === 'frameset') {
|
3044 |
-
$this->insertElement($token);
|
3045 |
-
|
3046 |
-
/* An end tag with the tag name "frameset" */
|
3047 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
3048 |
-
$token['name'] === 'frameset') {
|
3049 |
-
/* If the current node is the root html element, then this is a
|
3050 |
-
parse error; ignore the token. (fragment case) */
|
3051 |
-
if (end($this->stack)->tagName === 'html') {
|
3052 |
-
$this->ignored = true;
|
3053 |
-
// Parse error
|
3054 |
-
|
3055 |
-
} else {
|
3056 |
-
/* Otherwise, pop the current node from the stack of open
|
3057 |
-
elements. */
|
3058 |
-
array_pop($this->stack);
|
3059 |
-
|
3060 |
-
/* If the parser was not originally created as part of the HTML
|
3061 |
-
* fragment parsing algorithm (fragment case), and the current
|
3062 |
-
* node is no longer a frameset element, then switch the
|
3063 |
-
* insertion mode to "after frameset". */
|
3064 |
-
$this->mode = self::AFTER_FRAMESET;
|
3065 |
-
}
|
3066 |
-
|
3067 |
-
/* A start tag with the tag name "frame" */
|
3068 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
3069 |
-
$token['name'] === 'frame') {
|
3070 |
-
/* Insert an HTML element for the token. */
|
3071 |
-
$this->insertElement($token);
|
3072 |
-
|
3073 |
-
/* Immediately pop the current node off the stack of open elements. */
|
3074 |
-
array_pop($this->stack);
|
3075 |
-
|
3076 |
-
// XERROR: Acknowledge the token's self-closing flag, if it is set.
|
3077 |
-
|
3078 |
-
/* A start tag with the tag name "noframes" */
|
3079 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
3080 |
-
$token['name'] === 'noframes') {
|
3081 |
-
/* Process the token using the rules for the "in head" insertion mode. */
|
3082 |
-
$this->processwithRulesFor($token, self::IN_HEAD);
|
3083 |
-
|
3084 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3085 |
-
// XERROR: If the current node is not the root html element, then this is a parse error.
|
3086 |
-
/* Stop parsing */
|
3087 |
-
/* Anything else */
|
3088 |
-
} else {
|
3089 |
-
/* Parse error. Ignore the token. */
|
3090 |
-
$this->ignored = true;
|
3091 |
-
}
|
3092 |
-
break;
|
3093 |
-
|
3094 |
-
case self::AFTER_FRAMESET:
|
3095 |
-
/* Handle the token as follows: */
|
3096 |
-
|
3097 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
3098 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
3099 |
-
U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
|
3100 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
3101 |
-
/* Append the character to the current node. */
|
3102 |
-
$this->insertText($token['data']);
|
3103 |
-
|
3104 |
-
/* A comment token */
|
3105 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
3106 |
-
/* Append a Comment node to the current node with the data
|
3107 |
-
attribute set to the data given in the comment token. */
|
3108 |
-
$this->insertComment($token['data']);
|
3109 |
-
|
3110 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
3111 |
-
// parse error
|
3112 |
-
|
3113 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
3114 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
3115 |
-
|
3116 |
-
/* An end tag with the tag name "html" */
|
3117 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
3118 |
-
$token['name'] === 'html') {
|
3119 |
-
$this->mode = self::AFTER_AFTER_FRAMESET;
|
3120 |
-
|
3121 |
-
/* A start tag with the tag name "noframes" */
|
3122 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
3123 |
-
$token['name'] === 'noframes') {
|
3124 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
3125 |
-
|
3126 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3127 |
-
/* Stop parsing */
|
3128 |
-
|
3129 |
-
/* Anything else */
|
3130 |
-
} else {
|
3131 |
-
/* Parse error. Ignore the token. */
|
3132 |
-
$this->ignored = true;
|
3133 |
-
}
|
3134 |
-
break;
|
3135 |
-
|
3136 |
-
case self::AFTER_AFTER_BODY:
|
3137 |
-
/* A comment token */
|
3138 |
-
if ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
3139 |
-
/* Append a Comment node to the Document object with the data
|
3140 |
-
attribute set to the data given in the comment token. */
|
3141 |
-
// XDOM
|
3142 |
-
$comment = $this->dom->createComment($token['data']);
|
3143 |
-
$this->dom->appendChild($comment);
|
3144 |
-
|
3145 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE ||
|
3146 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER ||
|
3147 |
-
($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) {
|
3148 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
3149 |
-
|
3150 |
-
/* An end-of-file token */
|
3151 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3152 |
-
/* OMG DONE!! */
|
3153 |
-
} else {
|
3154 |
-
// parse error
|
3155 |
-
$this->mode = self::IN_BODY;
|
3156 |
-
$this->emitToken($token);
|
3157 |
-
}
|
3158 |
-
break;
|
3159 |
-
|
3160 |
-
case self::AFTER_AFTER_FRAMESET:
|
3161 |
-
/* A comment token */
|
3162 |
-
if ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
3163 |
-
/* Append a Comment node to the Document object with the data
|
3164 |
-
attribute set to the data given in the comment token. */
|
3165 |
-
// XDOM
|
3166 |
-
$comment = $this->dom->createComment($token['data']);
|
3167 |
-
$this->dom->appendChild($comment);
|
3168 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE ||
|
3169 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER ||
|
3170 |
-
($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) {
|
3171 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
3172 |
-
|
3173 |
-
/* An end-of-file token */
|
3174 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3175 |
-
/* OMG DONE!! */
|
3176 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'nofrmaes') {
|
3177 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
3178 |
-
} else {
|
3179 |
-
// parse error
|
3180 |
-
}
|
3181 |
-
break;
|
3182 |
-
}
|
3183 |
-
}
|
3184 |
-
|
3185 |
-
private function insertElement($token, $append = true) {
|
3186 |
-
$el = $this->dom->createElementNS(self::NS_HTML, $token['name']);
|
3187 |
-
|
3188 |
-
if (!empty($token['attr'])) {
|
3189 |
-
foreach ($token['attr'] as $attr) {
|
3190 |
-
if (!$el->hasAttribute($attr['name']) && preg_match("/^[a-zA-Z_:]/", $attr['name'])) {
|
3191 |
-
$el->setAttribute($attr['name'], $attr['value']);
|
3192 |
-
}
|
3193 |
-
}
|
3194 |
-
}
|
3195 |
-
if ($append) {
|
3196 |
-
$this->appendToRealParent($el);
|
3197 |
-
$this->stack[] = $el;
|
3198 |
-
}
|
3199 |
-
|
3200 |
-
return $el;
|
3201 |
-
}
|
3202 |
-
|
3203 |
-
/**
|
3204 |
-
* @param $data
|
3205 |
-
*/
|
3206 |
-
private function insertText($data) {
|
3207 |
-
if ($data === '') return;
|
3208 |
-
if ($this->ignore_lf_token) {
|
3209 |
-
if ($data[0] === "\n") {
|
3210 |
-
$data = substr($data, 1);
|
3211 |
-
if ($data === false) return;
|
3212 |
-
}
|
3213 |
-
}
|
3214 |
-
$text = $this->dom->createTextNode($data);
|
3215 |
-
$this->appendToRealParent($text);
|
3216 |
-
}
|
3217 |
-
|
3218 |
-
/**
|
3219 |
-
* @param $data
|
3220 |
-
*/
|
3221 |
-
private function insertComment($data) {
|
3222 |
-
$comment = $this->dom->createComment($data);
|
3223 |
-
$this->appendToRealParent($comment);
|
3224 |
-
}
|
3225 |
-
|
3226 |
-
/**
|
3227 |
-
* @param $node
|
3228 |
-
*/
|
3229 |
-
private function appendToRealParent($node) {
|
3230 |
-
// this is only for the foster_parent case
|
3231 |
-
/* If the current node is a table, tbody, tfoot, thead, or tr
|
3232 |
-
element, then, whenever a node would be inserted into the current
|
3233 |
-
node, it must instead be inserted into the foster parent element. */
|
3234 |
-
if (!$this->foster_parent || !in_array(end($this->stack)->tagName,
|
3235 |
-
array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
|
3236 |
-
end($this->stack)->appendChild($node);
|
3237 |
-
} else {
|
3238 |
-
$this->fosterParent($node);
|
3239 |
-
}
|
3240 |
-
}
|
3241 |
-
|
3242 |
-
/**
|
3243 |
-
* @param $el
|
3244 |
-
* @param int $scope
|
3245 |
-
* @return bool|null
|
3246 |
-
*/
|
3247 |
-
private function elementInScope($el, $scope = self::SCOPE) {
|
3248 |
-
if (is_array($el)) {
|
3249 |
-
foreach($el as $element) {
|
3250 |
-
if ($this->elementInScope($element, $scope)) {
|
3251 |
-
return true;
|
3252 |
-
}
|
3253 |
-
}
|
3254 |
-
|
3255 |
-
return false;
|
3256 |
-
}
|
3257 |
-
|
3258 |
-
$leng = count($this->stack);
|
3259 |
-
|
3260 |
-
for ($n = 0; $n < $leng; $n++) {
|
3261 |
-
/* 1. Initialise node to be the current node (the bottommost node of
|
3262 |
-
the stack). */
|
3263 |
-
$node = $this->stack[$leng - 1 - $n];
|
3264 |
-
|
3265 |
-
if ($node->tagName === $el) {
|
3266 |
-
/* 2. If node is the target node, terminate in a match state. */
|
3267 |
-
return true;
|
3268 |
-
|
3269 |
-
// We've expanded the logic for these states a little differently;
|
3270 |
-
// Hixie's refactoring into "specific scope" is more general, but
|
3271 |
-
// this "gets the job done"
|
3272 |
-
|
3273 |
-
// these are the common states for all scopes
|
3274 |
-
} elseif ($node->tagName === 'table' || $node->tagName === 'html') {
|
3275 |
-
return false;
|
3276 |
-
|
3277 |
-
// these are valid for "in scope" and "in list item scope"
|
3278 |
-
} elseif ($scope !== self::SCOPE_TABLE &&
|
3279 |
-
(in_array($node->tagName, array('applet', 'caption', 'td',
|
3280 |
-
'th', 'button', 'marquee', 'object')) ||
|
3281 |
-
$node->tagName === 'foreignObject' && $node->namespaceURI === self::NS_SVG)) {
|
3282 |
-
return false;
|
3283 |
-
|
3284 |
-
|
3285 |
-
// these are valid for "in list item scope"
|
3286 |
-
} elseif ($scope === self::SCOPE_LISTITEM && in_array($node->tagName, array('ol', 'ul'))) {
|
3287 |
-
return false;
|
3288 |
-
}
|
3289 |
-
|
3290 |
-
/* Otherwise, set node to the previous entry in the stack of open
|
3291 |
-
elements and return to step 2. (This will never fail, since the loop
|
3292 |
-
will always terminate in the previous step if the top of the stack
|
3293 |
-
is reached.) */
|
3294 |
-
}
|
3295 |
-
|
3296 |
-
// To fix warning. This never happens or should return true/false
|
3297 |
-
return null;
|
3298 |
-
}
|
3299 |
-
|
3300 |
-
/**
|
3301 |
-
* @return bool
|
3302 |
-
*/
|
3303 |
-
private function reconstructActiveFormattingElements() {
|
3304 |
-
/* 1. If there are no entries in the list of active formatting elements,
|
3305 |
-
then there is nothing to reconstruct; stop this algorithm. */
|
3306 |
-
$formatting_elements = count($this->a_formatting);
|
3307 |
-
|
3308 |
-
if ($formatting_elements === 0) {
|
3309 |
-
return false;
|
3310 |
-
}
|
3311 |
-
|
3312 |
-
/* 3. Let entry be the last (most recently added) element in the list
|
3313 |
-
of active formatting elements. */
|
3314 |
-
$entry = end($this->a_formatting);
|
3315 |
-
|
3316 |
-
/* 2. If the last (most recently added) entry in the list of active
|
3317 |
-
formatting elements is a marker, or if it is an element that is in the
|
3318 |
-
stack of open elements, then there is nothing to reconstruct; stop this
|
3319 |
-
algorithm. */
|
3320 |
-
if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
|
3321 |
-
return false;
|
3322 |
-
}
|
3323 |
-
|
3324 |
-
for ($a = $formatting_elements - 1; $a >= 0; true) {
|
3325 |
-
/* 4. If there are no entries before entry in the list of active
|
3326 |
-
formatting elements, then jump to step 8. */
|
3327 |
-
if ($a === 0) {
|
3328 |
-
$step_seven = false;
|
3329 |
-
break;
|
3330 |
-
}
|
3331 |
-
|
3332 |
-
/* 5. Let entry be the entry one earlier than entry in the list of
|
3333 |
-
active formatting elements. */
|
3334 |
-
$a--;
|
3335 |
-
$entry = $this->a_formatting[$a];
|
3336 |
-
|
3337 |
-
/* 6. If entry is neither a marker nor an element that is also in
|
3338 |
-
thetack of open elements, go to step 4. */
|
3339 |
-
if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
|
3340 |
-
break;
|
3341 |
-
}
|
3342 |
-
}
|
3343 |
-
|
3344 |
-
while (true) {
|
3345 |
-
/* 7. Let entry be the element one later than entry in the list of
|
3346 |
-
active formatting elements. */
|
3347 |
-
if (isset($step_seven) && $step_seven === true) {
|
3348 |
-
$a++;
|
3349 |
-
$entry = $this->a_formatting[$a];
|
3350 |
-
}
|
3351 |
-
|
3352 |
-
/* 8. Perform a shallow clone of the element entry to obtain clone. */
|
3353 |
-
$clone = $entry->cloneNode();
|
3354 |
-
|
3355 |
-
/* 9. Append clone to the current node and push it onto the stack
|
3356 |
-
of open elements so that it is the new current node. */
|
3357 |
-
$this->appendToRealParent($clone);
|
3358 |
-
$this->stack[] = $clone;
|
3359 |
-
|
3360 |
-
/* 10. Replace the entry for entry in the list with an entry for
|
3361 |
-
clone. */
|
3362 |
-
$this->a_formatting[$a] = $clone;
|
3363 |
-
|
3364 |
-
/* 11. If the entry for clone in the list of active formatting
|
3365 |
-
elements is not the last entry in the list, return to step 7. */
|
3366 |
-
if (end($this->a_formatting) !== $clone) {
|
3367 |
-
$step_seven = true;
|
3368 |
-
} else {
|
3369 |
-
break;
|
3370 |
-
}
|
3371 |
-
}
|
3372 |
-
|
3373 |
-
// Return value not in use ATM. Would just make sense to also return true here.
|
3374 |
-
return true;
|
3375 |
-
}
|
3376 |
-
|
3377 |
-
/**
|
3378 |
-
*
|
3379 |
-
*/
|
3380 |
-
private function clearTheActiveFormattingElementsUpToTheLastMarker() {
|
3381 |
-
/* When the steps below require the UA to clear the list of active
|
3382 |
-
formatting elements up to the last marker, the UA must perform the
|
3383 |
-
following steps: */
|
3384 |
-
|
3385 |
-
while (true) {
|
3386 |
-
/* 1. Let entry be the last (most recently added) entry in the list
|
3387 |
-
of active formatting elements. */
|
3388 |
-
$entry = end($this->a_formatting);
|
3389 |
-
|
3390 |
-
/* 2. Remove entry from the list of active formatting elements. */
|
3391 |
-
array_pop($this->a_formatting);
|
3392 |
-
|
3393 |
-
/* 3. If entry was a marker, then stop the algorithm at this point.
|
3394 |
-
The list has been cleared up to the last marker. */
|
3395 |
-
if ($entry === self::MARKER) {
|
3396 |
-
break;
|
3397 |
-
}
|
3398 |
-
}
|
3399 |
-
}
|
3400 |
-
|
3401 |
-
/**
|
3402 |
-
* @param array $exclude
|
3403 |
-
*/
|
3404 |
-
private function generateImpliedEndTags($exclude = array()) {
|
3405 |
-
/* When the steps below require the UA to generate implied end tags,
|
3406 |
-
* then, while the current node is a dc element, a dd element, a ds
|
3407 |
-
* element, a dt element, an li element, an option element, an optgroup
|
3408 |
-
* element, a p element, an rp element, or an rt element, the UA must
|
3409 |
-
* pop the current node off the stack of open elements. */
|
3410 |
-
$node = end($this->stack);
|
3411 |
-
$elements = array_diff(array('dc', 'dd', 'ds', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude);
|
3412 |
-
|
3413 |
-
while (in_array(end($this->stack)->tagName, $elements)) {
|
3414 |
-
array_pop($this->stack);
|
3415 |
-
}
|
3416 |
-
}
|
3417 |
-
|
3418 |
-
/**
|
3419 |
-
* @param $node
|
3420 |
-
* @return int
|
3421 |
-
*/
|
3422 |
-
private function getElementCategory($node) {
|
3423 |
-
if (!is_object($node)) {
|
3424 |
-
debug_print_backtrace();
|
3425 |
-
}
|
3426 |
-
$name = $node->tagName;
|
3427 |
-
if (in_array($name, $this->special)) {
|
3428 |
-
return self::SPECIAL;
|
3429 |
-
} elseif (in_array($name, $this->scoping)) {
|
3430 |
-
return self::SCOPING;
|
3431 |
-
} elseif (in_array($name, $this->formatting)) {
|
3432 |
-
return self::FORMATTING;
|
3433 |
-
} else {
|
3434 |
-
return self::PHRASING;
|
3435 |
-
}
|
3436 |
-
}
|
3437 |
-
|
3438 |
-
/**
|
3439 |
-
* @param $elements
|
3440 |
-
*/
|
3441 |
-
private function clearStackToTableContext($elements) {
|
3442 |
-
/* When the steps above require the UA to clear the stack back to a
|
3443 |
-
table context, it means that the UA must, while the current node is not
|
3444 |
-
a table element or an html element, pop elements from the stack of open
|
3445 |
-
elements. */
|
3446 |
-
while (true) {
|
3447 |
-
$name = end($this->stack)->tagName;
|
3448 |
-
|
3449 |
-
if (in_array($name, $elements)) {
|
3450 |
-
break;
|
3451 |
-
} else {
|
3452 |
-
array_pop($this->stack);
|
3453 |
-
}
|
3454 |
-
}
|
3455 |
-
}
|
3456 |
-
|
3457 |
-
/**
|
3458 |
-
* @param null $context
|
3459 |
-
*/
|
3460 |
-
private function resetInsertionMode($context = null) {
|
3461 |
-
/* 1. Let last be false. */
|
3462 |
-
$last = false;
|
3463 |
-
$leng = count($this->stack);
|
3464 |
-
|
3465 |
-
for ($n = $leng - 1; $n >= 0; $n--) {
|
3466 |
-
/* 2. Let node be the last node in the stack of open elements. */
|
3467 |
-
$node = $this->stack[$n];
|
3468 |
-
|
3469 |
-
/* 3. If node is the first node in the stack of open elements, then
|
3470 |
-
* set last to true and set node to the context element. (fragment
|
3471 |
-
* case) */
|
3472 |
-
if ($this->stack[0]->isSameNode($node)) {
|
3473 |
-
$last = true;
|
3474 |
-
$node = $context;
|
3475 |
-
}
|
3476 |
-
|
3477 |
-
/* 4. If node is a select element, then switch the insertion mode to
|
3478 |
-
"in select" and abort these steps. (fragment case) */
|
3479 |
-
if ($node->tagName === 'select') {
|
3480 |
-
$this->mode = self::IN_SELECT;
|
3481 |
-
break;
|
3482 |
-
|
3483 |
-
/* 5. If node is a td or th element, then switch the insertion mode
|
3484 |
-
to "in cell" and abort these steps. */
|
3485 |
-
} elseif ($node->tagName === 'td' || $node->nodeName === 'th') {
|
3486 |
-
$this->mode = self::IN_CELL;
|
3487 |
-
break;
|
3488 |
-
|
3489 |
-
/* 6. If node is a tr element, then switch the insertion mode to
|
3490 |
-
"in row" and abort these steps. */
|
3491 |
-
} elseif ($node->tagName === 'tr') {
|
3492 |
-
$this->mode = self::IN_ROW;
|
3493 |
-
break;
|
3494 |
-
|
3495 |
-
/* 7. If node is a tbody, thead, or tfoot element, then switch the
|
3496 |
-
insertion mode to "in table body" and abort these steps. */
|
3497 |
-
} elseif (in_array($node->tagName, array('tbody', 'thead', 'tfoot'))) {
|
3498 |
-
$this->mode = self::IN_TABLE_BODY;
|
3499 |
-
break;
|
3500 |
-
|
3501 |
-
/* 8. If node is a caption element, then switch the insertion mode
|
3502 |
-
to "in caption" and abort these steps. */
|
3503 |
-
} elseif ($node->tagName === 'caption') {
|
3504 |
-
$this->mode = self::IN_CAPTION;
|
3505 |
-
break;
|
3506 |
-
|
3507 |
-
/* 9. If node is a colgroup element, then switch the insertion mode
|
3508 |
-
to "in column group" and abort these steps. (innerHTML case) */
|
3509 |
-
} elseif ($node->tagName === 'colgroup') {
|
3510 |
-
$this->mode = self::IN_COLUMN_GROUP;
|
3511 |
-
break;
|
3512 |
-
|
3513 |
-
/* 10. If node is a table element, then switch the insertion mode
|
3514 |
-
to "in table" and abort these steps. */
|
3515 |
-
} elseif ($node->tagName === 'table') {
|
3516 |
-
$this->mode = self::IN_TABLE;
|
3517 |
-
break;
|
3518 |
-
|
3519 |
-
/* 11. If node is an element from the MathML namespace or the SVG
|
3520 |
-
* namespace, then switch the insertion mode to "in foreign
|
3521 |
-
* content", let the secondary insertion mode be "in body", and
|
3522 |
-
* abort these steps. */
|
3523 |
-
} elseif ($node->namespaceURI === self::NS_SVG ||
|
3524 |
-
$node->namespaceURI === self::NS_MATHML) {
|
3525 |
-
$this->mode = self::IN_FOREIGN_CONTENT;
|
3526 |
-
$this->secondary_mode = self::IN_BODY;
|
3527 |
-
break;
|
3528 |
-
|
3529 |
-
/* 12. If node is a head element, then switch the insertion mode
|
3530 |
-
to "in body" ("in body"! not "in head"!) and abort these steps.
|
3531 |
-
(fragment case) */
|
3532 |
-
} elseif ($node->tagName === 'head') {
|
3533 |
-
$this->mode = self::IN_BODY;
|
3534 |
-
break;
|
3535 |
-
|
3536 |
-
/* 13. If node is a body element, then switch the insertion mode to
|
3537 |
-
"in body" and abort these steps. */
|
3538 |
-
} elseif ($node->tagName === 'body') {
|
3539 |
-
$this->mode = self::IN_BODY;
|
3540 |
-
break;
|
3541 |
-
|
3542 |
-
/* 14. If node is a frameset element, then switch the insertion
|
3543 |
-
mode to "in frameset" and abort these steps. (fragment case) */
|
3544 |
-
} elseif ($node->tagName === 'frameset') {
|
3545 |
-
$this->mode = self::IN_FRAMESET;
|
3546 |
-
break;
|
3547 |
-
|
3548 |
-
/* 15. If node is an html element, then: if the head element
|
3549 |
-
pointer is null, switch the insertion mode to "before head",
|
3550 |
-
otherwise, switch the insertion mode to "after head". In either
|
3551 |
-
case, abort these steps. (fragment case) */
|
3552 |
-
} elseif ($node->tagName === 'html') {
|
3553 |
-
$this->mode = ($this->head_pointer === null)
|
3554 |
-
? self::BEFORE_HEAD
|
3555 |
-
: self::AFTER_HEAD;
|
3556 |
-
|
3557 |
-
break;
|
3558 |
-
|
3559 |
-
/* 16. If last is true, then set the insertion mode to "in body"
|
3560 |
-
and abort these steps. (fragment case) */
|
3561 |
-
} elseif ($last) {
|
3562 |
-
$this->mode = self::IN_BODY;
|
3563 |
-
break;
|
3564 |
-
}
|
3565 |
-
}
|
3566 |
-
}
|
3567 |
-
|
3568 |
-
/**
|
3569 |
-
*
|
3570 |
-
*/
|
3571 |
-
private function closeCell() {
|
3572 |
-
/* If the stack of open elements has a td or th element in table scope,
|
3573 |
-
then act as if an end tag token with that tag name had been seen. */
|
3574 |
-
foreach (array('td', 'th') as $cell) {
|
3575 |
-
if ($this->elementInScope($cell, self::SCOPE_TABLE)) {
|
3576 |
-
$this->emitToken(array(
|
3577 |
-
'name' => $cell,
|
3578 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
3579 |
-
));
|
3580 |
-
|
3581 |
-
break;
|
3582 |
-
}
|
3583 |
-
}
|
3584 |
-
}
|
3585 |
-
|
3586 |
-
/**
|
3587 |
-
* @param $token
|
3588 |
-
* @param $mode
|
3589 |
-
*/
|
3590 |
-
private function processWithRulesFor($token, $mode) {
|
3591 |
-
/* "using the rules for the m insertion mode", where m is one of these
|
3592 |
-
* modes, the user agent must use the rules described under the m
|
3593 |
-
* insertion mode's section, but must leave the insertion mode
|
3594 |
-
* unchanged unless the rules in m themselves switch the insertion mode
|
3595 |
-
* to a new value. */
|
3596 |
-
$this->emitToken($token, $mode);
|
3597 |
-
}
|
3598 |
-
|
3599 |
-
/**
|
3600 |
-
* @param $token
|
3601 |
-
*/
|
3602 |
-
private function insertCDATAElement($token) {
|
3603 |
-
$this->insertElement($token);
|
3604 |
-
$this->original_mode = $this->mode;
|
3605 |
-
$this->mode = self::IN_CDATA_RCDATA;
|
3606 |
-
$this->content_model = HTML5_Tokenizer::CDATA;
|
3607 |
-
}
|
3608 |
-
|
3609 |
-
/**
|
3610 |
-
* @param $token
|
3611 |
-
*/
|
3612 |
-
private function insertRCDATAElement($token) {
|
3613 |
-
$this->insertElement($token);
|
3614 |
-
$this->original_mode = $this->mode;
|
3615 |
-
$this->mode = self::IN_CDATA_RCDATA;
|
3616 |
-
$this->content_model = HTML5_Tokenizer::RCDATA;
|
3617 |
-
}
|
3618 |
-
|
3619 |
-
/**
|
3620 |
-
* @param $token
|
3621 |
-
* @param $key
|
3622 |
-
* @return bool
|
3623 |
-
*/
|
3624 |
-
private function getAttr($token, $key) {
|
3625 |
-
if (!isset($token['attr'])) return false;
|
3626 |
-
$ret = false;
|
3627 |
-
foreach ($token['attr'] as $keypair) {
|
3628 |
-
if ($keypair['name'] === $key) $ret = $keypair['value'];
|
3629 |
-
}
|
3630 |
-
return $ret;
|
3631 |
-
}
|
3632 |
-
|
3633 |
-
/**
|
3634 |
-
* @return mixed
|
3635 |
-
*/
|
3636 |
-
private function getCurrentTable() {
|
3637 |
-
/* The current table is the last table element in the stack of open
|
3638 |
-
* elements, if there is one. If there is no table element in the stack
|
3639 |
-
* of open elements (fragment case), then the current table is the
|
3640 |
-
* first element in the stack of open elements (the html element). */
|
3641 |
-
for ($i = count($this->stack) - 1; $i >= 0; $i--) {
|
3642 |
-
if ($this->stack[$i]->tagName === 'table') {
|
3643 |
-
return $this->stack[$i];
|
3644 |
-
}
|
3645 |
-
}
|
3646 |
-
return $this->stack[0];
|
3647 |
-
}
|
3648 |
-
|
3649 |
-
/**
|
3650 |
-
* @return mixed
|
3651 |
-
*/
|
3652 |
-
private function getFosterParent() {
|
3653 |
-
/* The foster parent element is the parent element of the last
|
3654 |
-
table element in the stack of open elements, if there is a
|
3655 |
-
table element and it has such a parent element. If there is no
|
3656 |
-
table element in the stack of open elements (innerHTML case),
|
3657 |
-
then the foster parent element is the first element in the
|
3658 |
-
stack of open elements (the html element). Otherwise, if there
|
3659 |
-
is a table element in the stack of open elements, but the last
|
3660 |
-
table element in the stack of open elements has no parent, or
|
3661 |
-
its parent node is not an element, then the foster parent
|
3662 |
-
element is the element before the last table element in the
|
3663 |
-
stack of open elements. */
|
3664 |
-
for ($n = count($this->stack) - 1; $n >= 0; $n--) {
|
3665 |
-
if ($this->stack[$n]->tagName === 'table') {
|
3666 |
-
$table = $this->stack[$n];
|
3667 |
-
break;
|
3668 |
-
}
|
3669 |
-
}
|
3670 |
-
|
3671 |
-
if (isset($table) && $table->parentNode !== null) {
|
3672 |
-
return $table->parentNode;
|
3673 |
-
|
3674 |
-
} elseif (!isset($table)) {
|
3675 |
-
return $this->stack[0];
|
3676 |
-
|
3677 |
-
} elseif (isset($table) && ($table->parentNode === null ||
|
3678 |
-
$table->parentNode->nodeType !== XML_ELEMENT_NODE)) {
|
3679 |
-
return $this->stack[$n - 1];
|
3680 |
-
}
|
3681 |
-
|
3682 |
-
return null;
|
3683 |
-
}
|
3684 |
-
|
3685 |
-
/**
|
3686 |
-
* @param $node
|
3687 |
-
*/
|
3688 |
-
public function fosterParent($node) {
|
3689 |
-
$foster_parent = $this->getFosterParent();
|
3690 |
-
$table = $this->getCurrentTable(); // almost equivalent to last table element, except it can be html
|
3691 |
-
/* When a node node is to be foster parented, the node node must be
|
3692 |
-
* be inserted into the foster parent element. */
|
3693 |
-
/* If the foster parent element is the parent element of the last table
|
3694 |
-
* element in the stack of open elements, then node must be inserted
|
3695 |
-
* immediately before the last table element in the stack of open
|
3696 |
-
* elements in the foster parent element; otherwise, node must be
|
3697 |
-
* appended to the foster parent element. */
|
3698 |
-
if ($table->tagName === 'table' && $table->parentNode->isSameNode($foster_parent)) {
|
3699 |
-
$foster_parent->insertBefore($node, $table);
|
3700 |
-
} else {
|
3701 |
-
$foster_parent->appendChild($node);
|
3702 |
-
}
|
3703 |
-
}
|
3704 |
-
|
3705 |
-
/**
|
3706 |
-
* For debugging, prints the stack
|
3707 |
-
*/
|
3708 |
-
private function printStack() {
|
3709 |
-
$names = array();
|
3710 |
-
foreach ($this->stack as $i => $element) {
|
3711 |
-
$names[] = $element->tagName;
|
3712 |
-
}
|
3713 |
-
echo " -> stack [" . implode(', ', $names) . "]\n";
|
3714 |
-
}
|
3715 |
-
|
3716 |
-
/**
|
3717 |
-
* For debugging, prints active formatting elements
|
3718 |
-
*/
|
3719 |
-
private function printActiveFormattingElements() {
|
3720 |
-
if (!$this->a_formatting) return;
|
3721 |
-
$names = array();
|
3722 |
-
foreach ($this->a_formatting as $node) {
|
3723 |
-
if ($node === self::MARKER) $names[] = 'MARKER';
|
3724 |
-
else $names[] = $node->tagName;
|
3725 |
-
}
|
3726 |
-
echo " -> active formatting [" . implode(', ', $names) . "]\n";
|
3727 |
-
}
|
3728 |
-
|
3729 |
-
/**
|
3730 |
-
* @return bool
|
3731 |
-
*/
|
3732 |
-
public function currentTableIsTainted() {
|
3733 |
-
return !empty($this->getCurrentTable()->tainted);
|
3734 |
-
}
|
3735 |
-
|
3736 |
-
/**
|
3737 |
-
* Sets up the tree constructor for building a fragment.
|
3738 |
-
*
|
3739 |
-
* @param null $context
|
3740 |
-
*/
|
3741 |
-
public function setupContext($context = null) {
|
3742 |
-
$this->fragment = true;
|
3743 |
-
if ($context) {
|
3744 |
-
$context = $this->dom->createElementNS(self::NS_HTML, $context);
|
3745 |
-
/* 4.1. Set the HTML parser's tokenization stage's content model
|
3746 |
-
* flag according to the context element, as follows: */
|
3747 |
-
switch ($context->tagName) {
|
3748 |
-
case 'title': case 'textarea':
|
3749 |
-
$this->content_model = HTML5_Tokenizer::RCDATA;
|
3750 |
-
break;
|
3751 |
-
case 'style': case 'script': case 'xmp': case 'iframe':
|
3752 |
-
case 'noembed': case 'noframes':
|
3753 |
-
$this->content_model = HTML5_Tokenizer::CDATA;
|
3754 |
-
break;
|
3755 |
-
case 'noscript':
|
3756 |
-
// XSCRIPT: assuming scripting is enabled
|
3757 |
-
$this->content_model = HTML5_Tokenizer::CDATA;
|
3758 |
-
break;
|
3759 |
-
case 'plaintext':
|
3760 |
-
$this->content_model = HTML5_Tokenizer::PLAINTEXT;
|
3761 |
-
break;
|
3762 |
-
}
|
3763 |
-
/* 4.2. Let root be a new html element with no attributes. */
|
3764 |
-
$root = $this->dom->createElementNS(self::NS_HTML, 'html');
|
3765 |
-
$this->root = $root;
|
3766 |
-
/* 4.3 Append the element root to the Document node created above. */
|
3767 |
-
$this->dom->appendChild($root);
|
3768 |
-
/* 4.4 Set up the parser's stack of open elements so that it
|
3769 |
-
* contains just the single element root. */
|
3770 |
-
$this->stack = array($root);
|
3771 |
-
/* 4.5 Reset the parser's insertion mode appropriately. */
|
3772 |
-
$this->resetInsertionMode($context);
|
3773 |
-
/* 4.6 Set the parser's form element pointer to the nearest node
|
3774 |
-
* to the context element that is a form element (going straight up
|
3775 |
-
* the ancestor chain, and including the element itself, if it is a
|
3776 |
-
* form element), or, if there is no such form element, to null. */
|
3777 |
-
$node = $context;
|
3778 |
-
do {
|
3779 |
-
if ($node->tagName === 'form') {
|
3780 |
-
$this->form_pointer = $node;
|
3781 |
-
break;
|
3782 |
-
}
|
3783 |
-
} while ($node = $node->parentNode);
|
3784 |
-
}
|
3785 |
-
}
|
3786 |
-
|
3787 |
-
/**
|
3788 |
-
* @param $token
|
3789 |
-
* @return mixed
|
3790 |
-
*/
|
3791 |
-
public function adjustMathMLAttributes($token) {
|
3792 |
-
foreach ($token['attr'] as &$kp) {
|
3793 |
-
if ($kp['name'] === 'definitionurl') {
|
3794 |
-
$kp['name'] = 'definitionURL';
|
3795 |
-
}
|
3796 |
-
}
|
3797 |
-
return $token;
|
3798 |
-
}
|
3799 |
-
|
3800 |
-
/**
|
3801 |
-
* @param $token
|
3802 |
-
* @return mixed
|
3803 |
-
*/
|
3804 |
-
public function adjustSVGAttributes($token) {
|
3805 |
-
static $lookup = array(
|
3806 |
-
'attributename' => 'attributeName',
|
3807 |
-
'attributetype' => 'attributeType',
|
3808 |
-
'basefrequency' => 'baseFrequency',
|
3809 |
-
'baseprofile' => 'baseProfile',
|
3810 |
-
'calcmode' => 'calcMode',
|
3811 |
-
'clippathunits' => 'clipPathUnits',
|
3812 |
-
'contentscripttype' => 'contentScriptType',
|
3813 |
-
'contentstyletype' => 'contentStyleType',
|
3814 |
-
'diffuseconstant' => 'diffuseConstant',
|
3815 |
-
'edgemode' => 'edgeMode',
|
3816 |
-
'externalresourcesrequired' => 'externalResourcesRequired',
|
3817 |
-
'filterres' => 'filterRes',
|
3818 |
-
'filterunits' => 'filterUnits',
|
3819 |
-
'glyphref' => 'glyphRef',
|
3820 |
-
'gradienttransform' => 'gradientTransform',
|
3821 |
-
'gradientunits' => 'gradientUnits',
|
3822 |
-
'kernelmatrix' => 'kernelMatrix',
|
3823 |
-
'kernelunitlength' => 'kernelUnitLength',
|
3824 |
-
'keypoints' => 'keyPoints',
|
3825 |
-
'keysplines' => 'keySplines',
|
3826 |
-
'keytimes' => 'keyTimes',
|
3827 |
-
'lengthadjust' => 'lengthAdjust',
|
3828 |
-
'limitingconeangle' => 'limitingConeAngle',
|
3829 |
-
'markerheight' => 'markerHeight',
|
3830 |
-
'markerunits' => 'markerUnits',
|
3831 |
-
'markerwidth' => 'markerWidth',
|
3832 |
-
'maskcontentunits' => 'maskContentUnits',
|
3833 |
-
'maskunits' => 'maskUnits',
|
3834 |
-
'numoctaves' => 'numOctaves',
|
3835 |
-
'pathlength' => 'pathLength',
|
3836 |
-
'patterncontentunits' => 'patternContentUnits',
|
3837 |
-
'patterntransform' => 'patternTransform',
|
3838 |
-
'patternunits' => 'patternUnits',
|
3839 |
-
'pointsatx' => 'pointsAtX',
|
3840 |
-
'pointsaty' => 'pointsAtY',
|
3841 |
-
'pointsatz' => 'pointsAtZ',
|
3842 |
-
'preservealpha' => 'preserveAlpha',
|
3843 |
-
'preserveaspectratio' => 'preserveAspectRatio',
|
3844 |
-
'primitiveunits' => 'primitiveUnits',
|
3845 |
-
'refx' => 'refX',
|
3846 |
-
'refy' => 'refY',
|
3847 |
-
'repeatcount' => 'repeatCount',
|
3848 |
-
'repeatdur' => 'repeatDur',
|
3849 |
-
'requiredextensions' => 'requiredExtensions',
|
3850 |
-
'requiredfeatures' => 'requiredFeatures',
|
3851 |
-
'specularconstant' => 'specularConstant',
|
3852 |
-
'specularexponent' => 'specularExponent',
|
3853 |
-
'spreadmethod' => 'spreadMethod',
|
3854 |
-
'startoffset' => 'startOffset',
|
3855 |
-
'stddeviation' => 'stdDeviation',
|
3856 |
-
'stitchtiles' => 'stitchTiles',
|
3857 |
-
'surfacescale' => 'surfaceScale',
|
3858 |
-
'systemlanguage' => 'systemLanguage',
|
3859 |
-
'tablevalues' => 'tableValues',
|
3860 |
-
'targetx' => 'targetX',
|
3861 |
-
'targety' => 'targetY',
|
3862 |
-
'textlength' => 'textLength',
|
3863 |
-
'viewbox' => 'viewBox',
|
3864 |
-
'viewtarget' => 'viewTarget',
|
3865 |
-
'xchannelselector' => 'xChannelSelector',
|
3866 |
-
'ychannelselector' => 'yChannelSelector',
|
3867 |
-
'zoomandpan' => 'zoomAndPan',
|
3868 |
-
);
|
3869 |
-
foreach ($token['attr'] as &$kp) {
|
3870 |
-
if (isset($lookup[$kp['name']])) {
|
3871 |
-
$kp['name'] = $lookup[$kp['name']];
|
3872 |
-
}
|
3873 |
-
}
|
3874 |
-
return $token;
|
3875 |
-
}
|
3876 |
-
|
3877 |
-
/**
|
3878 |
-
* @param $token
|
3879 |
-
* @return mixed
|
3880 |
-
*/
|
3881 |
-
public function adjustForeignAttributes($token) {
|
3882 |
-
static $lookup = array(
|
3883 |
-
'xlink:actuate' => array('xlink', 'actuate', self::NS_XLINK),
|
3884 |
-
'xlink:arcrole' => array('xlink', 'arcrole', self::NS_XLINK),
|
3885 |
-
'xlink:href' => array('xlink', 'href', self::NS_XLINK),
|
3886 |
-
'xlink:role' => array('xlink', 'role', self::NS_XLINK),
|
3887 |
-
'xlink:show' => array('xlink', 'show', self::NS_XLINK),
|
3888 |
-
'xlink:title' => array('xlink', 'title', self::NS_XLINK),
|
3889 |
-
'xlink:type' => array('xlink', 'type', self::NS_XLINK),
|
3890 |
-
'xml:base' => array('xml', 'base', self::NS_XML),
|
3891 |
-
'xml:lang' => array('xml', 'lang', self::NS_XML),
|
3892 |
-
'xml:space' => array('xml', 'space', self::NS_XML),
|
3893 |
-
'xmlns' => array(null, 'xmlns', self::NS_XMLNS),
|
3894 |
-
'xmlns:xlink' => array('xmlns', 'xlink', self::NS_XMLNS),
|
3895 |
-
);
|
3896 |
-
foreach ($token['attr'] as &$kp) {
|
3897 |
-
if (isset($lookup[$kp['name']])) {
|
3898 |
-
$kp['name'] = $lookup[$kp['name']];
|
3899 |
-
}
|
3900 |
-
}
|
3901 |
-
return $token;
|
3902 |
-
}
|
3903 |
-
|
3904 |
-
/**
|
3905 |
-
* @param $token
|
3906 |
-
* @param $namespaceURI
|
3907 |
-
*/
|
3908 |
-
public function insertForeignElement($token, $namespaceURI) {
|
3909 |
-
$el = $this->dom->createElementNS($namespaceURI, $token['name']);
|
3910 |
-
|
3911 |
-
if (!empty($token['attr'])) {
|
3912 |
-
foreach ($token['attr'] as $kp) {
|
3913 |
-
$attr = $kp['name'];
|
3914 |
-
if (is_array($attr)) {
|
3915 |
-
$ns = $attr[2];
|
3916 |
-
$attr = $attr[1];
|
3917 |
-
} else {
|
3918 |
-
$ns = self::NS_HTML;
|
3919 |
-
}
|
3920 |
-
if (!$el->hasAttributeNS($ns, $attr)) {
|
3921 |
-
// XSKETCHY: work around godawful libxml bug
|
3922 |
-
if ($ns === self::NS_XLINK) {
|
3923 |
-
$el->setAttribute('xlink:'.$attr, $kp['value']);
|
3924 |
-
} elseif ($ns === self::NS_HTML) {
|
3925 |
-
// Another godawful libxml bug
|
3926 |
-
$el->setAttribute($attr, $kp['value']);
|
3927 |
-
} else {
|
3928 |
-
$el->setAttributeNS($ns, $attr, $kp['value']);
|
3929 |
-
}
|
3930 |
-
}
|
3931 |
-
}
|
3932 |
-
}
|
3933 |
-
$this->appendToRealParent($el);
|
3934 |
-
$this->stack[] = $el;
|
3935 |
-
// XERROR: see below
|
3936 |
-
/* If the newly created element has an xmlns attribute in the XMLNS
|
3937 |
-
* namespace whose value is not exactly the same as the element's
|
3938 |
-
* namespace, that is a parse error. Similarly, if the newly created
|
3939 |
-
* element has an xmlns:xlink attribute in the XMLNS namespace whose
|
3940 |
-
* value is not the XLink Namespace, that is a parse error. */
|
3941 |
-
}
|
3942 |
-
|
3943 |
-
/**
|
3944 |
-
* @return DOMDocument|DOMNodeList
|
3945 |
-
*/
|
3946 |
-
public function save() {
|
3947 |
-
$this->dom->normalize();
|
3948 |
-
if (!$this->fragment) {
|
3949 |
-
return $this->dom;
|
3950 |
-
} else {
|
3951 |
-
if ($this->root) {
|
3952 |
-
return $this->root->childNodes;
|
3953 |
-
} else {
|
3954 |
-
return $this->dom->childNodes;
|
3955 |
-
}
|
3956 |
-
}
|
3957 |
-
}
|
3958 |
-
}
|
3959 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
|
5 |
+
Copyright 2007 Jeroen van der Meer <http://jero.net/>
|
6 |
+
Copyright 2009 Edward Z. Yang <edwardzyang@thewritingpot.com>
|
7 |
+
|
8 |
+
Permission is hereby granted, free of charge, to any person obtaining a
|
9 |
+
copy of this software and associated documentation files (the
|
10 |
+
"Software"), to deal in the Software without restriction, including
|
11 |
+
without limitation the rights to use, copy, modify, merge, publish,
|
12 |
+
distribute, sublicense, and/or sell copies of the Software, and to
|
13 |
+
permit persons to whom the Software is furnished to do so, subject to
|
14 |
+
the following conditions:
|
15 |
+
|
16 |
+
The above copyright notice and this permission notice shall be included
|
17 |
+
in all copies or substantial portions of the Software.
|
18 |
+
|
19 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
20 |
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
22 |
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
23 |
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
24 |
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
25 |
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26 |
+
|
27 |
+
*/
|
28 |
+
|
29 |
+
// Tags for FIX ME!!!: (in order of priority)
|
30 |
+
// XXX - should be fixed NAO!
|
31 |
+
// XERROR - with regards to parse errors
|
32 |
+
// XSCRIPT - with regards to scripting mode
|
33 |
+
// XENCODING - with regards to encoding (for reparsing tests)
|
34 |
+
// XDOM - DOM specific code (tagName is explicitly not marked).
|
35 |
+
// this is not (yet) in helper functions.
|
36 |
+
|
37 |
+
class HTML5_TreeBuilder {
|
38 |
+
public $stack = array();
|
39 |
+
public $content_model;
|
40 |
+
|
41 |
+
private $mode;
|
42 |
+
private $original_mode;
|
43 |
+
private $secondary_mode;
|
44 |
+
private $dom;
|
45 |
+
// Whether or not normal insertion of nodes should actually foster
|
46 |
+
// parent (used in one case in spec)
|
47 |
+
private $foster_parent = false;
|
48 |
+
private $a_formatting = array();
|
49 |
+
|
50 |
+
private $head_pointer = null;
|
51 |
+
private $form_pointer = null;
|
52 |
+
|
53 |
+
private $flag_frameset_ok = true;
|
54 |
+
private $flag_force_quirks = false;
|
55 |
+
private $ignored = false;
|
56 |
+
private $quirks_mode = null;
|
57 |
+
// this gets to 2 when we want to ignore the next lf character, and
|
58 |
+
// is decrement at the beginning of each processed token (this way,
|
59 |
+
// code can check for (bool)$ignore_lf_token, but it phases out
|
60 |
+
// appropriately)
|
61 |
+
private $ignore_lf_token = 0;
|
62 |
+
private $fragment = false;
|
63 |
+
private $root;
|
64 |
+
|
65 |
+
private $scoping = array('applet','button','caption','html','marquee','object','table','td','th', 'svg:foreignObject');
|
66 |
+
private $formatting = array('a','b','big','code','em','font','i','nobr','s','small','strike','strong','tt','u');
|
67 |
+
// dl and ds are speculative
|
68 |
+
private $special = array('address','area','article','aside','base','basefont','bgsound',
|
69 |
+
'blockquote','body','br','center','col','colgroup','command','dc','dd','details','dir','div','dl','ds',
|
70 |
+
'dt','embed','fieldset','figure','footer','form','frame','frameset','h1','h2','h3','h4','h5',
|
71 |
+
'h6','head','header','hgroup','hr','iframe','img','input','isindex','li','link',
|
72 |
+
'listing','menu','meta','nav','noembed','noframes','noscript','ol',
|
73 |
+
'p','param','plaintext','pre','script','select','spacer','style',
|
74 |
+
'tbody','textarea','tfoot','thead','title','tr','ul','wbr');
|
75 |
+
|
76 |
+
private $pendingTableCharacters;
|
77 |
+
private $pendingTableCharactersDirty;
|
78 |
+
|
79 |
+
// Tree construction modes
|
80 |
+
const INITIAL = 0;
|
81 |
+
const BEFORE_HTML = 1;
|
82 |
+
const BEFORE_HEAD = 2;
|
83 |
+
const IN_HEAD = 3;
|
84 |
+
const IN_HEAD_NOSCRIPT = 4;
|
85 |
+
const AFTER_HEAD = 5;
|
86 |
+
const IN_BODY = 6;
|
87 |
+
const IN_CDATA_RCDATA = 7;
|
88 |
+
const IN_TABLE = 8;
|
89 |
+
const IN_TABLE_TEXT = 9;
|
90 |
+
const IN_CAPTION = 10;
|
91 |
+
const IN_COLUMN_GROUP = 11;
|
92 |
+
const IN_TABLE_BODY = 12;
|
93 |
+
const IN_ROW = 13;
|
94 |
+
const IN_CELL = 14;
|
95 |
+
const IN_SELECT = 15;
|
96 |
+
const IN_SELECT_IN_TABLE= 16;
|
97 |
+
const IN_FOREIGN_CONTENT= 17;
|
98 |
+
const AFTER_BODY = 18;
|
99 |
+
const IN_FRAMESET = 19;
|
100 |
+
const AFTER_FRAMESET = 20;
|
101 |
+
const AFTER_AFTER_BODY = 21;
|
102 |
+
const AFTER_AFTER_FRAMESET = 22;
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Converts a magic number to a readable name. Use for debugging.
|
106 |
+
*/
|
107 |
+
private function strConst($number) {
|
108 |
+
static $lookup;
|
109 |
+
if (!$lookup) {
|
110 |
+
$lookup = array();
|
111 |
+
$r = new ReflectionClass('HTML5_TreeBuilder');
|
112 |
+
$consts = $r->getConstants();
|
113 |
+
foreach ($consts as $const => $num) {
|
114 |
+
if (!is_int($num)) {
|
115 |
+
continue;
|
116 |
+
}
|
117 |
+
$lookup[$num] = $const;
|
118 |
+
}
|
119 |
+
}
|
120 |
+
return $lookup[$number];
|
121 |
+
}
|
122 |
+
|
123 |
+
// The different types of elements.
|
124 |
+
const SPECIAL = 100;
|
125 |
+
const SCOPING = 101;
|
126 |
+
const FORMATTING = 102;
|
127 |
+
const PHRASING = 103;
|
128 |
+
|
129 |
+
// Quirks modes in $quirks_mode
|
130 |
+
const NO_QUIRKS = 200;
|
131 |
+
const QUIRKS_MODE = 201;
|
132 |
+
const LIMITED_QUIRKS_MODE = 202;
|
133 |
+
|
134 |
+
// Marker to be placed in $a_formatting
|
135 |
+
const MARKER = 300;
|
136 |
+
|
137 |
+
// Namespaces for foreign content
|
138 |
+
const NS_HTML = null; // to prevent DOM from requiring NS on everything
|
139 |
+
const NS_MATHML = 'http://www.w3.org/1998/Math/MathML';
|
140 |
+
const NS_SVG = 'http://www.w3.org/2000/svg';
|
141 |
+
const NS_XLINK = 'http://www.w3.org/1999/xlink';
|
142 |
+
const NS_XML = 'http://www.w3.org/XML/1998/namespace';
|
143 |
+
const NS_XMLNS = 'http://www.w3.org/2000/xmlns/';
|
144 |
+
|
145 |
+
// Different types of scopes to test for elements
|
146 |
+
const SCOPE = 0;
|
147 |
+
const SCOPE_LISTITEM = 1;
|
148 |
+
const SCOPE_TABLE = 2;
|
149 |
+
|
150 |
+
/**
|
151 |
+
* HTML5_TreeBuilder constructor.
|
152 |
+
*/
|
153 |
+
public function __construct() {
|
154 |
+
$this->mode = self::INITIAL;
|
155 |
+
$this->dom = new DOMDocument;
|
156 |
+
|
157 |
+
$this->dom->encoding = 'UTF-8';
|
158 |
+
$this->dom->preserveWhiteSpace = true;
|
159 |
+
$this->dom->substituteEntities = true;
|
160 |
+
$this->dom->strictErrorChecking = false;
|
161 |
+
}
|
162 |
+
|
163 |
+
public function getQuirksMode(){
|
164 |
+
return $this->quirks_mode;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Process tag tokens
|
169 |
+
*
|
170 |
+
* @param $token
|
171 |
+
* @param null $mode
|
172 |
+
*/
|
173 |
+
public function emitToken($token, $mode = null) {
|
174 |
+
// XXX: ignore parse errors... why are we emitting them, again?
|
175 |
+
if ($token['type'] === HTML5_Tokenizer::PARSEERROR) {
|
176 |
+
return;
|
177 |
+
}
|
178 |
+
if ($mode === null) {
|
179 |
+
$mode = $this->mode;
|
180 |
+
}
|
181 |
+
|
182 |
+
/*
|
183 |
+
$backtrace = debug_backtrace();
|
184 |
+
if ($backtrace[1]['class'] !== 'HTML5_TreeBuilder') echo "--\n";
|
185 |
+
echo $this->strConst($mode);
|
186 |
+
if ($this->original_mode) echo " (originally ".$this->strConst($this->original_mode).")";
|
187 |
+
echo "\n ";
|
188 |
+
token_dump($token);
|
189 |
+
$this->printStack();
|
190 |
+
$this->printActiveFormattingElements();
|
191 |
+
if ($this->foster_parent) echo " -> this is a foster parent mode\n";
|
192 |
+
if ($this->flag_frameset_ok) echo " -> frameset ok\n";
|
193 |
+
*/
|
194 |
+
|
195 |
+
if ($this->ignore_lf_token) {
|
196 |
+
$this->ignore_lf_token--;
|
197 |
+
}
|
198 |
+
$this->ignored = false;
|
199 |
+
|
200 |
+
switch ($mode) {
|
201 |
+
case self::INITIAL:
|
202 |
+
|
203 |
+
/* A character token that is one of U+0009 CHARACTER TABULATION,
|
204 |
+
* U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020 SPACE */
|
205 |
+
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
206 |
+
/* Ignore the token. */
|
207 |
+
$this->ignored = true;
|
208 |
+
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
209 |
+
if (
|
210 |
+
$token['name'] !== 'html' || !empty($token['public']) ||
|
211 |
+
!empty($token['system']) || $token !== 'about:legacy-compat'
|
212 |
+
) {
|
213 |
+
/* If the DOCTYPE token's name is not a case-sensitive match
|
214 |
+
* for the string "html", or if the token's public identifier
|
215 |
+
* is not missing, or if the token's system identifier is
|
216 |
+
* neither missing nor a case-sensitive match for the string
|
217 |
+
* "about:legacy-compat", then there is a parse error (this
|
218 |
+
* is the DOCTYPE parse error). */
|
219 |
+
// DOCTYPE parse error
|
220 |
+
}
|
221 |
+
/* Append a DocumentType node to the Document node, with the name
|
222 |
+
* attribute set to the name given in the DOCTYPE token, or the
|
223 |
+
* empty string if the name was missing; the publicId attribute
|
224 |
+
* set to the public identifier given in the DOCTYPE token, or
|
225 |
+
* the empty string if the public identifier was missing; the
|
226 |
+
* systemId attribute set to the system identifier given in the
|
227 |
+
* DOCTYPE token, or the empty string if the system identifier
|
228 |
+
* was missing; and the other attributes specific to
|
229 |
+
* DocumentType objects set to null and empty lists as
|
230 |
+
* appropriate. Associate the DocumentType node with the
|
231 |
+
* Document object so that it is returned as the value of the
|
232 |
+
* doctype attribute of the Document object. */
|
233 |
+
if (!isset($token['public'])) {
|
234 |
+
$token['public'] = null;
|
235 |
+
}
|
236 |
+
if (!isset($token['system'])) {
|
237 |
+
$token['system'] = null;
|
238 |
+
}
|
239 |
+
// XDOM
|
240 |
+
// Yes this is hacky. I'm kind of annoyed that I can't appendChild
|
241 |
+
// a doctype to DOMDocument. Maybe I haven't chanted the right
|
242 |
+
// syllables.
|
243 |
+
$impl = new DOMImplementation();
|
244 |
+
// This call can fail for particularly pathological cases (namely,
|
245 |
+
// the qualifiedName parameter ($token['name']) could be missing.
|
246 |
+
if ($token['name']) {
|
247 |
+
$doctype = $impl->createDocumentType($token['name'], $token['public'], $token['system']);
|
248 |
+
$this->dom->appendChild($doctype);
|
249 |
+
} else {
|
250 |
+
// It looks like libxml's not actually *able* to express this case.
|
251 |
+
// So... don't.
|
252 |
+
$this->dom->emptyDoctype = true;
|
253 |
+
}
|
254 |
+
$public = is_null($token['public']) ? false : strtolower($token['public']);
|
255 |
+
$system = is_null($token['system']) ? false : strtolower($token['system']);
|
256 |
+
$publicStartsWithForQuirks = array(
|
257 |
+
"+//silmaril//dtd html pro v0r11 19970101//",
|
258 |
+
"-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
|
259 |
+
"-//as//dtd html 3.0 aswedit + extensions//",
|
260 |
+
"-//ietf//dtd html 2.0 level 1//",
|
261 |
+
"-//ietf//dtd html 2.0 level 2//",
|
262 |
+
"-//ietf//dtd html 2.0 strict level 1//",
|
263 |
+
"-//ietf//dtd html 2.0 strict level 2//",
|
264 |
+
"-//ietf//dtd html 2.0 strict//",
|
265 |
+
"-//ietf//dtd html 2.0//",
|
266 |
+
"-//ietf//dtd html 2.1e//",
|
267 |
+
"-//ietf//dtd html 3.0//",
|
268 |
+
"-//ietf//dtd html 3.2 final//",
|
269 |
+
"-//ietf//dtd html 3.2//",
|
270 |
+
"-//ietf//dtd html 3//",
|
271 |
+
"-//ietf//dtd html level 0//",
|
272 |
+
"-//ietf//dtd html level 1//",
|
273 |
+
"-//ietf//dtd html level 2//",
|
274 |
+
"-//ietf//dtd html level 3//",
|
275 |
+
"-//ietf//dtd html strict level 0//",
|
276 |
+
"-//ietf//dtd html strict level 1//",
|
277 |
+
"-//ietf//dtd html strict level 2//",
|
278 |
+
"-//ietf//dtd html strict level 3//",
|
279 |
+
"-//ietf//dtd html strict//",
|
280 |
+
"-//ietf//dtd html//",
|
281 |
+
"-//metrius//dtd metrius presentational//",
|
282 |
+
"-//microsoft//dtd internet explorer 2.0 html strict//",
|
283 |
+
"-//microsoft//dtd internet explorer 2.0 html//",
|
284 |
+
"-//microsoft//dtd internet explorer 2.0 tables//",
|
285 |
+
"-//microsoft//dtd internet explorer 3.0 html strict//",
|
286 |
+
"-//microsoft//dtd internet explorer 3.0 html//",
|
287 |
+
"-//microsoft//dtd internet explorer 3.0 tables//",
|
288 |
+
"-//netscape comm. corp.//dtd html//",
|
289 |
+
"-//netscape comm. corp.//dtd strict html//",
|
290 |
+
"-//o'reilly and associates//dtd html 2.0//",
|
291 |
+
"-//o'reilly and associates//dtd html extended 1.0//",
|
292 |
+
"-//o'reilly and associates//dtd html extended relaxed 1.0//",
|
293 |
+
"-//spyglass//dtd html 2.0 extended//",
|
294 |
+
"-//sq//dtd html 2.0 hotmetal + extensions//",
|
295 |
+
"-//sun microsystems corp.//dtd hotjava html//",
|
296 |
+
"-//sun microsystems corp.//dtd hotjava strict html//",
|
297 |
+
"-//w3c//dtd html 3 1995-03-24//",
|
298 |
+
"-//w3c//dtd html 3.2 draft//",
|
299 |
+
"-//w3c//dtd html 3.2 final//",
|
300 |
+
"-//w3c//dtd html 3.2//",
|
301 |
+
"-//w3c//dtd html 3.2s draft//",
|
302 |
+
"-//w3c//dtd html 4.0 frameset//",
|
303 |
+
"-//w3c//dtd html 4.0 transitional//",
|
304 |
+
"-//w3c//dtd html experimental 19960712//",
|
305 |
+
"-//w3c//dtd html experimental 970421//",
|
306 |
+
"-//w3c//dtd w3 html//",
|
307 |
+
"-//w3o//dtd w3 html 3.0//",
|
308 |
+
"-//webtechs//dtd mozilla html 2.0//",
|
309 |
+
"-//webtechs//dtd mozilla html//",
|
310 |
+
);
|
311 |
+
$publicSetToForQuirks = array(
|
312 |
+
"-//w3o//dtd w3 html strict 3.0//",
|
313 |
+
"-/w3c/dtd html 4.0 transitional/en",
|
314 |
+
"html",
|
315 |
+
);
|
316 |
+
$publicStartsWithAndSystemForQuirks = array(
|
317 |
+
"-//w3c//dtd html 4.01 frameset//",
|
318 |
+
"-//w3c//dtd html 4.01 transitional//",
|
319 |
+
);
|
320 |
+
$publicStartsWithForLimitedQuirks = array(
|
321 |
+
"-//w3c//dtd xhtml 1.0 frameset//",
|
322 |
+
"-//w3c//dtd xhtml 1.0 transitional//",
|
323 |
+
);
|
324 |
+
$publicStartsWithAndSystemForLimitedQuirks = array(
|
325 |
+
"-//w3c//dtd html 4.01 frameset//",
|
326 |
+
"-//w3c//dtd html 4.01 transitional//",
|
327 |
+
);
|
328 |
+
// first, do easy checks
|
329 |
+
if (
|
330 |
+
!empty($token['force-quirks']) ||
|
331 |
+
strtolower($token['name']) !== 'html'
|
332 |
+
) {
|
333 |
+
$this->quirks_mode = self::QUIRKS_MODE;
|
334 |
+
} else {
|
335 |
+
do {
|
336 |
+
if ($system) {
|
337 |
+
foreach ($publicStartsWithAndSystemForQuirks as $x) {
|
338 |
+
if (strncmp($public, $x, strlen($x)) === 0) {
|
339 |
+
$this->quirks_mode = self::QUIRKS_MODE;
|
340 |
+
break;
|
341 |
+
}
|
342 |
+
}
|
343 |
+
if (!is_null($this->quirks_mode)) {
|
344 |
+
break;
|
345 |
+
}
|
346 |
+
foreach ($publicStartsWithAndSystemForLimitedQuirks as $x) {
|
347 |
+
if (strncmp($public, $x, strlen($x)) === 0) {
|
348 |
+
$this->quirks_mode = self::LIMITED_QUIRKS_MODE;
|
349 |
+
break;
|
350 |
+
}
|
351 |
+
}
|
352 |
+
if (!is_null($this->quirks_mode)) {
|
353 |
+
break;
|
354 |
+
}
|
355 |
+
}
|
356 |
+
foreach ($publicSetToForQuirks as $x) {
|
357 |
+
if ($public === $x) {
|
358 |
+
$this->quirks_mode = self::QUIRKS_MODE;
|
359 |
+
break;
|
360 |
+
}
|
361 |
+
}
|
362 |
+
if (!is_null($this->quirks_mode)) {
|
363 |
+
break;
|
364 |
+
}
|
365 |
+
foreach ($publicStartsWithForLimitedQuirks as $x) {
|
366 |
+
if (strncmp($public, $x, strlen($x)) === 0) {
|
367 |
+
$this->quirks_mode = self::LIMITED_QUIRKS_MODE;
|
368 |
+
}
|
369 |
+
}
|
370 |
+
if (!is_null($this->quirks_mode)) {
|
371 |
+
break;
|
372 |
+
}
|
373 |
+
if ($system === "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
|
374 |
+
$this->quirks_mode = self::QUIRKS_MODE;
|
375 |
+
break;
|
376 |
+
}
|
377 |
+
foreach ($publicStartsWithForQuirks as $x) {
|
378 |
+
if (strncmp($public, $x, strlen($x)) === 0) {
|
379 |
+
$this->quirks_mode = self::QUIRKS_MODE;
|
380 |
+
break;
|
381 |
+
}
|
382 |
+
}
|
383 |
+
if (is_null($this->quirks_mode)) {
|
384 |
+
$this->quirks_mode = self::NO_QUIRKS;
|
385 |
+
}
|
386 |
+
} while (false);
|
387 |
+
}
|
388 |
+
$this->mode = self::BEFORE_HTML;
|
389 |
+
} else {
|
390 |
+
// parse error
|
391 |
+
/* Switch the insertion mode to "before html", then reprocess the
|
392 |
+
* current token. */
|
393 |
+
$this->mode = self::BEFORE_HTML;
|
394 |
+
$this->quirks_mode = self::QUIRKS_MODE;
|
395 |
+
$this->emitToken($token);
|
396 |
+
}
|
397 |
+
break;
|
398 |
+
|
399 |
+
case self::BEFORE_HTML:
|
400 |
+
/* A DOCTYPE token */
|
401 |
+
if ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
402 |
+
// Parse error. Ignore the token.
|
403 |
+
$this->ignored = true;
|
404 |
+
|
405 |
+
/* A comment token */
|
406 |
+
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
407 |
+
/* Append a Comment node to the Document object with the data
|
408 |
+
attribute set to the data given in the comment token. */
|
409 |
+
// XDOM
|
410 |
+
$comment = $this->dom->createComment($token['data']);
|
411 |
+
$this->dom->appendChild($comment);
|
412 |
+
|
413 |
+
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
414 |
+
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
415 |
+
or U+0020 SPACE */
|
416 |
+
} elseif ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
417 |
+
/* Ignore the token. */
|
418 |
+
$this->ignored = true;
|
419 |
+
|
420 |
+
/* A start tag whose tag name is "html" */
|
421 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] == 'html') {
|
422 |
+
/* Create an element for the token in the HTML namespace. Append it
|
423 |
+
* to the Document object. Put this element in the stack of open
|
424 |
+
* elements. */
|
425 |
+
// XDOM
|
426 |
+
$html = $this->insertElement($token, false);
|
427 |
+
$this->dom->appendChild($html);
|
428 |
+
$this->stack[] = $html;
|
429 |
+
|
430 |
+
$this->mode = self::BEFORE_HEAD;
|
431 |
+
|
432 |
+
} else {
|
433 |
+
/* Create an html element. Append it to the Document object. Put
|
434 |
+
* this element in the stack of open elements. */
|
435 |
+
// XDOM
|
436 |
+
$html = $this->dom->createElementNS(self::NS_HTML, 'html');
|
437 |
+
$this->dom->appendChild($html);
|
438 |
+
$this->stack[] = $html;
|
439 |
+
|
440 |
+
/* Switch the insertion mode to "before head", then reprocess the
|
441 |
+
* current token. */
|
442 |
+
$this->mode = self::BEFORE_HEAD;
|
443 |
+
$this->emitToken($token);
|
444 |
+
}
|
445 |
+
break;
|
446 |
+
|
447 |
+
case self::BEFORE_HEAD:
|
448 |
+
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
449 |
+
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
450 |
+
or U+0020 SPACE */
|
451 |
+
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
452 |
+
/* Ignore the token. */
|
453 |
+
$this->ignored = true;
|
454 |
+
|
455 |
+
/* A comment token */
|
456 |
+
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
457 |
+
/* Append a Comment node to the current node with the data attribute
|
458 |
+
set to the data given in the comment token. */
|
459 |
+
$this->insertComment($token['data']);
|
460 |
+
|
461 |
+
/* A DOCTYPE token */
|
462 |
+
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
463 |
+
/* Parse error. Ignore the token */
|
464 |
+
$this->ignored = true;
|
465 |
+
// parse error
|
466 |
+
|
467 |
+
/* A start tag token with the tag name "html" */
|
468 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
469 |
+
/* Process the token using the rules for the "in body"
|
470 |
+
* insertion mode. */
|
471 |
+
$this->processWithRulesFor($token, self::IN_BODY);
|
472 |
+
|
473 |
+
/* A start tag token with the tag name "head" */
|
474 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') {
|
475 |
+
/* Insert an HTML element for the token. */
|
476 |
+
$element = $this->insertElement($token);
|
477 |
+
|
478 |
+
/* Set the head element pointer to this new element node. */
|
479 |
+
$this->head_pointer = $element;
|
480 |
+
|
481 |
+
/* Change the insertion mode to "in head". */
|
482 |
+
$this->mode = self::IN_HEAD;
|
483 |
+
|
484 |
+
/* An end tag whose tag name is one of: "head", "body", "html", "br" */
|
485 |
+
} elseif (
|
486 |
+
$token['type'] === HTML5_Tokenizer::ENDTAG && (
|
487 |
+
$token['name'] === 'head' || $token['name'] === 'body' ||
|
488 |
+
$token['name'] === 'html' || $token['name'] === 'br'
|
489 |
+
)) {
|
490 |
+
/* Act as if a start tag token with the tag name "head" and no
|
491 |
+
* attributes had been seen, then reprocess the current token. */
|
492 |
+
$this->emitToken(array(
|
493 |
+
'name' => 'head',
|
494 |
+
'type' => HTML5_Tokenizer::STARTTAG,
|
495 |
+
'attr' => array()
|
496 |
+
));
|
497 |
+
$this->emitToken($token);
|
498 |
+
|
499 |
+
/* Any other end tag */
|
500 |
+
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG) {
|
501 |
+
/* Parse error. Ignore the token. */
|
502 |
+
$this->ignored = true;
|
503 |
+
|
504 |
+
} else {
|
505 |
+
/* Act as if a start tag token with the tag name "head" and no
|
506 |
+
* attributes had been seen, then reprocess the current token.
|
507 |
+
* Note: This will result in an empty head element being
|
508 |
+
* generated, with the current token being reprocessed in the
|
509 |
+
* "after head" insertion mode. */
|
510 |
+
$this->emitToken(array(
|
511 |
+
'name' => 'head',
|
512 |
+
'type' => HTML5_Tokenizer::STARTTAG,
|
513 |
+
'attr' => array()
|
514 |
+
));
|
515 |
+
$this->emitToken($token);
|
516 |
+
}
|
517 |
+
break;
|
518 |
+
|
519 |
+
case self::IN_HEAD:
|
520 |
+
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
521 |
+
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
522 |
+
or U+0020 SPACE. */
|
523 |
+
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
524 |
+
/* Insert the character into the current node. */
|
525 |
+
$this->insertText($token['data']);
|
526 |
+
|
527 |
+
/* A comment token */
|
528 |
+
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
529 |
+
/* Append a Comment node to the current node with the data attribute
|
530 |
+
set to the data given in the comment token. */
|
531 |
+
$this->insertComment($token['data']);
|
532 |
+
|
533 |
+
/* A DOCTYPE token */
|
534 |
+
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
535 |
+
/* Parse error. Ignore the token. */
|
536 |
+
$this->ignored = true;
|
537 |
+
// parse error
|
538 |
+
|
539 |
+
/* A start tag whose tag name is "html" */
|
540 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
541 |
+
$token['name'] === 'html') {
|
542 |
+
$this->processWithRulesFor($token, self::IN_BODY);
|
543 |
+
|
544 |
+
/* A start tag whose tag name is one of: "base", "command", "link" */
|
545 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
546 |
+
($token['name'] === 'base' || $token['name'] === 'command' ||
|
547 |
+
$token['name'] === 'link')) {
|
548 |
+
/* Insert an HTML element for the token. Immediately pop the
|
549 |
+
* current node off the stack of open elements. */
|
550 |
+
$this->insertElement($token);
|
551 |
+
array_pop($this->stack);
|
552 |
+
|
553 |
+
// YYY: Acknowledge the token's self-closing flag, if it is set.
|
554 |
+
|
555 |
+
/* A start tag whose tag name is "meta" */
|
556 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'meta') {
|
557 |
+
/* Insert an HTML element for the token. Immediately pop the
|
558 |
+
* current node off the stack of open elements. */
|
559 |
+
$this->insertElement($token);
|
560 |
+
array_pop($this->stack);
|
561 |
+
|
562 |
+
// XERROR: Acknowledge the token's self-closing flag, if it is set.
|
563 |
+
|
564 |
+
// XENCODING: If the element has a charset attribute, and its value is a
|
565 |
+
// supported encoding, and the confidence is currently tentative,
|
566 |
+
// then change the encoding to the encoding given by the value of
|
567 |
+
// the charset attribute.
|
568 |
+
//
|
569 |
+
// Otherwise, if the element has a content attribute, and applying
|
570 |
+
// the algorithm for extracting an encoding from a Content-Type to
|
571 |
+
// its value returns a supported encoding encoding, and the
|
572 |
+
// confidence is currently tentative, then change the encoding to
|
573 |
+
// the encoding encoding.
|
574 |
+
|
575 |
+
/* A start tag with the tag name "title" */
|
576 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'title') {
|
577 |
+
$this->insertRCDATAElement($token);
|
578 |
+
|
579 |
+
/* A start tag whose tag name is "noscript", if the scripting flag is enabled, or
|
580 |
+
* A start tag whose tag name is one of: "noframes", "style" */
|
581 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
582 |
+
($token['name'] === 'noscript' || $token['name'] === 'noframes' || $token['name'] === 'style')) {
|
583 |
+
// XSCRIPT: Scripting flag not respected
|
584 |
+
$this->insertCDATAElement($token);
|
585 |
+
|
586 |
+
// XSCRIPT: Scripting flag disable not implemented
|
587 |
+
|
588 |
+
/* A start tag with the tag name "script" */
|
589 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') {
|
590 |
+
/* 1. Create an element for the token in the HTML namespace. */
|
591 |
+
$node = $this->insertElement($token, false);
|
592 |
+
|
593 |
+
/* 2. Mark the element as being "parser-inserted" */
|
594 |
+
// Uhhh... XSCRIPT
|
595 |
+
|
596 |
+
/* 3. If the parser was originally created for the HTML
|
597 |
+
* fragment parsing algorithm, then mark the script element as
|
598 |
+
* "already executed". (fragment case) */
|
599 |
+
// ditto... XSCRIPT
|
600 |
+
|
601 |
+
/* 4. Append the new element to the current node and push it onto
|
602 |
+
* the stack of open elements. */
|
603 |
+
end($this->stack)->appendChild($node);
|
604 |
+
$this->stack[] = $node;
|
605 |
+
// I guess we could squash these together
|
606 |
+
|
607 |
+
/* 6. Let the original insertion mode be the current insertion mode. */
|
608 |
+
$this->original_mode = $this->mode;
|
609 |
+
/* 7. Switch the insertion mode to "in CDATA/RCDATA" */
|
610 |
+
$this->mode = self::IN_CDATA_RCDATA;
|
611 |
+
/* 5. Switch the tokeniser's content model flag to the CDATA state. */
|
612 |
+
$this->content_model = HTML5_Tokenizer::CDATA;
|
613 |
+
|
614 |
+
/* An end tag with the tag name "head" */
|
615 |
+
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'head') {
|
616 |
+
/* Pop the current node (which will be the head element) off the stack of open elements. */
|
617 |
+
array_pop($this->stack);
|
618 |
+
|
619 |
+
/* Change the insertion mode to "after head". */
|
620 |
+
$this->mode = self::AFTER_HEAD;
|
621 |
+
|
622 |
+
// Slight logic inversion here to minimize duplication
|
623 |
+
/* A start tag with the tag name "head". */
|
624 |
+
/* An end tag whose tag name is not one of: "body", "html", "br" */
|
625 |
+
} elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') ||
|
626 |
+
($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] !== 'html' &&
|
627 |
+
$token['name'] !== 'body' && $token['name'] !== 'br')) {
|
628 |
+
// Parse error. Ignore the token.
|
629 |
+
$this->ignored = true;
|
630 |
+
|
631 |
+
/* Anything else */
|
632 |
+
} else {
|
633 |
+
/* Act as if an end tag token with the tag name "head" had been
|
634 |
+
* seen, and reprocess the current token. */
|
635 |
+
$this->emitToken(array(
|
636 |
+
'name' => 'head',
|
637 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
638 |
+
));
|
639 |
+
|
640 |
+
/* Then, reprocess the current token. */
|
641 |
+
$this->emitToken($token);
|
642 |
+
}
|
643 |
+
break;
|
644 |
+
|
645 |
+
case self::IN_HEAD_NOSCRIPT:
|
646 |
+
if ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
647 |
+
// parse error
|
648 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
649 |
+
$this->processWithRulesFor($token, self::IN_BODY);
|
650 |
+
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'noscript') {
|
651 |
+
/* Pop the current node (which will be a noscript element) from the
|
652 |
+
* stack of open elements; the new current node will be a head
|
653 |
+
* element. */
|
654 |
+
array_pop($this->stack);
|
655 |
+
$this->mode = self::IN_HEAD;
|
656 |
+
} elseif (
|
657 |
+
($token['type'] === HTML5_Tokenizer::SPACECHARACTER) ||
|
658 |
+
($token['type'] === HTML5_Tokenizer::COMMENT) ||
|
659 |
+
($token['type'] === HTML5_Tokenizer::STARTTAG && (
|
660 |
+
$token['name'] === 'link' || $token['name'] === 'meta' ||
|
661 |
+
$token['name'] === 'noframes' || $token['name'] === 'style'))) {
|
662 |
+
$this->processWithRulesFor($token, self::IN_HEAD);
|
663 |
+
// inverted logic
|
664 |
+
} elseif (
|
665 |
+
($token['type'] === HTML5_Tokenizer::STARTTAG && (
|
666 |
+
$token['name'] === 'head' || $token['name'] === 'noscript')) ||
|
667 |
+
($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
668 |
+
$token['name'] !== 'br')) {
|
669 |
+
// parse error
|
670 |
+
} else {
|
671 |
+
// parse error
|
672 |
+
$this->emitToken(array(
|
673 |
+
'type' => HTML5_Tokenizer::ENDTAG,
|
674 |
+
'name' => 'noscript',
|
675 |
+
));
|
676 |
+
$this->emitToken($token);
|
677 |
+
}
|
678 |
+
break;
|
679 |
+
|
680 |
+
case self::AFTER_HEAD:
|
681 |
+
/* Handle the token as follows: */
|
682 |
+
|
683 |
+
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
684 |
+
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
685 |
+
or U+0020 SPACE */
|
686 |
+
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
687 |
+
/* Append the character to the current node. */
|
688 |
+
$this->insertText($token['data']);
|
689 |
+
|
690 |
+
/* A comment token */
|
691 |
+
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
692 |
+
/* Append a Comment node to the current node with the data attribute
|
693 |
+
set to the data given in the comment token. */
|
694 |
+
$this->insertComment($token['data']);
|
695 |
+
|
696 |
+
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
697 |
+
// parse error
|
698 |
+
|
699 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
700 |
+
$this->processWithRulesFor($token, self::IN_BODY);
|
701 |
+
|
702 |
+
/* A start tag token with the tag name "body" */
|
703 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'body') {
|
704 |
+
$this->insertElement($token);
|
705 |
+
|
706 |
+
/* Set the frameset-ok flag to "not ok". */
|
707 |
+
$this->flag_frameset_ok = false;
|
708 |
+
|
709 |
+
/* Change the insertion mode to "in body". */
|
710 |
+
$this->mode = self::IN_BODY;
|
711 |
+
|
712 |
+
/* A start tag token with the tag name "frameset" */
|
713 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'frameset') {
|
714 |
+
/* Insert a frameset element for the token. */
|
715 |
+
$this->insertElement($token);
|
716 |
+
|
717 |
+
/* Change the insertion mode to "in frameset". */
|
718 |
+
$this->mode = self::IN_FRAMESET;
|
719 |
+
|
720 |
+
/* A start tag token whose tag name is one of: "base", "link", "meta",
|
721 |
+
"script", "style", "title" */
|
722 |
+
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
723 |
+
array('base', 'link', 'meta', 'noframes', 'script', 'style', 'title'))) {
|
724 |
+
// parse error
|
725 |
+
/* Push the node pointed to by the head element pointer onto the
|
726 |
+
* stack of open elements. */
|
727 |
+
$this->stack[] = $this->head_pointer;
|
728 |
+
$this->processWithRulesFor($token, self::IN_HEAD);
|
729 |
+
array_splice($this->stack, array_search($this->head_pointer, $this->stack, true), 1);
|
730 |
+
|
731 |
+
// inversion of specification
|
732 |
+
} elseif (
|
733 |
+
($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') ||
|
734 |
+
($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
735 |
+
$token['name'] !== 'body' && $token['name'] !== 'html' &&
|
736 |
+
$token['name'] !== 'br')) {
|
737 |
+
// parse error
|
738 |
+
|
739 |
+
/* Anything else */
|
740 |
+
} else {
|
741 |
+
$this->emitToken(array(
|
742 |
+
'name' => 'body',
|
743 |
+
'type' => HTML5_Tokenizer::STARTTAG,
|
744 |
+
'attr' => array()
|
745 |
+
));
|
746 |
+
$this->flag_frameset_ok = true;
|
747 |
+
$this->emitToken($token);
|
748 |
+
}
|
749 |
+
break;
|
750 |
+
|
751 |
+
case self::IN_BODY:
|
752 |
+
/* Handle the token as follows: */
|
753 |
+
|
754 |
+
switch($token['type']) {
|
755 |
+
/* A character token */
|
756 |
+
case HTML5_Tokenizer::CHARACTER:
|
757 |
+
case HTML5_Tokenizer::SPACECHARACTER:
|
758 |
+
/* Reconstruct the active formatting elements, if any. */
|
759 |
+
$this->reconstructActiveFormattingElements();
|
760 |
+
|
761 |
+
/* Append the token's character to the current node. */
|
762 |
+
$this->insertText($token['data']);
|
763 |
+
|
764 |
+
/* If the token is not one of U+0009 CHARACTER TABULATION,
|
765 |
+
* U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020
|
766 |
+
* SPACE, then set the frameset-ok flag to "not ok". */
|
767 |
+
// i.e., if any of the characters is not whitespace
|
768 |
+
if (strlen($token['data']) !== strspn($token['data'], HTML5_Tokenizer::WHITESPACE)) {
|
769 |
+
$this->flag_frameset_ok = false;
|
770 |
+
}
|
771 |
+
break;
|
772 |
+
|
773 |
+
/* A comment token */
|
774 |
+
case HTML5_Tokenizer::COMMENT:
|
775 |
+
/* Append a Comment node to the current node with the data
|
776 |
+
attribute set to the data given in the comment token. */
|
777 |
+
$this->insertComment($token['data']);
|
778 |
+
break;
|
779 |
+
|
780 |
+
case HTML5_Tokenizer::DOCTYPE:
|
781 |
+
// parse error
|
782 |
+
break;
|
783 |
+
|
784 |
+
case HTML5_Tokenizer::EOF:
|
785 |
+
// parse error
|
786 |
+
break;
|
787 |
+
|
788 |
+
case HTML5_Tokenizer::STARTTAG:
|
789 |
+
switch($token['name']) {
|
790 |
+
case 'html':
|
791 |
+
// parse error
|
792 |
+
/* For each attribute on the token, check to see if the
|
793 |
+
* attribute is already present on the top element of the
|
794 |
+
* stack of open elements. If it is not, add the attribute
|
795 |
+
* and its corresponding value to that element. */
|
796 |
+
foreach($token['attr'] as $attr) {
|
797 |
+
if (!$this->stack[0]->hasAttribute($attr['name'])) {
|
798 |
+
$this->stack[0]->setAttribute($attr['name'], $attr['value']);
|
799 |
+
}
|
800 |
+
}
|
801 |
+
break;
|
802 |
+
|
803 |
+
case 'base': case 'command': case 'link': case 'meta': case 'noframes':
|
804 |
+
case 'script': case 'style': case 'title':
|
805 |
+
/* Process the token as if the insertion mode had been "in
|
806 |
+
head". */
|
807 |
+
$this->processWithRulesFor($token, self::IN_HEAD);
|
808 |
+
break;
|
809 |
+
|
810 |
+
/* A start tag token with the tag name "body" */
|
811 |
+
case 'body':
|
812 |
+
/* Parse error. If the second element on the stack of open
|
813 |
+
elements is not a body element, or, if the stack of open
|
814 |
+
elements has only one node on it, then ignore the token.
|
815 |
+
(fragment case) */
|
816 |
+
if (count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') {
|
817 |
+
$this->ignored = true;
|
818 |
+
// Ignore
|
819 |
+
|
820 |
+
/* Otherwise, for each attribute on the token, check to see
|
821 |
+
if the attribute is already present on the body element (the
|
822 |
+
second element) on the stack of open elements. If it is not,
|
823 |
+
add the attribute and its corresponding value to that
|
824 |
+
element. */
|
825 |
+
} else {
|
826 |
+
foreach($token['attr'] as $attr) {
|
827 |
+
if (!$this->stack[1]->hasAttribute($attr['name'])) {
|
828 |
+
$this->stack[1]->setAttribute($attr['name'], $attr['value']);
|
829 |
+
}
|
830 |
+
}
|
831 |
+
}
|
832 |
+
break;
|
833 |
+
|
834 |
+
case 'frameset':
|
835 |
+
// parse error
|
836 |
+
/* If the second element on the stack of open elements is
|
837 |
+
* not a body element, or, if the stack of open elements
|
838 |
+
* has only one node on it, then ignore the token.
|
839 |
+
* (fragment case) */
|
840 |
+
if (count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') {
|
841 |
+
$this->ignored = true;
|
842 |
+
// Ignore
|
843 |
+
} elseif (!$this->flag_frameset_ok) {
|
844 |
+
$this->ignored = true;
|
845 |
+
// Ignore
|
846 |
+
} else {
|
847 |
+
/* 1. Remove the second element on the stack of open
|
848 |
+
* elements from its parent node, if it has one. */
|
849 |
+
if ($this->stack[1]->parentNode) {
|
850 |
+
$this->stack[1]->parentNode->removeChild($this->stack[1]);
|
851 |
+
}
|
852 |
+
|
853 |
+
/* 2. Pop all the nodes from the bottom of the stack of
|
854 |
+
* open elements, from the current node up to the root
|
855 |
+
* html element. */
|
856 |
+
array_splice($this->stack, 1);
|
857 |
+
|
858 |
+
$this->insertElement($token);
|
859 |
+
$this->mode = self::IN_FRAMESET;
|
860 |
+
}
|
861 |
+
break;
|
862 |
+
|
863 |
+
// in spec, there is a diversion here
|
864 |
+
|
865 |
+
case 'address': case 'article': case 'aside': case 'blockquote':
|
866 |
+
case 'center': case 'datagrid': case 'details': case 'dir':
|
867 |
+
case 'div': case 'dl': case 'fieldset': case 'figure': case 'footer':
|
868 |
+
case 'header': case 'hgroup': case 'menu': case 'nav':
|
869 |
+
case 'ol': case 'p': case 'section': case 'ul':
|
870 |
+
/* If the stack of open elements has a p element in scope,
|
871 |
+
then act as if an end tag with the tag name p had been
|
872 |
+
seen. */
|
873 |
+
if ($this->elementInScope('p')) {
|
874 |
+
$this->emitToken(array(
|
875 |
+
'name' => 'p',
|
876 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
877 |
+
));
|
878 |
+
}
|
879 |
+
|
880 |
+
/* Insert an HTML element for the token. */
|
881 |
+
$this->insertElement($token);
|
882 |
+
break;
|
883 |
+
|
884 |
+
/* A start tag whose tag name is one of: "h1", "h2", "h3", "h4",
|
885 |
+
"h5", "h6" */
|
886 |
+
case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
|
887 |
+
/* If the stack of open elements has a p element in scope,
|
888 |
+
then act as if an end tag with the tag name p had been seen. */
|
889 |
+
if ($this->elementInScope('p')) {
|
890 |
+
$this->emitToken(array(
|
891 |
+
'name' => 'p',
|
892 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
893 |
+
));
|
894 |
+
}
|
895 |
+
|
896 |
+
/* If the current node is an element whose tag name is one
|
897 |
+
* of "h1", "h2", "h3", "h4", "h5", or "h6", then this is a
|
898 |
+
* parse error; pop the current node off the stack of open
|
899 |
+
* elements. */
|
900 |
+
$peek = array_pop($this->stack);
|
901 |
+
if (in_array($peek->tagName, array("h1", "h2", "h3", "h4", "h5", "h6"))) {
|
902 |
+
// parse error
|
903 |
+
} else {
|
904 |
+
$this->stack[] = $peek;
|
905 |
+
}
|
906 |
+
|
907 |
+
/* Insert an HTML element for the token. */
|
908 |
+
$this->insertElement($token);
|
909 |
+
break;
|
910 |
+
|
911 |
+
case 'pre': case 'listing':
|
912 |
+
/* If the stack of open elements has a p element in scope,
|
913 |
+
then act as if an end tag with the tag name p had been seen. */
|
914 |
+
if ($this->elementInScope('p')) {
|
915 |
+
$this->emitToken(array(
|
916 |
+
'name' => 'p',
|
917 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
918 |
+
));
|
919 |
+
}
|
920 |
+
$this->insertElement($token);
|
921 |
+
/* If the next token is a U+000A LINE FEED (LF) character
|
922 |
+
* token, then ignore that token and move on to the next
|
923 |
+
* one. (Newlines at the start of pre blocks are ignored as
|
924 |
+
* an authoring convenience.) */
|
925 |
+
$this->ignore_lf_token = 2;
|
926 |
+
$this->flag_frameset_ok = false;
|
927 |
+
break;
|
928 |
+
|
929 |
+
/* A start tag whose tag name is "form" */
|
930 |
+
case 'form':
|
931 |
+
/* If the form element pointer is not null, ignore the
|
932 |
+
token with a parse error. */
|
933 |
+
if ($this->form_pointer !== null) {
|
934 |
+
$this->ignored = true;
|
935 |
+
// Ignore.
|
936 |
+
|
937 |
+
/* Otherwise: */
|
938 |
+
} else {
|
939 |
+
/* If the stack of open elements has a p element in
|
940 |
+
scope, then act as if an end tag with the tag name p
|
941 |
+
had been seen. */
|
942 |
+
if ($this->elementInScope('p')) {
|
943 |
+
$this->emitToken(array(
|
944 |
+
'name' => 'p',
|
945 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
946 |
+
));
|
947 |
+
}
|
948 |
+
|
949 |
+
/* Insert an HTML element for the token, and set the
|
950 |
+
form element pointer to point to the element created. */
|
951 |
+
$element = $this->insertElement($token);
|
952 |
+
$this->form_pointer = $element;
|
953 |
+
}
|
954 |
+
break;
|
955 |
+
|
956 |
+
// condensed specification
|
957 |
+
case 'li': case 'dc': case 'dd': case 'ds': case 'dt':
|
958 |
+
/* 1. Set the frameset-ok flag to "not ok". */
|
959 |
+
$this->flag_frameset_ok = false;
|
960 |
+
|
961 |
+
$stack_length = count($this->stack) - 1;
|
962 |
+
for($n = $stack_length; 0 <= $n; $n--) {
|
963 |
+
/* 2. Initialise node to be the current node (the
|
964 |
+
bottommost node of the stack). */
|
965 |
+
$stop = false;
|
966 |
+
$node = $this->stack[$n];
|
967 |
+
$cat = $this->getElementCategory($node);
|
968 |
+
|
969 |
+
// for case 'li':
|
970 |
+
/* 3. If node is an li element, then act as if an end
|
971 |
+
* tag with the tag name "li" had been seen, then jump
|
972 |
+
* to the last step. */
|
973 |
+
// for case 'dc': case 'dd': case 'ds': case 'dt':
|
974 |
+
/* If node is a dc, dd, ds or dt element, then act as if an end
|
975 |
+
* tag with the same tag name as node had been seen, then
|
976 |
+
* jump to the last step. */
|
977 |
+
if (($token['name'] === 'li' && $node->tagName === 'li') ||
|
978 |
+
($token['name'] !== 'li' && ($node->tagName == 'dc' || $node->tagName === 'dd' || $node->tagName == 'ds' || $node->tagName === 'dt'))) { // limited conditional
|
979 |
+
$this->emitToken(array(
|
980 |
+
'type' => HTML5_Tokenizer::ENDTAG,
|
981 |
+
'name' => $node->tagName,
|
982 |
+
));
|
983 |
+
break;
|
984 |
+
}
|
985 |
+
|
986 |
+
/* 4. If node is not in the formatting category, and is
|
987 |
+
not in the phrasing category, and is not an address,
|
988 |
+
div or p element, then stop this algorithm. */
|
989 |
+
if ($cat !== self::FORMATTING && $cat !== self::PHRASING &&
|
990 |
+
$node->tagName !== 'address' && $node->tagName !== 'div' &&
|
991 |
+
$node->tagName !== 'p') {
|
992 |
+
break;
|
993 |
+
}
|
994 |
+
|
995 |
+
/* 5. Otherwise, set node to the previous entry in the
|
996 |
+
* stack of open elements and return to step 2. */
|
997 |
+
}
|
998 |
+
|
999 |
+
/* 6. This is the last step. */
|
1000 |
+
|
1001 |
+
/* If the stack of open elements has a p element in scope,
|
1002 |
+
then act as if an end tag with the tag name p had been
|
1003 |
+
seen. */
|
1004 |
+
if ($this->elementInScope('p')) {
|
1005 |
+
$this->emitToken(array(
|
1006 |
+
'name' => 'p',
|
1007 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1008 |
+
));
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
/* Finally, insert an HTML element with the same tag
|
1012 |
+
name as the token's. */
|
1013 |
+
$this->insertElement($token);
|
1014 |
+
break;
|
1015 |
+
|
1016 |
+
/* A start tag token whose tag name is "plaintext" */
|
1017 |
+
case 'plaintext':
|
1018 |
+
/* If the stack of open elements has a p element in scope,
|
1019 |
+
then act as if an end tag with the tag name p had been
|
1020 |
+
seen. */
|
1021 |
+
if ($this->elementInScope('p')) {
|
1022 |
+
$this->emitToken(array(
|
1023 |
+
'name' => 'p',
|
1024 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1025 |
+
));
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
/* Insert an HTML element for the token. */
|
1029 |
+
$this->insertElement($token);
|
1030 |
+
|
1031 |
+
$this->content_model = HTML5_Tokenizer::PLAINTEXT;
|
1032 |
+
break;
|
1033 |
+
|
1034 |
+
// more diversions
|
1035 |
+
|
1036 |
+
/* A start tag whose tag name is "a" */
|
1037 |
+
case 'a':
|
1038 |
+
/* If the list of active formatting elements contains
|
1039 |
+
an element whose tag name is "a" between the end of the
|
1040 |
+
list and the last marker on the list (or the start of
|
1041 |
+
the list if there is no marker on the list), then this
|
1042 |
+
is a parse error; act as if an end tag with the tag name
|
1043 |
+
"a" had been seen, then remove that element from the list
|
1044 |
+
of active formatting elements and the stack of open
|
1045 |
+
elements if the end tag didn't already remove it (it
|
1046 |
+
might not have if the element is not in table scope). */
|
1047 |
+
$leng = count($this->a_formatting);
|
1048 |
+
|
1049 |
+
for ($n = $leng - 1; $n >= 0; $n--) {
|
1050 |
+
if ($this->a_formatting[$n] === self::MARKER) {
|
1051 |
+
break;
|
1052 |
+
|
1053 |
+
} elseif ($this->a_formatting[$n]->tagName === 'a') {
|
1054 |
+
$a = $this->a_formatting[$n];
|
1055 |
+
$this->emitToken(array(
|
1056 |
+
'name' => 'a',
|
1057 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1058 |
+
));
|
1059 |
+
if (in_array($a, $this->a_formatting)) {
|
1060 |
+
$a_i = array_search($a, $this->a_formatting, true);
|
1061 |
+
if ($a_i !== false) {
|
1062 |
+
array_splice($this->a_formatting, $a_i, 1);
|
1063 |
+
}
|
1064 |
+
}
|
1065 |
+
if (in_array($a, $this->stack)) {
|
1066 |
+
$a_i = array_search($a, $this->stack, true);
|
1067 |
+
if ($a_i !== false) {
|
1068 |
+
array_splice($this->stack, $a_i, 1);
|
1069 |
+
}
|
1070 |
+
}
|
1071 |
+
break;
|
1072 |
+
}
|
1073 |
+
}
|
1074 |
+
|
1075 |
+
/* Reconstruct the active formatting elements, if any. */
|
1076 |
+
$this->reconstructActiveFormattingElements();
|
1077 |
+
|
1078 |
+
/* Insert an HTML element for the token. */
|
1079 |
+
$el = $this->insertElement($token);
|
1080 |
+
|
1081 |
+
/* Add that element to the list of active formatting
|
1082 |
+
elements. */
|
1083 |
+
$this->a_formatting[] = $el;
|
1084 |
+
break;
|
1085 |
+
|
1086 |
+
case 'b': case 'big': case 'code': case 'em': case 'font': case 'i':
|
1087 |
+
case 's': case 'small': case 'strike':
|
1088 |
+
case 'strong': case 'tt': case 'u':
|
1089 |
+
/* Reconstruct the active formatting elements, if any. */
|
1090 |
+
$this->reconstructActiveFormattingElements();
|
1091 |
+
|
1092 |
+
/* Insert an HTML element for the token. */
|
1093 |
+
$el = $this->insertElement($token);
|
1094 |
+
|
1095 |
+
/* Add that element to the list of active formatting
|
1096 |
+
elements. */
|
1097 |
+
$this->a_formatting[] = $el;
|
1098 |
+
break;
|
1099 |
+
|
1100 |
+
case 'nobr':
|
1101 |
+
/* Reconstruct the active formatting elements, if any. */
|
1102 |
+
$this->reconstructActiveFormattingElements();
|
1103 |
+
|
1104 |
+
/* If the stack of open elements has a nobr element in
|
1105 |
+
* scope, then this is a parse error; act as if an end tag
|
1106 |
+
* with the tag name "nobr" had been seen, then once again
|
1107 |
+
* reconstruct the active formatting elements, if any. */
|
1108 |
+
if ($this->elementInScope('nobr')) {
|
1109 |
+
$this->emitToken(array(
|
1110 |
+
'name' => 'nobr',
|
1111 |
+
'type' => HTML5_Tokenizer::ENDTAG,
|
1112 |
+
));
|
1113 |
+
$this->reconstructActiveFormattingElements();
|
1114 |
+
}
|
1115 |
+
|
1116 |
+
/* Insert an HTML element for the token. */
|
1117 |
+
$el = $this->insertElement($token);
|
1118 |
+
|
1119 |
+
/* Add that element to the list of active formatting
|
1120 |
+
elements. */
|
1121 |
+
$this->a_formatting[] = $el;
|
1122 |
+
break;
|
1123 |
+
|
1124 |
+
// another diversion
|
1125 |
+
|
1126 |
+
/* A start tag token whose tag name is "button" */
|
1127 |
+
case 'button':
|
1128 |
+
/* If the stack of open elements has a button element in scope,
|
1129 |
+
then this is a parse error; act as if an end tag with the tag
|
1130 |
+
name "button" had been seen, then reprocess the token. (We don't
|
1131 |
+
do that. Unnecessary.) (I hope you're right! -- ezyang) */
|
1132 |
+
if ($this->elementInScope('button')) {
|
1133 |
+
$this->emitToken(array(
|
1134 |
+
'name' => 'button',
|
1135 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1136 |
+
));
|
1137 |
+
}
|
1138 |
+
|
1139 |
+
/* Reconstruct the active formatting elements, if any. */
|
1140 |
+
$this->reconstructActiveFormattingElements();
|
1141 |
+
|
1142 |
+
/* Insert an HTML element for the token. */
|
1143 |
+
$this->insertElement($token);
|
1144 |
+
|
1145 |
+
/* Insert a marker at the end of the list of active
|
1146 |
+
formatting elements. */
|
1147 |
+
$this->a_formatting[] = self::MARKER;
|
1148 |
+
|
1149 |
+
$this->flag_frameset_ok = false;
|
1150 |
+
break;
|
1151 |
+
|
1152 |
+
case 'applet': case 'marquee': case 'object':
|
1153 |
+
/* Reconstruct the active formatting elements, if any. */
|
1154 |
+
$this->reconstructActiveFormattingElements();
|
1155 |
+
|
1156 |
+
/* Insert an HTML element for the token. */
|
1157 |
+
$this->insertElement($token);
|
1158 |
+
|
1159 |
+
/* Insert a marker at the end of the list of active
|
1160 |
+
formatting elements. */
|
1161 |
+
$this->a_formatting[] = self::MARKER;
|
1162 |
+
|
1163 |
+
$this->flag_frameset_ok = false;
|
1164 |
+
break;
|
1165 |
+
|
1166 |
+
// spec diversion
|
1167 |
+
|
1168 |
+
/* A start tag whose tag name is "table" */
|
1169 |
+
case 'table':
|
1170 |
+
/* If the Document is not set to quirks mode, and the
|
1171 |
+
* stack of open elements has a p element in scope, then
|
1172 |
+
* act as if an end tag with the tag name "p" had been
|
1173 |
+
* seen. */
|
1174 |
+
if ($this->quirks_mode !== self::QUIRKS_MODE &&
|
1175 |
+
$this->elementInScope('p')) {
|
1176 |
+
$this->emitToken(array(
|
1177 |
+
'name' => 'p',
|
1178 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1179 |
+
));
|
1180 |
+
}
|
1181 |
+
|
1182 |
+
/* Insert an HTML element for the token. */
|
1183 |
+
$this->insertElement($token);
|
1184 |
+
|
1185 |
+
$this->flag_frameset_ok = false;
|
1186 |
+
|
1187 |
+
/* Change the insertion mode to "in table". */
|
1188 |
+
$this->mode = self::IN_TABLE;
|
1189 |
+
break;
|
1190 |
+
|
1191 |
+
/* A start tag whose tag name is one of: "area", "basefont",
|
1192 |
+
"bgsound", "br", "embed", "img", "param", "spacer", "wbr" */
|
1193 |
+
case 'area': case 'basefont': case 'bgsound': case 'br':
|
1194 |
+
case 'embed': case 'img': case 'input': case 'keygen': case 'spacer':
|
1195 |
+
case 'wbr':
|
1196 |
+
/* Reconstruct the active formatting elements, if any. */
|
1197 |
+
$this->reconstructActiveFormattingElements();
|
1198 |
+
|
1199 |
+
/* Insert an HTML element for the token. */
|
1200 |
+
$this->insertElement($token);
|
1201 |
+
|
1202 |
+
/* Immediately pop the current node off the stack of open elements. */
|
1203 |
+
array_pop($this->stack);
|
1204 |
+
|
1205 |
+
// YYY: Acknowledge the token's self-closing flag, if it is set.
|
1206 |
+
|
1207 |
+
$this->flag_frameset_ok = false;
|
1208 |
+
break;
|
1209 |
+
|
1210 |
+
case 'param': case 'source':
|
1211 |
+
/* Insert an HTML element for the token. */
|
1212 |
+
$this->insertElement($token);
|
1213 |
+
|
1214 |
+
/* Immediately pop the current node off the stack of open elements. */
|
1215 |
+
array_pop($this->stack);
|
1216 |
+
|
1217 |
+
// YYY: Acknowledge the token's self-closing flag, if it is set.
|
1218 |
+
break;
|
1219 |
+
|
1220 |
+
/* A start tag whose tag name is "hr" */
|
1221 |
+
case 'hr':
|
1222 |
+
/* If the stack of open elements has a p element in scope,
|
1223 |
+
then act as if an end tag with the tag name p had been seen. */
|
1224 |
+
if ($this->elementInScope('p')) {
|
1225 |
+
$this->emitToken(array(
|
1226 |
+
'name' => 'p',
|
1227 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1228 |
+
));
|
1229 |
+
}
|
1230 |
+
|
1231 |
+
/* Insert an HTML element for the token. */
|
1232 |
+
$this->insertElement($token);
|
1233 |
+
|
1234 |
+
/* Immediately pop the current node off the stack of open elements. */
|
1235 |
+
array_pop($this->stack);
|
1236 |
+
|
1237 |
+
// YYY: Acknowledge the token's self-closing flag, if it is set.
|
1238 |
+
|
1239 |
+
$this->flag_frameset_ok = false;
|
1240 |
+
break;
|
1241 |
+
|
1242 |
+
/* A start tag whose tag name is "image" */
|
1243 |
+
case 'image':
|
1244 |
+
/* Parse error. Change the token's tag name to "img" and
|
1245 |
+
reprocess it. (Don't ask.) */
|
1246 |
+
$token['name'] = 'img';
|
1247 |
+
$this->emitToken($token);
|
1248 |
+
break;
|
1249 |
+
|
1250 |
+
/* A start tag whose tag name is "isindex" */
|
1251 |
+
case 'isindex':
|
1252 |
+
/* Parse error. */
|
1253 |
+
|
1254 |
+
/* If the form element pointer is not null,
|
1255 |
+
then ignore the token. */
|
1256 |
+
if ($this->form_pointer === null) {
|
1257 |
+
/* Act as if a start tag token with the tag name "form" had
|
1258 |
+
been seen. */
|
1259 |
+
/* If the token has an attribute called "action", set
|
1260 |
+
* the action attribute on the resulting form
|
1261 |
+
* element to the value of the "action" attribute of
|
1262 |
+
* the token. */
|
1263 |
+
$attr = array();
|
1264 |
+
$action = $this->getAttr($token, 'action');
|
1265 |
+
if ($action !== false) {
|
1266 |
+
$attr[] = array('name' => 'action', 'value' => $action);
|
1267 |
+
}
|
1268 |
+
$this->emitToken(array(
|
1269 |
+
'name' => 'form',
|
1270 |
+
'type' => HTML5_Tokenizer::STARTTAG,
|
1271 |
+
'attr' => $attr
|
1272 |
+
));
|
1273 |
+
|
1274 |
+
/* Act as if a start tag token with the tag name "hr" had
|
1275 |
+
been seen. */
|
1276 |
+
$this->emitToken(array(
|
1277 |
+
'name' => 'hr',
|
1278 |
+
'type' => HTML5_Tokenizer::STARTTAG,
|
1279 |
+
'attr' => array()
|
1280 |
+
));
|
1281 |
+
|
1282 |
+
/* Act as if a start tag token with the tag name "label"
|
1283 |
+
had been seen. */
|
1284 |
+
$this->emitToken(array(
|
1285 |
+
'name' => 'label',
|
1286 |
+
'type' => HTML5_Tokenizer::STARTTAG,
|
1287 |
+
'attr' => array()
|
1288 |
+
));
|
1289 |
+
|
1290 |
+
/* Act as if a stream of character tokens had been seen. */
|
1291 |
+
$prompt = $this->getAttr($token, 'prompt');
|
1292 |
+
if ($prompt === false) {
|
1293 |
+
$prompt = 'This is a searchable index. '.
|
1294 |
+
'Insert your search keywords here: ';
|
1295 |
+
}
|
1296 |
+
$this->emitToken(array(
|
1297 |
+
'data' => $prompt,
|
1298 |
+
'type' => HTML5_Tokenizer::CHARACTER,
|
1299 |
+
));
|
1300 |
+
|
1301 |
+
/* Act as if a start tag token with the tag name "input"
|
1302 |
+
had been seen, with all the attributes from the "isindex"
|
1303 |
+
token, except with the "name" attribute set to the value
|
1304 |
+
"isindex" (ignoring any explicit "name" attribute). */
|
1305 |
+
$attr = array();
|
1306 |
+
foreach ($token['attr'] as $keypair) {
|
1307 |
+
if ($keypair['name'] === 'name' || $keypair['name'] === 'action' ||
|
1308 |
+
$keypair['name'] === 'prompt') {
|
1309 |
+
continue;
|
1310 |
+
}
|
1311 |
+
$attr[] = $keypair;
|
1312 |
+
}
|
1313 |
+
$attr[] = array('name' => 'name', 'value' => 'isindex');
|
1314 |
+
|
1315 |
+
$this->emitToken(array(
|
1316 |
+
'name' => 'input',
|
1317 |
+
'type' => HTML5_Tokenizer::STARTTAG,
|
1318 |
+
'attr' => $attr
|
1319 |
+
));
|
1320 |
+
|
1321 |
+
/* Act as if an end tag token with the tag name "label"
|
1322 |
+
had been seen. */
|
1323 |
+
$this->emitToken(array(
|
1324 |
+
'name' => 'label',
|
1325 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1326 |
+
));
|
1327 |
+
|
1328 |
+
/* Act as if a start tag token with the tag name "hr" had
|
1329 |
+
been seen. */
|
1330 |
+
$this->emitToken(array(
|
1331 |
+
'name' => 'hr',
|
1332 |
+
'type' => HTML5_Tokenizer::STARTTAG
|
1333 |
+
));
|
1334 |
+
|
1335 |
+
/* Act as if an end tag token with the tag name "form" had
|
1336 |
+
been seen. */
|
1337 |
+
$this->emitToken(array(
|
1338 |
+
'name' => 'form',
|
1339 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1340 |
+
));
|
1341 |
+
} else {
|
1342 |
+
$this->ignored = true;
|
1343 |
+
}
|
1344 |
+
break;
|
1345 |
+
|
1346 |
+
/* A start tag whose tag name is "textarea" */
|
1347 |
+
case 'textarea':
|
1348 |
+
$this->insertElement($token);
|
1349 |
+
|
1350 |
+
/* If the next token is a U+000A LINE FEED (LF)
|
1351 |
+
* character token, then ignore that token and move on to
|
1352 |
+
* the next one. (Newlines at the start of textarea
|
1353 |
+
* elements are ignored as an authoring convenience.)
|
1354 |
+
* need flag, see also <pre> */
|
1355 |
+
$this->ignore_lf_token = 2;
|
1356 |
+
|
1357 |
+
$this->original_mode = $this->mode;
|
1358 |
+
$this->flag_frameset_ok = false;
|
1359 |
+
$this->mode = self::IN_CDATA_RCDATA;
|
1360 |
+
|
1361 |
+
/* Switch the tokeniser's content model flag to the
|
1362 |
+
RCDATA state. */
|
1363 |
+
$this->content_model = HTML5_Tokenizer::RCDATA;
|
1364 |
+
break;
|
1365 |
+
|
1366 |
+
/* A start tag token whose tag name is "xmp" */
|
1367 |
+
case 'xmp':
|
1368 |
+
/* If the stack of open elements has a p element in
|
1369 |
+
scope, then act as if an end tag with the tag name
|
1370 |
+
"p" has been seen. */
|
1371 |
+
if ($this->elementInScope('p')) {
|
1372 |
+
$this->emitToken(array(
|
1373 |
+
'name' => 'p',
|
1374 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1375 |
+
));
|
1376 |
+
}
|
1377 |
+
|
1378 |
+
/* Reconstruct the active formatting elements, if any. */
|
1379 |
+
$this->reconstructActiveFormattingElements();
|
1380 |
+
|
1381 |
+
$this->flag_frameset_ok = false;
|
1382 |
+
|
1383 |
+
$this->insertCDATAElement($token);
|
1384 |
+
break;
|
1385 |
+
|
1386 |
+
case 'iframe':
|
1387 |
+
$this->flag_frameset_ok = false;
|
1388 |
+
$this->insertCDATAElement($token);
|
1389 |
+
break;
|
1390 |
+
|
1391 |
+
case 'noembed': case 'noscript':
|
1392 |
+
// XSCRIPT: should check scripting flag
|
1393 |
+
$this->insertCDATAElement($token);
|
1394 |
+
break;
|
1395 |
+
|
1396 |
+
/* A start tag whose tag name is "select" */
|
1397 |
+
case 'select':
|
1398 |
+
/* Reconstruct the active formatting elements, if any. */
|
1399 |
+
$this->reconstructActiveFormattingElements();
|
1400 |
+
|
1401 |
+
/* Insert an HTML element for the token. */
|
1402 |
+
$this->insertElement($token);
|
1403 |
+
|
1404 |
+
$this->flag_frameset_ok = false;
|
1405 |
+
|
1406 |
+
/* If the insertion mode is one of in table", "in caption",
|
1407 |
+
* "in column group", "in table body", "in row", or "in
|
1408 |
+
* cell", then switch the insertion mode to "in select in
|
1409 |
+
* table". Otherwise, switch the insertion mode to "in
|
1410 |
+
* select". */
|
1411 |
+
if (
|
1412 |
+
$this->mode === self::IN_TABLE || $this->mode === self::IN_CAPTION ||
|
1413 |
+
$this->mode === self::IN_COLUMN_GROUP || $this->mode ==+self::IN_TABLE_BODY ||
|
1414 |
+
$this->mode === self::IN_ROW || $this->mode === self::IN_CELL
|
1415 |
+
) {
|
1416 |
+
$this->mode = self::IN_SELECT_IN_TABLE;
|
1417 |
+
} else {
|
1418 |
+
$this->mode = self::IN_SELECT;
|
1419 |
+
}
|
1420 |
+
break;
|
1421 |
+
|
1422 |
+
case 'option': case 'optgroup':
|
1423 |
+
if ($this->elementInScope('option')) {
|
1424 |
+
$this->emitToken(array(
|
1425 |
+
'name' => 'option',
|
1426 |
+
'type' => HTML5_Tokenizer::ENDTAG,
|
1427 |
+
));
|
1428 |
+
}
|
1429 |
+
$this->reconstructActiveFormattingElements();
|
1430 |
+
$this->insertElement($token);
|
1431 |
+
break;
|
1432 |
+
|
1433 |
+
case 'rp': case 'rt':
|
1434 |
+
/* If the stack of open elements has a ruby element in scope, then generate
|
1435 |
+
* implied end tags. If the current node is not then a ruby element, this is
|
1436 |
+
* a parse error; pop all the nodes from the current node up to the node
|
1437 |
+
* immediately before the bottommost ruby element on the stack of open elements.
|
1438 |
+
*/
|
1439 |
+
if ($this->elementInScope('ruby')) {
|
1440 |
+
$this->generateImpliedEndTags();
|
1441 |
+
}
|
1442 |
+
$peek = false;
|
1443 |
+
do {
|
1444 |
+
/*if ($peek) {
|
1445 |
+
// parse error
|
1446 |
+
}*/
|
1447 |
+
$peek = array_pop($this->stack);
|
1448 |
+
} while ($peek->tagName !== 'ruby');
|
1449 |
+
$this->stack[] = $peek; // we popped one too many
|
1450 |
+
$this->insertElement($token);
|
1451 |
+
break;
|
1452 |
+
|
1453 |
+
// spec diversion
|
1454 |
+
|
1455 |
+
case 'math':
|
1456 |
+
$this->reconstructActiveFormattingElements();
|
1457 |
+
$token = $this->adjustMathMLAttributes($token);
|
1458 |
+
$token = $this->adjustForeignAttributes($token);
|
1459 |
+
$this->insertForeignElement($token, self::NS_MATHML);
|
1460 |
+
if (isset($token['self-closing'])) {
|
1461 |
+
// XERROR: acknowledge the token's self-closing flag
|
1462 |
+
array_pop($this->stack);
|
1463 |
+
}
|
1464 |
+
if ($this->mode !== self::IN_FOREIGN_CONTENT) {
|
1465 |
+
$this->secondary_mode = $this->mode;
|
1466 |
+
$this->mode = self::IN_FOREIGN_CONTENT;
|
1467 |
+
}
|
1468 |
+
break;
|
1469 |
+
|
1470 |
+
case 'svg':
|
1471 |
+
$this->reconstructActiveFormattingElements();
|
1472 |
+
$token = $this->adjustSVGAttributes($token);
|
1473 |
+
$token = $this->adjustForeignAttributes($token);
|
1474 |
+
$this->insertForeignElement($token, self::NS_SVG);
|
1475 |
+
if (isset($token['self-closing'])) {
|
1476 |
+
// XERROR: acknowledge the token's self-closing flag
|
1477 |
+
array_pop($this->stack);
|
1478 |
+
}
|
1479 |
+
if ($this->mode !== self::IN_FOREIGN_CONTENT) {
|
1480 |
+
$this->secondary_mode = $this->mode;
|
1481 |
+
$this->mode = self::IN_FOREIGN_CONTENT;
|
1482 |
+
}
|
1483 |
+
break;
|
1484 |
+
|
1485 |
+
case 'caption': case 'col': case 'colgroup': case 'frame': case 'head':
|
1486 |
+
case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead': case 'tr':
|
1487 |
+
// parse error
|
1488 |
+
break;
|
1489 |
+
|
1490 |
+
/* A start tag token not covered by the previous entries */
|
1491 |
+
default:
|
1492 |
+
/* Reconstruct the active formatting elements, if any. */
|
1493 |
+
$this->reconstructActiveFormattingElements();
|
1494 |
+
|
1495 |
+
$this->insertElement($token);
|
1496 |
+
/* This element will be a phrasing element. */
|
1497 |
+
break;
|
1498 |
+
}
|
1499 |
+
break;
|
1500 |
+
|
1501 |
+
case HTML5_Tokenizer::ENDTAG:
|
1502 |
+
switch ($token['name']) {
|
1503 |
+
/* An end tag with the tag name "body" */
|
1504 |
+
case 'body':
|
1505 |
+
/* If the stack of open elements does not have a body
|
1506 |
+
* element in scope, this is a parse error; ignore the
|
1507 |
+
* token. */
|
1508 |
+
if (!$this->elementInScope('body')) {
|
1509 |
+
$this->ignored = true;
|
1510 |
+
|
1511 |
+
/* Otherwise, if there is a node in the stack of open
|
1512 |
+
* elements that is not either a dc element, a dd element,
|
1513 |
+
* a ds element, a dt element, an li element, an optgroup
|
1514 |
+
* element, an option element, a p element, an rp element,
|
1515 |
+
* an rt element, a tbody element, a td element, a tfoot
|
1516 |
+
* element, a th element, a thead element, a tr element,
|
1517 |
+
* the body element, or the html element, then this is a
|
1518 |
+
* parse error.
|
1519 |
+
*/
|
1520 |
+
} else {
|
1521 |
+
// XERROR: implement this check for parse error
|
1522 |
+
}
|
1523 |
+
|
1524 |
+
/* Change the insertion mode to "after body". */
|
1525 |
+
$this->mode = self::AFTER_BODY;
|
1526 |
+
break;
|
1527 |
+
|
1528 |
+
/* An end tag with the tag name "html" */
|
1529 |
+
case 'html':
|
1530 |
+
/* Act as if an end tag with tag name "body" had been seen,
|
1531 |
+
then, if that token wasn't ignored, reprocess the current
|
1532 |
+
token. */
|
1533 |
+
$this->emitToken(array(
|
1534 |
+
'name' => 'body',
|
1535 |
+
'type' => HTML5_Tokenizer::ENDTAG
|
1536 |
+
));
|
1537 |
+
|
1538 |
+
if (!$this->ignored) {
|
1539 |
+
$this->emitToken($token);
|
1540 |
+
}
|
1541 |
+
break;
|
1542 |
+
|
1543 |
+
case 'address': case 'article': case 'aside': case 'blockquote':
|
1544 |
+
case 'center': case 'datagrid': case 'details': case 'dir':
|
1545 |
+
case 'div': case 'dl': case 'fieldset': case 'footer':
|
1546 |
+
case 'header': case 'hgroup': case 'listing': case 'menu':
|
1547 |
+
case 'nav': case 'ol': case 'pre': case 'section': case 'ul':
|
1548 |
+
/* If the stack of open elements has an element in scope
|
1549 |
+
with the same tag name as that of the token, then generate
|
1550 |
+
implied end tags. */
|
1551 |
+
if ($this->elementInScope($token['name'])) {
|
1552 |
+
$this->generateImpliedEndTags();
|
1553 |
+
|
1554 |
+
/* Now, if the current node is not an element with
|
1555 |
+
the same tag name as that of the token, then this
|
1556 |
+
is a parse error. */
|
1557 |
+
// XERROR: implement parse error logic
|
1558 |
+
|
1559 |
+
/* If the stack of open elements has an element in
|
1560 |
+
scope with the same tag name as that of the token,
|
1561 |
+
then pop elements from this stack until an element
|
1562 |
+
with that tag name has been popped from the stack. */
|
1563 |
+
do {
|
1564 |
+
$node = array_pop($this->stack);
|
1565 |
+
} while ($node->tagName !== $token['name']);
|
1566 |
+
} else {
|
1567 |
+
// parse error
|
1568 |
+
}
|
1569 |
+
break;
|
1570 |
+
|
1571 |
+
/* An end tag whose tag name is "form" */
|
1572 |
+
case 'form':
|
1573 |
+
/* Let node be the element that the form element pointer is set to. */
|
1574 |
+
$node = $this->form_pointer;
|
1575 |
+
/* Set the form element pointer to null. */
|
1576 |
+
$this->form_pointer = null;
|
1577 |
+
/* If node is null or the stack of open elements does not
|
1578 |
+
* have node in scope, then this is a parse error; ignore the token. */
|
1579 |
+
if ($node === null || !in_array($node, $this->stack)) {
|
1580 |
+
// parse error
|
1581 |
+
$this->ignored = true;
|
1582 |
+
} else {
|
1583 |
+
/* 1. Generate implied end tags. */
|
1584 |
+
$this->generateImpliedEndTags();
|
1585 |
+
/* 2. If the current node is not node, then this is a parse error. */
|
1586 |
+
if (end($this->stack) !== $node) {
|
1587 |
+
// parse error
|
1588 |
+
}
|
1589 |
+
/* 3. Remove node from the stack of open elements. */
|
1590 |
+
array_splice($this->stack, array_search($node, $this->stack, true), 1);
|
1591 |
+
}
|
1592 |
+
|
1593 |
+
break;
|
1594 |
+
|
1595 |
+
/* An end tag whose tag name is "p" */
|
1596 |
+
case 'p':
|
1597 |
+
/* If the stack of open elements has a p element in scope,
|
1598 |
+
then generate implied end tags, except for p elements. */
|
1599 |
+
if ($this->elementInScope('p')) {
|
1600 |
+
/* Generate implied end tags, except for elements with
|
1601 |
+
* the same tag name as the token. */
|
1602 |
+
$this->generateImpliedEndTags(array('p'));
|
1603 |
+
|
1604 |
+
/* If the current node is not a p element, then this is
|
1605 |
+
a parse error. */
|
1606 |
+
// XERROR: implement
|
1607 |
+
|
1608 |
+
/* Pop elements from the stack of open elements until
|
1609 |
+
* an element with the same tag name as the token has
|
1610 |
+
* been popped from the stack. */
|
1611 |
+
do {
|
1612 |
+
$node = array_pop($this->stack);
|
1613 |
+
} while ($node->tagName !== 'p');
|
1614 |
+
|
1615 |
+
} else {
|
1616 |
+
// parse error
|
1617 |
+
$this->emitToken(array(
|
1618 |
+
'name' => 'p',
|
1619 |
+
'type' => HTML5_Tokenizer::STARTTAG,
|
1620 |
+
));
|
1621 |
+
$this->emitToken($token);
|
1622 |
+
}
|
1623 |
+
break;
|
1624 |
+
|
1625 |
+
/* An end tag whose tag name is "li" */
|
1626 |
+
case 'li':
|
1627 |
+
/* If the stack of open elements does not have an element
|
1628 |
+
* in list item scope with the same tag name as that of the
|
1629 |
+
* token, then this is a parse error; ignore the token. */
|
1630 |
+
if ($this->elementInScope($token['name'], self::SCOPE_LISTITEM)) {
|
1631 |
+
/* Generate implied end tags, except for elements with the
|
1632 |
+
* same tag name as the token. */
|
1633 |
+
$this->generateImpliedEndTags(array($token['name']));
|
1634 |
+
/* If the current node is not an element with the same tag
|
1635 |
+
* name as that of the token, then this is a parse error. */
|
1636 |
+
// XERROR: parse error
|
1637 |
+
/* Pop elements from the stack of open elements until an
|
1638 |
+
* element with the same tag name as the token has been
|
1639 |
+
* popped from the stack. */
|
1640 |
+
do {
|
1641 |
+
$node = array_pop($this->stack);
|
1642 |
+
} while ($node->tagName !== $token['name']);
|
1643 |
+
}
|
1644 |
+
/*else {
|
1645 |
+
// XERROR: parse error
|
1646 |
+
}*/
|
1647 |
+
break;
|
1648 |
+
|
1649 |
+
/* An end tag whose tag name is "dc", "dd", "ds", "dt" */
|
1650 |
+
case 'dc': case 'dd': case 'ds': case 'dt':
|
1651 |
+
if ($this->elementInScope($token['name'])) {
|
1652 |
+
$this->generateImpliedEndTags(array($token['name']));
|
1653 |
+
|
1654 |
+
/* If the current node is not an element with the same
|
1655 |
+
tag name as the token, then this is a parse error. */
|
1656 |
+
// XERROR: implement parse error
|
1657 |
+
|
1658 |
+
/* Pop elements from the stack of open elements until
|
1659 |
+
* an element with the same tag name as the token has
|
1660 |
+
* been popped from the stack. */
|
1661 |
+
do {
|
1662 |
+
$node = array_pop($this->stack);
|
1663 |
+
} while ($node->tagName !== $token['name']);
|
1664 |
+
}
|
1665 |
+
/*else {
|
1666 |
+
// XERROR: parse error
|
1667 |
+
}*/
|
1668 |
+
break;
|
1669 |
+
|
1670 |
+
/* An end tag whose tag name is one of: "h1", "h2", "h3", "h4",
|
1671 |
+
"h5", "h6" */
|
1672 |
+
case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
|
1673 |
+
$elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6');
|
1674 |
+
|
1675 |
+
/* If the stack of open elements has in scope an element whose
|
1676 |
+
tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
|
1677 |
+
generate implied end tags. */
|
1678 |
+
if ($this->elementInScope($elements)) {
|
1679 |
+
$this->generateImpliedEndTags();
|
1680 |
+
|
1681 |
+
/* Now, if the current node is not an element with the same
|
1682 |
+
tag name as that of the token, then this is a parse error. */
|
1683 |
+
// XERROR: implement parse error
|
1684 |
+
|
1685 |
+
/* If the stack of open elements has in scope an element
|
1686 |
+
whose tag name is one of "h1", "h2", "h3", "h4", "h5", or
|
1687 |
+
"h6", then pop elements from the stack until an element
|
1688 |
+
with one of those tag names has been popped from the stack. */
|
1689 |
+
do {
|
1690 |
+
$node = array_pop($this->stack);
|
1691 |
+
} while (!in_array($node->tagName, $elements));
|
1692 |
+
}
|
1693 |
+
/*else {
|
1694 |
+
// parse error
|
1695 |
+
}*/
|
1696 |
+
break;
|
1697 |
+
|
1698 |
+
/* An end tag whose tag name is one of: "a", "b", "big", "em",
|
1699 |
+
"font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
|
1700 |
+
case 'a': case 'b': case 'big': case 'code': case 'em': case 'font':
|
1701 |
+
case 'i': case 'nobr': case 's': case 'small': case 'strike':
|
1702 |
+
case 'strong': case 'tt': case 'u':
|
1703 |
+
// XERROR: generally speaking this needs parse error logic
|
1704 |
+
/* 1. Let the formatting element be the last element in
|
1705 |
+
the list of active formatting elements that:
|
1706 |
+
* is between the end of the list and the last scope
|
1707 |
+
marker in the list, if any, or the start of the list
|
1708 |
+
otherwise, and
|
1709 |
+
* has the same tag name as the token.
|
1710 |
+
*/
|
1711 |
+
while (true) {
|
1712 |
+
for ($a = count($this->a_formatting) - 1; $a >= 0; $a--) {
|
1713 |
+
if ($this->a_formatting[$a] === self::MARKER) {
|
1714 |
+
break;
|
1715 |
+
} elseif ($this->a_formatting[$a]->tagName === $token['name']) {
|
1716 |
+
$formatting_element = $this->a_formatting[$a];
|
1717 |
+
$in_stack = in_array($formatting_element, $this->stack, true);
|
1718 |
+
$fe_af_pos = $a;
|
1719 |
+
break;
|
1720 |
+
}
|
1721 |
+
}
|
1722 |
+
|
1723 |
+
/* If there is no such node, or, if that node is
|
1724 |
+
also in the stack of open elements but the element
|
1725 |
+
is not in scope, then this is a parse error. Abort
|
1726 |
+
these steps. The token is ignored. */
|
1727 |
+
if (
|
1728 |
+
!isset($formatting_element) || (
|
1729 |
+
$in_stack &&
|
1730 |
+
!$this->elementInScope($token['name'])
|
1731 |
+
)
|
1732 |
+
) {
|
1733 |
+
$this->ignored = true;
|
1734 |
+
break;
|
1735 |
+
|
1736 |
+
/* Otherwise, if there is such a node, but that node
|
1737 |
+
is not in the stack of open elements, then this is a
|
1738 |
+
parse error; remove the element from the list, and
|
1739 |
+
abort these steps. */
|
1740 |
+
} elseif (isset($formatting_element) && !$in_stack) {
|
1741 |
+
unset($this->a_formatting[$fe_af_pos]);
|
1742 |
+
$this->a_formatting = array_merge($this->a_formatting);
|
1743 |
+
break;
|
1744 |
+
}
|
1745 |
+
|
1746 |
+
/* Otherwise, there is a formatting element and that
|
1747 |
+
* element is in the stack and is in scope. If the
|
1748 |
+
* element is not the current node, this is a parse
|
1749 |
+
* error. In any case, proceed with the algorithm as
|
1750 |
+
* written in the following steps. */
|
1751 |
+
// XERROR: implement me
|
1752 |
+
|
1753 |
+
/* 2. Let the furthest block be the topmost node in the
|
1754 |
+
stack of open elements that is lower in the stack
|
1755 |
+
than the formatting element, and is not an element in
|
1756 |
+
the phrasing or formatting categories. There might
|
1757 |
+
not be one. */
|
1758 |
+
$fe_s_pos = array_search($formatting_element, $this->stack, true);
|
1759 |
+
$length = count($this->stack);
|
1760 |
+
|
1761 |
+
for ($s = $fe_s_pos + 1; $s < $length; $s++) {
|
1762 |
+
$category = $this->getElementCategory($this->stack[$s]);
|
1763 |
+
|
1764 |
+
if ($category !== self::PHRASING && $category !== self::FORMATTING) {
|
1765 |
+
$furthest_block = $this->stack[$s];
|
1766 |
+
break;
|
1767 |
+
}
|
1768 |
+
}
|
1769 |
+
|
1770 |
+
/* 3. If there is no furthest block, then the UA must
|
1771 |
+
skip the subsequent steps and instead just pop all
|
1772 |
+
the nodes from the bottom of the stack of open
|
1773 |
+
elements, from the current node up to the formatting
|
1774 |
+
element, and remove the formatting element from the
|
1775 |
+
list of active formatting elements. */
|
1776 |
+
if (!isset($furthest_block)) {
|
1777 |
+
for ($n = $length - 1; $n >= $fe_s_pos; $n--) {
|
1778 |
+
array_pop($this->stack);
|
1779 |
+
}
|
1780 |
+
|
1781 |
+
unset($this->a_formatting[$fe_af_pos]);
|
1782 |
+
$this->a_formatting = array_merge($this->a_formatting);
|
1783 |
+
break;
|
1784 |
+
}
|
1785 |
+
|
1786 |
+
/* 4. Let the common ancestor be the element
|
1787 |
+
immediately above the formatting element in the stack
|
1788 |
+
of open elements. */
|
1789 |
+
$common_ancestor = $this->stack[$fe_s_pos - 1];
|
1790 |
+
|
1791 |
+
/* 5. Let a bookmark note the position of the
|
1792 |
+
formatting element in the list of active formatting
|
1793 |
+
elements relative to the elements on either side
|
1794 |
+
of it in the list. */
|
1795 |
+
$bookmark = $fe_af_pos;
|
1796 |
+
|
1797 |
+
/* 6. Let node and last node be the furthest block.
|
1798 |
+
Follow these steps: */
|
1799 |
+
$node = $furthest_block;
|
1800 |
+
$last_node = $furthest_block;
|
1801 |
+
|
1802 |
+
while (true) {
|
1803 |
+
for ($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) {
|
1804 |
+
/* 6.1 Let node be the element immediately
|
1805 |
+
prior to node in the stack of open elements. */
|
1806 |
+
$node = $this->stack[$n];
|
1807 |
+
|
1808 |
+
/* 6.2 If node is not in the list of active
|
1809 |
+
formatting elements, then remove node from
|
1810 |
+
the stack of open elements and then go back
|
1811 |
+
to step 1. */
|
1812 |
+
if (!in_array($node, $this->a_formatting, true)) {
|
1813 |
+
array_splice($this->stack, $n, 1);
|
1814 |
+
} else {
|
1815 |
+
break;
|
1816 |
+
}
|
1817 |
+
}
|
1818 |
+
|
1819 |
+
/* 6.3 Otherwise, if node is the formatting
|
1820 |
+
element, then go to the next step in the overall
|
1821 |
+
algorithm. */
|
1822 |
+
if ($node === $formatting_element) {
|
1823 |
+
break;
|
1824 |
+
|
1825 |
+
/* 6.4 Otherwise, if last node is the furthest
|
1826 |
+
block, then move the aforementioned bookmark to
|
1827 |
+
be immediately after the node in the list of
|
1828 |
+
active formatting elements. */
|
1829 |
+
} elseif ($last_node === $furthest_block) {
|
1830 |
+
$bookmark = array_search($node, $this->a_formatting, true) + 1;
|
1831 |
+
}
|
1832 |
+
|
1833 |
+
/* 6.5 Create an element for the token for which
|
1834 |
+
* the element node was created, replace the entry
|
1835 |
+
* for node in the list of active formatting
|
1836 |
+
* elements with an entry for the new element,
|
1837 |
+
* replace the entry for node in the stack of open
|
1838 |
+
* elements with an entry for the new element, and
|
1839 |
+
* let node be the new element. */
|
1840 |
+
// we don't know what the token is anymore
|
1841 |
+
// XDOM
|
1842 |
+
$clone = $node->cloneNode();
|
1843 |
+
$a_pos = array_search($node, $this->a_formatting, true);
|
1844 |
+
$s_pos = array_search($node, $this->stack, true);
|
1845 |
+
$this->a_formatting[$a_pos] = $clone;
|
1846 |
+
$this->stack[$s_pos] = $clone;
|
1847 |
+
$node = $clone;
|
1848 |
+
|
1849 |
+
/* 6.6 Insert last node into node, first removing
|
1850 |
+
it from its previous parent node if any. */
|
1851 |
+
// XDOM
|
1852 |
+
if ($last_node->parentNode !== null) {
|
1853 |
+
$last_node->parentNode->removeChild($last_node);
|
1854 |
+
}
|
1855 |
+
|
1856 |
+
// XDOM
|
1857 |
+
$node->appendChild($last_node);
|
1858 |
+
|
1859 |
+
/* 6.7 Let last node be node. */
|
1860 |
+
$last_node = $node;
|
1861 |
+
|
1862 |
+
/* 6.8 Return to step 1 of this inner set of steps. */
|
1863 |
+
}
|
1864 |
+
|
1865 |
+
/* 7. If the common ancestor node is a table, tbody,
|
1866 |
+
* tfoot, thead, or tr element, then, foster parent
|
1867 |
+
* whatever last node ended up being in the previous
|
1868 |
+
* step, first removing it from its previous parent
|
1869 |
+
* node if any. */
|
1870 |
+
// XDOM
|
1871 |
+
if ($last_node->parentNode) { // common step
|
1872 |
+
$last_node->parentNode->removeChild($last_node);
|
1873 |
+
}
|
1874 |
+
if (in_array($common_ancestor->tagName, array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
|
1875 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|