Version Description
March 23, 2015 =
Added: Send invoice to your personal cloud storage with emailitin.com
Added: Option to change the date format
Added: Option to change the invoice number format
Added: Prefix and suffix option for the invoice number
Added: Option to determine the number of zero digits for the invoice number
Added: Option to reset invoice number on first of january
Added: Option to change the color of the template
Improved: Template
Improved: Sequential invoice numbers
Improved: Input fields allows HTML tags for text markup
Improved: Server-side validation on the options
Fixed: Invoices saved into public upload folder
Download this release
Release Info
Developer | baaaaas |
Plugin | WooCommerce PDF Invoices |
Version | 2.0.0 |
Comparing to | |
See all releases |
Code changes from version 1.1.2 to 2.0.0
- admin/classes/woocommerce-pdf-invoices.php +337 -0
- admin/classes/wpi-general-settings.php +222 -0
- admin/classes/wpi-settings.php +99 -0
- admin/classes/wpi-template-settings.php +658 -0
- admin/views/invoice-sample.php +238 -0
- assets/css/admin-styles.css +0 -43
- assets/css/admin.css +59 -0
- assets/img/delete-icon.png +0 -0
- assets/img/invoice.png +0 -0
- assets/img/my-company-logo-blue.png +0 -0
- assets/img/my-company-logo.png +0 -0
- assets/js/admin.js +20 -0
- assets/screenshot-1.png +0 -0
- assets/screenshot-2.png +0 -0
- assets/screenshot-3.png +0 -0
- assets/screenshot-4.png +0 -0
- assets/screenshot-5.png +0 -0
- assets/screenshot-6.png +0 -0
- bootstrap.php +40 -0
- includes/class-admin.php +0 -64
- includes/class-invoice.php +0 -317
- includes/classes/wpi-invoice.php +427 -0
- includes/plugin.php +0 -47
- includes/views/settings-page.php +0 -153
- includes/views/templates/invoice-flat.php +281 -0
- includes/views/templates/invoice-micro.php +256 -0
- index.php +0 -28
- lang/be-woocommerce-pdf-invoices-nl_NL.mo +0 -0
- lang/be-woocommerce-pdf-invoices-nl_NL.po +365 -0
- lang/woocommerce-pdf-invoices.mo +0 -0
- lang/woocommerce-pdf-invoices.po +0 -101
- lib/mpdf/classes/barcode.php +1972 -0
- lib/mpdf/classes/bmp.php +248 -0
- {mpdf → lib/mpdf}/classes/cssmgr.php +410 -255
- lib/mpdf/classes/desktop.ini +4 -0
- {mpdf → lib/mpdf}/classes/directw.php +36 -32
- lib/mpdf/classes/gif.php +700 -0
- {mpdf → lib/mpdf}/classes/grad.php +10 -9
- lib/mpdf/classes/indic.php +1714 -0
- lib/mpdf/classes/meter.php +282 -0
- lib/mpdf/classes/mpdfform.php +1550 -0
- lib/mpdf/classes/myanmar.php +481 -0
- lib/mpdf/classes/otl.php +5719 -0
- lib/mpdf/classes/otl_dump.php +3897 -0
- lib/mpdf/classes/sea.php +349 -0
- lib/mpdf/classes/svg.php +3441 -0
- lib/mpdf/classes/tocontents.php +462 -0
admin/classes/woocommerce-pdf-invoices.php
ADDED
@@ -0,0 +1,337 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit; // Exit if accessed directly
|
4 |
+
}
|
5 |
+
|
6 |
+
if ( ! class_exists( 'BE_WooCommerce_PDF_Invoices' ) ) {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Implements main function for attaching invoice to email and show invoice buttons.
|
10 |
+
*/
|
11 |
+
class BE_WooCommerce_PDF_Invoices {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* All general user settings
|
15 |
+
* @var array
|
16 |
+
*/
|
17 |
+
public $general_settings = array();
|
18 |
+
|
19 |
+
/**
|
20 |
+
* All template user settings
|
21 |
+
* @var array
|
22 |
+
*/
|
23 |
+
public $template_settings = array();
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Constant options key
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $options_key = 'wpi-invoices';
|
30 |
+
|
31 |
+
/**
|
32 |
+
* All the settings tabs for the settings page.
|
33 |
+
* @var array
|
34 |
+
*/
|
35 |
+
private $settings_tabs = array(
|
36 |
+
'general_settings' => 'General',
|
37 |
+
'template_settings' => 'Template'
|
38 |
+
);
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Default textdomain.
|
42 |
+
* @var string
|
43 |
+
*/
|
44 |
+
private $textdomain = 'be-woocommerce-pdf-invoices';
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Initialize plugin and register actions and filters.
|
48 |
+
*
|
49 |
+
* @param $general_settings
|
50 |
+
* @param $template_settings
|
51 |
+
*/
|
52 |
+
public function __construct($general_settings, $template_settings) {
|
53 |
+
|
54 |
+
$this->general_settings = $general_settings;
|
55 |
+
|
56 |
+
$this->template_settings = $template_settings;
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Plugin actions to create, view or delete invoice.
|
60 |
+
*/
|
61 |
+
add_action( 'init', array( &$this, 'init_plugin_actions' ) );
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Initialize all the ajax calls.
|
65 |
+
*/
|
66 |
+
//add_action( 'init', array( &$this, 'init_ajax_calls' ) );
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Set textdomain.
|
70 |
+
*/
|
71 |
+
add_action( 'init', array( &$this, 'init_load_textdomain' ) );
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Delete invoices from tmp folder.
|
75 |
+
*/
|
76 |
+
//add_action( 'admin_init', array( &$this, 'delete_pdf_invoices' ) );
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Adds submenu to WooCommerce menu.
|
80 |
+
*/
|
81 |
+
add_action( 'admin_menu', array( &$this, 'add_woocommerce_submenu_page' ) );
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Enqueue admin scripts
|
85 |
+
*/
|
86 |
+
add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueue_scripts' ) );
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Adds the Email It In email as an extra recipient
|
90 |
+
*/
|
91 |
+
add_filter( 'woocommerce_email_headers', array( &$this, 'add_recipient_to_email_headers' ), 10, 2 );
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Attach invoice to a specific WooCommerce email
|
95 |
+
*/
|
96 |
+
add_filter( 'woocommerce_email_attachments', array( &$this, 'attach_invoice_to_email' ), 99, 3 );
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Adds some actions to the all orders page.
|
100 |
+
*/
|
101 |
+
add_action( 'woocommerce_admin_order_actions_end', array( &$this, 'woocommerce_order_page_action_view_invoice' ) );
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Adds a meta box to the order details page.
|
105 |
+
*/
|
106 |
+
add_action( 'add_meta_boxes', array( &$this, 'add_meta_box_to_order_page' ) );
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Callback to sniff for specific plugin actions to view, create or delete invoice.
|
111 |
+
*/
|
112 |
+
public function init_plugin_actions() {
|
113 |
+
if( isset( $_GET['wpi_action'] ) && isset( $_GET['post'] ) && is_numeric( $_GET['post'] ) && isset( $_GET['nonce'] ) ) {
|
114 |
+
$action = $_GET['wpi_action'];
|
115 |
+
$order_id = $_GET['post'];
|
116 |
+
$nonce = $_REQUEST["nonce"];
|
117 |
+
|
118 |
+
if (!wp_verify_nonce($nonce, $action)) {
|
119 |
+
die( 'Invalid request' );
|
120 |
+
} else if( empty($order_id) ) {
|
121 |
+
die( 'Invalid order ID');
|
122 |
+
} else {
|
123 |
+
$invoice = new WPI_Invoice(new WC_Order($order_id), $this->textdomain);
|
124 |
+
switch( $_GET['wpi_action'] ) {
|
125 |
+
case "view":
|
126 |
+
$invoice->view_invoice( true );
|
127 |
+
break;
|
128 |
+
case "cancel":
|
129 |
+
$invoice->delete();
|
130 |
+
break;
|
131 |
+
case "create":
|
132 |
+
$invoice->generate("F");
|
133 |
+
break;
|
134 |
+
}
|
135 |
+
}
|
136 |
+
}
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* No need for AJAX calls right now.
|
141 |
+
*/
|
142 |
+
/*public function init_ajax_calls() {
|
143 |
+
add_action( 'wp_ajax_wpi_show_invoice', array( &$this, 'wpi_show_invoice' ) );
|
144 |
+
add_action( 'wp_ajax_nopriv_wpi_show_invoice', array( &$this, 'wpi_show_invoice' ) );
|
145 |
+
}*/
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Loads the textdomain and localizes the plugin options tabs.
|
149 |
+
*/
|
150 |
+
public function init_load_textdomain() {
|
151 |
+
load_plugin_textdomain( $this->textdomain, false, WPI_LANG_DIR );
|
152 |
+
$this->settings_tabs['general_settings'] = __( 'General', $this->textdomain );
|
153 |
+
$this->settings_tabs['template_settings'] = __( 'Template', $this->textdomain );
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Delete pdf invoices from the tmp folder.
|
158 |
+
*/
|
159 |
+
/*public function delete_pdf_invoices() {
|
160 |
+
array_map('unlink', glob( WPI_TMP_DIR . "*.pdf"));
|
161 |
+
}*/
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Adds submenu to WooCommerce menu.
|
165 |
+
*/
|
166 |
+
public function add_woocommerce_submenu_page() {
|
167 |
+
add_submenu_page( 'woocommerce', __( 'Invoices', $this->textdomain ), __( 'Invoices', $this->textdomain ), 'manage_options', $this->options_key, array( &$this, 'options_page' ) );
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Admin scripts
|
172 |
+
*/
|
173 |
+
public function admin_enqueue_scripts() {
|
174 |
+
wp_enqueue_script( 'admin_settings_script', WPI_URL . '/assets/js/admin.js' );
|
175 |
+
wp_register_style( 'admin_settings_css', WPI_URL . '/assets/css/admin.css', false, '1.0.0' );
|
176 |
+
wp_enqueue_style( 'admin_settings_css' );
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Callback function for adding plugin options tabs.
|
181 |
+
*/
|
182 |
+
private function plugin_options_tabs() {
|
183 |
+
$current_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : 'general_settings';
|
184 |
+
|
185 |
+
screen_icon();
|
186 |
+
echo '<h2 class="nav-tab-wrapper">';
|
187 |
+
foreach ( $this->settings_tabs as $tab_key => $tab_caption ) {
|
188 |
+
$active = $current_tab == $tab_key ? 'nav-tab-active' : '';
|
189 |
+
echo '<a class="nav-tab ' . $active . '" href="?page=' . 'wpi-invoices' . '&tab=' . $tab_key . '">' . $tab_caption . '</a>';
|
190 |
+
}
|
191 |
+
echo '</h2>';
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* The options page..
|
196 |
+
*/
|
197 |
+
public function options_page() {
|
198 |
+
$tab = isset( $_GET['tab'] ) ? $_GET['tab'] : 'general_settings';
|
199 |
+
?>
|
200 |
+
<div class="wrap">
|
201 |
+
<?php $this->plugin_options_tabs(); ?>
|
202 |
+
<form class="be_woocommerce_pdf_invoices_settings_form" method="post" action="options.php" enctype="multipart/form-data">
|
203 |
+
<?php wp_nonce_field( 'update-options' ); ?>
|
204 |
+
<?php settings_fields( $tab ); ?>
|
205 |
+
<?php do_settings_sections( $tab ); ?>
|
206 |
+
<?php submit_button(); ?>
|
207 |
+
</form>
|
208 |
+
</div>
|
209 |
+
<?php
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Adds the Email It In email as an extra recipient
|
214 |
+
*
|
215 |
+
* @param $headers
|
216 |
+
* @param $status
|
217 |
+
* @return string
|
218 |
+
*/
|
219 |
+
function add_recipient_to_email_headers($headers, $status) {
|
220 |
+
if( $status == $this->general_settings->settings['email_type'] ) {
|
221 |
+
if( $this->general_settings->settings['email_it_in']
|
222 |
+
&& $this->general_settings->settings['email_it_in_account'] != "" ) {
|
223 |
+
$email_it_in_account = $this->general_settings->settings['email_it_in_account'];
|
224 |
+
$headers .= 'BCC: <' . $email_it_in_account . '>' . "\r\n";
|
225 |
+
}
|
226 |
+
}
|
227 |
+
return $headers;
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Attaches invoice to a specific WooCommerce email. Invoice will only be generated when it does not exists already.
|
232 |
+
* @param $attachments
|
233 |
+
* @param $status
|
234 |
+
* @param $order
|
235 |
+
* @return array
|
236 |
+
*/
|
237 |
+
function attach_invoice_to_email( $attachments, $status, $order ) {
|
238 |
+
if( $status == $this->general_settings->settings['email_type']
|
239 |
+
|| $this->general_settings->settings['new_order'] && $status == "new_order" ) {
|
240 |
+
|
241 |
+
$invoice = new WPI_Invoice($order, $this->textdomain);
|
242 |
+
|
243 |
+
if( $invoice->exists() ) {
|
244 |
+
$path_to_pdf = WPI_TMP_DIR . $invoice->get_formatted_invoice_number() . ".pdf";
|
245 |
+
} else {
|
246 |
+
$path_to_pdf = $invoice->generate("F");
|
247 |
+
}
|
248 |
+
|
249 |
+
$attachments[] = $path_to_pdf;
|
250 |
+
}
|
251 |
+
return $attachments;
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Adds a box to the main column on the Post and Page edit screens.
|
256 |
+
*/
|
257 |
+
function add_meta_box_to_order_page() {
|
258 |
+
add_meta_box(
|
259 |
+
'order_page_create_invoice',
|
260 |
+
__( 'PDF Invoice', $this->textdomain ),
|
261 |
+
array( &$this, 'woocommerce_order_details_page_meta_box_create_invoice' ),
|
262 |
+
'shop_order',
|
263 |
+
'side',
|
264 |
+
'high'
|
265 |
+
);
|
266 |
+
}
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Shows the view invoice button on the all orders page.
|
270 |
+
*
|
271 |
+
* @param $order
|
272 |
+
*/
|
273 |
+
public function woocommerce_order_page_action_view_invoice( $order ) {
|
274 |
+
$invoice = new WPI_Invoice(new WC_Order($order->id), $this->textdomain);
|
275 |
+
if( $invoice->exists() ) {
|
276 |
+
$this->show_invoice_button('View invoice', $order->id, 'view', '', ['class="button tips wpi-admin-order-create-invoice-btn"'] );
|
277 |
+
}
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* Shows invoice number info on the order details page.
|
282 |
+
* @param $date
|
283 |
+
* @param $number
|
284 |
+
*/
|
285 |
+
private function show_invoice_number_info($date, $number) {
|
286 |
+
echo '<table class="invoice-info" width="100%">
|
287 |
+
<tr>
|
288 |
+
<td>Invoiced on:</td>
|
289 |
+
<td align="right"><b>' . $date . '</b></td>
|
290 |
+
</tr>
|
291 |
+
<tr>
|
292 |
+
<td>Invoice number:</td>
|
293 |
+
<td align="right"><b>' . $number . '</b></td>
|
294 |
+
</tr>
|
295 |
+
</table>';
|
296 |
+
}
|
297 |
+
|
298 |
+
/**
|
299 |
+
* Show a specific invoice button to for example view, create or delete an invoice.
|
300 |
+
*
|
301 |
+
* @param $title
|
302 |
+
* @param $order_id
|
303 |
+
* @param $wpi_action
|
304 |
+
* @param $btn_title
|
305 |
+
* @param array $arr
|
306 |
+
*/
|
307 |
+
private function show_invoice_button($title, $order_id, $wpi_action, $btn_title, $arr = []) {
|
308 |
+
$title = __( $title, $this->textdomain );
|
309 |
+
$href = admin_url() . 'post.php?post=' . $order_id . '&action=edit&wpi_action=' . $wpi_action . '&nonce=' . wp_create_nonce($wpi_action);
|
310 |
+
$btn_title = __( $btn_title, $this->textdomain );
|
311 |
+
|
312 |
+
$attr = '';
|
313 |
+
foreach($arr as $str) {
|
314 |
+
$attr .= $str . ' ';
|
315 |
+
}
|
316 |
+
|
317 |
+
echo '<a title="' . $title . '" href="' . $href . '" ' . $attr . '><button type="button" class="button grant_access">' . $btn_title . '</button></a>';
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Show all the meta box actions/buttons on the order details page to create, view or cancel/delete an invoice.
|
322 |
+
* @param $post
|
323 |
+
*/
|
324 |
+
public function woocommerce_order_details_page_meta_box_create_invoice( $post ) {
|
325 |
+
$invoice = new WPI_Invoice(new WC_Order($post->ID), $this->textdomain);
|
326 |
+
|
327 |
+
if( $invoice->exists() ) {
|
328 |
+
$this->show_invoice_number_info($invoice->get_formatted_date(), $invoice->get_formatted_invoice_number());
|
329 |
+
$this->show_invoice_button('View invoice', $post->ID, 'view', 'View', ['class="invoice-btn"'] );
|
330 |
+
$this->show_invoice_button('Cancel invoice', $post->ID, 'cancel', 'Cancel', ['class="invoice-btn"', 'onclick="return confirm(\'Are you sure to delete the invoice?\')"'] );
|
331 |
+
} else {
|
332 |
+
$invoice->delete_all_post_meta();
|
333 |
+
$this->show_invoice_button('Create invoice', $post->ID, 'create', 'Create', ['class="invoice-btn"'] );
|
334 |
+
}
|
335 |
+
}
|
336 |
+
}
|
337 |
+
}
|
admin/classes/wpi-general-settings.php
ADDED
@@ -0,0 +1,222 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit; // Exit if accessed directly
|
4 |
+
}
|
5 |
+
|
6 |
+
if ( ! class_exists( 'WPI_General_Settings' ) ) {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Implements general settings.
|
10 |
+
*/
|
11 |
+
class WPI_General_Settings extends WPI_Settings
|
12 |
+
{
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Constant general settings key.
|
16 |
+
* @var string
|
17 |
+
*/
|
18 |
+
private $settings_key = 'general_settings';
|
19 |
+
|
20 |
+
/**
|
21 |
+
*
|
22 |
+
* @var array
|
23 |
+
*/
|
24 |
+
private $defaults = array(
|
25 |
+
'email_type' => 'customer_invoice',
|
26 |
+
'new_order' => 0,
|
27 |
+
'email_it_in' => 0,
|
28 |
+
'email_it_in_account' => ''
|
29 |
+
);
|
30 |
+
|
31 |
+
/**
|
32 |
+
* All settings from db.
|
33 |
+
* @var array
|
34 |
+
*/
|
35 |
+
public $settings = array();
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Initializes the general options.
|
39 |
+
*/
|
40 |
+
public function __construct()
|
41 |
+
{
|
42 |
+
/**
|
43 |
+
* Load all settings into settings array
|
44 |
+
*/
|
45 |
+
add_action('init', array(&$this, 'load_settings'));
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Register settings.
|
49 |
+
*/
|
50 |
+
add_action('admin_init', array(&$this, 'register_settings'));
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Displays all messages registered to 'template_settings'
|
54 |
+
*/
|
55 |
+
add_action('admin_notices', array(&$this, 'show_settings_notices'));
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Load all settings into settings var and merge with defaults.
|
60 |
+
*/
|
61 |
+
public function load_settings()
|
62 |
+
{
|
63 |
+
$this->settings = (array)get_option($this->settings_key); // Get all settings from database
|
64 |
+
$this->settings = array_merge($this->defaults, $this->settings); // Merge defaults with settings
|
65 |
+
update_option($this->settings_key, $this->settings);
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Register all settings fields etc.
|
70 |
+
*/
|
71 |
+
public function register_settings()
|
72 |
+
{
|
73 |
+
register_setting($this->settings_key, $this->settings_key, array(&$this, 'validate'));
|
74 |
+
add_settings_section('section_general', __('General Settings', $this->textdomain), '', $this->settings_key);
|
75 |
+
add_settings_field('email_type_option', __('Attach to Email', $this->textdomain), array(&$this, 'email_type_option'), $this->settings_key, 'section_general',
|
76 |
+
array(
|
77 |
+
array(
|
78 |
+
'id' => 'customer_processing_order',
|
79 |
+
'name' => __('Processing order', $this->textdomain)
|
80 |
+
),
|
81 |
+
array(
|
82 |
+
'id' => 'customer_completed_order',
|
83 |
+
'name' => __('Completed order', $this->textdomain)
|
84 |
+
),
|
85 |
+
array(
|
86 |
+
'id' => 'customer_invoice',
|
87 |
+
'name' => __('Customer invoice', $this->textdomain)
|
88 |
+
)
|
89 |
+
)
|
90 |
+
);
|
91 |
+
add_settings_field('new_order', __('Attach to New order Email', $this->textdomain), array(&$this, 'new_order_option'), $this->settings_key, 'section_general');
|
92 |
+
add_settings_field('email_it_in', __('Automatically send invoice to Google Drive, Egnyte, Dropbox or OneDrive', $this->textdomain), array(&$this, 'email_it_in_option'), $this->settings_key, 'section_general');
|
93 |
+
add_settings_field('email_it_in_account', __('Email It In account', $this->textdomain), array(&$this, 'email_it_in_account_option'), $this->settings_key, 'section_general');
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Settings notices callback to show the notices.
|
98 |
+
*/
|
99 |
+
public function show_settings_notices()
|
100 |
+
{
|
101 |
+
settings_errors($this->settings_key);
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Callback to determine wich email type should contain the invoice.
|
106 |
+
* @param $args
|
107 |
+
*/
|
108 |
+
public function email_type_option($args)
|
109 |
+
{
|
110 |
+
?>
|
111 |
+
<select id="email-type-option" name="<?php echo $this->settings_key; ?>[email_type]">
|
112 |
+
<!--<option selected hidden>-- Select --</option>-->
|
113 |
+
<?php
|
114 |
+
foreach ($args as $email) {
|
115 |
+
?>
|
116 |
+
<option
|
117 |
+
value="<?php echo $email['id']; ?>" <?php selected($this->settings['email_type'], $email['id']); ?>><?php echo $email['name']; ?></option>
|
118 |
+
<?php
|
119 |
+
}
|
120 |
+
?>
|
121 |
+
</select>
|
122 |
+
<?php
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Callback with checkbox to add the invoice to the new order email type.
|
127 |
+
*/
|
128 |
+
public function new_order_option()
|
129 |
+
{
|
130 |
+
?>
|
131 |
+
<input type="checkbox" name="<?php echo $this->settings_key; ?>[new_order]"
|
132 |
+
value="1" <?php checked($this->settings['new_order']); ?>/>
|
133 |
+
<div class="notes"><?php _e('For bookkeeping purposes.', $this->textdomain); ?></div>
|
134 |
+
<?php
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Enable or disable the Email It In option.
|
139 |
+
*/
|
140 |
+
public function email_it_in_option()
|
141 |
+
{
|
142 |
+
?>
|
143 |
+
<input type="checkbox" name="<?php echo $this->settings_key; ?>[email_it_in]"
|
144 |
+
value="1" <?php checked($this->settings['email_it_in']); ?>/>
|
145 |
+
<div
|
146 |
+
class="notes"><?php printf(__('Signup at %s and enter your account below.', $this->textdomain), '<a href="https://emailitin.com">emailitin.com</a>'); ?></div>
|
147 |
+
<?php
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Email It In account for sending the invoice to.
|
152 |
+
*/
|
153 |
+
public function email_it_in_account_option()
|
154 |
+
{
|
155 |
+
?>
|
156 |
+
<input type="text" name="<?php echo $this->settings_key; ?>[email_it_in_account]"
|
157 |
+
value="<?php echo $this->settings['email_it_in_account']; ?>"/>
|
158 |
+
<div class="notes">
|
159 |
+
<?php printf(__('Enter your %s account.', $this->textdomain), 'Email It In'); ?>
|
160 |
+
</div>
|
161 |
+
<?php
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* Validate all the general options.
|
166 |
+
* Later will be refactored to individual validation callabacks.
|
167 |
+
*
|
168 |
+
* @param $input
|
169 |
+
* @return array
|
170 |
+
*/
|
171 |
+
public function validate($input)
|
172 |
+
{
|
173 |
+
$output = array();
|
174 |
+
|
175 |
+
// Validate email type
|
176 |
+
if ($this->is_valid_str($input['email_type'])) {
|
177 |
+
$output['email_type'] = $input['email_type'];
|
178 |
+
} else {
|
179 |
+
add_settings_error(
|
180 |
+
esc_attr($this->settings_key),
|
181 |
+
'invalid-email-type',
|
182 |
+
__('Invalid type of Email.', $this->textdomain)
|
183 |
+
);
|
184 |
+
}
|
185 |
+
|
186 |
+
// Validate new order email
|
187 |
+
if ($this->is_valid_int($input['new_order'])) {
|
188 |
+
$output['new_order'] = $input['new_order'];
|
189 |
+
} else {
|
190 |
+
add_settings_error(
|
191 |
+
esc_attr($this->settings_key),
|
192 |
+
'invalid-new-order-email-value',
|
193 |
+
__('Please don\'t try to change the values.', $this->textdomain)
|
194 |
+
);
|
195 |
+
}
|
196 |
+
|
197 |
+
// Validate new order email
|
198 |
+
if ($this->validate_checkbox($input['email_it_in'])) {
|
199 |
+
$output['email_it_in'] = $input['email_it_in'];
|
200 |
+
} else {
|
201 |
+
add_settings_error(
|
202 |
+
esc_attr($this->settings_key),
|
203 |
+
'invalid-email-it-in-value',
|
204 |
+
__('Please don\'t try to change the values.', $this->textdomain)
|
205 |
+
);
|
206 |
+
}
|
207 |
+
|
208 |
+
// Validate Email
|
209 |
+
if (is_email(sanitize_email($input['email_it_in_account']))) {
|
210 |
+
$output['email_it_in_account'] = $input['email_it_in_account'];
|
211 |
+
} else {
|
212 |
+
add_settings_error(
|
213 |
+
esc_attr($this->settings_key),
|
214 |
+
'invalid-email',
|
215 |
+
__('Invalid Email address.', $this->textdomain)
|
216 |
+
);
|
217 |
+
}
|
218 |
+
|
219 |
+
return $output;
|
220 |
+
}
|
221 |
+
}
|
222 |
+
}
|
admin/classes/wpi-settings.php
ADDED
@@ -0,0 +1,99 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit; // Exit if accessed directly
|
4 |
+
}
|
5 |
+
|
6 |
+
if ( ! class_exists( 'WPI_Settings' ) ) {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Abstract class with validation functions to validate all the template and general settings.
|
10 |
+
* Class WPI_Settings
|
11 |
+
*/
|
12 |
+
abstract class WPI_Settings {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* The textdomain
|
16 |
+
* @var string
|
17 |
+
*/
|
18 |
+
public $textdomain = 'be-woocommerce-pdf-invoices';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* For <textarea>.
|
22 |
+
* @var array
|
23 |
+
*/
|
24 |
+
private $allowed_tags = ['<b>', '<i>', '<br>', '<br/>'];
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Validates an email.
|
28 |
+
* @param $email
|
29 |
+
* @return bool
|
30 |
+
*/
|
31 |
+
protected function validate_email($email) {
|
32 |
+
return is_email(sanitize_email($email)) ? true : false;
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Validates a string.
|
37 |
+
* @param $str
|
38 |
+
* @return bool
|
39 |
+
*/
|
40 |
+
protected function is_valid_str($str) {
|
41 |
+
return is_string(sanitize_text_field($str));
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Validates an integer.
|
46 |
+
* @param $int
|
47 |
+
* @return bool
|
48 |
+
*/
|
49 |
+
protected function is_valid_int($int) {
|
50 |
+
return intval($int) && absint($int);
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Validates a textarea.
|
55 |
+
* @param $str
|
56 |
+
* @return bool
|
57 |
+
*/
|
58 |
+
protected function validate_textarea($str) {
|
59 |
+
$str = preg_replace("/<([a-z][a-z0-9]*)[^>]*?(\/?)>/i", '<$1$2>', $str); // Removes the attributes in the HTML tags
|
60 |
+
return is_string(strip_tags($str, '<b><i><br><br/>'));
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Validates a checkbox
|
65 |
+
* @param $int
|
66 |
+
* @return bool
|
67 |
+
*/
|
68 |
+
protected function validate_checkbox($int) {
|
69 |
+
return $int == 1 || $int == 0;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Check for a valid hex color string like '#c1c2b4'
|
74 |
+
* @param $hex
|
75 |
+
*/
|
76 |
+
protected function is_valid_hex_color($hex)
|
77 |
+
{
|
78 |
+
$valid = false;
|
79 |
+
if (preg_match('/^#[a-f0-9]{6}$/i', $hex)) {
|
80 |
+
return true;
|
81 |
+
} else if (preg_match('/^[a-f0-9]{6}$/i', $hex)) { // Check for a hex color string without hash like 'c1c2b4'
|
82 |
+
return '#' . $hex;
|
83 |
+
}
|
84 |
+
return false;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Gets all the tags that are allowed to use for the textarea's.
|
89 |
+
* @return string|void
|
90 |
+
*/
|
91 |
+
protected function get_allowed_tags_str() {
|
92 |
+
( count( $this->allowed_tags ) > 0 ) ? $str = __('Allowed tags: ', $this->textdomain) : $str = '';
|
93 |
+
foreach ($this->allowed_tags as $i => $tag) {
|
94 |
+
($i == count($this->allowed_tags) - 1) ? $str .= sprintf('%s.', htmlspecialchars($tag)) : $str .= sprintf('%s', htmlspecialchars($tag));
|
95 |
+
}
|
96 |
+
return $str;
|
97 |
+
}
|
98 |
+
}
|
99 |
+
}
|
admin/classes/wpi-template-settings.php
ADDED
@@ -0,0 +1,658 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit; // Exit if accessed directly
|
4 |
+
}
|
5 |
+
|
6 |
+
if ( ! class_exists( 'WPI_Template_Settings' ) ) {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Implements the template settings.
|
10 |
+
*/
|
11 |
+
class WPI_Template_Settings extends WPI_Settings {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Constant template settings key
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
+
private $settings_key = 'template_settings';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Default template settings.
|
21 |
+
* @var array
|
22 |
+
*/
|
23 |
+
private $defaults = array(
|
24 |
+
'template_id' => 1,
|
25 |
+
'color_theme' => '#11B0E7',
|
26 |
+
'company_name' => '',
|
27 |
+
'company_logo' => '',
|
28 |
+
'intro_text' => '',
|
29 |
+
'company_address' => '',
|
30 |
+
'company_details' => '',
|
31 |
+
'terms' => '',
|
32 |
+
'show_discount' => 0,
|
33 |
+
'show_subtotal' => 0,
|
34 |
+
'show_tax' => 0,
|
35 |
+
'show_shipping' => 0,
|
36 |
+
'show_customer_notes' => 0,
|
37 |
+
'show_sku' => 0,
|
38 |
+
'next_invoice_number' => 1,
|
39 |
+
'invoice_number_digits' => 3,
|
40 |
+
'invoice_prefix' => '',
|
41 |
+
'invoice_suffix' => '',
|
42 |
+
'invoice_format' => '[prefix]-[number]-[suffix]',
|
43 |
+
'reset_invoice_number' => 0,
|
44 |
+
'invoice_date_format' => 'F jS Y',
|
45 |
+
'last_invoiced_year' => '',
|
46 |
+
'last_invoice_number' => 1
|
47 |
+
);
|
48 |
+
|
49 |
+
/**
|
50 |
+
* All the template settings.
|
51 |
+
* @var array
|
52 |
+
*/
|
53 |
+
public $settings = array();
|
54 |
+
|
55 |
+
/**
|
56 |
+
* All the different templates.
|
57 |
+
* @var array
|
58 |
+
*/
|
59 |
+
private $templates = array(
|
60 |
+
array(
|
61 |
+
'id' => 1,
|
62 |
+
'name' => 'Micro',
|
63 |
+
'filename' => 'invoice-micro.php'
|
64 |
+
)
|
65 |
+
);
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Initializes the template settings.
|
69 |
+
*/
|
70 |
+
public function __construct() {
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Loads all the template settings.
|
74 |
+
*/
|
75 |
+
add_action('init', array(&$this, 'load_settings'));
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Register all template settings.
|
79 |
+
*/
|
80 |
+
add_action('admin_init', array($this, 'register_settings'));
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Displays all messages registered to 'template_settings'
|
84 |
+
*/
|
85 |
+
add_action('admin_notices', array(&$this, 'show_settings_notices'));
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Load all settings into settings var and merge with defaults.
|
90 |
+
*/
|
91 |
+
public function load_settings() {
|
92 |
+
$this->settings = (array)get_option($this->settings_key);
|
93 |
+
$this->settings = array_merge($this->defaults, $this->settings);
|
94 |
+
|
95 |
+
if ($this->settings['template_id'] != "") {
|
96 |
+
$this->settings['template_filename'] = $this->get_template($this->settings['template_id'])['filename'];
|
97 |
+
}
|
98 |
+
|
99 |
+
update_option($this->settings_key, $this->settings);
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Register all settings fields.
|
104 |
+
*/
|
105 |
+
public function register_settings()
|
106 |
+
{
|
107 |
+
register_setting($this->settings_key, $this->settings_key, array(&$this, 'validate'));
|
108 |
+
add_settings_section('section_template', __('Template Settings', $this->textdomain), '', $this->settings_key);
|
109 |
+
add_settings_field('template_id', __('Template', $this->textdomain), array(&$this, 'template_id_option'), $this->settings_key, 'section_template', $this->templates);
|
110 |
+
add_settings_field('color_theme', __('Color theme', $this->textdomain), array(&$this, 'color_theme_option'), $this->settings_key, 'section_template');
|
111 |
+
add_settings_field('company_name', __('Company name', $this->textdomain), array(&$this, 'company_name_option'), $this->settings_key, 'section_template');
|
112 |
+
add_settings_field('company_logo', __('Company logo', $this->textdomain), array(&$this, 'company_logo_option'), $this->settings_key, 'section_template');
|
113 |
+
add_settings_field('intro_text', __('Intro text', $this->textdomain), array(&$this, 'intro_text_option'), $this->settings_key, 'section_template');
|
114 |
+
add_settings_field('company_address', __('Company address', $this->textdomain), array(&$this, 'company_address_option'), $this->settings_key, 'section_template');
|
115 |
+
add_settings_field('company_details', __('Company details', $this->textdomain), array(&$this, 'company_details_option'), $this->settings_key, 'section_template');
|
116 |
+
add_settings_field('terms', __('Terms & conditions, policies etc.', $this->textdomain), array(&$this, 'terms_option'), $this->settings_key, 'section_template');
|
117 |
+
add_settings_field('next_invoice_number', __('Next invoice number', $this->textdomain), array(&$this, 'next_invoice_number_option'), $this->settings_key, 'section_template');
|
118 |
+
add_settings_field('invoice_number_digits', __('Number of digits', $this->textdomain), array(&$this, 'invoice_number_digits_option'), $this->settings_key, 'section_template');
|
119 |
+
add_settings_field('invoice_prefix', __('Invoice number prefix', $this->textdomain), array(&$this, 'invoice_prefix_option'), $this->settings_key, 'section_template');
|
120 |
+
add_settings_field('invoice_suffix', __('Invoice number suffix', $this->textdomain), array(&$this, 'invoice_suffix_option'), $this->settings_key, 'section_template');
|
121 |
+
add_settings_field('invoice_format', __('Invoice number format', $this->textdomain), array(&$this, 'invoice_format_option'), $this->settings_key, 'section_template');
|
122 |
+
add_settings_field('reset_invoice_number', __('Reset on 1st January', $this->textdomain), array(&$this, 'reset_invoice_number_option'), $this->settings_key, 'section_template');
|
123 |
+
add_settings_field('invoice_date_format', __('Invoice date format', $this->textdomain), array(&$this, 'invoice_date_format_option'), $this->settings_key, 'section_template');
|
124 |
+
add_settings_field('show_sku', __('Show SKU', $this->textdomain), array(&$this, 'show_sku_option'), $this->settings_key, 'section_template');
|
125 |
+
add_settings_field('show_discount', __('Show discount', $this->textdomain), array(&$this, 'show_discount_option'), $this->settings_key, 'section_template');
|
126 |
+
add_settings_field('show_subtotal', __('Show subtotal', $this->textdomain), array(&$this, 'show_subtotal_option'), $this->settings_key, 'section_template');
|
127 |
+
add_settings_field('show_tax', __('Show tax', $this->textdomain), array(&$this, 'show_tax_option'), $this->settings_key, 'section_template');
|
128 |
+
add_settings_field('show_shipping', __('Show shipping', $this->textdomain), array(&$this, 'show_shipping_option'), $this->settings_key, 'section_template');
|
129 |
+
add_settings_field('show_customer_notes', __('Show customer notes', $this->textdomain), array(&$this, 'show_customer_notes_option'), $this->settings_key, 'section_template');
|
130 |
+
//add_settings_field( 'preview_invoice', 'Preview invoice', array( &$this, 'preview_invoice_option' ), $this->settings_key, 'section_template' );
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Show all settings notices.
|
135 |
+
*/
|
136 |
+
public function show_settings_notices() {
|
137 |
+
settings_errors($this->settings_key);
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* @param $args
|
142 |
+
*/
|
143 |
+
public function template_id_option($args)
|
144 |
+
{
|
145 |
+
?>
|
146 |
+
<select id="template-type-option" name="<?php echo $this->settings_key; ?>[template_id]">
|
147 |
+
<?php
|
148 |
+
foreach ($args as $template) {
|
149 |
+
?>
|
150 |
+
<option
|
151 |
+
value="<?php echo $template['id']; ?>" <?php selected($this->settings['template_id'], $template['id']); ?>><?php echo $template['name']; ?></option>
|
152 |
+
<?php
|
153 |
+
}
|
154 |
+
?>
|
155 |
+
</select>
|
156 |
+
<?php
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* @param $args
|
161 |
+
*/
|
162 |
+
public function color_theme_option($args)
|
163 |
+
{
|
164 |
+
?>
|
165 |
+
<input id="color-picker" type="color" name="<?php echo $this->settings_key; ?>[color_theme]"
|
166 |
+
value="<?php echo $this->settings['color_theme']; ?>"/>
|
167 |
+
<div class="notes"><?php _e('Color theme of the invoice.', $this->textdomain); ?></div>
|
168 |
+
<?php
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
*
|
173 |
+
*/
|
174 |
+
public function company_name_option()
|
175 |
+
{
|
176 |
+
?>
|
177 |
+
<input type="text"
|
178 |
+
name="<?php echo $this->settings_key; ?>[company_name]"
|
179 |
+
value="<?php echo $this->settings['company_name']; ?>"/>
|
180 |
+
<?php
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
*
|
185 |
+
*/
|
186 |
+
public function company_logo_option()
|
187 |
+
{
|
188 |
+
?>
|
189 |
+
<div
|
190 |
+
class="notes"><?php _e('Please upload an image less then 200Kb and make sure it\'s a jpeg, jpg or png.', $this->textdomain); ?></div>
|
191 |
+
<br/>
|
192 |
+
<input id="upload-file" type="file" name="company_logo" accept="image/*"/>
|
193 |
+
<input type="hidden" id="company-logo-value" name="company_logo"
|
194 |
+
value="<?php echo esc_attr($this->settings['company_logo']); ?>"/>
|
195 |
+
<?php
|
196 |
+
if ($this->settings['company_logo'] != "") {
|
197 |
+
?>
|
198 |
+
<div id="company-logo-wrapper">
|
199 |
+
<img id="company-logo" src="<?php echo esc_attr($this->settings['company_logo']); ?>"/>
|
200 |
+
<img id="delete" src="<?php echo WPI_URL . '/assets/img/delete-icon.png'; ?>"
|
201 |
+
onclick="Settings.removeCompanyLogo()" title="<?php _e('Remove logo', $this->textdomain); ?>"/>
|
202 |
+
</div>
|
203 |
+
<?php
|
204 |
+
}
|
205 |
+
?>
|
206 |
+
<?php
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
*
|
211 |
+
*/
|
212 |
+
public function intro_text_option()
|
213 |
+
{
|
214 |
+
?>
|
215 |
+
<div class="notes block"><?php echo $this->get_allowed_tags_str(); ?></div>
|
216 |
+
<textarea name="<?php echo $this->settings_key; ?>[intro_text]" rows="5"
|
217 |
+
cols="50"><?php _e(esc_textarea($this->settings['intro_text'], $this->textdomain)); ?></textarea>
|
218 |
+
<?php
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
*
|
223 |
+
*/
|
224 |
+
public function company_address_option()
|
225 |
+
{
|
226 |
+
?>
|
227 |
+
<div class="notes block"><?php echo $this->get_allowed_tags_str(); ?></div>
|
228 |
+
<textarea name="<?php echo $this->settings_key; ?>[company_address]" rows="5"
|
229 |
+
cols="50"><?php echo esc_textarea($this->settings['company_address']); ?></textarea>
|
230 |
+
<?php
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
*
|
235 |
+
*/
|
236 |
+
public function company_details_option()
|
237 |
+
{
|
238 |
+
?>
|
239 |
+
<div class="notes block"><?php echo $this->get_allowed_tags_str(); ?></div>
|
240 |
+
<textarea name="<?php echo $this->settings_key; ?>[company_details]" rows="5"
|
241 |
+
cols="50"><?php echo esc_textarea($this->settings['company_details']); ?></textarea>
|
242 |
+
<?php
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
*
|
247 |
+
*/
|
248 |
+
public function terms_option()
|
249 |
+
{
|
250 |
+
?>
|
251 |
+
<div class="notes block"><?php echo $this->get_allowed_tags_str(); ?></div>
|
252 |
+
<textarea name="<?php echo $this->settings_key; ?>[terms]" rows="5"
|
253 |
+
cols="50"><?php _e(esc_textarea($this->settings['terms'], $this->textdomain)); ?></textarea>
|
254 |
+
<?php
|
255 |
+
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
*
|
259 |
+
*/
|
260 |
+
public function show_subtotal_option()
|
261 |
+
{
|
262 |
+
?>
|
263 |
+
<input type="checkbox"
|
264 |
+
name="<?php echo $this->settings_key; ?>[show_subtotal]"
|
265 |
+
value="1"
|
266 |
+
<?php checked($this->settings['show_subtotal']); ?> />
|
267 |
+
<?php
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
*
|
272 |
+
*/
|
273 |
+
public function show_tax_option()
|
274 |
+
{
|
275 |
+
?>
|
276 |
+
<input type="checkbox"
|
277 |
+
name="<?php echo $this->settings_key; ?>[show_tax]"
|
278 |
+
value="1"
|
279 |
+
<?php checked($this->settings['show_tax']); ?> />
|
280 |
+
<?php
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
*
|
285 |
+
*/
|
286 |
+
public function show_discount_option()
|
287 |
+
{
|
288 |
+
?>
|
289 |
+
<input type="checkbox"
|
290 |
+
name="<?php echo $this->settings_key; ?>[show_discount]"
|
291 |
+
value="1"
|
292 |
+
<?php checked($this->settings['show_discount']); ?> />
|
293 |
+
<?php
|
294 |
+
}
|
295 |
+
|
296 |
+
/**
|
297 |
+
*
|
298 |
+
*/
|
299 |
+
public function show_shipping_option()
|
300 |
+
{
|
301 |
+
?>
|
302 |
+
<input type="checkbox"
|
303 |
+
name="<?php echo $this->settings_key; ?>[show_shipping]"
|
304 |
+
value="1"
|
305 |
+
<?php checked($this->settings['show_shipping']); ?> />
|
306 |
+
<?php
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
*
|
311 |
+
*/
|
312 |
+
public function show_customer_notes_option()
|
313 |
+
{
|
314 |
+
?>
|
315 |
+
<input type="checkbox"
|
316 |
+
name="<?php echo $this->settings_key; ?>[show_customer_notes]"
|
317 |
+
value="1"
|
318 |
+
<?php checked($this->settings['show_customer_notes']); ?> />
|
319 |
+
<?php
|
320 |
+
}
|
321 |
+
|
322 |
+
/**
|
323 |
+
*
|
324 |
+
*/
|
325 |
+
public function show_sku_option()
|
326 |
+
{
|
327 |
+
?>
|
328 |
+
<input type="checkbox"
|
329 |
+
name="<?php echo $this->settings_key; ?>[show_sku]"
|
330 |
+
value="1"
|
331 |
+
<?php checked($this->settings['show_sku']); ?> />
|
332 |
+
<?php
|
333 |
+
}
|
334 |
+
|
335 |
+
/**
|
336 |
+
*
|
337 |
+
*/
|
338 |
+
public function next_invoice_number_option()
|
339 |
+
{
|
340 |
+
?>
|
341 |
+
<input type="text"
|
342 |
+
name="<?php echo $this->settings_key; ?>[next_invoice_number]"
|
343 |
+
value="<?php echo $this->settings['next_invoice_number']; ?>"/>
|
344 |
+
<div class="notes"><?php _e('Invoice number to use for next invoice.', $this->textdomain); ?></div>
|
345 |
+
<?php
|
346 |
+
}
|
347 |
+
|
348 |
+
/**
|
349 |
+
*
|
350 |
+
*/
|
351 |
+
public function invoice_number_digits_option()
|
352 |
+
{
|
353 |
+
?>
|
354 |
+
<input type="number"
|
355 |
+
name="<?php echo $this->settings_key; ?>[invoice_number_digits]"
|
356 |
+
value="<?php echo $this->settings['invoice_number_digits']; ?>"
|
357 |
+
min="3"
|
358 |
+
max="6"
|
359 |
+
/>
|
360 |
+
<div class="notes"><?php _e('Number of zero digits.', $this->textdomain); ?></div>
|
361 |
+
<?php
|
362 |
+
}
|
363 |
+
|
364 |
+
/**
|
365 |
+
*
|
366 |
+
*/
|
367 |
+
public function invoice_prefix_option()
|
368 |
+
{
|
369 |
+
?>
|
370 |
+
<input type="text"
|
371 |
+
name="<?php echo $this->settings_key; ?>[invoice_prefix]"
|
372 |
+
value="<?php echo $this->settings['invoice_prefix']; ?>"/>
|
373 |
+
<div
|
374 |
+
class="notes"><?php _e('Prefix text for the invoice number. Not required.', $this->textdomain); ?></div>
|
375 |
+
<?php
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
*
|
380 |
+
*/
|
381 |
+
public function invoice_suffix_option()
|
382 |
+
{
|
383 |
+
?>
|
384 |
+
<input type="text"
|
385 |
+
name="<?php echo $this->settings_key; ?>[invoice_suffix]"
|
386 |
+
value="<?php echo $this->settings['invoice_suffix']; ?>"/>
|
387 |
+
<div
|
388 |
+
class="notes"><?php _e('Suffix text for the invoice number. Not required.', $this->textdomain); ?></div>
|
389 |
+
<?php
|
390 |
+
}
|
391 |
+
|
392 |
+
/**
|
393 |
+
*
|
394 |
+
*/
|
395 |
+
public function invoice_format_option()
|
396 |
+
{
|
397 |
+
?>
|
398 |
+
<input type="text"
|
399 |
+
name="<?php echo $this->settings_key; ?>[invoice_format]"
|
400 |
+
value="<?php echo $this->settings['invoice_format']; ?>"/>
|
401 |
+
<div
|
402 |
+
class="notes"><?php _e('Use [prefix], [suffix] and [number] as placeholders. [number] is required.', $this->textdomain); ?></div>
|
403 |
+
<?php
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
*
|
408 |
+
*/
|
409 |
+
public function reset_invoice_number_option()
|
410 |
+
{
|
411 |
+
?>
|
412 |
+
<input type="checkbox"
|
413 |
+
name="<?php echo $this->settings_key; ?>[reset_invoice_number]"
|
414 |
+
value="1"
|
415 |
+
<?php checked($this->settings['reset_invoice_number']); ?> />
|
416 |
+
<div class="notes"><?php _e('Reset on the first of January.', $this->textdomain); ?></div>
|
417 |
+
<?php
|
418 |
+
}
|
419 |
+
|
420 |
+
/**
|
421 |
+
*
|
422 |
+
*/
|
423 |
+
public function invoice_date_format_option()
|
424 |
+
{
|
425 |
+
?>
|
426 |
+
<input type="text"
|
427 |
+
name="<?php echo $this->settings_key; ?>[invoice_date_format]"
|
428 |
+
value="<?php echo $this->settings['invoice_date_format']; ?>"/>
|
429 |
+
<div
|
430 |
+
class="notes"><?php printf(__('%sFormat%s of the date. Examples: %s or %s.', $this->textdomain), '<a href="http://php.net/manual/en/datetime.formats.date.php">', '</a>', '"m.d.y"', '"F jS Y"'); ?></div>
|
431 |
+
<?php
|
432 |
+
}
|
433 |
+
|
434 |
+
/**
|
435 |
+
*
|
436 |
+
*/
|
437 |
+
/*function preview_invoice_option() {
|
438 |
+
?>
|
439 |
+
<a href="<?php echo admin_url('admin-ajax.php'); ?>?action=wpi_preview_invoice&security=<?php echo wp_create_nonce('wpi_preview_invoice'); ?>" target="_blank">Preview</a>
|
440 |
+
<?php
|
441 |
+
}*/
|
442 |
+
|
443 |
+
/**
|
444 |
+
* Gets a template from the templates array by id.
|
445 |
+
* @param $template_id
|
446 |
+
* @return string
|
447 |
+
*/
|
448 |
+
public function get_template($template_id)
|
449 |
+
{
|
450 |
+
$template = "";
|
451 |
+
foreach ($this->templates as $template) {
|
452 |
+
if ($template['id'] == $template_id) {
|
453 |
+
return $template;
|
454 |
+
}
|
455 |
+
}
|
456 |
+
return $template;
|
457 |
+
}
|
458 |
+
|
459 |
+
/**
|
460 |
+
* Validates all the settings values.
|
461 |
+
*
|
462 |
+
* @param $input
|
463 |
+
* @return array
|
464 |
+
*/
|
465 |
+
public function validate($input)
|
466 |
+
{
|
467 |
+
$output = array();
|
468 |
+
|
469 |
+
// Validate template id
|
470 |
+
if ($this->is_valid_int($input['template_id'])) {
|
471 |
+
$output['template_id'] = $input['template_id'];
|
472 |
+
} else {
|
473 |
+
add_settings_error(
|
474 |
+
esc_attr($this->settings_key),
|
475 |
+
'invalid-template-value',
|
476 |
+
__('Invalid template.', $this->textdomain)
|
477 |
+
);
|
478 |
+
}
|
479 |
+
|
480 |
+
// Validate color theme.
|
481 |
+
if (is_string($this->is_valid_hex_color($input['color_theme']))) {
|
482 |
+
$output['color_theme'] = $this->is_valid_hex_color($input['color_theme']);
|
483 |
+
} else if ($this->is_valid_hex_color($input['color_theme'])) {
|
484 |
+
$output['color_theme'] = $input['color_theme'];
|
485 |
+
} else {
|
486 |
+
add_settings_error(
|
487 |
+
esc_attr($this->settings_key),
|
488 |
+
'invalid-color-hex',
|
489 |
+
__('Invalid color theme code.', $this->textdomain)
|
490 |
+
);
|
491 |
+
}
|
492 |
+
|
493 |
+
// Validate company name
|
494 |
+
if ($this->is_valid_str($input['company_name'])) {
|
495 |
+
$output['company_name'] = $input['company_name'];
|
496 |
+
} else {
|
497 |
+
add_settings_error(
|
498 |
+
esc_attr($this->settings_key),
|
499 |
+
'invalid-company-name',
|
500 |
+
__('Invalid company name.', $this->textdomain)
|
501 |
+
);
|
502 |
+
}
|
503 |
+
|
504 |
+
// Validate company logo
|
505 |
+
$output['company_logo'] = $this->upload_file();
|
506 |
+
|
507 |
+
// Validate textarea's
|
508 |
+
$ta_errors = 0;
|
509 |
+
$textarea_values = array('intro_text' => $input['intro_text'], 'company_address' => $input['company_address'], 'company_details' => $input['company_details'], 'terms' => $input['terms']);
|
510 |
+
foreach ($textarea_values as $key => $value) {
|
511 |
+
($this->validate_textarea($value)) ? $output[$key] = $value : $ta_errors += 1;
|
512 |
+
}
|
513 |
+
|
514 |
+
if ($ta_errors > 0) {
|
515 |
+
add_settings_error(
|
516 |
+
esc_attr($this->settings_key),
|
517 |
+
'invalid_textarea_value',
|
518 |
+
__('Invalid input into one of the textarea\'s.', $this->textdomain)
|
519 |
+
);
|
520 |
+
}
|
521 |
+
|
522 |
+
// Validate next invoice number
|
523 |
+
if ($this->is_valid_int($input['next_invoice_number'])) {
|
524 |
+
$output['next_invoice_number'] = $input['next_invoice_number'];
|
525 |
+
} else {
|
526 |
+
add_settings_error(
|
527 |
+
esc_attr($this->settings_key),
|
528 |
+
'invalid_next_invoice_number',
|
529 |
+
__('Invalid (next) invoice number.', $this->textdomain)
|
530 |
+
);
|
531 |
+
}
|
532 |
+
|
533 |
+
// Validate zero digits
|
534 |
+
$ind_errors = 0;
|
535 |
+
if ($this->is_valid_int($input['invoice_number_digits'])) {
|
536 |
+
($input['invoice_number_digits'] >= 3 && $input['invoice_number_digits'] <= 6)
|
537 |
+
? $output['invoice_number_digits'] = $input['invoice_number_digits']
|
538 |
+
: $ind_errors += 1;
|
539 |
+
} else {
|
540 |
+
$ind_errors += 1;
|
541 |
+
}
|
542 |
+
|
543 |
+
if ($ind_errors > 0) {
|
544 |
+
add_settings_error(
|
545 |
+
esc_attr($this->settings_key),
|
546 |
+
'invalid_invoice_number_digits',
|
547 |
+
__('Invalid invoice number digits.', $this->textdomain)
|
548 |
+
);
|
549 |
+
}
|
550 |
+
|
551 |
+
// Validate invoice number prefix and suffix.
|
552 |
+
$output['invoice_prefix'] = esc_html($input['invoice_prefix']);
|
553 |
+
$output['invoice_suffix'] = esc_html($input['invoice_suffix']);
|
554 |
+
|
555 |
+
// Validate invoice number format
|
556 |
+
if ($this->is_valid_str($input['invoice_format'])) {
|
557 |
+
if (strpos($input['invoice_format'], '[number]') !== false) {
|
558 |
+
$output['invoice_format'] = $input['invoice_format'];
|
559 |
+
} else {
|
560 |
+
add_settings_error(
|
561 |
+
esc_attr($this->settings_key),
|
562 |
+
'invalid_invoice_format-1',
|
563 |
+
__('The [number] placeholder is required as invoice number format.', $this->textdomain)
|
564 |
+
);
|
565 |
+
}
|
566 |
+
} else {
|
567 |
+
add_settings_error(
|
568 |
+
esc_attr($this->settings_key),
|
569 |
+
'invalid_invoice_format-2',
|
570 |
+
__('Invalid invoice number format.', $this->textdomain)
|
571 |
+
);
|
572 |
+
}
|
573 |
+
|
574 |
+
// Validate all checkboxes
|
575 |
+
$cb_errors = 0;
|
576 |
+
$checkbox_values = array(
|
577 |
+
'reset_invoice_number' => $input['reset_invoice_number'],
|
578 |
+
'show_sku' => $input['show_sku'],
|
579 |
+
'show_discount' => $input['show_discount'],
|
580 |
+
'show_subtotal' => $input['show_subtotal'],
|
581 |
+
'show_tax' => $input['show_tax'],
|
582 |
+
'show_shipping' => $input['show_shipping'],
|
583 |
+
'show_customer_notes' => $input['show_customer_notes']
|
584 |
+
);
|
585 |
+
|
586 |
+
foreach ($checkbox_values as $key => $value) {
|
587 |
+
($this->validate_checkbox($value)) ? $output[$key] = $value : $output[$key] = 0;
|
588 |
+
}
|
589 |
+
|
590 |
+
if ($cb_errors > 0) {
|
591 |
+
add_settings_error(
|
592 |
+
esc_attr($this->settings_key),
|
593 |
+
'invalid-checkbox-value',
|
594 |
+
__('Please don\'t try to change the values.', $this->textdomain)
|
595 |
+
);
|
596 |
+
}
|
597 |
+
|
598 |
+
if ($this->is_valid_str($input['invoice_date_format'])) {
|
599 |
+
$output['invoice_date_format'] = $input['invoice_date_format'];
|
600 |
+
} else {
|
601 |
+
add_settings_error(
|
602 |
+
esc_attr($this->settings_key),
|
603 |
+
'invalid-date-format',
|
604 |
+
__('Invalid date format.', $this->textdomain)
|
605 |
+
);
|
606 |
+
}
|
607 |
+
|
608 |
+
return $output;
|
609 |
+
}
|
610 |
+
|
611 |
+
/**
|
612 |
+
* Checks if the company logo has changed and uploads new logo's.
|
613 |
+
* @return string
|
614 |
+
*/
|
615 |
+
private function upload_file() {
|
616 |
+
$return = "";
|
617 |
+
|
618 |
+
if ($_FILES['company_logo']['error'] == 0) {
|
619 |
+
$file = $_FILES['company_logo'];
|
620 |
+
if ($file['size'] <= 200000) {
|
621 |
+
$override = array('test_form' => false);
|
622 |
+
$company_logo = wp_handle_upload($file, $override);
|
623 |
+
$validate_file_code = validate_file($company_logo['url']);
|
624 |
+
if ($validate_file_code == 0) {
|
625 |
+
$return = $company_logo['url'];
|
626 |
+
} else {
|
627 |
+
switch ($validate_file_code) {
|
628 |
+
case 1:
|
629 |
+
add_settings_error(
|
630 |
+
esc_attr($this->settings_key),
|
631 |
+
'file-invalid-1',
|
632 |
+
__('File is invalid and contains either \'..\' or \'./\'.', $this->textdomain)
|
633 |
+
);
|
634 |
+
break;
|
635 |
+
case 2:
|
636 |
+
add_settings_error(
|
637 |
+
esc_attr($this->settings_key),
|
638 |
+
'file-invalid-2',
|
639 |
+
__('File is invalid and contains \':\' after the first character.', $this->textdomain)
|
640 |
+
);
|
641 |
+
break;
|
642 |
+
}
|
643 |
+
}
|
644 |
+
} else {
|
645 |
+
add_settings_error(
|
646 |
+
esc_attr($this->settings_key),
|
647 |
+
'file-invalid-3',
|
648 |
+
__('Please upload image with extension jpg, jpeg or png.', $this->textdomain)
|
649 |
+
);
|
650 |
+
}
|
651 |
+
} else if (!empty($_POST['company_logo'])) {
|
652 |
+
$return = $_POST['company_logo'];
|
653 |
+
}
|
654 |
+
|
655 |
+
return $return;
|
656 |
+
}
|
657 |
+
}
|
658 |
+
}
|
admin/views/invoice-sample.php
ADDED
@@ -0,0 +1,238 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<style>
|
2 |
+
/* Page template CSS */
|
3 |
+
#post-2 header, #post-2 footer {
|
4 |
+
display: none;
|
5 |
+
}
|
6 |
+
/* Invoice template 1 CSS */
|
7 |
+
#body {
|
8 |
+
padding: 40px 40px 60px;
|
9 |
+
color: #5A5E61;
|
10 |
+
}
|
11 |
+
.row {
|
12 |
+
width: 100%;
|
13 |
+
margin-bottom: 20px;
|
14 |
+
}
|
15 |
+
.logo .left, .logo .right, .logo .center {
|
16 |
+
float: left;
|
17 |
+
}
|
18 |
+
.logo .center {
|
19 |
+
width: 55%;
|
20 |
+
margin: 0 auto;
|
21 |
+
}
|
22 |
+
.logo .left, .logo .right {
|
23 |
+
width: 15%;
|
24 |
+
}
|
25 |
+
.logo .right {
|
26 |
+
float: right;
|
27 |
+
}
|
28 |
+
#company-logo {
|
29 |
+
margin: 0 auto;
|
30 |
+
height: 150px;
|
31 |
+
line-height: 150px;
|
32 |
+
width: 150px;
|
33 |
+
font-size: 18px;
|
34 |
+
background-color: #57C56F;
|
35 |
+
color: white;
|
36 |
+
text-align: center;
|
37 |
+
}
|
38 |
+
#company-logo span {
|
39 |
+
vertical-align: middle;
|
40 |
+
line-height: 14px;
|
41 |
+
font-weight: bold;
|
42 |
+
}
|
43 |
+
.company-address {
|
44 |
+
text-align: right;
|
45 |
+
padding-right: 40px;
|
46 |
+
}
|
47 |
+
.intro, .coupon, .title, #invoice-number {
|
48 |
+
text-align: center;
|
49 |
+
}
|
50 |
+
.intro {
|
51 |
+
font-size: 18px;
|
52 |
+
}
|
53 |
+
#expires, #invoice-number{
|
54 |
+
font-size: 12px;
|
55 |
+
}
|
56 |
+
.coupon {
|
57 |
+
padding-left: 40px;
|
58 |
+
padding-right: 40px;
|
59 |
+
background-color: #F8F8F8;
|
60 |
+
color: #32373B;
|
61 |
+
}
|
62 |
+
#coupon-code {
|
63 |
+
margin: 0 auto;
|
64 |
+
border: 1px dashed #F8F8F8;
|
65 |
+
padding: 10px;
|
66 |
+
background-color: #52AF68;
|
67 |
+
color: white;
|
68 |
+
font-weight: bold;
|
69 |
+
}
|
70 |
+
.title {
|
71 |
+
margin: 0;
|
72 |
+
padding: 20px;
|
73 |
+
color: white;
|
74 |
+
background-color: #52AF68;
|
75 |
+
}
|
76 |
+
.title h1 {
|
77 |
+
margin: 0; padding: 0;
|
78 |
+
}
|
79 |
+
#invoice-number {
|
80 |
+
padding: 20px;
|
81 |
+
background-color: #387747;
|
82 |
+
color: white;
|
83 |
+
font-size: 16px;
|
84 |
+
}
|
85 |
+
table {
|
86 |
+
padding: 20px;
|
87 |
+
width: 100%;
|
88 |
+
background-color: #F8F8F8;
|
89 |
+
}
|
90 |
+
td, th {
|
91 |
+
padding: 10px;
|
92 |
+
text-align: right;
|
93 |
+
font-weight: bold;
|
94 |
+
vertical-align: middle;
|
95 |
+
font-size: 14px;
|
96 |
+
text-transform: uppercase;
|
97 |
+
}
|
98 |
+
tr.border-bottom td, tr.border-bottom th, td.border-bottom {
|
99 |
+
border-bottom: 1px solid #E4E7E9;
|
100 |
+
}
|
101 |
+
.align-left { text-align: left; }
|
102 |
+
.align-center { text-align: center; }
|
103 |
+
.normalcase { text-transform: none; }
|
104 |
+
.uppercase { text-transform: uppercase; }
|
105 |
+
.circle { border-radius: 50%; }
|
106 |
+
.product td {
|
107 |
+
color: black;
|
108 |
+
}
|
109 |
+
.product td, .total {
|
110 |
+
font-weight: bold;
|
111 |
+
font-size: 16px;
|
112 |
+
}
|
113 |
+
.discount td, .subtotal td, .tax td, .shipping td {
|
114 |
+
font-weight: normal;
|
115 |
+
font-size: 12px;
|
116 |
+
}
|
117 |
+
.payment-method {
|
118 |
+
font-size: 12px;
|
119 |
+
font-weight: normal;
|
120 |
+
}
|
121 |
+
/*#footer {
|
122 |
+
position: absolute;
|
123 |
+
bottom: 0;
|
124 |
+
width: 100%;
|
125 |
+
font-size: 14px;
|
126 |
+
}
|
127 |
+
.questions, .company-address {
|
128 |
+
padding: 40px;
|
129 |
+
}
|
130 |
+
.questions {
|
131 |
+
width: 40%;
|
132 |
+
float: left;
|
133 |
+
}
|
134 |
+
.questions p {
|
135 |
+
margin: 0;
|
136 |
+
}*/
|
137 |
+
</style>
|
138 |
+
<div id="body">
|
139 |
+
<div class="row logo">
|
140 |
+
<div class="left"></div>
|
141 |
+
<div class="center">
|
142 |
+
<div id="company-logo" class="circle"><span>Your Logo</span></div>
|
143 |
+
</div>
|
144 |
+
<div class="company-address right">
|
145 |
+
<span class="uppercase"><strong>Company</strong></span><br/>
|
146 |
+
Street<br/>
|
147 |
+
City<br/>
|
148 |
+
Country
|
149 |
+
</div>
|
150 |
+
</div>
|
151 |
+
<div class="row intro">
|
152 |
+
<h1>Thank you!</h1>
|
153 |
+
<p>
|
154 |
+
Thanks for shopping with us today.<br/>
|
155 |
+
You'll find your invoice below.
|
156 |
+
</p>
|
157 |
+
</div>
|
158 |
+
<!-- COUPON -->
|
159 |
+
<div class="row coupon">
|
160 |
+
<h3>20% off next purchase</h3>
|
161 |
+
<p>
|
162 |
+
For being a regular customer, here's a little something from <br/>
|
163 |
+
us. Use the coupon code to get 20% off your next order!
|
164 |
+
</p>
|
165 |
+
<div id="coupon-code">
|
166 |
+
c0up0n_c0d3
|
167 |
+
</div>
|
168 |
+
<p id="expires">
|
169 |
+
<strong>Expires on: 1st January 2015</strong>
|
170 |
+
</p>
|
171 |
+
</div>
|
172 |
+
<div class="row invoice">
|
173 |
+
<div class="title">
|
174 |
+
<h1>Your Invoice</h1>
|
175 |
+
<span id="invoice-date" class="uppercase">25th dec 2014</span>
|
176 |
+
</div>
|
177 |
+
<div id="invoice-number">
|
178 |
+
Invoice Number: 11342
|
179 |
+
</div>
|
180 |
+
<table class="products">
|
181 |
+
<thead>
|
182 |
+
<tr class="border-bottom">
|
183 |
+
<th class="align-left">Description</th>
|
184 |
+
<th class="align-center">Quantity</th>
|
185 |
+
<th>Unit price</th>
|
186 |
+
<th>Total</th>
|
187 |
+
</tr>
|
188 |
+
</thead>
|
189 |
+
<tbody>
|
190 |
+
<tr class="product border-bottom">
|
191 |
+
<td class="align-left normalcase">Awesome Widget</td>
|
192 |
+
<td class="align-center">1</td>
|
193 |
+
<td>8.09</td>
|
194 |
+
<td>8.09</td>
|
195 |
+
</tr>
|
196 |
+
<tr class="discount">
|
197 |
+
<td colspan="2"></td>
|
198 |
+
<td class="border-bottom">Discount</td>
|
199 |
+
<td class="border-bottom">0.00</td>
|
200 |
+
</tr>
|
201 |
+
<tr class="subtotal">
|
202 |
+
<td colspan="2"></td>
|
203 |
+
<td class="border-bottom">Subtotal</td>
|
204 |
+
<td class="border-bottom">8.09</td>
|
205 |
+
</tr>
|
206 |
+
<tr class="tax">
|
207 |
+
<td colspan="2"></td>
|
208 |
+
<td class="border-bottom">Tax 21%</td>
|
209 |
+
<td class="border-bottom">1.70</td>
|
210 |
+
</tr>
|
211 |
+
<tr class="shipping">
|
212 |
+
<td colspan="2"></td>
|
213 |
+
<td class="border-bottom">Shipping</td>
|
214 |
+
<td class="border-bottom normalcase">Free Shipping</td>
|
215 |
+
</tr>
|
216 |
+
<tr>
|
217 |
+
<td class="payment-method align-left normalcase" colspan="2">Paid with Visa:<br/>**** **** **** 1234</td>
|
218 |
+
<td class="total border-bottom">Total</td>
|
219 |
+
<td class="total border-bottom">9.79</td>
|
220 |
+
</tr>
|
221 |
+
</tbody>
|
222 |
+
</table>
|
223 |
+
</div>
|
224 |
+
|
225 |
+
<!--<div id="footer">
|
226 |
+
<div class="questions">
|
227 |
+
<span><strong>Questions?</strong></span>
|
228 |
+
<p>
|
229 |
+
No problem. You can get in touch with us on Facebook and Twitter and we'll get back to you as soon as we can.
|
230 |
+
</p>
|
231 |
+
</div>
|
232 |
+
<div class="company-address">
|
233 |
+
<span><strong>Company</strong></span><br/>
|
234 |
+
Street<br/>
|
235 |
+
City<br/>
|
236 |
+
Country
|
237 |
+
</div>
|
238 |
+
</div>-->
|
assets/css/admin-styles.css
DELETED
@@ -1,43 +0,0 @@
|
|
1 |
-
textarea{
|
2 |
-
width:670px;
|
3 |
-
height:90px;
|
4 |
-
}
|
5 |
-
input[type='text']{
|
6 |
-
width:670px;
|
7 |
-
margin-bottom:7px;
|
8 |
-
}
|
9 |
-
select{
|
10 |
-
margin-bottom:7px;
|
11 |
-
}
|
12 |
-
#selectEmailType{
|
13 |
-
width: 210px;
|
14 |
-
}
|
15 |
-
#custom-logo-wrap{
|
16 |
-
position: relative;
|
17 |
-
display: inline-block;
|
18 |
-
padding-top: 16px;
|
19 |
-
}
|
20 |
-
#custom-logo-wrap:hover #delete{
|
21 |
-
display: block;
|
22 |
-
}
|
23 |
-
#uploadFile{
|
24 |
-
width: 660px;
|
25 |
-
margin-bottom: 7px;
|
26 |
-
}
|
27 |
-
#delete{
|
28 |
-
position: absolute;
|
29 |
-
top: 16px;
|
30 |
-
right: 0px;
|
31 |
-
background: #f7f7f7;
|
32 |
-
border: 1px solid #d5d5d5;
|
33 |
-
padding: 1px;
|
34 |
-
display: none;
|
35 |
-
}
|
36 |
-
.notes{
|
37 |
-
font-style: italic;
|
38 |
-
color: grey;
|
39 |
-
}
|
40 |
-
#startAt{
|
41 |
-
width:50px;
|
42 |
-
vertical-align: middle;
|
43 |
-
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
assets/css/admin.css
ADDED
@@ -0,0 +1,59 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
.be_woocommerce_pdf_invoices_settings_form select,
|
2 |
+
.be_woocommerce_pdf_invoices_settings_form input[type="text"],
|
3 |
+
.be_woocommerce_pdf_invoices_settings_form textarea {
|
4 |
+
width: 348px;
|
5 |
+
}
|
6 |
+
#company-logo-wrapper {
|
7 |
+
margin-top: 10px;
|
8 |
+
position: relative;
|
9 |
+
width: 300px;
|
10 |
+
padding: 21px 21px 0 0;
|
11 |
+
}
|
12 |
+
#company-logo {
|
13 |
+
position: relative;
|
14 |
+
width: inherit;
|
15 |
+
}
|
16 |
+
#company-logo-wrapper:hover #delete{
|
17 |
+
display: block;
|
18 |
+
}
|
19 |
+
#delete{
|
20 |
+
position: absolute;
|
21 |
+
top: 0;
|
22 |
+
right: 0;
|
23 |
+
display: none;
|
24 |
+
height: 30px;
|
25 |
+
width: 30px;
|
26 |
+
cursor: pointer;
|
27 |
+
}
|
28 |
+
.wpi-admin-order-create-invoice-btn {
|
29 |
+
background-image: url(../img/invoice.png) !important;
|
30 |
+
background-repeat: no-repeat !important;
|
31 |
+
background-position: center center !important;
|
32 |
+
text-indent: -9999px;
|
33 |
+
position: relative;
|
34 |
+
padding: 0 !important;
|
35 |
+
height: 2em !important;
|
36 |
+
width: 2em;
|
37 |
+
}
|
38 |
+
.wpi-admin-order-create-invoice-btn button {
|
39 |
+
display: none;
|
40 |
+
}
|
41 |
+
.notes {
|
42 |
+
display: inline;
|
43 |
+
font-style: italic;
|
44 |
+
}
|
45 |
+
.block {
|
46 |
+
display: block;
|
47 |
+
}
|
48 |
+
#color-picker {
|
49 |
+
height: 28px;
|
50 |
+
}
|
51 |
+
.foot {
|
52 |
+
font-size: 12px;
|
53 |
+
}
|
54 |
+
.invoice-info {
|
55 |
+
margin-bottom: 10px;
|
56 |
+
}
|
57 |
+
.invoice-btn {
|
58 |
+
margin-right: 3px;
|
59 |
+
}
|
assets/img/delete-icon.png
ADDED
Binary file
|
assets/img/invoice.png
ADDED
Binary file
|
assets/img/my-company-logo-blue.png
ADDED
Binary file
|
assets/img/my-company-logo.png
ADDED
Binary file
|
assets/js/admin.js
ADDED
@@ -0,0 +1,20 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
var Settings = {};
|
2 |
+
|
3 |
+
Settings.removeCompanyLogo = function () {
|
4 |
+
var elem = document.getElementById('company-logo-wrapper');
|
5 |
+
elem.parentNode.removeChild(elem);
|
6 |
+
document.getElementById('company-logo-value').value = '';
|
7 |
+
};
|
8 |
+
|
9 |
+
Settings.previewInvoice = function (data) {
|
10 |
+
// construct an HTTP request
|
11 |
+
var xhr = new XMLHttpRequest();
|
12 |
+
xhr.open("GET", ajax_url + "?action=wpi_preview_invoice&security=" + nonce, true);
|
13 |
+
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
14 |
+
|
15 |
+
xhr.send();
|
16 |
+
|
17 |
+
xhr.onloadend = function () {
|
18 |
+
// done
|
19 |
+
};
|
20 |
+
};
|
assets/screenshot-1.png
CHANGED
Binary file
|
assets/screenshot-2.png
CHANGED
Binary file
|
assets/screenshot-3.png
CHANGED
Binary file
|
assets/screenshot-4.png
ADDED
Binary file
|
assets/screenshot-5.png
ADDED
Binary file
|
assets/screenshot-6.png
ADDED
Binary file
|
bootstrap.php
ADDED
@@ -0,0 +1,40 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @version 2.0.0
|
5 |
+
* @package WooCommerce PDF Invoices
|
6 |
+
* @author baaaaas
|
7 |
+
*
|
8 |
+
* @wordpress-plugin
|
9 |
+
* Plugin Name: WooCommerce PDF Invoices
|
10 |
+
* Plugin URI:
|
11 |
+
* Description: Generates customized PDF invoices and automatically attaches it to a WooCommerce email type of your choice. Now sending invoices to your Google Drive, Egnyte, Dropbox or OneDrive and it's all FREE!
|
12 |
+
* Version: 2.0.0
|
13 |
+
* Author: baaaaas
|
14 |
+
* Author URI:
|
15 |
+
* License: GPL-2.0+
|
16 |
+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
|
17 |
+
* Text Domain: be-woocommerce-pdf-invoices
|
18 |
+
* Domain Path: /lang
|
19 |
+
*/
|
20 |
+
|
21 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
22 |
+
die( 'Access denied.' );
|
23 |
+
}
|
24 |
+
|
25 |
+
define( 'WPI_NAME', 'WooCommerce PDF Invoices' );
|
26 |
+
define( 'WPI_DIR', plugin_dir_path( __FILE__ ) );
|
27 |
+
define( 'WPI_URL', plugins_url( '', __FILE__ ) );
|
28 |
+
define( 'WPI_TEMPLATES_DIR', plugin_dir_path( __FILE__ ) . 'includes/views/templates/' );
|
29 |
+
define( 'WPI_TMP_DIR', plugin_dir_path( __FILE__ ) . 'tmp/' );
|
30 |
+
define( 'WPI_LANG_DIR', basename( dirname( __FILE__ ) ) . '/lang' );
|
31 |
+
|
32 |
+
require_once( WPI_DIR . 'admin/classes/woocommerce-pdf-invoices.php' );
|
33 |
+
require_once( WPI_DIR . 'admin/classes/wpi-settings.php' );
|
34 |
+
require_once( WPI_DIR . 'admin/classes/wpi-general-settings.php' );
|
35 |
+
require_once( WPI_DIR . 'admin/classes/wpi-template-settings.php' );
|
36 |
+
require_once( WPI_DIR . 'includes/classes/wpi-invoice.php' );
|
37 |
+
|
38 |
+
if ( class_exists( 'BE_WooCommerce_PDF_Invoices' ) ) {
|
39 |
+
new BE_WooCommerce_PDF_Invoices(new WPI_General_Settings(), new WPI_Template_Settings());
|
40 |
+
}
|
includes/class-admin.php
DELETED
@@ -1,64 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
class BEWPI_Admin {
|
3 |
-
|
4 |
-
function __construct() {
|
5 |
-
add_action( 'admin_init', array($this, 'register_settings' ));
|
6 |
-
add_action( 'admin_menu', array($this, 'add_menu_item'));
|
7 |
-
add_action( 'admin_notices', array($this, 'woocommerce_pdf_invoices_admin_notices' ));
|
8 |
-
}
|
9 |
-
|
10 |
-
function woocommerce_pdf_invoices_admin_notices() {
|
11 |
-
settings_errors( 'woocommerce_pdf_invoices_notices' );
|
12 |
-
}
|
13 |
-
|
14 |
-
function register_settings() {
|
15 |
-
register_setting( 'be_woocommerce_pdf_invoices', 'be_woocommerce_pdf_invoices', array($this, 'validate_settings'));
|
16 |
-
}
|
17 |
-
|
18 |
-
function add_menu_item() {
|
19 |
-
$page = add_submenu_page( 'woocommerce', 'Invoices by Bas Elbers', 'Invoices', 'manage_options', 'bewpi', array($this, 'show_settings_page') );
|
20 |
-
|
21 |
-
// Nieuwe functionaliteit
|
22 |
-
add_action( 'admin_print_styles-' . $page, array($this, 'woocommerce_pdf_invoices_admin_styles' ));
|
23 |
-
// Einde
|
24 |
-
}
|
25 |
-
|
26 |
-
// Nieuwe functionaliteit
|
27 |
-
function woocommerce_pdf_invoices_admin_styles() {
|
28 |
-
wp_enqueue_style( 'AdminStylesheet', plugins_url()."/woocommerce-pdf-invoices/assets/css/admin-styles.css" );
|
29 |
-
}
|
30 |
-
// Einde
|
31 |
-
|
32 |
-
function validate_settings($settings) {
|
33 |
-
$old_settings = get_option('be_woocommerce_pdf_invoices');
|
34 |
-
$errors;
|
35 |
-
|
36 |
-
if((isset($_FILES['logo']['name'])) && ($_FILES["logo"]["error"]==0))
|
37 |
-
{
|
38 |
-
if($_FILES['logo']['size'] <= 50000){
|
39 |
-
if(preg_match('/(jpg|jpeg|png)$/', $_FILES['logo']['type'])){
|
40 |
-
$override = array('test_form' => false);
|
41 |
-
$file = wp_handle_upload( $_FILES['logo'], $override );
|
42 |
-
$settings['file_upload'] = $file['url'];
|
43 |
-
}else{
|
44 |
-
add_settings_error('woocommerce_pdf_invoices_notices', esc_attr( 'settings_updated' ), __('Please upload image with extension jpg, jpeg or png.'), 'error');
|
45 |
-
$errors++;
|
46 |
-
}
|
47 |
-
}else{
|
48 |
-
add_settings_error('woocommerce_pdf_invoices_notices', esc_attr( 'settings_updated' ), __('Please upload image less then 50kB.'), 'error');
|
49 |
-
$errors++;
|
50 |
-
}
|
51 |
-
}
|
52 |
-
|
53 |
-
if(!$errors){
|
54 |
-
add_settings_error('woocommerce_pdf_invoices_notices', esc_attr( 'settings_updated' ), __('Settings saved.'), 'updated');
|
55 |
-
}
|
56 |
-
|
57 |
-
return $settings;
|
58 |
-
}
|
59 |
-
|
60 |
-
public function show_settings_page() {
|
61 |
-
$options = bewpi_get_options();
|
62 |
-
include BEWPI_PLUGIN_DIR . 'includes/views/settings-page.php';
|
63 |
-
}
|
64 |
-
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
includes/class-invoice.php
DELETED
@@ -1,317 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class BEWPI_Invoice {
|
4 |
-
|
5 |
-
function __construct(){
|
6 |
-
add_filter( 'woocommerce_email_attachments', array($this, 'generate_invoice'),10,3 );
|
7 |
-
}
|
8 |
-
|
9 |
-
// SEQUENTIAL INVOICE NUMBERS
|
10 |
-
function set_sequential_invoice_number( $post_id, $invoice_number_start ) {
|
11 |
-
global $wpdb;
|
12 |
-
$invoice_number = get_post_meta( $post_id, '_bewpi_invoice_number', true );
|
13 |
-
|
14 |
-
if($invoice_number_start == ""){
|
15 |
-
$invoice_number_start = 1;
|
16 |
-
}
|
17 |
-
|
18 |
-
if($invoice_number == ""){
|
19 |
-
// attempt the query up to 3 times for a much higher success rate if it fails (due to Deadlock)
|
20 |
-
$success = false;
|
21 |
-
for ( $i = 0; $i < 3 && ! $success; $i++ ) {
|
22 |
-
// this seems to me like the safest way to avoid order number clashes
|
23 |
-
$query = $wpdb->prepare( "
|
24 |
-
INSERT INTO {$wpdb->postmeta} (post_id, meta_key, meta_value)
|
25 |
-
SELECT %d, '_bewpi_invoice_number', IF( MAX( CAST( meta_value as UNSIGNED ) ) IS NULL, %d, MAX( CAST( meta_value as UNSIGNED ) ) + 1 )
|
26 |
-
FROM {$wpdb->postmeta}
|
27 |
-
WHERE meta_key='_bewpi_invoice_number'",
|
28 |
-
$post_id, $invoice_number_start );
|
29 |
-
$success = $wpdb->query( $query );
|
30 |
-
}
|
31 |
-
}
|
32 |
-
}
|
33 |
-
|
34 |
-
function get_invoice_number($post_id){
|
35 |
-
// for some reason this doesn't work the first time.
|
36 |
-
//return get_post_meta( $post_id, '_bewpi_invoice_number', true );
|
37 |
-
|
38 |
-
global $wpdb;
|
39 |
-
$results = $wpdb->get_results("SELECT meta_value FROM $wpdb->postmeta WHERE post_id = $post_id AND meta_key = '_bewpi_invoice_number'");
|
40 |
-
|
41 |
-
if(count($results) == 1){
|
42 |
-
return $results[0]->meta_value;
|
43 |
-
}
|
44 |
-
}
|
45 |
-
|
46 |
-
function get_formatted_invoice_number($number, $option){
|
47 |
-
$today = date('d');
|
48 |
-
$month = date('m');
|
49 |
-
$year = date('Y');
|
50 |
-
$number = sprintf("%04s", $number);
|
51 |
-
|
52 |
-
switch($option){
|
53 |
-
case 1:
|
54 |
-
$invoice_number = $number;
|
55 |
-
break;
|
56 |
-
case 2:
|
57 |
-
$invoice_number = $year.$month.$today."-".$number;
|
58 |
-
break;
|
59 |
-
case 3:
|
60 |
-
$year = substr($year, -2);
|
61 |
-
$invoice_number = $year."-".$number;
|
62 |
-
break;
|
63 |
-
}
|
64 |
-
return $invoice_number;
|
65 |
-
}
|
66 |
-
// END SEQUENTIAL INVOICE NUMBERS
|
67 |
-
|
68 |
-
function generate_invoice($val, $id, $order){
|
69 |
-
$options = get_option('be_woocommerce_pdf_invoices');
|
70 |
-
|
71 |
-
if($id == $options['email_type'] || $id == $options['attach_to_new_order']){
|
72 |
-
|
73 |
-
$order_number = str_replace("#", "", $order->get_order_number());
|
74 |
-
|
75 |
-
// Update db with sequential invoice number
|
76 |
-
$this->set_sequential_invoice_number($order_number, $options['invoice_number_start']);
|
77 |
-
|
78 |
-
// The library for generating HTML PDF files
|
79 |
-
include(BEWPI_PLUGIN_DIR . '/mpdf/mpdf.php');
|
80 |
-
|
81 |
-
// Some PDF settings..
|
82 |
-
$mpdf = new mPDF('win-1252','A4','','',20,15,48,25,10,10);
|
83 |
-
$mpdf->useOnlyCoreFonts = true; // false is default
|
84 |
-
$mpdf->SetProtection(array('print'));
|
85 |
-
$mpdf->SetDisplayMode('fullpage');
|
86 |
-
|
87 |
-
// Dates
|
88 |
-
$today = date('d');
|
89 |
-
$month = date('m');
|
90 |
-
$year = date('Y');
|
91 |
-
//$current_date = date('d-m-Y');
|
92 |
-
|
93 |
-
// Addresses
|
94 |
-
$billing_address = $order->get_formatted_billing_address();
|
95 |
-
$shipping_address = $order->get_formatted_shipping_address();
|
96 |
-
|
97 |
-
// Money money moneeeeey
|
98 |
-
$order_total = $order->get_total();
|
99 |
-
$total_tax = $order->get_total_tax();
|
100 |
-
$order_subtotal = $order_total - $total_tax;
|
101 |
-
|
102 |
-
// Get the vat rates
|
103 |
-
if($options['vat_rates']){ $vat_rates = explode(',', $options['vat_rates']); }
|
104 |
-
|
105 |
-
// For displaying the table the wright way
|
106 |
-
$rowspan = count($vat_rates) + 4;
|
107 |
-
|
108 |
-
// Get invoice number from db and create format
|
109 |
-
$number = $this->get_invoice_number($order_number);
|
110 |
-
$invoice_number = $this->get_formatted_invoice_number($number, $options['invoice_number']);
|
111 |
-
|
112 |
-
// Yeah! Let's do it!
|
113 |
-
ob_start();
|
114 |
-
?>
|
115 |
-
<html>
|
116 |
-
<head>
|
117 |
-
<style>
|
118 |
-
body {
|
119 |
-
font-family: 'calibri';
|
120 |
-
font-size: 10pt;
|
121 |
-
}
|
122 |
-
p {
|
123 |
-
margin: 0pt;
|
124 |
-
}
|
125 |
-
td {
|
126 |
-
vertical-align: top;
|
127 |
-
}
|
128 |
-
.items td {
|
129 |
-
border-left: 0.1mm solid #000000;
|
130 |
-
border-right: 0.1mm solid #000000;
|
131 |
-
}
|
132 |
-
table thead td { background-color: #EEEEEE;
|
133 |
-
text-align: center;
|
134 |
-
border: 0.1mm solid #000000;
|
135 |
-
}
|
136 |
-
.items td.blanktotal {
|
137 |
-
background-color: #FFFFFF;
|
138 |
-
border: 0mm none #000000;
|
139 |
-
border-top: 0.1mm solid #000000;
|
140 |
-
border-right: 0.1mm solid #000000;
|
141 |
-
}
|
142 |
-
.items td.totals {
|
143 |
-
text-align: right;
|
144 |
-
border: 0.1mm solid #000000;
|
145 |
-
}
|
146 |
-
</style>
|
147 |
-
</head>
|
148 |
-
<body>
|
149 |
-
<htmlpageheader name="myheader">
|
150 |
-
<table width="100%">
|
151 |
-
<tr>
|
152 |
-
<td width="50%">
|
153 |
-
<?php if($options['file_upload'] != ''){ ?>
|
154 |
-
<img src="<?php echo $options['file_upload']; ?>"/><br /><br />
|
155 |
-
<?php }
|
156 |
-
else
|
157 |
-
{ ?>
|
158 |
-
<span style="font-size: 16pt; font-weight: bold;"><?php echo $options['company_name']; ?></span><br />
|
159 |
-
<?php echo $options['company_slogan']; ?><br /><br />
|
160 |
-
<?php }
|
161 |
-
echo nl2br($options['address']); ?><br />
|
162 |
-
</td>
|
163 |
-
<td width="50%" style="text-align: right;">
|
164 |
-
<span style="font-size:22pt;">
|
165 |
-
<?php _e( 'INVOICE', 'woocommerce-pdf-invoices' ); ?><br />
|
166 |
-
</span>
|
167 |
-
</td>
|
168 |
-
</tr>
|
169 |
-
<tr>
|
170 |
-
<td width="50%">
|
171 |
-
<?php echo nl2br($options['extra_company_info']); ?><br />
|
172 |
-
</td>
|
173 |
-
<td width="50%" style="font-size: 9pt; text-align:right;">
|
174 |
-
<?php _e( 'INVOICE NUMBER: ', 'woocommerce-pdf-invoices' ); echo $invoice_number; ?><br />
|
175 |
-
<?php printf( __( 'DATE: %02d-%02d-%04d', 'woocommerce-pdf-invoices' ), $today, $month, $year ); ?>
|
176 |
-
</td>
|
177 |
-
</tr>
|
178 |
-
</table>
|
179 |
-
<br/>
|
180 |
-
<br/>
|
181 |
-
<table width="100%">
|
182 |
-
<tr>
|
183 |
-
<td style="50%">
|
184 |
-
<span style="font-weight: bold;"><?php _e( 'TO:', 'woocommerce-pdf-invoices' ); ?></span><br />
|
185 |
-
<?php echo $billing_address; ?>
|
186 |
-
</td>
|
187 |
-
<td style="50%">
|
188 |
-
<span style="font-weight: bold;"><?php _e( 'SHIP TO:', 'woocommerce-pdf-invoices' ); ?></span><br />
|
189 |
-
<?php echo $shipping_address; ?>
|
190 |
-
</td>
|
191 |
-
</tr>
|
192 |
-
</table>
|
193 |
-
<br/>
|
194 |
-
<?php if($options['display_customer_notes'] == 'yes'){ ?>
|
195 |
-
<table>
|
196 |
-
<tr>
|
197 |
-
<td>
|
198 |
-
<span style="font-weight: bold;"><?php _e( 'Notes:', 'woocommerce-pdf-invoices' ); ?></span><br />
|
199 |
-
<?php echo $purchase_note; ?>
|
200 |
-
</td>
|
201 |
-
</tr>
|
202 |
-
</table>
|
203 |
-
<?php } ?>
|
204 |
-
<br/>
|
205 |
-
<br/>
|
206 |
-
<table class="items" width="100%" style="font-size: 9pt; border-collapse: collapse;" cellpadding="8">
|
207 |
-
<thead>
|
208 |
-
<tr style="font-weight: bold">
|
209 |
-
<?php if($options['display_SKU'] == 'yes'){
|
210 |
-
$colspan = 3; ?>
|
211 |
-
<td width="15%"><?php _e( 'SKU', 'woocommerce-pdf-invoices' ); ?></td>
|
212 |
-
<?php } else { $colspan = 2; } ?>
|
213 |
-
<td width="45%"><?php _e( 'Description', 'woocommerce-pdf-invoices' ); ?></td>
|
214 |
-
<td width="10%"><?php _e( 'Quantity', 'woocommerce-pdf-invoices' ); ?></td>
|
215 |
-
<td width="15%"><?php _e( 'Unit price', 'woocommerce-pdf-invoices' ); ?></td>
|
216 |
-
<td width="15%"><?php _e( 'Total', 'woocommerce-pdf-invoices' ); ?></td>
|
217 |
-
</tr>
|
218 |
-
</thead>
|
219 |
-
<tbody>
|
220 |
-
<!-- ITEMS HERE -->
|
221 |
-
<?php
|
222 |
-
$total_order_discount = $order->get_total_discount();
|
223 |
-
$order_subtotal_excl_tax =- $total_order_discount;
|
224 |
-
|
225 |
-
foreach ( $order->get_items() as $item ) {
|
226 |
-
$product = get_product($item['product_id']);
|
227 |
-
|
228 |
-
$item_tax = $order->get_item_tax($item, true);
|
229 |
-
$item_unit_price_incl_tax = $order->get_item_subtotal($item, false, false);
|
230 |
-
$item_unit_price_excl_tax = $item_unit_price_incl_tax - $item_tax;
|
231 |
-
$item_total_price_excl_tax = $item['qty'] * $item_unit_price_excl_tax;
|
232 |
-
|
233 |
-
$order_subtotal_excl_tax += $item_total_price_excl_tax;
|
234 |
-
?>
|
235 |
-
|
236 |
-
<tr>
|
237 |
-
<?php if($options['display_SKU'] == 'yes'){ ?>
|
238 |
-
<td align='center'><?php echo $product->get_sku(); ?></td>
|
239 |
-
<?php } ?>
|
240 |
-
<td><?php echo $item['name']; ?></td>
|
241 |
-
<td align='center'><?php echo $item['qty']; ?></td>
|
242 |
-
<td align='right'><?php echo woocommerce_price($item_unit_price_excl_tax); ?></td>
|
243 |
-
<td align='right'><?php echo woocommerce_price($item_total_price_excl_tax); ?></td>
|
244 |
-
</tr>
|
245 |
-
<?php } ?>
|
246 |
-
<!-- END ITEMS HERE -->
|
247 |
-
<tr>
|
248 |
-
<td class="blanktotal" colspan="<?php echo $colspan; ?>" rowspan="<?php echo $rowspan; ?>"></td>
|
249 |
-
<td class="totals"><?php _e( 'Discount', 'woocommerce-pdf-invoices' ); ?></td>
|
250 |
-
<td class="totals"><?php echo woocommerce_price($total_order_discount); ?></td>
|
251 |
-
</tr>
|
252 |
-
<tr>
|
253 |
-
<td class="totals"><?php _e( 'Subtotal', 'woocommerce-pdf-invoices' ); ?></td>
|
254 |
-
<td class="totals"><?php echo woocommerce_price($order_subtotal_excl_tax); ?></td>
|
255 |
-
</tr>
|
256 |
-
<tr>
|
257 |
-
<td class="totals"><?php _e( 'Shipping', 'woocommerce-pdf-invoices' ); ?></td>
|
258 |
-
<td class="totals"><?php echo woocommerce_price($order->get_shipping_to_display()); ?></td>
|
259 |
-
</tr>
|
260 |
-
<?php
|
261 |
-
if($vat_rates != 0){
|
262 |
-
foreach($vat_rates as $rate){ ?>
|
263 |
-
<tr>
|
264 |
-
<td class="totals"><?php printf( __( 'VAT %s%%', 'woocommerce-pdf-invoices'), $rate ); ?></td>
|
265 |
-
<td class="totals"><?php echo woocommerce_price(($order->get_total() / (100+$rate)) * $rate); ?></td>
|
266 |
-
</tr>
|
267 |
-
<?php }} ?>
|
268 |
-
<tr>
|
269 |
-
<td class="totals"><strong><?php _e( 'Grand Total', 'woocommerce-pdf-invoices' ); ?></strong></td>
|
270 |
-
<td class="totals"><?php echo woocommerce_price($order->get_total()); ?></td>
|
271 |
-
</tr>
|
272 |
-
</tbody>
|
273 |
-
</table>
|
274 |
-
<br />
|
275 |
-
<br />
|
276 |
-
<table style="text-align: left; font-style: italic;">
|
277 |
-
<tr>
|
278 |
-
<td>
|
279 |
-
<?php echo nl2br($options['extra_info']); ?>
|
280 |
-
</td>
|
281 |
-
</tr>
|
282 |
-
</table>
|
283 |
-
</htmlpageheader>
|
284 |
-
<htmlpagefooter name="myfooter">
|
285 |
-
<div style="font-size: 9pt; text-align: center; padding-top: 3mm; ">
|
286 |
-
<?php printf( __( 'Page %s of %s', 'woocommerce-pdf-invoices' ), "{PAGENO}", "{nb}"); ?>
|
287 |
-
</div>
|
288 |
-
</htmlpagefooter>
|
289 |
-
|
290 |
-
<sethtmlpageheader name="myheader" value="on" show-this-page="1" />
|
291 |
-
<sethtmlpagefooter name="myfooter" value="on" />
|
292 |
-
</body>
|
293 |
-
</html>
|
294 |
-
|
295 |
-
<?php
|
296 |
-
$output = ob_get_contents();
|
297 |
-
ob_end_clean();
|
298 |
-
|
299 |
-
// Do the trick!
|
300 |
-
$mpdf->WriteHTML($output);
|
301 |
-
|
302 |
-
// Get upload folder and create filename.
|
303 |
-
$uploads_dir = WP_CONTENT_DIR . '/uploads';
|
304 |
-
$filename = '/' . $invoice_number . '.pdf';
|
305 |
-
$full_path = $uploads_dir.$filename;
|
306 |
-
|
307 |
-
// Upload invoice
|
308 |
-
$mpdf->Output($full_path, 'F');
|
309 |
-
|
310 |
-
return $full_path;
|
311 |
-
}
|
312 |
-
else
|
313 |
-
{
|
314 |
-
return "";
|
315 |
-
}
|
316 |
-
}
|
317 |
-
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
includes/classes/wpi-invoice.php
ADDED
@@ -0,0 +1,427 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit; // Exit if accessed directly
|
4 |
+
}
|
5 |
+
|
6 |
+
if ( ! class_exists( 'WPI_Invoice' ) ) {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Makes the invoice.
|
10 |
+
* Class WPI_Invoice
|
11 |
+
*/
|
12 |
+
class WPI_Invoice {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* WooCommerce order
|
16 |
+
* @var string
|
17 |
+
*/
|
18 |
+
private $order;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Textdomain from the plugin.
|
22 |
+
* @var
|
23 |
+
*/
|
24 |
+
private $textdomain;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* All settings from general tab.
|
28 |
+
* @var array
|
29 |
+
*/
|
30 |
+
private $general_settings;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* All settings from template tab.
|
34 |
+
* @var array
|
35 |
+
*/
|
36 |
+
private $template_settings;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Invoice number
|
40 |
+
* @var
|
41 |
+
*/
|
42 |
+
private $number;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Formatted invoice number with prefix and/or suffix
|
46 |
+
* @var
|
47 |
+
*/
|
48 |
+
private $formatted_number;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Invoice number database meta key
|
52 |
+
* @var string
|
53 |
+
*/
|
54 |
+
private $invoice_number_meta_key = '_bewpi_invoice_number';
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Path to invoice in tmp dir.
|
58 |
+
* @var
|
59 |
+
*/
|
60 |
+
private $file;
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Creation date.
|
64 |
+
* @var
|
65 |
+
*/
|
66 |
+
private $date;
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Initialize invoice with WooCommerce order and plugin textdomain.
|
70 |
+
* @param string $order
|
71 |
+
* @param $textdomain
|
72 |
+
*/
|
73 |
+
public function __construct($order = '', $textdomain) {
|
74 |
+
$this->order = $order;
|
75 |
+
$this->textdomain = $textdomain;
|
76 |
+
$this->general_settings = (array)get_option('general_settings');
|
77 |
+
$this->template_settings = (array)get_option('template_settings');
|
78 |
+
|
79 |
+
$this->init();
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Gets all the existing invoice data from database or creates new invoice number.
|
84 |
+
*/
|
85 |
+
private function init() {
|
86 |
+
$this->number = get_post_meta($this->order->id, '_bewpi_invoice_number', true);
|
87 |
+
$this->formatted_number = get_post_meta($this->order->id, '_bewpi_formatted_invoice_number', true);
|
88 |
+
$this->date = get_post_meta($this->order->id, '_bewpi_invoice_date', true);
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Gets next invoice number based on the user input.
|
93 |
+
* @param $order_id
|
94 |
+
*/
|
95 |
+
function get_next_invoice_number($last_invoice_number) {
|
96 |
+
// Check if it has been the first of january.
|
97 |
+
if ($this->template_settings['reset_invoice_number']) {
|
98 |
+
$last_year = $this->template_settings['last_invoiced_year'];
|
99 |
+
|
100 |
+
if ( !empty( $last_year ) && is_numeric($last_year)) {
|
101 |
+
$current_year = getdate()['year'];
|
102 |
+
if ($last_year < $current_year) {
|
103 |
+
// Set new year as last invoiced year and reset invoice number
|
104 |
+
return 1;
|
105 |
+
}
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
// Check if the next invoice number should be used.
|
110 |
+
$next_invoice_number = $this->template_settings['next_invoice_number'];
|
111 |
+
if ( !empty( $next_invoice_number )
|
112 |
+
&& empty( $last_invoice_number )
|
113 |
+
|| $next_invoice_number > $last_invoice_number) {
|
114 |
+
return $next_invoice_number;
|
115 |
+
}
|
116 |
+
|
117 |
+
return $last_invoice_number;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Create invoice date
|
122 |
+
* @return bool|string
|
123 |
+
*/
|
124 |
+
public function create_formatted_date() {
|
125 |
+
$date_format = $this->template_settings['invoice_date_format'];
|
126 |
+
//$date = DateTime::createFromFormat('Y-m-d H:i:s', $this->order->order_date);
|
127 |
+
//$date = date( $date_format );
|
128 |
+
|
129 |
+
if ($date_format != "") {
|
130 |
+
//$formatted_date = $date->format($date_format);
|
131 |
+
$formatted_date = date($date_format);
|
132 |
+
} else {
|
133 |
+
//$formatted_date = $date->format($date, "d-m-Y");
|
134 |
+
$formatted_date = date('d-m-Y');
|
135 |
+
}
|
136 |
+
|
137 |
+
add_post_meta($this->order->id, '_bewpi_invoice_date', $formatted_date);
|
138 |
+
|
139 |
+
return $formatted_date;
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Creates new invoice number with SQL MAX CAST.
|
144 |
+
* @param $order_id
|
145 |
+
* @param $number
|
146 |
+
*/
|
147 |
+
function create_invoice_number($next_number) {
|
148 |
+
global $wpdb;
|
149 |
+
|
150 |
+
// attempt the query up to 3 times for a much higher success rate if it fails (due to Deadlock)
|
151 |
+
$success = false;
|
152 |
+
for ($i = 0; $i < 3 && !$success; $i++) {
|
153 |
+
// this seems to me like the safest way to avoid order number clashes
|
154 |
+
$query = $wpdb->prepare(
|
155 |
+
"
|
156 |
+
INSERT INTO {$wpdb->postmeta} (post_id, meta_key, meta_value)
|
157 |
+
SELECT %d, %s, IF( MAX( CAST( meta_value as UNSIGNED ) ) IS NULL, %d, MAX( CAST( meta_value as UNSIGNED ) ) + 1 )
|
158 |
+
FROM {$wpdb->postmeta}
|
159 |
+
WHERE meta_key = %s
|
160 |
+
",
|
161 |
+
$this->order->id, $this->invoice_number_meta_key, $next_number, $this->invoice_number_meta_key
|
162 |
+
);
|
163 |
+
$success = $wpdb->query($query);
|
164 |
+
}
|
165 |
+
|
166 |
+
return $success;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Format the invoice number with prefix and/or suffix.
|
171 |
+
* @return mixed
|
172 |
+
*/
|
173 |
+
private function format_invoice_number() {
|
174 |
+
$invoice_number_format = $this->template_settings['invoice_format'];
|
175 |
+
$digit_str = "%0" . $this->template_settings['invoice_number_digits'] . "s";
|
176 |
+
$this->number = sprintf($digit_str, $this->number);
|
177 |
+
|
178 |
+
$invoice_number_format = str_replace(
|
179 |
+
array('[prefix]', '[suffix]', '[number]'),
|
180 |
+
array($this->template_settings['invoice_prefix'], $this->template_settings['invoice_suffix'], $this->number),
|
181 |
+
$invoice_number_format);
|
182 |
+
|
183 |
+
add_post_meta($this->order->id, '_bewpi_formatted_invoice_number', $invoice_number_format);
|
184 |
+
|
185 |
+
return $invoice_number_format;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* When an invoice gets generated again then the post meta needs to get deleted.
|
190 |
+
*/
|
191 |
+
public function delete_all_post_meta() {
|
192 |
+
delete_post_meta( $this->order->id, '_bewpi_invoice_number' );
|
193 |
+
delete_post_meta( $this->order->id, '_bewpi_formatted_invoice_number' );
|
194 |
+
delete_post_meta( $this->order->id, '_bewpi_invoice_date' );
|
195 |
+
}
|
196 |
+
|
197 |
+
/**
|
198 |
+
* Generates the invoice with MPDF lib.
|
199 |
+
* @param $dest
|
200 |
+
* @return string
|
201 |
+
*/
|
202 |
+
public function generate($dest) {
|
203 |
+
if( !$this->exists() ) {
|
204 |
+
|
205 |
+
$last_invoice_number = $this->template_settings['last_invoice_number'];
|
206 |
+
|
207 |
+
// Get the up following invoice number
|
208 |
+
$next_invoice_number = $this->get_next_invoice_number($last_invoice_number);
|
209 |
+
|
210 |
+
// Create new invoice number and insert into database.
|
211 |
+
$invoice_number_created = $this->create_invoice_number($next_invoice_number);
|
212 |
+
|
213 |
+
if( $invoice_number_created ) {
|
214 |
+
// Set the current year as the last invoiced.
|
215 |
+
$this->template_settings['last_invoiced_year'] = getdate()['year'];
|
216 |
+
|
217 |
+
// Get the new invoice number from db.
|
218 |
+
$this->number = $this->get_invoice_number();
|
219 |
+
$this->template_settings['last_invoice_number'] = $this->number;
|
220 |
+
|
221 |
+
$this->formatted_number = $this->format_invoice_number();
|
222 |
+
|
223 |
+
update_option('template_settings', $this->template_settings);
|
224 |
+
|
225 |
+
$this->date = $this->create_formatted_date();
|
226 |
+
|
227 |
+
// Go generate
|
228 |
+
set_time_limit(0);
|
229 |
+
include WPI_DIR . "lib/mpdf/mpdf.php";
|
230 |
+
|
231 |
+
$mpdf = new mPDF('', 'A4', 0, '', 17, 17, 20, 50, 0, 0, '');
|
232 |
+
$mpdf->useOnlyCoreFonts = true; // false is default
|
233 |
+
$mpdf->SetTitle(($this->template_settings['company_name'] != "") ? $this->template_settings['company_name'] . " - Invoice" : "Invoice");
|
234 |
+
$mpdf->SetAuthor(($this->template_settings['company_name'] != "") ? $this->template_settings['company_name'] : "");
|
235 |
+
$mpdf->showWatermarkText = false;
|
236 |
+
$mpdf->SetDisplayMode('fullpage');
|
237 |
+
$mpdf->useSubstitutions = false;
|
238 |
+
|
239 |
+
ob_start();
|
240 |
+
|
241 |
+
require_once $this->get_template();
|
242 |
+
|
243 |
+
$html = ob_get_contents();
|
244 |
+
|
245 |
+
ob_end_clean();
|
246 |
+
|
247 |
+
$footer = $this->get_footer();
|
248 |
+
|
249 |
+
$mpdf->SetHTMLFooter($footer);
|
250 |
+
|
251 |
+
$mpdf->WriteHTML($html);
|
252 |
+
|
253 |
+
$file = WPI_TMP_DIR . $this->formatted_number . ".pdf";
|
254 |
+
|
255 |
+
$mpdf->Output($file, $dest);
|
256 |
+
|
257 |
+
return $file;
|
258 |
+
}
|
259 |
+
} else {
|
260 |
+
die('Invoice already exists.');
|
261 |
+
}
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Get the invoice if exist and show.
|
266 |
+
* @param $download
|
267 |
+
*/
|
268 |
+
public function view_invoice($download) {
|
269 |
+
if ($this->exists()) {
|
270 |
+
$file = WPI_TMP_DIR . $this->formatted_number . ".pdf";
|
271 |
+
$filename = $this->formatted_number . ".pdf";
|
272 |
+
|
273 |
+
if ($download) {
|
274 |
+
header('Content-type: application / pdf');
|
275 |
+
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
276 |
+
header('Content-Transfer-Encoding: binary');
|
277 |
+
header('Content-Length: ' . filesize($file));
|
278 |
+
header('Accept-Ranges: bytes');
|
279 |
+
} else {
|
280 |
+
header('Content-type: application/pdf');
|
281 |
+
header('Content-Disposition: inline; filename="' . $filename . '"');
|
282 |
+
header('Content-Transfer-Encoding: binary');
|
283 |
+
header('Accept-Ranges: bytes');
|
284 |
+
}
|
285 |
+
|
286 |
+
@readfile($file);
|
287 |
+
exit;
|
288 |
+
|
289 |
+
} else {
|
290 |
+
die('No invoice found.');
|
291 |
+
}
|
292 |
+
}
|
293 |
+
|
294 |
+
/**
|
295 |
+
* Delete invoice from tmp dir.
|
296 |
+
*/
|
297 |
+
public function delete() {
|
298 |
+
if ($this->exists()) {
|
299 |
+
unlink($this->file);
|
300 |
+
$this->delete_all_post_meta();
|
301 |
+
}
|
302 |
+
}
|
303 |
+
|
304 |
+
/**
|
305 |
+
* Checks if the invoice exists.
|
306 |
+
* @return bool
|
307 |
+
*/
|
308 |
+
public function exists() {
|
309 |
+
$this->file = WPI_TMP_DIR . $this->get_formatted_invoice_number() . ".pdf";
|
310 |
+
return file_exists($this->file);
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Returns MPDF footer.
|
315 |
+
* @return string
|
316 |
+
*/
|
317 |
+
function get_footer() {
|
318 |
+
ob_start(); ?>
|
319 |
+
|
320 |
+
<table class="foot">
|
321 |
+
<tbody>
|
322 |
+
<tr>
|
323 |
+
<td class="border" colspan="2">
|
324 |
+
<?php echo $this->template_settings['terms']; ?><br/>
|
325 |
+
<?php if (count($this->order->get_customer_order_notes()) > 0) { ?>
|
326 |
+
<p>
|
327 |
+
<strong><?php _e('Customer note', $this->textdomain); ?> </strong><?php echo $this->order->get_customer_order_notes()[0]->comment_content; ?>
|
328 |
+
</p>
|
329 |
+
<?php } ?>
|
330 |
+
</td>
|
331 |
+
</tr>
|
332 |
+
<tr>
|
333 |
+
<td class="company-details">
|
334 |
+
<p>
|
335 |
+
<?php echo nl2br($this->template_settings['company_details']); ?>
|
336 |
+
</p>
|
337 |
+
</td>
|
338 |
+
<td class="payment">
|
339 |
+
<p>
|
340 |
+
<strong>Payment</strong> via <?php echo $this->order->payment_method_title; ?>
|
341 |
+
</p>
|
342 |
+
</td>
|
343 |
+
</tr>
|
344 |
+
</tbody>
|
345 |
+
</table>
|
346 |
+
|
347 |
+
<?php $html = ob_get_contents();
|
348 |
+
|
349 |
+
ob_end_clean();
|
350 |
+
|
351 |
+
return $html;
|
352 |
+
}
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Get's the invoice number from db.
|
356 |
+
* @param $order_id
|
357 |
+
* @return mixed
|
358 |
+
*/
|
359 |
+
function get_invoice_number() {
|
360 |
+
global $wpdb;
|
361 |
+
|
362 |
+
$results = $wpdb->get_results(
|
363 |
+
$wpdb->prepare(
|
364 |
+
"
|
365 |
+
SELECT meta_value
|
366 |
+
FROM $wpdb->postmeta
|
367 |
+
WHERE post_id = %d
|
368 |
+
AND meta_key = %s
|
369 |
+
", $this->order->id, $this->invoice_number_meta_key
|
370 |
+
)
|
371 |
+
);
|
372 |
+
|
373 |
+
if (count($results) == 1) {
|
374 |
+
return $results[0]->meta_value;
|
375 |
+
}
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* Getter for formatted invoice number.
|
380 |
+
* @return mixed
|
381 |
+
*/
|
382 |
+
public function get_formatted_invoice_number() {
|
383 |
+
return $this->formatted_number;
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Getter for formatted date.
|
388 |
+
* @return mixed
|
389 |
+
*/
|
390 |
+
public function get_formatted_date() {
|
391 |
+
return $this->date;
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* Gets the year from the WooCommerce order date.
|
396 |
+
* @return bool|string
|
397 |
+
*/
|
398 |
+
public function get_formatted_order_year() {
|
399 |
+
return date("Y", strtotime($this->order->order_date));
|
400 |
+
}
|
401 |
+
|
402 |
+
/**
|
403 |
+
* Gets the template from template dir.
|
404 |
+
* @return string
|
405 |
+
*/
|
406 |
+
private function get_template() {
|
407 |
+
return WPI_TEMPLATES_DIR . $this->template_settings['template_filename'];
|
408 |
+
}
|
409 |
+
|
410 |
+
/**
|
411 |
+
* Gets the file path.
|
412 |
+
* @return mixed
|
413 |
+
*/
|
414 |
+
public function get_file() {
|
415 |
+
return $this->file;
|
416 |
+
}
|
417 |
+
|
418 |
+
/**
|
419 |
+
* Get total with or without refunds
|
420 |
+
*/
|
421 |
+
public function get_formatted_total() {
|
422 |
+
if( $this->order->get_total_refunded() > 0 ) {
|
423 |
+
$total = wc_price( $this->order->get_total() - $this->order->get_total_refunded() );
|
424 |
+
}
|
425 |
+
}
|
426 |
+
}
|
427 |
+
}
|
includes/plugin.php
DELETED
@@ -1,47 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
function bewpi_get_options(){
|
4 |
-
static $options;
|
5 |
-
|
6 |
-
if(!$options){
|
7 |
-
$defaults = array(
|
8 |
-
'email_type' => '',
|
9 |
-
'attach_to_new_order' => '',
|
10 |
-
'company_name' => get_option('woocommerce_email_from_name'),
|
11 |
-
'company_slogan' => '',
|
12 |
-
'file_upload' => '',
|
13 |
-
'address' => '',
|
14 |
-
'zip_code' => '',
|
15 |
-
'city' => '',
|
16 |
-
'country' => '',
|
17 |
-
'telephone' => '',
|
18 |
-
'email' => '',
|
19 |
-
'extra_company_info' => '',
|
20 |
-
'extra_info' => '',
|
21 |
-
'vat_rates' => '',
|
22 |
-
'display_customer_notes' => '',
|
23 |
-
'display_SKU' => '',
|
24 |
-
'invoice_number' => '',
|
25 |
-
'invoice_number_start' => '1',
|
26 |
-
);
|
27 |
-
|
28 |
-
// Get options from database.
|
29 |
-
$db_option = get_option('be_woocommerce_pdf_invoices', array());
|
30 |
-
|
31 |
-
if(!$db_option) {
|
32 |
-
update_option('be_woocommerce_pdf_invoices', $defaults);
|
33 |
-
}
|
34 |
-
|
35 |
-
$options = wp_parse_args($db_option, $defaults);
|
36 |
-
}
|
37 |
-
|
38 |
-
return $options;
|
39 |
-
}
|
40 |
-
|
41 |
-
function bewpi_load_textdomain() {
|
42 |
-
load_plugin_textdomain('woocommerce-pdf-invoices', false, 'woocommerce-pdf-invoices/lang/' );
|
43 |
-
}
|
44 |
-
|
45 |
-
add_action('plugins_loaded', 'bewpi_load_textdomain');
|
46 |
-
|
47 |
-
?>
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
includes/views/settings-page.php
DELETED
@@ -1,153 +0,0 @@
|
|
1 |
-
<script type="text/javascript">
|
2 |
-
function removeImage(){
|
3 |
-
var elem = document.getElementById('custom-logo-wrap');
|
4 |
-
elem.parentNode.removeChild(elem);
|
5 |
-
document.getElementById('hiddenField').value = '';
|
6 |
-
}
|
7 |
-
</script>
|
8 |
-
<div class="wrap">
|
9 |
-
<h2>WooCommerce PDF Invoices</h2>
|
10 |
-
<form method="post" action="options.php" enctype="multipart/form-data">
|
11 |
-
<?php settings_fields( 'be_woocommerce_pdf_invoices' );
|
12 |
-
do_settings_sections( 'be_woocommerce_pdf_invoices' );
|
13 |
-
$today = date('d');
|
14 |
-
$month = date('m');
|
15 |
-
$year = date('Y');?>
|
16 |
-
<h3>General</h3>
|
17 |
-
<style>
|
18 |
-
br {display:none;}
|
19 |
-
</style>
|
20 |
-
<table class="form-table">
|
21 |
-
<tr valign="top">
|
22 |
-
<th scope="row"><strong>Email</strong></th>
|
23 |
-
<td>
|
24 |
-
<select id="selectEmailType" name="be_woocommerce_pdf_invoices[email_type]">
|
25 |
-
<option>-- Select --</option>
|
26 |
-
<option value="customer_processing_order" <?php selected($options['email_type'], 'customer_processing_order'); ?>>Customer processing order</option>
|
27 |
-
<option value="customer_completed_order" <?php selected($options['email_type'], 'customer_completed_order'); ?>>Customer completed order</option>
|
28 |
-
<option value="customer_invoice" <?php selected($options['email_type'], 'customer_invoice'); ?>>Customer invoice</option>
|
29 |
-
</select>
|
30 |
-
<div class="notes">
|
31 |
-
Select the type of email to attach the invoice to.
|
32 |
-
</div>
|
33 |
-
</td>
|
34 |
-
</tr>
|
35 |
-
<tr valign="top">
|
36 |
-
<th scope="row"><strong>Attach to New Order email type</strong></th>
|
37 |
-
<td>
|
38 |
-
<select id="selectAdminNewOrder" name="be_woocommerce_pdf_invoices[attach_to_new_order]">
|
39 |
-
<option value="new_order" <?php selected($options['attach_to_new_order'], 'new_order'); ?>>Yes</option>
|
40 |
-
<option value="no" <?php selected($options['attach_to_new_order'], 'no'); ?>>No</option>
|
41 |
-
</select>
|
42 |
-
<div class="notes">
|
43 |
-
Determine to attach the invoice to the "New Order" email type for bookkeeping purposes.
|
44 |
-
</div>
|
45 |
-
</td>
|
46 |
-
</tr>
|
47 |
-
<th scope="row"><strong>Invoice number format & starting point</strong></th><br/>
|
48 |
-
<td>
|
49 |
-
<select name="be_woocommerce_pdf_invoices[invoice_number]">
|
50 |
-
<option value="1" <?php selected($options['invoice_number'], '1'); ?>><?php echo "0001"; ?></option>
|
51 |
-
<option value="2" <?php selected($options['invoice_number'], '2'); ?>><?php echo $year.$month.$today."-0001"; ?></option>
|
52 |
-
<option value="3" <?php selected($options['invoice_number'], '3'); ?>><?php echo substr($year, -2)."-0001"; ?></option>
|
53 |
-
</select>
|
54 |
-
<input id="startAt" type="text" name="be_woocommerce_pdf_invoices[invoice_number_start]" value="<?php echo $options['invoice_number_start']; ?>" />
|
55 |
-
<div class="notes">Choose how to generate the invoice number by selecting one of the example formats. Optionally add number to start count.</div>
|
56 |
-
</td>
|
57 |
-
</tr>
|
58 |
-
<tr valign="top">
|
59 |
-
<th scope="row"><strong>VAT rates</strong></th>
|
60 |
-
<td>
|
61 |
-
<input type="text" name="be_woocommerce_pdf_invoices[vat_rates]" value="<?php echo $options['vat_rates'];?>"/><br/>
|
62 |
-
<div class="notes">Add tax rates seperated by comma. These rates will be calculated based upon the subtotal price.</div>
|
63 |
-
</td>
|
64 |
-
</tr>
|
65 |
-
<tr valign="top">
|
66 |
-
<th scope="row"><strong>Display customer notes</strong></th>
|
67 |
-
<td>
|
68 |
-
<select id="selectDisplayCustomerNotes" name="be_woocommerce_pdf_invoices[display_customer_notes]">
|
69 |
-
<option value="yes" <?php selected($options['display_customer_notes'], 'yes'); ?>>Yes</option>
|
70 |
-
<option value="no" <?php selected($options['display_customer_notes'], 'no'); ?>>No</option>
|
71 |
-
</select>
|
72 |
-
<div class="notes">
|
73 |
-
Choose to display customer notes.
|
74 |
-
</div>
|
75 |
-
</td>
|
76 |
-
</tr>
|
77 |
-
<th scope="row"><strong>Display SKU</strong></th>
|
78 |
-
<td>
|
79 |
-
<select id="selectDisplaySKU" name="be_woocommerce_pdf_invoices[display_SKU]">
|
80 |
-
<option value="yes" <?php selected($options['display_SKU'], 'yes'); ?>>Yes</option>
|
81 |
-
<option value="no" <?php selected($options['display_SKU'], 'no'); ?>>No</option>
|
82 |
-
</select>
|
83 |
-
<div class="notes">
|
84 |
-
Choose to display SKU into table.
|
85 |
-
</div>
|
86 |
-
</td>
|
87 |
-
</tr>
|
88 |
-
</table>
|
89 |
-
<h3>Template</h3>
|
90 |
-
<table class="form-table">
|
91 |
-
<tr valign="top">
|
92 |
-
<th scope="row"><strong>Company name</strong></th>
|
93 |
-
<td>
|
94 |
-
<input required type="text" size="40" name="be_woocommerce_pdf_invoices[company_name]" value="<?php echo $options['company_name']; ?>" /><br/>
|
95 |
-
<div class="notes">Add your company name here.</div>
|
96 |
-
</td>
|
97 |
-
</tr>
|
98 |
-
<tr valign="top">
|
99 |
-
<th scope="row"><strong>Company slogan</strong></th>
|
100 |
-
<td>
|
101 |
-
<input type="text" size="40" name="be_woocommerce_pdf_invoices[company_slogan]" value="<?php echo $options['company_slogan']; ?>" /><br/>
|
102 |
-
<div class="notes">Add your company slogan here. You can leave it blank.</div>
|
103 |
-
</td>
|
104 |
-
</tr>
|
105 |
-
<tr valign="top">
|
106 |
-
<th scope="row"><strong>Custom logo</strong></th>
|
107 |
-
<td>
|
108 |
-
<input id="uploadFile" type="file" name="logo" accept="image/*" /><br />
|
109 |
-
<div class="notes">Add your custom company logo. Please upload image with a size behond 50kB. You don't have to upload any, the plugin will use your company name.</div>
|
110 |
-
<?php if($options['file_upload'] != ''){ ?>
|
111 |
-
<div id="custom-logo-wrap">
|
112 |
-
<img id="custom-logo" src="<?php echo esc_attr($options['file_upload']); ?>" /><br/ >
|
113 |
-
<a href="#" title="Remove custom logo" onclick="removeImage();">
|
114 |
-
<img id="delete" src="<?php echo plugins_url().'/woocommerce/assets/images/icons/delete_10.png'?>" />
|
115 |
-
</a>
|
116 |
-
</div>
|
117 |
-
<?php } ?>
|
118 |
-
<input type="hidden" id="hiddenField" name="be_woocommerce_pdf_invoices[file_upload]" value="<?php echo esc_attr($options['file_upload']); ?>" />
|
119 |
-
</td>
|
120 |
-
</tr>
|
121 |
-
<tr valign="top">
|
122 |
-
<th scope="row"><strong>Company address</strong></th>
|
123 |
-
<td>
|
124 |
-
<textarea required name="be_woocommerce_pdf_invoices[address]" ><?php echo esc_textarea($options['address']); ?></textarea><br/>
|
125 |
-
<div class="notes">Add your company address here.</div>
|
126 |
-
</td>
|
127 |
-
</tr>
|
128 |
-
<tr valign="top">
|
129 |
-
<th scope="row"><strong>Additional company information</strong></th>
|
130 |
-
<td>
|
131 |
-
<textarea name="be_woocommerce_pdf_invoices[extra_company_info]" rows=6 cols=120 ><?php echo esc_textarea($options['extra_company_info']); ?></textarea><br/>
|
132 |
-
<div class="notes">Add some additional company information like a email address, telephone number, company number and tax number. You can leave it blank.</div>
|
133 |
-
</td>
|
134 |
-
</tr>
|
135 |
-
<tr valign="top">
|
136 |
-
<th scope="row"><strong>Refunds policy, conditions etc.</strong></th>
|
137 |
-
<td>
|
138 |
-
<textarea name="be_woocommerce_pdf_invoices[extra_info]" rows=6 cols=120 ><?php echo esc_textarea($options['extra_info']);?></textarea><br/>
|
139 |
-
<div class="notes">Add some policies, conditions etc. It will be placed beyond the products table. You can leave it blank.</div>
|
140 |
-
</td>
|
141 |
-
</tr>
|
142 |
-
</table>
|
143 |
-
<?php submit_button(); ?>
|
144 |
-
<p>
|
145 |
-
Thank you for using my FREE plugin. If you have any suggestions, please use the
|
146 |
-
<a href="http://wordpress.org/support/plugin/woocommerce-pdf-invoices"/>support forum</a> or feel free to contact me right away.
|
147 |
-
Please <a href="http://wordpress.org/support/view/plugin-reviews/woocommerce-pdf-invoices?rate=5#postform"/>rate</a> with 5 stars as a service on return.
|
148 |
-
</p>
|
149 |
-
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=baselbers%40hotmail%2ecom&lc=NL&item_name=WooCommerce%20PDF%20Invoices¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
|
150 |
-
<img alt="Donate Button" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" />
|
151 |
-
</a>
|
152 |
-
</form>
|
153 |
-
</div>
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
includes/views/templates/invoice-flat.php
ADDED
@@ -0,0 +1,281 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<style>
|
2 |
+
/* Page template CSS */
|
3 |
+
#post-2 header, #post-2 footer {
|
4 |
+
display: none;
|
5 |
+
}
|
6 |
+
h4 {
|
7 |
+
color: #32373A;
|
8 |
+
border-bottom: 2px solid #E4E7E9;
|
9 |
+
padding: 0 0 3px 0; margin: 0;
|
10 |
+
text-transform: uppercase;
|
11 |
+
}
|
12 |
+
p {
|
13 |
+
margin: 8px 0 0 0; padding: 0 ;
|
14 |
+
}
|
15 |
+
/* Invoice template 1 CSS */
|
16 |
+
#container {
|
17 |
+
min-height: 100%;
|
18 |
+
position: relative;
|
19 |
+
color: #32373A;
|
20 |
+
}
|
21 |
+
#body {
|
22 |
+
padding: 40px 40px 0;
|
23 |
+
}
|
24 |
+
.row {
|
25 |
+
width: 100%;
|
26 |
+
margin-bottom: 30px;
|
27 |
+
}
|
28 |
+
.logo {
|
29 |
+
width: 100%;
|
30 |
+
display: table;
|
31 |
+
}
|
32 |
+
.logo-wrapper {
|
33 |
+
display: table-cell;
|
34 |
+
text-align: center;
|
35 |
+
vertical-align: middle;
|
36 |
+
font-size: 18px;
|
37 |
+
}
|
38 |
+
.company-logo {
|
39 |
+
max-height: 150px;
|
40 |
+
}
|
41 |
+
.coupon, .title, #invoice-number {
|
42 |
+
text-align: center;
|
43 |
+
}
|
44 |
+
.intro {
|
45 |
+
font-size: 16px;
|
46 |
+
text-align: center;
|
47 |
+
}
|
48 |
+
#expires {
|
49 |
+
font-size: 12px;
|
50 |
+
}
|
51 |
+
.coupon {
|
52 |
+
padding-left: 40px;
|
53 |
+
padding-right: 40px;
|
54 |
+
background-color: #F8F8F8;
|
55 |
+
color: #32373B;
|
56 |
+
}
|
57 |
+
#coupon-code {
|
58 |
+
margin: 0 auto;
|
59 |
+
border: 1px dashed #F8F8F8;
|
60 |
+
padding: 10px;
|
61 |
+
background-color: #52AF68;
|
62 |
+
color: white;
|
63 |
+
font-weight: bold;
|
64 |
+
}
|
65 |
+
.title {
|
66 |
+
margin: 0;
|
67 |
+
padding: 20px;
|
68 |
+
color: white;
|
69 |
+
background-color: #52AF68;
|
70 |
+
}
|
71 |
+
.title h1, .title span {
|
72 |
+
margin: 0; padding: 0;
|
73 |
+
}
|
74 |
+
#invoice-number {
|
75 |
+
padding: 20px;
|
76 |
+
background-color: #387747;
|
77 |
+
color: white;
|
78 |
+
font-size: 14px;
|
79 |
+
}
|
80 |
+
table.products {
|
81 |
+
padding: 20px 20px;
|
82 |
+
width: 100%;
|
83 |
+
background-color: #F8F8F8;
|
84 |
+
/*margin-bottom: 20px;*/
|
85 |
+
}
|
86 |
+
td, th {
|
87 |
+
padding: 10px;
|
88 |
+
text-align: right;
|
89 |
+
font-weight: bold;
|
90 |
+
vertical-align: middle;
|
91 |
+
font-size: 14px;
|
92 |
+
text-transform: uppercase;
|
93 |
+
}
|
94 |
+
tr.border-bottom td, td.border-bottom {
|
95 |
+
border-bottom: 1px solid #E4E7E9;
|
96 |
+
}
|
97 |
+
tr.border-bottom th {
|
98 |
+
border-bottom: 3px solid #E4E7E9;
|
99 |
+
}
|
100 |
+
.align-left { text-align: left; }
|
101 |
+
.align-center { text-align: center; }
|
102 |
+
.align-right { text-align: right; }
|
103 |
+
.normalcase { text-transform: none; }
|
104 |
+
.uppercase { text-transform: uppercase; }
|
105 |
+
.circle { border-radius: 50%; }
|
106 |
+
.two-column { width: 40%; text-transform: uppercase; padding: 20px 30px; background-color: #F8F8F8; }
|
107 |
+
.one-column { width: 100%; padding: 20px 30px; background-color: #F8F8F8; }
|
108 |
+
.left { float: left; text-align: left; }
|
109 |
+
.right { float: right; text-align: right; }
|
110 |
+
.border-bottom-non-product { border-bottom: 2px solid #E4E7E9; }
|
111 |
+
.product td {
|
112 |
+
font-weight: normal;
|
113 |
+
}
|
114 |
+
.total {
|
115 |
+
border-bottom: 3px solid #E4E7E9;
|
116 |
+
}
|
117 |
+
.discount td, .subtotal td, .tax td, .shipping td, .total td {
|
118 |
+
font-weight: bold;
|
119 |
+
}
|
120 |
+
.payment-method{
|
121 |
+
font-size: 12px;
|
122 |
+
font-weight: normal;
|
123 |
+
}
|
124 |
+
.payment-method {
|
125 |
+
vertical-align: middle;
|
126 |
+
}
|
127 |
+
#footer {
|
128 |
+
width: 100%;
|
129 |
+
font-size: 14px;
|
130 |
+
background-color: #3A3F43;
|
131 |
+
color: white;
|
132 |
+
padding: 40px;
|
133 |
+
}
|
134 |
+
.questions {
|
135 |
+
float: left;
|
136 |
+
width: 40%;
|
137 |
+
}
|
138 |
+
.company-address {
|
139 |
+
float: right;
|
140 |
+
text-align: right;
|
141 |
+
width: 35%;
|
142 |
+
}
|
143 |
+
</style>
|
144 |
+
<div id="container">
|
145 |
+
<div id="body">
|
146 |
+
<div class="row logo">
|
147 |
+
<div class="logo-wrapper">
|
148 |
+
<!--<div id="company-logo" class="circle uppercase"><span>My company</span></div>-->
|
149 |
+
<?php if( $this->template_settings['company_logo'] != "" ) { ?>
|
150 |
+
<img class="company-logo" src="<?php echo $this->template_settings['company_logo']; ?>" alt="Company logo"/>
|
151 |
+
<?php } else { ?>
|
152 |
+
<div class="company-logo"><?php echo $this->template_settings['company_name']; ?></div>
|
153 |
+
<?php } ?>
|
154 |
+
</div>
|
155 |
+
</div>
|
156 |
+
<div class="row intro">
|
157 |
+
<?php echo $this->template_settings['intro_text']; ?>
|
158 |
+
</div>
|
159 |
+
<div class="row">
|
160 |
+
<div class="two-column left">
|
161 |
+
<h4>Billing address</h4>
|
162 |
+
<p class="normalcase">
|
163 |
+
<?php echo $this->order->get_formatted_billing_address(); ?>
|
164 |
+
</p>
|
165 |
+
</div>
|
166 |
+
<div class="two-column right">
|
167 |
+
<h4>Shipping address</h4>
|
168 |
+
<p class="normalcase">
|
169 |
+
<?php echo $this->order->get_formatted_shipping_address(); ?>
|
170 |
+
</p>
|
171 |
+
</div>
|
172 |
+
</div>
|
173 |
+
<!-- COUPON -->
|
174 |
+
<!--<div class="row coupon">
|
175 |
+
<h3>20% off next purchase</h3>
|
176 |
+
<p>
|
177 |
+
For being a regular customer, here's a little something from <br/>
|
178 |
+
us. Use the coupon code to get 20% off your next order!
|
179 |
+
</p>
|
180 |
+
<div id="coupon-code">
|
181 |
+
c0up0n_c0d3
|
182 |
+
</div>
|
183 |
+
<p id="expires">
|
184 |
+
<strong>Expires on: 1st January 2015</strong>
|
185 |
+
</p>
|
186 |
+
</div>-->
|
187 |
+
<div class="row invoice">
|
188 |
+
<div class="title">
|
189 |
+
<h1>Your Invoice</h1>
|
190 |
+
<span id="invoice-date" class="uppercase"><?php echo $this->get_formatted_date(); ?></span>
|
191 |
+
</div>
|
192 |
+
<div id="invoice-number">
|
193 |
+
Invoice Number: <?php echo $this->get_formatted_invoice_number(); ?>
|
194 |
+
</div>
|
195 |
+
<table class="products">
|
196 |
+
<thead>
|
197 |
+
<tr class="border-bottom">
|
198 |
+
<th class="align-left">Description</th>
|
199 |
+
<?php if( $this->template_settings['show_sku'] ) { $colspan = 3; ?>
|
200 |
+
<th class="align-center uppercase">SKU</th>
|
201 |
+
<?php } else { $colspan = 2; } ?>
|
202 |
+
<th class="align-center">Quantity</th>
|
203 |
+
<th>Unit price</th>
|
204 |
+
<th>Total</th>
|
205 |
+
</tr>
|
206 |
+
</thead>
|
207 |
+
<tbody>
|
208 |
+
<?php foreach( $this->order->get_items( 'line_item' ) as $item ) {
|
209 |
+
$product = wc_get_product( $item['product_id'] ); ?>
|
210 |
+
<tr class="product border-bottom">
|
211 |
+
<td class="align-left normalcase"><?php echo $product->get_title(); ?></td>
|
212 |
+
<?php if( $this->template_settings['show_sku'] ) { ?>
|
213 |
+
<td class="align-center uppercase"><?php echo $product->get_sku(); ?></td>
|
214 |
+
<?php } ?>
|
215 |
+
<td class="align-center"><?php echo $item['qty']; ?></td>
|
216 |
+
<td><?php echo wc_price( $product->get_price_excluding_tax() ); ?></td>
|
217 |
+
<td><?php echo wc_price( $product->get_price_excluding_tax( $item['qty'] ) ); ?></td>
|
218 |
+
</tr>
|
219 |
+
<?php } ?>
|
220 |
+
<?php if( $this->template_settings['show_discount'] && $this->order->get_total_discount != 0 ) { ?>
|
221 |
+
<tr class="discount">
|
222 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
223 |
+
<td class="border-bottom-non-product">Discount</td>
|
224 |
+
<td class="border-bottom-non-product"><?php echo wc_price( $this->order->get_total_discount() ); ?></td>
|
225 |
+
</tr>
|
226 |
+
<?php } ?>
|
227 |
+
<?php if( $this->template_settings['show_shipping'] ) { ?>
|
228 |
+
<tr class="shipping">
|
229 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
230 |
+
<td class="border-bottom-non-product">Shipping</td>
|
231 |
+
<td class="border-bottom-non-product normalcase"><?php echo wc_price( $this->order->get_total_shipping() ); ?></td>
|
232 |
+
</tr>
|
233 |
+
<?php } ?>
|
234 |
+
<?php if( $this->template_settings['show_subtotal'] ) { ?>
|
235 |
+
<tr class="subtotal">
|
236 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
237 |
+
<td class="border-bottom-non-product">Subtotal</td>
|
238 |
+
<td class="border-bottom-non-product"><?php echo wc_price( $this->order->get_subtotal() ); ?></td>
|
239 |
+
</tr>
|
240 |
+
<?php } ?>
|
241 |
+
<?php if( $this->template_settings['show_tax'] ) { ?>
|
242 |
+
<tr class="tax">
|
243 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
244 |
+
<td class="border-bottom-non-product">Tax</td>
|
245 |
+
<td class="border-bottom-non-product"><?php echo wc_price( $this->order->get_total_tax() ); ?></td>
|
246 |
+
</tr>
|
247 |
+
<?php } ?>
|
248 |
+
<tr>
|
249 |
+
<td class="payment-method align-left normalcase" colspan="<?php echo $colspan; ?>">Payment via <?php echo $this->order->payment_method_title; ?></td>
|
250 |
+
<td class="total">Total</td>
|
251 |
+
<td class="total"><?php echo wc_price( $this->order->get_total() ); ?></td>
|
252 |
+
</tr>
|
253 |
+
</tbody>
|
254 |
+
</table>
|
255 |
+
</div>
|
256 |
+
<?php if( count($this->order->get_customer_order_notes()) > 0 ) { ?>
|
257 |
+
<div class="row one-column align-center order-notes">
|
258 |
+
<h4>Order notes</h4>
|
259 |
+
<p>
|
260 |
+
<?php
|
261 |
+
foreach( $this->order->get_customer_order_notes() as $note ) {
|
262 |
+
echo $note->comment_content . "<br/>";
|
263 |
+
}
|
264 |
+
?>
|
265 |
+
</p>
|
266 |
+
</div>
|
267 |
+
<?php } ?>
|
268 |
+
</div>
|
269 |
+
<div id="footer">
|
270 |
+
<div class="questions">
|
271 |
+
<!--<span><strong>Questions?</strong></span>
|
272 |
+
<p>
|
273 |
+
No problem. You can get in touch with us on Facebook and Twitter and we'll get back to you as soon as we can.
|
274 |
+
</p>-->
|
275 |
+
</div>
|
276 |
+
<div class="company-address normalcase">
|
277 |
+
<?php echo nl2br( $this->template_settings['company_address'] ); ?>
|
278 |
+
<?php echo nl2br( $this->template_settings['company_details'] ); ?>
|
279 |
+
</div>
|
280 |
+
</div>
|
281 |
+
</div>
|
includes/views/templates/invoice-micro.php
ADDED
@@ -0,0 +1,256 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<html>
|
2 |
+
<head>
|
3 |
+
<style>
|
4 |
+
h1.company-logo {
|
5 |
+
font-size: 30px;
|
6 |
+
}
|
7 |
+
img.company-logo {
|
8 |
+
max-height: 150px;
|
9 |
+
}
|
10 |
+
table {
|
11 |
+
border-collapse: collapse;
|
12 |
+
font-size: 14px;
|
13 |
+
width: 100%;
|
14 |
+
color: #757575;
|
15 |
+
}
|
16 |
+
table.products th {
|
17 |
+
border-bottom: 2px solid #A5A5A5;
|
18 |
+
}
|
19 |
+
table.products td, table.products th {
|
20 |
+
padding: 5px;
|
21 |
+
}
|
22 |
+
tr.product td {
|
23 |
+
border-bottom: 1px solid #CECECE;
|
24 |
+
}
|
25 |
+
td.total, td.grand-total {
|
26 |
+
border-top: 4px solid #8D8D8D;
|
27 |
+
}
|
28 |
+
td.grand-total {
|
29 |
+
font-size: 16px;
|
30 |
+
}
|
31 |
+
table, tbody, h1 {
|
32 |
+
margin: 0;
|
33 |
+
padding: 0;
|
34 |
+
}
|
35 |
+
h1 {
|
36 |
+
font-size: 36px;
|
37 |
+
}
|
38 |
+
span {
|
39 |
+
display: block;
|
40 |
+
width: 100%;
|
41 |
+
}
|
42 |
+
.align-left { text-align: left; }
|
43 |
+
.align-right { text-align: right; }
|
44 |
+
.company {
|
45 |
+
width: 60%;
|
46 |
+
vertical-align: middle;
|
47 |
+
margin-bottom: 40px;
|
48 |
+
display: inline-block;
|
49 |
+
}
|
50 |
+
.company .info {
|
51 |
+
padding-left: 30px;
|
52 |
+
text-align: left;
|
53 |
+
}
|
54 |
+
.two-column {
|
55 |
+
margin-bottom: 40px;
|
56 |
+
width: 100%;
|
57 |
+
}
|
58 |
+
.two-column td {
|
59 |
+
text-align: left;
|
60 |
+
vertical-align: top;
|
61 |
+
width: 50%;
|
62 |
+
}
|
63 |
+
.invoice-head {
|
64 |
+
margin-bottom: 20px;
|
65 |
+
margin-right: -64px;
|
66 |
+
}
|
67 |
+
.invoice-head td {
|
68 |
+
text-align: left;
|
69 |
+
}
|
70 |
+
.invoice-head .title {
|
71 |
+
color: #525252;
|
72 |
+
}
|
73 |
+
td.invoice-details {
|
74 |
+
vertical-align: top;
|
75 |
+
}
|
76 |
+
.number {
|
77 |
+
font-size: 16px;
|
78 |
+
}
|
79 |
+
.date, .thanks {
|
80 |
+
font-size: 12px;
|
81 |
+
}
|
82 |
+
.total-amount p {
|
83 |
+
margin: 0; padding: 0;
|
84 |
+
}
|
85 |
+
.total-amount {
|
86 |
+
padding: 20px 20px 30px 20px;
|
87 |
+
width: 54%;
|
88 |
+
}
|
89 |
+
.total-amount, .total-amount h1 {
|
90 |
+
color: white;
|
91 |
+
}
|
92 |
+
.invoice {
|
93 |
+
margin-bottom: 20px;
|
94 |
+
}
|
95 |
+
.foot {
|
96 |
+
margin: 0 -64px;
|
97 |
+
}
|
98 |
+
.foot td.border {
|
99 |
+
padding: 20px 40px;
|
100 |
+
width: 100%;
|
101 |
+
text-align: center;
|
102 |
+
}
|
103 |
+
td.company-details, td.payment {
|
104 |
+
padding: 20px 40px 40px 40px;
|
105 |
+
text-align: center;
|
106 |
+
vertical-align: top;
|
107 |
+
width: 50%;
|
108 |
+
}
|
109 |
+
.foot td {
|
110 |
+
border: 1px solid white;
|
111 |
+
}
|
112 |
+
.number, .grand-total {
|
113 |
+
color: <?php echo $this->template_settings['color_theme']; ?>;
|
114 |
+
}
|
115 |
+
.foot td.border {
|
116 |
+
border-bottom: 8px solid <?php echo $this->template_settings['color_theme']; ?>;
|
117 |
+
}
|
118 |
+
/* End change colors */
|
119 |
+
.space td {
|
120 |
+
padding-bottom: 50px;
|
121 |
+
}
|
122 |
+
</style>
|
123 |
+
</head>
|
124 |
+
<body>
|
125 |
+
<div id="container">
|
126 |
+
<table class="company">
|
127 |
+
<tbody>
|
128 |
+
<tr>
|
129 |
+
<td class="logo">
|
130 |
+
<?php if( $this->template_settings['company_logo'] != "" ) { ?>
|
131 |
+
<img class="company-logo" src="<?php echo $this->template_settings['company_logo']; ?>" alt="Company logo"/>
|
132 |
+
<?php } else { ?>
|
133 |
+
<h1 class="company-logo"><?php echo $this->template_settings['company_name']; ?></h1>
|
134 |
+
<?php } ?>
|
135 |
+
</td>
|
136 |
+
<td class="info">
|
137 |
+
<?php echo nl2br( $this->template_settings['company_address'] ); ?>
|
138 |
+
</td>
|
139 |
+
</tr>
|
140 |
+
</tbody>
|
141 |
+
</table>
|
142 |
+
<table class="two-column customer">
|
143 |
+
<tbody>
|
144 |
+
<tr>
|
145 |
+
<td></td>
|
146 |
+
<td class="address">
|
147 |
+
<?php echo $this->order->get_formatted_billing_address(); ?>
|
148 |
+
</td>
|
149 |
+
</tr>
|
150 |
+
</tbody>
|
151 |
+
</table>
|
152 |
+
<table class="invoice-head">
|
153 |
+
<tbody>
|
154 |
+
<tr>
|
155 |
+
<td class="invoice-details">
|
156 |
+
<h1 class="title"><?php _e( 'Invoice', $this->textdomain ); ?></h1>
|
157 |
+
<span class="number"># <?php echo $this->get_formatted_invoice_number(); ?></span><br/>
|
158 |
+
<span class="date"><?php echo $this->get_formatted_date(); ?></span>
|
159 |
+
</td>
|
160 |
+
<td class="total-amount" bgcolor="<?php echo $this->template_settings['color_theme']; ?>">
|
161 |
+
<span>
|
162 |
+
<h1 class="amount"><?php echo wc_price( $this->order->get_total() ); ?></h1>
|
163 |
+
<p class="thanks">
|
164 |
+
<?php echo $this->template_settings['intro_text']; ?>
|
165 |
+
</p>
|
166 |
+
</span>
|
167 |
+
</td>
|
168 |
+
</tr>
|
169 |
+
</tbody>
|
170 |
+
</table>
|
171 |
+
<table class="products">
|
172 |
+
<thead>
|
173 |
+
<tr>
|
174 |
+
<th class="align-left"><?php _e( 'Description', $this->textdomain ); ?></th>
|
175 |
+
<?php
|
176 |
+
if( $this->template_settings['show_sku'] ) {
|
177 |
+
$colspan = 3;
|
178 |
+
echo '<th class="align-left">' . __( "SKU", $this->textdomain ) . '</th>';
|
179 |
+
} else {
|
180 |
+
$colspan = 2; }
|
181 |
+
?>
|
182 |
+
<th class="align-left"><?php _e( 'Quantity', $this->textdomain ); ?></th>
|
183 |
+
<th class="align-left"><?php _e( 'Unit price', $this->textdomain ); ?></th>
|
184 |
+
<th class="align-right"><?php _e( 'Total', $this->textdomain ); ?></th>
|
185 |
+
</tr>
|
186 |
+
</thead>
|
187 |
+
<tbody>
|
188 |
+
<?php foreach( $this->order->get_items( 'line_item' ) as $item ) {
|
189 |
+
$product = wc_get_product( $item['product_id'] ); ?>
|
190 |
+
<tr class="product">
|
191 |
+
<td><?php echo $product->get_title(); ?></td>
|
192 |
+
<?php if( $this->template_settings['show_sku'] ) { ?>
|
193 |
+
<td><?php echo $product->get_sku(); ?></td>
|
194 |
+
<?php } ?>
|
195 |
+
<td><?php echo $item['qty']; ?></td>
|
196 |
+
<td><?php echo wc_price( $product->get_price_excluding_tax() ); ?></td>
|
197 |
+
<td class="align-right"><?php echo wc_price( $product->get_price_excluding_tax( $item['qty'] ) ); ?></td>
|
198 |
+
</tr>
|
199 |
+
<?php } ?>
|
200 |
+
<!-- Space -->
|
201 |
+
<tr class="space">
|
202 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
203 |
+
<td colspan="2"></td>
|
204 |
+
</tr>
|
205 |
+
<!-- Discount -->
|
206 |
+
<?php if( $this->template_settings['show_discount'] && $this->order->get_total_discount != 0 ) { ?>
|
207 |
+
<tr class="discount after-products">
|
208 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
209 |
+
<td width="25%"><?php _e( 'Discount', $this->textdomain ); ?></td>
|
210 |
+
<td width="25%" class="align-right"><?php echo wc_price( $this->order->get_total_discount() ); ?></td>
|
211 |
+
</tr>
|
212 |
+
<?php } ?>
|
213 |
+
<!-- Shipping -->
|
214 |
+
<?php if( $this->template_settings['show_shipping'] ) { ?>
|
215 |
+
<tr class="shipping after-products">
|
216 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
217 |
+
<td width="25%"><?php _e( 'Shipping', $this->textdomain ); ?></td>
|
218 |
+
<td width="25%" class="align-right"><?php echo wc_price( $this->order->get_total_shipping() ); ?></td>
|
219 |
+
</tr>
|
220 |
+
<?php } ?>
|
221 |
+
<!-- Subtotal -->
|
222 |
+
<?php if( $this->template_settings['show_subtotal'] ) { ?>
|
223 |
+
<tr class="subtotal after-products">
|
224 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
225 |
+
<td width="25%"><?php _e( 'Subtotal', $this->textdomain ); ?></td>
|
226 |
+
<td width="25%" class="align-right"><?php echo wc_price( $this->order->get_subtotal() ); ?></td>
|
227 |
+
</tr>
|
228 |
+
<?php } ?>
|
229 |
+
<!-- Tax -->
|
230 |
+
<?php if( $this->template_settings['show_tax'] ) { ?>
|
231 |
+
<tr class="tax">
|
232 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
233 |
+
<td width="25%"><?php _e( 'Tax', $this->textdomain ); ?></td>
|
234 |
+
<td width="25%" class="align-right"><?php echo wc_price( $this->order->get_total_tax() ); ?></td>
|
235 |
+
</tr>
|
236 |
+
<?php } ?>
|
237 |
+
<!-- Total -->
|
238 |
+
<tr>
|
239 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
240 |
+
<td class="total" width="25%"><?php _e( 'Total', $this->textdomain ); ?></td>
|
241 |
+
<td class="grand-total align-right" width="25%">
|
242 |
+
<?php echo wc_price( $this->order->get_total() ) ?>
|
243 |
+
</td>
|
244 |
+
</tr>
|
245 |
+
<?php /*<tr>
|
246 |
+
<td colspan="<?php echo $colspan; ?>"></td>
|
247 |
+
<td class="refunded" width="25%"><?php _e( 'Refunded', $this->textdomain ); ?></td>
|
248 |
+
<td class="refunded-total align-right" width="25%">
|
249 |
+
<?php echo wc_price( $this->order->get_total_refunded() ) ?>
|
250 |
+
</td>
|
251 |
+
</tr> */?>
|
252 |
+
</tbody>
|
253 |
+
</table>
|
254 |
+
</div>
|
255 |
+
</body>
|
256 |
+
</html>
|
index.php
DELETED
@@ -1,28 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Plugin Name: WooCommerce PDF Invoices
|
5 |
-
* Description: Generate PDF invoice and automatically attach to WooCommerce email type of your choice.
|
6 |
-
* Version: 1.1.2
|
7 |
-
* Author: Bas Elbers
|
8 |
-
* License: GPL2
|
9 |
-
*/
|
10 |
-
|
11 |
-
if ( ! defined( 'ABSPATH' ) ) exit;
|
12 |
-
|
13 |
-
define("BEWPI_VERSION", "1.1.2");
|
14 |
-
define("BEWPI_PLUGIN_DIR", plugin_dir_path(__FILE__));
|
15 |
-
define("BEWPI_PLUGIN_URL", plugins_url( '/' , __FILE__ ));
|
16 |
-
|
17 |
-
require_once BEWPI_PLUGIN_DIR . 'includes/plugin.php';
|
18 |
-
require_once BEWPI_PLUGIN_DIR . 'includes/class-invoice.php';
|
19 |
-
new BEWPI_Invoice;
|
20 |
-
|
21 |
-
if((is_admin()) && (!defined("DOING_AJAX") || !DOING_AJAX)) {
|
22 |
-
|
23 |
-
// ADMIN SECTION
|
24 |
-
require BEWPI_PLUGIN_DIR . 'includes/class-admin.php';
|
25 |
-
new BEWPI_Admin();
|
26 |
-
}
|
27 |
-
|
28 |
-
?>
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
lang/be-woocommerce-pdf-invoices-nl_NL.mo
ADDED
Binary file
|
lang/be-woocommerce-pdf-invoices-nl_NL.po
ADDED
@@ -0,0 +1,365 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: WooCommerce PDF Invoices\n"
|
4 |
+
"POT-Creation-Date: 2015-03-24 21:18+0100\n"
|
5 |
+
"PO-Revision-Date: 2015-03-24 21:19+0100\n"
|
6 |
+
"Last-Translator: \n"
|
7 |
+
"Language-Team: \n"
|
8 |
+
"Language: nl_NL\n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Generator: Poedit 1.7.3\n"
|
13 |
+
"X-Poedit-Basepath: ..\n"
|
14 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
15 |
+
"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
|
16 |
+
"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;"
|
17 |
+
"_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
|
18 |
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
19 |
+
"X-Poedit-SearchPath-0: .\n"
|
20 |
+
"X-Poedit-SearchPathExcluded-0: *.js\n"
|
21 |
+
|
22 |
+
#: admin/classes/woocommerce-pdf-invoices.php:152
|
23 |
+
msgid "General"
|
24 |
+
msgstr "Algemeen"
|
25 |
+
|
26 |
+
#: admin/classes/woocommerce-pdf-invoices.php:153
|
27 |
+
#: admin/classes/wpi-template-settings.php:109
|
28 |
+
msgid "Template"
|
29 |
+
msgstr ""
|
30 |
+
|
31 |
+
#: admin/classes/woocommerce-pdf-invoices.php:167
|
32 |
+
msgid "Invoices"
|
33 |
+
msgstr "Facturen"
|
34 |
+
|
35 |
+
#: admin/classes/woocommerce-pdf-invoices.php:260
|
36 |
+
msgid "PDF Invoice"
|
37 |
+
msgstr "PDF Factuur"
|
38 |
+
|
39 |
+
#: admin/classes/wpi-general-settings.php:74
|
40 |
+
msgid "General Settings"
|
41 |
+
msgstr "Algemene Opties"
|
42 |
+
|
43 |
+
#: admin/classes/wpi-general-settings.php:75
|
44 |
+
msgid "Attach to Email"
|
45 |
+
msgstr "Voeg toe aan Email"
|
46 |
+
|
47 |
+
#: admin/classes/wpi-general-settings.php:79
|
48 |
+
msgid "Processing order"
|
49 |
+
msgstr "Bestelling wordt verwerkt"
|
50 |
+
|
51 |
+
#: admin/classes/wpi-general-settings.php:83
|
52 |
+
msgid "Completed order"
|
53 |
+
msgstr "Bestelling voltooid"
|
54 |
+
|
55 |
+
#: admin/classes/wpi-general-settings.php:87
|
56 |
+
msgid "Customer invoice"
|
57 |
+
msgstr "Klant factuur"
|
58 |
+
|
59 |
+
#: admin/classes/wpi-general-settings.php:91
|
60 |
+
msgid "Attach to New order Email"
|
61 |
+
msgstr "Voeg toe aan New order Email"
|
62 |
+
|
63 |
+
#: admin/classes/wpi-general-settings.php:92
|
64 |
+
msgid "Automatically send invoice to Google Drive, Egnyte, Dropbox or OneDrive"
|
65 |
+
msgstr "Verzend automatisch naar Google Drive, Egnyte, Dropbox of OneDrive"
|
66 |
+
|
67 |
+
#: admin/classes/wpi-general-settings.php:93
|
68 |
+
msgid "Email It In account"
|
69 |
+
msgstr ""
|
70 |
+
|
71 |
+
#: admin/classes/wpi-general-settings.php:133
|
72 |
+
msgid "For bookkeeping purposes."
|
73 |
+
msgstr "Voor boekhoudkundige doeleinden."
|
74 |
+
|
75 |
+
#: admin/classes/wpi-general-settings.php:146
|
76 |
+
#, php-format
|
77 |
+
msgid "Signup at %s and enter your account below."
|
78 |
+
msgstr "Meld je aan op %s en geef je account onderstaand in."
|
79 |
+
|
80 |
+
#: admin/classes/wpi-general-settings.php:159
|
81 |
+
#, php-format
|
82 |
+
msgid "Enter your %s account."
|
83 |
+
msgstr "Het %s account."
|
84 |
+
|
85 |
+
#: admin/classes/wpi-general-settings.php:182
|
86 |
+
msgid "Invalid type of Email."
|
87 |
+
msgstr "Ongeldig type Email."
|
88 |
+
|
89 |
+
#: admin/classes/wpi-general-settings.php:193
|
90 |
+
#: admin/classes/wpi-general-settings.php:204
|
91 |
+
#: admin/classes/wpi-template-settings.php:594
|
92 |
+
msgid "Please don't try to change the values."
|
93 |
+
msgstr "Probeer de waardes niet te wijzigen a.u.b."
|
94 |
+
|
95 |
+
#: admin/classes/wpi-general-settings.php:215
|
96 |
+
msgid "Invalid Email address."
|
97 |
+
msgstr "Ongeldig Emailadres."
|
98 |
+
|
99 |
+
#: admin/classes/wpi-settings.php:92
|
100 |
+
msgid "Allowed tags: "
|
101 |
+
msgstr "Toegestane tags: "
|
102 |
+
|
103 |
+
#: admin/classes/wpi-template-settings.php:108
|
104 |
+
msgid "Template Settings"
|
105 |
+
msgstr "Template Opties"
|
106 |
+
|
107 |
+
#: admin/classes/wpi-template-settings.php:110
|
108 |
+
msgid "Color theme"
|
109 |
+
msgstr "Kleur thema"
|
110 |
+
|
111 |
+
#: admin/classes/wpi-template-settings.php:111
|
112 |
+
msgid "Company name"
|
113 |
+
msgstr "Bedrijfsnaam"
|
114 |
+
|
115 |
+
#: admin/classes/wpi-template-settings.php:112
|
116 |
+
msgid "Company logo"
|
117 |
+
msgstr "Bedrijfslogo"
|
118 |
+
|
119 |
+
#: admin/classes/wpi-template-settings.php:113
|
120 |
+
msgid "Intro text"
|
121 |
+
msgstr "Introtekst"
|
122 |
+
|
123 |
+
#: admin/classes/wpi-template-settings.php:114
|
124 |
+
msgid "Company address"
|
125 |
+
msgstr "Bedrijfsadres"
|
126 |
+
|
127 |
+
#: admin/classes/wpi-template-settings.php:115
|
128 |
+
msgid "Company details"
|
129 |
+
msgstr "Algemene bedrijfsgegevens"
|
130 |
+
|
131 |
+
#: admin/classes/wpi-template-settings.php:116
|
132 |
+
msgid "Terms & conditions, policies etc."
|
133 |
+
msgstr "Algemene voorwaarden"
|
134 |
+
|
135 |
+
#: admin/classes/wpi-template-settings.php:117
|
136 |
+
msgid "Next invoice number"
|
137 |
+
msgstr "Eerstvolgende factuurnummer"
|
138 |
+
|
139 |
+
#: admin/classes/wpi-template-settings.php:118
|
140 |
+
msgid "Number of digits"
|
141 |
+
msgstr "Aantal cijfers"
|
142 |
+
|
143 |
+
#: admin/classes/wpi-template-settings.php:119
|
144 |
+
msgid "Invoice number prefix"
|
145 |
+
msgstr "Factuurnummer voorvoegsel"
|
146 |
+
|
147 |
+
#: admin/classes/wpi-template-settings.php:120
|
148 |
+
msgid "Invoice number suffix"
|
149 |
+
msgstr "Factuurnummer achtervoegsel"
|
150 |
+
|
151 |
+
#: admin/classes/wpi-template-settings.php:121
|
152 |
+
msgid "Invoice number format"
|
153 |
+
msgstr "Factuurnummer format"
|
154 |
+
|
155 |
+
#: admin/classes/wpi-template-settings.php:122
|
156 |
+
msgid "Reset on 1st January"
|
157 |
+
msgstr "Reset op 1 januari"
|
158 |
+
|
159 |
+
#: admin/classes/wpi-template-settings.php:123
|
160 |
+
msgid "Invoice date format"
|
161 |
+
msgstr "Factuur datumnotatie"
|
162 |
+
|
163 |
+
#: admin/classes/wpi-template-settings.php:124
|
164 |
+
msgid "Show SKU"
|
165 |
+
msgstr "Toon SKU"
|
166 |
+
|
167 |
+
#: admin/classes/wpi-template-settings.php:125
|
168 |
+
msgid "Show discount"
|
169 |
+
msgstr "Toon korting"
|
170 |
+
|
171 |
+
#: admin/classes/wpi-template-settings.php:126
|
172 |
+
msgid "Show subtotal"
|
173 |
+
msgstr "Toon subtotaal"
|
174 |
+
|
175 |
+
#: admin/classes/wpi-template-settings.php:127
|
176 |
+
msgid "Show tax"
|
177 |
+
msgstr "Toon BTW"
|
178 |
+
|
179 |
+
#: admin/classes/wpi-template-settings.php:128
|
180 |
+
msgid "Show shipping"
|
181 |
+
msgstr "Toon verzendkosten"
|
182 |
+
|
183 |
+
#: admin/classes/wpi-template-settings.php:129
|
184 |
+
msgid "Show customer notes"
|
185 |
+
msgstr "Toon notities klant"
|
186 |
+
|
187 |
+
#: admin/classes/wpi-template-settings.php:167
|
188 |
+
msgid "Color theme of the invoice."
|
189 |
+
msgstr "Kleurthema van de factuur."
|
190 |
+
|
191 |
+
#: admin/classes/wpi-template-settings.php:190
|
192 |
+
msgid ""
|
193 |
+
"Please upload an image less then 200Kb and make sure it's a jpeg, jpg or png."
|
194 |
+
msgstr ""
|
195 |
+
"Upload een afbeelding van maximaal 200Kb en zorg dat het een jpeg, jpg of "
|
196 |
+
"png is."
|
197 |
+
|
198 |
+
#: admin/classes/wpi-template-settings.php:201
|
199 |
+
msgid "Remove logo"
|
200 |
+
msgstr "Verwijder logo"
|
201 |
+
|
202 |
+
#: admin/classes/wpi-template-settings.php:344
|
203 |
+
msgid "Invoice number to use for next invoice."
|
204 |
+
msgstr "Factuurnummer te gebruiken voor de volgende factuur."
|
205 |
+
|
206 |
+
#: admin/classes/wpi-template-settings.php:360
|
207 |
+
msgid "Number of zero digits."
|
208 |
+
msgstr "Aantal nullen."
|
209 |
+
|
210 |
+
#: admin/classes/wpi-template-settings.php:374
|
211 |
+
msgid "Prefix text for the invoice number. Not required."
|
212 |
+
msgstr "Voorvoegsel van het factuurnummer is niet verplicht."
|
213 |
+
|
214 |
+
#: admin/classes/wpi-template-settings.php:388
|
215 |
+
msgid "Suffix text for the invoice number. Not required."
|
216 |
+
msgstr "Achtervoegsel van het factuurnummer is niet verplicht."
|
217 |
+
|
218 |
+
#: admin/classes/wpi-template-settings.php:402
|
219 |
+
msgid ""
|
220 |
+
"Use [prefix], [suffix] and [number] as placeholders. [number] is required."
|
221 |
+
msgstr ""
|
222 |
+
"Gebruik [prefix], [suffix] en [number] als aanduidingen. [number] is "
|
223 |
+
"verplicht."
|
224 |
+
|
225 |
+
#: admin/classes/wpi-template-settings.php:416
|
226 |
+
msgid "Reset on the first of January."
|
227 |
+
msgstr "Reset op de eerste van januari."
|
228 |
+
|
229 |
+
#: admin/classes/wpi-template-settings.php:430
|
230 |
+
#, php-format
|
231 |
+
msgid "%sFormat%s of the date. Examples: %s or %s."
|
232 |
+
msgstr "%sFormat%s van de datum. Bijvoorbeeld: %s of %s."
|
233 |
+
|
234 |
+
#: admin/classes/wpi-template-settings.php:476
|
235 |
+
msgid "Invalid template."
|
236 |
+
msgstr "Ongeldige template."
|
237 |
+
|
238 |
+
#: admin/classes/wpi-template-settings.php:489
|
239 |
+
msgid "Invalid color theme code."
|
240 |
+
msgstr "Ongeldige kleurcode."
|
241 |
+
|
242 |
+
#: admin/classes/wpi-template-settings.php:500
|
243 |
+
msgid "Invalid company name."
|
244 |
+
msgstr "Ongeldige bedsrijfsnaam."
|
245 |
+
|
246 |
+
#: admin/classes/wpi-template-settings.php:518
|
247 |
+
msgid "Invalid input into one of the textarea's."
|
248 |
+
msgstr "Ongeldige invoer in een van de textarea's."
|
249 |
+
|
250 |
+
#: admin/classes/wpi-template-settings.php:529
|
251 |
+
msgid "Invalid (next) invoice number."
|
252 |
+
msgstr "Ongeldig (eerstvolgend) factuurnummer."
|
253 |
+
|
254 |
+
#: admin/classes/wpi-template-settings.php:547
|
255 |
+
msgid "Invalid invoice number digits."
|
256 |
+
msgstr "Ongeldige factuurnummer cijfers."
|
257 |
+
|
258 |
+
#: admin/classes/wpi-template-settings.php:563
|
259 |
+
msgid "The [number] placeholder is required as invoice number format."
|
260 |
+
msgstr "De aanduiding [number] is vereist."
|
261 |
+
|
262 |
+
#: admin/classes/wpi-template-settings.php:570
|
263 |
+
msgid "Invalid invoice number format."
|
264 |
+
msgstr "Ongeldig factuurnummer format."
|
265 |
+
|
266 |
+
#: admin/classes/wpi-template-settings.php:604
|
267 |
+
msgid "Invalid date format."
|
268 |
+
msgstr "Ongeldige datumnotatie."
|
269 |
+
|
270 |
+
#: admin/classes/wpi-template-settings.php:632
|
271 |
+
msgid "File is invalid and contains either '..' or './'."
|
272 |
+
msgstr "Bestand is niet valide en bevat ofwel '..' of './'."
|
273 |
+
|
274 |
+
#: admin/classes/wpi-template-settings.php:639
|
275 |
+
msgid "File is invalid and contains ':' after the first character."
|
276 |
+
msgstr "Bestand is niet valide en bevat ':' na het eerste teken."
|
277 |
+
|
278 |
+
#: admin/classes/wpi-template-settings.php:648
|
279 |
+
msgid "Please upload image with extension jpg, jpeg or png."
|
280 |
+
msgstr "Gelieve te uploaden een afbeelding met extensie jpg, jpeg of png."
|
281 |
+
|
282 |
+
#: includes/classes/wpi-invoice.php:327
|
283 |
+
msgid "Customer note"
|
284 |
+
msgstr "Opmerking klant"
|
285 |
+
|
286 |
+
#: includes/views/templates/invoice-micro.php:156
|
287 |
+
msgid "Invoice"
|
288 |
+
msgstr "Factuur"
|
289 |
+
|
290 |
+
#: includes/views/templates/invoice-micro.php:174
|
291 |
+
msgid "Description"
|
292 |
+
msgstr "Beschrijving"
|
293 |
+
|
294 |
+
#: includes/views/templates/invoice-micro.php:178
|
295 |
+
msgid "SKU"
|
296 |
+
msgstr "Productcode"
|
297 |
+
|
298 |
+
#: includes/views/templates/invoice-micro.php:182
|
299 |
+
msgid "Quantity"
|
300 |
+
msgstr "Aantal"
|
301 |
+
|
302 |
+
#: includes/views/templates/invoice-micro.php:183
|
303 |
+
msgid "Unit price"
|
304 |
+
msgstr "Prijs per stuk"
|
305 |
+
|
306 |
+
#: includes/views/templates/invoice-micro.php:184
|
307 |
+
#: includes/views/templates/invoice-micro.php:240
|
308 |
+
msgid "Total"
|
309 |
+
msgstr "Totaal"
|
310 |
+
|
311 |
+
#: includes/views/templates/invoice-micro.php:209
|
312 |
+
msgid "Discount"
|
313 |
+
msgstr "Korting"
|
314 |
+
|
315 |
+
#: includes/views/templates/invoice-micro.php:217
|
316 |
+
msgid "Shipping"
|
317 |
+
msgstr "Verzending"
|
318 |
+
|
319 |
+
#: includes/views/templates/invoice-micro.php:225
|
320 |
+
msgid "Subtotal"
|
321 |
+
msgstr "Subtotaal"
|
322 |
+
|
323 |
+
#: includes/views/templates/invoice-micro.php:233
|
324 |
+
msgid "Tax"
|
325 |
+
msgstr "BTW"
|
326 |
+
|
327 |
+
#~ msgid "Signup at %s and enter your account beyond."
|
328 |
+
#~ msgstr "Meld je aan op %s en vul je account onderstaand in."
|
329 |
+
|
330 |
+
#~ msgid "%sFormat%s of the date. Examples: %s or %s"
|
331 |
+
#~ msgstr "%sFormat%s van de datum. Bijvoorbeeld: %s of %s"
|
332 |
+
|
333 |
+
#~ msgid "Show invoice"
|
334 |
+
#~ msgstr "Toon factuur"
|
335 |
+
|
336 |
+
#~ msgid "Create invoice"
|
337 |
+
#~ msgstr "Maak factuur"
|
338 |
+
|
339 |
+
#~ msgid "%sFormat%s of the date."
|
340 |
+
#~ msgstr "%sFormat%s van de datum."
|
341 |
+
|
342 |
+
#~ msgid "Use [prefix], [suffix] and [number] as placeholders."
|
343 |
+
#~ msgstr "Gebruik [suffix], [prefix] en [number] als aanduidingen."
|
344 |
+
|
345 |
+
#~ msgid "Choose the color witch fits your company."
|
346 |
+
#~ msgstr "Kies uw bedrijfskleur."
|
347 |
+
|
348 |
+
#~ msgid ""
|
349 |
+
#~ "Choose the format for the invoice number. Use [prefix], [suffix] and "
|
350 |
+
#~ "[number] as placeholders."
|
351 |
+
#~ msgstr ""
|
352 |
+
#~ "Kies het formaat voor het factuurnummer. Gebruik [prefix], [suffix] en "
|
353 |
+
#~ "[number] als aanduidingen."
|
354 |
+
|
355 |
+
#~ msgid "Text to greet, congratulate or thank the customer. "
|
356 |
+
#~ msgstr "Tekst om de klant te groeten, te feliciteren of te bedanken. "
|
357 |
+
|
358 |
+
#~ msgid "Some text"
|
359 |
+
#~ msgstr "De vertaling werkt!!!!"
|
360 |
+
|
361 |
+
#~ msgid "Text to greet, congratulate or thank the customer."
|
362 |
+
#~ msgstr "Tekst om de klant te begroeten, te bedanken of te feliciteren."
|
363 |
+
|
364 |
+
#~ msgid "Start all over on the first of January."
|
365 |
+
#~ msgstr "Opnieuw beginnen op de eerste van januari."
|
lang/woocommerce-pdf-invoices.mo
DELETED
Binary file
|
lang/woocommerce-pdf-invoices.po
DELETED
@@ -1,101 +0,0 @@
|
|
1 |
-
msgid ""
|
2 |
-
msgstr ""
|
3 |
-
"Project-Id-Version: WooCommerce PDF Invoices\n"
|
4 |
-
"POT-Creation-Date: 2014-02-06 21:36+0100\n"
|
5 |
-
"PO-Revision-Date: 2014-02-06 21:38+0100\n"
|
6 |
-
"Last-Translator: Bas Elbers <baselbers@hotmail.com>\n"
|
7 |
-
"Language-Team: \n"
|
8 |
-
"Language: en_GB\n"
|
9 |
-
"MIME-Version: 1.0\n"
|
10 |
-
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
-
"Content-Transfer-Encoding: 8bit\n"
|
12 |
-
"X-Generator: Poedit 1.6.2\n"
|
13 |
-
"X-Poedit-Basepath: .\n"
|
14 |
-
"X-Poedit-KeywordsList: __;_e\n"
|
15 |
-
"X-Poedit-SearchPath-0: ../.\n"
|
16 |
-
|
17 |
-
#: .././includes/class-admin.php:44
|
18 |
-
msgid "Please upload image with extension jpg, jpeg or png."
|
19 |
-
msgstr ""
|
20 |
-
|
21 |
-
#: .././includes/class-admin.php:48
|
22 |
-
msgid "Please upload image less then 50kB."
|
23 |
-
msgstr ""
|
24 |
-
|
25 |
-
#: .././includes/class-admin.php:54
|
26 |
-
msgid "Settings saved."
|
27 |
-
msgstr ""
|
28 |
-
|
29 |
-
#: .././includes/class-invoice.php:165
|
30 |
-
msgid "INVOICE"
|
31 |
-
msgstr ""
|
32 |
-
|
33 |
-
#: .././includes/class-invoice.php:174
|
34 |
-
msgid "INVOICE NUMBER: "
|
35 |
-
msgstr ""
|
36 |
-
|
37 |
-
#: .././includes/class-invoice.php:175
|
38 |
-
#, php-format
|
39 |
-
msgid "DATE: %02d-%02d-%04d"
|
40 |
-
msgstr ""
|
41 |
-
|
42 |
-
#: .././includes/class-invoice.php:184
|
43 |
-
msgid "TO:"
|
44 |
-
msgstr ""
|
45 |
-
|
46 |
-
#: .././includes/class-invoice.php:188
|
47 |
-
msgid "SHIP TO:"
|
48 |
-
msgstr ""
|
49 |
-
|
50 |
-
#: .././includes/class-invoice.php:198
|
51 |
-
msgid "Notes:"
|
52 |
-
msgstr ""
|
53 |
-
|
54 |
-
#: .././includes/class-invoice.php:211
|
55 |
-
msgid "SKU"
|
56 |
-
msgstr ""
|
57 |
-
|
58 |
-
#: .././includes/class-invoice.php:213
|
59 |
-
msgid "Description"
|
60 |
-
msgstr ""
|
61 |
-
|
62 |
-
#: .././includes/class-invoice.php:214
|
63 |
-
msgid "Quantity"
|
64 |
-
msgstr ""
|
65 |
-
|
66 |
-
#: .././includes/class-invoice.php:215
|
67 |
-
msgid "Unit price"
|
68 |
-
msgstr ""
|
69 |
-
|
70 |
-
#: .././includes/class-invoice.php:216
|
71 |
-
msgid "Total"
|
72 |
-
msgstr ""
|
73 |
-
|
74 |
-
#: .././includes/class-invoice.php:249
|
75 |
-
msgid "Discount"
|
76 |
-
msgstr ""
|
77 |
-
|
78 |
-
#: .././includes/class-invoice.php:253
|
79 |
-
msgid "Subtotal"
|
80 |
-
msgstr ""
|
81 |
-
|
82 |
-
#: .././includes/class-invoice.php:257
|
83 |
-
msgid "Shipping"
|
84 |
-
msgstr ""
|
85 |
-
|
86 |
-
#: .././includes/class-invoice.php:264
|
87 |
-
#, php-format
|
88 |
-
msgid "VAT %s%%"
|
89 |
-
msgstr ""
|
90 |
-
|
91 |
-
#: .././includes/class-invoice.php:269
|
92 |
-
msgid "Grand Total"
|
93 |
-
msgstr ""
|
94 |
-
|
95 |
-
#: .././includes/class-invoice.php:286
|
96 |
-
#, php-format
|
97 |
-
msgid "Page %s of %s"
|
98 |
-
msgstr ""
|
99 |
-
|
100 |
-
#~ msgid "Test"
|
101 |
-
#~ msgstr "test"
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
lib/mpdf/classes/barcode.php
ADDED
@@ -0,0 +1,1972 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// Adapted for mPDF from TCPDF barcode. Original Details left below.
|
4 |
+
|
5 |
+
//============================================================+
|
6 |
+
// File name : barcodes.php
|
7 |
+
// Begin : 2008-06-09
|
8 |
+
// Last Update : 2009-04-15
|
9 |
+
// Version : 1.0.008
|
10 |
+
// License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
|
11 |
+
// ----------------------------------------------------------------------------
|
12 |
+
// Copyright (C) 2008-2009 Nicola Asuni - Tecnick.com S.r.l.
|
13 |
+
//
|
14 |
+
// This program is free software: you can redistribute it and/or modify
|
15 |
+
// it under the terms of the GNU Lesser General Public License as published by
|
16 |
+
// the Free Software Foundation, either version 2.1 of the License, or
|
17 |
+
// (at your option) any later version.
|
18 |
+
//
|
19 |
+
// This program is distributed in the hope that it will be useful,
|
20 |
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
21 |
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
22 |
+
// GNU Lesser General Public License for more details.
|
23 |
+
//
|
24 |
+
// You should have received a copy of the GNU Lesser General Public License
|
25 |
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
26 |
+
//
|
27 |
+
// See LICENSE.TXT file for more information.
|
28 |
+
// ----------------------------------------------------------------------------
|
29 |
+
//
|
30 |
+
// Description : PHP class to creates array representations for
|
31 |
+
// common 1D barcodes to be used with TCPDF.
|
32 |
+
//
|
33 |
+
// Author: Nicola Asuni
|
34 |
+
//
|
35 |
+
// (c) Copyright:
|
36 |
+
// Nicola Asuni
|
37 |
+
// Tecnick.com S.r.l.
|
38 |
+
// Via della Pace, 11
|
39 |
+
// 09044 Quartucciu (CA)
|
40 |
+
// ITALY
|
41 |
+
// www.tecnick.com
|
42 |
+
// info@tecnick.com
|
43 |
+
//============================================================+
|
44 |
+
|
45 |
+
class PDFBarcode {
|
46 |
+
|
47 |
+
protected $barcode_array;
|
48 |
+
protected $gapwidth;
|
49 |
+
protected $print_ratio;
|
50 |
+
protected $daft;
|
51 |
+
|
52 |
+
public function __construct() {
|
53 |
+
|
54 |
+
}
|
55 |
+
|
56 |
+
public function getBarcodeArray($code, $type, $pr='') {
|
57 |
+
$this->setBarcode($code, $type, $pr);
|
58 |
+
return $this->barcode_array;
|
59 |
+
}
|
60 |
+
public function getChecksum($code, $type) {
|
61 |
+
$this->setBarcode($code, $type);
|
62 |
+
if (!$this->barcode_array) { return ''; }
|
63 |
+
else { return $this->barcode_array['checkdigit']; }
|
64 |
+
}
|
65 |
+
|
66 |
+
public function setBarcode($code, $type, $pr='') {
|
67 |
+
$this->print_ratio = 1;
|
68 |
+
switch (strtoupper($type)) {
|
69 |
+
case 'ISBN':
|
70 |
+
case 'ISSN':
|
71 |
+
case 'EAN13': { // EAN 13
|
72 |
+
$arrcode = $this->barcode_eanupc($code, 13);
|
73 |
+
$arrcode['lightmL'] = 11; // LEFT light margin = x X-dim (http://www.gs1uk.org)
|
74 |
+
$arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (http://www.gs1uk.org)
|
75 |
+
$arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
|
76 |
+
$arrcode['nom-H'] = 25.93; // Nominal bar height in mm incl. numerals (http://www.gs1uk.org)
|
77 |
+
break;
|
78 |
+
}
|
79 |
+
case 'UPCA': { // UPC-A
|
80 |
+
$arrcode = $this->barcode_eanupc($code, 12);
|
81 |
+
$arrcode['lightmL'] = 9; // LEFT light margin = x X-dim (http://www.gs1uk.org)
|
82 |
+
$arrcode['lightmR'] = 9; // RIGHT light margin = x X-dim (http://www.gs1uk.org)
|
83 |
+
$arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
|
84 |
+
$arrcode['nom-H'] = 25.91; // Nominal bar height in mm incl. numerals (http://www.gs1uk.org)
|
85 |
+
break;
|
86 |
+
}
|
87 |
+
case 'UPCE': { // UPC-E
|
88 |
+
$arrcode = $this->barcode_eanupc($code, 6);
|
89 |
+
$arrcode['lightmL'] = 9; // LEFT light margin = x X-dim (http://www.gs1uk.org)
|
90 |
+
$arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (http://www.gs1uk.org)
|
91 |
+
$arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
|
92 |
+
$arrcode['nom-H'] = 25.93; // Nominal bar height in mm incl. numerals (http://www.gs1uk.org)
|
93 |
+
break;
|
94 |
+
}
|
95 |
+
case 'EAN8': { // EAN 8
|
96 |
+
$arrcode = $this->barcode_eanupc($code, 8);
|
97 |
+
$arrcode['lightmL'] = 7; // LEFT light margin = x X-dim (http://www.gs1uk.org)
|
98 |
+
$arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (http://www.gs1uk.org)
|
99 |
+
$arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
|
100 |
+
$arrcode['nom-H'] = 21.64; // Nominal bar height in mm incl. numerals (http://www.gs1uk.org)
|
101 |
+
break;
|
102 |
+
}
|
103 |
+
case 'EAN2': { // 2-Digits UPC-Based Extention
|
104 |
+
$arrcode = $this->barcode_eanext($code, 2);
|
105 |
+
$arrcode['lightmL'] = 7; // LEFT light margin = x X-dim (estimated)
|
106 |
+
$arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (estimated)
|
107 |
+
$arrcode['sepM'] = 9; // SEPARATION margin = x X-dim (http://web.archive.org/web/19990501035133/http://www.uc-council.org/d36-d.htm)
|
108 |
+
$arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
|
109 |
+
$arrcode['nom-H'] = 20; // Nominal bar height in mm incl. numerals (estimated) not used when combined
|
110 |
+
break;
|
111 |
+
}
|
112 |
+
case 'EAN5': { // 5-Digits UPC-Based Extention
|
113 |
+
$arrcode = $this->barcode_eanext($code, 5);
|
114 |
+
$arrcode['lightmL'] = 7; // LEFT light margin = x X-dim (estimated)
|
115 |
+
$arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (estimated)
|
116 |
+
$arrcode['sepM'] = 9; // SEPARATION margin = x X-dim (http://web.archive.org/web/19990501035133/http://www.uc-council.org/d36-d.htm)
|
117 |
+
$arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
|
118 |
+
$arrcode['nom-H'] = 20; // Nominal bar height in mm incl. numerals (estimated) not used when combined
|
119 |
+
break;
|
120 |
+
}
|
121 |
+
|
122 |
+
case 'IMB': { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
|
123 |
+
$xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
|
124 |
+
$bpi = 22; // Bars per inch
|
125 |
+
// Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
|
126 |
+
$this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
|
127 |
+
$this->daft = array('D'=>2, 'A'=>2, 'F'=>3, 'T'=>1); // Descender; Ascender; Full; Tracker bar heights
|
128 |
+
$arrcode = $this->barcode_imb($code);
|
129 |
+
$arrcode['nom-X'] = $xdim ;
|
130 |
+
$arrcode['nom-H'] = 3.68; // Nominal value for Height of Full bar in mm (spec.)
|
131 |
+
// USPS-B-3200 Revision C = 4.623
|
132 |
+
// USPS-B-3200 Revision E = 3.68
|
133 |
+
$arrcode['quietL'] = 3.175; // LEFT Quiet margin = mm (spec.)
|
134 |
+
$arrcode['quietR'] = 3.175; // RIGHT Quiet margin = mm (spec.)
|
135 |
+
$arrcode['quietTB'] = 0.711; // TOP/BOTTOM Quiet margin = mm (spec.)
|
136 |
+
break;
|
137 |
+
}
|
138 |
+
case 'RM4SCC': { // RM4SCC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
|
139 |
+
$xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
|
140 |
+
$bpi = 22; // Bars per inch
|
141 |
+
// Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
|
142 |
+
$this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
|
143 |
+
$this->daft = array('D'=>5, 'A'=>5, 'F'=>8, 'T'=>2); // Descender; Ascender; Full; Tracker bar heights
|
144 |
+
$arrcode = $this->barcode_rm4scc($code, false);
|
145 |
+
$arrcode['nom-X'] = $xdim ;
|
146 |
+
$arrcode['nom-H'] = 5.0; // Nominal value for Height of Full bar in mm (spec.)
|
147 |
+
$arrcode['quietL'] = 2; // LEFT Quiet margin = mm (spec.)
|
148 |
+
$arrcode['quietR'] = 2; // RIGHT Quiet margin = mm (spec.)
|
149 |
+
$arrcode['quietTB'] = 2; // TOP/BOTTOM Quiet margin = mm (spec?)
|
150 |
+
break;
|
151 |
+
}
|
152 |
+
case 'KIX': { // KIX (Klant index - Customer index)
|
153 |
+
$xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
|
154 |
+
$bpi = 22; // Bars per inch
|
155 |
+
// Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
|
156 |
+
$this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
|
157 |
+
$this->daft = array('D'=>5, 'A'=>5, 'F'=>8, 'T'=>2); // Descender; Ascender; Full; Tracker bar heights
|
158 |
+
$arrcode = $this->barcode_rm4scc($code, true);
|
159 |
+
$arrcode['nom-X'] = $xdim ;
|
160 |
+
$arrcode['nom-H'] = 5.0; // Nominal value for Height of Full bar in mm (? spec.)
|
161 |
+
$arrcode['quietL'] = 2; // LEFT Quiet margin = mm (spec.)
|
162 |
+
$arrcode['quietR'] = 2; // RIGHT Quiet margin = mm (spec.)
|
163 |
+
$arrcode['quietTB'] = 2; // TOP/BOTTOM Quiet margin = mm (spec.)
|
164 |
+
break;
|
165 |
+
}
|
166 |
+
case 'POSTNET': { // POSTNET
|
167 |
+
$xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
|
168 |
+
$bpi = 22; // Bars per inch
|
169 |
+
// Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
|
170 |
+
$this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
|
171 |
+
$arrcode = $this->barcode_postnet($code, false);
|
172 |
+
$arrcode['nom-X'] = $xdim ;
|
173 |
+
$arrcode['nom-H'] = 3.175; // Nominal value for Height of Full bar in mm (spec.)
|
174 |
+
$arrcode['quietL'] = 3.175; // LEFT Quiet margin = mm (?spec.)
|
175 |
+
$arrcode['quietR'] = 3.175; // RIGHT Quiet margin = mm (?spec.)
|
176 |
+
$arrcode['quietTB'] = 1.016; // TOP/BOTTOM Quiet margin = mm (?spec.)
|
177 |
+
break;
|
178 |
+
}
|
179 |
+
case 'PLANET': { // PLANET
|
180 |
+
$xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
|
181 |
+
$bpi = 22; // Bars per inch
|
182 |
+
// Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
|
183 |
+
$this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
|
184 |
+
$arrcode = $this->barcode_postnet($code, true);
|
185 |
+
$arrcode['nom-X'] = $xdim ;
|
186 |
+
$arrcode['nom-H'] = 3.175; // Nominal value for Height of Full bar in mm (spec.)
|
187 |
+
$arrcode['quietL'] = 3.175; // LEFT Quiet margin = mm (?spec.)
|
188 |
+
$arrcode['quietR'] = 3.175; // RIGHT Quiet margin = mm (?spec.)
|
189 |
+
$arrcode['quietTB'] = 1.016; // TOP/BOTTOM Quiet margin = mm (?spec.)
|
190 |
+
break;
|
191 |
+
}
|
192 |
+
|
193 |
+
case 'C93': { // CODE 93 - USS-93
|
194 |
+
$arrcode = $this->barcode_code93($code);
|
195 |
+
if ($arrcode == false) { break; }
|
196 |
+
$arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
|
197 |
+
$arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
|
198 |
+
$arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
|
199 |
+
$arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
|
200 |
+
$arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
|
201 |
+
break;
|
202 |
+
}
|
203 |
+
case 'CODE11': { // CODE 11
|
204 |
+
if ($pr > 0) { $this->print_ratio = $pr; }
|
205 |
+
else { $this->print_ratio = 3; } // spec: Pr= 1:2.24 - 1:3.5
|
206 |
+
$arrcode = $this->barcode_code11($code);
|
207 |
+
if ($arrcode == false) { break; }
|
208 |
+
$arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
|
209 |
+
$arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
|
210 |
+
$arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
|
211 |
+
$arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
|
212 |
+
$arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
|
213 |
+
break;
|
214 |
+
}
|
215 |
+
case 'MSI': // MSI (Variation of Plessey code)
|
216 |
+
case 'MSI+': { // MSI + CHECKSUM (modulo 11)
|
217 |
+
if (strtoupper($type)=='MSI') { $arrcode = $this->barcode_msi($code, false); }
|
218 |
+
if (strtoupper($type)=='MSI+') { $arrcode = $this->barcode_msi($code, true); }
|
219 |
+
if ($arrcode == false) { break; }
|
220 |
+
$arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
|
221 |
+
$arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
|
222 |
+
$arrcode['lightmL'] = 12; // LEFT light margin = x X-dim (spec.)
|
223 |
+
$arrcode['lightmR'] = 12; // RIGHT light margin = x X-dim (spec.)
|
224 |
+
$arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
|
225 |
+
break;
|
226 |
+
}
|
227 |
+
case 'CODABAR': { // CODABAR
|
228 |
+
if ($pr > 0) { $this->print_ratio = $pr; }
|
229 |
+
else { $this->print_ratio = 2.5; } // spec: Pr= 1:2 - 1:3 (>2.2 if X<0.50)
|
230 |
+
if (strtoupper($type)=='CODABAR') { $arrcode = $this->barcode_codabar($code); }
|
231 |
+
if ($arrcode == false) { break; }
|
232 |
+
$arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
|
233 |
+
$arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
|
234 |
+
$arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
|
235 |
+
$arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
|
236 |
+
$arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
|
237 |
+
break;
|
238 |
+
}
|
239 |
+
case 'C128A': // CODE 128 A
|
240 |
+
case 'C128B': // CODE 128 B
|
241 |
+
case 'C128C': // CODE 128 C
|
242 |
+
case 'EAN128A': // EAN 128 A
|
243 |
+
case 'EAN128B': // EAN 128 B
|
244 |
+
case 'EAN128C': { // EAN 128 C
|
245 |
+
if (strtoupper($type)=='C128A') { $arrcode = $this->barcode_c128($code, 'A'); }
|
246 |
+
if (strtoupper($type)=='C128B') { $arrcode = $this->barcode_c128($code, 'B'); }
|
247 |
+
if (strtoupper($type)=='C128C') { $arrcode = $this->barcode_c128($code, 'C'); }
|
248 |
+
if (strtoupper($type)=='EAN128A') { $arrcode = $this->barcode_c128($code, 'A', true); }
|
249 |
+
if (strtoupper($type)=='EAN128B') { $arrcode = $this->barcode_c128($code, 'B', true); }
|
250 |
+
if (strtoupper($type)=='EAN128C') { $arrcode = $this->barcode_c128($code, 'C', true); }
|
251 |
+
if ($arrcode == false) { break; }
|
252 |
+
$arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
|
253 |
+
$arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
|
254 |
+
$arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
|
255 |
+
$arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
|
256 |
+
$arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
|
257 |
+
break;
|
258 |
+
}
|
259 |
+
case 'C39': // CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
|
260 |
+
case 'C39+': // CODE 39 with checksum
|
261 |
+
case 'C39E': // CODE 39 EXTENDED
|
262 |
+
case 'C39E+': { // CODE 39 EXTENDED + CHECKSUM
|
263 |
+
if ($pr > 0) { $this->print_ratio = $pr; }
|
264 |
+
else { $this->print_ratio = 2.5; } // spec: Pr= 1:2 - 1:3 (>2.2 if X<0.50)
|
265 |
+
$code = str_replace(chr(194).chr(160), ' ', $code); // mPDF 5.3.95 (for utf-8 encoded)
|
266 |
+
$code = str_replace(chr(160), ' ', $code); // mPDF 5.3.95 (for win-1252)
|
267 |
+
if (strtoupper($type)=='C39') { $arrcode = $this->barcode_code39($code, false, false); }
|
268 |
+
if (strtoupper($type)=='C39+') { $arrcode = $this->barcode_code39($code, false, true); }
|
269 |
+
if (strtoupper($type)=='C39E') { $arrcode = $this->barcode_code39($code, true, false); }
|
270 |
+
if (strtoupper($type)=='C39E+') { $arrcode = $this->barcode_code39($code, true, true); }
|
271 |
+
if ($arrcode == false) { break; }
|
272 |
+
$arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
|
273 |
+
$arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
|
274 |
+
$arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
|
275 |
+
$arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
|
276 |
+
$arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
|
277 |
+
break;
|
278 |
+
}
|
279 |
+
case 'S25': // Standard 2 of 5
|
280 |
+
case 'S25+': { // Standard 2 of 5 + CHECKSUM
|
281 |
+
if ($pr > 0) { $this->print_ratio = $pr; }
|
282 |
+
else { $this->print_ratio = 3; } // spec: Pr=1:3/1:4.5
|
283 |
+
if (strtoupper($type)=='S25') { $arrcode = $this->barcode_s25($code, false); }
|
284 |
+
if (strtoupper($type)=='S25+') { $arrcode = $this->barcode_s25($code, true); }
|
285 |
+
if ($arrcode == false) { break; }
|
286 |
+
$arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
|
287 |
+
$arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
|
288 |
+
$arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
|
289 |
+
$arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
|
290 |
+
$arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
|
291 |
+
break;
|
292 |
+
}
|
293 |
+
case 'I25': // Interleaved 2 of 5
|
294 |
+
case 'I25+': { // Interleaved 2 of 5 + CHECKSUM
|
295 |
+
if ($pr > 0) { $this->print_ratio = $pr; }
|
296 |
+
else { $this->print_ratio = 2.5; } // spec: Pr= 1:2 - 1:3 (>2.2 if X<0.50)
|
297 |
+
if (strtoupper($type)=='I25') { $arrcode = $this->barcode_i25($code, false); }
|
298 |
+
if (strtoupper($type)=='I25+') { $arrcode = $this->barcode_i25($code, true); }
|
299 |
+
if ($arrcode == false) { break; }
|
300 |
+
$arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
|
301 |
+
$arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
|
302 |
+
$arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
|
303 |
+
$arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
|
304 |
+
$arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
|
305 |
+
break;
|
306 |
+
}
|
307 |
+
case 'I25B': // Interleaved 2 of 5 + Bearer bars
|
308 |
+
case 'I25B+': { // Interleaved 2 of 5 + CHECKSUM + Bearer bars
|
309 |
+
if ($pr > 0) { $this->print_ratio = $pr; }
|
310 |
+
else { $this->print_ratio = 2.5; } // spec: Pr= 1:2 - 1:3 (>2.2 if X<0.50)
|
311 |
+
if (strtoupper($type)=='I25B') { $arrcode = $this->barcode_i25($code, false); }
|
312 |
+
if (strtoupper($type)=='I25B+') { $arrcode = $this->barcode_i25($code, true); }
|
313 |
+
if ($arrcode == false) { break; }
|
314 |
+
$arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
|
315 |
+
$arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
|
316 |
+
$arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
|
317 |
+
$arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
|
318 |
+
$arrcode['lightTB'] = 2; // TOP/BOTTOM light margin = x X-dim (non-spec.) - used for bearer bars
|
319 |
+
break;
|
320 |
+
}
|
321 |
+
default: {
|
322 |
+
$this->barcode_array = false;
|
323 |
+
}
|
324 |
+
}
|
325 |
+
$this->barcode_array = $arrcode;
|
326 |
+
}
|
327 |
+
|
328 |
+
/**
|
329 |
+
* CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
|
330 |
+
*/
|
331 |
+
protected function barcode_code39($code, $extended=false, $checksum=false) {
|
332 |
+
$chr['0'] = '111221211';
|
333 |
+
$chr['1'] = '211211112';
|
334 |
+
$chr['2'] = '112211112';
|
335 |
+
$chr['3'] = '212211111';
|
336 |
+
$chr['4'] = '111221112';
|
337 |
+
$chr['5'] = '211221111';
|
338 |
+
$chr['6'] = '112221111';
|
339 |
+
$chr['7'] = '111211212';
|
340 |
+
$chr['8'] = '211211211';
|
341 |
+
$chr['9'] = '112211211';
|
342 |
+
$chr['A'] = '211112112';
|
343 |
+
$chr['B'] = '112112112';
|
344 |
+
$chr['C'] = '212112111';
|
345 |
+
$chr['D'] = '111122112';
|
346 |
+
$chr['E'] = '211122111';
|
347 |
+
$chr['F'] = '112122111';
|
348 |
+
$chr['G'] = '111112212';
|
349 |
+
$chr['H'] = '211112211';
|
350 |
+
$chr['I'] = '112112211';
|
351 |
+
$chr['J'] = '111122211';
|
352 |
+
$chr['K'] = '211111122';
|
353 |
+
$chr['L'] = '112111122';
|
354 |
+
$chr['M'] = '212111121';
|
355 |
+
$chr['N'] = '111121122';
|
356 |
+
$chr['O'] = '211121121';
|
357 |
+
$chr['P'] = '112121121';
|
358 |
+
$chr['Q'] = '111111222';
|
359 |
+
$chr['R'] = '211111221';
|
360 |
+
$chr['S'] = '112111221';
|
361 |
+
$chr['T'] = '111121221';
|
362 |
+
$chr['U'] = '221111112';
|
363 |
+
$chr['V'] = '122111112';
|
364 |
+
$chr['W'] = '222111111';
|
365 |
+
$chr['X'] = '121121112';
|
366 |
+
$chr['Y'] = '221121111';
|
367 |
+
$chr['Z'] = '122121111';
|
368 |
+
$chr['-'] = '121111212';
|
369 |
+
$chr['.'] = '221111211';
|
370 |
+
$chr[' '] = '122111211';
|
371 |
+
$chr['$'] = '121212111';
|
372 |
+
$chr['/'] = '121211121';
|
373 |
+
$chr['+'] = '121112121';
|
374 |
+
$chr['%'] = '111212121';
|
375 |
+
$chr['*'] = '121121211';
|
376 |
+
|
377 |
+
$code = strtoupper($code);
|
378 |
+
$checkdigit = '';
|
379 |
+
if ($extended) {
|
380 |
+
// extended mode
|
381 |
+
$code = $this->encode_code39_ext($code);
|
382 |
+
}
|
383 |
+
if ($code === false) {
|
384 |
+
return false;
|
385 |
+
}
|
386 |
+
if ($checksum) {
|
387 |
+
// checksum
|
388 |
+
$checkdigit = $this->checksum_code39($code);
|
389 |
+
$code .= $checkdigit ;
|
390 |
+
}
|
391 |
+
// add start and stop codes
|
392 |
+
$code = '*'.$code.'*';
|
393 |
+
|
394 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
395 |
+
$k = 0;
|
396 |
+
$clen = strlen($code);
|
397 |
+
for ($i = 0; $i < $clen; ++$i) {
|
398 |
+
$char = $code[$i];
|
399 |
+
if(!isset($chr[$char])) {
|
400 |
+
// invalid character
|
401 |
+
return false;
|
402 |
+
}
|
403 |
+
for ($j = 0; $j < 9; ++$j) {
|
404 |
+
if (($j % 2) == 0) {
|
405 |
+
$t = true; // bar
|
406 |
+
} else {
|
407 |
+
$t = false; // space
|
408 |
+
}
|
409 |
+
$x = $chr[$char][$j];
|
410 |
+
if ($x == 2) { $w = $this->print_ratio; }
|
411 |
+
else { $w = 1; }
|
412 |
+
|
413 |
+
$bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
|
414 |
+
$bararray['maxw'] += $w;
|
415 |
+
++$k;
|
416 |
+
}
|
417 |
+
$bararray['bcode'][$k] = array('t' => false, 'w' => 1, 'h' => 1, 'p' => 0);
|
418 |
+
$bararray['maxw'] += 1;
|
419 |
+
++$k;
|
420 |
+
}
|
421 |
+
$bararray['checkdigit'] = $checkdigit;
|
422 |
+
return $bararray;
|
423 |
+
}
|
424 |
+
|
425 |
+
/**
|
426 |
+
* Encode a string to be used for CODE 39 Extended mode.
|
427 |
+
*/
|
428 |
+
protected function encode_code39_ext($code) {
|
429 |
+
$encode = array(
|
430 |
+
chr(0) => '%U', chr(1) => '$A', chr(2) => '$B', chr(3) => '$C',
|
431 |
+
chr(4) => '$D', chr(5) => '$E', chr(6) => '$F', chr(7) => '$G',
|
432 |
+
chr(8) => '$H', chr(9) => '$I', chr(10) => '$J', chr(11) => '�K',
|
433 |
+
chr(12) => '$L', chr(13) => '$M', chr(14) => '$N', chr(15) => '$O',
|
434 |
+
chr(16) => '$P', chr(17) => '$Q', chr(18) => '$R', chr(19) => '$S',
|
435 |
+
chr(20) => '$T', chr(21) => '$U', chr(22) => '$V', chr(23) => '$W',
|
436 |
+
chr(24) => '$X', chr(25) => '$Y', chr(26) => '$Z', chr(27) => '%A',
|
437 |
+
chr(28) => '%B', chr(29) => '%C', chr(30) => '%D', chr(31) => '%E',
|
438 |
+
chr(32) => ' ', chr(33) => '/A', chr(34) => '/B', chr(35) => '/C',
|
439 |
+
chr(36) => '/D', chr(37) => '/E', chr(38) => '/F', chr(39) => '/G',
|
440 |
+
chr(40) => '/H', chr(41) => '/I', chr(42) => '/J', chr(43) => '/K',
|
441 |
+
chr(44) => '/L', chr(45) => '-', chr(46) => '.', chr(47) => '/O',
|
442 |
+
chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
|
443 |
+
chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
|
444 |
+
chr(56) => '8', chr(57) => '9', chr(58) => '/Z', chr(59) => '%F',
|
445 |
+
chr(60) => '%G', chr(61) => '%H', chr(62) => '%I', chr(63) => '%J',
|
446 |
+
chr(64) => '%V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
|
447 |
+
chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
|
448 |
+
chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
|
449 |
+
chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
|
450 |
+
chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
|
451 |
+
chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
|
452 |
+
chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => '%K',
|
453 |
+
chr(92) => '%L', chr(93) => '%M', chr(94) => '%N', chr(95) => '%O',
|
454 |
+
chr(96) => '%W', chr(97) => '+A', chr(98) => '+B', chr(99) => '+C',
|
455 |
+
chr(100) => '+D', chr(101) => '+E', chr(102) => '+F', chr(103) => '+G',
|
456 |
+
chr(104) => '+H', chr(105) => '+I', chr(106) => '+J', chr(107) => '+K',
|
457 |
+
chr(108) => '+L', chr(109) => '+M', chr(110) => '+N', chr(111) => '+O',
|
458 |
+
chr(112) => '+P', chr(113) => '+Q', chr(114) => '+R', chr(115) => '+S',
|
459 |
+
chr(116) => '+T', chr(117) => '+U', chr(118) => '+V', chr(119) => '+W',
|
460 |
+
chr(120) => '+X', chr(121) => '+Y', chr(122) => '+Z', chr(123) => '%P',
|
461 |
+
chr(124) => '%Q', chr(125) => '%R', chr(126) => '%S', chr(127) => '%T');
|
462 |
+
$code_ext = '';
|
463 |
+
$clen = strlen($code);
|
464 |
+
for ($i = 0 ; $i < $clen; ++$i) {
|
465 |
+
if (ord($code[$i]) > 127) {
|
466 |
+
return false;
|
467 |
+
}
|
468 |
+
$code_ext .= $encode[$code[$i]];
|
469 |
+
}
|
470 |
+
return $code_ext;
|
471 |
+
}
|
472 |
+
|
473 |
+
/**
|
474 |
+
* Calculate CODE 39 checksum (modulo 43).
|
475 |
+
*/
|
476 |
+
protected function checksum_code39($code) {
|
477 |
+
$chars = array(
|
478 |
+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
479 |
+
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
|
480 |
+
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
481 |
+
'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%');
|
482 |
+
$sum = 0;
|
483 |
+
$clen = strlen($code);
|
484 |
+
for ($i = 0 ; $i < $clen; ++$i) {
|
485 |
+
$k = array_keys($chars, $code[$i]);
|
486 |
+
$sum += $k[0];
|
487 |
+
}
|
488 |
+
$j = ($sum % 43);
|
489 |
+
return $chars[$j];
|
490 |
+
}
|
491 |
+
|
492 |
+
/**
|
493 |
+
* CODE 93 - USS-93
|
494 |
+
* Compact code similar to Code 39
|
495 |
+
*/
|
496 |
+
protected function barcode_code93($code) {
|
497 |
+
$chr[48] = '131112'; // 0
|
498 |
+
$chr[49] = '111213'; // 1
|
499 |
+
$chr[50] = '111312'; // 2
|
500 |
+
$chr[51] = '111411'; // 3
|
501 |
+
$chr[52] = '121113'; // 4
|
502 |
+
$chr[53] = '121212'; // 5
|
503 |
+
$chr[54] = '121311'; // 6
|
504 |
+
$chr[55] = '111114'; // 7
|
505 |
+
$chr[56] = '131211'; // 8
|
506 |
+
$chr[57] = '141111'; // 9
|
507 |
+
$chr[65] = '211113'; // A
|
508 |
+
$chr[66] = '211212'; // B
|
509 |
+
$chr[67] = '211311'; // C
|
510 |
+
$chr[68] = '221112'; // D
|
511 |
+
$chr[69] = '221211'; // E
|
512 |
+
$chr[70] = '231111'; // F
|
513 |
+
$chr[71] = '112113'; // G
|
514 |
+
$chr[72] = '112212'; // H
|
515 |
+
$chr[73] = '112311'; // I
|
516 |
+
$chr[74] = '122112'; // J
|
517 |
+
$chr[75] = '132111'; // K
|
518 |
+
$chr[76] = '111123'; // L
|
519 |
+
$chr[77] = '111222'; // M
|
520 |
+
$chr[78] = '111321'; // N
|
521 |
+
$chr[79] = '121122'; // O
|
522 |
+
$chr[80] = '131121'; // P
|
523 |
+
$chr[81] = '212112'; // Q
|
524 |
+
$chr[82] = '212211'; // R
|
525 |
+
$chr[83] = '211122'; // S
|
526 |
+
$chr[84] = '211221'; // T
|
527 |
+
$chr[85] = '221121'; // U
|
528 |
+
$chr[86] = '222111'; // V
|
529 |
+
$chr[87] = '112122'; // W
|
530 |
+
$chr[88] = '112221'; // X
|
531 |
+
$chr[89] = '122121'; // Y
|
532 |
+
$chr[90] = '123111'; // Z
|
533 |
+
$chr[45] = '121131'; // -
|
534 |
+
$chr[46] = '311112'; // .
|
535 |
+
$chr[32] = '311211'; //
|
536 |
+
$chr[36] = '321111'; // $
|
537 |
+
$chr[47] = '112131'; // /
|
538 |
+
$chr[43] = '113121'; // +
|
539 |
+
$chr[37] = '211131'; // %
|
540 |
+
$chr[128] = '121221'; // ($)
|
541 |
+
$chr[129] = '311121'; // (/)
|
542 |
+
$chr[130] = '122211'; // (+)
|
543 |
+
$chr[131] = '312111'; // (%)
|
544 |
+
$chr[42] = '111141'; // start-stop
|
545 |
+
$code = strtoupper($code);
|
546 |
+
$encode = array(
|
547 |
+
chr(0) => chr(131).'U', chr(1) => chr(128).'A', chr(2) => chr(128).'B', chr(3) => chr(128).'C',
|
548 |
+
chr(4) => chr(128).'D', chr(5) => chr(128).'E', chr(6) => chr(128).'F', chr(7) => chr(128).'G',
|
549 |
+
chr(8) => chr(128).'H', chr(9) => chr(128).'I', chr(10) => chr(128).'J', chr(11) => '�K',
|
550 |
+
chr(12) => chr(128).'L', chr(13) => chr(128).'M', chr(14) => chr(128).'N', chr(15) => chr(128).'O',
|
551 |
+
chr(16) => chr(128).'P', chr(17) => chr(128).'Q', chr(18) => chr(128).'R', chr(19) => chr(128).'S',
|
552 |
+
chr(20) => chr(128).'T', chr(21) => chr(128).'U', chr(22) => chr(128).'V', chr(23) => chr(128).'W',
|
553 |
+
chr(24) => chr(128).'X', chr(25) => chr(128).'Y', chr(26) => chr(128).'Z', chr(27) => chr(131).'A',
|
554 |
+
chr(28) => chr(131).'B', chr(29) => chr(131).'C', chr(30) => chr(131).'D', chr(31) => chr(131).'E',
|
555 |
+
chr(32) => ' ', chr(33) => chr(129).'A', chr(34) => chr(129).'B', chr(35) => chr(129).'C',
|
556 |
+
chr(36) => chr(129).'D', chr(37) => chr(129).'E', chr(38) => chr(129).'F', chr(39) => chr(129).'G',
|
557 |
+
chr(40) => chr(129).'H', chr(41) => chr(129).'I', chr(42) => chr(129).'J', chr(43) => chr(129).'K',
|
558 |
+
chr(44) => chr(129).'L', chr(45) => '-', chr(46) => '.', chr(47) => chr(129).'O',
|
559 |
+
chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
|
560 |
+
chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
|
561 |
+
chr(56) => '8', chr(57) => '9', chr(58) => chr(129).'Z', chr(59) => chr(131).'F',
|
562 |
+
chr(60) => chr(131).'G', chr(61) => chr(131).'H', chr(62) => chr(131).'I', chr(63) => chr(131).'J',
|
563 |
+
chr(64) => chr(131).'V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
|
564 |
+
chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
|
565 |
+
chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
|
566 |
+
chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
|
567 |
+
chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
|
568 |
+
chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
|
569 |
+
chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => chr(131).'K',
|
570 |
+
chr(92) => chr(131).'L', chr(93) => chr(131).'M', chr(94) => chr(131).'N', chr(95) => chr(131).'O',
|
571 |
+
chr(96) => chr(131).'W', chr(97) => chr(130).'A', chr(98) => chr(130).'B', chr(99) => chr(130).'C',
|
572 |
+
chr(100) => chr(130).'D', chr(101) => chr(130).'E', chr(102) => chr(130).'F', chr(103) => chr(130).'G',
|
573 |
+
chr(104) => chr(130).'H', chr(105) => chr(130).'I', chr(106) => chr(130).'J', chr(107) => chr(130).'K',
|
574 |
+
chr(108) => chr(130).'L', chr(109) => chr(130).'M', chr(110) => chr(130).'N', chr(111) => chr(130).'O',
|
575 |
+
chr(112) => chr(130).'P', chr(113) => chr(130).'Q', chr(114) => chr(130).'R', chr(115) => chr(130).'S',
|
576 |
+
chr(116) => chr(130).'T', chr(117) => chr(130).'U', chr(118) => chr(130).'V', chr(119) => chr(130).'W',
|
577 |
+
chr(120) => chr(130).'X', chr(121) => chr(130).'Y', chr(122) => chr(130).'Z', chr(123) => chr(131).'P',
|
578 |
+
chr(124) => chr(131).'Q', chr(125) => chr(131).'R', chr(126) => chr(131).'S', chr(127) => chr(131).'T');
|
579 |
+
$code_ext = '';
|
580 |
+
$clen = strlen($code);
|
581 |
+
for ($i = 0 ; $i < $clen; ++$i) {
|
582 |
+
if (ord($code{$i}) > 127) {
|
583 |
+
return false;
|
584 |
+
}
|
585 |
+
$code_ext .= $encode[$code{$i}];
|
586 |
+
}
|
587 |
+
// checksum
|
588 |
+
$code_ext .= $this->checksum_code93($code_ext);
|
589 |
+
// add start and stop codes
|
590 |
+
$code = '*'.$code_ext.'*';
|
591 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
592 |
+
$k = 0;
|
593 |
+
$clen = strlen($code);
|
594 |
+
for ($i = 0; $i < $clen; ++$i) {
|
595 |
+
$char = ord($code{$i});
|
596 |
+
if(!isset($chr[$char])) {
|
597 |
+
// invalid character
|
598 |
+
return false;
|
599 |
+
}
|
600 |
+
for ($j = 0; $j < 6; ++$j) {
|
601 |
+
if (($j % 2) == 0) {
|
602 |
+
$t = true; // bar
|
603 |
+
} else {
|
604 |
+
$t = false; // space
|
605 |
+
}
|
606 |
+
$w = $chr[$char]{$j};
|
607 |
+
$bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
|
608 |
+
$bararray['maxw'] += $w;
|
609 |
+
++$k;
|
610 |
+
}
|
611 |
+
}
|
612 |
+
$bararray['bcode'][$k] = array('t' => true, 'w' => 1, 'h' => 1, 'p' => 0);
|
613 |
+
$bararray['maxw'] += 1;
|
614 |
+
++$k;
|
615 |
+
return $bararray;
|
616 |
+
}
|
617 |
+
|
618 |
+
/**
|
619 |
+
* Calculate CODE 93 checksum (modulo 47).
|
620 |
+
*/
|
621 |
+
protected function checksum_code93($code) {
|
622 |
+
$chars = array(
|
623 |
+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
624 |
+
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
|
625 |
+
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
626 |
+
'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%',
|
627 |
+
'<', '=', '>', '?');
|
628 |
+
// translate special characters
|
629 |
+
$code = strtr($code, chr(128).chr(131).chr(129).chr(130), '<=>?');
|
630 |
+
$len = strlen($code);
|
631 |
+
// calculate check digit C
|
632 |
+
$p = 1;
|
633 |
+
$check = 0;
|
634 |
+
for ($i = ($len - 1); $i >= 0; --$i) {
|
635 |
+
$k = array_keys($chars, $code{$i});
|
636 |
+
$check += ($k[0] * $p);
|
637 |
+
++$p;
|
638 |
+
if ($p > 20) {
|
639 |
+
$p = 1;
|
640 |
+
}
|
641 |
+
}
|
642 |
+
$check %= 47;
|
643 |
+
$c = $chars[$check];
|
644 |
+
$code .= $c;
|
645 |
+
// calculate check digit K
|
646 |
+
$p = 1;
|
647 |
+
$check = 0;
|
648 |
+
for ($i = $len; $i >= 0; --$i) {
|
649 |
+
$k = array_keys($chars, $code{$i});
|
650 |
+
$check += ($k[0] * $p);
|
651 |
+
++$p;
|
652 |
+
if ($p > 15) {
|
653 |
+
$p = 1;
|
654 |
+
}
|
655 |
+
}
|
656 |
+
$check %= 47;
|
657 |
+
$k = $chars[$check];
|
658 |
+
$checksum = $c.$k;
|
659 |
+
// resto respecial characters
|
660 |
+
$checksum = strtr($checksum, '<=>?', chr(128).chr(131).chr(129).chr(130));
|
661 |
+
return $checksum;
|
662 |
+
}
|
663 |
+
|
664 |
+
/**
|
665 |
+
* Checksum for standard 2 of 5 barcodes.
|
666 |
+
*/
|
667 |
+
protected function checksum_s25($code) {
|
668 |
+
$len = strlen($code);
|
669 |
+
$sum = 0;
|
670 |
+
for ($i = 0; $i < $len; $i+=2) {
|
671 |
+
$sum += $code[$i];
|
672 |
+
}
|
673 |
+
$sum *= 3;
|
674 |
+
for ($i = 1; $i < $len; $i+=2) {
|
675 |
+
$sum += ($code[$i]);
|
676 |
+
}
|
677 |
+
$r = $sum % 10;
|
678 |
+
if($r > 0) {
|
679 |
+
$r = (10 - $r);
|
680 |
+
}
|
681 |
+
return $r;
|
682 |
+
}
|
683 |
+
|
684 |
+
/**
|
685 |
+
* MSI.
|
686 |
+
* Variation of Plessey code, with similar applications
|
687 |
+
* Contains digits (0 to 9) and encodes the data only in the width of bars.
|
688 |
+
*/
|
689 |
+
protected function barcode_msi($code, $checksum=false) {
|
690 |
+
$chr['0'] = '100100100100';
|
691 |
+
$chr['1'] = '100100100110';
|
692 |
+
$chr['2'] = '100100110100';
|
693 |
+
$chr['3'] = '100100110110';
|
694 |
+
$chr['4'] = '100110100100';
|
695 |
+
$chr['5'] = '100110100110';
|
696 |
+
$chr['6'] = '100110110100';
|
697 |
+
$chr['7'] = '100110110110';
|
698 |
+
$chr['8'] = '110100100100';
|
699 |
+
$chr['9'] = '110100100110';
|
700 |
+
$chr['A'] = '110100110100';
|
701 |
+
$chr['B'] = '110100110110';
|
702 |
+
$chr['C'] = '110110100100';
|
703 |
+
$chr['D'] = '110110100110';
|
704 |
+
$chr['E'] = '110110110100';
|
705 |
+
$chr['F'] = '110110110110';
|
706 |
+
$checkdigit = '';
|
707 |
+
if ($checksum) {
|
708 |
+
// add checksum
|
709 |
+
$clen = strlen($code);
|
710 |
+
$p = 2;
|
711 |
+
$check = 0;
|
712 |
+
for ($i = ($clen - 1); $i >= 0; --$i) {
|
713 |
+
$check += (hexdec($code[$i]) * $p);
|
714 |
+
++$p;
|
715 |
+
if ($p > 7) {
|
716 |
+
$p = 2;
|
717 |
+
}
|
718 |
+
}
|
719 |
+
$check %= 11;
|
720 |
+
if ($check > 0) {
|
721 |
+
$check = 11 - $check;
|
722 |
+
}
|
723 |
+
$code .= $check;
|
724 |
+
$checkdigit = $check;
|
725 |
+
}
|
726 |
+
$seq = '110'; // left guard
|
727 |
+
$clen = strlen($code);
|
728 |
+
for ($i = 0; $i < $clen; ++$i) {
|
729 |
+
$digit = $code[$i];
|
730 |
+
if (!isset($chr[$digit])) {
|
731 |
+
// invalid character
|
732 |
+
return false;
|
733 |
+
}
|
734 |
+
$seq .= $chr[$digit];
|
735 |
+
}
|
736 |
+
$seq .= '1001'; // right guard
|
737 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
738 |
+
$bararray['checkdigit'] = $checkdigit;
|
739 |
+
return $this->binseq_to_array($seq, $bararray);
|
740 |
+
}
|
741 |
+
|
742 |
+
/**
|
743 |
+
* Standard 2 of 5 barcodes.
|
744 |
+
* Used in airline ticket marking, photofinishing
|
745 |
+
* Contains digits (0 to 9) and encodes the data only in the width of bars.
|
746 |
+
*/
|
747 |
+
protected function barcode_s25($code, $checksum=false) {
|
748 |
+
$chr['0'] = '10101110111010';
|
749 |
+
$chr['1'] = '11101010101110';
|
750 |
+
$chr['2'] = '10111010101110';
|
751 |
+
$chr['3'] = '11101110101010';
|
752 |
+
$chr['4'] = '10101110101110';
|
753 |
+
$chr['5'] = '11101011101010';
|
754 |
+
$chr['6'] = '10111011101010';
|
755 |
+
$chr['7'] = '10101011101110';
|
756 |
+
$chr['8'] = '10101110111010';
|
757 |
+
$chr['9'] = '10111010111010';
|
758 |
+
$checkdigit = '';
|
759 |
+
if ($checksum) {
|
760 |
+
// add checksum
|
761 |
+
$checkdigit = $this->checksum_s25($code);
|
762 |
+
$code .= $checkdigit ;
|
763 |
+
}
|
764 |
+
if((strlen($code) % 2) != 0) {
|
765 |
+
// add leading zero if code-length is odd
|
766 |
+
$code = '0'.$code;
|
767 |
+
}
|
768 |
+
$seq = '11011010';
|
769 |
+
$clen = strlen($code);
|
770 |
+
for ($i = 0; $i < $clen; ++$i) {
|
771 |
+
$digit = $code[$i];
|
772 |
+
if (!isset($chr[$digit])) {
|
773 |
+
// invalid character
|
774 |
+
return false;
|
775 |
+
}
|
776 |
+
$seq .= $chr[$digit];
|
777 |
+
}
|
778 |
+
$seq .= '1101011';
|
779 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
780 |
+
$bararray['checkdigit'] = $checkdigit;
|
781 |
+
return $this->binseq_to_array($seq, $bararray);
|
782 |
+
}
|
783 |
+
|
784 |
+
/**
|
785 |
+
* Convert binary barcode sequence to barcode array
|
786 |
+
*/
|
787 |
+
protected function binseq_to_array($seq, $bararray) {
|
788 |
+
$len = strlen($seq);
|
789 |
+
$w = 0;
|
790 |
+
$k = 0;
|
791 |
+
for ($i = 0; $i < $len; ++$i) {
|
792 |
+
$w += 1;
|
793 |
+
if (($i == ($len - 1)) OR (($i < ($len - 1)) AND ($seq[$i] != $seq[($i+1)]))) {
|
794 |
+
if ($seq[$i] == '1') {
|
795 |
+
$t = true; // bar
|
796 |
+
} else {
|
797 |
+
$t = false; // space
|
798 |
+
}
|
799 |
+
$bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
|
800 |
+
$bararray['maxw'] += $w;
|
801 |
+
++$k;
|
802 |
+
$w = 0;
|
803 |
+
}
|
804 |
+
}
|
805 |
+
return $bararray;
|
806 |
+
}
|
807 |
+
|
808 |
+
/**
|
809 |
+
* Interleaved 2 of 5 barcodes.
|
810 |
+
* Compact numeric code, widely used in industry, air cargo
|
811 |
+
* Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
|
812 |
+
*/
|
813 |
+
protected function barcode_i25($code, $checksum=false) {
|
814 |
+
$chr['0'] = '11221';
|
815 |
+
$chr['1'] = '21112';
|
816 |
+
$chr['2'] = '12112';
|
817 |
+
$chr['3'] = '22111';
|
818 |
+
$chr['4'] = '11212';
|
819 |
+
$chr['5'] = '21211';
|
820 |
+
$chr['6'] = '12211';
|
821 |
+
$chr['7'] = '11122';
|
822 |
+
$chr['8'] = '21121';
|
823 |
+
$chr['9'] = '12121';
|
824 |
+
$chr['A'] = '11';
|
825 |
+
$chr['Z'] = '21';
|
826 |
+
$checkdigit = '';
|
827 |
+
if ($checksum) {
|
828 |
+
// add checksum
|
829 |
+
$checkdigit = $this->checksum_s25($code);
|
830 |
+
$code .= $checkdigit ;
|
831 |
+
}
|
832 |
+
if((strlen($code) % 2) != 0) {
|
833 |
+
// add leading zero if code-length is odd
|
834 |
+
$code = '0'.$code;
|
835 |
+
}
|
836 |
+
// add start and stop codes
|
837 |
+
$code = 'AA'.strtolower($code).'ZA';
|
838 |
+
|
839 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
840 |
+
$k = 0;
|
841 |
+
$clen = strlen($code);
|
842 |
+
for ($i = 0; $i < $clen; $i = ($i + 2)) {
|
843 |
+
$char_bar = $code[$i];
|
844 |
+
$char_space = $code[$i+1];
|
845 |
+
if((!isset($chr[$char_bar])) OR (!isset($chr[$char_space]))) {
|
846 |
+
// invalid character
|
847 |
+
return false;
|
848 |
+
}
|
849 |
+
// create a bar-space sequence
|
850 |
+
$seq = '';
|
851 |
+
$chrlen = strlen($chr[$char_bar]);
|
852 |
+
for ($s = 0; $s < $chrlen; $s++){
|
853 |
+
$seq .= $chr[$char_bar][$s] . $chr[$char_space][$s];
|
854 |
+
}
|
855 |
+
$seqlen = strlen($seq);
|
856 |
+
for ($j = 0; $j < $seqlen; ++$j) {
|
857 |
+
if (($j % 2) == 0) {
|
858 |
+
$t = true; // bar
|
859 |
+
} else {
|
860 |
+
$t = false; // space
|
861 |
+
}
|
862 |
+
$x = $seq[$j];
|
863 |
+
if ($x == 2) { $w = $this->print_ratio; }
|
864 |
+
else { $w = 1; }
|
865 |
+
|
866 |
+
$bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
|
867 |
+
$bararray['maxw'] += $w;
|
868 |
+
++$k;
|
869 |
+
}
|
870 |
+
}
|
871 |
+
$bararray['checkdigit'] = $checkdigit;
|
872 |
+
return $bararray;
|
873 |
+
}
|
874 |
+
|
875 |
+
/**
|
876 |
+
* C128 barcodes.
|
877 |
+
* Very capable code, excellent density, high reliability; in very wide use world-wide
|
878 |
+
*/
|
879 |
+
protected function barcode_c128($code, $type='B', $ean=false) {
|
880 |
+
$code = strcode2utf($code); // mPDF 5.7.1 Allows e.g. <barcode code="5432
1068" type="C128A" />
|
881 |
+
$chr = array(
|
882 |
+
'212222', /* 00 */
|
883 |
+
'222122', /* 01 */
|
884 |
+
'222221', /* 02 */
|
885 |
+
'121223', /* 03 */
|
886 |
+
'121322', /* 04 */
|
887 |
+
'131222', /* 05 */
|
888 |
+
'122213', /* 06 */
|
889 |
+
'122312', /* 07 */
|
890 |
+
'132212', /* 08 */
|
891 |
+
'221213', /* 09 */
|
892 |
+
'221312', /* 10 */
|
893 |
+
'231212', /* 11 */
|
894 |
+
'112232', /* 12 */
|
895 |
+
'122132', /* 13 */
|
896 |
+
'122231', /* 14 */
|
897 |
+
'113222', /* 15 */
|
898 |
+
'123122', /* 16 */
|
899 |
+
'123221', /* 17 */
|
900 |
+
'223211', /* 18 */
|
901 |
+
'221132', /* 19 */
|
902 |
+
'221231', /* 20 */
|
903 |
+
'213212', /* 21 */
|
904 |
+
'223112', /* 22 */
|
905 |
+
'312131', /* 23 */
|
906 |
+
'311222', /* 24 */
|
907 |
+
'321122', /* 25 */
|
908 |
+
'321221', /* 26 */
|
909 |
+
'312212', /* 27 */
|
910 |
+
'322112', /* 28 */
|
911 |
+
'322211', /* 29 */
|
912 |
+
'212123', /* 30 */
|
913 |
+
'212321', /* 31 */
|
914 |
+
'232121', /* 32 */
|
915 |
+
'111323', /* 33 */
|
916 |
+
'131123', /* 34 */
|
917 |
+
'131321', /* 35 */
|
918 |
+
'112313', /* 36 */
|
919 |
+
'132113', /* 37 */
|
920 |
+
'132311', /* 38 */
|
921 |
+
'211313', /* 39 */
|
922 |
+
'231113', /* 40 */
|
923 |
+
'231311', /* 41 */
|
924 |
+
'112133', /* 42 */
|
925 |
+
'112331', /* 43 */
|
926 |
+
'132131', /* 44 */
|
927 |
+
'113123', /* 45 */
|
928 |
+
'113321', /* 46 */
|
929 |
+
'133121', /* 47 */
|
930 |
+
'313121', /* 48 */
|
931 |
+
'211331', /* 49 */
|
932 |
+
'231131', /* 50 */
|
933 |
+
'213113', /* 51 */
|
934 |
+
'213311', /* 52 */
|
935 |
+
'213131', /* 53 */
|
936 |
+
'311123', /* 54 */
|
937 |
+
'311321', /* 55 */
|
938 |
+
'331121', /* 56 */
|
939 |
+
'312113', /* 57 */
|
940 |
+
'312311', /* 58 */
|
941 |
+
'332111', /* 59 */
|
942 |
+
'314111', /* 60 */
|
943 |
+
'221411', /* 61 */
|
944 |
+
'431111', /* 62 */
|
945 |
+
'111224', /* 63 */
|
946 |
+
'111422', /* 64 */
|
947 |
+
'121124', /* 65 */
|
948 |
+
'121421', /* 66 */
|
949 |
+
'141122', /* 67 */
|
950 |
+
'141221', /* 68 */
|
951 |
+
'112214', /* 69 */
|
952 |
+
'112412', /* 70 */
|
953 |
+
'122114', /* 71 */
|
954 |
+
'122411', /* 72 */
|
955 |
+
'142112', /* 73 */
|
956 |
+
'142211', /* 74 */
|
957 |
+
'241211', /* 75 */
|
958 |
+
'221114', /* 76 */
|
959 |
+
'413111', /* 77 */
|
960 |
+
'241112', /* 78 */
|
961 |
+
'134111', /* 79 */
|
962 |
+
'111242', /* 80 */
|
963 |
+
'121142', /* 81 */
|
964 |
+
'121241', /* 82 */
|
965 |
+
'114212', /* 83 */
|
966 |
+
'124112', /* 84 */
|
967 |
+
'124211', /* 85 */
|
968 |
+
'411212', /* 86 */
|
969 |
+
'421112', /* 87 */
|
970 |
+
'421211', /* 88 */
|
971 |
+
'212141', /* 89 */
|
972 |
+
'214121', /* 90 */
|
973 |
+
'412121', /* 91 */
|
974 |
+
'111143', /* 92 */
|
975 |
+
'111341', /* 93 */
|
976 |
+
'131141', /* 94 */
|
977 |
+
'114113', /* 95 */
|
978 |
+
'114311', /* 96 */
|
979 |
+
'411113', /* 97 */
|
980 |
+
'411311', /* 98 */
|
981 |
+
'113141', /* 99 */
|
982 |
+
'114131', /* 100 */
|
983 |
+
'311141', /* 101 */
|
984 |
+
'411131', /* 102 */
|
985 |
+
'211412', /* 103 START A */
|
986 |
+
'211214', /* 104 START B */
|
987 |
+
'211232', /* 105 START C */
|
988 |
+
'233111', /* STOP */
|
989 |
+
'200000' /* END */
|
990 |
+
);
|
991 |
+
$keys = '';
|
992 |
+
switch(strtoupper($type)) {
|
993 |
+
case 'A': {
|
994 |
+
$startid = 103;
|
995 |
+
$keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
|
996 |
+
for ($i = 0; $i < 32; ++$i) {
|
997 |
+
$keys .= chr($i);
|
998 |
+
}
|
999 |
+
break;
|
1000 |
+
}
|
1001 |
+
case 'B': {
|
1002 |
+
$startid = 104;
|
1003 |
+
$keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.chr(127);
|
1004 |
+
break;
|
1005 |
+
}
|
1006 |
+
case 'C': {
|
1007 |
+
$startid = 105;
|
1008 |
+
$keys = '';
|
1009 |
+
if ((strlen($code) % 2) != 0) {
|
1010 |
+
// The length of barcode value must be even ($code). You must pad the number with zeros
|
1011 |
+
return false;
|
1012 |
+
}
|
1013 |
+
for ($i = 0; $i <= 99; ++$i) {
|
1014 |
+
$keys .= chr($i);
|
1015 |
+
}
|
1016 |
+
$new_code = '';
|
1017 |
+
$hclen = (strlen($code) / 2);
|
1018 |
+
for ($i = 0; $i < $hclen; ++$i) {
|
1019 |
+
$new_code .= chr(intval($code{(2 * $i)}.$code{(2 * $i + 1)}));
|
1020 |
+
}
|
1021 |
+
$code = $new_code;
|
1022 |
+
break;
|
1023 |
+
}
|
1024 |
+
default: {
|
1025 |
+
return false;
|
1026 |
+
}
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
// calculate check character
|
1030 |
+
$sum = $startid;
|
1031 |
+
if ($ean) { $code = chr(102) . $code; } // Add FNC 1 - which identifies it as EAN-128
|
1032 |
+
$clen = strlen($code);
|
1033 |
+
for ($i = 0; $i < $clen; ++$i) {
|
1034 |
+
if ($ean && $i==0) { $sum += 102; }
|
1035 |
+
else { $sum += (strpos($keys, $code[$i]) * ($i+1)); }
|
1036 |
+
}
|
1037 |
+
$check = ($sum % 103);
|
1038 |
+
$checkdigit = $check ;
|
1039 |
+
// add start, check and stop codes
|
1040 |
+
$code = chr($startid).$code.chr($check).chr(106).chr(107);
|
1041 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
1042 |
+
$k = 0;
|
1043 |
+
$len = strlen($code);
|
1044 |
+
for ($i = 0; $i < $len; ++$i) {
|
1045 |
+
$ck = strpos($keys, $code[$i]);
|
1046 |
+
if (($i == 0) || ($ean && $i==1) | ($i > ($len-4))) {
|
1047 |
+
$char_num = ord($code[$i]);
|
1048 |
+
$seq = $chr[$char_num];
|
1049 |
+
} elseif(($ck >= 0) AND isset($chr[$ck])) {
|
1050 |
+
$seq = $chr[$ck];
|
1051 |
+
} else {
|
1052 |
+
// invalid character
|
1053 |
+
return false;
|
1054 |
+
}
|
1055 |
+
for ($j = 0; $j < 6; ++$j) {
|
1056 |
+
if (($j % 2) == 0) {
|
1057 |
+
$t = true; // bar
|
1058 |
+
} else {
|
1059 |
+
$t = false; // space
|
1060 |
+
}
|
1061 |
+
$w = $seq[$j];
|
1062 |
+
$bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
|
1063 |
+
$bararray['maxw'] += $w;
|
1064 |
+
++$k;
|
1065 |
+
}
|
1066 |
+
}
|
1067 |
+
$bararray['checkdigit'] = $checkdigit;
|
1068 |
+
return $bararray;
|
1069 |
+
}
|
1070 |
+
|
1071 |
+
/**
|
1072 |
+
* EAN13 and UPC-A barcodes.
|
1073 |
+
* EAN13: European Article Numbering international retail product code
|
1074 |
+
* UPC-A: Universal product code seen on almost all retail products in the USA and Canada
|
1075 |
+
* UPC-E: Short version of UPC symbol
|
1076 |
+
*/
|
1077 |
+
protected function barcode_eanupc($code, $len=13) {
|
1078 |
+
$upce = false;
|
1079 |
+
$checkdigit = false;
|
1080 |
+
if ($len == 6) {
|
1081 |
+
$len = 12; // UPC-A
|
1082 |
+
$upce = true; // UPC-E mode
|
1083 |
+
}
|
1084 |
+
$data_len = $len - 1;
|
1085 |
+
//Padding
|
1086 |
+
$code = str_pad($code, $data_len, '0', STR_PAD_LEFT);
|
1087 |
+
$code_len = strlen($code);
|
1088 |
+
// calculate check digit
|
1089 |
+
$sum_a = 0;
|
1090 |
+
for ($i = 1; $i < $data_len; $i+=2) {
|
1091 |
+
$sum_a += $code[$i];
|
1092 |
+
}
|
1093 |
+
if ($len > 12) {
|
1094 |
+
$sum_a *= 3;
|
1095 |
+
}
|
1096 |
+
$sum_b = 0;
|
1097 |
+
for ($i = 0; $i < $data_len; $i+=2) {
|
1098 |
+
$sum_b += ($code[$i]);
|
1099 |
+
}
|
1100 |
+
if ($len < 13) {
|
1101 |
+
$sum_b *= 3;
|
1102 |
+
}
|
1103 |
+
$r = ($sum_a + $sum_b) % 10;
|
1104 |
+
if($r > 0) {
|
1105 |
+
$r = (10 - $r);
|
1106 |
+
}
|
1107 |
+
if ($code_len == $data_len) {
|
1108 |
+
// add check digit
|
1109 |
+
$code .= $r;
|
1110 |
+
$checkdigit = $r;
|
1111 |
+
} elseif ($r !== intval($code[$data_len])) {
|
1112 |
+
// wrong checkdigit
|
1113 |
+
return false;
|
1114 |
+
}
|
1115 |
+
if ($len == 12) {
|
1116 |
+
// UPC-A
|
1117 |
+
$code = '0'.$code;
|
1118 |
+
++$len;
|
1119 |
+
}
|
1120 |
+
if ($upce) {
|
1121 |
+
// convert UPC-A to UPC-E
|
1122 |
+
$tmp = substr($code, 4, 3);
|
1123 |
+
$prod_code = intval(substr($code,7,5)); // product code
|
1124 |
+
$invalid_upce = false;
|
1125 |
+
if (($tmp == '000') OR ($tmp == '100') OR ($tmp == '200')) {
|
1126 |
+
// manufacturer code ends in 000, 100, or 200
|
1127 |
+
$upce_code = substr($code, 2, 2).substr($code, 9, 3).substr($code, 4, 1);
|
1128 |
+
if ($prod_code > 999) { $invalid_upce = true; }
|
1129 |
+
} else {
|
1130 |
+
$tmp = substr($code, 5, 2);
|
1131 |
+
if ($tmp == '00') {
|
1132 |
+
// manufacturer code ends in 00
|
1133 |
+
$upce_code = substr($code, 2, 3).substr($code, 10, 2).'3';
|
1134 |
+
if ($prod_code > 99) { $invalid_upce = true; }
|
1135 |
+
} else {
|
1136 |
+
$tmp = substr($code, 6, 1);
|
1137 |
+
if ($tmp == '0') {
|
1138 |
+
// manufacturer code ends in 0
|
1139 |
+
$upce_code = substr($code, 2, 4).substr($code, 11, 1).'4';
|
1140 |
+
if ($prod_code > 9) { $invalid_upce = true; }
|
1141 |
+
} else {
|
1142 |
+
// manufacturer code does not end in zero
|
1143 |
+
$upce_code = substr($code, 2, 5).substr($code, 11, 1);
|
1144 |
+
if ($prod_code > 9) { $invalid_upce = true; }
|
1145 |
+
}
|
1146 |
+
}
|
1147 |
+
}
|
1148 |
+
if ($invalid_upce) { die("Error - UPC-A cannot produce a valid UPC-E barcode"); } // Error generating a UPCE code
|
1149 |
+
}
|
1150 |
+
//Convert digits to bars
|
1151 |
+
$codes = array(
|
1152 |
+
'A'=>array( // left odd parity
|
1153 |
+
'0'=>'0001101',
|
1154 |
+
'1'=>'0011001',
|
1155 |
+
'2'=>'0010011',
|
1156 |
+
'3'=>'0111101',
|
1157 |
+
'4'=>'0100011',
|
1158 |
+
'5'=>'0110001',
|
1159 |
+
'6'=>'0101111',
|
1160 |
+
'7'=>'0111011',
|
1161 |
+
'8'=>'0110111',
|
1162 |
+
'9'=>'0001011'),
|
1163 |
+
'B'=>array( // left even parity
|
1164 |
+
'0'=>'0100111',
|
1165 |
+
'1'=>'0110011',
|
1166 |
+
'2'=>'0011011',
|
1167 |
+
'3'=>'0100001',
|
1168 |
+
'4'=>'0011101',
|
1169 |
+
'5'=>'0111001',
|
1170 |
+
'6'=>'0000101',
|
1171 |
+
'7'=>'0010001',
|
1172 |
+
'8'=>'0001001',
|
1173 |
+
'9'=>'0010111'),
|
1174 |
+
'C'=>array( // right
|
1175 |
+
'0'=>'1110010',
|
1176 |
+
'1'=>'1100110',
|
1177 |
+
'2'=>'1101100',
|
1178 |
+
'3'=>'1000010',
|
1179 |
+
'4'=>'1011100',
|
1180 |
+
'5'=>'1001110',
|
1181 |
+
'6'=>'1010000',
|
1182 |
+
'7'=>'1000100',
|
1183 |
+
'8'=>'1001000',
|
1184 |
+
'9'=>'1110100')
|
1185 |
+
);
|
1186 |
+
$parities = array(
|
1187 |
+
'0'=>array('A','A','A','A','A','A'),
|
1188 |
+
'1'=>array('A','A','B','A','B','B'),
|
1189 |
+
'2'=>array('A','A','B','B','A','B'),
|
1190 |
+
'3'=>array('A','A','B','B','B','A'),
|
1191 |
+
'4'=>array('A','B','A','A','B','B'),
|
1192 |
+
'5'=>array('A','B','B','A','A','B'),
|
1193 |
+
'6'=>array('A','B','B','B','A','A'),
|
1194 |
+
'7'=>array('A','B','A','B','A','B'),
|
1195 |
+
'8'=>array('A','B','A','B','B','A'),
|
1196 |
+
'9'=>array('A','B','B','A','B','A')
|
1197 |
+
);
|
1198 |
+
$upce_parities = array();
|
1199 |
+
$upce_parities[0] = array(
|
1200 |
+
'0'=>array('B','B','B','A','A','A'),
|
1201 |
+
'1'=>array('B','B','A','B','A','A'),
|
1202 |
+
'2'=>array('B','B','A','A','B','A'),
|
1203 |
+
'3'=>array('B','B','A','A','A','B'),
|
1204 |
+
'4'=>array('B','A','B','B','A','A'),
|
1205 |
+
'5'=>array('B','A','A','B','B','A'),
|
1206 |
+
'6'=>array('B','A','A','A','B','B'),
|
1207 |
+
'7'=>array('B','A','B','A','B','A'),
|
1208 |
+
'8'=>array('B','A','B','A','A','B'),
|
1209 |
+
'9'=>array('B','A','A','B','A','B')
|
1210 |
+
);
|
1211 |
+
$upce_parities[1] = array(
|
1212 |
+
'0'=>array('A','A','A','B','B','B'),
|
1213 |
+
'1'=>array('A','A','B','A','B','B'),
|
1214 |
+
'2'=>array('A','A','B','B','A','B'),
|
1215 |
+
'3'=>array('A','A','B','B','B','A'),
|
1216 |
+
'4'=>array('A','B','A','A','B','B'),
|
1217 |
+
'5'=>array('A','B','B','A','A','B'),
|
1218 |
+
'6'=>array('A','B','B','B','A','A'),
|
1219 |
+
'7'=>array('A','B','A','B','A','B'),
|
1220 |
+
'8'=>array('A','B','A','B','B','A'),
|
1221 |
+
'9'=>array('A','B','B','A','B','A')
|
1222 |
+
);
|
1223 |
+
$k = 0;
|
1224 |
+
$seq = '101'; // left guard bar
|
1225 |
+
if ($upce) {
|
1226 |
+
$bararray = array('code' => $upce_code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
1227 |
+
$p = $upce_parities[$code{1}][$r];
|
1228 |
+
for ($i = 0; $i < 6; ++$i) {
|
1229 |
+
$seq .= $codes[$p[$i]][$upce_code[$i]];
|
1230 |
+
}
|
1231 |
+
$seq .= '010101'; // right guard bar
|
1232 |
+
} else {
|
1233 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
1234 |
+
$half_len = ceil($len / 2);
|
1235 |
+
if ($len == 8) {
|
1236 |
+
for ($i = 0; $i < $half_len; ++$i) {
|
1237 |
+
$seq .= $codes['A'][$code[$i]];
|
1238 |
+
}
|
1239 |
+
} else {
|
1240 |
+
$p = $parities[$code{0}];
|
1241 |
+
for ($i = 1; $i < $half_len; ++$i) {
|
1242 |
+
$seq .= $codes[$p[$i-1]][$code[$i]];
|
1243 |
+
}
|
1244 |
+
}
|
1245 |
+
$seq .= '01010'; // center guard bar
|
1246 |
+
for ($i = $half_len; $i < $len; ++$i) {
|
1247 |
+
$seq .= $codes['C'][$code[$i]];
|
1248 |
+
}
|
1249 |
+
$seq .= '101'; // right guard bar
|
1250 |
+
}
|
1251 |
+
$clen = strlen($seq);
|
1252 |
+
$w = 0;
|
1253 |
+
for ($i = 0; $i < $clen; ++$i) {
|
1254 |
+
$w += 1;
|
1255 |
+
if (($i == ($clen - 1)) OR (($i < ($clen - 1)) AND ($seq[$i] != $seq[($i+1)]))) {
|
1256 |
+
if ($seq[$i] == '1') {
|
1257 |
+
$t = true; // bar
|
1258 |
+
} else {
|
1259 |
+
$t = false; // space
|
1260 |
+
}
|
1261 |
+
$bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
|
1262 |
+
$bararray['maxw'] += $w;
|
1263 |
+
++$k;
|
1264 |
+
$w = 0;
|
1265 |
+
}
|
1266 |
+
}
|
1267 |
+
$bararray['checkdigit'] = $checkdigit;
|
1268 |
+
return $bararray;
|
1269 |
+
}
|
1270 |
+
|
1271 |
+
/**
|
1272 |
+
* UPC-Based Extentions
|
1273 |
+
* 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers
|
1274 |
+
* 5-Digit Ext.: Used to mark suggested retail price of books
|
1275 |
+
*/
|
1276 |
+
protected function barcode_eanext($code, $len=5) {
|
1277 |
+
//Padding
|
1278 |
+
$code = str_pad($code, $len, '0', STR_PAD_LEFT);
|
1279 |
+
// calculate check digit
|
1280 |
+
if ($len == 2) {
|
1281 |
+
$r = $code % 4;
|
1282 |
+
} elseif ($len == 5) {
|
1283 |
+
$r = (3 * ($code{0} + $code{2} + $code{4})) + (9 * ($code{1} + $code{3}));
|
1284 |
+
$r %= 10;
|
1285 |
+
} else {
|
1286 |
+
return false;
|
1287 |
+
}
|
1288 |
+
//Convert digits to bars
|
1289 |
+
$codes = array(
|
1290 |
+
'A'=>array( // left odd parity
|
1291 |
+
'0'=>'0001101',
|
1292 |
+
'1'=>'0011001',
|
1293 |
+
'2'=>'0010011',
|
1294 |
+
'3'=>'0111101',
|
1295 |
+
'4'=>'0100011',
|
1296 |
+
'5'=>'0110001',
|
1297 |
+
'6'=>'0101111',
|
1298 |
+
'7'=>'0111011',
|
1299 |
+
'8'=>'0110111',
|
1300 |
+
'9'=>'0001011'),
|
1301 |
+
'B'=>array( // left even parity
|
1302 |
+
'0'=>'0100111',
|
1303 |
+
'1'=>'0110011',
|
1304 |
+
'2'=>'0011011',
|
1305 |
+
'3'=>'0100001',
|
1306 |
+
'4'=>'0011101',
|
1307 |
+
'5'=>'0111001',
|
1308 |
+
'6'=>'0000101',
|
1309 |
+
'7'=>'0010001',
|
1310 |
+
'8'=>'0001001',
|
1311 |
+
'9'=>'0010111')
|
1312 |
+
);
|
1313 |
+
$parities = array();
|
1314 |
+
$parities[2] = array(
|
1315 |
+
'0'=>array('A','A'),
|
1316 |
+
'1'=>array('A','B'),
|
1317 |
+
'2'=>array('B','A'),
|
1318 |
+
'3'=>array('B','B')
|
1319 |
+
);
|
1320 |
+
$parities[5] = array(
|
1321 |
+
'0'=>array('B','B','A','A','A'),
|
1322 |
+
'1'=>array('B','A','B','A','A'),
|
1323 |
+
'2'=>array('B','A','A','B','A'),
|
1324 |
+
'3'=>array('B','A','A','A','B'),
|
1325 |
+
'4'=>array('A','B','B','A','A'),
|
1326 |
+
'5'=>array('A','A','B','B','A'),
|
1327 |
+
'6'=>array('A','A','A','B','B'),
|
1328 |
+
'7'=>array('A','B','A','B','A'),
|
1329 |
+
'8'=>array('A','B','A','A','B'),
|
1330 |
+
'9'=>array('A','A','B','A','B')
|
1331 |
+
);
|
1332 |
+
$p = $parities[$len][$r];
|
1333 |
+
$seq = '1011'; // left guard bar
|
1334 |
+
$seq .= $codes[$p[0]][$code{0}];
|
1335 |
+
for ($i = 1; $i < $len; ++$i) {
|
1336 |
+
$seq .= '01'; // separator
|
1337 |
+
$seq .= $codes[$p[$i]][$code[$i]];
|
1338 |
+
}
|
1339 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
1340 |
+
return $this->binseq_to_array($seq, $bararray);
|
1341 |
+
}
|
1342 |
+
|
1343 |
+
/**
|
1344 |
+
* POSTNET and PLANET barcodes.
|
1345 |
+
* Used by U.S. Postal Service for automated mail sorting
|
1346 |
+
*/
|
1347 |
+
protected function barcode_postnet($code, $planet=false) {
|
1348 |
+
// bar lenght
|
1349 |
+
if ($planet) {
|
1350 |
+
$barlen = Array(
|
1351 |
+
0 => Array(1,1,2,2,2),
|
1352 |
+
1 => Array(2,2,2,1,1),
|
1353 |
+
2 => Array(2,2,1,2,1),
|
1354 |
+
3 => Array(2,2,1,1,2),
|
1355 |
+
4 => Array(2,1,2,2,1),
|
1356 |
+
5 => Array(2,1,2,1,2),
|
1357 |
+
6 => Array(2,1,1,2,2),
|
1358 |
+
7 => Array(1,2,2,2,1),
|
1359 |
+
8 => Array(1,2,2,1,2),
|
1360 |
+
9 => Array(1,2,1,2,2)
|
1361 |
+
);
|
1362 |
+
} else {
|
1363 |
+
$barlen = Array(
|
1364 |
+
0 => Array(2,2,1,1,1),
|
1365 |
+
1 => Array(1,1,1,2,2),
|
1366 |
+
2 => Array(1,1,2,1,2),
|
1367 |
+
3 => Array(1,1,2,2,1),
|
1368 |
+
4 => Array(1,2,1,1,2),
|
1369 |
+
5 => Array(1,2,1,2,1),
|
1370 |
+
6 => Array(1,2,2,1,1),
|
1371 |
+
7 => Array(2,1,1,1,2),
|
1372 |
+
8 => Array(2,1,1,2,1),
|
1373 |
+
9 => Array(2,1,2,1,1)
|
1374 |
+
);
|
1375 |
+
}
|
1376 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 5, 'bcode' => array());
|
1377 |
+
$k = 0;
|
1378 |
+
$code = str_replace('-', '', $code);
|
1379 |
+
$code = str_replace(' ', '', $code);
|
1380 |
+
$len = strlen($code);
|
1381 |
+
// calculate checksum
|
1382 |
+
$sum = 0;
|
1383 |
+
for ($i = 0; $i < $len; ++$i) {
|
1384 |
+
$sum += intval($code[$i]);
|
1385 |
+
}
|
1386 |
+
$chkd = ($sum % 10);
|
1387 |
+
if($chkd > 0) {
|
1388 |
+
$chkd = (10 - $chkd);
|
1389 |
+
}
|
1390 |
+
$code .= $chkd;
|
1391 |
+
$checkdigit = $chkd;
|
1392 |
+
$len = strlen($code);
|
1393 |
+
// start bar
|
1394 |
+
$bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 5, 'p' => 0);
|
1395 |
+
$bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth , 'h' => 5, 'p' => 0);
|
1396 |
+
$bararray['maxw'] += (1 + $this->gapwidth );
|
1397 |
+
for ($i = 0; $i < $len; ++$i) {
|
1398 |
+
for ($j = 0; $j < 5; ++$j) {
|
1399 |
+
$bh = $barlen[$code[$i]][$j];
|
1400 |
+
if ($bh == 2) {
|
1401 |
+
$h = 5;
|
1402 |
+
$p = 0;
|
1403 |
+
}
|
1404 |
+
else {
|
1405 |
+
$h = 2;
|
1406 |
+
$p = 3;
|
1407 |
+
}
|
1408 |
+
$bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
|
1409 |
+
$bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth , 'h' => 2, 'p' => 0);
|
1410 |
+
$bararray['maxw'] += (1 + $this->gapwidth );
|
1411 |
+
}
|
1412 |
+
}
|
1413 |
+
// end bar
|
1414 |
+
$bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 5, 'p' => 0);
|
1415 |
+
$bararray['maxw'] += 1;
|
1416 |
+
$bararray['checkdigit'] = $checkdigit;
|
1417 |
+
return $bararray;
|
1418 |
+
}
|
1419 |
+
|
1420 |
+
/**
|
1421 |
+
* RM4SCC - CBC - KIX
|
1422 |
+
* RM4SCC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index)
|
1423 |
+
* RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service.
|
1424 |
+
*/
|
1425 |
+
protected function barcode_rm4scc($code, $kix=false) {
|
1426 |
+
$notkix = !$kix;
|
1427 |
+
// bar mode
|
1428 |
+
// 1 = pos 1, length 2
|
1429 |
+
// 2 = pos 1, length 3
|
1430 |
+
// 3 = pos 2, length 1
|
1431 |
+
// 4 = pos 2, length 2
|
1432 |
+
$barmode = array(
|
1433 |
+
'0' => array(3,3,2,2),
|
1434 |
+
'1' => array(3,4,1,2),
|
1435 |
+
'2' => array(3,4,2,1),
|
1436 |
+
'3' => array(4,3,1,2),
|
1437 |
+
'4' => array(4,3,2,1),
|
1438 |
+
'5' => array(4,4,1,1),
|
1439 |
+
'6' => array(3,1,4,2),
|
1440 |
+
'7' => array(3,2,3,2),
|
1441 |
+
'8' => array(3,2,4,1),
|
1442 |
+
'9' => array(4,1,3,2),
|
1443 |
+
'A' => array(4,1,4,1),
|
1444 |
+
'B' => array(4,2,3,1),
|
1445 |
+
'C' => array(3,1,2,4),
|
1446 |
+
'D' => array(3,2,1,4),
|
1447 |
+
'E' => array(3,2,2,3),
|
1448 |
+
'F' => array(4,1,1,4),
|
1449 |
+
'G' => array(4,1,2,3),
|
1450 |
+
'H' => array(4,2,1,3),
|
1451 |
+
'I' => array(1,3,4,2),
|
1452 |
+
'J' => array(1,4,3,2),
|
1453 |
+
'K' => array(1,4,4,1),
|
1454 |
+
'L' => array(2,3,3,2),
|
1455 |
+
'M' => array(2,3,4,1),
|
1456 |
+
'N' => array(2,4,3,1),
|
1457 |
+
'O' => array(1,3,2,4),
|
1458 |
+
'P' => array(1,4,1,4),
|
1459 |
+
'Q' => array(1,4,2,3),
|
1460 |
+
'R' => array(2,3,1,4),
|
1461 |
+
'S' => array(2,3,2,3),
|
1462 |
+
'T' => array(2,4,1,3),
|
1463 |
+
'U' => array(1,1,4,4),
|
1464 |
+
'V' => array(1,2,3,4),
|
1465 |
+
'W' => array(1,2,4,3),
|
1466 |
+
'X' => array(2,1,3,4),
|
1467 |
+
'Y' => array(2,1,4,3),
|
1468 |
+
'Z' => array(2,2,3,3)
|
1469 |
+
);
|
1470 |
+
$code = strtoupper($code);
|
1471 |
+
$len = strlen($code);
|
1472 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => $this->daft['F'], 'bcode' => array());
|
1473 |
+
if ($notkix) {
|
1474 |
+
// table for checksum calculation (row,col)
|
1475 |
+
$checktable = array(
|
1476 |
+
'0' => array(1,1),
|
1477 |
+
'1' => array(1,2),
|
1478 |
+
'2' => array(1,3),
|
1479 |
+
'3' => array(1,4),
|
1480 |
+
'4' => array(1,5),
|
1481 |
+
'5' => array(1,0),
|
1482 |
+
'6' => array(2,1),
|
1483 |
+
'7' => array(2,2),
|
1484 |
+
'8' => array(2,3),
|
1485 |
+
'9' => array(2,4),
|
1486 |
+
'A' => array(2,5),
|
1487 |
+
'B' => array(2,0),
|
1488 |
+
'C' => array(3,1),
|
1489 |
+
'D' => array(3,2),
|
1490 |
+
'E' => array(3,3),
|
1491 |
+
'F' => array(3,4),
|
1492 |
+
'G' => array(3,5),
|
1493 |
+
'H' => array(3,0),
|
1494 |
+
'I' => array(4,1),
|
1495 |
+
'J' => array(4,2),
|
1496 |
+
'K' => array(4,3),
|
1497 |
+
'L' => array(4,4),
|
1498 |
+
'M' => array(4,5),
|
1499 |
+
'N' => array(4,0),
|
1500 |
+
'O' => array(5,1),
|
1501 |
+
'P' => array(5,2),
|
1502 |
+
'Q' => array(5,3),
|
1503 |
+
'R' => array(5,4),
|
1504 |
+
'S' => array(5,5),
|
1505 |
+
'T' => array(5,0),
|
1506 |
+
'U' => array(0,1),
|
1507 |
+
'V' => array(0,2),
|
1508 |
+
'W' => array(0,3),
|
1509 |
+
'X' => array(0,4),
|
1510 |
+
'Y' => array(0,5),
|
1511 |
+
'Z' => array(0,0)
|
1512 |
+
);
|
1513 |
+
$row = 0;
|
1514 |
+
$col = 0;
|
1515 |
+
for ($i = 0; $i < $len; ++$i) {
|
1516 |
+
$row += $checktable[$code[$i]][0];
|
1517 |
+
$col += $checktable[$code[$i]][1];
|
1518 |
+
}
|
1519 |
+
$row %= 6;
|
1520 |
+
$col %= 6;
|
1521 |
+
$chk = array_keys($checktable, array($row,$col));
|
1522 |
+
$code .= $chk[0];
|
1523 |
+
$bararray['checkdigit'] = $chk[0];
|
1524 |
+
++$len;
|
1525 |
+
}
|
1526 |
+
$k = 0;
|
1527 |
+
if ($notkix) {
|
1528 |
+
// start bar
|
1529 |
+
$bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $this->daft['A'] , 'p' => 0);
|
1530 |
+
$bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth , 'h' => $this->daft['A'] , 'p' => 0);
|
1531 |
+
$bararray['maxw'] += (1 + $this->gapwidth) ;
|
1532 |
+
}
|
1533 |
+
for ($i = 0; $i < $len; ++$i) {
|
1534 |
+
for ($j = 0; $j < 4; ++$j) {
|
1535 |
+
switch ($barmode[$code[$i]][$j]) {
|
1536 |
+
case 1: {
|
1537 |
+
// ascender (A)
|
1538 |
+
$p = 0;
|
1539 |
+
$h = $this->daft['A'];
|
1540 |
+
break;
|
1541 |
+
}
|
1542 |
+
case 2: {
|
1543 |
+
// full bar (F)
|
1544 |
+
$p = 0;
|
1545 |
+
$h = $this->daft['F'];
|
1546 |
+
break;
|
1547 |
+
}
|
1548 |
+
case 3: {
|
1549 |
+
// tracker (T)
|
1550 |
+
$p = ($this->daft['F'] - $this->daft['T'])/2;
|
1551 |
+
$h = $this->daft['T'];
|
1552 |
+
break;
|
1553 |
+
}
|
1554 |
+
case 4: {
|
1555 |
+
// descender (D)
|
1556 |
+
$p = $this->daft['F'] - $this->daft['D'];
|
1557 |
+
$h = $this->daft['D'];
|
1558 |
+
break;
|
1559 |
+
}
|
1560 |
+
}
|
1561 |
+
|
1562 |
+
$bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
|
1563 |
+
$bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth, 'h' => 2, 'p' => 0);
|
1564 |
+
$bararray['maxw'] += (1 + $this->gapwidth) ;
|
1565 |
+
}
|
1566 |
+
}
|
1567 |
+
if ($notkix) {
|
1568 |
+
// stop bar
|
1569 |
+
$bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $this->daft['F'], 'p' => 0);
|
1570 |
+
$bararray['maxw'] += 1;
|
1571 |
+
}
|
1572 |
+
return $bararray;
|
1573 |
+
}
|
1574 |
+
|
1575 |
+
/**
|
1576 |
+
* CODABAR barcodes.
|
1577 |
+
* Older code often used in library systems, sometimes in blood banks
|
1578 |
+
*/
|
1579 |
+
protected function barcode_codabar($code) {
|
1580 |
+
$chr = array(
|
1581 |
+
'0' => '11111221',
|
1582 |
+
'1' => '11112211',
|
1583 |
+
'2' => '11121121',
|
1584 |
+
'3' => '22111111',
|
1585 |
+
'4' => '11211211',
|
1586 |
+
'5' => '21111211',
|
1587 |
+
'6' => '12111121',
|
1588 |
+
'7' => '12112111',
|
1589 |
+
'8' => '12211111',
|
1590 |
+
'9' => '21121111',
|
1591 |
+
'-' => '11122111',
|
1592 |
+
'$' => '11221111',
|
1593 |
+
':' => '21112121',
|
1594 |
+
'/' => '21211121',
|
1595 |
+
'.' => '21212111',
|
1596 |
+
'+' => '11222221',
|
1597 |
+
'A' => '11221211',
|
1598 |
+
'B' => '12121121',
|
1599 |
+
'C' => '11121221',
|
1600 |
+
'D' => '11122211'
|
1601 |
+
);
|
1602 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
1603 |
+
$k = 0;
|
1604 |
+
$w = 0;
|
1605 |
+
$seq = '';
|
1606 |
+
$code = strtoupper($code);
|
1607 |
+
$len = strlen($code);
|
1608 |
+
for ($i = 0; $i < $len; ++$i) {
|
1609 |
+
if (!isset($chr[$code[$i]])) {
|
1610 |
+
return false;
|
1611 |
+
}
|
1612 |
+
$seq = $chr[$code[$i]];
|
1613 |
+
for ($j = 0; $j < 8; ++$j) {
|
1614 |
+
if (($j % 2) == 0) {
|
1615 |
+
$t = true; // bar
|
1616 |
+
} else {
|
1617 |
+
$t = false; // space
|
1618 |
+
}
|
1619 |
+
$x = $seq[$j];
|
1620 |
+
if ($x == 2) { $w = $this->print_ratio; }
|
1621 |
+
else { $w = 1; }
|
1622 |
+
$bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
|
1623 |
+
$bararray['maxw'] += $w;
|
1624 |
+
++$k;
|
1625 |
+
}
|
1626 |
+
}
|
1627 |
+
return $bararray;
|
1628 |
+
}
|
1629 |
+
|
1630 |
+
/**
|
1631 |
+
* CODE11 barcodes.
|
1632 |
+
* Used primarily for labeling telecommunications equipment
|
1633 |
+
*/
|
1634 |
+
protected function barcode_code11($code) {
|
1635 |
+
$chr = array(
|
1636 |
+
'0' => '111121',
|
1637 |
+
'1' => '211121',
|
1638 |
+
'2' => '121121',
|
1639 |
+
'3' => '221111',
|
1640 |
+
'4' => '112121',
|
1641 |
+
'5' => '212111',
|
1642 |
+
'6' => '122111',
|
1643 |
+
'7' => '111221',
|
1644 |
+
'8' => '211211',
|
1645 |
+
'9' => '211111',
|
1646 |
+
'-' => '112111',
|
1647 |
+
'S' => '112211'
|
1648 |
+
);
|
1649 |
+
|
1650 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
|
1651 |
+
$k = 0;
|
1652 |
+
$w = 0;
|
1653 |
+
$seq = '';
|
1654 |
+
$len = strlen($code);
|
1655 |
+
// calculate check digit C
|
1656 |
+
$p = 1;
|
1657 |
+
$check = 0;
|
1658 |
+
for ($i = ($len - 1); $i >= 0; --$i) {
|
1659 |
+
$digit = $code[$i];
|
1660 |
+
if ($digit == '-') {
|
1661 |
+
$dval = 10;
|
1662 |
+
} else {
|
1663 |
+
$dval = intval($digit);
|
1664 |
+
}
|
1665 |
+
$check += ($dval * $p);
|
1666 |
+
++$p;
|
1667 |
+
if ($p > 10) {
|
1668 |
+
$p = 1;
|
1669 |
+
}
|
1670 |
+
}
|
1671 |
+
$check %= 11;
|
1672 |
+
if ($check == 10) {
|
1673 |
+
$check = '-';
|
1674 |
+
}
|
1675 |
+
$code .= $check;
|
1676 |
+
$checkdigit = $check;
|
1677 |
+
if ($len > 10) {
|
1678 |
+
// calculate check digit K
|
1679 |
+
$p = 1;
|
1680 |
+
$check = 0;
|
1681 |
+
for ($i = $len; $i >= 0; --$i) {
|
1682 |
+
$digit = $code[$i];
|
1683 |
+
if ($digit == '-') {
|
1684 |
+
$dval = 10;
|
1685 |
+
} else {
|
1686 |
+
$dval = intval($digit);
|
1687 |
+
}
|
1688 |
+
$check += ($dval * $p);
|
1689 |
+
++$p;
|
1690 |
+
if ($p > 9) {
|
1691 |
+
$p = 1;
|
1692 |
+
}
|
1693 |
+
}
|
1694 |
+
$check %= 11;
|
1695 |
+
$code .= $check;
|
1696 |
+
$checkdigit .= $check;
|
1697 |
+
++$len;
|
1698 |
+
}
|
1699 |
+
$code = 'S'.$code.'S';
|
1700 |
+
$len += 3;
|
1701 |
+
for ($i = 0; $i < $len; ++$i) {
|
1702 |
+
if (!isset($chr[$code[$i]])) {
|
1703 |
+
return false;
|
1704 |
+
}
|
1705 |
+
$seq = $chr[$code[$i]];
|
1706 |
+
for ($j = 0; $j < 6; ++$j) {
|
1707 |
+
if (($j % 2) == 0) {
|
1708 |
+
$t = true; // bar
|
1709 |
+
} else {
|
1710 |
+
$t = false; // space
|
1711 |
+
}
|
1712 |
+
$x = $seq[$j];
|
1713 |
+
if ($x == 2) { $w = $this->print_ratio; }
|
1714 |
+
else { $w = 1; }
|
1715 |
+
$bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
|
1716 |
+
$bararray['maxw'] += $w;
|
1717 |
+
++$k;
|
1718 |
+
}
|
1719 |
+
}
|
1720 |
+
$bararray['checkdigit'] = $checkdigit;
|
1721 |
+
return $bararray;
|
1722 |
+
}
|
1723 |
+
|
1724 |
+
|
1725 |
+
/**
|
1726 |
+
* IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
|
1727 |
+
* (requires PHP bcmath extension)
|
1728 |
+
* Intelligent Mail barcode is a 65-bar code for use on mail in the United States.
|
1729 |
+
* The fields are described as follows:<ul><li>The Barcode Identifier shall be assigned by USPS to encode the presort identification that is currently printed in human readable form on the optional endorsement line (OEL) as well as for future USPS use. This shall be two digits, with the second digit in the range of 0-4. The allowable encoding ranges shall be 00-04, 10-14, 20-24, 30-34, 40-44, 50-54, 60-64, 70-74, 80-84, and 90-94.</li><li>The Service Type Identifier shall be assigned by USPS for any combination of services requested on the mailpiece. The allowable encoding range shall be 000-999. Each 3-digit value shall correspond to a particular mail class with a particular combination of service(s). Each service program, such as OneCode Confirm and OneCode ACS, shall provide the list of Service Type Identifier values.</li><li>The Mailer or Customer Identifier shall be assigned by USPS as a unique, 6 or 9 digit number that identifies a business entity. The allowable encoding range for the 6 digit Mailer ID shall be 000000- 899999, while the allowable encoding range for the 9 digit Mailer ID shall be 900000000-999999999.</li><li>The Serial or Sequence Number shall be assigned by the mailer for uniquely identifying and tracking mailpieces. The allowable encoding range shall be 000000000-999999999 when used with a 6 digit Mailer ID and 000000-999999 when used with a 9 digit Mailer ID. e. The Delivery Point ZIP Code shall be assigned by the mailer for routing the mailpiece. This shall replace POSTNET for routing the mailpiece to its final delivery point. The length may be 0, 5, 9, or 11 digits. The allowable encoding ranges shall be no ZIP Code, 00000-99999, 000000000-999999999, and 00000000000-99999999999.</li></ul>
|
1730 |
+
*/
|
1731 |
+
protected function barcode_imb($code) {
|
1732 |
+
$asc_chr = array(4,0,2,6,3,5,1,9,8,7,1,2,0,6,4,8,2,9,5,3,0,1,3,7,4,6,8,9,2,0,5,1,9,4,3,8,6,7,1,2,4,3,9,5,7,8,3,0,2,1,4,0,9,1,7,0,2,4,6,3,7,1,9,5,8);
|
1733 |
+
$dsc_chr = array(7,1,9,5,8,0,2,4,6,3,5,8,9,7,3,0,6,1,7,4,6,8,9,2,5,1,7,5,4,3,8,7,6,0,2,5,4,9,3,0,1,6,8,2,0,4,5,9,6,7,5,2,6,3,8,5,1,9,8,7,4,0,2,6,3);
|
1734 |
+
$asc_pos = array(3,0,8,11,1,12,8,11,10,6,4,12,2,7,9,6,7,9,2,8,4,0,12,7,10,9,0,7,10,5,7,9,6,8,2,12,1,4,2,0,1,5,4,6,12,1,0,9,4,7,5,10,2,6,9,11,2,12,6,7,5,11,0,3,2);
|
1735 |
+
$dsc_pos = array(2,10,12,5,9,1,5,4,3,9,11,5,10,1,6,3,4,1,10,0,2,11,8,6,1,12,3,8,6,4,4,11,0,6,1,9,11,5,3,7,3,10,7,11,8,2,10,3,5,8,0,3,12,11,8,4,5,1,3,0,7,12,9,8,10);
|
1736 |
+
$code_arr = explode('-', $code);
|
1737 |
+
$tracking_number = $code_arr[0];
|
1738 |
+
if (isset($code_arr[1])) {
|
1739 |
+
$routing_code = $code_arr[1];
|
1740 |
+
} else {
|
1741 |
+
$routing_code = '';
|
1742 |
+
}
|
1743 |
+
// Conversion of Routing Code
|
1744 |
+
switch (strlen($routing_code)) {
|
1745 |
+
case 0: {
|
1746 |
+
$binary_code = 0;
|
1747 |
+
break;
|
1748 |
+
}
|
1749 |
+
case 5: {
|
1750 |
+
$binary_code = bcadd($routing_code, '1');
|
1751 |
+
break;
|
1752 |
+
}
|
1753 |
+
case 9: {
|
1754 |
+
$binary_code = bcadd($routing_code, '100001');
|
1755 |
+
break;
|
1756 |
+
}
|
1757 |
+
case 11: {
|
1758 |
+
$binary_code = bcadd($routing_code, '1000100001');
|
1759 |
+
break;
|
1760 |
+
}
|
1761 |
+
default: {
|
1762 |
+
return false;
|
1763 |
+
break;
|
1764 |
+
}
|
1765 |
+
}
|
1766 |
+
$binary_code = bcmul($binary_code, 10);
|
1767 |
+
$binary_code = bcadd($binary_code, $tracking_number{0});
|
1768 |
+
$binary_code = bcmul($binary_code, 5);
|
1769 |
+
$binary_code = bcadd($binary_code, $tracking_number{1});
|
1770 |
+
$binary_code .= substr($tracking_number, 2, 18);
|
1771 |
+
// convert to hexadecimal
|
1772 |
+
$binary_code = $this->dec_to_hex($binary_code);
|
1773 |
+
// pad to get 13 bytes
|
1774 |
+
$binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT);
|
1775 |
+
// convert string to array of bytes
|
1776 |
+
$binary_code_arr = chunk_split($binary_code, 2, "\r");
|
1777 |
+
$binary_code_arr = substr($binary_code_arr, 0, -1);
|
1778 |
+
$binary_code_arr = explode("\r", $binary_code_arr);
|
1779 |
+
// calculate frame check sequence
|
1780 |
+
$fcs = $this->imb_crc11fcs($binary_code_arr);
|
1781 |
+
// exclude first 2 bits from first byte
|
1782 |
+
$first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2));
|
1783 |
+
$binary_code_102bit = $first_byte.substr($binary_code, 2);
|
1784 |
+
// convert binary data to codewords
|
1785 |
+
$codewords = array();
|
1786 |
+
$data = $this->hex_to_dec($binary_code_102bit);
|
1787 |
+
$codewords[0] = bcmod($data, 636) * 2;
|
1788 |
+
$data = bcdiv($data, 636);
|
1789 |
+
for ($i = 1; $i < 9; ++$i) {
|
1790 |
+
$codewords[$i] = bcmod($data, 1365);
|
1791 |
+
$data = bcdiv($data, 1365);
|
1792 |
+
}
|
1793 |
+
$codewords[9] = $data;
|
1794 |
+
if (($fcs >> 10) == 1) {
|
1795 |
+
$codewords[9] += 659;
|
1796 |
+
}
|
1797 |
+
// generate lookup tables
|
1798 |
+
$table2of13 = $this->imb_tables(2, 78);
|
1799 |
+
$table5of13 = $this->imb_tables(5, 1287);
|
1800 |
+
// convert codewords to characters
|
1801 |
+
$characters = array();
|
1802 |
+
$bitmask = 512;
|
1803 |
+
foreach($codewords as $k => $val) {
|
1804 |
+
if ($val <= 1286) {
|
1805 |
+
$chrcode = $table5of13[$val];
|
1806 |
+
} else {
|
1807 |
+
$chrcode = $table2of13[($val - 1287)];
|
1808 |
+
}
|
1809 |
+
if (($fcs & $bitmask) > 0) {
|
1810 |
+
// bitwise invert
|
1811 |
+
$chrcode = ((~$chrcode) & 8191);
|
1812 |
+
}
|
1813 |
+
$characters[] = $chrcode;
|
1814 |
+
$bitmask /= 2;
|
1815 |
+
}
|
1816 |
+
$characters = array_reverse($characters);
|
1817 |
+
// build bars
|
1818 |
+
$k = 0;
|
1819 |
+
$bararray = array('code' => $code, 'maxw' => 0, 'maxh' => $this->daft['F'], 'bcode' => array());
|
1820 |
+
for ($i = 0; $i < 65; ++$i) {
|
1821 |
+
$asc = (($characters[$asc_chr[$i]] & pow(2, $asc_pos[$i])) > 0);
|
1822 |
+
$dsc = (($characters[$dsc_chr[$i]] & pow(2, $dsc_pos[$i])) > 0);
|
1823 |
+
if ($asc AND $dsc) {
|
1824 |
+
// full bar (F)
|
1825 |
+
$p = 0;
|
1826 |
+
$h = $this->daft['F'];
|
1827 |
+
} elseif ($asc) {
|
1828 |
+
// ascender (A)
|
1829 |
+
$p = 0;
|
1830 |
+
$h = $this->daft['A'];
|
1831 |
+
} elseif ($dsc) {
|
1832 |
+
// descender (D)
|
1833 |
+
$p = $this->daft['F'] - $this->daft['D'];
|
1834 |
+
$h = $this->daft['D'];
|
1835 |
+
} else {
|
1836 |
+
// tracker (T)
|
1837 |
+
$p = ($this->daft['F'] - $this->daft['T'])/2;
|
1838 |
+
$h = $this->daft['T'];
|
1839 |
+
}
|
1840 |
+
$bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
|
1841 |
+
// Gap
|
1842 |
+
$bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth , 'h' => 1, 'p' => 0);
|
1843 |
+
$bararray['maxw'] += (1 + $this->gapwidth );
|
1844 |
+
}
|
1845 |
+
unset($bararray['bcode'][($k - 1)]);
|
1846 |
+
$bararray['maxw'] -= $this->gapwidth ;
|
1847 |
+
return $bararray;
|
1848 |
+
}
|
1849 |
+
|
1850 |
+
/**
|
1851 |
+
* Convert large integer number to hexadecimal representation.
|
1852 |
+
* (requires PHP bcmath extension)
|
1853 |
+
*/
|
1854 |
+
public function dec_to_hex($number) {
|
1855 |
+
$i = 0;
|
1856 |
+
$hex = array();
|
1857 |
+
if($number == 0) {
|
1858 |
+
return '00';
|
1859 |
+
}
|
1860 |
+
while($number > 0) {
|
1861 |
+
if($number == 0) {
|
1862 |
+
array_push($hex, '0');
|
1863 |
+
} else {
|
1864 |
+
array_push($hex, strtoupper(dechex(bcmod($number, '16'))));
|
1865 |
+
$number = bcdiv($number, '16', 0);
|
1866 |
+
}
|
1867 |
+
}
|
1868 |
+
$hex = array_reverse($hex);
|
1869 |
+
return implode($hex);
|
1870 |
+
}
|
1871 |
+
|
1872 |
+
/**
|
1873 |
+
* Convert large hexadecimal number to decimal representation (string).
|
1874 |
+
* (requires PHP bcmath extension)
|
1875 |
+
*/
|
1876 |
+
public function hex_to_dec($hex) {
|
1877 |
+
$dec = 0;
|
1878 |
+
$bitval = 1;
|
1879 |
+
$len = strlen($hex);
|
1880 |
+
for($pos = ($len - 1); $pos >= 0; --$pos) {
|
1881 |
+
$dec = bcadd($dec, bcmul(hexdec($hex[$pos]), $bitval));
|
1882 |
+
$bitval = bcmul($bitval, 16);
|
1883 |
+
}
|
1884 |
+
return $dec;
|
1885 |
+
}
|
1886 |
+
|
1887 |
+
/**
|
1888 |
+
* Intelligent Mail Barcode calculation of Frame Check Sequence
|
1889 |
+
*/
|
1890 |
+
protected function imb_crc11fcs($code_arr) {
|
1891 |
+
$genpoly = 0x0F35; // generator polynomial
|
1892 |
+
$fcs = 0x07FF; // Frame Check Sequence
|
1893 |
+
// do most significant byte skipping the 2 most significant bits
|
1894 |
+
$data = hexdec($code_arr[0]) << 5;
|
1895 |
+
for ($bit = 2; $bit < 8; ++$bit) {
|
1896 |
+
if (($fcs ^ $data) & 0x400) {
|
1897 |
+
$fcs = ($fcs << 1) ^ $genpoly;
|
1898 |
+
} else {
|
1899 |
+
$fcs = ($fcs << 1);
|
1900 |
+
}
|
1901 |
+
$fcs &= 0x7FF;
|
1902 |
+
$data <<= 1;
|
1903 |
+
}
|
1904 |
+
// do rest of bytes
|
1905 |
+
for ($byte = 1; $byte < 13; ++$byte) {
|
1906 |
+
$data = hexdec($code_arr[$byte]) << 3;
|
1907 |
+
for ($bit = 0; $bit < 8; ++$bit) {
|
1908 |
+
if (($fcs ^ $data) & 0x400) {
|
1909 |
+
$fcs = ($fcs << 1) ^ $genpoly;
|
1910 |
+
} else {
|
1911 |
+
$fcs = ($fcs << 1);
|
1912 |
+
}
|
1913 |
+
$fcs &= 0x7FF;
|
1914 |
+
$data <<= 1;
|
1915 |
+
}
|
1916 |
+
}
|
1917 |
+
return $fcs;
|
1918 |
+
}
|
1919 |
+
|
1920 |
+
/**
|
1921 |
+
* Reverse unsigned short value
|
1922 |
+
*/
|
1923 |
+
protected function imb_reverse_us($num) {
|
1924 |
+
$rev = 0;
|
1925 |
+
for ($i = 0; $i < 16; ++$i) {
|
1926 |
+
$rev <<= 1;
|
1927 |
+
$rev |= ($num & 1);
|
1928 |
+
$num >>= 1;
|
1929 |
+
}
|
1930 |
+
return $rev;
|
1931 |
+
}
|
1932 |
+
|
1933 |
+
/**
|
1934 |
+
* generate Nof13 tables used for Intelligent Mail Barcode
|
1935 |
+
*/
|
1936 |
+
protected function imb_tables($n, $size) {
|
1937 |
+
$table = array();
|
1938 |
+
$lli = 0; // LUT lower index
|
1939 |
+
$lui = $size - 1; // LUT upper index
|
1940 |
+
for ($count = 0; $count < 8192; ++$count) {
|
1941 |
+
$bit_count = 0;
|
1942 |
+
for ($bit_index = 0; $bit_index < 13; ++$bit_index) {
|
1943 |
+
$bit_count += intval(($count & (1 << $bit_index)) != 0);
|
1944 |
+
}
|
1945 |
+
// if we don't have the right number of bits on, go on to the next value
|
1946 |
+
if ($bit_count == $n) {
|
1947 |
+
$reverse = ($this->imb_reverse_us($count) >> 3);
|
1948 |
+
// if the reverse is less than count, we have already visited this pair before
|
1949 |
+
if ($reverse >= $count) {
|
1950 |
+
// If count is symmetric, place it at the first free slot from the end of the list.
|
1951 |
+
// Otherwise, place it at the first free slot from the beginning of the list AND place $reverse ath the next free slot from the beginning of the list
|
1952 |
+
if ($reverse == $count) {
|
1953 |
+
$table[$lui] = $count;
|
1954 |
+
--$lui;
|
1955 |
+
} else {
|
1956 |
+
$table[$lli] = $count;
|
1957 |
+
++$lli;
|
1958 |
+
$table[$lli] = $reverse;
|
1959 |
+
++$lli;
|
1960 |
+
}
|
1961 |
+
}
|
1962 |
+
}
|
1963 |
+
}
|
1964 |
+
return $table;
|
1965 |
+
}
|
1966 |
+
|
1967 |
+
} // end of class
|
1968 |
+
|
1969 |
+
//============================================================+
|
1970 |
+
// END OF FILE
|
1971 |
+
//============================================================+
|
1972 |
+
?>
|
lib/mpdf/classes/bmp.php
ADDED
@@ -0,0 +1,248 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class bmp {
|
4 |
+
|
5 |
+
var $mpdf = null;
|
6 |
+
|
7 |
+
function bmp(&$mpdf) {
|
8 |
+
$this->mpdf = $mpdf;
|
9 |
+
}
|
10 |
+
|
11 |
+
|
12 |
+
function _getBMPimage($data, $file) {
|
13 |
+
$info = array();
|
14 |
+
// Adapted from script by Valentin Schmidt
|
15 |
+
// http://staff.dasdeck.de/valentin/fpdf/fpdf_bmp/
|
16 |
+
$bfOffBits=$this->_fourbytes2int_le(substr($data,10,4));
|
17 |
+
$width=$this->_fourbytes2int_le(substr($data,18,4));
|
18 |
+
$height=$this->_fourbytes2int_le(substr($data,22,4));
|
19 |
+
$flip = ($height<0);
|
20 |
+
if ($flip) $height =-$height;
|
21 |
+
$biBitCount=$this->_twobytes2int_le(substr($data,28,2));
|
22 |
+
$biCompression=$this->_fourbytes2int_le(substr($data,30,4));
|
23 |
+
$info = array('w'=>$width, 'h'=>$height);
|
24 |
+
if ($biBitCount<16){
|
25 |
+
$info['cs'] = 'Indexed';
|
26 |
+
$info['bpc'] = $biBitCount;
|
27 |
+
$palStr = substr($data,54,($bfOffBits-54));
|
28 |
+
$pal = '';
|
29 |
+
$cnt = strlen($palStr)/4;
|
30 |
+
for ($i=0;$i<$cnt;$i++){
|
31 |
+
$n = 4*$i;
|
32 |
+
$pal .= $palStr[$n+2].$palStr[$n+1].$palStr[$n];
|
33 |
+
}
|
34 |
+
$info['pal'] = $pal;
|
35 |
+
}
|
36 |
+
else{
|
37 |
+
$info['cs'] = 'DeviceRGB';
|
38 |
+
$info['bpc'] = 8;
|
39 |
+
}
|
40 |
+
|
41 |
+
if ($this->mpdf->restrictColorSpace==1 || $this->mpdf->PDFX || $this->mpdf->restrictColorSpace==3) {
|
42 |
+
if (($this->mpdf->PDFA && !$this->mpdf->PDFAauto) || ($this->mpdf->PDFX && !$this->mpdf->PDFXauto)) { $this->mpdf->PDFAXwarnings[] = "Image cannot be converted to suitable colour space for PDFA or PDFX file - ".$file." - (Image replaced by 'no-image'.)"; }
|
43 |
+
return array('error' => "BMP Image cannot be converted to suitable colour space - ".$file." - (Image replaced by 'no-image'.)");
|
44 |
+
}
|
45 |
+
|
46 |
+
$biXPelsPerMeter=$this->_fourbytes2int_le(substr($data,38,4)); // horizontal pixels per meter, usually set to zero
|
47 |
+
//$biYPelsPerMeter=$this->_fourbytes2int_le(substr($data,42,4)); // vertical pixels per meter, usually set to zero
|
48 |
+
$biXPelsPerMeter=round($biXPelsPerMeter/1000 *25.4);
|
49 |
+
//$biYPelsPerMeter=round($biYPelsPerMeter/1000 *25.4);
|
50 |
+
$info['set-dpi'] = $biXPelsPerMeter;
|
51 |
+
|
52 |
+
switch ($biCompression){
|
53 |
+
case 0:
|
54 |
+
$str = substr($data,$bfOffBits);
|
55 |
+
break;
|
56 |
+
case 1: # BI_RLE8
|
57 |
+
$str = $this->rle8_decode(substr($data,$bfOffBits), $width);
|
58 |
+
break;
|
59 |
+
case 2: # BI_RLE4
|
60 |
+
$str = $this->rle4_decode(substr($data,$bfOffBits), $width);
|
61 |
+
break;
|
62 |
+
}
|
63 |
+
$bmpdata = '';
|
64 |
+
$padCnt = (4-ceil(($width/(8/$biBitCount)))%4)%4;
|
65 |
+
switch ($biBitCount){
|
66 |
+
case 1:
|
67 |
+
case 4:
|
68 |
+
case 8:
|
69 |
+
$w = floor($width/(8/$biBitCount)) + ($width%(8/$biBitCount)?1:0);
|
70 |
+
$w_row = $w + $padCnt;
|
71 |
+
if ($flip){
|
72 |
+
for ($y=0;$y<$height;$y++){
|
73 |
+
$y0 = $y*$w_row;
|
74 |
+
for ($x=0;$x<$w;$x++)
|
75 |
+
$bmpdata .= $str[$y0+$x];
|
76 |
+
}
|
77 |
+
}else{
|
78 |
+
for ($y=$height-1;$y>=0;$y--){
|
79 |
+
$y0 = $y*$w_row;
|
80 |
+
for ($x=0;$x<$w;$x++)
|
81 |
+
$bmpdata .= $str[$y0+$x];
|
82 |
+
}
|
83 |
+
}
|
84 |
+
break;
|
85 |
+
|
86 |
+
case 16:
|
87 |
+
$w_row = $width*2 + $padCnt;
|
88 |
+
if ($flip){
|
89 |
+
for ($y=0;$y<$height;$y++){
|
90 |
+
$y0 = $y*$w_row;
|
91 |
+
for ($x=0;$x<$width;$x++){
|
92 |
+
$n = (ord( $str[$y0 + 2*$x + 1])*256 + ord( $str[$y0 + 2*$x]));
|
93 |
+
$b = ($n & 31)<<3; $g = ($n & 992)>>2; $r = ($n & 31744)>>7128;
|
94 |
+
$bmpdata .= chr($r) . chr($g) . chr($b);
|
95 |
+
}
|
96 |
+
}
|
97 |
+
}else{
|
98 |
+
for ($y=$height-1;$y>=0;$y--){
|
99 |
+
$y0 = $y*$w_row;
|
100 |
+
for ($x=0;$x<$width;$x++){
|
101 |
+
$n = (ord( $str[$y0 + 2*$x + 1])*256 + ord( $str[$y0 + 2*$x]));
|
102 |
+
$b = ($n & 31)<<3; $g = ($n & 992)>>2; $r = ($n & 31744)>>7;
|
103 |
+
$bmpdata .= chr($r) . chr($g) . chr($b);
|
104 |
+
}
|
105 |
+
}
|
106 |
+
}
|
107 |
+
break;
|
108 |
+
|
109 |
+
case 24:
|
110 |
+
case 32:
|
111 |
+
$byteCnt = $biBitCount/8;
|
112 |
+
$w_row = $width*$byteCnt + $padCnt;
|
113 |
+
|
114 |
+
if ($flip){
|
115 |
+
for ($y=0;$y<$height;$y++){
|
116 |
+
$y0 = $y*$w_row;
|
117 |
+
for ($x=0;$x<$width;$x++){
|
118 |
+
$i = $y0 + $x*$byteCnt ; # + 1
|
119 |
+
$bmpdata .= $str[$i+2].$str[$i+1].$str[$i];
|
120 |
+
}
|
121 |
+
}
|
122 |
+
}else{
|
123 |
+
for ($y=$height-1;$y>=0;$y--){
|
124 |
+
$y0 = $y*$w_row;
|
125 |
+
for ($x=0;$x<$width;$x++){
|
126 |
+
$i = $y0 + $x*$byteCnt ; # + 1
|
127 |
+
$bmpdata .= $str[$i+2].$str[$i+1].$str[$i];
|
128 |
+
}
|
129 |
+
}
|
130 |
+
}
|
131 |
+
break;
|
132 |
+
|
133 |
+
default:
|
134 |
+
return array('error' => 'Error parsing BMP image - Unsupported image biBitCount');
|
135 |
+
}
|
136 |
+
if ($this->mpdf->compress) {
|
137 |
+
$bmpdata=gzcompress($bmpdata);
|
138 |
+
$info['f']='FlateDecode';
|
139 |
+
}
|
140 |
+
$info['data']=$bmpdata;
|
141 |
+
$info['type']='bmp';
|
142 |
+
return $info;
|
143 |
+
}
|
144 |
+
|
145 |
+
function _fourbytes2int_le($s) {
|
146 |
+
//Read a 4-byte integer from string
|
147 |
+
return (ord($s[3])<<24) + (ord($s[2])<<16) + (ord($s[1])<<8) + ord($s[0]);
|
148 |
+
}
|
149 |
+
|
150 |
+
function _twobytes2int_le($s) {
|
151 |
+
//Read a 2-byte integer from string
|
152 |
+
return (ord(substr($s, 1, 1))<<8) + ord(substr($s, 0, 1));
|
153 |
+
}
|
154 |
+
|
155 |
+
|
156 |
+
# Decoder for RLE8 compression in windows bitmaps
|
157 |
+
# see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
|
158 |
+
function rle8_decode ($str, $width){
|
159 |
+
$lineWidth = $width + (3 - ($width-1) % 4);
|
160 |
+
$out = '';
|
161 |
+
$cnt = strlen($str);
|
162 |
+
for ($i=0;$i<$cnt;$i++){
|
163 |
+
$o = ord($str[$i]);
|
164 |
+
switch ($o){
|
165 |
+
case 0: # ESCAPE
|
166 |
+
$i++;
|
167 |
+
switch (ord($str[$i])){
|
168 |
+
case 0: # NEW LINE
|
169 |
+
$padCnt = $lineWidth - strlen($out)%$lineWidth;
|
170 |
+
if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
|
171 |
+
break;
|
172 |
+
case 1: # END OF FILE
|
173 |
+
$padCnt = $lineWidth - strlen($out)%$lineWidth;
|
174 |
+
if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
|
175 |
+
break 3;
|
176 |
+
case 2: # DELTA
|
177 |
+
$i += 2;
|
178 |
+
break;
|
179 |
+
default: # ABSOLUTE MODE
|
180 |
+
$num = ord($str[$i]);
|
181 |
+
for ($j=0;$j<$num;$j++)
|
182 |
+
$out .= $str[++$i];
|
183 |
+
if ($num % 2) $i++;
|
184 |
+
}
|
185 |
+
break;
|
186 |
+
default:
|
187 |
+
$out .= str_repeat($str[++$i], $o);
|
188 |
+
}
|
189 |
+
}
|
190 |
+
return $out;
|
191 |
+
}
|
192 |
+
|
193 |
+
# Decoder for RLE4 compression in windows bitmaps
|
194 |
+
# see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
|
195 |
+
function rle4_decode ($str, $width){
|
196 |
+
$w = floor($width/2) + ($width % 2);
|
197 |
+
$lineWidth = $w + (3 - ( ($width-1) / 2) % 4);
|
198 |
+
$pixels = array();
|
199 |
+
$cnt = strlen($str);
|
200 |
+
for ($i=0;$i<$cnt;$i++){
|
201 |
+
$o = ord($str[$i]);
|
202 |
+
switch ($o){
|
203 |
+
case 0: # ESCAPE
|
204 |
+
$i++;
|
205 |
+
switch (ord($str[$i])){
|
206 |
+
case 0: # NEW LINE
|
207 |
+
while (count($pixels)%$lineWidth!=0)
|
208 |
+
$pixels[]=0;
|
209 |
+
break;
|
210 |
+
case 1: # END OF FILE
|
211 |
+
while (count($pixels)%$lineWidth!=0)
|
212 |
+
$pixels[]=0;
|
213 |
+
break 3;
|
214 |
+
case 2: # DELTA
|
215 |
+
$i += 2;
|
216 |
+
break;
|
217 |
+
default: # ABSOLUTE MODE
|
218 |
+
$num = ord($str[$i]);
|
219 |
+
for ($j=0;$j<$num;$j++){
|
220 |
+
if ($j%2==0){
|
221 |
+
$c = ord($str[++$i]);
|
222 |
+
$pixels[] = ($c & 240)>>4;
|
223 |
+
} else
|
224 |
+
$pixels[] = $c & 15;
|
225 |
+
}
|
226 |
+
if ($num % 2) $i++;
|
227 |
+
}
|
228 |
+
break;
|
229 |
+
default:
|
230 |
+
$c = ord($str[++$i]);
|
231 |
+
for ($j=0;$j<$o;$j++)
|
232 |
+
$pixels[] = ($j%2==0 ? ($c & 240)>>4 : $c & 15);
|
233 |
+
}
|
234 |
+
}
|
235 |
+
|
236 |
+
$out = '';
|
237 |
+
if (count($pixels)%2) $pixels[]=0;
|
238 |
+
$cnt = count($pixels)/2;
|
239 |
+
for ($i=0;$i<$cnt;$i++)
|
240 |
+
$out .= chr(16*$pixels[2*$i] + $pixels[2*$i+1]);
|
241 |
+
return $out;
|
242 |
+
}
|
243 |
+
|
244 |
+
|
245 |
+
|
246 |
+
}
|
247 |
+
|
248 |
+
?>
|
{mpdf → lib/mpdf}/classes/cssmgr.php
RENAMED
@@ -5,73 +5,19 @@ class cssmgr {
|
|
5 |
Â
var $mpdf = null;
|
6 |
Â
|
7 |
Â
var $tablecascadeCSS;
|
8 |
-
var $listcascadeCSS;
|
9 |
Â
var $cascadeCSS;
|
10 |
Â
var $CSS;
|
11 |
Â
var $tbCSSlvl;
|
12 |
-
var $listCSSlvl;
|
13 |
Â
|
14 |
Â
|
15 |
Â
function cssmgr(&$mpdf) {
|
16 |
Â
$this->mpdf = $mpdf;
|
17 |
Â
$this->tablecascadeCSS = array();
|
18 |
-
$this->listcascadeCSS = array();
|
19 |
Â
$this->CSS=array();
|
20 |
Â
$this->cascadeCSS = array();
|
21 |
Â
$this->tbCSSlvl = 0;
|
22 |
-
$this->listCSSlvl = 0;
|
23 |
Â
}
|
24 |
Â
|
25 |
-
|
26 |
-
function ReadDefaultCSS($CSSstr) {
|
27 |
-
$CSS = array();
|
28 |
-
$CSSstr = preg_replace('|/\*.*?\*/|s',' ',$CSSstr);
|
29 |
-
$CSSstr = preg_replace('/[\s\n\r\t\f]/s',' ',$CSSstr);
|
30 |
-
$CSSstr = preg_replace('/(<\!\-\-|\-\->)/s',' ',$CSSstr);
|
31 |
-
if ($CSSstr ) {
|
32 |
-
preg_match_all('/(.*?)\{(.*?)\}/',$CSSstr,$styles);
|
33 |
-
for($i=0; $i < count($styles[1]) ; $i++) {
|
34 |
-
$stylestr= trim($styles[2][$i]);
|
35 |
-
$stylearr = explode(';',$stylestr);
|
36 |
-
foreach($stylearr AS $sta) {
|
37 |
-
if (trim($sta)) {
|
38 |
-
// Changed to allow style="background: url('http://www.bpm1.com/bg.jpg')"
|
39 |
-
list($property,$value) = explode(':',$sta,2);
|
40 |
-
$property = trim($property);
|
41 |
-
$value = preg_replace('/\s*!important/i','',$value);
|
42 |
-
$value = trim($value);
|
43 |
-
if ($property && ($value || $value==='0')) {
|
44 |
-
$classproperties[strtoupper($property)] = $value;
|
45 |
-
}
|
46 |
-
}
|
47 |
-
}
|
48 |
-
$classproperties = $this->fixCSS($classproperties);
|
49 |
-
$tagstr = strtoupper(trim($styles[1][$i]));
|
50 |
-
$tagarr = explode(',',$tagstr);
|
51 |
-
foreach($tagarr AS $tg) {
|
52 |
-
$tags = preg_split('/\s+/',trim($tg));
|
53 |
-
$level = count($tags);
|
54 |
-
if ($level == 1) { // e.g. p or .class or #id or p.class or p#id
|
55 |
-
$t = trim($tags[0]);
|
56 |
-
if ($t) {
|
57 |
-
$tag = '';
|
58 |
-
if (preg_match('/^('.$this->mpdf->allowedCSStags.')$/',$t)) { $tag= $t; }
|
59 |
-
if ($this->CSS[$tag] && $tag) { $CSS[$tag] = $this->array_merge_recursive_unique($CSS[$tag], $classproperties); }
|
60 |
-
else if ($tag) { $CSS[$tag] = $classproperties; }
|
61 |
-
}
|
62 |
-
}
|
63 |
-
}
|
64 |
-
$properties = array();
|
65 |
-
$values = array();
|
66 |
-
$classproperties = array();
|
67 |
-
}
|
68 |
-
|
69 |
-
} // end of if
|
70 |
-
return $CSS;
|
71 |
-
}
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
Â
function ReadCSS($html) {
|
76 |
Â
preg_match_all('/<style[^>]*media=["\']([^"\'>]*)["\'].*?<\/style>/is',$html,$m);
|
77 |
Â
for($i=0; $i<count($m[0]); $i++) {
|
@@ -118,7 +64,6 @@ function ReadCSS($html) {
|
|
118 |
Â
$match += $x;
|
119 |
Â
$CSSext = $cxt[1];
|
120 |
Â
}
|
121 |
-
|
122 |
Â
$regexp = '/<link[^>]*href=["\']([^>"\']*)["\'][^>]*?rel=["\']stylesheet["\'].*?>/si';
|
123 |
Â
$x = preg_match_all($regexp,$html,$cxt);
|
124 |
Â
if ($x) {
|
@@ -151,6 +96,9 @@ function ReadCSS($html) {
|
|
151 |
Â
|
152 |
Â
while($match){
|
153 |
Â
$path = $CSSext[$ind];
|
Â
|
|
Â
|
|
Â
|
|
154 |
Â
$this->mpdf->GetFullPath($path);
|
155 |
Â
$CSSextblock = $this->mpdf->_get_file($path);
|
156 |
Â
if ($CSSextblock) {
|
@@ -221,9 +169,8 @@ function ReadCSS($html) {
|
|
221 |
Â
}
|
222 |
Â
}
|
223 |
Â
|
224 |
-
// mPDF 5.5.13
|
225 |
Â
// Replace any background: url(data:image... with temporary image file reference
|
226 |
-
preg_match_all("/(url\(data:image\/(jpeg|gif|png);base64,(
|
227 |
Â
if (count($idata[0])) {
|
228 |
Â
for($i=0;$i<count($idata[0]);$i++) {
|
229 |
Â
$file = _MPDF_TEMP_PATH.'_tempCSSidata'.RAND(1,10000).'_'.$i.'.'.$idata[2][$i];
|
@@ -235,7 +182,34 @@ function ReadCSS($html) {
|
|
235 |
Â
}
|
236 |
Â
|
237 |
Â
$CSSstr = preg_replace('/(<\!\-\-|\-\->)/s',' ',$CSSstr);
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
238 |
Â
if ($CSSstr ) {
|
Â
|
|
239 |
Â
preg_match_all('/(.*?)\{(.*?)\}/',$CSSstr,$styles);
|
240 |
Â
for($i=0; $i < count($styles[1]) ; $i++) {
|
241 |
Â
// SET array e.g. $classproperties['COLOR'] = '#ffffff';
|
@@ -244,7 +218,11 @@ function ReadCSS($html) {
|
|
244 |
Â
foreach($stylearr AS $sta) {
|
245 |
Â
if (trim($sta)) {
|
246 |
Â
// Changed to allow style="background: url('http://www.bpm1.com/bg.jpg')"
|
247 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
248 |
Â
$property = trim($property);
|
249 |
Â
$value = preg_replace('/\s*!important/i','',$value);
|
250 |
Â
$value = trim($value);
|
@@ -262,6 +240,10 @@ function ReadCSS($html) {
|
|
262 |
Â
$tagarr = explode(',',$tagstr);
|
263 |
Â
$pageselectors = false; // used to turn on $this->mpdf->mirrorMargins
|
264 |
Â
foreach($tagarr AS $tg) {
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
265 |
Â
$tags = preg_split('/\s+/',trim($tg));
|
266 |
Â
$level = count($tags);
|
267 |
Â
$t = '';
|
@@ -292,9 +274,13 @@ function ReadCSS($html) {
|
|
292 |
Â
$tag = '';
|
293 |
Â
if (preg_match('/^[.](.*)$/',$t,$m)) { $tag = 'CLASS>>'.$m[1]; }
|
294 |
Â
else if (preg_match('/^[#](.*)$/',$t,$m)) { $tag = 'ID>>'.$m[1]; }
|
Â
|
|
Â
|
|
295 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[.](.*)$/',$t,$m)) { $tag = $m[1].'>>CLASS>>'.$m[2]; }
|
296 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')\s*:NTH-CHILD\((.*)\)$/',$t,$m)) { $tag = $m[1].'>>SELECTORNTHCHILD>>'.$m[2]; }
|
297 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[#](.*)$/',$t,$m)) { $tag = $m[1].'>>ID>>'.$m[2]; }
|
Â
|
|
Â
|
|
298 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')$/',$t)) { $tag= $t; }
|
299 |
Â
if (isset($this->CSS[$tag]) && $tag) { $this->CSS[$tag] = $this->array_merge_recursive_unique($this->CSS[$tag], $classproperties); }
|
300 |
Â
else if ($tag) { $this->CSS[$tag] = $classproperties; }
|
@@ -309,9 +295,13 @@ function ReadCSS($html) {
|
|
309 |
Â
$tag = '';
|
310 |
Â
if (preg_match('/^[.](.*)$/',$t,$m)) { $tag = 'CLASS>>'.$m[1]; }
|
311 |
Â
else if (preg_match('/^[#](.*)$/',$t,$m)) { $tag = 'ID>>'.$m[1]; }
|
Â
|
|
Â
|
|
312 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[.](.*)$/',$t,$m)) { $tag = $m[1].'>>CLASS>>'.$m[2]; }
|
313 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')\s*:NTH-CHILD\((.*)\)$/',$t,$m)) { $tag = $m[1].'>>SELECTORNTHCHILD>>'.$m[2]; }
|
314 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[#](.*)$/',$t,$m)) { $tag = $m[1].'>>ID>>'.$m[2]; }
|
Â
|
|
Â
|
|
315 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')$/',$t)) { $tag= $t; }
|
316 |
Â
|
317 |
Â
if ($tag) $tmp[] = $tag;
|
@@ -344,6 +334,29 @@ function ReadCSS($html) {
|
|
344 |
Â
|
345 |
Â
|
346 |
Â
function readInlineCSS($html) {
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
347 |
Â
//Fix incomplete CSS code
|
348 |
Â
$size = strlen($html)-1;
|
349 |
Â
if (substr($html,$size,1) != ';') $html .= ';';
|
@@ -359,6 +372,7 @@ function readInlineCSS($html) {
|
|
359 |
Â
if ((strtoupper($properties[$i])=='BACKGROUND-IMAGE' || strtoupper($properties[$i])=='BACKGROUND') && preg_match('/-webkit-gradient/i',$values[$i])) {
|
360 |
Â
continue;
|
361 |
Â
}
|
Â
|
|
362 |
Â
$classproperties[strtoupper($properties[$i])] = trim($values[$i]);
|
363 |
Â
}
|
364 |
Â
return $this->fixCSS($classproperties);
|
@@ -455,7 +469,7 @@ function fixCSS($prop) {
|
|
455 |
Â
if (preg_match('/small-caps/i',$s)) { $newprop['TEXT-TRANSFORM'] = 'uppercase'; }
|
456 |
Â
}
|
457 |
Â
}
|
458 |
-
if ($k == 'FONT-FAMILY') {
|
459 |
Â
$aux_fontlist = explode(",",$v);
|
460 |
Â
$found = 0;
|
461 |
Â
foreach($aux_fontlist AS $f) {
|
@@ -487,6 +501,29 @@ function fixCSS($prop) {
|
|
487 |
Â
}
|
488 |
Â
}
|
489 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
490 |
Â
else if ($k == 'MARGIN') {
|
491 |
Â
$tmp = $this->expand24($v);
|
492 |
Â
$newprop['MARGIN-TOP'] = $tmp['T'];
|
@@ -536,24 +573,30 @@ function fixCSS($prop) {
|
|
536 |
Â
}
|
537 |
Â
else if ($k == 'BORDER-STYLE') {
|
538 |
Â
$e = $this->expand24($v);
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
Â
|
|
Â
|
|
543 |
Â
}
|
544 |
Â
else if ($k == 'BORDER-WIDTH') {
|
545 |
Â
$e = $this->expand24($v);
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
Â
|
|
Â
|
|
550 |
Â
}
|
551 |
Â
else if ($k == 'BORDER-COLOR') {
|
552 |
Â
$e = $this->expand24($v);
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
Â
|
|
Â
|
|
557 |
Â
}
|
558 |
Â
|
559 |
Â
else if ($k == 'BORDER-SPACING') {
|
@@ -682,7 +725,6 @@ function fixCSS($prop) {
|
|
682 |
Â
$newprop['IMAGE-ORIENTATION'] = $angle;
|
683 |
Â
}
|
684 |
Â
}
|
685 |
-
// mPDF 5.6.13
|
686 |
Â
else if ($k == 'TEXT-ALIGN') {
|
687 |
Â
if (preg_match('/["\'](.){1}["\']/i',$v,$m)) {
|
688 |
Â
$d = array_search($m[1],$this->mpdf->decimal_align);
|
@@ -699,6 +741,25 @@ function fixCSS($prop) {
|
|
699 |
Â
}
|
700 |
Â
else { $newprop[$k] = $v; }
|
701 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
702 |
Â
|
703 |
Â
else {
|
704 |
Â
$newprop[$k] = $v;
|
@@ -754,18 +815,18 @@ function setCSStextshadow($v) {
|
|
754 |
Â
foreach ($ss AS $s) {
|
755 |
Â
$new = array('blur'=>0);
|
756 |
Â
$p = explode(' ',trim($s));
|
757 |
-
if (isset($p[0])) { $new['x'] = $this->mpdf->ConvertSize(trim($p[0]),$this->mpdf->
|
758 |
-
if (isset($p[1])) { $new['y'] = $this->mpdf->ConvertSize(trim($p[1]),$this->mpdf->
|
759 |
Â
if (isset($p[2])) {
|
760 |
Â
if (preg_match('/^\s*[\.\-0-9]/',$p[2])) {
|
761 |
-
$new['blur'] = $this->mpdf->ConvertSize(trim($p[2]),$this->mpdf->blk[$this->mpdf->blklvl
|
762 |
Â
}
|
763 |
Â
else { $new['col'] = $this->mpdf->ConvertColor(preg_replace('/\*/',',',$p[2])); }
|
764 |
Â
if (isset($p[3])) {
|
765 |
Â
$new['col'] = $this->mpdf->ConvertColor(preg_replace('/\*/',',',$p[3]));
|
766 |
Â
}
|
767 |
Â
}
|
768 |
-
if (!$new['col']) { $new['col'] = $this->mpdf->ConvertColor('#888888'); }
|
769 |
Â
if (isset($new['y'])) { array_unshift($sh, $new); }
|
770 |
Â
}
|
771 |
Â
return $sh;
|
@@ -962,11 +1023,11 @@ function array_merge_recursive_unique($array1, $array2) {
|
|
962 |
Â
|
963 |
Â
|
964 |
Â
|
965 |
-
function _mergeFullCSS($p, &$t, $tag, $classes, $id) {
|
966 |
-
$this->_mergeCSS($p[$tag], $t);
|
967 |
Â
// STYLESHEET CLASS e.g. .smallone{} .redletter{}
|
968 |
Â
foreach($classes AS $class) {
|
969 |
-
|
970 |
Â
}
|
971 |
Â
// STYLESHEET nth-child SELECTOR e.g. tr:nth-child(odd) td:nth-child(2n+1)
|
972 |
Â
if ($tag=='TR' && isset($p) && $p) {
|
@@ -979,17 +1040,13 @@ function _mergeFullCSS($p, &$t, $tag, $classes, $id) {
|
|
979 |
Â
$tfnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) : 0);
|
980 |
Â
if ($this->mpdf->tabletfoot) { $row -= $thnr; }
|
981 |
Â
else if (!$this->mpdf->tablethead) { $row -= ($thnr + $tfnr); }
|
982 |
-
if (
|
983 |
-
|
984 |
-
else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
|
985 |
-
if ((($row + 1) % $a[1]) == $a[2]) { $select = true; }
|
986 |
Â
}
|
987 |
Â
}
|
988 |
Â
else if ($tag=='TD' || $tag=='TH') {
|
989 |
-
if (
|
990 |
-
|
991 |
-
else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
|
992 |
-
if ((($this->mpdf->col + 1) % $a[1]) == $a[2]) { $select = true; }
|
993 |
Â
}
|
994 |
Â
}
|
995 |
Â
if ($select) {
|
@@ -998,16 +1055,25 @@ function _mergeFullCSS($p, &$t, $tag, $classes, $id) {
|
|
998 |
Â
}
|
999 |
Â
}
|
1000 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1001 |
Â
// STYLESHEET CLASS e.g. #smallone{} #redletter{}
|
1002 |
-
if (isset($id) && $id) {
|
1003 |
Â
$this->_mergeCSS($p['ID>>'.$id], $t);
|
1004 |
Â
}
|
Â
|
|
1005 |
Â
// STYLESHEET CLASS e.g. .smallone{} .redletter{}
|
1006 |
Â
foreach($classes AS $class) {
|
1007 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1008 |
Â
}
|
1009 |
Â
// STYLESHEET CLASS e.g. #smallone{} #redletter{}
|
1010 |
-
if (isset($id)) {
|
1011 |
Â
$this->_mergeCSS($p[$tag.'>>ID>>'.$id], $t);
|
1012 |
Â
}
|
1013 |
Â
}
|
@@ -1068,6 +1134,15 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1068 |
Â
$classes = preg_split('/\s+/',$attr['CLASS']);
|
1069 |
Â
}
|
1070 |
Â
if (!isset($attr['ID'])) { $attr['ID']=''; }
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1071 |
Â
//===============================================
|
1072 |
Â
/*-- TABLES --*/
|
1073 |
Â
// Set Inherited properties
|
@@ -1091,47 +1166,16 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1091 |
Â
$this->tablecascadeCSS[$this->tbCSSlvl][$k] = $v;
|
1092 |
Â
}
|
1093 |
Â
}
|
1094 |
-
$this->_mergeFullCSS($this->cascadeCSS, $this->tablecascadeCSS[$this->tbCSSlvl], $tag, $classes, $attr['ID']);
|
1095 |
Â
//===============================================
|
1096 |
Â
// Cascading forward CSS e.g. "table.topic td" for this table in $this->tablecascadeCSS
|
1097 |
Â
//===============================================
|
1098 |
Â
// STYLESHEET TAG e.g. table
|
1099 |
-
$this->_mergeFullCSS($this->tablecascadeCSS[$this->tbCSSlvl-1], $this->tablecascadeCSS[$this->tbCSSlvl], $tag, $classes, $attr['ID']);
|
1100 |
Â
//===============================================
|
1101 |
Â
}
|
1102 |
Â
/*-- END TABLES --*/
|
1103 |
Â
//===============================================
|
1104 |
-
/*-- LISTS --*/
|
1105 |
-
// Set Inherited properties
|
1106 |
-
if ($inherit == 'TOPLIST') { // $tag = UL,OL
|
1107 |
-
//===============================================
|
1108 |
-
// Save Cascading CSS e.g. "div.topic p" at this block level
|
1109 |
-
if (isset($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'])) {
|
1110 |
-
$this->listcascadeCSS[0] = $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'];
|
1111 |
-
}
|
1112 |
-
else {
|
1113 |
-
$this->listcascadeCSS[0] = $this->cascadeCSS;
|
1114 |
-
}
|
1115 |
-
}
|
1116 |
-
//===============================================
|
1117 |
-
// Set Inherited properties
|
1118 |
-
if ($inherit == 'TOPLIST' || $inherit == 'LIST') {
|
1119 |
-
//Cascade everything from last level that is not an actual property, or defined by current tag/attributes
|
1120 |
-
if (isset($this->listcascadeCSS[$this->listCSSlvl-1]) && is_array($this->listcascadeCSS[$this->listCSSlvl-1])) {
|
1121 |
-
foreach($this->listcascadeCSS[$this->listCSSlvl-1] AS $k=>$v) {
|
1122 |
-
$this->listcascadeCSS[$this->listCSSlvl][$k] = $v;
|
1123 |
-
}
|
1124 |
-
}
|
1125 |
-
$this->_mergeFullCSS($this->cascadeCSS, $this->listcascadeCSS[$this->listCSSlvl], $tag, $classes, $attr['ID']);
|
1126 |
-
//===============================================
|
1127 |
-
// Cascading forward CSS e.g. "table.topic td" for this list in $this->listcascadeCSS
|
1128 |
-
//===============================================
|
1129 |
-
// STYLESHEET TAG e.g. table
|
1130 |
-
$this->_mergeFullCSS($this->listcascadeCSS[$this->listCSSlvl-1], $this->listcascadeCSS[$this->listCSSlvl], $tag, $classes, $attr['ID']);
|
1131 |
-
//===============================================
|
1132 |
-
}
|
1133 |
-
/*-- END LISTS --*/
|
1134 |
-
//===============================================
|
1135 |
Â
// Set Inherited properties
|
1136 |
Â
if ($inherit == 'BLOCK') {
|
1137 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS']) && is_array($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'])) {
|
@@ -1143,17 +1187,28 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1143 |
Â
|
1144 |
Â
//===============================================
|
1145 |
Â
// Save Cascading CSS e.g. "div.topic p" at this block level
|
1146 |
-
$this->_mergeFullCSS($this->cascadeCSS, $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'], $tag, $classes, $attr['ID']);
|
1147 |
Â
//===============================================
|
1148 |
Â
// Cascading forward CSS
|
1149 |
Â
//===============================================
|
1150 |
-
|
Â
|
|
Â
|
|
1151 |
Â
//===============================================
|
1152 |
-
// Block properties
|
1153 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['margin_collapse']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['margin_collapse']) { $p['MARGIN-COLLAPSE'] = 'COLLAPSE'; } // custom tag, but follows CSS principle that border-collapse is inherited
|
1154 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['line_height']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['line_height']) { $p['LINE-HEIGHT'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['line_height']; }
|
Â
|
|
Â
|
|
Â
|
|
1155 |
Â
|
1156 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['direction']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['direction']) { $p['DIRECTION'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['direction']; }
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1157 |
Â
|
1158 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['align']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['align']) {
|
1159 |
Â
if ($this->mpdf->blk[$this->mpdf->blklvl-1]['align'] == 'L') { $p['TEXT-ALIGN'] = 'left'; }
|
@@ -1171,64 +1226,62 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1171 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent']) && ($this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent'] || $this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent']===0)) { $p['TEXT-INDENT'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent']; }
|
1172 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['InlineProperties'])) {
|
1173 |
Â
$biilp = $this->mpdf->blk[$this->mpdf->blklvl-1]['InlineProperties'];
|
Â
|
|
1174 |
Â
}
|
1175 |
Â
else { $biilp = null; }
|
1176 |
-
if (isset($biilp[ 'family' ]) && $biilp[ 'family' ]) { $p['FONT-FAMILY'] = $biilp[ 'family' ]; }
|
1177 |
-
if (isset($biilp[ 'I' ]) && $biilp[ 'I' ]) { $p['FONT-STYLE'] = 'italic'; }
|
1178 |
-
if (isset($biilp[ 'sizePt' ]) && $biilp[ 'sizePt' ]) { $p['FONT-SIZE'] = $biilp[ 'sizePt' ] . 'pt'; }
|
1179 |
-
if (isset($biilp[ 'B' ]) && $biilp[ 'B' ]) { $p['FONT-WEIGHT'] = 'bold'; }
|
1180 |
-
if (isset($biilp[ 'colorarray' ]) && $biilp[ 'colorarray' ]) {
|
1181 |
-
$cor = $biilp[ 'colorarray' ];
|
1182 |
-
$p['COLOR'] = $this->mpdf->_colAtoString($cor);
|
1183 |
-
}
|
1184 |
-
if (isset($biilp[ 'fontkerning' ])) {
|
1185 |
-
if ($biilp[ 'fontkerning' ]) { $p['FONT-KERNING'] = 'normal'; }
|
1186 |
-
else { $p['FONT-KERNING'] = 'none'; }
|
1187 |
-
}
|
1188 |
-
if (isset($biilp[ 'lSpacingCSS' ]) && $biilp[ 'lSpacingCSS' ]) { $p['LETTER-SPACING'] = $biilp[ 'lSpacingCSS' ]; }
|
1189 |
-
if (isset($biilp[ 'wSpacingCSS' ]) && $biilp[ 'wSpacingCSS' ]) { $p['WORD-SPACING'] = $biilp[ 'wSpacingCSS' ]; }
|
1190 |
-
if (isset($biilp[ 'toupper' ]) && $biilp[ 'toupper' ]) { $p['TEXT-TRANSFORM'] = 'uppercase'; }
|
1191 |
-
else if (isset($biilp[ 'tolower' ]) && $biilp[ 'tolower' ]) { $p['TEXT-TRANSFORM'] = 'lowercase'; }
|
1192 |
-
else if (isset($biilp[ 'capitalize' ]) && $biilp[ 'capitalize' ]) { $p['TEXT-TRANSFORM'] = 'capitalize'; }
|
1193 |
-
// CSS says text-decoration is not inherited, but IE7 does??
|
1194 |
-
if (isset($biilp[ 'underline' ]) && $biilp[ 'underline' ]) { $p['TEXT-DECORATION'] = 'underline'; }
|
1195 |
-
if (isset($biilp[ 'smCaps' ]) && $biilp[ 'smCaps' ]) { $p['FONT-VARIANT'] = 'small-caps'; }
|
1196 |
-
|
1197 |
Â
}
|
1198 |
Â
//===============================================
|
1199 |
Â
//===============================================
|
1200 |
-
|
1201 |
-
//
|
1202 |
-
if ($
|
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 |
Â
// DEFAULT for this TAG set in DefaultCSS
|
@@ -1241,8 +1294,13 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1241 |
Â
}
|
1242 |
Â
//===============================================
|
1243 |
Â
/*-- TABLES --*/
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1244 |
Â
// cellPadding overwrites TD/TH default but not specific CSS set on cell
|
1245 |
-
if (($tag=='TD' || $tag=='TH') && isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']) && ($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'] || $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']===0)) {
|
1246 |
Â
$p['PADDING-LEFT'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
|
1247 |
Â
$p['PADDING-RIGHT'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
|
1248 |
Â
$p['PADDING-TOP'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
|
@@ -1283,17 +1341,13 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1283 |
Â
$tfnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) : 0);
|
1284 |
Â
if ($this->mpdf->tabletfoot) { $row -= $thnr; }
|
1285 |
Â
else if (!$this->mpdf->tablethead) { $row -= ($thnr + $tfnr); }
|
1286 |
-
if (
|
1287 |
-
|
1288 |
-
else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
|
1289 |
-
if ((($row + 1) % $a[1]) == $a[2]) { $select = true; }
|
1290 |
Â
}
|
1291 |
Â
}
|
1292 |
Â
else if ($tag=='TD' || $tag=='TH') {
|
1293 |
-
if (
|
1294 |
-
|
1295 |
-
else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
|
1296 |
-
if ((($this->mpdf->col+1) % $a[1]) == $a[2]) { $select = true; }
|
1297 |
Â
}
|
1298 |
Â
}
|
1299 |
Â
if ($select) {
|
@@ -1308,6 +1362,26 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1308 |
Â
}
|
1309 |
Â
}
|
1310 |
Â
/*-- END TABLES --*/
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1311 |
Â
//===============================================
|
1312 |
Â
// STYLESHEET ID e.g. #smallone{} #redletter{}
|
1313 |
Â
if (isset($attr['ID']) && isset($this->CSS['ID>>'.$attr['ID']]) && $this->CSS['ID>>'.$attr['ID']]) {
|
@@ -1318,6 +1392,7 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1318 |
Â
$this->_mergeBorders($p,$zp);
|
1319 |
Â
}
|
1320 |
Â
}
|
Â
|
|
1321 |
Â
//===============================================
|
1322 |
Â
// STYLESHEET CLASS e.g. p.smallone{} div.redletter{}
|
1323 |
Â
foreach($classes AS $class) {
|
@@ -1330,6 +1405,26 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1330 |
Â
}
|
1331 |
Â
}
|
1332 |
Â
//===============================================
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1333 |
Â
// STYLESHEET CLASS e.g. p#smallone{} div#redletter{}
|
1334 |
Â
if (isset($attr['ID']) && isset($this->CSS[$tag.'>>ID>>'.$attr['ID']]) && $this->CSS[$tag.'>>ID>>'.$attr['ID']]) {
|
1335 |
Â
$zp = $this->CSS[$tag.'>>ID>>'.$attr['ID']];
|
@@ -1342,6 +1437,7 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1342 |
Â
//===============================================
|
1343 |
Â
// Cascaded e.g. div.class p only works for block level
|
1344 |
Â
if ($inherit == 'BLOCK') {
|
Â
|
|
1345 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'][$tag], $p);
|
1346 |
Â
foreach($classes AS $class) {
|
1347 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS']['CLASS>>'.$class], $p);
|
@@ -1351,6 +1447,7 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1351 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'][$tag.'>>CLASS>>'.$class], $p);
|
1352 |
Â
}
|
1353 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'][$tag.'>>ID>>'.$attr['ID']], $p);
|
Â
|
|
1354 |
Â
}
|
1355 |
Â
else if ($inherit == 'INLINE') {
|
1356 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'][$tag], $p);
|
@@ -1365,6 +1462,7 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1365 |
Â
}
|
1366 |
Â
/*-- TABLES --*/
|
1367 |
Â
else if ($inherit == 'TOPTABLE' || $inherit == 'TABLE') { // NB looks at $this->tablecascadeCSS-1 for cascading CSS
|
Â
|
|
1368 |
Â
// false, 9 = don't check for 'depth' and do set border dominance
|
1369 |
Â
$this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1][$tag], $p, false, 9);
|
1370 |
Â
foreach($classes AS $class) {
|
@@ -1381,17 +1479,13 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1381 |
Â
$tfnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) : 0);
|
1382 |
Â
if ($this->mpdf->tabletfoot) { $row -= $thnr; }
|
1383 |
Â
else if (!$this->mpdf->tablethead) { $row -= ($thnr + $tfnr); }
|
1384 |
-
if (
|
1385 |
-
|
1386 |
-
else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
|
1387 |
-
if ((($row + 1) % $a[1]) == $a[2]) { $select = true; }
|
1388 |
Â
}
|
1389 |
Â
}
|
1390 |
Â
else if ($tag=='TD' || $tag=='TH') {
|
1391 |
-
if (
|
1392 |
-
|
1393 |
-
else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
|
1394 |
-
if ((($this->mpdf->col + 1) % $a[1]) == $a[2]) { $select = true; }
|
1395 |
Â
}
|
1396 |
Â
}
|
1397 |
Â
if ($select) {
|
@@ -1399,6 +1493,7 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1399 |
Â
}
|
1400 |
Â
}
|
1401 |
Â
}
|
Â
|
|
1402 |
Â
}
|
1403 |
Â
$this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1]['ID>>'.$attr['ID']], $p, false, 9);
|
1404 |
Â
foreach($classes AS $class) {
|
@@ -1408,21 +1503,6 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1408 |
Â
}
|
1409 |
Â
/*-- END TABLES --*/
|
1410 |
Â
//===============================================
|
1411 |
-
/*-- LISTS --*/
|
1412 |
-
else if ($inherit == 'TOPLIST' || $inherit == 'LIST') { // NB looks at $this->listcascadeCSS-1 for cascading CSS
|
1413 |
-
// false = don't check for 'depth'
|
1414 |
-
$this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1][$tag], $p, false);
|
1415 |
-
foreach($classes AS $class) {
|
1416 |
-
$this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1]['CLASS>>'.$class], $p, false);
|
1417 |
-
}
|
1418 |
-
$this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1]['ID>>'.$attr['ID']], $p, false);
|
1419 |
-
foreach($classes AS $class) {
|
1420 |
-
$this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1][$tag.'>>CLASS>>'.$class], $p, false);
|
1421 |
-
}
|
1422 |
-
$this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1][$tag.'>>ID>>'.$attr['ID']], $p, false);
|
1423 |
-
}
|
1424 |
-
/*-- END LISTS --*/
|
1425 |
-
//===============================================
|
1426 |
Â
//===============================================
|
1427 |
Â
// INLINE STYLE e.g. style="CSS:property"
|
1428 |
Â
if (isset($attr['STYLE'])) {
|
@@ -1435,52 +1515,93 @@ function MergeCSS($inherit,$tag,$attr) {
|
|
1435 |
Â
}
|
1436 |
Â
//===============================================
|
1437 |
Â
//===============================================
|
1438 |
-
|
1439 |
-
|
1440 |
-
|
1441 |
-
|
1442 |
-
|
1443 |
-
|
1444 |
-
}
|
1445 |
-
if ($
|
1446 |
-
|
1447 |
-
|
1448 |
-
|
1449 |
-
|
1450 |
-
|
1451 |
-
|
1452 |
-
}
|
1453 |
-
if ($
|
1454 |
-
|
1455 |
-
|
1456 |
-
|
1457 |
-
|
1458 |
-
$
|
1459 |
-
if ($
|
1460 |
-
|
1461 |
-
|
1462 |
-
|
1463 |
-
|
1464 |
-
|
1465 |
-
|
1466 |
-
|
1467 |
-
|
1468 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1469 |
Â
}
|
Â
|
|
1470 |
Â
}
|
1471 |
-
|
1472 |
-
|
1473 |
-
}
|
1474 |
-
if (isset($attr['VSPACE']) and $attr['VSPACE']!='') {
|
1475 |
-
$p['MARGIN-TOP'] = $attr['VSPACE'];
|
1476 |
-
$p['MARGIN-BOTTOM'] = $attr['VSPACE'];
|
1477 |
-
}
|
1478 |
-
if (isset($attr['HSPACE']) and $attr['HSPACE']!='') {
|
1479 |
-
$p['MARGIN-LEFT'] = $attr['HSPACE'];
|
1480 |
-
$p['MARGIN-RIGHT'] = $attr['HSPACE'];
|
1481 |
-
}
|
1482 |
-
//===============================================
|
1483 |
-
return $p;
|
1484 |
Â
}
|
1485 |
Â
|
1486 |
Â
function PreviewBlockCSS($tag,$attr) {
|
@@ -1558,6 +1679,40 @@ function PreviewBlockCSS($tag,$attr) {
|
|
1558 |
Â
}
|
1559 |
Â
|
1560 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1561 |
Â
|
1562 |
Â
|
1563 |
Â
|
5 |
Â
var $mpdf = null;
|
6 |
Â
|
7 |
Â
var $tablecascadeCSS;
|
Â
|
|
8 |
Â
var $cascadeCSS;
|
9 |
Â
var $CSS;
|
10 |
Â
var $tbCSSlvl;
|
Â
|
|
11 |
Â
|
12 |
Â
|
13 |
Â
function cssmgr(&$mpdf) {
|
14 |
Â
$this->mpdf = $mpdf;
|
15 |
Â
$this->tablecascadeCSS = array();
|
Â
|
|
16 |
Â
$this->CSS=array();
|
17 |
Â
$this->cascadeCSS = array();
|
18 |
Â
$this->tbCSSlvl = 0;
|
Â
|
|
19 |
Â
}
|
20 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
21 |
Â
function ReadCSS($html) {
|
22 |
Â
preg_match_all('/<style[^>]*media=["\']([^"\'>]*)["\'].*?<\/style>/is',$html,$m);
|
23 |
Â
for($i=0; $i<count($m[0]); $i++) {
|
64 |
Â
$match += $x;
|
65 |
Â
$CSSext = $cxt[1];
|
66 |
Â
}
|
Â
|
|
67 |
Â
$regexp = '/<link[^>]*href=["\']([^>"\']*)["\'][^>]*?rel=["\']stylesheet["\'].*?>/si';
|
68 |
Â
$x = preg_match_all($regexp,$html,$cxt);
|
69 |
Â
if ($x) {
|
96 |
Â
|
97 |
Â
while($match){
|
98 |
Â
$path = $CSSext[$ind];
|
99 |
+
|
100 |
+
$path = htmlspecialchars_decode($path); // mPDF 6
|
101 |
+
|
102 |
Â
$this->mpdf->GetFullPath($path);
|
103 |
Â
$CSSextblock = $this->mpdf->_get_file($path);
|
104 |
Â
if ($CSSextblock) {
|
169 |
Â
}
|
170 |
Â
}
|
171 |
Â
|
Â
|
|
172 |
Â
// Replace any background: url(data:image... with temporary image file reference
|
173 |
+
preg_match_all("/(url\(data:image\/(jpeg|gif|png);base64,(.*?)\))/si", $CSSstr, $idata); // mPDF 5.7.2
|
174 |
Â
if (count($idata[0])) {
|
175 |
Â
for($i=0;$i<count($idata[0]);$i++) {
|
176 |
Â
$file = _MPDF_TEMP_PATH.'_tempCSSidata'.RAND(1,10000).'_'.$i.'.'.$idata[2][$i];
|
182 |
Â
}
|
183 |
Â
|
184 |
Â
$CSSstr = preg_replace('/(<\!\-\-|\-\->)/s',' ',$CSSstr);
|
185 |
+
|
186 |
+
// mPDF 5.7.4 URLs
|
187 |
+
// Characters "(" ")" and ";" in url() e.g. background-image, cause problems parsing the CSS string
|
188 |
+
// URLencode ( and ), but change ";" to a code which can be converted back after parsing (so as not to confuse ;
|
189 |
+
// with a segment delimiter in the URI)
|
190 |
+
$tempmarker = '%ZZ';
|
191 |
+
if (strpos($CSSstr,'url(')!==false) {
|
192 |
+
preg_match_all( '/url\(\"(.*?)\"\)/', $CSSstr, $m);
|
193 |
+
for($i = 0; $i < count($m[1]) ; $i++) {
|
194 |
+
$tmp = str_replace(array('(',')',';'),array('%28','%29',$tempmarker),$m[1][$i]);
|
195 |
+
$CSSstr = preg_replace('/'.preg_quote($m[0][$i],'/').'/', 'url(\''.$tmp.'\')', $CSSstr);
|
196 |
+
}
|
197 |
+
preg_match_all( '/url\(\'(.*?)\'\)/', $CSSstr, $m);
|
198 |
+
for($i = 0; $i < count($m[1]) ; $i++) {
|
199 |
+
$tmp = str_replace(array('(',')',';'),array('%28','%29',$tempmarker),$m[1][$i]);
|
200 |
+
$CSSstr = preg_replace('/'.preg_quote($m[0][$i],'/').'/', 'url(\''.$tmp.'\')', $CSSstr);
|
201 |
+
}
|
202 |
+
preg_match_all( '/url\(([^\'\"].*?[^\'\"])\)/', $CSSstr, $m);
|
203 |
+
for($i = 0; $i < count($m[1]) ; $i++) {
|
204 |
+
$tmp = str_replace(array('(',')',';'),array('%28','%29',$tempmarker),$m[1][$i]);
|
205 |
+
$CSSstr = preg_replace('/'.preg_quote($m[0][$i],'/').'/', 'url(\''.$tmp.'\')', $CSSstr);
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
|
210 |
+
|
211 |
Â
if ($CSSstr ) {
|
212 |
+
$classproperties = array(); // mPDF 6
|
213 |
Â
preg_match_all('/(.*?)\{(.*?)\}/',$CSSstr,$styles);
|
214 |
Â
for($i=0; $i < count($styles[1]) ; $i++) {
|
215 |
Â
// SET array e.g. $classproperties['COLOR'] = '#ffffff';
|
218 |
Â
foreach($stylearr AS $sta) {
|
219 |
Â
if (trim($sta)) {
|
220 |
Â
// Changed to allow style="background: url('http://www.bpm1.com/bg.jpg')"
|
221 |
+
$tmp = explode(':',$sta,2);
|
222 |
+
$property = $tmp[0];
|
223 |
+
if (isset($tmp[1])) { $value = $tmp[1]; }
|
224 |
+
else { $value = ''; }
|
225 |
+
$value = str_replace($tempmarker,';',$value); // mPDF 5.7.4 URLs
|
226 |
Â
$property = trim($property);
|
227 |
Â
$value = preg_replace('/\s*!important/i','',$value);
|
228 |
Â
$value = trim($value);
|
240 |
Â
$tagarr = explode(',',$tagstr);
|
241 |
Â
$pageselectors = false; // used to turn on $this->mpdf->mirrorMargins
|
242 |
Â
foreach($tagarr AS $tg) {
|
243 |
+
// mPDF 5.7.4
|
244 |
+
if (preg_match('/NTH-CHILD\((\s*(([\-+]?\d*)N(\s*[\-+]\s*\d+)?|[\-+]?\d+|ODD|EVEN)\s*)\)/',$tg,$m) ) {
|
245 |
+
$tg = preg_replace('/NTH-CHILD\(.*\)/', 'NTH-CHILD('.str_replace(' ','',$m[1]).')', $tg);
|
246 |
+
}
|
247 |
Â
$tags = preg_split('/\s+/',trim($tg));
|
248 |
Â
$level = count($tags);
|
249 |
Â
$t = '';
|
274 |
Â
$tag = '';
|
275 |
Â
if (preg_match('/^[.](.*)$/',$t,$m)) { $tag = 'CLASS>>'.$m[1]; }
|
276 |
Â
else if (preg_match('/^[#](.*)$/',$t,$m)) { $tag = 'ID>>'.$m[1]; }
|
277 |
+
else if (preg_match('/^\[LANG=[\'\"]{0,1}([A-Z\-]{2,11})[\'\"]{0,1}\]$/',$t,$m)) { $tag = 'LANG>>'.strtolower($m[1]); } // mPDF 6 Special case for lang as attribute selector
|
278 |
+
else if (preg_match('/^:LANG\([\'\"]{0,1}([A-Z\-]{2,11})[\'\"]{0,1}\)$/',$t,$m)) { $tag = 'LANG>>'.strtolower($m[1]); } // mPDF 6 Special case for lang as attribute selector
|
279 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[.](.*)$/',$t,$m)) { $tag = $m[1].'>>CLASS>>'.$m[2]; }
|
280 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')\s*:NTH-CHILD\((.*)\)$/',$t,$m)) { $tag = $m[1].'>>SELECTORNTHCHILD>>'.$m[2]; }
|
281 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[#](.*)$/',$t,$m)) { $tag = $m[1].'>>ID>>'.$m[2]; }
|
282 |
+
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')\[LANG=[\'\"]{0,1}([A-Z\-]{2,11})[\'\"]{0,1}\]$/',$t,$m)) { $tag = $m[1].'>>LANG>>'.strtolower($m[2]); } // mPDF 6 Special case for lang as attribute selector
|
283 |
+
else if (preg_match('/^('.$this->mpdf->allowedCSStags.'):LANG\([\'\"]{0,1}([A-Z\-]{2,11})[\'\"]{0,1}\)$/',$t,$m)) { $tag = $m[1].'>>LANG>>'.strtolower($m[2]); } // mPDF 6 Special case for lang as attribute selector
|
284 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')$/',$t)) { $tag= $t; }
|
285 |
Â
if (isset($this->CSS[$tag]) && $tag) { $this->CSS[$tag] = $this->array_merge_recursive_unique($this->CSS[$tag], $classproperties); }
|
286 |
Â
else if ($tag) { $this->CSS[$tag] = $classproperties; }
|
295 |
Â
$tag = '';
|
296 |
Â
if (preg_match('/^[.](.*)$/',$t,$m)) { $tag = 'CLASS>>'.$m[1]; }
|
297 |
Â
else if (preg_match('/^[#](.*)$/',$t,$m)) { $tag = 'ID>>'.$m[1]; }
|
298 |
+
else if (preg_match('/^\[LANG=[\'\"]{0,1}([A-Z\-]{2,11})[\'\"]{0,1}\]$/',$t,$m)) { $tag = 'LANG>>'.strtolower($m[1]); } // mPDF 6 Special case for lang as attribute selector
|
299 |
+
else if (preg_match('/^:LANG\([\'\"]{0,1}([A-Z\-]{2,11})[\'\"]{0,1}\)$/',$t,$m)) { $tag = 'LANG>>'.strtolower($m[1]); } // mPDF 6 Special case for lang as attribute selector
|
300 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[.](.*)$/',$t,$m)) { $tag = $m[1].'>>CLASS>>'.$m[2]; }
|
301 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')\s*:NTH-CHILD\((.*)\)$/',$t,$m)) { $tag = $m[1].'>>SELECTORNTHCHILD>>'.$m[2]; }
|
302 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[#](.*)$/',$t,$m)) { $tag = $m[1].'>>ID>>'.$m[2]; }
|
303 |
+
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')\[LANG=[\'\"]{0,1}([A-Z\-]{2,11})[\'\"]{0,1}\]$/',$t,$m)) { $tag = $m[1].'>>LANG>>'.strtolower($m[2]); } // mPDF 6 Special case for lang as attribute selector
|
304 |
+
else if (preg_match('/^('.$this->mpdf->allowedCSStags.'):LANG\([\'\"]{0,1}([A-Z\-]{2,11})[\'\"]{0,1}\)$/',$t,$m)) { $tag = $m[1].'>>LANG>>'.strtolower($m[2]); } // mPDF 6 Special case for lang as attribute selector
|
305 |
Â
else if (preg_match('/^('.$this->mpdf->allowedCSStags.')$/',$t)) { $tag= $t; }
|
306 |
Â
|
307 |
Â
if ($tag) $tmp[] = $tag;
|
334 |
Â
|
335 |
Â
|
336 |
Â
function readInlineCSS($html) {
|
337 |
+
$html=htmlspecialchars_decode($html); // mPDF 5.7.4 URLs
|
338 |
+
// mPDF 5.7.4 URLs
|
339 |
+
// Characters "(" ")" and ";" in url() e.g. background-image, cause probems parsing the CSS string
|
340 |
+
// URLencode ( and ), but change ";" to a code which can be converted back after parsing (so as not to confuse ;
|
341 |
+
// with a segment delimiter in the URI)
|
342 |
+
$tempmarker = '%ZZ';
|
343 |
+
if (strpos($html,'url(')!==false) {
|
344 |
+
preg_match_all( '/url\(\"(.*?)\"\)/', $html, $m);
|
345 |
+
for($i = 0; $i < count($m[1]) ; $i++) {
|
346 |
+
$tmp = str_replace(array('(',')',';'),array('%28','%29',$tempmarker),$m[1][$i]);
|
347 |
+
$html = preg_replace('/'.preg_quote($m[0][$i],'/').'/', 'url(\''.$tmp.'\')', $html);
|
348 |
+
}
|
349 |
+
preg_match_all( '/url\(\'(.*?)\'\)/', $html, $m);
|
350 |
+
for($i = 0; $i < count($m[1]) ; $i++) {
|
351 |
+
$tmp = str_replace(array('(',')',';'),array('%28','%29',$tempmarker),$m[1][$i]);
|
352 |
+
$html = preg_replace('/'.preg_quote($m[0][$i],'/').'/', 'url(\''.$tmp.'\')', $html);
|
353 |
+
}
|
354 |
+
preg_match_all( '/url\(([^\'\"].*?[^\'\"])\)/', $html, $m);
|
355 |
+
for($i = 0; $i < count($m[1]) ; $i++) {
|
356 |
+
$tmp = str_replace(array('(',')',';'),array('%28','%29',$tempmarker),$m[1][$i]);
|
357 |
+
$html = preg_replace('/'.preg_quote($m[0][$i],'/').'/', 'url(\''.$tmp.'\')', $html);
|
358 |
+
}
|
359 |
+
}
|
360 |
Â
//Fix incomplete CSS code
|
361 |
Â
$size = strlen($html)-1;
|
362 |
Â
if (substr($html,$size,1) != ';') $html .= ';';
|
372 |
Â
if ((strtoupper($properties[$i])=='BACKGROUND-IMAGE' || strtoupper($properties[$i])=='BACKGROUND') && preg_match('/-webkit-gradient/i',$values[$i])) {
|
373 |
Â
continue;
|
374 |
Â
}
|
375 |
+
$values[$i] = str_replace($tempmarker,';',$values[$i]); // mPDF 5.7.4 URLs
|
376 |
Â
$classproperties[strtoupper($properties[$i])] = trim($values[$i]);
|
377 |
Â
}
|
378 |
Â
return $this->fixCSS($classproperties);
|
469 |
Â
if (preg_match('/small-caps/i',$s)) { $newprop['TEXT-TRANSFORM'] = 'uppercase'; }
|
470 |
Â
}
|
471 |
Â
}
|
472 |
+
else if ($k == 'FONT-FAMILY') {
|
473 |
Â
$aux_fontlist = explode(",",$v);
|
474 |
Â
$found = 0;
|
475 |
Â
foreach($aux_fontlist AS $f) {
|
501 |
Â
}
|
502 |
Â
}
|
503 |
Â
}
|
504 |
+
// mPDF 5.7.1
|
505 |
+
else if ($k == 'FONT-VARIANT') {
|
506 |
+
if (preg_match('/(normal|none)/',$v, $m)) { // mPDF 6
|
507 |
+
$newprop['FONT-VARIANT-LIGATURES'] = $m[1];
|
508 |
+
$newprop['FONT-VARIANT-CAPS'] = $m[1];
|
509 |
+
$newprop['FONT-VARIANT-NUMERIC'] = $m[1];
|
510 |
+
$newprop['FONT-VARIANT-ALTERNATES'] = $m[1];
|
511 |
+
}
|
512 |
+
else {
|
513 |
+
if (preg_match_all('/(no-common-ligatures|\bcommon-ligatures|no-discretionary-ligatures|\bdiscretionary-ligatures|no-historical-ligatures|\bhistorical-ligatures|no-contextual|\bcontextual)/i',$v, $m)) {
|
514 |
+
$newprop['FONT-VARIANT-LIGATURES'] = implode(' ',$m[1]);
|
515 |
+
}
|
516 |
+
if (preg_match('/(all-small-caps|\bsmall-caps|all-petite-caps|\bpetite-caps|unicase|titling-caps)/i',$v, $m)) {
|
517 |
+
$newprop['FONT-VARIANT-CAPS'] = $m[1];
|
518 |
+
}
|
519 |
+
if (preg_match_all('/(lining-nums|oldstyle-nums|proportional-nums|tabular-nums|diagonal-fractions|stacked-fractions)/i',$v, $m)) {
|
520 |
+
$newprop['FONT-VARIANT-NUMERIC'] = implode(' ',$m[1]);
|
521 |
+
}
|
522 |
+
if (preg_match('/(historical-forms)/i',$v, $m)) {
|
523 |
+
$newprop['FONT-VARIANT-ALTERNATES'] = $m[1];
|
524 |
+
}
|
525 |
+
}
|
526 |
+
}
|
527 |
Â
else if ($k == 'MARGIN') {
|
528 |
Â
$tmp = $this->expand24($v);
|
529 |
Â
$newprop['MARGIN-TOP'] = $tmp['T'];
|
573 |
Â
}
|
574 |
Â
else if ($k == 'BORDER-STYLE') {
|
575 |
Â
$e = $this->expand24($v);
|
576 |
+
if (!empty($e)) {
|
577 |
+
$newprop['BORDER-TOP-STYLE'] = $e['T'];
|
578 |
+
$newprop['BORDER-RIGHT-STYLE'] = $e['R'];
|
579 |
+
$newprop['BORDER-BOTTOM-STYLE'] = $e['B'];
|
580 |
+
$newprop['BORDER-LEFT-STYLE'] = $e['L'];
|
581 |
+
}
|
582 |
Â
}
|
583 |
Â
else if ($k == 'BORDER-WIDTH') {
|
584 |
Â
$e = $this->expand24($v);
|
585 |
+
if (!empty($e)) {
|
586 |
+
$newprop['BORDER-TOP-WIDTH'] = $e['T'];
|
587 |
+
$newprop['BORDER-RIGHT-WIDTH'] = $e['R'];
|
588 |
+
$newprop['BORDER-BOTTOM-WIDTH'] = $e['B'];
|
589 |
+
$newprop['BORDER-LEFT-WIDTH'] = $e['L'];
|
590 |
+
}
|
591 |
Â
}
|
592 |
Â
else if ($k == 'BORDER-COLOR') {
|
593 |
Â
$e = $this->expand24($v);
|
594 |
+
if (!empty($e)) {
|
595 |
+
$newprop['BORDER-TOP-COLOR'] = $e['T'];
|
596 |
+
$newprop['BORDER-RIGHT-COLOR'] = $e['R'];
|
597 |
+
$newprop['BORDER-BOTTOM-COLOR'] = $e['B'];
|
598 |
+
$newprop['BORDER-LEFT-COLOR'] = $e['L'];
|
599 |
+
}
|
600 |
Â
}
|
601 |
Â
|
602 |
Â
else if ($k == 'BORDER-SPACING') {
|
725 |
Â
$newprop['IMAGE-ORIENTATION'] = $angle;
|
726 |
Â
}
|
727 |
Â
}
|
Â
|
|
728 |
Â
else if ($k == 'TEXT-ALIGN') {
|
729 |
Â
if (preg_match('/["\'](.){1}["\']/i',$v,$m)) {
|
730 |
Â
$d = array_search($m[1],$this->mpdf->decimal_align);
|
741 |
Â
}
|
742 |
Â
else { $newprop[$k] = $v; }
|
743 |
Â
}
|
744 |
+
// mpDF 6 Lists
|
745 |
+
else if ($k == 'LIST-STYLE') {
|
746 |
+
if (preg_match('/none/i',$v,$m)) {
|
747 |
+
$newprop['LIST-STYLE-TYPE'] = 'none';
|
748 |
+
$newprop['LIST-STYLE-IMAGE'] = 'none';
|
749 |
+
}
|
750 |
+
if (preg_match('/(lower-roman|upper-roman|lower-latin|lower-alpha|upper-latin|upper-alpha|decimal|disc|circle|square|arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu|cambodian|khmer|lao|cjk-decimal|hebrew)/i',$v,$m)) {
|
751 |
+
$newprop['LIST-STYLE-TYPE'] = strtolower(trim($m[1]));
|
752 |
+
}
|
753 |
+
else if (preg_match('/U\+([a-fA-F0-9]+)/i',$v,$m)) {
|
754 |
+
$newprop['LIST-STYLE-TYPE'] = strtolower(trim($m[1]));
|
755 |
+
}
|
756 |
+
if (preg_match('/url\([\'\"]{0,1}(.*?)[\'\"]{0,1}\)/i',$v,$m)) {
|
757 |
+
$newprop['LIST-STYLE-IMAGE'] = strtolower(trim($m[1]));
|
758 |
+
}
|
759 |
+
if (preg_match('/(inside|outside)/i',$v,$m)) {
|
760 |
+
$newprop['LIST-STYLE-POSITION'] = strtolower(trim($m[1]));
|
761 |
+
}
|
762 |
+
}
|
763 |
Â
|
764 |
Â
else {
|
765 |
Â
$newprop[$k] = $v;
|
815 |
Â
foreach ($ss AS $s) {
|
816 |
Â
$new = array('blur'=>0);
|
817 |
Â
$p = explode(' ',trim($s));
|
818 |
+
if (isset($p[0])) { $new['x'] = $this->mpdf->ConvertSize(trim($p[0]),$this->mpdf->FontSize,$this->mpdf->FontSize,false); }
|
819 |
+
if (isset($p[1])) { $new['y'] = $this->mpdf->ConvertSize(trim($p[1]),$this->mpdf->FontSize,$this->mpdf->FontSize,false); }
|
820 |
Â
if (isset($p[2])) {
|
821 |
Â
if (preg_match('/^\s*[\.\-0-9]/',$p[2])) {
|
822 |
+
$new['blur'] = $this->mpdf->ConvertSize(trim($p[2]),$this->mpdf->blk[$this->mpdf->blklvl]['inner_width'],$this->mpdf->FontSize,false);
|
823 |
Â
}
|
824 |
Â
else { $new['col'] = $this->mpdf->ConvertColor(preg_replace('/\*/',',',$p[2])); }
|
825 |
Â
if (isset($p[3])) {
|
826 |
Â
$new['col'] = $this->mpdf->ConvertColor(preg_replace('/\*/',',',$p[3]));
|
827 |
Â
}
|
828 |
Â
}
|
829 |
+
if (!isset($new['col']) || !$new['col']) { $new['col'] = $this->mpdf->ConvertColor('#888888'); }
|
830 |
Â
if (isset($new['y'])) { array_unshift($sh, $new); }
|
831 |
Â
}
|
832 |
Â
return $sh;
|
1023 |
Â
|
1024 |
Â
|
1025 |
Â
|
1026 |
+
function _mergeFullCSS($p, &$t, $tag, $classes, $id, $lang) { // mPDF 6
|
1027 |
+
if (isset($p[$tag])) { $this->_mergeCSS($p[$tag], $t); }
|
1028 |
Â
// STYLESHEET CLASS e.g. .smallone{} .redletter{}
|
1029 |
Â
foreach($classes AS $class) {
|
1030 |
+
if (isset($p['CLASS>>'.$class])) { $this->_mergeCSS($p['CLASS>>'.$class], $t); }
|
1031 |
Â
}
|
1032 |
Â
// STYLESHEET nth-child SELECTOR e.g. tr:nth-child(odd) td:nth-child(2n+1)
|
1033 |
Â
if ($tag=='TR' && isset($p) && $p) {
|
1040 |
Â
$tfnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) : 0);
|
1041 |
Â
if ($this->mpdf->tabletfoot) { $row -= $thnr; }
|
1042 |
Â
else if (!$this->mpdf->tablethead) { $row -= ($thnr + $tfnr); }
|
1043 |
+
if (preg_match('/(([\-+]?\d*)?N([\-+]\d+)?|[\-+]?\d+|ODD|EVEN)/',$m[1],$a)) { // mPDF 5.7.4
|
1044 |
+
$select = $this->_nthchild($a, $row);
|
Â
|
|
Â
|
|
1045 |
Â
}
|
1046 |
Â
}
|
1047 |
Â
else if ($tag=='TD' || $tag=='TH') {
|
1048 |
+
if (preg_match('/(([\-+]?\d*)?N([\-+]\d+)?|[\-+]?\d+|ODD|EVEN)/',$m[1],$a)) { // mPDF 5.7.4
|
1049 |
+
$select = $this->_nthchild($a, $this->mpdf->col);
|
Â
|
|
Â
|
|
1050 |
Â
}
|
1051 |
Â
}
|
1052 |
Â
if ($select) {
|
1055 |
Â
}
|
1056 |
Â
}
|
1057 |
Â
}
|
1058 |
+
// STYLESHEET CLASS e.g. [lang=fr]{} or :lang(fr)
|
1059 |
+
if (isset($lang) && isset($p['LANG>>'.$lang])) {
|
1060 |
+
$this->_mergeCSS($p['LANG>>'.$lang], $t);
|
1061 |
+
}
|
1062 |
Â
// STYLESHEET CLASS e.g. #smallone{} #redletter{}
|
1063 |
+
if (isset($id) && isset($p['ID>>'.$id])) {
|
1064 |
Â
$this->_mergeCSS($p['ID>>'.$id], $t);
|
1065 |
Â
}
|
1066 |
+
|
1067 |
Â
// STYLESHEET CLASS e.g. .smallone{} .redletter{}
|
1068 |
Â
foreach($classes AS $class) {
|
1069 |
+
if (isset($p[$tag.'>>CLASS>>'.$class])) { $this->_mergeCSS($p[$tag.'>>CLASS>>'.$class], $t); }
|
1070 |
+
}
|
1071 |
+
// STYLESHEET CLASS e.g. [lang=fr]{} or :lang(fr)
|
1072 |
+
if (isset($lang) && isset($p[$tag.'>>LANG>>'.$lang])) {
|
1073 |
+
$this->_mergeCSS($p[$tag.'>>LANG>>'.$lang], $t);
|
1074 |
Â
}
|
1075 |
Â
// STYLESHEET CLASS e.g. #smallone{} #redletter{}
|
1076 |
+
if (isset($id) && isset($p[$tag.'>>ID>>'.$id])) {
|
1077 |
Â
$this->_mergeCSS($p[$tag.'>>ID>>'.$id], $t);
|
1078 |
Â
}
|
1079 |
Â
}
|
1134 |
Â
$classes = preg_split('/\s+/',$attr['CLASS']);
|
1135 |
Â
}
|
1136 |
Â
if (!isset($attr['ID'])) { $attr['ID']=''; }
|
1137 |
+
// mPDF 6
|
1138 |
+
$shortlang = '';
|
1139 |
+
if (!isset($attr['LANG'])) { $attr['LANG']=''; }
|
1140 |
+
else {
|
1141 |
+
$attr['LANG'] = strtolower($attr['LANG']);
|
1142 |
+
if (strlen($attr['LANG']) == 5) {
|
1143 |
+
$shortlang = substr($attr['LANG'],0,2);
|
1144 |
+
}
|
1145 |
+
}
|
1146 |
Â
//===============================================
|
1147 |
Â
/*-- TABLES --*/
|
1148 |
Â
// Set Inherited properties
|
1166 |
Â
$this->tablecascadeCSS[$this->tbCSSlvl][$k] = $v;
|
1167 |
Â
}
|
1168 |
Â
}
|
1169 |
+
$this->_mergeFullCSS($this->cascadeCSS, $this->tablecascadeCSS[$this->tbCSSlvl], $tag, $classes, $attr['ID'], $attr['LANG']);
|
1170 |
Â
//===============================================
|
1171 |
Â
// Cascading forward CSS e.g. "table.topic td" for this table in $this->tablecascadeCSS
|
1172 |
Â
//===============================================
|
1173 |
Â
// STYLESHEET TAG e.g. table
|
1174 |
+
$this->_mergeFullCSS($this->tablecascadeCSS[$this->tbCSSlvl-1], $this->tablecascadeCSS[$this->tbCSSlvl], $tag, $classes, $attr['ID'], $attr['LANG']);
|
1175 |
Â
//===============================================
|
1176 |
Â
}
|
1177 |
Â
/*-- END TABLES --*/
|
1178 |
Â
//===============================================
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1179 |
Â
// Set Inherited properties
|
1180 |
Â
if ($inherit == 'BLOCK') {
|
1181 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS']) && is_array($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'])) {
|
1187 |
Â
|
1188 |
Â
//===============================================
|
1189 |
Â
// Save Cascading CSS e.g. "div.topic p" at this block level
|
1190 |
+
$this->_mergeFullCSS($this->cascadeCSS, $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'], $tag, $classes, $attr['ID'], $attr['LANG']);
|
1191 |
Â
//===============================================
|
1192 |
Â
// Cascading forward CSS
|
1193 |
Â
//===============================================
|
1194 |
+
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1])) {
|
1195 |
+
$this->_mergeFullCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'], $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'], $tag, $classes, $attr['ID'], $attr['LANG']);
|
1196 |
+
}
|
1197 |
Â
//===============================================
|
1198 |
+
// Block properties which are inherited
|
1199 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['margin_collapse']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['margin_collapse']) { $p['MARGIN-COLLAPSE'] = 'COLLAPSE'; } // custom tag, but follows CSS principle that border-collapse is inherited
|
1200 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['line_height']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['line_height']) { $p['LINE-HEIGHT'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['line_height']; }
|
1201 |
+
// mPDF 6
|
1202 |
+
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['line_stacking_strategy']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['line_stacking_strategy']) { $p['LINE-STACKING-STRATEGY'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['line_stacking_strategy']; }
|
1203 |
+
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['line_stacking_shift']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['line_stacking_shift']) { $p['LINE-STACKING-SHIFT'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['line_stacking_shift']; }
|
1204 |
Â
|
1205 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['direction']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['direction']) { $p['DIRECTION'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['direction']; }
|
1206 |
+
// mPDF 6 Lists
|
1207 |
+
if ($tag == 'LI') {
|
1208 |
+
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['list_style_type']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['list_style_type']) { $p['LIST-STYLE-TYPE'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['list_style_type']; }
|
1209 |
+
}
|
1210 |
+
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['list_style_image']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['list_style_image']) { $p['LIST-STYLE-IMAGE'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['list_style_image']; }
|
1211 |
+
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['list_style_position']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['list_style_position']) { $p['LIST-STYLE-POSITION'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['list_style_position']; }
|
1212 |
Â
|
1213 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['align']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['align']) {
|
1214 |
Â
if ($this->mpdf->blk[$this->mpdf->blklvl-1]['align'] == 'L') { $p['TEXT-ALIGN'] = 'left'; }
|
1226 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent']) && ($this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent'] || $this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent']===0)) { $p['TEXT-INDENT'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent']; }
|
1227 |
Â
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['InlineProperties'])) {
|
1228 |
Â
$biilp = $this->mpdf->blk[$this->mpdf->blklvl-1]['InlineProperties'];
|
1229 |
+
$this->inlinePropsToCSS($biilp, $p); // mPDF 5.7.1
|
1230 |
Â
}
|
1231 |
Â
else { $biilp = null; }
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1232 |
Â
}
|
1233 |
Â
//===============================================
|
1234 |
Â
//===============================================
|
1235 |
+
// INLINE HTML ATTRIBUTES e.g. .. ALIGN="CENTER">
|
1236 |
+
// mPDF 6 (added)
|
1237 |
+
if (isset($attr['DIR']) and $attr['DIR']!='') {
|
1238 |
+
$p['DIRECTION'] = $attr['DIR'];
|
1239 |
+
}
|
1240 |
+
// mPDF 6 (moved)
|
1241 |
+
if (isset($attr['LANG']) and $attr['LANG']!='') {
|
1242 |
+
$p['LANG'] = $attr['LANG'];
|
1243 |
+
}
|
1244 |
+
if (isset($attr['COLOR']) and $attr['COLOR']!='') {
|
1245 |
+
$p['COLOR'] = $attr['COLOR'];
|
1246 |
+
}
|
1247 |
+
|
1248 |
+
if ($tag != 'INPUT') {
|
1249 |
+
if (isset($attr['WIDTH']) and $attr['WIDTH']!='') {
|
1250 |
+
$p['WIDTH'] = $attr['WIDTH'];
|
1251 |
+
}
|
1252 |
+
if (isset($attr['HEIGHT']) and $attr['HEIGHT']!='') {
|
1253 |
+
$p['HEIGHT'] = $attr['HEIGHT'];
|
1254 |
+
}
|
1255 |
+
}
|
1256 |
+
if ($tag == 'FONT') {
|
1257 |
+
if (isset($attr['FACE'])) {
|
1258 |
+
$p['FONT-FAMILY'] = $attr['FACE'];
|
1259 |
+
}
|
1260 |
+
if (isset($attr['SIZE']) and $attr['SIZE']!='') {
|
1261 |
+
$s = '';
|
1262 |
+
if ($attr['SIZE'] === '+1') { $s = '120%'; }
|
1263 |
+
else if ($attr['SIZE'] === '-1') { $s = '86%'; }
|
1264 |
+
else if ($attr['SIZE'] === '1') { $s = 'XX-SMALL'; }
|
1265 |
+
else if ($attr['SIZE'] == '2') { $s = 'X-SMALL'; }
|
1266 |
+
else if ($attr['SIZE'] == '3') { $s = 'SMALL'; }
|
1267 |
+
else if ($attr['SIZE'] == '4') { $s = 'MEDIUM'; }
|
1268 |
+
else if ($attr['SIZE'] == '5') { $s = 'LARGE'; }
|
1269 |
+
else if ($attr['SIZE'] == '6') { $s = 'X-LARGE'; }
|
1270 |
+
else if ($attr['SIZE'] == '7') { $s = 'XX-LARGE'; }
|
1271 |
+
if ($s) $p['FONT-SIZE'] = $s;
|
1272 |
+
}
|
1273 |
+
}
|
1274 |
+
if (isset($attr['VALIGN']) and $attr['VALIGN']!='') {
|
1275 |
+
$p['VERTICAL-ALIGN'] = $attr['VALIGN'];
|
1276 |
+
}
|
1277 |
+
if (isset($attr['VSPACE']) and $attr['VSPACE']!='') {
|
1278 |
+
$p['MARGIN-TOP'] = $attr['VSPACE'];
|
1279 |
+
$p['MARGIN-BOTTOM'] = $attr['VSPACE'];
|
1280 |
+
}
|
1281 |
+
if (isset($attr['HSPACE']) and $attr['HSPACE']!='') {
|
1282 |
+
$p['MARGIN-LEFT'] = $attr['HSPACE'];
|
1283 |
+
$p['MARGIN-RIGHT'] = $attr['HSPACE'];
|
1284 |
+
}
|
1285 |
Â
//===============================================
|
1286 |
Â
//===============================================
|
1287 |
Â
// DEFAULT for this TAG set in DefaultCSS
|
1294 |
Â
}
|
1295 |
Â
//===============================================
|
1296 |
Â
/*-- TABLES --*/
|
1297 |
+
// mPDF 5.7.3
|
1298 |
+
// cellSpacing overwrites TABLE default but not specific CSS set on table
|
1299 |
+
if ($tag=='TABLE' && isset($attr['CELLSPACING'])) {
|
1300 |
+
$p['BORDER-SPACING-H'] = $p['BORDER-SPACING-V'] = $attr['CELLSPACING'];
|
1301 |
+
}
|
1302 |
Â
// cellPadding overwrites TD/TH default but not specific CSS set on cell
|
1303 |
+
if (($tag=='TD' || $tag=='TH') && isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']) && ($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'] || $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']==='0')) { // mPDF 5.7.3
|
1304 |
Â
$p['PADDING-LEFT'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
|
1305 |
Â
$p['PADDING-RIGHT'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
|
1306 |
Â
$p['PADDING-TOP'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
|
1341 |
Â
$tfnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) : 0);
|
1342 |
Â
if ($this->mpdf->tabletfoot) { $row -= $thnr; }
|
1343 |
Â
else if (!$this->mpdf->tablethead) { $row -= ($thnr + $tfnr); }
|
1344 |
+
if (preg_match('/(([\-+]?\d*)?N([\-+]\d+)?|[\-+]?\d+|ODD|EVEN)/',$m[1],$a)) { // mPDF 5.7.4
|
1345 |
+
$select = $this->_nthchild($a, $row);
|
Â
|
|
Â
|
|
1346 |
Â
}
|
1347 |
Â
}
|
1348 |
Â
else if ($tag=='TD' || $tag=='TH') {
|
1349 |
+
if (preg_match('/(([\-+]?\d*)?N([\-+]\d+)?|[\-+]?\d+|ODD|EVEN)/',$m[1],$a)) { // mPDF 5.7.4
|
1350 |
+
$select = $this->_nthchild($a, $this->mpdf->col);
|
Â
|
|
Â
|
|
1351 |
Â
}
|
1352 |
Â
}
|
1353 |
Â
if ($select) {
|
1362 |
Â
}
|
1363 |
Â
}
|
1364 |
Â
/*-- END TABLES --*/
|
1365 |
+
//===============================================
|
1366 |
+
// STYLESHEET LANG e.g. [lang=fr]{} or :lang(fr)
|
1367 |
+
if (isset($attr['LANG'])) {
|
1368 |
+
if (isset($this->CSS['LANG>>'.$attr['LANG']]) && $this->CSS['LANG>>'.$attr['LANG']]) {
|
1369 |
+
$zp = $this->CSS['LANG>>'.$attr['LANG']];
|
1370 |
+
if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
|
1371 |
+
if (is_array($zp)) {
|
1372 |
+
$p = array_merge($p,$zp);
|
1373 |
+
$this->_mergeBorders($p,$zp);
|
1374 |
+
}
|
1375 |
+
}
|
1376 |
+
else if (isset($this->CSS['LANG>>'.$shortlang]) && $this->CSS['LANG>>'.$shortlang]) {
|
1377 |
+
$zp = $this->CSS['LANG>>'.$shortlang];
|
1378 |
+
if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
|
1379 |
+
if (is_array($zp)) {
|
1380 |
+
$p = array_merge($p,$zp);
|
1381 |
+
$this->_mergeBorders($p,$zp);
|
1382 |
+
}
|
1383 |
+
}
|
1384 |
+
}
|
1385 |
Â
//===============================================
|
1386 |
Â
// STYLESHEET ID e.g. #smallone{} #redletter{}
|
1387 |
Â
if (isset($attr['ID']) && isset($this->CSS['ID>>'.$attr['ID']]) && $this->CSS['ID>>'.$attr['ID']]) {
|
1392 |
Â
$this->_mergeBorders($p,$zp);
|
1393 |
Â
}
|
1394 |
Â
}
|
1395 |
+
|
1396 |
Â
//===============================================
|
1397 |
Â
// STYLESHEET CLASS e.g. p.smallone{} div.redletter{}
|
1398 |
Â
foreach($classes AS $class) {
|
1405 |
Â
}
|
1406 |
Â
}
|
1407 |
Â
//===============================================
|
1408 |
+
// STYLESHEET LANG e.g. [lang=fr]{} or :lang(fr)
|
1409 |
+
if (isset($attr['LANG'])) {
|
1410 |
+
if (isset($this->CSS[$tag.'>>LANG>>'.$attr['LANG']]) && $this->CSS[$tag.'>>LANG>>'.$attr['LANG']]) {
|
1411 |
+
$zp = $this->CSS[$tag.'>>LANG>>'.$attr['LANG']];
|
1412 |
+
if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
|
1413 |
+
if (is_array($zp)) {
|
1414 |
+
$p = array_merge($p,$zp);
|
1415 |
+
$this->_mergeBorders($p,$zp);
|
1416 |
+
}
|
1417 |
+
}
|
1418 |
+
else if (isset($this->CSS[$tag.'>>LANG>>'.$shortlang]) && $this->CSS[$tag.'>>LANG>>'.$shortlang]) {
|
1419 |
+
$zp = $this->CSS[$tag.'>>LANG>>'.$shortlang];
|
1420 |
+
if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
|
1421 |
+
if (is_array($zp)) {
|
1422 |
+
$p = array_merge($p,$zp);
|
1423 |
+
$this->_mergeBorders($p,$zp);
|
1424 |
+
}
|
1425 |
+
}
|
1426 |
+
}
|
1427 |
+
//===============================================
|
1428 |
Â
// STYLESHEET CLASS e.g. p#smallone{} div#redletter{}
|
1429 |
Â
if (isset($attr['ID']) && isset($this->CSS[$tag.'>>ID>>'.$attr['ID']]) && $this->CSS[$tag.'>>ID>>'.$attr['ID']]) {
|
1430 |
Â
$zp = $this->CSS[$tag.'>>ID>>'.$attr['ID']];
|
1437 |
Â
//===============================================
|
1438 |
Â
// Cascaded e.g. div.class p only works for block level
|
1439 |
Â
if ($inherit == 'BLOCK') {
|
1440 |
+
if (isset($this->mpdf->blk[$this->mpdf->blklvl-1])) { // mPDF 6
|
1441 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'][$tag], $p);
|
1442 |
Â
foreach($classes AS $class) {
|
1443 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS']['CLASS>>'.$class], $p);
|
1447 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'][$tag.'>>CLASS>>'.$class], $p);
|
1448 |
Â
}
|
1449 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'][$tag.'>>ID>>'.$attr['ID']], $p);
|
1450 |
+
}
|
1451 |
Â
}
|
1452 |
Â
else if ($inherit == 'INLINE') {
|
1453 |
Â
$this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'][$tag], $p);
|
1462 |
Â
}
|
1463 |
Â
/*-- TABLES --*/
|
1464 |
Â
else if ($inherit == 'TOPTABLE' || $inherit == 'TABLE') { // NB looks at $this->tablecascadeCSS-1 for cascading CSS
|
1465 |
+
if (isset($this->tablecascadeCSS[$this->tbCSSlvl-1])) { // mPDF 6
|
1466 |
Â
// false, 9 = don't check for 'depth' and do set border dominance
|
1467 |
Â
$this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1][$tag], $p, false, 9);
|
1468 |
Â
foreach($classes AS $class) {
|
1479 |
Â
$tfnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) : 0);
|
1480 |
Â
if ($this->mpdf->tabletfoot) { $row -= $thnr; }
|
1481 |
Â
else if (!$this->mpdf->tablethead) { $row -= ($thnr + $tfnr); }
|
1482 |
+
if (preg_match('/(([\-+]?\d*)?N([\-+]\d+)?|[\-+]?\d+|ODD|EVEN)/',$m[1],$a)) { // mPDF 5.7.4
|
1483 |
+
$select = $this->_nthchild($a, $row);
|
Â
|
|
Â
|
|
1484 |
Â
}
|
1485 |
Â
}
|
1486 |
Â
else if ($tag=='TD' || $tag=='TH') {
|
1487 |
+
if (preg_match('/(([\-+]?\d*)?N([\-+]\d+)?|[\-+]?\d+|ODD|EVEN)/',$m[1],$a)) { // mPDF 5.7.4
|
1488 |
+
$select = $this->_nthchild($a, $this->mpdf->col);
|
Â
|
|
Â
|
|
1489 |
Â
}
|
1490 |
Â
}
|
1491 |
Â
if ($select) {
|
1493 |
Â
}
|
1494 |
Â
}
|
1495 |
Â
}
|
1496 |
+
}
|
1497 |
Â
}
|
1498 |
Â
$this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1]['ID>>'.$attr['ID']], $p, false, 9);
|
1499 |
Â
foreach($classes AS $class) {
|
1503 |
Â
}
|
1504 |
Â
/*-- END TABLES --*/
|
1505 |
Â
//===============================================
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1506 |
Â
//===============================================
|
1507 |
Â
// INLINE STYLE e.g. style="CSS:property"
|
1508 |
Â
if (isset($attr['STYLE'])) {
|
1515 |
Â
}
|
1516 |
Â
//===============================================
|
1517 |
Â
//===============================================
|
1518 |
+
return $p;
|
1519 |
+
}
|
1520 |
+
|
1521 |
+
|
1522 |
+
// Convert inline Properties back to CSS
|
1523 |
+
function inlinePropsToCSS($bilp, &$p) {
|
1524 |
+
if (isset($bilp[ 'family' ]) && $bilp[ 'family' ]) { $p['FONT-FAMILY'] = $bilp[ 'family' ]; }
|
1525 |
+
if (isset($bilp[ 'I' ]) && $bilp[ 'I' ]) { $p['FONT-STYLE'] = 'italic'; }
|
1526 |
+
if (isset($bilp[ 'sizePt' ]) && $bilp[ 'sizePt' ]) { $p['FONT-SIZE'] = $bilp[ 'sizePt' ] . 'pt'; }
|
1527 |
+
if (isset($bilp[ 'B' ]) && $bilp[ 'B' ]) { $p['FONT-WEIGHT'] = 'bold'; }
|
1528 |
+
if (isset($bilp[ 'colorarray' ]) && $bilp[ 'colorarray' ]) {
|
1529 |
+
$cor = $bilp[ 'colorarray' ];
|
1530 |
+
$p['COLOR'] = $this->mpdf->_colAtoString($cor);
|
1531 |
+
}
|
1532 |
+
if (isset($bilp[ 'lSpacingCSS' ]) && $bilp[ 'lSpacingCSS' ]) { $p['LETTER-SPACING'] = $bilp[ 'lSpacingCSS' ]; }
|
1533 |
+
if (isset($bilp[ 'wSpacingCSS' ]) && $bilp[ 'wSpacingCSS' ]) { $p['WORD-SPACING'] = $bilp[ 'wSpacingCSS' ]; }
|
1534 |
+
|
1535 |
+
if (isset($bilp[ 'textparam' ]) && $bilp[ 'textparam' ]) {
|
1536 |
+
if (isset($bilp[ 'textparam' ]['hyphens'])) {
|
1537 |
+
if ($bilp[ 'textparam' ]['hyphens']==2) { $p['HYPHENS'] = 'none'; }
|
1538 |
+
if ($bilp[ 'textparam' ]['hyphens']==1) { $p['HYPHENS'] = 'auto'; }
|
1539 |
+
if ($bilp[ 'textparam' ]['hyphens']==0) { $p['HYPHENS'] = 'manual'; }
|
1540 |
+
}
|
1541 |
+
if (isset($bilp[ 'textparam' ]['outline-s']) && !$bilp[ 'textparam' ]['outline-s']) { $p['TEXT-OUTLINE'] = 'none'; }
|
1542 |
+
if (isset($bilp[ 'textparam' ]['outline-COLOR']) && $bilp[ 'textparam' ]['outline-COLOR']) { $p['TEXT-OUTLINE-COLOR'] = $this->mpdf->_colAtoString($bilp[ 'textparam' ]['outline-COLOR']); }
|
1543 |
+
if (isset($bilp[ 'textparam' ]['outline-WIDTH']) && $bilp[ 'textparam' ]['outline-WIDTH']) { $p['TEXT-OUTLINE-WIDTH'] = $bilp[ 'textparam' ]['outline-WIDTH'].'mm'; }
|
1544 |
+
}
|
1545 |
+
|
1546 |
+
if (isset($bilp[ 'textvar' ]) && $bilp[ 'textvar' ]) {
|
1547 |
+
// CSS says text-decoration is not inherited, but IE7 does??
|
1548 |
+
if ($bilp[ 'textvar' ] & FD_LINETHROUGH) {
|
1549 |
+
if ($bilp[ 'textvar' ] & FD_UNDERLINE) { $p['TEXT-DECORATION'] = 'underline line-through'; }
|
1550 |
+
else { $p['TEXT-DECORATION'] = 'line-through'; }
|
1551 |
+
}
|
1552 |
+
else if ($bilp[ 'textvar' ] & FD_UNDERLINE) { $p['TEXT-DECORATION'] = 'underline'; }
|
1553 |
+
else { $p['TEXT-DECORATION'] = 'none'; }
|
1554 |
+
|
1555 |
+
if ($bilp[ 'textvar' ] & FA_SUPERSCRIPT) { $p['VERTICAL-ALIGN'] = 'super'; }
|
1556 |
+
else if ($bilp[ 'textvar' ] & FA_SUBSCRIPT) { $p['VERTICAL-ALIGN'] = 'sub'; }
|
1557 |
+
else { $p['VERTICAL-ALIGN'] = 'baseline'; }
|
1558 |
+
|
1559 |
+
if ($bilp[ 'textvar' ] & FT_CAPITALIZE) { $p['TEXT-TRANSFORM'] = 'capitalize'; }
|
1560 |
+
else if ($bilp[ 'textvar' ] & FT_UPPERCASE) { $p['TEXT-TRANSFORM'] = 'uppercase'; }
|
1561 |
+
else if ($bilp[ 'textvar' ] & FT_LOWERCASE) { $p['TEXT-TRANSFORM'] = 'lowercase'; }
|
1562 |
+
else { $p['TEXT-TRANSFORM'] = 'none'; }
|
1563 |
+
|
1564 |
+
if ($bilp[ 'textvar' ] & FC_KERNING) { $p['FONT-KERNING'] = 'normal'; } // ignore 'auto' as default already applied
|
1565 |
+
//if (isset($bilp[ 'OTLtags' ]) && $bilp[ 'OTLtags' ]['Plus'] contains 'kern'
|
1566 |
+
else { $p['FONT-KERNING'] = 'none'; }
|
1567 |
+
|
1568 |
+
if ($bilp[ 'textvar' ] & FA_SUPERSCRIPT) { $p['FONT-VARIANT-POSITION'] = 'super'; }
|
1569 |
+
//if (isset($bilp[ 'OTLtags' ]) && $bilp[ 'OTLtags' ]['Plus'] contains 'sups' / 'subs'
|
1570 |
+
else if ($bilp[ 'textvar' ] & FA_SUBSCRIPT) { $p['FONT-VARIANT-POSITION'] = 'sub'; }
|
1571 |
+
else { $p['FONT-VARIANT-POSITION'] = 'normal'; }
|
1572 |
+
|
1573 |
+
if ($bilp[ 'textvar' ] & FC_SMALLCAPS) { $p['FONT-VARIANT-CAPS'] = 'small-caps'; }
|
1574 |
+
}
|
1575 |
+
if (isset($bilp[ 'fontLanguageOverride' ])) {
|
1576 |
+
if ($bilp[ 'fontLanguageOverride' ]) { $p['FONT-LANGUAGE-OVERRIDE'] = $bilp[ 'fontLanguageOverride' ]; }
|
1577 |
+
else { $p['FONT-LANGUAGE-OVERRIDE'] = 'normal'; }
|
1578 |
+
}
|
1579 |
+
// All the variations of font-variant-* we are going to set as font-feature-settings...
|
1580 |
+
if (isset($bilp[ 'OTLtags' ]) && $bilp[ 'OTLtags' ]) {
|
1581 |
+
$ffs = array();
|
1582 |
+
if (isset($bilp['OTLtags']['Minus']) && $bilp['OTLtags']['Minus']) {
|
1583 |
+
$f = preg_split('/\s+/', trim($bilp['OTLtags']['Minus']));
|
1584 |
+
foreach($f AS $ff) { $ffs[] = "'".$ff."' 0"; }
|
1585 |
+
}
|
1586 |
+
if (isset($bilp['OTLtags']['FFMinus']) && $bilp['OTLtags']['FFMinus']) {
|
1587 |
+
$f = preg_split('/\s+/', trim($bilp['OTLtags']['FFMinus']));
|
1588 |
+
foreach($f AS $ff) { $ffs[] = "'".$ff."' 0"; }
|
1589 |
+
}
|
1590 |
+
if (isset($bilp['OTLtags']['Plus']) && $bilp['OTLtags']['Plus']) {
|
1591 |
+
$f = preg_split('/\s+/', trim($bilp['OTLtags']['Plus']));
|
1592 |
+
foreach($f AS $ff) { $ffs[] = "'".$ff."' 1"; }
|
1593 |
+
}
|
1594 |
+
if (isset($bilp['OTLtags']['FFPlus']) && $bilp['OTLtags']['FFPlus']) { // May contain numeric value e.g. salt4
|
1595 |
+
$f = preg_split('/\s+/', trim($bilp['OTLtags']['FFPlus']));
|
1596 |
+
foreach($f AS $ff) {
|
1597 |
+
if (strlen($ff)>4) { $ffs[] = "'".substr($ff,0,4)."' ".substr($ff,4); }
|
1598 |
+
else { $ffs[] = "'".$ff."' 1"; }
|
1599 |
+
}
|
1600 |
Â
}
|
1601 |
+
$p['FONT-FEATURE-SETTINGS'] = implode(', ', $ffs);
|
1602 |
Â
}
|
1603 |
+
|
1604 |
+
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1605 |
Â
}
|
1606 |
Â
|
1607 |
Â
function PreviewBlockCSS($tag,$attr) {
|
1679 |
Â
}
|
1680 |
Â
|
1681 |
Â
|
1682 |
+
// mPDF 5.7.4 nth-child
|
1683 |
+
function _nthchild($f, $c) {
|
1684 |
+
// $f is formual e.g. 2N+1 spilt into a preg_match array
|
1685 |
+
// $c is the comparator value e.g row or column number
|
1686 |
+
$c += 1;
|
1687 |
+
$select = false;
|
1688 |
+
$a=1; $b=1;
|
1689 |
+
if ($f[0]=='ODD') { $a=2; $b=1; }
|
1690 |
+
else if ($f[0]=='EVEN') { $a=2; $b=0; }
|
1691 |
+
else if (count($f)==2) { $a=0; $b=$f[1]+0; } // e.g. (+6)
|
1692 |
+
else if (count($f)==3) { // e.g. (2N)
|
1693 |
+
if ($f[2]=='') { $a=1; }
|
1694 |
+
else if ($f[2]=='-') { $a=-1; }
|
1695 |
+
else { $a=$f[2]+0; }
|
1696 |
+
$b=0;
|
1697 |
+
}
|
1698 |
+
else if (count($f)==4) { // e.g. (2N+6)
|
1699 |
+
if ($f[2]=='') { $a=1; }
|
1700 |
+
else if ($f[2]=='-') { $a=-1; }
|
1701 |
+
else { $a=$f[2]+0; }
|
1702 |
+
$b=$f[3]+0;
|
1703 |
+
}
|
1704 |
+
else { return false; }
|
1705 |
+
if ($a>0) {
|
1706 |
+
if (((($c % $a) - $b) % $a) == 0 && $c >= $b) { $select = true; }
|
1707 |
+
}
|
1708 |
+
else if ($a==0) {
|
1709 |
+
if ($c == $b) { $select = true; }
|
1710 |
+
}
|
1711 |
+
else { // if ($a<0)
|
1712 |
+
if (((($c % $a) - $b) % $a) == 0 && $c <= $b) { $select = true; }
|
1713 |
+
}
|
1714 |
+
return $select;
|
1715 |
+
}
|
1716 |
Â
|
1717 |
Â
|
1718 |
Â
|
lib/mpdf/classes/desktop.ini
ADDED
@@ -0,0 +1,4 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
[ViewState]
|
2 |
+
Mode=
|
3 |
+
Vid=
|
4 |
+
FolderType=Documents
|
{mpdf → lib/mpdf}/classes/directw.php
RENAMED
@@ -36,9 +36,6 @@ function Write($h,$txt,$currentx=0,$link='',$directionality='ltr',$align='') {
|
|
36 |
Â
$nl=1;
|
37 |
Â
if (!$this->mpdf->usingCoreFont) {
|
38 |
Â
if (preg_match("/([".$this->mpdf->pregRTLchars."])/u", $txt)) { $this->mpdf->biDirectional = true; } // *RTL*
|
39 |
-
$checkCursive=false;
|
40 |
-
if ($this->mpdf->biDirectional) { $checkCursive=true; } // *RTL*
|
41 |
-
else if (isset($this->mpdf->CurrentFont['indic']) && $this->mpdf->CurrentFont['indic']) { $checkCursive=true; } // *INDIC*
|
42 |
Â
while($i<$nb) {
|
43 |
Â
//Get next character
|
44 |
Â
$c = mb_substr($s,$i,1,$this->mpdf->mb_enc );
|
@@ -47,8 +44,6 @@ function Write($h,$txt,$currentx=0,$link='',$directionality='ltr',$align='') {
|
|
47 |
Â
$this->mpdf->ResetSpacing();
|
48 |
Â
//Explicit line break
|
49 |
Â
$tmp = rtrim(mb_substr($s,$j,$i-$j,$this->mpdf->mb_enc));
|
50 |
-
if ($directionality == 'rtl' && $align == 'J') { $align = 'R'; } // *RTL*
|
51 |
-
$this->mpdf->magic_reverse_dir($tmp, true, $directionality); // *RTL*
|
52 |
Â
$this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link);
|
53 |
Â
$i++;
|
54 |
Â
$sep = -1;
|
@@ -83,14 +78,10 @@ function Write($h,$txt,$currentx=0,$link='',$directionality='ltr',$align='') {
|
|
83 |
Â
}
|
84 |
Â
if($i==$j) { $i++; }
|
85 |
Â
$tmp = rtrim(mb_substr($s,$j,$i-$j,$this->mpdf->mb_enc));
|
86 |
-
if ($directionality == 'rtl' && $align == 'J') { $align = 'R'; } // *RTL*
|
87 |
-
$this->mpdf->magic_reverse_dir($tmp, true, $directionality); // *RTL*
|
88 |
Â
$this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link);
|
89 |
Â
}
|
90 |
Â
else {
|
91 |
Â
$tmp = rtrim(mb_substr($s,$j,$sep-$j,$this->mpdf->mb_enc));
|
92 |
-
if ($directionality == 'rtl' && $align == 'J') { $align = 'R'; } // *RTL*
|
93 |
-
$this->mpdf->magic_reverse_dir($tmp, true, $directionality); // *RTL*
|
94 |
Â
|
95 |
Â
if($align=='J') {
|
96 |
Â
//////////////////////////////////////////
|
@@ -102,9 +93,8 @@ function Write($h,$txt,$currentx=0,$link='',$directionality='ltr',$align='') {
|
|
102 |
Â
$nb_carac = mb_strlen( $tmp , $this->mpdf->mb_enc ) ;
|
103 |
Â
$nb_spaces = mb_substr_count( $tmp ,' ', $this->mpdf->mb_enc ) ;
|
104 |
Â
$inclCursive=false;
|
105 |
-
if ($
|
106 |
-
if (preg_match("/([".$this->mpdf->
|
107 |
-
if (preg_match("/([".$this->mpdf->pregHIchars.$this->mpdf->pregBNchars.$this->mpdf->pregPAchars."])/u", $tmp)) { $inclCursive = true; } // *INDIC*
|
108 |
Â
}
|
109 |
Â
list($charspacing,$ws) = $this->mpdf->GetJspacing($nb_carac,$nb_spaces,((($w-2) - $len_ligne) * _MPDFK),$inclCursive);
|
110 |
Â
$this->mpdf->SetSpacing($charspacing,$ws);
|
@@ -177,8 +167,8 @@ function Write($h,$txt,$currentx=0,$link='',$directionality='ltr',$align='') {
|
|
177 |
Â
$tmp = substr($s, $j, $sep-$j);
|
178 |
Â
if($align=='J') {
|
179 |
Â
//////////////////////////////////////////
|
180 |
-
// JUSTIFY J using Unicode fonts
|
181 |
-
// WORD SPACING
|
182 |
Â
// Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
|
183 |
Â
$tmp = str_replace(chr(160),chr(32),$tmp );
|
184 |
Â
$len_ligne = $this->mpdf->GetStringWidth($tmp );
|
@@ -214,18 +204,14 @@ function Write($h,$txt,$currentx=0,$link='',$directionality='ltr',$align='') {
|
|
214 |
Â
if ($currentx != 0) $this->mpdf->x=$currentx;
|
215 |
Â
else $this->mpdf->x=$this->mpdf->lMargin;
|
216 |
Â
if ($this->mpdf->usingCoreFont) { $tmp = substr($s,$j,$i-$j); }
|
217 |
-
else {
|
218 |
-
$tmp = mb_substr($s,$j,$i-$j,$this->mpdf->mb_enc);
|
219 |
-
if ($directionality == 'rtl' && $align == 'J') { $align = 'R'; } // *RTL*
|
220 |
-
$this->mpdf->magic_reverse_dir($tmp, true, $directionality); // *RTL*
|
221 |
-
}
|
222 |
Â
$this->mpdf->Cell($w,$h,$tmp,0,0,$align,$fill,$link);
|
223 |
Â
}
|
224 |
Â
}
|
225 |
Â
|
226 |
Â
|
227 |
-
function CircularText($x, $y, $r, $text, $align='top', $fontfamily='', $fontsizePt=0, $fontstyle='', $kerning=120, $fontwidth=100, $divider='') {
|
228 |
-
if ($
|
229 |
Â
$kerning/=100;
|
230 |
Â
$fontwidth/=100;
|
231 |
Â
if($kerning==0) $this->mpdf->Error('Please use values unequal to zero for kerning (CircularText)');
|
@@ -233,7 +219,6 @@ function CircularText($x, $y, $r, $text, $align='top', $fontfamily='', $fontsize
|
|
233 |
Â
$text=str_replace("\r",'',$text);
|
234 |
Â
//circumference
|
235 |
Â
$u=($r*2)*M_PI;
|
236 |
-
// mPDF 5.5.23
|
237 |
Â
$checking = true;
|
238 |
Â
$autoset = false;
|
239 |
Â
while($checking) {
|
@@ -352,22 +337,40 @@ function CircularText($x, $y, $r, $text, $align='top', $fontfamily='', $fontsize
|
|
352 |
Â
}
|
353 |
Â
}
|
354 |
Â
|
355 |
-
function Shaded_box( $text,$font='',$fontstyle='B',$szfont='',$width='70%',$style='DF',$radius=2.5,$fill='#FFFFFF',$color='#000000',$pad=2 )
|
356 |
-
|
357 |
-
// F (shading - no line),S (line, no shading),DF (both)
|
358 |
Â
if (!$font) { $font= $this->mpdf->default_font; }
|
359 |
Â
if (!$szfont) { $szfont = ($this->mpdf->default_font_size * 1.8); }
|
360 |
Â
|
Â
|
|
Â
|
|
Â
|
|
361 |
Â
$text = $this->mpdf->purify_utf8_text($text);
|
362 |
Â
if ($this->mpdf->text_input_as_HTML) {
|
363 |
Â
$text = $this->mpdf->all_entities_to_utf8($text);
|
364 |
Â
}
|
365 |
Â
if ($this->mpdf->usingCoreFont) { $text = mb_convert_encoding($text,$this->mpdf->mb_enc,'UTF-8'); }
|
Â
|
|
Â
|
|
366 |
Â
// DIRECTIONALITY
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
$
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
371 |
Â
if (!$width) { $width = $this->mpdf->pgwidth; } else { $width=$this->mpdf->ConvertSize($width,$this->mpdf->pgwidth); }
|
372 |
Â
$midpt = $this->mpdf->lMargin+($this->mpdf->pgwidth/2);
|
373 |
Â
$r1 = $midpt-($width/2); //($this->mpdf->w / 2) - 40;
|
@@ -380,13 +383,14 @@ function Shaded_box( $text,$font='',$fontstyle='B',$szfont='',$width='70%',$styl
|
|
380 |
Â
|
381 |
Â
while ( $loop == 0 )
|
382 |
Â
{
|
383 |
-
$this->mpdf->SetFont( $font, $fontstyle, $szfont );
|
384 |
-
$sz = $this->mpdf->GetStringWidth( $text );
|
385 |
Â
if ( ($r1+$sz) > $r2 )
|
386 |
Â
$szfont --;
|
387 |
Â
else
|
388 |
Â
$loop ++;
|
389 |
Â
}
|
Â
|
|
390 |
Â
|
391 |
Â
$y2 = $this->mpdf->FontSize+($pad*2);
|
392 |
Â
|
@@ -397,7 +401,7 @@ function Shaded_box( $text,$font='',$fontstyle='B',$szfont='',$width='70%',$styl
|
|
397 |
Â
$this->mpdf->SetTColor($tc);
|
398 |
Â
$this->mpdf->RoundedRect($r1, $y1, ($r2 - $r1), $y2, $radius, $style);
|
399 |
Â
$this->mpdf->SetX( $r1);
|
400 |
-
$this->mpdf->Cell($r2-$r1, $y2, $text, 0, 1, "C" );
|
401 |
Â
$this->mpdf->SetY($y1+$y2+2); // +2 = mm margin below shaded box
|
402 |
Â
$this->mpdf->Reset();
|
403 |
Â
}
|
36 |
Â
$nl=1;
|
37 |
Â
if (!$this->mpdf->usingCoreFont) {
|
38 |
Â
if (preg_match("/([".$this->mpdf->pregRTLchars."])/u", $txt)) { $this->mpdf->biDirectional = true; } // *RTL*
|
Â
|
|
Â
|
|
Â
|
|
39 |
Â
while($i<$nb) {
|
40 |
Â
//Get next character
|
41 |
Â
$c = mb_substr($s,$i,1,$this->mpdf->mb_enc );
|
44 |
Â
$this->mpdf->ResetSpacing();
|
45 |
Â
//Explicit line break
|
46 |
Â
$tmp = rtrim(mb_substr($s,$j,$i-$j,$this->mpdf->mb_enc));
|
Â
|
|
Â
|
|
47 |
Â
$this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link);
|
48 |
Â
$i++;
|
49 |
Â
$sep = -1;
|
78 |
Â
}
|
79 |
Â
if($i==$j) { $i++; }
|
80 |
Â
$tmp = rtrim(mb_substr($s,$j,$i-$j,$this->mpdf->mb_enc));
|
Â
|
|
Â
|
|
81 |
Â
$this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link);
|
82 |
Â
}
|
83 |
Â
else {
|
84 |
Â
$tmp = rtrim(mb_substr($s,$j,$sep-$j,$this->mpdf->mb_enc));
|
Â
|
|
Â
|
|
85 |
Â
|
86 |
Â
if($align=='J') {
|
87 |
Â
//////////////////////////////////////////
|
93 |
Â
$nb_carac = mb_strlen( $tmp , $this->mpdf->mb_enc ) ;
|
94 |
Â
$nb_spaces = mb_substr_count( $tmp ,' ', $this->mpdf->mb_enc ) ;
|
95 |
Â
$inclCursive=false;
|
96 |
+
if (isset($this->mpdf->CurrentFont['useOTL']) && $this->mpdf->CurrentFont['useOTL']) {
|
97 |
+
if (preg_match("/([".$this->mpdf->pregCURSchars."])/u", $tmp)) { $inclCursive = true; }
|
Â
|
|
98 |
Â
}
|
99 |
Â
list($charspacing,$ws) = $this->mpdf->GetJspacing($nb_carac,$nb_spaces,((($w-2) - $len_ligne) * _MPDFK),$inclCursive);
|
100 |
Â
$this->mpdf->SetSpacing($charspacing,$ws);
|
167 |
Â
$tmp = substr($s, $j, $sep-$j);
|
168 |
Â
if($align=='J') {
|
169 |
Â
//////////////////////////////////////////
|
170 |
+
// JUSTIFY J using Unicode fonts
|
171 |
+
// WORD SPACING is not fully supported for complex scripts
|
172 |
Â
// Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
|
173 |
Â
$tmp = str_replace(chr(160),chr(32),$tmp );
|
174 |
Â
$len_ligne = $this->mpdf->GetStringWidth($tmp );
|
204 |
Â
if ($currentx != 0) $this->mpdf->x=$currentx;
|
205 |
Â
else $this->mpdf->x=$this->mpdf->lMargin;
|
206 |
Â
if ($this->mpdf->usingCoreFont) { $tmp = substr($s,$j,$i-$j); }
|
207 |
+
else { $tmp = mb_substr($s,$j,$i-$j,$this->mpdf->mb_enc); }
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
208 |
Â
$this->mpdf->Cell($w,$h,$tmp,0,0,$align,$fill,$link);
|
209 |
Â
}
|
210 |
Â
}
|
211 |
Â
|
212 |
Â
|
213 |
+
function CircularText($x, $y, $r, $text, $align='top', $fontfamily='', $fontsizePt=0, $fontstyle='', $kerning=120, $fontwidth=100, $divider='') {
|
214 |
+
if ($fontfamily || $fontstyle || $fontsizePt) $this->mpdf->SetFont($fontfamily,$fontstyle,$fontsizePt);
|
215 |
Â
$kerning/=100;
|
216 |
Â
$fontwidth/=100;
|
217 |
Â
if($kerning==0) $this->mpdf->Error('Please use values unequal to zero for kerning (CircularText)');
|
219 |
Â
$text=str_replace("\r",'',$text);
|
220 |
Â
//circumference
|
221 |
Â
$u=($r*2)*M_PI;
|
Â
|
|
222 |
Â
$checking = true;
|
223 |
Â
$autoset = false;
|
224 |
Â
while($checking) {
|
337 |
Â
}
|
338 |
Â
}
|
339 |
Â
|
340 |
+
function Shaded_box( $text,$font='',$fontstyle='B',$szfont='',$width='70%',$style='DF',$radius=2.5,$fill='#FFFFFF',$color='#000000',$pad=2 ) {
|
341 |
+
// F (shading - no line),S (line, no shading),DF (both)
|
Â
|
|
342 |
Â
if (!$font) { $font= $this->mpdf->default_font; }
|
343 |
Â
if (!$szfont) { $szfont = ($this->mpdf->default_font_size * 1.8); }
|
344 |
Â
|
345 |
+
$text = ' '.$text.' ';
|
346 |
+
$this->mpdf->SetFont( $font, $fontstyle, $szfont, false );
|
347 |
+
|
348 |
Â
$text = $this->mpdf->purify_utf8_text($text);
|
349 |
Â
if ($this->mpdf->text_input_as_HTML) {
|
350 |
Â
$text = $this->mpdf->all_entities_to_utf8($text);
|
351 |
Â
}
|
352 |
Â
if ($this->mpdf->usingCoreFont) { $text = mb_convert_encoding($text,$this->mpdf->mb_enc,'UTF-8'); }
|
353 |
+
|
354 |
+
|
355 |
Â
// DIRECTIONALITY
|
356 |
+
if (preg_match("/([".$this->mpdf->pregRTLchars."])/u", $text)) { $this->mpdf->biDirectional = true; } // *RTL*
|
357 |
+
|
358 |
+
$textvar = 0;
|
359 |
+
$save_OTLtags = $this->mpdf->OTLtags;
|
360 |
+
$this->mpdf->OTLtags = array();
|
361 |
+
if ($this->mpdf->useKerning) {
|
362 |
+
if ($this->mpdf->CurrentFont['haskernGPOS']) { $this->mpdf->OTLtags['Plus'] .= ' kern'; }
|
363 |
+
else { $textvar = ($textvar | FC_KERNING); }
|
364 |
+
}
|
365 |
+
// Use OTL OpenType Table Layout - GSUB & GPOS
|
366 |
+
if (isset($this->mpdf->CurrentFont['useOTL']) && $this->mpdf->CurrentFont['useOTL']) {
|
367 |
+
$text = $this->mpdf->otl->applyOTL($text, $this->mpdf->CurrentFont['useOTL']);
|
368 |
+
$OTLdata = $this->mpdf->otl->OTLdata;
|
369 |
+
}
|
370 |
+
$this->mpdf->OTLtags = $save_OTLtags ;
|
371 |
+
|
372 |
+
$this->mpdf->magic_reverse_dir($text, $this->mpdf->directionality, $OTLdata);
|
373 |
+
|
374 |
Â
if (!$width) { $width = $this->mpdf->pgwidth; } else { $width=$this->mpdf->ConvertSize($width,$this->mpdf->pgwidth); }
|
375 |
Â
$midpt = $this->mpdf->lMargin+($this->mpdf->pgwidth/2);
|
376 |
Â
$r1 = $midpt-($width/2); //($this->mpdf->w / 2) - 40;
|
383 |
Â
|
384 |
Â
while ( $loop == 0 )
|
385 |
Â
{
|
386 |
+
$this->mpdf->SetFont( $font, $fontstyle, $szfont, false );
|
387 |
+
$sz = $this->mpdf->GetStringWidth( $text, true, $OTLdata, $textvar );
|
388 |
Â
if ( ($r1+$sz) > $r2 )
|
389 |
Â
$szfont --;
|
390 |
Â
else
|
391 |
Â
$loop ++;
|
392 |
Â
}
|
393 |
+
$this->mpdf->SetFont( $font, $fontstyle, $szfont, true, true );
|
394 |
Â
|
395 |
Â
$y2 = $this->mpdf->FontSize+($pad*2);
|
396 |
Â
|
401 |
Â
$this->mpdf->SetTColor($tc);
|
402 |
Â
$this->mpdf->RoundedRect($r1, $y1, ($r2 - $r1), $y2, $radius, $style);
|
403 |
Â
$this->mpdf->SetX( $r1);
|
404 |
+
$this->mpdf->Cell($r2-$r1, $y2, $text, 0, 1, "C",0,'',0,0,0,'M', 0, false, $OTLdata, $textvar );
|
405 |
Â
$this->mpdf->SetY($y1+$y2+2); // +2 = mm margin below shaded box
|
406 |
Â
$this->mpdf->Reset();
|
407 |
Â
}
|
lib/mpdf/classes/gif.php
ADDED
@@ -0,0 +1,700 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
3 |
+
// 2009-12-22 Adapted for mPDF 4.2
|
4 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
5 |
+
// GIF Util - (C) 2003 Yamasoft (S/C)
|
6 |
+
// http://www.yamasoft.com
|
7 |
+
// All Rights Reserved
|
8 |
+
// This file can be freely copied, distributed, modified, updated by anyone under the only
|
9 |
+
// condition to leave the original address (Yamasoft, http://www.yamasoft.com) and this header.
|
10 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
11 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
12 |
+
// 2009-12-22 Adapted INB
|
13 |
+
// Functions calling functionname($x, $len = 0) were not working on PHP5.1.5 as pass by reference
|
14 |
+
// All edited to $len = 0; then call function.
|
15 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
16 |
+
|
17 |
+
|
18 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
19 |
+
|
20 |
+
class CGIFLZW
|
21 |
+
{
|
22 |
+
var $MAX_LZW_BITS;
|
23 |
+
var $Fresh, $CodeSize, $SetCodeSize, $MaxCode, $MaxCodeSize, $FirstCode, $OldCode;
|
24 |
+
var $ClearCode, $EndCode, $Next, $Vals, $Stack, $sp, $Buf, $CurBit, $LastBit, $Done, $LastByte;
|
25 |
+
|
26 |
+
///////////////////////////////////////////////////////////////////////////
|
27 |
+
|
28 |
+
// CONSTRUCTOR
|
29 |
+
function CGIFLZW()
|
30 |
+
{
|
31 |
+
$this->MAX_LZW_BITS = 12;
|
32 |
+
unSet($this->Next);
|
33 |
+
unSet($this->Vals);
|
34 |
+
unSet($this->Stack);
|
35 |
+
unSet($this->Buf);
|
36 |
+
|
37 |
+
$this->Next = range(0, (1 << $this->MAX_LZW_BITS) - 1);
|
38 |
+
$this->Vals = range(0, (1 << $this->MAX_LZW_BITS) - 1);
|
39 |
+
$this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1);
|
40 |
+
$this->Buf = range(0, 279);
|
41 |
+
}
|
42 |
+
|
43 |
+
///////////////////////////////////////////////////////////////////////////
|
44 |
+
|
45 |
+
function deCompress($data, &$datLen)
|
46 |
+
{
|
47 |
+
$stLen = strlen($data);
|
48 |
+
$datLen = 0;
|
49 |
+
$ret = "";
|
50 |
+
$dp = 0; // data pointer
|
51 |
+
|
52 |
+
// INITIALIZATION
|
53 |
+
$this->LZWCommandInit($data, $dp);
|
54 |
+
|
55 |
+
while(($iIndex = $this->LZWCommand($data, $dp)) >= 0) {
|
56 |
+
$ret .= chr($iIndex);
|
57 |
+
}
|
58 |
+
|
59 |
+
$datLen = $dp;
|
60 |
+
|
61 |
+
if($iIndex != -2) {
|
62 |
+
return false;
|
63 |
+
}
|
64 |
+
|
65 |
+
return $ret;
|
66 |
+
}
|
67 |
+
|
68 |
+
///////////////////////////////////////////////////////////////////////////
|
69 |
+
function LZWCommandInit(&$data, &$dp)
|
70 |
+
{
|
71 |
+
$this->SetCodeSize = ord($data[0]);
|
72 |
+
$dp += 1;
|
73 |
+
|
74 |
+
$this->CodeSize = $this->SetCodeSize + 1;
|
75 |
+
$this->ClearCode = 1 << $this->SetCodeSize;
|
76 |
+
$this->EndCode = $this->ClearCode + 1;
|
77 |
+
$this->MaxCode = $this->ClearCode + 2;
|
78 |
+
$this->MaxCodeSize = $this->ClearCode << 1;
|
79 |
+
|
80 |
+
$this->GetCodeInit($data, $dp);
|
81 |
+
|
82 |
+
$this->Fresh = 1;
|
83 |
+
for($i = 0; $i < $this->ClearCode; $i++) {
|
84 |
+
$this->Next[$i] = 0;
|
85 |
+
$this->Vals[$i] = $i;
|
86 |
+
}
|
87 |
+
|
88 |
+
for(; $i < (1 << $this->MAX_LZW_BITS); $i++) {
|
89 |
+
$this->Next[$i] = 0;
|
90 |
+
$this->Vals[$i] = 0;
|
91 |
+
}
|
92 |
+
|
93 |
+
$this->sp = 0;
|
94 |
+
return 1;
|
95 |
+
}
|
96 |
+
|
97 |
+
function LZWCommand(&$data, &$dp)
|
98 |
+
{
|
99 |
+
if($this->Fresh) {
|
100 |
+
$this->Fresh = 0;
|
101 |
+
do {
|
102 |
+
$this->FirstCode = $this->GetCode($data, $dp);
|
103 |
+
$this->OldCode = $this->FirstCode;
|
104 |
+
}
|
105 |
+
while($this->FirstCode == $this->ClearCode);
|
106 |
+
|
107 |
+
return $this->FirstCode;
|
108 |
+
}
|
109 |
+
|
110 |
+
if($this->sp > 0) {
|
111 |
+
$this->sp--;
|
112 |
+
return $this->Stack[$this->sp];
|
113 |
+
}
|
114 |
+
|
115 |
+
while(($Code = $this->GetCode($data, $dp)) >= 0) {
|
116 |
+
if($Code == $this->ClearCode) {
|
117 |
+
for($i = 0; $i < $this->ClearCode; $i++) {
|
118 |
+
$this->Next[$i] = 0;
|
119 |
+
$this->Vals[$i] = $i;
|
120 |
+
}
|
121 |
+
|
122 |
+
for(; $i < (1 << $this->MAX_LZW_BITS); $i++) {
|
123 |
+
$this->Next[$i] = 0;
|
124 |
+
$this->Vals[$i] = 0;
|
125 |
+
}
|
126 |
+
|
127 |
+
$this->CodeSize = $this->SetCodeSize + 1;
|
128 |
+
$this->MaxCodeSize = $this->ClearCode << 1;
|
129 |
+
$this->MaxCode = $this->ClearCode + 2;
|
130 |
+
$this->sp = 0;
|
131 |
+
$this->FirstCode = $this->GetCode($data, $dp);
|
132 |
+
$this->OldCode = $this->FirstCode;
|
133 |
+
|
134 |
+
return $this->FirstCode;
|
135 |
+
}
|
136 |
+
|
137 |
+
if($Code == $this->EndCode) {
|
138 |
+
return -2;
|
139 |
+
}
|
140 |
+
|
141 |
+
$InCode = $Code;
|
142 |
+
if($Code >= $this->MaxCode) {
|
143 |
+
$this->Stack[$this->sp++] = $this->FirstCode;
|
144 |
+
$Code = $this->OldCode;
|
145 |
+
}
|
146 |
+
|
147 |
+
while($Code >= $this->ClearCode) {
|
148 |
+
$this->Stack[$this->sp++] = $this->Vals[$Code];
|
149 |
+
|
150 |
+
if($Code == $this->Next[$Code]) // Circular table entry, big GIF Error!
|
151 |
+
return -1;
|
152 |
+
|
153 |
+
$Code = $this->Next[$Code];
|
154 |
+
}
|
155 |
+
|
156 |
+
$this->FirstCode = $this->Vals[$Code];
|
157 |
+
$this->Stack[$this->sp++] = $this->FirstCode;
|
158 |
+
|
159 |
+
if(($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS)) {
|
160 |
+
$this->Next[$Code] = $this->OldCode;
|
161 |
+
$this->Vals[$Code] = $this->FirstCode;
|
162 |
+
$this->MaxCode++;
|
163 |
+
|
164 |
+
if(($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS))) {
|
165 |
+
$this->MaxCodeSize *= 2;
|
166 |
+
$this->CodeSize++;
|
167 |
+
}
|
168 |
+
}
|
169 |
+
|
170 |
+
$this->OldCode = $InCode;
|
171 |
+
if($this->sp > 0) {
|
172 |
+
$this->sp--;
|
173 |
+
return $this->Stack[$this->sp];
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
return $Code;
|
178 |
+
}
|
179 |
+
|
180 |
+
///////////////////////////////////////////////////////////////////////////
|
181 |
+
|
182 |
+
function GetCodeInit(&$data, &$dp)
|
183 |
+
{
|
184 |
+
$this->CurBit = 0;
|
185 |
+
$this->LastBit = 0;
|
186 |
+
$this->Done = 0;
|
187 |
+
$this->LastByte = 2;
|
188 |
+
return 1;
|
189 |
+
}
|
190 |
+
|
191 |
+
function GetCode(&$data, &$dp)
|
192 |
+
{
|
193 |
+
if(($this->CurBit + $this->CodeSize) >= $this->LastBit) {
|
194 |
+
if($this->Done) {
|
195 |
+
if($this->CurBit >= $this->LastBit) {
|
196 |
+
// Ran off the end of my bits
|
197 |
+
return 0;
|
198 |
+
}
|
199 |
+
return -1;
|
200 |
+
}
|
201 |
+
|
202 |
+
$this->Buf[0] = $this->Buf[$this->LastByte - 2];
|
203 |
+
$this->Buf[1] = $this->Buf[$this->LastByte - 1];
|
204 |
+
|
205 |
+
$Count = ord($data[$dp]);
|
206 |
+
$dp += 1;
|
207 |
+
|
208 |
+
if($Count) {
|
209 |
+
for($i = 0; $i < $Count; $i++) {
|
210 |
+
$this->Buf[2 + $i] = ord($data[$dp+$i]);
|
211 |
+
}
|
212 |
+
$dp += $Count;
|
213 |
+
}
|
214 |
+
else {
|
215 |
+
$this->Done = 1;
|
216 |
+
}
|
217 |
+
|
218 |
+
$this->LastByte = 2 + $Count;
|
219 |
+
$this->CurBit = ($this->CurBit - $this->LastBit) + 16;
|
220 |
+
$this->LastBit = (2 + $Count) << 3;
|
221 |
+
}
|
222 |
+
|
223 |
+
$iRet = 0;
|
224 |
+
for($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++) {
|
225 |
+
$iRet |= (($this->Buf[intval($i / 8)] & (1 << ($i % 8))) != 0) << $j;
|
226 |
+
}
|
227 |
+
|
228 |
+
$this->CurBit += $this->CodeSize;
|
229 |
+
return $iRet;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
234 |
+
|
235 |
+
class CGIFCOLORTABLE
|
236 |
+
{
|
237 |
+
var $m_nColors;
|
238 |
+
var $m_arColors;
|
239 |
+
|
240 |
+
///////////////////////////////////////////////////////////////////////////
|
241 |
+
|
242 |
+
// CONSTRUCTOR
|
243 |
+
function CGIFCOLORTABLE()
|
244 |
+
{
|
245 |
+
unSet($this->m_nColors);
|
246 |
+
unSet($this->m_arColors);
|
247 |
+
}
|
248 |
+
|
249 |
+
///////////////////////////////////////////////////////////////////////////
|
250 |
+
|
251 |
+
function load($lpData, $num)
|
252 |
+
{
|
253 |
+
$this->m_nColors = 0;
|
254 |
+
$this->m_arColors = array();
|
255 |
+
|
256 |
+
for($i = 0; $i < $num; $i++) {
|
257 |
+
$rgb = substr($lpData, $i * 3, 3);
|
258 |
+
if(strlen($rgb) < 3) {
|
259 |
+
return false;
|
260 |
+
}
|
261 |
+
|
262 |
+
$this->m_arColors[] = (ord($rgb[2]) << 16) + (ord($rgb[1]) << 8) + ord($rgb[0]);
|
263 |
+
$this->m_nColors++;
|
264 |
+
}
|
265 |
+
|
266 |
+
return true;
|
267 |
+
}
|
268 |
+
|
269 |
+
///////////////////////////////////////////////////////////////////////////
|
270 |
+
|
271 |
+
function toString()
|
272 |
+
{
|
273 |
+
$ret = "";
|
274 |
+
|
275 |
+
for($i = 0; $i < $this->m_nColors; $i++) {
|
276 |
+
$ret .=
|
277 |
+
chr(($this->m_arColors[$i] & 0x000000FF)) . // R
|
278 |
+
chr(($this->m_arColors[$i] & 0x0000FF00) >> 8) . // G
|
279 |
+
chr(($this->m_arColors[$i] & 0x00FF0000) >> 16); // B
|
280 |
+
}
|
281 |
+
|
282 |
+
return $ret;
|
283 |
+
}
|
284 |
+
|
285 |
+
|
286 |
+
///////////////////////////////////////////////////////////////////////////
|
287 |
+
|
288 |
+
function colorIndex($rgb)
|
289 |
+
{
|
290 |
+
$rgb = intval($rgb) & 0xFFFFFF;
|
291 |
+
$r1 = ($rgb & 0x0000FF);
|
292 |
+
$g1 = ($rgb & 0x00FF00) >> 8;
|
293 |
+
$b1 = ($rgb & 0xFF0000) >> 16;
|
294 |
+
$idx = -1;
|
295 |
+
|
296 |
+
for($i = 0; $i < $this->m_nColors; $i++) {
|
297 |
+
$r2 = ($this->m_arColors[$i] & 0x000000FF);
|
298 |
+
$g2 = ($this->m_arColors[$i] & 0x0000FF00) >> 8;
|
299 |
+
$b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16;
|
300 |
+
$d = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1);
|
301 |
+
|
302 |
+
if(($idx == -1) || ($d < $dif)) {
|
303 |
+
$idx = $i;
|
304 |
+
$dif = $d;
|
305 |
+
}
|
306 |
+
}
|
307 |
+
|
308 |
+
return $idx;
|
309 |
+
}
|
310 |
+
}
|
311 |
+
|
312 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
313 |
+
|
314 |
+
class CGIFFILEHEADER
|
315 |
+
{
|
316 |
+
var $m_lpVer;
|
317 |
+
var $m_nWidth;
|
318 |
+
var $m_nHeight;
|
319 |
+
var $m_bGlobalClr;
|
320 |
+
var $m_nColorRes;
|
321 |
+
var $m_bSorted;
|
322 |
+
var $m_nTableSize;
|
323 |
+
var $m_nBgColor;
|
324 |
+
var $m_nPixelRatio;
|
325 |
+
var $m_colorTable;
|
326 |
+
|
327 |
+
///////////////////////////////////////////////////////////////////////////
|
328 |
+
|
329 |
+
// CONSTRUCTOR
|
330 |
+
function CGIFFILEHEADER()
|
331 |
+
{
|
332 |
+
unSet($this->m_lpVer);
|
333 |
+
unSet($this->m_nWidth);
|
334 |
+
unSet($this->m_nHeight);
|
335 |
+
unSet($this->m_bGlobalClr);
|
336 |
+
unSet($this->m_nColorRes);
|
337 |
+
unSet($this->m_bSorted);
|
338 |
+
unSet($this->m_nTableSize);
|
339 |
+
unSet($this->m_nBgColor);
|
340 |
+
unSet($this->m_nPixelRatio);
|
341 |
+
unSet($this->m_colorTable);
|
342 |
+
}
|
343 |
+
|
344 |
+
///////////////////////////////////////////////////////////////////////////
|
345 |
+
|
346 |
+
function load($lpData, &$hdrLen)
|
347 |
+
{
|
348 |
+
$hdrLen = 0;
|
349 |
+
|
350 |
+
$this->m_lpVer = substr($lpData, 0, 6);
|
351 |
+
if(($this->m_lpVer <> "GIF87a") && ($this->m_lpVer <> "GIF89a")) {
|
352 |
+
return false;
|
353 |
+
}
|
354 |
+
|
355 |
+
$this->m_nWidth = $this->w2i(substr($lpData, 6, 2));
|
356 |
+
$this->m_nHeight = $this->w2i(substr($lpData, 8, 2));
|
357 |
+
if(!$this->m_nWidth || !$this->m_nHeight) {
|
358 |
+
return false;
|
359 |
+
}
|
360 |
+
|
361 |
+
$b = ord(substr($lpData, 10, 1));
|
362 |
+
$this->m_bGlobalClr = ($b & 0x80) ? true : false;
|
363 |
+
$this->m_nColorRes = ($b & 0x70) >> 4;
|
364 |
+
$this->m_bSorted = ($b & 0x08) ? true : false;
|
365 |
+
$this->m_nTableSize = 2 << ($b & 0x07);
|
366 |
+
$this->m_nBgColor = ord(substr($lpData, 11, 1));
|
367 |
+
$this->m_nPixelRatio = ord(substr($lpData, 12, 1));
|
368 |
+
$hdrLen = 13;
|
369 |
+
|
370 |
+
if($this->m_bGlobalClr) {
|
371 |
+
$this->m_colorTable = new CGIFCOLORTABLE();
|
372 |
+
if(!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
|
373 |
+
return false;
|
374 |
+
}
|
375 |
+
$hdrLen += 3 * $this->m_nTableSize;
|
376 |
+
}
|
377 |
+
|
378 |
+
return true;
|
379 |
+
}
|
380 |
+
|
381 |
+
///////////////////////////////////////////////////////////////////////////
|
382 |
+
|
383 |
+
function w2i($str)
|
384 |
+
{
|
385 |
+
return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
|
386 |
+
}
|
387 |
+
}
|
388 |
+
|
389 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
390 |
+
|
391 |
+
class CGIFIMAGEHEADER
|
392 |
+
{
|
393 |
+
var $m_nLeft;
|
394 |
+
var $m_nTop;
|
395 |
+
var $m_nWidth;
|
396 |
+
var $m_nHeight;
|
397 |
+
var $m_bLocalClr;
|
398 |
+
var $m_bInterlace;
|
399 |
+
var $m_bSorted;
|
400 |
+
var $m_nTableSize;
|
401 |
+
var $m_colorTable;
|
402 |
+
|
403 |
+
///////////////////////////////////////////////////////////////////////////
|
404 |
+
|
405 |
+
// CONSTRUCTOR
|
406 |
+
function CGIFIMAGEHEADER()
|
407 |
+
{
|
408 |
+
unSet($this->m_nLeft);
|
409 |
+
unSet($this->m_nTop);
|
410 |
+
unSet($this->m_nWidth);
|
411 |
+
unSet($this->m_nHeight);
|
412 |
+
unSet($this->m_bLocalClr);
|
413 |
+
unSet($this->m_bInterlace);
|
414 |
+
unSet($this->m_bSorted);
|
415 |
+
unSet($this->m_nTableSize);
|
416 |
+
unSet($this->m_colorTable);
|
417 |
+
}
|
418 |
+
|
419 |
+
///////////////////////////////////////////////////////////////////////////
|
420 |
+
|
421 |
+
function load($lpData, &$hdrLen)
|
422 |
+
{
|
423 |
+
$hdrLen = 0;
|
424 |
+
|
425 |
+
$this->m_nLeft = $this->w2i(substr($lpData, 0, 2));
|
426 |
+
$this->m_nTop = $this->w2i(substr($lpData, 2, 2));
|
427 |
+
$this->m_nWidth = $this->w2i(substr($lpData, 4, 2));
|
428 |
+
$this->m_nHeight = $this->w2i(substr($lpData, 6, 2));
|
429 |
+
|
430 |
+
if(!$this->m_nWidth || !$this->m_nHeight) {
|
431 |
+
return false;
|
432 |
+
}
|
433 |
+
|
434 |
+
$b = ord($lpData{8});
|
435 |
+
$this->m_bLocalClr = ($b & 0x80) ? true : false;
|
436 |
+
$this->m_bInterlace = ($b & 0x40) ? true : false;
|
437 |
+
$this->m_bSorted = ($b & 0x20) ? true : false;
|
438 |
+
$this->m_nTableSize = 2 << ($b & 0x07);
|
439 |
+
$hdrLen = 9;
|
440 |
+
|
441 |
+
if($this->m_bLocalClr) {
|
442 |
+
$this->m_colorTable = new CGIFCOLORTABLE();
|
443 |
+
if(!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
|
444 |
+
return false;
|
445 |
+
}
|
446 |
+
$hdrLen += 3 * $this->m_nTableSize;
|
447 |
+
}
|
448 |
+
|
449 |
+
return true;
|
450 |
+
}
|
451 |
+
|
452 |
+
///////////////////////////////////////////////////////////////////////////
|
453 |
+
|
454 |
+
function w2i($str)
|
455 |
+
{
|
456 |
+
return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
|
457 |
+
}
|
458 |
+
}
|
459 |
+
|
460 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
461 |
+
|
462 |
+
class CGIFIMAGE
|
463 |
+
{
|
464 |
+
var $m_disp;
|
465 |
+
var $m_bUser;
|
466 |
+
var $m_bTrans;
|
467 |
+
var $m_nDelay;
|
468 |
+
var $m_nTrans;
|
469 |
+
var $m_lpComm;
|
470 |
+
var $m_gih;
|
471 |
+
var $m_data;
|
472 |
+
var $m_lzw;
|
473 |
+
|
474 |
+
///////////////////////////////////////////////////////////////////////////
|
475 |
+
|
476 |
+
function CGIFIMAGE()
|
477 |
+
{
|
478 |
+
unSet($this->m_disp);
|
479 |
+
unSet($this->m_bUser);
|
480 |
+
unSet($this->m_bTrans);
|
481 |
+
unSet($this->m_nDelay);
|
482 |
+
unSet($this->m_nTrans);
|
483 |
+
unSet($this->m_lpComm);
|
484 |
+
unSet($this->m_data);
|
485 |
+
$this->m_gih = new CGIFIMAGEHEADER();
|
486 |
+
$this->m_lzw = new CGIFLZW();
|
487 |
+
}
|
488 |
+
|
489 |
+
///////////////////////////////////////////////////////////////////////////
|
490 |
+
|
491 |
+
function load($data, &$datLen)
|
492 |
+
{
|
493 |
+
$datLen = 0;
|
494 |
+
|
495 |
+
while(true) {
|
496 |
+
$b = ord($data[0]);
|
497 |
+
$data = substr($data, 1);
|
498 |
+
$datLen++;
|
499 |
+
|
500 |
+
switch($b) {
|
501 |
+
case 0x21: // Extension
|
502 |
+
$len = 0;
|
503 |
+
if(!$this->skipExt($data, $len)) {
|
504 |
+
return false;
|
505 |
+
}
|
506 |
+
$datLen += $len;
|
507 |
+
break;
|
508 |
+
|
509 |
+
case 0x2C: // Image
|
510 |
+
// LOAD HEADER & COLOR TABLE
|
511 |
+
$len = 0;
|
512 |
+
if(!$this->m_gih->load($data, $len)) {
|
513 |
+
return false;
|
514 |
+
}
|
515 |
+
$data = substr($data, $len);
|
516 |
+
$datLen += $len;
|
517 |
+
|
518 |
+
// ALLOC BUFFER
|
519 |
+
$len = 0;
|
520 |
+
|
521 |
+
if(!($this->m_data = $this->m_lzw->deCompress($data, $len))) {
|
522 |
+
return false;
|
523 |
+
}
|
524 |
+
|
525 |
+
$data = substr($data, $len);
|
526 |
+
$datLen += $len;
|
527 |
+
|
528 |
+
if($this->m_gih->m_bInterlace) {
|
529 |
+
$this->deInterlace();
|
530 |
+
}
|
531 |
+
|
532 |
+
return true;
|
533 |
+
|
534 |
+
case 0x3B: // EOF
|
535 |
+
default:
|
536 |
+
return false;
|
537 |
+
}
|
538 |
+
}
|
539 |
+
return false;
|
540 |
+
}
|
541 |
+
|
542 |
+
///////////////////////////////////////////////////////////////////////////
|
543 |
+
|
544 |
+
function skipExt(&$data, &$extLen)
|
545 |
+
{
|
546 |
+
$extLen = 0;
|
547 |
+
|
548 |
+
$b = ord($data[0]);
|
549 |
+
$data = substr($data, 1);
|
550 |
+
$extLen++;
|
551 |
+
|
552 |
+
switch($b) {
|
553 |
+
case 0xF9: // Graphic Control
|
554 |
+
$b = ord($data[1]);
|
555 |
+
$this->m_disp = ($b & 0x1C) >> 2;
|
556 |
+
$this->m_bUser = ($b & 0x02) ? true : false;
|
557 |
+
$this->m_bTrans = ($b & 0x01) ? true : false;
|
558 |
+
$this->m_nDelay = $this->w2i(substr($data, 2, 2));
|
559 |
+
$this->m_nTrans = ord($data[4]);
|
560 |
+
break;
|
561 |
+
|
562 |
+
case 0xFE: // Comment
|
563 |
+
$this->m_lpComm = substr($data, 1, ord($data[0]));
|
564 |
+
break;
|
565 |
+
|
566 |
+
case 0x01: // Plain text
|
567 |
+
break;
|
568 |
+
|
569 |
+
case 0xFF: // Application
|
570 |
+
break;
|
571 |
+
}
|
572 |
+
|
573 |
+
// SKIP DEFAULT AS DEFS MAY CHANGE
|
574 |
+
$b = ord($data[0]);
|
575 |
+
$data = substr($data, 1);
|
576 |
+
$extLen++;
|
577 |
+
while($b > 0) {
|
578 |
+
$data = substr($data, $b);
|
579 |
+
$extLen += $b;
|
580 |
+
$b = ord($data[0]);
|
581 |
+
$data = substr($data, 1);
|
582 |
+
$extLen++;
|
583 |
+
}
|
584 |
+
return true;
|
585 |
+
}
|
586 |
+
|
587 |
+
///////////////////////////////////////////////////////////////////////////
|
588 |
+
|
589 |
+
function w2i($str)
|
590 |
+
{
|
591 |
+
return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
|
592 |
+
}
|
593 |
+
|
594 |
+
///////////////////////////////////////////////////////////////////////////
|
595 |
+
|
596 |
+
function deInterlace()
|
597 |
+
{
|
598 |
+
$data = $this->m_data;
|
599 |
+
|
600 |
+
for($i = 0; $i < 4; $i++) {
|
601 |
+
switch($i) {
|
602 |
+
case 0:
|
603 |
+
$s = 8;
|
604 |
+
$y = 0;
|
605 |
+
break;
|
606 |
+
|
607 |
+
case 1:
|
608 |
+
$s = 8;
|
609 |
+
$y = 4;
|
610 |
+
break;
|
611 |
+
|
612 |
+
case 2:
|
613 |
+
$s = 4;
|
614 |
+
$y = 2;
|
615 |
+
break;
|
616 |
+
|
617 |
+
case 3:
|
618 |
+
$s = 2;
|
619 |
+
$y = 1;
|
620 |
+
break;
|
621 |
+
}
|
622 |
+
|
623 |
+
for(; $y < $this->m_gih->m_nHeight; $y += $s) {
|
624 |
+
$lne = substr($this->m_data, 0, $this->m_gih->m_nWidth);
|
625 |
+
$this->m_data = substr($this->m_data, $this->m_gih->m_nWidth);
|
626 |
+
|
627 |
+
$data =
|
628 |
+
substr($data, 0, $y * $this->m_gih->m_nWidth) .
|
629 |
+
$lne .
|
630 |
+
substr($data, ($y + 1) * $this->m_gih->m_nWidth);
|
631 |
+
}
|
632 |
+
}
|
633 |
+
|
634 |
+
$this->m_data = $data;
|
635 |
+
}
|
636 |
+
}
|
637 |
+
|
638 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
639 |
+
|
640 |
+
class CGIF
|
641 |
+
{
|
642 |
+
var $m_gfh;
|
643 |
+
var $m_lpData;
|
644 |
+
var $m_img;
|
645 |
+
var $m_bLoaded;
|
646 |
+
|
647 |
+
///////////////////////////////////////////////////////////////////////////
|
648 |
+
|
649 |
+
// CONSTRUCTOR
|
650 |
+
function CGIF()
|
651 |
+
{
|
652 |
+
$this->m_gfh = new CGIFFILEHEADER();
|
653 |
+
$this->m_img = new CGIFIMAGE();
|
654 |
+
$this->m_lpData = "";
|
655 |
+
$this->m_bLoaded = false;
|
656 |
+
}
|
657 |
+
|
658 |
+
///////////////////////////////////////////////////////////////////////////
|
659 |
+
function ClearData() {
|
660 |
+
$this->m_lpData = '';
|
661 |
+
unSet($this->m_img->m_data);
|
662 |
+
unSet($this->m_img->m_lzw->Next);
|
663 |
+
unSet($this->m_img->m_lzw->Vals);
|
664 |
+
unSet($this->m_img->m_lzw->Stack);
|
665 |
+
unSet($this->m_img->m_lzw->Buf);
|
666 |
+
}
|
667 |
+
|
668 |
+
function loadFile(&$data, $iIndex)
|
669 |
+
{
|
670 |
+
if($iIndex < 0) {
|
671 |
+
return false;
|
672 |
+
}
|
673 |
+
$this->m_lpData = $data;
|
674 |
+
|
675 |
+
// GET FILE HEADER
|
676 |
+
$len = 0;
|
677 |
+
if(!$this->m_gfh->load($this->m_lpData, $len)) {
|
678 |
+
return false;
|
679 |
+
}
|
680 |
+
|
681 |
+
$this->m_lpData = substr($this->m_lpData, $len);
|
682 |
+
|
683 |
+
do {
|
684 |
+
$imgLen = 0;
|
685 |
+
if(!$this->m_img->load($this->m_lpData, $imgLen)) {
|
686 |
+
return false;
|
687 |
+
}
|
688 |
+
$this->m_lpData = substr($this->m_lpData, $imgLen);
|
689 |
+
}
|
690 |
+
while($iIndex-- > 0);
|
691 |
+
|
692 |
+
$this->m_bLoaded = true;
|
693 |
+
return true;
|
694 |
+
}
|
695 |
+
|
696 |
+
}
|
697 |
+
|
698 |
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
699 |
+
|
700 |
+
?>
|
{mpdf → lib/mpdf}/classes/grad.php
RENAMED
@@ -111,6 +111,7 @@ function Gradient($x, $y, $w, $h, $type, $stops=array(), $colorspace='RGB', $coo
|
|
111 |
Â
$usey = $y;
|
112 |
Â
$usew = $bboxw;
|
113 |
Â
$useh = $bboxh;
|
Â
|
|
114 |
Â
if ($type < 1) { $type = 2; }
|
115 |
Â
if ($coords[0]!==false && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$coords[0],$m)) {
|
116 |
Â
$tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
|
@@ -122,8 +123,8 @@ function Gradient($x, $y, $w, $h, $type, $stops=array(), $colorspace='RGB', $coo
|
|
122 |
Â
}
|
123 |
Â
// LINEAR
|
124 |
Â
if ($type == 2) {
|
125 |
-
$angle = $coords[4];
|
126 |
-
$repeat = $coords[5];
|
127 |
Â
// ALL POINTS SET (default for custom mPDF linear gradient) - no -moz
|
128 |
Â
if ($coords[0]!==false && $coords[1]!==false && $coords[2]!==false && $coords[3]!==false) {
|
129 |
Â
// do nothing - coords used as they are
|
@@ -258,11 +259,11 @@ function Gradient($x, $y, $w, $h, $type, $stops=array(), $colorspace='RGB', $coo
|
|
258 |
Â
|
259 |
Â
// RADIAL
|
260 |
Â
else if ($type == 3) {
|
261 |
-
$radius = $coords[4];
|
262 |
-
$angle = $coords[5]; // ?? no effect
|
263 |
-
$shape = $coords[6];
|
264 |
-
$size = $coords[7];
|
265 |
-
$repeat = $coords[8];
|
266 |
Â
// ALL POINTS AND RADIUS SET (default for custom mPDF radial gradient) - no -moz
|
267 |
Â
if ($coords[0]!==false && $coords[1]!==false && $coords[2]!==false && $coords[3]!==false && $coords[4]!==false) {
|
268 |
Â
// do nothing - coords used as they are
|
@@ -497,7 +498,7 @@ function parseMozGradient($bg) {
|
|
497 |
Â
if ($tmp) { $startx = $m[1]; }
|
498 |
Â
}
|
499 |
Â
if (isset($first[1]) && preg_match('/(\d+)[%]/i',$first[1],$m)) { $starty = 1 - ($m[1]/100); }
|
500 |
-
else if (!isset($starty) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$first[1],$m)) {
|
501 |
Â
$tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
|
502 |
Â
if ($tmp) { $starty = $m[1]; }
|
503 |
Â
}
|
@@ -598,7 +599,7 @@ function parseMozGradient($bg) {
|
|
598 |
Â
if ($tmp) { $startx = $m[1]; }
|
599 |
Â
}
|
600 |
Â
if (isset($first[1]) && preg_match('/(\d+)[%]/i',$first[1],$m)) { $starty = 1 - ($m[1]/100); }
|
601 |
-
else if (!isset($starty) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$first[1],$m)) {
|
602 |
Â
$tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
|
603 |
Â
if ($tmp) { $starty = $m[1]; }
|
604 |
Â
}
|
111 |
Â
$usey = $y;
|
112 |
Â
$usew = $bboxw;
|
113 |
Â
$useh = $bboxh;
|
114 |
+
|
115 |
Â
if ($type < 1) { $type = 2; }
|
116 |
Â
if ($coords[0]!==false && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$coords[0],$m)) {
|
117 |
Â
$tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
|
123 |
Â
}
|
124 |
Â
// LINEAR
|
125 |
Â
if ($type == 2) {
|
126 |
+
$angle = (isset($coords[4]) ? $coords[4] : false);
|
127 |
+
$repeat = (isset($coords[5]) ? $coords[5] : false);
|
128 |
Â
// ALL POINTS SET (default for custom mPDF linear gradient) - no -moz
|
129 |
Â
if ($coords[0]!==false && $coords[1]!==false && $coords[2]!==false && $coords[3]!==false) {
|
130 |
Â
// do nothing - coords used as they are
|
259 |
Â
|
260 |
Â
// RADIAL
|
261 |
Â
else if ($type == 3) {
|
262 |
+
$radius = (isset($coords[4]) ? $coords[4] : false);
|
263 |
+
$angle = (isset($coords[5]) ? $coords[5] : false); // ?? no effect
|
264 |
+
$shape = (isset($coords[6]) ? $coords[6] : false);
|
265 |
+
$size = (isset($coords[7]) ? $coords[7] : false);
|
266 |
+
$repeat = (isset($coords[8]) ? $coords[8] : false);
|
267 |
Â
// ALL POINTS AND RADIUS SET (default for custom mPDF radial gradient) - no -moz
|
268 |
Â
if ($coords[0]!==false && $coords[1]!==false && $coords[2]!==false && $coords[3]!==false && $coords[4]!==false) {
|
269 |
Â
// do nothing - coords used as they are
|
498 |
Â
if ($tmp) { $startx = $m[1]; }
|
499 |
Â
}
|
500 |
Â
if (isset($first[1]) && preg_match('/(\d+)[%]/i',$first[1],$m)) { $starty = 1 - ($m[1]/100); }
|
501 |
+
else if (!isset($starty) && isset($first[1]) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$first[1],$m)) {
|
502 |
Â
$tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
|
503 |
Â
if ($tmp) { $starty = $m[1]; }
|
504 |
Â
}
|
599 |
Â
if ($tmp) { $startx = $m[1]; }
|
600 |
Â
}
|
601 |
Â
if (isset($first[1]) && preg_match('/(\d+)[%]/i',$first[1],$m)) { $starty = 1 - ($m[1]/100); }
|
602 |
+
else if (!isset($starty) && isset($first[1]) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$first[1],$m)) {
|
603 |
Â
$tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
|
604 |
Â
if ($tmp) { $starty = $m[1]; }
|
605 |
Â
}
|
lib/mpdf/classes/indic.php
ADDED
@@ -0,0 +1,1714 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
|
4 |
+
class INDIC {
|
5 |
+
|
6 |
+
/* FROM hb-ot-shape-complex-indic-private.hh */
|
7 |
+
// indic_category
|
8 |
+
const OT_X = 0;
|
9 |
+
const OT_C = 1;
|
10 |
+
const OT_V = 2;
|
11 |
+
const OT_N = 3;
|
12 |
+
const OT_H = 4;
|
13 |
+
const OT_ZWNJ = 5;
|
14 |
+
const OT_ZWJ = 6;
|
15 |
+
const OT_M = 7; /* Matra or Dependent Vowel */
|
16 |
+
const OT_SM = 8;
|
17 |
+
const OT_VD = 9;
|
18 |
+
const OT_A = 10;
|
19 |
+
const OT_NBSP = 11;
|
20 |
+
const OT_DOTTEDCIRCLE = 12; /* Not in the spec, but special in Uniscribe. /Very very/ special! */
|
21 |
+
const OT_RS = 13; /* Register Shifter, used in Khmer OT spec */
|
22 |
+
const OT_Coeng = 14;
|
23 |
+
const OT_Repha = 15;
|
24 |
+
const OT_Ra = 16; /* Not explicitly listed in the OT spec, but used in the grammar. */
|
25 |
+
const OT_CM = 17;
|
26 |
+
|
27 |
+
|
28 |
+
// Based on indic_category used to make string to find syllables
|
29 |
+
// OT_ to string character (using e.g. OT_C from INDIC) hb-ot-shape-complex-indic-private.hh
|
30 |
+
public static $indic_category_char = array(
|
31 |
+
'x',
|
32 |
+
'C',
|
33 |
+
'V',
|
34 |
+
'N',
|
35 |
+
'H',
|
36 |
+
'Z',
|
37 |
+
'J',
|
38 |
+
'M',
|
39 |
+
'S',
|
40 |
+
'v',
|
41 |
+
'A', /* Spec gives Andutta U+0952 as OT_A. However, testing shows that Uniscribe
|
42 |
+
* treats U+0951..U+0952 all as OT_VD - see set_indic_properties */
|
43 |
+
's',
|
44 |
+
'D',
|
45 |
+
'F', /* Register shift Khmer only */
|
46 |
+
'G', /* Khmer only */
|
47 |
+
'r', /* 0D4E (dot reph) only one in Malayalam */
|
48 |
+
'R',
|
49 |
+
'm', /* Consonant medial only used in Indic 0A75 in Gurmukhi (0A00..0A7F) : also in Lao, Myanmar, Tai Tham, Javanese & Cham */
|
50 |
+
);
|
51 |
+
|
52 |
+
|
53 |
+
/* Visual positions in a syllable from left to right. */
|
54 |
+
/* FROM hb-ot-shape-complex-indic-private.hh */
|
55 |
+
// indic_position
|
56 |
+
const POS_START = 0;
|
57 |
+
|
58 |
+
const POS_RA_TO_BECOME_REPH = 1;
|
59 |
+
const POS_PRE_M = 2;
|
60 |
+
const POS_PRE_C = 3;
|
61 |
+
|
62 |
+
const POS_BASE_C = 4;
|
63 |
+
const POS_AFTER_MAIN = 5;
|
64 |
+
|
65 |
+
const POS_ABOVE_C = 6;
|
66 |
+
|
67 |
+
const POS_BEFORE_SUB = 7;
|
68 |
+
const POS_BELOW_C = 8;
|
69 |
+
const POS_AFTER_SUB = 9;
|
70 |
+
|
71 |
+
const POS_BEFORE_POST = 10;
|
72 |
+
const POS_POST_C = 11;
|
73 |
+
const POS_AFTER_POST = 12;
|
74 |
+
|
75 |
+
const POS_FINAL_C = 13;
|
76 |
+
const POS_SMVD = 14;
|
77 |
+
|
78 |
+
const POS_END = 15;
|
79 |
+
|
80 |
+
/*
|
81 |
+
* Basic features.
|
82 |
+
* These features are applied in order, one at a time, after initial_reordering.
|
83 |
+
*/
|
84 |
+
/*
|
85 |
+
* Must be in the same order as the indic_features array. Ones starting with _ are F_GLOBAL
|
86 |
+
* Ones without the _ are only applied where the mask says!
|
87 |
+
*/
|
88 |
+
const _NUKT = 0;
|
89 |
+
const _AKHN = 1;
|
90 |
+
const RPHF = 2;
|
91 |
+
const _RKRF = 3;
|
92 |
+
const PREF = 4;
|
93 |
+
const BLWF = 5;
|
94 |
+
const HALF = 6;
|
95 |
+
const ABVF = 7;
|
96 |
+
const PSTF = 8;
|
97 |
+
const CFAR = 9; // Khmer only
|
98 |
+
const _VATU = 10;
|
99 |
+
const _CJCT = 11;
|
100 |
+
const INIT = 12;
|
101 |
+
|
102 |
+
|
103 |
+
public static function set_indic_properties(&$info, $scriptblock ) {
|
104 |
+
$u = $info['uni'];
|
105 |
+
$type = self::indic_get_categories($u);
|
106 |
+
$cat = ($type & 0x7F);
|
107 |
+
$pos = ($type >> 8);
|
108 |
+
|
109 |
+
/*
|
110 |
+
* Re-assign category
|
111 |
+
*/
|
112 |
+
|
113 |
+
if ($u == 0x17D1) $cat = self::OT_X;
|
114 |
+
|
115 |
+
if ($cat == self::OT_X && self::in_range($u, 0x17CB, 0x17D3)) { /* Khmer Various signs */
|
116 |
+
/* These are like Top Matras. */
|
117 |
+
$cat = self::OT_M;
|
118 |
+
$pos = self::POS_ABOVE_C;
|
119 |
+
}
|
120 |
+
|
121 |
+
if ($u == 0x17C6) $cat = self::OT_N; /* Khmer Bindu doesn't like to be repositioned. */
|
122 |
+
|
123 |
+
if ($u == 0x17D2) $cat = self::OT_Coeng; /* Khmer coeng */
|
124 |
+
|
125 |
+
/* The spec says U+0952 is OT_A. However, testing shows that Uniscribe
|
126 |
+
* treats U+0951..U+0952 all as OT_VD.
|
127 |
+
* TESTS:
|
128 |
+
* U+092E,U+0947,U+0952
|
129 |
+
* U+092E,U+0952,U+0947
|
130 |
+
* U+092E,U+0947,U+0951
|
131 |
+
* U+092E,U+0951,U+0947
|
132 |
+
* */
|
133 |
+
//if ($u == 0x0952) $cat = self::OT_A;
|
134 |
+
if (self::in_range($u, 0x0951, 0x0954))
|
135 |
+
$cat = self::OT_VD;
|
136 |
+
|
137 |
+
if ($u == 0x200C) $cat = self::OT_ZWNJ;
|
138 |
+
else if ($u == 0x200D) $cat = self::OT_ZWJ;
|
139 |
+
else if ($u == 0x25CC) $cat = self::OT_DOTTEDCIRCLE;
|
140 |
+
else if ($u == 0x0A71) $cat = self::OT_SM; /* GURMUKHI ADDAK. More like consonant medial. like 0A75. */
|
141 |
+
|
142 |
+
if ($cat == self::OT_Repha) {
|
143 |
+
/* There are two kinds of characters marked as Repha:
|
144 |
+
* - The ones that are GenCat=Mn are already positioned visually, ie. after base. (eg. Khmer)
|
145 |
+
* - The ones that are GenCat=Lo is encoded logically, ie. beginning of syllable. (eg. Malayalam)
|
146 |
+
*
|
147 |
+
* We recategorize the first kind to look like a Nukta and attached to the base directly.
|
148 |
+
*/
|
149 |
+
if ($info['general_category'] == UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
|
150 |
+
$cat = self::OT_N;
|
151 |
+
}
|
152 |
+
|
153 |
+
/*
|
154 |
+
* Re-assign position.
|
155 |
+
*/
|
156 |
+
|
157 |
+
if ((self::FLAG($cat) & (self::FLAG(self::OT_C) | self::FLAG(self::OT_CM) | self::FLAG(self::OT_Ra) | self::FLAG(self::OT_V) | self::FLAG(self::OT_NBSP) | self::FLAG(self::OT_DOTTEDCIRCLE)))) { // = CONSONANT_FLAGS like is_consonant
|
158 |
+
if ($scriptblock == UCDN::SCRIPT_KHMER) $pos = self::POS_BELOW_C; /* Khmer differs from Indic here. */
|
159 |
+
else $pos = self::POS_BASE_C; /* Will recategorize later based on font lookups. */
|
160 |
+
|
161 |
+
if (self::is_ra ($u))
|
162 |
+
$cat = self::OT_Ra;
|
163 |
+
}
|
164 |
+
else if ($cat == self::OT_M) {
|
165 |
+
$pos = self::matra_position($u, $pos);
|
166 |
+
}
|
167 |
+
else if ($cat == self::OT_SM || $cat == self::OT_VD) {
|
168 |
+
$pos = self::POS_SMVD;
|
169 |
+
}
|
170 |
+
|
171 |
+
if ($u == 0x0B01) $pos = self::POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
|
172 |
+
|
173 |
+
$info['indic_category'] = $cat;
|
174 |
+
$info['indic_position'] = $pos;
|
175 |
+
}
|
176 |
+
|
177 |
+
// syllable_type
|
178 |
+
const CONSONANT_SYLLABLE = 0;
|
179 |
+
const VOWEL_SYLLABLE = 1;
|
180 |
+
const STANDALONE_CLUSTER = 2;
|
181 |
+
const BROKEN_CLUSTER = 3;
|
182 |
+
const NON_INDIC_CLUSTER = 4;
|
183 |
+
|
184 |
+
public static function set_syllables(&$o, $s, &$broken_syllables) {
|
185 |
+
$ptr = 0;
|
186 |
+
$syllable_serial = 1;
|
187 |
+
$broken_syllables = false;
|
188 |
+
|
189 |
+
while($ptr < strlen($s)) {
|
190 |
+
$match = '';
|
191 |
+
$syllable_length = 1;
|
192 |
+
$syllable_type = self::NON_INDIC_CLUSTER ;
|
193 |
+
// CONSONANT_SYLLABLE Consonant syllable
|
194 |
+
// From OT spec:
|
195 |
+
if (preg_match('/^([CR]m*[N]?(H[ZJ]?|[ZJ]H))*[CR]m*[N]?[A]?(H[ZJ]?|[M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s,$ptr), $ma)) {
|
196 |
+
// From HarfBuzz:
|
197 |
+
//if (preg_match('/^r?([CR]J?(Z?[N]{0,2})?[ZJ]?H(J[N]?)?){0,4}[CR]J?(Z?[N]{0,2})?A?((([ZJ]?H(J[N]?)?)|HZ)|(HJ)?([ZJ]{0,3}M[N]?(H|JHJR)?){0,4})?(S[Z]?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
|
198 |
+
$syllable_length = strlen($ma[0]);
|
199 |
+
$syllable_type = self::CONSONANT_SYLLABLE ;
|
200 |
+
}
|
201 |
+
// VOWEL_SYLLABLE Vowel-based syllable
|
202 |
+
// From OT spec:
|
203 |
+
else if (preg_match('/^(RH|r)?V[N]?([ZJ]?H[CR]m*|J[CR]m*)?([M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s,$ptr), $ma)) {
|
204 |
+
// From HarfBuzz:
|
205 |
+
//else if (preg_match('/^(RH|r)?V(Z?[N]{0,2})?(J|([ZJ]?H(J[N]?)?[CR]J?(Z?[N]{0,2})?){0,4}((([ZJ]?H(J[N]?)?)|HZ)|(HJ)?([ZJ]{0,3}M[N]?(H|JHJR)?){0,4})?(S[Z]?)?[v]{0,2})/', substr($s,$ptr), $ma)) {
|
206 |
+
$syllable_length = strlen($ma[0]);
|
207 |
+
$syllable_type = self::VOWEL_SYLLABLE ;
|
208 |
+
}
|
209 |
+
|
210 |
+
/* Apply only if it's a word start. */
|
211 |
+
// STANDALONE_CLUSTER Stand Alone syllable at start of word
|
212 |
+
// From OT spec:
|
213 |
+
else if (($ptr==0 ||
|
214 |
+
$o[$ptr - 1]['general_category'] < UCDN::UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER ||
|
215 |
+
$o[$ptr - 1]['general_category'] > UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK
|
216 |
+
)
|
217 |
+
|
218 |
+
&& (preg_match('/^(RH|r)?[sD][N]?([ZJ]?H[CR]m*)?([M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s,$ptr), $ma))) {
|
219 |
+
// From HarfBuzz:
|
220 |
+
// && (preg_match('/^(RH|r)?[sD](Z?[N]{0,2})?(([ZJ]?H(J[N]?)?)[CR]J?(Z?[N]{0,2})?){0,4}((([ZJ]?H(J[N]?)?)|HZ)|(HJ)?([ZJ]{0,3}M[N]?(H|JHJR)?){0,4})?(S[Z]?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
|
221 |
+
$syllable_length = strlen($ma[0]);
|
222 |
+
$syllable_type = self::STANDALONE_CLUSTER ;
|
223 |
+
}
|
224 |
+
|
225 |
+
// BROKEN_CLUSTER syllable
|
226 |
+
else if (preg_match('/^(RH|r)?[N]?([ZJ]?H[CR])?([M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s,$ptr), $ma)) {
|
227 |
+
// From HarfBuzz:
|
228 |
+
//else if (preg_match('/^(RH|r)?(Z?[N]{0,2})?(([ZJ]?H(J[N]?)?)[CR]J?(Z?[N]{0,2})?){0,4}((([ZJ]?H(J[N]?)?)|HZ)|(HJ)?([ZJ]{0,3}M[N]?(H|JHJR)?){0,4})(S[Z]?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
|
229 |
+
if (strlen($ma[0])) { // May match blank
|
230 |
+
$syllable_length = strlen($ma[0]);
|
231 |
+
$syllable_type = self::BROKEN_CLUSTER ;
|
232 |
+
$broken_syllables = true;
|
233 |
+
}
|
234 |
+
}
|
235 |
+
|
236 |
+
for ($i = $ptr; $i < $ptr+$syllable_length; $i++) { $o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type; }
|
237 |
+
$ptr += $syllable_length ;
|
238 |
+
$syllable_serial++;
|
239 |
+
if ($syllable_serial == 16) $syllable_serial = 1;
|
240 |
+
}
|
241 |
+
}
|
242 |
+
|
243 |
+
|
244 |
+
public static function set_syllables_sinhala(&$o, $s, &$broken_syllables) {
|
245 |
+
$ptr = 0;
|
246 |
+
$syllable_serial = 1;
|
247 |
+
$broken_syllables = false;
|
248 |
+
|
249 |
+
while($ptr < strlen($s)) {
|
250 |
+
$match = '';
|
251 |
+
$syllable_length = 1;
|
252 |
+
$syllable_type = self::NON_INDIC_CLUSTER ;
|
253 |
+
// CONSONANT_SYLLABLE Consonant syllable
|
254 |
+
// From OT spec:
|
255 |
+
if (preg_match('/^([CR]HJ|[CR]JH){0,8}[CR][HM]{0,3}[S]{0,1}/', substr($s,$ptr), $ma)) {
|
256 |
+
$syllable_length = strlen($ma[0]);
|
257 |
+
$syllable_type = self::CONSONANT_SYLLABLE ;
|
258 |
+
}
|
259 |
+
// VOWEL_SYLLABLE Vowel-based syllable
|
260 |
+
// From OT spec:
|
261 |
+
else if (preg_match('/^V[S]{0,1}/', substr($s,$ptr), $ma)) {
|
262 |
+
$syllable_length = strlen($ma[0]);
|
263 |
+
$syllable_type = self::VOWEL_SYLLABLE ;
|
264 |
+
}
|
265 |
+
|
266 |
+
for ($i = $ptr; $i < $ptr+$syllable_length; $i++) { $o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type; }
|
267 |
+
$ptr += $syllable_length ;
|
268 |
+
$syllable_serial++;
|
269 |
+
if ($syllable_serial == 16) $syllable_serial = 1;
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
public static function set_syllables_khmer(&$o, $s, &$broken_syllables) {
|
274 |
+
$ptr = 0;
|
275 |
+
$syllable_serial = 1;
|
276 |
+
$broken_syllables = false;
|
277 |
+
|
278 |
+
while($ptr < strlen($s)) {
|
279 |
+
$match = '';
|
280 |
+
$syllable_length = 1;
|
281 |
+
$syllable_type = self::NON_INDIC_CLUSTER ;
|
282 |
+
// CONSONANT_SYLLABLE Consonant syllable
|
283 |
+
if (preg_match('/^r?([CR]J?((Z?F)?[N]{0,2})?[ZJ]?G(JN?)?){0,4}[CR]J?((Z?F)?[N]{0,2})?A?((([ZJ]?G(JN?)?)|GZ)|(GJ)?([ZJ]{0,3}MN?(H|JHJR)?){0,4})?(G([CR]J?((Z?F)?[N]{0,2})?|V))?(SZ?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
|
284 |
+
$syllable_length = strlen($ma[0]);
|
285 |
+
$syllable_type = self::CONSONANT_SYLLABLE ;
|
286 |
+
}
|
287 |
+
// VOWEL_SYLLABLE Vowel-based syllable
|
288 |
+
else if (preg_match('/^(RH|r)?V((Z?F)?[N]{0,2})?(J|([ZJ]?G(JN?)?[CR]J?((Z?F)?[N]{0,2})?){0,4}((([ZJ]?G(JN?)?)|GZ)|(GJ)?([ZJ]{0,3}MN?(H|JHJR)?){0,4})?(G([CR]J?((Z?F)?[N]{0,2})?|V))?(SZ?)?[v]{0,2})/', substr($s,$ptr), $ma)) {
|
289 |
+
$syllable_length = strlen($ma[0]);
|
290 |
+
$syllable_type = self::VOWEL_SYLLABLE ;
|
291 |
+
}
|
292 |
+
|
293 |
+
|
294 |
+
// BROKEN_CLUSTER syllable
|
295 |
+
else if (preg_match('/^(RH|r)?((Z?F)?[N]{0,2})?(([ZJ]?G(JN?)?)[CR]J?((Z?F)?[N]{0,2})?){0,4}((([ZJ]?G(JN?)?)|GZ)|(GJ)?([ZJ]{0,3}MN?(H|JHJR)?){0,4})(G([CR]J?((Z?F)?[N]{0,2})?|V))?(SZ?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
|
296 |
+
if (strlen($ma[0])) { // May match blank
|
297 |
+
$syllable_length = strlen($ma[0]);
|
298 |
+
$syllable_type = self::BROKEN_CLUSTER ;
|
299 |
+
$broken_syllables = true;
|
300 |
+
}
|
301 |
+
}
|
302 |
+
|
303 |
+
for ($i = $ptr; $i < $ptr+$syllable_length; $i++) { $o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type; }
|
304 |
+
$ptr += $syllable_length ;
|
305 |
+
$syllable_serial++;
|
306 |
+
if ($syllable_serial == 16) $syllable_serial = 1;
|
307 |
+
}
|
308 |
+
}
|
309 |
+
|
310 |
+
public static function initial_reordering(&$info, $GSUBdata, $broken_syllables, $indic_config, $scriptblock, $is_old_spec, $dottedcircle) {
|
311 |
+
|
312 |
+
self::update_consonant_positions ($info, $GSUBdata);
|
313 |
+
|
314 |
+
if ($broken_syllables && $dottedcircle) { self::insert_dotted_circles ($info, $dottedcircle); }
|
315 |
+
|
316 |
+
$count = count($info);
|
317 |
+
if (!$count) return;
|
318 |
+
$last = 0;
|
319 |
+
$last_syllable = $info[0]['syllable'];
|
320 |
+
for ($i = 1; $i < $count; $i++) {
|
321 |
+
if ($last_syllable != $info[$i]['syllable']) {
|
322 |
+
self::initial_reordering_syllable ($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $i);
|
323 |
+
$last = $i;
|
324 |
+
$last_syllable = $info[$last]['syllable'];
|
325 |
+
}
|
326 |
+
}
|
327 |
+
self::initial_reordering_syllable($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $count);
|
328 |
+
}
|
329 |
+
|
330 |
+
public static function update_consonant_positions(&$info, $GSUBdata) {
|
331 |
+
$count = count($info);
|
332 |
+
for ($i = 0; $i < $count; $i++) {
|
333 |
+
if ($info[$i]['indic_position'] == self::POS_BASE_C) {
|
334 |
+
$c = $info[$i]['uni'];
|
335 |
+
// If would substitute...
|
336 |
+
if (isset($GSUBdata['pref'][$c])) { $info[$i]['indic_position'] = self::POS_POST_C; }
|
337 |
+
else if (isset($GSUBdata['blwf'][$c])) { $info[$i]['indic_position'] = self::POS_BELOW_C; }
|
338 |
+
else if (isset($GSUBdata['pstf'][$c])) { $info[$i]['indic_position'] = self::POS_POST_C; }
|
339 |
+
}
|
340 |
+
}
|
341 |
+
}
|
342 |
+
|
343 |
+
public static function insert_dotted_circles(&$info, $dottedcircle) {
|
344 |
+
$idx = 0;
|
345 |
+
$last_syllable = 0;
|
346 |
+
while ($idx < count($info)) {
|
347 |
+
$syllable = $info[$idx]['syllable'];
|
348 |
+
$syllable_type = ($syllable & 0x0F);
|
349 |
+
if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
|
350 |
+
$last_syllable = $syllable;
|
351 |
+
|
352 |
+
$dottedcircle[0]['syllable'] = $info[$idx]['syllable'];
|
353 |
+
|
354 |
+
/* Insert dottedcircle after possible Repha. */
|
355 |
+
while ($idx < count($info) && $last_syllable == $info[$idx]['syllable'] && $info[$idx]['indic_category'] == self::OT_Repha)
|
356 |
+
$idx++;
|
357 |
+
array_splice($info, $idx, 0, $dottedcircle);
|
358 |
+
}
|
359 |
+
else
|
360 |
+
$idx++;
|
361 |
+
}
|
362 |
+
// I am not sue how this code below got in here, since $idx should now be > count($info) and thus invalid.
|
363 |
+
// In case I am missing something(!) I'll leave a warning here for now:
|
364 |
+
if (isset($info[$idx])) { die("This shouldn't happen (in otl.php)"); exit; }
|
365 |
+
// In case of final bloken cluster...
|
366 |
+
//$syllable = $info[$idx]['syllable'];
|
367 |
+
//$syllable_type = ($syllable & 0x0F);
|
368 |
+
//if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
|
369 |
+
// $dottedcircle[0]['syllable'] = $info[$idx]['syllable'];
|
370 |
+
// array_splice($info, $idx, 0, $dottedcircle);
|
371 |
+
//}
|
372 |
+
}
|
373 |
+
|
374 |
+
|
375 |
+
|
376 |
+
/* Rules from:
|
377 |
+
* https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
|
378 |
+
|
379 |
+
public static function initial_reordering_syllable (&$info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $start, $end) {
|
380 |
+
/* vowel_syllable: We made the vowels look like consonants. So uses the consonant logic! */
|
381 |
+
/* broken_cluster: We already inserted dotted-circles, so just call the standalone_cluster. */
|
382 |
+
/* standalone_cluster: We treat NBSP/dotted-circle as if they are consonants, so we should just chain. */
|
383 |
+
|
384 |
+
$syllable_type = ($info[$start]['syllable'] & 0x0F);
|
385 |
+
if ($syllable_type==self::NON_INDIC_CLUSTER ) { return; }
|
386 |
+
if ($syllable_type==self::BROKEN_CLUSTER || $syllable_type==self::STANDALONE_CLUSTER ) {
|
387 |
+
//if ($uniscribe_bug_compatible) {
|
388 |
+
/* For dotted-circle, this is what Uniscribe does:
|
389 |
+
* If dotted-circle is the last glyph, it just does nothing.
|
390 |
+
* i.e. It doesn't form Reph. */
|
391 |
+
if ($info[$end - 1]['indic_category'] == self::OT_DOTTEDCIRCLE) {
|
392 |
+
return;
|
393 |
+
}
|
394 |
+
}
|
395 |
+
|
396 |
+
/* 1. Find base consonant:
|
397 |
+
*
|
398 |
+
* The shaping engine finds the base consonant of the syllable, using the
|
399 |
+
* following algorithm: starting from the end of the syllable, move backwards
|
400 |
+
* until a consonant is found that does not have a below-base or post-base
|
401 |
+
* form (post-base forms have to follow below-base forms), or that is not a
|
402 |
+
* pre-base reordering Ra, or arrive at the first consonant. The consonant
|
403 |
+
* stopped at will be the base.
|
404 |
+
*
|
405 |
+
* o If the syllable starts with Ra + Halant (in a script that has Reph)
|
406 |
+
* and has more than one consonant, Ra is excluded from candidates for
|
407 |
+
* base consonants.
|
408 |
+
*/
|
409 |
+
|
410 |
+
$base = $end;
|
411 |
+
$has_reph = false;
|
412 |
+
$limit = $start;
|
413 |
+
|
414 |
+
if ($scriptblock != UCDN::SCRIPT_KHMER) {
|
415 |
+
/* -> If the syllable starts with Ra + Halant (in a script that has Reph)
|
416 |
+
* and has more than one consonant, Ra is excluded from candidates for
|
417 |
+
* base consonants. */
|
418 |
+
if (count($GSUBdata['rphf']) /* ?? $indic_plan->mask_array[RPHF] */ && $start + 3 <= $end &&
|
419 |
+
(
|
420 |
+
($indic_config[4] == self::REPH_MODE_IMPLICIT && !self::is_joiner($info[$start + 2])) ||
|
421 |
+
($indic_config[4] == self::REPH_MODE_EXPLICIT && $info[$start + 2]['indic_category'] == self::OT_ZWJ)
|
422 |
+
)) {
|
423 |
+
/* See if it matches the 'rphf' feature. */
|
424 |
+
//$glyphs = array($info[$start]['uni'], $info[$start + 1]['uni']);
|
425 |
+
//if ($indic_plan->rphf->would_substitute ($glyphs, count($glyphs), true, face)) {
|
426 |
+
if (isset($GSUBdata['rphf'][$info[$start]['uni']]) && self::is_halant_or_coeng($info[$start + 1]) ) {
|
427 |
+
$limit += 2;
|
428 |
+
while ($limit < $end && self::is_joiner($info[$limit]))
|
429 |
+
$limit++;
|
430 |
+
$base = $start;
|
431 |
+
$has_reph = true;
|
432 |
+
}
|
433 |
+
}
|
434 |
+
else if ($indic_config[4] == self::REPH_MODE_LOG_REPHA && $info[$start]['indic_category'] == self::OT_Repha) {
|
435 |
+
$limit += 1;
|
436 |
+
while ($limit < $end && self::is_joiner($info[$limit]))
|
437 |
+
$limit++;
|
438 |
+
$base = $start;
|
439 |
+
$has_reph = true;
|
440 |
+
}
|
441 |
+
}
|
442 |
+
|
443 |
+
switch ($indic_config[2]) { // base_pos
|
444 |
+
case self::BASE_POS_LAST:
|
445 |
+
/* -> starting from the end of the syllable, move backwards */
|
446 |
+
$i = $end;
|
447 |
+
$seen_below = false;
|
448 |
+
do {
|
449 |
+
$i--;
|
450 |
+
/* -> until a consonant is found */
|
451 |
+
if (self::is_consonant($info[$i])) {
|
452 |
+
/* -> that does not have a below-base or post-base form
|
453 |
+
* (post-base forms have to follow below-base forms), */
|
454 |
+
if ($info[$i]['indic_position'] != self::POS_BELOW_C && ($info[$i]['indic_position'] != self::POS_POST_C || $seen_below)) {
|
455 |
+
$base = $i;
|
456 |
+
break;
|
457 |
+
}
|
458 |
+
if ($info[$i]['indic_position'] == self::POS_BELOW_C)
|
459 |
+
$seen_below = true;
|
460 |
+
|
461 |
+
/* -> or that is not a pre-base reordering Ra,
|
462 |
+
*
|
463 |
+
* IMPLEMENTATION NOTES:
|
464 |
+
*
|
465 |
+
* Our pre-base reordering Ra's are marked POS_POST_C, so will be skipped
|
466 |
+
* by the logic above already.
|
467 |
+
*/
|
468 |
+
|
469 |
+
/* -> or arrive at the first consonant. The consonant stopped at will
|
470 |
+
* be the base. */
|
471 |
+
$base = $i;
|
472 |
+
}
|
473 |
+
else {
|
474 |
+
/* A ZWJ after a Halant stops the base search, and requests an explicit
|
475 |
+
* half form.
|
476 |
+
* [A ZWJ before a Halant, requests a subjoined form instead, and hence
|
477 |
+
* search continues. This is particularly important for Bengali
|
478 |
+
* sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya] */
|
479 |
+
if ($start < $i && $info[$i]['indic_category'] == self::OT_ZWJ && $info[$i - 1]['indic_category'] == self::OT_H) {
|
480 |
+
if (!defined("OMIT_INDIC_FIX_1") || OMIT_INDIC_FIX_1!=1) { $base = $i; } // INDIC_FIX_1
|
481 |
+
break;
|
482 |
+
}
|
483 |
+
// ZKI8
|
484 |
+
if ($start < $i && $info[$i]['indic_category'] == self::OT_ZWNJ) {
|
485 |
+
break;
|
486 |
+
}
|
487 |
+
}
|
488 |
+
} while ($i > $limit);
|
489 |
+
break;
|
490 |
+
|
491 |
+
case self::BASE_POS_FIRST:
|
492 |
+
/* In scripts without half forms (eg. Khmer), the first consonant is always the base. */
|
493 |
+
|
494 |
+
if (!$has_reph)
|
495 |
+
$base = $limit;
|
496 |
+
|
497 |
+
/* Find the last base consonant that is not blocked by ZWJ. If there is
|
498 |
+
* a ZWJ right before a base consonant, that would request a subjoined form. */
|
499 |
+
for ($i = $limit; $i < $end; $i++) {
|
500 |
+
if (self::is_consonant($info[$i]) && $info[$i]['indic_position'] == self::POS_BASE_C) {
|
501 |
+
if ($limit < $i && $info[$i - 1]['indic_category'] == self::OT_ZWJ)
|
502 |
+
break;
|
503 |
+
else
|
504 |
+
$base = $i;
|
505 |
+
}
|
506 |
+
}
|
507 |
+
|
508 |
+
/* Mark all subsequent consonants as below. */
|
509 |
+
for ($i = $base + 1; $i < $end; $i++) {
|
510 |
+
if (self::is_consonant ($info[$i]) && $info[$i]['indic_position'] == self::POS_BASE_C)
|
511 |
+
$info[$i]['indic_position'] = self::POS_BELOW_C;
|
512 |
+
}
|
513 |
+
break;
|
514 |
+
//default:
|
515 |
+
//assert (false);
|
516 |
+
/* fallthrough */
|
517 |
+
}
|
518 |
+
|
519 |
+
/* -> If the syllable starts with Ra + Halant (in a script that has Reph)
|
520 |
+
* and has more than one consonant, Ra is excluded from candidates for
|
521 |
+
* base consonants.
|
522 |
+
*
|
523 |
+
* Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */
|
524 |
+
if ($scriptblock != UCDN::SCRIPT_KHMER) {
|
525 |
+
if ($has_reph && $base == $start && $limit - $base <= 2) {
|
526 |
+
/* Have no other consonant, so Reph is not formed and Ra becomes base. */
|
527 |
+
$has_reph = false;
|
528 |
+
}
|
529 |
+
}
|
530 |
+
|
531 |
+
/* 2. Decompose and reorder Matras:
|
532 |
+
*
|
533 |
+
* Each matra and any syllable modifier sign in the cluster are moved to the
|
534 |
+
* appropriate position relative to the consonant(s) in the cluster. The
|
535 |
+
* shaping engine decomposes two- or three-part matras into their constituent
|
536 |
+
* parts before any repositioning. Matra characters are classified by which
|
537 |
+
* consonant in a conjunct they have affinity for and are reordered to the
|
538 |
+
* following positions:
|
539 |
+
*
|
540 |
+
* o Before first half form in the syllable
|
541 |
+
* o After subjoined consonants
|
542 |
+
* o After post-form consonant
|
543 |
+
* o After main consonant (for above marks)
|
544 |
+
*
|
545 |
+
* IMPLEMENTATION NOTES:
|
546 |
+
*
|
547 |
+
* The normalize() routine has already decomposed matras for us, so we don't
|
548 |
+
* need to worry about that.
|
549 |
+
*/
|
550 |
+
|
551 |
+
|
552 |
+
/* 3. Reorder marks to canonical order:
|
553 |
+
*
|
554 |
+
* Adjacent nukta and halant or nukta and vedic sign are always repositioned
|
555 |
+
* if necessary, so that the nukta is first.
|
556 |
+
*
|
557 |
+
* IMPLEMENTATION NOTES:
|
558 |
+
*
|
559 |
+
* Use the combining Class from Unicode categories? to bubble_sort.
|
560 |
+
*/
|
561 |
+
|
562 |
+
/* Reorder characters */
|
563 |
+
|
564 |
+
for ($i = $start; $i < $base; $i++)
|
565 |
+
$info[$i]['indic_position'] = min(self::POS_PRE_C, $info[$i]['indic_position']);
|
566 |
+
|
567 |
+
if ($base < $end)
|
568 |
+
$info[$base]['indic_position'] = self::POS_BASE_C;
|
569 |
+
|
570 |
+
/* Mark final consonants. A final consonant is one appearing after a matra,
|
571 |
+
* ? only in Khmer. */
|
572 |
+
for ($i = $base + 1; $i < $end; $i++)
|
573 |
+
if ($info[$i]['indic_category'] == self::OT_M) {
|
574 |
+
for ($j = $i + 1; $j < $end; $j++)
|
575 |
+
if (self::is_consonant ($info[$j])) {
|
576 |
+
$info[$j]['indic_position'] = self::POS_FINAL_C;
|
577 |
+
break;
|
578 |
+
}
|
579 |
+
break;
|
580 |
+
}
|
581 |
+
|
582 |
+
/* Handle beginning Ra */
|
583 |
+
if ($scriptblock != UCDN::SCRIPT_KHMER) {
|
584 |
+
if ($has_reph)
|
585 |
+
$info[$start]['indic_position'] = self::POS_RA_TO_BECOME_REPH;
|
586 |
+
}
|
587 |
+
|
588 |
+
|
589 |
+
/* For old-style Indic script tags, move the first post-base Halant after
|
590 |
+
* last consonant. Only do this if there is *not* a Halant after last
|
591 |
+
* consonant. Otherwise it becomes messy. */
|
592 |
+
if ($is_old_spec) {
|
593 |
+
for ($i = $base + 1; $i < $end; $i++) {
|
594 |
+
if ($info[$i]['indic_category'] == self::OT_H) {
|
595 |
+
for ($j = $end - 1; $j > $i; $j--) {
|
596 |
+
if (self::is_consonant($info[$j]) || $info[$j]['indic_category'] == self::OT_H) { break; }
|
597 |
+
}
|
598 |
+
if ($info[$j]['indic_category'] != self::OT_H && $j > $i) {
|
599 |
+
/* Move Halant to after last consonant. */
|
600 |
+
self::_move_info_pos($info, $i, $j+1);
|
601 |
+
}
|
602 |
+
break;
|
603 |
+
}
|
604 |
+
}
|
605 |
+
}
|
606 |
+
|
607 |
+
/* Attach misc marks to previous char to move with them. */
|
608 |
+
$last_pos = self::POS_START;
|
609 |
+
for ($i = $start; $i < $end; $i++) {
|
610 |
+
if ((self::FLAG($info[$i]['indic_category']) & (self::FLAG(self::OT_ZWJ)| self::FLAG(self::OT_ZWNJ) | self::FLAG(self::OT_N) | self::FLAG (self::OT_RS) | self::FLAG (self::OT_H) | self::FLAG (self::OT_Coeng) ))) {
|
611 |
+
$info[$i]['indic_position'] = $last_pos;
|
612 |
+
if ($info[$i]['indic_category'] == self::OT_H && $info[$i]['indic_position'] == self::POS_PRE_M) {
|
613 |
+
/*
|
614 |
+
* Uniscribe doesn't move the Halant with Left Matra.
|
615 |
+
* TEST: U+092B,U+093F,U+094DE
|
616 |
+
* We follow. This is important for the Sinhala
|
617 |
+
* U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
|
618 |
+
* where U+0DD9 is a left matra and U+0DCA is the virama.
|
619 |
+
* We don't want to move the virama with the left matra.
|
620 |
+
* TEST: U+0D9A,U+0DDA
|
621 |
+
*/
|
622 |
+
for ($j = $i; $j > $start; $j--)
|
623 |
+
if ($info[$j - 1]['indic_position'] != self::POS_PRE_M) {
|
624 |
+
$info[$i]['indic_position'] = $info[$j - 1]['indic_position'];
|
625 |
+
break;
|
626 |
+
}
|
627 |
+
}
|
628 |
+
}
|
629 |
+
else if ($info[$i]['indic_position'] != self::POS_SMVD) {
|
630 |
+
$last_pos = $info[$i]['indic_position'];
|
631 |
+
}
|
632 |
+
}
|
633 |
+
|
634 |
+
/* Re-attach ZWJ, ZWNJ, and halant to next char, for after-base consonants. */
|
635 |
+
$last_halant = $end;
|
636 |
+
for ($i = $base + 1; $i < $end; $i++) {
|
637 |
+
if (self::is_halant_or_coeng($info[$i]))
|
638 |
+
$last_halant = $i;
|
639 |
+
else if (self::is_consonant($info[$i])) {
|
640 |
+
for ($j = $last_halant; $j < $i; $j++)
|
641 |
+
if ($info[$j]['indic_position'] != self::POS_SMVD)
|
642 |
+
$info[$j]['indic_position'] = $info[$i]['indic_position'];
|
643 |
+
}
|
644 |
+
}
|
645 |
+
|
646 |
+
|
647 |
+
if ($scriptblock == UCDN::SCRIPT_KHMER) {
|
648 |
+
/* KHMER_FIX_2 */
|
649 |
+
/* Move Coeng+RO (Halant,Ra) sequence before base consonant. */
|
650 |
+
for ($i = $base + 1; $i < $end; $i++) {
|
651 |
+
if (self::is_halant_or_coeng($info[$i]) && self::is_ra($info[$i + 1]['uni'])) {
|
652 |
+
$info[$i]['indic_position'] = self::POS_PRE_C;
|
653 |
+
$info[$i + 1]['indic_position'] = self::POS_PRE_C;
|
654 |
+
break;
|
655 |
+
}
|
656 |
+
}
|
657 |
+
}
|
658 |
+
|
659 |
+
|
660 |
+
/*
|
661 |
+
if (!defined("OMIT_INDIC_FIX_2") || OMIT_INDIC_FIX_2 != 1) {
|
662 |
+
// INDIC_FIX_2
|
663 |
+
$ZWNJ_found = false;
|
664 |
+
$POST_ZWNJ_c_found = false;
|
665 |
+
for ($i = $base + 1; $i < $end; $i++) {
|
666 |
+
if ($info[$i]['indic_category'] == self::OT_ZWNJ) { $ZWNJ_found = true; }
|
667 |
+
else if ($ZWNJ_found && $info[$i]['indic_category'] == self::OT_C) { $POST_ZWNJ_c_found = true; }
|
668 |
+
else if ($POST_ZWNJ_c_found && $info[$i]['indic_position'] == self::POS_BEFORE_SUB) { $info[$i]['indic_position'] = self::POS_AFTER_SUB; }
|
669 |
+
}
|
670 |
+
}
|
671 |
+
*/
|
672 |
+
|
673 |
+
/* Setup masks now */
|
674 |
+
for ($i = $start; $i < $end; $i++) {
|
675 |
+
$info[$i]['mask'] = 0;
|
676 |
+
}
|
677 |
+
|
678 |
+
|
679 |
+
if ($scriptblock == UCDN::SCRIPT_KHMER) {
|
680 |
+
/* Find a Coeng+RO (Halant,Ra) sequence and mark it for pre-base processing. */
|
681 |
+
$mask = self::FLAG(self::PREF);
|
682 |
+
for ($i = $base; $i < $end-1; $i++) { /* KHMER_FIX_1 From $start (not base) */
|
683 |
+
if (self::is_halant_or_coeng($info[$i]) && self::is_ra($info[$i + 1]['uni']) ) {
|
684 |
+
|
685 |
+
$info[$i]['mask'] |= self::FLAG(self::PREF);
|
686 |
+
$info[$i + 1]['mask'] |= self::FLAG(self::PREF);
|
687 |
+
|
688 |
+
/* Mark the subsequent stuff with 'cfar'. Used in Khmer.
|
689 |
+
* Read the feature spec.
|
690 |
+
* This allows distinguishing the following cases with MS Khmer fonts:
|
691 |
+
* U+1784,U+17D2,U+179A,U+17D2,U+1782 [C+Coeng+RO+Coeng+C] => Should activate CFAR
|
692 |
+
* U+1784,U+17D2,U+1782,U+17D2,U+179A [C+Coeng+C+Coeng+RO] => Should NOT activate CFAR
|
693 |
+
*/
|
694 |
+
for ($j=($i+2); $j < $end; $j++)
|
695 |
+
$info[$j]['mask'] |= self::FLAG(self::CFAR);
|
696 |
+
|
697 |
+
break;
|
698 |
+
}
|
699 |
+
}
|
700 |
+
}
|
701 |
+
|
702 |
+
|
703 |
+
|
704 |
+
/* Sit tight, rock 'n roll! */
|
705 |
+
self::bubble_sort ($info, $start, $end - $start);
|
706 |
+
|
707 |
+
/* Find base again */
|
708 |
+
$base = $end;
|
709 |
+
for ($i = $start; $i < $end; $i++) {
|
710 |
+
if ($info[$i]['indic_position'] == self::POS_BASE_C) {
|
711 |
+
$base = $i;
|
712 |
+
break;
|
713 |
+
}
|
714 |
+
}
|
715 |
+
|
716 |
+
if ($scriptblock != UCDN::SCRIPT_KHMER) {
|
717 |
+
/* Reph */
|
718 |
+
for ($i = $start; $i < $end; $i++) {
|
719 |
+
if ($info[$i]['indic_position'] == self::POS_RA_TO_BECOME_REPH) {
|
720 |
+
$info[$i]['mask'] |= self::FLAG(self::RPHF);
|
721 |
+
}
|
722 |
+
}
|
723 |
+
|
724 |
+
/* Pre-base */
|
725 |
+
$mask = self::FLAG(self::HALF);
|
726 |
+
for ($i = $start; $i < $base; $i++) {
|
727 |
+
$info[$i]['mask'] |= $mask;
|
728 |
+
}
|
729 |
+
}
|
730 |
+
|
731 |
+
/* Post-base */
|
732 |
+
$mask = (self::FLAG(self::BLWF) | self::FLAG(self::ABVF) | self::FLAG(self::PSTF));
|
733 |
+
for ($i = $base + 1; $i < $end; $i++) {
|
734 |
+
$info[$i]['mask'] |= $mask;
|
735 |
+
}
|
736 |
+
|
737 |
+
|
738 |
+
if ($scriptblock != UCDN::SCRIPT_KHMER) {
|
739 |
+
if (!defined("OMIT_INDIC_FIX_3") || OMIT_INDIC_FIX_3 != 1) {
|
740 |
+
/* INDIC_FIX_3 */
|
741 |
+
/* Find a (pre-base) Consonant, Halant,Ra sequence and mark Halant|Ra for below-base BLWF processing. */
|
742 |
+
// TEST CASE ক্র্ক in FreeSans versus Vrinda
|
743 |
+
if (($base - $start) >= 3) {
|
744 |
+
for ($i = $start; $i < ($base-2); $i++) {
|
745 |
+
if (self::is_consonant($info[$i])) {
|
746 |
+
if (self::is_halant_or_coeng($info[$i + 1]) && self::is_ra($info[$i + 2]['uni'])) {
|
747 |
+
// If would substitute Halant+Ra...BLWF
|
748 |
+
if (isset($GSUBdata['blwf'][$info[$i+2]['uni']])) {
|
749 |
+
$info[$i + 1]['mask'] |= self::FLAG(self::BLWF);
|
750 |
+
$info[$i + 2]['mask'] |= self::FLAG(self::BLWF);
|
751 |
+
}
|
752 |
+
/* If would not substitute as blwf, mark Ra+Halant for RPHF using following Halant (if present) */
|
753 |
+
else if (self::is_halant_or_coeng($info[$i + 3])) {
|
754 |
+
$info[$i + 2]['mask'] |= self::FLAG(self::RPHF);
|
755 |
+
$info[$i + 3]['mask'] |= self::FLAG(self::RPHF);
|
756 |
+
}
|
757 |
+
break;
|
758 |
+
}
|
759 |
+
}
|
760 |
+
}
|
761 |
+
}
|
762 |
+
}
|
763 |
+
}
|
764 |
+
|
765 |
+
|
766 |
+
|
767 |
+
if ($is_old_spec && $scriptblock == UCDN::SCRIPT_DEVANAGARI) {
|
768 |
+
/* Old-spec eye-lash Ra needs special handling. From the spec:
|
769 |
+
* "The feature 'below-base form' is applied to consonants
|
770 |
+
* having below-base forms and following the base consonant.
|
771 |
+
* The exception is vattu, which may appear below half forms
|
772 |
+
* as well as below the base glyph. The feature 'below-base
|
773 |
+
* form' will be applied to all such occurrences of Ra as well."
|
774 |
+
*
|
775 |
+
* Test case: U+0924,U+094D,U+0930,U+094d,U+0915
|
776 |
+
* with Sanskrit 2003 font.
|
777 |
+
*
|
778 |
+
* However, note that Ra,Halant,ZWJ is the correct way to
|
779 |
+
* request eyelash form of Ra, so we wouldbn't inhibit it
|
780 |
+
* in that sequence.
|
781 |
+
*
|
782 |
+
* Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915
|
783 |
+
*/
|
784 |
+
for ($i = $start; ($i + 1) < $base; $i++) {
|
785 |
+
if ($info[$i]['indic_category'] == self::OT_Ra && $info[$i+1]['indic_category'] == self::OT_H &&
|
786 |
+
($i + 2 == $base || $info[$i+2]['indic_category'] != self::OT_ZWJ)) {
|
787 |
+
$info[$i]['mask'] |= self::FLAG(self::BLWF);
|
788 |
+
$info[$i+1]['mask'] |= self::FLAG(self::BLWF);
|
789 |
+
}
|
790 |
+
}
|
791 |
+
}
|
792 |
+
|
793 |
+
if ($scriptblock != UCDN::SCRIPT_KHMER) {
|
794 |
+
if (count($GSUBdata['pref']) && $base + 2 < $end) {
|
795 |
+
/* Find a Halant,Ra sequence and mark it for pre-base processing. */
|
796 |
+
for ($i = $base + 1; $i + 1 < $end; $i++) {
|
797 |
+
// If old_spec find Ra-Halant...
|
798 |
+
if ((isset($GSUBdata['pref'][$info[$i + 1]['uni']]) && self::is_halant_or_coeng($info[$i]) && self::is_ra($info[$i + 1]['uni']) ) ||
|
799 |
+
($is_old_spec && isset($GSUBdata['pref'][$info[$i]['uni']]) && self::is_halant_or_coeng($info[$i + 1]) && self::is_ra($info[$i]['uni']) )
|
800 |
+
) {
|
801 |
+
$info[$i++]['mask'] |= self::FLAG(self::PREF);
|
802 |
+
$info[$i++]['mask'] |= self::FLAG(self::PREF);
|
803 |
+
break;
|
804 |
+
}
|
805 |
+
}
|
806 |
+
}
|
807 |
+
}
|
808 |
+
|
809 |
+
|
810 |
+
/* Apply ZWJ/ZWNJ effects */
|
811 |
+
for ($i = $start + 1; $i < $end; $i++) {
|
812 |
+
if (self::is_joiner ($info[$i])) {
|
813 |
+
$non_joiner = ($info[$i]['indic_category'] == self::OT_ZWNJ);
|
814 |
+
$j = $i;
|
815 |
+
while ($j > $start) {
|
816 |
+
if (defined("OMIT_INDIC_FIX_4") && OMIT_INDIC_FIX_4 == 1) {
|
817 |
+
// INDIC_FIX_4 = do nothing - carry on //
|
818 |
+
// ZWNJ should block H C from forming blwf post-base - need to unmask backwards beyond first consonant arrived at //
|
819 |
+
if (!self::is_consonant($info[$j])) { break; }
|
820 |
+
}
|
821 |
+
$j--;
|
822 |
+
|
823 |
+
/* ZWJ/ZWNJ should disable CJCT. They do that by simply
|
824 |
+
* being there, since we don't skip them for the CJCT
|
825 |
+
* feature (ie. F_MANUAL_ZWJ) */
|
826 |
+
|
827 |
+
/* A ZWNJ disables HALF. */
|
828 |
+
if ($non_joiner) {
|
829 |
+
$info[$j]['mask'] &= ~(self::FLAG(self::HALF) | self::FLAG(self::BLWF));
|
830 |
+
}
|
831 |
+
|
832 |
+
}
|
833 |
+
}
|
834 |
+
}
|
835 |
+
}
|
836 |
+
|
837 |
+
public static function final_reordering (&$info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec) {
|
838 |
+
$count = count($info);
|
839 |
+
if (!$count) return;
|
840 |
+
$last = 0;
|
841 |
+
$last_syllable = $info[0]['syllable'];
|
842 |
+
for ($i = 1; $i < $count; $i++) {
|
843 |
+
if ($last_syllable != $info[$i]['syllable']) {
|
844 |
+
self::final_reordering_syllable ($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $i);
|
845 |
+
$last = $i;
|
846 |
+
$last_syllable = $info[$last]['syllable'];
|
847 |
+
}
|
848 |
+
}
|
849 |
+
self::final_reordering_syllable ($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $count);
|
850 |
+
|
851 |
+
}
|
852 |
+
|
853 |
+
public static function final_reordering_syllable (&$info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $start, $end) {
|
854 |
+
|
855 |
+
/* 4. Final reordering:
|
856 |
+
*
|
857 |
+
* After the localized forms and basic shaping forms GSUB features have been
|
858 |
+
* applied (see below), the shaping engine performs some final glyph
|
859 |
+
* reordering before applying all the remaining font features to the entire
|
860 |
+
* cluster.
|
861 |
+
*/
|
862 |
+
|
863 |
+
/* Find base again */
|
864 |
+
for ($base = $start; $base < $end; $base++)
|
865 |
+
if ($info[$base]['indic_position'] >= self::POS_BASE_C) {
|
866 |
+
if ($start < $base && $info[$base]['indic_position'] > self::POS_BASE_C)
|
867 |
+
$base--;
|
868 |
+
break;
|
869 |
+
}
|
870 |
+
if ($base == $end && $start < $base && $info[$base - 1]['indic_category'] != self::OT_ZWJ)
|
871 |
+
$base--;
|
872 |
+
while ($start < $base && isset($info[$base]) && ($info[$base]['indic_category'] == self::OT_H || $info[$base]['indic_category'] == self::OT_N))
|
873 |
+
$base--;
|
874 |
+
|
875 |
+
|
876 |
+
/* o Reorder matras:
|
877 |
+
*
|
878 |
+
* If a pre-base matra character had been reordered before applying basic
|
879 |
+
* features, the glyph can be moved closer to the main consonant based on
|
880 |
+
* whether half-forms had been formed. Actual position for the matra is
|
881 |
+
* defined as "after last standalone halant glyph, after initial matra
|
882 |
+
* position and before the main consonant". If ZWJ or ZWNJ follow this
|
883 |
+
* halant, position is moved after it.
|
884 |
+
*/
|
885 |
+
|
886 |
+
|
887 |
+
if ($start + 1 < $end && $start < $base) { /* Otherwise there can't be any pre-base matra characters. */
|
888 |
+
/* If we lost track of base, alas, position before last thingy. */
|
889 |
+
$new_pos = ($base == $end) ? $base - 2 : $base - 1;
|
890 |
+
|
891 |
+
/* Malayalam / Tamil do not have "half" forms or explicit virama forms.
|
892 |
+
* The glyphs formed by 'half' are Chillus or ligated explicit viramas.
|
893 |
+
* We want to position matra after them.
|
894 |
+
*/
|
895 |
+
if ($scriptblock != UCDN::SCRIPT_MALAYALAM && $scriptblock != UCDN::SCRIPT_TAMIL) {
|
896 |
+
while ($new_pos > $start && !(self::is_one_of ($info[$new_pos], (self::FLAG(self::OT_M) | self::FLAG(self::OT_H) | self::FLAG(self::OT_Coeng)))))
|
897 |
+
$new_pos--;
|
898 |
+
|
899 |
+
/* If we found no Halant we are done.
|
900 |
+
* Otherwise only proceed if the Halant does
|
901 |
+
* not belong to the Matra itself! */
|
902 |
+
if (self::is_halant_or_coeng($info[$new_pos]) && $info[$new_pos]['indic_position'] != self::POS_PRE_M) {
|
903 |
+
/* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
|
904 |
+
if ($new_pos + 1 < $end && self::is_joiner($info[$new_pos + 1]))
|
905 |
+
$new_pos++;
|
906 |
+
}
|
907 |
+
else
|
908 |
+
$new_pos = $start; /* No move. */
|
909 |
+
}
|
910 |
+
|
911 |
+
if ($start < $new_pos && $info[$new_pos]['indic_position'] != self::POS_PRE_M) {
|
912 |
+
/* Now go see if there's actually any matras... */
|
913 |
+
for ($i = $new_pos; $i > $start; $i--)
|
914 |
+
if ($info[$i - 1]['indic_position'] == self::POS_PRE_M) {
|
915 |
+
$old_pos = $i - 1;
|
916 |
+
//memmove (&info[$old_pos], &info[$old_pos + 1], ($new_pos - $old_pos) * sizeof ($info[0]));
|
917 |
+
self::_move_info_pos($info, $old_pos, $new_pos+1);
|
918 |
+
|
919 |
+
if ($old_pos < $base && $base <= $new_pos) /* Shouldn't actually happen. */
|
920 |
+
$base--;
|
921 |
+
$new_pos--;
|
922 |
+
}
|
923 |
+
}
|
924 |
+
}
|
925 |
+
|
926 |
+
|
927 |
+
/* o Reorder reph:
|
928 |
+
*
|
929 |
+
* Reph's original position is always at the beginning of the syllable,
|
930 |
+
* (i.e. it is not reordered at the character reordering stage). However,
|
931 |
+
* it will be reordered according to the basic-forms shaping results.
|
932 |
+
* Possible positions for reph, depending on the script, are; after main,
|
933 |
+
* before post-base consonant forms, and after post-base consonant forms.
|
934 |
+
*/
|
935 |
+
|
936 |
+
/* If there's anything after the Ra that has the REPH pos, it ought to be halant.
|
937 |
+
* Which means that the font has failed to ligate the Reph. In which case, we
|
938 |
+
* shouldn't move. */
|
939 |
+
if ($start + 1 < $end &&
|
940 |
+
$info[$start]['indic_position'] == self::POS_RA_TO_BECOME_REPH && $info[$start + 1]['indic_position'] != self::POS_RA_TO_BECOME_REPH) {
|
941 |
+
$reph_pos = $indic_config[3];
|
942 |
+
$skip_to_reph_step_5 = false;
|
943 |
+
$skip_to_reph_move = false;
|
944 |
+
|
945 |
+
/* 1. If reph should be positioned after post-base consonant forms,
|
946 |
+
* proceed to step 5.
|
947 |
+
*/
|
948 |
+
if ($reph_pos == self::REPH_POS_AFTER_POST) {
|
949 |
+
$skip_to_reph_step_5 = true;
|
950 |
+
}
|
951 |
+
|
952 |
+
/* 2. If the reph repositioning class is not after post-base: target
|
953 |
+
* position is after the first explicit halant glyph between the
|
954 |
+
* first post-reph consonant and last main consonant. If ZWJ or ZWNJ
|
955 |
+
* are following this halant, position is moved after it. If such
|
956 |
+
* position is found, this is the target position. Otherwise,
|
957 |
+
* proceed to the next step.
|
958 |
+
*
|
959 |
+
* Note: in old-implementation fonts, where classifications were
|
960 |
+
* fixed in shaping engine, there was no case where reph position
|
961 |
+
* will be found on this step.
|
962 |
+
*/
|
963 |
+
|
964 |
+
if (!$skip_to_reph_step_5) {
|
965 |
+
|
966 |
+
$new_reph_pos = $start + 1;
|
967 |
+
|
968 |
+
while ($new_reph_pos < $base && !self::is_halant_or_coeng($info[$new_reph_pos]))
|
969 |
+
$new_reph_pos++;
|
970 |
+
|
971 |
+
if ($new_reph_pos < $base && self::is_halant_or_coeng($info[$new_reph_pos])) {
|
972 |
+
/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
|
973 |
+
if ($new_reph_pos + 1 < $base && self::is_joiner ($info[$new_reph_pos + 1]))
|
974 |
+
$new_reph_pos++;
|
975 |
+
$skip_to_reph_move =true;
|
976 |
+
}
|
977 |
+
}
|
978 |
+
|
979 |
+
/* 3. If reph should be repositioned after the main consonant: find the
|
980 |
+
* first consonant not ligated with main, or find the first
|
981 |
+
* consonant that is not a potential pre-base reordering Ra.
|
982 |
+
*/
|
983 |
+
if ($reph_pos == self::REPH_POS_AFTER_MAIN && !$skip_to_reph_move && !$skip_to_reph_step_5) {
|
984 |
+
$new_reph_pos = $base;
|
985 |
+
/* XXX Skip potential pre-base reordering Ra. */
|
986 |
+
while ($new_reph_pos + 1 < $end && $info[$new_reph_pos + 1]['indic_position'] <= self::POS_AFTER_MAIN)
|
987 |
+
$new_reph_pos++;
|
988 |
+
if ($new_reph_pos < $end)
|
989 |
+
$skip_to_reph_move =true;
|
990 |
+
}
|
991 |
+
|
992 |
+
/* 4. If reph should be positioned before post-base consonant, find
|
993 |
+
* first post-base classified consonant not ligated with main. If no
|
994 |
+
* consonant is found, the target position should be before the
|
995 |
+
* first matra, syllable modifier sign or vedic sign.
|
996 |
+
*/
|
997 |
+
/* This is our take on what step 4 is trying to say (and failing, BADLY). */
|
998 |
+
if ($reph_pos == self::REPH_POS_AFTER_SUB && !$skip_to_reph_move && !$skip_to_reph_step_5) {
|
999 |
+
$new_reph_pos = $base;
|
1000 |
+
while ($new_reph_pos < $end && isset($info[$new_reph_pos + 1]['indic_position']) &&
|
1001 |
+
!( self::FLAG($info[$new_reph_pos + 1]['indic_position']) & (self::FLAG(self::POS_POST_C) | self::FLAG(self::POS_AFTER_POST) | self::FLAG(self::POS_SMVD)))) {
|
1002 |
+
$new_reph_pos++;
|
1003 |
+
}
|
1004 |
+
if ($new_reph_pos < $end) { $skip_to_reph_move =true; }
|
1005 |
+
}
|
1006 |
+
|
1007 |
+
/* 5. If no consonant is found in steps 3 or 4, move reph to a position
|
1008 |
+
* immediately before the first post-base matra, syllable modifier
|
1009 |
+
* sign or vedic sign that has a reordering class after the intended
|
1010 |
+
* reph position. For example, if the reordering position for reph
|
1011 |
+
* is post-main, it will skip above-base matras that also have a
|
1012 |
+
* post-main position.
|
1013 |
+
*/
|
1014 |
+
if (!$skip_to_reph_move) {
|
1015 |
+
/* Copied from step 2. */
|
1016 |
+
$new_reph_pos = $start + 1;
|
1017 |
+
while ($new_reph_pos < $base && !self::is_halant_or_coeng($info[$new_reph_pos]))
|
1018 |
+
$new_reph_pos++;
|
1019 |
+
|
1020 |
+
if ($new_reph_pos < $base && self::is_halant_or_coeng($info[$new_reph_pos])) {
|
1021 |
+
/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
|
1022 |
+
if ($new_reph_pos + 1 < $base && self::is_joiner($info[$new_reph_pos + 1]))
|
1023 |
+
$new_reph_pos++;
|
1024 |
+
$skip_to_reph_move =true;
|
1025 |
+
}
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
|
1029 |
+
/* 6. Otherwise, reorder reph to the end of the syllable.
|
1030 |
+
*/
|
1031 |
+
if (!$skip_to_reph_move) {
|
1032 |
+
$new_reph_pos = $end - 1;
|
1033 |
+
while ($new_reph_pos > $start && $info[$new_reph_pos]['indic_position'] == self::POS_SMVD)
|
1034 |
+
$new_reph_pos--;
|
1035 |
+
|
1036 |
+
/*
|
1037 |
+
* If the Reph is to be ending up after a Matra,Halant sequence,
|
1038 |
+
* position it before that Halant so it can interact with the Matra.
|
1039 |
+
* However, if it's a plain Consonant,Halant we shouldn't do that.
|
1040 |
+
* Uniscribe doesn't do this.
|
1041 |
+
* TEST: U+0930,U+094D,U+0915,U+094B,U+094D
|
1042 |
+
*/
|
1043 |
+
//if (!$hb_options.uniscribe_bug_compatible && self::is_halant_or_coeng($info[$new_reph_pos])) {
|
1044 |
+
if (self::is_halant_or_coeng($info[$new_reph_pos])) {
|
1045 |
+
for ($i = $base + 1; $i < $new_reph_pos; $i++)
|
1046 |
+
if ($info[$i]['indic_category'] == self::OT_M) {
|
1047 |
+
/* Ok, got it. */
|
1048 |
+
$new_reph_pos--;
|
1049 |
+
}
|
1050 |
+
}
|
1051 |
+
}
|
1052 |
+
|
1053 |
+
|
1054 |
+
/* Move */
|
1055 |
+
self::_move_info_pos($info, $start, $new_reph_pos+1);
|
1056 |
+
|
1057 |
+
if ($start < $base && $base <= $new_reph_pos) {
|
1058 |
+
$base--;
|
1059 |
+
}
|
1060 |
+
}
|
1061 |
+
|
1062 |
+
|
1063 |
+
/* o Reorder pre-base reordering consonants:
|
1064 |
+
*
|
1065 |
+
* If a pre-base reordering consonant is found, reorder it according to
|
1066 |
+
* the following rules:
|
1067 |
+
*/
|
1068 |
+
|
1069 |
+
|
1070 |
+
if (count($GSUBdata['pref']) && $base + 1 < $end) { /* Otherwise there can't be any pre-base reordering Ra. */
|
1071 |
+
for ($i = $base + 1; $i < $end; $i++) {
|
1072 |
+
if ($info[$i]['mask'] & self::FLAG(self::PREF)) {
|
1073 |
+
/* 1. Only reorder a glyph produced by substitution during application
|
1074 |
+
* of the <pref> feature. (Note that a font may shape a Ra consonant with
|
1075 |
+
* the feature generally but block it in certain contexts.)
|
1076 |
+
*/
|
1077 |
+
// ??? Need to TEST if actual substitution has occurred
|
1078 |
+
if ($i + 1 == $end || ($info[$i + 1]['mask'] & self::FLAG(self::PREF)) == 0) {
|
1079 |
+
/*
|
1080 |
+
* 2. Try to find a target position the same way as for pre-base matra.
|
1081 |
+
* If it is found, reorder pre-base consonant glyph.
|
1082 |
+
*
|
1083 |
+
* 3. If position is not found, reorder immediately before main
|
1084 |
+
* consonant.
|
1085 |
+
*/
|
1086 |
+
$new_pos = $base;
|
1087 |
+
/* Malayalam / Tamil do not have "half" forms or explicit virama forms.
|
1088 |
+
* The glyphs formed by 'half' are Chillus or ligated explicit viramas.
|
1089 |
+
* We want to position matra after them.
|
1090 |
+
*/
|
1091 |
+
if ($scriptblock != UCDN::SCRIPT_MALAYALAM && $scriptblock != UCDN::SCRIPT_TAMIL) {
|
1092 |
+
while ($new_pos > $start &&
|
1093 |
+
!(self::is_one_of($info[$new_pos - 1], self::FLAG(self::OT_M) | self::FLAG(self::OT_H) | self::FLAG(self::OT_Coeng))))
|
1094 |
+
$new_pos--;
|
1095 |
+
|
1096 |
+
/* In Khmer coeng model, a V,Ra can go *after* matras. If it goes after a
|
1097 |
+
* split matra, it should be reordered to *before* the left part of such matra. */
|
1098 |
+
if ($new_pos > $start && $info[$new_pos - 1]['indic_category'] == self::OT_M) {
|
1099 |
+
$old_pos = i;
|
1100 |
+
for ($i = $base + 1; $i < $old_pos; $i++)
|
1101 |
+
if ($info[$i]['indic_category'] == self::OT_M) {
|
1102 |
+
$new_pos--;
|
1103 |
+
break;
|
1104 |
+
}
|
1105 |
+
}
|
1106 |
+
}
|
1107 |
+
|
1108 |
+
if ($new_pos > $start && self::is_halant_or_coeng($info[$new_pos - 1])) {
|
1109 |
+
/* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
|
1110 |
+
if ($new_pos < $end && self::is_joiner($info[$new_pos]))
|
1111 |
+
$new_pos++;
|
1112 |
+
}
|
1113 |
+
|
1114 |
+
$old_pos = $i;
|
1115 |
+
self::_move_info_pos($info, $old_pos, $new_pos);
|
1116 |
+
|
1117 |
+
if ($new_pos <= $base && $base < $old_pos)
|
1118 |
+
$base++;
|
1119 |
+
}
|
1120 |
+
|
1121 |
+
break;
|
1122 |
+
}
|
1123 |
+
}
|
1124 |
+
}
|
1125 |
+
|
1126 |
+
|
1127 |
+
/* Apply 'init' to the Left Matra if it's a word start. */
|
1128 |
+
if ($info[$start]['indic_position'] == self::POS_PRE_M &&
|
1129 |
+
($start==0 ||
|
1130 |
+
($info[$start - 1]['general_category'] < UCDN::UNICODE_GENERAL_CATEGORY_FORMAT || $info[$start - 1]['general_category'] > UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
|
1131 |
+
)) {
|
1132 |
+
$info[$start]['mask'] |= self::FLAG(self::INIT);
|
1133 |
+
}
|
1134 |
+
|
1135 |
+
|
1136 |
+
/*
|
1137 |
+
* Finish off and go home!
|
1138 |
+
*/
|
1139 |
+
|
1140 |
+
}
|
1141 |
+
|
1142 |
+
function _move_info_pos(&$info, $from, $to) {
|
1143 |
+
$t = array();
|
1144 |
+
$t[0] = $info[$from];
|
1145 |
+
if ($from > $to) {
|
1146 |
+
array_splice($info, $from, 1);
|
1147 |
+
array_splice($info, $to, 0, $t);
|
1148 |
+
}
|
1149 |
+
else {
|
1150 |
+
array_splice($info, $to, 0, $t);
|
1151 |
+
array_splice($info, $from, 1);
|
1152 |
+
}
|
1153 |
+
}
|
1154 |
+
|
1155 |
+
|
1156 |
+
public static $ra_chars = array(
|
1157 |
+
0x0930 => 1, /* Devanagari */
|
1158 |
+
0x09B0 => 1, /* Bengali */
|
1159 |
+
0x09F0 => 1, /* Bengali (Assamese) */
|
1160 |
+
0x0A30 => 1, /* Gurmukhi */ /* No Reph */
|
1161 |
+
0x0AB0 => 1, /* Gujarati */
|
1162 |
+
0x0B30 => 1, /* Oriya */
|
1163 |
+
0x0BB0 => 1, /* Tamil */ /* No Reph */
|
1164 |
+
0x0C30 => 1, /* Telugu */ /* Reph formed only with ZWJ */
|
1165 |
+
0x0CB0 => 1, /* Kannada */
|
1166 |
+
0x0D30 => 1, /* Malayalam */ /* No Reph, Logical Repha */
|
1167 |
+
|
1168 |
+
0x0DBB => 1, /* Sinhala */ /* Reph formed only with ZWJ */
|
1169 |
+
0x179A => 1, /* Khmer */ /* No Reph, Visual Repha */
|
1170 |
+
);
|
1171 |
+
|
1172 |
+
public static function is_ra ($u) {
|
1173 |
+
if (isset(self::$ra_chars[$u])) return true;
|
1174 |
+
return false;
|
1175 |
+
}
|
1176 |
+
|
1177 |
+
public static function is_one_of ($info, $flags) {
|
1178 |
+
if (isset($info['is_ligature']) && $info['is_ligature']) return false; /* If it ligated, all bets are off. */
|
1179 |
+
return !!(self::FLAG($info['indic_category']) & $flags);
|
1180 |
+
}
|
1181 |
+
|
1182 |
+
public static function is_joiner($info) {
|
1183 |
+
return self::is_one_of ($info, (self::FLAG(self::OT_ZWJ) | self::FLAG(self::OT_ZWNJ)));
|
1184 |
+
}
|
1185 |
+
|
1186 |
+
|
1187 |
+
/* Vowels and placeholders treated as if they were consonants. */
|
1188 |
+
public static function is_consonant($info) {
|
1189 |
+
return self::is_one_of($info, (self::FLAG(self::OT_C) | self::FLAG(self::OT_CM) | self::FLAG(self::OT_Ra) | self::FLAG(self::OT_V) | self::FLAG(self::OT_NBSP) | self::FLAG(self::OT_DOTTEDCIRCLE)));
|
1190 |
+
}
|
1191 |
+
|
1192 |
+
|
1193 |
+
public static function is_halant_or_coeng($info) {
|
1194 |
+
return self::is_one_of($info, (self::FLAG(self::OT_H) | self::FLAG(self::OT_Coeng)));
|
1195 |
+
}
|
1196 |
+
|
1197 |
+
|
1198 |
+
|
1199 |
+
// From hb-private.hh
|
1200 |
+
public static function in_range ($u, $lo, $hi) {
|
1201 |
+
if ( (($lo^$hi) & $lo) == 0 && (($lo^$hi) & $hi) == ($lo^$hi) && (($lo^$hi) & (($lo^$hi) + 1)) == 0 )
|
1202 |
+
return ($u & ~($lo^$hi)) == $lo;
|
1203 |
+
else
|
1204 |
+
return $lo <= $u && $u <= $hi;
|
1205 |
+
}
|
1206 |
+
// From hb-private.hh
|
1207 |
+
public static function FLAG($x) { return (1<<($x)); }
|
1208 |
+
|
1209 |
+
|
1210 |
+
// BELOW from hb-ot-shape-complex-indic.cc
|
1211 |
+
|
1212 |
+
/*
|
1213 |
+
* Indic configurations.
|
1214 |
+
*/
|
1215 |
+
|
1216 |
+
// base_position
|
1217 |
+
const BASE_POS_FIRST = 0;
|
1218 |
+
const BASE_POS_LAST = 1;
|
1219 |
+
|
1220 |
+
// reph_position
|
1221 |
+
const REPH_POS_DEFAULT = 10; // POS_BEFORE_POST,
|
1222 |
+
|
1223 |
+
const REPH_POS_AFTER_MAIN = 5; // POS_AFTER_MAIN,
|
1224 |
+
const REPH_POS_BEFORE_SUB = 7; // POS_BEFORE_SUB,
|
1225 |
+
const REPH_POS_AFTER_SUB = 9; // POS_AFTER_SUB,
|
1226 |
+
const REPH_POS_BEFORE_POST = 10; // POS_BEFORE_POST,
|
1227 |
+
const REPH_POS_AFTER_POST = 12; // POS_AFTER_POST
|
1228 |
+
|
1229 |
+
// reph_mode
|
1230 |
+
const REPH_MODE_IMPLICIT = 0; /* Reph formed out of initial Ra,H sequence. */
|
1231 |
+
const REPH_MODE_EXPLICIT = 1; /* Reph formed out of initial Ra,H,ZWJ sequence. */
|
1232 |
+
const REPH_MODE_VIS_REPHA = 2; /* Encoded Repha character, no reordering needed. */
|
1233 |
+
const REPH_MODE_LOG_REPHA = 3; /* Encoded Repha character, needs reordering. */
|
1234 |
+
|
1235 |
+
|
1236 |
+
|
1237 |
+
/*
|
1238 |
+
struct of indic_configs{
|
1239 |
+
KEY - script;
|
1240 |
+
0 - has_old_spec;
|
1241 |
+
1 - virama;
|
1242 |
+
2 - base_pos;
|
1243 |
+
3 - reph_pos;
|
1244 |
+
4 - reph_mode;
|
1245 |
+
};
|
1246 |
+
*/
|
1247 |
+
|
1248 |
+
public static $indic_configs = array( /* index is SCRIPT_number from UCDN */
|
1249 |
+
9 => array(true, 0x094D, 1, 10, 0),
|
1250 |
+
10 => array(true, 0x09CD, 1, 9, 0),
|
1251 |
+
11 => array(true, 0x0A4D, 1, 7, 0),
|
1252 |
+
12 => array(true, 0x0ACD, 1, 10, 0),
|
1253 |
+
13 => array(true, 0x0B4D, 1, 5, 0),
|
1254 |
+
14 => array(true, 0x0BCD, 1, 12, 0),
|
1255 |
+
15 => array(true, 0x0C4D, 1, 12, 1),
|
1256 |
+
16 => array(true, 0x0CCD, 1, 12, 0),
|
1257 |
+
17 => array(true, 0x0D4D, 1, 5, 3),
|
1258 |
+
18 => array(false, 0x0DCA, 0, 5, 1), /* Sinhala */
|
1259 |
+
30 => array(false, 0x17D2, 0, 10, 2), /* Khmer */
|
1260 |
+
84 => array(false, 0xA9C0, 1, 10, 0), /* Javanese */
|
1261 |
+
|
1262 |
+
);
|
1263 |
+
|
1264 |
+
|
1265 |
+
|
1266 |
+
/*
|
1267 |
+
|
1268 |
+
// from "hb-ot-shape-complex-indic-table.cc"
|
1269 |
+
|
1270 |
+
|
1271 |
+
const ISC_A = 0; // INDIC_SYLLABIC_CATEGORY_AVAGRAHA Avagraha
|
1272 |
+
const ISC_Bi = 8; // INDIC_SYLLABIC_CATEGORY_BINDU Bindu
|
1273 |
+
const ISC_C = 1; // INDIC_SYLLABIC_CATEGORY_CONSONANT Consonant
|
1274 |
+
const ISC_CD = 1; // INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD Consonant_Dead
|
1275 |
+
const ISC_CF = 17; // INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL Consonant_Final
|
1276 |
+
const ISC_CHL = 1; // INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER Consonant_Head_Letter
|
1277 |
+
const ISC_CM = 17; // INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL Consonant_Medial
|
1278 |
+
const ISC_CP = 11; // INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER Consonant_Placeholder
|
1279 |
+
const ISC_CR = 15; // INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA Consonant_Repha
|
1280 |
+
const ISC_CS = 1; // INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED Consonant_Subjoined
|
1281 |
+
const ISC_ML = 0; // INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER Modifying_Letter
|
1282 |
+
const ISC_N = 3; // INDIC_SYLLABIC_CATEGORY_NUKTA Nukta
|
1283 |
+
const ISC_x = 0; // INDIC_SYLLABIC_CATEGORY_OTHER Other
|
1284 |
+
const ISC_RS = 13; // INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER Register_Shifter
|
1285 |
+
const ISC_TL = 0; // INDIC_SYLLABIC_CATEGORY_TONE_LETTER Tone_Letter
|
1286 |
+
const ISC_TM = 3; // INDIC_SYLLABIC_CATEGORY_TONE_MARK Tone_Mark
|
1287 |
+
const ISC_V = 4; // INDIC_SYLLABIC_CATEGORY_VIRAMA Virama
|
1288 |
+
const ISC_Vs = 8; // INDIC_SYLLABIC_CATEGORY_VISARGA Visarga
|
1289 |
+
const ISC_Vo = 2; // INDIC_SYLLABIC_CATEGORY_VOWEL Vowel
|
1290 |
+
const ISC_M = 7; // INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT Vowel_Dependent
|
1291 |
+
const ISC_VI = 2; // INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT Vowel_Independent
|
1292 |
+
|
1293 |
+
const IMC_B = 8; // INDIC_MATRA_CATEGORY_BOTTOM Bottom
|
1294 |
+
const IMC_BR = 11; // INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT Bottom_And_Right
|
1295 |
+
const IMC_I = 15; // INDIC_MATRA_CATEGORY_INVISIBLE Invisible
|
1296 |
+
const IMC_L = 3; // INDIC_MATRA_CATEGORY_LEFT Left
|
1297 |
+
const IMC_LR = 11; // INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT Left_And_Right
|
1298 |
+
const IMC_x = 15; // INDIC_MATRA_CATEGORY_NOT_APPLICABLE Not_Applicable
|
1299 |
+
const IMC_O = 5; // INDIC_MATRA_CATEGORY_OVERSTRUCK Overstruck
|
1300 |
+
const IMC_R = 11; // INDIC_MATRA_CATEGORY_RIGHT Right
|
1301 |
+
const IMC_T = 6; // INDIC_MATRA_CATEGORY_TOP Top
|
1302 |
+
const IMC_TB = 8; // INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM Top_And_Bottom
|
1303 |
+
const IMC_TBR = 11; // INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT Top_And_Bottom_And_Right
|
1304 |
+
const IMC_TL = 6; // INDIC_MATRA_CATEGORY_TOP_AND_LEFT Top_And_Left
|
1305 |
+
const IMC_TLR = 11; // INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT Top_And_Left_And_Right
|
1306 |
+
const IMC_TR = 11; // INDIC_MATRA_CATEGORY_TOP_AND_RIGHT Top_And_Right
|
1307 |
+
const IMC_VOL = 2; // INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT Visual_Order_Left
|
1308 |
+
|
1309 |
+
If in original table = _(C,x), that = ISC_C,IMC_x
|
1310 |
+
Value is IMC_x << 8 (or IMC_x * 256) = 3840
|
1311 |
+
plus ISC_C = 1, so = 3841
|
1312 |
+
|
1313 |
+
*/
|
1314 |
+
|
1315 |
+
|
1316 |
+
|
1317 |
+
public static $indic_table = array(
|
1318 |
+
|
1319 |
+
/* Devanagari (0900..097F) */
|
1320 |
+
|
1321 |
+
/* 0900 */ 3848,3848,3848,3848,3842,3842,3842,3842,
|
1322 |
+
/* 0908 */ 3842,3842,3842,3842,3842,3842,3842,3842,
|
1323 |
+
/* 0910 */ 3842,3842,3842,3842,3842, 3841, 3841, 3841,
|
1324 |
+
/* 0918 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1325 |
+
/* 0920 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1326 |
+
/* 0928 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1327 |
+
/* 0930 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1328 |
+
/* 0938 */ 3841, 3841, 1543, 2823, 3843, 3840, 2823, 775,
|
1329 |
+
/* 0940 */ 2823, 2055, 2055, 2055, 2055, 1543, 1543, 1543,
|
1330 |
+
/* 0948 */ 1543, 2823, 2823, 2823, 2823, 2052, 775, 2823,
|
1331 |
+
/* 0950 */ 3840, 3840, 3840, 3840, 3840, 1543, 2055, 2055,
|
1332 |
+
/* 0958 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1333 |
+
/* 0960 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
|
1334 |
+
/* 0968 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1335 |
+
/* 0970 */ 3840, 3840,3842,3842,3842,3842,3842,3842,
|
1336 |
+
/* 0978 */ 3840, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1337 |
+
|
1338 |
+
/* Bengali (0980..09FF) */
|
1339 |
+
|
1340 |
+
/* 0980 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
|
1341 |
+
/* 0988 */ 3842,3842,3842,3842,3842, 3840, 3840,3842,
|
1342 |
+
/* 0990 */ 3842, 3840, 3840,3842,3842, 3841, 3841, 3841,
|
1343 |
+
/* 0998 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1344 |
+
/* 09A0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1345 |
+
/* 09A8 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
|
1346 |
+
/* 09B0 */ 3841, 3840, 3841, 3840, 3840, 3840, 3841, 3841,
|
1347 |
+
/* 09B8 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 775,
|
1348 |
+
/* 09C0 */ 2823, 2055, 2055, 2055, 2055, 3840, 3840, 775,
|
1349 |
+
/* 09C8 */ 775, 3840, 3840,2823,2823, 2052,3841, 3840,
|
1350 |
+
/* 09D0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 2823,
|
1351 |
+
/* 09D8 */ 3840, 3840, 3840, 3840, 3841, 3841, 3840, 3841,
|
1352 |
+
/* 09E0 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
|
1353 |
+
/* 09E8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1354 |
+
/* 09F0 */ 3841, 3841, 3840, 3840, 3840, 3840, 3840, 3840,
|
1355 |
+
/* 09F8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1356 |
+
|
1357 |
+
/* Gurmukhi (0A00..0A7F) */
|
1358 |
+
|
1359 |
+
/* 0A00 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
|
1360 |
+
/* 0A08 */ 3842,3842,3842, 3840, 3840, 3840, 3840,3842,
|
1361 |
+
/* 0A10 */ 3842, 3840, 3840,3842,3842, 3841, 3841, 3841,
|
1362 |
+
/* 0A18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1363 |
+
/* 0A20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1364 |
+
/* 0A28 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
|
1365 |
+
/* 0A30 */ 3841, 3840, 3841, 3841, 3840, 3841, 3841, 3840,
|
1366 |
+
/* 0A38 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 775,
|
1367 |
+
/* 0A40 */ 2823, 2055, 2055, 3840, 3840, 3840, 3840, 1543,
|
1368 |
+
/* 0A48 */ 1543, 3840, 3840, 1543, 1543, 2052, 3840, 3840,
|
1369 |
+
/* 0A50 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1370 |
+
/* 0A58 */ 3840, 3841, 3841, 3841, 3841, 3840, 3841, 3840,
|
1371 |
+
/* 0A60 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1372 |
+
/* 0A68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1373 |
+
/* 0A70 */ 3848, 3840,13841,13841, 3840, 3857, 3840, 3840,
|
1374 |
+
/* 0A78 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1375 |
+
|
1376 |
+
/* Gujarati (0A80..0AFF) */
|
1377 |
+
|
1378 |
+
/* 0A80 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
|
1379 |
+
/* 0A88 */ 3842,3842,3842,3842,3842,3842, 3840,3842,
|
1380 |
+
/* 0A90 */ 3842,3842, 3840,3842,3842, 3841, 3841, 3841,
|
1381 |
+
/* 0A98 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1382 |
+
/* 0AA0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1383 |
+
/* 0AA8 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
|
1384 |
+
/* 0AB0 */ 3841, 3840, 3841, 3841, 3840, 3841, 3841, 3841,
|
1385 |
+
/* 0AB8 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 775,
|
1386 |
+
/* 0AC0 */ 2823, 2055, 2055, 2055, 2055, 1543, 3840, 1543,
|
1387 |
+
/* 0AC8 */ 1543,2823, 3840, 2823, 2823, 2052, 3840, 3840,
|
1388 |
+
/* 0AD0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1389 |
+
/* 0AD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1390 |
+
/* 0AE0 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
|
1391 |
+
/* 0AE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1392 |
+
/* 0AF0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1393 |
+
/* 0AF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1394 |
+
|
1395 |
+
/* Oriya (0B00..0B7F) */
|
1396 |
+
|
1397 |
+
/* 0B00 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
|
1398 |
+
/* 0B08 */ 3842,3842,3842,3842,3842, 3840, 3840,3842,
|
1399 |
+
/* 0B10 */ 3842, 3840, 3840,3842,3842, 3841, 3841, 3841,
|
1400 |
+
/* 0B18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1401 |
+
/* 0B20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1402 |
+
/* 0B28 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
|
1403 |
+
/* 0B30 */ 3841, 3840, 3841, 3841, 3840, 3841, 3841, 3841,
|
1404 |
+
/* 0B38 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 1543,
|
1405 |
+
/* 0B40 */ 2823, 2055, 2055, 2055, 2055, 3840, 3840, 775,
|
1406 |
+
/* 0B48 */ 1543, 3840, 3840,2823,2823,2052, 3840, 3840,
|
1407 |
+
/* 0B50 */ 3840, 3840, 3840, 3840, 3840, 3840, 1543,2823,
|
1408 |
+
/* 0B58 */ 3840, 3840, 3840, 3840, 3841, 3841, 3840, 3841,
|
1409 |
+
/* 0B60 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
|
1410 |
+
/* 0B68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1411 |
+
/* 0B70 */ 3840, 3841, 3840, 3840, 3840, 3840, 3840, 3840,
|
1412 |
+
/* 0B78 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1413 |
+
|
1414 |
+
/* Tamil (0B80..0BFF) */
|
1415 |
+
|
1416 |
+
/* 0B80 */ 3840, 3840, 3848, 3840, 3840, 3842, 3842, 3842,
|
1417 |
+
/* 0B88 */ 3842, 3842, 3842, 3840, 3840, 3840, 3842,3842,
|
1418 |
+
/* 0B90 */ 3842, 3840, 3842, 3842, 3842, 3841, 3840, 3840,
|
1419 |
+
/* 0B98 */ 3840, 3841, 3841, 3840, 3841, 3840, 3841, 3841,
|
1420 |
+
/* 0BA0 */ 3840, 3840, 3840, 3841, 3841, 3840, 3840, 3840,
|
1421 |
+
/* 0BA8 */ 3841, 3841, 3841, 3840, 3840, 3840, 3841, 3841,
|
1422 |
+
/* 0BB0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1423 |
+
/* 0BB8 */ 3841, 3841, 3840, 3840, 3840, 3840, 2823, 2823,
|
1424 |
+
/* 0BC0 */ 1543, 2055, 2055, 3840, 3840, 3840, 775, 775,
|
1425 |
+
/* 0BC8 */ 775, 3840, 2823, 2823, 2823, 1540, 3840, 3840,
|
1426 |
+
/* 0BD0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 2823,
|
1427 |
+
/* 0BD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1428 |
+
/* 0BE0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1429 |
+
/* 0BE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1430 |
+
/* 0BF0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1431 |
+
/* 0BF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1432 |
+
|
1433 |
+
/* Telugu (0C00..0C7F) */
|
1434 |
+
|
1435 |
+
/* 0C00 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
|
1436 |
+
/* 0C08 */ 3842,3842,3842,3842,3842, 3840,3842,3842,
|
1437 |
+
/* 0C10 */ 3842, 3840,3842,3842,3842, 3841, 3841, 3841,
|
1438 |
+
/* 0C18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1439 |
+
/* 0C20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1440 |
+
/* 0C28 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
|
1441 |
+
/* 0C30 */ 3841, 3841, 3841, 3841, 3840, 3841, 3841, 3841,
|
1442 |
+
/* 0C38 */ 3841, 3841, 3840, 3840, 3840, 3840, 1543, 1543,
|
1443 |
+
/* 0C40 */ 1543, 2823, 2823, 2823, 2823, 3840, 1543, 1543,
|
1444 |
+
/* 0C48 */ 2055, 3840, 1543, 1543, 1543, 1540, 3840, 3840,
|
1445 |
+
/* 0C50 */ 3840, 3840, 3840, 3840, 3840, 1543, 2055, 3840,
|
1446 |
+
/* 0C58 */ 3841, 3841, 3840, 3840, 3840, 3840, 3840, 3840,
|
1447 |
+
/* 0C60 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
|
1448 |
+
/* 0C68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1449 |
+
/* 0C70 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1450 |
+
/* 0C78 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1451 |
+
|
1452 |
+
/* Kannada (0C80..0CFF) */
|
1453 |
+
|
1454 |
+
/* 0C80 */ 3840, 3840,3848,3848, 3840,3842,3842,3842,
|
1455 |
+
/* 0C88 */ 3842,3842,3842,3842,3842, 3840,3842,3842,
|
1456 |
+
/* 0C90 */ 3842, 3840,3842,3842,3842, 3841, 3841, 3841,
|
1457 |
+
/* 0C98 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1458 |
+
/* 0CA0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1459 |
+
/* 0CA8 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
|
1460 |
+
/* 0CB0 */ 3841, 3841, 3841, 3841, 3840, 3841, 3841, 3841,
|
1461 |
+
/* 0CB8 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 1543,
|
1462 |
+
/* 0CC0 */ 2823, 2823, 2823, 2823, 2823, 3840, 1543,2823,
|
1463 |
+
/* 0CC8 */ 2823, 3840,2823,2823, 1543, 1540, 3840, 3840,
|
1464 |
+
/* 0CD0 */ 3840, 3840, 3840, 3840, 3840, 2823, 2823, 3840,
|
1465 |
+
/* 0CD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3841, 3840,
|
1466 |
+
/* 0CE0 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
|
1467 |
+
/* 0CE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1468 |
+
/* 0CF0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1469 |
+
/* 0CF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1470 |
+
|
1471 |
+
/* Malayalam (0D00..0D7F) */
|
1472 |
+
|
1473 |
+
/* 0D00 */ 3840, 3840,3848,3848, 3840,3842,3842,3842,
|
1474 |
+
/* 0D08 */ 3842,3842,3842,3842,3842, 3840,3842,3842,
|
1475 |
+
/* 0D10 */ 3842, 3840,3842,3842,3842, 3841, 3841, 3841,
|
1476 |
+
/* 0D18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1477 |
+
/* 0D20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1478 |
+
/* 0D28 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1479 |
+
/* 0D30 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1480 |
+
/* 0D38 */ 3841, 3841, 3841, 3840, 3840, 3840, 2823, 2823,
|
1481 |
+
/* 0D40 */ 2823, 2823, 2823, 2055, 2055, 3840, 775, 775,
|
1482 |
+
/* 0D48 */ 775, 3840,2823,2823,2823, 1540, 3855, 3840,
|
1483 |
+
/* 0D50 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 2823,
|
1484 |
+
/* 0D58 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1485 |
+
/* 0D60 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
|
1486 |
+
/* 0D68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1487 |
+
/* 0D70 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1488 |
+
/* 0D78 */ 3840, 3840,3841,3841,3841,3841,3841,3841,
|
1489 |
+
|
1490 |
+
/* Sinhala (0D80..0DFF) */
|
1491 |
+
|
1492 |
+
/* 0D80 */ 3840, 3840, 3848, 3848, 3840, 3842, 3842, 3842,
|
1493 |
+
/* 0D88 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3842,
|
1494 |
+
/* 0D90 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3840,
|
1495 |
+
/* 0D98 */ 3840, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
|
1496 |
+
/* 0DA0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1497 |
+
/* 0DA8 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1498 |
+
/* 0DB0 */ 3841, 3841, 3840, 3841, 3841, 3841, 3841, 3841,
|
1499 |
+
/* 0DB8 */ 3841, 3841, 3841, 3841, 3840, 3841, 3840, 3840,
|
1500 |
+
/* 0DC0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3840,
|
1501 |
+
/* 0DC8 */ 3840, 3840, 1540, 3840, 3840, 3840, 3840, 2823,
|
1502 |
+
/* 0DD0 */ 2823, 2823, 1543, 1543, 2055, 3840, 2055, 3840,
|
1503 |
+
/* 0DD8 */ 2823, 775, 1543, 775, 2823, 2823, 2823, 2823,
|
1504 |
+
/* 0DE0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1505 |
+
/* 0DE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1506 |
+
/* 0DF0 */ 3840, 3840, 2823, 2823, 3840, 3840, 3840, 3840,
|
1507 |
+
/* 0DF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1508 |
+
|
1509 |
+
|
1510 |
+
/* Vedic Extensions (1CD0..1CFF) */
|
1511 |
+
|
1512 |
+
/* 1CD0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1513 |
+
/* 1CD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1514 |
+
/* 1CE0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1515 |
+
/* 1CE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1516 |
+
/* 1CF0 */ 3840, 3840,3848,3848, 3840, 3840, 3840, 3840,
|
1517 |
+
/* 1CF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1518 |
+
|
1519 |
+
|
1520 |
+
);
|
1521 |
+
|
1522 |
+
public static $khmer_table = array(
|
1523 |
+
|
1524 |
+
/* Khmer (1780..17FF) */
|
1525 |
+
|
1526 |
+
/* 1780 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1527 |
+
/* 1788 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1528 |
+
/* 1790 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1529 |
+
/* 1798 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
1530 |
+
/* 17A0 */ 3841, 3841, 3841, 3842, 3842, 3842, 3842, 3842,
|
1531 |
+
/* 17A8 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3842,
|
1532 |
+
/* 17B0 */ 3842, 3842, 3842, 3842, 3840, 3840, 2823, 1543,
|
1533 |
+
/* 17B8 */ 1543, 1543, 1543, 2055, 2055, 2055, 1543,2823,
|
1534 |
+
/* 17C0 */ 2823, 775, 775, 775, 2823, 2823, 3848, 3848,
|
1535 |
+
/* 17C8 */ 2823, 3853, 3853, 3840, 3855, 3840, 3840, 3840,
|
1536 |
+
/* 17D0 */ 3840, 1540, 3844, 3840, 3840, 3840, 3840, 3840,
|
1537 |
+
/* 17D8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1538 |
+
/* 17E0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1539 |
+
/* 17E8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1540 |
+
/* 17F0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1541 |
+
/* 17F8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
1542 |
+
|
1543 |
+
|
1544 |
+
);
|
1545 |
+
|
1546 |
+
|
1547 |
+
|
1548 |
+
// from "hb-ot-shape-complex-indic-table.cc"
|
1549 |
+
public static function indic_get_categories ($u) {
|
1550 |
+
if (0x0900 <= $u && $u <= 0x0DFF) return self::$indic_table[$u - 0x0900 + 0]; // offset 0 for Most "indic"
|
1551 |
+
if (0x1CD0 <= $u && $u <= 0x1D00) return self::$indic_table[$u - 0x1CD0 + 1152]; // offset for Vedic extensions
|
1552 |
+
if (0x1780 <= $u && $u <= 0x17FF) return self::$khmer_table[$u - 0x1780]; // Khmer
|
1553 |
+
if ($u == 0x00A0) return 3851; // (ISC_CP | (IMC_x << 8))
|
1554 |
+
if ($u == 0x25CC) return 3851; // (ISC_CP | (IMC_x << 8))
|
1555 |
+
return 3840; // (ISC_x | (IMC_x << 8))
|
1556 |
+
}
|
1557 |
+
|
1558 |
+
// BELOW from hb-ot-shape-complex-indic.cc
|
1559 |
+
/*
|
1560 |
+
* Indic shaper.
|
1561 |
+
*/
|
1562 |
+
|
1563 |
+
public static function IN_HALF_BLOCK($u, $Base) { return (($u & ~0x7F) == $Base); }
|
1564 |
+
|
1565 |
+
public static function IS_DEVA($u) { return self::IN_HALF_BLOCK ($u, 0x0900); }
|
1566 |
+
public static function IS_BENG($u) { return self::IN_HALF_BLOCK ($u, 0x0980); }
|
1567 |
+
public static function IS_GURU($u) { return self::IN_HALF_BLOCK ($u, 0x0A00); }
|
1568 |
+
public static function IS_GUJR($u) { return self::IN_HALF_BLOCK ($u, 0x0A80); }
|
1569 |
+
public static function IS_ORYA($u) { return self::IN_HALF_BLOCK ($u, 0x0B00); }
|
1570 |
+
public static function IS_TAML($u) { return self::IN_HALF_BLOCK ($u, 0x0B80); }
|
1571 |
+
public static function IS_TELU($u) { return self::IN_HALF_BLOCK ($u, 0x0C00); }
|
1572 |
+
public static function IS_KNDA($u) { return self::IN_HALF_BLOCK ($u, 0x0C80); }
|
1573 |
+
public static function IS_MLYM($u) { return self::IN_HALF_BLOCK ($u, 0x0D00); }
|
1574 |
+
public static function IS_SINH($u) { return self::IN_HALF_BLOCK ($u, 0x0D80); }
|
1575 |
+
public static function IS_KHMR($u) { return self::IN_HALF_BLOCK ($u, 0x1780); }
|
1576 |
+
|
1577 |
+
|
1578 |
+
public static function MATRA_POS_LEFT($u) { return self::POS_PRE_M; }
|
1579 |
+
public static function MATRA_POS_RIGHT($u) { return
|
1580 |
+
(self::IS_DEVA($u) ? self::POS_AFTER_SUB :
|
1581 |
+
(self::IS_BENG($u) ? self::POS_AFTER_POST :
|
1582 |
+
(self::IS_GURU($u) ? self::POS_AFTER_POST :
|
1583 |
+
(self::IS_GUJR($u) ? self::POS_AFTER_POST :
|
1584 |
+
(self::IS_ORYA($u) ? self::POS_AFTER_POST :
|
1585 |
+
(self::IS_TAML($u) ? self::POS_AFTER_POST :
|
1586 |
+
(self::IS_TELU($u) ? ($u <= 0x0C42 ? self::POS_BEFORE_SUB : self::POS_AFTER_SUB) :
|
1587 |
+
(self::IS_KNDA($u) ? ($u < 0x0CC3 || $u > 0xCD6 ? self::POS_BEFORE_SUB : self::POS_AFTER_SUB) :
|
1588 |
+
(self::IS_MLYM($u) ? self::POS_AFTER_POST :
|
1589 |
+
(self::IS_SINH($u) ? self::POS_AFTER_SUB :
|
1590 |
+
(self::IS_KHMR($u) ? self::POS_AFTER_POST :
|
1591 |
+
self::POS_AFTER_SUB))))))))))); /*default*/
|
1592 |
+
}
|
1593 |
+
public static function MATRA_POS_TOP($u) { return /* BENG and MLYM don't have top matras. */
|
1594 |
+
(self::IS_DEVA($u) ? self::POS_AFTER_SUB :
|
1595 |
+
(self::IS_GURU($u) ? self::POS_AFTER_POST : /* Deviate from spec */
|
1596 |
+
(self::IS_GUJR($u) ? self::POS_AFTER_SUB :
|
1597 |
+
(self::IS_ORYA($u) ? self::POS_AFTER_MAIN :
|
1598 |
+
(self::IS_TAML($u) ? self::POS_AFTER_SUB :
|
1599 |
+
(self::IS_TELU($u) ? self::POS_BEFORE_SUB :
|
1600 |
+
(self::IS_KNDA($u) ? self::POS_BEFORE_SUB :
|
1601 |
+
(self::IS_SINH($u) ? self::POS_AFTER_SUB :
|
1602 |
+
(self::IS_KHMR($u) ? self::POS_AFTER_POST :
|
1603 |
+
self::POS_AFTER_SUB))))))))); /*default*/
|
1604 |
+
}
|
1605 |
+
public static function MATRA_POS_BOTTOM($u) { return
|
1606 |
+
(self::IS_DEVA($u) ? self::POS_AFTER_SUB :
|
1607 |
+
(self::IS_BENG($u) ? self::POS_AFTER_SUB :
|
1608 |
+
(self::IS_GURU($u) ? self::POS_AFTER_POST :
|
1609 |
+
(self::IS_GUJR($u) ? self::POS_AFTER_POST :
|
1610 |
+
(self::IS_ORYA($u) ? self::POS_AFTER_SUB :
|
1611 |
+
(self::IS_TAML($u) ? self::POS_AFTER_POST :
|
1612 |
+
(self::IS_TELU($u) ? self::POS_BEFORE_SUB :
|
1613 |
+
(self::IS_KNDA($u) ? self::POS_BEFORE_SUB :
|
1614 |
+
(self::IS_MLYM($u) ? self::POS_AFTER_POST :
|
1615 |
+
(self::IS_SINH($u) ? self::POS_AFTER_SUB :
|
1616 |
+
(self::IS_KHMR($u) ? self::POS_AFTER_POST :
|
1617 |
+
self::POS_AFTER_SUB))))))))))); /*default*/
|
1618 |
+
}
|
1619 |
+
|
1620 |
+
public static function matra_position ($u, $side) {
|
1621 |
+
switch ($side) {
|
1622 |
+
case self::POS_PRE_C: return self::MATRA_POS_LEFT($u);
|
1623 |
+
case self::POS_POST_C: return self::MATRA_POS_RIGHT($u);
|
1624 |
+
case self::POS_ABOVE_C: return self::MATRA_POS_TOP($u);
|
1625 |
+
case self::POS_BELOW_C: return self::MATRA_POS_BOTTOM($u);
|
1626 |
+
}
|
1627 |
+
return $side;
|
1628 |
+
}
|
1629 |
+
|
1630 |
+
// vowel matras that have to be split into two parts.
|
1631 |
+
// From Harfbuzz (old)
|
1632 |
+
// New HarfBuzz uses /src/hb-ucdn/ucdn.c and unicodedata_db.h for full method of decomposition for all characters
|
1633 |
+
// Should always fully decompose and then recompose back, but we will just do the split matras
|
1634 |
+
public static function decompose_indic($ab) {
|
1635 |
+
$sub = array();
|
1636 |
+
switch ($ab) {
|
1637 |
+
/*
|
1638 |
+
* Decompose split matras.
|
1639 |
+
*/
|
1640 |
+
/* bengali */
|
1641 |
+
case 0x9cb : $sub[0] = 0x9c7; $sub[1]= 0x9be; return $sub;
|
1642 |
+
case 0x9cc : $sub[0] = 0x9c7; $sub[1]= 0x9d7; return $sub;
|
1643 |
+
/* oriya */
|
1644 |
+
case 0xb48 : $sub[0] = 0xb47; $sub[1]= 0xb56; return $sub;
|
1645 |
+
case 0xb4b : $sub[0] = 0xb47; $sub[1]= 0xb3e; return $sub;
|
1646 |
+
case 0xb4c : $sub[0] = 0xb47; $sub[1]= 0xb57; return $sub;
|
1647 |
+
/* tamil */
|
1648 |
+
case 0xbca : $sub[0] = 0xbc6; $sub[1]= 0xbbe; return $sub;
|
1649 |
+
case 0xbcb : $sub[0] = 0xbc7; $sub[1]= 0xbbe; return $sub;
|
1650 |
+
case 0xbcc : $sub[0] = 0xbc6; $sub[1]= 0xbd7; return $sub;
|
1651 |
+
/* telugu */
|
1652 |
+
case 0xc48 : $sub[0] = 0xc46; $sub[1]= 0xc56; return $sub;
|
1653 |
+
/* kannada */
|
1654 |
+
case 0xcc0 : $sub[0] = 0xcbf; $sub[1]= 0xcd5; return $sub;
|
1655 |
+
case 0xcc7 : $sub[0] = 0xcc6; $sub[1]= 0xcd5; return $sub;
|
1656 |
+
case 0xcc8 : $sub[0] = 0xcc6; $sub[1]= 0xcd6; return $sub;
|
1657 |
+
case 0xcca : $sub[0] = 0xcc6; $sub[1]= 0xcc2; return $sub;
|
1658 |
+
case 0xccb : $sub[0] = 0xcc6; $sub[1]= 0xcc2; $sub[2]= 0xcd5; return $sub;
|
1659 |
+
/* malayalam */
|
1660 |
+
case 0xd4a : $sub[0] = 0xd46; $sub[1]= 0xd3e; return $sub;
|
1661 |
+
case 0xd4b : $sub[0] = 0xd47; $sub[1]= 0xd3e; return $sub;
|
1662 |
+
case 0xd4c : $sub[0] = 0xd46; $sub[1]= 0xd57; return $sub;
|
1663 |
+
/* sinhala */
|
1664 |
+
// NB Some fonts break with these Sinhala decomps (although this is Uniscribe spec)
|
1665 |
+
// Can check if character would be substituted by pstf and only decompose if true
|
1666 |
+
// e.g. if (isset($GSUBdata['pstf'][$ab])) - would need to pass $GSUBdata as parameter to this function
|
1667 |
+
case 0xdda : $sub[0] = 0xdd9; $sub[1]= 0xdca; return $sub;
|
1668 |
+
case 0xddc : $sub[0] = 0xdd9; $sub[1]= 0xdcf; return $sub;
|
1669 |
+
case 0xddd : $sub[0] = 0xdd9; $sub[1]= 0xdcf; $sub[2]= 0xdca; return $sub;
|
1670 |
+
case 0xdde : $sub[0] = 0xdd9; $sub[1]= 0xddf; return $sub;
|
1671 |
+
/* khmer */
|
1672 |
+
case 0x17be : $sub[0] = 0x17c1; $sub[1]= 0x17be; return $sub;
|
1673 |
+
case 0x17bf : $sub[0] = 0x17c1; $sub[1]= 0x17bf; return $sub;
|
1674 |
+
case 0x17c0 : $sub[0] = 0x17c1; $sub[1]= 0x17c0; return $sub;
|
1675 |
+
|
1676 |
+
case 0x17c4 : $sub[0] = 0x17c1; $sub[1]= 0x17c4; return $sub;
|
1677 |
+
case 0x17c5 : $sub[0] = 0x17c1; $sub[1]= 0x17c5; return $sub;
|
1678 |
+
/* tibetan - included here although does not use Inidc shaper in other ways */
|
1679 |
+
case 0xf73 : $sub[0] = 0xf71; $sub[1]= 0xf72; return $sub;
|
1680 |
+
case 0xf75 : $sub[0] = 0xf71; $sub[1]= 0xf74; return $sub;
|
1681 |
+
case 0xf76 : $sub[0] = 0xfb2; $sub[1]= 0xf80; return $sub;
|
1682 |
+
case 0xf77 : $sub[0] = 0xfb2; $sub[1]= 0xf81; return $sub;
|
1683 |
+
case 0xf78 : $sub[0] = 0xfb3; $sub[1]= 0xf80; return $sub;
|
1684 |
+
case 0xf79 : $sub[0] = 0xfb3; $sub[1]= 0xf71; $sub[2]= 0xf80; return $sub;
|
1685 |
+
case 0xf81 : $sub[0] = 0xf71; $sub[1]= 0xf80; return $sub;
|
1686 |
+
}
|
1687 |
+
return false;
|
1688 |
+
}
|
1689 |
+
|
1690 |
+
|
1691 |
+
|
1692 |
+
|
1693 |
+
|
1694 |
+
public static function bubble_sort(&$arr, $start, $len) {
|
1695 |
+
if ($len<2) { return;}
|
1696 |
+
$k = $start+$len-2;
|
1697 |
+
while ($k >= $start) {
|
1698 |
+
for ($j=$start; $j<=$k; $j++) {
|
1699 |
+
if ($arr[$j]['indic_position'] > $arr[$j + 1]['indic_position']) {
|
1700 |
+
$t = $arr[$j];
|
1701 |
+
$arr[$j] = $arr[$j + 1];
|
1702 |
+
$arr[$j + 1] = $t;
|
1703 |
+
}
|
1704 |
+
}
|
1705 |
+
$k--;
|
1706 |
+
}
|
1707 |
+
}
|
1708 |
+
|
1709 |
+
|
1710 |
+
|
1711 |
+
|
1712 |
+
} // end Class
|
1713 |
+
|
1714 |
+
?>
|
lib/mpdf/classes/meter.php
ADDED
@@ -0,0 +1,282 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class meter {
|
4 |
+
|
5 |
+
|
6 |
+
function __construct() {
|
7 |
+
|
8 |
+
}
|
9 |
+
|
10 |
+
function makeSVG($tag, $type, $value, $max, $min, $optimum, $low, $high) {
|
11 |
+
$svg = '';
|
12 |
+
if ($tag == 'meter') {
|
13 |
+
|
14 |
+
if ($type=='2') {
|
15 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
16 |
+
///////// CUSTOM <meter type="2">
|
17 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
18 |
+
$h = 10;
|
19 |
+
$w = 160;
|
20 |
+
$border_radius = 0.143; // Factor of Height
|
21 |
+
|
22 |
+
$svg = '<?xml version="1.0" encoding="UTF-8"?>
|
23 |
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
24 |
+
<svg width="'.$w.'px" height="'.$h.'px" viewBox="0 0 '.$w.' '.$h.'" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><g>
|
25 |
+
|
26 |
+
|
27 |
+
<defs>
|
28 |
+
<linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
|
29 |
+
<stop offset="0%" stop-color="rgb(222, 222, 222)" />
|
30 |
+
<stop offset="20%" stop-color="rgb(232, 232, 232)" />
|
31 |
+
<stop offset="25%" stop-color="rgb(232, 232, 232)" />
|
32 |
+
<stop offset="100%" stop-color="rgb(182, 182, 182)" />
|
33 |
+
</linearGradient>
|
34 |
+
|
35 |
+
</defs>
|
36 |
+
';
|
37 |
+
$svg .= '<rect x="0" y="0" width="'.$w.'" height="'.$h.'" fill="#f4f4f4" stroke="none" />';
|
38 |
+
|
39 |
+
// LOW to HIGH region
|
40 |
+
//if ($low && $high && ($low != $min || $high != $max)) {
|
41 |
+
if ($low && $high) {
|
42 |
+
$barx = (($low-$min) / ($max-$min) ) * $w;
|
43 |
+
$barw = (($high-$low) / ($max-$min) ) * $w;
|
44 |
+
$svg .= '<rect x="'.$barx.'" y="0" width="'.$barw.'" height="'.$h.'" fill="url(#GrGRAY)" stroke="#888888" stroke-width="0.5px" />';
|
45 |
+
}
|
46 |
+
|
47 |
+
// OPTIMUM Marker (? AVERAGE)
|
48 |
+
if ($optimum) {
|
49 |
+
$barx = (($optimum-$min) / ($max-$min) ) * $w;
|
50 |
+
$barw = $h/2;
|
51 |
+
$barcol = '#888888';
|
52 |
+
$svg .= '<rect x="'.$barx.'" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="'.$barcol.'" stroke="none" />';
|
53 |
+
}
|
54 |
+
|
55 |
+
// VALUE Marker
|
56 |
+
if ($value) {
|
57 |
+
if ($min != $low && $value < $low) { $col = 'orange'; }
|
58 |
+
else if ($max != $high && $value > $high) { $col = 'orange'; }
|
59 |
+
else { $col = '#008800'; }
|
60 |
+
$cx = (($value-$min) / ($max-$min) ) * $w;
|
61 |
+
$cy = $h/2;
|
62 |
+
$rx = $h/3.5;
|
63 |
+
$ry = $h/2.2;
|
64 |
+
$svg .= '<ellipse fill="'.$col.'" stroke="#000000" stroke-width="0.5px" cx="'.$cx.'" cy="'.$cy.'" rx="'.$rx.'" ry="'.$ry.'"/>';
|
65 |
+
}
|
66 |
+
|
67 |
+
// BoRDER
|
68 |
+
$svg .= '<rect x="0" y="0" width="'.$w.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
|
69 |
+
|
70 |
+
$svg .= '</g></svg>';
|
71 |
+
}
|
72 |
+
else if ($type=='3') {
|
73 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
74 |
+
///////// CUSTOM <meter type="2">
|
75 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
76 |
+
$h = 10;
|
77 |
+
$w = 100;
|
78 |
+
$border_radius = 0.143; // Factor of Height
|
79 |
+
|
80 |
+
$svg = '<?xml version="1.0" encoding="UTF-8"?>
|
81 |
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
82 |
+
<svg width="'.$w.'px" height="'.$h.'px" viewBox="0 0 '.$w.' '.$h.'" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><g>
|
83 |
+
|
84 |
+
|
85 |
+
<defs>
|
86 |
+
<linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
|
87 |
+
<stop offset="0%" stop-color="rgb(222, 222, 222)" />
|
88 |
+
<stop offset="20%" stop-color="rgb(232, 232, 232)" />
|
89 |
+
<stop offset="25%" stop-color="rgb(232, 232, 232)" />
|
90 |
+
<stop offset="100%" stop-color="rgb(182, 182, 182)" />
|
91 |
+
</linearGradient>
|
92 |
+
|
93 |
+
</defs>
|
94 |
+
';
|
95 |
+
$svg .= '<rect x="0" y="0" width="'.$w.'" height="'.$h.'" fill="#f4f4f4" stroke="none" />';
|
96 |
+
|
97 |
+
// LOW to HIGH region
|
98 |
+
if ($low && $high && ($low != $min || $high != $max)) {
|
99 |
+
//if ($low && $high) {
|
100 |
+
$barx = (($low-$min) / ($max-$min) ) * $w;
|
101 |
+
$barw = (($high-$low) / ($max-$min) ) * $w;
|
102 |
+
$svg .= '<rect x="'.$barx.'" y="0" width="'.$barw.'" height="'.$h.'" fill="url(#GrGRAY)" stroke="#888888" stroke-width="0.5px" />';
|
103 |
+
}
|
104 |
+
|
105 |
+
// OPTIMUM Marker (? AVERAGE)
|
106 |
+
if ($optimum) {
|
107 |
+
$barx = (($optimum-$min) / ($max-$min) ) * $w;
|
108 |
+
$barw = $h/2;
|
109 |
+
$barcol = '#888888';
|
110 |
+
$svg .= '<rect x="'.$barx.'" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="'.$barcol.'" stroke="none" />';
|
111 |
+
}
|
112 |
+
|
113 |
+
// VALUE Marker
|
114 |
+
if ($value) {
|
115 |
+
if ($min != $low && $value < $low) { $col = 'orange'; }
|
116 |
+
else if ($max != $high && $value > $high) { $col = 'orange'; }
|
117 |
+
else { $col = 'orange'; }
|
118 |
+
$cx = (($value-$min) / ($max-$min) ) * $w;
|
119 |
+
$cy = $h/2;
|
120 |
+
$rx = $h/2.2;
|
121 |
+
$ry = $h/2.2;
|
122 |
+
$svg .= '<ellipse fill="'.$col.'" stroke="#000000" stroke-width="0.5px" cx="'.$cx.'" cy="'.$cy.'" rx="'.$rx.'" ry="'.$ry.'"/>';
|
123 |
+
}
|
124 |
+
|
125 |
+
// BoRDER
|
126 |
+
$svg .= '<rect x="0" y="0" width="'.$w.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
|
127 |
+
|
128 |
+
$svg .= '</g></svg>';
|
129 |
+
}
|
130 |
+
else {
|
131 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
132 |
+
///////// DEFAULT <meter>
|
133 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
134 |
+
$h = 10;
|
135 |
+
$w = 50;
|
136 |
+
$border_radius = 0.143; // Factor of Height
|
137 |
+
|
138 |
+
$svg = '<?xml version="1.0" encoding="UTF-8"?>
|
139 |
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
140 |
+
<svg width="'.$w.'px" height="'.$h.'px" viewBox="0 0 '.$w.' '.$h.'" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><g>
|
141 |
+
|
142 |
+
<defs>
|
143 |
+
<linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
|
144 |
+
<stop offset="0%" stop-color="rgb(222, 222, 222)" />
|
145 |
+
<stop offset="20%" stop-color="rgb(232, 232, 232)" />
|
146 |
+
<stop offset="25%" stop-color="rgb(232, 232, 232)" />
|
147 |
+
<stop offset="100%" stop-color="rgb(182, 182, 182)" />
|
148 |
+
</linearGradient>
|
149 |
+
|
150 |
+
<linearGradient id="GrRED" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
|
151 |
+
<stop offset="0%" stop-color="rgb(255, 162, 162)" />
|
152 |
+
<stop offset="20%" stop-color="rgb(255, 218, 218)" />
|
153 |
+
<stop offset="25%" stop-color="rgb(255, 218, 218)" />
|
154 |
+
<stop offset="100%" stop-color="rgb(255, 0, 0)" />
|
155 |
+
</linearGradient>
|
156 |
+
|
157 |
+
<linearGradient id="GrGREEN" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
|
158 |
+
<stop offset="0%" stop-color="rgb(102, 230, 102)" />
|
159 |
+
<stop offset="20%" stop-color="rgb(218, 255, 218)" />
|
160 |
+
<stop offset="25%" stop-color="rgb(218, 255, 218)" />
|
161 |
+
<stop offset="100%" stop-color="rgb(0, 148, 0)" />
|
162 |
+
</linearGradient>
|
163 |
+
|
164 |
+
<linearGradient id="GrBLUE" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
|
165 |
+
<stop offset="0%" stop-color="rgb(102, 102, 230)" />
|
166 |
+
<stop offset="20%" stop-color="rgb(238, 238, 238)" />
|
167 |
+
<stop offset="25%" stop-color="rgb(238, 238, 238)" />
|
168 |
+
<stop offset="100%" stop-color="rgb(0, 0, 128)" />
|
169 |
+
</linearGradient>
|
170 |
+
|
171 |
+
<linearGradient id="GrORANGE" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
|
172 |
+
<stop offset="0%" stop-color="rgb(255, 186, 0)" />
|
173 |
+
<stop offset="20%" stop-color="rgb(255, 238, 168)" />
|
174 |
+
<stop offset="25%" stop-color="rgb(255, 238, 168)" />
|
175 |
+
<stop offset="100%" stop-color="rgb(255, 155, 0)" />
|
176 |
+
</linearGradient>
|
177 |
+
</defs>
|
178 |
+
|
179 |
+
<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$w.'" height="'.$h.'" fill="url(#GrGRAY)" stroke="none" />
|
180 |
+
';
|
181 |
+
|
182 |
+
if ($value) {
|
183 |
+
$barw = (($value-$min) / ($max-$min) ) * $w;
|
184 |
+
if ($optimum < $low) {
|
185 |
+
if ($value < $low) { $barcol = 'url(#GrGREEN)'; }
|
186 |
+
else if ($value > $high) { $barcol = 'url(#GrRED)'; }
|
187 |
+
else { $barcol = 'url(#GrORANGE)'; }
|
188 |
+
}
|
189 |
+
else if ($optimum > $high) {
|
190 |
+
if ($value < $low) { $barcol = 'url(#GrRED)'; }
|
191 |
+
else if ($value > $high) { $barcol = 'url(#GrGREEN)'; }
|
192 |
+
else { $barcol = 'url(#GrORANGE)'; }
|
193 |
+
}
|
194 |
+
else {
|
195 |
+
if ($value < $low) { $barcol = 'url(#GrORANGE)'; }
|
196 |
+
else if ($value > $high) { $barcol = 'url(#GrORANGE)'; }
|
197 |
+
else { $barcol = 'url(#GrGREEN)'; }
|
198 |
+
}
|
199 |
+
$svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="'.$barcol.'" stroke="none" />';
|
200 |
+
}
|
201 |
+
|
202 |
+
|
203 |
+
// Borders
|
204 |
+
//$svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$w.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
|
205 |
+
if ($value) {
|
206 |
+
// $svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
|
207 |
+
}
|
208 |
+
|
209 |
+
|
210 |
+
$svg .= '</g></svg>';
|
211 |
+
}
|
212 |
+
}
|
213 |
+
else { // $tag == 'progress'
|
214 |
+
|
215 |
+
if ($type=='2') {
|
216 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
217 |
+
///////// CUSTOM <progress type="2">
|
218 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
219 |
+
}
|
220 |
+
else {
|
221 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
222 |
+
///////// DEFAULT <progress>
|
223 |
+
/////////////////////////////////////////////////////////////////////////////////////
|
224 |
+
$h = 10;
|
225 |
+
$w = 100;
|
226 |
+
$border_radius = 0.143; // Factor of Height
|
227 |
+
|
228 |
+
if ($value or $value==='0') {
|
229 |
+
$fill = 'url(#GrGRAY)';
|
230 |
+
}
|
231 |
+
else {
|
232 |
+
$fill = '#f8f8f8';
|
233 |
+
}
|
234 |
+
|
235 |
+
$svg = '<svg width="'.$w.'px" height="'.$h.'px" viewBox="0 0 '.$w.' '.$h.'"><g>
|
236 |
+
|
237 |
+
<defs>
|
238 |
+
<linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
|
239 |
+
<stop offset="0%" stop-color="rgb(222, 222, 222)" />
|
240 |
+
<stop offset="20%" stop-color="rgb(232, 232, 232)" />
|
241 |
+
<stop offset="25%" stop-color="rgb(232, 232, 232)" />
|
242 |
+
<stop offset="100%" stop-color="rgb(182, 182, 182)" />
|
243 |
+
</linearGradient>
|
244 |
+
|
245 |
+
<linearGradient id="GrGREEN" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
|
246 |
+
<stop offset="0%" stop-color="rgb(102, 230, 102)" />
|
247 |
+
<stop offset="20%" stop-color="rgb(218, 255, 218)" />
|
248 |
+
<stop offset="25%" stop-color="rgb(218, 255, 218)" />
|
249 |
+
<stop offset="100%" stop-color="rgb(0, 148, 0)" />
|
250 |
+
</linearGradient>
|
251 |
+
|
252 |
+
</defs>
|
253 |
+
|
254 |
+
<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$w.'" height="'.$h.'" fill="'.$fill.'" stroke="none" />
|
255 |
+
';
|
256 |
+
|
257 |
+
if ($value) {
|
258 |
+
$barw = (($value-$min) / ($max-$min) ) * $w;
|
259 |
+
$barcol = 'url(#GrGREEN)';
|
260 |
+
$svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="'.$barcol.'" stroke="none" />';
|
261 |
+
}
|
262 |
+
|
263 |
+
|
264 |
+
// Borders
|
265 |
+
$svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$w.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
|
266 |
+
if ($value) {
|
267 |
+
// $svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
|
268 |
+
}
|
269 |
+
|
270 |
+
|
271 |
+
$svg .= '</g></svg>';
|
272 |
+
|
273 |
+
}
|
274 |
+
}
|
275 |
+
|
276 |
+
return $svg;
|
277 |
+
}
|
278 |
+
|
279 |
+
|
280 |
+
} // end of class
|
281 |
+
|
282 |
+
?>
|
lib/mpdf/classes/mpdfform.php
ADDED
@@ -0,0 +1,1550 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class mpdfform {
|
4 |
+
|
5 |
+
var $mpdf = null;
|
6 |
+
|
7 |
+
var $forms;
|
8 |
+
var $formn;
|
9 |
+
|
10 |
+
//Active Forms
|
11 |
+
var $formSubmitNoValueFields;
|
12 |
+
var $formExportType;
|
13 |
+
var $formSelectDefaultOption;
|
14 |
+
var $formUseZapD;
|
15 |
+
/* Form Styles */
|
16 |
+
var $form_border_color;
|
17 |
+
var $form_background_color;
|
18 |
+
var $form_border_width;
|
19 |
+
var $form_border_style;
|
20 |
+
var $form_button_border_color;
|
21 |
+
var $form_button_background_color;
|
22 |
+
var $form_button_border_width;
|
23 |
+
var $form_button_border_style;
|
24 |
+
var $form_radio_color;
|
25 |
+
var $form_radio_background_color;
|
26 |
+
|
27 |
+
var $form_element_spacing;
|
28 |
+
|
29 |
+
// Active forms
|
30 |
+
var $formMethod;
|
31 |
+
var $formAction;
|
32 |
+
var $form_fonts;
|
33 |
+
var $form_radio_groups;
|
34 |
+
var $form_checkboxes;
|
35 |
+
var $pdf_acro_array;
|
36 |
+
|
37 |
+
var $pdf_array_co;
|
38 |
+
var $array_form_button_js;
|
39 |
+
var $array_form_choice_js;
|
40 |
+
var $array_form_text_js;
|
41 |
+
|
42 |
+
/* Button Text */
|
43 |
+
var $form_button_text;
|
44 |
+
var $form_button_text_over;
|
45 |
+
var $form_button_text_click;
|
46 |
+
var $form_button_icon;
|
47 |
+
|
48 |
+
|
49 |
+
// FORMS
|
50 |
+
var $textarea_lineheight;
|
51 |
+
|
52 |
+
function mpdfform(&$mpdf) {
|
53 |
+
$this->mpdf = $mpdf;
|
54 |
+
|
55 |
+
// ACTIVE FORMS
|
56 |
+
$this->formExportType = 'xfdf'; // 'xfdf' or 'html'
|
57 |
+
$this->formSubmitNoValueFields = true; // Whether to include blank fields when submitting data
|
58 |
+
$this->formSelectDefaultOption = true; // for Select drop down box; if no option is explicitly maked as selected,
|
59 |
+
// this determines whether to select 1st option (as per browser)
|
60 |
+
// - affects whether "required" attribute is relevant
|
61 |
+
$this->formUseZapD = true; // Determine whether to use ZapfDingbat icons for radio/checkboxes
|
62 |
+
|
63 |
+
// FORM STYLES
|
64 |
+
// These can alternatively use a 4 number string to represent CMYK colours
|
65 |
+
$this->form_border_color = '0.6 0.6 0.72'; // RGB
|
66 |
+
$this->form_background_color = '0.975 0.975 0.975'; // RGB
|
67 |
+
$this->form_border_width = '1'; // 0 doesn't seem to work as it should
|
68 |
+
$this->form_border_style = 'S'; // B - Bevelled; D - Double
|
69 |
+
$this->form_button_border_color = '0.2 0.2 0.55';
|
70 |
+
$this->form_button_background_color = '0.941 0.941 0.941';
|
71 |
+
$this->form_button_border_width = '1';
|
72 |
+
$this->form_button_border_style = 'S';
|
73 |
+
$this->form_radio_color = '0.0 0.0 0.4'; // radio and checkbox
|
74 |
+
$this->form_radio_background_color = '0.9 0.9 0.9';
|
75 |
+
|
76 |
+
// FORMS
|
77 |
+
$this->textarea_lineheight = 1.25;
|
78 |
+
|
79 |
+
// FORM ELEMENT SPACING
|
80 |
+
$this->form_element_spacing['select']['outer']['h'] = 0.5; // Horizontal spacing around SELECT
|
81 |
+
$this->form_element_spacing['select']['outer']['v'] = 0.5; // Vertical spacing around SELECT
|
82 |
+
$this->form_element_spacing['select']['inner']['h'] = 0.7; // Horizontal padding around SELECT
|
83 |
+
$this->form_element_spacing['select']['inner']['v'] = 0.7; // Vertical padding around SELECT
|
84 |
+
$this->form_element_spacing['input']['outer']['h'] = 0.5;
|
85 |
+
$this->form_element_spacing['input']['outer']['v'] = 0.5;
|
86 |
+
$this->form_element_spacing['input']['inner']['h'] = 0.7;
|
87 |
+
$this->form_element_spacing['input']['inner']['v'] = 0.7;
|
88 |
+
$this->form_element_spacing['textarea']['outer']['h'] = 0.5;
|
89 |
+
$this->form_element_spacing['textarea']['outer']['v'] = 0.5;
|
90 |
+
$this->form_element_spacing['textarea']['inner']['h'] = 1;
|
91 |
+
$this->form_element_spacing['textarea']['inner']['v'] = 0.5;
|
92 |
+
$this->form_element_spacing['button']['outer']['h'] = 0.5;
|
93 |
+
$this->form_element_spacing['button']['outer']['v'] = 0.5;
|
94 |
+
$this->form_element_spacing['button']['inner']['h'] = 2;
|
95 |
+
$this->form_element_spacing['button']['inner']['v'] = 1;
|
96 |
+
|
97 |
+
// INITIALISE non-configurable
|
98 |
+
$this->formMethod = 'POST';
|
99 |
+
$this->formAction = '';
|
100 |
+
$this->form_fonts = array();
|
101 |
+
$this->form_radio_groups = array();
|
102 |
+
$this->form_checkboxes = false;
|
103 |
+
$this->forms = array();
|
104 |
+
$this->pdf_array_co = '';
|
105 |
+
|
106 |
+
|
107 |
+
}
|
108 |
+
|
109 |
+
|
110 |
+
function print_ob_text($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
|
111 |
+
// TEXT/PASSWORD INPUT
|
112 |
+
if ($this->mpdf->useActiveForms) {
|
113 |
+
// Flags: 1 - Readonly; 2 - Required; 3 - No export; 13 - textarea; 14 - Password
|
114 |
+
$flags = array();
|
115 |
+
if ((isset($objattr['disabled']) && $objattr['disabled']) || (isset($objattr['readonly']) && $objattr['readonly'])) { $flags[] = 1; } // readonly
|
116 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
117 |
+
$flags[] = 3; // no export
|
118 |
+
$objattr['color'] = array(3,128,128,128); // gray out disabled
|
119 |
+
}
|
120 |
+
if (isset($objattr['required']) && $objattr['required']) { $flags[] = 2; } // required
|
121 |
+
if (!isset($objattr['spellcheck']) || !$objattr['spellcheck']) { $flags[] = 23; } // DoNotSpellCheck
|
122 |
+
if (isset($objattr['subtype']) && $objattr['subtype']=='PASSWORD') {
|
123 |
+
$flags[] = 14;
|
124 |
+
$val = $objattr['value'];
|
125 |
+
}
|
126 |
+
if (isset($objattr['color'])) {
|
127 |
+
$this->mpdf->SetTColor($objattr['color']);
|
128 |
+
}
|
129 |
+
else {
|
130 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
131 |
+
}
|
132 |
+
$fieldalign = $rtlalign;
|
133 |
+
if (isset($objattr['text_align']) && $objattr['text_align']) { $fieldalign = $objattr['text_align']; }
|
134 |
+
else { $val = $objattr['text']; }
|
135 |
+
// mPDF 5.3.25
|
136 |
+
$js = array();
|
137 |
+
if (isset($objattr['onCalculate']) && $objattr['onCalculate']) { $js[] = array('C', $objattr['onCalculate']); }
|
138 |
+
if (isset($objattr['onValidate']) && $objattr['onValidate']) { $js[] = array('V', $objattr['onValidate']); }
|
139 |
+
if (isset($objattr['onFormat']) && $objattr['onFormat']) { $js[] = array('F', $objattr['onFormat']); }
|
140 |
+
if (isset($objattr['onKeystroke']) && $objattr['onKeystroke']) { $js[] = array('K', $objattr['onKeystroke']); }
|
141 |
+
$this->SetFormText( $w, $h, $objattr['fieldname'], $val, $val, $objattr['title'], $flags, $fieldalign, false, (isset($objattr['maxlength']) ? $objattr['maxlength'] : false), $js, (isset($objattr['background-col']) ? $objattr['background-col'] : false), (isset($objattr['border-col']) ? $objattr['border-col'] : false) );
|
142 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
143 |
+
}
|
144 |
+
else {
|
145 |
+
$w -= $this->form_element_spacing['input']['outer']['h']*2 /$k;
|
146 |
+
$h -= $this->form_element_spacing['input']['outer']['v']*2 /$k;
|
147 |
+
$this->mpdf->x += $this->form_element_spacing['input']['outer']['h'] /$k;
|
148 |
+
$this->mpdf->y += $this->form_element_spacing['input']['outer']['v'] /$k;
|
149 |
+
// Chop texto to max length $w-inner-padding
|
150 |
+
while ($this->mpdf->GetStringWidth($texto) > $w-($this->form_element_spacing['input']['inner']['h']*2)) {
|
151 |
+
$texto = mb_substr($texto,0,mb_strlen($texto,$this->mpdf->mb_enc)-1,$this->mpdf->mb_enc);
|
152 |
+
}
|
153 |
+
|
154 |
+
// DIRECTIONALITY
|
155 |
+
if (preg_match("/([".$this->mpdf->pregRTLchars."])/u", $texto)) { $this->mpdf->biDirectional = true; } // *RTL*
|
156 |
+
|
157 |
+
// Use OTL OpenType Table Layout - GSUB & GPOS
|
158 |
+
if (isset($this->mpdf->CurrentFont['useOTL']) && $this->mpdf->CurrentFont['useOTL']) {
|
159 |
+
$texto = $this->mpdf->otl->applyOTL($texto, $this->mpdf->CurrentFont['useOTL']);
|
160 |
+
$OTLdata = $this->mpdf->otl->OTLdata;
|
161 |
+
}
|
162 |
+
|
163 |
+
$this->mpdf->magic_reverse_dir($texto, $this->mpdf->directionality, $OTLdata);
|
164 |
+
|
165 |
+
$this->mpdf->SetLineWidth(0.2 /$k );
|
166 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
167 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
|
168 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(127));
|
169 |
+
}
|
170 |
+
else if (isset($objattr['readonly']) && $objattr['readonly']) {
|
171 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
|
172 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
173 |
+
}
|
174 |
+
else {
|
175 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(250));
|
176 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
177 |
+
}
|
178 |
+
$this->mpdf->Cell($w,$h,$texto,1,0,$rtlalign,1,'',0,$this->form_element_spacing['input']['inner']['h'] /$k ,$this->form_element_spacing['input']['inner']['h'] /$k , 'M', 0, false, $OTLdata);
|
179 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
|
180 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
181 |
+
}
|
182 |
+
}
|
183 |
+
|
184 |
+
function print_ob_textarea($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
|
185 |
+
// TEXTAREA
|
186 |
+
if ($this->mpdf->useActiveForms) {
|
187 |
+
// Flags: 1 - Readonly; 2 - Required; 3 - No export; 13 - textarea; 14 - Password
|
188 |
+
$flags = array();
|
189 |
+
$flags = array(13); // textarea
|
190 |
+
if ((isset($objattr['disabled']) && $objattr['disabled']) || (isset($objattr['readonly']) && $objattr['readonly'])) { $flags[] = 1; } // readonly
|
191 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
192 |
+
$flags[] = 3; // no export
|
193 |
+
$objattr['color'] = array(3,128,128,128); // gray out disabled
|
194 |
+
}
|
195 |
+
if (isset($objattr['required']) && $objattr['required']) { $flags[] = 2; } // required
|
196 |
+
if (!isset($objattr['spellcheck']) || !$objattr['spellcheck']) { $flags[] = 23; } // DoNotSpellCheck
|
197 |
+
if (isset($objattr['donotscroll']) && $objattr['donotscroll']) { $flags[] = 24; } // DoNotScroll
|
198 |
+
if (isset($objattr['color'])) {
|
199 |
+
$this->mpdf->SetTColor($objattr['color']);
|
200 |
+
}
|
201 |
+
else {
|
202 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
203 |
+
}
|
204 |
+
$fieldalign = $rtlalign;
|
205 |
+
if ($texto == ' ') { $texto = ''; } // mPDF 5.3.24
|
206 |
+
if (isset($objattr['text_align']) && $objattr['text_align']) { $fieldalign = $objattr['text_align']; }
|
207 |
+
// mPDF 5.3.25
|
208 |
+
$js = array();
|
209 |
+
if (isset($objattr['onCalculate']) && $objattr['onCalculate']) { $js[] = array('C', $objattr['onCalculate']); }
|
210 |
+
if (isset($objattr['onValidate']) && $objattr['onValidate']) { $js[] = array('V', $objattr['onValidate']); }
|
211 |
+
if (isset($objattr['onFormat']) && $objattr['onFormat']) { $js[] = array('F', $objattr['onFormat']); }
|
212 |
+
if (isset($objattr['onKeystroke']) && $objattr['onKeystroke']) { $js[] = array('K', $objattr['onKeystroke']); }
|
213 |
+
$this->SetFormText( $w, $h, $objattr['fieldname'], $texto, $texto, (isset($objattr['title']) ? $objattr['title'] : ''), $flags, $fieldalign , false, -1, $js, (isset($objattr['background-col']) ? $objattr['background-col'] : false), (isset($objattr['border-col']) ? $objattr['border-col'] : false));
|
214 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
215 |
+
}
|
216 |
+
else {
|
217 |
+
$w -= $this->form_element_spacing['textarea']['outer']['h']*2 /$k ;
|
218 |
+
$h -= $this->form_element_spacing['textarea']['outer']['v']*2 /$k ;
|
219 |
+
$this->mpdf->x += $this->form_element_spacing['textarea']['outer']['h'] /$k ;
|
220 |
+
$this->mpdf->y += $this->form_element_spacing['textarea']['outer']['v'] /$k ;
|
221 |
+
$this->mpdf->SetLineWidth(0.2 /$k );
|
222 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
223 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
|
224 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(127));
|
225 |
+
}
|
226 |
+
else if (isset($objattr['readonly']) && $objattr['readonly']) {
|
227 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
|
228 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
229 |
+
}
|
230 |
+
else {
|
231 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(250));
|
232 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
233 |
+
}
|
234 |
+
$this->mpdf->Rect($this->mpdf->x,$this->mpdf->y,$w,$h,'DF');
|
235 |
+
$ClipPath = sprintf('q %.3F %.3F %.3F %.3F re W n ',$this->mpdf->x*_MPDFK,($this->mpdf->h-$this->mpdf->y)*_MPDFK,$w*_MPDFK,-$h*_MPDFK);
|
236 |
+
$this->mpdf->_out($ClipPath);
|
237 |
+
|
238 |
+
$w -= $this->form_element_spacing['textarea']['inner']['h']*2 /$k ;
|
239 |
+
$this->mpdf->x += $this->form_element_spacing['textarea']['inner']['h'] /$k ;
|
240 |
+
$this->mpdf->y += $this->form_element_spacing['textarea']['inner']['v'] /$k ;
|
241 |
+
|
242 |
+
if ($texto != '') $this->mpdf->MultiCell($w,$this->mpdf->FontSize*$this->textarea_lineheight,$texto,0,'',0,'',$blockdir,true,$objattr['OTLdata'], $objattr['rows']);
|
243 |
+
$this->mpdf->_out('Q');
|
244 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
|
245 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
246 |
+
}
|
247 |
+
}
|
248 |
+
|
249 |
+
function print_ob_select($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
|
250 |
+
// SELECT
|
251 |
+
if ($this->mpdf->useActiveForms) {
|
252 |
+
// Flags: 1 - Readonly; 2 - Required; 3 - No export; 19 - edit (only if combo)
|
253 |
+
$flags = array();
|
254 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
255 |
+
$flags[] = 1; // readonly
|
256 |
+
$flags[] = 3; // no export
|
257 |
+
$objattr['color'] = array(3,128,128,128); // gray out disabled
|
258 |
+
}
|
259 |
+
if (isset($objattr['required']) && $objattr['required']) { $flags[] = 2; } // required
|
260 |
+
if (isset($objattr['multiple']) && $objattr['multiple'] && isset($objattr['size']) && $objattr['size']>1) { $flags[] = 22; } //flag 22 = multiselect (listbox)
|
261 |
+
if (isset($objattr['size']) && $objattr['size']<2) {
|
262 |
+
$flags[] = 18; //flag 18 = combobox (else a listbox)
|
263 |
+
if (isset($objattr['editable']) && $objattr['editable']) { $flags[] = 19; } // editable
|
264 |
+
}
|
265 |
+
// only allow spellcheck if combo and editable
|
266 |
+
if ((!isset($objattr['spellcheck']) || !$objattr['spellcheck']) || (isset($objattr['size']) && $objattr['size']>1) || (!isset($objattr['editable']) || !$objattr['editable'])) { $flags[] = 23; } // DoNotSpellCheck
|
267 |
+
if (isset($objattr['subtype']) && $objattr['subtype']=='PASSWORD') { $flags[] = 14; }
|
268 |
+
if (isset($objattr['onChange']) && $objattr['onChange']) { $js = $objattr['onChange']; }
|
269 |
+
else { $js = ''; } // mPDF 5.3.37
|
270 |
+
$data = array('VAL' => array(), 'OPT' => array(), 'SEL' => array(), );
|
271 |
+
if (isset($objattr['items'])) {
|
272 |
+
for($i=0; $i<count($objattr['items']); $i++) {
|
273 |
+
$item = $objattr['items'][$i];
|
274 |
+
$data['VAL'][] = (isset($item['exportValue']) ? $item['exportValue'] : '');
|
275 |
+
$data['OPT'][] = (isset($item['content']) ? $item['content'] : '');
|
276 |
+
if (isset($item['selected']) && $item['selected']) { $data['SEL'][] = $i; }
|
277 |
+
}
|
278 |
+
}
|
279 |
+
if (count($data['SEL'])==0 && $this->formSelectDefaultOption) {$data['SEL'][] = 0; }
|
280 |
+
if (isset($objattr['color'])) {
|
281 |
+
$this->mpdf->SetTColor($objattr['color']);
|
282 |
+
}
|
283 |
+
else {
|
284 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
285 |
+
}
|
286 |
+
$this->SetFormChoice( $w, $h, $objattr['fieldname'], $flags, $data, $rtlalign, $js );
|
287 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
288 |
+
}
|
289 |
+
else {
|
290 |
+
$this->mpdf->SetLineWidth(0.2 /$k );
|
291 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
292 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
|
293 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(127));
|
294 |
+
}
|
295 |
+
else {
|
296 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(250));
|
297 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
298 |
+
}
|
299 |
+
$w -= $this->form_element_spacing['select']['outer']['h']*2 /$k ;
|
300 |
+
$h -= $this->form_element_spacing['select']['outer']['v']*2 /$k ;
|
301 |
+
$this->mpdf->x += $this->form_element_spacing['select']['outer']['h'] /$k ;
|
302 |
+
$this->mpdf->y += $this->form_element_spacing['select']['outer']['v'] /$k ;
|
303 |
+
|
304 |
+
// DIRECTIONALITY
|
305 |
+
if (preg_match("/([".$this->mpdf->pregRTLchars."])/u", $texto)) { $this->mpdf->biDirectional = true; } // *RTL*
|
306 |
+
|
307 |
+
$this->mpdf->magic_reverse_dir($texto, $this->mpdf->directionality, $objattr['OTLdata']);
|
308 |
+
|
309 |
+
$this->mpdf->Cell($w-($this->mpdf->FontSize*1.4),$h,$texto,1,0,$rtlalign,1,'',0,$this->form_element_spacing['select']['inner']['h'] /$k,$this->form_element_spacing['select']['inner']['h'] /$k , 'M', 0, false, $objattr['OTLdata']) ;
|
310 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(190));
|
311 |
+
$save_font = $this->mpdf->FontFamily;
|
312 |
+
$save_currentfont = $this->mpdf->currentfontfamily;
|
313 |
+
if ($this->mpdf->PDFA || $this->mpdf->PDFX) {
|
314 |
+
if (($this->mpdf->PDFA && !$this->mpdf->PDFAauto) || ($this->mpdf->PDFX && !$this->mpdf->PDFXauto)) { $this->mpdf->PDFAXwarnings[] = "Core Adobe font Zapfdingbats cannot be embedded in mPDF - used in Form element: Select - which is required for PDFA1-b or PDFX/1-a. (Different character/font will be substituted.)"; }
|
315 |
+
$this->mpdf->SetFont('sans');
|
316 |
+
if ($this->mpdf->_charDefined($this->mpdf->CurrentFont['cw'], 9660)) { $down = "\xe2\x96\xbc"; }
|
317 |
+
else { $down = '='; }
|
318 |
+
$this->mpdf->Cell(($this->mpdf->FontSize*1.4),$h,$down,1,0,'C',1,'',0,0,0, 'M') ;
|
319 |
+
}
|
320 |
+
else {
|
321 |
+
$this->mpdf->SetFont('czapfdingbats','',0);
|
322 |
+
$this->mpdf->Cell(($this->mpdf->FontSize*1.4),$h,chr(116),1,0,'C',1,'',0,0,0, 'M') ;
|
323 |
+
}
|
324 |
+
$this->mpdf->SetFont($save_font,'',0);
|
325 |
+
$this->mpdf->currentfontfamily = $save_currentfont;
|
326 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
|
327 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
328 |
+
}
|
329 |
+
}
|
330 |
+
|
331 |
+
function print_ob_imageinput($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
|
332 |
+
// INPUT/BUTTON as IMAGE
|
333 |
+
if ($this->mpdf->useActiveForms) {
|
334 |
+
// Flags: 1 - Readonly; 3 - No export;
|
335 |
+
$flags = array();
|
336 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
337 |
+
$flags[] = 1; // readonly
|
338 |
+
$flags[] = 3; // no export
|
339 |
+
}
|
340 |
+
if (isset($objattr['onClick']) && $objattr['onClick']) { $js = $objattr['onClick']; }
|
341 |
+
else { $js = ''; }
|
342 |
+
$this->SetJSButton( $w, $h, $objattr['fieldname'], (isset($objattr['value']) ? $objattr['value'] : ''), $js, $objattr['ID'], $objattr['title'], $flags, (isset($objattr['Indexed']) ? $objattr['Indexed'] : false));
|
343 |
+
}
|
344 |
+
else {
|
345 |
+
$this->mpdf->y = $objattr['INNER-Y'];
|
346 |
+
$this->mpdf->_out( sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q",$objattr['INNER-WIDTH'] *_MPDFK,$objattr['INNER-HEIGHT'] *_MPDFK,$objattr['INNER-X'] *_MPDFK,($this->mpdf->h-($objattr['INNER-Y'] +$objattr['INNER-HEIGHT'] ))*_MPDFK,$objattr['ID'] ) );
|
347 |
+
if (isset($objattr['BORDER-WIDTH']) && $objattr['BORDER-WIDTH']) { $this->mpdf->PaintImgBorder($objattr,$is_table); }
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
function print_ob_button($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
|
352 |
+
// BUTTON
|
353 |
+
if ($this->mpdf->useActiveForms) {
|
354 |
+
// Flags: 1 - Readonly; 3 - No export;
|
355 |
+
$flags = array();
|
356 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
357 |
+
$flags[] = 1; // readonly
|
358 |
+
$flags[] = 3; // no export
|
359 |
+
$objattr['color'] = array(3,128,128,128);
|
360 |
+
}
|
361 |
+
if (isset($objattr['color'])) {
|
362 |
+
$this->mpdf->SetTColor($objattr['color']);
|
363 |
+
}
|
364 |
+
else {
|
365 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
366 |
+
}
|
367 |
+
if (isset($objattr['subtype']) && $objattr['subtype'] == 'RESET') {
|
368 |
+
$this->SetFormButtonText( $objattr['value'] );
|
369 |
+
$this->SetFormReset( $w, $h, $objattr['fieldname'], $objattr['value'], $objattr['title'], $flags, (isset($objattr['background-col']) ? $objattr['background-col'] : false), (isset($objattr['border-col']) ? $objattr['border-col'] : false), (isset($objattr['noprint']) ? $objattr['noprint'] : false) );
|
370 |
+
}
|
371 |
+
else if (isset($objattr['subtype']) && $objattr['subtype'] == 'SUBMIT') {
|
372 |
+
$url = $this->formAction;
|
373 |
+
$type = $this->formExportType;
|
374 |
+
$method = $this->formMethod;
|
375 |
+
$this->SetFormButtonText( $objattr['value'] );
|
376 |
+
$this->SetFormSubmit( $w, $h, $objattr['fieldname'], $objattr['value'], $url, $objattr['title'], $type, $method, $flags, (isset($objattr['background-col']) ? $objattr['background-col'] : false), (isset($objattr['border-col']) ? $objattr['border-col'] : false), (isset($objattr['noprint']) ? $objattr['noprint'] : false) );
|
377 |
+
}
|
378 |
+
else if (isset($objattr['subtype']) && $objattr['subtype'] == 'BUTTON') {
|
379 |
+
$this->SetFormButtonText( $objattr['value'] );
|
380 |
+
if (isset($objattr['onClick']) && $objattr['onClick']) { $js = $objattr['onClick']; }
|
381 |
+
else { $js = ''; }
|
382 |
+
$this->SetJSButton( $w, $h, $objattr['fieldname'], $objattr['value'], $js, 0, $objattr['title'], $flags, false, (isset($objattr['background-col']) ? $objattr['background-col'] : false), (isset($objattr['border-col']) ? $objattr['border-col'] : false), (isset($objattr['noprint']) ? $objattr['noprint'] : false) );
|
383 |
+
}
|
384 |
+
$this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
|
385 |
+
}
|
386 |
+
else {
|
387 |
+
$this->mpdf->SetLineWidth(0.2 /$k );
|
388 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(190));
|
389 |
+
$w -= $this->form_element_spacing['button']['outer']['h']*2 /$k ;
|
390 |
+
$h -= $this->form_element_spacing['button']['outer']['v']*2 /$k ;
|
391 |
+
$this->mpdf->x += $this->form_element_spacing['button']['outer']['h'] /$k ;
|
392 |
+
$this->mpdf->y += $this->form_element_spacing['button']['outer']['v'] /$k ;
|
393 |
+
$this->mpdf->RoundedRect($this->mpdf->x, $this->mpdf->y, $w, $h, 0.5 /$k , 'DF');
|
394 |
+
$w -= $this->form_element_spacing['button']['inner']['h']*2 /$k ;
|
395 |
+
$h -= $this->form_element_spacing['button']['inner']['v']*2 /$k ;
|
396 |
+
$this->mpdf->x += $this->form_element_spacing['button']['inner']['h'] /$k ;
|
397 |
+
$this->mpdf->y += $this->form_element_spacing['button']['inner']['v'] /$k ;
|
398 |
+
|
399 |
+
// DIRECTIONALITY
|
400 |
+
if (preg_match("/([".$this->mpdf->pregRTLchars."])/u", $texto)) { $this->mpdf->biDirectional = true; } // *RTL*
|
401 |
+
|
402 |
+
// Use OTL OpenType Table Layout - GSUB & GPOS
|
403 |
+
if (isset($this->mpdf->CurrentFont['useOTL']) && $this->mpdf->CurrentFont['useOTL']) {
|
404 |
+
$texto = $this->mpdf->otl->applyOTL($texto, $this->mpdf->CurrentFont['useOTL']);
|
405 |
+
$OTLdata = $this->mpdf->otl->OTLdata;
|
406 |
+
}
|
407 |
+
|
408 |
+
$this->mpdf->magic_reverse_dir($texto, $this->mpdf->directionality, $OTLdata);
|
409 |
+
|
410 |
+
$this->mpdf->Cell($w,$h,$texto,'',0,'C',0,'',0,0,0, 'M', 0, false, $OTLdata ) ;
|
411 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(0));
|
412 |
+
}
|
413 |
+
}
|
414 |
+
|
415 |
+
function print_ob_checkbox($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir,$x,$y) {
|
416 |
+
// CHECKBOX
|
417 |
+
if ($this->mpdf->useActiveForms) {
|
418 |
+
// Flags: 1 - Readonly; 2 - Required; 3 - No export;
|
419 |
+
$flags = array();
|
420 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
421 |
+
$flags[] = 1; // readonly
|
422 |
+
$flags[] = 3; // no export
|
423 |
+
}
|
424 |
+
$checked = false;
|
425 |
+
if (isset($objattr['checked']) && $objattr['checked']) { $checked = true; }
|
426 |
+
if ($this->formUseZapD) {
|
427 |
+
$save_font = $this->mpdf->FontFamily;
|
428 |
+
$save_currentfont = $this->mpdf->currentfontfamily;
|
429 |
+
$this->mpdf->SetFont('czapfdingbats','',0);
|
430 |
+
}
|
431 |
+
$this->SetCheckBox( $w, $h, $objattr['fieldname'], $objattr['value'], $objattr['title'], $checked, $flags, (isset($objattr['disabled']) ? $objattr['disabled'] : false));
|
432 |
+
if ($this->formUseZapD) {
|
433 |
+
$this->mpdf->SetFont($save_font,'',0);
|
434 |
+
$this->mpdf->currentfontfamily = $save_currentfont;
|
435 |
+
}
|
436 |
+
}
|
437 |
+
else {
|
438 |
+
$iw = $w * 0.7;
|
439 |
+
$ih = $h * 0.7;
|
440 |
+
$lx = $x + (($w-$iw)/2);
|
441 |
+
$ty = $y + (($h-$ih)/2);
|
442 |
+
$rx = $lx + $iw;
|
443 |
+
$by = $ty + $ih;
|
444 |
+
$this->mpdf->SetLineWidth(0.2 /$k );
|
445 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
446 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
|
447 |
+
$this->mpdf->SetDColor($this->mpdf->ConvertColor(127));
|
448 |
+
}
|
449 |
+
else {
|
450 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(250));
|
451 |
+
$this->mpdf->SetDColor($this->mpdf->ConvertColor(0));
|
452 |
+
}
|
453 |
+
$this->mpdf->Rect($lx,$ty,$iw,$ih,'DF');
|
454 |
+
if (isset($objattr['checked']) && $objattr['checked']) {
|
455 |
+
//Round join and cap
|
456 |
+
$this->mpdf->SetLineCap(1);
|
457 |
+
$this->mpdf->Line($lx,$ty,$rx,$by);
|
458 |
+
$this->mpdf->Line($lx,$by,$rx,$ty);
|
459 |
+
//Set line cap style back to square
|
460 |
+
$this->mpdf->SetLineCap(2);
|
461 |
+
}
|
462 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
|
463 |
+
$this->mpdf->SetDColor($this->mpdf->ConvertColor(0));
|
464 |
+
}
|
465 |
+
}
|
466 |
+
|
467 |
+
function print_ob_radio($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir,$x,$y) {
|
468 |
+
// RADIO
|
469 |
+
if ($this->mpdf->useActiveForms) {
|
470 |
+
// Flags: 1 - Readonly; 2 - Required; 3 - No export;
|
471 |
+
$flags = array();
|
472 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
473 |
+
$flags[] = 1; // readonly
|
474 |
+
$flags[] = 3; // no export
|
475 |
+
}
|
476 |
+
$checked = false;
|
477 |
+
if (isset($objattr['checked']) && $objattr['checked']) { $checked = true; }
|
478 |
+
if ($this->formUseZapD) {
|
479 |
+
$save_font = $this->mpdf->FontFamily;
|
480 |
+
$save_currentfont = $this->mpdf->currentfontfamily;
|
481 |
+
$this->mpdf->SetFont('czapfdingbats','',0);
|
482 |
+
}
|
483 |
+
$this->SetRadio( $w, $h, $objattr['fieldname'], $objattr['value'], (isset($objattr['title']) ? $objattr['title'] : ''), $checked, $flags, (isset($objattr['disabled']) ? $objattr['disabled'] : false) );
|
484 |
+
if ($this->formUseZapD) {
|
485 |
+
$this->mpdf->SetFont($save_font,'',0);
|
486 |
+
$this->mpdf->currentfontfamily = $save_currentfont;
|
487 |
+
}
|
488 |
+
}
|
489 |
+
else {
|
490 |
+
$this->mpdf->SetLineWidth(0.2 /$k );
|
491 |
+
$radius = $this->mpdf->FontSize *0.35;
|
492 |
+
$cx = $x + ($w/2);
|
493 |
+
$cy = $y + ($h/2);
|
494 |
+
if (isset($objattr['disabled']) && $objattr['disabled']) {
|
495 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(127));
|
496 |
+
$this->mpdf->SetDColor($this->mpdf->ConvertColor(127));
|
497 |
+
}
|
498 |
+
else {
|
499 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(0));
|
500 |
+
$this->mpdf->SetDColor($this->mpdf->ConvertColor(0));
|
501 |
+
}
|
502 |
+
$this->mpdf->Circle($cx,$cy,$radius,'D');
|
503 |
+
if (isset($objattr['checked']) && $objattr['checked']) {
|
504 |
+
$this->mpdf->Circle($cx,$cy,$radius*0.4,'DF');
|
505 |
+
}
|
506 |
+
$this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
|
507 |
+
$this->mpdf->SetDColor($this->mpdf->ConvertColor(0));
|
508 |
+
|
509 |
+
}
|
510 |
+
}
|
511 |
+
|
512 |
+
|
513 |
+
|
514 |
+
// In _putpages
|
515 |
+
function countPageForms($n, &$totaladdnum) {
|
516 |
+
foreach( $this->forms as $form ) {
|
517 |
+
if ( $form['page'] == $n ) {
|
518 |
+
$totaladdnum++;
|
519 |
+
if ( $form['typ'] == 'Tx' ) {
|
520 |
+
if ( isset($this->array_form_text_js[$form['T']]) ) {
|
521 |
+
if ( isset($this->array_form_text_js[$form['T']]['F']) ) { $totaladdnum++; }
|
522 |
+
if ( isset($this->array_form_text_js[$form['T']]['K']) ) { $totaladdnum++; }
|
523 |
+
if ( isset($this->array_form_text_js[$form['T']]['V']) ) { $totaladdnum++; }
|
524 |
+
if ( isset($this->array_form_text_js[$form['T']]['C']) ) { $totaladdnum++; }
|
525 |
+
}
|
526 |
+
}
|
527 |
+
if ( $form['typ'] == 'Bt' ) {
|
528 |
+
if ( isset($this->array_form_button_js[$form['T']]) ) { $totaladdnum++; }
|
529 |
+
if ( isset($this->form_button_icon[$form['T']]) ) {
|
530 |
+
$totaladdnum++;
|
531 |
+
if ( $this->form_button_icon[$form['T']]['Indexed'] ) { $totaladdnum++; }
|
532 |
+
}
|
533 |
+
if ( $form['subtype'] == 'radio' ) { $totaladdnum+=2; }
|
534 |
+
else if ( $form['subtype'] == 'checkbox' && $this->formUseZapD ) { $totaladdnum++; }
|
535 |
+
else if ( $form['subtype'] == 'checkbox' && !$this->formUseZapD ) { $totaladdnum+=2; }
|
536 |
+
}
|
537 |
+
if ( $form['typ'] == 'Ch' ) {
|
538 |
+
if ( isset($this->array_form_choice_js[$form['T']]) ) { $totaladdnum++; }
|
539 |
+
}
|
540 |
+
}
|
541 |
+
}
|
542 |
+
}
|
543 |
+
|
544 |
+
// In _putpages
|
545 |
+
function addFormIds($n, &$s, &$annotid) {
|
546 |
+
foreach( $this->forms as $form ) {
|
547 |
+
if ( $form['page'] == $n ) {
|
548 |
+
$s .= ($annotid) . ' 0 R ';
|
549 |
+
$annotid++;
|
550 |
+
if ( $form['typ'] == 'Tx' ) {
|
551 |
+
if ( isset($this->array_form_text_js[$form['T']]) ) {
|
552 |
+
if ( isset($this->array_form_text_js[$form['T']]['F']) ) { $annotid++; }
|
553 |
+
if ( isset($this->array_form_text_js[$form['T']]['K']) ) { $annotid++; }
|
554 |
+
if ( isset($this->array_form_text_js[$form['T']]['V']) ) { $annotid++; }
|
555 |
+
if ( isset($this->array_form_text_js[$form['T']]['C']) ) { $annotid++; }
|
556 |
+
}
|
557 |
+
}
|
558 |
+
if ( $form['typ'] == 'Bt' ) {
|
559 |
+
if ( isset($this->array_form_button_js[$form['T']]) ) { $annotid++; }
|
560 |
+
if ( isset($this->form_button_icon[$form['T']]) ) {
|
561 |
+
$annotid++;
|
562 |
+
if ( $this->form_button_icon[$form['T']]['Indexed'] ) { $annotid++; }
|
563 |
+
}
|
564 |
+
if ( $form['subtype'] == 'radio' ) { $annotid+=2; }
|
565 |
+
else if ( $form['subtype'] == 'checkbox' && $this->formUseZapD ) { $annotid++; }
|
566 |
+
else if ( $form['subtype'] == 'checkbox' && !$this->formUseZapD ) { $annotid+=2; }
|
567 |
+
}
|
568 |
+
if ( $form['typ'] == 'Ch' ) {
|
569 |
+
if ( isset($this->array_form_choice_js[$form['T']]) ) { $annotid++; }
|
570 |
+
}
|
571 |
+
}
|
572 |
+
}
|
573 |
+
}
|
574 |
+
|
575 |
+
// In _putannots
|
576 |
+
function _putFormItems($n, $hPt) {
|
577 |
+
foreach( $this->forms as $val) {
|
578 |
+
if ( $val['page'] == $n ) {
|
579 |
+
if ( $val['typ'] == 'Tx' ) $this->_putform_tx( $val, $hPt );
|
580 |
+
if ( $val['typ'] == 'Ch' ) $this->_putform_ch( $val, $hPt );
|
581 |
+
if ( $val['typ'] == 'Bt' ) $this->_putform_bt( $val, $hPt );
|
582 |
+
}
|
583 |
+
}
|
584 |
+
}
|
585 |
+
|
586 |
+
// In _putannots
|
587 |
+
function _putRadioItems($n) {
|
588 |
+
// Output Radio Groups
|
589 |
+
$key = 1;
|
590 |
+
foreach($this->form_radio_groups AS $name=>$frg) {
|
591 |
+
$this->mpdf->_newobj();
|
592 |
+
$this->pdf_acro_array .= $this->mpdf->n.' 0 R ';
|
593 |
+
$this->mpdf->_out('<<');
|
594 |
+
$this->mpdf->_out('/Type /Annot ');
|
595 |
+
$this->mpdf->_out('/Subtype /Widget');
|
596 |
+
$this->mpdf->_out('/NM '.$this->mpdf->_textstring(sprintf('%04u-%04u', $n, (3000 + $key++))));
|
597 |
+
$this->mpdf->_out('/M '.$this->mpdf->_textstring('D:'.date('YmdHis')));
|
598 |
+
$this->mpdf->_out('/Rect [0 0 0 0] ');
|
599 |
+
$this->mpdf->_out('/FT /Btn ');
|
600 |
+
if (isset($frg['disabled']) && $frg['disabled']) { $flags=array(1,3,15,16); } // NoExport and readonly
|
601 |
+
else { $flags=array(15,16); } // Flags for Radiobutton, and NoToggleToOff
|
602 |
+
$this->mpdf->_out('/Ff '.$this->_setflag($flags) );
|
603 |
+
$kstr = '';
|
604 |
+
$optstr = '';
|
605 |
+
foreach($frg['kids'] AS $kid) {
|
606 |
+
$kstr .= $this->forms[$kid['n']]['obj'].' 0 R ';
|
607 |
+
// $optstr .= ' '.$this->mpdf->_textstring($kid['OPT']).' ';
|
608 |
+
}
|
609 |
+
$this->mpdf->_out('/Kids [ '.$kstr.' ] '); // 11 0 R 12 0 R etc.
|
610 |
+
// $this->mpdf->_out('/Opt [ '.$optstr.' ] ');
|
611 |
+
|
612 |
+
//V entry holds index corresponding to the appearance state of
|
613 |
+
//whichever child field is currently in the on state = or Off
|
614 |
+
if (isset($frg['on'])) { $state = $frg['on']; }
|
615 |
+
else { $state = 'Off'; }
|
616 |
+
$this->mpdf->_out('/V /'.$state.' ');
|
617 |
+
$this->mpdf->_out('/DV /'.$state.' ');
|
618 |
+
$this->mpdf->_out('/T '.$this->mpdf->_textstring($name).' ');
|
619 |
+
$this->mpdf->_out('>>');
|
620 |
+
$this->mpdf->_out('endobj');
|
621 |
+
}
|
622 |
+
}
|
623 |
+
|
624 |
+
function _putFormsCatalog() {
|
625 |
+
if (isset($this->pdf_acro_array) ) {
|
626 |
+
$this->mpdf->_out('/AcroForm << /DA (/F1 0 Tf 0 g )');
|
627 |
+
$this->mpdf->_out('/Q 0');
|
628 |
+
$this->mpdf->_out('/Fields ['.$this->pdf_acro_array.']');
|
629 |
+
$f = '';
|
630 |
+
foreach($this->form_fonts AS $fn) {
|
631 |
+
if (is_array($this->mpdf->fonts[$fn]['n'])) { $this->mpdf->Error("Cannot use fonts with SMP or SIP characters for interactive Form elements"); }
|
632 |
+
$f .= '/F'.$this->mpdf->fonts[$fn]['i'].' '.$this->mpdf->fonts[$fn]['n'].' 0 R ';
|
633 |
+
}
|
634 |
+
$this->mpdf->_out('/DR << /Font << '.$f.' >> >>');
|
635 |
+
// CO Calculation Order
|
636 |
+
if ( $this->pdf_array_co ) {
|
637 |
+
$this->mpdf->_out('/CO ['.$this->pdf_array_co.']');
|
638 |
+
}
|
639 |
+
$this->mpdf->_out('/NeedAppearances true');
|
640 |
+
$this->mpdf->_out('>>');
|
641 |
+
}
|
642 |
+
}
|
643 |
+
|
644 |
+
|
645 |
+
|
646 |
+
function SetFormButtonJS( $name, $js ) {
|
647 |
+
$js = str_replace("\t",' ', trim($js) );
|
648 |
+
if ( isset($name) && isset($js) ) {
|
649 |
+
$this->array_form_button_js[$this->mpdf->_escape($name)] = array(
|
650 |
+
'js' => $js
|
651 |
+
);
|
652 |
+
}
|
653 |
+
}
|
654 |
+
|
655 |
+
function SetFormChoiceJS( $name, $js ) {
|
656 |
+
$js = str_replace("\t",' ', trim($js) );
|
657 |
+
if ( isset($name) && isset($js) ) {
|
658 |
+
$this->array_form_choice_js[$this->mpdf->_escape($name)] = array(
|
659 |
+
'js' => $js
|
660 |
+
);
|
661 |
+
}
|
662 |
+
}
|
663 |
+
|
664 |
+
function SetFormTextJS( $name, $js) {
|
665 |
+
for ($i=0; $i<count($js); $i++) {
|
666 |
+
$j = str_replace("\t",' ', trim($js[$i][1]) );
|
667 |
+
$format = $js[$i][0];
|
668 |
+
if ($name) {
|
669 |
+
$this->array_form_text_js[$this->mpdf->_escape($name)][$format] = array('js' => $j);
|
670 |
+
}
|
671 |
+
}
|
672 |
+
}
|
673 |
+
|
674 |
+
|
675 |
+
function Win1252ToPDFDocEncoding($txt) {
|
676 |
+
$Win1252ToPDFDocEncoding = array(
|
677 |
+
chr(0200) => chr(0240), chr(0214) => chr(0226), chr(0212) => chr(0227), chr(0237) => chr(0230),
|
678 |
+
chr(0225) => chr(0200), chr(0210) => chr(0032), chr(0206) => chr(0201), chr(0207) => chr(0202),
|
679 |
+
chr(0205) => chr(0203), chr(0227) => chr(0204), chr(0226) => chr(0205), chr(0203) => chr(0206),
|
680 |
+
chr(0213) => chr(0210), chr(0233) => chr(0211), chr(0211) => chr(0213), chr(0204) => chr(0214),
|
681 |
+
chr(0223) => chr(0215), chr(0224) => chr(0216), chr(0221) => chr(0217), chr(0222) => chr(0220),
|
682 |
+
chr(0202) => chr(0221), chr(0232) => chr(0235), chr(0230) => chr(0037), chr(0231) => chr(0222),
|
683 |
+
chr(0216) => chr(0231), chr(0240) => chr(0040)
|
684 |
+
); // mPDF 5.3.46
|
685 |
+
return strtr($txt, $Win1252ToPDFDocEncoding );
|
686 |
+
}
|
687 |
+
|
688 |
+
|
689 |
+
function SetFormText( $w, $h, $name, $value = '', $default = '', $title = '', $flags = array(), $align='L', $hidden = false, $maxlen=-1, $js='', $background_col=false, $border_col=false ) {
|
690 |
+
// Flags: 1 - Readonly; 2 - Required; 3 - No export; 13 - textarea; 14 - Password
|
691 |
+
$this->formn++;
|
692 |
+
if( $align == 'C' ) { $align = '1'; }
|
693 |
+
else if( $align == 'R' ) { $align = '2'; }
|
694 |
+
else { $align = '0'; }
|
695 |
+
if ($maxlen < 1) { $maxlen = false; }
|
696 |
+
if (!preg_match('/^[a-zA-Z0-9_:\-]+$/', $name)) {
|
697 |
+
$this->mpdf->Error("Field [".$name."] must have a name attribute, which can only contain letters, numbers, colon(:), undersore(_) or hyphen(-)");
|
698 |
+
}
|
699 |
+
if ($this->mpdf->onlyCoreFonts) {
|
700 |
+
$value = $this->Win1252ToPDFDocEncoding($value);
|
701 |
+
$default = $this->Win1252ToPDFDocEncoding($default);
|
702 |
+
$title = $this->Win1252ToPDFDocEncoding($title);
|
703 |
+
}
|
704 |
+
else {
|
705 |
+
if (isset($this->mpdf->CurrentFont['subset'])) {
|
706 |
+
$this->mpdf->UTF8StringToArray($value, true); // Add characters to font subset
|
707 |
+
$this->mpdf->UTF8StringToArray($default, true); // Add characters to font subset
|
708 |
+
$this->mpdf->UTF8StringToArray($title, true); // Add characters to font subset
|
709 |
+
}
|
710 |
+
if ($value) $value = $this->mpdf->UTF8ToUTF16BE($value, true);
|
711 |
+
if ($default ) $default = $this->mpdf->UTF8ToUTF16BE($default, true);
|
712 |
+
$title = $this->mpdf->UTF8ToUTF16BE($title, true);
|
713 |
+
}
|
714 |
+
if ($background_col) { $bg_c = $this->mpdf->SetColor($background_col, 'CodeOnly'); }
|
715 |
+
else { $bg_c = $this->form_background_color; }
|
716 |
+
if ($border_col) { $bc_c = $this->mpdf->SetColor($border_col, 'CodeOnly'); }
|
717 |
+
else { $bc_c = $this->form_border_color; }
|
718 |
+
$f = array( 'n' => $this->formn,
|
719 |
+
'typ' => 'Tx',
|
720 |
+
'page' => $this->mpdf->page,
|
721 |
+
'x' => $this->mpdf->x,
|
722 |
+
'y' => $this->mpdf->y,
|
723 |
+
'w' => $w,
|
724 |
+
'h' => $h,
|
725 |
+
'T' => $name,
|
726 |
+
'FF' => $flags,
|
727 |
+
'V' => $value,
|
728 |
+
'DV' => $default,
|
729 |
+
'TU' => $title,
|
730 |
+
'hidden' => $hidden,
|
731 |
+
'Q' => $align,
|
732 |
+
'maxlen' => $maxlen,
|
733 |
+
'BS_W' => $this->form_border_width,
|
734 |
+
'BS_S' => $this->form_border_style,
|
735 |
+
'BC_C' => $bc_c,
|
736 |
+
'BG_C' => $bg_c,
|
737 |
+
'style' => array(
|
738 |
+
'font' => $this->mpdf->FontFamily,
|
739 |
+
'fontsize' => $this->mpdf->FontSizePt,
|
740 |
+
'fontcolor' => $this->mpdf->TextColor,
|
741 |
+
)
|
742 |
+
);
|
743 |
+
if (is_array($js) && count($js)>0) { $this->SetFormTextJS( $name, $js); } // mPDF 5.3.25
|
744 |
+
if ($this->mpdf->keep_block_together) { $this->mpdf->ktForms[]= $f; }
|
745 |
+
else if ($this->mpdf->writingHTMLheader || $this->mpdf->writingHTMLfooter) { $this->mpdf->HTMLheaderPageForms[]= $f; }
|
746 |
+
else {
|
747 |
+
if ($this->mpdf->ColActive) {
|
748 |
+
$this->mpdf->columnbuffer[] = array('s' => 'ACROFORM', 'col' => $this->mpdf->CurrCol, 'x' => $this->mpdf->x, 'y' => $this->mpdf->y,
|
749 |
+
'h' => $h);
|
750 |
+
$this->mpdf->columnForms[$this->mpdf->CurrCol][INTVAL($this->mpdf->x)][INTVAL($this->mpdf->y)] = $this->formn;
|
751 |
+
}
|
752 |
+
$this->forms[$this->formn] = $f;
|
753 |
+
}
|
754 |
+
if (!in_array($this->mpdf->FontFamily, $this->form_fonts)) {
|
755 |
+
$this->form_fonts[] = $this->mpdf->FontFamily;
|
756 |
+
$this->mpdf->fonts[$this->mpdf->FontFamily]['used'] = true;
|
757 |
+
}
|
758 |
+
if ( !$hidden ) $this->mpdf->x += $w;
|
759 |
+
|
760 |
+
}
|
761 |
+
|
762 |
+
|
763 |
+
function SetFormChoice( $w, $h, $name, $flags, $array, $align='L', $js = '' ) {
|
764 |
+
$this->formn++;
|
765 |
+
if( $this->mpdf->blk[$this->mpdf->blklvl]['direction'] == 'rtl' ) { $align = '2'; }
|
766 |
+
else { $align = '0'; }
|
767 |
+
if (!preg_match('/^[a-zA-Z0-9_:\-]+$/', $name)) {
|
768 |
+
$this->mpdf->Error("Field [".$name."] must have a name attribute, which can only contain letters, numbers, colon(:), undersore(_) or hyphen(-)");
|
769 |
+
}
|
770 |
+
if ($this->mpdf->onlyCoreFonts) {
|
771 |
+
for($i=0;$i<count($array['VAL']);$i++) {
|
772 |
+
$array['VAL'][$i] = $this->Win1252ToPDFDocEncoding($array['VAL'][$i]);
|
773 |
+
$array['OPT'][$i] = $this->Win1252ToPDFDocEncoding($array['OPT'][$i]);
|
774 |
+
}
|
775 |
+
}
|
776 |
+
else {
|
777 |
+
for($i=0;$i<count($array['VAL']);$i++) {
|
778 |
+
if (isset($this->mpdf->CurrentFont['subset'])) {
|
779 |
+
$this->mpdf->UTF8StringToArray($array['VAL'][$i], true); // Add characters to font subset
|
780 |
+
$this->mpdf->UTF8StringToArray($array['OPT'][$i], true); // Add characters to font subset
|
781 |
+
}
|
782 |
+
if ($array['VAL'][$i] ) $array['VAL'][$i] = $this->mpdf->UTF8ToUTF16BE($array['VAL'][$i], true);
|
783 |
+
if ($array['OPT'][$i] ) $array['OPT'][$i] = $this->mpdf->UTF8ToUTF16BE($array['OPT'][$i], true);
|
784 |
+
}
|
785 |
+
}
|
786 |
+
$f = array( 'n' => $this->formn,
|
787 |
+
'typ' => 'Ch',
|
788 |
+
'page' => $this->mpdf->page,
|
789 |
+
'x' => $this->mpdf->x,
|
790 |
+
'y' => $this->mpdf->y,
|
791 |
+
'w' => $w,
|
792 |
+
'h' => $h,
|
793 |
+
'T' => $name,
|
794 |
+
'OPT' => $array,
|
795 |
+
'FF' => $flags,
|
796 |
+
'Q' => $align,
|
797 |
+
'BS_W' => $this->form_border_width,
|
798 |
+
'BS_S' => $this->form_border_style,
|
799 |
+
'BC_C' => $this->form_border_color,
|
800 |
+
'BG_C' => $this->form_background_color,
|
801 |
+
'style' => array(
|
802 |
+
'font' => $this->mpdf->FontFamily,
|
803 |
+
'fontsize' => $this->mpdf->FontSizePt,
|
804 |
+
'fontcolor' => $this->mpdf->TextColor,
|
805 |
+
)
|
806 |
+
);
|
807 |
+
if ($js) { $this->SetFormChoiceJS( $name, $js ); }
|
808 |
+
if ($this->mpdf->keep_block_together) { $this->mpdf->ktForms[]= $f; }
|
809 |
+
else if ($this->mpdf->writingHTMLheader || $this->mpdf->writingHTMLfooter) { $this->mpdf->HTMLheaderPageForms[]= $f; }
|
810 |
+
else {
|
811 |
+
if ($this->mpdf->ColActive) {
|
812 |
+
$this->mpdf->columnbuffer[] = array('s' => 'ACROFORM', 'col' => $this->mpdf->CurrCol, 'x' => $this->mpdf->x, 'y' => $this->mpdf->y,
|
813 |
+
'h' => $h);
|
814 |
+
$this->mpdf->columnForms[$this->mpdf->CurrCol][INTVAL($this->mpdf->x)][INTVAL($this->mpdf->y)] = $this->formn;
|
815 |
+
}
|
816 |
+
$this->forms[$this->formn] = $f;
|
817 |
+
}
|
818 |
+
if (!in_array($this->mpdf->FontFamily, $this->form_fonts)) {
|
819 |
+
$this->form_fonts[] = $this->mpdf->FontFamily;
|
820 |
+
$this->mpdf->fonts[$this->mpdf->FontFamily]['used'] = true;
|
821 |
+
}
|
822 |
+
$this->mpdf->x += $w;
|
823 |
+
}
|
824 |
+
|
825 |
+
// CHECKBOX
|
826 |
+
function SetCheckBox( $w, $h, $name, $value, $title = '', $checked = false, $flags = array(), $disabled=false ) {
|
827 |
+
$this->SetFormButton( $w, $h, $name, $value, 'checkbox', $title, $flags, $checked, $disabled );
|
828 |
+
$this->mpdf->x += $w;
|
829 |
+
}
|
830 |
+
|
831 |
+
|
832 |
+
// RADIO
|
833 |
+
function SetRadio( $w, $h, $name, $value, $title = '', $checked = false, $flags = array(), $disabled=false ) {
|
834 |
+
$this->SetFormButton( $w, $h, $name, $value, 'radio', $title, $flags, $checked, $disabled );
|
835 |
+
$this->mpdf->x += $w;
|
836 |
+
}
|
837 |
+
|
838 |
+
|
839 |
+
function SetFormReset( $w, $h, $name, $value = 'Reset', $title = '', $flags = array(), $background_col=false, $border_col=false, $noprint=false ) {
|
840 |
+
if (!$name) { $name = 'Reset'; }
|
841 |
+
$this->SetFormButton( $w, $h, $name, $value, 'reset', $title, $flags, false, false, $background_col, $border_col, $noprint);
|
842 |
+
$this->mpdf->x += $w;
|
843 |
+
}
|
844 |
+
|
845 |
+
|
846 |
+
function SetJSButton( $w, $h, $name, $value, $js, $image_id = 0, $title = '', $flags = array(), $indexed=false , $background_col=false, $border_col=false, $noprint=false ) {
|
847 |
+
$this->SetFormButton( $w, $h, $name, $value, 'js_button', $title, $flags, false, false, $background_col, $border_col, $noprint);
|
848 |
+
// pos => 1 = no caption, icon only; 0 = caption only
|
849 |
+
if ($image_id) {
|
850 |
+
$this->form_button_icon[$this->mpdf->_escape($name)] = array(
|
851 |
+
'pos' => 1,
|
852 |
+
'image_id' => $image_id,
|
853 |
+
'Indexed' => $indexed,
|
854 |
+
);
|
855 |
+
}
|
856 |
+
if ($js) { $this->SetFormButtonJS( $name, $js ); }
|
857 |
+
$this->mpdf->x += $w;
|
858 |
+
}
|
859 |
+
|
860 |
+
|
861 |
+
function SetFormSubmit( $w, $h, $name, $value = 'Submit', $url, $title = '', $typ = 'html', $method = 'POST', $flags = array(), $background_col=false, $border_col=false, $noprint=false) {
|
862 |
+
if (!$name) { $name = 'Submit'; }
|
863 |
+
$this->SetFormButton( $w, $h, $name, $value, 'submit', $title, $flags, false, false, $background_col, $border_col, $noprint);
|
864 |
+
$this->forms[$this->formn]['URL'] = $url;
|
865 |
+
$this->forms[$this->formn]['method'] = $method;
|
866 |
+
$this->forms[$this->formn]['exporttype'] = $typ;
|
867 |
+
$this->mpdf->x += $w;
|
868 |
+
}
|
869 |
+
|
870 |
+
|
871 |
+
function SetFormButtonText( $ca, $rc = '', $ac = '' ) {
|
872 |
+
if ($this->mpdf->onlyCoreFonts) {
|
873 |
+
$ca = $this->Win1252ToPDFDocEncoding($ca);
|
874 |
+
if ($rc) $rc = $this->Win1252ToPDFDocEncoding($rc);
|
875 |
+
if ($ac) $ac = $this->Win1252ToPDFDocEncoding($ac);
|
876 |
+
}
|
877 |
+
else {
|
878 |
+
if (isset($this->mpdf->CurrentFont['subset'])) {
|
879 |
+
$this->mpdf->UTF8StringToArray($ca, true); // Add characters to font subset
|
880 |
+
}
|
881 |
+
$ca = $this->mpdf->UTF8ToUTF16BE($ca, true);
|
882 |
+
if ($rc) {
|
883 |
+
if (isset($this->mpdf->CurrentFont['subset'])) { $this->mpdf->UTF8StringToArray($rc, true); }
|
884 |
+
$rc = $this->mpdf->UTF8ToUTF16BE($rc, true);
|
885 |
+
}
|
886 |
+
if ($ac) {
|
887 |
+
if (isset($this->mpdf->CurrentFont['subset'])) { $this->mpdf->UTF8StringToArray($ac, true); }
|
888 |
+
$ac = $this->mpdf->UTF8ToUTF16BE($ac, true);
|
889 |
+
}
|
890 |
+
}
|
891 |
+
$this->form_button_text = $ca;
|
892 |
+
$this->form_button_text_over = $rc ? $rc : $ca;
|
893 |
+
$this->form_button_text_click = $ac ? $ac : $ca;
|
894 |
+
}
|
895 |
+
|
896 |
+
|
897 |
+
function SetFormButton( $bb, $hh, $name, $value, $type, $title = '', $flags = array(), $checked=false, $disabled=false, $background_col=false, $border_col=false, $noprint=false ) {
|
898 |
+
$this->formn++;
|
899 |
+
if (!preg_match('/^[a-zA-Z0-9_:\-]+$/', $name)) {
|
900 |
+
$this->mpdf->Error("Field [".$name."] must have a name attribute, which can only contain letters, numbers, colon(:), undersore(_) or hyphen(-)");
|
901 |
+
}
|
902 |
+
if (!$this->mpdf->onlyCoreFonts) {
|
903 |
+
if (isset($this->mpdf->CurrentFont['subset'])) {
|
904 |
+
$this->mpdf->UTF8StringToArray($title, true); // Add characters to font subset
|
905 |
+
$this->mpdf->UTF8StringToArray($value, true); // Add characters to font subset
|
906 |
+
}
|
907 |
+
$title = $this->mpdf->UTF8ToUTF16BE($title, true);
|
908 |
+
if ($type == 'checkbox') {
|
909 |
+
$uvalue = $this->mpdf->UTF8ToUTF16BE($value, true);
|
910 |
+
}
|
911 |
+
else if ($type == 'radio') {
|
912 |
+
$uvalue = $this->mpdf->UTF8ToUTF16BE($value, true);
|
913 |
+
$value = mb_convert_encoding($value, 'Windows-1252', 'UTF-8');
|
914 |
+
}
|
915 |
+
else {
|
916 |
+
$value = $this->mpdf->UTF8ToUTF16BE($value, true);
|
917 |
+
$uvalue = $value;
|
918 |
+
}
|
919 |
+
}
|
920 |
+
else {
|
921 |
+
$title = $this->Win1252ToPDFDocEncoding($title);
|
922 |
+
$value = $this->Win1252ToPDFDocEncoding($value); //// ??? not needed
|
923 |
+
$uvalue = mb_convert_encoding($value, 'UTF-8', 'Windows-1252');
|
924 |
+
$uvalue = $this->mpdf->UTF8ToUTF16BE($uvalue, true);
|
925 |
+
}
|
926 |
+
if ($type == 'radio' || $type == 'checkbox') {
|
927 |
+
if (!preg_match('/^[a-zA-Z0-9_:\-\.]+$/', $value)) {
|
928 |
+
$this->mpdf->Error("Field '".$name."' must have a value, which can only contain letters, numbers, colon(:), undersore(_), hyphen(-) or period(.)");
|
929 |
+
}
|
930 |
+
}
|
931 |
+
if ($type == 'radio') {
|
932 |
+
if (!isset($this->form_radio_groups[$name])) {
|
933 |
+
$this->form_radio_groups[$name] = array(
|
934 |
+
'page' => $this->mpdf->page,
|
935 |
+
'kids' => array(),
|
936 |
+
);
|
937 |
+
}
|
938 |
+
$this->form_radio_groups[$name]['kids'][] = array(
|
939 |
+
'n' => $this->formn, 'V'=> $value, 'OPT'=>$uvalue, 'disabled'=>$disabled
|
940 |
+
);
|
941 |
+
if ( $checked ) { $this->form_radio_groups[$name]['on'] = $value; }
|
942 |
+
// Disable the whole radio group if one is disabled, because of inconsistency in PDF readers
|
943 |
+
if ( $disabled ) { $this->form_radio_groups[$name]['disabled'] = true; }
|
944 |
+
}
|
945 |
+
if ($type == 'checkbox') {
|
946 |
+
$this->form_checkboxes = true;
|
947 |
+
}
|
948 |
+
if ( $checked ) { $activ = 1; }
|
949 |
+
else { $activ = 0; }
|
950 |
+
if ($background_col) { $bg_c = $this->mpdf->SetColor($background_col, 'CodeOnly'); }
|
951 |
+
else { $bg_c = $this->form_button_background_color; }
|
952 |
+
if ($border_col) { $bc_c = $this->mpdf->SetColor($border_col, 'CodeOnly'); }
|
953 |
+
else { $bc_c = $this->form_button_border_color; }
|
954 |
+
$f = array( 'n' => $this->formn,
|
955 |
+
'typ' => 'Bt',
|
956 |
+
'page' => $this->mpdf->page,
|
957 |
+
'subtype' => $type,
|
958 |
+
'x' => $this->mpdf->x,
|
959 |
+
'y' => $this->mpdf->y,
|
960 |
+
'w' => $bb,
|
961 |
+
'h' => $hh,
|
962 |
+
'T' => $name,
|
963 |
+
'V' => $value,
|
964 |
+
'OPT' => $uvalue,
|
965 |
+
'TU' => $title,
|
966 |
+
'FF' => $flags,
|
967 |
+
'CA' => $this->form_button_text,
|
968 |
+
'RC' => $this->form_button_text_over,
|
969 |
+
'AC' => $this->form_button_text_click,
|
970 |
+
'BS_W' => $this->form_button_border_width,
|
971 |
+
'BS_S' => $this->form_button_border_style,
|
972 |
+
'BC_C' => $bc_c,
|
973 |
+
'BG_C' => $bg_c,
|
974 |
+
'activ' => $activ,
|
975 |
+
'disabled' => $disabled,
|
976 |
+
'noprint' => $noprint,
|
977 |
+
'style' => array(
|
978 |
+
'font' => $this->mpdf->FontFamily,
|
979 |
+
'fontsize' => $this->mpdf->FontSizePt,
|
980 |
+
'fontcolor' => $this->mpdf->TextColor,
|
981 |
+
)
|
982 |
+
);
|
983 |
+
if ($this->mpdf->keep_block_together) { $this->mpdf->ktForms[]= $f; }
|
984 |
+
else if ($this->mpdf->writingHTMLheader || $this->mpdf->writingHTMLfooter) { $this->mpdf->HTMLheaderPageForms[]= $f; }
|
985 |
+
else {
|
986 |
+
if ($this->mpdf->ColActive) {
|
987 |
+
$this->mpdf->columnbuffer[] = array('s' => 'ACROFORM', 'col' => $this->mpdf->CurrCol, 'x' => $this->mpdf->x, 'y' => $this->mpdf->y,
|
988 |
+
'h' => $hh);
|
989 |
+
$this->mpdf->columnForms[$this->mpdf->CurrCol][INTVAL($this->mpdf->x)][INTVAL($this->mpdf->y)] = $this->formn;
|
990 |
+
}
|
991 |
+
$this->forms[$this->formn] = $f;
|
992 |
+
}
|
993 |
+
if (!in_array($this->mpdf->FontFamily, $this->form_fonts)) {
|
994 |
+
$this->form_fonts[] = $this->mpdf->FontFamily;
|
995 |
+
$this->mpdf->fonts[$this->mpdf->FontFamily]['used'] = true;
|
996 |
+
}
|
997 |
+
|
998 |
+
$this->form_button_text = NULL;
|
999 |
+
$this->form_button_text_over = NULL;
|
1000 |
+
$this->form_button_text_click = NULL;
|
1001 |
+
}
|
1002 |
+
|
1003 |
+
|
1004 |
+
|
1005 |
+
function SetFormBorderWidth ( $string ) {
|
1006 |
+
switch( $string ) {
|
1007 |
+
case 'S': $this->form_border_width = '1';
|
1008 |
+
break;
|
1009 |
+
case 'M': $this->form_border_width = '2';
|
1010 |
+
break;
|
1011 |
+
case 'B': $this->form_border_width = '3';
|
1012 |
+
break;
|
1013 |
+
case '0': $this->form_border_width = '0';
|
1014 |
+
break;
|
1015 |
+
default: $this->form_border_width = '0';
|
1016 |
+
break;
|
1017 |
+
}
|
1018 |
+
}
|
1019 |
+
|
1020 |
+
|
1021 |
+
function SetFormBorderStyle ( $string ) {
|
1022 |
+
switch( $string ) {
|
1023 |
+
case 'S': $this->form_border_style = 'S';
|
1024 |
+
break;
|
1025 |
+
case 'D': $this->form_border_style = 'D /D [3]';
|
1026 |
+
break;
|
1027 |
+
case 'B': $this->form_border_style = 'B';
|
1028 |
+
break;
|
1029 |
+
case 'I': $this->form_border_style = 'I';
|
1030 |
+
break;
|
1031 |
+
case 'U': $this->form_border_style = 'U';
|
1032 |
+
break;
|
1033 |
+
default: $this->form_border_style = 'B';
|
1034 |
+
break;
|
1035 |
+
}
|
1036 |
+
}
|
1037 |
+
|
1038 |
+
function SetFormBorderColor ( $r, $g=-1, $b=-1 ) {
|
1039 |
+
if ( ($r==0 and $g==0 and $b==0) || $g==-1 )
|
1040 |
+
$this->form_border_color = sprintf('%.3F', $r/255);
|
1041 |
+
else
|
1042 |
+
$this->form_border_color = sprintf('%.3F %.3F %.3F', $r/255, $g/255, $b/255);
|
1043 |
+
}
|
1044 |
+
|
1045 |
+
function SetFormBackgroundColor ( $r, $g=-1, $b=-1 ) {
|
1046 |
+
if ( ($r==0 and $g==0 and $b==0) || $g==-1 )
|
1047 |
+
$this->form_background_color = sprintf('%.3F', $r/255);
|
1048 |
+
else
|
1049 |
+
$this->form_background_color = sprintf('%.3F %.3F %.3F', $r/255, $g/255, $b/255);
|
1050 |
+
}
|
1051 |
+
|
1052 |
+
function SetFormD ( $W, $S, $BC, $BG ) {
|
1053 |
+
$this->SetFormBorderWidth ( $W );
|
1054 |
+
$this->SetFormBorderStyle ( $S );
|
1055 |
+
$this->SetFormBorderColor ( $BC );
|
1056 |
+
$this->SetFormBackgroundColor ( $BG );
|
1057 |
+
}
|
1058 |
+
|
1059 |
+
function _setflag( $array ) {
|
1060 |
+
$flag = 0;
|
1061 |
+
foreach($array as $val) { $flag += 1 << ($val-1); }
|
1062 |
+
return $flag;
|
1063 |
+
}
|
1064 |
+
|
1065 |
+
function _form_rect( $x, $y, $w, $h, $hPt ) {
|
1066 |
+
$x = $x * _MPDFK;
|
1067 |
+
$y = $hPt - ($y * _MPDFK);
|
1068 |
+
$x2 = $x + ($w * _MPDFK);
|
1069 |
+
$y2 = $y - ($h * _MPDFK);
|
1070 |
+
$rect = sprintf('%.3F %.3F %.3F %.3F', $x, $y2, $x2, $y );
|
1071 |
+
return $rect;
|
1072 |
+
}
|
1073 |
+
|
1074 |
+
|
1075 |
+
function _put_button_icon( $array , $w, $h ) {
|
1076 |
+
if (isset($array['image_id'])) {
|
1077 |
+
$info = false;
|
1078 |
+
foreach($this->mpdf->images AS $iid=>$img) {
|
1079 |
+
if ($img['i'] == $array['image_id']) {
|
1080 |
+
$info = $this->mpdf->images[$iid];
|
1081 |
+
break;
|
1082 |
+
}
|
1083 |
+
}
|
1084 |
+
}
|
1085 |
+
if (!$info) { die("Cannot find Button image"); }
|
1086 |
+
$this->mpdf->_newobj();
|
1087 |
+
$this->mpdf->_out('<<');
|
1088 |
+
$this->mpdf->_out('/Type /XObject');
|
1089 |
+
$this->mpdf->_out('/Subtype /Image');
|
1090 |
+
$this->mpdf->_out('/BBox [0 0 1 1]');
|
1091 |
+
$this->mpdf->_out('/Length '.strlen($info['data']));
|
1092 |
+
$this->mpdf->_out('/BitsPerComponent '.$info['bpc']);
|
1093 |
+
if ($info['cs']=='Indexed') {
|
1094 |
+
$this->mpdf->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->mpdf->n+1).' 0 R]');
|
1095 |
+
}
|
1096 |
+
else {
|
1097 |
+
$this->mpdf->_out('/ColorSpace /'.$info['cs']);
|
1098 |
+
if ($info['cs']=='DeviceCMYK')
|
1099 |
+
if($info['type']=='jpg') { $this->mpdf->_out('/Decode [1 0 1 0 1 0 1 0]'); }
|
1100 |
+
}
|
1101 |
+
if ( isset($info['f']) )
|
1102 |
+
$this->mpdf->_out('/Filter /'.$info['f']);
|
1103 |
+
if ( isset($info['parms']) )
|
1104 |
+
$this->mpdf->_out($info['parms']);
|
1105 |
+
$this->mpdf->_out('/Width '.$info['w']);
|
1106 |
+
$this->mpdf->_out('/Height '.$info['h']);
|
1107 |
+
$this->mpdf->_out('>>');
|
1108 |
+
$this->mpdf->_putstream($info['data']);
|
1109 |
+
$this->mpdf->_out('endobj');
|
1110 |
+
unset($array);
|
1111 |
+
//Palette
|
1112 |
+
if($info['cs']=='Indexed') {
|
1113 |
+
$filter=($this->mpdf->compress) ? '/Filter /FlateDecode ' : '';
|
1114 |
+
$this->mpdf->_newobj();
|
1115 |
+
$pal=($this->mpdf->compress) ? gzcompress($info['pal']) : $info['pal'];
|
1116 |
+
$this->mpdf->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
|
1117 |
+
$this->mpdf->_putstream($pal);
|
1118 |
+
$this->mpdf->_out('endobj');
|
1119 |
+
}
|
1120 |
+
|
1121 |
+
}
|
1122 |
+
|
1123 |
+
|
1124 |
+
function _putform_bt( $form, $hPt ) {
|
1125 |
+
$cc = 0;
|
1126 |
+
$put_xobject = 0;
|
1127 |
+
$put_js = 0;
|
1128 |
+
$put_icon = 0;
|
1129 |
+
$this->mpdf->_newobj();
|
1130 |
+
$n = $this->mpdf->n;
|
1131 |
+
if ($form['subtype'] != 'radio') $this->pdf_acro_array .= $n.' 0 R '; // Add to /Field element
|
1132 |
+
$this->forms[ $form['n'] ]['obj'] = $n;
|
1133 |
+
$this->mpdf->_out('<<');
|
1134 |
+
$this->mpdf->_out('/Type /Annot ');
|
1135 |
+
$this->mpdf->_out('/Subtype /Widget');
|
1136 |
+
$this->mpdf->_out('/NM '.$this->mpdf->_textstring(sprintf('%04u-%04u', $n, (7000 + $form['n']))));
|
1137 |
+
$this->mpdf->_out('/M '.$this->mpdf->_textstring('D:'.date('YmdHis')));
|
1138 |
+
$this->mpdf->_out('/Rect [ '.$this->_form_rect($form['x'],$form['y'],$form['w'],$form['h'], $hPt).' ]');
|
1139 |
+
$form['noprint'] ? $this->mpdf->_out('/F 0 ') : $this->mpdf->_out('/F 4 ');
|
1140 |
+
$this->mpdf->_out('/FT /Btn ');
|
1141 |
+
$this->mpdf->_out('/H /P ');
|
1142 |
+
if ( $form['subtype'] != 'radio' ) // mPDF 5.3.23
|
1143 |
+
$this->mpdf->_out('/T '.$this->mpdf->_textstring($form['T']) );
|
1144 |
+
$this->mpdf->_out('/TU '.$this->mpdf->_textstring($form['TU']) );
|
1145 |
+
if ( isset( $this->form_button_icon[ $form['T'] ] ) ) { $form['BS_W'] = 0; }
|
1146 |
+
if ($form['BS_W'] == 0) { $form['BC_C'] = $form['BG_C']; }
|
1147 |
+
$bstemp = '';
|
1148 |
+
$bstemp .= '/W '.$form['BS_W'].' ';
|
1149 |
+
$bstemp .= '/S /'.$form['BS_S'].' ';
|
1150 |
+
$temp = '';
|
1151 |
+
$temp .= '/BC [ '.$form['BC_C']." ] ";
|
1152 |
+
$temp .= '/BG [ '.$form['BG_C']." ] ";
|
1153 |
+
if ( $form['subtype'] == 'checkbox' ) {
|
1154 |
+
if ($form['disabled']) {
|
1155 |
+
$radio_color = '0.5 0.5 0.5';
|
1156 |
+
$radio_background_color = '0.9 0.9 0.9';
|
1157 |
+
}
|
1158 |
+
else {
|
1159 |
+
$radio_color = $this->form_radio_color;
|
1160 |
+
$radio_background_color = $this->form_radio_background_color;
|
1161 |
+
}
|
1162 |
+
$temp = '';
|
1163 |
+
$temp .= '/BC [ '.$radio_color." ] ";
|
1164 |
+
$temp .= '/BG [ '.$radio_background_color." ] ";
|
1165 |
+
$this->mpdf->_out("/BS << /W 1 /S /S >>");
|
1166 |
+
$this->mpdf->_out("/MK << $temp >>");
|
1167 |
+
$this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
|
1168 |
+
if ( $form['activ'] ) {
|
1169 |
+
$this->mpdf->_out('/V /'.$this->mpdf->_escape($form['V']).' ');
|
1170 |
+
$this->mpdf->_out('/DV /'.$this->mpdf->_escape($form['V']).' ');
|
1171 |
+
$this->mpdf->_out('/AS /'.$this->mpdf->_escape($form['V']).' ');
|
1172 |
+
} else {
|
1173 |
+
$this->mpdf->_out('/AS /Off ');
|
1174 |
+
}
|
1175 |
+
if ($this->formUseZapD) {
|
1176 |
+
$this->mpdf->_out('/DA (/F'.$this->mpdf->fonts['czapfdingbats']['i'].' 0 Tf '.$radio_color.' rg)');
|
1177 |
+
$this->mpdf->_out("/AP << /N << /".$this->mpdf->_escape($form['V'])." ".($this->mpdf->n+1)." 0 R /Off /Off >> >>");
|
1178 |
+
}
|
1179 |
+
else {
|
1180 |
+
$this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$this->mpdf->CurrentFont['fontkey']]['i'].' 0 Tf '.$radio_color.' rg)');
|
1181 |
+
$this->mpdf->_out("/AP << /N << /".$this->mpdf->_escape($form['V'])." ".($this->mpdf->n+1)." 0 R /Off ".($this->mpdf->n+2)." 0 R >> >>");
|
1182 |
+
}
|
1183 |
+
$this->mpdf->_out('/Opt [ '.$this->mpdf->_textstring($form['OPT']).' '.$this->mpdf->_textstring($form['OPT']).' ]');
|
1184 |
+
}
|
1185 |
+
|
1186 |
+
|
1187 |
+
if ( $form['subtype'] == 'radio' ) {
|
1188 |
+
if ((isset($form['disabled']) && $form['disabled']) || (isset($this->form_radio_groups[$form['T']]['disabled']) && $this->form_radio_groups[$form['T']]['disabled'])) {
|
1189 |
+
$radio_color = '0.5 0.5 0.5';
|
1190 |
+
$radio_background_color = '0.9 0.9 0.9';
|
1191 |
+
}
|
1192 |
+
else {
|
1193 |
+
$radio_color = $this->form_radio_color;
|
1194 |
+
$radio_background_color = $this->form_radio_background_color;
|
1195 |
+
}
|
1196 |
+
$this->mpdf->_out('/Parent '.$this->form_radio_groups[$form['T']]['obj_id'].' 0 R ');
|
1197 |
+
$temp = '';
|
1198 |
+
$temp .= '/BC [ '.$radio_color." ] ";
|
1199 |
+
$temp .= '/BG [ '.$radio_background_color." ] ";
|
1200 |
+
$this->mpdf->_out("/BS << /W 1 /S /S >>");
|
1201 |
+
$this->mpdf->_out('/MK << '.$temp.' >> ');
|
1202 |
+
$form['FF'][] = 16; // Radiobutton
|
1203 |
+
$form['FF'][] = 15; // NoToggleOff - must be same as radio button group setting?
|
1204 |
+
$this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
|
1205 |
+
if ($this->formUseZapD)
|
1206 |
+
$this->mpdf->_out('/DA (/F'.$this->mpdf->fonts['czapfdingbats']['i'].' 0 Tf '.$radio_color.' rg)');
|
1207 |
+
else
|
1208 |
+
$this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$this->mpdf->CurrentFont['fontkey']]['i'].' 0 Tf '.$radio_color.' rg)');
|
1209 |
+
$this->mpdf->_out("/AP << /N << /".$this->mpdf->_escape($form['V'])." ".($this->mpdf->n+1)." 0 R /Off ".($this->mpdf->n+2)." 0 R >> >>");
|
1210 |
+
if ( $form['activ'] ) {
|
1211 |
+
$this->mpdf->_out('/V /'.$this->mpdf->_escape($form['V']).' ');
|
1212 |
+
$this->mpdf->_out('/DV /'.$this->mpdf->_escape($form['V']).' ');
|
1213 |
+
$this->mpdf->_out('/AS /'.$this->mpdf->_escape($form['V']).' ');
|
1214 |
+
}
|
1215 |
+
else {
|
1216 |
+
$this->mpdf->_out('/AS /Off ');
|
1217 |
+
}
|
1218 |
+
$this->mpdf->_out("/AP << /N << /".$this->mpdf->_escape($form['V'])." ".($this->mpdf->n+1)." 0 R /Off ".($this->mpdf->n+2)." 0 R >> >>");
|
1219 |
+
// $this->mpdf->_out('/Opt [ '.$this->mpdf->_textstring($form['OPT']).' '.$this->mpdf->_textstring($form['OPT']).' ]');
|
1220 |
+
}
|
1221 |
+
|
1222 |
+
if ( $form['subtype'] == 'reset' ) {
|
1223 |
+
$temp .= $form['CA'] ? '/CA '.$this->mpdf->_textstring($form['CA']).' ' : '/CA '.$this->mpdf->_textstring($form['T']).' ';
|
1224 |
+
$temp .= $form['RC'] ? '/RC '.$this->mpdf->_textstring($form['RC']).' ' : '/RC '.$this->mpdf->_textstring($form['T']).' ';
|
1225 |
+
$temp .= $form['AC'] ? '/AC '.$this->mpdf->_textstring($form['AC']).' ' : '/AC '.$this->mpdf->_textstring($form['T']).' ';
|
1226 |
+
$this->mpdf->_out("/BS << $bstemp >>");
|
1227 |
+
$this->mpdf->_out('/MK << '.$temp.' >>');
|
1228 |
+
$this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
|
1229 |
+
$this->mpdf->_out('/AA << /D << /S /ResetForm /Flags 1 >> >>');
|
1230 |
+
$form['FF'][] = 17;
|
1231 |
+
$this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
|
1232 |
+
}
|
1233 |
+
|
1234 |
+
|
1235 |
+
if ( $form['subtype'] == 'submit' ) {
|
1236 |
+
$temp .= $form['CA'] ? '/CA '.$this->mpdf->_textstring($form['CA']).' ' : '/CA '.$this->mpdf->_textstring($form['T']).' ';
|
1237 |
+
$temp .= $form['RC'] ? '/RC '.$this->mpdf->_textstring($form['RC']).' ' : '/RC '.$this->mpdf->_textstring($form['T']).' ';
|
1238 |
+
$temp .= $form['AC'] ? '/AC '.$this->mpdf->_textstring($form['AC']).' ' : '/AC '.$this->mpdf->_textstring($form['T']).' ';
|
1239 |
+
$this->mpdf->_out("/BS << $bstemp >>");
|
1240 |
+
$this->mpdf->_out("/MK << $temp >>");
|
1241 |
+
$this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
|
1242 |
+
// Bit 4 (8) = useGETmethod else use POST
|
1243 |
+
// Bit 3 (4) = HTML export format (charset chosen by Adobe)--- OR ---
|
1244 |
+
// Bit 6 (32) = XFDF export format (form of XML in UTF-8)
|
1245 |
+
if ($form['exporttype'] == 'xfdf') { $flag = 32; } // 'xfdf' or 'html'
|
1246 |
+
else {
|
1247 |
+
if ($form['method'] == 'GET') { $flag = 12; }
|
1248 |
+
else { $flag = 4; }
|
1249 |
+
}
|
1250 |
+
// Bit 2 (2) = IncludeNoValueFields
|
1251 |
+
if ($this->formSubmitNoValueFields) $flag += 2;
|
1252 |
+
// To submit a value, needs to be in /AP dictionary, AND this object must contain a /Fields entry
|
1253 |
+
// listing all fields to output
|
1254 |
+
$this->mpdf->_out('/AA << /D << /S /SubmitForm /F ('.$form['URL'].') /Flags '.$flag.' >> >>');
|
1255 |
+
$form['FF'][] = 17;
|
1256 |
+
$this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
|
1257 |
+
}
|
1258 |
+
|
1259 |
+
if ( $form['subtype'] == 'js_button' ) {
|
1260 |
+
// Icon / image
|
1261 |
+
if ( isset( $this->form_button_icon[ $form['T'] ] ) ) {
|
1262 |
+
$cc++;
|
1263 |
+
$temp .= '/TP '.$this->form_button_icon[$form['T']]['pos'].' ';
|
1264 |
+
$temp .= '/I '.($cc + $this->mpdf->n).' 0 R '; // Normal icon
|
1265 |
+
$temp .= '/RI '.($cc + $this->mpdf->n).' 0 R '; // onMouseOver
|
1266 |
+
$temp .= '/IX '.($cc + $this->mpdf->n).' 0 R '; // onClick / onMouseDown
|
1267 |
+
$temp .= '/IF << /SW /A /S /A /A [0.0 0.0] >> '; // Icon fit dictionary
|
1268 |
+
if ($this->form_button_icon[ $form['T'] ]['Indexed']) { $cc++; }
|
1269 |
+
$put_icon = 1;
|
1270 |
+
}
|
1271 |
+
$temp .= $form['CA'] ? '/CA '.$this->mpdf->_textstring($form['CA']).' ' : '/CA '.$this->mpdf->_textstring($form['T']).' ';
|
1272 |
+
$temp .= $form['RC'] ? '/RC '.$this->mpdf->_textstring($form['RC']).' ' : '/RC '.$this->mpdf->_textstring($form['T']).' ';
|
1273 |
+
$temp .= $form['AC'] ? '/AC '.$this->mpdf->_textstring($form['AC']).' ' : '/AC '.$this->mpdf->_textstring($form['T']).' ';
|
1274 |
+
$this->mpdf->_out("/BS << $bstemp >>");
|
1275 |
+
$this->mpdf->_out("/MK << $temp >>");
|
1276 |
+
$this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
|
1277 |
+
$form['FF'][] = 17;
|
1278 |
+
$this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
|
1279 |
+
// Javascript
|
1280 |
+
if ( isset($this->array_form_button_js[$form['T']]) ) {
|
1281 |
+
$cc++;
|
1282 |
+
$this->mpdf->_out("/AA << /D ".($cc + $this->mpdf->n)." 0 R >>");
|
1283 |
+
$put_js = 1;
|
1284 |
+
}
|
1285 |
+
}
|
1286 |
+
|
1287 |
+
$this->mpdf->_out('>>');
|
1288 |
+
$this->mpdf->_out('endobj');
|
1289 |
+
|
1290 |
+
// additional objects
|
1291 |
+
// obj icon
|
1292 |
+
if ( $put_icon == 1 ) {
|
1293 |
+
$this->_put_button_icon( $this->form_button_icon[ $form['T'] ], $form['w'], $form['h'] );
|
1294 |
+
$put_icon = NULL;
|
1295 |
+
}
|
1296 |
+
// obj + 1
|
1297 |
+
if ( $put_js == 1 ) {
|
1298 |
+
$this->mpdf->_set_object_javascript( $this->array_form_button_js[$form['T']]['js'] );
|
1299 |
+
unset( $this->array_form_button_js[$form['T']] );
|
1300 |
+
$put_js = NULL;
|
1301 |
+
}
|
1302 |
+
|
1303 |
+
// RADIO and CHECK BOX appearance streams
|
1304 |
+
$filter=($this->mpdf->compress) ? '/Filter /FlateDecode ' : '';
|
1305 |
+
if ( $form['subtype'] == 'radio' ) {
|
1306 |
+
// output 2 appearance streams for radio buttons on/off
|
1307 |
+
if ($this->formUseZapD) {
|
1308 |
+
$fs = sprintf('%.3F', $form['style']['fontsize']*1.25);
|
1309 |
+
$fi = 'czapfdingbats';
|
1310 |
+
$r_on = 'q '.$radio_color .' rg BT /F'.$this->mpdf->fonts[$fi]['i'].' '.$fs.' Tf 0 0 Td (4) Tj ET Q';
|
1311 |
+
$r_off = 'q '.$radio_color .' rg BT /F'.$this->mpdf->fonts[$fi]['i'].' '.$fs.' Tf 0 0 Td (8) Tj ET Q';
|
1312 |
+
}
|
1313 |
+
else {
|
1314 |
+
$matrix = sprintf('%.3F 0 0 %.3F 0 %.3F', $form['style']['fontsize']*1.33/10, $form['style']['fontsize']*1.25/10, $form['style']['fontsize']);
|
1315 |
+
$fill = $radio_background_color.' rg 3.778 -7.410 m 2.800 -7.410 1.947 -7.047 1.225 -6.322 c 0.500 -5.600 0.138 -4.747 0.138 -3.769 c 0.138 -2.788 0.500 -1.938 1.225 -1.213 c 1.947 -0.491 2.800 -0.128 3.778 -0.128 c 4.757 -0.128 5.610 -0.491 6.334 -1.213 c 7.056 -1.938 7.419 -2.788 7.419 -3.769 c 7.419 -4.747 7.056 -5.600 6.334 -6.322 c 5.610 -7.047 4.757 -7.410 3.778 -7.410 c h f ';
|
1316 |
+
$circle = '3.778 -6.963 m 4.631 -6.963 5.375 -6.641 6.013 -6.004 c 6.653 -5.366 6.972 -4.619 6.972 -3.769 c 6.972 -2.916 6.653 -2.172 6.013 -1.532 c 5.375 -0.894 4.631 -0.576 3.778 -0.576 c 2.928 -0.576 2.182 -0.894 1.544 -1.532 c 0.904 -2.172 0.585 -2.916 0.585 -3.769 c 0.585 -4.619 0.904 -5.366 1.544 -6.004 c 2.182 -6.641 2.928 -6.963 3.778 -6.963 c h 3.778 -7.410 m 2.800 -7.410 1.947 -7.047 1.225 -6.322 c 0.500 -5.600 0.138 -4.747 0.138 -3.769 c 0.138 -2.788 0.500 -1.938 1.225 -1.213 c 1.947 -0.491 2.800 -0.128 3.778 -0.128 c 4.757 -0.128 5.610 -0.491 6.334 -1.213 c 7.056 -1.938 7.419 -2.788 7.419 -3.769 c 7.419 -4.747 7.056 -5.600 6.334 -6.322 c 5.610 -7.047 4.757 -7.410 3.778 -7.410 c h f ';
|
1317 |
+
$r_on = 'q '.$matrix.' cm '.$fill .$radio_color.' rg '.$circle.' '.$radio_color.' rg
|
1318 |
+
5.184 -5.110 m 4.800 -5.494 4.354 -5.685 3.841 -5.685 c 3.331 -5.685 2.885 -5.494 2.501 -5.110 c 2.119 -4.725 1.925 -4.279 1.925 -3.769 c 1.925 -3.257 2.119 -2.810 2.501 -2.429 c 2.885 -2.044 3.331 -1.853 3.841 -1.853 c 4.354 -1.853 4.800 -2.044 5.184 -2.429 c 5.566 -2.810 5.760 -3.257 5.760 -3.769 c 5.760 -4.279 5.566 -4.725 5.184 -5.110 c h
|
1319 |
+
f Q ';
|
1320 |
+
$r_off = 'q '.$matrix.' cm '.$fill .$radio_color.' rg '.$circle.' Q ';
|
1321 |
+
}
|
1322 |
+
|
1323 |
+
$this->mpdf->_newobj();
|
1324 |
+
$p=($this->mpdf->compress) ? gzcompress($r_on) : $r_on;
|
1325 |
+
$this->mpdf->_out('<<'.$filter.'/Length '.strlen($p).' /Resources 2 0 R>>');
|
1326 |
+
$this->mpdf->_putstream($p);
|
1327 |
+
$this->mpdf->_out('endobj');
|
1328 |
+
|
1329 |
+
$this->mpdf->_newobj();
|
1330 |
+
$p=($this->mpdf->compress) ? gzcompress($r_off) : $r_off;
|
1331 |
+
$this->mpdf->_out('<<'.$filter.'/Length '.strlen($p).' /Resources 2 0 R>>');
|
1332 |
+
$this->mpdf->_putstream($p);
|
1333 |
+
$this->mpdf->_out('endobj');
|
1334 |
+
}
|
1335 |
+
if ( $form['subtype'] == 'checkbox' ) {
|
1336 |
+
// First output appearance stream for check box on
|
1337 |
+
if ($this->formUseZapD) {
|
1338 |
+
$fs = sprintf('%.3F', $form['style']['fontsize']*1.25);
|
1339 |
+
$fi = 'czapfdingbats';
|
1340 |
+
$cb_on = 'q '.$radio_color .' rg BT /F'.$this->mpdf->fonts[$fi]['i'].' '.$fs.' Tf 0 0 Td (4) Tj ET Q';
|
1341 |
+
$cb_off = 'q '.$radio_color .' rg BT /F'.$this->mpdf->fonts[$fi]['i'].' '.$fs.' Tf 0 0 Td (8) Tj ET Q';
|
1342 |
+
}
|
1343 |
+
else {
|
1344 |
+
$matrix = sprintf('%.3F 0 0 %.3F 0 %.3F', $form['style']['fontsize']*1.33/10, $form['style']['fontsize']*1.25/10, $form['style']['fontsize']);
|
1345 |
+
$fill = $radio_background_color.' rg 7.395 -0.070 m 7.395 -7.344 l 0.121 -7.344 l 0.121 -0.070 l 7.395 -0.070 l h f ';
|
1346 |
+
$square = '0.508 -6.880 m 6.969 -6.880 l 6.969 -0.534 l 0.508 -0.534 l 0.508 -6.880 l h 7.395 -0.070 m 7.395 -7.344 l 0.121 -7.344 l 0.121 -0.070 l 7.395 -0.070 l h ';
|
1347 |
+
$cb_on = 'q '.$matrix.' cm '.$fill. $radio_color.' rg '.$square.' f '.$radio_color.' rg
|
1348 |
+
6.321 -1.352 m 5.669 -2.075 5.070 -2.801 4.525 -3.532 c 3.979 -4.262 3.508 -4.967 3.112 -5.649 c 3.080 -5.706 3.039 -5.779 2.993 -5.868 c 2.858 -6.118 2.638 -6.243 2.334 -6.243 c 2.194 -6.243 2.100 -6.231 2.052 -6.205 c 2.003 -6.180 1.954 -6.118 1.904 -6.020 c 1.787 -5.788 1.688 -5.523 1.604 -5.226 c 1.521 -4.930 1.480 -4.721 1.480 -4.600 c 1.480 -4.535 1.491 -4.484 1.512 -4.447 c 1.535 -4.410 1.579 -4.367 1.647 -4.319 c 1.733 -4.259 1.828 -4.210 1.935 -4.172 c 2.040 -4.134 2.131 -4.115 2.205 -4.115 c 2.267 -4.115 2.341 -4.232 2.429 -4.469 c 2.437 -4.494 2.444 -4.511 2.448 -4.522 c 2.451 -4.531 2.456 -4.546 2.465 -4.568 c 2.546 -4.795 2.614 -4.910 2.668 -4.910 c 2.714 -4.910 2.898 -4.652 3.219 -4.136 c 3.539 -3.620 3.866 -3.136 4.197 -2.683 c 4.426 -2.367 4.633 -2.103 4.816 -1.889 c 4.998 -1.676 5.131 -1.544 5.211 -1.493 c 5.329 -1.426 5.483 -1.368 5.670 -1.319 c 5.856 -1.271 6.066 -1.238 6.296 -1.217 c 6.321 -1.352 l h f Q ';
|
1349 |
+
$cb_off = 'q '.$matrix.' cm '.$fill. $radio_color.' rg '.$square.' f Q ';
|
1350 |
+
|
1351 |
+
}
|
1352 |
+
$this->mpdf->_newobj();
|
1353 |
+
$p=($this->mpdf->compress) ? gzcompress($cb_on) : $cb_on;
|
1354 |
+
$this->mpdf->_out('<<'.$filter.'/Length '.strlen($p).' /Resources 2 0 R>>');
|
1355 |
+
$this->mpdf->_putstream($p);
|
1356 |
+
$this->mpdf->_out('endobj');
|
1357 |
+
|
1358 |
+
// output appearance stream for check box off (only if not using ZapfDingbats)
|
1359 |
+
if (!$this->formUseZapD) {
|
1360 |
+
$this->mpdf->_newobj();
|
1361 |
+
$p=($this->mpdf->compress) ? gzcompress($cb_off) : $cb_off;
|
1362 |
+
$this->mpdf->_out('<<'.$filter.'/Length '.strlen($p).' /Resources 2 0 R>>');
|
1363 |
+
$this->mpdf->_putstream($p);
|
1364 |
+
$this->mpdf->_out('endobj');
|
1365 |
+
}
|
1366 |
+
|
1367 |
+
}
|
1368 |
+
return $n;
|
1369 |
+
}
|
1370 |
+
|
1371 |
+
|
1372 |
+
function _putform_ch( $form, $hPt ) {
|
1373 |
+
$put_js = 0;
|
1374 |
+
$this->mpdf->_newobj();
|
1375 |
+
$n = $this->mpdf->n;
|
1376 |
+
$this->pdf_acro_array .= $n.' 0 R ';
|
1377 |
+
$this->forms[ $form['n'] ]['obj'] = $n;
|
1378 |
+
|
1379 |
+
$this->mpdf->_out('<<');
|
1380 |
+
$this->mpdf->_out('/Type /Annot ');
|
1381 |
+
$this->mpdf->_out('/Subtype /Widget');
|
1382 |
+
$this->mpdf->_out('/Rect [ '.$this->_form_rect($form['x'],$form['y'],$form['w'],$form['h'], $hPt).' ]');
|
1383 |
+
$this->mpdf->_out('/F 4');
|
1384 |
+
$this->mpdf->_out('/FT /Ch');
|
1385 |
+
if ($form['Q']) $this->mpdf->_out('/Q '.$form['Q'].'');
|
1386 |
+
$temp = '';
|
1387 |
+
$temp .= '/W '.$form['BS_W'].' ';
|
1388 |
+
$temp .= '/S /'.$form['BS_S'].' ';
|
1389 |
+
$this->mpdf->_out("/BS << $temp >>");
|
1390 |
+
|
1391 |
+
$temp = '';
|
1392 |
+
$temp .= '/BC [ '.$form['BC_C']." ] ";
|
1393 |
+
$temp .= '/BG [ '.$form['BG_C']." ] ";
|
1394 |
+
$this->mpdf->_out('/MK << '.$temp.' >>');
|
1395 |
+
|
1396 |
+
$this->mpdf->_out('/NM '.$this->mpdf->_textstring(sprintf('%04u-%04u', $n, (6000 + $form['n']))));
|
1397 |
+
$this->mpdf->_out('/M '.$this->mpdf->_textstring('D:'.date('YmdHis')));
|
1398 |
+
|
1399 |
+
$this->mpdf->_out('/T '.$this->mpdf->_textstring($form['T']) );
|
1400 |
+
$this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
|
1401 |
+
|
1402 |
+
$opt = '';
|
1403 |
+
for( $i = 0; $i < count($form['OPT']['VAL']) ; $i++ ) {
|
1404 |
+
$opt .= '[ '.$this->mpdf->_textstring($form['OPT']['VAL'][$i]).' '.$this->mpdf->_textstring($form['OPT']['OPT'][$i]).' ] ';
|
1405 |
+
}
|
1406 |
+
$this->mpdf->_out('/Opt [ '.$opt.']');
|
1407 |
+
|
1408 |
+
// selected
|
1409 |
+
$selectItem = false;
|
1410 |
+
$selectIndex = false;
|
1411 |
+
foreach ( $form['OPT']['SEL'] as $selectKey => $selectVal ) {
|
1412 |
+
$selectName = $this->mpdf->_textstring($form['OPT']['VAL'][$selectVal]);
|
1413 |
+
$selectItem .= ' '.$selectName.' ';
|
1414 |
+
$selectIndex .= ' '.$selectVal.' ';
|
1415 |
+
}
|
1416 |
+
if ( $selectItem ) {
|
1417 |
+
if (count($form['OPT']['SEL']) < 2) {
|
1418 |
+
$this->mpdf->_out('/V '.$selectItem.' ');
|
1419 |
+
$this->mpdf->_out('/DV '.$selectItem.' ');
|
1420 |
+
}
|
1421 |
+
else {
|
1422 |
+
$this->mpdf->_out('/V ['.$selectItem.'] ');
|
1423 |
+
$this->mpdf->_out('/DV ['.$selectItem.'] ');
|
1424 |
+
}
|
1425 |
+
$this->mpdf->_out('/I ['.$selectIndex.'] ');
|
1426 |
+
}
|
1427 |
+
|
1428 |
+
if ( is_array($form['FF']) && count($form['FF'])>0 ) {
|
1429 |
+
$this->mpdf->_out('/Ff '.$this->_setflag($form['FF']).' ');
|
1430 |
+
}
|
1431 |
+
// Javascript
|
1432 |
+
if ( isset($this->array_form_choice_js[$form['T']]) ) {
|
1433 |
+
$this->mpdf->_out("/AA << /V ".($this->mpdf->n+1)." 0 R >>");
|
1434 |
+
$put_js = 1;
|
1435 |
+
}
|
1436 |
+
|
1437 |
+
$this->mpdf->_out('>>');
|
1438 |
+
$this->mpdf->_out('endobj');
|
1439 |
+
// obj + 1
|
1440 |
+
if ( $put_js == 1 ) {
|
1441 |
+
$this->mpdf->_set_object_javascript( $this->array_form_choice_js[$form['T']]['js'] );
|
1442 |
+
unset( $this->array_form_choice_js[$form['T']] );
|
1443 |
+
$put_js = NULL;
|
1444 |
+
}
|
1445 |
+
|
1446 |
+
return $n;
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
|
1450 |
+
function _putform_tx( $form, $hPt ) {
|
1451 |
+
$put_js = 0;
|
1452 |
+
$this->mpdf->_newobj();
|
1453 |
+
$n = $this->mpdf->n;
|
1454 |
+
$this->pdf_acro_array .= $n.' 0 R ';
|
1455 |
+
$this->forms[ $form['n'] ]['obj'] = $n;
|
1456 |
+
|
1457 |
+
$this->mpdf->_out('<<');
|
1458 |
+
$this->mpdf->_out('/Type /Annot ');
|
1459 |
+
$this->mpdf->_out('/Subtype /Widget ');
|
1460 |
+
|
1461 |
+
$this->mpdf->_out('/Rect [ '.$this->_form_rect($form['x'],$form['y'],$form['w'],$form['h'], $hPt).' ] ');
|
1462 |
+
$form['hidden'] ? $this->mpdf->_out('/F 2 ') : $this->mpdf->_out('/F 4 ');
|
1463 |
+
$this->mpdf->_out('/FT /Tx ');
|
1464 |
+
|
1465 |
+
$this->mpdf->_out('/H /N ');
|
1466 |
+
$this->mpdf->_out('/R 0 ');
|
1467 |
+
|
1468 |
+
if ( is_array($form['FF']) && count($form['FF'])>0 ) {
|
1469 |
+
$this->mpdf->_out('/Ff '.$this->_setflag($form['FF']).' ');
|
1470 |
+
}
|
1471 |
+
if ( isset($form['maxlen']) && $form['maxlen']>0 ) {
|
1472 |
+
$this->mpdf->_out('/MaxLen '.$form['maxlen']);
|
1473 |
+
}
|
1474 |
+
|
1475 |
+
$temp = '';
|
1476 |
+
$temp .= '/W '.$form['BS_W'].' ';
|
1477 |
+
$temp .= '/S /'.$form['BS_S'].' ';
|
1478 |
+
$this->mpdf->_out("/BS << $temp >>");
|
1479 |
+
|
1480 |
+
$temp = '';
|
1481 |
+
$temp .= '/BC [ '.$form['BC_C']." ] ";
|
1482 |
+
$temp .= '/BG [ '.$form['BG_C']." ] ";
|
1483 |
+
$this->mpdf->_out('/MK <<'.$temp.' >>');
|
1484 |
+
|
1485 |
+
$this->mpdf->_out('/T '.$this->mpdf->_textstring($form['T']) );
|
1486 |
+
$this->mpdf->_out('/TU '.$this->mpdf->_textstring($form['TU']) );
|
1487 |
+
if ($form['V'] || $form['V']==='0')
|
1488 |
+
$this->mpdf->_out('/V '.$this->mpdf->_textstring($form['V']) );
|
1489 |
+
$this->mpdf->_out('/DV '.$this->mpdf->_textstring($form['DV']) );
|
1490 |
+
$this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
|
1491 |
+
if ( $form['Q'] ) $this->mpdf->_out('/Q '.$form['Q'].'');
|
1492 |
+
|
1493 |
+
$this->mpdf->_out('/NM '.$this->mpdf->_textstring(sprintf('%04u-%04u', $n, (5000 + $form['n']))));
|
1494 |
+
$this->mpdf->_out('/M '.$this->mpdf->_textstring('D:'.date('YmdHis')));
|
1495 |
+
|
1496 |
+
|
1497 |
+
if ( isset($this->array_form_text_js[$form['T']]) ) {
|
1498 |
+
$put_js = 1;
|
1499 |
+
$cc = 0;
|
1500 |
+
$js_str = '';
|
1501 |
+
|
1502 |
+
if ( isset($this->array_form_text_js[$form['T']]['F']) ) {
|
1503 |
+
$cc++;
|
1504 |
+
$js_str .= '/F '.($cc + $this->mpdf->n).' 0 R ';
|
1505 |
+
}
|
1506 |
+
if ( isset($this->array_form_text_js[$form['T']]['K']) ) {
|
1507 |
+
$cc++;
|
1508 |
+
$js_str .= '/K '.($cc + $this->mpdf->n).' 0 R ';
|
1509 |
+
}
|
1510 |
+
if ( isset($this->array_form_text_js[$form['T']]['V']) ) {
|
1511 |
+
$cc++;
|
1512 |
+
$js_str .= '/V '.($cc + $this->mpdf->n).' 0 R ';
|
1513 |
+
}
|
1514 |
+
if ( isset($this->array_form_text_js[$form['T']]['C']) ) {
|
1515 |
+
$cc++;
|
1516 |
+
$js_str .= '/C '.($cc + $this->mpdf->n).' 0 R ';
|
1517 |
+
$this->pdf_array_co .= $this->mpdf->n.' 0 R ';
|
1518 |
+
}
|
1519 |
+
$this->mpdf->_out('/AA << '.$js_str.' >>');
|
1520 |
+
}
|
1521 |
+
|
1522 |
+
$this->mpdf->_out('>>');
|
1523 |
+
$this->mpdf->_out('endobj');
|
1524 |
+
|
1525 |
+
if ( $put_js == 1 ) {
|
1526 |
+
if ( isset($this->array_form_text_js[$form['T']]['F']) ) {
|
1527 |
+
$this->mpdf->_set_object_javascript( $this->array_form_text_js[$form['T']]['F']['js'] );
|
1528 |
+
unset( $this->array_form_text_js[$form['T']]['F'] );
|
1529 |
+
}
|
1530 |
+
if ( isset($this->array_form_text_js[$form['T']]['K']) ) {
|
1531 |
+
$this->mpdf->_set_object_javascript( $this->array_form_text_js[$form['T']]['K']['js'] );
|
1532 |
+
unset( $this->array_form_text_js[$form['T']]['K'] );
|
1533 |
+
}
|
1534 |
+
if ( isset($this->array_form_text_js[$form['T']]['V']) ) {
|
1535 |
+
$this->mpdf->_set_object_javascript( $this->array_form_text_js[$form['T']]['V']['js'] );
|
1536 |
+
unset( $this->array_form_text_js[$form['T']]['V'] );
|
1537 |
+
}
|
1538 |
+
if ( isset($this->array_form_text_js[$form['T']]['C']) ) {
|
1539 |
+
$this->mpdf->_set_object_javascript( $this->array_form_text_js[$form['T']]['C']['js'] );
|
1540 |
+
unset( $this->array_form_text_js[$form['T']]['C'] );
|
1541 |
+
}
|
1542 |
+
}
|
1543 |
+
return $n;
|
1544 |
+
}
|
1545 |
+
|
1546 |
+
|
1547 |
+
|
1548 |
+
}
|
1549 |
+
|
1550 |
+
?>
|
lib/mpdf/classes/myanmar.php
ADDED
@@ -0,0 +1,481 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
|
4 |
+
class MYANMAR {
|
5 |
+
|
6 |
+
/* FROM hb-ot-shape-complex-indic-private.hh */
|
7 |
+
// indic_category
|
8 |
+
const OT_X = 0;
|
9 |
+
const OT_C = 1;
|
10 |
+
const OT_V = 2;
|
11 |
+
const OT_N = 3;
|
12 |
+
const OT_H = 4;
|
13 |
+
const OT_ZWNJ = 5;
|
14 |
+
const OT_ZWJ = 6;
|
15 |
+
const OT_M = 7; /* Matra or Dependent Vowel */
|
16 |
+
const OT_SM = 8;
|
17 |
+
const OT_VD = 9;
|
18 |
+
const OT_A = 10;
|
19 |
+
const OT_NBSP = 11;
|
20 |
+
const OT_DOTTEDCIRCLE = 12; /* Not in the spec, but special in Uniscribe. /Very very/ special! */
|
21 |
+
const OT_RS = 13; /* Register Shifter, used in Khmer OT spec */
|
22 |
+
const OT_Coeng = 14;
|
23 |
+
const OT_Repha = 15;
|
24 |
+
const OT_Ra = 16; /* Not explicitly listed in the OT spec, but used in the grammar. */
|
25 |
+
const OT_CM = 17;
|
26 |
+
|
27 |
+
/* FROM hb-ot-shape-complex-myanmar.hh */
|
28 |
+
// myanmar_category
|
29 |
+
const OT_DB = 3; // same as INDIC::OT_N; /* Dot below */
|
30 |
+
const OT_GB = 12; // same as INDIC::OT_DOTTEDCIRCLE;
|
31 |
+
|
32 |
+
const OT_As = 18; /* Asat */
|
33 |
+
const OT_D = 19; /* Digits except zero */
|
34 |
+
const OT_D0 = 20; /* Digit zero */
|
35 |
+
const OT_MH = 21; /* Various consonant medial types */
|
36 |
+
const OT_MR = 22; /* Various consonant medial types */
|
37 |
+
const OT_MW = 23; /* Various consonant medial types */
|
38 |
+
const OT_MY = 24; /* Various consonant medial types */
|
39 |
+
const OT_PT = 25; /* Pwo and other tones */
|
40 |
+
const OT_VAbv = 26;
|
41 |
+
const OT_VBlw = 27;
|
42 |
+
const OT_VPre = 28;
|
43 |
+
const OT_VPst = 29;
|
44 |
+
const OT_VS = 30; /* Variation selectors */
|
45 |
+
|
46 |
+
|
47 |
+
// Based on myanmar_category used to make string to find syllables
|
48 |
+
// OT_ to string character (using e.g. OT_C from MYANMAR) hb-ot-shape-complex-myanmar-private.hh
|
49 |
+
public static $myanmar_category_char = array(
|
50 |
+
'x',
|
51 |
+
'C',
|
52 |
+
'V',
|
53 |
+
'N',
|
54 |
+
'H',
|
55 |
+
'Z',
|
56 |
+
'J',
|
57 |
+
'x',
|
58 |
+
'S',
|
59 |
+
'x',
|
60 |
+
'A',
|
61 |
+
'x',
|
62 |
+
'D',
|
63 |
+
'x',
|
64 |
+
'x',
|
65 |
+
'x',
|
66 |
+
'R',
|
67 |
+
'x',
|
68 |
+
|
69 |
+
|
70 |
+
'a', /* As Asat */
|
71 |
+
'd', /* Digits except zero */
|
72 |
+
'o', /* Digit zero */
|
73 |
+
'k', /* Medial types */
|
74 |
+
'l', /* Medial types */
|
75 |
+
'm', /* Medial types */
|
76 |
+
'n', /* Medial types */
|
77 |
+
'p', /* Pwo and other tones */
|
78 |
+
'v', /* Vowel aboVe */
|
79 |
+
'b', /* Vowel Below */
|
80 |
+
'e', /* Vowel prE */
|
81 |
+
't', /* Vowel posT */
|
82 |
+
's', /* variation Selector */
|
83 |
+
|
84 |
+
);
|
85 |
+
|
86 |
+
|
87 |
+
/* Visual positions in a syllable from left to right. */
|
88 |
+
/* FROM hb-ot-shape-complex-myanmar-private.hh */
|
89 |
+
// myanmar_position
|
90 |
+
const POS_START = 0;
|
91 |
+
|
92 |
+
const POS_RA_TO_BECOME_REPH = 1;
|
93 |
+
const POS_PRE_M = 2;
|
94 |
+
const POS_PRE_C = 3;
|
95 |
+
|
96 |
+
const POS_BASE_C = 4;
|
97 |
+
const POS_AFTER_MAIN = 5;
|
98 |
+
|
99 |
+
const POS_ABOVE_C = 6;
|
100 |
+
|
101 |
+
const POS_BEFORE_SUB = 7;
|
102 |
+
const POS_BELOW_C = 8;
|
103 |
+
const POS_AFTER_SUB = 9;
|
104 |
+
|
105 |
+
const POS_BEFORE_POST = 10;
|
106 |
+
const POS_POST_C = 11;
|
107 |
+
const POS_AFTER_POST = 12;
|
108 |
+
|
109 |
+
const POS_FINAL_C = 13;
|
110 |
+
const POS_SMVD = 14;
|
111 |
+
|
112 |
+
const POS_END = 15;
|
113 |
+
|
114 |
+
|
115 |
+
|
116 |
+
public static function set_myanmar_properties(&$info) {
|
117 |
+
$u = $info['uni'];
|
118 |
+
$type = self::myanmar_get_categories($u);
|
119 |
+
$cat = ($type & 0x7F);
|
120 |
+
$pos = ($type >> 8);
|
121 |
+
/*
|
122 |
+
* Re-assign category
|
123 |
+
* http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm#analyze
|
124 |
+
*/
|
125 |
+
if (self::in_range($u, 0xFE00, 0xFE0F))
|
126 |
+
$cat = self::OT_VS;
|
127 |
+
else if ($u == 0x200C) $cat = self::OT_ZWNJ;
|
128 |
+
else if ($u == 0x200D) $cat = self::OT_ZWJ;
|
129 |
+
|
130 |
+
switch ($u) {
|
131 |
+
case 0x002D: case 0x00A0: case 0x00D7: case 0x2012:
|
132 |
+
case 0x2013: case 0x2014: case 0x2015: case 0x2022:
|
133 |
+
case 0x25CC: case 0x25FB: case 0x25FC: case 0x25FD:
|
134 |
+
case 0x25FE:
|
135 |
+
$cat = self::OT_GB;
|
136 |
+
break;
|
137 |
+
|
138 |
+
case 0x1004: case 0x101B: case 0x105A:
|
139 |
+
$cat = self::OT_Ra;
|
140 |
+
break;
|
141 |
+
|
142 |
+
case 0x1032: case 0x1036:
|
143 |
+
$cat = self::OT_A;
|
144 |
+
break;
|
145 |
+
|
146 |
+
case 0x103A:
|
147 |
+
$cat = self::OT_As;
|
148 |
+
break;
|
149 |
+
|
150 |
+
case 0x1041: case 0x1042: case 0x1043: case 0x1044:
|
151 |
+
case 0x1045: case 0x1046: case 0x1047: case 0x1048:
|
152 |
+
case 0x1049: case 0x1090: case 0x1091: case 0x1092:
|
153 |
+
case 0x1093: case 0x1094: case 0x1095: case 0x1096:
|
154 |
+
case 0x1097: case 0x1098: case 0x1099:
|
155 |
+
$cat = self::OT_D;
|
156 |
+
break;
|
157 |
+
|
158 |
+
case 0x1040:
|
159 |
+
$cat = self::OT_D; /* XXX The spec says D0, but Uniscribe doesn't seem to do. */
|
160 |
+
break;
|
161 |
+
|
162 |
+
case 0x103E: case 0x1060:
|
163 |
+
$cat = self::OT_MH;
|
164 |
+
break;
|
165 |
+
|
166 |
+
case 0x103C:
|
167 |
+
$cat = self::OT_MR;
|
168 |
+
break;
|
169 |
+
|
170 |
+
case 0x103D: case 0x1082:
|
171 |
+
$cat = self::OT_MW;
|
172 |
+
break;
|
173 |
+
|
174 |
+
case 0x103B: case 0x105E: case 0x105F:
|
175 |
+
$cat = self::OT_MY;
|
176 |
+
break;
|
177 |
+
|
178 |
+
case 0x1063: case 0x1064: case 0x1069: case 0x106A:
|
179 |
+
case 0x106B: case 0x106C: case 0x106D: case 0xAA7B:
|
180 |
+
$cat = self::OT_PT;
|
181 |
+
break;
|
182 |
+
|
183 |
+
case 0x1038: case 0x1087: case 0x1088: case 0x1089:
|
184 |
+
case 0x108A: case 0x108B: case 0x108C: case 0x108D:
|
185 |
+
case 0x108F: case 0x109A: case 0x109B: case 0x109C:
|
186 |
+
$cat = self::OT_SM;
|
187 |
+
break;
|
188 |
+
}
|
189 |
+
|
190 |
+
if ($cat == self::OT_M) {
|
191 |
+
switch ($pos) {
|
192 |
+
case self::POS_PRE_C:
|
193 |
+
$cat = self::OT_VPre;
|
194 |
+
$pos = self::POS_PRE_M;
|
195 |
+
break;
|
196 |
+
case self::POS_ABOVE_C: $cat = self::OT_VAbv; break;
|
197 |
+
case self::POS_BELOW_C: $cat = self::OT_VBlw; break;
|
198 |
+
case self::POS_POST_C: $cat = self::OT_VPst; break;
|
199 |
+
}
|
200 |
+
}
|
201 |
+
$info['myanmar_category'] = $cat;
|
202 |
+
$info['myanmar_position'] = $pos;
|
203 |
+
}
|
204 |
+
|
205 |
+
// syllable_type
|
206 |
+
const CONSONANT_SYLLABLE = 0;
|
207 |
+
const BROKEN_CLUSTER = 3;
|
208 |
+
const NON_MYANMAR_CLUSTER = 4;
|
209 |
+
|
210 |
+
|
211 |
+
|
212 |
+
public static function set_syllables(&$o, $s, &$broken_syllables) {
|
213 |
+
$ptr = 0;
|
214 |
+
$syllable_serial = 1;
|
215 |
+
$broken_syllables = false;
|
216 |
+
|
217 |
+
while($ptr < strlen($s)) {
|
218 |
+
$match = '';
|
219 |
+
$syllable_length = 1;
|
220 |
+
$syllable_type = self::NON_MYANMAR_CLUSTER ;
|
221 |
+
// CONSONANT_SYLLABLE Consonant syllable
|
222 |
+
// From OT spec:
|
223 |
+
if (preg_match('/^(RaH)?([C|R]|V|d|D)[s]?(H([C|R|V])[s]?)*(H|[a]*[n]?[l]?((m[k]?|k)[a]?)?[e]*[v]*[b]*[A]*(N[a]?)?(t[k]?[a]*[v]*[A]*(N[a]?)?)*(p[A]*(N[a]?)?)*S*[J|Z]?)/', substr($s,$ptr), $ma)) {
|
224 |
+
$syllable_length = strlen($ma[0]);
|
225 |
+
$syllable_type = self::CONSONANT_SYLLABLE ;
|
226 |
+
}
|
227 |
+
|
228 |
+
// BROKEN_CLUSTER syllable
|
229 |
+
else if (preg_match('/^(RaH)?s?(H|[a]*[n]?[l]?((m[k]?|k)[a]?)?[e]*[v]*[b]*[A]*(N[a]?)?(t[k]?[a]*[v]*[A]*(N[a]?)?)*(p[A]*(N[a]?)?)*S*[J|Z]?)/', substr($s,$ptr), $ma)) {
|
230 |
+
if (strlen($ma[0])) { // May match blank
|
231 |
+
$syllable_length = strlen($ma[0]);
|
232 |
+
$syllable_type = self::BROKEN_CLUSTER ;
|
233 |
+
$broken_syllables = true;
|
234 |
+
}
|
235 |
+
}
|
236 |
+
for ($i = $ptr; $i < $ptr+$syllable_length; $i++) { $o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type; }
|
237 |
+
$ptr += $syllable_length ;
|
238 |
+
$syllable_serial++;
|
239 |
+
if ($syllable_serial == 16) $syllable_serial = 1;
|
240 |
+
}
|
241 |
+
}
|
242 |
+
|
243 |
+
|
244 |
+
|
245 |
+
public static function reordering(&$info, $GSUBdata, $broken_syllables, $dottedcircle) {
|
246 |
+
if ($broken_syllables && $dottedcircle) { self::insert_dotted_circles ($info, $dottedcircle); }
|
247 |
+
$count = count($info);
|
248 |
+
if (!$count) return;
|
249 |
+
$last = 0;
|
250 |
+
$last_syllable = $info[0]['syllable'];
|
251 |
+
for ($i = 1; $i < $count; $i++) {
|
252 |
+
if ($last_syllable != $info[$i]['syllable']) {
|
253 |
+
self::reordering_syllable ($info, $GSUBdata, $last, $i);
|
254 |
+
$last = $i;
|
255 |
+
$last_syllable = $info[$last]['syllable'];
|
256 |
+
}
|
257 |
+
}
|
258 |
+
self::reordering_syllable($info, $GSUBdata, $last, $count);
|
259 |
+
}
|
260 |
+
|
261 |
+
public static function insert_dotted_circles(&$info, $dottedcircle) {
|
262 |
+
$idx = 0;
|
263 |
+
$last_syllable = 0;
|
264 |
+
while ($idx < count($info)) {
|
265 |
+
$syllable = $info[$idx]['syllable'];
|
266 |
+
$syllable_type = ($syllable & 0x0F);
|
267 |
+
if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
|
268 |
+
$last_syllable = $syllable;
|
269 |
+
$dottedcircle[0]['syllable'] = $info[$idx]['syllable'];
|
270 |
+
array_splice($info, $idx, 0, $dottedcircle);
|
271 |
+
}
|
272 |
+
else
|
273 |
+
$idx++;
|
274 |
+
}
|
275 |
+
// In case of final bloken cluster...
|
276 |
+
$syllable = $info[$idx]['syllable'];
|
277 |
+
$syllable_type = ($syllable & 0x0F);
|
278 |
+
if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
|
279 |
+
$dottedcircle[0]['syllable'] = $info[$idx]['syllable'];
|
280 |
+
array_splice($info, $idx, 0, $dottedcircle);
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
|
285 |
+
|
286 |
+
/* Rules from:
|
287 |
+
* https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
|
288 |
+
|
289 |
+
public static function reordering_syllable (&$info, $GSUBdata, $start, $end) {
|
290 |
+
/* vowel_syllable: We made the vowels look like consonants. So uses the consonant logic! */
|
291 |
+
/* broken_cluster: We already inserted dotted-circles, so just call the standalone_cluster. */
|
292 |
+
|
293 |
+
$syllable_type = ($info[$start]['syllable'] & 0x0F);
|
294 |
+
if ($syllable_type==self::NON_MYANMAR_CLUSTER ) { return; }
|
295 |
+
if ($syllable_type==self::BROKEN_CLUSTER) {
|
296 |
+
//if ($uniscribe_bug_compatible) {
|
297 |
+
/* For dotted-circle, this is what Uniscribe does:
|
298 |
+
* If dotted-circle is the last glyph, it just does nothing.
|
299 |
+
* i.e. It doesn't form Reph. */
|
300 |
+
if ($info[$end - 1]['myanmar_category'] == self::OT_DOTTEDCIRCLE) {
|
301 |
+
return;
|
302 |
+
}
|
303 |
+
}
|
304 |
+
|
305 |
+
$base = $end;
|
306 |
+
$has_reph = false;
|
307 |
+
$limit = $start;
|
308 |
+
|
309 |
+
if (($start + 3 <= $end) &&
|
310 |
+
$info[$start]['myanmar_category'] == self::OT_Ra &&
|
311 |
+
$info[$start+1]['myanmar_category'] == self::OT_As &&
|
312 |
+
$info[$start+2]['myanmar_category'] == self::OT_H ) {
|
313 |
+
$limit += 3;
|
314 |
+
$base = $start;
|
315 |
+
$has_reph = true;
|
316 |
+
}
|
317 |
+
|
318 |
+
if (!$has_reph)
|
319 |
+
$base = $limit;
|
320 |
+
|
321 |
+
for ($i = $limit; $i < $end; $i++) {
|
322 |
+
if (self::is_consonant($info[$i])) {
|
323 |
+
$base = $i;
|
324 |
+
break;
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
|
329 |
+
/* Reorder! */
|
330 |
+
$i = $start;
|
331 |
+
for (; $i < $start + ($has_reph ? 3 : 0); $i++)
|
332 |
+
$info[$i]['myanmar_position'] = self::POS_AFTER_MAIN;
|
333 |
+
for (; $i < $base; $i++)
|
334 |
+
$info[$i]['myanmar_position'] = self::POS_PRE_C;
|
335 |
+
if ($i < $end) {
|
336 |
+
$info[$i]['myanmar_position'] = self::POS_BASE_C;
|
337 |
+
$i++;
|
338 |
+
}
|
339 |
+
$pos = self::POS_AFTER_MAIN;
|
340 |
+
/* The following loop may be ugly, but it implements all of
|
341 |
+
* Myanmar reordering! */
|
342 |
+
for (; $i < $end; $i++) {
|
343 |
+
if ($info[$i]['myanmar_category'] == self::OT_MR) /* Pre-base reordering */
|
344 |
+
{
|
345 |
+
$info[$i]['myanmar_position'] = self::POS_PRE_C;
|
346 |
+
continue;
|
347 |
+
}
|
348 |
+
if ($info[$i]['myanmar_position'] < self::POS_BASE_C) /* Left matra */
|
349 |
+
{
|
350 |
+
continue;
|
351 |
+
}
|
352 |
+
|
353 |
+
if ($pos == self::POS_AFTER_MAIN && $info[$i]['myanmar_category'] == self::OT_VBlw)
|
354 |
+
{
|
355 |
+
$pos = self::POS_BELOW_C;
|
356 |
+
$info[$i]['myanmar_position'] = $pos;
|
357 |
+
continue;
|
358 |
+
}
|
359 |
+
|
360 |
+
if ($pos == self::POS_BELOW_C && $info[$i]['myanmar_category'] == self::OT_A)
|
361 |
+
{
|
362 |
+
$info[$i]['myanmar_position'] = self::POS_BEFORE_SUB;
|
363 |
+
continue;
|
364 |
+
}
|
365 |
+
if ($pos == self::POS_BELOW_C && $info[$i]['myanmar_category'] == self::OT_VBlw)
|
366 |
+
{
|
367 |
+
$info[$i]['myanmar_position'] = $pos;
|
368 |
+
continue;
|
369 |
+
}
|
370 |
+
if ($pos == self::POS_BELOW_C && $info[$i]['myanmar_category'] != self::OT_A)
|
371 |
+
{
|
372 |
+
$pos = self::POS_AFTER_SUB;
|
373 |
+
$info[$i]['myanmar_position'] = $pos;
|
374 |
+
continue;
|
375 |
+
}
|
376 |
+
$info[$i]['myanmar_position'] = $pos;
|
377 |
+
}
|
378 |
+
|
379 |
+
|
380 |
+
/* Sit tight, rock 'n roll! */
|
381 |
+
self::bubble_sort ($info, $start, $end - $start);
|
382 |
+
|
383 |
+
}
|
384 |
+
|
385 |
+
|
386 |
+
public static function is_one_of ($info, $flags) {
|
387 |
+
if (isset($info['is_ligature']) && $info['is_ligature']) return false; /* If it ligated, all bets are off. */
|
388 |
+
return !!(self::FLAG($info['myanmar_category']) & $flags);
|
389 |
+
}
|
390 |
+
|
391 |
+
/* Vowels and placeholders treated as if they were consonants. */
|
392 |
+
public static function is_consonant($info) {
|
393 |
+
return self::is_one_of($info, (self::FLAG(self::OT_C) | self::FLAG(self::OT_CM) | self::FLAG(self::OT_Ra) | self::FLAG(self::OT_V) | self::FLAG(self::OT_NBSP) | self::FLAG(self::OT_GB)));
|
394 |
+
}
|
395 |
+
|
396 |
+
|
397 |
+
|
398 |
+
// From hb-private.hh
|
399 |
+
public static function in_range ($u, $lo, $hi) {
|
400 |
+
if ( (($lo^$hi) & $lo) == 0 && (($lo^$hi) & $hi) == ($lo^$hi) && (($lo^$hi) & (($lo^$hi) + 1)) == 0 )
|
401 |
+
return ($u & ~($lo^$hi)) == $lo;
|
402 |
+
else
|
403 |
+
return $lo <= $u && $u <= $hi;
|
404 |
+
}
|
405 |
+
|
406 |
+
// From hb-private.hh
|
407 |
+
public static function FLAG($x) { return (1<<($x)); }
|
408 |
+
|
409 |
+
public static function FLAG_RANGE($x,$y) { self::FLAG(y+1) - self::FLAG(x); }
|
410 |
+
|
411 |
+
|
412 |
+
|
413 |
+
// BELOW from hb-ot-shape-complex-indic.cc
|
414 |
+
// see INDIC for details
|
415 |
+
|
416 |
+
public static $myanmar_table = array(
|
417 |
+
|
418 |
+
/* Myanmar (1000..109F) */
|
419 |
+
|
420 |
+
/* 1000 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
421 |
+
/* 1008 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
422 |
+
/* 1010 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
423 |
+
/* 1018 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
424 |
+
/* 1020 */ 3841, 3842, 3842, 3842, 3842, 3842, 3842, 3842,
|
425 |
+
/* 1028 */ 3842, 3842, 3842, 2823, 2823, 1543, 1543, 2055,
|
426 |
+
/* 1030 */ 2055, 775, 1543, 1543, 1543, 1543, 3848, 3843,
|
427 |
+
/* 1038 */ 3848, 3844, 1540, 3857, 3857, 3857, 3857, 3841,
|
428 |
+
/* 1040 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
429 |
+
/* 1048 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
430 |
+
/* 1050 */ 3841, 3841, 3842, 3842, 3842, 3842, 2823, 2823,
|
431 |
+
/* 1058 */ 2055, 2055, 3841, 3841, 3841, 3841, 3857, 3857,
|
432 |
+
/* 1060 */ 3857, 3841, 2823, 3843, 3843, 3841, 3841, 2823,
|
433 |
+
/* 1068 */ 2823, 3843, 3843, 3843, 3843, 3843, 3841, 3841,
|
434 |
+
/* 1070 */ 3841, 1543, 1543, 1543, 1543, 3841, 3841, 3841,
|
435 |
+
/* 1078 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
436 |
+
/* 1080 */ 3841, 3841, 3857, 2823, 775, 1543, 1543, 3843,
|
437 |
+
/* 1088 */ 3843, 3843, 3843, 3843, 3843, 3843, 3841, 3843,
|
438 |
+
/* 1090 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
439 |
+
/* 1098 */ 3840, 3840, 3843, 3843, 2823, 1543, 3840, 3840,
|
440 |
+
|
441 |
+
/* Myanmar Extended-A (AA60..AA7F) */
|
442 |
+
|
443 |
+
/* AA60 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
444 |
+
/* AA68 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
445 |
+
/* AA70 */ 3840, 3841, 3841, 3841, 3840, 3840, 3840, 3840,
|
446 |
+
/* AA78 */ 3840, 3840, 3841, 3843, 3840, 3840, 3840, 3840,
|
447 |
+
|
448 |
+
|
449 |
+
);
|
450 |
+
|
451 |
+
// from "hb-ot-shape-complex-indic-table.cc"
|
452 |
+
public static function myanmar_get_categories ($u) {
|
453 |
+
if (0x1000 <= $u && $u <= 0x109F) return self::$myanmar_table[$u - 0x1000 + 0]; // offset 0 for Most "myanmar"
|
454 |
+
if (0xAA60 <= $u && $u <= 0xAA7F) return self::$myanmar_table[$u - 0xAA60 + 160]; // offset for extensions
|
455 |
+
if ($u == 0x00A0) return 3851; // (ISC_CP | (IMC_x << 8))
|
456 |
+
if ($u == 0x25CC) return 3851; // (ISC_CP | (IMC_x << 8))
|
457 |
+
return 3840; // (ISC_x | (IMC_x << 8))
|
458 |
+
}
|
459 |
+
|
460 |
+
|
461 |
+
public static function bubble_sort(&$arr, $start, $len) {
|
462 |
+
if ($len<2) { return;}
|
463 |
+
$k = $start+$len-2;
|
464 |
+
while ($k >= $start) {
|
465 |
+
for ($j=$start; $j<=$k; $j++) {
|
466 |
+
if ($arr[$j]['myanmar_position'] > $arr[$j + 1]['myanmar_position']) {
|
467 |
+
$t = $arr[$j];
|
468 |
+
$arr[$j] = $arr[$j + 1];
|
469 |
+
$arr[$j + 1] = $t;
|
470 |
+
}
|
471 |
+
}
|
472 |
+
$k--;
|
473 |
+
}
|
474 |
+
}
|
475 |
+
|
476 |
+
|
477 |
+
|
478 |
+
|
479 |
+
} // end Class
|
480 |
+
|
481 |
+
?>
|
lib/mpdf/classes/otl.php
ADDED
@@ -0,0 +1,5719 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
define("_OTL_OLD_SPEC_COMPAT_1", true);
|
4 |
+
|
5 |
+
define("_DICT_NODE_TYPE_SPLIT", 0x01);
|
6 |
+
define("_DICT_NODE_TYPE_LINEAR", 0x02);
|
7 |
+
define("_DICT_INTERMEDIATE_MATCH", 0x03);
|
8 |
+
define("_DICT_FINAL_MATCH", 0x04);
|
9 |
+
|
10 |
+
|
11 |
+
|
12 |
+
class otl {
|
13 |
+
|
14 |
+
var $mpdf;
|
15 |
+
var $arabLeftJoining;
|
16 |
+
var $arabRightJoining;
|
17 |
+
var $arabTransparentJoin;
|
18 |
+
var $arabTransparent;
|
19 |
+
var $GSUBdata;
|
20 |
+
var $GPOSdata;
|
21 |
+
var $GSUBfont;
|
22 |
+
var $fontkey;
|
23 |
+
var $ttfOTLdata;
|
24 |
+
var $glyphIDtoUni;
|
25 |
+
var $_pos;
|
26 |
+
var $GSUB_offset;
|
27 |
+
var $GPOS_offset;
|
28 |
+
var $MarkAttachmentType;
|
29 |
+
var $MarkGlyphSets;
|
30 |
+
var $GlyphClassMarks;
|
31 |
+
var $GlyphClassLigatures;
|
32 |
+
var $GlyphClassBases;
|
33 |
+
var $GlyphClassComponents;
|
34 |
+
var $Ignores;
|
35 |
+
var $LuCoverage;
|
36 |
+
var $OTLdata;
|
37 |
+
var $assocLigs;
|
38 |
+
var $assocMarks;
|
39 |
+
var $shaper;
|
40 |
+
var $restrictToSyllable;
|
41 |
+
var $lbdicts; // Line-breaking dictionaries
|
42 |
+
var $LuDataCache;
|
43 |
+
|
44 |
+
var $debugOTL = false;
|
45 |
+
|
46 |
+
function otl(&$mpdf) {
|
47 |
+
$this->mpdf = $mpdf;
|
48 |
+
|
49 |
+
$this->arabic_initialise();
|
50 |
+
$this->current_fh = '';
|
51 |
+
|
52 |
+
$this->lbdicts = array();
|
53 |
+
$this->LuDataCache = array();
|
54 |
+
}
|
55 |
+
|
56 |
+
////////////////////////////////////////////////////////////////
|
57 |
+
////////////////////////////////////////////////////////////////
|
58 |
+
////////// APPLY OTL ////////////////////////////
|
59 |
+
////////////////////////////////////////////////////////////////
|
60 |
+
////////////////////////////////////////////////////////////////
|
61 |
+
|
62 |
+
function applyOTL($str, $useOTL) {
|
63 |
+
$this->OTLdata = array();
|
64 |
+
if (trim($str)=='') { return $str; }
|
65 |
+
if (!$useOTL) { return $str; }
|
66 |
+
|
67 |
+
// 1. Load GDEF data
|
68 |
+
//==============================
|
69 |
+
$this->fontkey = $this->mpdf->CurrentFont['fontkey'];
|
70 |
+
$this->glyphIDtoUni = $this->mpdf->CurrentFont['glyphIDtoUni'];
|
71 |
+
if (!isset($this->GDEFdata[$this->fontkey])) {
|
72 |
+
include(_MPDF_TTFONTDATAPATH.$this->fontkey.'.GDEFdata.php');
|
73 |
+
$this->GSUB_offset = $this->GDEFdata[$this->fontkey]['GSUB_offset'] = $GSUB_offset;
|
74 |
+
$this->GPOS_offset = $this->GDEFdata[$this->fontkey]['GPOS_offset'] = $GPOS_offset;
|
75 |
+
$this->GSUB_length = $this->GDEFdata[$this->fontkey]['GSUB_length'] = $GSUB_length;
|
76 |
+
$this->MarkAttachmentType = $this->GDEFdata[$this->fontkey]['MarkAttachmentType'] = $MarkAttachmentType;
|
77 |
+
$this->MarkGlyphSets = $this->GDEFdata[$this->fontkey]['MarkGlyphSets'] = $MarkGlyphSets;
|
78 |
+
$this->GlyphClassMarks = $this->GDEFdata[$this->fontkey]['GlyphClassMarks'] = $GlyphClassMarks;
|
79 |
+
$this->GlyphClassLigatures = $this->GDEFdata[$this->fontkey]['GlyphClassLigatures'] = $GlyphClassLigatures;
|
80 |
+
$this->GlyphClassComponents = $this->GDEFdata[$this->fontkey]['GlyphClassComponents'] = $GlyphClassComponents;
|
81 |
+
$this->GlyphClassBases = $this->GDEFdata[$this->fontkey]['GlyphClassBases'] = $GlyphClassBases;
|
82 |
+
}
|
83 |
+
else {
|
84 |
+
$this->GSUB_offset = $this->GDEFdata[$this->fontkey]['GSUB_offset'];
|
85 |
+
$this->GPOS_offset = $this->GDEFdata[$this->fontkey]['GPOS_offset'];
|
86 |
+
$this->GSUB_length = $this->GDEFdata[$this->fontkey]['GSUB_length'];
|
87 |
+
$this->MarkAttachmentType = $this->GDEFdata[$this->fontkey]['MarkAttachmentType'];
|
88 |
+
$this->MarkGlyphSets = $this->GDEFdata[$this->fontkey]['MarkGlyphSets'];
|
89 |
+
$this->GlyphClassMarks = $this->GDEFdata[$this->fontkey]['GlyphClassMarks'];
|
90 |
+
$this->GlyphClassLigatures = $this->GDEFdata[$this->fontkey]['GlyphClassLigatures'];
|
91 |
+
$this->GlyphClassComponents = $this->GDEFdata[$this->fontkey]['GlyphClassComponents'];
|
92 |
+
$this->GlyphClassBases = $this->GDEFdata[$this->fontkey]['GlyphClassBases'];
|
93 |
+
}
|
94 |
+
|
95 |
+
// 2. Prepare string as HEX string and Analyse character properties
|
96 |
+
//=================================================================
|
97 |
+
$earr = $this->mpdf->UTF8StringToArray($str, false);
|
98 |
+
|
99 |
+
$scriptblock = 0;
|
100 |
+
$scriptblocks = array();
|
101 |
+
$scriptblocks[0] = 0;
|
102 |
+
$vstr = '';
|
103 |
+
$OTLdata = array();
|
104 |
+
$subchunk = 0;
|
105 |
+
$charctr = 0;
|
106 |
+
foreach($earr as $char) {
|
107 |
+
$ucd_record = UCDN::get_ucd_record($char);
|
108 |
+
$sbl = $ucd_record[6];
|
109 |
+
|
110 |
+
// Special case - Arabic End of Ayah
|
111 |
+
if ($char==1757) { $sbl = UCDN::SCRIPT_ARABIC; }
|
112 |
+
|
113 |
+
if ($sbl && $sbl != 40 && $sbl != 102) {
|
114 |
+
if ($scriptblock == 0) { $scriptblock = $sbl; $scriptblocks[$subchunk] = $scriptblock; }
|
115 |
+
else if ($scriptblock > 0 && $scriptblock != $sbl) {
|
116 |
+
// *************************************************
|
117 |
+
// NEW (non-common) Script encountered in this chunk. Start a new subchunk
|
118 |
+
$subchunk++;
|
119 |
+
$scriptblock = $sbl;
|
120 |
+
$charctr = 0;
|
121 |
+
$scriptblocks[$subchunk] = $scriptblock;
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
$OTLdata[$subchunk][$charctr]['general_category'] = $ucd_record[0];
|
126 |
+
$OTLdata[$subchunk][$charctr]['bidi_type'] = $ucd_record[2];
|
127 |
+
|
128 |
+
//$OTLdata[$subchunk][$charctr]['combining_class'] = $ucd_record[1];
|
129 |
+
//$OTLdata[$subchunk][$charctr]['bidi_type'] = $ucd_record[2];
|
130 |
+
//$OTLdata[$subchunk][$charctr]['mirrored'] = $ucd_record[3];
|
131 |
+
//$OTLdata[$subchunk][$charctr]['east_asian_width'] = $ucd_record[4];
|
132 |
+
//$OTLdata[$subchunk][$charctr]['normalization_check'] = $ucd_record[5];
|
133 |
+
//$OTLdata[$subchunk][$charctr]['script'] = $ucd_record[6];
|
134 |
+
|
135 |
+
$charasstr = $this->unicode_hex($char);
|
136 |
+
|
137 |
+
if (strpos($this->GlyphClassMarks, $charasstr)!==false) { $OTLdata[$subchunk][$charctr]['group'] = 'M'; }
|
138 |
+
else if ($char == 32 || $char == 12288) { $OTLdata[$subchunk][$charctr]['group'] = 'S'; } // 12288 = 0x3000 = CJK space
|
139 |
+
else { $OTLdata[$subchunk][$charctr]['group'] = 'C'; }
|
140 |
+
|
141 |
+
$OTLdata[$subchunk][$charctr]['uni'] = $char;
|
142 |
+
$OTLdata[$subchunk][$charctr]['hex'] = $charasstr;
|
143 |
+
$charctr++;
|
144 |
+
}
|
145 |
+
|
146 |
+
/* PROCESS EACH SUBCHUNK WITH DIFFERENT SCRIPTS */
|
147 |
+
for($sch=0;$sch<=$subchunk;$sch++) {
|
148 |
+
$this->OTLdata = $OTLdata[$sch];
|
149 |
+
$scriptblock = $scriptblocks[$sch];
|
150 |
+
|
151 |
+
// 3. Get Appropriate Scripts, and Shaper engine from analysing text and list of available scripts/langsys in font
|
152 |
+
//==============================
|
153 |
+
// Based on actual script block of text, select shaper (and line-breaking dictionaries)
|
154 |
+
if (UCDN::SCRIPT_DEVANAGARI <= $scriptblock && $scriptblock <= UCDN::SCRIPT_MALAYALAM) { $this->shaper = "I"; } // INDIC shaper
|
155 |
+
else if ($scriptblock == UCDN::SCRIPT_ARABIC || $scriptblock == UCDN::SCRIPT_SYRIAC) { $this->shaper = "A"; } // ARABIC shaper
|
156 |
+
else if ($scriptblock == UCDN::SCRIPT_NKO || $scriptblock == UCDN::SCRIPT_MANDAIC) { $this->shaper = "A"; } // ARABIC shaper
|
157 |
+
else if ($scriptblock == UCDN::SCRIPT_KHMER) { $this->shaper = "K"; } // KHMER shaper
|
158 |
+
else if ($scriptblock == UCDN::SCRIPT_THAI) { $this->shaper = "T"; } // THAI shaper
|
159 |
+
else if ($scriptblock == UCDN::SCRIPT_LAO) { $this->shaper = "L"; } // LAO shaper
|
160 |
+
else if ($scriptblock == UCDN::SCRIPT_SINHALA) { $this->shaper = "S"; } // SINHALA shaper
|
161 |
+
else if ($scriptblock == UCDN::SCRIPT_MYANMAR) { $this->shaper = "M"; } // MYANMAR shaper
|
162 |
+
else if ($scriptblock == UCDN::SCRIPT_NEW_TAI_LUE) { $this->shaper = "E"; } // SEA South East Asian shaper
|
163 |
+
else if ($scriptblock == UCDN::SCRIPT_CHAM) { $this->shaper = "E"; } // SEA South East Asian shaper
|
164 |
+
else if ($scriptblock == UCDN::SCRIPT_TAI_THAM) { $this->shaper = "E"; } // SEA South East Asian shaper
|
165 |
+
else $this->shaper = "";
|
166 |
+
// Get scripttag based on actual text script
|
167 |
+
$scripttag = UCDN::$uni_scriptblock[$scriptblock];
|
168 |
+
|
169 |
+
$GSUBscriptTag = '';
|
170 |
+
$GSUBlangsys = '';
|
171 |
+
$GPOSscriptTag = '';
|
172 |
+
$GPOSlangsys = '';
|
173 |
+
$is_old_spec = false;
|
174 |
+
|
175 |
+
$ScriptLang = $this->mpdf->CurrentFont['GSUBScriptLang'];
|
176 |
+
if (count($ScriptLang)) {
|
177 |
+
list($GSUBscriptTag,$is_old_spec) = $this->_getOTLscriptTag($ScriptLang, $scripttag, $scriptblock, $this->shaper, $useOTL, 'GSUB');
|
178 |
+
if ($this->mpdf->fontLanguageOverride && strpos($ScriptLang[$GSUBscriptTag], $this->mpdf->fontLanguageOverride)!==false) {
|
179 |
+
$GSUBlangsys = str_pad($this->mpdf->fontLanguageOverride,4);
|
180 |
+
}
|
181 |
+
else if ($GSUBscriptTag && isset($ScriptLang[$GSUBscriptTag]) && $ScriptLang[$GSUBscriptTag]!='') {
|
182 |
+
$GSUBlangsys = $this->_getOTLLangTag($this->mpdf->currentLang, $ScriptLang[$GSUBscriptTag]);
|
183 |
+
}
|
184 |
+
}
|
185 |
+
$ScriptLang = $this->mpdf->CurrentFont['GPOSScriptLang'];
|
186 |
+
|
187 |
+
// NB If after GSUB, the same script/lang exist for GPOS, just use these...
|
188 |
+
if ($GSUBscriptTag && $GSUBlangsys && isset($ScriptLang[$GSUBscriptTag]) && strpos($ScriptLang[$GSUBscriptTag], $GSUBlangsys)!==false) {
|
189 |
+
$GPOSlangsys = $GSUBlangsys;
|
190 |
+
$GPOSscriptTag = $GSUBscriptTag;
|
191 |
+
}
|
192 |
+
|
193 |
+
// else repeat for GPOS
|
194 |
+
// [Font XBRiyaz has GSUB tables for latn, but not GPOS for latn]
|
195 |
+
else if (count($ScriptLang)) {
|
196 |
+
list($GPOSscriptTag,$dummy) = $this->_getOTLscriptTag($ScriptLang, $scripttag, $scriptblock, $this->shaper, $useOTL, 'GPOS');
|
197 |
+
if ($GPOSscriptTag && $this->mpdf->fontLanguageOverride && strpos($ScriptLang[$GPOSscriptTag], $this->mpdf->fontLanguageOverride)!==false) {
|
198 |
+
$GPOSlangsys = str_pad($this->mpdf->fontLanguageOverride,4);
|
199 |
+
}
|
200 |
+
else if ($GPOSscriptTag && isset($ScriptLang[$GPOSscriptTag]) && $ScriptLang[$GPOSscriptTag]!='') {
|
201 |
+
$GPOSlangsys = $this->_getOTLLangTag($this->mpdf->currentLang, $ScriptLang[$GPOSscriptTag]);
|
202 |
+
}
|
203 |
+
}
|
204 |
+
|
205 |
+
////////////////////////////////////////////////////////////////
|
206 |
+
// This is just for the font_dump_OTL utility to set script and langsys override
|
207 |
+
if (isset($this->mpdf->overrideOTLsettings) && isset($this->mpdf->overrideOTLsettings[$this->fontkey])) {
|
208 |
+
$GSUBscriptTag = $GPOSscriptTag = $this->mpdf->overrideOTLsettings[$this->fontkey]['script'];
|
209 |
+
$GSUBlangsys = $GPOSlangsys = $this->mpdf->overrideOTLsettings[$this->fontkey]['lang'];
|
210 |
+
}
|
211 |
+
////////////////////////////////////////////////////////////////
|
212 |
+
|
213 |
+
if (!$GSUBscriptTag && !$GSUBlangsys && !$GPOSscriptTag && !$GPOSlangsys) {
|
214 |
+
// Remove ZWJ and ZWNJ
|
215 |
+
for ($i=0;$i<count($this->OTLdata);$i++) {
|
216 |
+
if ($this->OTLdata[$i]['uni']==8204 || $this->OTLdata[$i]['uni']==8205) {
|
217 |
+
array_splice($this->OTLdata, $i, 1);
|
218 |
+
}
|
219 |
+
}
|
220 |
+
$this->schOTLdata[$sch] = $this->OTLdata;
|
221 |
+
$this->OTLdata = array();
|
222 |
+
continue;
|
223 |
+
}
|
224 |
+
|
225 |
+
// Don't use MYANMAR shaper unless using v2 scripttag
|
226 |
+
if ($this->shaper == 'M' && $GSUBscriptTag != 'mym2') { $this->shaper = ''; }
|
227 |
+
|
228 |
+
$GSUBFeatures = (isset($this->mpdf->CurrentFont['GSUBFeatures'][$GSUBscriptTag][$GSUBlangsys]) ? $this->mpdf->CurrentFont['GSUBFeatures'][$GSUBscriptTag][$GSUBlangsys] : false);
|
229 |
+
$GPOSFeatures = (isset($this->mpdf->CurrentFont['GPOSFeatures'][$GPOSscriptTag][$GPOSlangsys]) ? $this->mpdf->CurrentFont['GPOSFeatures'][$GPOSscriptTag][$GPOSlangsys] : false);
|
230 |
+
|
231 |
+
$this->assocLigs = array(); // Ligatures[$posarr lpos] => nc
|
232 |
+
$this->assocMarks = array(); // assocMarks[$posarr mpos] => array(compID, ligPos)
|
233 |
+
|
234 |
+
if (!isset($this->GDEFdata[$this->fontkey]['GSUBGPOStables'])) {
|
235 |
+
$this->ttfOTLdata = $this->GDEFdata[$this->fontkey]['GSUBGPOStables'] = file_get_contents(_MPDF_TTFONTDATAPATH.$this->fontkey.'.GSUBGPOStables.dat','rb') or die('Can\'t open file ' . _MPDF_TTFONTDATAPATH.$this->fontkey.'.GSUBGPOStables.dat');
|
236 |
+
}
|
237 |
+
else {
|
238 |
+
$this->ttfOTLdata = $this->GDEFdata[$this->fontkey]['GSUBGPOStables'];
|
239 |
+
}
|
240 |
+
|
241 |
+
|
242 |
+
if ($this->debugOTL) { $this->_dumpproc('BEGIN', '-', '-', '-', '-', -1, '-', 0); }
|
243 |
+
|
244 |
+
|
245 |
+
////////////////////////////////////////////////////////////////
|
246 |
+
////////////////////////////////////////////////////////////////
|
247 |
+
///////// LINE BREAKING FOR KHMER, THAI + LAO /////////////////
|
248 |
+
////////////////////////////////////////////////////////////////
|
249 |
+
////////////////////////////////////////////////////////////////
|
250 |
+
// Insert U+200B at word boundaries using dictionaries
|
251 |
+
if ($this->mpdf->useDictionaryLBR && ($this->shaper == "K" || $this->shaper == "T" || $this->shaper == "L")) {
|
252 |
+
// Sets $this->OTLdata[$i]['wordend']=true at possible end of word boundaries
|
253 |
+
$this->SEAlineBreaking();
|
254 |
+
}
|
255 |
+
// Insert U+200B at word boundaries for Tibetan
|
256 |
+
else if ($this->mpdf->useTibetanLBR && $scriptblock == UCDN::SCRIPT_TIBETAN ) {
|
257 |
+
// Sets $this->OTLdata[$i]['wordend']=true at possible end of word boundaries
|
258 |
+
$this->TibetanlineBreaking();
|
259 |
+
}
|
260 |
+
////////////////////////////////////////////////////////////////
|
261 |
+
////////////////////////////////////////////////////////////////
|
262 |
+
////////// GSUB /////////////////////////////////
|
263 |
+
////////////////////////////////////////////////////////////////
|
264 |
+
////////////////////////////////////////////////////////////////
|
265 |
+
if (($useOTL & 0xFF) && $GSUBscriptTag && $GSUBlangsys && $GSUBFeatures) {
|
266 |
+
|
267 |
+
// 4. Load GSUB data, Coverage & Lookups
|
268 |
+
//=================================================================
|
269 |
+
|
270 |
+
$this->GSUBfont = $this->fontkey.'.GSUB.'.$GSUBscriptTag.'.'.$GSUBlangsys;
|
271 |
+
|
272 |
+
if (!isset($this->GSUBdata[$this->GSUBfont])) {
|
273 |
+
if (file_exists(_MPDF_TTFONTDATAPATH.$this->mpdf->CurrentFont['fontkey'].'.GSUB.'.$GSUBscriptTag.'.'.$GSUBlangsys.'.php')) {
|
274 |
+
include_once(_MPDF_TTFONTDATAPATH.$this->mpdf->CurrentFont['fontkey'].'.GSUB.'.$GSUBscriptTag.'.'.$GSUBlangsys.'.php');
|
275 |
+
$this->GSUBdata[$this->GSUBfont]['rtlSUB'] = $rtlSUB;
|
276 |
+
$this->GSUBdata[$this->GSUBfont]['finals'] = $finals;
|
277 |
+
if ($this->shaper=='I') {
|
278 |
+
$this->GSUBdata[$this->GSUBfont]['rphf'] = $rphf;
|
279 |
+
$this->GSUBdata[$this->GSUBfont]['half'] = $half;
|
280 |
+
$this->GSUBdata[$this->GSUBfont]['pref'] = $pref;
|
281 |
+
$this->GSUBdata[$this->GSUBfont]['blwf'] = $blwf;
|
282 |
+
$this->GSUBdata[$this->GSUBfont]['pstf'] = $pstf;
|
283 |
+
}
|
284 |
+
}
|
285 |
+
else { $this->GSUBdata[$this->GSUBfont] = array('rtlSUB'=>array(), 'rphf'=>array(), 'rphf'=>array(),
|
286 |
+
'pref'=>array(), 'blwf'=>array(), 'pstf'=>array(), 'finals'=>''
|
287 |
+
);
|
288 |
+
}
|
289 |
+
}
|
290 |
+
|
291 |
+
if (!isset($this->GSUBdata[$this->fontkey])) {
|
292 |
+
include(_MPDF_TTFONTDATAPATH.$this->fontkey.'.GSUBdata.php');
|
293 |
+
$this->GSLuCoverage = $this->GSUBdata[$this->fontkey]['GSLuCoverage'] = $GSLuCoverage;
|
294 |
+
}
|
295 |
+
else {
|
296 |
+
$this->GSLuCoverage = $this->GSUBdata[$this->fontkey]['GSLuCoverage'];
|
297 |
+
}
|
298 |
+
|
299 |
+
$this->GSUBLookups = $this->mpdf->CurrentFont['GSUBLookups'];
|
300 |
+
|
301 |
+
|
302 |
+
// 5(A). GSUB - Shaper - ARABIC
|
303 |
+
//==============================
|
304 |
+
if ($this->shaper == 'A') {
|
305 |
+
//-----------------------------------------------------------------------------------
|
306 |
+
// a. Apply initial GSUB Lookups (in order specified in lookup list but only selecting from certain tags)
|
307 |
+
//-----------------------------------------------------------------------------------
|
308 |
+
$tags = 'locl ccmp';
|
309 |
+
$omittags = '';
|
310 |
+
$usetags = $tags;
|
311 |
+
if(!empty($this->mpdf->OTLtags)) {
|
312 |
+
$usetags = $this->_applyTagSettings($tags, $GSUBFeatures, $omittags, true) ;
|
313 |
+
}
|
314 |
+
$this->_applyGSUBrules($usetags, $GSUBscriptTag, $GSUBlangsys);
|
315 |
+
|
316 |
+
//-----------------------------------------------------------------------------------
|
317 |
+
// b. Apply context-specific forms GSUB Lookups (initial, isolated, medial, final)
|
318 |
+
//-----------------------------------------------------------------------------------
|
319 |
+
// Arab and Syriac are the only scripts requiring the special joining - which takes the place of
|
320 |
+
// isol fina medi init rules in GSUB (+ fin2 fin3 med2 in Syriac syrc)
|
321 |
+
$tags = 'isol fina fin2 fin3 medi med2 init';
|
322 |
+
$omittags = '';
|
323 |
+
$usetags = $tags;
|
324 |
+
if(!empty($this->mpdf->OTLtags)) {
|
325 |
+
$usetags = $this->_applyTagSettings($tags, $GSUBFeatures, $omittags, true) ;
|
326 |
+
}
|
327 |
+
|
328 |
+
$this->arabGlyphs = $this->GSUBdata[$this->GSUBfont]['rtlSUB'];
|
329 |
+
|
330 |
+
$gcms = explode("| ",$this->GlyphClassMarks);
|
331 |
+
$gcm = array();
|
332 |
+
foreach($gcms AS $g) { $gcm[hexdec($g)] = 1; }
|
333 |
+
$this->arabTransparentJoin = $this->arabTransparent + $gcm;
|
334 |
+
$this->arabic_shaper($usetags, $GSUBscriptTag);
|
335 |
+
|
336 |
+
//-----------------------------------------------------------------------------------
|
337 |
+
// c. Set Kashida points (after joining occurred - medi, fina, init) but before other substitutions
|
338 |
+
//-----------------------------------------------------------------------------------
|
339 |
+
//if ($scriptblock == UCDN::SCRIPT_ARABIC ) {
|
340 |
+
for ($i=0;$i<count($this->OTLdata);$i++) {
|
341 |
+
// Put the kashida marker on the character BEFORE which is inserted the kashida
|
342 |
+
// Kashida marker is inverse of priority i.e. Priority 1 => 7, Priority 7 => 1.
|
343 |
+
|
344 |
+
// Priority 1 User-inserted Kashida 0640 = Tatweel
|
345 |
+
// The user entered a Kashida in a position
|
346 |
+
// Position: Before the user-inserted kashida
|
347 |
+
if ($this->OTLdata[$i]['uni']==0x0640) {
|
348 |
+
$this->OTLdata[$i]['GPOSinfo']['kashida'] = 8; // Put before the next character
|
349 |
+
}
|
350 |
+
|
351 |
+
// Priority 2 Seen (0633) FEB3, FEB4; Sad (0635) FEBB, FEBC
|
352 |
+
// Initial or medial form
|
353 |
+
// Connecting to the next character
|
354 |
+
// Position: After the character
|
355 |
+
else if ($this->OTLdata[$i]['uni']==0xFEB3 || $this->OTLdata[$i]['uni']==0xFEB4 || $this->OTLdata[$i]['uni']==0xFEBB || $this->OTLdata[$i]['uni']==0xFEBC) {
|
356 |
+
$checkpos = $i+1;
|
357 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($this->GlyphClassMarks, $this->OTLdata[$checkpos]['hex'])!==false) {
|
358 |
+
$checkpos++;
|
359 |
+
}
|
360 |
+
if (isset($this->OTLdata[$checkpos])) {
|
361 |
+
$this->OTLdata[$checkpos]['GPOSinfo']['kashida'] = 7; // Put after marks on next character
|
362 |
+
}
|
363 |
+
}
|
364 |
+
|
365 |
+
// Priority 3 Taa Marbutah (0629) FE94; Haa (062D) FEA2; Dal (062F) FEAA
|
366 |
+
// Final form
|
367 |
+
// Connecting to previous character
|
368 |
+
// Position: Before the character
|
369 |
+
else if ($this->OTLdata[$i]['uni']==0xFE94 || $this->OTLdata[$i]['uni']==0xFEA2 || $this->OTLdata[$i]['uni']==0xFEAA) {
|
370 |
+
$this->OTLdata[$i]['GPOSinfo']['kashida'] = 6;
|
371 |
+
}
|
372 |
+
|
373 |
+
// Priority 4 Alef (0627) FE8E; Tah (0637) FEC2; Lam (0644) FEDE; Kaf (0643) FEDA; Gaf (06AF) FB93
|
374 |
+
// Final form
|
375 |
+
// Connecting to previous character
|
376 |
+
// Position: Before the character
|
377 |
+
else if ($this->OTLdata[$i]['uni']==0xFE8E || $this->OTLdata[$i]['uni']==0xFEC2 || $this->OTLdata[$i]['uni']==0xFEDE || $this->OTLdata[$i]['uni']==0xFEDA || $this->OTLdata[$i]['uni']==0xFB93) {
|
378 |
+
$this->OTLdata[$i]['GPOSinfo']['kashida'] = 5;
|
379 |
+
}
|
380 |
+
|
381 |
+
// Priority 5 RA (0631) FEAE; Ya (064A) FEF2 FEF4; Alef Maqsurah (0649) FEF0 FBE9
|
382 |
+
// Final or Medial form
|
383 |
+
// Connected to preceding medial BAA (0628) = FE92
|
384 |
+
// Position: Before preceding medial Baa
|
385 |
+
// Although not mentioned in spec, added Farsi Yeh (06CC) FBFD FBFF; equivalent to 064A or 0649
|
386 |
+
else if ($this->OTLdata[$i]['uni']==0xFEAE || $this->OTLdata[$i]['uni']==0xFEF2 || $this->OTLdata[$i]['uni']==0xFEF0
|
387 |
+
|| $this->OTLdata[$i]['uni']==0xFEF4 || $this->OTLdata[$i]['uni']==0xFBE9
|
388 |
+
|| $this->OTLdata[$i]['uni']==0xFBFD || $this->OTLdata[$i]['uni']==0xFBFF
|
389 |
+
) {
|
390 |
+
$checkpos = $i-1;
|
391 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($this->GlyphClassMarks, $this->OTLdata[$checkpos]['hex'])!==false) {
|
392 |
+
$checkpos--;
|
393 |
+
}
|
394 |
+
if (isset($this->OTLdata[$checkpos]) && $this->OTLdata[$checkpos]['uni']==0xFE92) {
|
395 |
+
$this->OTLdata[$checkpos]['GPOSinfo']['kashida'] = 4; // ******* Before preceding BAA
|
396 |
+
}
|
397 |
+
}
|
398 |
+
|
399 |
+
// Priority 6 WAW (0648) FEEE; Ain (0639) FECA; Qaf (0642) FED6; Fa (0641) FED2
|
400 |
+
// Final form
|
401 |
+
// Connecting to previous character
|
402 |
+
// Position: Before the character
|
403 |
+
else if ($this->OTLdata[$i]['uni']==0xFEEE || $this->OTLdata[$i]['uni']==0xFECA || $this->OTLdata[$i]['uni']==0xFED6 || $this->OTLdata[$i]['uni']==0xFED2) {
|
404 |
+
$this->OTLdata[$i]['GPOSinfo']['kashida'] = 3;
|
405 |
+
}
|
406 |
+
|
407 |
+
// Priority 7 Other connecting characters
|
408 |
+
// Final form
|
409 |
+
// Connecting to previous character
|
410 |
+
// Position: Before the character
|
411 |
+
/* This isn't in the spec, but using MS WORD as a basis, give a lower priority to the 3 characters already checked
|
412 |
+
in (5) above. Test case:
|
413 |
+
خْرَىٰ
|
414 |
+
فَتُذَكِّر
|
415 |
+
*/
|
416 |
+
|
417 |
+
if (!isset($this->OTLdata[$i]['GPOSinfo']['kashida'])) {
|
418 |
+
if (strpos($this->GSUBdata[$this->GSUBfont]['finals'], $this->OTLdata[$i]['hex'])!==false) { // ANY OTHER FINAL FORM
|
419 |
+
$this->OTLdata[$i]['GPOSinfo']['kashida'] = 2;
|
420 |
+
}
|
421 |
+
else if (strpos('0FEAE 0FEF0 0FEF2',$this->OTLdata[$i]['hex'])!==false) { // not already included in 5 above
|
422 |
+
$this->OTLdata[$i]['GPOSinfo']['kashida'] = 1;
|
423 |
+
}
|
424 |
+
}
|
425 |
+
}
|
426 |
+
|
427 |
+
//-----------------------------------------------------------------------------------
|
428 |
+
// d. Apply Presentation Forms GSUB Lookups (+ any discretionary) - Apply one at a time in Feature order
|
429 |
+
//-----------------------------------------------------------------------------------
|
430 |
+
$tags = 'rlig calt liga clig mset';
|
431 |
+
|
432 |
+
$omittags = 'locl ccmp nukt akhn rphf rkrf pref blwf abvf half pstf cfar vatu cjct init medi fina isol med2 fin2 fin3 ljmo vjmo tjmo';
|
433 |
+
$usetags = $tags;
|
434 |
+
if(!empty($this->mpdf->OTLtags)) {
|
435 |
+
$usetags = $this->_applyTagSettings($tags, $GSUBFeatures, $omittags, false) ;
|
436 |
+
}
|
437 |
+
|
438 |
+
$ts = explode(' ',$usetags);
|
439 |
+
foreach($ts AS $ut) { // - Apply one at a time in Feature order
|
440 |
+
$this->_applyGSUBrules($ut, $GSUBscriptTag, $GSUBlangsys);
|
441 |
+
}
|
442 |
+
//-----------------------------------------------------------------------------------
|
443 |
+
// e. NOT IN SPEC
|
444 |
+
// If space precedes a mark -> substitute a before the Mark, to prevent line breaking Test:
|
445 |
+
//-----------------------------------------------------------------------------------
|
446 |
+
for($ptr=1; $ptr<count($this->OTLdata); $ptr++) {
|
447 |
+
if ($this->OTLdata[$ptr]['general_category'] == UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK && $this->OTLdata[$ptr-1]['uni'] == 32) {
|
448 |
+
$this->OTLdata[$ptr-1]['uni'] = 0xa0;
|
449 |
+
$this->OTLdata[$ptr-1]['hex'] = '000A0';
|
450 |
+
}
|
451 |
+
}
|
452 |
+
}
|
453 |
+
|
454 |
+
// 5(I). GSUB - Shaper - INDIC and SINHALA and KHMER
|
455 |
+
//===================================
|
456 |
+
else if ($this->shaper == 'I' || $this->shaper == 'K' || $this->shaper == 'S') {
|
457 |
+
$this->restrictToSyllable = true;
|
458 |
+
//-----------------------------------------------------------------------------------
|
459 |
+
// a. First decompose/compose split mattras
|
460 |
+
// (normalize) ??????? Nukta/Halant order etc ??????????????????????????????????????????????????????????????????????????
|
461 |
+
//-----------------------------------------------------------------------------------
|
462 |
+
for($ptr=0; $ptr<count($this->OTLdata); $ptr++) {
|
463 |
+
$char = $this->OTLdata[$ptr]['uni'];
|
464 |
+
$sub = INDIC::decompose_indic($char);
|
465 |
+
if ($sub) {
|
466 |
+
$newinfo = array();
|
467 |
+
for($i=0;$i<count($sub);$i++) {
|
468 |
+
$newinfo[$i] = array();
|
469 |
+
$ucd_record = UCDN::get_ucd_record($sub[$i]);
|
470 |
+
$newinfo[$i]['general_category'] = $ucd_record[0];
|
471 |
+
$newinfo[$i]['bidi_type'] = $ucd_record[2];
|
472 |
+
$charasstr = $this->unicode_hex($sub[$i]);
|
473 |
+
if (strpos($this->GlyphClassMarks, $charasstr)!==false) { $newinfo[$i]['group'] = 'M'; }
|
474 |
+
else { $newinfo[$i]['group'] = 'C'; }
|
475 |
+
$newinfo[$i]['uni'] = $sub[$i];
|
476 |
+
$newinfo[$i]['hex'] = $charasstr;
|
477 |
+
}
|
478 |
+
array_splice($this->OTLdata, $ptr, 1, $newinfo);
|
479 |
+
$ptr += count($sub)-1;
|
480 |
+
}
|
481 |
+
/* Only Composition-exclusion exceptions that we want to recompose. */
|
482 |
+
if ($this->shaper == 'I') {
|
483 |
+
if ($char == 0x09AF && isset($this->OTLdata[$ptr + 1]) && $this->OTLdata[$ptr + 1]['uni'] == 0x09BC) {
|
484 |
+
$sub = 0x09DF;
|
485 |
+
$newinfo = array();
|
486 |
+
$newinfo[0] = array();
|
487 |
+
$ucd_record = UCDN::get_ucd_record($sub);
|
488 |
+
$newinfo[0]['general_category'] = $ucd_record[0];
|
489 |
+
$newinfo[0]['bidi_type'] = $ucd_record[2];
|
490 |
+
$newinfo[0]['group'] = 'C';
|
491 |
+
$newinfo[0]['uni'] = $sub;
|
492 |
+
$newinfo[0]['hex'] = $this->unicode_hex($sub);
|
493 |
+
array_splice($this->OTLdata, $ptr, 2, $newinfo);
|
494 |
+
}
|
495 |
+
}
|
496 |
+
}
|
497 |
+
//-----------------------------------------------------------------------------------
|
498 |
+
// b. Analyse characters - group as syllables/clusters (Indic); invalid diacritics; add dotted circle
|
499 |
+
//-----------------------------------------------------------------------------------
|
500 |
+
$indic_category_string = '';
|
501 |
+
foreach($this->OTLdata AS $eid=>$c) {
|
502 |
+
INDIC::set_indic_properties($this->OTLdata[$eid], $scriptblock ); // sets ['indic_category'] and ['indic_position']
|
503 |
+
//$c['general_category']
|
504 |
+
//$c['combining_class']
|
505 |
+
//$c['uni'] = $char;
|
506 |
+
|
507 |
+
$indic_category_string .= INDIC::$indic_category_char[$this->OTLdata[$eid]['indic_category']];
|
508 |
+
}
|
509 |
+
|
510 |
+
$broken_syllables = false;
|
511 |
+
if ($this->shaper == 'I') {
|
512 |
+
INDIC::set_syllables($this->OTLdata, $indic_category_string, $broken_syllables);
|
513 |
+
}
|
514 |
+
else if ($this->shaper == 'S') {
|
515 |
+
INDIC::set_syllables_sinhala($this->OTLdata, $indic_category_string, $broken_syllables);
|
516 |
+
}
|
517 |
+
else if ($this->shaper == 'K') {
|
518 |
+
INDIC::set_syllables_khmer($this->OTLdata, $indic_category_string, $broken_syllables);
|
519 |
+
}
|
520 |
+
$indic_category_string = '';
|
521 |
+
|
522 |
+
//-----------------------------------------------------------------------------------
|
523 |
+
// c. Initial Re-ordering (Indic / Khmer / Sinhala)
|
524 |
+
//-----------------------------------------------------------------------------------
|
525 |
+
// Find base consonant
|
526 |
+
// Decompose/compose and reorder Matras
|
527 |
+
// Reorder marks to canonical order
|
528 |
+
|
529 |
+
$indic_config = INDIC::$indic_configs[$scriptblock];
|
530 |
+
$dottedcircle = false;
|
531 |
+
if ($broken_syllables) {
|
532 |
+
if ($this->mpdf->_charDefined($this->mpdf->fonts[$this->fontkey]['cw'],0x25CC) ) {
|
533 |
+
$dottedcircle = array();
|
534 |
+
$ucd_record = UCDN::get_ucd_record(0x25CC);
|
535 |
+
$dottedcircle[0]['general_category'] = $ucd_record[0];
|
536 |
+
$dottedcircle[0]['bidi_type'] = $ucd_record[2];
|
537 |
+
$dottedcircle[0]['group'] = 'C';
|
538 |
+
$dottedcircle[0]['uni'] = 0x25CC;
|
539 |
+
$dottedcircle[0]['indic_category'] = INDIC::OT_DOTTEDCIRCLE;
|
540 |
+
$dottedcircle[0]['indic_position'] = INDIC::POS_BASE_C;
|
541 |
+
|
542 |
+
$dottedcircle[0]['hex'] = '025CC'; // TEMPORARY *****
|
543 |
+
}
|
544 |
+
}
|
545 |
+
INDIC::initial_reordering($this->OTLdata, $this->GSUBdata[$this->GSUBfont], $broken_syllables, $indic_config, $scriptblock, $is_old_spec, $dottedcircle);
|
546 |
+
|
547 |
+
//-----------------------------------------------------------------------------------
|
548 |
+
// d. Apply initial and basic shaping forms GSUB Lookups (one at a time)
|
549 |
+
//-----------------------------------------------------------------------------------
|
550 |
+
if ($this->shaper == 'I' || $this->shaper == 'S') {
|
551 |
+
$tags = 'locl ccmp nukt akhn rphf rkrf pref blwf half pstf vatu cjct';
|
552 |
+
}
|
553 |
+
else if ($this->shaper == 'K') {
|
554 |
+
$tags = 'locl ccmp pref blwf abvf pstf cfar';
|
555 |
+
}
|
556 |
+
$this->_applyGSUBrulesIndic($tags, $GSUBscriptTag, $GSUBlangsys, $is_old_spec);
|
557 |
+
|
558 |
+
//-----------------------------------------------------------------------------------
|
559 |
+
// e. Final Re-ordering (Indic / Khmer / Sinhala)
|
560 |
+
//-----------------------------------------------------------------------------------
|
561 |
+
// Reorder matras
|
562 |
+
// Reorder reph
|
563 |
+
// Reorder pre-base reordering consonants:
|
564 |
+
|
565 |
+
INDIC::final_reordering($this->OTLdata, $this->GSUBdata[$this->GSUBfont], $indic_config, $scriptblock, $is_old_spec);
|
566 |
+
|
567 |
+
//-----------------------------------------------------------------------------------
|
568 |
+
// f. Apply 'init' feature to first syllable in word (indicated by ['mask']) INDIC::FLAG(INDIC::INIT);
|
569 |
+
//-----------------------------------------------------------------------------------
|
570 |
+
if ($this->shaper == 'I' || $this->shaper == 'S') {
|
571 |
+
$tags = 'init';
|
572 |
+
$this->_applyGSUBrulesIndic($tags, $GSUBscriptTag, $GSUBlangsys, $is_old_spec);
|
573 |
+
}
|
574 |
+
|
575 |
+
//-----------------------------------------------------------------------------------
|
576 |
+
// g. Apply Presentation Forms GSUB Lookups (+ any discretionary)
|
577 |
+
//-----------------------------------------------------------------------------------
|
578 |
+
$tags = 'pres abvs blws psts haln rlig calt liga clig mset';
|
579 |
+
|
580 |
+
$omittags = 'locl ccmp nukt akhn rphf rkrf pref blwf abvf half pstf cfar vatu cjct init medi fina isol med2 fin2 fin3 ljmo vjmo tjmo';
|
581 |
+
$usetags = $tags;
|
582 |
+
if(!empty($this->mpdf->OTLtags)) {
|
583 |
+
$usetags = $this->_applyTagSettings($tags, $GSUBFeatures, $omittags, false) ;
|
584 |
+
}
|
585 |
+
if ($this->shaper == 'K') { // Features are applied one at a time, working through each codepoint
|
586 |
+
$this->_applyGSUBrulesSingly($usetags, $GSUBscriptTag, $GSUBlangsys);
|
587 |
+
}
|
588 |
+
else {
|
589 |
+
$this->_applyGSUBrules($usetags, $GSUBscriptTag, $GSUBlangsys);
|
590 |
+
}
|
591 |
+
$this->restrictToSyllable = false;
|
592 |
+
}
|
593 |
+
|
594 |
+
|
595 |
+
// 5(M). GSUB - Shaper - MYANMAR (ONLY mym2)
|
596 |
+
//==============================
|
597 |
+
// NB Old style 'mymr' is left to go through the default shaper
|
598 |
+
else if ($this->shaper == 'M') {
|
599 |
+
$this->restrictToSyllable = true;
|
600 |
+
//-----------------------------------------------------------------------------------
|
601 |
+
// a. Analyse characters - group as syllables/clusters (Myanmar); invalid diacritics; add dotted circle
|
602 |
+
//-----------------------------------------------------------------------------------
|
603 |
+
$myanmar_category_string = '';
|
604 |
+
foreach($this->OTLdata AS $eid=>$c) {
|
605 |
+
MYANMAR::set_myanmar_properties($this->OTLdata[$eid]); // sets ['myanmar_category'] and ['myanmar_position']
|
606 |
+
$myanmar_category_string .= MYANMAR::$myanmar_category_char[$this->OTLdata[$eid]['myanmar_category']];
|
607 |
+
}
|
608 |
+
$broken_syllables = false;
|
609 |
+
MYANMAR::set_syllables($this->OTLdata, $myanmar_category_string, $broken_syllables);
|
610 |
+
$myanmar_category_string = '';
|
611 |
+
|
612 |
+
//-----------------------------------------------------------------------------------
|
613 |
+
// b. Re-ordering (Myanmar mym2)
|
614 |
+
//-----------------------------------------------------------------------------------
|
615 |
+
$dottedcircle = false;
|
616 |
+
if ($broken_syllables) {
|
617 |
+
if ($this->mpdf->_charDefined($this->mpdf->fonts[$this->fontkey]['cw'],0x25CC) ) {
|
618 |
+
$dottedcircle = array();
|
619 |
+
$ucd_record = UCDN::get_ucd_record(0x25CC);
|
620 |
+
$dottedcircle[0]['general_category'] = $ucd_record[0];
|
621 |
+
$dottedcircle[0]['bidi_type'] = $ucd_record[2];
|
622 |
+
$dottedcircle[0]['group'] = 'C';
|
623 |
+
$dottedcircle[0]['uni'] = 0x25CC;
|
624 |
+
$dottedcircle[0]['myanmar_category'] = MYANMAR::OT_DOTTEDCIRCLE;
|
625 |
+
$dottedcircle[0]['myanmar_position'] = MYANMAR::POS_BASE_C;
|
626 |
+
$dottedcircle[0]['hex'] = '025CC';
|
627 |
+
}
|
628 |
+
}
|
629 |
+
MYANMAR::reordering($this->OTLdata, $this->GSUBdata[$this->GSUBfont], $broken_syllables, $dottedcircle);
|
630 |
+
|
631 |
+
//-----------------------------------------------------------------------------------
|
632 |
+
// c. Apply initial and basic shaping forms GSUB Lookups (one at a time)
|
633 |
+
//-----------------------------------------------------------------------------------
|
634 |
+
|
635 |
+
$tags = 'locl ccmp rphf pref blwf pstf';
|
636 |
+
$this->_applyGSUBrulesMyanmar($tags, $GSUBscriptTag, $GSUBlangsys);
|
637 |
+
|
638 |
+
//-----------------------------------------------------------------------------------
|
639 |
+
// d. Apply Presentation Forms GSUB Lookups (+ any discretionary)
|
640 |
+
//-----------------------------------------------------------------------------------
|
641 |
+
$tags = 'pres abvs blws psts haln rlig calt liga clig mset';
|
642 |
+
$omittags = 'locl ccmp nukt akhn rphf rkrf pref blwf abvf half pstf cfar vatu cjct init medi fina isol med2 fin2 fin3 ljmo vjmo tjmo';
|
643 |
+
$usetags = $tags;
|
644 |
+
if(!empty($this->mpdf->OTLtags)) {
|
645 |
+
$usetags = $this->_applyTagSettings($tags, $GSUBFeatures, $omittags, false) ;
|
646 |
+
}
|
647 |
+
$this->_applyGSUBrules($usetags, $GSUBscriptTag, $GSUBlangsys);
|
648 |
+
$this->restrictToSyllable = false;
|
649 |
+
}
|
650 |
+
|
651 |
+
|
652 |
+
// 5(E). GSUB - Shaper - SEA South East Asian (New Tai Lue, Cham, Tai Tam)
|
653 |
+
//==============================
|
654 |
+
else if ($this->shaper == 'E') {
|
655 |
+
/* HarfBuzz says: If the designer designed the font for the 'DFLT' script,
|
656 |
+
* use the default shaper. Otherwise, use the SEA shaper.
|
657 |
+
* Note that for some simple scripts, there may not be *any*
|
658 |
+
* GSUB/GPOS needed, so there may be no scripts found! */
|
659 |
+
|
660 |
+
$this->restrictToSyllable = true;
|
661 |
+
//-----------------------------------------------------------------------------------
|
662 |
+
// a. Analyse characters - group as syllables/clusters (Indic); invalid diacritics; add dotted circle
|
663 |
+
//-----------------------------------------------------------------------------------
|
664 |
+
$sea_category_string = '';
|
665 |
+
foreach($this->OTLdata AS $eid=>$c) {
|
666 |
+
SEA::set_sea_properties($this->OTLdata[$eid], $scriptblock ); // sets ['sea_category'] and ['sea_position']
|
667 |
+
//$c['general_category']
|
668 |
+
//$c['combining_class']
|
669 |
+
//$c['uni'] = $char;
|
670 |
+
|
671 |
+
$sea_category_string .= SEA::$sea_category_char[$this->OTLdata[$eid]['sea_category']];
|
672 |
+
}
|
673 |
+
|
674 |
+
$broken_syllables = false;
|
675 |
+
SEA::set_syllables($this->OTLdata, $sea_category_string, $broken_syllables);
|
676 |
+
$sea_category_string = '';
|
677 |
+
|
678 |
+
//-----------------------------------------------------------------------------------
|
679 |
+
// b. Apply locl and ccmp shaping forms - before initial re-ordering; GSUB Lookups (one at a time)
|
680 |
+
//-----------------------------------------------------------------------------------
|
681 |
+
$tags = 'locl ccmp';
|
682 |
+
$this->_applyGSUBrulesSingly($tags, $GSUBscriptTag, $GSUBlangsys);
|
683 |
+
|
684 |
+
//-----------------------------------------------------------------------------------
|
685 |
+
// c. Initial Re-ordering
|
686 |
+
//-----------------------------------------------------------------------------------
|
687 |
+
// Find base consonant
|
688 |
+
// Decompose/compose and reorder Matras
|
689 |
+
// Reorder marks to canonical order
|
690 |
+
|
691 |
+
$dottedcircle = false;
|
692 |
+
if ($broken_syllables) {
|
693 |
+
if ($this->mpdf->_charDefined($this->mpdf->fonts[$this->fontkey]['cw'],0x25CC) ) {
|
694 |
+
$dottedcircle = array();
|
695 |
+
$ucd_record = UCDN::get_ucd_record(0x25CC);
|
696 |
+
$dottedcircle[0]['general_category'] = $ucd_record[0];
|
697 |
+
$dottedcircle[0]['bidi_type'] = $ucd_record[2];
|
698 |
+
$dottedcircle[0]['group'] = 'C';
|
699 |
+
$dottedcircle[0]['uni'] = 0x25CC;
|
700 |
+
$dottedcircle[0]['sea_category'] = SEA::OT_GB;
|
701 |
+
$dottedcircle[0]['sea_position'] = SEA::POS_BASE_C;
|
702 |
+
|
703 |
+
$dottedcircle[0]['hex'] = '025CC'; // TEMPORARY *****
|
704 |
+
}
|
705 |
+
}
|
706 |
+
SEA::initial_reordering($this->OTLdata, $this->GSUBdata[$this->GSUBfont], $broken_syllables, $scriptblock, $dottedcircle);
|
707 |
+
|
708 |
+
//-----------------------------------------------------------------------------------
|
709 |
+
// d. Apply basic shaping forms GSUB Lookups (one at a time)
|
710 |
+
//-----------------------------------------------------------------------------------
|
711 |
+
$tags = 'pref abvf blwf pstf';
|
712 |
+
$this->_applyGSUBrulesSingly($tags, $GSUBscriptTag, $GSUBlangsys);
|
713 |
+
|
714 |
+
//-----------------------------------------------------------------------------------
|
715 |
+
// e. Final Re-ordering
|
716 |
+
//-----------------------------------------------------------------------------------
|
717 |
+
|
718 |
+
SEA::final_reordering($this->OTLdata, $this->GSUBdata[$this->GSUBfont], $scriptblock);
|
719 |
+
|
720 |
+
//-----------------------------------------------------------------------------------
|
721 |
+
// f. Apply Presentation Forms GSUB Lookups (+ any discretionary)
|
722 |
+
//-----------------------------------------------------------------------------------
|
723 |
+
$tags = 'pres abvs blws psts';
|
724 |
+
|
725 |
+
$omittags = 'locl ccmp nukt akhn rphf rkrf pref blwf abvf half pstf cfar vatu cjct init medi fina isol med2 fin2 fin3 ljmo vjmo tjmo';
|
726 |
+
$usetags = $tags;
|
727 |
+
if(!empty($this->mpdf->OTLtags)) {
|
728 |
+
$usetags = $this->_applyTagSettings($tags, $GSUBFeatures, $omittags, false) ;
|
729 |
+
}
|
730 |
+
$this->_applyGSUBrules($usetags, $GSUBscriptTag, $GSUBlangsys);
|
731 |
+
$this->restrictToSyllable = false;
|
732 |
+
}
|
733 |
+
|
734 |
+
|
735 |
+
// 5(D). GSUB - Shaper - DEFAULT (including THAI and LAO and MYANMAR v1 [mymr] and TIBETAN)
|
736 |
+
//==============================
|
737 |
+
else { // DEFAULT
|
738 |
+
//-----------------------------------------------------------------------------------
|
739 |
+
// a. First decompose/compose in Thai / Lao - Tibetan
|
740 |
+
//-----------------------------------------------------------------------------------
|
741 |
+
// Decomposition for THAI or LAO
|
742 |
+
/* This function implements the shaping logic documented here:
|
743 |
+
*
|
744 |
+
* http://linux.thai.net/~thep/th-otf/shaping.html
|
745 |
+
*
|
746 |
+
* The first shaping rule listed there is needed even if the font has Thai
|
747 |
+
* OpenType tables.
|
748 |
+
*
|
749 |
+
*
|
750 |
+
* The following is NOT specified in the MS OT Thai spec, however, it seems
|
751 |
+
* to be what Uniscribe and other engines implement. According to Eric Muller:
|
752 |
+
*
|
753 |
+
* When you have a SARA AM, decompose it in NIKHAHIT + SARA AA, *and* move the
|
754 |
+
* NIKHAHIT backwards over any tone mark (0E48-0E4B).
|
755 |
+
*
|
756 |
+
* <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32>
|
757 |
+
*
|
758 |
+
* This reordering is legit only when the NIKHAHIT comes from a SARA AM, not
|
759 |
+
* when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably
|
760 |
+
* not what a user wanted, but the rendering is nevertheless nikhahit above
|
761 |
+
* chattawa.
|
762 |
+
*
|
763 |
+
* Same for Lao.
|
764 |
+
*
|
765 |
+
* Thai Lao
|
766 |
+
* SARA AM: U+0E33 U+0EB3
|
767 |
+
* SARA AA: U+0E32 U+0EB2
|
768 |
+
* Nikhahit: U+0E4D U+0ECD
|
769 |
+
*
|
770 |
+
* Testing shows that Uniscribe reorder the following marks:
|
771 |
+
* Thai: <0E31,0E34..0E37,0E47..0E4E>
|
772 |
+
* Lao: <0EB1,0EB4..0EB7,0EC7..0ECE>
|
773 |
+
*
|
774 |
+
* Lao versions are the same as Thai + 0x80.
|
775 |
+
*/
|
776 |
+
if ($this->shaper == 'T' || $this->shaper == 'L') {
|
777 |
+
for($ptr=0; $ptr<count($this->OTLdata); $ptr++) {
|
778 |
+
$char = $this->OTLdata[$ptr]['uni'];
|
779 |
+
if (($char & ~0x0080) == 0x0E33) { // if SARA_AM (U+0E33 or U+0EB3)
|
780 |
+
|
781 |
+
$NIKHAHIT = $char + 0x1A;
|
782 |
+
$SARA_AA = $char - 1;
|
783 |
+
$sub = array($SARA_AA, $NIKHAHIT);
|
784 |
+
|
785 |
+
$newinfo = array();
|
786 |
+
$ucd_record = UCDN::get_ucd_record($sub[0]);
|
787 |
+
$newinfo[0]['general_category'] = $ucd_record[0];
|
788 |
+
$newinfo[0]['bidi_type'] = $ucd_record[2];
|
789 |
+
$charasstr = $this->unicode_hex($sub[0]);
|
790 |
+
if (strpos($this->GlyphClassMarks, $charasstr)!==false) { $newinfo[0]['group'] = 'M'; }
|
791 |
+
else { $newinfo[0]['group'] = 'C'; }
|
792 |
+
$newinfo[0]['uni'] = $sub[0];
|
793 |
+
$newinfo[0]['hex'] = $charasstr;
|
794 |
+
$this->OTLdata[$ptr] = $newinfo[0]; // Substitute SARA_AM => SARA_AA
|
795 |
+
|
796 |
+
$ntones = 0; // number of (preceding) tone marks
|
797 |
+
// IS_TONE_MARK ((x) & ~0x0080, 0x0E34 - 0x0E37, 0x0E47 - 0x0E4E, 0x0E31)
|
798 |
+
while (isset($this->OTLdata[$ptr - 1 - $ntones])
|
799 |
+
&& (
|
800 |
+
($this->OTLdata[$ptr - 1 - $ntones]['uni'] & ~0x0080) == 0x0E31 ||
|
801 |
+
|
802 |
+
(($this->OTLdata[$ptr - 1 - $ntones]['uni'] & ~0x0080) >= 0x0E34 &&
|
803 |
+
($this->OTLdata[$ptr - 1 - $ntones]['uni'] & ~0x0080) <= 0x0E37) ||
|
804 |
+
|
805 |
+
(($this->OTLdata[$ptr - 1 - $ntones]['uni'] & ~0x0080) >= 0x0E47 &&
|
806 |
+
($this->OTLdata[$ptr - 1 - $ntones]['uni'] & ~0x0080) <= 0x0E4E)
|
807 |
+
)
|
808 |
+
) { $ntones++; }
|
809 |
+
|
810 |
+
$newinfo = array();
|
811 |
+
$ucd_record = UCDN::get_ucd_record($sub[1]);
|
812 |
+
$newinfo[0]['general_category'] = $ucd_record[0];
|
813 |
+
$newinfo[0]['bidi_type'] = $ucd_record[2];
|
814 |
+
$charasstr = $this->unicode_hex($sub[1]);
|
815 |
+
if (strpos($this->GlyphClassMarks, $charasstr)!==false) { $newinfo[0]['group'] = 'M'; }
|
816 |
+
else { $newinfo[0]['group'] = 'C'; }
|
817 |
+
$newinfo[0]['uni'] = $sub[1];
|
818 |
+
$newinfo[0]['hex'] = $charasstr;
|
819 |
+
// Insert NIKAHIT
|
820 |
+
array_splice($this->OTLdata, $ptr - $ntones, 0, $newinfo);
|
821 |
+
|
822 |
+
$ptr++;
|
823 |
+
}
|
824 |
+
}
|
825 |
+
}
|
826 |
+
|
827 |
+
if ($scriptblock == UCDN::SCRIPT_TIBETAN) {
|
828 |
+
// =========================
|
829 |
+
// Reordering TIBETAN
|
830 |
+
// =========================
|
831 |
+
// Tibetan does not need to need a shaper generally, as long as characters are presented in the correct order
|
832 |
+
// so we will do one minor change here:
|
833 |
+
// From ICU: If the present character is a number, and the next character is a pre-number combining mark
|
834 |
+
// then the two characters are reordered
|
835 |
+
// From MS OTL spec the following are Digit modifiers (Md): 0F18�0F19, 0F3E�0F3F
|
836 |
+
// Digits: 0F20�0F33
|
837 |
+
// On testing only 0x0F3F (pre-based mark) seems to need re-ordering
|
838 |
+
for($ptr=0; $ptr<count($this->OTLdata)-1; $ptr++) {
|
839 |
+
if (INDIC::in_range($this->OTLdata[$ptr]['uni'], 0x0F20, 0x0F33) && $this->OTLdata[$ptr+1]['uni'] == 0x0F3F ) {
|
840 |
+
$tmp = $this->OTLdata[$ptr+1];
|
841 |
+
$this->OTLdata[$ptr+1] = $this->OTLdata[$ptr];
|
842 |
+
$this->OTLdata[$ptr] = $tmp;
|
843 |
+
}
|
844 |
+
}
|
845 |
+
|
846 |
+
|
847 |
+
// =========================
|
848 |
+
// Decomposition for TIBETAN
|
849 |
+
// =========================
|
850 |
+
/* Recommended, but does not seem to change anything...
|
851 |
+
for($ptr=0; $ptr<count($this->OTLdata); $ptr++) {
|
852 |
+
$char = $this->OTLdata[$ptr]['uni'];
|
853 |
+
$sub = INDIC::decompose_indic($char);
|
854 |
+
if ($sub) {
|
855 |
+
$newinfo = array();
|
856 |
+
for($i=0;$i<count($sub);$i++) {
|
857 |
+
$newinfo[$i] = array();
|
858 |
+
$ucd_record = UCDN::get_ucd_record($sub[$i]);
|
859 |
+
$newinfo[$i]['general_category'] = $ucd_record[0];
|
860 |
+
$newinfo[$i]['bidi_type'] = $ucd_record[2];
|
861 |
+
$charasstr = $this->unicode_hex($sub[$i]);
|
862 |
+
if (strpos($this->GlyphClassMarks, $charasstr)!==false) { $newinfo[$i]['group'] = 'M'; }
|
863 |
+
else { $newinfo[$i]['group'] = 'C'; }
|
864 |
+
$newinfo[$i]['uni'] = $sub[$i];
|
865 |
+
$newinfo[$i]['hex'] = $charasstr;
|
866 |
+
}
|
867 |
+
array_splice($this->OTLdata, $ptr, 1, $newinfo);
|
868 |
+
$ptr += count($sub)-1;
|
869 |
+
}
|
870 |
+
}
|
871 |
+
*/
|
872 |
+
|
873 |
+
}
|
874 |
+
|
875 |
+
|
876 |
+
//-----------------------------------------------------------------------------------
|
877 |
+
// b. Apply all GSUB Lookups (in order specified in lookup list)
|
878 |
+
//-----------------------------------------------------------------------------------
|
879 |
+
$tags = 'locl ccmp pref blwf abvf pstf pres abvs blws psts haln rlig calt liga clig mset RQD';
|
880 |
+
// pref blwf abvf pstf required for Tibetan
|
881 |
+
// " RQD" is a non-standard tag in Garuda font - presumably intended to be used by default ? "ReQuireD"
|
882 |
+
// Being a 3 letter tag is non-standard, and does not allow it to be set by font-feature-settings
|
883 |
+
|
884 |
+
|
885 |
+
/* ?Add these until shapers witten?
|
886 |
+
Hangul: ljmo vjmo tjmo
|
887 |
+
*/
|
888 |
+
|
889 |
+
$omittags = '';
|
890 |
+
$useGSUBtags = $tags;
|
891 |
+
if(!empty($this->mpdf->OTLtags)) {
|
892 |
+
$useGSUBtags = $this->_applyTagSettings($tags, $GSUBFeatures, $omittags, false) ;
|
893 |
+
}
|
894 |
+
// APPLY GSUB rules (as long as not Latin + SmallCaps - but not OTL smcp)
|
895 |
+
if (!(($this->mpdf->textvar & FC_SMALLCAPS) && $scriptblock == UCDN::SCRIPT_LATIN && strpos($useGSUBtags, 'smcp')===false)) {
|
896 |
+
$this->_applyGSUBrules($useGSUBtags, $GSUBscriptTag, $GSUBlangsys);
|
897 |
+
}
|
898 |
+
}
|
899 |
+
|
900 |
+
|
901 |
+
}
|
902 |
+
|
903 |
+
// Shapers - KHMER & THAI & LAO - Replace Word boundary marker with U+200B
|
904 |
+
// Also TIBETAN (no shaper)
|
905 |
+
//=======================================================
|
906 |
+
if (($this->shaper == "K" || $this->shaper == "T" || $this->shaper == "L") || $scriptblock == UCDN::SCRIPT_TIBETAN ) {
|
907 |
+
// Set up properties to insert a U+200B character
|
908 |
+
$newinfo = array();
|
909 |
+
//$newinfo[0] = array('general_category' => 1, 'bidi_type' => 14, 'group' => 'S', 'uni' => 0x200B, 'hex' => '0200B');
|
910 |
+
$newinfo[0] = array(
|
911 |
+
'general_category' => UCDN::UNICODE_GENERAL_CATEGORY_FORMAT,
|
912 |
+
'bidi_type' => UCDN::BIDI_CLASS_BN,
|
913 |
+
'group' => 'S', 'uni' => 0x200B, 'hex' => '0200B');
|
914 |
+
// Then insert U+200B at (after) all word end boundaries
|
915 |
+
for ($i=count($this->OTLdata)-1;$i>0;$i--) {
|
916 |
+
// Make sure after GSUB that wordend has not been moved - check next char is not in the same syllable
|
917 |
+
if (isset($this->OTLdata[$i]['wordend']) && $this->OTLdata[$i]['wordend'] &&
|
918 |
+
isset($this->OTLdata[$i+1]['uni']) && (!isset($this->OTLdata[$i+1]['syllable']) || !isset($this->OTLdata[$i+1]['syllable']) || $this->OTLdata[$i+1]['syllable']!=$this->OTLdata[$i]['syllable'])) {
|
919 |
+
array_splice($this->OTLdata, $i+1, 0, $newinfo);
|
920 |
+
$this->_updateLigatureMarks($i, 1);
|
921 |
+
}
|
922 |
+
else if ($this->OTLdata[$i]['uni']==0x2e) { // Word end if Full-stop.
|
923 |
+
array_splice($this->OTLdata, $i+1, 0, $newinfo);
|
924 |
+
$this->_updateLigatureMarks($i, 1);
|
925 |
+
}
|
926 |
+
}
|
927 |
+
}
|
928 |
+
|
929 |
+
|
930 |
+
// Shapers - INDIC & ARABIC & KHMER & SINHALA & MYANMAR - Remove ZWJ and ZWNJ
|
931 |
+
//=======================================================
|
932 |
+
if ($this->shaper == 'I' || $this->shaper == 'S' || $this->shaper == 'A' || $this->shaper == 'K' || $this->shaper == 'M') {
|
933 |
+
// Remove ZWJ and ZWNJ
|
934 |
+
for ($i=0;$i<count($this->OTLdata);$i++) {
|
935 |
+
if ($this->OTLdata[$i]['uni']==8204 || $this->OTLdata[$i]['uni']==8205) {
|
936 |
+
array_splice($this->OTLdata, $i, 1);
|
937 |
+
$this->_updateLigatureMarks($i, -1);
|
938 |
+
}
|
939 |
+
}
|
940 |
+
}
|
941 |
+
|
942 |
+
//print_r($this->OTLdata); echo '<br />';
|
943 |
+
//print_r($this->assocMarks); echo '<br />';
|
944 |
+
//print_r($this->assocLigs); exit;
|
945 |
+
|
946 |
+
////////////////////////////////////////////////////////////////
|
947 |
+
////////////////////////////////////////////////////////////////
|
948 |
+
////////// GPOS /////////////////////////////////
|
949 |
+
////////////////////////////////////////////////////////////////
|
950 |
+
////////////////////////////////////////////////////////////////
|
951 |
+
|
952 |
+
if (($useOTL & 0xFF) && $GPOSscriptTag && $GPOSlangsys && $GPOSFeatures) {
|
953 |
+
$this->Entry = array();
|
954 |
+
$this->Exit = array();
|
955 |
+
|
956 |
+
// 6. Load GPOS data, Coverage & Lookups
|
957 |
+
//=================================================================
|
958 |
+
if (!isset($this->GPOSdata[$this->fontkey])) {
|
959 |
+
include(_MPDF_TTFONTDATAPATH.$this->mpdf->CurrentFont['fontkey'].'.GPOSdata.php');
|
960 |
+
$this->LuCoverage = $this->GPOSdata[$this->fontkey]['LuCoverage'] = $LuCoverage;
|
961 |
+
}
|
962 |
+
else {
|
963 |
+
$this->LuCoverage = $this->GPOSdata[$this->fontkey]['LuCoverage'];
|
964 |
+
}
|
965 |
+
|
966 |
+
$this->GPOSLookups = $this->mpdf->CurrentFont['GPOSLookups'];
|
967 |
+
|
968 |
+
|
969 |
+
// 7. Select Feature tags to use (incl optional)
|
970 |
+
//==============================
|
971 |
+
$tags = 'abvm blwm mark mkmk curs cpsp dist requ'; // Default set
|
972 |
+
/* 'requ' is not listed in the Microsoft registry of Feature tags
|
973 |
+
Found in Arial Unicode MS, it repositions the baseline for punctuation in Kannada script */
|
974 |
+
|
975 |
+
// ZZZ96
|
976 |
+
// Set kern to be included by default in non-Latin script (? just when shapers used)
|
977 |
+
// Kern is used in some fonts to reposition marks etc. and is essential for correct display
|
978 |
+
//if ($this->shaper) {$tags .= ' kern'; }
|
979 |
+
if ($scriptblock != UCDN::SCRIPT_LATIN) { $tags .= ' kern'; }
|
980 |
+
|
981 |
+
$omittags = '';
|
982 |
+
$usetags = $tags;
|
983 |
+
if(!empty($this->mpdf->OTLtags)) {
|
984 |
+
$usetags = $this->_applyTagSettings($tags, $GPOSFeatures, $omittags, false) ;
|
985 |
+
}
|
986 |
+
|
987 |
+
|
988 |
+
|
989 |
+
// 8. Get GPOS LookupList from Feature tags
|
990 |
+
//==============================
|
991 |
+
$LookupList = array();
|
992 |
+
foreach($GPOSFeatures AS $tag=>$arr) {
|
993 |
+
if (strpos($usetags, $tag)!==false) {
|
994 |
+
foreach($arr AS $lu) { $LookupList[$lu] = $tag; }
|
995 |
+
}
|
996 |
+
}
|
997 |
+
ksort($LookupList);
|
998 |
+
|
999 |
+
|
1000 |
+
// 9. Apply GPOS Lookups (in order specified in lookup list but selecting from specified tags)
|
1001 |
+
//==============================
|
1002 |
+
|
1003 |
+
// APPLY THE GPOS RULES (as long as not Latin + SmallCaps - but not OTL smcp)
|
1004 |
+
if (!(($this->mpdf->textvar & FC_SMALLCAPS) && $scriptblock == UCDN::SCRIPT_LATIN && strpos($useGSUBtags, 'smcp')===false)) {
|
1005 |
+
$this->_applyGPOSrules($LookupList, $is_old_spec);
|
1006 |
+
// (sets: $this->OTLdata[n]['GPOSinfo'] XPlacement YPlacement XAdvance Entry Exit )
|
1007 |
+
}
|
1008 |
+
|
1009 |
+
// 10. Process cursive text
|
1010 |
+
//==============================
|
1011 |
+
if (count($this->Entry) || count($this->Exit)) {
|
1012 |
+
// RTL
|
1013 |
+
$incurs = false;
|
1014 |
+
for ($i=(count($this->OTLdata)-1);$i>=0;$i--) {
|
1015 |
+
if (isset($this->Entry[$i]) && isset($this->Entry[$i]['Y']) && $this->Entry[$i]['dir']=='RTL') {
|
1016 |
+
$nextbase = $i-1; // Set as next base ignoring marks (next base reading RTL in logical oder
|
1017 |
+
while(isset($this->OTLdata[$nextbase]['hex']) && strpos($this->GlyphClassMarks, $this->OTLdata[$nextbase]['hex'])!==false) { $nextbase--; }
|
1018 |
+
if (isset($this->Exit[$nextbase]) && isset($this->Exit[$nextbase]['Y']) ) {
|
1019 |
+
$diff = $this->Entry[$i]['Y'] - $this->Exit[$nextbase]['Y'];
|
1020 |
+
if ($incurs===false) { $incurs = $diff; }
|
1021 |
+
else { $incurs += $diff; }
|
1022 |
+
for ($j=($i-1);$j>=$nextbase;$j--) {
|
1023 |
+
if (isset($this->OTLdata[$j]['GPOSinfo']['YPlacement'])) { $this->OTLdata[$j]['GPOSinfo']['YPlacement'] += $incurs; }
|
1024 |
+
else { $this->OTLdata[$j]['GPOSinfo']['YPlacement'] = $incurs; }
|
1025 |
+
}
|
1026 |
+
if (isset($this->Exit[$i]['X']) && isset($this->Entry[$nextbase]['X']) ) {
|
1027 |
+
$adj = -($this->Entry[$i]['X'] - $this->Exit[$nextbase]['X']);
|
1028 |
+
// If XAdvance is aplied - in order for PDF to position the Advance correctly need to place it on:
|
1029 |
+
// in RTL - the current glyph or the last of any associated marks
|
1030 |
+
if (isset($this->OTLdata[$nextbase+1]['GPOSinfo']['XAdvance'])) { $this->OTLdata[$nextbase+1]['GPOSinfo']['XAdvance'] += $adj; }
|
1031 |
+
else { $this->OTLdata[$nextbase+1]['GPOSinfo']['XAdvance'] = $adj; }
|
1032 |
+
}
|
1033 |
+
}
|
1034 |
+
else { $incurs = false; }
|
1035 |
+
}
|
1036 |
+
else if (strpos($this->GlyphClassMarks, $this->OTLdata[$i]['hex'])!==false) { continue; } // ignore Marks
|
1037 |
+
else { $incurs = false; }
|
1038 |
+
}
|
1039 |
+
// LTR
|
1040 |
+
$incurs = false;
|
1041 |
+
for ($i=0;$i<count($this->OTLdata);$i++) {
|
1042 |
+
if (isset($this->Exit[$i]) && isset($this->Exit[$i]['Y']) && $this->Exit[$i]['dir']=='LTR') {
|
1043 |
+
$nextbase = $i+1; // Set as next base ignoring marks
|
1044 |
+
while(strpos($this->GlyphClassMarks, $this->OTLdata[$nextbase]['hex'])!==false) { $nextbase++; }
|
1045 |
+
if (isset($this->Entry[$nextbase]) && isset($this->Entry[$nextbase]['Y']) ) {
|
1046 |
+
|
1047 |
+
$diff = $this->Exit[$i]['Y'] - $this->Entry[$nextbase]['Y'];
|
1048 |
+
if ($incurs===false) { $incurs = $diff; }
|
1049 |
+
else { $incurs += $diff; }
|
1050 |
+
for ($j=($i+1);$j<=$nextbase;$j++) {
|
1051 |
+
if (isset($this->OTLdata[$j]['GPOSinfo']['YPlacement'])) { $this->OTLdata[$j]['GPOSinfo']['YPlacement'] += $incurs; }
|
1052 |
+
else { $this->OTLdata[$j]['GPOSinfo']['YPlacement'] = $incurs; }
|
1053 |
+
}
|
1054 |
+
if (isset($this->Exit[$i]['X']) && isset($this->Entry[$nextbase]['X']) ) {
|
1055 |
+
$adj = -($this->Exit[$i]['X'] - $this->Entry[$nextbase]['X']);
|
1056 |
+
// If XAdvance is aplied - in order for PDF to position the Advance correctly need to place it on:
|
1057 |
+
// in LTR - the next glyph, ignoring marks
|
1058 |
+
if (isset($this->OTLdata[$nextbase]['GPOSinfo']['XAdvance'])) { $this->OTLdata[$nextbase]['GPOSinfo']['XAdvance'] += $adj; }
|
1059 |
+
else { $this->OTLdata[$nextbase]['GPOSinfo']['XAdvance'] = $adj; }
|
1060 |
+
}
|
1061 |
+
}
|
1062 |
+
else { $incurs = false; }
|
1063 |
+
}
|
1064 |
+
else if (strpos($this->GlyphClassMarks, $this->OTLdata[$i]['hex'])!==false) { continue; } // ignore Marks
|
1065 |
+
else { $incurs = false; }
|
1066 |
+
}
|
1067 |
+
}
|
1068 |
+
|
1069 |
+
|
1070 |
+
|
1071 |
+
|
1072 |
+
} // end GPOS
|
1073 |
+
|
1074 |
+
if ($this->debugOTL) { $this->_dumpproc('END', '-', '-', '-', '-', 0, '-', 0); exit; }
|
1075 |
+
|
1076 |
+
$this->schOTLdata[$sch] = $this->OTLdata;
|
1077 |
+
$this->OTLdata = array();
|
1078 |
+
} // END foreach subchunk
|
1079 |
+
|
1080 |
+
|
1081 |
+
// 11. Re-assemble and return text string
|
1082 |
+
//==============================
|
1083 |
+
$newGPOSinfo = array();
|
1084 |
+
$newOTLdata = array();
|
1085 |
+
$newchar_data = array();
|
1086 |
+
$newgroup = '';
|
1087 |
+
$e = '';
|
1088 |
+
$ectr = 0;
|
1089 |
+
|
1090 |
+
for($sch=0;$sch<=$subchunk;$sch++) {
|
1091 |
+
for ($i=0;$i<count($this->schOTLdata[$sch]);$i++) {
|
1092 |
+
if (isset($this->schOTLdata[$sch][$i]['GPOSinfo'])) {
|
1093 |
+
$newGPOSinfo[$ectr] = $this->schOTLdata[$sch][$i]['GPOSinfo'];
|
1094 |
+
}
|
1095 |
+
$newchar_data[$ectr] = array('bidi_class' => $this->schOTLdata[$sch][$i]['bidi_type'], 'uni' => $this->schOTLdata[$sch][$i]['uni']);
|
1096 |
+
$newgroup .= $this->schOTLdata[$sch][$i]['group'];
|
1097 |
+
$e.=code2utf($this->schOTLdata[$sch][$i]['uni']);
|
1098 |
+
if (isset($this->mpdf->CurrentFont['subset'])) {
|
1099 |
+
$this->mpdf->CurrentFont['subset'][$this->schOTLdata[$sch][$i]['uni']] = $this->schOTLdata[$sch][$i]['uni'];
|
1100 |
+
}
|
1101 |
+
$ectr++;
|
1102 |
+
}
|
1103 |
+
|
1104 |
+
}
|
1105 |
+
$this->OTLdata['GPOSinfo'] = $newGPOSinfo;
|
1106 |
+
$this->OTLdata['char_data'] = $newchar_data ;
|
1107 |
+
$this->OTLdata['group'] = $newgroup ;
|
1108 |
+
|
1109 |
+
|
1110 |
+
// This leaves OTLdata::GPOSinfo, ::bidi_type, & ::group
|
1111 |
+
|
1112 |
+
return $e;
|
1113 |
+
|
1114 |
+
}
|
1115 |
+
|
1116 |
+
function _applyTagSettings($tags, $Features, $omittags='', $onlytags=false) {
|
1117 |
+
if (empty($this->mpdf->OTLtags['Plus']) && empty($this->mpdf->OTLtags['Minus']) && empty($this->mpdf->OTLtags['FFPlus']) && empty($this->mpdf->OTLtags['FFMinus'])) { return $tags; }
|
1118 |
+
|
1119 |
+
// Use $tags as starting point
|
1120 |
+
$usetags = $tags;
|
1121 |
+
|
1122 |
+
// Only set / unset tags which are in the font
|
1123 |
+
// Ignore tags which are in $omittags
|
1124 |
+
// If $onlytags, then just unset tags which are already in the Tag list
|
1125 |
+
|
1126 |
+
$fp = $fm = $ffp = $ffm = '';
|
1127 |
+
|
1128 |
+
// Font features to enable - set by font-variant-xx
|
1129 |
+
if (isset($this->mpdf->OTLtags['Plus'])) $fp = $this->mpdf->OTLtags['Plus'];
|
1130 |
+
preg_match_all('/([a-zA-Z0-9]{4})/',$fp,$m);
|
1131 |
+
for($i=0;$i<count($m[0]);$i++) {
|
1132 |
+
$t = $m[1][$i];
|
1133 |
+
// Is it a valid tag?
|
1134 |
+
if(isset($Features[$t]) && strpos($omittags,$t)===false && (!$onlytags || strpos($tags,$t)!==false )) {
|
1135 |
+
$usetags .= ' '.$t;
|
1136 |
+
}
|
1137 |
+
}
|
1138 |
+
|
1139 |
+
// Font features to disable - set by font-variant-xx
|
1140 |
+
if (isset($this->mpdf->OTLtags['Minus'])) $fm = $this->mpdf->OTLtags['Minus'];
|
1141 |
+
preg_match_all('/([a-zA-Z0-9]{4})/',$fm,$m);
|
1142 |
+
for($i=0;$i<count($m[0]);$i++) {
|
1143 |
+
$t = $m[1][$i];
|
1144 |
+
// Is it a valid tag?
|
1145 |
+
if(isset($Features[$t]) && strpos($omittags,$t)===false && (!$onlytags || strpos($tags,$t)!==false )) {
|
1146 |
+
$usetags = str_replace($t,'',$usetags);
|
1147 |
+
}
|
1148 |
+
}
|
1149 |
+
|
1150 |
+
// Font features to enable - set by font-feature-settings
|
1151 |
+
if (isset($this->mpdf->OTLtags['FFPlus'])) $ffp = $this->mpdf->OTLtags['FFPlus']; // Font Features - may include integer: salt4
|
1152 |
+
preg_match_all('/([a-zA-Z0-9]{4})([\d+]*)/',$ffp,$m);
|
1153 |
+
for($i=0;$i<count($m[0]);$i++) {
|
1154 |
+
$t = $m[1][$i];
|
1155 |
+
// Is it a valid tag?
|
1156 |
+
if(isset($Features[$t]) && strpos($omittags,$t)===false && (!$onlytags || strpos($tags,$t)!==false )) {
|
1157 |
+
$usetags .= ' '.$m[0][$i]; // - may include integer: salt4
|
1158 |
+
}
|
1159 |
+
}
|
1160 |
+
|
1161 |
+
// Font features to disable - set by font-feature-settings
|
1162 |
+
if (isset($this->mpdf->OTLtags['FFMinus'])) $ffm = $this->mpdf->OTLtags['FFMinus'];
|
1163 |
+
preg_match_all('/([a-zA-Z0-9]{4})/',$ffm,$m);
|
1164 |
+
for($i=0;$i<count($m[0]);$i++) {
|
1165 |
+
$t = $m[1][$i];
|
1166 |
+
// Is it a valid tag?
|
1167 |
+
if(isset($Features[$t]) && strpos($omittags,$t)===false && (!$onlytags || strpos($tags,$t)!==false )) {
|
1168 |
+
$usetags = str_replace($t,'',$usetags);
|
1169 |
+
}
|
1170 |
+
}
|
1171 |
+
return $usetags;
|
1172 |
+
}
|
1173 |
+
|
1174 |
+
function _applyGSUBrules($usetags, $scriptTag, $langsys) {
|
1175 |
+
// Features from all Tags are applied together, in Lookup List order.
|
1176 |
+
// For Indic - should be applied one syllable at a time
|
1177 |
+
// - Implemented in functions checkContextMatch and checkContextMatchMultiple by failing to match if outside scope of current 'syllable'
|
1178 |
+
// if $this->restrictToSyllable is true
|
1179 |
+
|
1180 |
+
$GSUBFeatures = $this->mpdf->CurrentFont['GSUBFeatures'][$scriptTag][$langsys];
|
1181 |
+
$LookupList = array();
|
1182 |
+
foreach($GSUBFeatures AS $tag=>$arr) {
|
1183 |
+
if (strpos($usetags, $tag)!==false) {
|
1184 |
+
foreach($arr AS $lu) { $LookupList[$lu] = $tag; }
|
1185 |
+
}
|
1186 |
+
}
|
1187 |
+
ksort($LookupList);
|
1188 |
+
|
1189 |
+
foreach($LookupList AS $lu=>$tag) {
|
1190 |
+
$Type = $this->GSUBLookups[$lu]['Type'];
|
1191 |
+
$Flag = $this->GSUBLookups[$lu]['Flag'];
|
1192 |
+
$MarkFilteringSet = $this->GSUBLookups[$lu]['MarkFilteringSet'];
|
1193 |
+
$tagInt = 1;
|
1194 |
+
if (preg_match('/'.$tag.'([0-9]{1,2})/', $usetags, $m)) {
|
1195 |
+
$tagInt = $m[1];
|
1196 |
+
}
|
1197 |
+
$ptr = 0;
|
1198 |
+
// Test each glyph sequentially
|
1199 |
+
while($ptr < (count($this->OTLdata))) { // whilst there is another glyph ..0064
|
1200 |
+
$currGlyph = $this->OTLdata[$ptr]['hex'];
|
1201 |
+
$currGID = $this->OTLdata[$ptr]['uni'];
|
1202 |
+
$shift = 1;
|
1203 |
+
foreach($this->GSUBLookups[$lu]['Subtables'] AS $c=>$subtable_offset) {
|
1204 |
+
// NB Coverage only looks at glyphs for position 1 (esp. 7.3 and 8.3)
|
1205 |
+
if (isset($this->GSLuCoverage[$lu][$c][$currGID])) {
|
1206 |
+
// Get rules from font GSUB subtable
|
1207 |
+
$shift = $this->_applyGSUBsubtable($lu, $c, $ptr, $currGlyph, $currGID, ($subtable_offset - $this->GSUB_offset), $Type, $Flag, $MarkFilteringSet, $this->GSLuCoverage[$lu][$c], 0, $tag, 0, $tagInt);
|
1208 |
+
|
1209 |
+
if ($shift) { break; }
|
1210 |
+
}
|
1211 |
+
}
|
1212 |
+
if ($shift == 0) { $shift = 1; }
|
1213 |
+
$ptr += $shift;
|
1214 |
+
|
1215 |
+
}
|
1216 |
+
}
|
1217 |
+
}
|
1218 |
+
|
1219 |
+
function _applyGSUBrulesSingly($usetags, $scriptTag, $langsys) {
|
1220 |
+
// Features are applied one at a time, working through each codepoint
|
1221 |
+
|
1222 |
+
$GSUBFeatures = $this->mpdf->CurrentFont['GSUBFeatures'][$scriptTag][$langsys];
|
1223 |
+
|
1224 |
+
$tags = explode(' ',$usetags);
|
1225 |
+
foreach($tags AS $usetag) {
|
1226 |
+
$LookupList = array();
|
1227 |
+
foreach($GSUBFeatures AS $tag=>$arr) {
|
1228 |
+
if (strpos($usetags, $tag)!==false) {
|
1229 |
+
foreach($arr AS $lu) { $LookupList[$lu] = $tag; }
|
1230 |
+
}
|
1231 |
+
}
|
1232 |
+
ksort($LookupList);
|
1233 |
+
|
1234 |
+
$ptr = 0;
|
1235 |
+
// Test each glyph sequentially
|
1236 |
+
while($ptr < (count($this->OTLdata))) { // whilst there is another glyph ..0064
|
1237 |
+
$currGlyph = $this->OTLdata[$ptr]['hex'];
|
1238 |
+
$currGID = $this->OTLdata[$ptr]['uni'];
|
1239 |
+
$shift = 1;
|
1240 |
+
|
1241 |
+
foreach($LookupList AS $lu=>$tag) {
|
1242 |
+
$Type = $this->GSUBLookups[$lu]['Type'];
|
1243 |
+
$Flag = $this->GSUBLookups[$lu]['Flag'];
|
1244 |
+
$MarkFilteringSet = $this->GSUBLookups[$lu]['MarkFilteringSet'];
|
1245 |
+
$tagInt = 1;
|
1246 |
+
if (preg_match('/'.$tag.'([0-9]{1,2})/', $usetags, $m)) {
|
1247 |
+
$tagInt = $m[1];
|
1248 |
+
}
|
1249 |
+
|
1250 |
+
foreach($this->GSUBLookups[$lu]['Subtables'] AS $c=>$subtable_offset) {
|
1251 |
+
// NB Coverage only looks at glyphs for position 1 (esp. 7.3 and 8.3)
|
1252 |
+
if (isset($this->GSLuCoverage[$lu][$c][$currGID])) {
|
1253 |
+
// Get rules from font GSUB subtable
|
1254 |
+
$shift = $this->_applyGSUBsubtable($lu, $c, $ptr, $currGlyph, $currGID, ($subtable_offset - $this->GSUB_offset), $Type, $Flag, $MarkFilteringSet, $this->GSLuCoverage[$lu][$c], 0, $tag, 0, $tagInt);
|
1255 |
+
|
1256 |
+
if ($shift) { break 2; }
|
1257 |
+
}
|
1258 |
+
}
|
1259 |
+
}
|
1260 |
+
if ($shift == 0) { $shift = 1; }
|
1261 |
+
$ptr += $shift;
|
1262 |
+
|
1263 |
+
}
|
1264 |
+
}
|
1265 |
+
}
|
1266 |
+
|
1267 |
+
function _applyGSUBrulesMyanmar($usetags, $scriptTag, $langsys) {
|
1268 |
+
// $usetags = locl ccmp rphf pref blwf pstf';
|
1269 |
+
// applied to all characters
|
1270 |
+
|
1271 |
+
$GSUBFeatures = $this->mpdf->CurrentFont['GSUBFeatures'][$scriptTag][$langsys];
|
1272 |
+
|
1273 |
+
// ALL should be applied one syllable at a time
|
1274 |
+
// Implemented in functions checkContextMatch and checkContextMatchMultiple by failing to match if outside scope of current 'syllable'
|
1275 |
+
$tags = explode(' ',$usetags);
|
1276 |
+
foreach($tags AS $usetag) {
|
1277 |
+
|
1278 |
+
$LookupList = array();
|
1279 |
+
foreach($GSUBFeatures AS $tag=>$arr) {
|
1280 |
+
if ($tag==$usetag) {
|
1281 |
+
foreach($arr AS $lu) { $LookupList[$lu] = $tag; }
|
1282 |
+
}
|
1283 |
+
}
|
1284 |
+
ksort($LookupList);
|
1285 |
+
|
1286 |
+
foreach($LookupList AS $lu=>$tag) {
|
1287 |
+
|
1288 |
+
$Type = $this->GSUBLookups[$lu]['Type'];
|
1289 |
+
$Flag = $this->GSUBLookups[$lu]['Flag'];
|
1290 |
+
$MarkFilteringSet = $this->GSUBLookups[$lu]['MarkFilteringSet'];
|
1291 |
+
$tagInt = 1;
|
1292 |
+
if (preg_match('/'.$tag.'([0-9]{1,2})/', $usetags, $m)) {
|
1293 |
+
$tagInt = $m[1];
|
1294 |
+
}
|
1295 |
+
|
1296 |
+
$ptr = 0;
|
1297 |
+
// Test each glyph sequentially
|
1298 |
+
while($ptr < (count($this->OTLdata))) { // whilst there is another glyph ..0064
|
1299 |
+
$currGlyph = $this->OTLdata[$ptr]['hex'];
|
1300 |
+
$currGID = $this->OTLdata[$ptr]['uni'];
|
1301 |
+
$shift = 1;
|
1302 |
+
foreach($this->GSUBLookups[$lu]['Subtables'] AS $c=>$subtable_offset) {
|
1303 |
+
// NB Coverage only looks at glyphs for position 1 (esp. 7.3 and 8.3)
|
1304 |
+
if (isset($this->GSLuCoverage[$lu][$c][$currGID])) {
|
1305 |
+
// Get rules from font GSUB subtable
|
1306 |
+
$shift = $this->_applyGSUBsubtable($lu, $c, $ptr, $currGlyph, $currGID, ($subtable_offset - $this->GSUB_offset), $Type, $Flag, $MarkFilteringSet, $this->GSLuCoverage[$lu][$c], 0, $usetag, 0, $tagInt);
|
1307 |
+
|
1308 |
+
if ($shift) { break; }
|
1309 |
+
}
|
1310 |
+
}
|
1311 |
+
if ($shift == 0) { $shift = 1; }
|
1312 |
+
$ptr += $shift;
|
1313 |
+
|
1314 |
+
}
|
1315 |
+
}
|
1316 |
+
}
|
1317 |
+
}
|
1318 |
+
|
1319 |
+
function _applyGSUBrulesIndic($usetags, $scriptTag, $langsys, $is_old_spec) {
|
1320 |
+
// $usetags = 'locl ccmp nukt akhn rphf rkrf pref blwf half pstf vatu cjct'; then later - init
|
1321 |
+
// rphf, pref, blwf, half, abvf, pstf, and init are only applied where ['mask'] indicates: INDIC::FLAG(INDIC::RPHF);
|
1322 |
+
// The rest are applied to all characters
|
1323 |
+
|
1324 |
+
$GSUBFeatures = $this->mpdf->CurrentFont['GSUBFeatures'][$scriptTag][$langsys];
|
1325 |
+
|
1326 |
+
// ALL should be applied one syllable at a time
|
1327 |
+
// Implemented in functions checkContextMatch and checkContextMatchMultiple by failing to match if outside scope of current 'syllable'
|
1328 |
+
$tags = explode(' ',$usetags);
|
1329 |
+
foreach($tags AS $usetag) {
|
1330 |
+
|
1331 |
+
$LookupList = array();
|
1332 |
+
foreach($GSUBFeatures AS $tag=>$arr) {
|
1333 |
+
if ($tag==$usetag) {
|
1334 |
+
foreach($arr AS $lu) { $LookupList[$lu] = $tag; }
|
1335 |
+
}
|
1336 |
+
}
|
1337 |
+
ksort($LookupList);
|
1338 |
+
|
1339 |
+
foreach($LookupList AS $lu=>$tag) {
|
1340 |
+
|
1341 |
+
$Type = $this->GSUBLookups[$lu]['Type'];
|
1342 |
+
$Flag = $this->GSUBLookups[$lu]['Flag'];
|
1343 |
+
$MarkFilteringSet = $this->GSUBLookups[$lu]['MarkFilteringSet'];
|
1344 |
+
$tagInt = 1;
|
1345 |
+
if (preg_match('/'.$tag.'([0-9]{1,2})/', $usetags, $m)) {
|
1346 |
+
$tagInt = $m[1];
|
1347 |
+
}
|
1348 |
+
|
1349 |
+
$ptr = 0;
|
1350 |
+
// Test each glyph sequentially
|
1351 |
+
while($ptr < (count($this->OTLdata))) { // whilst there is another glyph ..0064
|
1352 |
+
$currGlyph = $this->OTLdata[$ptr]['hex'];
|
1353 |
+
$currGID = $this->OTLdata[$ptr]['uni'];
|
1354 |
+
$shift = 1;
|
1355 |
+
foreach($this->GSUBLookups[$lu]['Subtables'] AS $c=>$subtable_offset) {
|
1356 |
+
// NB Coverage only looks at glyphs for position 1 (esp. 7.3 and 8.3)
|
1357 |
+
if (isset($this->GSLuCoverage[$lu][$c][$currGID])) {
|
1358 |
+
if (strpos('rphf pref blwf half pstf cfar init' , $usetag)!==false) { // only apply when mask indicates
|
1359 |
+
$mask = 0;
|
1360 |
+
switch ($usetag) {
|
1361 |
+
case 'rphf': $mask = (1<<(INDIC::RPHF)); break;
|
1362 |
+
case 'pref': $mask = (1<<(INDIC::PREF)); break;
|
1363 |
+
case 'blwf': $mask = (1<<(INDIC::BLWF)); break;
|
1364 |
+
case 'half': $mask = (1<<(INDIC::HALF)); break;
|
1365 |
+
case 'pstf': $mask = (1<<(INDIC::PSTF)); break;
|
1366 |
+
case 'cfar': $mask = (1<<(INDIC::CFAR)); break;
|
1367 |
+
case 'init': $mask = (1<<(INDIC::INIT)); break;
|
1368 |
+
}
|
1369 |
+
if (!($this->OTLdata[$ptr]['mask'] & $mask)) { continue; }
|
1370 |
+
}
|
1371 |
+
// Get rules from font GSUB subtable
|
1372 |
+
$shift = $this->_applyGSUBsubtable($lu, $c, $ptr, $currGlyph, $currGID, ($subtable_offset - $this->GSUB_offset), $Type, $Flag, $MarkFilteringSet, $this->GSLuCoverage[$lu][$c], 0, $usetag, $is_old_spec, $tagInt);
|
1373 |
+
|
1374 |
+
if ($shift) { break; }
|
1375 |
+
}
|
1376 |
+
|
1377 |
+
// Special case for Indic ZZZ99S
|
1378 |
+
// Check to substitute Halant-Consonant in PREF, BLWF or PSTF
|
1379 |
+
// i.e. new spec but GSUB tables have Consonant-Halant in Lookups e.g. FreeSerif, which
|
1380 |
+
// incorrectly just moved old spec tables to new spec. Uniscribe seems to cope with this
|
1381 |
+
// See also ttffontsuni.php
|
1382 |
+
// First check if current glyph is a Halant/Virama
|
1383 |
+
else if (_OTL_OLD_SPEC_COMPAT_1 && $Type==4 && !$is_old_spec && strpos('0094D 009CD 00A4D 00ACD 00B4D 00BCD 00C4D 00CCD 00D4D',$currGlyph)!== false) {
|
1384 |
+
// only apply when 'pref blwf pstf' tags, and when mask indicates
|
1385 |
+
if (strpos('pref blwf pstf' , $usetag)!==false) {
|
1386 |
+
$mask = 0;
|
1387 |
+
switch ($usetag) {
|
1388 |
+
case 'pref': $mask = (1<<(INDIC::PREF)); break;
|
1389 |
+
case 'blwf': $mask = (1<<(INDIC::BLWF)); break;
|
1390 |
+
case 'pstf': $mask = (1<<(INDIC::PSTF)); break;
|
1391 |
+
}
|
1392 |
+
if (!($this->OTLdata[$ptr]['mask'] & $mask)) { continue; }
|
1393 |
+
|
1394 |
+
$nextGlyph = $this->OTLdata[$ptr+1]['hex'];
|
1395 |
+
$nextGID = $this->OTLdata[$ptr+1]['uni'];
|
1396 |
+
if (isset($this->GSLuCoverage[$lu][$c][$nextGID])) {
|
1397 |
+
|
1398 |
+
// Get rules from font GSUB subtable
|
1399 |
+
$shift = $this->_applyGSUBsubtableSpecial($lu, $c, $ptr, $currGlyph, $currGID, $nextGlyph, $nextGID, ($subtable_offset - $this->GSUB_offset), $Type, $this->GSLuCoverage[$lu][$c]);
|
1400 |
+
|
1401 |
+
if ($shift) { break; }
|
1402 |
+
}
|
1403 |
+
}
|
1404 |
+
}
|
1405 |
+
|
1406 |
+
|
1407 |
+
}
|
1408 |
+
if ($shift == 0) { $shift = 1; }
|
1409 |
+
$ptr += $shift;
|
1410 |
+
|
1411 |
+
}
|
1412 |
+
}
|
1413 |
+
}
|
1414 |
+
}
|
1415 |
+
|
1416 |
+
|
1417 |
+
function _applyGSUBsubtableSpecial($lookupID, $subtable, $ptr, $currGlyph, $currGID, $nextGlyph, $nextGID, $subtable_offset, $Type, $LuCoverage) {
|
1418 |
+
|
1419 |
+
// Special case for Indic
|
1420 |
+
// Check to substitute Halant-Consonant in PREF, BLWF or PSTF
|
1421 |
+
// i.e. new spec but GSUB tables have Consonant-Halant in Lookups e.g. FreeSerif, which
|
1422 |
+
// incorrectly just moved old spec tables to new spec. Uniscribe seems to cope with this
|
1423 |
+
// See also ttffontsuni.php
|
1424 |
+
|
1425 |
+
$this->seek($subtable_offset);
|
1426 |
+
$SubstFormat= $this->read_ushort();
|
1427 |
+
|
1428 |
+
// Subtable contains Consonant - Halant
|
1429 |
+
// Text string contains Halant ($CurrGlyph) - Consonant ($nextGlyph)
|
1430 |
+
// Halant has already been matched, and already checked that $nextGID is in Coverage table
|
1431 |
+
|
1432 |
+
////////////////////////////////////////////////////////////////////////////////
|
1433 |
+
// Only does: LookupType 4: Ligature Substitution Subtable : n to 1
|
1434 |
+
////////////////////////////////////////////////////////////////////////////////
|
1435 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
1436 |
+
$NextGlyphPos = $LuCoverage[$nextGID];
|
1437 |
+
$LigSetCount = $this->read_short();
|
1438 |
+
|
1439 |
+
$this->skip($NextGlyphPos * 2);
|
1440 |
+
$LigSet = $subtable_offset + $this->read_short();
|
1441 |
+
|
1442 |
+
$this->seek($LigSet);
|
1443 |
+
$LigCount = $this->read_short();
|
1444 |
+
// LigatureSet i.e. all starting with the same Glyph $nextGlyph [Consonant]
|
1445 |
+
$LigatureOffset = array();
|
1446 |
+
for ($g=0;$g<$LigCount;$g++) {
|
1447 |
+
$LigatureOffset[$g] = $LigSet + $this->read_ushort();
|
1448 |
+
}
|
1449 |
+
for ($g=0;$g<$LigCount;$g++) {
|
1450 |
+
// Ligature tables
|
1451 |
+
$this->seek($LigatureOffset[$g]);
|
1452 |
+
$LigGlyph = $this->read_ushort();
|
1453 |
+
$substitute = $this->glyphToChar($LigGlyph);
|
1454 |
+
$CompCount = $this->read_ushort();
|
1455 |
+
|
1456 |
+
if ($CompCount != 2) { return 0; } // Only expecting to work with 2:1 (and no ignore characters in between)
|
1457 |
+
|
1458 |
+
|
1459 |
+
$gid = $this->read_ushort();
|
1460 |
+
$checkGlyph = $this->glyphToChar($gid); // Other component/input Glyphs starting at position 2 (arrayindex 1)
|
1461 |
+
|
1462 |
+
if ($currGID == $checkGlyph) { $match = true; }
|
1463 |
+
else { $match = false; break; }
|
1464 |
+
|
1465 |
+
$GlyphPos = array();
|
1466 |
+
$GlyphPos[] = $ptr;
|
1467 |
+
$GlyphPos[] = $ptr+1;
|
1468 |
+
|
1469 |
+
|
1470 |
+
if ($match) {
|
1471 |
+
$shift = $this->GSUBsubstitute($ptr, $substitute, 4, $GlyphPos ); // GlyphPos contains positions to set null
|
1472 |
+
if ($shift) return 1;
|
1473 |
+
}
|
1474 |
+
|
1475 |
+
}
|
1476 |
+
return 0;
|
1477 |
+
}
|
1478 |
+
|
1479 |
+
function _applyGSUBsubtable($lookupID, $subtable, $ptr, $currGlyph, $currGID, $subtable_offset, $Type, $Flag, $MarkFilteringSet, $LuCoverage, $level=0, $currentTag, $is_old_spec, $tagInt) {
|
1480 |
+
$ignore = $this->_getGCOMignoreString($Flag, $MarkFilteringSet);
|
1481 |
+
|
1482 |
+
// Lets start
|
1483 |
+
$this->seek($subtable_offset);
|
1484 |
+
$SubstFormat= $this->read_ushort();
|
1485 |
+
|
1486 |
+
////////////////////////////////////////////////////////////////////////////////
|
1487 |
+
// LookupType 1: Single Substitution Subtable : 1 to 1
|
1488 |
+
////////////////////////////////////////////////////////////////////////////////
|
1489 |
+
if ($Type == 1) {
|
1490 |
+
// Flag = Ignore
|
1491 |
+
if ($this->_checkGCOMignore($Flag, $currGlyph, $MarkFilteringSet)) { return 0; }
|
1492 |
+
$CoverageOffset = $subtable_offset + $this->read_ushort();
|
1493 |
+
$GlyphPos = $LuCoverage[$currGID];
|
1494 |
+
//===========
|
1495 |
+
// Format 1:
|
1496 |
+
//===========
|
1497 |
+
if ($SubstFormat==1) { // Calculated output glyph indices
|
1498 |
+
$DeltaGlyphID = $this->read_short();
|
1499 |
+
$this->seek($CoverageOffset);
|
1500 |
+
$glyphs = $this->_getCoverageGID();
|
1501 |
+
$GlyphID = $glyphs[$GlyphPos] + $DeltaGlyphID;
|
1502 |
+
}
|
1503 |
+
//===========
|
1504 |
+
// Format 2:
|
1505 |
+
//===========
|
1506 |
+
else if ($SubstFormat==2) { // Specified output glyph indices
|
1507 |
+
$GlyphCount = $this->read_ushort();
|
1508 |
+
$this->skip($GlyphPos * 2 );
|
1509 |
+
$GlyphID = $this->read_ushort();
|
1510 |
+
}
|
1511 |
+
|
1512 |
+
$substitute = $this->glyphToChar($GlyphID);
|
1513 |
+
$shift = $this->GSUBsubstitute($ptr, $substitute, $Type );
|
1514 |
+
if ($this->debugOTL && $shift) { $this->_dumpproc('GSUB', $lookupID, $subtable, $Type, $SubstFormat, $ptr, $currGlyph, $level); }
|
1515 |
+
if ($shift) return 1;
|
1516 |
+
return 0;
|
1517 |
+
}
|
1518 |
+
|
1519 |
+
////////////////////////////////////////////////////////////////////////////////
|
1520 |
+
// LookupType 2: Multiple Substitution Subtable : 1 to n
|
1521 |
+
////////////////////////////////////////////////////////////////////////////////
|
1522 |
+
else if ($Type == 2) {
|
1523 |
+
// Flag = Ignore
|
1524 |
+
if ($this->_checkGCOMignore($Flag, $currGlyph, $MarkFilteringSet)) { return 0; }
|
1525 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
1526 |
+
$GlyphPos = $LuCoverage[$currGID];
|
1527 |
+
$this->skip(2);
|
1528 |
+
$this->skip($GlyphPos * 2);
|
1529 |
+
$Sequences = $subtable_offset + $this->read_short();
|
1530 |
+
|
1531 |
+
$this->seek($Sequences);
|
1532 |
+
$GlyphCount = $this->read_short();
|
1533 |
+
$SubstituteGlyphs = array();
|
1534 |
+
for ($g=0;$g<$GlyphCount;$g++) {
|
1535 |
+
$sgid = $this->read_ushort();
|
1536 |
+
$SubstituteGlyphs[] = $this->glyphToChar($sgid);
|
1537 |
+
}
|
1538 |
+
|
1539 |
+
$shift = $this->GSUBsubstitute($ptr, $SubstituteGlyphs, $Type );
|
1540 |
+
if ($this->debugOTL && $shift) { $this->_dumpproc('GSUB', $lookupID, $subtable, $Type, $SubstFormat, $ptr, $currGlyph, $level); }
|
1541 |
+
if ($shift) return $shift;
|
1542 |
+
return 0;
|
1543 |
+
}
|
1544 |
+
////////////////////////////////////////////////////////////////////////////////
|
1545 |
+
// LookupType 3: Alternate Forms : 1 to 1(n)
|
1546 |
+
////////////////////////////////////////////////////////////////////////////////
|
1547 |
+
else if ($Type == 3) {
|
1548 |
+
// Flag = Ignore
|
1549 |
+
if ($this->_checkGCOMignore($Flag, $currGlyph, $MarkFilteringSet)) { return 0; }
|
1550 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
1551 |
+
$AlternateSetCount = $this->read_short();
|
1552 |
+
///////////////////////////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
1553 |
+
// Need to set alternate IF set by CSS3 font-feature for a tag
|
1554 |
+
// i.e. if this is 'salt' alternate may be set to 2
|
1555 |
+
// default value will be $alt=1 ( === index of 0 in list of alternates)
|
1556 |
+
$alt = 1; // $alt=1 points to Alternative[0]
|
1557 |
+
if ($tagInt>1) { $alt = $tagInt; }
|
1558 |
+
///////////////////////////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
1559 |
+
if ($alt == 0) { return 0; } // If specified alternate not present, cancel [ or could default $alt = 1 ?]
|
1560 |
+
|
1561 |
+
$GlyphPos = $LuCoverage[$currGID];
|
1562 |
+
$this->skip($GlyphPos * 2);
|
1563 |
+
|
1564 |
+
$AlternateSets = $subtable_offset + $this->read_short();
|
1565 |
+
$this->seek($AlternateSets );
|
1566 |
+
|
1567 |
+
$AlternateGlyphCount = $this->read_short();
|
1568 |
+
if ($alt > $AlternateGlyphCount) { return 0; } // If specified alternate not present, cancel [ or could default $alt = 1 ?]
|
1569 |
+
|
1570 |
+
$this->skip(($alt-1) * 2);
|
1571 |
+
$GlyphID = $this->read_ushort();
|
1572 |
+
|
1573 |
+
$substitute = $this->glyphToChar($GlyphID);
|
1574 |
+
$shift = $this->GSUBsubstitute($ptr, $substitute, $Type );
|
1575 |
+
if ($this->debugOTL && $shift) { $this->_dumpproc('GSUB', $lookupID, $subtable, $Type, $SubstFormat, $ptr, $currGlyph, $level); }
|
1576 |
+
if ($shift) return 1;
|
1577 |
+
return 0;
|
1578 |
+
}
|
1579 |
+
////////////////////////////////////////////////////////////////////////////////
|
1580 |
+
// LookupType 4: Ligature Substitution Subtable : n to 1
|
1581 |
+
////////////////////////////////////////////////////////////////////////////////
|
1582 |
+
else if ($Type == 4) {
|
1583 |
+
// Flag = Ignore
|
1584 |
+
if ($this->_checkGCOMignore($Flag, $currGlyph, $MarkFilteringSet)) { return 0; }
|
1585 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
1586 |
+
$FirstGlyphPos = $LuCoverage[$currGID];
|
1587 |
+
|
1588 |
+
$LigSetCount = $this->read_short();
|
1589 |
+
|
1590 |
+
$this->skip($FirstGlyphPos * 2);
|
1591 |
+
$LigSet = $subtable_offset + $this->read_short();
|
1592 |
+
|
1593 |
+
$this->seek($LigSet);
|
1594 |
+
$LigCount = $this->read_short();
|
1595 |
+
// LigatureSet i.e. all starting with the same first Glyph $currGlyph
|
1596 |
+
$LigatureOffset = array();
|
1597 |
+
for ($g=0;$g<$LigCount;$g++) {
|
1598 |
+
$LigatureOffset[$g] = $LigSet + $this->read_ushort();
|
1599 |
+
}
|
1600 |
+
for ($g=0;$g<$LigCount;$g++) {
|
1601 |
+
// Ligature tables
|
1602 |
+
$this->seek($LigatureOffset[$g]);
|
1603 |
+
$LigGlyph = $this->read_ushort(); // Output Ligature GlyphID
|
1604 |
+
$substitute = $this->glyphToChar($LigGlyph);
|
1605 |
+
$CompCount = $this->read_ushort();
|
1606 |
+
|
1607 |
+
$spos = $ptr;
|
1608 |
+
$match = true;
|
1609 |
+
$GlyphPos = array();
|
1610 |
+
$GlyphPos[] = $spos;
|
1611 |
+
for ($l=1;$l<$CompCount;$l++) {
|
1612 |
+
$gid = $this->read_ushort();
|
1613 |
+
$checkGlyph = $this->glyphToChar($gid); // Other component/input Glyphs starting at position 2 (arrayindex 1)
|
1614 |
+
|
1615 |
+
$spos++;
|
1616 |
+
//while $this->OTLdata[$spos]['uni'] is an "ignore" => spos++
|
1617 |
+
while (isset($this->OTLdata[$spos]) && strpos($ignore, $this->OTLdata[$spos]['hex'])!==false) { $spos++; }
|
1618 |
+
|
1619 |
+
if (isset($this->OTLdata[$spos]) && $this->OTLdata[$spos]['uni'] == $checkGlyph) {
|
1620 |
+
$GlyphPos[] = $spos;
|
1621 |
+
}
|
1622 |
+
else { $match = false; break; }
|
1623 |
+
|
1624 |
+
}
|
1625 |
+
|
1626 |
+
|
1627 |
+
if ($match) {
|
1628 |
+
$shift = $this->GSUBsubstitute($ptr, $substitute, $Type, $GlyphPos ); // GlyphPos contains positions to set null
|
1629 |
+
if ($this->debugOTL && $shift) { $this->_dumpproc('GSUB', $lookupID, $subtable, $Type, $SubstFormat, $ptr, $currGlyph, $level); }
|
1630 |
+
if ($shift) return ($spos-$ptr+1-($CompCount-1));
|
1631 |
+
}
|
1632 |
+
|
1633 |
+
}
|
1634 |
+
return 0;
|
1635 |
+
}
|
1636 |
+
|
1637 |
+
////////////////////////////////////////////////////////////////////////////////
|
1638 |
+
// LookupType 5: Contextual Substitution Subtable
|
1639 |
+
////////////////////////////////////////////////////////////////////////////////
|
1640 |
+
else if ($Type == 5) {
|
1641 |
+
//===========
|
1642 |
+
// Format 1: Simple Context Glyph Substitution
|
1643 |
+
//===========
|
1644 |
+
if ($SubstFormat==1) {
|
1645 |
+
$CoverageTableOffset = $subtable_offset + $this->read_ushort();
|
1646 |
+
$SubRuleSetCount = $this->read_ushort();
|
1647 |
+
$SubRuleSetOffset = array();
|
1648 |
+
for ($b=0;$b<$SubRuleSetCount;$b++) {
|
1649 |
+
$offset = $this->read_ushort();
|
1650 |
+
if ($offset==0x0000) {
|
1651 |
+
$SubRuleSetOffset[] = $offset;
|
1652 |
+
}
|
1653 |
+
else {
|
1654 |
+
$SubRuleSetOffset[] = $subtable_offset + $offset;
|
1655 |
+
}
|
1656 |
+
}
|
1657 |
+
|
1658 |
+
// SubRuleSet tables: All contexts beginning with the same glyph
|
1659 |
+
// Select the SubRuleSet required using the position of the glyph in the coverage table
|
1660 |
+
$GlyphPos = $LuCoverage[$currGID];
|
1661 |
+
if ($SubRuleSetOffset[$GlyphPos]>0) {
|
1662 |
+
$this->seek($SubRuleSetOffset[$GlyphPos]);
|
1663 |
+
$SubRuleCnt = $this->read_ushort();
|
1664 |
+
$SubRule = array();
|
1665 |
+
for($b=0;$b<$SubRuleCnt;$b++) {
|
1666 |
+
$SubRule[$b] = $SubRuleSetOffset[$GlyphPos]+$this->read_ushort();
|
1667 |
+
}
|
1668 |
+
for($b=0;$b<$SubRuleCnt;$b++) { // EACH RULE
|
1669 |
+
$this->seek($SubRule[$b]);
|
1670 |
+
$InputGlyphCount = $this->read_ushort();
|
1671 |
+
$SubstCount = $this->read_ushort();
|
1672 |
+
|
1673 |
+
$Backtrack = array();
|
1674 |
+
$Lookahead = array();
|
1675 |
+
$Input = array();
|
1676 |
+
$Input[0] = $this->OTLdata[$ptr]['uni'];
|
1677 |
+
for ($r=1;$r<$InputGlyphCount;$r++) {
|
1678 |
+
$gid = $this->read_ushort();
|
1679 |
+
$Input[$r] = $this->glyphToChar($gid);
|
1680 |
+
}
|
1681 |
+
$matched = $this->checkContextMatch($Input, $Backtrack, $Lookahead, $ignore, $ptr);
|
1682 |
+
if ($matched) {
|
1683 |
+
if ($this->debugOTL) { $this->_dumpproc('GSUB', $lookupID, $subtable, $Type, $SubstFormat, $ptr, $currGlyph, $level); }
|
1684 |
+
for ($p=0;$p<$SubstCount;$p++) { // EACH LOOKUP
|
1685 |
+
$SequenceIndex[$p] = $this->read_ushort();
|
1686 |
+
$LookupListIndex[$p] = $this->read_ushort();
|
1687 |
+
}
|
1688 |
+
|
1689 |
+
for ($p=0;$p<$SubstCount;$p++) {
|
1690 |
+
// Apply $LookupListIndex at $SequenceIndex
|
1691 |
+
if ($SequenceIndex[$p] >= $InputGlyphCount) { continue; }
|
1692 |
+
$lu = $LookupListIndex[$p];
|
1693 |
+
$luType = $this->GSUBLookups[$lu]['Type'];
|
1694 |
+
$luFlag = $this->GSUBLookups[$lu]['Flag'];
|
1695 |
+
$luMarkFilteringSet = $this->GSUBLookups[$lu]['MarkFilteringSet'];
|
1696 |
+
|
1697 |
+
$luptr = $matched[$SequenceIndex[$p]];
|
1698 |
+
$lucurrGlyph = $this->OTLdata[$luptr]['hex'];
|
1699 |
+
$lucurrGID = $this->OTLdata[$luptr]['uni'];
|
1700 |
+
|
1701 |
+
foreach($this->GSUBLookups[$lu]['Subtables'] AS $luc=>$lusubtable_offset) {
|
1702 |
+
$shift = $this->_applyGSUBsubtable($lu, $luc, $luptr, $lucurrGlyph, $lucurrGID, ($lusubtable_offset - $this->GSUB_offset) , $luType, $luFlag, $luMarkFilteringSet, $this->GSLuCoverage[$lu][$luc], 1, $currentTag, $is_old_spec, $tagInt);
|
1703 |
+
if ($shift) { break; }
|
1704 |
+
}
|
1705 |
+
}
|
1706 |
+
|
1707 |
+
if (!defined("OMIT_OTL_FIX_3") || OMIT_OTL_FIX_3 != 1) { return $shift ; } /* OTL_FIX_3 */
|
1708 |
+
else return $InputGlyphCount ; // should be + matched ignores in Input Sequence
|
1709 |
+
|
1710 |
+
}
|
1711 |
+
}
|
1712 |
+
|
1713 |
+
}
|
1714 |
+
return 0;
|
1715 |
+
}
|
1716 |
+
|
1717 |
+
//===========
|
1718 |
+
// Format 2:
|
1719 |
+
//===========
|
1720 |
+
// Format 2: Class-based Context Glyph Substitution
|
1721 |
+
else if ($SubstFormat==2) {
|
1722 |
+
|
1723 |
+
$CoverageTableOffset = $subtable_offset + $this->read_ushort();
|
1724 |
+
$InputClassDefOffset = $subtable_offset + $this->read_ushort();
|
1725 |
+
$SubClassSetCnt = $this->read_ushort();
|
1726 |
+
$SubClassSetOffset = array();
|
1727 |
+
for ($b=0;$b<$SubClassSetCnt;$b++) {
|
1728 |
+
$offset = $this->read_ushort();
|
1729 |
+
if ($offset==0x0000) {
|
1730 |
+
$SubClassSetOffset[] = $offset;
|
1731 |
+
}
|
1732 |
+
else {
|
1733 |
+
$SubClassSetOffset[] = $subtable_offset + $offset;
|
1734 |
+
}
|
1735 |
+
}
|
1736 |
+
|
1737 |
+
$InputClasses = $this->_getClasses($InputClassDefOffset);
|
1738 |
+
|
1739 |
+
for ($s=0;$s<$SubClassSetCnt;$s++) { // $SubClassSet is ordered by input class-may be NULL
|
1740 |
+
// Select $SubClassSet if currGlyph is in First Input Class
|
1741 |
+
if ($SubClassSetOffset[$s]>0 && isset($InputClasses[$s][$currGID])) {
|
1742 |
+
$this->seek($SubClassSetOffset[$s]);
|
1743 |
+
$SubClassRuleCnt = $this->read_ushort();
|
1744 |
+
$SubClassRule = array();
|
1745 |
+
for($b=0;$b<$SubClassRuleCnt;$b++) {
|
1746 |
+
$SubClassRule[$b] = $SubClassSetOffset[$s]+$this->read_ushort();
|
1747 |
+
}
|
1748 |
+
|
1749 |
+
for($b=0;$b<$SubClassRuleCnt;$b++) { // EACH RULE
|
1750 |
+
$this->seek($SubClassRule[$b]);
|
1751 |
+
$InputGlyphCount = $this->read_ushort();
|
1752 |
+
$SubstCount = $this->read_ushort();
|
1753 |
+
$Input = array();
|
1754 |
+
for ($r=1;$r<$InputGlyphCount;$r++) {
|
1755 |
+
$Input[$r] = $this->read_ushort();
|
1756 |
+
}
|
1757 |
+
|
1758 |
+
$inputClass = $s;
|
1759 |
+
|
1760 |
+
$inputGlyphs = array();
|
1761 |
+
$inputGlyphs[0] = $InputClasses[$inputClass];
|
1762 |
+
|
1763 |
+
if ($InputGlyphCount>1) {
|
1764 |
+
// NB starts at 1
|
1765 |
+
for ($gcl=1;$gcl<$InputGlyphCount;$gcl++) {
|
1766 |
+
$classindex = $Input[$gcl];
|
1767 |
+
if (isset($InputClasses[$classindex])) { $inputGlyphs[$gcl] = $InputClasses[$classindex]; }
|
1768 |
+
else { $inputGlyphs[$gcl] = ''; }
|
1769 |
+
}
|
1770 |
+
}
|
1771 |
+
|
1772 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
1773 |
+
$class0excl = array();
|
1774 |
+
for ($gc=1;$gc<=count($InputClasses);$gc++) {
|
1775 |
+
if (is_array($InputClasses[$gc])) $class0excl = $class0excl + $InputClasses[$gc];
|
1776 |
+
}
|
1777 |
+
|
1778 |
+
$backtrackGlyphs = array();
|
1779 |
+
$lookaheadGlyphs = array();
|
1780 |
+
|
1781 |
+
$matched = $this->checkContextMatchMultipleUni($inputGlyphs, $backtrackGlyphs, $lookaheadGlyphs, $ignore, $ptr, $class0excl);
|
1782 |
+
if ($matched) {
|
1783 |
+
if ($this->debugOTL) { $this->_dumpproc('GSUB', $lookupID, $subtable, $Type, $SubstFormat, $ptr, $currGlyph, $level); }
|
1784 |
+
for ($p=0;$p<$SubstCount;$p++) { // EACH LOOKUP
|
1785 |
+
$SequenceIndex[$p] = $this->read_ushort();
|
1786 |
+
$LookupListIndex[$p] = $this->read_ushort();
|
1787 |
+
}
|
1788 |
+
|
1789 |
+
for ($p=0;$p<$SubstCount;$p++) {
|
1790 |
+
// Apply $LookupListIndex at $SequenceIndex
|
1791 |
+
if ($SequenceIndex[$p] >= $InputGlyphCount) { continue; }
|
1792 |
+
$lu = $LookupListIndex[$p];
|
1793 |
+
$luType = $this->GSUBLookups[$lu]['Type'];
|
1794 |
+
$luFlag = $this->GSUBLookups[$lu]['Flag'];
|
1795 |
+
$luMarkFilteringSet = $this->GSUBLookups[$lu]['MarkFilteringSet'];
|
1796 |
+
|
1797 |
+
$luptr = $matched[$SequenceIndex[$p]];
|
1798 |
+
$lucurrGlyph = $this->OTLdata[$luptr]['hex'];
|
1799 |
+
$lucurrGID = $this->OTLdata[$luptr]['uni'];
|
1800 |
+
|
1801 |
+
foreach($this->GSUBLookups[$lu]['Subtables'] AS $luc=>$lusubtable_offset) {
|
1802 |
+
$shift = $this->_applyGSUBsubtable($lu, $luc, $luptr, $lucurrGlyph, $lucurrGID, ($lusubtable_offset - $this->GSUB_offset) , $luType, $luFlag, $luMarkFilteringSet, $this->GSLuCoverage[$lu][$luc], 1, $currentTag, $is_old_spec, $tagInt);
|
1803 |
+
if ($shift) { break; }
|
1804 |
+
}
|
1805 |
+
}
|
1806 |
+
|
1807 |
+
if (!defined("OMIT_OTL_FIX_3") || OMIT_OTL_FIX_3 != 1) { return $shift ; } /* OTL_FIX_3 */
|
1808 |
+
else return $InputGlyphCount ; // should be + matched ignores in Input Sequence
|
1809 |
+
|
1810 |
+
}
|
1811 |
+
|
1812 |
+
}
|
1813 |
+
|
1814 |
+
}
|
1815 |
+
}
|
1816 |
+
|
1817 |
+
return 0;
|
1818 |
+
}
|
1819 |
+
|
1820 |
+
//===========
|
1821 |
+
// Format 3:
|
1822 |
+
//===========
|
1823 |
+
// Format 3: Coverage-based Context Glyph Substitution
|
1824 |
+
else if ($SubstFormat==3) {
|
1825 |
+
die("GSUB Lookup Type ".$Type." Format ".$SubstFormat." not TESTED YET.");
|
1826 |
+
return 0;
|
1827 |
+
}
|
1828 |
+
|
1829 |
+
}
|
1830 |
+
|
1831 |
+
////////////////////////////////////////////////////////////////////////////////
|
1832 |
+
// LookupType 6: Chaining Contextual Substitution Subtable
|
1833 |
+
////////////////////////////////////////////////////////////////////////////////
|
1834 |
+
else if ($Type == 6) {
|
1835 |
+
|
1836 |
+
//===========
|
1837 |
+
// Format 1:
|
1838 |
+
//===========
|
1839 |
+
// Format 1: Simple Chaining Context Glyph Substitution
|
1840 |
+
if ($SubstFormat==1) {
|
1841 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
1842 |
+
$GlyphPos = $LuCoverage[$currGID];
|
1843 |
+
$ChainSubRuleSetCount = $this->read_ushort();
|
1844 |
+
// All of the ChainSubRule tables defining contexts that begin with the same first glyph are grouped together and defined in a ChainSubRuleSet table
|
1845 |
+
$this->skip($GlyphPos * 2);
|
1846 |
+
$ChainSubRuleSet= $subtable_offset + $this->read_ushort();
|
1847 |
+
$this->seek($ChainSubRuleSet);
|
1848 |
+
$ChainSubRuleCount = $this->read_ushort();
|
1849 |
+
|
1850 |
+
for($s=0;$s<$ChainSubRuleCount;$s++) {
|
1851 |
+
$ChainSubRule[$s] = $ChainSubRuleSet + $this->read_ushort();
|
1852 |
+
}
|
1853 |
+
|
1854 |
+
for($s=0;$s<$ChainSubRuleCount;$s++) {
|
1855 |
+
$this->seek($ChainSubRule[$s]);
|
1856 |
+
|
1857 |
+
$BacktrackGlyphCount = $this->read_ushort();
|
1858 |
+
$Backtrack = array();
|
1859 |
+
for ($b=0;$b<$BacktrackGlyphCount;$b++) {
|
1860 |
+
$gid = $this->read_ushort();
|
1861 |
+
$Backtrack[] = $this->glyphToChar($gid);
|
1862 |
+
}
|
1863 |
+
$Input = array();
|
1864 |
+
$Input[0] = $this->OTLdata[$ptr]['uni'];
|
1865 |
+
$InputGlyphCount = $this->read_ushort();
|
1866 |
+
for ($b=1;$b<$InputGlyphCount;$b++) {
|
1867 |
+
$gid = $this->read_ushort();
|
1868 |
+
$Input[$b] = $this->glyphToChar($gid);
|
1869 |
+
}
|
1870 |
+
$LookaheadGlyphCount = $this->read_ushort();
|
1871 |
+
$Lookahead = array();
|
1872 |
+
for ($b=0;$b<$LookaheadGlyphCount;$b++) {
|
1873 |
+
$gid = $this->read_ushort();
|
1874 |
+
$Lookahead[] = $this->glyphToChar($gid);
|
1875 |
+
}
|
1876 |
+
|
1877 |
+
$matched = $this->checkContextMatch($Input, $Backtrack, $Lookahead, $ignore, $ptr);
|
1878 |
+
if ($matched) {
|
1879 |
+
if ($this->debugOTL) { $this->_dumpproc('GSUB', $lookupID, $subtable, $Type, $SubstFormat, $ptr, $currGlyph, $level); }
|
1880 |
+
$SubstCount = $this->read_ushort();
|
1881 |
+
for ($p=0;$p<$SubstCount;$p++) {
|
1882 |
+
// SubstLookupRecord
|
1883 |
+
$SubstLookupRecord[$p]['SequenceIndex'] = $this->read_ushort();
|
1884 |
+
$SubstLookupRecord[$p]['LookupListIndex'] = $this->read_ushort();
|
1885 |
+
}
|
1886 |
+
for ($p=0;$p<$SubstCount;$p++) {
|
1887 |
+
// Apply $SubstLookupRecord[$p]['LookupListIndex'] at $SubstLookupRecord[$p]['SequenceIndex']
|
1888 |
+
if ($SubstLookupRecord[$p]['SequenceIndex'] >= $InputGlyphCount) { continue; }
|
1889 |
+
$lu = $SubstLookupRecord[$p]['LookupListIndex'];
|
1890 |
+
$luType = $this->GSUBLookups[$lu]['Type'];
|
1891 |
+
$luFlag = $this->GSUBLookups[$lu]['Flag'];
|
1892 |
+
$luMarkFilteringSet = $this->GSUBLookups[$lu]['MarkFilteringSet'];
|
1893 |
+
|
1894 |
+
$luptr = $matched[$SubstLookupRecord[$p]['SequenceIndex']];
|
1895 |
+
$lucurrGlyph = $this->OTLdata[$luptr]['hex'];
|
1896 |
+
$lucurrGID = $this->OTLdata[$luptr]['uni'];
|
1897 |
+
|
1898 |
+
foreach($this->GSUBLookups[$lu]['Subtables'] AS $luc=>$lusubtable_offset) {
|
1899 |
+
$shift = $this->_applyGSUBsubtable($lu, $luc, $luptr, $lucurrGlyph, $lucurrGID, ($lusubtable_offset - $this->GSUB_offset), $luType, $luFlag, $luMarkFilteringSet, $this->GSLuCoverage[$lu][$luc], 1, $currentTag, $is_old_spec, $tagInt);
|
1900 |
+
if ($shift) { break; }
|
1901 |
+
}
|
1902 |
+
}
|
1903 |
+
if (!defined("OMIT_OTL_FIX_3") || OMIT_OTL_FIX_3 != 1) { return $shift ; } /* OTL_FIX_3 */
|
1904 |
+
else return $InputGlyphCount ; // should be + matched ignores in Input Sequence
|
1905 |
+
}
|
1906 |
+
|
1907 |
+
|
1908 |
+
|
1909 |
+
|
1910 |
+
}
|
1911 |
+
return 0;
|
1912 |
+
}
|
1913 |
+
|
1914 |
+
//===========
|
1915 |
+
// Format 2:
|
1916 |
+
//===========
|
1917 |
+
// Format 2: Class-based Chaining Context Glyph Substitution p257
|
1918 |
+
else if ($SubstFormat==2) {
|
1919 |
+
|
1920 |
+
// NB Format 2 specifies fixed class assignments (identical for each position in the backtrack, input, or lookahead sequence) and exclusive classes (a glyph cannot be in more than one class at a time)
|
1921 |
+
|
1922 |
+
$CoverageTableOffset = $subtable_offset + $this->read_ushort();
|
1923 |
+
$BacktrackClassDefOffset = $subtable_offset + $this->read_ushort();
|
1924 |
+
$InputClassDefOffset = $subtable_offset + $this->read_ushort();
|
1925 |
+
$LookaheadClassDefOffset = $subtable_offset + $this->read_ushort();
|
1926 |
+
$ChainSubClassSetCnt = $this->read_ushort();
|
1927 |
+
$ChainSubClassSetOffset = array();
|
1928 |
+
for ($b=0;$b<$ChainSubClassSetCnt;$b++) {
|
1929 |
+
$offset = $this->read_ushort();
|
1930 |
+
if ($offset==0x0000) {
|
1931 |
+
$ChainSubClassSetOffset[] = $offset;
|
1932 |
+
}
|
1933 |
+
else {
|
1934 |
+
$ChainSubClassSetOffset[] = $subtable_offset + $offset;
|
1935 |
+
}
|
1936 |
+
}
|
1937 |
+
|
1938 |
+
$BacktrackClasses = $this->_getClasses($BacktrackClassDefOffset);
|
1939 |
+
$InputClasses = $this->_getClasses($InputClassDefOffset);
|
1940 |
+
$LookaheadClasses = $this->_getClasses($LookaheadClassDefOffset);
|
1941 |
+
|
1942 |
+
for ($s=0;$s<$ChainSubClassSetCnt;$s++) { // $ChainSubClassSet is ordered by input class-may be NULL
|
1943 |
+
// Select $ChainSubClassSet if currGlyph is in First Input Class
|
1944 |
+
if ($ChainSubClassSetOffset[$s]>0 && isset($InputClasses[$s][$currGID])) {
|
1945 |
+
$this->seek($ChainSubClassSetOffset[$s]);
|
1946 |
+
$ChainSubClassRuleCnt = $this->read_ushort();
|
1947 |
+
$ChainSubClassRule = array();
|
1948 |
+
for($b=0;$b<$ChainSubClassRuleCnt;$b++) {
|
1949 |
+
$ChainSubClassRule[$b] = $ChainSubClassSetOffset[$s]+$this->read_ushort();
|
1950 |
+
}
|
1951 |
+
|
1952 |
+
for($b=0;$b<$ChainSubClassRuleCnt;$b++) { // EACH RULE
|
1953 |
+
$this->seek($ChainSubClassRule[$b]);
|
1954 |
+
$BacktrackGlyphCount = $this->read_ushort();
|
1955 |
+
for ($r=0;$r<$BacktrackGlyphCount;$r++) {
|
1956 |
+
$Backtrack[$r] = $this->read_ushort();
|
1957 |
+
}
|
1958 |
+
$InputGlyphCount = $this->read_ushort();
|
1959 |
+
for ($r=1;$r<$InputGlyphCount;$r++) {
|
1960 |
+
$Input[$r] = $this->read_ushort();
|
1961 |
+
}
|
1962 |
+
$LookaheadGlyphCount = $this->read_ushort();
|
1963 |
+
for ($r=0;$r<$LookaheadGlyphCount;$r++) {
|
1964 |
+
$Lookahead[$r] = $this->read_ushort();
|
1965 |
+
}
|
1966 |
+
|
1967 |
+
|
1968 |
+
// These contain classes of glyphs as arrays
|
1969 |
+
// $InputClasses[(class)] e.g. 0x02E6,0x02E7,0x02E8
|
1970 |
+
// $LookaheadClasses[(class)]
|
1971 |
+
// $BacktrackClasses[(class)]
|
1972 |
+
|
1973 |
+
// These contain arrays of classIndexes
|
1974 |
+
// [Backtrack] [Lookahead] and [Input] (Input is from the second position only)
|
1975 |
+
|
1976 |
+
|
1977 |
+
$inputClass = $s; //???
|
1978 |
+
|
1979 |
+
$inputGlyphs = array();
|
1980 |
+
$inputGlyphs[0] = $InputClasses[$inputClass];
|
1981 |
+
|
1982 |
+
if ($InputGlyphCount>1) {
|
1983 |
+
// NB starts at 1
|
1984 |
+
for ($gcl=1;$gcl<$InputGlyphCount;$gcl++) {
|
1985 |
+
$classindex = $Input[$gcl];
|
1986 |
+
if (isset($InputClasses[$classindex])) { $inputGlyphs[$gcl] = $InputClasses[$classindex]; }
|
1987 |
+
else { $inputGlyphs[$gcl] = ''; }
|
1988 |
+
}
|
1989 |
+
}
|
1990 |
+
|
1991 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
1992 |
+
$class0excl = array();
|
1993 |
+
for ($gc=1;$gc<=count($InputClasses);$gc++) {
|
1994 |
+
if (isset($InputClasses[$gc])) $class0excl = $class0excl + $InputClasses[$gc];
|
1995 |
+
}
|
1996 |
+
|
1997 |
+
if ($BacktrackGlyphCount) {
|
1998 |
+
for ($gcl=0;$gcl<$BacktrackGlyphCount;$gcl++) {
|
1999 |
+
$classindex = $Backtrack[$gcl];
|
2000 |
+
if (isset($BacktrackClasses[$classindex])) { $backtrackGlyphs[$gcl] = $BacktrackClasses[$classindex]; }
|
2001 |
+
else { $backtrackGlyphs[$gcl] = ''; }
|
2002 |
+
}
|
2003 |
+
}
|
2004 |
+
else { $backtrackGlyphs = array(); }
|
2005 |
+
|
2006 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
2007 |
+
$bclass0excl = array();
|
2008 |
+
for ($gc=1;$gc<=count($BacktrackClasses);$gc++) {
|
2009 |
+
if (isset($BacktrackClasses[$gc])) $bclass0excl = $bclass0excl + $BacktrackClasses[$gc];
|
2010 |
+
}
|
2011 |
+
|
2012 |
+
|
2013 |
+
if ($LookaheadGlyphCount) {
|
2014 |
+
for ($gcl=0;$gcl<$LookaheadGlyphCount;$gcl++) {
|
2015 |
+
$classindex = $Lookahead[$gcl];
|
2016 |
+
if (isset($LookaheadClasses[$classindex])) { $lookaheadGlyphs[$gcl] = $LookaheadClasses[$classindex]; }
|
2017 |
+
else { $lookaheadGlyphs[$gcl] = ''; }
|
2018 |
+
}
|
2019 |
+
}
|
2020 |
+
else { $lookaheadGlyphs = array(); }
|
2021 |
+
|
2022 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
2023 |
+
$lclass0excl = array();
|
2024 |
+
for ($gc=1;$gc<=count($LookaheadClasses);$gc++) {
|
2025 |
+
if (isset($LookaheadClasses[$gc])) $lclass0excl = $lclass0excl + $LookaheadClasses[$gc];
|
2026 |
+
}
|
2027 |
+
|
2028 |
+
|
2029 |
+
$matched = $this->checkContextMatchMultipleUni($inputGlyphs, $backtrackGlyphs, $lookaheadGlyphs, $ignore, $ptr, $class0excl, $bclass0excl, $lclass0excl );
|
2030 |
+
if ($matched) {
|
2031 |
+
if ($this->debugOTL) { $this->_dumpproc('GSUB', $lookupID, $subtable, $Type, $SubstFormat, $ptr, $currGlyph, $level); }
|
2032 |
+
$SubstCount = $this->read_ushort();
|
2033 |
+
for ($p=0;$p<$SubstCount;$p++) { // EACH LOOKUP
|
2034 |
+
$SequenceIndex[$p] = $this->read_ushort();
|
2035 |
+
$LookupListIndex[$p] = $this->read_ushort();
|
2036 |
+
}
|
2037 |
+
|
2038 |
+
for ($p=0;$p<$SubstCount;$p++) {
|
2039 |
+
// Apply $LookupListIndex at $SequenceIndex
|
2040 |
+
if ($SequenceIndex[$p] >= $InputGlyphCount) { continue; }
|
2041 |
+
$lu = $LookupListIndex[$p];
|
2042 |
+
$luType = $this->GSUBLookups[$lu]['Type'];
|
2043 |
+
$luFlag = $this->GSUBLookups[$lu]['Flag'];
|
2044 |
+
$luMarkFilteringSet = $this->GSUBLookups[$lu]['MarkFilteringSet'];
|
2045 |
+
|
2046 |
+
$luptr = $matched[$SequenceIndex[$p]];
|
2047 |
+
$lucurrGlyph = $this->OTLdata[$luptr]['hex'];
|
2048 |
+
$lucurrGID = $this->OTLdata[$luptr]['uni'];
|
2049 |
+
|
2050 |
+
foreach($this->GSUBLookups[$lu]['Subtables'] AS $luc=>$lusubtable_offset) {
|
2051 |
+
$shift = $this->_applyGSUBsubtable($lu, $luc, $luptr, $lucurrGlyph, $lucurrGID, ($lusubtable_offset - $this->GSUB_offset) , $luType, $luFlag, $luMarkFilteringSet, $this->GSLuCoverage[$lu][$luc], 1, $currentTag, $is_old_spec, $tagInt);
|
2052 |
+
if ($shift) { break; }
|
2053 |
+
}
|
2054 |
+
}
|
2055 |
+
|
2056 |
+
if (!defined("OMIT_OTL_FIX_3") || OMIT_OTL_FIX_3 != 1) { return $shift ; } /* OTL_FIX_3 */
|
2057 |
+
else return $InputGlyphCount ; // should be + matched ignores in Input Sequence
|
2058 |
+
|
2059 |
+
}
|
2060 |
+
|
2061 |
+
}
|
2062 |
+
|
2063 |
+
}
|
2064 |
+
}
|
2065 |
+
|
2066 |
+
return 0;
|
2067 |
+
}
|
2068 |
+
|
2069 |
+
//===========
|
2070 |
+
// Format 3:
|
2071 |
+
//===========
|
2072 |
+
// Format 3: Coverage-based Chaining Context Glyph Substitution p259
|
2073 |
+
else if ($SubstFormat==3) {
|
2074 |
+
|
2075 |
+
$BacktrackGlyphCount = $this->read_ushort();
|
2076 |
+
for ($b=0;$b<$BacktrackGlyphCount;$b++) {
|
2077 |
+
$CoverageBacktrackOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order
|
2078 |
+
}
|
2079 |
+
$InputGlyphCount = $this->read_ushort();
|
2080 |
+
for ($b=0;$b<$InputGlyphCount;$b++) {
|
2081 |
+
$CoverageInputOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order
|
2082 |
+
}
|
2083 |
+
$LookaheadGlyphCount = $this->read_ushort();
|
2084 |
+
for ($b=0;$b<$LookaheadGlyphCount;$b++) {
|
2085 |
+
$CoverageLookaheadOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order
|
2086 |
+
}
|
2087 |
+
$SubstCount = $this->read_ushort();
|
2088 |
+
$save_pos = $this->_pos; // Save the point just after PosCount
|
2089 |
+
|
2090 |
+
$CoverageBacktrackGlyphs = array();
|
2091 |
+
for ($b=0;$b<$BacktrackGlyphCount;$b++) {
|
2092 |
+
$this->seek($CoverageBacktrackOffset[$b]);
|
2093 |
+
$glyphs = $this->_getCoverage();
|
2094 |
+
$CoverageBacktrackGlyphs[$b] = implode("|",$glyphs);
|
2095 |
+
}
|
2096 |
+
$CoverageInputGlyphs = array();
|
2097 |
+
for ($b=0;$b<$InputGlyphCount;$b++) {
|
2098 |
+
$this->seek($CoverageInputOffset[$b]);
|
2099 |
+
$glyphs = $this->_getCoverage();
|
2100 |
+
$CoverageInputGlyphs[$b] = implode("|",$glyphs);
|
2101 |
+
}
|
2102 |
+
$CoverageLookaheadGlyphs = array();
|
2103 |
+
for ($b=0;$b<$LookaheadGlyphCount;$b++) {
|
2104 |
+
$this->seek($CoverageLookaheadOffset[$b]);
|
2105 |
+
$glyphs = $this->_getCoverage();
|
2106 |
+
$CoverageLookaheadGlyphs[$b] = implode("|",$glyphs);
|
2107 |
+
}
|
2108 |
+
|
2109 |
+
$matched = $this->checkContextMatchMultiple($CoverageInputGlyphs, $CoverageBacktrackGlyphs, $CoverageLookaheadGlyphs , $ignore, $ptr);
|
2110 |
+
if ($matched) {
|
2111 |
+
if ($this->debugOTL) { $this->_dumpproc('GSUB', $lookupID, $subtable, $Type, $SubstFormat, $ptr, $currGlyph, $level); }
|
2112 |
+
|
2113 |
+
$this->seek($save_pos); // Return to just after PosCount
|
2114 |
+
for ($p=0;$p<$SubstCount;$p++) {
|
2115 |
+
// SubstLookupRecord
|
2116 |
+
$SubstLookupRecord[$p]['SequenceIndex'] = $this->read_ushort();
|
2117 |
+
$SubstLookupRecord[$p]['LookupListIndex'] = $this->read_ushort();
|
2118 |
+
}
|
2119 |
+
for ($p=0;$p<$SubstCount;$p++) {
|
2120 |
+
// Apply $SubstLookupRecord[$p]['LookupListIndex'] at $SubstLookupRecord[$p]['SequenceIndex']
|
2121 |
+
if ($SubstLookupRecord[$p]['SequenceIndex'] >= $InputGlyphCount) { continue; }
|
2122 |
+
$lu = $SubstLookupRecord[$p]['LookupListIndex'];
|
2123 |
+
$luType = $this->GSUBLookups[$lu]['Type'];
|
2124 |
+
$luFlag = $this->GSUBLookups[$lu]['Flag'];
|
2125 |
+
$luMarkFilteringSet = $this->GSUBLookups[$lu]['MarkFilteringSet'];
|
2126 |
+
|
2127 |
+
$luptr = $matched[$SubstLookupRecord[$p]['SequenceIndex']];
|
2128 |
+
$lucurrGlyph = $this->OTLdata[$luptr]['hex'];
|
2129 |
+
$lucurrGID = $this->OTLdata[$luptr]['uni'];
|
2130 |
+
|
2131 |
+
foreach($this->GSUBLookups[$lu]['Subtables'] AS $luc=>$lusubtable_offset) {
|
2132 |
+
$shift = $this->_applyGSUBsubtable($lu, $luc, $luptr, $lucurrGlyph, $lucurrGID, ($lusubtable_offset - $this->GSUB_offset), $luType, $luFlag, $luMarkFilteringSet, $this->GSLuCoverage[$lu][$luc], 1, $currentTag, $is_old_spec, $tagInt);
|
2133 |
+
if ($shift) { break; }
|
2134 |
+
}
|
2135 |
+
}
|
2136 |
+
if (!defined("OMIT_OTL_FIX_3") || OMIT_OTL_FIX_3 != 1) { return (isset($shift) ? $shift : 0) ; } /* OTL_FIX_3 */
|
2137 |
+
else return $InputGlyphCount ; // should be + matched ignores in Input Sequence
|
2138 |
+
}
|
2139 |
+
|
2140 |
+
return 0;
|
2141 |
+
|
2142 |
+
}
|
2143 |
+
}
|
2144 |
+
|
2145 |
+
else { die("GSUB Lookup Type ".$Type." not supported."); }
|
2146 |
+
|
2147 |
+
}
|
2148 |
+
|
2149 |
+
function _updateLigatureMarks($pos, $n) {
|
2150 |
+
if ($n > 0) {
|
2151 |
+
// Update position of Ligatures and associated Marks
|
2152 |
+
// Foreach lig/assocMarks
|
2153 |
+
// Any position lpos or mpos > $pos + count($substitute)
|
2154 |
+
// $this->assocMarks = array(); // assocMarks[$pos mpos] => array(compID, ligPos)
|
2155 |
+
// $this->assocLigs = array(); // Ligatures[$pos lpos] => nc
|
2156 |
+
for ($p=count($this->OTLdata)-1;$p>=($pos+$n);$p--) {
|
2157 |
+
if (isset($this->assocLigs[$p])) {
|
2158 |
+
$tmp = $this->assocLigs[$p];
|
2159 |
+
unset($this->assocLigs[$p]);
|
2160 |
+
$this->assocLigs[($p + $n)] = $tmp;
|
2161 |
+
}
|
2162 |
+
}
|
2163 |
+
for ($p=count($this->OTLdata)-1;$p>=0;$p--) {
|
2164 |
+
if (isset($this->assocMarks[$p])) {
|
2165 |
+
if ($this->assocMarks[$p]['ligPos'] >=($pos+$n)) { $this->assocMarks[$p]['ligPos'] += $n; }
|
2166 |
+
if ($p>=($pos+$n)) {
|
2167 |
+
$tmp = $this->assocMarks[$p];
|
2168 |
+
unset($this->assocMarks[$p]);
|
2169 |
+
$this->assocMarks[($p + $n)] = $tmp;
|
2170 |
+
}
|
2171 |
+
}
|
2172 |
+
}
|
2173 |
+
}
|
2174 |
+
|
2175 |
+
else if ($n<1) { // glyphs removed
|
2176 |
+
$nrem = -$n;
|
2177 |
+
// Update position of pre-existing Ligatures and associated Marks
|
2178 |
+
for ($p=($pos+1);$p<count($this->OTLdata);$p++) {
|
2179 |
+
if (isset($this->assocLigs[$p])) {
|
2180 |
+
$tmp = $this->assocLigs[$p];
|
2181 |
+
unset($this->assocLigs[$p]);
|
2182 |
+
$this->assocLigs[($p - $nrem)] = $tmp;
|
2183 |
+
}
|
2184 |
+
}
|
2185 |
+
for ($p=0;$p<count($this->OTLdata);$p++) {
|
2186 |
+
if (isset($this->assocMarks[$p])) {
|
2187 |
+
if ($this->assocMarks[$p]['ligPos'] >=($pos)) { $this->assocMarks[$p]['ligPos'] -= $nrem; }
|
2188 |
+
if ($p>$pos) {
|
2189 |
+
$tmp = $this->assocMarks[$p];
|
2190 |
+
unset($this->assocMarks[$p]);
|
2191 |
+
$this->assocMarks[($p - $nrem)] = $tmp;
|
2192 |
+
}
|
2193 |
+
}
|
2194 |
+
}
|
2195 |
+
}
|
2196 |
+
}
|
2197 |
+
|
2198 |
+
function GSUBsubstitute($pos, $substitute, $Type, $GlyphPos=NULL ) {
|
2199 |
+
|
2200 |
+
// LookupType 1: Simple Substitution Subtable : 1 to 1
|
2201 |
+
// LookupType 3: Alternate Forms : 1 to 1(n)
|
2202 |
+
if ($Type == 1 || $Type == 3) {
|
2203 |
+
$this->OTLdata[$pos]['uni'] = $substitute;
|
2204 |
+
$this->OTLdata[$pos]['hex'] = $this->unicode_hex($substitute);
|
2205 |
+
return 1;
|
2206 |
+
}
|
2207 |
+
// LookupType 2: Multiple Substitution Subtable : 1 to n
|
2208 |
+
else if ($Type == 2) {
|
2209 |
+
for($i=0;$i<count($substitute);$i++) {
|
2210 |
+
$uni = $substitute[$i];
|
2211 |
+
$newOTLdata[$i] = array();
|
2212 |
+
$newOTLdata[$i]['uni'] = $uni;
|
2213 |
+
$newOTLdata[$i]['hex'] = $this->unicode_hex($uni);
|
2214 |
+
|
2215 |
+
|
2216 |
+
// Get types of new inserted chars - or replicate type of char being replaced
|
2217 |
+
// $bt = UCDN::get_bidi_class($uni);
|
2218 |
+
// if (!$bt) {
|
2219 |
+
$bt = $this->OTLdata[$pos]['bidi_type'];
|
2220 |
+
// }
|
2221 |
+
|
2222 |
+
if (strpos($this->GlyphClassMarks, $newOTLdata[$i]['hex'] )!==false) { $gp = 'M'; }
|
2223 |
+
else if ($uni == 32) { $gp = 'S'; }
|
2224 |
+
else { $gp = 'C'; }
|
2225 |
+
|
2226 |
+
// Need to update matra_type ??? of new glyphs inserted ???????????????????????????????????????
|
2227 |
+
|
2228 |
+
$newOTLdata[$i]['bidi_type'] = $bt;
|
2229 |
+
$newOTLdata[$i]['group'] = $gp;
|
2230 |
+
|
2231 |
+
// Need to update details of new glyphs inserted
|
2232 |
+
$newOTLdata[$i]['general_category'] = $this->OTLdata[$pos]['general_category'];
|
2233 |
+
|
2234 |
+
if ($this->shaper=='I' || $this->shaper=='K' || $this->shaper=='S') {
|
2235 |
+
$newOTLdata[$i]['indic_category'] = $this->OTLdata[$pos]['indic_category'];
|
2236 |
+
$newOTLdata[$i]['indic_position'] = $this->OTLdata[$pos]['indic_position'];
|
2237 |
+
}
|
2238 |
+
else if ($this->shaper=='M') {
|
2239 |
+
$newOTLdata[$i]['myanmar_category'] = $this->OTLdata[$pos]['myanmar_category'];
|
2240 |
+
$newOTLdata[$i]['myanmar_position'] = $this->OTLdata[$pos]['myanmar_position'];
|
2241 |
+
}
|
2242 |
+
if (isset($this->OTLdata[$pos]['mask'])) { $newOTLdata[$i]['mask'] = $this->OTLdata[$pos]['mask']; }
|
2243 |
+
if (isset($this->OTLdata[$pos]['syllable'])) { $newOTLdata[$i]['syllable'] = $this->OTLdata[$pos]['syllable']; }
|
2244 |
+
|
2245 |
+
}
|
2246 |
+
if ($this->shaper=='K' || $this->shaper=='T' || $this->shaper=='L') {
|
2247 |
+
if ($this->OTLdata[$pos]['wordend']) { $newOTLdata[count($substitute)-1]['wordend'] = true; }
|
2248 |
+
}
|
2249 |
+
|
2250 |
+
array_splice($this->OTLdata, $pos, 1, $newOTLdata); // Replace 1 with n
|
2251 |
+
// Update position of Ligatures and associated Marks
|
2252 |
+
// count($substitute)-1 is the number of glyphs added
|
2253 |
+
$nadd = count($substitute)-1;
|
2254 |
+
$this->_updateLigatureMarks($pos, $nadd);
|
2255 |
+
return count($substitute);
|
2256 |
+
}
|
2257 |
+
// LookupType 4: Ligature Substitution Subtable : n to 1
|
2258 |
+
else if ($Type == 4) {
|
2259 |
+
// Create Ligatures and associated Marks
|
2260 |
+
$firstGlyph = $this->OTLdata[$pos]['hex'];
|
2261 |
+
|
2262 |
+
// If all components of the ligature are marks (and in the same syllable), we call this a mark ligature.
|
2263 |
+
$contains_marks = false;
|
2264 |
+
$contains_nonmarks = false;
|
2265 |
+
if (isset($this->OTLdata[$pos]['syllable'])) { $current_syllable = $this->OTLdata[$pos]['syllable']; }
|
2266 |
+
else { $current_syllable = 0; }
|
2267 |
+
for($i=0;$i<count($GlyphPos);$i++) {
|
2268 |
+
// If subsequent components are not Marks as well - don't ligate
|
2269 |
+
$unistr = $this->OTLdata[$GlyphPos[$i]]['hex'];
|
2270 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$GlyphPos[$i]]['syllable']) && $this->OTLdata[$GlyphPos[$i]]['syllable'] != $current_syllable) {
|
2271 |
+
return 0;
|
2272 |
+
}
|
2273 |
+
if (strpos($this->GlyphClassMarks, $unistr )!==false) { $contains_marks = true; }
|
2274 |
+
else { $contains_nonmarks = true; }
|
2275 |
+
}
|
2276 |
+
if ($contains_marks && !$contains_nonmarks) {
|
2277 |
+
// Mark Ligature (all components are Marks)
|
2278 |
+
$firstMarkAssoc = '';
|
2279 |
+
if (isset($this->assocMarks[$pos])) {
|
2280 |
+
$firstMarkAssoc = $this->assocMarks[$pos];
|
2281 |
+
}
|
2282 |
+
// If all components of the ligature are marks, we call this a mark ligature.
|
2283 |
+
for($i=1;$i<count($GlyphPos);$i++) {
|
2284 |
+
|
2285 |
+
// If subsequent components are not Marks as well - don't ligate
|
2286 |
+
// $unistr = $this->OTLdata[$GlyphPos[$i]]['hex'];
|
2287 |
+
// if (strpos($this->GlyphClassMarks, $unistr )===false) { return; }
|
2288 |
+
|
2289 |
+
$nextMarkAssoc = '';
|
2290 |
+
if (isset($this->assocMarks[$GlyphPos[$i]])) {
|
2291 |
+
$nextMarkAssoc = $this->assocMarks[$GlyphPos[$i]];
|
2292 |
+
}
|
2293 |
+
// If first component was attached to a previous ligature component,
|
2294 |
+
// all subsequent components should be attached to the same ligature
|
2295 |
+
// component, otherwise we shouldn't ligate them.
|
2296 |
+
// If first component was NOT attached to a previous ligature component,
|
2297 |
+
// all subsequent components should also NOT be attached to any ligature component,
|
2298 |
+
if ($firstMarkAssoc != $nextMarkAssoc ) {
|
2299 |
+
// unless they are attached to the first component itself!
|
2300 |
+
// if (!is_array($nextMarkAssoc) || $nextMarkAssoc['ligPos']!= $pos) { return; }
|
2301 |
+
|
2302 |
+
// Update/Edit - In test with myanmartext font
|
2303 |
+
// င်္က္ကျြွေိ
|
2304 |
+
// => Lookup 17 E003 E066B E05A 102D
|
2305 |
+
// E003 and 102D should form a mark ligature, but 102D is already associated with (non-mark) ligature E05A
|
2306 |
+
// So instead of disallowing the mark ligature to form, just dissociate...
|
2307 |
+
if (!is_array($nextMarkAssoc) || $nextMarkAssoc['ligPos']!= $pos) { unset($this->assocMarks[$GlyphPos[$i]]); }
|
2308 |
+
}
|
2309 |
+
}
|
2310 |
+
|
2311 |
+
/*
|
2312 |
+
* - If it *is* a mark ligature, we don't allocate a new ligature id, and leave
|
2313 |
+
* the ligature to keep its old ligature id. This will allow it to attach to
|
2314 |
+
* a base ligature in GPOS. Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH,
|
2315 |
+
* and LAM,LAM,HEH form a ligature, they will leave SHADDA and FATHA wit a
|
2316 |
+
* ligature id and component value of 2. Then if SHADDA,FATHA form a ligature
|
2317 |
+
* later, we don't want them to lose their ligature id/component, otherwise
|
2318 |
+
* GPOS will fail to correctly position the mark ligature on top of the
|
2319 |
+
* LAM,LAM,HEH ligature.
|
2320 |
+
*/
|
2321 |
+
// So if is_array($firstMarkAssoc) - the new (Mark) ligature should keep this association
|
2322 |
+
|
2323 |
+
$lastPos = $GlyphPos[(count($GlyphPos)-1)];
|
2324 |
+
}
|
2325 |
+
else {
|
2326 |
+
/*
|
2327 |
+
* - Ligatures cannot be formed across glyphs attached to different components
|
2328 |
+
* of previous ligatures. Eg. the sequence is LAM,SHADDA,LAM,FATHA,HEH, and
|
2329 |
+
* LAM,LAM,HEH form a ligature, leaving SHADDA,FATHA next to eachother.
|
2330 |
+
* However, it would be wrong to ligate that SHADDA,FATHA sequence.
|
2331 |
+
* There is an exception to this: If a ligature tries ligating with marks that
|
2332 |
+
* belong to it itself, go ahead, assuming that the font designer knows what
|
2333 |
+
* they are doing (otherwise it can break Indic stuff when a matra wants to
|
2334 |
+
* ligate with a conjunct...)
|
2335 |
+
*/
|
2336 |
+
|
2337 |
+
/*
|
2338 |
+
* - If a ligature is formed of components that some of which are also ligatures
|
2339 |
+
* themselves, and those ligature components had marks attached to *their*
|
2340 |
+
* components, we have to attach the marks to the new ligature component
|
2341 |
+
* positions! Now *that*'s tricky! And these marks may be following the
|
2342 |
+
* last component of the whole sequence, so we should loop forward looking
|
2343 |
+
* for them and update them.
|
2344 |
+
*
|
2345 |
+
* Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a
|
2346 |
+
* 'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature
|
2347 |
+
* id and component == 1. Now, during 'liga', the LAM and the LAM-HEH ligature
|
2348 |
+
* form a LAM-LAM-HEH ligature. We need to reassign the SHADDA and FATHA to
|
2349 |
+
* the new ligature with a component value of 2.
|
2350 |
+
*
|
2351 |
+
* This in fact happened to a font... See:
|
2352 |
+
* https://bugzilla.gnome.org/show_bug.cgi?id=437633
|
2353 |
+
*/
|
2354 |
+
|
2355 |
+
$currComp = 0;
|
2356 |
+
for($i=0;$i<count($GlyphPos);$i++) {
|
2357 |
+
if ($i>0 && isset($this->assocLigs[$GlyphPos[$i]])) { // One of the other components is already a ligature
|
2358 |
+
$nc = $this->assocLigs[$GlyphPos[$i]];
|
2359 |
+
}
|
2360 |
+
else { $nc = 1; }
|
2361 |
+
// While next char to right is a mark (but not the next matched glyph)
|
2362 |
+
// ?? + also include a Mark Ligature here
|
2363 |
+
$ic = 1;
|
2364 |
+
while((($i==count($GlyphPos)-1) || (isset($GlyphPos[$i+1]) && ($GlyphPos[$i]+$ic) < $GlyphPos[$i+1])) && isset($this->OTLdata[($GlyphPos[$i]+$ic)]) && strpos($this->GlyphClassMarks, $this->OTLdata[($GlyphPos[$i]+$ic)]['hex'])!== false) {
|
2365 |
+
$newComp = $currComp;
|
2366 |
+
if (isset($this->assocMarks[$GlyphPos[$i]+$ic])) { // One of the inbetween Marks is already associated with a Lig
|
2367 |
+
// OK as long as it is associated with the current Lig
|
2368 |
+
// if ($this->assocMarks[($GlyphPos[$i]+$ic)]['ligPos'] != ($GlyphPos[$i]+$ic)) { die("Problem #1"); }
|
2369 |
+
$newComp += $this->assocMarks[($GlyphPos[$i]+$ic)]['compID'];
|
2370 |
+
}
|
2371 |
+
$this->assocMarks[($GlyphPos[$i]+$ic)] = array('compID'=>$newComp, 'ligPos'=>$pos);
|
2372 |
+
$ic++;
|
2373 |
+
}
|
2374 |
+
$currComp += $nc;
|
2375 |
+
}
|
2376 |
+
$lastPos = $GlyphPos[(count($GlyphPos)-1)]+$ic-1;
|
2377 |
+
$this->assocLigs[$pos] = $currComp ; // Number of components in new Ligature
|
2378 |
+
}
|
2379 |
+
|
2380 |
+
// Now remove the unwanted glyphs and associated metadata
|
2381 |
+
$newOTLdata[0] = array();
|
2382 |
+
|
2383 |
+
// Get types of new inserted chars - or replicate type of char being replaced
|
2384 |
+
// $bt = UCDN::get_bidi_class($substitute);
|
2385 |
+
// if (!$bt) {
|
2386 |
+
$bt = $this->OTLdata[$pos]['bidi_type'];
|
2387 |
+
// }
|
2388 |
+
|
2389 |
+
if (strpos($this->GlyphClassMarks, $this->unicode_hex($substitute))!==false) { $gp = 'M'; }
|
2390 |
+
else if ($substitute == 32) { $gp = 'S'; }
|
2391 |
+
else { $gp = 'C'; }
|
2392 |
+
|
2393 |
+
// Need to update details of new glyphs inserted
|
2394 |
+
$newOTLdata[0]['general_category'] = $this->OTLdata[$pos]['general_category'];
|
2395 |
+
|
2396 |
+
$newOTLdata[0]['bidi_type'] = $bt;
|
2397 |
+
$newOTLdata[0]['group'] = $gp;
|
2398 |
+
|
2399 |
+
// KASHIDA: If forming a ligature when the last component was identified as a kashida point (final form)
|
2400 |
+
// If previous/first component of ligature is a medial form, then keep this as a kashida point
|
2401 |
+
// TEST (Arabic Typesetting) يَنتُم
|
2402 |
+
$ka = 0;
|
2403 |
+
if (isset($this->OTLdata[$GlyphPos[(count($GlyphPos)-1)]]['GPOSinfo']['kashida'])) {
|
2404 |
+
$ka = $this->OTLdata[$GlyphPos[(count($GlyphPos)-1)]]['GPOSinfo']['kashida'];
|
2405 |
+
}
|
2406 |
+
if ($ka==1 && isset($this->OTLdata[$pos]['form']) && $this->OTLdata[$pos]['form']==3) { $newOTLdata[0]['GPOSinfo']['kashida'] = $ka; }
|
2407 |
+
|
2408 |
+
$newOTLdata[0]['uni'] = $substitute;
|
2409 |
+
$newOTLdata[0]['hex'] = $this->unicode_hex($substitute);
|
2410 |
+
|
2411 |
+
if ($this->shaper=='I' || $this->shaper=='K' || $this->shaper=='S') {
|
2412 |
+
$newOTLdata[0]['indic_category'] = $this->OTLdata[$pos]['indic_category'];
|
2413 |
+
$newOTLdata[0]['indic_position'] = $this->OTLdata[$pos]['indic_position'];
|
2414 |
+
}
|
2415 |
+
else if ($this->shaper=='M') {
|
2416 |
+
$newOTLdata[0]['myanmar_category'] = $this->OTLdata[$pos]['myanmar_category'];
|
2417 |
+
$newOTLdata[0]['myanmar_position'] = $this->OTLdata[$pos]['myanmar_position'];
|
2418 |
+
}
|
2419 |
+
if (isset($this->OTLdata[$pos]['mask'])) {$newOTLdata[0]['mask'] = $this->OTLdata[$pos]['mask']; }
|
2420 |
+
if (isset($this->OTLdata[$pos]['syllable'])) {$newOTLdata[0]['syllable'] = $this->OTLdata[$pos]['syllable']; }
|
2421 |
+
|
2422 |
+
$newOTLdata[0]['is_ligature'] = true;
|
2423 |
+
|
2424 |
+
|
2425 |
+
array_splice($this->OTLdata, $pos, 1, $newOTLdata);
|
2426 |
+
|
2427 |
+
// GlyphPos contains array of arr_pos to set null - not necessarily contiguous
|
2428 |
+
|
2429 |
+
// +- Remove any assocMarks or assocLigs from the main components (the ones that are deleted)
|
2430 |
+
for($i=count($GlyphPos)-1;$i>0;$i--) {
|
2431 |
+
$gpos = $GlyphPos[$i];
|
2432 |
+
array_splice($this->OTLdata, $gpos, 1);
|
2433 |
+
unset($this->assocLigs[$gpos]);
|
2434 |
+
unset($this->assocMarks[$gpos]);
|
2435 |
+
}
|
2436 |
+
// $this->assocLigs = array(); // Ligatures[$posarr lpos] => nc
|
2437 |
+
// $this->assocMarks = array(); // assocMarks[$posarr mpos] => array(compID, ligPos)
|
2438 |
+
|
2439 |
+
// Update position of pre-existing Ligatures and associated Marks
|
2440 |
+
// Start after first GlyphPos
|
2441 |
+
// count($GlyphPos)-1 is the number of glyphs removed from string
|
2442 |
+
for($p=($GlyphPos[0]+1);$p<(count($this->OTLdata)+count($GlyphPos)-1) ;$p++ ) {
|
2443 |
+
$nrem = 0; // Number of Glyphs removed at this point in the string
|
2444 |
+
for($i=0;$i<count($GlyphPos);$i++) {
|
2445 |
+
if ($i>0 && $p > $GlyphPos[$i]) { $nrem++; }
|
2446 |
+
}
|
2447 |
+
if (isset($this->assocLigs[$p])) {
|
2448 |
+
$tmp = $this->assocLigs[$p];
|
2449 |
+
unset($this->assocLigs[$p]);
|
2450 |
+
$this->assocLigs[($p - $nrem)] = $tmp;
|
2451 |
+
}
|
2452 |
+
if (isset($this->assocMarks[$p])) {
|
2453 |
+
$tmp = $this->assocMarks[$p];
|
2454 |
+
unset($this->assocMarks[$p]);
|
2455 |
+
if ($tmp['ligPos'] > $GlyphPos[0]) { $tmp['ligPos'] -= $nrem; }
|
2456 |
+
$this->assocMarks[($p - $nrem)] = $tmp;
|
2457 |
+
}
|
2458 |
+
}
|
2459 |
+
return 1;
|
2460 |
+
}
|
2461 |
+
else { return 0; }
|
2462 |
+
}
|
2463 |
+
|
2464 |
+
|
2465 |
+
|
2466 |
+
////////////////////////////////////////////////////////////////
|
2467 |
+
////////////////////////////////////////////////////////////////
|
2468 |
+
////////// ARABIC /////////////////////////////////
|
2469 |
+
////////////////////////////////////////////////////////////////
|
2470 |
+
////////////////////////////////////////////////////////////////
|
2471 |
+
|
2472 |
+
function arabic_initialise() {
|
2473 |
+
// cf. http://unicode.org/Public/UNIDATA/ArabicShaping.txt
|
2474 |
+
// http://unicode.org/Public/UNIDATA/extracted/DerivedJoiningType.txt
|
2475 |
+
// JOIN TO FOLLOWING LETTER IN LOGICAL ORDER (i.e. AS INITIAL/MEDIAL FORM) = Unicode Left-Joining (+ Dual-Joining + Join_Causing 00640)
|
2476 |
+
$this->arabLeftJoining = array(
|
2477 |
+
0x0620=>1, 0x0626=>1, 0x0628=>1, 0x062A=>1, 0x062B=>1, 0x062C=>1, 0x062D=>1, 0x062E=>1,
|
2478 |
+
0x0633=>1, 0x0634=>1, 0x0635=>1, 0x0636=>1, 0x0637=>1, 0x0638=>1, 0x0639=>1, 0x063A=>1,
|
2479 |
+
0x063B=>1, 0x063C=>1, 0x063D=>1, 0x063E=>1, 0x063F=>1, 0x0640=>1, 0x0641=>1, 0x0642=>1,
|
2480 |
+
0x0643=>1, 0x0644=>1, 0x0645=>1, 0x0646=>1, 0x0647=>1, 0x0649=>1, 0x064A=>1, 0x066E=>1,
|
2481 |
+
0x066F=>1, 0x0678=>1, 0x0679=>1, 0x067A=>1, 0x067B=>1, 0x067C=>1, 0x067D=>1, 0x067E=>1,
|
2482 |
+
0x067F=>1, 0x0680=>1, 0x0681=>1, 0x0682=>1, 0x0683=>1, 0x0684=>1, 0x0685=>1, 0x0686=>1,
|
2483 |
+
0x0687=>1, 0x069A=>1, 0x069B=>1, 0x069C=>1, 0x069D=>1, 0x069E=>1, 0x069F=>1, 0x06A0=>1,
|
2484 |
+
0x06A1=>1, 0x06A2=>1, 0x06A3=>1, 0x06A4=>1, 0x06A5=>1, 0x06A6=>1, 0x06A7=>1, 0x06A8=>1,
|
2485 |
+
0x06A9=>1, 0x06AA=>1, 0x06AB=>1, 0x06AC=>1, 0x06AD=>1, 0x06AE=>1, 0x06AF=>1, 0x06B0=>1,
|
2486 |
+
0x06B1=>1, 0x06B2=>1, 0x06B3=>1, 0x06B4=>1, 0x06B5=>1, 0x06B6=>1, 0x06B7=>1, 0x06B8=>1,
|
2487 |
+
0x06B9=>1, 0x06BA=>1, 0x06BB=>1, 0x06BC=>1, 0x06BD=>1, 0x06BE=>1, 0x06BF=>1, 0x06C1=>1,
|
2488 |
+
0x06C2=>1, 0x06CC=>1, 0x06CE=>1, 0x06D0=>1, 0x06D1=>1, 0x06FA=>1, 0x06FB=>1, 0x06FC=>1,
|
2489 |
+
0x06FF=>1,
|
2490 |
+
/* Arabic Supplement */
|
2491 |
+
0x0750=>1, 0x0751=>1, 0x0752=>1, 0x0753=>1, 0x0754=>1, 0x0755=>1, 0x0756=>1, 0x0757=>1,
|
2492 |
+
0x0758=>1, 0x075C=>1, 0x075D=>1, 0x075E=>1, 0x075F=>1, 0x0760=>1, 0x0761=>1, 0x0762=>1,
|
2493 |
+
0x0763=>1, 0x0764=>1, 0x0765=>1, 0x0766=>1, 0x0767=>1, 0x0768=>1, 0x0769=>1, 0x076A=>1,
|
2494 |
+
0x076D=>1, 0x076E=>1, 0x076F=>1, 0x0770=>1, 0x0772=>1, 0x0775=>1, 0x0776=>1, 0x0777=>1,
|
2495 |
+
0x077A=>1, 0x077B=>1, 0x077C=>1, 0x077D=>1, 0x077E=>1, 0x077F=>1,
|
2496 |
+
/* Extended Arabic */
|
2497 |
+
0x08A0=>1, 0x08A2=>1, 0x08A3=>1, 0x08A4=>1, 0x08A5=>1, 0x08A6=>1, 0x08A7=>1, 0x08A8=>1,
|
2498 |
+
0x08A9=>1,
|
2499 |
+
/* 'syrc' Syriac */
|
2500 |
+
0x0712=>1, 0x0713=>1, 0x0714=>1, 0x071A=>1, 0x071B=>1, 0x071C=>1, 0x071D=>1, 0x071F=>1,
|
2501 |
+
0x0720=>1, 0x0721=>1, 0x0722=>1, 0x0723=>1, 0x0724=>1, 0x0725=>1, 0x0726=>1, 0x0727=>1,
|
2502 |
+
0x0729=>1, 0x072B=>1, 0x072D=>1, 0x072E=>1, 0x074E=>1, 0x074F=>1,
|
2503 |
+
/* N'Ko */
|
2504 |
+
0x07CA=>1, 0x07CB=>1, 0x07CC=>1, 0x07CD=>1, 0x07CE=>1, 0x07CF=>1, 0x07D0=>1, 0x07D1=>1,
|
2505 |
+
0x07D2=>1, 0x07D3=>1, 0x07D4=>1, 0x07D5=>1, 0x07D6=>1, 0x07D7=>1, 0x07D8=>1, 0x07D9=>1,
|
2506 |
+
0x07DA=>1, 0x07DB=>1, 0x07DC=>1, 0x07DD=>1, 0x07DE=>1, 0x07DF=>1, 0x07E0=>1, 0x07E1=>1,
|
2507 |
+
0x07E2=>1, 0x07E3=>1, 0x07E4=>1, 0x07E5=>1, 0x07E6=>1, 0x07E7=>1, 0x07E8=>1, 0x07E9=>1,
|
2508 |
+
0x07EA=>1, 0x07FA=>1,
|
2509 |
+
/* Mandaic */
|
2510 |
+
0x0841=>1, 0x0842=>1, 0x0843=>1, 0x0844=>1, 0x0845=>1, 0x0847=>1, 0x0848=>1, 0x084A=>1,
|
2511 |
+
0x084B=>1, 0x084C=>1, 0x084D=>1, 0x084E=>1, 0x0850=>1, 0x0851=>1, 0x0852=>1, 0x0853=>1,
|
2512 |
+
0x0855=>1,
|
2513 |
+
/* ZWJ U+200D */
|
2514 |
+
0x0200D=>1);
|
2515 |
+
|
2516 |
+
/* JOIN TO PREVIOUS LETTER IN LOGICAL ORDER (i.e. AS FINAL/MEDIAL FORM) = Unicode Right-Joining (+ Dual-Joining + Join_Causing) */
|
2517 |
+
$this->arabRightJoining = array(
|
2518 |
+
0x0620=>1, 0x0622=>1, 0x0623=>1, 0x0624=>1, 0x0625=>1, 0x0626=>1, 0x0627=>1, 0x0628=>1,
|
2519 |
+
0x0629=>1, 0x062A=>1, 0x062B=>1, 0x062C=>1, 0x062D=>1, 0x062E=>1, 0x062F=>1, 0x0630=>1,
|
2520 |
+
0x0631=>1, 0x0632=>1, 0x0633=>1, 0x0634=>1, 0x0635=>1, 0x0636=>1, 0x0637=>1, 0x0638=>1,
|
2521 |
+
0x0639=>1, 0x063A=>1, 0x063B=>1, 0x063C=>1, 0x063D=>1, 0x063E=>1, 0x063F=>1, 0x0640=>1,
|
2522 |
+
0x0641=>1, 0x0642=>1, 0x0643=>1, 0x0644=>1, 0x0645=>1, 0x0646=>1, 0x0647=>1, 0x0648=>1,
|
2523 |
+
0x0649=>1, 0x064A=>1, 0x066E=>1, 0x066F=>1, 0x0671=>1, 0x0672=>1, 0x0673=>1, 0x0675=>1,
|
2524 |
+
0x0676=>1, 0x0677=>1, 0x0678=>1, 0x0679=>1, 0x067A=>1, 0x067B=>1, 0x067C=>1, 0x067D=>1,
|
2525 |
+
0x067E=>1, 0x067F=>1, 0x0680=>1, 0x0681=>1, 0x0682=>1, 0x0683=>1, 0x0684=>1, 0x0685=>1,
|
2526 |
+
0x0686=>1, 0x0687=>1, 0x0688=>1, 0x0689=>1, 0x068A=>1, 0x068B=>1, 0x068C=>1, 0x068D=>1,
|
2527 |
+
0x068E=>1, 0x068F=>1, 0x0690=>1, 0x0691=>1, 0x0692=>1, 0x0693=>1, 0x0694=>1, 0x0695=>1,
|
2528 |
+
0x0696=>1, 0x0697=>1, 0x0698=>1, 0x0699=>1, 0x069A=>1, 0x069B=>1, 0x069C=>1, 0x069D=>1,
|
2529 |
+
0x069E=>1, 0x069F=>1, 0x06A0=>1, 0x06A1=>1, 0x06A2=>1, 0x06A3=>1, 0x06A4=>1, 0x06A5=>1,
|
2530 |
+
0x06A6=>1, 0x06A7=>1, 0x06A8=>1, 0x06A9=>1, 0x06AA=>1, 0x06AB=>1, 0x06AC=>1, 0x06AD=>1,
|
2531 |
+
0x06AE=>1, 0x06AF=>1, 0x06B0=>1, 0x06B1=>1, 0x06B2=>1, 0x06B3=>1, 0x06B4=>1, 0x06B5=>1,
|
2532 |
+
0x06B6=>1, 0x06B7=>1, 0x06B8=>1, 0x06B9=>1, 0x06BA=>1, 0x06BB=>1, 0x06BC=>1, 0x06BD=>1,
|
2533 |
+
0x06BE=>1, 0x06BF=>1, 0x06C0=>1, 0x06C1=>1, 0x06C2=>1, 0x06C3=>1, 0x06C4=>1, 0x06C5=>1,
|
2534 |
+
0x06C6=>1, 0x06C7=>1, 0x06C8=>1, 0x06C9=>1, 0x06CA=>1, 0x06CB=>1, 0x06CC=>1, 0x06CD=>1,
|
2535 |
+
0x06CE=>1, 0x06CF=>1, 0x06D0=>1, 0x06D1=>1, 0x06D2=>1, 0x06D3=>1, 0x06D5=>1, 0x06EE=>1,
|
2536 |
+
0x06EF=>1, 0x06FA=>1, 0x06FB=>1, 0x06FC=>1, 0x06FF=>1,
|
2537 |
+
/* Arabic Supplement */
|
2538 |
+
0x0750=>1, 0x0751=>1, 0x0752=>1, 0x0753=>1, 0x0754=>1, 0x0755=>1, 0x0756=>1, 0x0757=>1,
|
2539 |
+
0x0758=>1, 0x0759=>1, 0x075A=>1, 0x075B=>1, 0x075C=>1, 0x075D=>1, 0x075E=>1, 0x075F=>1,
|
2540 |
+
0x0760=>1, 0x0761=>1, 0x0762=>1, 0x0763=>1, 0x0764=>1, 0x0765=>1, 0x0766=>1, 0x0767=>1,
|
2541 |
+
0x0768=>1, 0x0769=>1, 0x076A=>1, 0x076B=>1, 0x076C=>1, 0x076D=>1, 0x076E=>1, 0x076F=>1,
|
2542 |
+
0x0770=>1, 0x0771=>1, 0x0772=>1, 0x0773=>1, 0x0774=>1, 0x0775=>1, 0x0776=>1, 0x0777=>1,
|
2543 |
+
0x0778=>1, 0x0779=>1, 0x077A=>1, 0x077B=>1, 0x077C=>1, 0x077D=>1, 0x077E=>1, 0x077F=>1,
|
2544 |
+
/* Extended Arabic */
|
2545 |
+
0x08A0=>1, 0x08A2=>1, 0x08A3=>1, 0x08A4=>1, 0x08A5=>1, 0x08A6=>1, 0x08A7=>1, 0x08A8=>1,
|
2546 |
+
0x08A9=>1, 0x08AA=>1, 0x08AB=>1, 0x08AC=>1,
|
2547 |
+
/* 'syrc' Syriac */
|
2548 |
+
0x0710=>1, 0x0712=>1, 0x0713=>1, 0x0714=>1, 0x0715=>1, 0x0716=>1, 0x0717=>1, 0x0718=>1,
|
2549 |
+
0x0719=>1, 0x071A=>1, 0x071B=>1, 0x071C=>1, 0x071D=>1, 0x071E=>1, 0x071F=>1, 0x0720=>1,
|
2550 |
+
0x0721=>1, 0x0722=>1, 0x0723=>1, 0x0724=>1, 0x0725=>1, 0x0726=>1, 0x0727=>1, 0x0728=>1,
|
2551 |
+
0x0729=>1, 0x072A=>1, 0x072B=>1, 0x072C=>1, 0x072D=>1, 0x072E=>1, 0x072F=>1, 0x074D=>1,
|
2552 |
+
0x074E=>1, 0x074F,
|
2553 |
+
/* N'Ko */
|
2554 |
+
0x07CA=>1, 0x07CB=>1, 0x07CC=>1, 0x07CD=>1, 0x07CE=>1, 0x07CF=>1, 0x07D0=>1, 0x07D1=>1,
|
2555 |
+
0x07D2=>1, 0x07D3=>1, 0x07D4=>1, 0x07D5=>1, 0x07D6=>1, 0x07D7=>1, 0x07D8=>1, 0x07D9=>1,
|
2556 |
+
0x07DA=>1, 0x07DB=>1, 0x07DC=>1, 0x07DD=>1, 0x07DE=>1, 0x07DF=>1, 0x07E0=>1, 0x07E1=>1,
|
2557 |
+
0x07E2=>1, 0x07E3=>1, 0x07E4=>1, 0x07E5=>1, 0x07E6=>1, 0x07E7=>1, 0x07E8=>1, 0x07E9=>1,
|
2558 |
+
0x07EA=>1, 0x07FA=>1,
|
2559 |
+
/* Mandaic */
|
2560 |
+
0x0841=>1, 0x0842=>1, 0x0843=>1, 0x0844=>1, 0x0845=>1, 0x0847=>1, 0x0848=>1, 0x084A=>1,
|
2561 |
+
0x084B=>1, 0x084C=>1, 0x084D=>1, 0x084E=>1, 0x0850=>1, 0x0851=>1, 0x0852=>1, 0x0853=>1,
|
2562 |
+
0x0855=>1,
|
2563 |
+
0x0840=>1, 0x0846=>1, 0x0849=>1, 0x084F=>1, 0x0854=>1, /* Right joining */
|
2564 |
+
/* ZWJ U+200D */
|
2565 |
+
0x0200D=>1);
|
2566 |
+
|
2567 |
+
|
2568 |
+
/* VOWELS = TRANSPARENT-JOINING = Unicode Transparent-Joining type (not just vowels) */
|
2569 |
+
$this->arabTransparent = array(
|
2570 |
+
0x0610=>1, 0x0611=>1, 0x0612=>1, 0x0613=>1, 0x0614=>1, 0x0615=>1, 0x0616=>1, 0x0617=>1,
|
2571 |
+
0x0618=>1, 0x0619=>1, 0x061A=>1, 0x064B=>1, 0x064C=>1, 0x064D=>1, 0x064E=>1, 0x064F=>1,
|
2572 |
+
0x0650=>1, 0x0651=>1, 0x0652=>1, 0x0653=>1, 0x0654=>1, 0x0655=>1, 0x0656=>1, 0x0657=>1,
|
2573 |
+
0x0658=>1, 0x0659=>1, 0x065A=>1, 0x065B=>1, 0x065C=>1, 0x065D=>1, 0x065E=>1, 0x065F=>1,
|
2574 |
+
0x0670=>1, 0x06D6=>1, 0x06D7=>1, 0x06D8=>1, 0x06D9=>1, 0x06DA=>1, 0x06DB=>1, 0x06DC=>1,
|
2575 |
+
0x06DF=>1, 0x06E0=>1, 0x06E1=>1, 0x06E2=>1, 0x06E3=>1, 0x06E4=>1, 0x06E7=>1, 0x06E8=>1,
|
2576 |
+
0x06EA=>1, 0x06EB=>1, 0x06EC=>1, 0x06ED=>1,
|
2577 |
+
/* Extended Arabic */
|
2578 |
+
0x08E4=>1, 0x08E5=>1, 0x08E6=>1, 0x08E7=>1, 0x08E8=>1, 0x08E9=>1, 0x08EA=>1, 0x08EB=>1,
|
2579 |
+
0x08EC=>1, 0x08ED=>1, 0x08EE=>1, 0x08EF=>1, 0x08F0=>1, 0x08F1=>1, 0x08F2=>1, 0x08F3=>1,
|
2580 |
+
0x08F4=>1, 0x08F5=>1, 0x08F6=>1, 0x08F7=>1, 0x08F8=>1, 0x08F9=>1, 0x08FA=>1, 0x08FB=>1,
|
2581 |
+
0x08FC=>1, 0x08FD=>1, 0x08FE=>1,
|
2582 |
+
/* Arabic ligatures in presentation form (converted in 'ccmp' in e.g. Arial and Times ? need to add others in this range) */
|
2583 |
+
0xFC5E=>1, 0xFC5F=>1, 0xFC60=>1, 0xFC61=>1, 0xFC62=>1,
|
2584 |
+
/* 'syrc' Syriac */
|
2585 |
+
0x070F=>1, 0x0711=>1, 0x0730=>1, 0x0731=>1, 0x0732=>1, 0x0733=>1, 0x0734=>1, 0x0735=>1,
|
2586 |
+
0x0736=>1, 0x0737=>1, 0x0738=>1, 0x0739=>1, 0x073A=>1, 0x073B=>1, 0x073C=>1, 0x073D=>1,
|
2587 |
+
0x073E=>1, 0x073F=>1, 0x0740=>1, 0x0741=>1, 0x0742=>1, 0x0743=>1, 0x0744=>1, 0x0745=>1,
|
2588 |
+
0x0746=>1, 0x0747=>1, 0x0748=>1, 0x0749=>1, 0x074A=>1,
|
2589 |
+
/* N'Ko */
|
2590 |
+
0x07EB=>1, 0x07EC=>1, 0x07ED=>1, 0x07EE=>1, 0x07EF=>1, 0x07F0=>1, 0x07F1=>1, 0x07F2=>1,
|
2591 |
+
0x07F3=>1,
|
2592 |
+
/* Mandaic */
|
2593 |
+
0x0859=>1, 0x085A=>1, 0x085B=>1,
|
2594 |
+
);
|
2595 |
+
|
2596 |
+
}
|
2597 |
+
|
2598 |
+
|
2599 |
+
function arabic_shaper($usetags, $scriptTag) {
|
2600 |
+
$chars = array();
|
2601 |
+
for($i=0;$i<count($this->OTLdata);$i++) {
|
2602 |
+
$chars[] = $this->OTLdata[$i]['hex'];
|
2603 |
+
}
|
2604 |
+
$crntChar = null;
|
2605 |
+
$prevChar = null;
|
2606 |
+
$nextChar = null;
|
2607 |
+
$output = array();
|
2608 |
+
$max = count($chars);
|
2609 |
+
for ($i = $max - 1; $i >= 0; $i--) {
|
2610 |
+
$crntChar = $chars[$i];
|
2611 |
+
if ($i > 0){ $prevChar = hexdec($chars[$i - 1]); }
|
2612 |
+
else{ $prevChar = NULL; }
|
2613 |
+
if ($prevChar && isset($this->arabTransparentJoin[$prevChar]) && isset($chars[$i - 2]) ) {
|
2614 |
+
$prevChar = hexdec($chars[$i - 2]);
|
2615 |
+
if ($prevChar && isset($this->arabTransparentJoin[$prevChar]) && isset($chars[$i - 3])) {
|
2616 |
+
$prevChar = hexdec($chars[$i - 3]);
|
2617 |
+
if ($prevChar && isset($this->arabTransparentJoin[$prevChar]) && isset($chars[$i - 4])) {
|
2618 |
+
$prevChar = hexdec($chars[$i - 4]);
|
2619 |
+
}
|
2620 |
+
}
|
2621 |
+
}
|
2622 |
+
if ($crntChar && isset($this->arabTransparentJoin[ hexdec($crntChar)]) ) {
|
2623 |
+
// If next_char = RightJoining && prev_char = LeftJoining:
|
2624 |
+
if (isset($chars[$i + 1]) && $chars[$i + 1] && isset($this->arabRightJoining[ hexdec($chars[$i + 1]) ]) && $prevChar && isset($this->arabLeftJoining[$prevChar])) {
|
2625 |
+
$output[] = $this->get_arab_glyphs($crntChar, 1, $chars, $i, $scriptTag, $usetags); // <final> form
|
2626 |
+
}
|
2627 |
+
else {
|
2628 |
+
$output[] = $this->get_arab_glyphs($crntChar, 0, $chars, $i, $scriptTag, $usetags); // <isolated> form
|
2629 |
+
}
|
2630 |
+
continue;
|
2631 |
+
}
|
2632 |
+
if (hexdec($crntChar) < 128) {
|
2633 |
+
$output[] = array($crntChar,0);
|
2634 |
+
$nextChar = $crntChar;
|
2635 |
+
continue;
|
2636 |
+
}
|
2637 |
+
// 0=ISOLATED FORM :: 1=FINAL :: 2=INITIAL :: 3=MEDIAL
|
2638 |
+
$form = 0;
|
2639 |
+
if ($prevChar && isset($this->arabLeftJoining[$prevChar])) {
|
2640 |
+
$form++;
|
2641 |
+
}
|
2642 |
+
if ($nextChar && isset($this->arabRightJoining[ hexdec($nextChar) ])) {
|
2643 |
+
$form += 2;
|
2644 |
+
}
|
2645 |
+
$output[] = $this->get_arab_glyphs($crntChar, $form, $chars, $i, $scriptTag, $usetags) ;
|
2646 |
+
$nextChar = $crntChar;
|
2647 |
+
}
|
2648 |
+
$ra = array_reverse($output);
|
2649 |
+
for($i=0;$i<count($this->OTLdata);$i++) {
|
2650 |
+
$this->OTLdata[$i]['uni'] = hexdec($ra[$i][0]);
|
2651 |
+
$this->OTLdata[$i]['hex'] = $ra[$i][0];
|
2652 |
+
$this->OTLdata[$i]['form'] = $ra[$i][1]; // Actaul form substituted 0=ISOLATED FORM :: 1=FINAL :: 2=INITIAL :: 3=MEDIAL
|
2653 |
+
}
|
2654 |
+
}
|
2655 |
+
|
2656 |
+
|
2657 |
+
function get_arab_glyphs($char, $type, &$chars, $i, $scriptTag, $usetags) {
|
2658 |
+
|
2659 |
+
// Optional Feature settings // doesn't control Syriac at present
|
2660 |
+
if (($type===0 && strpos($usetags, 'isol')===false) || ($type===1 && strpos($usetags, 'fina')===false) || ($type===2 && strpos($usetags, 'init')===false) || ($type===3 && strpos($usetags, 'medi')===false)) {
|
2661 |
+
return array($char,0);
|
2662 |
+
}
|
2663 |
+
|
2664 |
+
// 0=ISOLATED FORM :: 1=FINAL :: 2=INITIAL :: 3=MEDIAL (:: 4=MED2 :: 5=FIN2 :: 6=FIN3)
|
2665 |
+
$retk = -1;
|
2666 |
+
// Alaph 00710 in Syriac
|
2667 |
+
if ($scriptTag=='syrc' && $char=='00710') {
|
2668 |
+
// if there is a preceding (base?) character *** should search back to previous base - ignoring vowels and change $n
|
2669 |
+
// set $n as the position of the last base; for now we'll just do this:
|
2670 |
+
$n = $i-1;
|
2671 |
+
// if the preceding (base) character cannot be joined to
|
2672 |
+
// not in $this->arabLeftJoining i.e. not a char which can join to the next one
|
2673 |
+
if (isset($chars[$n]) && isset($this->arabLeftJoining[hexdec($chars[$n])])) {
|
2674 |
+
// if in the middle of Syriac words
|
2675 |
+
if (isset($chars[$i+1]) && preg_match('/[\x{0700}-\x{0745}]/u',code2utf(hexdec($chars[$n]))) && preg_match('/[\x{0700}-\x{0745}]/u',code2utf(hexdec($chars[$i+1]))) && isset($this->arabGlyphs[$char][4])) { $retk = 4; }
|
2676 |
+
// if at the end of Syriac words
|
2677 |
+
else if(!isset($chars[$i+1]) || !preg_match('/[\x{0700}-\x{0745}]/u',code2utf(hexdec($chars[$i+1])))) {
|
2678 |
+
// if preceding base character IS (00715|00716|0072A)
|
2679 |
+
if (strpos('0715|0716|072A',$chars[$n])!==false && isset($this->arabGlyphs[$char][6])) { $retk = 6; }
|
2680 |
+
|
2681 |
+
// else if preceding base character is NOT (00715|00716|0072A)
|
2682 |
+
else if (isset($this->arabGlyphs[$char][5])) { $retk = 5; }
|
2683 |
+
}
|
2684 |
+
}
|
2685 |
+
if ($retk != -1) {
|
2686 |
+
return array($this->arabGlyphs[$char][$retk],$retk);
|
2687 |
+
}
|
2688 |
+
else { return array($char,0); }
|
2689 |
+
}
|
2690 |
+
|
2691 |
+
if (($type>0 || $type===0) && isset($this->arabGlyphs[$char][$type])) {
|
2692 |
+
$retk = $type;
|
2693 |
+
}
|
2694 |
+
else if ($type==3 && isset($this->arabGlyphs[$char][1])) { // if <medial> not defined, but <final>, return <final>
|
2695 |
+
$retk = 1;
|
2696 |
+
}
|
2697 |
+
else if ($type==2 && isset($this->arabGlyphs[$char][0])) { // if <initial> not defined, but <isolated>, return <isolated>
|
2698 |
+
$retk = 0;
|
2699 |
+
}
|
2700 |
+
if ($retk != -1) {
|
2701 |
+
$match = true;
|
2702 |
+
// If GSUB includes a Backtrack or Lookahead condition (e.g. font ArabicTypesetting)
|
2703 |
+
if (isset($this->arabGlyphs[$char]['prel'][$retk]) && $this->arabGlyphs[$char]['prel'][$retk]) {
|
2704 |
+
$ig = 1;
|
2705 |
+
foreach($this->arabGlyphs[$char]['prel'][$retk] AS $k=>$v) { // $k starts 0, 1...
|
2706 |
+
if (!isset($chars[$i-$ig-$k])) { $match = false; }
|
2707 |
+
else if (strpos($v,$chars[$i-$ig-$k])===false) {
|
2708 |
+
while (strpos($this->arabGlyphs[$char]['ignore'][$retk],$chars[$i-$ig-$k])!==false) { // ignore
|
2709 |
+
$ig++;
|
2710 |
+
}
|
2711 |
+
if (!isset($chars[$i-$ig-$k])) { $match = false; }
|
2712 |
+
else if (strpos($v,$chars[$i-$ig-$k])===false) { $match = false; }
|
2713 |
+
}
|
2714 |
+
}
|
2715 |
+
}
|
2716 |
+
if (isset($this->arabGlyphs[$char]['postl'][$retk]) && $this->arabGlyphs[$char]['postl'][$retk]) {
|
2717 |
+
$ig = 1;
|
2718 |
+
foreach($this->arabGlyphs[$char]['postl'][$retk] AS $k=>$v) { // $k starts 0, 1...
|
2719 |
+
if (!isset($chars[$i+$ig+$k])) { $match = false; }
|
2720 |
+
else if (strpos($v,$chars[$i+$ig+$k])===false) {
|
2721 |
+
while (strpos($this->arabGlyphs[$char]['ignore'][$retk],$chars[$i+$ig+$k])!==false) { // ignore
|
2722 |
+
$ig++;
|
2723 |
+
}
|
2724 |
+
if (!isset($chars[$i+$ig+$k])) { $match = false; }
|
2725 |
+
else if (strpos($v,$chars[$i+$ig+$k])===false) { $match = false; }
|
2726 |
+
}
|
2727 |
+
}
|
2728 |
+
}
|
2729 |
+
if ($match) {
|
2730 |
+
return array($this->arabGlyphs[$char][$retk],$retk);
|
2731 |
+
}
|
2732 |
+
else { return array($char,0); }
|
2733 |
+
}
|
2734 |
+
else { return array($char,0); }
|
2735 |
+
}
|
2736 |
+
|
2737 |
+
|
2738 |
+
////////////////////////////////////////////////////////////////
|
2739 |
+
////////////////////////////////////////////////////////////////
|
2740 |
+
///////////////// LINE BREAKING ///////////////////////
|
2741 |
+
////////////////////////////////////////////////////////////////
|
2742 |
+
////////////////////////////////////////////////////////////////
|
2743 |
+
|
2744 |
+
////////////////////////////////////////////////////////////////
|
2745 |
+
///////////// TIBETAN LINE BREAKING ///////////////////
|
2746 |
+
////////////////////////////////////////////////////////////////
|
2747 |
+
// Sets $this->OTLdata[$i]['wordend']=true at possible end of word boundaries
|
2748 |
+
function TibetanlineBreaking() {
|
2749 |
+
for ($ptr=0; $ptr<count($this->OTLdata);$ptr++) {
|
2750 |
+
// Break opportunities at U+0F0B Tsheg or U=0F0D
|
2751 |
+
if (isset($this->OTLdata[$ptr]['uni']) && ($this->OTLdata[$ptr]['uni']==0x0F0B || $this->OTLdata[$ptr]['uni']==0x0F0D)) {
|
2752 |
+
if (isset($this->OTLdata[$ptr+1]['uni']) && ($this->OTLdata[$ptr+1]['uni']==0x0F0D || $this->OTLdata[$ptr+1]['uni']==0xF0E)) { continue; }
|
2753 |
+
// Set end of word marker in OTLdata at matchpos
|
2754 |
+
$this->OTLdata[$ptr]['wordend'] = true;
|
2755 |
+
}
|
2756 |
+
}
|
2757 |
+
|
2758 |
+
}
|
2759 |
+
|
2760 |
+
////////////////////////////////////////////////////////////////
|
2761 |
+
////////// SOUTH EAST ASIAN LINE BREAKING /////////////
|
2762 |
+
////////////////////////////////////////////////////////////////
|
2763 |
+
// South East Asian Linebreaking (Thai, Khmer and Lao) using dictionary of words
|
2764 |
+
// Sets $this->OTLdata[$i]['wordend']=true at possible end of word boundaries
|
2765 |
+
function SEAlineBreaking() {
|
2766 |
+
// Load Line-breaking dictionary
|
2767 |
+
if (!isset($this->lbdicts[$this->shaper]) && file_exists(_MPDF_PATH.'includes/linebrdict'.$this->shaper.'.dat')) { $this->lbdicts[$this->shaper] = file_get_contents(_MPDF_PATH.'includes/linebrdict'.$this->shaper.'.dat'); }
|
2768 |
+
|
2769 |
+
$dict =&$this->lbdicts[$this->shaper];
|
2770 |
+
|
2771 |
+
// Find all word boundaries and mark end of word $this->OTLdata[$i]['wordend']=true on last character
|
2772 |
+
// If Thai, allow for possible suffixes (not in Lao or Khmer)
|
2773 |
+
|
2774 |
+
// repeater/ellision characters
|
2775 |
+
// (0x0E2F); // Ellision character THAI_PAIYANNOI 0x0E2F UTF-8 0xE0 0xB8 0xAF
|
2776 |
+
// (0x0E46); // Repeat character THAI_MAIYAMOK 0x0E46 UTF-8 0xE0 0xB9 0x86
|
2777 |
+
// (0x0EC6); // Repeat character LAO UTF-8 0xE0 0xBB 0x86
|
2778 |
+
|
2779 |
+
$rollover = array();
|
2780 |
+
$ptr = 0;
|
2781 |
+
while ($ptr<count($this->OTLdata)-3) {
|
2782 |
+
if (count($rollover)) {
|
2783 |
+
$matches = $rollover;
|
2784 |
+
$rollover = array();
|
2785 |
+
}
|
2786 |
+
else {
|
2787 |
+
$matches = $this->checkwordmatch($dict, $ptr);
|
2788 |
+
}
|
2789 |
+
if (count($matches)==1) {
|
2790 |
+
$matchpos = $matches[0];
|
2791 |
+
// Check for repeaters - if so $matchpos++
|
2792 |
+
if (isset($this->OTLdata[$matchpos+1]['uni']) && ($this->OTLdata[$matchpos+1]['uni']==0x0E2F || $this->OTLdata[$matchpos+1]['uni']==0x0E46 || $this->OTLdata[$matchpos+1]['uni']==0x0EC6)) { $matchpos++; }
|
2793 |
+
// Set end of word marker in OTLdata at matchpos
|
2794 |
+
$this->OTLdata[$matchpos]['wordend'] = true;
|
2795 |
+
$ptr = $matchpos + 1;
|
2796 |
+
}
|
2797 |
+
else if (empty($matches)) {
|
2798 |
+
$ptr++;
|
2799 |
+
// Move past any ASCII characters
|
2800 |
+
while (isset($this->OTLdata[$ptr]['uni']) && ($this->OTLdata[$ptr]['uni'] >> 8) == 0) { $ptr++; }
|
2801 |
+
}
|
2802 |
+
else { // Multiple matches
|
2803 |
+
$secondmatch = false;
|
2804 |
+
for ($m=count($matches)-1;$m>=0;$m--) {
|
2805 |
+
//for ($m=0;$m<count($matches);$m++) {
|
2806 |
+
$firstmatch = $matches[$m];
|
2807 |
+
$matches2 = $this->checkwordmatch($dict, $firstmatch+1);
|
2808 |
+
if (count($matches2)) {
|
2809 |
+
// Set end of word marker in OTLdata at matchpos
|
2810 |
+
$this->OTLdata[$firstmatch]['wordend'] = true;
|
2811 |
+
$ptr = $firstmatch + 1;
|
2812 |
+
$rollover = $matches2;
|
2813 |
+
$secondmatch = true;
|
2814 |
+
break;
|
2815 |
+
}
|
2816 |
+
}
|
2817 |
+
if (!$secondmatch) {
|
2818 |
+
// Set end of word marker in OTLdata at end of longest first match
|
2819 |
+
$this->OTLdata[$matches[count($matches)-1]]['wordend'] = true;
|
2820 |
+
$ptr = $matches[count($matches)-1] + 1;
|
2821 |
+
// Move past any ASCII characters
|
2822 |
+
while (isset($this->OTLdata[$ptr]['uni']) && ($this->OTLdata[$ptr]['uni'] >> 8) == 0) { $ptr++; }
|
2823 |
+
}
|
2824 |
+
}
|
2825 |
+
|
2826 |
+
}
|
2827 |
+
|
2828 |
+
}
|
2829 |
+
|
2830 |
+
function checkwordmatch(&$dict, $ptr) {
|
2831 |
+
/*
|
2832 |
+
define("_DICT_NODE_TYPE_SPLIT", 0x01);
|
2833 |
+
define("_DICT_NODE_TYPE_LINEAR", 0x02);
|
2834 |
+
define("_DICT_INTERMEDIATE_MATCH", 0x03);
|
2835 |
+
define("_DICT_FINAL_MATCH", 0x04);
|
2836 |
+
|
2837 |
+
Node type: Split.
|
2838 |
+
Divide at < 98 >= 98
|
2839 |
+
Offset for >= 98 == 79 (long 4-byte unsigned)
|
2840 |
+
|
2841 |
+
Node type: Linear match.
|
2842 |
+
Char = 97
|
2843 |
+
|
2844 |
+
Intermediate match
|
2845 |
+
|
2846 |
+
Final match
|
2847 |
+
*/
|
2848 |
+
|
2849 |
+
$dictptr = 0;
|
2850 |
+
$ok = true;
|
2851 |
+
$matches = array();
|
2852 |
+
while ($ok) {
|
2853 |
+
$x = ord($dict{$dictptr});
|
2854 |
+
$c = $this->OTLdata[$ptr]['uni'] & 0xFF;
|
2855 |
+
if ($x==_DICT_INTERMEDIATE_MATCH) {
|
2856 |
+
//echo "DICT_INTERMEDIATE_MATCH: ".dechex($c).'<br />';
|
2857 |
+
// Do not match if next character in text is a Mark
|
2858 |
+
if (isset($this->OTLdata[$ptr]['uni']) && strpos($this->GlyphClassMarks, $this->OTLdata[$ptr]['hex'])===false) {
|
2859 |
+
$matches[] = $ptr - 1;
|
2860 |
+
}
|
2861 |
+
$dictptr++;
|
2862 |
+
}
|
2863 |
+
else if ($x==_DICT_FINAL_MATCH) {
|
2864 |
+
//echo "DICT_FINAL_MATCH: ".dechex($c).'<br />';
|
2865 |
+
// Do not match if next character in text is a Mark
|
2866 |
+
if (isset($this->OTLdata[$ptr]['uni']) && strpos($this->GlyphClassMarks, $this->OTLdata[$ptr]['hex'])===false) {
|
2867 |
+
$matches[] = $ptr - 1;
|
2868 |
+
}
|
2869 |
+
return $matches;
|
2870 |
+
}
|
2871 |
+
else if ($x==_DICT_NODE_TYPE_LINEAR) {
|
2872 |
+
//echo "DICT_NODE_TYPE_LINEAR: ".dechex($c).'<br />';
|
2873 |
+
$dictptr++;
|
2874 |
+
$m = ord($dict{$dictptr});
|
2875 |
+
if ($c == $m) {
|
2876 |
+
$ptr++;
|
2877 |
+
if ($ptr > count($this->OTLdata)-1) {
|
2878 |
+
$next = ord($dict{$dictptr+1});
|
2879 |
+
if ($next==_DICT_INTERMEDIATE_MATCH || $next==_DICT_FINAL_MATCH) {
|
2880 |
+
// Do not match if next character in text is a Mark
|
2881 |
+
if (isset($this->OTLdata[$ptr]['uni']) && strpos($this->GlyphClassMarks, $this->OTLdata[$ptr]['hex'])===false) {
|
2882 |
+
$matches[] = $ptr - 1;
|
2883 |
+
}
|
2884 |
+
}
|
2885 |
+
return $matches;
|
2886 |
+
}
|
2887 |
+
$dictptr++;
|
2888 |
+
continue;
|
2889 |
+
}
|
2890 |
+
else {
|
2891 |
+
//echo "DICT_NODE_TYPE_LINEAR NOT: ".dechex($c).'<br />';
|
2892 |
+
return $matches;
|
2893 |
+
}
|
2894 |
+
}
|
2895 |
+
else if ($x==_DICT_NODE_TYPE_SPLIT) {
|
2896 |
+
//echo "DICT_NODE_TYPE_SPLIT ON ".dechex($d).": ".dechex($c).'<br />';
|
2897 |
+
$dictptr++;
|
2898 |
+
$d = ord($dict{$dictptr});
|
2899 |
+
if ($c < $d) {
|
2900 |
+
$dictptr += 5;
|
2901 |
+
}
|
2902 |
+
else {
|
2903 |
+
$dictptr++;
|
2904 |
+
// Unsigned long 32-bit offset
|
2905 |
+
$offset = (ord($dict{$dictptr})*16777216) + (ord($dict{$dictptr+1})<<16) + (ord($dict{$dictptr+2})<<8) + ord($dict{$dictptr+3});
|
2906 |
+
$dictptr = $offset;
|
2907 |
+
}
|
2908 |
+
}
|
2909 |
+
else {
|
2910 |
+
//echo "PROBLEM: ".($x).'<br />';
|
2911 |
+
$ok = false; // Something has gone wrong
|
2912 |
+
}
|
2913 |
+
}
|
2914 |
+
|
2915 |
+
return $matches;
|
2916 |
+
}
|
2917 |
+
|
2918 |
+
|
2919 |
+
|
2920 |
+
////////////////////////////////////////////////////////////////
|
2921 |
+
////////////////////////////////////////////////////////////////
|
2922 |
+
////////// GPOS ///////////////////////////////////////
|
2923 |
+
////////////////////////////////////////////////////////////////
|
2924 |
+
////////////////////////////////////////////////////////////////
|
2925 |
+
|
2926 |
+
function _applyGPOSrules($LookupList, $is_old_spec=false) {
|
2927 |
+
foreach($LookupList AS $lu=>$tag) {
|
2928 |
+
$Type = $this->GPOSLookups[$lu]['Type'];
|
2929 |
+
$Flag = $this->GPOSLookups[$lu]['Flag'];
|
2930 |
+
$MarkFilteringSet = '';
|
2931 |
+
if (isset($this->GPOSLookups[$lu]['MarkFilteringSet']))
|
2932 |
+
$MarkFilteringSet = $this->GPOSLookups[$lu]['MarkFilteringSet'];
|
2933 |
+
$ptr = 0;
|
2934 |
+
// Test each glyph sequentially
|
2935 |
+
while($ptr < (count($this->OTLdata))) { // whilst there is another glyph ..0064
|
2936 |
+
$currGlyph = $this->OTLdata[$ptr]['hex'];
|
2937 |
+
$currGID = $this->OTLdata[$ptr]['uni'];
|
2938 |
+
$shift = 1;
|
2939 |
+
foreach($this->GPOSLookups[$lu]['Subtables'] AS $c=>$subtable_offset) {
|
2940 |
+
// NB Coverage only looks at glyphs for position 1 (esp. 7.3 and 8.3)
|
2941 |
+
if (isset($this->LuCoverage[$lu][$c][$currGID])) {
|
2942 |
+
// Get rules from font GPOS subtable
|
2943 |
+
if (isset($this->OTLdata[$ptr]['bidi_type'])) { // No need to check bidi_type - just a check that it exists
|
2944 |
+
$shift = $this->_applyGPOSsubtable($lu, $c, $ptr, $currGlyph, $currGID, ($subtable_offset - $this->GPOS_offset + $this->GSUB_length), $Type, $Flag, $MarkFilteringSet, $this->LuCoverage[$lu][$c], $tag, 0, $is_old_spec);
|
2945 |
+
if ($shift) { break; }
|
2946 |
+
}
|
2947 |
+
}
|
2948 |
+
}
|
2949 |
+
if ($shift == 0) { $shift = 1; }
|
2950 |
+
$ptr += $shift;
|
2951 |
+
|
2952 |
+
}
|
2953 |
+
}
|
2954 |
+
}
|
2955 |
+
|
2956 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2957 |
+
// GPOS Types
|
2958 |
+
// Lookup Type 1: Single Adjustment Positioning Subtable Adjust position of a single glyph
|
2959 |
+
// Lookup Type 2: Pair Adjustment Positioning Subtable Adjust position of a pair of glyphs
|
2960 |
+
// Lookup Type 3: Cursive Attachment Positioning Subtable Attach cursive glyphs
|
2961 |
+
// Lookup Type 4: MarkToBase Attachment Positioning Subtable Attach a combining mark to a base glyph
|
2962 |
+
// Lookup Type 5: MarkToLigature Attachment Positioning Subtable Attach a combining mark to a ligature
|
2963 |
+
// Lookup Type 6: MarkToMark Attachment Positioning Subtable Attach a combining mark to another mark
|
2964 |
+
// Lookup Type 7: Contextual Positioning Subtables Position one or more glyphs in context
|
2965 |
+
// Lookup Type 8: Chaining Contextual Positioning Subtable Position one or more glyphs in chained context
|
2966 |
+
// Lookup Type 9: Extension positioning
|
2967 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2968 |
+
function _applyGPOSvaluerecord($basepos,$Value) {
|
2969 |
+
|
2970 |
+
// If current glyph is a mark with a defined width, any XAdvance is considered to REPLACE the character Advance Width
|
2971 |
+
// Test case <div style="font-family:myanmartext">င်္က္ကျြွေိ</div>
|
2972 |
+
if (strpos($this->GlyphClassMarks, $this->OTLdata[$basepos]['hex'])!==false) {
|
2973 |
+
$cw = round($this->mpdf->_getCharWidth($this->mpdf->CurrentFont['cw'], $this->OTLdata[$basepos]['uni']) * $this->mpdf->CurrentFont['unitsPerEm'] / 1000); // convert back to font design units
|
2974 |
+
}
|
2975 |
+
else {
|
2976 |
+
$cw = 0;
|
2977 |
+
}
|
2978 |
+
|
2979 |
+
$apos = $this->_getXAdvancePos($basepos);
|
2980 |
+
|
2981 |
+
if (isset($Value['XAdvance']) && ($Value['XAdvance']-$cw) != 0) {
|
2982 |
+
// However DON'T REPLACE the character Advance Width if Advance Width is negative
|
2983 |
+
// Test case <div style="font-family: dejavusansmono">ру́сский</div>
|
2984 |
+
if ($Value['XAdvance'] < 0) { $cw = 0; }
|
2985 |
+
|
2986 |
+
// For LTR apply XAdvanceL to the last mark following the base = at $apos
|
2987 |
+
// For RTL apply XAdvanceR to base = at $basepos
|
2988 |
+
if (isset($this->OTLdata[$apos]['GPOSinfo']['XAdvanceL'])) { $this->OTLdata[$apos]['GPOSinfo']['XAdvanceL'] += $Value['XAdvance']-$cw; }
|
2989 |
+
else { $this->OTLdata[$apos]['GPOSinfo']['XAdvanceL'] = $Value['XAdvance']-$cw; }
|
2990 |
+
if (isset($this->OTLdata[$basepos]['GPOSinfo']['XAdvanceR'])) { $this->OTLdata[$basepos]['GPOSinfo']['XAdvanceR'] += $Value['XAdvance']-$cw; }
|
2991 |
+
else { $this->OTLdata[$basepos]['GPOSinfo']['XAdvanceR'] = $Value['XAdvance']-$cw; }
|
2992 |
+
}
|
2993 |
+
|
2994 |
+
// Any XPlacement (? and Y Placement) apply to base and marks (from basepos to apos)
|
2995 |
+
for ($a=$basepos;$a<=$apos;$a++) {
|
2996 |
+
if (isset($Value['XPlacement'])) {
|
2997 |
+
if (isset($this->OTLdata[$a]['GPOSinfo']['XPlacement'])) { $this->OTLdata[$a]['GPOSinfo']['XPlacement'] += $Value['XPlacement']; }
|
2998 |
+
else { $this->OTLdata[$a]['GPOSinfo']['XPlacement'] = $Value['XPlacement']; }
|
2999 |
+
}
|
3000 |
+
if (isset($Value['YPlacement'])) {
|
3001 |
+
if (isset($this->OTLdata[$a]['GPOSinfo']['YPlacement'])) { $this->OTLdata[$a]['GPOSinfo']['YPlacement'] += $Value['YPlacement']; }
|
3002 |
+
else { $this->OTLdata[$a]['GPOSinfo']['YPlacement'] = $Value['YPlacement']; }
|
3003 |
+
}
|
3004 |
+
}
|
3005 |
+
}
|
3006 |
+
|
3007 |
+
|
3008 |
+
|
3009 |
+
// If XAdvance is aplied to $ptr - in order for PDF to position the Advance correctly need to place it on
|
3010 |
+
// the last of any Marks which immediately follow the current glyph
|
3011 |
+
function _getXAdvancePos($pos) {
|
3012 |
+
// NB Not all fonts have all marks specified in GlyphClassMarks
|
3013 |
+
|
3014 |
+
// If the current glyph is not a base (but a mark) then ignore this, and apply to the current position
|
3015 |
+
if (strpos($this->GlyphClassMarks, $this->OTLdata[$pos]['hex'])!==false) { return $pos; }
|
3016 |
+
|
3017 |
+
while(isset($this->OTLdata[$pos+1]['hex']) && strpos($this->GlyphClassMarks, $this->OTLdata[$pos+1]['hex'])!==false) { $pos++; }
|
3018 |
+
return $pos ;
|
3019 |
+
}
|
3020 |
+
|
3021 |
+
|
3022 |
+
|
3023 |
+
|
3024 |
+
function _applyGPOSsubtable($lookupID, $subtable, $ptr, $currGlyph, $currGID, $subtable_offset, $Type, $Flag, $MarkFilteringSet, $LuCoverage, $tag, $level=0, $is_old_spec) {
|
3025 |
+
if (($Flag & 0x0001) == 1) { $dir = 'RTL'; } // only used for Type 3
|
3026 |
+
else { $dir = 'LTR'; }
|
3027 |
+
$ignore = $this->_getGCOMignoreString($Flag, $MarkFilteringSet);
|
3028 |
+
|
3029 |
+
// Lets start
|
3030 |
+
$this->seek($subtable_offset);
|
3031 |
+
$PosFormat = $this->read_ushort();
|
3032 |
+
|
3033 |
+
////////////////////////////////////////////////////////////////////////////////
|
3034 |
+
// LookupType 1: Single adjustment Adjust position of a single glyph (e.g. SmallCaps/Sups/Subs)
|
3035 |
+
////////////////////////////////////////////////////////////////////////////////
|
3036 |
+
if ($Type == 1) {
|
3037 |
+
//===========
|
3038 |
+
// Format 1:
|
3039 |
+
//===========
|
3040 |
+
if ($PosFormat==1) {
|
3041 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
3042 |
+
$ValueFormat = $this->read_ushort();
|
3043 |
+
$Value = $this->_getValueRecord($ValueFormat);
|
3044 |
+
}
|
3045 |
+
//===========
|
3046 |
+
// Format 2:
|
3047 |
+
//===========
|
3048 |
+
else if ($PosFormat==2) {
|
3049 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
3050 |
+
$ValueFormat = $this->read_ushort();
|
3051 |
+
$ValueCount = $this->read_ushort();
|
3052 |
+
$GlyphPos = $LuCoverage[$currGID];
|
3053 |
+
$this->skip($GlyphPos * 2 * $this->count_bits($ValueFormat) );
|
3054 |
+
$Value = $this->_getValueRecord($ValueFormat);
|
3055 |
+
}
|
3056 |
+
$this->_applyGPOSvaluerecord($ptr,$Value);
|
3057 |
+
if ($this->debugOTL) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3058 |
+
return 1;
|
3059 |
+
}
|
3060 |
+
|
3061 |
+
////////////////////////////////////////////////////////////////////////////////
|
3062 |
+
// LookupType 2: Pair adjustment Adjust position of a pair of glyphs (Kerning)
|
3063 |
+
////////////////////////////////////////////////////////////////////////////////
|
3064 |
+
else if ($Type == 2) {
|
3065 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
3066 |
+
$ValueFormat1 = $this->read_ushort();
|
3067 |
+
$ValueFormat2 = $this->read_ushort();
|
3068 |
+
$sizeOfPair = ( 2*$this->count_bits($ValueFormat1) ) + ( 2*$this->count_bits($ValueFormat2) );
|
3069 |
+
//===========
|
3070 |
+
// Format 1:
|
3071 |
+
//===========
|
3072 |
+
if ($PosFormat==1) {
|
3073 |
+
$PairSetCount = $this->read_ushort();
|
3074 |
+
$PairSetOffset = array();
|
3075 |
+
for($p=0;$p<$PairSetCount;$p++) {
|
3076 |
+
$PairSetOffset[] = $subtable_offset + $this->read_ushort();
|
3077 |
+
}
|
3078 |
+
for($p=0;$p<$PairSetCount;$p++) {
|
3079 |
+
if (isset($LuCoverage[$currGID]) && $LuCoverage[$currGID]==$p) {
|
3080 |
+
$this->seek($PairSetOffset[$p]);
|
3081 |
+
//PairSet table
|
3082 |
+
$PairValueCount = $this->read_ushort();
|
3083 |
+
for($pv=0;$pv<$PairValueCount;$pv++) {
|
3084 |
+
//PairValueRecord
|
3085 |
+
$gid = $this->read_ushort();
|
3086 |
+
$SecondGlyph = $this->glyphToChar($gid);
|
3087 |
+
$FirstGlyph = $this->OTLdata[$ptr]['uni'];
|
3088 |
+
|
3089 |
+
$checkpos = $ptr;
|
3090 |
+
$checkpos++;
|
3091 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) {
|
3092 |
+
$checkpos++;
|
3093 |
+
}
|
3094 |
+
if (isset($this->OTLdata[$checkpos]) && $this->OTLdata[$checkpos]['uni']==$SecondGlyph) {
|
3095 |
+
$matchedpos = $checkpos;
|
3096 |
+
}
|
3097 |
+
else { $matchedpos = false; }
|
3098 |
+
|
3099 |
+
if ($matchedpos !== false) {
|
3100 |
+
$Value1 = $this->_getValueRecord($ValueFormat1);
|
3101 |
+
$Value2 = $this->_getValueRecord($ValueFormat2);
|
3102 |
+
if($ValueFormat1) {
|
3103 |
+
$this->_applyGPOSvaluerecord($ptr,$Value1);
|
3104 |
+
}
|
3105 |
+
if($ValueFormat2) {
|
3106 |
+
$this->_applyGPOSvaluerecord($matchedpos,$Value2);
|
3107 |
+
if ($this->debugOTL) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3108 |
+
return $matchedpos - $ptr +1;
|
3109 |
+
}
|
3110 |
+
if ($this->debugOTL) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3111 |
+
return $matchedpos - $ptr;
|
3112 |
+
}
|
3113 |
+
else {
|
3114 |
+
$this->skip($sizeOfPair);
|
3115 |
+
}
|
3116 |
+
}
|
3117 |
+
}
|
3118 |
+
}
|
3119 |
+
return 0;
|
3120 |
+
}
|
3121 |
+
//===========
|
3122 |
+
// Format 2:
|
3123 |
+
//===========
|
3124 |
+
else if ($PosFormat==2) {
|
3125 |
+
$ClassDef1 = $subtable_offset + $this->read_ushort();
|
3126 |
+
$ClassDef2 = $subtable_offset + $this->read_ushort();
|
3127 |
+
$Class1Count = $this->read_ushort();
|
3128 |
+
$Class2Count = $this->read_ushort();
|
3129 |
+
|
3130 |
+
$sizeOfValueRecords = $Class1Count * $Class2Count * $sizeOfPair;
|
3131 |
+
|
3132 |
+
//$this->skip($sizeOfValueRecords ); ???? NOT NEEDED
|
3133 |
+
|
3134 |
+
// NB Class1Count includes Class 0 even though it is not defined by $ClassDef1
|
3135 |
+
// i.e. Class1Count = 5; Class1 will contain array(indices 1-4);
|
3136 |
+
$Class1 = $this->_getClassDefinitionTable($ClassDef1);
|
3137 |
+
$Class2 = $this->_getClassDefinitionTable($ClassDef2);
|
3138 |
+
$FirstGlyph = $this->OTLdata[$ptr]['uni'];
|
3139 |
+
$checkpos = $ptr;
|
3140 |
+
$checkpos++;
|
3141 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) {
|
3142 |
+
$checkpos++;
|
3143 |
+
}
|
3144 |
+
if (isset($this->OTLdata[$checkpos])) { $matchedpos = $checkpos; }
|
3145 |
+
else { return 0; }
|
3146 |
+
|
3147 |
+
$SecondGlyph = $this->OTLdata[$matchedpos]['uni'];
|
3148 |
+
for($i=0;$i<$Class1Count;$i++) {
|
3149 |
+
if (isset($Class1[$i]) && count($Class1[$i])) {
|
3150 |
+
$FirstClassPos = array_search($FirstGlyph, $Class1[$i]);
|
3151 |
+
if ($FirstClassPos === false) { continue; }
|
3152 |
+
else {
|
3153 |
+
for($j=0;$j<$Class2Count;$j++) {
|
3154 |
+
if (isset($Class2[$j]) && count($Class2[$j])) {
|
3155 |
+
|
3156 |
+
$SecondClassPos = array_search($SecondGlyph, $Class2[$j]);
|
3157 |
+
if ($SecondClassPos === false) { continue; }
|
3158 |
+
|
3159 |
+
// Get ValueRecord[$i][$j]
|
3160 |
+
$offs = ($i*$Class2Count*$sizeOfPair) + ($j*$sizeOfPair);
|
3161 |
+
$this->seek($subtable_offset + 16 + $offs);
|
3162 |
+
|
3163 |
+
$Value1 = $this->_getValueRecord($ValueFormat1);
|
3164 |
+
$Value2 = $this->_getValueRecord($ValueFormat2);
|
3165 |
+
if($ValueFormat1) {
|
3166 |
+
$this->_applyGPOSvaluerecord($ptr,$Value1);
|
3167 |
+
}
|
3168 |
+
if($ValueFormat2) {
|
3169 |
+
$this->_applyGPOSvaluerecord($matchedpos,$Value2);
|
3170 |
+
if ($this->debugOTL) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3171 |
+
return $matchedpos - $ptr +1;
|
3172 |
+
}
|
3173 |
+
if ($this->debugOTL) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3174 |
+
return $matchedpos - $ptr;
|
3175 |
+
}
|
3176 |
+
}
|
3177 |
+
}
|
3178 |
+
|
3179 |
+
}
|
3180 |
+
}
|
3181 |
+
return 0;
|
3182 |
+
}
|
3183 |
+
}
|
3184 |
+
|
3185 |
+
////////////////////////////////////////////////////////////////////////////////
|
3186 |
+
// LookupType 3: Cursive attachment Attach cursive glyphs
|
3187 |
+
////////////////////////////////////////////////////////////////////////////////
|
3188 |
+
else if ($Type == 3) {
|
3189 |
+
$this->skip(4);
|
3190 |
+
// Need default XAdvance for glyph
|
3191 |
+
$pdfWidth = $this->mpdf->_getCharWidth($this->mpdf->CurrentFont['cw'], hexdec($currGlyph)); // DON'T convert back to design units
|
3192 |
+
|
3193 |
+
$CPos = $LuCoverage[$currGID];
|
3194 |
+
$this->skip($CPos * 4);
|
3195 |
+
$EntryAnchor = $this->read_ushort();
|
3196 |
+
$ExitAnchor = $this->read_ushort();
|
3197 |
+
if ($EntryAnchor != 0) {
|
3198 |
+
$EntryAnchor += $subtable_offset;
|
3199 |
+
list($x,$y) = $this->_getAnchorTable($EntryAnchor);
|
3200 |
+
if ($dir == 'RTL') {
|
3201 |
+
if (round($pdfWidth) == round($x * 1000/ $this->mpdf->CurrentFont['unitsPerEm']) ) {
|
3202 |
+
$x = 0;
|
3203 |
+
}
|
3204 |
+
else { $x = $x - ($pdfWidth * $this->mpdf->CurrentFont['unitsPerEm']/1000); }
|
3205 |
+
}
|
3206 |
+
|
3207 |
+
$this->Entry[$ptr] = array('X'=>$x, 'Y'=>$y, 'dir'=>$dir);
|
3208 |
+
}
|
3209 |
+
if ($ExitAnchor != 0) {
|
3210 |
+
$ExitAnchor += $subtable_offset;
|
3211 |
+
list($x,$y) = $this->_getAnchorTable($ExitAnchor);
|
3212 |
+
if ($dir == 'LTR') {
|
3213 |
+
if (round($pdfWidth) == round($x * 1000/ $this->mpdf->CurrentFont['unitsPerEm']) ) {
|
3214 |
+
$x = 0;
|
3215 |
+
}
|
3216 |
+
else { $x = $x - ($pdfWidth * $this->mpdf->CurrentFont['unitsPerEm']/1000); }
|
3217 |
+
}
|
3218 |
+
$this->Exit[$ptr] = array('X'=>$x, 'Y'=>$y, 'dir'=>$dir);
|
3219 |
+
}
|
3220 |
+
if ($this->debugOTL) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3221 |
+
return 1;
|
3222 |
+
}
|
3223 |
+
|
3224 |
+
////////////////////////////////////////////////////////////////////////////////
|
3225 |
+
// LookupType 4: MarkToBase attachment Attach a combining mark to a base glyph
|
3226 |
+
////////////////////////////////////////////////////////////////////////////////
|
3227 |
+
else if ($Type == 4) {
|
3228 |
+
$MarkCoverage = $subtable_offset + $this->read_ushort();
|
3229 |
+
//$MarkCoverage is already set in $LuCoverage 00065|00073 etc
|
3230 |
+
$BaseCoverage = $subtable_offset + $this->read_ushort();
|
3231 |
+
$ClassCount = $this->read_ushort(); // Number of classes defined for marks = Number of mark glyphs in the MarkCoverage table
|
3232 |
+
$MarkArray = $subtable_offset + $this->read_ushort(); // Offset to MarkArray table
|
3233 |
+
$BaseArray = $subtable_offset + $this->read_ushort(); // Offset to BaseArray table
|
3234 |
+
|
3235 |
+
$this->seek($BaseCoverage);
|
3236 |
+
$BaseGlyphs = implode('|',$this->_getCoverage());
|
3237 |
+
|
3238 |
+
$checkpos = $ptr;
|
3239 |
+
$checkpos--;
|
3240 |
+
|
3241 |
+
// ZZZ93
|
3242 |
+
// In Lohit-Kannada font (old-spec), rules specify a Type 4 GPOS to attach below-forms to base glyph
|
3243 |
+
// the repositioning does not happen in MS Word, and shouldn't happen comparing with other fonts
|
3244 |
+
// ?Why not
|
3245 |
+
// This Fix blocks the GPOS rule if the "mark" is not actually classified as a mark in the GlyphClasses of GDEF
|
3246 |
+
// but only in Indic old-spec.
|
3247 |
+
// Test cases: ನ್ನು and ಕ್ರೌ
|
3248 |
+
if ($this->shaper=='I' && $is_old_spec && strpos($this->GlyphClassMarks, $this->OTLdata[$ptr]['hex'])===false) { return; }
|
3249 |
+
|
3250 |
+
|
3251 |
+
// "To identify the base glyph that combines with a mark, the text-processing client must look backward in the glyph string from the mark to the preceding base glyph."
|
3252 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($this->GlyphClassMarks, $this->OTLdata[$checkpos]['hex'])!==false) {
|
3253 |
+
$checkpos--;
|
3254 |
+
}
|
3255 |
+
|
3256 |
+
if (isset($this->OTLdata[$checkpos]) && strpos($BaseGlyphs, $this->OTLdata[$checkpos]['hex'])!==false) {
|
3257 |
+
$matchedpos = $checkpos;
|
3258 |
+
}
|
3259 |
+
else { $matchedpos = false; }
|
3260 |
+
|
3261 |
+
if ($matchedpos !== false) {
|
3262 |
+
|
3263 |
+
// Get the relevant MarkRecord
|
3264 |
+
$MarkPos = $LuCoverage[$currGID];
|
3265 |
+
$MarkRecord = $this->_getMarkRecord($MarkArray, $MarkPos); // e.g. Array ( [Class] => 0 [AnchorX] => -549 [AnchorY] => 1548 )
|
3266 |
+
//Mark Class is = $MarkRecord['Class']
|
3267 |
+
|
3268 |
+
// Get the relevant BaseRecord
|
3269 |
+
$this->seek($BaseArray);
|
3270 |
+
$BaseCount = $this->read_ushort();
|
3271 |
+
$BasePos = strpos($BaseGlyphs, $this->OTLdata[$matchedpos]['hex'])/6;
|
3272 |
+
|
3273 |
+
// Move to the BaseRecord we want
|
3274 |
+
$nSkip = (2 * $BasePos * $ClassCount );
|
3275 |
+
$this->skip($nSkip);
|
3276 |
+
|
3277 |
+
// Read BaseRecord we want for appropriate Class
|
3278 |
+
$nSkip = 2*$MarkRecord['Class'];
|
3279 |
+
$this->skip($nSkip);
|
3280 |
+
$BaseRecordOffset = $BaseArray + $this->read_ushort();
|
3281 |
+
list($x,$y) = $this->_getAnchorTable($BaseRecordOffset);
|
3282 |
+
$BaseRecord = array('AnchorX'=>$x, 'AnchorY'=>$y); // e.g. Array ( [AnchorX] => 660 [AnchorY] => 1556 )
|
3283 |
+
|
3284 |
+
// Need default XAdvance for Base glyph
|
3285 |
+
$BaseWidth = $this->mpdf->_getCharWidth($this->mpdf->CurrentFont['cw'], $this->OTLdata[$matchedpos]['uni']) * $this->mpdf->CurrentFont['unitsPerEm'] / 1000; // convert back to font design units
|
3286 |
+
$this->OTLdata[$ptr]['GPOSinfo']['BaseWidth'] = $BaseWidth;
|
3287 |
+
// And any intervening (ignored) characters
|
3288 |
+
if (($ptr - $matchedpos) > 1) {
|
3289 |
+
for ($i=$matchedpos+1; $i<$ptr; $i++) {
|
3290 |
+
$BaseWidthExtra = $this->mpdf->_getCharWidth($this->mpdf->CurrentFont['cw'], $this->OTLdata[$i]['uni']) * $this->mpdf->CurrentFont['unitsPerEm'] / 1000; // convert back to font design units
|
3291 |
+
$this->OTLdata[$ptr]['GPOSinfo']['BaseWidth'] += $BaseWidthExtra;
|
3292 |
+
|
3293 |
+
}
|
3294 |
+
}
|
3295 |
+
|
3296 |
+
// Align to previous Glyph by attachment - so need to add to previous placement values
|
3297 |
+
$prevXPlacement = (isset($this->OTLdata[$matchedpos]['GPOSinfo']['XPlacement']) ? $this->OTLdata[$matchedpos]['GPOSinfo']['XPlacement'] : 0);
|
3298 |
+
$prevYPlacement = (isset($this->OTLdata[$matchedpos]['GPOSinfo']['YPlacement']) ? $this->OTLdata[$matchedpos]['GPOSinfo']['YPlacement'] : 0);
|
3299 |
+
|
3300 |
+
$this->OTLdata[$ptr]['GPOSinfo']['XPlacement'] = $prevXPlacement + $BaseRecord['AnchorX'] - $MarkRecord['AnchorX'];
|
3301 |
+
$this->OTLdata[$ptr]['GPOSinfo']['YPlacement'] = $prevYPlacement + $BaseRecord['AnchorY'] - $MarkRecord['AnchorY'];
|
3302 |
+
if ($this->debugOTL) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3303 |
+
return 1;
|
3304 |
+
|
3305 |
+
}
|
3306 |
+
return 0;
|
3307 |
+
}
|
3308 |
+
|
3309 |
+
////////////////////////////////////////////////////////////////////////////////
|
3310 |
+
// LookupType 5: MarkToLigature attachment Attach a combining mark to a ligature
|
3311 |
+
////////////////////////////////////////////////////////////////////////////////
|
3312 |
+
else if ($Type == 5) {
|
3313 |
+
$MarkCoverage = $subtable_offset + $this->read_ushort();
|
3314 |
+
//$MarkCoverage is already set in $LuCoverage 00065|00073 etc
|
3315 |
+
$LigatureCoverage = $subtable_offset + $this->read_ushort();
|
3316 |
+
$ClassCount = $this->read_ushort(); // Number of classes defined for marks = Number of mark glyphs in the MarkCoverage table
|
3317 |
+
$MarkArray = $subtable_offset + $this->read_ushort(); // Offset to MarkArray table
|
3318 |
+
$LigatureArray = $subtable_offset + $this->read_ushort(); // Offset to LigatureArray table
|
3319 |
+
|
3320 |
+
$this->seek($LigatureCoverage);
|
3321 |
+
$LigatureGlyphs = implode('|',$this->_getCoverage());
|
3322 |
+
|
3323 |
+
|
3324 |
+
$checkpos = $ptr;
|
3325 |
+
$checkpos--;
|
3326 |
+
|
3327 |
+
// "To position a combining mark using a MarkToLigature attachment subtable, the text-processing client must work backward from the mark to the preceding ligature glyph."
|
3328 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($this->GlyphClassMarks, $this->OTLdata[$checkpos]['hex'])!==false) {
|
3329 |
+
$checkpos--;
|
3330 |
+
}
|
3331 |
+
|
3332 |
+
if (isset($this->OTLdata[$checkpos]) && strpos($LigatureGlyphs, $this->OTLdata[$checkpos]['hex'])!==false) {
|
3333 |
+
$matchedpos = $checkpos;
|
3334 |
+
}
|
3335 |
+
else { $matchedpos = false; }
|
3336 |
+
|
3337 |
+
if ($matchedpos !== false) {
|
3338 |
+
|
3339 |
+
// Get the relevant MarkRecord
|
3340 |
+
$MarkPos = $LuCoverage[$currGID];
|
3341 |
+
$MarkRecord = $this->_getMarkRecord($MarkArray, $MarkPos); // e.g. Array ( [Class] => 0 [AnchorX] => -549 [AnchorY] => 1548 )
|
3342 |
+
//Mark Class is = $MarkRecord['Class']
|
3343 |
+
|
3344 |
+
|
3345 |
+
// Get the relevant LigatureRecord
|
3346 |
+
$this->seek($LigatureArray);
|
3347 |
+
$LigatureCount = $this->read_ushort();
|
3348 |
+
$LigaturePos = strpos($LigatureGlyphs, $this->OTLdata[$matchedpos]['hex'])/6;
|
3349 |
+
|
3350 |
+
// Move to the LigatureAttach table Record we want
|
3351 |
+
$nSkip = (2 * $LigaturePos);
|
3352 |
+
$this->skip($nSkip);
|
3353 |
+
$LigatureAttachOffset = $LigatureArray + $this->read_ushort();
|
3354 |
+
$this->seek($LigatureAttachOffset);
|
3355 |
+
$ComponentCount = $this->read_ushort();
|
3356 |
+
$offsets = array();
|
3357 |
+
for ($comp=0;$comp<$ComponentCount;$comp++) {
|
3358 |
+
// ComponentRecords
|
3359 |
+
for ($class=0;$class<$ClassCount;$class++) {
|
3360 |
+
$offsets[$comp][$class] = $this->read_ushort();
|
3361 |
+
}
|
3362 |
+
}
|
3363 |
+
|
3364 |
+
// Get the specific component for this mark attachment
|
3365 |
+
if (isset($this->assocLigs[$matchedpos]) && isset($this->assocMarks[$ptr]['ligPos']) && $this->assocMarks[$ptr]['ligPos']==$matchedpos) {
|
3366 |
+
$component = $this->assocMarks[$ptr]['compID'] ;
|
3367 |
+
}
|
3368 |
+
else { $component = $ComponentCount-1; }
|
3369 |
+
|
3370 |
+
$offset = $offsets[$component][$MarkRecord['Class']];
|
3371 |
+
if ($offset!=0) {
|
3372 |
+
$LigatureRecordOffset = $offset + $LigatureAttachOffset;
|
3373 |
+
list($x,$y) = $this->_getAnchorTable($LigatureRecordOffset);
|
3374 |
+
$LigatureRecord = array('AnchorX'=>$x, 'AnchorY'=>$y);
|
3375 |
+
|
3376 |
+
// Need default XAdvance for Ligature glyph
|
3377 |
+
$LigatureWidth = $this->mpdf->_getCharWidth($this->mpdf->CurrentFont['cw'], $this->OTLdata[$matchedpos]['uni']) * $this->mpdf->CurrentFont['unitsPerEm'] / 1000; // convert back to font design units
|
3378 |
+
$this->OTLdata[$ptr]['GPOSinfo']['BaseWidth'] = $LigatureWidth;
|
3379 |
+
// And any intervening (ignored)characters
|
3380 |
+
if (($ptr - $matchedpos) > 1) {
|
3381 |
+
for ($i=$matchedpos+1; $i<$ptr; $i++) {
|
3382 |
+
$LigatureWidthExtra = $this->mpdf->_getCharWidth($this->mpdf->CurrentFont['cw'], $this->OTLdata[$i]['uni']) * $this->mpdf->CurrentFont['unitsPerEm'] / 1000; // convert back to font design units
|
3383 |
+
$this->OTLdata[$ptr]['GPOSinfo']['BaseWidth'] += $LigatureWidthExtra;
|
3384 |
+
|
3385 |
+
}
|
3386 |
+
}
|
3387 |
+
|
3388 |
+
// Align to previous Ligature by attachment - so need to add to previous placement values
|
3389 |
+
if (isset($this->OTLdata[$matchedpos]['GPOSinfo']['XPlacement'])) $prevXPlacement = $this->OTLdata[$matchedpos]['GPOSinfo']['XPlacement'];
|
3390 |
+
else { $prevXPlacement = 0; }
|
3391 |
+
if (isset($this->OTLdata[$matchedpos]['GPOSinfo']['YPlacement'])) { $prevYPlacement = $this->OTLdata[$matchedpos]['GPOSinfo']['YPlacement']; }
|
3392 |
+
else { $prevYPlacement = 0; }
|
3393 |
+
|
3394 |
+
$this->OTLdata[$ptr]['GPOSinfo']['XPlacement'] = $prevXPlacement + $LigatureRecord['AnchorX'] - $MarkRecord['AnchorX'];
|
3395 |
+
$this->OTLdata[$ptr]['GPOSinfo']['YPlacement'] = $prevYPlacement + $LigatureRecord['AnchorY'] - $MarkRecord['AnchorY'];
|
3396 |
+
if ($this->debugOTL) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3397 |
+
return 1;
|
3398 |
+
|
3399 |
+
}
|
3400 |
+
}
|
3401 |
+
return 0;
|
3402 |
+
}
|
3403 |
+
|
3404 |
+
////////////////////////////////////////////////////////////////////////////////
|
3405 |
+
// LookupType 6: MarkToMark attachment Attach a combining mark to another mark
|
3406 |
+
////////////////////////////////////////////////////////////////////////////////
|
3407 |
+
else if ($Type == 6) {
|
3408 |
+
$Mark1Coverage = $subtable_offset + $this->read_ushort(); // Combining Mark
|
3409 |
+
//$Mark1Coverage is already set in $LuCoverage 0065|0073 etc
|
3410 |
+
$Mark2Coverage = $subtable_offset + $this->read_ushort(); // Base Mark
|
3411 |
+
$ClassCount = $this->read_ushort(); // Number of classes defined for marks = No. of Combining mark1 glyphs in the MarkCoverage table
|
3412 |
+
$Mark1Array = $subtable_offset + $this->read_ushort(); // Offset to MarkArray table
|
3413 |
+
$Mark2Array = $subtable_offset + $this->read_ushort(); // Offset to Mark2Array table
|
3414 |
+
$this->seek($Mark2Coverage);
|
3415 |
+
$Mark2Glyphs = implode('|',$this->_getCoverage());
|
3416 |
+
$checkpos = $ptr;
|
3417 |
+
$checkpos--;
|
3418 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) {
|
3419 |
+
$checkpos--;
|
3420 |
+
}
|
3421 |
+
if (isset($this->OTLdata[$checkpos]) && strpos($Mark2Glyphs, $this->OTLdata[$checkpos]['hex'])!==false) {
|
3422 |
+
$matchedpos = $checkpos;
|
3423 |
+
}
|
3424 |
+
else { $matchedpos = false; }
|
3425 |
+
|
3426 |
+
if ($matchedpos !== false) {
|
3427 |
+
|
3428 |
+
// Get the relevant MarkRecord
|
3429 |
+
$Mark1Pos = $LuCoverage[$currGID];
|
3430 |
+
$Mark1Record = $this->_getMarkRecord($Mark1Array, $Mark1Pos); // e.g. Array ( [Class] => 0 [AnchorX] => -549 [AnchorY] => 1548 )
|
3431 |
+
//Mark Class is = $Mark1Record['Class']
|
3432 |
+
|
3433 |
+
// Get the relevant Mark2Record
|
3434 |
+
$this->seek($Mark2Array);
|
3435 |
+
$Mark2Count = $this->read_ushort();
|
3436 |
+
$Mark2Pos = strpos($Mark2Glyphs, $this->OTLdata[$matchedpos]['hex'])/6;
|
3437 |
+
|
3438 |
+
// Move to the Mark2Record we want
|
3439 |
+
$nSkip = (2 * $Mark2Pos * $ClassCount );
|
3440 |
+
$this->skip($nSkip);
|
3441 |
+
|
3442 |
+
// Read Mark2Record we want for appropriate Class
|
3443 |
+
$nSkip = 2*$Mark1Record['Class'];
|
3444 |
+
$this->skip($nSkip);
|
3445 |
+
$Mark2RecordOffset = $Mark2Array + $this->read_ushort();
|
3446 |
+
list($x,$y) = $this->_getAnchorTable($Mark2RecordOffset);
|
3447 |
+
$Mark2Record = array('AnchorX'=>$x, 'AnchorY'=>$y); // e.g. Array ( [AnchorX] => 660 [AnchorY] => 1556 )
|
3448 |
+
|
3449 |
+
// Need default XAdvance for Mark2 glyph
|
3450 |
+
$Mark2Width = $this->mpdf->_getCharWidth($this->mpdf->CurrentFont['cw'], $this->OTLdata[$matchedpos]['uni']) * $this->mpdf->CurrentFont['unitsPerEm'] / 1000; // convert back to font design units
|
3451 |
+
|
3452 |
+
|
3453 |
+
// IF combining marks are set on different components of a ligature glyph, do not apply this rule
|
3454 |
+
// Test: arabictypesetting: إِلَىٰٓ
|
3455 |
+
// Test: arabictypesetting: بَّيْنَكُمْ
|
3456 |
+
$prevLig = -1;
|
3457 |
+
$thisLig = -1;
|
3458 |
+
$prevComp = -1;
|
3459 |
+
$thisComp = -1;
|
3460 |
+
if (isset($this->assocMarks[$matchedpos])) {
|
3461 |
+
$prevLig = $this->assocMarks[$matchedpos]['ligPos'];
|
3462 |
+
$prevComp = $this->assocMarks[$matchedpos]['compID'];
|
3463 |
+
}
|
3464 |
+
if (isset($this->assocMarks[$ptr])) {
|
3465 |
+
$thisLig = $this->assocMarks[$ptr]['ligPos'];
|
3466 |
+
$thisComp = $this->assocMarks[$ptr]['compID'];
|
3467 |
+
}
|
3468 |
+
|
3469 |
+
// However IF Mark2 (first in logical order, i.e. being attached to) is not associated with a base, carry on
|
3470 |
+
// This happens in Indic when the Mark being attached to e.g. [Halant Ma lig] -> MatraU, [U+0B4D + U+B2E as E0F5]-> U+0B41 become E135
|
3471 |
+
if (!defined("OMIT_OTL_FIX_1") || OMIT_OTL_FIX_1 != 1) {
|
3472 |
+
/* OTL_FIX_1 */
|
3473 |
+
if (isset($this->assocMarks[$matchedpos]) && ($prevLig != $thisLig || $prevComp != $thisComp )) { return 0; }
|
3474 |
+
}
|
3475 |
+
else {
|
3476 |
+
/* Original code */
|
3477 |
+
if ($prevLig != $thisLig || $prevComp != $thisComp ) { return 0; }
|
3478 |
+
}
|
3479 |
+
|
3480 |
+
|
3481 |
+
if (!defined("OMIT_OTL_FIX_2") || OMIT_OTL_FIX_2 != 1) {
|
3482 |
+
/* OTL_FIX_2 */
|
3483 |
+
if (!isset($this->OTLdata[$matchedpos]['GPOSinfo']['BaseWidth']) || !$this->OTLdata[$matchedpos]['GPOSinfo']['BaseWidth']) { $this->OTLdata[$ptr]['GPOSinfo']['BaseWidth'] = $Mark2Width; }
|
3484 |
+
}
|
3485 |
+
|
3486 |
+
// ZZZ99Q - Test Case font-family: garuda น้ำ
|
3487 |
+
if (isset($this->OTLdata[$matchedpos]['GPOSinfo']['BaseWidth']) && $this->OTLdata[$matchedpos]['GPOSinfo']['BaseWidth']) { $this->OTLdata[$ptr]['GPOSinfo']['BaseWidth'] = $this->OTLdata[$matchedpos]['GPOSinfo']['BaseWidth']; }
|
3488 |
+
|
3489 |
+
// Align to previous Mark by attachment - so need to add the previous placement values
|
3490 |
+
$prevXPlacement = (isset($this->OTLdata[$matchedpos]['GPOSinfo']['XPlacement']) ? $this->OTLdata[$matchedpos]['GPOSinfo']['XPlacement'] : 0);
|
3491 |
+
$prevYPlacement = (isset($this->OTLdata[$matchedpos]['GPOSinfo']['YPlacement']) ? $this->OTLdata[$matchedpos]['GPOSinfo']['YPlacement'] : 0);
|
3492 |
+
$this->OTLdata[$ptr]['GPOSinfo']['XPlacement'] = $prevXPlacement + $Mark2Record['AnchorX'] - $Mark1Record['AnchorX'];
|
3493 |
+
$this->OTLdata[$ptr]['GPOSinfo']['YPlacement'] = $prevYPlacement + $Mark2Record['AnchorY'] - $Mark1Record['AnchorY'];
|
3494 |
+
if ($this->debugOTL) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3495 |
+
return 1;
|
3496 |
+
|
3497 |
+
}
|
3498 |
+
return 0;
|
3499 |
+
}
|
3500 |
+
|
3501 |
+
////////////////////////////////////////////////////////////////////////////////
|
3502 |
+
// LookupType 7: Context positioning Position one or more glyphs in context
|
3503 |
+
////////////////////////////////////////////////////////////////////////////////
|
3504 |
+
else if ($Type == 7) {
|
3505 |
+
//===========
|
3506 |
+
// Format 1:
|
3507 |
+
//===========
|
3508 |
+
if ($PosFormat==1) {
|
3509 |
+
die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not TESTED YET.");
|
3510 |
+
return 0;
|
3511 |
+
}
|
3512 |
+
//===========
|
3513 |
+
// Format 2:
|
3514 |
+
//===========
|
3515 |
+
else if ($PosFormat==2) {
|
3516 |
+
$CoverageTableOffset = $subtable_offset + $this->read_ushort();
|
3517 |
+
$InputClassDefOffset = $subtable_offset + $this->read_ushort();
|
3518 |
+
$PosClassSetCnt = $this->read_ushort();
|
3519 |
+
$PosClassSetOffset = array();
|
3520 |
+
for ($b=0;$b<$PosClassSetCnt;$b++) {
|
3521 |
+
$offset = $this->read_ushort();
|
3522 |
+
if ($offset==0x0000) {
|
3523 |
+
$PosClassSetOffset[] = $offset;
|
3524 |
+
}
|
3525 |
+
else {
|
3526 |
+
$PosClassSetOffset[] = $subtable_offset + $offset;
|
3527 |
+
}
|
3528 |
+
}
|
3529 |
+
|
3530 |
+
$InputClasses = $this->_getClasses($InputClassDefOffset);
|
3531 |
+
|
3532 |
+
for ($s=0;$s<$PosClassSetCnt;$s++) { // $ChainPosClassSet is ordered by input class-may be NULL
|
3533 |
+
// Select $PosClassSet if currGlyph is in First Input Class
|
3534 |
+
if ($PosClassSetOffset[$s]>0 && isset($InputClasses[$s][$currGID])) {
|
3535 |
+
$this->seek($PosClassSetOffset[$s]);
|
3536 |
+
$PosClassRuleCnt = $this->read_ushort();
|
3537 |
+
$PosClassRule = array();
|
3538 |
+
for($b=0;$b<$PosClassRuleCnt;$b++) {
|
3539 |
+
$PosClassRule[$b] = $PosClassSetOffset[$s]+$this->read_ushort();
|
3540 |
+
}
|
3541 |
+
|
3542 |
+
for($b=0;$b<$PosClassRuleCnt;$b++) { // EACH RULE
|
3543 |
+
$this->seek($PosClassRule[$b]);
|
3544 |
+
$InputGlyphCount = $this->read_ushort();
|
3545 |
+
$PosCount = $this->read_ushort();
|
3546 |
+
|
3547 |
+
$Input = array();
|
3548 |
+
for ($r=1;$r<$InputGlyphCount;$r++) {
|
3549 |
+
$Input[$r] = $this->read_ushort();
|
3550 |
+
}
|
3551 |
+
$inputClass = $s;
|
3552 |
+
|
3553 |
+
$inputGlyphs = array();
|
3554 |
+
$inputGlyphs[0] = $InputClasses[$inputClass];
|
3555 |
+
|
3556 |
+
if ($InputGlyphCount>1) {
|
3557 |
+
// NB starts at 1
|
3558 |
+
for ($gcl=1;$gcl<$InputGlyphCount;$gcl++) {
|
3559 |
+
$classindex = $Input[$gcl];
|
3560 |
+
if (isset($InputClasses[$classindex])) { $inputGlyphs[$gcl] = $InputClasses[$classindex]; }
|
3561 |
+
else { $inputGlyphs[$gcl] = ''; }
|
3562 |
+
}
|
3563 |
+
}
|
3564 |
+
|
3565 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
3566 |
+
$class0excl = array();
|
3567 |
+
for ($gc=1;$gc<=count($InputClasses);$gc++) {
|
3568 |
+
if (is_array($InputClasses[$gc])) $class0excl = $class0excl + $InputClasses[$gc];
|
3569 |
+
}
|
3570 |
+
|
3571 |
+
$backtrackGlyphs = array();
|
3572 |
+
$lookaheadGlyphs = array();
|
3573 |
+
|
3574 |
+
$matched = $this->checkContextMatchMultipleUni($inputGlyphs, $backtrackGlyphs, $lookaheadGlyphs, $ignore, $ptr, $class0excl );
|
3575 |
+
if ($matched) {
|
3576 |
+
for ($p=0;$p<$PosCount;$p++) { // EACH LOOKUP
|
3577 |
+
$SequenceIndex[$p] = $this->read_ushort();
|
3578 |
+
$LookupListIndex[$p] = $this->read_ushort();
|
3579 |
+
}
|
3580 |
+
|
3581 |
+
for ($p=0;$p<$PosCount;$p++) {
|
3582 |
+
// Apply $LookupListIndex at $SequenceIndex
|
3583 |
+
if ($SequenceIndex[$p] >= $InputGlyphCount) { continue; }
|
3584 |
+
$lu = $LookupListIndex[$p];
|
3585 |
+
$luType = $this->GPOSLookups[$lu]['Type'];
|
3586 |
+
$luFlag = $this->GPOSLookups[$lu]['Flag'];
|
3587 |
+
$luMarkFilteringSet = $this->GPOSLookups[$lu]['MarkFilteringSet'];
|
3588 |
+
|
3589 |
+
$luptr = $matched[$SequenceIndex[$p]];
|
3590 |
+
$lucurrGlyph = $this->OTLdata[$luptr]['hex'];
|
3591 |
+
$lucurrGID = $this->OTLdata[$luptr]['uni'];
|
3592 |
+
|
3593 |
+
foreach($this->GPOSLookups[$lu]['Subtables'] AS $luc=>$lusubtable_offset) {
|
3594 |
+
$shift = $this->_applyGPOSsubtable($lu, $luc, $luptr, $lucurrGlyph, $lucurrGID, ($lusubtable_offset - $this->GPOS_offset + $this->GSUB_length), $luType, $luFlag, $luMarkFilteringSet, $this->LuCoverage[$lu][$luc], $tag, 1, $is_old_spec);
|
3595 |
+
if ($this->debugOTL && $shift) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3596 |
+
if ($shift) { break; }
|
3597 |
+
}
|
3598 |
+
}
|
3599 |
+
|
3600 |
+
if (!defined("OMIT_OTL_FIX_3") || OMIT_OTL_FIX_3 != 1) { return $shift ; } /* OTL_FIX_3 */
|
3601 |
+
else return $InputGlyphCount ; // should be + matched ignores in Input Sequence
|
3602 |
+
|
3603 |
+
}
|
3604 |
+
}
|
3605 |
+
|
3606 |
+
}
|
3607 |
+
}
|
3608 |
+
|
3609 |
+
return 0;
|
3610 |
+
}
|
3611 |
+
//===========
|
3612 |
+
// Format 3:
|
3613 |
+
//===========
|
3614 |
+
else if ($PosFormat==3) {
|
3615 |
+
die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not TESTED YET.");
|
3616 |
+
return 0;
|
3617 |
+
}
|
3618 |
+
else { die("GPOS Lookup Type ".$Type.", Format ".$PosFormat." not supported."); }
|
3619 |
+
}
|
3620 |
+
|
3621 |
+
////////////////////////////////////////////////////////////////////////////////
|
3622 |
+
// LookupType 8: Chained Context positioning Position one or more glyphs in chained context
|
3623 |
+
////////////////////////////////////////////////////////////////////////////////
|
3624 |
+
else if ($Type == 8) {
|
3625 |
+
//===========
|
3626 |
+
// Format 1:
|
3627 |
+
//===========
|
3628 |
+
if ($PosFormat==1) {
|
3629 |
+
die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not TESTED YET.");
|
3630 |
+
return 0;
|
3631 |
+
}
|
3632 |
+
//===========
|
3633 |
+
// Format 2:
|
3634 |
+
//===========
|
3635 |
+
else if ($PosFormat==2) {
|
3636 |
+
|
3637 |
+
$CoverageTableOffset = $subtable_offset + $this->read_ushort();
|
3638 |
+
$BacktrackClassDefOffset = $subtable_offset + $this->read_ushort();
|
3639 |
+
$InputClassDefOffset = $subtable_offset + $this->read_ushort();
|
3640 |
+
$LookaheadClassDefOffset = $subtable_offset + $this->read_ushort();
|
3641 |
+
$ChainPosClassSetCnt = $this->read_ushort();
|
3642 |
+
$ChainPosClassSetOffset = array();
|
3643 |
+
for ($b=0;$b<$ChainPosClassSetCnt;$b++) {
|
3644 |
+
$offset = $this->read_ushort();
|
3645 |
+
if ($offset==0x0000) {
|
3646 |
+
$ChainPosClassSetOffset[] = $offset;
|
3647 |
+
}
|
3648 |
+
else {
|
3649 |
+
$ChainPosClassSetOffset[] = $subtable_offset + $offset;
|
3650 |
+
}
|
3651 |
+
}
|
3652 |
+
|
3653 |
+
$BacktrackClasses = $this->_getClasses($BacktrackClassDefOffset);
|
3654 |
+
$InputClasses = $this->_getClasses($InputClassDefOffset);
|
3655 |
+
$LookaheadClasses = $this->_getClasses($LookaheadClassDefOffset);
|
3656 |
+
|
3657 |
+
for ($s=0;$s<$ChainPosClassSetCnt;$s++) { // $ChainPosClassSet is ordered by input class-may be NULL
|
3658 |
+
// Select $ChainPosClassSet if currGlyph is in First Input Class
|
3659 |
+
if ($ChainPosClassSetOffset[$s]>0 && isset($InputClasses[$s][$currGID])) {
|
3660 |
+
$this->seek($ChainPosClassSetOffset[$s]);
|
3661 |
+
$ChainPosClassRuleCnt = $this->read_ushort();
|
3662 |
+
$ChainPosClassRule = array();
|
3663 |
+
for($b=0;$b<$ChainPosClassRuleCnt;$b++) {
|
3664 |
+
$ChainPosClassRule[$b] = $ChainPosClassSetOffset[$s]+$this->read_ushort();
|
3665 |
+
}
|
3666 |
+
|
3667 |
+
for($b=0;$b<$ChainPosClassRuleCnt;$b++) { // EACH RULE
|
3668 |
+
$this->seek($ChainPosClassRule[$b]);
|
3669 |
+
$BacktrackGlyphCount = $this->read_ushort();
|
3670 |
+
$Backtrack = array();
|
3671 |
+
for ($r=0;$r<$BacktrackGlyphCount;$r++) {
|
3672 |
+
$Backtrack[$r] = $this->read_ushort();
|
3673 |
+
}
|
3674 |
+
$InputGlyphCount = $this->read_ushort();
|
3675 |
+
$Input = array();
|
3676 |
+
for ($r=1;$r<$InputGlyphCount;$r++) {
|
3677 |
+
$Input[$r] = $this->read_ushort();
|
3678 |
+
}
|
3679 |
+
$LookaheadGlyphCount = $this->read_ushort();
|
3680 |
+
$Lookahead = array();
|
3681 |
+
for ($r=0;$r<$LookaheadGlyphCount;$r++) {
|
3682 |
+
$Lookahead[$r] = $this->read_ushort();
|
3683 |
+
}
|
3684 |
+
|
3685 |
+
$inputClass = $s; //???
|
3686 |
+
|
3687 |
+
$inputGlyphs = array();
|
3688 |
+
$inputGlyphs[0] = $InputClasses[$inputClass];
|
3689 |
+
|
3690 |
+
if ($InputGlyphCount>1) {
|
3691 |
+
// NB starts at 1
|
3692 |
+
for ($gcl=1;$gcl<$InputGlyphCount;$gcl++) {
|
3693 |
+
$classindex = $Input[$gcl];
|
3694 |
+
if (isset($InputClasses[$classindex])) { $inputGlyphs[$gcl] = $InputClasses[$classindex]; }
|
3695 |
+
else { $inputGlyphs[$gcl] = ''; }
|
3696 |
+
}
|
3697 |
+
}
|
3698 |
+
|
3699 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
3700 |
+
$class0excl = array();
|
3701 |
+
for ($gc=1;$gc<=count($InputClasses);$gc++) {
|
3702 |
+
if (isset($InputClasses[$gc]) && is_array($InputClasses[$gc])) $class0excl = $class0excl + $InputClasses[$gc];
|
3703 |
+
}
|
3704 |
+
|
3705 |
+
if ($BacktrackGlyphCount) {
|
3706 |
+
$backtrackGlyphs = array();
|
3707 |
+
for ($gcl=0;$gcl<$BacktrackGlyphCount;$gcl++) {
|
3708 |
+
$classindex = $Backtrack[$gcl];
|
3709 |
+
if (isset($BacktrackClasses[$classindex])) { $backtrackGlyphs[$gcl] = $BacktrackClasses[$classindex]; }
|
3710 |
+
else { $backtrackGlyphs[$gcl] = ''; }
|
3711 |
+
}
|
3712 |
+
}
|
3713 |
+
else { $backtrackGlyphs = array(); }
|
3714 |
+
|
3715 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
3716 |
+
$bclass0excl = array();
|
3717 |
+
for ($gc=1;$gc<=count($BacktrackClasses);$gc++) {
|
3718 |
+
if (isset($BacktrackClasses[$gc]) && is_array($BacktrackClasses[$gc])) $bclass0excl = $bclass0excl + $BacktrackClasses[$gc];
|
3719 |
+
}
|
3720 |
+
|
3721 |
+
if ($LookaheadGlyphCount) {
|
3722 |
+
$lookaheadGlyphs = array();
|
3723 |
+
for ($gcl=0;$gcl<$LookaheadGlyphCount;$gcl++) {
|
3724 |
+
$classindex = $Lookahead[$gcl];
|
3725 |
+
if (isset($LookaheadClasses[$classindex])) { $lookaheadGlyphs[$gcl] = $LookaheadClasses[$classindex]; }
|
3726 |
+
else { $lookaheadGlyphs[$gcl] = ''; }
|
3727 |
+
}
|
3728 |
+
}
|
3729 |
+
else { $lookaheadGlyphs = array(); }
|
3730 |
+
|
3731 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
3732 |
+
$lclass0excl = array();
|
3733 |
+
for ($gc=1;$gc<=count($LookaheadClasses);$gc++) {
|
3734 |
+
if (isset($LookaheadClasses[$gc]) && is_array($LookaheadClasses[$gc])) $lclass0excl = $lclass0excl + $LookaheadClasses[$gc];
|
3735 |
+
}
|
3736 |
+
|
3737 |
+
$matched = $this->checkContextMatchMultipleUni($inputGlyphs, $backtrackGlyphs, $lookaheadGlyphs, $ignore, $ptr, $class0excl, $bclass0excl, $lclass0excl );
|
3738 |
+
if ($matched) {
|
3739 |
+
$PosCount = $this->read_ushort();
|
3740 |
+
$SequenceIndex = array();
|
3741 |
+
$LookupListIndex = array();
|
3742 |
+
for ($p=0;$p<$PosCount;$p++) { // EACH LOOKUP
|
3743 |
+
$SequenceIndex[$p] = $this->read_ushort();
|
3744 |
+
$LookupListIndex[$p] = $this->read_ushort();
|
3745 |
+
}
|
3746 |
+
|
3747 |
+
for ($p=0;$p<$PosCount;$p++) {
|
3748 |
+
// Apply $LookupListIndex at $SequenceIndex
|
3749 |
+
if ($SequenceIndex[$p] >= $InputGlyphCount) { continue; }
|
3750 |
+
$lu = $LookupListIndex[$p];
|
3751 |
+
$luType = $this->GPOSLookups[$lu]['Type'];
|
3752 |
+
$luFlag = $this->GPOSLookups[$lu]['Flag'];
|
3753 |
+
$luMarkFilteringSet = $this->GPOSLookups[$lu]['MarkFilteringSet'];
|
3754 |
+
|
3755 |
+
$luptr = $matched[$SequenceIndex[$p]];
|
3756 |
+
$lucurrGlyph = $this->OTLdata[$luptr]['hex'];
|
3757 |
+
$lucurrGID = $this->OTLdata[$luptr]['uni'];
|
3758 |
+
|
3759 |
+
foreach($this->GPOSLookups[$lu]['Subtables'] AS $luc=>$lusubtable_offset) {
|
3760 |
+
$shift = $this->_applyGPOSsubtable($lu, $luc, $luptr, $lucurrGlyph, $lucurrGID, ($lusubtable_offset - $this->GPOS_offset + $this->GSUB_length), $luType, $luFlag, $luMarkFilteringSet, $this->LuCoverage[$lu][$luc], $tag, 1, $is_old_spec);
|
3761 |
+
if ($this->debugOTL && $shift) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3762 |
+
if ($shift) { break; }
|
3763 |
+
}
|
3764 |
+
}
|
3765 |
+
|
3766 |
+
if (!defined("OMIT_OTL_FIX_3") || OMIT_OTL_FIX_3 != 1) { return $shift ; } /* OTL_FIX_3 */
|
3767 |
+
else return $InputGlyphCount ; // should be + matched ignores in Input Sequence
|
3768 |
+
|
3769 |
+
}
|
3770 |
+
}
|
3771 |
+
|
3772 |
+
}
|
3773 |
+
}
|
3774 |
+
|
3775 |
+
return 0;
|
3776 |
+
}
|
3777 |
+
//===========
|
3778 |
+
// Format 3:
|
3779 |
+
//===========
|
3780 |
+
else if ($PosFormat==3) {
|
3781 |
+
$BacktrackGlyphCount = $this->read_ushort();
|
3782 |
+
for ($b=0;$b<$BacktrackGlyphCount;$b++) {
|
3783 |
+
$CoverageBacktrackOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order
|
3784 |
+
}
|
3785 |
+
$InputGlyphCount = $this->read_ushort();
|
3786 |
+
for ($b=0;$b<$InputGlyphCount;$b++) {
|
3787 |
+
$CoverageInputOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order
|
3788 |
+
}
|
3789 |
+
$LookaheadGlyphCount = $this->read_ushort();
|
3790 |
+
for ($b=0;$b<$LookaheadGlyphCount;$b++) {
|
3791 |
+
$CoverageLookaheadOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order
|
3792 |
+
}
|
3793 |
+
$PosCount = $this->read_ushort();
|
3794 |
+
$save_pos = $this->_pos; // Save the point just after PosCount
|
3795 |
+
|
3796 |
+
$CoverageBacktrackGlyphs = array();
|
3797 |
+
for ($b=0;$b<$BacktrackGlyphCount;$b++) {
|
3798 |
+
$this->seek($CoverageBacktrackOffset[$b]);
|
3799 |
+
$glyphs = $this->_getCoverage();
|
3800 |
+
$CoverageBacktrackGlyphs[$b] = implode("|",$glyphs);
|
3801 |
+
}
|
3802 |
+
$CoverageInputGlyphs = array();
|
3803 |
+
for ($b=0;$b<$InputGlyphCount;$b++) {
|
3804 |
+
$this->seek($CoverageInputOffset[$b]);
|
3805 |
+
$glyphs = $this->_getCoverage();
|
3806 |
+
$CoverageInputGlyphs[$b] = implode("|",$glyphs);
|
3807 |
+
}
|
3808 |
+
$CoverageLookaheadGlyphs = array();
|
3809 |
+
for ($b=0;$b<$LookaheadGlyphCount;$b++) {
|
3810 |
+
$this->seek($CoverageLookaheadOffset[$b]);
|
3811 |
+
$glyphs = $this->_getCoverage();
|
3812 |
+
$CoverageLookaheadGlyphs[$b] = implode("|",$glyphs);
|
3813 |
+
}
|
3814 |
+
$matched = $this->checkContextMatchMultiple($CoverageInputGlyphs, $CoverageBacktrackGlyphs, $CoverageLookaheadGlyphs , $ignore, $ptr);
|
3815 |
+
if ($matched) {
|
3816 |
+
|
3817 |
+
$this->seek($save_pos); // Return to just after PosCount
|
3818 |
+
for ($p=0;$p<$PosCount;$p++) {
|
3819 |
+
// PosLookupRecord
|
3820 |
+
$PosLookupRecord[$p]['SequenceIndex'] = $this->read_ushort();
|
3821 |
+
$PosLookupRecord[$p]['LookupListIndex'] = $this->read_ushort();
|
3822 |
+
}
|
3823 |
+
for ($p=0;$p<$PosCount;$p++) {
|
3824 |
+
// Apply $PosLookupRecord[$p]['LookupListIndex'] at $PosLookupRecord[$p]['SequenceIndex']
|
3825 |
+
if ($PosLookupRecord[$p]['SequenceIndex'] >= $InputGlyphCount) { continue; }
|
3826 |
+
$lu = $PosLookupRecord[$p]['LookupListIndex'];
|
3827 |
+
$luType = $this->GPOSLookups[$lu]['Type'];
|
3828 |
+
$luFlag = $this->GPOSLookups[$lu]['Flag'];
|
3829 |
+
if (isset($this->GPOSLookups[$lu]['MarkFilteringSet'])) { $luMarkFilteringSet = $this->GPOSLookups[$lu]['MarkFilteringSet']; }
|
3830 |
+
else { $luMarkFilteringSet = ''; }
|
3831 |
+
|
3832 |
+
$luptr = $matched[$PosLookupRecord[$p]['SequenceIndex']];
|
3833 |
+
$lucurrGlyph = $this->OTLdata[$luptr]['hex'];
|
3834 |
+
$lucurrGID = $this->OTLdata[$luptr]['uni'];
|
3835 |
+
|
3836 |
+
foreach($this->GPOSLookups[$lu]['Subtables'] AS $luc=>$lusubtable_offset) {
|
3837 |
+
$shift = $this->_applyGPOSsubtable($lu, $luc, $luptr, $lucurrGlyph, $lucurrGID, ($lusubtable_offset - $this->GPOS_offset + $this->GSUB_length), $luType, $luFlag, $luMarkFilteringSet, $this->LuCoverage[$lu][$luc], $tag, 1, $is_old_spec);
|
3838 |
+
if ($this->debugOTL && $shift) { $this->_dumpproc('GPOS', $lookupID, $subtable, $Type, $PosFormat, $ptr, $currGlyph, $level); }
|
3839 |
+
if ($shift) { break; }
|
3840 |
+
}
|
3841 |
+
}
|
3842 |
+
}
|
3843 |
+
|
3844 |
+
|
3845 |
+
}
|
3846 |
+
else { die("GPOS Lookup Type ".$Type.", Format ".$PosFormat." not supported."); }
|
3847 |
+
}
|
3848 |
+
|
3849 |
+
else { die("GPOS Lookup Type ".$Type." not supported."); }
|
3850 |
+
}
|
3851 |
+
|
3852 |
+
//////////////////////////////////////////////////////////////////////////////////
|
3853 |
+
//////////////////////////////////////////////////////////////////////////////////
|
3854 |
+
// GPOS / GSUB / GCOM (common) functions
|
3855 |
+
//////////////////////////////////////////////////////////////////////////////////
|
3856 |
+
//////////////////////////////////////////////////////////////////////////////////
|
3857 |
+
|
3858 |
+
function checkContextMatch($Input, $Backtrack, $Lookahead, $ignore, $ptr) {
|
3859 |
+
// Input etc are single numbers - GSUB Format 6.1
|
3860 |
+
// Input starts with (1=>xxx)
|
3861 |
+
// return false if no match, else an array of ptr for matches (0=>0, 1=>3,...)
|
3862 |
+
|
3863 |
+
$current_syllable = (isset($this->OTLdata[$ptr]['syllable']) ? $this->OTLdata[$ptr]['syllable'] : 0);
|
3864 |
+
|
3865 |
+
// BACKTRACK
|
3866 |
+
$checkpos = $ptr;
|
3867 |
+
for ($i=0;$i<count($Backtrack);$i++) {
|
3868 |
+
$checkpos--;
|
3869 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) { $checkpos--; }
|
3870 |
+
// If outside scope of current syllable - return no match
|
3871 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$checkpos]['syllable']) && $this->OTLdata[$checkpos]['syllable'] != $current_syllable) {
|
3872 |
+
return false;
|
3873 |
+
}
|
3874 |
+
else if (!isset($this->OTLdata[$checkpos]) || $this->OTLdata[$checkpos]['uni'] != $Backtrack[$i]) {
|
3875 |
+
return false;
|
3876 |
+
}
|
3877 |
+
}
|
3878 |
+
|
3879 |
+
// INPUT
|
3880 |
+
$matched = array(0=>$ptr);
|
3881 |
+
$checkpos = $ptr;
|
3882 |
+
for ($i=1;$i<count($Input);$i++) {
|
3883 |
+
$checkpos++;
|
3884 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) { $checkpos++; }
|
3885 |
+
// If outside scope of current syllable - return no match
|
3886 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$checkpos]['syllable']) && $this->OTLdata[$checkpos]['syllable'] != $current_syllable) {
|
3887 |
+
return false;
|
3888 |
+
}
|
3889 |
+
else if (isset($this->OTLdata[$checkpos]) && $this->OTLdata[$checkpos]['uni'] == $Input[$i]) {
|
3890 |
+
$matched[] = $checkpos;
|
3891 |
+
}
|
3892 |
+
else { return false; }
|
3893 |
+
}
|
3894 |
+
|
3895 |
+
// LOOKAHEAD
|
3896 |
+
for ($i=0;$i<count($Lookahead);$i++) {
|
3897 |
+
$checkpos++;
|
3898 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) { $checkpos++; }
|
3899 |
+
// If outside scope of current syllable - return no match
|
3900 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$checkpos]['syllable']) && $this->OTLdata[$checkpos]['syllable'] != $current_syllable) {
|
3901 |
+
return false;
|
3902 |
+
}
|
3903 |
+
else if (!isset($this->OTLdata[$checkpos]) || $this->OTLdata[$checkpos]['uni'] != $Lookahead[$i]) {
|
3904 |
+
return false;
|
3905 |
+
}
|
3906 |
+
}
|
3907 |
+
|
3908 |
+
return $matched;
|
3909 |
+
}
|
3910 |
+
|
3911 |
+
|
3912 |
+
function checkContextMatchMultiple($Input, $Backtrack, $Lookahead, $ignore, $ptr, $class0excl='', $bclass0excl='', $lclass0excl='') {
|
3913 |
+
// Input etc are string/array of glyph strings - GSUB Format 5.2, 5.3, 6.2, 6.3, GPOS Format 7.2, 7.3, 8.2, 8.3
|
3914 |
+
// Input starts with (1=>xxx)
|
3915 |
+
// return false if no match, else an array of ptr for matches (0=>0, 1=>3,...)
|
3916 |
+
// $class0excl is the string of glyphs in all classes except Class 0 (GSUB 5.2, 6.2, GPOS 7.2, 8.2)
|
3917 |
+
// $bclass0excl & $lclass0excl are the same for lookahead and backtrack (GSUB 6.2, GPOS 8.2)
|
3918 |
+
|
3919 |
+
$current_syllable = (isset($this->OTLdata[$ptr]['syllable']) ? $this->OTLdata[$ptr]['syllable'] : 0);
|
3920 |
+
|
3921 |
+
// BACKTRACK
|
3922 |
+
$checkpos = $ptr;
|
3923 |
+
for ($i=0;$i<count($Backtrack);$i++) {
|
3924 |
+
$checkpos--;
|
3925 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) { $checkpos--; }
|
3926 |
+
// If outside scope of current syllable - return no match
|
3927 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$checkpos]['syllable']) && $this->OTLdata[$checkpos]['syllable'] != $current_syllable) {
|
3928 |
+
return false;
|
3929 |
+
}
|
3930 |
+
// If Class 0 specified, matches anything NOT in $bclass0excl
|
3931 |
+
else if (!$Backtrack[$i] && isset($this->OTLdata[$checkpos]) && strpos($bclass0excl,$this->OTLdata[$checkpos]['hex'])!==false) {
|
3932 |
+
return false;
|
3933 |
+
}
|
3934 |
+
else if (!isset($this->OTLdata[$checkpos]) || strpos($Backtrack[$i], $this->OTLdata[$checkpos]['hex'])===false) {
|
3935 |
+
return false;
|
3936 |
+
}
|
3937 |
+
}
|
3938 |
+
|
3939 |
+
// INPUT
|
3940 |
+
$matched = array(0=>$ptr);
|
3941 |
+
$checkpos = $ptr;
|
3942 |
+
for ($i=1;$i<count($Input);$i++) { // Start at 1 - already matched the first InputGlyph
|
3943 |
+
$checkpos++;
|
3944 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) { $checkpos++; }
|
3945 |
+
// If outside scope of current syllable - return no match
|
3946 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$checkpos]['syllable']) && $this->OTLdata[$checkpos]['syllable'] != $current_syllable) {
|
3947 |
+
return false;
|
3948 |
+
}
|
3949 |
+
// If Input Class 0 specified, matches anything NOT in $class0excl
|
3950 |
+
else if (!$Input[$i] && isset($this->OTLdata[$checkpos]) && strpos($class0excl,$this->OTLdata[$checkpos]['hex'])===false) {
|
3951 |
+
$matched[] = $checkpos;
|
3952 |
+
}
|
3953 |
+
else if (isset($this->OTLdata[$checkpos]) && strpos($Input[$i],$this->OTLdata[$checkpos]['hex'])!==false) {
|
3954 |
+
$matched[] = $checkpos;
|
3955 |
+
}
|
3956 |
+
else { return false; }
|
3957 |
+
}
|
3958 |
+
|
3959 |
+
// LOOKAHEAD
|
3960 |
+
for ($i=0;$i<count($Lookahead);$i++) {
|
3961 |
+
$checkpos++;
|
3962 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) { $checkpos++; }
|
3963 |
+
// If outside scope of current syllable - return no match
|
3964 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$checkpos]['syllable']) && $this->OTLdata[$checkpos]['syllable'] != $current_syllable) {
|
3965 |
+
return false;
|
3966 |
+
}
|
3967 |
+
// If Class 0 specified, matches anything NOT in $lclass0excl
|
3968 |
+
else if (!$Lookahead[$i] && isset($this->OTLdata[$checkpos]) && strpos($lclass0excl,$this->OTLdata[$checkpos]['hex'])!==false) {
|
3969 |
+
return false;
|
3970 |
+
}
|
3971 |
+
else if (!isset($this->OTLdata[$checkpos]) || strpos($Lookahead[$i],$this->OTLdata[$checkpos]['hex'])===false) {
|
3972 |
+
return false;
|
3973 |
+
}
|
3974 |
+
}
|
3975 |
+
return $matched;
|
3976 |
+
}
|
3977 |
+
|
3978 |
+
function checkContextMatchMultipleUni($Input, $Backtrack, $Lookahead, $ignore, $ptr, $class0excl=array(), $bclass0excl=array(), $lclass0excl=array()) {
|
3979 |
+
// Input etc are array of glyphs - GSUB Format 5.2, 5.3, 6.2, 6.3, GPOS Format 7.2, 7.3, 8.2, 8.3
|
3980 |
+
// Input starts with (1=>xxx)
|
3981 |
+
// return false if no match, else an array of ptr for matches (0=>0, 1=>3,...)
|
3982 |
+
// $class0excl is array of glyphs in all classes except Class 0 (GSUB 5.2, 6.2, GPOS 7.2, 8.2)
|
3983 |
+
// $bclass0excl & $lclass0excl are the same for lookahead and backtrack (GSUB 6.2, GPOS 8.2)
|
3984 |
+
|
3985 |
+
$current_syllable = (isset($this->OTLdata[$ptr]['syllable']) ? $this->OTLdata[$ptr]['syllable'] : 0);
|
3986 |
+
|
3987 |
+
// BACKTRACK
|
3988 |
+
$checkpos = $ptr;
|
3989 |
+
for ($i=0;$i<count($Backtrack);$i++) {
|
3990 |
+
$checkpos--;
|
3991 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) { $checkpos--; }
|
3992 |
+
// If outside scope of current syllable - return no match
|
3993 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$checkpos]['syllable']) && $this->OTLdata[$checkpos]['syllable'] != $current_syllable) {
|
3994 |
+
return false;
|
3995 |
+
}
|
3996 |
+
// If Class 0 specified, matches anything NOT in $bclass0excl
|
3997 |
+
else if (!$Backtrack[$i] && isset($this->OTLdata[$checkpos]) && isset($bclass0excl[$this->OTLdata[$checkpos]['uni']]) ) {
|
3998 |
+
return false;
|
3999 |
+
}
|
4000 |
+
else if (!isset($this->OTLdata[$checkpos]) || !isset($Backtrack[$i][$this->OTLdata[$checkpos]['uni']])) {
|
4001 |
+
return false;
|
4002 |
+
}
|
4003 |
+
}
|
4004 |
+
|
4005 |
+
// INPUT
|
4006 |
+
$matched = array(0=>$ptr);
|
4007 |
+
$checkpos = $ptr;
|
4008 |
+
for ($i=1;$i<count($Input);$i++) { // Start at 1 - already matched the first InputGlyph
|
4009 |
+
$checkpos++;
|
4010 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) { $checkpos++; }
|
4011 |
+
// If outside scope of current syllable - return no match
|
4012 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$checkpos]['syllable']) && $this->OTLdata[$checkpos]['syllable'] != $current_syllable) {
|
4013 |
+
return false;
|
4014 |
+
}
|
4015 |
+
// If Input Class 0 specified, matches anything NOT in $class0excl
|
4016 |
+
else if (!$Input[$i] && isset($this->OTLdata[$checkpos]) && !isset($class0excl[$this->OTLdata[$checkpos]['uni']]) ) {
|
4017 |
+
$matched[] = $checkpos;
|
4018 |
+
}
|
4019 |
+
else if (isset($this->OTLdata[$checkpos]) && isset($Input[$i][$this->OTLdata[$checkpos]['uni']])) {
|
4020 |
+
$matched[] = $checkpos;
|
4021 |
+
}
|
4022 |
+
else { return false; }
|
4023 |
+
}
|
4024 |
+
|
4025 |
+
// LOOKAHEAD
|
4026 |
+
for ($i=0;$i<count($Lookahead);$i++) {
|
4027 |
+
$checkpos++;
|
4028 |
+
while (isset($this->OTLdata[$checkpos]) && strpos($ignore, $this->OTLdata[$checkpos]['hex'])!==false) { $checkpos++; }
|
4029 |
+
// If outside scope of current syllable - return no match
|
4030 |
+
if ($this->restrictToSyllable && isset($this->OTLdata[$checkpos]['syllable']) && $this->OTLdata[$checkpos]['syllable'] != $current_syllable) {
|
4031 |
+
return false;
|
4032 |
+
}
|
4033 |
+
// If Class 0 specified, matches anything NOT in $lclass0excl
|
4034 |
+
else if (!$Lookahead[$i] && isset($this->OTLdata[$checkpos]) && isset($lclass0excl[$this->OTLdata[$checkpos]['uni']]) ) {
|
4035 |
+
return false;
|
4036 |
+
}
|
4037 |
+
else if (!isset($this->OTLdata[$checkpos]) || !isset($Lookahead[$i][$this->OTLdata[$checkpos]['uni']])) {
|
4038 |
+
return false;
|
4039 |
+
}
|
4040 |
+
}
|
4041 |
+
return $matched;
|
4042 |
+
}
|
4043 |
+
|
4044 |
+
|
4045 |
+
|
4046 |
+
|
4047 |
+
|
4048 |
+
function _getClassDefinitionTable($offset) {
|
4049 |
+
if (isset($this->LuDataCache[$this->fontkey][$offset])) {
|
4050 |
+
$GlyphByClass = $this->LuDataCache[$this->fontkey][$offset];
|
4051 |
+
}
|
4052 |
+
else {
|
4053 |
+
$this->seek($offset);
|
4054 |
+
$ClassFormat = $this->read_ushort();
|
4055 |
+
$GlyphClass = array();
|
4056 |
+
// $GlyphByClass = array(0=>array()); // NB This forces an index[0]
|
4057 |
+
if ($ClassFormat == 1) {
|
4058 |
+
$StartGlyph = $this->read_ushort();
|
4059 |
+
$GlyphCount = $this->read_ushort();
|
4060 |
+
for ($i=0;$i<$GlyphCount;$i++) {
|
4061 |
+
$GlyphClass[$i]['startGlyphID'] = $StartGlyph + $i;
|
4062 |
+
$GlyphClass[$i]['endGlyphID'] = $StartGlyph + $i;
|
4063 |
+
$GlyphClass[$i]['class'] = $this->read_ushort();
|
4064 |
+
for($g=$GlyphClass[$i]['startGlyphID'];$g<=$GlyphClass[$i]['endGlyphID'];$g++) {
|
4065 |
+
$GlyphByClass[$GlyphClass[$i]['class']][] = $this->glyphToChar($g);
|
4066 |
+
}
|
4067 |
+
}
|
4068 |
+
}
|
4069 |
+
else if ($ClassFormat == 2) {
|
4070 |
+
$tableCount = $this->read_ushort();
|
4071 |
+
for ($i=0;$i<$tableCount;$i++) {
|
4072 |
+
$GlyphClass[$i]['startGlyphID'] = $this->read_ushort();
|
4073 |
+
$GlyphClass[$i]['endGlyphID'] = $this->read_ushort();
|
4074 |
+
$GlyphClass[$i]['class'] = $this->read_ushort();
|
4075 |
+
for($g=$GlyphClass[$i]['startGlyphID'];$g<=$GlyphClass[$i]['endGlyphID'];$g++) {
|
4076 |
+
$GlyphByClass[$GlyphClass[$i]['class']][] = $this->glyphToChar($g);
|
4077 |
+
}
|
4078 |
+
}
|
4079 |
+
}
|
4080 |
+
ksort($GlyphByClass);
|
4081 |
+
$this->LuDataCache[$this->fontkey][$offset] = $GlyphByClass;
|
4082 |
+
}
|
4083 |
+
return $GlyphByClass;
|
4084 |
+
}
|
4085 |
+
|
4086 |
+
function count_bits($n) {
|
4087 |
+
for ($c=0; $n; $c++) {
|
4088 |
+
$n &= $n - 1; // clear the least significant bit set
|
4089 |
+
}
|
4090 |
+
return $c;
|
4091 |
+
}
|
4092 |
+
|
4093 |
+
function _getValueRecord($ValueFormat) { // Common ValueRecord for GPOS
|
4094 |
+
// Only returns 3 possible: $vra['XPlacement'] $vra['YPlacement'] $vra['XAdvance']
|
4095 |
+
$vra = array();
|
4096 |
+
// Horizontal adjustment for placement - in design units
|
4097 |
+
if (($ValueFormat & 0x0001) == 0x0001) { $vra['XPlacement'] = $this->read_short(); }
|
4098 |
+
// Vertical adjustment for placement - in design units
|
4099 |
+
if (($ValueFormat & 0x0002) == 0x0002) { $vra['YPlacement'] = $this->read_short(); }
|
4100 |
+
// Horizontal adjustment for advance - in design units (only used for horizontal writing)
|
4101 |
+
if (($ValueFormat & 0x0004) == 0x0004) { $vra['XAdvance'] = $this->read_short(); }
|
4102 |
+
// Vertical adjustment for advance - in design units (only used for vertical writing)
|
4103 |
+
if (($ValueFormat & 0x0008) == 0x0008) { $this->read_short(); }
|
4104 |
+
// Offset to Device table for horizontal placement-measured from beginning of PosTable (may be NULL)
|
4105 |
+
if (($ValueFormat & 0x0010) == 0x0010) { $this->read_ushort(); }
|
4106 |
+
// Offset to Device table for vertical placement-measured from beginning of PosTable (may be NULL)
|
4107 |
+
if (($ValueFormat & 0x0020) == 0x0020) { $this->read_ushort(); }
|
4108 |
+
// Offset to Device table for horizontal advance-measured from beginning of PosTable (may be NULL)
|
4109 |
+
if (($ValueFormat & 0x0040) == 0x0040) { $this->read_ushort(); }
|
4110 |
+
// Offset to Device table for vertical advance-measured from beginning of PosTable (may be NULL)
|
4111 |
+
if (($ValueFormat & 0x0080) == 0x0080) { $this->read_ushort(); }
|
4112 |
+
return $vra;
|
4113 |
+
}
|
4114 |
+
|
4115 |
+
function _getAnchorTable($offset=0) {
|
4116 |
+
if ($offset) { $this->seek($offset); }
|
4117 |
+
$AnchorFormat = $this->read_ushort();
|
4118 |
+
$XCoordinate = $this->read_short();
|
4119 |
+
$YCoordinate = $this->read_short();
|
4120 |
+
// Format 2 specifies additional link to contour point; Format 3 additional Device table
|
4121 |
+
return array($XCoordinate, $YCoordinate);
|
4122 |
+
}
|
4123 |
+
|
4124 |
+
function _getMarkRecord($offset, $MarkPos) {
|
4125 |
+
$this->seek($offset);
|
4126 |
+
$MarkCount = $this->read_ushort();
|
4127 |
+
$this->skip($MarkPos*4);
|
4128 |
+
$Class = $this->read_ushort();
|
4129 |
+
$MarkAnchor = $offset + $this->read_ushort(); // = Offset to anchor table
|
4130 |
+
list($x,$y) = $this->_getAnchorTable($MarkAnchor );
|
4131 |
+
$MarkRecord = array('Class'=>$Class, 'AnchorX'=>$x, 'AnchorY'=>$y);
|
4132 |
+
return $MarkRecord;
|
4133 |
+
}
|
4134 |
+
|
4135 |
+
function _getGCOMignoreString($flag, $MarkFilteringSet) {
|
4136 |
+
// If ignoreFlag set, combine all ignore glyphs into -> "(?:( 0FBA1| 0FBA2| 0FBA3)*)"
|
4137 |
+
// else "()"
|
4138 |
+
// for Input - set on secondary Lookup table if in Context, and set Backtrack and Lookahead on Context Lookup
|
4139 |
+
$str = "";
|
4140 |
+
$ignoreflag = 0;
|
4141 |
+
|
4142 |
+
// Flag & 0xFF?? = MarkAttachmentType
|
4143 |
+
if ($flag & 0xFF00) {
|
4144 |
+
// "a lookup must ignore any mark glyphs that are not in the specified mark attachment class"
|
4145 |
+
// $this->MarkAttachmentType is already adjusted for this i.e. contains all Marks except those in the MarkAttachmentClassDef table
|
4146 |
+
$MarkAttachmentType = $flag >> 8;
|
4147 |
+
$ignoreflag = $flag;
|
4148 |
+
$str = $this->MarkAttachmentType[$MarkAttachmentType];
|
4149 |
+
}
|
4150 |
+
|
4151 |
+
// Flag & 0x0010 = UseMarkFilteringSet
|
4152 |
+
if ($flag & 0x0010) {
|
4153 |
+
die("This font [".$this->fontkey."] contains MarkGlyphSets - Not tested yet");
|
4154 |
+
// Change also in ttfontsuni.php
|
4155 |
+
if ($MarkFilteringSet=='') die("This font [".$this->fontkey."] contains MarkGlyphSets - but MarkFilteringSet not set");
|
4156 |
+
$str = $this->MarkGlyphSets[$MarkFilteringSet];
|
4157 |
+
}
|
4158 |
+
|
4159 |
+
// If Ignore Marks set, supercedes any above
|
4160 |
+
// Flag & 0x0008 = Ignore Marks - (unless already done with MarkAttachmentType)
|
4161 |
+
if (($flag & 0x0008) == 0x0008 && ($flag & 0xFF00) == 0) {
|
4162 |
+
$ignoreflag = 8;
|
4163 |
+
$str = $this->GlyphClassMarks;
|
4164 |
+
}
|
4165 |
+
|
4166 |
+
// Flag & 0x0004 = Ignore Ligatures
|
4167 |
+
if (($flag & 0x0004) == 0x0004) {
|
4168 |
+
$ignoreflag += 4;
|
4169 |
+
if ($str) { $str .= "|"; }
|
4170 |
+
$str .= $this->GlyphClassLigatures;
|
4171 |
+
}
|
4172 |
+
// Flag & 0x0002 = Ignore BaseGlyphs
|
4173 |
+
if (($flag & 0x0002) == 0x0002) {
|
4174 |
+
$ignoreflag += 2;
|
4175 |
+
if ($str) { $str .= "|"; }
|
4176 |
+
$str .= $this->GlyphClassBases;
|
4177 |
+
}
|
4178 |
+
if ($str) { return "((?:(?:" . $str . "))*)"; }
|
4179 |
+
else return "()";
|
4180 |
+
}
|
4181 |
+
|
4182 |
+
function _checkGCOMignore($flag, $glyph, $MarkFilteringSet) {
|
4183 |
+
$ignore = false;
|
4184 |
+
// Flag & 0x0008 = Ignore Marks - (unless already done with MarkAttachmentType)
|
4185 |
+
if (($flag & 0x0008 && ($flag & 0xFF00) == 0) && strpos($this->GlyphClassMarks,$glyph)) { $ignore = true; }
|
4186 |
+
if (($flag & 0x0004) && strpos($this->GlyphClassLigatures,$glyph)) { $ignore = true; }
|
4187 |
+
if (($flag & 0x0002) && strpos($this->GlyphClassBases,$glyph)) { $ignore = true; }
|
4188 |
+
// Flag & 0xFF?? = MarkAttachmentType
|
4189 |
+
if ($flag & 0xFF00) {
|
4190 |
+
// "a lookup must ignore any mark glyphs that are not in the specified mark attachment class"
|
4191 |
+
// $this->MarkAttachmentType is already adjusted for this i.e. contains all Marks except those in the MarkAttachmentClassDef table
|
4192 |
+
if (strpos($this->MarkAttachmentType[($flag >> 8)],$glyph)) { $ignore = true; }
|
4193 |
+
}
|
4194 |
+
// Flag & 0x0010 = UseMarkFilteringSet
|
4195 |
+
if (($flag & 0x0010) && strpos($this->MarkGlyphSets[$MarkFilteringSet],$glyph)) { $ignore = true; }
|
4196 |
+
return $ignore;
|
4197 |
+
}
|
4198 |
+
|
4199 |
+
////////////////////////////////////////////////////////////////
|
4200 |
+
////////////////////////////////////////////////////////////////
|
4201 |
+
////////// BIDI ALGORITHM ////////////////////////
|
4202 |
+
////////////////////////////////////////////////////////////////
|
4203 |
+
////////////////////////////////////////////////////////////////
|
4204 |
+
////////////////////////////////////////////////////////////////
|
4205 |
+
////////////////////////////////////////////////////////////////
|
4206 |
+
// These functions are called from mpdf after GSUB/GPOS has taken place
|
4207 |
+
// At this stage the bidi-type is in string form
|
4208 |
+
////////////////////////////////////////////////////////////////
|
4209 |
+
////////////////////////////////////////////////////////////////
|
4210 |
+
/*
|
4211 |
+
Bidirectional Character Types
|
4212 |
+
=============================
|
4213 |
+
Type Description General Scope
|
4214 |
+
Strong
|
4215 |
+
L Left-to-Right LRM, most alphabetic, syllabic, Han ideographs, non-European or non-Arabic digits, ...
|
4216 |
+
LRE Left-to-Right Embedding LRE
|
4217 |
+
LRO Left-to-Right Override LRO
|
4218 |
+
R Right-to-Left RLM, Hebrew alphabet, and related punctuation
|
4219 |
+
AL Right-to-Left Arabic Arabic, Thaana, and Syriac alphabets, most punctuation specific to those scripts, ...
|
4220 |
+
RLE Right-to-Left Embedding RLE
|
4221 |
+
RLO Right-to-Left Override RLO
|
4222 |
+
Weak
|
4223 |
+
PDF Pop Directional Format PDF
|
4224 |
+
EN European Number European digits, Eastern Arabic-Indic digits, ...
|
4225 |
+
ES European Number Separator Plus sign, minus sign
|
4226 |
+
ET European Number Terminator Degree sign, currency symbols, ...
|
4227 |
+
AN Arabic Number Arabic-Indic digits, Arabic decimal and thousands separators, ...
|
4228 |
+
CS Common Number Separator Colon, comma, full stop (period), No-break space, ...
|
4229 |
+
NSM Nonspacing Mark Characters marked Mn (Nonspacing_Mark) and Me (Enclosing_Mark) in the Unicode Character Database
|
4230 |
+
BN Boundary Neutral Default ignorables, non-characters, and control characters, other than those explicitly given other types.
|
4231 |
+
Neutral
|
4232 |
+
B Paragraph Separator Paragraph separator, appropriate Newline Functions, higher-level protocol paragraph determination
|
4233 |
+
S Segment Separator Tab
|
4234 |
+
WS Whitespace Space, figure space, line separator, form feed, General Punctuation spaces, ...
|
4235 |
+
ON Other Neutrals All other characters, including OBJECT REPLACEMENT CHARACTER
|
4236 |
+
*/
|
4237 |
+
|
4238 |
+
function _bidiSort($ta, $str='', $dir, &$chunkOTLdata, $useGPOS) {
|
4239 |
+
|
4240 |
+
$pel = 0; // paragraph embedding level
|
4241 |
+
$maxlevel = 0;
|
4242 |
+
$numchars = count($chunkOTLdata['char_data']);
|
4243 |
+
|
4244 |
+
// Set the initial paragraph embedding level
|
4245 |
+
if ($dir == 'rtl') { $pel = 1; }
|
4246 |
+
else { $pel = 0; }
|
4247 |
+
|
4248 |
+
|
4249 |
+
// X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral.
|
4250 |
+
// Current Embedding Level
|
4251 |
+
$cel = $pel;
|
4252 |
+
// directional override status (-1 is Neutral)
|
4253 |
+
$dos = -1;
|
4254 |
+
$remember = array();
|
4255 |
+
|
4256 |
+
// Array of characters data
|
4257 |
+
$chardata = Array();
|
4258 |
+
|
4259 |
+
// Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase.
|
4260 |
+
// In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
|
4261 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4262 |
+
if ($chunkOTLdata['char_data'][$i]['uni'] == 8235) { // RLE
|
4263 |
+
// X2. With each RLE, compute the least greater odd embedding level.
|
4264 |
+
// a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
|
4265 |
+
// b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
|
4266 |
+
$next_level = $cel + ($cel % 2) + 1;
|
4267 |
+
if ($next_level < 62) {
|
4268 |
+
$remember[] = array('num' => 8235, 'cel' => $cel, 'dos' => $dos);
|
4269 |
+
$cel = $next_level;
|
4270 |
+
$dos = -1;
|
4271 |
+
}
|
4272 |
+
}
|
4273 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8234) { // LRE
|
4274 |
+
// X3. With each LRE, compute the least greater even embedding level.
|
4275 |
+
// a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
|
4276 |
+
// b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
|
4277 |
+
$next_level = $cel + 2 - ($cel % 2);
|
4278 |
+
if ( $next_level < 62 ) {
|
4279 |
+
$remember[] = array('num' => 8234, 'cel' => $cel, 'dos' => $dos);
|
4280 |
+
$cel = $next_level;
|
4281 |
+
$dos = -1;
|
4282 |
+
}
|
4283 |
+
}
|
4284 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8238) { // RLO
|
4285 |
+
// X4. With each RLO, compute the least greater odd embedding level.
|
4286 |
+
// a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left.
|
4287 |
+
// b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
|
4288 |
+
$next_level = $cel + ($cel % 2) + 1;
|
4289 |
+
if ($next_level < 62) {
|
4290 |
+
$remember[] = array('num' => 8238, 'cel' => $cel, 'dos' => $dos);
|
4291 |
+
$cel = $next_level;
|
4292 |
+
$dos = UCDN::BIDI_CLASS_R;
|
4293 |
+
}
|
4294 |
+
}
|
4295 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8237) { // LRO
|
4296 |
+
// X5. With each LRO, compute the least greater even embedding level.
|
4297 |
+
// a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right.
|
4298 |
+
// b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
|
4299 |
+
$next_level = $cel + 2 - ($cel % 2);
|
4300 |
+
if ( $next_level < 62 ) {
|
4301 |
+
$remember[] = array('num' => 8237, 'cel' => $cel, 'dos' => $dos);
|
4302 |
+
$cel = $next_level;
|
4303 |
+
$dos = UCDN::BIDI_CLASS_L;
|
4304 |
+
}
|
4305 |
+
}
|
4306 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8236) { // PDF
|
4307 |
+
// X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override.
|
4308 |
+
if (count($remember)) {
|
4309 |
+
$last = count($remember ) - 1;
|
4310 |
+
if (($remember[$last]['num'] == 8235) || ($remember[$last]['num'] == 8234) || ($remember[$last]['num'] == 8238) ||
|
4311 |
+
($remember[$last]['num'] == 8237)) {
|
4312 |
+
$match = array_pop($remember);
|
4313 |
+
$cel = $match['cel'];
|
4314 |
+
$dos = $match['dos'];
|
4315 |
+
}
|
4316 |
+
}
|
4317 |
+
}
|
4318 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 10) { // NEW LINE
|
4319 |
+
// Reset to start values
|
4320 |
+
$cel = $pel;
|
4321 |
+
$dos = -1;
|
4322 |
+
$remember = array();
|
4323 |
+
}
|
4324 |
+
else {
|
4325 |
+
// X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
|
4326 |
+
// a. Set the level of the current character to the current embedding level.
|
4327 |
+
// b. When the directional override status is not neutral, reset the current character type to directional override status.
|
4328 |
+
if ($dos != -1) { $chardir = $dos; }
|
4329 |
+
else {
|
4330 |
+
$chardir = $chunkOTLdata['char_data'][$i]['bidi_class'];
|
4331 |
+
}
|
4332 |
+
// stores string characters and other information
|
4333 |
+
if (isset($chunkOTLdata['GPOSinfo'][$i])) { $gpos = $chunkOTLdata['GPOSinfo'][$i]; }
|
4334 |
+
else $gpos = '';
|
4335 |
+
$chardata[] = array('char' => $chunkOTLdata['char_data'][$i]['uni'], 'level' => $cel, 'type' => $chardir, 'group' => $chunkOTLdata['group']{$i}, 'GPOSinfo' => $gpos);
|
4336 |
+
}
|
4337 |
+
}
|
4338 |
+
|
4339 |
+
$numchars = count($chardata);
|
4340 |
+
|
4341 |
+
// X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph.
|
4342 |
+
// Paragraph separators are not included in the embedding.
|
4343 |
+
// X9. Remove all RLE, LRE, RLO, LRO, and PDF codes.
|
4344 |
+
// This is effectively done by only saving other codes to chardata
|
4345 |
+
|
4346 |
+
// X10. Determine the start-of-sequence (sor) and end-of-sequence (eor) types, either L or R, for each isolating run sequence. These depend on the higher of the two levels on either side of the sequence boundary:
|
4347 |
+
// For sor, compare the level of the first character in the sequence with the level of the character preceding it in the paragraph or if there is none, with the paragraph embedding level.
|
4348 |
+
// For eor, compare the level of the last character in the sequence with the level of the character following it in the paragraph or if there is none, with the paragraph embedding level.
|
4349 |
+
// If the higher level is odd, the sor or eor is R; otherwise, it is L.
|
4350 |
+
|
4351 |
+
$prelevel = $pel;
|
4352 |
+
$postlevel = $pel;
|
4353 |
+
$cel = $prelevel; // current embedding level
|
4354 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4355 |
+
$level = $chardata[$i]['level'];
|
4356 |
+
if ($i==0) { $left = $prelevel; }
|
4357 |
+
else { $left = $chardata[$i-1]['level']; }
|
4358 |
+
if ($i==($numchars-1)) { $right = $postlevel; }
|
4359 |
+
else { $right = $chardata[$i+1]['level']; }
|
4360 |
+
$chardata[$i]['sor'] = max($left, $level) % 2 ? UCDN::BIDI_CLASS_R : UCDN::BIDI_CLASS_L;
|
4361 |
+
$chardata[$i]['eor'] = max($right, $level) % 2 ? UCDN::BIDI_CLASS_R : UCDN::BIDI_CLASS_L;
|
4362 |
+
}
|
4363 |
+
|
4364 |
+
|
4365 |
+
|
4366 |
+
// 3.3.3 Resolving Weak Types
|
4367 |
+
// Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used.
|
4368 |
+
// Nonspacing marks are now resolved based on the previous characters.
|
4369 |
+
|
4370 |
+
// W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor.
|
4371 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4372 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_NSM) {
|
4373 |
+
if ($i==0 || $chardata[$i]['level']!=$chardata[$i-1]['level']) {
|
4374 |
+
$chardata[$i]['type'] = $chardata[$i]['sor'];
|
4375 |
+
}
|
4376 |
+
else {
|
4377 |
+
$chardata[$i]['type'] = $chardata[($i-1)]['type'];
|
4378 |
+
}
|
4379 |
+
}
|
4380 |
+
}
|
4381 |
+
|
4382 |
+
// W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number.
|
4383 |
+
$prevlevel = -1;
|
4384 |
+
$levcount = 0;
|
4385 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4386 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_EN) {
|
4387 |
+
$found = false;
|
4388 |
+
for ($j=$levcount; $j >= 0; $j--) {
|
4389 |
+
if ($chardata[$j]['type'] == UCDN::BIDI_CLASS_AL) { $chardata[$i]['type'] = UCDN::BIDI_CLASS_AN; $found = true; break; }
|
4390 |
+
else if (($chardata[$j]['type'] == UCDN::BIDI_CLASS_L) || ($chardata[$j]['type'] == UCDN::BIDI_CLASS_R)) { $found = true; break; }
|
4391 |
+
}
|
4392 |
+
}
|
4393 |
+
if ($chardata[$i]['level'] != $prevlevel) { $levcount = 0; }
|
4394 |
+
else { ++$levcount; }
|
4395 |
+
$prevlevel = $chardata[$i]['level'];
|
4396 |
+
}
|
4397 |
+
|
4398 |
+
// W3. Change all ALs to R.
|
4399 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4400 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_AL) { $chardata[$i]['type'] = UCDN::BIDI_CLASS_R; }
|
4401 |
+
}
|
4402 |
+
|
4403 |
+
// W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type.
|
4404 |
+
for ($i=1; $i < $numchars; ++$i) {
|
4405 |
+
if ( ($i+1) < $numchars && $chardata[($i)]['level'] == $chardata[($i+1)]['level'] && $chardata[($i)]['level'] == $chardata[($i-1)]['level']) {
|
4406 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ES && $chardata[($i-1)]['type'] == UCDN::BIDI_CLASS_EN && $chardata[($i+1)]['type'] == UCDN::BIDI_CLASS_EN) {
|
4407 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_EN;
|
4408 |
+
}
|
4409 |
+
else if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_CS && $chardata[($i-1)]['type'] == UCDN::BIDI_CLASS_EN && $chardata[($i+1)]['type'] == UCDN::BIDI_CLASS_EN) {
|
4410 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_EN;
|
4411 |
+
}
|
4412 |
+
else if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_CS && $chardata[($i-1)]['type'] == UCDN::BIDI_CLASS_AN && $chardata[($i+1)]['type'] == UCDN::BIDI_CLASS_AN) {
|
4413 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_AN;
|
4414 |
+
}
|
4415 |
+
}
|
4416 |
+
}
|
4417 |
+
|
4418 |
+
// W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
|
4419 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4420 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ET) {
|
4421 |
+
if ($i > 0 && $chardata[($i-1)]['type'] == UCDN::BIDI_CLASS_EN && $chardata[($i)]['level'] == $chardata[($i-1)]['level']) {
|
4422 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_EN;
|
4423 |
+
}
|
4424 |
+
else {
|
4425 |
+
$j = $i+1;
|
4426 |
+
while ($j < $numchars && $chardata[$j]['level'] == $chardata[$i]['level'] ) {
|
4427 |
+
if ($chardata[$j]['type'] == UCDN::BIDI_CLASS_EN) {
|
4428 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_EN;
|
4429 |
+
break;
|
4430 |
+
}
|
4431 |
+
else if ($chardata[$j]['type'] != UCDN::BIDI_CLASS_ET) { break; }
|
4432 |
+
++$j;
|
4433 |
+
}
|
4434 |
+
}
|
4435 |
+
}
|
4436 |
+
}
|
4437 |
+
|
4438 |
+
// W6. Otherwise, separators and terminators change to Other Neutral.
|
4439 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4440 |
+
if (($chardata[$i]['type'] == UCDN::BIDI_CLASS_ET) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ES) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_CS)) {
|
4441 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_ON;
|
4442 |
+
}
|
4443 |
+
}
|
4444 |
+
|
4445 |
+
//W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L.
|
4446 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4447 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_EN) {
|
4448 |
+
if ($i==0) { // Start of Level run
|
4449 |
+
if ($chardata[$i]['sor']==UCDN::BIDI_CLASS_L) $chardata[$i]['type'] = $chardata[$i]['sor'];
|
4450 |
+
}
|
4451 |
+
else {
|
4452 |
+
for ($j=$i-1; $j >= 0; $j--) {
|
4453 |
+
if ($chardata[$j]['level'] != $chardata[$i]['level']) { // Level run boundary
|
4454 |
+
if ($chardata[$j+1]['sor']==UCDN::BIDI_CLASS_L) $chardata[$i]['type'] = $chardata[$j+1]['sor'];
|
4455 |
+
break;
|
4456 |
+
}
|
4457 |
+
else if ($chardata[$j]['type'] == UCDN::BIDI_CLASS_L) {
|
4458 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_L;
|
4459 |
+
break;
|
4460 |
+
}
|
4461 |
+
else if ($chardata[$j]['type'] == UCDN::BIDI_CLASS_R) {
|
4462 |
+
break;
|
4463 |
+
}
|
4464 |
+
}
|
4465 |
+
}
|
4466 |
+
}
|
4467 |
+
}
|
4468 |
+
|
4469 |
+
// N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries.
|
4470 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4471 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ON || $chardata[$i]['type'] == UCDN::BIDI_CLASS_WS) {
|
4472 |
+
$left = -1;
|
4473 |
+
// LEFT
|
4474 |
+
if ($i==0) { // first char
|
4475 |
+
$left = $chardata[($i)]['sor'];
|
4476 |
+
}
|
4477 |
+
else if ($chardata[($i-1)]['level'] != $chardata[($i)]['level']) { // run boundary
|
4478 |
+
$left = $chardata[($i)]['sor'];
|
4479 |
+
}
|
4480 |
+
else if ($chardata[($i-1)]['type'] == UCDN::BIDI_CLASS_L) {
|
4481 |
+
$left = UCDN::BIDI_CLASS_L;
|
4482 |
+
}
|
4483 |
+
else if ($chardata[($i-1)]['type'] == UCDN::BIDI_CLASS_R || $chardata[($i-1)]['type'] == UCDN::BIDI_CLASS_EN || $chardata[($i-1)]['type'] == UCDN::BIDI_CLASS_AN) {
|
4484 |
+
$left = UCDN::BIDI_CLASS_R;
|
4485 |
+
}
|
4486 |
+
// RIGHT
|
4487 |
+
$right = -1;
|
4488 |
+
$j=$i;
|
4489 |
+
// move to the right of any following neutrals OR hit a run boundary
|
4490 |
+
while(($chardata[$j]['type'] == UCDN::BIDI_CLASS_ON || $chardata[$j]['type'] == UCDN::BIDI_CLASS_WS) && $j<=($numchars-1)) {
|
4491 |
+
if ($j==($numchars-1)) { // last char
|
4492 |
+
$right = $chardata[($j)]['eor'];
|
4493 |
+
break;
|
4494 |
+
}
|
4495 |
+
else if ($chardata[($j+1)]['level'] != $chardata[($j)]['level']) { // run boundary
|
4496 |
+
$right = $chardata[($j)]['eor'];
|
4497 |
+
break;
|
4498 |
+
}
|
4499 |
+
else if ($chardata[($j+1)]['type'] == UCDN::BIDI_CLASS_L) {
|
4500 |
+
$right = UCDN::BIDI_CLASS_L;
|
4501 |
+
break;
|
4502 |
+
}
|
4503 |
+
else if ($chardata[($j+1)]['type'] == UCDN::BIDI_CLASS_R || $chardata[($j+1)]['type'] == UCDN::BIDI_CLASS_EN || $chardata[($j+1)]['type'] == UCDN::BIDI_CLASS_AN) {
|
4504 |
+
$right = UCDN::BIDI_CLASS_R;
|
4505 |
+
break;
|
4506 |
+
}
|
4507 |
+
$j++;
|
4508 |
+
}
|
4509 |
+
if ($left > -1 && $left==$right) {
|
4510 |
+
$chardata[$i]['orig_type'] = $chardata[$i]['type']; // Need to store the original 'WS' for reference in L1 below
|
4511 |
+
$chardata[$i]['type'] = $left;
|
4512 |
+
}
|
4513 |
+
}
|
4514 |
+
}
|
4515 |
+
|
4516 |
+
// N2. Any remaining neutrals take the embedding direction
|
4517 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4518 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ON || $chardata[$i]['type'] == UCDN::BIDI_CLASS_WS) {
|
4519 |
+
$chardata[$i]['type'] = ($chardata[$i]['level'] % 2) ? UCDN::BIDI_CLASS_R : UCDN::BIDI_CLASS_L;
|
4520 |
+
$chardata[$i]['orig_type'] = $chardata[$i]['type']; // Need to store the original 'WS' for reference in L1 below
|
4521 |
+
}
|
4522 |
+
}
|
4523 |
+
|
4524 |
+
// I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels.
|
4525 |
+
// I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
|
4526 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4527 |
+
$odd = $chardata[$i]['level'] % 2;
|
4528 |
+
if ($odd) {
|
4529 |
+
if (($chardata[$i]['type'] == UCDN::BIDI_CLASS_L) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_AN) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_EN)) {
|
4530 |
+
$chardata[$i]['level'] += 1;
|
4531 |
+
}
|
4532 |
+
}
|
4533 |
+
else {
|
4534 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_R) { $chardata[$i]['level'] += 1; }
|
4535 |
+
else if (($chardata[$i]['type'] == UCDN::BIDI_CLASS_AN) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_EN)) { $chardata[$i]['level'] += 2; }
|
4536 |
+
}
|
4537 |
+
$maxlevel = max($chardata[$i]['level'],$maxlevel);
|
4538 |
+
}
|
4539 |
+
|
4540 |
+
// NB
|
4541 |
+
// Separate into lines at this point************
|
4542 |
+
//
|
4543 |
+
|
4544 |
+
// L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
|
4545 |
+
// 1. Segment separators (Tab) 'S',
|
4546 |
+
// 2. Paragraph separators 'B',
|
4547 |
+
// 3. Any sequence of whitespace characters 'WS' preceding a segment separator or paragraph separator, and
|
4548 |
+
// 4. Any sequence of whitespace characters 'WS' at the end of the line.
|
4549 |
+
// The types of characters used here are the original types, not those modified by the previous phase cf N1 and N2*******
|
4550 |
+
// Because a Paragraph Separator breaks lines, there will be at most one per line, at the end of that line.
|
4551 |
+
|
4552 |
+
for ($i=($numchars-1); $i>0; $i--) {
|
4553 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_WS || (isset($chardata[$i]['orig_type']) && $chardata[$i]['orig_type'] == UCDN::BIDI_CLASS_WS)) {
|
4554 |
+
$chardata[$i]['level'] = $pel;
|
4555 |
+
}
|
4556 |
+
else { break; }
|
4557 |
+
}
|
4558 |
+
|
4559 |
+
|
4560 |
+
// L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
|
4561 |
+
for ($j=$maxlevel; $j > 0; $j--) {
|
4562 |
+
$ordarray = array();
|
4563 |
+
$revarr = array();
|
4564 |
+
$onlevel = false;
|
4565 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4566 |
+
if ($chardata[$i]['level'] >= $j) {
|
4567 |
+
$onlevel = true;
|
4568 |
+
|
4569 |
+
// L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true.
|
4570 |
+
if (isset(UCDN::$mirror_pairs[$chardata[$i]['char']]) && $chardata[$i]['type']==UCDN::BIDI_CLASS_R) {
|
4571 |
+
$chardata[$i]['char'] = UCDN::$mirror_pairs[$chardata[$i]['char']];
|
4572 |
+
}
|
4573 |
+
|
4574 |
+
$revarr[] = $chardata[$i];
|
4575 |
+
}
|
4576 |
+
else {
|
4577 |
+
if ($onlevel) {
|
4578 |
+
$revarr = array_reverse($revarr);
|
4579 |
+
$ordarray = array_merge($ordarray, $revarr);
|
4580 |
+
$revarr = Array();
|
4581 |
+
$onlevel = false;
|
4582 |
+
}
|
4583 |
+
$ordarray[] = $chardata[$i];
|
4584 |
+
}
|
4585 |
+
}
|
4586 |
+
if ($onlevel) {
|
4587 |
+
$revarr = array_reverse($revarr);
|
4588 |
+
$ordarray = array_merge($ordarray, $revarr);
|
4589 |
+
}
|
4590 |
+
$chardata = $ordarray;
|
4591 |
+
}
|
4592 |
+
|
4593 |
+
$group = '';
|
4594 |
+
$e = '';
|
4595 |
+
$GPOS = array();
|
4596 |
+
$cctr = 0;
|
4597 |
+
$rtl_content = 0x0;
|
4598 |
+
foreach ($chardata as $cd) {
|
4599 |
+
$e.=code2utf($cd['char']);
|
4600 |
+
$group .= $cd['group'];
|
4601 |
+
if ($useGPOS && is_array($cd['GPOSinfo'])) {
|
4602 |
+
$GPOS[$cctr] = $cd['GPOSinfo'];
|
4603 |
+
$GPOS[$cctr]['wDir'] = ($cd['level'] % 2) ? 'RTL' : 'LTR';
|
4604 |
+
}
|
4605 |
+
if($cd['type']==UCDN::BIDI_CLASS_L) { $rtl_content |= 1; }
|
4606 |
+
else if($cd['type']==UCDN::BIDI_CLASS_R) { $rtl_content |= 2; }
|
4607 |
+
$cctr++;
|
4608 |
+
}
|
4609 |
+
|
4610 |
+
|
4611 |
+
$chunkOTLdata['group'] = $group ;
|
4612 |
+
if ($useGPOS) {
|
4613 |
+
$chunkOTLdata['GPOSinfo'] = $GPOS;
|
4614 |
+
}
|
4615 |
+
|
4616 |
+
return array($e,$rtl_content);
|
4617 |
+
}
|
4618 |
+
|
4619 |
+
// **********************************************************************************************
|
4620 |
+
// The following versions for BidiSort work on amalgamated chunks to process the whole paragraph
|
4621 |
+
// Firstly set the level in the OTLdata - called from fn printbuffer() [_bidiPrepare]
|
4622 |
+
// Secondly re-order - called from fn writeFlowingBlock and FinishFlowingBlock, when already divided into lines. [_bidiReorder]
|
4623 |
+
// **********************************************************************************************
|
4624 |
+
|
4625 |
+
function _bidiPrepare(&$para, $dir) {
|
4626 |
+
|
4627 |
+
// Set the initial paragraph embedding level
|
4628 |
+
$pel = 0; // paragraph embedding level
|
4629 |
+
if ($dir == 'rtl') { $pel = 1; }
|
4630 |
+
|
4631 |
+
// X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral.
|
4632 |
+
// Current Embedding Level
|
4633 |
+
$cel = $pel;
|
4634 |
+
// directional override status (-1 is Neutral)
|
4635 |
+
$dos = -1;
|
4636 |
+
$remember = array();
|
4637 |
+
$controlchars = false;
|
4638 |
+
$strongrtl = false;
|
4639 |
+
$diid = 0; // direction isolate ID
|
4640 |
+
$dictr = 0; // direction isolate counter
|
4641 |
+
|
4642 |
+
// Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase.
|
4643 |
+
// In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
|
4644 |
+
$numchunks = count($para);
|
4645 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
4646 |
+
$chunkOTLdata =& $para[$nc][18];
|
4647 |
+
|
4648 |
+
$numchars = count($chunkOTLdata['char_data']);
|
4649 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4650 |
+
if ($chunkOTLdata['char_data'][$i]['uni'] == 8235) { // RLE
|
4651 |
+
// X2. With each RLE, compute the least greater odd embedding level.
|
4652 |
+
// a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
|
4653 |
+
// b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
|
4654 |
+
$next_level = $cel + ($cel % 2) + 1;
|
4655 |
+
if ($next_level < 62) {
|
4656 |
+
$remember[] = array('num' => 8235, 'cel' => $cel, 'dos' => $dos);
|
4657 |
+
$cel = $next_level;
|
4658 |
+
$dos = -1;
|
4659 |
+
$controlchars = true;
|
4660 |
+
}
|
4661 |
+
}
|
4662 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8234) { // LRE
|
4663 |
+
// X3. With each LRE, compute the least greater even embedding level.
|
4664 |
+
// a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
|
4665 |
+
// b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
|
4666 |
+
$next_level = $cel + 2 - ($cel % 2);
|
4667 |
+
if ( $next_level < 62 ) {
|
4668 |
+
$remember[] = array('num' => 8234, 'cel' => $cel, 'dos' => $dos);
|
4669 |
+
$cel = $next_level;
|
4670 |
+
$dos = -1;
|
4671 |
+
$controlchars = true;
|
4672 |
+
}
|
4673 |
+
}
|
4674 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8238) { // RLO
|
4675 |
+
// X4. With each RLO, compute the least greater odd embedding level.
|
4676 |
+
// a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left.
|
4677 |
+
// b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
|
4678 |
+
$next_level = $cel + ($cel % 2) + 1;
|
4679 |
+
if ($next_level < 62) {
|
4680 |
+
$remember[] = array('num' => 8238, 'cel' => $cel, 'dos' => $dos);
|
4681 |
+
$cel = $next_level;
|
4682 |
+
$dos = UCDN::BIDI_CLASS_R;
|
4683 |
+
$controlchars = true;
|
4684 |
+
}
|
4685 |
+
}
|
4686 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8237) { // LRO
|
4687 |
+
// X5. With each LRO, compute the least greater even embedding level.
|
4688 |
+
// a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right.
|
4689 |
+
// b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
|
4690 |
+
$next_level = $cel + 2 - ($cel % 2);
|
4691 |
+
if ( $next_level < 62 ) {
|
4692 |
+
$remember[] = array('num' => 8237, 'cel' => $cel, 'dos' => $dos);
|
4693 |
+
$cel = $next_level;
|
4694 |
+
$dos = UCDN::BIDI_CLASS_L;
|
4695 |
+
$controlchars = true;
|
4696 |
+
}
|
4697 |
+
}
|
4698 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8236) { // PDF
|
4699 |
+
// X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override.
|
4700 |
+
if (count($remember)) {
|
4701 |
+
$last = count($remember ) - 1;
|
4702 |
+
if (($remember[$last]['num'] == 8235) || ($remember[$last]['num'] == 8234) || ($remember[$last]['num'] == 8238) ||
|
4703 |
+
($remember[$last]['num'] == 8237)) {
|
4704 |
+
$match = array_pop($remember);
|
4705 |
+
$cel = $match['cel'];
|
4706 |
+
$dos = $match['dos'];
|
4707 |
+
}
|
4708 |
+
}
|
4709 |
+
}
|
4710 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8294 || $chunkOTLdata['char_data'][$i]['uni'] == 8295 ||
|
4711 |
+
$chunkOTLdata['char_data'][$i]['uni'] == 8296) { // LRI // RLI // FSI
|
4712 |
+
// X5a. With each RLI:
|
4713 |
+
// X5b. With each LRI:
|
4714 |
+
// X5c. With each FSI, apply rules P2 and P3 for First Strong character
|
4715 |
+
// Set the RLI/LRI/FSI embedding level to the embedding level of the last entry on the directional status stack.
|
4716 |
+
if ($dos != -1) { $chardir = $dos; }
|
4717 |
+
else { $chardir = $chunkOTLdata['char_data'][$i]['bidi_class']; }
|
4718 |
+
$chunkOTLdata['char_data'][$i]['level'] = $cel;
|
4719 |
+
$chunkOTLdata['char_data'][$i]['type'] = $chardir;
|
4720 |
+
$chunkOTLdata['char_data'][$i]['diid'] = $diid;
|
4721 |
+
|
4722 |
+
$fsi = '';
|
4723 |
+
// X5c. With each FSI, apply rules P2 and P3 within the isolate run for First Strong character
|
4724 |
+
if ($chunkOTLdata['char_data'][$i]['uni'] == 8296) { // FSI
|
4725 |
+
$lvl = 0;
|
4726 |
+
$nc2 = $nc;
|
4727 |
+
$i2 = $i;
|
4728 |
+
while (!($nc2==($numchunks-1) && $i2==((count($para[$nc2][18]['char_data']))-1))) { // while not at end of last chunk
|
4729 |
+
$i2++;
|
4730 |
+
if ($i2 >= count($para[$nc2][18]['char_data'])) {
|
4731 |
+
$nc2++;
|
4732 |
+
$i2 = 0;
|
4733 |
+
}
|
4734 |
+
if ($lvl > 0) { continue; }
|
4735 |
+
if ($para[$nc2][18]['char_data'][$i2]['uni'] == 8294 || $para[$nc2][18]['char_data'][$i2]['uni'] == 8295 || $para[$nc2][18]['char_data'][$i2]['uni'] == 8296) {
|
4736 |
+
$lvl++;
|
4737 |
+
continue;
|
4738 |
+
}
|
4739 |
+
if ($para[$nc2][18]['char_data'][$i2]['uni'] == 8297) {
|
4740 |
+
$lvl--;
|
4741 |
+
if ($lvl < 0) { break; }
|
4742 |
+
}
|
4743 |
+
if ($para[$nc2][18]['char_data'][$i2]['bidi_class'] === UCDN::BIDI_CLASS_L || $para[$nc2][18]['char_data'][$i2]['bidi_class'] == UCDN::BIDI_CLASS_AL || $para[$nc2][18]['char_data'][$i2]['bidi_class'] === UCDN::BIDI_CLASS_R) {
|
4744 |
+
$fsi = $para[$nc2][18]['char_data'][$i2]['bidi_class'];
|
4745 |
+
break;
|
4746 |
+
}
|
4747 |
+
}
|
4748 |
+
// if fsi not found, fsi is same as paragraph embedding level
|
4749 |
+
if (!$fsi && $fsi!==0) {
|
4750 |
+
if ($pel==1) { $fsi = UCDN::BIDI_CLASS_R ; }
|
4751 |
+
else { $fsi = UCDN::BIDI_CLASS_L ; }
|
4752 |
+
}
|
4753 |
+
}
|
4754 |
+
|
4755 |
+
if ($chunkOTLdata['char_data'][$i]['uni'] == 8294 || $fsi === UCDN::BIDI_CLASS_L ) { // LRI or FSI-L
|
4756 |
+
// Compute the least even embedding level greater than the embedding level of the last entry on the directional status stack.
|
4757 |
+
$next_level = $cel + 2 - ($cel % 2);
|
4758 |
+
}
|
4759 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8295 || $fsi == UCDN::BIDI_CLASS_R || $fsi == UCDN::BIDI_CLASS_AL ) { // RLI or FSI-R
|
4760 |
+
// Compute the least odd embedding level greater than the embedding level of the last entry on the directional status stack.
|
4761 |
+
$next_level = $cel + ($cel % 2) + 1;
|
4762 |
+
}
|
4763 |
+
|
4764 |
+
|
4765 |
+
// Increment the isolate count by one, and push an entry consisting of the new embedding level,
|
4766 |
+
// neutral directional override status, and true directional isolate status onto the directional status stack.
|
4767 |
+
$remember[] = array('num' => $chunkOTLdata['char_data'][$i]['uni'], 'cel' => $cel, 'dos' => $dos, 'diid' => $diid);
|
4768 |
+
$cel = $next_level;
|
4769 |
+
$dos = -1;
|
4770 |
+
$diid = ++$dictr; // Set new direction isolate ID after incrementing direction isolate counter
|
4771 |
+
|
4772 |
+
$controlchars = true;
|
4773 |
+
}
|
4774 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 8297) { // PDI
|
4775 |
+
// X6a. With each PDI, perform the following steps:
|
4776 |
+
// Pop the last entry from the directional status stack and decrement the isolate count by one.
|
4777 |
+
while (count($remember)) {
|
4778 |
+
$last = count($remember ) - 1;
|
4779 |
+
if (($remember[$last]['num'] == 8294) || ($remember[$last]['num'] == 8295) || ($remember[$last]['num'] == 8296)) {
|
4780 |
+
$match = array_pop($remember);
|
4781 |
+
$cel = $match['cel'];
|
4782 |
+
$dos = $match['dos'];
|
4783 |
+
$diid = $match['diid'];
|
4784 |
+
break;
|
4785 |
+
}
|
4786 |
+
// End/close any open embedding states not explicitly closed during the isolate
|
4787 |
+
else if (($remember[$last]['num'] == 8235) || ($remember[$last]['num'] == 8234) || ($remember[$last]['num'] == 8238) ||
|
4788 |
+
($remember[$last]['num'] == 8237)) {
|
4789 |
+
$match = array_pop($remember);
|
4790 |
+
}
|
4791 |
+
}
|
4792 |
+
// In all cases, set the PDI�s level to the embedding level of the last entry on the directional status stack left after the steps above.
|
4793 |
+
// NB The level assigned to an isolate initiator is always the same as that assigned to the matching PDI.
|
4794 |
+
if ($dos != -1) { $chardir = $dos; }
|
4795 |
+
else { $chardir = $chunkOTLdata['char_data'][$i]['bidi_class']; }
|
4796 |
+
$chunkOTLdata['char_data'][$i]['level'] = $cel;
|
4797 |
+
$chunkOTLdata['char_data'][$i]['type'] = $chardir;
|
4798 |
+
$chunkOTLdata['char_data'][$i]['diid'] = $diid;
|
4799 |
+
$controlchars = true;
|
4800 |
+
}
|
4801 |
+
else if ($chunkOTLdata['char_data'][$i]['uni'] == 10) { // NEW LINE
|
4802 |
+
// Reset to start values
|
4803 |
+
$cel = $pel;
|
4804 |
+
$dos = -1;
|
4805 |
+
$remember = array();
|
4806 |
+
}
|
4807 |
+
else {
|
4808 |
+
// X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
|
4809 |
+
// a. Set the level of the current character to the current embedding level.
|
4810 |
+
// b. When the directional override status is not neutral, reset the current character type to directional override status.
|
4811 |
+
if ($dos != -1) { $chardir = $dos; }
|
4812 |
+
else {
|
4813 |
+
$chardir = $chunkOTLdata['char_data'][$i]['bidi_class'];
|
4814 |
+
if ($chardir == UCDN::BIDI_CLASS_R || $chardir == UCDN::BIDI_CLASS_AL) { $strongrtl = true; }
|
4815 |
+
}
|
4816 |
+
$chunkOTLdata['char_data'][$i]['level'] = $cel;
|
4817 |
+
$chunkOTLdata['char_data'][$i]['type'] = $chardir;
|
4818 |
+
$chunkOTLdata['char_data'][$i]['diid'] = $diid;
|
4819 |
+
}
|
4820 |
+
}
|
4821 |
+
// X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph.
|
4822 |
+
// Paragraph separators are not included in the embedding.
|
4823 |
+
// X9. Remove all RLE, LRE, RLO, LRO, and PDF codes.
|
4824 |
+
if ($controlchars) {
|
4825 |
+
$this->removeChar($para[$nc][0], $para[$nc][18], "\xe2\x80\xaa");
|
4826 |
+
$this->removeChar($para[$nc][0], $para[$nc][18], "\xe2\x80\xab");
|
4827 |
+
$this->removeChar($para[$nc][0], $para[$nc][18], "\xe2\x80\xac");
|
4828 |
+
$this->removeChar($para[$nc][0], $para[$nc][18], "\xe2\x80\xad");
|
4829 |
+
$this->removeChar($para[$nc][0], $para[$nc][18], "\xe2\x80\xae");
|
4830 |
+
preg_replace("/\x{202a}-\x{202e}/u", '', $para[$nc][0]);
|
4831 |
+
}
|
4832 |
+
}
|
4833 |
+
|
4834 |
+
// Remove any blank chunks made by removing directional codes
|
4835 |
+
$numchunks = count($para);
|
4836 |
+
for ($nc=($numchunks-1);$nc>=0;$nc--) {
|
4837 |
+
if (count($para[$nc][18]['char_data'])==0) { array_splice($para, $nc, 1); }
|
4838 |
+
}
|
4839 |
+
if ($dir != 'rtl' && !$strongrtl && !$controlchars) { return; }
|
4840 |
+
|
4841 |
+
$numchunks = count($para);
|
4842 |
+
|
4843 |
+
// X10. Determine the start-of-sequence (sor) and end-of-sequence (eor) types, either L or R, for each isolating run sequence. These depend on the higher of the two levels on either side of the sequence boundary:
|
4844 |
+
// For sor, compare the level of the first character in the sequence with the level of the character preceding it in the paragraph or if there is none, with the paragraph embedding level.
|
4845 |
+
// For eor, compare the level of the last character in the sequence with the level of the character following it in the paragraph or if there is none, with the paragraph embedding level.
|
4846 |
+
// If the higher level is odd, the sor or eor is R; otherwise, it is L.
|
4847 |
+
|
4848 |
+
for ($ir=0; $ir<=$dictr;$ir++) {
|
4849 |
+
$prelevel = $pel;
|
4850 |
+
$postlevel = $pel;
|
4851 |
+
$firstchar = true;
|
4852 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
4853 |
+
$chardata =& $para[$nc][18]['char_data'];
|
4854 |
+
$numchars = count($chardata);
|
4855 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4856 |
+
if (!isset($chardata[$i]['diid']) || $chardata[$i]['diid']!=$ir) { continue; } // Ignore characters in a different isolate run
|
4857 |
+
$right = $postlevel;
|
4858 |
+
$nc2 = $nc;
|
4859 |
+
$i2 = $i;
|
4860 |
+
while (!($nc2==($numchunks-1) && $i2==((count($para[$nc2][18]['char_data']))-1))) { // while not at end of last chunk
|
4861 |
+
$i2++;
|
4862 |
+
if ($i2 >= count($para[$nc2][18]['char_data'])) {
|
4863 |
+
$nc2++;
|
4864 |
+
$i2 = 0;
|
4865 |
+
}
|
4866 |
+
|
4867 |
+
if (isset($para[$nc2][18]['char_data'][$i2]['diid']) && $para[$nc2][18]['char_data'][$i2]['diid']==$ir) { $right = $para[$nc2][18]['char_data'][$i2]['level']; break; }
|
4868 |
+
}
|
4869 |
+
|
4870 |
+
$level = $chardata[$i]['level'];
|
4871 |
+
if ($firstchar || $level!=$prelevel) {
|
4872 |
+
$chardata[$i]['sor'] = max($prelevel, $level) % 2 ? UCDN::BIDI_CLASS_R : UCDN::BIDI_CLASS_L;
|
4873 |
+
}
|
4874 |
+
if (($nc==($numchunks-1) && $i==($numchars-1)) || $level != $right) {
|
4875 |
+
$chardata[$i]['eor'] = max($right, $level) % 2 ? UCDN::BIDI_CLASS_R : UCDN::BIDI_CLASS_L;
|
4876 |
+
}
|
4877 |
+
$prelevel = $level;
|
4878 |
+
$firstchar = false;
|
4879 |
+
}
|
4880 |
+
}
|
4881 |
+
}
|
4882 |
+
|
4883 |
+
|
4884 |
+
// 3.3.3 Resolving Weak Types
|
4885 |
+
// Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used.
|
4886 |
+
// Nonspacing marks are now resolved based on the previous characters.
|
4887 |
+
|
4888 |
+
// W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor.
|
4889 |
+
for ($ir=0; $ir<=$dictr;$ir++) {
|
4890 |
+
$prevtype = 0;
|
4891 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
4892 |
+
$chardata =& $para[$nc][18]['char_data'];
|
4893 |
+
$numchars = count($chardata);
|
4894 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4895 |
+
if (!isset($chardata[$i]['diid']) || $chardata[$i]['diid']!=$ir) { continue; } // Ignore characters in a different isolate run
|
4896 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_NSM) {
|
4897 |
+
if (isset($chardata[$i]['sor'])) {
|
4898 |
+
$chardata[$i]['type'] = $chardata[$i]['sor'];
|
4899 |
+
}
|
4900 |
+
else {
|
4901 |
+
$chardata[$i]['type'] = $prevtype;
|
4902 |
+
}
|
4903 |
+
}
|
4904 |
+
$prevtype = $chardata[$i]['type'];
|
4905 |
+
}
|
4906 |
+
}
|
4907 |
+
}
|
4908 |
+
|
4909 |
+
// W2. Search backward from each instance of a European number until the first strong type (R, L, AL or sor) is found. If an AL is found, change the type of the European number to Arabic number.
|
4910 |
+
for ($ir=0; $ir<=$dictr;$ir++) {
|
4911 |
+
$laststrongtype = -1;
|
4912 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
4913 |
+
$chardata =& $para[$nc][18]['char_data'];
|
4914 |
+
$numchars = count($chardata);
|
4915 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4916 |
+
if (!isset($chardata[$i]['diid']) || $chardata[$i]['diid']!=$ir) { continue; } // Ignore characters in a different isolate run
|
4917 |
+
if (isset($chardata[$i]['sor'])) { $laststrongtype = $chardata[$i]['sor']; }
|
4918 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_EN && $laststrongtype == UCDN::BIDI_CLASS_AL ) {
|
4919 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_AN;
|
4920 |
+
}
|
4921 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_L || $chardata[$i]['type'] == UCDN::BIDI_CLASS_R || $chardata[$i]['type'] == UCDN::BIDI_CLASS_AL) {
|
4922 |
+
$laststrongtype = $chardata[$i]['type'];
|
4923 |
+
}
|
4924 |
+
}
|
4925 |
+
}
|
4926 |
+
}
|
4927 |
+
|
4928 |
+
|
4929 |
+
// W3. Change all ALs to R.
|
4930 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
4931 |
+
$chardata =& $para[$nc][18]['char_data'];
|
4932 |
+
$numchars = count($chardata);
|
4933 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4934 |
+
if (isset($chardata[$i]['type']) && $chardata[$i]['type'] == UCDN::BIDI_CLASS_AL) { $chardata[$i]['type'] = UCDN::BIDI_CLASS_R; }
|
4935 |
+
}
|
4936 |
+
}
|
4937 |
+
|
4938 |
+
|
4939 |
+
// W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type.
|
4940 |
+
for ($ir=0; $ir<=$dictr;$ir++) {
|
4941 |
+
$prevtype = -1;
|
4942 |
+
$nexttype = -1;
|
4943 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
4944 |
+
$chardata =& $para[$nc][18]['char_data'];
|
4945 |
+
$numchars = count($chardata);
|
4946 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4947 |
+
if (!isset($chardata[$i]['diid']) || $chardata[$i]['diid']!=$ir) { continue; } // Ignore characters in a different isolate run
|
4948 |
+
|
4949 |
+
// Get next type
|
4950 |
+
$nexttype = -1;
|
4951 |
+
$nc2 = $nc;
|
4952 |
+
$i2 = $i;
|
4953 |
+
while (!($nc2==($numchunks-1) && $i2==((count($para[$nc2][18]['char_data']))-1))) { // while not at end of last chunk
|
4954 |
+
$i2++;
|
4955 |
+
if ($i2 >= count($para[$nc2][18]['char_data'])) {
|
4956 |
+
$nc2++;
|
4957 |
+
$i2 = 0;
|
4958 |
+
}
|
4959 |
+
|
4960 |
+
if (isset($para[$nc2][18]['char_data'][$i2]['diid']) && $para[$nc2][18]['char_data'][$i2]['diid']==$ir) { $nexttype = $para[$nc2][18]['char_data'][$i2]['type']; break; }
|
4961 |
+
}
|
4962 |
+
|
4963 |
+
if (!isset($chardata[$i]['sor']) && !isset($chardata[$i]['eor'])) {
|
4964 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ES && $prevtype == UCDN::BIDI_CLASS_EN && $nexttype == UCDN::BIDI_CLASS_EN) {
|
4965 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_EN;
|
4966 |
+
}
|
4967 |
+
else if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_CS && $prevtype == UCDN::BIDI_CLASS_EN && $nexttype == UCDN::BIDI_CLASS_EN) {
|
4968 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_EN;
|
4969 |
+
}
|
4970 |
+
else if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_CS && $prevtype == UCDN::BIDI_CLASS_AN && $nexttype == UCDN::BIDI_CLASS_AN) {
|
4971 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_AN;
|
4972 |
+
}
|
4973 |
+
}
|
4974 |
+
$prevtype = $chardata[$i]['type'];
|
4975 |
+
}
|
4976 |
+
}
|
4977 |
+
}
|
4978 |
+
|
4979 |
+
// W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
|
4980 |
+
for ($ir=0; $ir<=$dictr;$ir++) {
|
4981 |
+
$prevtype = -1;
|
4982 |
+
$nexttype = -1;
|
4983 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
4984 |
+
$chardata =& $para[$nc][18]['char_data'];
|
4985 |
+
$numchars = count($chardata);
|
4986 |
+
for ($i=0; $i < $numchars; ++$i) {
|
4987 |
+
if (!isset($chardata[$i]['diid']) || $chardata[$i]['diid']!=$ir) { continue; } // Ignore characters in a different isolate run
|
4988 |
+
if (isset($chardata[$i]['sor'])) { $prevtype = $chardata[$i]['sor']; }
|
4989 |
+
|
4990 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ET) {
|
4991 |
+
if ($prevtype == UCDN::BIDI_CLASS_EN) {
|
4992 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_EN;
|
4993 |
+
}
|
4994 |
+
else if (!isset($chardata[$i]['eor'])) {
|
4995 |
+
$nexttype = -1;
|
4996 |
+
$nc2 = $nc;
|
4997 |
+
$i2 = $i;
|
4998 |
+
while (!($nc2==($numchunks-1) && $i2==((count($para[$nc2][18]['char_data']))-1))) { // while not at end of last chunk
|
4999 |
+
$i2++;
|
5000 |
+
if ($i2 >= count($para[$nc2][18]['char_data'])) {
|
5001 |
+
$nc2++;
|
5002 |
+
$i2 = 0;
|
5003 |
+
}
|
5004 |
+
if ($para[$nc2][18]['char_data'][$i2]['diid']!=$ir) { continue; }
|
5005 |
+
$nexttype = $para[$nc2][18]['char_data'][$i2]['type'];
|
5006 |
+
if (isset($para[$nc2][18]['char_data'][$i2]['sor'])) { break; }
|
5007 |
+
if ($nexttype == UCDN::BIDI_CLASS_EN) {
|
5008 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_EN;
|
5009 |
+
break;
|
5010 |
+
}
|
5011 |
+
else if ($nexttype != UCDN::BIDI_CLASS_ET) { break; }
|
5012 |
+
}
|
5013 |
+
}
|
5014 |
+
}
|
5015 |
+
$prevtype = $chardata[$i]['type'];
|
5016 |
+
}
|
5017 |
+
}
|
5018 |
+
}
|
5019 |
+
|
5020 |
+
// W6. Otherwise, separators and terminators change to Other Neutral.
|
5021 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
5022 |
+
$chardata =& $para[$nc][18]['char_data'];
|
5023 |
+
$numchars = count($chardata);
|
5024 |
+
for ($i=0; $i < $numchars; ++$i) {
|
5025 |
+
if (isset($chardata[$i]['type']) && (($chardata[$i]['type'] == UCDN::BIDI_CLASS_ET) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ES) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_CS))) {
|
5026 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_ON;
|
5027 |
+
}
|
5028 |
+
}
|
5029 |
+
}
|
5030 |
+
|
5031 |
+
//W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L.
|
5032 |
+
for ($ir=0; $ir<=$dictr;$ir++) {
|
5033 |
+
$laststrongtype = -1;
|
5034 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
5035 |
+
$chardata =& $para[$nc][18]['char_data'];
|
5036 |
+
$numchars = count($chardata);
|
5037 |
+
for ($i=0; $i < $numchars; ++$i) {
|
5038 |
+
if (!isset($chardata[$i]['diid']) || $chardata[$i]['diid']!=$ir) { continue; } // Ignore characters in a different isolate run
|
5039 |
+
if (isset($chardata[$i]['sor'])) { $laststrongtype = $chardata[$i]['sor']; }
|
5040 |
+
if (isset($chardata[$i]['type']) && $chardata[$i]['type'] == UCDN::BIDI_CLASS_EN && $laststrongtype == UCDN::BIDI_CLASS_L ) {
|
5041 |
+
$chardata[$i]['type'] = UCDN::BIDI_CLASS_L;
|
5042 |
+
}
|
5043 |
+
if (isset($chardata[$i]['type']) && ($chardata[$i]['type'] == UCDN::BIDI_CLASS_L || $chardata[$i]['type'] == UCDN::BIDI_CLASS_R || $chardata[$i]['type'] == UCDN::BIDI_CLASS_AL)) {
|
5044 |
+
$laststrongtype = $chardata[$i]['type'];
|
5045 |
+
}
|
5046 |
+
}
|
5047 |
+
}
|
5048 |
+
}
|
5049 |
+
|
5050 |
+
// N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries.
|
5051 |
+
for ($ir=0; $ir<=$dictr;$ir++) {
|
5052 |
+
$laststrongtype = -1;
|
5053 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
5054 |
+
$chardata =& $para[$nc][18]['char_data'];
|
5055 |
+
$numchars = count($chardata);
|
5056 |
+
for ($i=0; $i < $numchars; ++$i) {
|
5057 |
+
if (!isset($chardata[$i]['diid']) || $chardata[$i]['diid']!=$ir) { continue; } // Ignore characters in a different isolate run
|
5058 |
+
if (isset($chardata[$i]['sor'])) { $laststrongtype = $chardata[$i]['sor']; }
|
5059 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ON || $chardata[$i]['type'] == UCDN::BIDI_CLASS_WS) {
|
5060 |
+
$left = -1;
|
5061 |
+
// LEFT
|
5062 |
+
if ($laststrongtype == UCDN::BIDI_CLASS_R || $laststrongtype == UCDN::BIDI_CLASS_EN || $laststrongtype == UCDN::BIDI_CLASS_AN) {
|
5063 |
+
$left = UCDN::BIDI_CLASS_R;
|
5064 |
+
}
|
5065 |
+
else if ($laststrongtype == UCDN::BIDI_CLASS_L) {
|
5066 |
+
$left = UCDN::BIDI_CLASS_L;
|
5067 |
+
}
|
5068 |
+
// RIGHT
|
5069 |
+
$right = -1;
|
5070 |
+
// move to the right of any following neutrals OR hit a run boundary
|
5071 |
+
|
5072 |
+
if (isset($chardata[$i]['eor'])) {
|
5073 |
+
$right = $chardata[$i]['eor'];
|
5074 |
+
}
|
5075 |
+
else {
|
5076 |
+
$nexttype = -1;
|
5077 |
+
$nc2 = $nc;
|
5078 |
+
$i2 = $i;
|
5079 |
+
while (!($nc2==($numchunks-1) && $i2==((count($para[$nc2][18]['char_data']))-1))) { // while not at end of last chunk
|
5080 |
+
$i2++;
|
5081 |
+
if ($i2 >= count($para[$nc2][18]['char_data'])) {
|
5082 |
+
$nc2++;
|
5083 |
+
$i2 = 0;
|
5084 |
+
}
|
5085 |
+
if (!isset($para[$nc2][18]['char_data'][$i2]['diid']) || $para[$nc2][18]['char_data'][$i2]['diid']!=$ir) { continue; }
|
5086 |
+
$nexttype = $para[$nc2][18]['char_data'][$i2]['type'];
|
5087 |
+
if ($nexttype == UCDN::BIDI_CLASS_R || $nexttype == UCDN::BIDI_CLASS_EN || $nexttype == UCDN::BIDI_CLASS_AN) {
|
5088 |
+
$right = UCDN::BIDI_CLASS_R;
|
5089 |
+
break;
|
5090 |
+
}
|
5091 |
+
else if ($nexttype == UCDN::BIDI_CLASS_L) {
|
5092 |
+
$right = UCDN::BIDI_CLASS_L;
|
5093 |
+
break;
|
5094 |
+
}
|
5095 |
+
else if (isset($para[$nc2][18]['char_data'][$i2]['eor'])) {
|
5096 |
+
$right = $para[$nc2][18]['char_data'][$i2]['eor'];
|
5097 |
+
break;
|
5098 |
+
}
|
5099 |
+
}
|
5100 |
+
}
|
5101 |
+
|
5102 |
+
if ($left > -1 && $left==$right) {
|
5103 |
+
$chardata[$i]['orig_type'] = $chardata[$i]['type']; // Need to store the original 'WS' for reference in L1 below
|
5104 |
+
$chardata[$i]['type'] = $left;
|
5105 |
+
}
|
5106 |
+
}
|
5107 |
+
else if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_L || $chardata[$i]['type'] == UCDN::BIDI_CLASS_R || $chardata[$i]['type'] == UCDN::BIDI_CLASS_EN || $chardata[$i]['type'] == UCDN::BIDI_CLASS_AN) {
|
5108 |
+
$laststrongtype = $chardata[$i]['type'];
|
5109 |
+
}
|
5110 |
+
}
|
5111 |
+
}
|
5112 |
+
}
|
5113 |
+
|
5114 |
+
// N2. Any remaining neutrals take the embedding direction
|
5115 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
5116 |
+
$chardata =& $para[$nc][18]['char_data'];
|
5117 |
+
$numchars = count($chardata);
|
5118 |
+
for ($i=0; $i < $numchars; ++$i) {
|
5119 |
+
if (isset($chardata[$i]['type']) && ($chardata[$i]['type'] == UCDN::BIDI_CLASS_ON || $chardata[$i]['type'] == UCDN::BIDI_CLASS_WS)) {
|
5120 |
+
$chardata[$i]['orig_type'] = $chardata[$i]['type']; // Need to store the original 'WS' for reference in L1 below
|
5121 |
+
$chardata[$i]['type'] = ($chardata[$i]['level'] % 2) ? UCDN::BIDI_CLASS_R : UCDN::BIDI_CLASS_L;
|
5122 |
+
}
|
5123 |
+
}
|
5124 |
+
}
|
5125 |
+
|
5126 |
+
// I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels.
|
5127 |
+
// I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
|
5128 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
5129 |
+
$chardata =& $para[$nc][18]['char_data'];
|
5130 |
+
$numchars = count($chardata);
|
5131 |
+
for ($i=0; $i < $numchars; ++$i) {
|
5132 |
+
if (isset($chardata[$i]['level'])) {
|
5133 |
+
$odd = $chardata[$i]['level'] % 2;
|
5134 |
+
if ($odd) {
|
5135 |
+
if (($chardata[$i]['type'] == UCDN::BIDI_CLASS_L) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_AN) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_EN)) {
|
5136 |
+
$chardata[$i]['level'] += 1;
|
5137 |
+
}
|
5138 |
+
}
|
5139 |
+
else {
|
5140 |
+
if ($chardata[$i]['type'] == UCDN::BIDI_CLASS_R) { $chardata[$i]['level'] += 1; }
|
5141 |
+
else if (($chardata[$i]['type'] == UCDN::BIDI_CLASS_AN) || ($chardata[$i]['type'] == UCDN::BIDI_CLASS_EN)) { $chardata[$i]['level'] += 2; }
|
5142 |
+
}
|
5143 |
+
}
|
5144 |
+
}
|
5145 |
+
}
|
5146 |
+
|
5147 |
+
// Remove Isolate formatters
|
5148 |
+
$numchunks = count($para);
|
5149 |
+
if ($controlchars) {
|
5150 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
5151 |
+
$this->removeChar($para[$nc][0], $para[$nc][18], "\xe2\x81\xa6");
|
5152 |
+
$this->removeChar($para[$nc][0], $para[$nc][18], "\xe2\x81\xa7");
|
5153 |
+
$this->removeChar($para[$nc][0], $para[$nc][18], "\xe2\x81\xa8");
|
5154 |
+
$this->removeChar($para[$nc][0], $para[$nc][18], "\xe2\x81\xa9");
|
5155 |
+
preg_replace("/\x{2066}-\x{2069}/u", '', $para[$nc][0]);
|
5156 |
+
}
|
5157 |
+
// Remove any blank chunks made by removing directional codes
|
5158 |
+
for ($nc=($numchunks-1);$nc>=0;$nc--) {
|
5159 |
+
if (count($para[$nc][18]['char_data'])==0) { array_splice($para, $nc, 1); }
|
5160 |
+
}
|
5161 |
+
}
|
5162 |
+
|
5163 |
+
}
|
5164 |
+
|
5165 |
+
|
5166 |
+
|
5167 |
+
// Reorder, once divided into lines
|
5168 |
+
|
5169 |
+
function _bidiReorder(&$chunkorder, &$content, &$cOTLdata, $blockdir) {
|
5170 |
+
|
5171 |
+
$bidiData = array();
|
5172 |
+
|
5173 |
+
// First combine into one array (and get the highest level in use)
|
5174 |
+
$numchunks = count($content);
|
5175 |
+
$maxlevel = 0;
|
5176 |
+
for ($nc=0;$nc<$numchunks;$nc++) {
|
5177 |
+
$numchars = count($cOTLdata[$nc]['char_data']);
|
5178 |
+
for ($i=0; $i < $numchars; ++$i) {
|
5179 |
+
|
5180 |
+
$carac = array();
|
5181 |
+
if (isset($cOTLdata[$nc]['GPOSinfo'][$i])) {$carac['GPOSinfo'] = $cOTLdata[$nc]['GPOSinfo'][$i]; }
|
5182 |
+
$carac['uni'] = $cOTLdata[$nc]['char_data'][$i]['uni'];
|
5183 |
+
if (isset($cOTLdata[$nc]['char_data'][$i]['type'])) $carac['type'] = $cOTLdata[$nc]['char_data'][$i]['type'];
|
5184 |
+
if (isset($cOTLdata[$nc]['char_data'][$i]['level'])) $carac['level'] = $cOTLdata[$nc]['char_data'][$i]['level'];
|
5185 |
+
if (isset($cOTLdata[$nc]['char_data'][$i]['orig_type'])) { $carac['orig_type'] = $cOTLdata[$nc]['char_data'][$i]['orig_type']; }
|
5186 |
+
$carac['group'] = $cOTLdata[$nc]['group']{$i};
|
5187 |
+
$carac['chunkid'] = $chunkorder[$nc]; // gives font id and/or object ID
|
5188 |
+
|
5189 |
+
$maxlevel = max((isset($carac['level']) ? $carac['level'] : 0),$maxlevel);
|
5190 |
+
$bidiData[] = $carac;
|
5191 |
+
}
|
5192 |
+
}
|
5193 |
+
if ($maxlevel==0) { return; }
|
5194 |
+
|
5195 |
+
$numchars = count($bidiData);
|
5196 |
+
|
5197 |
+
// L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
|
5198 |
+
// 1. Segment separators (Tab) 'S',
|
5199 |
+
// 2. Paragraph separators 'B',
|
5200 |
+
// 3. Any sequence of whitespace characters 'WS' preceding a segment separator or paragraph separator, and
|
5201 |
+
// 4. Any sequence of whitespace characters 'WS' at the end of the line.
|
5202 |
+
// The types of characters used here are the original types, not those modified by the previous phase cf N1 and N2*******
|
5203 |
+
// Because a Paragraph Separator breaks lines, there will be at most one per line, at the end of that line.
|
5204 |
+
|
5205 |
+
// Set the initial paragraph embedding level
|
5206 |
+
if ($blockdir == 'rtl') { $pel = 1; }
|
5207 |
+
else { $pel = 0; }
|
5208 |
+
|
5209 |
+
for ($i=($numchars-1); $i>0; $i--) {
|
5210 |
+
if ($bidiData[$i]['type'] == UCDN::BIDI_CLASS_WS || (isset($bidiData[$i]['orig_type']) && $bidiData[$i]['orig_type'] == UCDN::BIDI_CLASS_WS)) {
|
5211 |
+
$bidiData[$i]['level'] = $pel;
|
5212 |
+
}
|
5213 |
+
else { break; }
|
5214 |
+
}
|
5215 |
+
|
5216 |
+
|
5217 |
+
// L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
|
5218 |
+
for ($j=$maxlevel; $j > 0; $j--) {
|
5219 |
+
$ordarray = array();
|
5220 |
+
$revarr = array();
|
5221 |
+
$onlevel = false;
|
5222 |
+
for ($i=0; $i < $numchars; ++$i) {
|
5223 |
+
if ($bidiData[$i]['level'] >= $j) {
|
5224 |
+
$onlevel = true;
|
5225 |
+
// L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true.
|
5226 |
+
if (isset(UCDN::$mirror_pairs[$bidiData[$i]['uni']]) && $bidiData[$i]['type']==UCDN::BIDI_CLASS_R) {
|
5227 |
+
$bidiData[$i]['uni'] = UCDN::$mirror_pairs[$bidiData[$i]['uni']];
|
5228 |
+
}
|
5229 |
+
|
5230 |
+
$revarr[] = $bidiData[$i];
|
5231 |
+
}
|
5232 |
+
else {
|
5233 |
+
if ($onlevel) {
|
5234 |
+
$revarr = array_reverse($revarr);
|
5235 |
+
$ordarray = array_merge($ordarray, $revarr);
|
5236 |
+
$revarr = Array();
|
5237 |
+
$onlevel = false;
|
5238 |
+
}
|
5239 |
+
$ordarray[] = $bidiData[$i];
|
5240 |
+
}
|
5241 |
+
}
|
5242 |
+
if ($onlevel) {
|
5243 |
+
$revarr = array_reverse($revarr);
|
5244 |
+
$ordarray = array_merge($ordarray, $revarr);
|
5245 |
+
}
|
5246 |
+
$bidiData = $ordarray;
|
5247 |
+
}
|
5248 |
+
|
5249 |
+
$content = array();
|
5250 |
+
$cOTLdata = array();
|
5251 |
+
$chunkorder = array();
|
5252 |
+
|
5253 |
+
|
5254 |
+
|
5255 |
+
$nc = -1; // New chunk order ID
|
5256 |
+
$chunkid = -1;
|
5257 |
+
|
5258 |
+
foreach ($bidiData as $carac) {
|
5259 |
+
if ($carac['chunkid'] != $chunkid) {
|
5260 |
+
$nc++;
|
5261 |
+
$chunkorder[$nc] = $carac['chunkid'];
|
5262 |
+
$cctr = 0;
|
5263 |
+
$content[$nc] = '';
|
5264 |
+
$cOTLdata[$nc]['group'] = '';
|
5265 |
+
}
|
5266 |
+
if ($carac['uni'] != 0xFFFC) { // Object replacement character (65532)
|
5267 |
+
$content[$nc] .= code2utf($carac['uni']);
|
5268 |
+
$cOTLdata[$nc]['group'] .= $carac['group'];
|
5269 |
+
if (!empty($carac['GPOSinfo'])) {
|
5270 |
+
if (isset($carac['GPOSinfo'])) { $cOTLdata[$nc]['GPOSinfo'][$cctr] = $carac['GPOSinfo']; }
|
5271 |
+
$cOTLdata[$nc]['GPOSinfo'][$cctr]['wDir'] = ($carac['level'] % 2) ? 'RTL' : 'LTR';
|
5272 |
+
}
|
5273 |
+
}
|
5274 |
+
$chunkid = $carac['chunkid'];
|
5275 |
+
$cctr++;
|
5276 |
+
}
|
5277 |
+
|
5278 |
+
}
|
5279 |
+
|
5280 |
+
|
5281 |
+
|
5282 |
+
|
5283 |
+
|
5284 |
+
|
5285 |
+
////////////////////////////////////////////////////////////////
|
5286 |
+
////////////////////////////////////////////////////////////////
|
5287 |
+
// These functions are called from mpdf after GSUB/GPOS has taken place
|
5288 |
+
// At this stage the bidi-type is in string form
|
5289 |
+
////////////////////////////////////////////////////////////////
|
5290 |
+
////////////////////////////////////////////////////////////////
|
5291 |
+
function splitOTLdata(&$cOTLdata, $OTLcutoffpos, $OTLrestartpos='') {
|
5292 |
+
if (!$OTLrestartpos) { $OTLrestartpos = $OTLcutoffpos; }
|
5293 |
+
$newOTLdata = array('GPOSinfo' => array(), 'char_data' => array());
|
5294 |
+
$newOTLdata['group'] = substr($cOTLdata['group'],$OTLrestartpos);
|
5295 |
+
$cOTLdata['group'] = substr($cOTLdata['group'],0,$OTLcutoffpos);
|
5296 |
+
|
5297 |
+
if (isset($cOTLdata['GPOSinfo']) && $cOTLdata['GPOSinfo']) {
|
5298 |
+
foreach($cOTLdata['GPOSinfo'] AS $k => $val) {
|
5299 |
+
if ($k >= $OTLrestartpos) {
|
5300 |
+
$newOTLdata['GPOSinfo'][($k - $OTLrestartpos)] = $val;
|
5301 |
+
}
|
5302 |
+
if ($k >= $OTLcutoffpos) {
|
5303 |
+
unset($cOTLdata['GPOSinfo'][$k]);
|
5304 |
+
//$cOTLdata['GPOSinfo'][$k] = array();
|
5305 |
+
}
|
5306 |
+
}
|
5307 |
+
}
|
5308 |
+
if (isset($cOTLdata['char_data'])) {
|
5309 |
+
$newOTLdata['char_data'] = array_slice($cOTLdata['char_data'], $OTLrestartpos);
|
5310 |
+
array_splice($cOTLdata['char_data'], $OTLcutoffpos);
|
5311 |
+
}
|
5312 |
+
|
5313 |
+
// Not necessary - easier to debug
|
5314 |
+
if (isset($cOTLdata['GPOSinfo'])) ksort($cOTLdata['GPOSinfo']);
|
5315 |
+
if (isset($newOTLdata['GPOSinfo'])) ksort($newOTLdata['GPOSinfo']);
|
5316 |
+
|
5317 |
+
return $newOTLdata;
|
5318 |
+
}
|
5319 |
+
|
5320 |
+
function sliceOTLdata($OTLdata, $pos, $len) {
|
5321 |
+
$newOTLdata = array('GPOSinfo' => array(), 'char_data' => array());
|
5322 |
+
$newOTLdata['group'] = substr($OTLdata['group'],$pos,$len);
|
5323 |
+
|
5324 |
+
if ($OTLdata['GPOSinfo']) {
|
5325 |
+
foreach($OTLdata['GPOSinfo'] AS $k => $val) {
|
5326 |
+
if ($k >= $pos && $k <($pos+$len)) {
|
5327 |
+
$newOTLdata['GPOSinfo'][($k - $pos)] = $val;
|
5328 |
+
}
|
5329 |
+
}
|
5330 |
+
}
|
5331 |
+
|
5332 |
+
if (isset($OTLdata['char_data'])) { $newOTLdata['char_data'] = array_slice($OTLdata['char_data'], $pos, $len); }
|
5333 |
+
|
5334 |
+
// Not necessary - easier to debug
|
5335 |
+
if ($newOTLdata['GPOSinfo']) ksort($newOTLdata['GPOSinfo']);
|
5336 |
+
|
5337 |
+
return $newOTLdata;
|
5338 |
+
}
|
5339 |
+
|
5340 |
+
// Remove one or more occurrences of $char (single character) from $txt and adjust OTLdata
|
5341 |
+
function removeChar(&$txt, &$cOTLdata, $char) {
|
5342 |
+
while(mb_strpos($txt, $char, 0, $this->mpdf->mb_enc )!== false) {
|
5343 |
+
$pos = mb_strpos($txt, $char, 0, $this->mpdf->mb_enc );
|
5344 |
+
$newGPOSinfo = array();
|
5345 |
+
$cOTLdata['group'] = substr_replace($cOTLdata['group'], '', $pos, 1);
|
5346 |
+
if ($cOTLdata['GPOSinfo']) {
|
5347 |
+
foreach($cOTLdata['GPOSinfo'] AS $k => $val) {
|
5348 |
+
if ($k > $pos) {
|
5349 |
+
$newGPOSinfo[($k - 1)] = $val;
|
5350 |
+
}
|
5351 |
+
else if ($k!=$pos) {
|
5352 |
+
$newGPOSinfo[$k] = $val;
|
5353 |
+
}
|
5354 |
+
}
|
5355 |
+
$cOTLdata['GPOSinfo'] = $newGPOSinfo;
|
5356 |
+
}
|
5357 |
+
if (isset($cOTLdata['char_data'])) { array_splice($cOTLdata['char_data'], $pos, 1); }
|
5358 |
+
|
5359 |
+
$txt = preg_replace("/".$char."/",'',$txt, 1);
|
5360 |
+
}
|
5361 |
+
}
|
5362 |
+
|
5363 |
+
// Remove one or more occurrences of $char (single character) from $txt and adjust OTLdata
|
5364 |
+
function replaceSpace(&$txt, &$cOTLdata) {
|
5365 |
+
$char = chr(194).chr(160); // NBSP
|
5366 |
+
while(mb_strpos($txt, $char, 0, $this->mpdf->mb_enc )!== false) {
|
5367 |
+
$pos = mb_strpos($txt, $char, 0, $this->mpdf->mb_enc );
|
5368 |
+
if ($cOTLdata['char_data'][$pos]['uni'] == 160) {
|
5369 |
+
$cOTLdata['char_data'][$pos]['uni'] = 32;
|
5370 |
+
}
|
5371 |
+
$txt = preg_replace("/".$char."/",' ',$txt, 1);
|
5372 |
+
}
|
5373 |
+
}
|
5374 |
+
|
5375 |
+
function trimOTLdata(&$cOTLdata, $Left=true, $Right=true) {
|
5376 |
+
|
5377 |
+
$len = count($cOTLdata['char_data']);
|
5378 |
+
$nLeft = 0;
|
5379 |
+
$nRight = 0;
|
5380 |
+
for($i=0;$i<$len;$i++) {
|
5381 |
+
if($cOTLdata['char_data'][$i]['uni']==32 || $cOTLdata['char_data'][$i]['uni']==12288) { $nLeft++; } // 12288 = 0x3000 = CJK space
|
5382 |
+
else { break; }
|
5383 |
+
}
|
5384 |
+
for($i=($len-1);$i>=0;$i--) {
|
5385 |
+
if($cOTLdata['char_data'][$i]['uni']==32 || $cOTLdata['char_data'][$i]['uni']==12288) { $nRight++; } // 12288 = 0x3000 = CJK space
|
5386 |
+
else { break; }
|
5387 |
+
}
|
5388 |
+
|
5389 |
+
// Trim Right
|
5390 |
+
if ($Right && $nRight) {
|
5391 |
+
$cOTLdata['group'] = substr($cOTLdata['group'],0,strlen($cOTLdata['group'])-$nRight);
|
5392 |
+
if ($cOTLdata['GPOSinfo']) {
|
5393 |
+
foreach($cOTLdata['GPOSinfo'] AS $k => $val) {
|
5394 |
+
if ($k >= $len-$nRight) {
|
5395 |
+
unset($cOTLdata['GPOSinfo'][$k]);
|
5396 |
+
}
|
5397 |
+
}
|
5398 |
+
}
|
5399 |
+
if (isset($cOTLdata['char_data'])) {
|
5400 |
+
for($i=0;$i<$nRight;$i++) {
|
5401 |
+
array_pop($cOTLdata['char_data']);
|
5402 |
+
}
|
5403 |
+
}
|
5404 |
+
}
|
5405 |
+
// Trim Left
|
5406 |
+
if ($Left && $nLeft) {
|
5407 |
+
$cOTLdata['group'] = substr($cOTLdata['group'],$nLeft);
|
5408 |
+
if ($cOTLdata['GPOSinfo']) {
|
5409 |
+
$newPOSinfo = array();
|
5410 |
+
foreach($cOTLdata['GPOSinfo'] AS $k => $val) {
|
5411 |
+
if ($k >= $nLeft) {
|
5412 |
+
$newPOSinfo[$k-$nLeft] = $cOTLdata['GPOSinfo'][$k];
|
5413 |
+
}
|
5414 |
+
}
|
5415 |
+
$cOTLdata['GPOSinfo'] = $newPOSinfo;
|
5416 |
+
}
|
5417 |
+
if (isset($cOTLdata['char_data'])) {
|
5418 |
+
for($i=0;$i<$nLeft;$i++) {
|
5419 |
+
array_shift($cOTLdata['char_data']);
|
5420 |
+
}
|
5421 |
+
}
|
5422 |
+
}
|
5423 |
+
}
|
5424 |
+
|
5425 |
+
|
5426 |
+
////////////////////////////////////////////////////////////////
|
5427 |
+
////////////////////////////////////////////////////////////////
|
5428 |
+
////////// GENERAL OTL FUNCTIONS /////////////////
|
5429 |
+
////////////////////////////////////////////////////////////////
|
5430 |
+
////////////////////////////////////////////////////////////////
|
5431 |
+
|
5432 |
+
|
5433 |
+
function glyphToChar($gid) {
|
5434 |
+
return (ord($this->glyphIDtoUni[$gid*3]) << 16) + (ord($this->glyphIDtoUni[$gid*3+1]) << 8) + ord($this->glyphIDtoUni[$gid*3+2]);
|
5435 |
+
}
|
5436 |
+
|
5437 |
+
function unicode_hex($unicode_dec) {
|
5438 |
+
return (str_pad(strtoupper(dechex($unicode_dec)),5,'0',STR_PAD_LEFT));
|
5439 |
+
}
|
5440 |
+
|
5441 |
+
function seek($pos) {
|
5442 |
+
$this->_pos = $pos;
|
5443 |
+
}
|
5444 |
+
|
5445 |
+
function skip($delta) {
|
5446 |
+
$this->_pos += $delta;
|
5447 |
+
}
|
5448 |
+
function read_short() {
|
5449 |
+
$a = (ord($this->ttfOTLdata[$this->_pos])<<8) + ord($this->ttfOTLdata[$this->_pos+1]);
|
5450 |
+
if ($a & (1 << 15) ) {
|
5451 |
+
$a = ($a - (1 << 16));
|
5452 |
+
}
|
5453 |
+
$this->_pos += 2;
|
5454 |
+
return $a;
|
5455 |
+
}
|
5456 |
+
|
5457 |
+
function read_ushort() {
|
5458 |
+
$a = (ord($this->ttfOTLdata[$this->_pos])<<8) + ord($this->ttfOTLdata[$this->_pos+1]);
|
5459 |
+
$this->_pos += 2;
|
5460 |
+
return $a;
|
5461 |
+
}
|
5462 |
+
|
5463 |
+
|
5464 |
+
function _getCoverageGID() {
|
5465 |
+
// Called from Lookup Type 1, Format 1 - returns glyphIDs rather than hexstrings
|
5466 |
+
// Need to do this separately to cache separately
|
5467 |
+
// Otherwise the same as fn below _getCoverage
|
5468 |
+
$offset = $this->_pos;
|
5469 |
+
if (isset($this->LuDataCache[$this->fontkey]['GID'][$offset])) {
|
5470 |
+
$g = $this->LuDataCache[$this->fontkey]['GID'][$offset];
|
5471 |
+
}
|
5472 |
+
else {
|
5473 |
+
$g = array();
|
5474 |
+
$CoverageFormat= $this->read_ushort();
|
5475 |
+
if ($CoverageFormat == 1) {
|
5476 |
+
$CoverageGlyphCount= $this->read_ushort();
|
5477 |
+
for ($gid=0;$gid<$CoverageGlyphCount;$gid++) {
|
5478 |
+
$glyphID = $this->read_ushort();
|
5479 |
+
$g[] = $glyphID;
|
5480 |
+
}
|
5481 |
+
}
|
5482 |
+
if ($CoverageFormat == 2) {
|
5483 |
+
$RangeCount= $this->read_ushort();
|
5484 |
+
for ($r=0;$r<$RangeCount;$r++) {
|
5485 |
+
$start = $this->read_ushort();
|
5486 |
+
$end = $this->read_ushort();
|
5487 |
+
$StartCoverageIndex = $this->read_ushort(); // n/a
|
5488 |
+
for ($glyphID=$start;$glyphID<=$end;$glyphID++) {
|
5489 |
+
$g[] = $glyphID;
|
5490 |
+
}
|
5491 |
+
}
|
5492 |
+
}
|
5493 |
+
$this->LuDataCache[$this->fontkey]['GID'][$offset] = $g;
|
5494 |
+
}
|
5495 |
+
return $g;
|
5496 |
+
}
|
5497 |
+
|
5498 |
+
|
5499 |
+
function _getCoverage() {
|
5500 |
+
$offset = $this->_pos;
|
5501 |
+
if (isset($this->LuDataCache[$this->fontkey][$offset])) {
|
5502 |
+
$g = $this->LuDataCache[$this->fontkey][$offset];
|
5503 |
+
}
|
5504 |
+
else {
|
5505 |
+
$g = array();
|
5506 |
+
$CoverageFormat= $this->read_ushort();
|
5507 |
+
if ($CoverageFormat == 1) {
|
5508 |
+
$CoverageGlyphCount= $this->read_ushort();
|
5509 |
+
for ($gid=0;$gid<$CoverageGlyphCount;$gid++) {
|
5510 |
+
$glyphID = $this->read_ushort();
|
5511 |
+
$g[] = $this->unicode_hex($this->glyphToChar($glyphID));
|
5512 |
+
}
|
5513 |
+
}
|
5514 |
+
if ($CoverageFormat == 2) {
|
5515 |
+
$RangeCount= $this->read_ushort();
|
5516 |
+
for ($r=0;$r<$RangeCount;$r++) {
|
5517 |
+
$start = $this->read_ushort();
|
5518 |
+
$end = $this->read_ushort();
|
5519 |
+
$StartCoverageIndex = $this->read_ushort(); // n/a
|
5520 |
+
for ($glyphID=$start;$glyphID<=$end;$glyphID++) {
|
5521 |
+
$g[] = $this->unicode_hex($this->glyphToChar($glyphID));
|
5522 |
+
}
|
5523 |
+
}
|
5524 |
+
}
|
5525 |
+
$this->LuDataCache[$this->fontkey][$offset] = $g;
|
5526 |
+
}
|
5527 |
+
return $g;
|
5528 |
+
}
|
5529 |
+
|
5530 |
+
function _getClasses($offset) {
|
5531 |
+
if (isset($this->LuDataCache[$this->fontkey][$offset])) {
|
5532 |
+
$GlyphByClass = $this->LuDataCache[$this->fontkey][$offset];
|
5533 |
+
}
|
5534 |
+
else {
|
5535 |
+
$this->seek($offset);
|
5536 |
+
$ClassFormat = $this->read_ushort();
|
5537 |
+
$GlyphByClass = array();
|
5538 |
+
if ($ClassFormat == 1) {
|
5539 |
+
$StartGlyph = $this->read_ushort();
|
5540 |
+
$GlyphCount = $this->read_ushort();
|
5541 |
+
for ($i=0;$i<$GlyphCount;$i++) {
|
5542 |
+
$startGlyphID = $StartGlyph + $i;
|
5543 |
+
$endGlyphID = $StartGlyph + $i;
|
5544 |
+
$class = $this->read_ushort();
|
5545 |
+
// Note: Font FreeSerif , tag "blws"
|
5546 |
+
// $BacktrackClasses[0] is defined ? a mistake in the font ???
|
5547 |
+
// Let's ignore for now
|
5548 |
+
if ($class > 0) {
|
5549 |
+
for($g=$startGlyphID;$g<=$endGlyphID;$g++) {
|
5550 |
+
if ($this->glyphToChar($g)) {
|
5551 |
+
$GlyphByClass[$class][$this->glyphToChar($g)] = 1;
|
5552 |
+
}
|
5553 |
+
}
|
5554 |
+
}
|
5555 |
+
}
|
5556 |
+
}
|
5557 |
+
else if ($ClassFormat == 2) {
|
5558 |
+
$tableCount = $this->read_ushort();
|
5559 |
+
for ($i=0;$i<$tableCount;$i++) {
|
5560 |
+
$startGlyphID = $this->read_ushort();
|
5561 |
+
$endGlyphID = $this->read_ushort();
|
5562 |
+
$class = $this->read_ushort();
|
5563 |
+
// Note: Font FreeSerif , tag "blws"
|
5564 |
+
// $BacktrackClasses[0] is defined ? a mistake in the font ???
|
5565 |
+
// Let's ignore for now
|
5566 |
+
if ($class > 0) {
|
5567 |
+
for($g=$startGlyphID;$g<=$endGlyphID;$g++) {
|
5568 |
+
if ($this->glyphToChar($g)) {
|
5569 |
+
$GlyphByClass[$class][$this->glyphToChar($g)] = 1;
|
5570 |
+
}
|
5571 |
+
}
|
5572 |
+
}
|
5573 |
+
}
|
5574 |
+
}
|
5575 |
+
$this->LuDataCache[$this->fontkey][$offset] = $GlyphByClass;
|
5576 |
+
}
|
5577 |
+
return $GlyphByClass;
|
5578 |
+
}
|
5579 |
+
|
5580 |
+
|
5581 |
+
function _getOTLscriptTag($ScriptLang, $scripttag, $scriptblock, $shaper, $useOTL, $mode) {
|
5582 |
+
// ScriptLang is the array of available script/lang tags supported by the font
|
5583 |
+
// $scriptblock is the (number/code) for the script of the actual text string based on Unicode properties (UCDN::$uni_scriptblock)
|
5584 |
+
// $scripttag is the default tag derived from $scriptblock
|
5585 |
+
/*
|
5586 |
+
http://www.microsoft.com/typography/otspec/ttoreg.htm
|
5587 |
+
http://www.microsoft.com/typography/otspec/scripttags.htm
|
5588 |
+
|
5589 |
+
Values for useOTL
|
5590 |
+
|
5591 |
+
Bit dn hn Value
|
5592 |
+
1 1 0x0001 GSUB/GPOS - Latin scripts
|
5593 |
+
2 2 0x0002 GSUB/GPOS - Cyrillic scripts
|
5594 |
+
3 4 0x0004 GSUB/GPOS - Greek scripts
|
5595 |
+
4 8 0x0008 GSUB/GPOS - CJK scripts (excluding Hangul-Jamo)
|
5596 |
+
5 16 0x0010 (Reserved)
|
5597 |
+
6 32 0x0020 (Reserved)
|
5598 |
+
7 64 0x0040 (Reserved)
|
5599 |
+
8 128 0x0080 GSUB/GPOS - All other scripts (including all RTL scripts, complex scripts with shapers etc)
|
5600 |
+
|
5601 |
+
NB If change for RTL - cf. function magic_reverse_dir in mpdf.php to update
|
5602 |
+
|
5603 |
+
*/
|
5604 |
+
|
5605 |
+
|
5606 |
+
if ($scriptblock == UCDN::SCRIPT_LATIN) {
|
5607 |
+
if (!($useOTL & 0x01)) { return array('',false); }
|
5608 |
+
}
|
5609 |
+
else if ($scriptblock == UCDN::SCRIPT_CYRILLIC) {
|
5610 |
+
if (!($useOTL & 0x02)) { return array('',false); }
|
5611 |
+
}
|
5612 |
+
else if ($scriptblock == UCDN::SCRIPT_GREEK) {
|
5613 |
+
if (!($useOTL & 0x04)) { return array('',false); }
|
5614 |
+
}
|
5615 |
+
else if ($scriptblock >= UCDN::SCRIPT_HIRAGANA && $scriptblock <= UCDN::SCRIPT_YI ) {
|
5616 |
+
if (!($useOTL & 0x08)) { return array('',false); }
|
5617 |
+
}
|
5618 |
+
else {
|
5619 |
+
if (!($useOTL & 0x80)) { return array('',false); }
|
5620 |
+
}
|
5621 |
+
|
5622 |
+
// If availabletags includes scripttag - choose
|
5623 |
+
if (isset($ScriptLang[$scripttag])) { return array($scripttag, false); }
|
5624 |
+
|
5625 |
+
// If INDIC (or Myanmar) and available tag not includes new version, check if includes old version & choose old version
|
5626 |
+
if ($shaper) {
|
5627 |
+
switch($scripttag) {
|
5628 |
+
CASE 'bng2': if (isset($ScriptLang['beng'])) return array('beng',true);
|
5629 |
+
CASE 'dev2': if (isset($ScriptLang['deva'])) return array('deva',true);
|
5630 |
+
CASE 'gjr2': if (isset($ScriptLang['gujr'])) return array('gujr',true);
|
5631 |
+
CASE 'gur2': if (isset($ScriptLang['guru'])) return array('guru',true);
|
5632 |
+
CASE 'knd2': if (isset($ScriptLang['knda'])) return array('knda',true);
|
5633 |
+
CASE 'mlm2': if (isset($ScriptLang['mlym'])) return array('mlym',true);
|
5634 |
+
CASE 'ory2': if (isset($ScriptLang['orya'])) return array('orya',true);
|
5635 |
+
CASE 'tml2': if (isset($ScriptLang['taml'])) return array('taml',true);
|
5636 |
+
CASE 'tel2': if (isset($ScriptLang['telu'])) return array('telu',true);
|
5637 |
+
CASE 'mym2': if (isset($ScriptLang['mymr'])) return array('mymr',true);
|
5638 |
+
}
|
5639 |
+
}
|
5640 |
+
|
5641 |
+
// choose DFLT if present
|
5642 |
+
if (isset($ScriptLang['DFLT'])) { return array('DFLT', false); }
|
5643 |
+
// else choose dflt if present
|
5644 |
+
if (isset($ScriptLang['dflt'])) { return array('dflt', false); }
|
5645 |
+
// else return no scriptTag
|
5646 |
+
if (isset($ScriptLang['latn'])) { return array('latn', false); }
|
5647 |
+
// else return no scriptTag
|
5648 |
+
return array('',false);
|
5649 |
+
|
5650 |
+
}
|
5651 |
+
|
5652 |
+
|
5653 |
+
|
5654 |
+
// LangSys tags
|
5655 |
+
function _getOTLLangTag($ietf, $available) {
|
5656 |
+
// http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
|
5657 |
+
// http://www.microsoft.com/typography/otspec/languagetags.htm
|
5658 |
+
// IETF tag = e.g. en-US, und-Arab, sr-Cyrl cf. config_lang2fonts.php
|
5659 |
+
if ($available=='') { return ''; }
|
5660 |
+
$tags = preg_split('/-/',$ietf);
|
5661 |
+
$lang = '';
|
5662 |
+
$country = '';
|
5663 |
+
$script = '';
|
5664 |
+
$lang = strtolower($tags[0]);
|
5665 |
+
if (isset($tags[1]) && $tags[1]) {
|
5666 |
+
if (strlen($tags[1]) == 2) { $country = strtolower($tags[1]); }
|
5667 |
+
}
|
5668 |
+
if (isset($tags[2]) && $tags[2]) { $country = strtolower($tags[2]); }
|
5669 |
+
|
5670 |
+
if ($lang!='' && isset(UCDN::$ot_languages[$lang])) { $langsys = UCDN::$ot_languages[$lang]; }
|
5671 |
+
else if ($lang!='' && $country !='' && isset(UCDN::$ot_languages[$lang.''.$country])) {
|
5672 |
+
$langsys = UCDN::$ot_languages[$lang.''.$country];
|
5673 |
+
}
|
5674 |
+
else { $langsys = "DFLT"; }
|
5675 |
+
if (strpos($available, $langsys)===false) {
|
5676 |
+
if (strpos($available, "DFLT")!==false) { return "DFLT"; }
|
5677 |
+
else return '';
|
5678 |
+
}
|
5679 |
+
return $langsys;
|
5680 |
+
}
|
5681 |
+
|
5682 |
+
function _dumpproc($GPOSSUB, $lookupID, $subtable, $Type, $Format, $ptr, $currGlyph, $level) {
|
5683 |
+
echo '<div style="padding-left: '.($level*2).'em;">';
|
5684 |
+
echo $GPOSSUB .' LookupID #'.$lookupID.' Subtable#'.$subtable .' Type: '.$Type.' Format: '.$Format.'<br />';
|
5685 |
+
echo '<div style="font-family:monospace">';
|
5686 |
+
echo 'Glyph position: '.$ptr.' Current Glyph: '.$currGlyph.'<br />';
|
5687 |
+
|
5688 |
+
for ($i=0;$i<count($this->OTLdata);$i++) {
|
5689 |
+
if ($i==$ptr) { echo '<b>'; }
|
5690 |
+
echo $this->OTLdata[$i]['hex'] . ' ';
|
5691 |
+
if ($i==$ptr) { echo '</b>'; }
|
5692 |
+
}
|
5693 |
+
echo '<br />';
|
5694 |
+
|
5695 |
+
for ($i=0;$i<count($this->OTLdata);$i++) {
|
5696 |
+
if ($i==$ptr) { echo '<b>'; }
|
5697 |
+
echo str_pad($this->OTLdata[$i]['uni'],5) . ' ';
|
5698 |
+
if ($i==$ptr) { echo '</b>'; }
|
5699 |
+
}
|
5700 |
+
echo '<br />';
|
5701 |
+
|
5702 |
+
if ($GPOSSUB == 'GPOS') {
|
5703 |
+
for ($i=0;$i<count($this->OTLdata);$i++) {
|
5704 |
+
if (!empty($this->OTLdata[$i]['GPOSinfo'])) {
|
5705 |
+
echo $this->OTLdata[$i]['hex'] . ' &#x'.$this->OTLdata[$i]['hex'].'; ';
|
5706 |
+
print_r($this->OTLdata[$i]['GPOSinfo']);
|
5707 |
+
echo ' ';
|
5708 |
+
}
|
5709 |
+
}
|
5710 |
+
}
|
5711 |
+
|
5712 |
+
echo '</div>';
|
5713 |
+
echo '</div>';
|
5714 |
+
}
|
5715 |
+
|
5716 |
+
|
5717 |
+
}
|
5718 |
+
|
5719 |
+
?>
|
lib/mpdf/classes/otl_dump.php
ADDED
@@ -0,0 +1,3897 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*******************************************************************************
|
4 |
+
* otl_dump class *
|
5 |
+
*******************************************************************************/
|
6 |
+
|
7 |
+
// Define the value used in the "head" table of a created TTF file
|
8 |
+
// 0x74727565 "true" for Mac
|
9 |
+
// 0x00010000 for Windows
|
10 |
+
// Either seems to work for a font embedded in a PDF file
|
11 |
+
// when read by Adobe Reader on a Windows PC(!)
|
12 |
+
if (!defined('_TTF_MAC_HEADER')) define("_TTF_MAC_HEADER", false);
|
13 |
+
|
14 |
+
// Recalculate correct metadata/profiles when making subset fonts (not SIP/SMP)
|
15 |
+
// e.g. xMin, xMax, maxNContours
|
16 |
+
if (!defined('_RECALC_PROFILE')) define("_RECALC_PROFILE", false);
|
17 |
+
|
18 |
+
// TrueType Font Glyph operators
|
19 |
+
define("GF_WORDS",(1 << 0));
|
20 |
+
define("GF_SCALE",(1 << 3));
|
21 |
+
define("GF_MORE",(1 << 5));
|
22 |
+
define("GF_XYSCALE",(1 << 6));
|
23 |
+
define("GF_TWOBYTWO",(1 << 7));
|
24 |
+
|
25 |
+
// mPDF 5.7.1
|
26 |
+
if(!function_exists('unicode_hex')){
|
27 |
+
function unicode_hex($unicode_dec) {
|
28 |
+
return (sprintf("%05s", strtoupper(dechex($unicode_dec))));
|
29 |
+
}
|
30 |
+
}
|
31 |
+
class OTLdump {
|
32 |
+
|
33 |
+
var $GPOSFeatures; // mPDF 5.7.1
|
34 |
+
var $GPOSLookups; // mPDF 5.7.1
|
35 |
+
var $GPOSScriptLang; // mPDF 5.7.1
|
36 |
+
var $ignoreStrings; // mPDF 5.7.1
|
37 |
+
var $MarkAttachmentType; // mPDF 5.7.1
|
38 |
+
var $MarkGlyphSets; // mPDF 7.5.1
|
39 |
+
var $GlyphClassMarks; // mPDF 5.7.1
|
40 |
+
var $GlyphClassLigatures; // mPDF 5.7.1
|
41 |
+
var $GlyphClassBases; // mPDF 5.7.1
|
42 |
+
var $GlyphClassComponents; // mPDF 5.7.1
|
43 |
+
var $GSUBScriptLang; // mPDF 5.7.1
|
44 |
+
var $rtlPUAstr; // mPDF 5.7.1
|
45 |
+
var $rtlPUAarr; // mPDF 5.7.1
|
46 |
+
var $fontkey; // mPDF 5.7.1
|
47 |
+
var $useOTL; // mPDF 5.7.1
|
48 |
+
var $panose;
|
49 |
+
var $maxUni;
|
50 |
+
var $sFamilyClass;
|
51 |
+
var $sFamilySubClass;
|
52 |
+
var $sipset;
|
53 |
+
var $smpset;
|
54 |
+
var $_pos;
|
55 |
+
var $numTables;
|
56 |
+
var $searchRange;
|
57 |
+
var $entrySelector;
|
58 |
+
var $rangeShift;
|
59 |
+
var $tables;
|
60 |
+
var $otables;
|
61 |
+
var $filename;
|
62 |
+
var $fh;
|
63 |
+
var $glyphPos;
|
64 |
+
var $charToGlyph;
|
65 |
+
var $ascent;
|
66 |
+
var $descent;
|
67 |
+
var $name;
|
68 |
+
var $familyName;
|
69 |
+
var $styleName;
|
70 |
+
var $fullName;
|
71 |
+
var $uniqueFontID;
|
72 |
+
var $unitsPerEm;
|
73 |
+
var $bbox;
|
74 |
+
var $capHeight;
|
75 |
+
var $stemV;
|
76 |
+
var $italicAngle;
|
77 |
+
var $flags;
|
78 |
+
var $underlinePosition;
|
79 |
+
var $underlineThickness;
|
80 |
+
var $charWidths;
|
81 |
+
var $defaultWidth;
|
82 |
+
var $maxStrLenRead;
|
83 |
+
var $numTTCFonts;
|
84 |
+
var $TTCFonts;
|
85 |
+
var $maxUniChar;
|
86 |
+
var $kerninfo;
|
87 |
+
|
88 |
+
function OTLdump(&$mpdf) {
|
89 |
+
$this->mpdf = $mpdf;
|
90 |
+
$this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
|
91 |
+
}
|
92 |
+
|
93 |
+
|
94 |
+
function getMetrics($file, $fontkey, $TTCfontID=0, $debug=false, $BMPonly=false, $kerninfo=false, $useOTL=0, $mode) { // mPDF 5.7.1
|
95 |
+
$this->mode = $mode;
|
96 |
+
$this->useOTL = $useOTL; // mPDF 5.7.1
|
97 |
+
$this->fontkey = $fontkey; // mPDF 5.7.1
|
98 |
+
$this->filename = $file;
|
99 |
+
$this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file);
|
100 |
+
$this->_pos = 0;
|
101 |
+
$this->charWidths = '';
|
102 |
+
$this->glyphPos = array();
|
103 |
+
$this->charToGlyph = array();
|
104 |
+
$this->tables = array();
|
105 |
+
$this->otables = array();
|
106 |
+
$this->kerninfo = array();
|
107 |
+
$this->ascent = 0;
|
108 |
+
$this->descent = 0;
|
109 |
+
$this->numTTCFonts = 0;
|
110 |
+
$this->TTCFonts = array();
|
111 |
+
$this->version = $version = $this->read_ulong();
|
112 |
+
$this->panose = array();
|
113 |
+
if ($version==0x4F54544F)
|
114 |
+
die("Postscript outlines are not supported");
|
115 |
+
if ($version==0x74746366 && !$TTCfontID)
|
116 |
+
die("ERROR - You must define the TTCfontID for a TrueType Collection in config_fonts.php (". $file.")");
|
117 |
+
if (!in_array($version, array(0x00010000,0x74727565)) && !$TTCfontID)
|
118 |
+
die("Not a TrueType font: version=".$version);
|
119 |
+
if ($TTCfontID > 0) {
|
120 |
+
$this->version = $version = $this->read_ulong(); // TTC Header version now
|
121 |
+
if (!in_array($version, array(0x00010000,0x00020000)))
|
122 |
+
die("ERROR - Error parsing TrueType Collection: version=".$version." - " . $file);
|
123 |
+
$this->numTTCFonts = $this->read_ulong();
|
124 |
+
for ($i=1; $i<=$this->numTTCFonts; $i++) {
|
125 |
+
$this->TTCFonts[$i]['offset'] = $this->read_ulong();
|
126 |
+
}
|
127 |
+
$this->seek($this->TTCFonts[$TTCfontID]['offset']);
|
128 |
+
$this->version = $version = $this->read_ulong(); // TTFont version again now
|
129 |
+
}
|
130 |
+
$this->readTableDirectory($debug);
|
131 |
+
$this->extractInfo($debug, $BMPonly, $kerninfo, $useOTL);
|
132 |
+
fclose($this->fh);
|
133 |
+
}
|
134 |
+
|
135 |
+
|
136 |
+
function readTableDirectory($debug=false) {
|
137 |
+
$this->numTables = $this->read_ushort();
|
138 |
+
$this->searchRange = $this->read_ushort();
|
139 |
+
$this->entrySelector = $this->read_ushort();
|
140 |
+
$this->rangeShift = $this->read_ushort();
|
141 |
+
$this->tables = array();
|
142 |
+
for ($i=0;$i<$this->numTables;$i++) {
|
143 |
+
$record = array();
|
144 |
+
$record['tag'] = $this->read_tag();
|
145 |
+
$record['checksum'] = array($this->read_ushort(),$this->read_ushort());
|
146 |
+
$record['offset'] = $this->read_ulong();
|
147 |
+
$record['length'] = $this->read_ulong();
|
148 |
+
$this->tables[$record['tag']] = $record;
|
149 |
+
}
|
150 |
+
if ($debug) $this->checksumTables();
|
151 |
+
}
|
152 |
+
|
153 |
+
function checksumTables() {
|
154 |
+
// Check the checksums for all tables
|
155 |
+
foreach($this->tables AS $t) {
|
156 |
+
if ($t['length'] > 0 && $t['length'] < $this->maxStrLenRead) { // 1.02
|
157 |
+
$table = $this->get_chunk($t['offset'], $t['length']);
|
158 |
+
$checksum = $this->calcChecksum($table);
|
159 |
+
if ($t['tag'] == 'head') {
|
160 |
+
$up = unpack('n*', substr($table,8,4));
|
161 |
+
$adjustment[0] = $up[1];
|
162 |
+
$adjustment[1] = $up[2];
|
163 |
+
$checksum = $this->sub32($checksum, $adjustment);
|
164 |
+
}
|
165 |
+
$xchecksum = $t['checksum'];
|
166 |
+
if ($xchecksum != $checksum)
|
167 |
+
die(sprintf('TTF file "%s": invalid checksum %s table: %s (expected %s)', $this->filename,dechex($checksum[0]).dechex($checksum[1]),$t['tag'],dechex($xchecksum[0]).dechex($xchecksum[1])));
|
168 |
+
}
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
function sub32($x, $y) {
|
173 |
+
$xlo = $x[1];
|
174 |
+
$xhi = $x[0];
|
175 |
+
$ylo = $y[1];
|
176 |
+
$yhi = $y[0];
|
177 |
+
if ($ylo > $xlo) { $xlo += 1 << 16; $yhi += 1; }
|
178 |
+
$reslo = $xlo-$ylo;
|
179 |
+
if ($yhi > $xhi) { $xhi += 1 << 16; }
|
180 |
+
$reshi = $xhi-$yhi;
|
181 |
+
$reshi = $reshi & 0xFFFF;
|
182 |
+
return array($reshi, $reslo);
|
183 |
+
}
|
184 |
+
|
185 |
+
function calcChecksum($data) {
|
186 |
+
if (strlen($data) % 4) { $data .= str_repeat("\0",(4-(strlen($data) % 4))); }
|
187 |
+
$len = strlen($data);
|
188 |
+
$hi=0x0000;
|
189 |
+
$lo=0x0000;
|
190 |
+
for($i=0;$i<$len;$i+=4) {
|
191 |
+
$hi += (ord($data[$i])<<8) + ord($data[$i+1]);
|
192 |
+
$lo += (ord($data[$i+2])<<8) + ord($data[$i+3]);
|
193 |
+
$hi += ($lo >> 16) & 0xFFFF;
|
194 |
+
$lo = $lo & 0xFFFF;
|
195 |
+
}
|
196 |
+
return array($hi, $lo);
|
197 |
+
}
|
198 |
+
|
199 |
+
function get_table_pos($tag) {
|
200 |
+
$offset = $this->tables[$tag]['offset'];
|
201 |
+
$length = $this->tables[$tag]['length'];
|
202 |
+
return array($offset, $length);
|
203 |
+
}
|
204 |
+
|
205 |
+
function seek($pos) {
|
206 |
+
$this->_pos = $pos;
|
207 |
+
fseek($this->fh,$this->_pos);
|
208 |
+
}
|
209 |
+
|
210 |
+
function skip($delta) {
|
211 |
+
$this->_pos = $this->_pos + $delta;
|
212 |
+
fseek($this->fh,$delta,SEEK_CUR);
|
213 |
+
}
|
214 |
+
|
215 |
+
function seek_table($tag, $offset_in_table = 0) {
|
216 |
+
$tpos = $this->get_table_pos($tag);
|
217 |
+
$this->_pos = $tpos[0] + $offset_in_table;
|
218 |
+
fseek($this->fh, $this->_pos);
|
219 |
+
return $this->_pos;
|
220 |
+
}
|
221 |
+
|
222 |
+
function read_tag() {
|
223 |
+
$this->_pos += 4;
|
224 |
+
return fread($this->fh,4);
|
225 |
+
}
|
226 |
+
|
227 |
+
function read_short() {
|
228 |
+
$this->_pos += 2;
|
229 |
+
$s = fread($this->fh,2);
|
230 |
+
$a = (ord($s[0])<<8) + ord($s[1]);
|
231 |
+
if ($a & (1 << 15) ) {
|
232 |
+
$a = ($a - (1 << 16));
|
233 |
+
}
|
234 |
+
return $a;
|
235 |
+
}
|
236 |
+
|
237 |
+
function unpack_short($s) {
|
238 |
+
$a = (ord($s[0])<<8) + ord($s[1]);
|
239 |
+
if ($a & (1 << 15) ) {
|
240 |
+
$a = ($a - (1 << 16));
|
241 |
+
}
|
242 |
+
return $a;
|
243 |
+
}
|
244 |
+
|
245 |
+
function read_ushort() {
|
246 |
+
$this->_pos += 2;
|
247 |
+
$s = fread($this->fh,2);
|
248 |
+
return (ord($s[0])<<8) + ord($s[1]);
|
249 |
+
}
|
250 |
+
|
251 |
+
function read_ulong() {
|
252 |
+
$this->_pos += 4;
|
253 |
+
$s = fread($this->fh,4);
|
254 |
+
// if large uInt32 as an integer, PHP converts it to -ve
|
255 |
+
return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 16777216 = 1<<24
|
256 |
+
}
|
257 |
+
|
258 |
+
function get_ushort($pos) {
|
259 |
+
fseek($this->fh,$pos);
|
260 |
+
$s = fread($this->fh,2);
|
261 |
+
return (ord($s[0])<<8) + ord($s[1]);
|
262 |
+
}
|
263 |
+
|
264 |
+
function get_ulong($pos) {
|
265 |
+
fseek($this->fh,$pos);
|
266 |
+
$s = fread($this->fh,4);
|
267 |
+
// iF large uInt32 as an integer, PHP converts it to -ve
|
268 |
+
return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 16777216 = 1<<24
|
269 |
+
}
|
270 |
+
|
271 |
+
function pack_short($val) {
|
272 |
+
if ($val<0) {
|
273 |
+
$val = abs($val);
|
274 |
+
$val = ~$val;
|
275 |
+
$val += 1;
|
276 |
+
}
|
277 |
+
return pack("n",$val);
|
278 |
+
}
|
279 |
+
|
280 |
+
function splice($stream, $offset, $value) {
|
281 |
+
return substr($stream,0,$offset) . $value . substr($stream,$offset+strlen($value));
|
282 |
+
}
|
283 |
+
|
284 |
+
function _set_ushort($stream, $offset, $value) {
|
285 |
+
$up = pack("n", $value);
|
286 |
+
return $this->splice($stream, $offset, $up);
|
287 |
+
}
|
288 |
+
|
289 |
+
function _set_short($stream, $offset, $val) {
|
290 |
+
if ($val<0) {
|
291 |
+
$val = abs($val);
|
292 |
+
$val = ~$val;
|
293 |
+
$val += 1;
|
294 |
+
}
|
295 |
+
$up = pack("n",$val);
|
296 |
+
return $this->splice($stream, $offset, $up);
|
297 |
+
}
|
298 |
+
|
299 |
+
function get_chunk($pos, $length) {
|
300 |
+
fseek($this->fh,$pos);
|
301 |
+
if ($length <1) { return ''; }
|
302 |
+
return (fread($this->fh,$length));
|
303 |
+
}
|
304 |
+
|
305 |
+
function get_table($tag) {
|
306 |
+
list($pos, $length) = $this->get_table_pos($tag);
|
307 |
+
if ($length == 0) { return ''; }
|
308 |
+
fseek($this->fh,$pos);
|
309 |
+
return (fread($this->fh,$length));
|
310 |
+
}
|
311 |
+
|
312 |
+
function add($tag, $data) {
|
313 |
+
if ($tag == 'head') {
|
314 |
+
$data = $this->splice($data, 8, "\0\0\0\0");
|
315 |
+
}
|
316 |
+
$this->otables[$tag] = $data;
|
317 |
+
}
|
318 |
+
|
319 |
+
|
320 |
+
|
321 |
+
|
322 |
+
/////////////////////////////////////////////////////////////////////////////////////////
|
323 |
+
|
324 |
+
/////////////////////////////////////////////////////////////////////////////////////////
|
325 |
+
|
326 |
+
function extractInfo($debug=false, $BMPonly=false, $kerninfo=false, $useOTL=0) {
|
327 |
+
$this->panose = array();
|
328 |
+
$this->sFamilyClass = 0;
|
329 |
+
$this->sFamilySubClass = 0;
|
330 |
+
///////////////////////////////////
|
331 |
+
// name - Naming table
|
332 |
+
///////////////////////////////////
|
333 |
+
$name_offset = $this->seek_table("name");
|
334 |
+
$format = $this->read_ushort();
|
335 |
+
if ($format != 0 && $format != 1)
|
336 |
+
die("Unknown name table format ".$format);
|
337 |
+
$numRecords = $this->read_ushort();
|
338 |
+
$string_data_offset = $name_offset + $this->read_ushort();
|
339 |
+
$names = array(1=>'',2=>'',3=>'',4=>'',6=>'');
|
340 |
+
$K = array_keys($names);
|
341 |
+
$nameCount = count($names);
|
342 |
+
for ($i=0;$i<$numRecords; $i++) {
|
343 |
+
$platformId = $this->read_ushort();
|
344 |
+
$encodingId = $this->read_ushort();
|
345 |
+
$languageId = $this->read_ushort();
|
346 |
+
$nameId = $this->read_ushort();
|
347 |
+
$length = $this->read_ushort();
|
348 |
+
$offset = $this->read_ushort();
|
349 |
+
if (!in_array($nameId,$K)) continue;
|
350 |
+
$N = '';
|
351 |
+
if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name
|
352 |
+
$opos = $this->_pos;
|
353 |
+
$this->seek($string_data_offset + $offset);
|
354 |
+
if ($length % 2 != 0)
|
355 |
+
die("PostScript name is UTF-16BE string of odd length");
|
356 |
+
$length /= 2;
|
357 |
+
$N = '';
|
358 |
+
while ($length > 0) {
|
359 |
+
$char = $this->read_ushort();
|
360 |
+
$N .= (chr($char));
|
361 |
+
$length -= 1;
|
362 |
+
}
|
363 |
+
$this->_pos = $opos;
|
364 |
+
$this->seek($opos);
|
365 |
+
}
|
366 |
+
else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name
|
367 |
+
$opos = $this->_pos;
|
368 |
+
$N = $this->get_chunk($string_data_offset + $offset, $length);
|
369 |
+
$this->_pos = $opos;
|
370 |
+
$this->seek($opos);
|
371 |
+
}
|
372 |
+
if ($N && $names[$nameId]=='') {
|
373 |
+
$names[$nameId] = $N;
|
374 |
+
$nameCount -= 1;
|
375 |
+
if ($nameCount==0) break;
|
376 |
+
}
|
377 |
+
}
|
378 |
+
if ($names[6])
|
379 |
+
$psName = $names[6];
|
380 |
+
else if ($names[4])
|
381 |
+
$psName = preg_replace('/ /','-',$names[4]);
|
382 |
+
else if ($names[1])
|
383 |
+
$psName = preg_replace('/ /','-',$names[1]);
|
384 |
+
else
|
385 |
+
$psName = '';
|
386 |
+
if (!$psName)
|
387 |
+
die("Could not find PostScript font name: ".$this->filename);
|
388 |
+
if ($debug) {
|
389 |
+
for ($i=0;$i<count($psName);$i++) {
|
390 |
+
$c = $psName[$i];
|
391 |
+
$oc = ord($c);
|
392 |
+
if ($oc>126 || strpos(' [](){}<>/%',$c)!==false)
|
393 |
+
die("psName=".$psName." contains invalid character ".$c." ie U+".ord(c));
|
394 |
+
}
|
395 |
+
}
|
396 |
+
$this->name = $psName;
|
397 |
+
if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; }
|
398 |
+
if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
|
399 |
+
if ($names[4]) { $this->fullName = $names[4]; } else { $this->fullName = $psName; }
|
400 |
+
if ($names[3]) { $this->uniqueFontID = $names[3]; } else { $this->uniqueFontID = $psName; }
|
401 |
+
|
402 |
+
if ($names[6]) { $this->fullName = $names[6]; }
|
403 |
+
|
404 |
+
///////////////////////////////////
|
405 |
+
// head - Font header table
|
406 |
+
///////////////////////////////////
|
407 |
+
$this->seek_table("head");
|
408 |
+
if ($debug) {
|
409 |
+
$ver_maj = $this->read_ushort();
|
410 |
+
$ver_min = $this->read_ushort();
|
411 |
+
if ($ver_maj != 1)
|
412 |
+
die('Unknown head table version '. $ver_maj .'.'. $ver_min);
|
413 |
+
$this->fontRevision = $this->read_ushort() . $this->read_ushort();
|
414 |
+
|
415 |
+
$this->skip(4);
|
416 |
+
$magic = $this->read_ulong();
|
417 |
+
if ($magic != 0x5F0F3CF5)
|
418 |
+
die('Invalid head table magic ' .$magic);
|
419 |
+
$this->skip(2);
|
420 |
+
}
|
421 |
+
else {
|
422 |
+
$this->skip(18);
|
423 |
+
}
|
424 |
+
$this->unitsPerEm = $unitsPerEm = $this->read_ushort();
|
425 |
+
$scale = 1000 / $unitsPerEm;
|
426 |
+
$this->skip(16);
|
427 |
+
$xMin = $this->read_short();
|
428 |
+
$yMin = $this->read_short();
|
429 |
+
$xMax = $this->read_short();
|
430 |
+
$yMax = $this->read_short();
|
431 |
+
$this->bbox = array(($xMin*$scale), ($yMin*$scale), ($xMax*$scale), ($yMax*$scale));
|
432 |
+
$this->skip(3*2);
|
433 |
+
$indexToLocFormat = $this->read_ushort();
|
434 |
+
$glyphDataFormat = $this->read_ushort();
|
435 |
+
if ($glyphDataFormat != 0)
|
436 |
+
die('Unknown glyph data format '.$glyphDataFormat);
|
437 |
+
|
438 |
+
///////////////////////////////////
|
439 |
+
// hhea metrics table
|
440 |
+
///////////////////////////////////
|
441 |
+
// ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility
|
442 |
+
if (isset($this->tables["hhea"])) {
|
443 |
+
$this->seek_table("hhea");
|
444 |
+
$this->skip(4);
|
445 |
+
$hheaAscender = $this->read_short();
|
446 |
+
$hheaDescender = $this->read_short();
|
447 |
+
$this->ascent = ($hheaAscender *$scale);
|
448 |
+
$this->descent = ($hheaDescender *$scale);
|
449 |
+
}
|
450 |
+
|
451 |
+
///////////////////////////////////
|
452 |
+
// OS/2 - OS/2 and Windows metrics table
|
453 |
+
///////////////////////////////////
|
454 |
+
if (isset($this->tables["OS/2"])) {
|
455 |
+
$this->seek_table("OS/2");
|
456 |
+
$version = $this->read_ushort();
|
457 |
+
$this->skip(2);
|
458 |
+
$usWeightClass = $this->read_ushort();
|
459 |
+
$this->skip(2);
|
460 |
+
$fsType = $this->read_ushort();
|
461 |
+
if ($fsType == 0x0002 || ($fsType & 0x0300) != 0) {
|
462 |
+
global $overrideTTFFontRestriction;
|
463 |
+
if (!$overrideTTFFontRestriction) die('ERROR - Font file '.$this->filename.' cannot be embedded due to copyright restrictions.');
|
464 |
+
$this->restrictedUse = true;
|
465 |
+
}
|
466 |
+
$this->skip(20);
|
467 |
+
$sF = $this->read_short();
|
468 |
+
$this->sFamilyClass = ($sF >> 8);
|
469 |
+
$this->sFamilySubClass = ($sF & 0xFF);
|
470 |
+
$this->_pos += 10; //PANOSE = 10 byte length
|
471 |
+
$panose = fread($this->fh,10);
|
472 |
+
$this->panose = array();
|
473 |
+
for ($p=0;$p<strlen($panose);$p++) { $this->panose[] = ord($panose[$p]); }
|
474 |
+
$this->skip(26);
|
475 |
+
$sTypoAscender = $this->read_short();
|
476 |
+
$sTypoDescender = $this->read_short();
|
477 |
+
if (!$this->ascent) $this->ascent = ($sTypoAscender*$scale);
|
478 |
+
if (!$this->descent) $this->descent = ($sTypoDescender*$scale);
|
479 |
+
if ($version > 1) {
|
480 |
+
$this->skip(16);
|
481 |
+
$sCapHeight = $this->read_short();
|
482 |
+
$this->capHeight = ($sCapHeight*$scale);
|
483 |
+
}
|
484 |
+
else {
|
485 |
+
$this->capHeight = $this->ascent;
|
486 |
+
}
|
487 |
+
}
|
488 |
+
else {
|
489 |
+
$usWeightClass = 500;
|
490 |
+
if (!$this->ascent) $this->ascent = ($yMax*$scale);
|
491 |
+
if (!$this->descent) $this->descent = ($yMin*$scale);
|
492 |
+
$this->capHeight = $this->ascent;
|
493 |
+
}
|
494 |
+
$this->stemV = 50 + intval(pow(($usWeightClass / 65.0),2));
|
495 |
+
|
496 |
+
///////////////////////////////////
|
497 |
+
// post - PostScript table
|
498 |
+
///////////////////////////////////
|
499 |
+
$this->seek_table("post");
|
500 |
+
if ($debug) {
|
501 |
+
$ver_maj = $this->read_ushort();
|
502 |
+
$ver_min = $this->read_ushort();
|
503 |
+
if ($ver_maj <1 || $ver_maj >4)
|
504 |
+
die('Unknown post table version '.$ver_maj);
|
505 |
+
}
|
506 |
+
else {
|
507 |
+
$this->skip(4);
|
508 |
+
}
|
509 |
+
$this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0;
|
510 |
+
$this->underlinePosition = $this->read_short() * $scale;
|
511 |
+
$this->underlineThickness = $this->read_short() * $scale;
|
512 |
+
$isFixedPitch = $this->read_ulong();
|
513 |
+
|
514 |
+
$this->flags = 4;
|
515 |
+
|
516 |
+
if ($this->italicAngle!= 0)
|
517 |
+
$this->flags = $this->flags | 64;
|
518 |
+
if ($usWeightClass >= 600)
|
519 |
+
$this->flags = $this->flags | 262144;
|
520 |
+
if ($isFixedPitch)
|
521 |
+
$this->flags = $this->flags | 1;
|
522 |
+
|
523 |
+
///////////////////////////////////
|
524 |
+
// hhea - Horizontal header table
|
525 |
+
///////////////////////////////////
|
526 |
+
$this->seek_table("hhea");
|
527 |
+
if ($debug) {
|
528 |
+
$ver_maj = $this->read_ushort();
|
529 |
+
$ver_min = $this->read_ushort();
|
530 |
+
if ($ver_maj != 1)
|
531 |
+
die('Unknown hhea table version '.$ver_maj);
|
532 |
+
$this->skip(28);
|
533 |
+
}
|
534 |
+
else {
|
535 |
+
$this->skip(32);
|
536 |
+
}
|
537 |
+
$metricDataFormat = $this->read_ushort();
|
538 |
+
if ($metricDataFormat != 0)
|
539 |
+
die('Unknown horizontal metric data format '.$metricDataFormat);
|
540 |
+
$numberOfHMetrics = $this->read_ushort();
|
541 |
+
if ($numberOfHMetrics == 0)
|
542 |
+
die('Number of horizontal metrics is 0');
|
543 |
+
|
544 |
+
///////////////////////////////////
|
545 |
+
// maxp - Maximum profile table
|
546 |
+
///////////////////////////////////
|
547 |
+
$this->seek_table("maxp");
|
548 |
+
if ($debug) {
|
549 |
+
$ver_maj = $this->read_ushort();
|
550 |
+
$ver_min = $this->read_ushort();
|
551 |
+
if ($ver_maj != 1)
|
552 |
+
die('Unknown maxp table version '.$ver_maj);
|
553 |
+
}
|
554 |
+
else {
|
555 |
+
$this->skip(4);
|
556 |
+
}
|
557 |
+
$numGlyphs = $this->read_ushort();
|
558 |
+
|
559 |
+
|
560 |
+
///////////////////////////////////
|
561 |
+
// cmap - Character to glyph index mapping table
|
562 |
+
///////////////////////////////////
|
563 |
+
$cmap_offset = $this->seek_table("cmap");
|
564 |
+
$this->skip(2);
|
565 |
+
$cmapTableCount = $this->read_ushort();
|
566 |
+
$unicode_cmap_offset = 0;
|
567 |
+
for ($i=0;$i<$cmapTableCount;$i++) {
|
568 |
+
$platformID = $this->read_ushort();
|
569 |
+
$encodingID = $this->read_ushort();
|
570 |
+
$offset = $this->read_ulong();
|
571 |
+
$save_pos = $this->_pos;
|
572 |
+
if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
|
573 |
+
$format = $this->get_ushort($cmap_offset + $offset);
|
574 |
+
if ($format == 4) {
|
575 |
+
if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset;
|
576 |
+
if ($BMPonly) break;
|
577 |
+
}
|
578 |
+
}
|
579 |
+
// Microsoft, Unicode Format 12 table HKCS
|
580 |
+
else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0) && !$BMPonly) {
|
581 |
+
$format = $this->get_ushort($cmap_offset + $offset);
|
582 |
+
if ($format == 12) {
|
583 |
+
$unicode_cmap_offset = $cmap_offset + $offset;
|
584 |
+
break;
|
585 |
+
}
|
586 |
+
}
|
587 |
+
$this->seek($save_pos );
|
588 |
+
}
|
589 |
+
|
590 |
+
if (!$unicode_cmap_offset)
|
591 |
+
die('Font ('.$this->filename .') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
|
592 |
+
|
593 |
+
|
594 |
+
$sipset = false;
|
595 |
+
$smpset = false;
|
596 |
+
|
597 |
+
// mPDF 5.7.1
|
598 |
+
$this->GSUBScriptLang = array();
|
599 |
+
$this->rtlPUAstr = '';
|
600 |
+
$this->rtlPUAarr = array();
|
601 |
+
$this->GSUBFeatures = array();
|
602 |
+
$this->GSUBLookups = array();
|
603 |
+
$this->GPOSScriptLang = array();
|
604 |
+
$this->GPOSFeatures = array();
|
605 |
+
$this->GPOSLookups = array();
|
606 |
+
$this->glyphIDtoUni = '';
|
607 |
+
|
608 |
+
// Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
|
609 |
+
if ($format == 12 && !$BMPonly) {
|
610 |
+
$this->maxUniChar = 0;
|
611 |
+
$this->seek($unicode_cmap_offset + 4);
|
612 |
+
$length = $this->read_ulong();
|
613 |
+
$limit = $unicode_cmap_offset + $length;
|
614 |
+
$this->skip(4);
|
615 |
+
|
616 |
+
$nGroups = $this->read_ulong();
|
617 |
+
|
618 |
+
$glyphToChar = array();
|
619 |
+
$charToGlyph = array();
|
620 |
+
for($i=0; $i<$nGroups ; $i++) {
|
621 |
+
$startCharCode = $this->read_ulong();
|
622 |
+
$endCharCode = $this->read_ulong();
|
623 |
+
$startGlyphCode = $this->read_ulong();
|
624 |
+
if ($endCharCode > 0x20000 && $endCharCode < 0x2FFFF) {
|
625 |
+
$sipset = true;
|
626 |
+
}
|
627 |
+
else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
|
628 |
+
$smpset = true;
|
629 |
+
}
|
630 |
+
$offset = 0;
|
631 |
+
for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
|
632 |
+
$glyph = $startGlyphCode + $offset ;
|
633 |
+
$offset++;
|
634 |
+
if ($unichar < 0x30000) {
|
635 |
+
$charToGlyph[$unichar] = $glyph;
|
636 |
+
$this->maxUniChar = max($unichar,$this->maxUniChar);
|
637 |
+
$glyphToChar[$glyph][] = $unichar;
|
638 |
+
}
|
639 |
+
}
|
640 |
+
}
|
641 |
+
}
|
642 |
+
else {
|
643 |
+
|
644 |
+
$glyphToChar = array();
|
645 |
+
$charToGlyph = array();
|
646 |
+
$this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
|
647 |
+
|
648 |
+
}
|
649 |
+
$this->sipset = $sipset ;
|
650 |
+
$this->smpset = $smpset ;
|
651 |
+
|
652 |
+
|
653 |
+
///////////////////////////////////
|
654 |
+
// mPDF 5.7.1
|
655 |
+
// Map Unmapped glyphs - from $numGlyphs
|
656 |
+
if ($this->useOTL) {
|
657 |
+
$bctr = 0xE000;
|
658 |
+
for ($gid=1; $gid<$numGlyphs; $gid++) {
|
659 |
+
if (!isset($glyphToChar[$gid])) {
|
660 |
+
while(isset($charToGlyph[$bctr])) { $bctr++; } // Avoid overwriting a glyph already mapped in PUA
|
661 |
+
if (($bctr > 0xF8FF) && ($bctr < 0x2CEB0)) {
|
662 |
+
if (!$BMPonly) {
|
663 |
+
$bctr = 0x2CEB0; // Use unassigned area 0x2CEB0 to 0x2F7FF (space for 10,000 characters)
|
664 |
+
$this->sipset = $sipset = true; // forces subsetting; also ensure charwidths are saved
|
665 |
+
while(isset($charToGlyph[$bctr])) { $bctr++; }
|
666 |
+
}
|
667 |
+
else { die($names[1]." : WARNING - The font does not have enough space to map all (unmapped) included glyphs into Private Use Area U+E000 - U+F8FF"); }
|
668 |
+
}
|
669 |
+
$glyphToChar[$gid][] = $bctr;
|
670 |
+
$charToGlyph[$bctr] = $gid;
|
671 |
+
$this->maxUniChar = max($bctr,$this->maxUniChar);
|
672 |
+
$bctr++;
|
673 |
+
}
|
674 |
+
}
|
675 |
+
}
|
676 |
+
$this->glyphToChar = $glyphToChar;
|
677 |
+
$this->charToGlyph = $charToGlyph;
|
678 |
+
///////////////////////////////////
|
679 |
+
// mPDF 5.7.1 OpenType Layout tables
|
680 |
+
$this->GSUBScriptLang=array(); $this->rtlPUAstr = ''; $this->rtlPUAarr = array();
|
681 |
+
if ($useOTL) {
|
682 |
+
$this->_getGDEFtables();
|
683 |
+
list($this->GSUBScriptLang, $this->GSUBFeatures, $this->GSUBLookups, $this->rtlPUAstr, $this->rtlPUAarr) = $this->_getGSUBtables();
|
684 |
+
list($this->GPOSScriptLang, $this->GPOSFeatures, $this->GPOSLookups) = $this->_getGPOStables();
|
685 |
+
$this->glyphIDtoUni = str_pad('', 256*256*3, "\x00");
|
686 |
+
foreach($glyphToChar AS $gid=>$arr) {
|
687 |
+
if (isset($glyphToChar[$gid][0])) {
|
688 |
+
$char = $glyphToChar[$gid][0];
|
689 |
+
if ($char != 0 && $char != 65535) {
|
690 |
+
$this->glyphIDtoUni[$gid*3] = chr($char >> 16);
|
691 |
+
$this->glyphIDtoUni[$gid*3 + 1] = chr(($char >> 8) & 0xFF);
|
692 |
+
$this->glyphIDtoUni[$gid*3 + 2] = chr($char & 0xFF);
|
693 |
+
}
|
694 |
+
}
|
695 |
+
}
|
696 |
+
}
|
697 |
+
///////////////////////////////////
|
698 |
+
|
699 |
+
///////////////////////////////////
|
700 |
+
// hmtx - Horizontal metrics table
|
701 |
+
///////////////////////////////////
|
702 |
+
$this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
|
703 |
+
|
704 |
+
///////////////////////////////////
|
705 |
+
// kern - Kerning pair table
|
706 |
+
///////////////////////////////////
|
707 |
+
if ($kerninfo) {
|
708 |
+
// Recognises old form of Kerning table - as required by Windows - Format 0 only
|
709 |
+
$kern_offset = $this->seek_table("kern");
|
710 |
+
$version = $this->read_ushort();
|
711 |
+
$nTables = $this->read_ushort();
|
712 |
+
// subtable header
|
713 |
+
$sversion = $this->read_ushort();
|
714 |
+
$slength = $this->read_ushort();
|
715 |
+
$scoverage = $this->read_ushort();
|
716 |
+
$format = $scoverage >> 8;
|
717 |
+
if ($kern_offset && $version==0 && $format==0) {
|
718 |
+
// Format 0
|
719 |
+
$nPairs = $this->read_ushort();
|
720 |
+
$this->skip(6);
|
721 |
+
for ($i=0; $i<$nPairs; $i++) {
|
722 |
+
$left = $this->read_ushort();
|
723 |
+
$right = $this->read_ushort();
|
724 |
+
$val = $this->read_short();
|
725 |
+
if (count($glyphToChar[$left])==1 && count($glyphToChar[$right])==1) {
|
726 |
+
if ($left != 32 && $right != 32) {
|
727 |
+
$this->kerninfo[$glyphToChar[$left][0]][$glyphToChar[$right][0]] = intval($val*$scale);
|
728 |
+
}
|
729 |
+
}
|
730 |
+
}
|
731 |
+
}
|
732 |
+
}
|
733 |
+
}
|
734 |
+
|
735 |
+
|
736 |
+
/////////////////////////////////////////////////////////////////////////////////////////
|
737 |
+
function _getGDEFtables() {
|
738 |
+
///////////////////////////////////
|
739 |
+
// GDEF - Glyph Definition
|
740 |
+
///////////////////////////////////
|
741 |
+
// http://www.microsoft.com/typography/otspec/gdef.htm
|
742 |
+
if (isset($this->tables["GDEF"])) {
|
743 |
+
if ($this->mode == 'summary') { $this->mpdf->WriteHTML('<h1>GDEF table</h1>'); }
|
744 |
+
$gdef_offset = $this->seek_table("GDEF");
|
745 |
+
// ULONG Version of the GDEF table-currently 0x00010000
|
746 |
+
$ver_maj = $this->read_ushort();
|
747 |
+
$ver_min = $this->read_ushort();
|
748 |
+
// Version 0x00010002 of GDEF header contains additional Offset to a list defining mark glyph set definitions (MarkGlyphSetDef)
|
749 |
+
$GlyphClassDef_offset = $this->read_ushort();
|
750 |
+
$AttachList_offset = $this->read_ushort();
|
751 |
+
$LigCaretList_offset = $this->read_ushort();
|
752 |
+
$MarkAttachClassDef_offset = $this->read_ushort();
|
753 |
+
if ($ver_min == 2) {
|
754 |
+
$MarkGlyphSetsDef_offset = $this->read_ushort();
|
755 |
+
}
|
756 |
+
|
757 |
+
// GlyphClassDef
|
758 |
+
$this->seek($gdef_offset+$GlyphClassDef_offset );
|
759 |
+
/*
|
760 |
+
1 Base glyph (single character, spacing glyph)
|
761 |
+
2 Ligature glyph (multiple character, spacing glyph)
|
762 |
+
3 Mark glyph (non-spacing combining glyph)
|
763 |
+
4 Component glyph (part of single character, spacing glyph)
|
764 |
+
*/
|
765 |
+
$GlyphByClass = $this->_getClassDefinitionTable();
|
766 |
+
|
767 |
+
if ($this->mode == 'summary') {
|
768 |
+
$this->mpdf->WriteHTML('<h2>Glyph classes</h2>');
|
769 |
+
}
|
770 |
+
|
771 |
+
if (isset($GlyphByClass[1]) && count($GlyphByClass[1])>0) {
|
772 |
+
$this->GlyphClassBases = $this->formatClassArr($GlyphByClass[1]);
|
773 |
+
if ($this->mode == 'summary') {
|
774 |
+
$this->mpdf->WriteHTML('<h3>Glyph class 1</h3>');
|
775 |
+
$this->mpdf->WriteHTML('<h5>Base glyph (single character, spacing glyph)</h5>');
|
776 |
+
$html = '';
|
777 |
+
$html .= '<div class="glyphs">';
|
778 |
+
foreach ($GlyphByClass[1] AS $g) {
|
779 |
+
$html .= '&#x'.$g.'; ';
|
780 |
+
}
|
781 |
+
$html .= '</div>';
|
782 |
+
$this->mpdf->WriteHTML($html);
|
783 |
+
}
|
784 |
+
}
|
785 |
+
else { $this->GlyphClassBases = ''; }
|
786 |
+
if (isset($GlyphByClass[2]) && count($GlyphByClass[2])>0) {
|
787 |
+
$this->GlyphClassLigatures = $this->formatClassArr($GlyphByClass[2]);
|
788 |
+
if ($this->mode == 'summary') {
|
789 |
+
$this->mpdf->WriteHTML('<h3>Glyph class 2</h3>');
|
790 |
+
$this->mpdf->WriteHTML('<h5>Ligature glyph (multiple character, spacing glyph)</h5>');
|
791 |
+
$html = '';
|
792 |
+
$html .= '<div class="glyphs">';
|
793 |
+
foreach ($GlyphByClass[2] AS $g) {
|
794 |
+
$html .= '&#x'.$g.'; ';
|
795 |
+
}
|
796 |
+
$html .= '</div>';
|
797 |
+
$this->mpdf->WriteHTML($html);
|
798 |
+
}
|
799 |
+
}
|
800 |
+
else { $this->GlyphClassLigatures = ''; }
|
801 |
+
if (isset($GlyphByClass[3]) && count($GlyphByClass[3])>0) {
|
802 |
+
$this->GlyphClassMarks = $this->formatClassArr($GlyphByClass[3]);
|
803 |
+
if ($this->mode == 'summary') {
|
804 |
+
$this->mpdf->WriteHTML('<h3>Glyph class 3</h3>');
|
805 |
+
$this->mpdf->WriteHTML('<h5>Mark glyph (non-spacing combining glyph)</h5>');
|
806 |
+
$html = '';
|
807 |
+
$html .= '<div class="glyphs">';
|
808 |
+
foreach ($GlyphByClass[3] AS $g) {
|
809 |
+
$html .= '◌&#x'.$g.'; ';
|
810 |
+
}
|
811 |
+
$html .= '</div>';
|
812 |
+
$this->mpdf->WriteHTML($html);
|
813 |
+
}
|
814 |
+
}
|
815 |
+
else { $this->GlyphClassMarks = ''; }
|
816 |
+
if (isset($GlyphByClass[4]) && count($GlyphByClass[4])>0) {
|
817 |
+
$this->GlyphClassComponents = $this->formatClassArr($GlyphByClass[4]);
|
818 |
+
if ($this->mode == 'summary') {
|
819 |
+
$this->mpdf->WriteHTML('<h3>Glyph class 4</h3>');
|
820 |
+
$this->mpdf->WriteHTML('<h5>Component glyph (part of single character, spacing glyph)</h5>');
|
821 |
+
$html = '';
|
822 |
+
$html .= '<div class="glyphs">';
|
823 |
+
foreach ($GlyphByClass[4] AS $g) {
|
824 |
+
$html .= '&#x'.$g.'; ';
|
825 |
+
}
|
826 |
+
$html .= '</div>';
|
827 |
+
$this->mpdf->WriteHTML($html);
|
828 |
+
}
|
829 |
+
}
|
830 |
+
else { $this->GlyphClassComponents = ''; }
|
831 |
+
|
832 |
+
$Marks = $GlyphByClass[3]; // to use for MarkAttachmentType
|
833 |
+
|
834 |
+
|
835 |
+
/* Required for GPOS
|
836 |
+
// Attachment List
|
837 |
+
if ($AttachList_offset) {
|
838 |
+
$this->seek($gdef_offset+$AttachList_offset );
|
839 |
+
}
|
840 |
+
The Attachment Point List table (AttachmentList) identifies all the attachment points defined in the GPOS table and their associated glyphs so a client can quickly access coordinates for each glyph's attachment points. As a result, the client can cache coordinates for attachment points along with glyph bitmaps and avoid recalculating the attachment points each time it displays a glyph. Without this table, processing speed would be slower because the client would have to decode the GPOS lookups that define attachment points and compile the points in a list.
|
841 |
+
|
842 |
+
The Attachment List table (AttachList) may be used to cache attachment point coordinates along with glyph bitmaps.
|
843 |
+
|
844 |
+
The table consists of an offset to a Coverage table (Coverage) listing all glyphs that define attachment points in the GPOS table, a count of the glyphs with attachment points (GlyphCount), and an array of offsets to AttachPoint tables (AttachPoint). The array lists the AttachPoint tables, one for each glyph in the Coverage table, in the same order as the Coverage Index.
|
845 |
+
AttachList table
|
846 |
+
Type Name Description
|
847 |
+
Offset Coverage Offset to Coverage table - from beginning of AttachList table
|
848 |
+
uint16 GlyphCount Number of glyphs with attachment points
|
849 |
+
Offset AttachPoint[GlyphCount] Array of offsets to AttachPoint tables-from beginning of AttachList table-in Coverage Index order
|
850 |
+
|
851 |
+
An AttachPoint table consists of a count of the attachment points on a single glyph (PointCount) and an array of contour indices of those points (PointIndex), listed in increasing numerical order.
|
852 |
+
|
853 |
+
AttachPoint table
|
854 |
+
Type Name Description
|
855 |
+
uint16 PointCount Number of attachment points on this glyph
|
856 |
+
uint16 PointIndex[PointCount] Array of contour point indices -in increasing numerical order
|
857 |
+
|
858 |
+
See Example 3 - http://www.microsoft.com/typography/otspec/gdef.htm
|
859 |
+
*/
|
860 |
+
|
861 |
+
|
862 |
+
// Ligature Caret List
|
863 |
+
// The Ligature Caret List table (LigCaretList) defines caret positions for all the ligatures in a font.
|
864 |
+
// Not required for mDPF
|
865 |
+
|
866 |
+
|
867 |
+
// MarkAttachmentType
|
868 |
+
if ($MarkAttachClassDef_offset) {
|
869 |
+
if ($this->mode == 'summary') { $this->mpdf->WriteHTML('<h1>Mark Attachment Types</h1>'); }
|
870 |
+
$this->seek($gdef_offset+$MarkAttachClassDef_offset );
|
871 |
+
$MarkAttachmentTypes = $this->_getClassDefinitionTable();
|
872 |
+
foreach($MarkAttachmentTypes AS $class=>$glyphs) {
|
873 |
+
|
874 |
+
if (is_array($Marks) && count($Marks)) {
|
875 |
+
$mat = array_diff($Marks, $MarkAttachmentTypes[$class]);
|
876 |
+
sort($mat, SORT_STRING);
|
877 |
+
}
|
878 |
+
else { $mat = array(); }
|
879 |
+
|
880 |
+
$this->MarkAttachmentType[$class] = $this->formatClassArr($mat);
|
881 |
+
|
882 |
+
if ($this->mode == 'summary') {
|
883 |
+
$this->mpdf->WriteHTML('<h3>Mark Attachment Type: '.$class.'</h3>');
|
884 |
+
$html = '';
|
885 |
+
$html .= '<div class="glyphs">';
|
886 |
+
foreach ($glyphs AS $g) {
|
887 |
+
$html .= '◌&#x'.$g.'; ';
|
888 |
+
}
|
889 |
+
$html .= '</div>';
|
890 |
+
$this->mpdf->WriteHTML($html);
|
891 |
+
}
|
892 |
+
}
|
893 |
+
}
|
894 |
+
else { $this->MarkAttachmentType = array(); }
|
895 |
+
|
896 |
+
|
897 |
+
// MarkGlyphSets only in Version 0x00010002 of GDEF
|
898 |
+
if ($ver_min == 2 && $MarkGlyphSetsDef_offset) {
|
899 |
+
if ($this->mode == 'summary') { $this->mpdf->WriteHTML('<h1>Mark Glyph Sets</h1>'); }
|
900 |
+
$this->seek($gdef_offset+$MarkGlyphSetsDef_offset);
|
901 |
+
$MarkSetTableFormat = $this->read_ushort();
|
902 |
+
$MarkSetCount = $this->read_ushort();
|
903 |
+
$MarkSetOffset = array();
|
904 |
+
for ($i=0;$i<$MarkSetCount;$i++) {
|
905 |
+
$MarkSetOffset[] = $this->read_ulong();
|
906 |
+
}
|
907 |
+
for ($i=0;$i<$MarkSetCount;$i++) {
|
908 |
+
$this->seek($MarkSetOffset[$i]);
|
909 |
+
$glyphs = $this->_getCoverage();
|
910 |
+
$this->MarkGlyphSets[$i] = $this->formatClassArr($glyphs);
|
911 |
+
if ($this->mode == 'summary') {
|
912 |
+
$this->mpdf->WriteHTML('<h3>Mark Glyph Set class: '.$i.'</h3>');
|
913 |
+
$html = '';
|
914 |
+
$html .= '<div class="glyphs">';
|
915 |
+
foreach ($glyphs AS $g) {
|
916 |
+
$html .= '◌&#x'.$g.'; ';
|
917 |
+
}
|
918 |
+
$html .= '</div>';
|
919 |
+
$this->mpdf->WriteHTML($html);
|
920 |
+
}
|
921 |
+
}
|
922 |
+
}
|
923 |
+
else { $this->MarkGlyphSets = array(); }
|
924 |
+
}
|
925 |
+
else { $this->mpdf->WriteHTML('<div>GDEF table not defined</div>'); }
|
926 |
+
|
927 |
+
|
928 |
+
//echo $this->GlyphClassMarks ; exit;
|
929 |
+
//print_r($GlyphClass); exit;
|
930 |
+
//print_r($GlyphByClass); exit;
|
931 |
+
}
|
932 |
+
|
933 |
+
function _getClassDefinitionTable($offset=0) {
|
934 |
+
|
935 |
+
if ($offset>0) {
|
936 |
+
$this->seek($offset);
|
937 |
+
}
|
938 |
+
|
939 |
+
// NB Any glyph not included in the range of covered GlyphIDs automatically belongs to Class 0. This is not returned by this function
|
940 |
+
$ClassFormat = $this->read_ushort();
|
941 |
+
$GlyphByClass = array();
|
942 |
+
if ($ClassFormat == 1) {
|
943 |
+
$StartGlyph = $this->read_ushort();
|
944 |
+
$GlyphCount = $this->read_ushort();
|
945 |
+
for ($i=0;$i<$GlyphCount;$i++) {
|
946 |
+
$gid = $StartGlyph + $i;
|
947 |
+
$class = $this->read_ushort();
|
948 |
+
$GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$gid][0]);
|
949 |
+
}
|
950 |
+
}
|
951 |
+
else if ($ClassFormat == 2) {
|
952 |
+
$tableCount = $this->read_ushort();
|
953 |
+
for ($i=0;$i<$tableCount;$i++) {
|
954 |
+
$startGlyphID = $this->read_ushort();
|
955 |
+
$endGlyphID = $this->read_ushort();
|
956 |
+
$class = $this->read_ushort();
|
957 |
+
for($gid=$startGlyphID;$gid<=$endGlyphID;$gid++) {
|
958 |
+
$GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$gid][0]);
|
959 |
+
}
|
960 |
+
}
|
961 |
+
}
|
962 |
+
ksort($GlyphByClass);
|
963 |
+
return $GlyphByClass;
|
964 |
+
}
|
965 |
+
|
966 |
+
function _getGSUBtables() {
|
967 |
+
///////////////////////////////////
|
968 |
+
// GSUB - Glyph Substitution
|
969 |
+
///////////////////////////////////
|
970 |
+
if (isset($this->tables["GSUB"])) {
|
971 |
+
$this->mpdf->WriteHTML('<h1>GSUB Tables</h1>');
|
972 |
+
$ffeats = array();
|
973 |
+
$gsub_offset = $this->seek_table("GSUB");
|
974 |
+
$this->skip(4);
|
975 |
+
$ScriptList_offset = $gsub_offset + $this->read_ushort();
|
976 |
+
$FeatureList_offset = $gsub_offset + $this->read_ushort();
|
977 |
+
$LookupList_offset = $gsub_offset + $this->read_ushort();
|
978 |
+
|
979 |
+
// ScriptList
|
980 |
+
$this->seek($ScriptList_offset );
|
981 |
+
$ScriptCount = $this->read_ushort();
|
982 |
+
for ($i=0;$i<$ScriptCount;$i++) {
|
983 |
+
$ScriptTag = $this->read_tag(); // = "beng", "deva" etc.
|
984 |
+
$ScriptTableOffset = $this->read_ushort();
|
985 |
+
$ffeats[$ScriptTag] = $ScriptList_offset + $ScriptTableOffset;
|
986 |
+
}
|
987 |
+
|
988 |
+
// Script Table
|
989 |
+
foreach($ffeats AS $t=>$o) {
|
990 |
+
$ls = array();
|
991 |
+
$this->seek($o);
|
992 |
+
$DefLangSys_offset = $this->read_ushort();
|
993 |
+
if ($DefLangSys_offset > 0) {
|
994 |
+
$ls['DFLT'] = $DefLangSys_offset + $o;
|
995 |
+
}
|
996 |
+
$LangSysCount = $this->read_ushort();
|
997 |
+
for ($i=0;$i<$LangSysCount;$i++) {
|
998 |
+
$LangTag = $this->read_tag(); // =
|
999 |
+
$LangTableOffset = $this->read_ushort();
|
1000 |
+
$ls[$LangTag] = $o + $LangTableOffset;
|
1001 |
+
}
|
1002 |
+
$ffeats[$t] = $ls;
|
1003 |
+
}
|
1004 |
+
//print_r($ffeats); exit;
|
1005 |
+
|
1006 |
+
|
1007 |
+
// Get FeatureIndexList
|
1008 |
+
// LangSys Table - from first listed langsys
|
1009 |
+
foreach($ffeats AS $st=>$scripts) {
|
1010 |
+
foreach($scripts AS $t=>$o) {
|
1011 |
+
$FeatureIndex = array();
|
1012 |
+
$langsystable_offset = $o;
|
1013 |
+
$this->seek($langsystable_offset);
|
1014 |
+
$LookUpOrder = $this->read_ushort(); //==NULL
|
1015 |
+
$ReqFeatureIndex = $this->read_ushort();
|
1016 |
+
if ($ReqFeatureIndex != 0xFFFF) { $FeatureIndex[] = $ReqFeatureIndex; }
|
1017 |
+
$FeatureCount = $this->read_ushort();
|
1018 |
+
for ($i=0;$i<$FeatureCount;$i++) {
|
1019 |
+
$FeatureIndex[] = $this->read_ushort(); // = index of feature
|
1020 |
+
}
|
1021 |
+
$ffeats[$st][$t] = $FeatureIndex;
|
1022 |
+
}
|
1023 |
+
}
|
1024 |
+
//print_r($ffeats); exit;
|
1025 |
+
|
1026 |
+
|
1027 |
+
// Feauture List => LookupListIndex es
|
1028 |
+
$this->seek($FeatureList_offset );
|
1029 |
+
$FeatureCount = $this->read_ushort();
|
1030 |
+
$Feature = array();
|
1031 |
+
for ($i=0;$i<$FeatureCount;$i++) {
|
1032 |
+
$Feature[$i] = array('tag' => $this->read_tag() );
|
1033 |
+
$Feature[$i]['offset'] = $FeatureList_offset + $this->read_ushort();
|
1034 |
+
}
|
1035 |
+
for ($i=0;$i<$FeatureCount;$i++) {
|
1036 |
+
$this->seek($Feature[$i]['offset']);
|
1037 |
+
$this->read_ushort(); // null
|
1038 |
+
$Feature[$i]['LookupCount'] = $Lookupcount = $this->read_ushort();
|
1039 |
+
$Feature[$i]['LookupListIndex'] = array();
|
1040 |
+
for ($c=0;$c<$Lookupcount;$c++) {
|
1041 |
+
$Feature[$i]['LookupListIndex'][] = $this->read_ushort();
|
1042 |
+
}
|
1043 |
+
}
|
1044 |
+
|
1045 |
+
|
1046 |
+
foreach($ffeats AS $st=>$scripts) {
|
1047 |
+
foreach($scripts AS $t=>$o) {
|
1048 |
+
$FeatureIndex = $ffeats[$st][$t];
|
1049 |
+
foreach($FeatureIndex AS $k=>$fi) {
|
1050 |
+
$ffeats[$st][$t][$k] = $Feature[$fi];
|
1051 |
+
}
|
1052 |
+
}
|
1053 |
+
}
|
1054 |
+
//=====================================================================================
|
1055 |
+
$gsub = array();
|
1056 |
+
$GSUBScriptLang = array();
|
1057 |
+
foreach($ffeats AS $st=>$scripts) {
|
1058 |
+
foreach($scripts AS $t=>$langsys) {
|
1059 |
+
$lg = array();
|
1060 |
+
foreach($langsys AS $ft) {
|
1061 |
+
$lg[$ft['LookupListIndex'][0]] = $ft;
|
1062 |
+
}
|
1063 |
+
// list of Lookups in order they need to be run i.e. order listed in Lookup table
|
1064 |
+
ksort($lg);
|
1065 |
+
foreach($lg AS $ft) {
|
1066 |
+
$gsub[$st][$t][$ft['tag']] = $ft['LookupListIndex'];
|
1067 |
+
}
|
1068 |
+
if (!isset($GSUBScriptLang[$st])) { $GSUBScriptLang[$st] = ''; }
|
1069 |
+
$GSUBScriptLang[$st] .= $t.' ';
|
1070 |
+
}
|
1071 |
+
}
|
1072 |
+
|
1073 |
+
//print_r($gsub); exit;
|
1074 |
+
|
1075 |
+
if ($this->mode == 'summary') {
|
1076 |
+
$this->mpdf->WriteHTML('<h3>GSUB Scripts & Languages</h3>');
|
1077 |
+
$this->mpdf->WriteHTML('<div class="glyphs">');
|
1078 |
+
$html = '';
|
1079 |
+
if (count($gsub)) {
|
1080 |
+
foreach ($gsub AS $st=>$g) {
|
1081 |
+
$html .= '<h5>'.$st.'</h5>';
|
1082 |
+
foreach ($g AS $l=>$t) {
|
1083 |
+
$html .= '<div><a href="font_dump_OTL.php?script='.$st.'&lang='.$l.'">'.$l.'</a></b>: ';
|
1084 |
+
foreach ($t AS $tag=>$o) {
|
1085 |
+
$html .= $tag.' ';
|
1086 |
+
}
|
1087 |
+
$html .= '</div>';
|
1088 |
+
}
|
1089 |
+
}
|
1090 |
+
}
|
1091 |
+
else {
|
1092 |
+
$html .= '<div>No entries in GSUB table.</div>';
|
1093 |
+
}
|
1094 |
+
$this->mpdf->WriteHTML($html);
|
1095 |
+
$this->mpdf->WriteHTML('</div>');
|
1096 |
+
return 0;
|
1097 |
+
}
|
1098 |
+
|
1099 |
+
|
1100 |
+
|
1101 |
+
//=====================================================================================
|
1102 |
+
// Get metadata and offsets for whole Lookup List table
|
1103 |
+
$this->seek($LookupList_offset );
|
1104 |
+
$LookupCount = $this->read_ushort();
|
1105 |
+
$GSLookup = array();
|
1106 |
+
$Offsets = array();
|
1107 |
+
$SubtableCount = array();
|
1108 |
+
for ($i=0;$i<$LookupCount;$i++) {
|
1109 |
+
$Offsets[$i] = $LookupList_offset + $this->read_ushort();
|
1110 |
+
}
|
1111 |
+
for ($i=0;$i<$LookupCount;$i++) {
|
1112 |
+
$this->seek($Offsets[$i]);
|
1113 |
+
$GSLookup[$i]['Type'] = $this->read_ushort();
|
1114 |
+
$GSLookup[$i]['Flag'] = $flag = $this->read_ushort();
|
1115 |
+
$GSLookup[$i]['SubtableCount'] = $SubtableCount[$i] = $this->read_ushort();
|
1116 |
+
for ($c=0;$c<$SubtableCount[$i] ;$c++) {
|
1117 |
+
$GSLookup[$i]['Subtables'][$c] = $Offsets[$i] + $this->read_ushort();
|
1118 |
+
|
1119 |
+
}
|
1120 |
+
// MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure
|
1121 |
+
if (($flag & 0x0010) == 0x0010) {
|
1122 |
+
$GSLookup[$i]['MarkFilteringSet'] = $this->read_ushort();
|
1123 |
+
}
|
1124 |
+
// else { $GSLookup[$i]['MarkFilteringSet'] = ''; }
|
1125 |
+
|
1126 |
+
// Lookup Type 7: Extension
|
1127 |
+
if ($GSLookup[$i]['Type'] == 7) {
|
1128 |
+
// Overwrites new offset (32-bit) for each subtable, and a new lookup Type
|
1129 |
+
for ($c=0;$c<$SubtableCount[$i] ;$c++) {
|
1130 |
+
$this->seek($GSLookup[$i]['Subtables'][$c]);
|
1131 |
+
$ExtensionPosFormat = $this->read_ushort();
|
1132 |
+
$type = $this->read_ushort();
|
1133 |
+
$GSLookup[$i]['Subtables'][$c] = $GSLookup[$i]['Subtables'][$c] + $this->read_ulong();
|
1134 |
+
}
|
1135 |
+
$GSLookup[$i]['Type'] = $type;
|
1136 |
+
}
|
1137 |
+
|
1138 |
+
}
|
1139 |
+
|
1140 |
+
//print_r($GSLookup); exit;
|
1141 |
+
//=====================================================================================
|
1142 |
+
// Process Whole LookupList - Get LuCoverage = Lookup coverage just for first glyph
|
1143 |
+
$this->GSLuCoverage = array();
|
1144 |
+
for ($i=0;$i<$LookupCount;$i++) {
|
1145 |
+
for ($c=0;$c<$GSLookup[$i]['SubtableCount'] ;$c++) {
|
1146 |
+
|
1147 |
+
$this->seek($GSLookup[$i]['Subtables'][$c]);
|
1148 |
+
$PosFormat= $this->read_ushort();
|
1149 |
+
|
1150 |
+
if ($GSLookup[$i]['Type']==5 && $PosFormat==3) { $this->skip(4); }
|
1151 |
+
else if ($GSLookup[$i]['Type']==6 && $PosFormat==3) {
|
1152 |
+
$BacktrackGlyphCount= $this->read_ushort();
|
1153 |
+
$this->skip(2*$BacktrackGlyphCount + 2);
|
1154 |
+
}
|
1155 |
+
// NB Coverage only looks at glyphs for position 1 (i.e. 5.3 and 6.3) // NEEDS TO READ ALL ********************
|
1156 |
+
$Coverage = $GSLookup[$i]['Subtables'][$c] + $this->read_ushort();
|
1157 |
+
$this->seek($Coverage);
|
1158 |
+
$glyphs = $this->_getCoverage();
|
1159 |
+
$this->GSLuCoverage[$i][$c] = implode('|',$glyphs);
|
1160 |
+
}
|
1161 |
+
}
|
1162 |
+
|
1163 |
+
// $this->GSLuCoverage and $GSLookup
|
1164 |
+
|
1165 |
+
//=====================================================================================
|
1166 |
+
$s = '<?php
|
1167 |
+
$GSLuCoverage = '.var_export($this->GSLuCoverage , true).';
|
1168 |
+
?>';
|
1169 |
+
|
1170 |
+
|
1171 |
+
//=====================================================================================
|
1172 |
+
$s = '<?php
|
1173 |
+
$GlyphClassBases = \''.$this->GlyphClassBases.'\';
|
1174 |
+
$GlyphClassMarks = \''.$this->GlyphClassMarks.'\';
|
1175 |
+
$GlyphClassLigatures = \''.$this->GlyphClassLigatures.'\';
|
1176 |
+
$GlyphClassComponents = \''.$this->GlyphClassComponents.'\';
|
1177 |
+
$MarkGlyphSets = '.var_export($this->MarkGlyphSets , true).';
|
1178 |
+
$MarkAttachmentType = '.var_export($this->MarkAttachmentType , true).';
|
1179 |
+
?>';
|
1180 |
+
|
1181 |
+
|
1182 |
+
//=====================================================================================
|
1183 |
+
//=====================================================================================
|
1184 |
+
//=====================================================================================
|
1185 |
+
// Now repeats as original to get Substitution rules
|
1186 |
+
//=====================================================================================
|
1187 |
+
//=====================================================================================
|
1188 |
+
//=====================================================================================
|
1189 |
+
// Get metadata and offsets for whole Lookup List table
|
1190 |
+
$this->seek($LookupList_offset );
|
1191 |
+
$LookupCount = $this->read_ushort();
|
1192 |
+
$Lookup = array();
|
1193 |
+
for ($i=0;$i<$LookupCount;$i++) {
|
1194 |
+
$Lookup[$i]['offset'] = $LookupList_offset + $this->read_ushort();
|
1195 |
+
}
|
1196 |
+
for ($i=0;$i<$LookupCount;$i++) {
|
1197 |
+
$this->seek($Lookup[$i]['offset']);
|
1198 |
+
$Lookup[$i]['Type'] = $this->read_ushort();
|
1199 |
+
$Lookup[$i]['Flag'] = $flag = $this->read_ushort();
|
1200 |
+
$Lookup[$i]['SubtableCount'] = $this->read_ushort();
|
1201 |
+
for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
|
1202 |
+
$Lookup[$i]['Subtable'][$c]['Offset'] = $Lookup[$i]['offset'] + $this->read_ushort();
|
1203 |
+
|
1204 |
+
}
|
1205 |
+
// MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure
|
1206 |
+
if (($flag & 0x0010) == 0x0010) {
|
1207 |
+
$Lookup[$i]['MarkFilteringSet'] = $this->read_ushort();
|
1208 |
+
}
|
1209 |
+
else { $Lookup[$i]['MarkFilteringSet'] = ''; }
|
1210 |
+
|
1211 |
+
// Lookup Type 7: Extension
|
1212 |
+
if ($Lookup[$i]['Type'] == 7) {
|
1213 |
+
// Overwrites new offset (32-bit) for each subtable, and a new lookup Type
|
1214 |
+
for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
|
1215 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['Offset']);
|
1216 |
+
$ExtensionPosFormat = $this->read_ushort();
|
1217 |
+
$type = $this->read_ushort();
|
1218 |
+
$Lookup[$i]['Subtable'][$c]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ulong();
|
1219 |
+
}
|
1220 |
+
$Lookup[$i]['Type'] = $type;
|
1221 |
+
}
|
1222 |
+
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
//print_r($Lookup); exit;
|
1226 |
+
//=====================================================================================
|
1227 |
+
// Process (1) Whole LookupList
|
1228 |
+
for ($i=0;$i<$LookupCount;$i++) {
|
1229 |
+
for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
|
1230 |
+
|
1231 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['Offset']);
|
1232 |
+
$SubstFormat= $this->read_ushort();
|
1233 |
+
$Lookup[$i]['Subtable'][$c]['Format'] = $SubstFormat;
|
1234 |
+
|
1235 |
+
/*
|
1236 |
+
Lookup['Type'] Enumeration table for glyph substitution
|
1237 |
+
Value Type Description
|
1238 |
+
1 Single Replace one glyph with one glyph
|
1239 |
+
2 Multiple Replace one glyph with more than one glyph
|
1240 |
+
3 Alternate Replace one glyph with one of many glyphs
|
1241 |
+
4 Ligature Replace multiple glyphs with one glyph
|
1242 |
+
5 Context Replace one or more glyphs in context
|
1243 |
+
6 Chaining Context Replace one or more glyphs in chained context
|
1244 |
+
7 Extension Substitution Extension mechanism for other substitutions (i.e. this excludes the Extension type substitution itself)
|
1245 |
+
8 Reverse chaining context single Applied in reverse order, replace single glyph in chaining context
|
1246 |
+
*/
|
1247 |
+
|
1248 |
+
// LookupType 1: Single Substitution Subtable
|
1249 |
+
if ($Lookup[$i]['Type'] == 1) {
|
1250 |
+
$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1251 |
+
if ($SubstFormat==1) { // Calculated output glyph indices
|
1252 |
+
$Lookup[$i]['Subtable'][$c]['DeltaGlyphID'] = $this->read_short();
|
1253 |
+
}
|
1254 |
+
else if ($SubstFormat==2) { // Specified output glyph indices
|
1255 |
+
$GlyphCount = $this->read_ushort();
|
1256 |
+
for ($g=0;$g<$GlyphCount;$g++) {
|
1257 |
+
$Lookup[$i]['Subtable'][$c]['Glyphs'][] = $this->read_ushort();
|
1258 |
+
}
|
1259 |
+
}
|
1260 |
+
}
|
1261 |
+
// LookupType 2: Multiple Substitution Subtable
|
1262 |
+
else if ($Lookup[$i]['Type'] == 2) {
|
1263 |
+
$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1264 |
+
$Lookup[$i]['Subtable'][$c]['SequenceCount'] = $SequenceCount = $this->read_short();
|
1265 |
+
for($s=0;$s<$SequenceCount;$s++) {
|
1266 |
+
$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
|
1267 |
+
}
|
1268 |
+
for($s=0;$s<$SequenceCount;$s++) {
|
1269 |
+
// Sequence Tables
|
1270 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['Sequences'][$s]['Offset']);
|
1271 |
+
$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['GlyphCount'] = $this->read_short();
|
1272 |
+
for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['GlyphCount'];$g++) {
|
1273 |
+
$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['SubstituteGlyphID'][] = $this->read_ushort();
|
1274 |
+
}
|
1275 |
+
}
|
1276 |
+
}
|
1277 |
+
// LookupType 3: Alternate Forms
|
1278 |
+
else if ($Lookup[$i]['Type'] == 3) {
|
1279 |
+
$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1280 |
+
$Lookup[$i]['Subtable'][$c]['AlternateSetCount'] = $AlternateSetCount = $this->read_short();
|
1281 |
+
for($s=0;$s<$AlternateSetCount;$s++) {
|
1282 |
+
$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
|
1283 |
+
}
|
1284 |
+
|
1285 |
+
for($s=0;$s<$AlternateSetCount;$s++) {
|
1286 |
+
// AlternateSet Tables
|
1287 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['Offset']);
|
1288 |
+
$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['GlyphCount'] = $this->read_short();
|
1289 |
+
for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['GlyphCount'];$g++) {
|
1290 |
+
$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['SubstituteGlyphID'][] = $this->read_ushort();
|
1291 |
+
}
|
1292 |
+
}
|
1293 |
+
}
|
1294 |
+
// LookupType 4: Ligature Substitution Subtable
|
1295 |
+
else if ($Lookup[$i]['Type'] == 4) {
|
1296 |
+
$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1297 |
+
$Lookup[$i]['Subtable'][$c]['LigSetCount'] = $LigSetCount = $this->read_short();
|
1298 |
+
for($s=0;$s<$LigSetCount;$s++) {
|
1299 |
+
$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
|
1300 |
+
}
|
1301 |
+
for($s=0;$s<$LigSetCount;$s++) {
|
1302 |
+
// LigatureSet Tables
|
1303 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset']);
|
1304 |
+
$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'] = $this->read_short();
|
1305 |
+
for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) {
|
1306 |
+
$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigatureOffset'][$g] = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset'] + $this->read_ushort();
|
1307 |
+
}
|
1308 |
+
}
|
1309 |
+
for($s=0;$s<$LigSetCount;$s++) {
|
1310 |
+
for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) {
|
1311 |
+
// Ligature tables
|
1312 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigatureOffset'][$g]);
|
1313 |
+
$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['LigGlyph'] = $this->read_ushort();
|
1314 |
+
$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'] = $this->read_ushort();
|
1315 |
+
for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'];$l++) {
|
1316 |
+
$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['GlyphID'][$l] = $this->read_ushort();
|
1317 |
+
}
|
1318 |
+
}
|
1319 |
+
}
|
1320 |
+
}
|
1321 |
+
|
1322 |
+
// LookupType 5: Contextual Substitution Subtable
|
1323 |
+
else if ($Lookup[$i]['Type'] == 5) {
|
1324 |
+
// Format 1: Context Substitution
|
1325 |
+
if ($SubstFormat==1) {
|
1326 |
+
$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1327 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'] = $SubRuleSetCount = $this->read_short();
|
1328 |
+
for($s=0;$s<$SubRuleSetCount;$s++) {
|
1329 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
|
1330 |
+
}
|
1331 |
+
for($s=0;$s<$SubRuleSetCount;$s++) {
|
1332 |
+
// SubRuleSet Tables
|
1333 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset']);
|
1334 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'] = $this->read_short();
|
1335 |
+
for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$g++) {
|
1336 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleOffset'][$g] = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset'] + $this->read_ushort();
|
1337 |
+
}
|
1338 |
+
}
|
1339 |
+
for($s=0;$s<$SubRuleSetCount;$s++) {
|
1340 |
+
// SubRule Tables
|
1341 |
+
for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$g++) {
|
1342 |
+
// Ligature tables
|
1343 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleOffset'][$g]);
|
1344 |
+
|
1345 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['GlyphCount'] = $this->read_ushort();
|
1346 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstCount'] = $this->read_ushort();
|
1347 |
+
// "Input"::[GlyphCount - 1]::Array of input GlyphIDs-start with second glyph
|
1348 |
+
for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['GlyphCount'];$l++) {
|
1349 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['Input'][$l] = $this->read_ushort();
|
1350 |
+
}
|
1351 |
+
// "SubstLookupRecord"::[SubstCount]::Array of SubstLookupRecords-in design order
|
1352 |
+
for ($l=0;$l<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstCount'];$l++) {
|
1353 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstLookupRecord'][$l]['SequenceIndex'] = $this->read_ushort();
|
1354 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstLookupRecord'][$l]['LookupListIndex'] = $this->read_ushort();
|
1355 |
+
}
|
1356 |
+
|
1357 |
+
}
|
1358 |
+
}
|
1359 |
+
|
1360 |
+
}
|
1361 |
+
// Format 2: Class-based Context Glyph Substitution
|
1362 |
+
else if ($SubstFormat==2) {
|
1363 |
+
$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1364 |
+
$Lookup[$i]['Subtable'][$c]['ClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1365 |
+
$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'] = $this->read_ushort();
|
1366 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$b++) {
|
1367 |
+
$offset = $this->read_ushort();
|
1368 |
+
if ($offset==0x0000) {
|
1369 |
+
$Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][] = 0;
|
1370 |
+
}
|
1371 |
+
else {
|
1372 |
+
$Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $offset;
|
1373 |
+
}
|
1374 |
+
}
|
1375 |
+
}
|
1376 |
+
else { die("GPOS Lookup Type ".$Lookup[$i]['Type'].", Format ".$SubstFormat." not supported (ttfontsuni.php)."); }
|
1377 |
+
}
|
1378 |
+
|
1379 |
+
// LookupType 6: Chaining Contextual Substitution Subtable
|
1380 |
+
else if ($Lookup[$i]['Type'] == 6) {
|
1381 |
+
// Format 1: Simple Chaining Context Glyph Substitution p255
|
1382 |
+
if ($SubstFormat==1) {
|
1383 |
+
$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1384 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'] = $this->read_ushort();
|
1385 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];$b++) {
|
1386 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1387 |
+
}
|
1388 |
+
}
|
1389 |
+
// Format 2: Class-based Chaining Context Glyph Substitution p257
|
1390 |
+
else if ($SubstFormat==2) {
|
1391 |
+
$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1392 |
+
$Lookup[$i]['Subtable'][$c]['BacktrackClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1393 |
+
$Lookup[$i]['Subtable'][$c]['InputClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1394 |
+
$Lookup[$i]['Subtable'][$c]['LookaheadClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1395 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'] = $this->read_ushort();
|
1396 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$b++) {
|
1397 |
+
$offset = $this->read_ushort();
|
1398 |
+
if ($offset==0x0000) {
|
1399 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][] = $offset;
|
1400 |
+
}
|
1401 |
+
else {
|
1402 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $offset;
|
1403 |
+
}
|
1404 |
+
}
|
1405 |
+
}
|
1406 |
+
// Format 3: Coverage-based Chaining Context Glyph Substitution p259
|
1407 |
+
else if ($SubstFormat==3) {
|
1408 |
+
$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'] = $this->read_ushort();
|
1409 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'];$b++) {
|
1410 |
+
$Lookup[$i]['Subtable'][$c]['CoverageBacktrack'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1411 |
+
}
|
1412 |
+
$Lookup[$i]['Subtable'][$c]['InputGlyphCount'] = $this->read_ushort();
|
1413 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) {
|
1414 |
+
$Lookup[$i]['Subtable'][$c]['CoverageInput'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1415 |
+
}
|
1416 |
+
$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'] = $this->read_ushort();
|
1417 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'];$b++) {
|
1418 |
+
$Lookup[$i]['Subtable'][$c]['CoverageLookahead'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
|
1419 |
+
}
|
1420 |
+
$Lookup[$i]['Subtable'][$c]['SubstCount'] = $this->read_ushort();
|
1421 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) {
|
1422 |
+
$Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'] = $this->read_ushort();
|
1423 |
+
$Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'] = $this->read_ushort();
|
1424 |
+
/*
|
1425 |
+
Substitution Lookup Record
|
1426 |
+
All contextual substitution subtables specify the substitution data in a Substitution Lookup Record (SubstLookupRecord). Each record contains a SequenceIndex, which indicates the position where the substitution will occur in the glyph sequence. In addition, a LookupListIndex identifies the lookup to be applied at the glyph position specified by the SequenceIndex.
|
1427 |
+
*/
|
1428 |
+
|
1429 |
+
}
|
1430 |
+
}
|
1431 |
+
}
|
1432 |
+
else { die("Lookup Type ".$Lookup[$i]['Type']." not supported."); }
|
1433 |
+
}
|
1434 |
+
}
|
1435 |
+
//print_r($Lookup); exit;
|
1436 |
+
|
1437 |
+
|
1438 |
+
|
1439 |
+
|
1440 |
+
//=====================================================================================
|
1441 |
+
// Process (2) Whole LookupList
|
1442 |
+
// Get Coverage tables and prepare preg_replace
|
1443 |
+
for ($i=0;$i<$LookupCount;$i++) {
|
1444 |
+
for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
|
1445 |
+
$SubstFormat= $Lookup[$i]['Subtable'][$c]['Format'] ;
|
1446 |
+
|
1447 |
+
// LookupType 1: Single Substitution Subtable 1 => 1
|
1448 |
+
if ($Lookup[$i]['Type'] == 1) {
|
1449 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
|
1450 |
+
$glyphs = $this->_getCoverage(false);
|
1451 |
+
for ($g=0;$g<count($glyphs);$g++) {
|
1452 |
+
$replace = array();
|
1453 |
+
$substitute = array();
|
1454 |
+
$replace[] = unicode_hex($this->glyphToChar[$glyphs[$g]][0]);
|
1455 |
+
// Flag = Ignore
|
1456 |
+
if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
|
1457 |
+
if (isset($Lookup[$i]['Subtable'][$c]['DeltaGlyphID'])) { // Format 1
|
1458 |
+
$substitute[] = unicode_hex($this->glyphToChar[($glyphs[$g]+$Lookup[$i]['Subtable'][$c]['DeltaGlyphID'])][0]);
|
1459 |
+
}
|
1460 |
+
else { // Format 2
|
1461 |
+
$substitute[] = unicode_hex($this->glyphToChar[($Lookup[$i]['Subtable'][$c]['Glyphs'][$g])][0]);
|
1462 |
+
}
|
1463 |
+
$Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute);
|
1464 |
+
}
|
1465 |
+
}
|
1466 |
+
|
1467 |
+
// LookupType 2: Multiple Substitution Subtable 1 => n
|
1468 |
+
else if ($Lookup[$i]['Type'] == 2) {
|
1469 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
|
1470 |
+
$glyphs = $this->_getCoverage();
|
1471 |
+
for ($g=0;$g<count($glyphs);$g++) {
|
1472 |
+
$replace = array();
|
1473 |
+
$substitute = array();
|
1474 |
+
$replace[] = $glyphs[$g];
|
1475 |
+
// Flag = Ignore
|
1476 |
+
if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
|
1477 |
+
if (!isset($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID']) || count($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID'])==0) { continue; } // Illegal for GlyphCount to be 0; either error in font, or something has gone wrong - lets carry on for now!
|
1478 |
+
foreach($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID'] AS $sub) {
|
1479 |
+
$substitute[] = unicode_hex($this->glyphToChar[$sub][0]);
|
1480 |
+
}
|
1481 |
+
$Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute);
|
1482 |
+
}
|
1483 |
+
}
|
1484 |
+
// LookupType 3: Alternate Forms 1 => 1 (only first alternate form is used)
|
1485 |
+
else if ($Lookup[$i]['Type'] == 3) {
|
1486 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
|
1487 |
+
$glyphs = $this->_getCoverage();
|
1488 |
+
for ($g=0;$g<count($glyphs);$g++) {
|
1489 |
+
$replace = array();
|
1490 |
+
$substitute = array();
|
1491 |
+
$replace[] = $glyphs[$g];
|
1492 |
+
// Flag = Ignore
|
1493 |
+
if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
|
1494 |
+
|
1495 |
+
for ($gl=0;$gl<$Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['GlyphCount'];$gl++) {
|
1496 |
+
$gid = $Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['SubstituteGlyphID'][$gl];
|
1497 |
+
$substitute[] = unicode_hex($this->glyphToChar[$gid][0]);
|
1498 |
+
}
|
1499 |
+
|
1500 |
+
//$gid = $Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['SubstituteGlyphID'][0];
|
1501 |
+
//$substitute[] = unicode_hex($this->glyphToChar[$gid][0]);
|
1502 |
+
|
1503 |
+
$Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute);
|
1504 |
+
}
|
1505 |
+
if ($i==166) {
|
1506 |
+
print_r($Lookup[$i]['Subtable']);
|
1507 |
+
exit;
|
1508 |
+
}
|
1509 |
+
}
|
1510 |
+
// LookupType 4: Ligature Substitution Subtable n => 1
|
1511 |
+
else if ($Lookup[$i]['Type'] == 4) {
|
1512 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
|
1513 |
+
$glyphs = $this->_getCoverage();
|
1514 |
+
$LigSetCount = $Lookup[$i]['Subtable'][$c]['LigSetCount'];
|
1515 |
+
for($s=0;$s<$LigSetCount;$s++) {
|
1516 |
+
for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) {
|
1517 |
+
$replace = array();
|
1518 |
+
$substitute = array();
|
1519 |
+
$replace[] = $glyphs[$s];
|
1520 |
+
// Flag = Ignore
|
1521 |
+
if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
|
1522 |
+
for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'];$l++) {
|
1523 |
+
$gid = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['GlyphID'][$l];
|
1524 |
+
$rpl = unicode_hex($this->glyphToChar[$gid][0]);
|
1525 |
+
// Flag = Ignore
|
1526 |
+
if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $rpl, $Lookup[$i]['MarkFilteringSet'])) { continue 2; }
|
1527 |
+
$replace[] = $rpl;
|
1528 |
+
}
|
1529 |
+
$gid = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['LigGlyph'];
|
1530 |
+
$substitute[] = unicode_hex($this->glyphToChar[$gid][0]);
|
1531 |
+
$Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute, 'CompCount' => $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount']);
|
1532 |
+
}
|
1533 |
+
}
|
1534 |
+
}
|
1535 |
+
|
1536 |
+
// LookupType 5: Contextual Substitution Subtable
|
1537 |
+
else if ($Lookup[$i]['Type'] == 5) {
|
1538 |
+
// Format 1: Context Substitution
|
1539 |
+
if ($SubstFormat==1) {
|
1540 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
|
1541 |
+
$Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
|
1542 |
+
|
1543 |
+
for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'];$s++) {
|
1544 |
+
$SubRuleSet = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s];
|
1545 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['FirstGlyph'] = $CoverageGlyphs[$s];
|
1546 |
+
for ($r=0;$r<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$r++) {
|
1547 |
+
$GlyphCount = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['GlyphCount'];
|
1548 |
+
for ($g=1;$g<$GlyphCount;$g++) {
|
1549 |
+
$glyphID = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['Input'][$g];
|
1550 |
+
$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['InputGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
|
1551 |
+
}
|
1552 |
+
|
1553 |
+
}
|
1554 |
+
}
|
1555 |
+
}
|
1556 |
+
// Format 2: Class-based Context Glyph Substitution
|
1557 |
+
else if ($SubstFormat==2) {
|
1558 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
|
1559 |
+
$Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
|
1560 |
+
|
1561 |
+
$InputClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['ClassDefOffset']);
|
1562 |
+
$Lookup[$i]['Subtable'][$c]['InputClasses'] = $InputClasses;
|
1563 |
+
|
1564 |
+
for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$s++) {
|
1565 |
+
if ($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]>0) {
|
1566 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]);
|
1567 |
+
$Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRuleCnt'] = $SubClassRuleCnt = $this->read_ushort();
|
1568 |
+
$SubClassRule = array();
|
1569 |
+
for($b=0;$b<$SubClassRuleCnt;$b++) {
|
1570 |
+
$SubClassRule[$b] = $Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]+$this->read_ushort();
|
1571 |
+
$Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b] = $SubClassRule[$b];
|
1572 |
+
}
|
1573 |
+
}
|
1574 |
+
}
|
1575 |
+
|
1576 |
+
for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$s++) {
|
1577 |
+
$SubClassRuleCnt = $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRuleCnt'];
|
1578 |
+
for($b=0;$b<$SubClassRuleCnt;$b++) {
|
1579 |
+
if ($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]>0) {
|
1580 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b]);
|
1581 |
+
$Rule = array();
|
1582 |
+
$Rule['InputGlyphCount'] = $this->read_ushort();
|
1583 |
+
$Rule['SubstCount'] = $this->read_ushort();
|
1584 |
+
for ($r=1;$r<$Rule['InputGlyphCount'];$r++) {
|
1585 |
+
$Rule['Input'][$r] = $this->read_ushort();
|
1586 |
+
}
|
1587 |
+
for ($r=0;$r<$Rule['SubstCount'];$r++) {
|
1588 |
+
$Rule['SequenceIndex'][$r] = $this->read_ushort();
|
1589 |
+
$Rule['LookupListIndex'][$r] = $this->read_ushort();
|
1590 |
+
}
|
1591 |
+
|
1592 |
+
$Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b] = $Rule;
|
1593 |
+
}
|
1594 |
+
}
|
1595 |
+
}
|
1596 |
+
}
|
1597 |
+
// Format 3: Coverage-based Context Glyph Substitution
|
1598 |
+
else if ($SubstFormat==3) {
|
1599 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) {
|
1600 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageInput'][$b]);
|
1601 |
+
$glyphs = $this->_getCoverage();
|
1602 |
+
$Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'][] = implode("|",$glyphs);
|
1603 |
+
}
|
1604 |
+
die("Lookup Type 5, SubstFormat 3 not tested. Please report this with the name of font used - ".$this->fontkey);
|
1605 |
+
}
|
1606 |
+
|
1607 |
+
}
|
1608 |
+
|
1609 |
+
// LookupType 6: Chaining Contextual Substitution Subtable
|
1610 |
+
else if ($Lookup[$i]['Type'] == 6) {
|
1611 |
+
// Format 1: Simple Chaining Context Glyph Substitution p255
|
1612 |
+
if ($SubstFormat==1) {
|
1613 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
|
1614 |
+
$Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
|
1615 |
+
|
1616 |
+
$ChainSubRuleSetCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];
|
1617 |
+
|
1618 |
+
for ($s=0;$s<$ChainSubRuleSetCnt;$s++) {
|
1619 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][$s]);
|
1620 |
+
$ChainSubRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleCount'] = $this->read_ushort();
|
1621 |
+
for ($r=0;$r<$ChainSubRuleCnt;$r++) {
|
1622 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleOffset'][$r] = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][$s] + $this->read_ushort();
|
1623 |
+
|
1624 |
+
}
|
1625 |
+
}
|
1626 |
+
for ($s=0;$s<$ChainSubRuleSetCnt;$s++) {
|
1627 |
+
$ChainSubRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleCount'];
|
1628 |
+
for ($r=0;$r<$ChainSubRuleCnt;$r++) {
|
1629 |
+
// ChainSubRule
|
1630 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleOffset'][$r]);
|
1631 |
+
|
1632 |
+
$BacktrackGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['BacktrackGlyphCount'] = $this->read_ushort();
|
1633 |
+
for ($g=0;$g<$BacktrackGlyphCount;$g++) {
|
1634 |
+
$glyphID = $this->read_ushort();
|
1635 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['BacktrackGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
|
1636 |
+
}
|
1637 |
+
|
1638 |
+
$InputGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['InputGlyphCount'] = $this->read_ushort();
|
1639 |
+
for ($g=1;$g<$InputGlyphCount;$g++) {
|
1640 |
+
$glyphID = $this->read_ushort();
|
1641 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['InputGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
|
1642 |
+
}
|
1643 |
+
|
1644 |
+
|
1645 |
+
$LookaheadGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookaheadGlyphCount'] = $this->read_ushort();
|
1646 |
+
for ($g=0;$g<$LookaheadGlyphCount;$g++) {
|
1647 |
+
$glyphID = $this->read_ushort();
|
1648 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookaheadGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
|
1649 |
+
}
|
1650 |
+
|
1651 |
+
$SubstCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['SubstCount'] = $this->read_ushort();
|
1652 |
+
for ($lu=0;$lu<$SubstCount;$lu++) {
|
1653 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['SequenceIndex'][$lu] = $this->read_ushort();
|
1654 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookupListIndex'][$lu] = $this->read_ushort();
|
1655 |
+
}
|
1656 |
+
}
|
1657 |
+
}
|
1658 |
+
|
1659 |
+
}
|
1660 |
+
// Format 2: Class-based Chaining Context Glyph Substitution p257
|
1661 |
+
else if ($SubstFormat==2) {
|
1662 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
|
1663 |
+
$Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
|
1664 |
+
|
1665 |
+
$BacktrackClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['BacktrackClassDefOffset']);
|
1666 |
+
$Lookup[$i]['Subtable'][$c]['BacktrackClasses'] = $BacktrackClasses;
|
1667 |
+
|
1668 |
+
$InputClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['InputClassDefOffset']);
|
1669 |
+
$Lookup[$i]['Subtable'][$c]['InputClasses'] = $InputClasses;
|
1670 |
+
|
1671 |
+
$LookaheadClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['LookaheadClassDefOffset']);
|
1672 |
+
$Lookup[$i]['Subtable'][$c]['LookaheadClasses'] = $LookaheadClasses;
|
1673 |
+
|
1674 |
+
for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$s++) {
|
1675 |
+
if ($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]>0) {
|
1676 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]);
|
1677 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRuleCnt'] = $ChainSubClassRuleCnt = $this->read_ushort();
|
1678 |
+
$ChainSubClassRule = array();
|
1679 |
+
for($b=0;$b<$ChainSubClassRuleCnt;$b++) {
|
1680 |
+
$ChainSubClassRule[$b] = $Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]+$this->read_ushort();
|
1681 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b] = $ChainSubClassRule[$b];
|
1682 |
+
}
|
1683 |
+
}
|
1684 |
+
}
|
1685 |
+
|
1686 |
+
for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$s++) {
|
1687 |
+
$ChainSubClassRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRuleCnt'];
|
1688 |
+
for($b=0;$b<$ChainSubClassRuleCnt;$b++) {
|
1689 |
+
if ($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]>0) {
|
1690 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b]);
|
1691 |
+
$Rule = array();
|
1692 |
+
$Rule['BacktrackGlyphCount'] = $this->read_ushort();
|
1693 |
+
for ($r=0;$r<$Rule['BacktrackGlyphCount'];$r++) {
|
1694 |
+
$Rule['Backtrack'][$r] = $this->read_ushort();
|
1695 |
+
}
|
1696 |
+
$Rule['InputGlyphCount'] = $this->read_ushort();
|
1697 |
+
for ($r=1;$r<$Rule['InputGlyphCount'];$r++) {
|
1698 |
+
$Rule['Input'][$r] = $this->read_ushort();
|
1699 |
+
}
|
1700 |
+
$Rule['LookaheadGlyphCount'] = $this->read_ushort();
|
1701 |
+
for ($r=0;$r<$Rule['LookaheadGlyphCount'];$r++) {
|
1702 |
+
$Rule['Lookahead'][$r] = $this->read_ushort();
|
1703 |
+
}
|
1704 |
+
$Rule['SubstCount'] = $this->read_ushort();
|
1705 |
+
for ($r=0;$r<$Rule['SubstCount'];$r++) {
|
1706 |
+
$Rule['SequenceIndex'][$r] = $this->read_ushort();
|
1707 |
+
$Rule['LookupListIndex'][$r] = $this->read_ushort();
|
1708 |
+
}
|
1709 |
+
|
1710 |
+
$Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b] = $Rule;
|
1711 |
+
}
|
1712 |
+
}
|
1713 |
+
}
|
1714 |
+
}
|
1715 |
+
// Format 3: Coverage-based Chaining Context Glyph Substitution p259
|
1716 |
+
else if ($SubstFormat==3) {
|
1717 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'];$b++) {
|
1718 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageBacktrack'][$b]);
|
1719 |
+
$glyphs = $this->_getCoverage();
|
1720 |
+
$Lookup[$i]['Subtable'][$c]['CoverageBacktrackGlyphs'][] = implode("|",$glyphs);
|
1721 |
+
}
|
1722 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) {
|
1723 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageInput'][$b]);
|
1724 |
+
$glyphs = $this->_getCoverage();
|
1725 |
+
$Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'][] = implode("|",$glyphs);
|
1726 |
+
// Don't use above value as these are ordered numerically not as need to process
|
1727 |
+
}
|
1728 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'];$b++) {
|
1729 |
+
$this->seek($Lookup[$i]['Subtable'][$c]['CoverageLookahead'][$b]);
|
1730 |
+
$glyphs = $this->_getCoverage();
|
1731 |
+
$Lookup[$i]['Subtable'][$c]['CoverageLookaheadGlyphs'][] = implode("|",$glyphs);
|
1732 |
+
}
|
1733 |
+
|
1734 |
+
}
|
1735 |
+
}
|
1736 |
+
}
|
1737 |
+
}
|
1738 |
+
|
1739 |
+
|
1740 |
+
//=====================================================================================
|
1741 |
+
//=====================================================================================
|
1742 |
+
//=====================================================================================
|
1743 |
+
|
1744 |
+
|
1745 |
+
|
1746 |
+
$st = $this->mpdf->OTLscript;
|
1747 |
+
$t = $this->mpdf->OTLlang;
|
1748 |
+
$langsys = $gsub[$st][$t];
|
1749 |
+
|
1750 |
+
|
1751 |
+
$lul = array(); // array of LookupListIndexes
|
1752 |
+
$tags = array(); // corresponding array of feature tags e.g. 'ccmp'
|
1753 |
+
foreach($langsys AS $tag=>$ft) {
|
1754 |
+
foreach($ft AS $ll) {
|
1755 |
+
$lul[$ll] = $tag;
|
1756 |
+
}
|
1757 |
+
}
|
1758 |
+
ksort($lul); // Order the Lookups in the order they are in the GUSB table, regardless of Feature order
|
1759 |
+
$this->_getGSUBarray($Lookup, $lul, $st);
|
1760 |
+
//print_r($lul); exit;
|
1761 |
+
|
1762 |
+
|
1763 |
+
|
1764 |
+
|
1765 |
+
}
|
1766 |
+
//print_r($Lookup); exit;
|
1767 |
+
|
1768 |
+
return array($GSUBScriptLang, $gsub, $GSLookup, $rtlPUAstr, $rtlPUAarr);
|
1769 |
+
|
1770 |
+
}
|
1771 |
+
/////////////////////////////////////////////////////////////////////////////////////////
|
1772 |
+
// GSUB functions
|
1773 |
+
function _getGSUBarray(&$Lookup, &$lul, $scripttag, $level=1, $coverage='', $exB='', $exL='') {
|
1774 |
+
// Process (3) LookupList for specific Script-LangSys
|
1775 |
+
// Generate preg_replace
|
1776 |
+
$html = '';
|
1777 |
+
if ($level==1) { $html .= '<bookmark level="0" content="GSUB features">'; }
|
1778 |
+
foreach($lul AS $i=>$tag) {
|
1779 |
+
$html .= '<div class="level'.$level.'">';
|
1780 |
+
$html .= '<h5 class="level'.$level.'">';
|
1781 |
+
if ($level==1) { $html .= '<bookmark level="1" content="'.$tag.' [#'.$i.']">'; }
|
1782 |
+
$html .= 'Lookup #'.$i.' [tag: <span style="color:#000066;">'.$tag.'</span>]</h5>';
|
1783 |
+
$ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']);
|
1784 |
+
if ($ignore) { $html .= '<div class="ignore">Ignoring: '.$ignore.'</div> '; }
|
1785 |
+
|
1786 |
+
$Type = $Lookup[$i]['Type'];
|
1787 |
+
$Flag = $Lookup[$i]['Flag'];
|
1788 |
+
if (($Flag & 0x0001) == 1) { $dir = 'RTL'; }
|
1789 |
+
else { $dir = 'LTR'; }
|
1790 |
+
|
1791 |
+
for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
|
1792 |
+
$html .= '<div class="subtable">Subtable #'.$c;
|
1793 |
+
if ($level==1) { $html .= '<bookmark level="2" content="Subtable #'.$c.'">'; }
|
1794 |
+
$html .= '</div>';
|
1795 |
+
|
1796 |
+
$SubstFormat= $Lookup[$i]['Subtable'][$c]['Format'] ;
|
1797 |
+
|
1798 |
+
// LookupType 1: Single Substitution Subtable
|
1799 |
+
if ($Lookup[$i]['Type'] == 1) {
|
1800 |
+
$html .= '<div class="lookuptype">LookupType 1: Single Substitution Subtable</div>';
|
1801 |
+
for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
|
1802 |
+
$inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
|
1803 |
+
$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0];
|
1804 |
+
if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; }
|
1805 |
+
$html .= '<div class="substitution">';
|
1806 |
+
$html .= '<span class="unicode">'.$this->formatUni($inputGlyphs[0]).' </span> ';
|
1807 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
1808 |
+
$html .= '<span class="unchanged"> '.$this->formatEntity($inputGlyphs[0]).'</span>';
|
1809 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
1810 |
+
$html .= ' » » ';
|
1811 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
1812 |
+
$html .= '<span class="changed"> '.$this->formatEntity($substitute).'</span>';
|
1813 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
1814 |
+
$html .= ' <span class="unicode">'.$this->formatUni($substitute).'</span> ';
|
1815 |
+
$html .= '</div>';
|
1816 |
+
}
|
1817 |
+
}
|
1818 |
+
// LookupType 2: Multiple Substitution Subtable
|
1819 |
+
else if ($Lookup[$i]['Type'] == 2) {
|
1820 |
+
$html .= '<div class="lookuptype">LookupType 2: Multiple Substitution Subtable</div>';
|
1821 |
+
for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
|
1822 |
+
$inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
|
1823 |
+
$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'];
|
1824 |
+
if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; }
|
1825 |
+
$html .= '<div class="substitution">';
|
1826 |
+
$html .= '<span class="unicode">'.$this->formatUni($inputGlyphs[0]).' </span> ';
|
1827 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
1828 |
+
$html .= '<span class="unchanged"> '.$this->formatEntity($inputGlyphs[0]).'</span>';
|
1829 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
1830 |
+
$html .= ' » » ';
|
1831 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
1832 |
+
$html .= '<span class="changed"> '.$this->formatEntityArr($substitute).'</span>';
|
1833 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
1834 |
+
$html .= ' <span class="unicode">'.$this->formatUniArr($substitute).'</span> ';
|
1835 |
+
$html .= '</div>';
|
1836 |
+
}
|
1837 |
+
}
|
1838 |
+
// LookupType 3: Alternate Forms
|
1839 |
+
else if ($Lookup[$i]['Type'] == 3) {
|
1840 |
+
$html .= '<div class="lookuptype">LookupType 3: Alternate Forms</div>';
|
1841 |
+
for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
|
1842 |
+
$inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
|
1843 |
+
$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0];
|
1844 |
+
if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; }
|
1845 |
+
$html .= '<div class="substitution">';
|
1846 |
+
$html .= '<span class="unicode">'.$this->formatUni($inputGlyphs[0]).' </span> ';
|
1847 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
1848 |
+
$html .= '<span class="unchanged"> '.$this->formatEntity($inputGlyphs[0]).'</span>';
|
1849 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
1850 |
+
$html .= ' » » ';
|
1851 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
1852 |
+
$html .= '<span class="changed"> '.$this->formatEntity($substitute).'</span>';
|
1853 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
1854 |
+
$html .= ' <span class="unicode">'.$this->formatUni($substitute).'</span> ';
|
1855 |
+
if (count($Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'])>1) {
|
1856 |
+
for ($alt=1;$alt<count($Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute']);$alt++) {
|
1857 |
+
$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][$alt];
|
1858 |
+
$html .= ' | ALT #'.$alt.' ';
|
1859 |
+
$html .= '<span class="changed"> '.$this->formatEntity($substitute).'</span>';
|
1860 |
+
$html .= ' <span class="unicode">'.$this->formatUni($substitute).'</span> ';
|
1861 |
+
}
|
1862 |
+
}
|
1863 |
+
$html .= '</div>';
|
1864 |
+
}
|
1865 |
+
}
|
1866 |
+
// LookupType 4: Ligature Substitution Subtable
|
1867 |
+
else if ($Lookup[$i]['Type'] == 4) {
|
1868 |
+
$html .= '<div class="lookuptype">LookupType 4: Ligature Substitution Subtable</div>';
|
1869 |
+
for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
|
1870 |
+
$inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
|
1871 |
+
$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0];
|
1872 |
+
if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; }
|
1873 |
+
$html .= '<div class="substitution">';
|
1874 |
+
$html .= '<span class="unicode">'.$this->formatUniArr($inputGlyphs).' </span> ';
|
1875 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
1876 |
+
$html .= '<span class="unchanged"> '.$this->formatEntityArr($inputGlyphs).'</span>';
|
1877 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
1878 |
+
$html .= ' » » ';
|
1879 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
1880 |
+
$html .= '<span class="changed"> '.$this->formatEntity($substitute).'</span>';
|
1881 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
1882 |
+
$html .= ' <span class="unicode">'.$this->formatUni($substitute).'</span> ';
|
1883 |
+
$html .= '</div>';
|
1884 |
+
}
|
1885 |
+
}
|
1886 |
+
|
1887 |
+
// LookupType 5: Contextual Substitution Subtable
|
1888 |
+
else if ($Lookup[$i]['Type'] == 5) {
|
1889 |
+
$html .= '<div class="lookuptype">LookupType 5: Contextual Substitution Subtable</div>';
|
1890 |
+
// Format 1: Context Substitution
|
1891 |
+
if ($SubstFormat==1) {
|
1892 |
+
$html .= '<div class="lookuptypesub">Format 1: Context Substitution</div>';
|
1893 |
+
for($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'];$s++) {
|
1894 |
+
// SubRuleSet
|
1895 |
+
$subRule = array();
|
1896 |
+
$html .= '<div class="rule">Subrule Set: '.$s.'</div>';
|
1897 |
+
foreach($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'] AS $rctr=>$rule) {
|
1898 |
+
// SubRule
|
1899 |
+
$html .= '<div class="rule">SubRule: '.$rctr.'</div>';
|
1900 |
+
$inputGlyphs = array();
|
1901 |
+
if ($rule['GlyphCount']>1) {
|
1902 |
+
$inputGlyphs = $rule['InputGlyphs'];
|
1903 |
+
}
|
1904 |
+
$inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['FirstGlyph'];
|
1905 |
+
ksort($inputGlyphs);
|
1906 |
+
$nInput = count($inputGlyphs);
|
1907 |
+
|
1908 |
+
|
1909 |
+
$exampleI = array();
|
1910 |
+
$html .= '<div class="context">CONTEXT: ';
|
1911 |
+
for ($ff=0;$ff<count($inputGlyphs);$ff++) {
|
1912 |
+
$html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>';
|
1913 |
+
$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
|
1914 |
+
}
|
1915 |
+
$html .= '</div>';
|
1916 |
+
|
1917 |
+
|
1918 |
+
for ($b=0;$b<$rule['SubstCount'];$b++) {
|
1919 |
+
$lup = $rule['SubstLookupRecord'][$b]['LookupListIndex'];
|
1920 |
+
$seqIndex = $rule['SubstLookupRecord'][$b]['SequenceIndex'];
|
1921 |
+
|
1922 |
+
// GENERATE exampleI[<seqIndex] .... exampleI[>seqIndex]
|
1923 |
+
$exB = '';
|
1924 |
+
$exL = '';
|
1925 |
+
if ($seqIndex>0) {
|
1926 |
+
$exB .= '<span class="inputother">';
|
1927 |
+
for($ip=0;$ip<$seqIndex;$ip++) {
|
1928 |
+
$exB .= $this->formatEntity($inputGlyphs[$ip]).'‍';
|
1929 |
+
}
|
1930 |
+
$exB .= '</span>';
|
1931 |
+
}
|
1932 |
+
if (count($inputGlyphs)>($seqIndex+1)) {
|
1933 |
+
$exL .= '<span class="inputother">';
|
1934 |
+
for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
|
1935 |
+
$exL .= $this->formatEntity($inputGlyphs[$ip]).'‍';
|
1936 |
+
}
|
1937 |
+
$exL .= '</span>';
|
1938 |
+
}
|
1939 |
+
$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>';
|
1940 |
+
|
1941 |
+
$lul2 = array($lup=>$tag);
|
1942 |
+
|
1943 |
+
// Only apply if the (first) 'Replace' glyph from the
|
1944 |
+
// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
|
1945 |
+
// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
|
1946 |
+
// to level 2 and only apply if first Replace glyph is in this list
|
1947 |
+
$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
|
1948 |
+
}
|
1949 |
+
|
1950 |
+
|
1951 |
+
if (count($subRule['rules'])) $volt[] = $subRule;
|
1952 |
+
|
1953 |
+
}
|
1954 |
+
}
|
1955 |
+
}
|
1956 |
+
// Format 2: Class-based Context Glyph Substitution
|
1957 |
+
else if ($SubstFormat==2) {
|
1958 |
+
$html .= '<div class="lookuptypesub">Format 2: Class-based Context Glyph Substitution</div>';
|
1959 |
+
foreach($Lookup[$i]['Subtable'][$c]['SubClassSet'] AS $inputClass=>$cscs) {
|
1960 |
+
$html .= '<div class="rule">Input Class: '.$inputClass.'</div>';
|
1961 |
+
for($cscrule=0;$cscrule<$cscs['SubClassRuleCnt'];$cscrule++) {
|
1962 |
+
$html .= '<div class="rule">Rule: '.$cscrule.'</div>';
|
1963 |
+
$rule = $cscs['SubClassRule'][$cscrule];
|
1964 |
+
|
1965 |
+
$inputGlyphs = array();
|
1966 |
+
|
1967 |
+
$inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$inputClass];
|
1968 |
+
|
1969 |
+
if ($rule['InputGlyphCount']>1) {
|
1970 |
+
// NB starts at 1
|
1971 |
+
for ($gcl=1;$gcl<$rule['InputGlyphCount'];$gcl++) {
|
1972 |
+
$classindex = $rule['Input'][$gcl];
|
1973 |
+
$inputGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex];
|
1974 |
+
}
|
1975 |
+
}
|
1976 |
+
|
1977 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
1978 |
+
$class0excl = implode('|', $Lookup[$i]['Subtable'][$c]['InputClasses']);
|
1979 |
+
|
1980 |
+
$exampleI = array();
|
1981 |
+
$html .= '<div class="context">CONTEXT: ';
|
1982 |
+
for ($ff=0;$ff<count($inputGlyphs);$ff++) {
|
1983 |
+
|
1984 |
+
if (!$inputGlyphs[$ff]) {
|
1985 |
+
|
1986 |
+
$html .= '<div>Input #'.$ff.': <span class="unchanged"> [NOT '.$this->formatEntityStr($class0excl).'] </span></div>';
|
1987 |
+
$exampleI[] = '[NOT '.$this->formatEntityFirst($class0excl).']';
|
1988 |
+
}
|
1989 |
+
else {
|
1990 |
+
$html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>';
|
1991 |
+
$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
|
1992 |
+
}
|
1993 |
+
}
|
1994 |
+
$html .= '</div>';
|
1995 |
+
|
1996 |
+
|
1997 |
+
for ($b=0;$b<$rule['SubstCount'];$b++) {
|
1998 |
+
$lup = $rule['LookupListIndex'][$b];
|
1999 |
+
$seqIndex = $rule['SequenceIndex'][$b];
|
2000 |
+
|
2001 |
+
// GENERATE exampleI[<seqIndex] .... exampleI[>seqIndex]
|
2002 |
+
$exB = '';
|
2003 |
+
$exL = '';
|
2004 |
+
|
2005 |
+
if ($seqIndex>0) {
|
2006 |
+
$exB .= '<span class="inputother">';
|
2007 |
+
for($ip=0;$ip<$seqIndex;$ip++) {
|
2008 |
+
if (!$inputGlyphs[$ip]) {
|
2009 |
+
$exB .= '[*]';
|
2010 |
+
}
|
2011 |
+
else {
|
2012 |
+
$exB .= $this->formatEntityFirst($inputGlyphs[$ip]).'‍';
|
2013 |
+
}
|
2014 |
+
}
|
2015 |
+
$exB .= '</span>';
|
2016 |
+
}
|
2017 |
+
|
2018 |
+
if (count($inputGlyphs)>($seqIndex+1)) {
|
2019 |
+
$exL .= '<span class="inputother">';
|
2020 |
+
for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
|
2021 |
+
if (!$inputGlyphs[$ip]) {
|
2022 |
+
$exL .= '[*]';
|
2023 |
+
}
|
2024 |
+
else {
|
2025 |
+
$exL .= $this->formatEntityFirst($inputGlyphs[$ip]).'‍';
|
2026 |
+
}
|
2027 |
+
}
|
2028 |
+
$exL .= '</span>';
|
2029 |
+
}
|
2030 |
+
|
2031 |
+
$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>';
|
2032 |
+
|
2033 |
+
$lul2 = array($lup=>$tag);
|
2034 |
+
|
2035 |
+
// Only apply if the (first) 'Replace' glyph from the
|
2036 |
+
// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
|
2037 |
+
// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
|
2038 |
+
// to level 2 and only apply if first Replace glyph is in this list
|
2039 |
+
$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
|
2040 |
+
}
|
2041 |
+
if (count($subRule['rules'])) $volt[] = $subRule;
|
2042 |
+
|
2043 |
+
}
|
2044 |
+
}
|
2045 |
+
|
2046 |
+
|
2047 |
+
|
2048 |
+
}
|
2049 |
+
// Format 3: Coverage-based Context Glyph Substitution p259
|
2050 |
+
else if ($SubstFormat==3) {
|
2051 |
+
$html .= '<div class="lookuptypesub">Format 3: Coverage-based Context Glyph Substitution </div>';
|
2052 |
+
// IgnoreMarks flag set on main Lookup table
|
2053 |
+
$inputGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'];
|
2054 |
+
$CoverageInputGlyphs = implode('|', $inputGlyphs);
|
2055 |
+
$nInput = $Lookup[$i]['Subtable'][$c]['InputGlyphCount'];
|
2056 |
+
|
2057 |
+
$exampleI = array();
|
2058 |
+
$html .= '<div class="context">CONTEXT: ';
|
2059 |
+
for ($ff=0;$ff<count($inputGlyphs);$ff++) {
|
2060 |
+
$html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>';
|
2061 |
+
$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
|
2062 |
+
}
|
2063 |
+
$html .= '</div>';
|
2064 |
+
|
2065 |
+
|
2066 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) {
|
2067 |
+
$lup = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'];
|
2068 |
+
$seqIndex = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'];
|
2069 |
+
// GENERATE exampleI[<seqIndex] .... exampleI[>seqIndex]
|
2070 |
+
$exB = '';
|
2071 |
+
$exL = '';
|
2072 |
+
if ($seqIndex>0) {
|
2073 |
+
$exB .= '<span class="inputother">';
|
2074 |
+
for($ip=0;$ip<$seqIndex;$ip++) {
|
2075 |
+
$exB .= $exampleI[$ip].'‍';
|
2076 |
+
}
|
2077 |
+
$exB .= '</span>';
|
2078 |
+
}
|
2079 |
+
|
2080 |
+
if (count($inputGlyphs)>($seqIndex+1)) {
|
2081 |
+
$exL .= '<span class="inputother">';
|
2082 |
+
for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
|
2083 |
+
$exL .= $exampleI[$ip].'‍';
|
2084 |
+
}
|
2085 |
+
$exL .= '</span>';
|
2086 |
+
}
|
2087 |
+
|
2088 |
+
$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>';
|
2089 |
+
|
2090 |
+
$lul2 = array($lup=>$tag);
|
2091 |
+
|
2092 |
+
// Only apply if the (first) 'Replace' glyph from the
|
2093 |
+
// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
|
2094 |
+
// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
|
2095 |
+
// to level 2 and only apply if first Replace glyph is in this list
|
2096 |
+
$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
|
2097 |
+
}
|
2098 |
+
if (count($subRule['rules'])) $volt[] = $subRule;
|
2099 |
+
}
|
2100 |
+
|
2101 |
+
//print_r($Lookup[$i]);
|
2102 |
+
//print_r($volt[(count($volt)-1)]); exit;
|
2103 |
+
}
|
2104 |
+
// LookupType 6: Chaining Contextual Substitution Subtable
|
2105 |
+
else if ($Lookup[$i]['Type'] == 6) {
|
2106 |
+
$html .= '<div class="lookuptype">LookupType 6: Chaining Contextual Substitution Subtable</div>';
|
2107 |
+
// Format 1: Simple Chaining Context Glyph Substitution p255
|
2108 |
+
if ($SubstFormat==1) {
|
2109 |
+
$html .= '<div class="lookuptypesub">Format 1: Simple Chaining Context Glyph Substitution </div>';
|
2110 |
+
for($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];$s++) {
|
2111 |
+
// ChainSubRuleSet
|
2112 |
+
$subRule = array();
|
2113 |
+
$html .= '<div class="rule">Subrule Set: '.$s.'</div>';
|
2114 |
+
$firstInputGlyph = $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'][$s]; // First input gyyph
|
2115 |
+
foreach($Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'] AS $rctr=>$rule) {
|
2116 |
+
$html .= '<div class="rule">SubRule: '.$rctr.'</div>';
|
2117 |
+
// ChainSubRule
|
2118 |
+
$inputGlyphs = array();
|
2119 |
+
if ($rule['InputGlyphCount']>1) {
|
2120 |
+
$inputGlyphs = $rule['InputGlyphs'];
|
2121 |
+
}
|
2122 |
+
$inputGlyphs[0] = $firstInputGlyph;
|
2123 |
+
ksort($inputGlyphs);
|
2124 |
+
$nInput = count($inputGlyphs);
|
2125 |
+
|
2126 |
+
if ($rule['BacktrackGlyphCount']) { $backtrackGlyphs = $rule['BacktrackGlyphs']; }
|
2127 |
+
else { $backtrackGlyphs = array(); }
|
2128 |
+
|
2129 |
+
if ($rule['LookaheadGlyphCount']) { $lookaheadGlyphs = $rule['LookaheadGlyphs']; }
|
2130 |
+
else { $lookaheadGlyphs = array(); }
|
2131 |
+
|
2132 |
+
|
2133 |
+
$exampleB = array();
|
2134 |
+
$exampleI = array();
|
2135 |
+
$exampleL = array();
|
2136 |
+
$html .= '<div class="context">CONTEXT: ';
|
2137 |
+
for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) {
|
2138 |
+
$html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>';
|
2139 |
+
$exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]);
|
2140 |
+
}
|
2141 |
+
for ($ff=0;$ff<count($inputGlyphs);$ff++) {
|
2142 |
+
$html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>';
|
2143 |
+
$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
|
2144 |
+
}
|
2145 |
+
for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) {
|
2146 |
+
$html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>';
|
2147 |
+
$exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]);
|
2148 |
+
}
|
2149 |
+
$html .= '</div>';
|
2150 |
+
|
2151 |
+
|
2152 |
+
for ($b=0;$b<$rule['SubstCount'];$b++) {
|
2153 |
+
$lup = $rule['LookupListIndex'][$b];
|
2154 |
+
$seqIndex = $rule['SequenceIndex'][$b];
|
2155 |
+
|
2156 |
+
// GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n]
|
2157 |
+
$exB = '';
|
2158 |
+
$exL = '';
|
2159 |
+
if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('‍',$exampleB).'</span>'; }
|
2160 |
+
|
2161 |
+
if ($seqIndex>0) {
|
2162 |
+
$exB .= '<span class="inputother">';
|
2163 |
+
for($ip=0;$ip<$seqIndex;$ip++) {
|
2164 |
+
$exB .= $this->formatEntity($inputGlyphs[$ip]).'‍';
|
2165 |
+
}
|
2166 |
+
$exB .= '</span>';
|
2167 |
+
}
|
2168 |
+
|
2169 |
+
if (count($inputGlyphs)>($seqIndex+1)) {
|
2170 |
+
$exL .= '<span class="inputother">';
|
2171 |
+
for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
|
2172 |
+
$exL .= $this->formatEntity($inputGlyphs[$ip]).'‍';
|
2173 |
+
}
|
2174 |
+
$exL .= '</span>';
|
2175 |
+
}
|
2176 |
+
|
2177 |
+
if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('‍',$exampleL).'</span>'; }
|
2178 |
+
|
2179 |
+
$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>';
|
2180 |
+
|
2181 |
+
$lul2 = array($lup=>$tag);
|
2182 |
+
|
2183 |
+
// Only apply if the (first) 'Replace' glyph from the
|
2184 |
+
// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
|
2185 |
+
// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
|
2186 |
+
// to level 2 and only apply if first Replace glyph is in this list
|
2187 |
+
$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
|
2188 |
+
}
|
2189 |
+
|
2190 |
+
|
2191 |
+
if (count($subRule['rules'])) $volt[] = $subRule;
|
2192 |
+
|
2193 |
+
|
2194 |
+
|
2195 |
+
}
|
2196 |
+
}
|
2197 |
+
|
2198 |
+
}
|
2199 |
+
// Format 2: Class-based Chaining Context Glyph Substitution p257
|
2200 |
+
else if ($SubstFormat==2) {
|
2201 |
+
$html .= '<div class="lookuptypesub">Format 2: Class-based Chaining Context Glyph Substitution </div>';
|
2202 |
+
foreach($Lookup[$i]['Subtable'][$c]['ChainSubClassSet'] AS $inputClass=>$cscs) {
|
2203 |
+
$html .= '<div class="rule">Input Class: '.$inputClass.'</div>';
|
2204 |
+
for($cscrule=0;$cscrule<$cscs['ChainSubClassRuleCnt'];$cscrule++) {
|
2205 |
+
$html .= '<div class="rule">Rule: '.$cscrule.'</div>';
|
2206 |
+
$rule = $cscs['ChainSubClassRule'][$cscrule];
|
2207 |
+
|
2208 |
+
// These contain classes of glyphs as strings
|
2209 |
+
// $Lookup[$i]['Subtable'][$c]['InputClasses'][(class)] e.g. 02E6|02E7|02E8
|
2210 |
+
// $Lookup[$i]['Subtable'][$c]['LookaheadClasses'][(class)]
|
2211 |
+
// $Lookup[$i]['Subtable'][$c]['BacktrackClasses'][(class)]
|
2212 |
+
|
2213 |
+
// These contain arrays of classIndexes
|
2214 |
+
// [Backtrack] [Lookahead] and [Input] (Input is from the second position only)
|
2215 |
+
|
2216 |
+
$inputGlyphs = array();
|
2217 |
+
|
2218 |
+
$inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$inputClass];
|
2219 |
+
if ($rule['InputGlyphCount']>1) {
|
2220 |
+
// NB starts at 1
|
2221 |
+
for ($gcl=1;$gcl<$rule['InputGlyphCount'];$gcl++) {
|
2222 |
+
$classindex = $rule['Input'][$gcl];
|
2223 |
+
$inputGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex];
|
2224 |
+
}
|
2225 |
+
}
|
2226 |
+
// Class 0 contains all the glyphs NOT in the other classes
|
2227 |
+
$class0excl = implode('|', $Lookup[$i]['Subtable'][$c]['InputClasses']);
|
2228 |
+
|
2229 |
+
$nInput = $rule['InputGlyphCount'];
|
2230 |
+
|
2231 |
+
if ($rule['BacktrackGlyphCount']) {
|
2232 |
+
for ($gcl=0;$gcl<$rule['BacktrackGlyphCount'];$gcl++) {
|
2233 |
+
$classindex = $rule['Backtrack'][$gcl];
|
2234 |
+
$backtrackGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['BacktrackClasses'][$classindex];
|
2235 |
+
}
|
2236 |
+
}
|
2237 |
+
else { $backtrackGlyphs = array(); }
|
2238 |
+
|
2239 |
+
if ($rule['LookaheadGlyphCount']) {
|
2240 |
+
for ($gcl=0;$gcl<$rule['LookaheadGlyphCount'];$gcl++) {
|
2241 |
+
$classindex = $rule['Lookahead'][$gcl];
|
2242 |
+
$lookaheadGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['LookaheadClasses'][$classindex];
|
2243 |
+
}
|
2244 |
+
}
|
2245 |
+
else { $lookaheadGlyphs = array(); }
|
2246 |
+
|
2247 |
+
|
2248 |
+
$exampleB = array();
|
2249 |
+
$exampleI = array();
|
2250 |
+
$exampleL = array();
|
2251 |
+
$html .= '<div class="context">CONTEXT: ';
|
2252 |
+
for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) {
|
2253 |
+
if (!$backtrackGlyphs[$ff]) {
|
2254 |
+
$html .= '<div>Backtrack #'.$ff.': <span class="unchanged"> [NOT '.$this->formatEntityStr($class0excl).'] </span></div>';
|
2255 |
+
$exampleB[] = '[NOT '.$this->formatEntityFirst($class0excl).']';
|
2256 |
+
|
2257 |
+
}
|
2258 |
+
else {
|
2259 |
+
$html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>';
|
2260 |
+
$exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]);
|
2261 |
+
}
|
2262 |
+
}
|
2263 |
+
for ($ff=0;$ff<count($inputGlyphs);$ff++) {
|
2264 |
+
if (!$inputGlyphs[$ff]) {
|
2265 |
+
$html .= '<div>Input #'.$ff.': <span class="unchanged"> [NOT '.$this->formatEntityStr($class0excl).'] </span></div>';
|
2266 |
+
$exampleI[] = '[NOT '.$this->formatEntityFirst($class0excl).']';
|
2267 |
+
}
|
2268 |
+
else {
|
2269 |
+
$html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>';
|
2270 |
+
$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
|
2271 |
+
}
|
2272 |
+
}
|
2273 |
+
for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) {
|
2274 |
+
if (!$lookaheadGlyphs[$ff]) {
|
2275 |
+
$html .= '<div>Lookahead #'.$ff.': <span class="unchanged"> [NOT '.$this->formatEntityStr($class0excl).'] </span></div>';
|
2276 |
+
$exampleL[] = '[NOT '.$this->formatEntityFirst($class0excl).']';
|
2277 |
+
|
2278 |
+
}
|
2279 |
+
else {
|
2280 |
+
$html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>';
|
2281 |
+
$exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]);
|
2282 |
+
}
|
2283 |
+
}
|
2284 |
+
$html .= '</div>';
|
2285 |
+
|
2286 |
+
|
2287 |
+
for ($b=0;$b<$rule['SubstCount'];$b++) {
|
2288 |
+
$lup = $rule['LookupListIndex'][$b];
|
2289 |
+
$seqIndex = $rule['SequenceIndex'][$b];
|
2290 |
+
|
2291 |
+
// GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n]
|
2292 |
+
$exB = '';
|
2293 |
+
$exL = '';
|
2294 |
+
if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('‍',$exampleB).'</span>'; }
|
2295 |
+
|
2296 |
+
if ($seqIndex>0) {
|
2297 |
+
$exB .= '<span class="inputother">';
|
2298 |
+
for($ip=0;$ip<$seqIndex;$ip++) {
|
2299 |
+
if (!$inputGlyphs[$ip]) {
|
2300 |
+
$exB .= '[*]';
|
2301 |
+
}
|
2302 |
+
else {
|
2303 |
+
$exB .= $this->formatEntityFirst($inputGlyphs[$ip]).'‍';
|
2304 |
+
}
|
2305 |
+
}
|
2306 |
+
$exB .= '</span>';
|
2307 |
+
}
|
2308 |
+
|
2309 |
+
if (count($inputGlyphs)>($seqIndex+1)) {
|
2310 |
+
$exL .= '<span class="inputother">';
|
2311 |
+
for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
|
2312 |
+
if (!$inputGlyphs[$ip]) {
|
2313 |
+
$exL .= '[*]';
|
2314 |
+
}
|
2315 |
+
else {
|
2316 |
+
$exL .= $this->formatEntityFirst($inputGlyphs[$ip]).'‍';
|
2317 |
+
}
|
2318 |
+
}
|
2319 |
+
$exL .= '</span>';
|
2320 |
+
}
|
2321 |
+
|
2322 |
+
if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('‍',$exampleL).'</span>'; }
|
2323 |
+
|
2324 |
+
$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>';
|
2325 |
+
|
2326 |
+
$lul2 = array($lup=>$tag);
|
2327 |
+
|
2328 |
+
// Only apply if the (first) 'Replace' glyph from the
|
2329 |
+
// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
|
2330 |
+
// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
|
2331 |
+
// to level 2 and only apply if first Replace glyph is in this list
|
2332 |
+
$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
|
2333 |
+
|
2334 |
+
}
|
2335 |
+
|
2336 |
+
}
|
2337 |
+
}
|
2338 |
+
|
2339 |
+
|
2340 |
+
//print_r($Lookup[$i]['Subtable'][$c]); exit;
|
2341 |
+
|
2342 |
+
}
|
2343 |
+
// Format 3: Coverage-based Chaining Context Glyph Substitution p259
|
2344 |
+
else if ($SubstFormat==3) {
|
2345 |
+
$html .= '<div class="lookuptypesub">Format 3: Coverage-based Chaining Context Glyph Substitution </div>';
|
2346 |
+
// IgnoreMarks flag set on main Lookup table
|
2347 |
+
$inputGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'];
|
2348 |
+
$CoverageInputGlyphs = implode('|', $inputGlyphs);
|
2349 |
+
$nInput = $Lookup[$i]['Subtable'][$c]['InputGlyphCount'];
|
2350 |
+
|
2351 |
+
if ($Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount']) {
|
2352 |
+
$backtrackGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageBacktrackGlyphs'];
|
2353 |
+
}
|
2354 |
+
else { $backtrackGlyphs = array(); }
|
2355 |
+
|
2356 |
+
if ($Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount']) {
|
2357 |
+
$lookaheadGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageLookaheadGlyphs'];
|
2358 |
+
}
|
2359 |
+
else { $lookaheadGlyphs = array(); }
|
2360 |
+
|
2361 |
+
|
2362 |
+
$exampleB = array();
|
2363 |
+
$exampleI = array();
|
2364 |
+
$exampleL = array();
|
2365 |
+
$html .= '<div class="context">CONTEXT: ';
|
2366 |
+
for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) {
|
2367 |
+
$html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>';
|
2368 |
+
$exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]);
|
2369 |
+
}
|
2370 |
+
for ($ff=0;$ff<count($inputGlyphs);$ff++) {
|
2371 |
+
$html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>';
|
2372 |
+
$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
|
2373 |
+
}
|
2374 |
+
for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) {
|
2375 |
+
$html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>';
|
2376 |
+
$exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]);
|
2377 |
+
}
|
2378 |
+
$html .= '</div>';
|
2379 |
+
|
2380 |
+
|
2381 |
+
for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) {
|
2382 |
+
$lup = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'];
|
2383 |
+
$seqIndex = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'];
|
2384 |
+
|
2385 |
+
|
2386 |
+
// GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n]
|
2387 |
+
$exB = '';
|
2388 |
+
$exL = '';
|
2389 |
+
if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('‍',$exampleB).'</span>'; }
|
2390 |
+
|
2391 |
+
if ($seqIndex>0) {
|
2392 |
+
$exB .= '<span class="inputother">';
|
2393 |
+
for($ip=0;$ip<$seqIndex;$ip++) {
|
2394 |
+
$exB .= $exampleI[$ip].'‍';
|
2395 |
+
}
|
2396 |
+
$exB .= '</span>';
|
2397 |
+
}
|
2398 |
+
|
2399 |
+
if (count($inputGlyphs)>($seqIndex+1)) {
|
2400 |
+
$exL .= '<span class="inputother">';
|
2401 |
+
for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
|
2402 |
+
$exL .= $exampleI[$ip].'‍';
|
2403 |
+
}
|
2404 |
+
$exL .= '</span>';
|
2405 |
+
}
|
2406 |
+
|
2407 |
+
if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('‍',$exampleL).'</span>'; }
|
2408 |
+
|
2409 |
+
$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>';
|
2410 |
+
|
2411 |
+
$lul2 = array($lup=>$tag);
|
2412 |
+
|
2413 |
+
// Only apply if the (first) 'Replace' glyph from the
|
2414 |
+
// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
|
2415 |
+
// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
|
2416 |
+
// to level 2 and only apply if first Replace glyph is in this list
|
2417 |
+
$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
|
2418 |
+
|
2419 |
+
}
|
2420 |
+
}
|
2421 |
+
}
|
2422 |
+
}
|
2423 |
+
$html .= '</div>';
|
2424 |
+
}
|
2425 |
+
if ($level ==1) { $this->mpdf->WriteHTML($html); }
|
2426 |
+
else { return $html; }
|
2427 |
+
//print_r($Lookup); exit;
|
2428 |
+
}
|
2429 |
+
//=====================================================================================
|
2430 |
+
//=====================================================================================
|
2431 |
+
// mPDF 5.7.1
|
2432 |
+
function _checkGSUBignore($flag, $glyph, $MarkFilteringSet) {
|
2433 |
+
$ignore = false;
|
2434 |
+
// Flag & 0x0008 = Ignore Marks
|
2435 |
+
if ((($flag & 0x0008) == 0x0008) && strpos($this->GlyphClassMarks,$glyph)) { $ignore = true; }
|
2436 |
+
if ((($flag & 0x0004) == 0x0004) && strpos($this->GlyphClassLigatures,$glyph)) { $ignore = true; }
|
2437 |
+
if ((($flag & 0x0002) == 0x0002) && strpos($this->GlyphClassBases,$glyph)) { $ignore = true; }
|
2438 |
+
// Flag & 0xFF?? = MarkAttachmentType
|
2439 |
+
if (($flag & 0xFF00) && strpos($this->MarkAttachmentType[($flag >> 8)],$glyph)) { $ignore = true; }
|
2440 |
+
// Flag & 0x0010 = UseMarkFilteringSet
|
2441 |
+
if (($flag & 0x0010) && strpos($this->MarkGlyphSets[$MarkFilteringSet],$glyph)) { $ignore = true; }
|
2442 |
+
return $ignore;
|
2443 |
+
}
|
2444 |
+
|
2445 |
+
function _getGSUBignoreString($flag, $MarkFilteringSet) {
|
2446 |
+
// If ignoreFlag set, combine all ignore glyphs into -> "((?:(?: FBA1| FBA2| FBA3))*)"
|
2447 |
+
// else "()"
|
2448 |
+
// for Input - set on secondary Lookup table if in Context, and set Backtrack and Lookahead on Context Lookup
|
2449 |
+
$str = "";
|
2450 |
+
$ignoreflag = 0;
|
2451 |
+
|
2452 |
+
// Flag & 0xFF?? = MarkAttachmentType
|
2453 |
+
if ($flag & 0xFF00) {
|
2454 |
+
$MarkAttachmentType = $flag >> 8;
|
2455 |
+
$ignoreflag = $flag;
|
2456 |
+
//$str = $this->MarkAttachmentType[$MarkAttachmentType];
|
2457 |
+
$str = "MarkAttachmentType[".$MarkAttachmentType."] ";
|
2458 |
+
}
|
2459 |
+
|
2460 |
+
// Flag & 0x0010 = UseMarkFilteringSet
|
2461 |
+
if ($flag & 0x0010) {
|
2462 |
+
die("This font ".$this->fontkey." contains MarkGlyphSets");
|
2463 |
+
$str = "Mark Glyph Set: ";
|
2464 |
+
$str .= $this->MarkGlyphSets[$MarkFilteringSet];
|
2465 |
+
}
|
2466 |
+
|
2467 |
+
// If Ignore Marks set, supercedes any above
|
2468 |
+
// Flag & 0x0008 = Ignore Marks
|
2469 |
+
if (($flag & 0x0008) == 0x0008) {
|
2470 |
+
$ignoreflag = 8;
|
2471 |
+
//$str = $this->GlyphClassMarks;
|
2472 |
+
$str = "Mark Glyphs ";
|
2473 |
+
}
|
2474 |
+
|
2475 |
+
// Flag & 0x0004 = Ignore Ligatures
|
2476 |
+
if (($flag & 0x0004) == 0x0004) {
|
2477 |
+
$ignoreflag += 4;
|
2478 |
+
if ($str) { $str .= "|"; }
|
2479 |
+
//$str .= $this->GlyphClassLigatures;
|
2480 |
+
$str .= "Ligature Glyphs ";
|
2481 |
+
}
|
2482 |
+
// Flag & 0x0002 = Ignore BaseGlyphs
|
2483 |
+
if (($flag & 0x0002) == 0x0002) {
|
2484 |
+
$ignoreflag += 2;
|
2485 |
+
if ($str) { $str .= "|"; }
|
2486 |
+
//$str .= $this->GlyphClassBases;
|
2487 |
+
$str .= "Base Glyphs ";
|
2488 |
+
}
|
2489 |
+
if ($str) {
|
2490 |
+
return $str;
|
2491 |
+
}
|
2492 |
+
else return "";
|
2493 |
+
}
|
2494 |
+
|
2495 |
+
// GSUB Patterns
|
2496 |
+
|
2497 |
+
/*
|
2498 |
+
BACKTRACK INPUT LOOKAHEAD
|
2499 |
+
================================== ================== ==================================
|
2500 |
+
(FEEB|FEEC)(ign) �(FD12|FD13)(ign) �(0612)�(ign) (0613)�(ign) (FD12|FD13)�(ign) (FEEB|FEEC)
|
2501 |
+
---------------- ---------------- ----- ------------ --------------- ---------------
|
2502 |
+
Backtrack 1 Backtrack 2 Input 1 Input 2 Lookahead 1 Lookahead 2
|
2503 |
+
-------- --- --------- --- ---- --- ---- --- --------- --- -------
|
2504 |
+
\${1} \${2} \${3} \${4} \${5+} \${6+} \${7+} \${8+}
|
2505 |
+
|
2506 |
+
nBacktrack = 2 nInput = 2 nLookahead = 2
|
2507 |
+
|
2508 |
+
nBsubs = 2xnBack nIsubs = (nBsubs+) nLsubs = (nBsubs+nIsubs+) 2xnLookahead
|
2509 |
+
"\${1}\${2} " (nInput*2)-1 "\${5+} \${6+}"
|
2510 |
+
"REPL"
|
2511 |
+
|
2512 |
+
�\${1}\${2} �\${3}\${4} �REPL�\${5+} \${6+}�\${7+} \${8+}�
|
2513 |
+
|
2514 |
+
|
2515 |
+
INPUT nInput = 5
|
2516 |
+
============================================================
|
2517 |
+
�(0612)�(ign) (0613)�(ign) (0614)�(ign) (0615)�(ign) (0615)�
|
2518 |
+
\${1} \${2} \${3} \${4} \${5} \${6} \${7} \${8} \${9} (All backreference numbers are + nBsubs)
|
2519 |
+
----- ------------ ------------ ------------ ------------
|
2520 |
+
Input 1 Input 2 Input 3 Input 4 Input 5
|
2521 |
+
|
2522 |
+
A====== SequenceIndex=1 ; Lookup match nGlyphs=1
|
2523 |
+
B=================== SequenceIndex=1 ; Lookup match nGlyphs=2
|
2524 |
+
C=============================== SequenceIndex=1 ; Lookup match nGlyphs=3
|
2525 |
+
D======================= SequenceIndex=2 ; Lookup match nGlyphs=2
|
2526 |
+
E===================================== SequenceIndex=2 ; Lookup match nGlyphs=3
|
2527 |
+
F====================== SequenceIndex=4 ; Lookup match nGlyphs=2
|
2528 |
+
|
2529 |
+
All backreference numbers are + nBsubs
|
2530 |
+
A - "REPL\${2} \${3}\${4} \${5}\${6} \${7}\${8} \${9}"
|
2531 |
+
B - "REPL\${2}\${4} \${5}\${6} \${7}\${8} \${9}"
|
2532 |
+
C - "REPL\${2}\${4}\${6} \${7}\${8} \${9}"
|
2533 |
+
D - "\${1} REPL\${2}\${4}\${6} \${7}\${8} \${9}"
|
2534 |
+
E - "\${1} REPL\${2}\${4}\${6}\${8} \${9}"
|
2535 |
+
F - "\${1}\${2} \${3}\${4} \${5} REPL\${6}\${8}"
|
2536 |
+
*/
|
2537 |
+
|
2538 |
+
function _makeGSUBcontextInputMatch($inputGlyphs, $ignore, $lookupGlyphs, $seqIndex) {
|
2539 |
+
// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
|
2540 |
+
// Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
|
2541 |
+
// $inputGlyphs = array of glyphs(glyphstrings) making up Input sequence in Context
|
2542 |
+
// $lookupGlyphs = array of glyphs (single Glyphs) making up Lookup Input sequence
|
2543 |
+
$mLen = count($lookupGlyphs); // nGlyphs in the secondary Lookup match
|
2544 |
+
$nInput = count($inputGlyphs); // nGlyphs in the Primary Input sequence
|
2545 |
+
$str = "";
|
2546 |
+
for($i=0;$i<$nInput;$i++) {
|
2547 |
+
if ($i>0) { $str .= $ignore." "; }
|
2548 |
+
if ($i>=$seqIndex && $i<($seqIndex+$mLen)) { $str .= "".$lookupGlyphs[($i-$seqIndex)].""; }
|
2549 |
+
else { $str .= "".$inputGlyphs[($i)].""; }
|
2550 |
+
}
|
2551 |
+
return $str;
|
2552 |
+
}
|
2553 |
+
|
2554 |
+
function _makeGSUBinputMatch($inputGlyphs, $ignore) {
|
2555 |
+
// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
|
2556 |
+
// Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
|
2557 |
+
// $inputGlyphs = array of glyphs(glyphstrings) making up Input sequence in Context
|
2558 |
+
// $lookupGlyphs = array of glyphs making up Lookup Input sequence - if applicable
|
2559 |
+
$str = "";
|
2560 |
+
for($i=1;$i<=count($inputGlyphs);$i++) {
|
2561 |
+
if ($i>1) { $str .= $ignore." "; }
|
2562 |
+
$str .= "".$inputGlyphs[($i-1)]."";
|
2563 |
+
}
|
2564 |
+
return $str;
|
2565 |
+
}
|
2566 |
+
|
2567 |
+
function _makeGSUBbacktrackMatch($backtrackGlyphs, $ignore) {
|
2568 |
+
// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
|
2569 |
+
// Returns e.g. �(FEEB|FEEC)(ignore) �(FD12|FD13)(ignore) �
|
2570 |
+
// $backtrackGlyphs = array of glyphstrings making up Backtrack sequence
|
2571 |
+
// 3 2 1 0
|
2572 |
+
// each item being e.g. E0AD|E0AF|F1FD
|
2573 |
+
$str = "";
|
2574 |
+
for($i=(count($backtrackGlyphs)-1);$i>=0;$i--) {
|
2575 |
+
$str .= "".$backtrackGlyphs[$i]." ".$ignore." ";
|
2576 |
+
}
|
2577 |
+
return $str;
|
2578 |
+
}
|
2579 |
+
|
2580 |
+
function _makeGSUBlookaheadMatch($lookaheadGlyphs, $ignore) {
|
2581 |
+
// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
|
2582 |
+
// Returns e.g. �(ignore) (FD12|FD13)�(ignore) (FEEB|FEEC)�
|
2583 |
+
// $lookaheadGlyphs = array of glyphstrings making up Lookahead sequence
|
2584 |
+
// 0 1 2 3
|
2585 |
+
// each item being e.g. E0AD|E0AF|F1FD
|
2586 |
+
$str = "";
|
2587 |
+
for($i=0;$i<count($lookaheadGlyphs);$i++) {
|
2588 |
+
$str .= $ignore." ".$lookaheadGlyphs[$i]."";
|
2589 |
+
}
|
2590 |
+
return $str;
|
2591 |
+
}
|
2592 |
+
|
2593 |
+
|
2594 |
+
|
2595 |
+
function _makeGSUBinputReplacement($nInput, $REPL, $ignore, $nBsubs, $mLen, $seqIndex) {
|
2596 |
+
// Returns e.g. "REPL\${6}\${8}" or "\${1}\${2} \${3} REPL\${4}\${6}\${8} \${9}"
|
2597 |
+
// $nInput nGlyphs in the Primary Input sequence
|
2598 |
+
// $REPL replacement glyphs from secondary lookup
|
2599 |
+
// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
|
2600 |
+
// $nBsubs Number of Backtrack substitutions (= 2x Number of Backtrack glyphs)
|
2601 |
+
// $mLen nGlyphs in the secondary Lookup match - if no secondary lookup, should=$nInput
|
2602 |
+
// $seqIndex Sequence Index to apply the secondary match
|
2603 |
+
if ($ignore=="()") { $ign = false; }
|
2604 |
+
else { $ign = true; }
|
2605 |
+
$str = "";
|
2606 |
+
if ($nInput == 1) { $str = $REPL; }
|
2607 |
+
else if ($nInput>1) {
|
2608 |
+
if ($mLen==$nInput) { // whole string replaced
|
2609 |
+
$str = $REPL;
|
2610 |
+
if ($ign) {
|
2611 |
+
// for every nInput over 1, add another replacement backreference, to move IGNORES after replacement
|
2612 |
+
for($x=2;$x<=$nInput;$x++) {
|
2613 |
+
$str .= '\\'.($nBsubs+(2*($x-1)));
|
2614 |
+
}
|
2615 |
+
}
|
2616 |
+
}
|
2617 |
+
else { // if only part of string replaced:
|
2618 |
+
for($x=1;$x<($seqIndex+1);$x++) {
|
2619 |
+
if ($x==1) { $str .= '\\'.($nBsubs + 1); }
|
2620 |
+
else {
|
2621 |
+
if ($ign) { $str .= '\\'.($nBsubs+(2*($x-1))); }
|
2622 |
+
$str .= ' \\'.($nBsubs+1+(2*($x-1)));
|
2623 |
+
}
|
2624 |
+
}
|
2625 |
+
if ($seqIndex>0) { $str .= " "; }
|
2626 |
+
$str .= $REPL;
|
2627 |
+
if ($ign) {
|
2628 |
+
for($x=(max(($seqIndex+1),2));$x<($seqIndex+1+$mLen);$x++) { // move IGNORES after replacement
|
2629 |
+
$str .= '\\'.($nBsubs+(2*($x-1)));
|
2630 |
+
}
|
2631 |
+
}
|
2632 |
+
for($x=($seqIndex+1+$mLen);$x<=$nInput;$x++) {
|
2633 |
+
if ($ign) { $str .= '\\'.($nBsubs+(2*($x-1))); }
|
2634 |
+
$str .= ' \\'.($nBsubs+1+(2*($x-1)));
|
2635 |
+
}
|
2636 |
+
}
|
2637 |
+
}
|
2638 |
+
return $str;
|
2639 |
+
}
|
2640 |
+
|
2641 |
+
|
2642 |
+
|
2643 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2644 |
+
function _getCoverage($convert2hex=true) {
|
2645 |
+
$g = array();
|
2646 |
+
$CoverageFormat= $this->read_ushort();
|
2647 |
+
if ($CoverageFormat == 1) {
|
2648 |
+
$CoverageGlyphCount= $this->read_ushort();
|
2649 |
+
for ($gid=0;$gid<$CoverageGlyphCount;$gid++) {
|
2650 |
+
$glyphID = $this->read_ushort();
|
2651 |
+
if ($convert2hex) { $g[] = unicode_hex($this->glyphToChar[$glyphID][0]); }
|
2652 |
+
else { $g[] = $glyphID; }
|
2653 |
+
}
|
2654 |
+
}
|
2655 |
+
if ($CoverageFormat == 2) {
|
2656 |
+
$RangeCount= $this->read_ushort();
|
2657 |
+
for ($r=0;$r<$RangeCount;$r++) {
|
2658 |
+
$start = $this->read_ushort();
|
2659 |
+
$end = $this->read_ushort();
|
2660 |
+
$StartCoverageIndex = $this->read_ushort(); // n/a
|
2661 |
+
for ($gid=$start;$gid<=$end;$gid++) {
|
2662 |
+
$glyphID = $gid;
|
2663 |
+
if ($convert2hex) { $g[] = unicode_hex($this->glyphToChar[$glyphID][0]); }
|
2664 |
+
else { $g[] = $glyphID; }
|
2665 |
+
}
|
2666 |
+
}
|
2667 |
+
}
|
2668 |
+
return $g;
|
2669 |
+
}
|
2670 |
+
|
2671 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2672 |
+
function _getClasses($offset) {
|
2673 |
+
$this->seek($offset);
|
2674 |
+
$ClassFormat = $this->read_ushort();
|
2675 |
+
$GlyphByClass = array();
|
2676 |
+
if ($ClassFormat == 1) {
|
2677 |
+
$StartGlyph = $this->read_ushort();
|
2678 |
+
$GlyphCount = $this->read_ushort();
|
2679 |
+
for ($i=0;$i<$GlyphCount;$i++) {
|
2680 |
+
$startGlyphID = $StartGlyph + $i;
|
2681 |
+
$endGlyphID = $StartGlyph + $i;
|
2682 |
+
$class = $this->read_ushort();
|
2683 |
+
for($g=$startGlyphID;$g<=$endGlyphID;$g++) {
|
2684 |
+
if ($this->glyphToChar[$g][0]) {
|
2685 |
+
$GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$g][0]);
|
2686 |
+
}
|
2687 |
+
}
|
2688 |
+
}
|
2689 |
+
}
|
2690 |
+
else if ($ClassFormat == 2) {
|
2691 |
+
$tableCount = $this->read_ushort();
|
2692 |
+
for ($i=0;$i<$tableCount;$i++) {
|
2693 |
+
$startGlyphID = $this->read_ushort();
|
2694 |
+
$endGlyphID = $this->read_ushort();
|
2695 |
+
$class = $this->read_ushort();
|
2696 |
+
for($g=$startGlyphID;$g<=$endGlyphID;$g++) {
|
2697 |
+
if ($this->glyphToChar[$g][0]) {
|
2698 |
+
$GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$g][0]);
|
2699 |
+
}
|
2700 |
+
}
|
2701 |
+
}
|
2702 |
+
}
|
2703 |
+
$gbc = array();
|
2704 |
+
foreach($GlyphByClass AS $class=>$garr) { $gbc[$class] = implode('|',$garr); }
|
2705 |
+
return $gbc;
|
2706 |
+
}
|
2707 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2708 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2709 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2710 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2711 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2712 |
+
function _getGPOStables() {
|
2713 |
+
///////////////////////////////////
|
2714 |
+
// GPOS - Glyph Positioning
|
2715 |
+
///////////////////////////////////
|
2716 |
+
if (isset($this->tables["GPOS"])) {
|
2717 |
+
$this->mpdf->WriteHTML('<h1>GPOS Tables</h1>');
|
2718 |
+
$ffeats = array();
|
2719 |
+
$gpos_offset = $this->seek_table("GPOS");
|
2720 |
+
$this->skip(4);
|
2721 |
+
$ScriptList_offset = $gpos_offset + $this->read_ushort();
|
2722 |
+
$FeatureList_offset = $gpos_offset + $this->read_ushort();
|
2723 |
+
$LookupList_offset = $gpos_offset + $this->read_ushort();
|
2724 |
+
|
2725 |
+
// ScriptList
|
2726 |
+
$this->seek($ScriptList_offset );
|
2727 |
+
$ScriptCount = $this->read_ushort();
|
2728 |
+
for ($i=0;$i<$ScriptCount;$i++) {
|
2729 |
+
$ScriptTag = $this->read_tag(); // = "beng", "deva" etc.
|
2730 |
+
$ScriptTableOffset = $this->read_ushort();
|
2731 |
+
$ffeats[$ScriptTag] = $ScriptList_offset + $ScriptTableOffset;
|
2732 |
+
}
|
2733 |
+
|
2734 |
+
// Script Table
|
2735 |
+
foreach($ffeats AS $t=>$o) {
|
2736 |
+
$ls = array();
|
2737 |
+
$this->seek($o);
|
2738 |
+
$DefLangSys_offset = $this->read_ushort();
|
2739 |
+
if ($DefLangSys_offset > 0) {
|
2740 |
+
$ls['DFLT'] = $DefLangSys_offset + $o;
|
2741 |
+
}
|
2742 |
+
$LangSysCount = $this->read_ushort();
|
2743 |
+
for ($i=0;$i<$LangSysCount;$i++) {
|
2744 |
+
$LangTag = $this->read_tag(); // =
|
2745 |
+
$LangTableOffset = $this->read_ushort();
|
2746 |
+
$ls[$LangTag] = $o + $LangTableOffset;
|
2747 |
+
}
|
2748 |
+
$ffeats[$t] = $ls;
|
2749 |
+
}
|
2750 |
+
|
2751 |
+
|
2752 |
+
// Get FeatureIndexList
|
2753 |
+
// LangSys Table - from first listed langsys
|
2754 |
+
foreach($ffeats AS $st=>$scripts) {
|
2755 |
+
foreach($scripts AS $t=>$o) {
|
2756 |
+
$FeatureIndex = array();
|
2757 |
+
$langsystable_offset = $o;
|
2758 |
+
$this->seek($langsystable_offset);
|
2759 |
+
$LookUpOrder = $this->read_ushort(); //==NULL
|
2760 |
+
$ReqFeatureIndex = $this->read_ushort();
|
2761 |
+
if ($ReqFeatureIndex != 0xFFFF) { $FeatureIndex[] = $ReqFeatureIndex ; }
|
2762 |
+
$FeatureCount = $this->read_ushort();
|
2763 |
+
for ($i=0;$i<$FeatureCount;$i++) {
|
2764 |
+
$FeatureIndex[] = $this->read_ushort(); // = index of feature
|
2765 |
+
}
|
2766 |
+
$ffeats[$st][$t] = $FeatureIndex;
|
2767 |
+
}
|
2768 |
+
}
|
2769 |
+
//print_r($ffeats); exit;
|
2770 |
+
|
2771 |
+
|
2772 |
+
// Feauture List => LookupListIndex es
|
2773 |
+
$this->seek($FeatureList_offset );
|
2774 |
+
$FeatureCount = $this->read_ushort();
|
2775 |
+
$Feature = array();
|
2776 |
+
for ($i=0;$i<$FeatureCount;$i++) {
|
2777 |
+
$Feature[$i] = array('tag' => $this->read_tag() );
|
2778 |
+
$Feature[$i]['offset'] = $FeatureList_offset + $this->read_ushort();
|
2779 |
+
}
|
2780 |
+
for ($i=0;$i<$FeatureCount;$i++) {
|
2781 |
+
$this->seek($Feature[$i]['offset']);
|
2782 |
+
$this->read_ushort(); // null
|
2783 |
+
$Feature[$i]['LookupCount'] = $Lookupcount = $this->read_ushort();
|
2784 |
+
$Feature[$i]['LookupListIndex'] = array();
|
2785 |
+
for ($c=0;$c<$Lookupcount;$c++) {
|
2786 |
+
$Feature[$i]['LookupListIndex'][] = $this->read_ushort();
|
2787 |
+
}
|
2788 |
+
}
|
2789 |
+
|
2790 |
+
|
2791 |
+
foreach($ffeats AS $st=>$scripts) {
|
2792 |
+
foreach($scripts AS $t=>$o) {
|
2793 |
+
$FeatureIndex = $ffeats[$st][$t];
|
2794 |
+
foreach($FeatureIndex AS $k=>$fi) {
|
2795 |
+
$ffeats[$st][$t][$k] = $Feature[$fi];
|
2796 |
+
}
|
2797 |
+
}
|
2798 |
+
}
|
2799 |
+
//print_r($ffeats); exit;
|
2800 |
+
//=====================================================================================
|
2801 |
+
$gpos = array();
|
2802 |
+
$GPOSScriptLang = array();
|
2803 |
+
foreach($ffeats AS $st=>$scripts) {
|
2804 |
+
foreach($scripts AS $t=>$langsys) {
|
2805 |
+
$lg = array();
|
2806 |
+
foreach($langsys AS $ft) {
|
2807 |
+
$lg[$ft['LookupListIndex'][0]] = $ft;
|
2808 |
+
}
|
2809 |
+
// list of Lookups in order they need to be run i.e. order listed in Lookup table
|
2810 |
+
ksort($lg);
|
2811 |
+
foreach($lg AS $ft) {
|
2812 |
+
$gpos[$st][$t][$ft['tag']] = $ft['LookupListIndex'];
|
2813 |
+
}
|
2814 |
+
if (!isset($GPOSScriptLang[$st])) { $GPOSScriptLang[$st] = ''; }
|
2815 |
+
$GPOSScriptLang[$st] .= $t.' ';
|
2816 |
+
}
|
2817 |
+
}
|
2818 |
+
if ($this->mode == 'summary') {
|
2819 |
+
$this->mpdf->WriteHTML('<h3>GPOS Scripts & Languages</h3>');
|
2820 |
+
$html = '';
|
2821 |
+
if (count($gpos)) {
|
2822 |
+
foreach ($gpos AS $st=>$g) {
|
2823 |
+
$html .= '<h5>'.$st.'</h5>';
|
2824 |
+
foreach ($g AS $l=>$t) {
|
2825 |
+
$html .= '<div><a href="font_dump_OTL.php?script='.$st.'&lang='.$l.'">'.$l.'</a></b>: ';
|
2826 |
+
foreach ($t AS $tag=>$o) {
|
2827 |
+
$html .= $tag.' ';
|
2828 |
+
}
|
2829 |
+
$html .= '</div>';
|
2830 |
+
}
|
2831 |
+
}
|
2832 |
+
}
|
2833 |
+
else {
|
2834 |
+
$html .= '<div>No entries in GPOS table.</div>';
|
2835 |
+
}
|
2836 |
+
$this->mpdf->WriteHTML($html);
|
2837 |
+
$this->mpdf->WriteHTML('</div>');
|
2838 |
+
return 0;
|
2839 |
+
}
|
2840 |
+
|
2841 |
+
|
2842 |
+
|
2843 |
+
//=====================================================================================
|
2844 |
+
// Get metadata and offsets for whole Lookup List table
|
2845 |
+
$this->seek($LookupList_offset );
|
2846 |
+
$LookupCount = $this->read_ushort();
|
2847 |
+
$Lookup = array();
|
2848 |
+
$Offsets = array();
|
2849 |
+
$SubtableCount = array();
|
2850 |
+
for ($i=0;$i<$LookupCount;$i++) {
|
2851 |
+
$Offsets[$i] = $LookupList_offset + $this->read_ushort();
|
2852 |
+
}
|
2853 |
+
for ($i=0;$i<$LookupCount;$i++) {
|
2854 |
+
$this->seek($Offsets[$i]);
|
2855 |
+
$Lookup[$i]['Type'] = $this->read_ushort();
|
2856 |
+
$Lookup[$i]['Flag'] = $flag = $this->read_ushort();
|
2857 |
+
$Lookup[$i]['SubtableCount'] = $SubtableCount[$i] = $this->read_ushort();
|
2858 |
+
for ($c=0;$c<$SubtableCount[$i] ;$c++) {
|
2859 |
+
$Lookup[$i]['Subtables'][$c] = $Offsets[$i] + $this->read_ushort();
|
2860 |
+
|
2861 |
+
}
|
2862 |
+
// MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure
|
2863 |
+
if (($flag & 0x0010) == 0x0010) {
|
2864 |
+
$Lookup[$i]['MarkFilteringSet'] = $this->read_ushort();
|
2865 |
+
}
|
2866 |
+
// else { $Lookup[$i]['MarkFilteringSet'] = ''; }
|
2867 |
+
|
2868 |
+
// Lookup Type 9: Extension
|
2869 |
+
if ($Lookup[$i]['Type'] == 9) {
|
2870 |
+
// Overwrites new offset (32-bit) for each subtable, and a new lookup Type
|
2871 |
+
for ($c=0;$c<$SubtableCount[$i] ;$c++) {
|
2872 |
+
$this->seek($Lookup[$i]['Subtables'][$c]);
|
2873 |
+
$ExtensionPosFormat = $this->read_ushort();
|
2874 |
+
$type = $this->read_ushort();
|
2875 |
+
$Lookup[$i]['Subtables'][$c] = $Lookup[$i]['Subtables'][$c] + $this->read_ulong();
|
2876 |
+
}
|
2877 |
+
$Lookup[$i]['Type'] = $type;
|
2878 |
+
}
|
2879 |
+
|
2880 |
+
}
|
2881 |
+
|
2882 |
+
|
2883 |
+
//=====================================================================================
|
2884 |
+
|
2885 |
+
$st = $this->mpdf->OTLscript;
|
2886 |
+
$t = $this->mpdf->OTLlang;
|
2887 |
+
$langsys = $gpos[$st][$t];
|
2888 |
+
|
2889 |
+
|
2890 |
+
$lul = array(); // array of LookupListIndexes
|
2891 |
+
$tags = array(); // corresponding array of feature tags e.g. 'ccmp'
|
2892 |
+
if (count($langsys)) {
|
2893 |
+
foreach($langsys AS $tag=>$ft) {
|
2894 |
+
foreach($ft AS $ll) {
|
2895 |
+
$lul[$ll] = $tag;
|
2896 |
+
}
|
2897 |
+
}
|
2898 |
+
}
|
2899 |
+
ksort($lul); // Order the Lookups in the order they are in the GUSB table, regardless of Feature order
|
2900 |
+
$this->_getGPOSarray($Lookup, $lul, $st);
|
2901 |
+
//print_r($lul); exit;
|
2902 |
+
|
2903 |
+
|
2904 |
+
return array($GPOSScriptLang, $gpos, $Lookup);
|
2905 |
+
|
2906 |
+
} // end if GPOS
|
2907 |
+
}
|
2908 |
+
|
2909 |
+
//////////////////////////////////////////////////////////////////////////////////
|
2910 |
+
|
2911 |
+
//=====================================================================================
|
2912 |
+
//=====================================================================================
|
2913 |
+
//=====================================================================================
|
2914 |
+
/////////////////////////////////////////////////////////////////////////////////////////
|
2915 |
+
// GPOS functions
|
2916 |
+
function _getGPOSarray(&$Lookup, $lul, $scripttag, $level=1, $lcoverage='', $exB='', $exL='') {
|
2917 |
+
// Process (3) LookupList for specific Script-LangSys
|
2918 |
+
$html = '';
|
2919 |
+
if ($level==1) { $html .= '<bookmark level="0" content="GPOS features">'; }
|
2920 |
+
foreach($lul AS $luli=>$tag) {
|
2921 |
+
$html .= '<div class="level'.$level.'">';
|
2922 |
+
$html .= '<h5 class="level'.$level.'">';
|
2923 |
+
if ($level==1) { $html .= '<bookmark level="1" content="'.$tag.' [#'.$luli.']">'; }
|
2924 |
+
$html .= 'Lookup #'.$luli.' [tag: <span style="color:#000066;">'.$tag.'</span>]</h5>';
|
2925 |
+
$ignore = $this->_getGSUBignoreString($Lookup[$luli]['Flag'], $Lookup[$luli]['MarkFilteringSet']);
|
2926 |
+
if ($ignore) { $html .= '<div class="ignore">Ignoring: '.$ignore.'</div> '; }
|
2927 |
+
|
2928 |
+
$Type = $Lookup[$luli]['Type'];
|
2929 |
+
$Flag = $Lookup[$luli]['Flag'];
|
2930 |
+
if (($Flag & 0x0001) == 1) { $dir = 'RTL'; }
|
2931 |
+
else { $dir = 'LTR'; }
|
2932 |
+
|
2933 |
+
for ($c=0;$c<$Lookup[$luli]['SubtableCount'] ;$c++) {
|
2934 |
+
$html .= '<div class="subtable">Subtable #'.$c;
|
2935 |
+
if ($level==1) { $html .= '<bookmark level="2" content="Subtable #'.$c.'">'; }
|
2936 |
+
$html .= '</div>';
|
2937 |
+
|
2938 |
+
// Lets start
|
2939 |
+
$subtable_offset = $Lookup[$luli]['Subtables'][$c];
|
2940 |
+
$this->seek($subtable_offset);
|
2941 |
+
$PosFormat = $this->read_ushort();
|
2942 |
+
|
2943 |
+
////////////////////////////////////////////////////////////////////////////////
|
2944 |
+
// LookupType 1: Single adjustment Adjust position of a single glyph (e.g. SmallCaps/Sups/Subs)
|
2945 |
+
////////////////////////////////////////////////////////////////////////////////
|
2946 |
+
if ($Lookup[$luli]['Type'] == 1) {
|
2947 |
+
$html .= '<div class="lookuptype">LookupType 1: Single adjustment [Format '.$PosFormat.']</div>';
|
2948 |
+
//===========
|
2949 |
+
// Format 1:
|
2950 |
+
//===========
|
2951 |
+
if ($PosFormat==1) {
|
2952 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
2953 |
+
$ValueFormat = $this->read_ushort();
|
2954 |
+
$Value = $this->_getValueRecord($ValueFormat);
|
2955 |
+
|
2956 |
+
$this->seek($Coverage);
|
2957 |
+
$glyphs = $this->_getCoverage(); // Array of Hex Glyphs
|
2958 |
+
for($g=0;$g<count($glyphs);$g++) {
|
2959 |
+
if ($level==2 && strpos($lcoverage, $glyphs[$g])===false) { continue; }
|
2960 |
+
|
2961 |
+
$html .= '<div class="substitution">';
|
2962 |
+
$html .= '<span class="unicode">'.$this->formatUni($glyphs[$g]).' </span> ';
|
2963 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
2964 |
+
$html .= '<span class="unchanged"> '.$this->formatEntity($glyphs[$g]).'</span>';
|
2965 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
2966 |
+
$html .= ' » » ';
|
2967 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
2968 |
+
$html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;"> '.$this->formatEntity($glyphs[$g]).'</span>';
|
2969 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
2970 |
+
$html .= ' <span class="unicode">';
|
2971 |
+
if ($Value['XPlacement']) { $html .= ' Xpl: '.$Value['XPlacement'].';'; }
|
2972 |
+
if ($Value['YPlacement']) { $html .= ' YPl: '.$Value['YPlacement'].';'; }
|
2973 |
+
if ($Value['XAdvance']) { $html .= ' Xadv: '.$Value['XAdvance']; }
|
2974 |
+
$html .= '</span>';
|
2975 |
+
$html .= '</div>';
|
2976 |
+
}
|
2977 |
+
|
2978 |
+
}
|
2979 |
+
//===========
|
2980 |
+
// Format 2:
|
2981 |
+
//===========
|
2982 |
+
else if ($PosFormat==2) {
|
2983 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
2984 |
+
$ValueFormat = $this->read_ushort();
|
2985 |
+
$ValueCount = $this->read_ushort();
|
2986 |
+
$Values = array();
|
2987 |
+
for($v=0;$v<$ValueCount;$v++) {
|
2988 |
+
$Values[] = $this->_getValueRecord($ValueFormat);
|
2989 |
+
}
|
2990 |
+
|
2991 |
+
$this->seek($Coverage);
|
2992 |
+
$glyphs = $this->_getCoverage(); // Array of Hex Glyphs
|
2993 |
+
|
2994 |
+
for($g=0;$g<count($glyphs);$g++) {
|
2995 |
+
if ($level==2 && strpos($lcoverage, $glyphs[$g])===false) { continue; }
|
2996 |
+
$Value = $Values[$g];
|
2997 |
+
|
2998 |
+
$html .= '<div class="substitution">';
|
2999 |
+
$html .= '<span class="unicode">'.$this->formatUni($glyphs[$g]).' </span> ';
|
3000 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
3001 |
+
$html .= '<span class="unchanged"> '.$this->formatEntity($glyphs[$g]).'</span>';
|
3002 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
3003 |
+
$html .= ' » » ';
|
3004 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
3005 |
+
$html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;"> '.$this->formatEntity($glyphs[$g]).'</span>';
|
3006 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
3007 |
+
$html .= ' <span class="unicode">';
|
3008 |
+
if ($Value['XPlacement']) { $html .= ' Xpl: '.$Value['XPlacement'].';'; }
|
3009 |
+
if ($Value['YPlacement']) { $html .= ' YPl: '.$Value['YPlacement'].';'; }
|
3010 |
+
if ($Value['XAdvance']) { $html .= ' Xadv: '.$Value['XAdvance']; }
|
3011 |
+
$html .= '</span>';
|
3012 |
+
$html .= '</div>';
|
3013 |
+
}
|
3014 |
+
}
|
3015 |
+
|
3016 |
+
|
3017 |
+
|
3018 |
+
}
|
3019 |
+
////////////////////////////////////////////////////////////////////////////////
|
3020 |
+
// LookupType 2: Pair adjustment Adjust position of a pair of glyphs (Kerning)
|
3021 |
+
////////////////////////////////////////////////////////////////////////////////
|
3022 |
+
else if ($Lookup[$luli]['Type'] == 2) {
|
3023 |
+
$html .= '<div class="lookuptype">LookupType 2: Pair adjustment e.g. Kerning [Format '.$PosFormat.']</div>';
|
3024 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
3025 |
+
$ValueFormat1 = $this->read_ushort();
|
3026 |
+
$ValueFormat2 = $this->read_ushort();
|
3027 |
+
//===========
|
3028 |
+
// Format 1:
|
3029 |
+
//===========
|
3030 |
+
if ($PosFormat==1) {
|
3031 |
+
$PairSetCount = $this->read_ushort();
|
3032 |
+
$PairSetOffset = array();
|
3033 |
+
for($p=0;$p<$PairSetCount;$p++) {
|
3034 |
+
$PairSetOffset[] = $subtable_offset + $this->read_ushort();
|
3035 |
+
}
|
3036 |
+
$this->seek($Coverage);
|
3037 |
+
$glyphs = $this->_getCoverage(); // Array of Hex Glyphs
|
3038 |
+
for($p=0;$p<$PairSetCount;$p++) {
|
3039 |
+
if ($level==2 && strpos($lcoverage, $glyphs[$p])===false) { continue; }
|
3040 |
+
$this->seek($PairSetOffset[$p]);
|
3041 |
+
// First Glyph = $glyphs[$p]
|
3042 |
+
|
3043 |
+
// Takes too long e.g. Calibri font - just list kerning pairs with this:
|
3044 |
+
$html .= '<div class="glyphs">';
|
3045 |
+
$html .= '<span class="unchanged"> '.$this->formatEntity($glyphs[$p]).' </span>';
|
3046 |
+
|
3047 |
+
//PairSet table
|
3048 |
+
$PairValueCount = $this->read_ushort();
|
3049 |
+
for($pv=0;$pv<$PairValueCount;$pv++) {
|
3050 |
+
//PairValueRecord
|
3051 |
+
$gid = $this->read_ushort();
|
3052 |
+
$SecondGlyph = unicode_hex($this->glyphToChar[$gid][0]);
|
3053 |
+
$Value1 = $this->_getValueRecord($ValueFormat1);
|
3054 |
+
$Value2 = $this->_getValueRecord($ValueFormat2);
|
3055 |
+
|
3056 |
+
// If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 to take
|
3057 |
+
// account of direction. mPDF does not need the XPlacement adjustment
|
3058 |
+
if ($dir=='RTL' && $Value1['XPlacement']) {
|
3059 |
+
$Value1['XPlacement'] -= $Value1['XAdvance'];
|
3060 |
+
}
|
3061 |
+
|
3062 |
+
if($ValueFormat2) {
|
3063 |
+
// If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 to take
|
3064 |
+
// account of direction. mPDF does not need the XPlacement adjustment
|
3065 |
+
if ($dir=='RTL' && $Value2['XPlacement'] && $Value2['XAdvance']) {
|
3066 |
+
$Value2['XPlacement'] -= $Value2['XAdvance'];
|
3067 |
+
}
|
3068 |
+
}
|
3069 |
+
|
3070 |
+
$html .= ' '.$this->formatEntity($SecondGlyph).' ';
|
3071 |
+
|
3072 |
+
/*
|
3073 |
+
$html .= '<div class="substitution">';
|
3074 |
+
$html .= '<span class="unicode">'.$this->formatUni($glyphs[$p]).' </span> ';
|
3075 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
3076 |
+
$html .= '<span class="unchanged"> '.$this->formatEntity($glyphs[$p]).$this->formatEntity($SecondGlyph).'</span>';
|
3077 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
3078 |
+
$html .= ' » » ';
|
3079 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
3080 |
+
$html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;"> '.$this->formatEntity($glyphs[$p]).$this->formatEntity($SecondGlyph).'</span>';
|
3081 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
3082 |
+
$html .= ' <span class="unicode">';
|
3083 |
+
if ($Value1['XPlacement']) { $html .= ' Xpl[1]: '.$Value1['XPlacement'].';'; }
|
3084 |
+
if ($Value1['YPlacement']) { $html .= ' YPl[1]: '.$Value1['YPlacement'].';'; }
|
3085 |
+
if ($Value1['XAdvance']) { $html .= ' Xadv[1]: '.$Value1['XAdvance']; }
|
3086 |
+
if ($Value2['XPlacement']) { $html .= ' Xpl[2]: '.$Value2['XPlacement'].';'; }
|
3087 |
+
if ($Value2['YPlacement']) { $html .= ' YPl[2]: '.$Value2['YPlacement'].';'; }
|
3088 |
+
if ($Value2['XAdvance']) { $html .= ' Xadv[2]: '.$Value2['XAdvance']; }
|
3089 |
+
$html .= '</span>';
|
3090 |
+
$html .= '</div>';
|
3091 |
+
*/
|
3092 |
+
|
3093 |
+
}
|
3094 |
+
$html .= '</div>';
|
3095 |
+
}
|
3096 |
+
}
|
3097 |
+
//===========
|
3098 |
+
// Format 2:
|
3099 |
+
//===========
|
3100 |
+
else if ($PosFormat==2) {
|
3101 |
+
$ClassDef1 = $subtable_offset + $this->read_ushort();
|
3102 |
+
$ClassDef2 = $subtable_offset + $this->read_ushort();
|
3103 |
+
$Class1Count = $this->read_ushort();
|
3104 |
+
$Class2Count = $this->read_ushort();
|
3105 |
+
|
3106 |
+
$sizeOfPair = ( 2*$this->count_bits($ValueFormat1) ) + ( 2*$this->count_bits($ValueFormat2) );
|
3107 |
+
$sizeOfValueRecords = $Class1Count * $Class2Count * $sizeOfPair;
|
3108 |
+
|
3109 |
+
|
3110 |
+
// NB Class1Count includes Class 0 even though it is not defined by $ClassDef1
|
3111 |
+
// i.e. Class1Count = 5; Class1 will contain array(indices 1-4);
|
3112 |
+
$Class1 = $this->_getClassDefinitionTable($ClassDef1);
|
3113 |
+
$Class2 = $this->_getClassDefinitionTable($ClassDef2);
|
3114 |
+
|
3115 |
+
$this->seek($subtable_offset + 16);
|
3116 |
+
|
3117 |
+
for($i=0;$i<$Class1Count;$i++) {
|
3118 |
+
for($j=0;$j<$Class2Count;$j++) {
|
3119 |
+
$Value1 = $this->_getValueRecord($ValueFormat1);
|
3120 |
+
$Value2 = $this->_getValueRecord($ValueFormat2);
|
3121 |
+
|
3122 |
+
// If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180
|
3123 |
+
// of direction. mPDF does not need the XPlacement adjustment
|
3124 |
+
if ($dir=='RTL' && $Value1['XPlacement'] && $Value1['XAdvance']) {
|
3125 |
+
$Value1['XPlacement'] -= $Value1['XAdvance'];
|
3126 |
+
}
|
3127 |
+
if($ValueFormat2) {
|
3128 |
+
if ($dir=='RTL' && $Value2['XPlacement'] && $Value2['XAdvance']) {
|
3129 |
+
$Value2['XPlacement'] -= $Value2['XAdvance'];
|
3130 |
+
}
|
3131 |
+
}
|
3132 |
+
|
3133 |
+
|
3134 |
+
for($c1=0;$c1<count($Class1[$i]);$c1++) {
|
3135 |
+
|
3136 |
+
$FirstGlyph = $Class1[$i][$c1];
|
3137 |
+
if ($level==2 && strpos($lcoverage, $FirstGlyph)===false) {
|
3138 |
+
continue;
|
3139 |
+
}
|
3140 |
+
|
3141 |
+
|
3142 |
+
for($c2=0;$c2<count($Class2[$j]);$c2++) {
|
3143 |
+
$SecondGlyph = $Class2[$j][$c2];
|
3144 |
+
|
3145 |
+
|
3146 |
+
if (!$Value1['XPlacement'] && !$Value1['YPlacement'] && !$Value1['XAdvance'] && !$Value2['XPlacement'] && !$Value2['YPlacement'] && !$Value2['XAdvance']) { continue; }
|
3147 |
+
|
3148 |
+
|
3149 |
+
$html .= '<div class="substitution">';
|
3150 |
+
$html .= '<span class="unicode">'.$this->formatUni($FirstGlyph).' </span> ';
|
3151 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
3152 |
+
$html .= '<span class="unchanged"> '.$this->formatEntity($FirstGlyph).$this->formatEntity($SecondGlyph).'</span>';
|
3153 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
3154 |
+
$html .= ' » » ';
|
3155 |
+
if ($level==2 && $exB) { $html .= $exB; }
|
3156 |
+
$html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;"> '.$this->formatEntity($FirstGlyph).$this->formatEntity($SecondGlyph).'</span>';
|
3157 |
+
if ($level==2 && $exL) { $html .= $exL; }
|
3158 |
+
$html .= ' <span class="unicode">';
|
3159 |
+
if ($Value1['XPlacement']) { $html .= ' Xpl[1]: '.$Value1['XPlacement'].';'; }
|
3160 |
+
if ($Value1['YPlacement']) { $html .= ' YPl[1]: '.$Value1['YPlacement'].';'; }
|
3161 |
+
if ($Value1['XAdvance']) { $html .= ' Xadv[1]: '.$Value1['XAdvance']; }
|
3162 |
+
if ($Value2['XPlacement']) { $html .= ' Xpl[2]: '.$Value2['XPlacement'].';'; }
|
3163 |
+
if ($Value2['YPlacement']) { $html .= ' YPl[2]: '.$Value2['YPlacement'].';'; }
|
3164 |
+
if ($Value2['XAdvance']) { $html .= ' Xadv[2]: '.$Value2['XAdvance']; }
|
3165 |
+
$html .= '</span>';
|
3166 |
+
$html .= '</div>';
|
3167 |
+
|
3168 |
+
}
|
3169 |
+
}
|
3170 |
+
}
|
3171 |
+
|
3172 |
+
}
|
3173 |
+
}
|
3174 |
+
}
|
3175 |
+
////////////////////////////////////////////////////////////////////////////////
|
3176 |
+
// LookupType 3: Cursive attachment Attach cursive glyphs
|
3177 |
+
////////////////////////////////////////////////////////////////////////////////
|
3178 |
+
else if ($Lookup[$luli]['Type'] == 3) {
|
3179 |
+
$html .= '<div class="lookuptype">LookupType 3: Cursive attachment </div>';
|
3180 |
+
$Coverage = $subtable_offset + $this->read_ushort();
|
3181 |
+
$EntryExitCount = $this->read_ushort();
|
3182 |
+
$EntryAnchors = array();
|
3183 |
+
$ExitAnchors = array();
|
3184 |
+
for($i=0;$i<$EntryExitCount;$i++) {
|
3185 |
+
$EntryAnchors[$i] = $this->read_ushort();
|
3186 |
+
$ExitAnchors[$i] = $this->read_ushort();
|
3187 |
+
}
|
3188 |
+
|
3189 |
+
$this->seek($Coverage);
|
3190 |
+
$Glyphs = $this->_getCoverage();
|
3191 |
+
for($i=0;$i<$EntryExitCount;$i++) {
|
3192 |
+
// Need default XAdvance for glyph
|
3193 |
+
$pdfWidth = $this->mpdf->_getCharWidth($this->mpdf->fonts[$this->fontkey]['cw'], hexdec($Glyphs[$i]));
|
3194 |
+
$EntryAnchor = $EntryAnchors[$i] ;
|
3195 |
+
$ExitAnchor = $ExitAnchors[$i] ;
|
3196 |
+
$html .= '<div class="glyphs">';
|
3197 |
+
$html .= '<span class="unchanged">'.$this->formatEntity($Glyphs[$i]).' </span> ';
|
3198 |
+
$html .= '<span class="unicode"> '.$this->formatUni($Glyphs[$i]).' => ';
|
3199 |
+
|
3200 |
+
if ($EntryAnchor != 0) {
|
3201 |
+
$EntryAnchor += $subtable_offset;
|
3202 |
+
list($x,$y) = $this->_getAnchorTable($EntryAnchor);
|
3203 |
+
if ($dir == 'RTL') {
|
3204 |
+
if (round($pdfWidth) == round($x * 1000/ $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']) ) {
|
3205 |
+
$x = 0;
|
3206 |
+
}
|
3207 |
+
else { $x = $x - ($pdfWidth * $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']/1000); }
|
3208 |
+
}
|
3209 |
+
$html .= " Entry X: ".$x." Y: ".$y."; ";
|
3210 |
+
}
|
3211 |
+
if ($ExitAnchor != 0) {
|
3212 |
+
$ExitAnchor += $subtable_offset;
|
3213 |
+
list($x,$y) = $this->_getAnchorTable($ExitAnchor);
|
3214 |
+
if ($dir == 'LTR') {
|
3215 |
+
if (round($pdfWidth) == round($x * 1000/ $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']) ) {
|
3216 |
+
$x = 0;
|
3217 |
+
}
|
3218 |
+
else { $x = $x - ($pdfWidth * $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']/1000); }
|
3219 |
+
}
|
3220 |
+
$html .= " Exit X: ".$x." Y: ".$y."; ";
|
3221 |
+
}
|
3222 |
+
|
3223 |
+
|
3224 |
+
$html .= '</span></div>';
|
3225 |
+
}
|
3226 |
+
|
3227 |
+
}
|
3228 |
+
////////////////////////////////////////////////////////////////////////////////
|
3229 |
+
// LookupType 4: MarkToBase attachment Attach a combining mark to a base glyph
|
3230 |
+
////////////////////////////////////////////////////////////////////////////////
|
3231 |
+
else if ($Lookup[$luli]['Type'] == 4) {
|
3232 |
+
$html .= '<div class="lookuptype">LookupType 4: MarkToBase attachment </div>';
|
3233 |
+
$MarkCoverage = $subtable_offset + $this->read_ushort();
|
3234 |
+
$BaseCoverage = $subtable_offset + $this->read_ushort();
|
3235 |
+
|
3236 |
+
$this->seek($MarkCoverage);
|
3237 |
+
$MarkGlyphs = $this->_getCoverage();
|
3238 |
+
|
3239 |
+
$this->seek($BaseCoverage);
|
3240 |
+
$BaseGlyphs = $this->_getCoverage();
|
3241 |
+
|
3242 |
+
$firstMark = '';
|
3243 |
+
$html .= '<div class="glyphs">Marks: ';
|
3244 |
+
for($i=0;$i<count($MarkGlyphs);$i++) {
|
3245 |
+
if ($level==2 && strpos($lcoverage, $MarkGlyphs[$i])===false) { continue; }
|
3246 |
+
else {
|
3247 |
+
if (!$firstMark) { $firstMark = $MarkGlyphs[$i]; }
|
3248 |
+
}
|
3249 |
+
$html .= ' '.$this->formatEntity($MarkGlyphs[$i]).' ';
|
3250 |
+
}
|
3251 |
+
$html .= '</div>';
|
3252 |
+
if (!$firstMark) { return; }
|
3253 |
+
|
3254 |
+
$html .= '<div class="glyphs">Bases: ';
|
3255 |
+
for($j=0;$j<count($BaseGlyphs);$j++) {
|
3256 |
+
$html .= ' '.$this->formatEntity($BaseGlyphs[$j]).' ';
|
3257 |
+
}
|
3258 |
+
$html .= '</div>';
|
3259 |
+
|
3260 |
+
// Example
|
3261 |
+
$html .= '<div class="glyphs" style="font-feature-settings:\''.$tag.'\' 1;">Example(s): ';
|
3262 |
+
for ($j=0;$j<min(count($BaseGlyphs),20);$j++) {
|
3263 |
+
$html .= ' '.$this->formatEntity($BaseGlyphs[$j]).$this->formatEntity($firstMark,true).' ';
|
3264 |
+
}
|
3265 |
+
$html .= '</div>';
|
3266 |
+
|
3267 |
+
|
3268 |
+
}
|
3269 |
+
////////////////////////////////////////////////////////////////////////////////
|
3270 |
+
// LookupType 5: MarkToLigature attachment Attach a combining mark to a ligature
|
3271 |
+
////////////////////////////////////////////////////////////////////////////////
|
3272 |
+
else if ($Lookup[$luli]['Type'] == 5) {
|
3273 |
+
$html .= '<div class="lookuptype">LookupType 5: MarkToLigature attachment </div>';
|
3274 |
+
$MarkCoverage = $subtable_offset + $this->read_ushort();
|
3275 |
+
//$MarkCoverage is already set in $lcoverage 00065|00073 etc
|
3276 |
+
$LigatureCoverage = $subtable_offset + $this->read_ushort();
|
3277 |
+
$ClassCount = $this->read_ushort(); // Number of classes defined for marks = Number of mark glyphs in the MarkCoverage table
|
3278 |
+
$MarkArray = $subtable_offset + $this->read_ushort(); // Offset to MarkArray table
|
3279 |
+
$LigatureArray = $subtable_offset + $this->read_ushort(); // Offset to LigatureArray table
|
3280 |
+
|
3281 |
+
$this->seek($MarkCoverage);
|
3282 |
+
$MarkGlyphs = $this->_getCoverage();
|
3283 |
+
$this->seek($LigatureCoverage);
|
3284 |
+
$LigatureGlyphs = $this->_getCoverage();
|
3285 |
+
|
3286 |
+
$firstMark = '';
|
3287 |
+
$html .= '<div class="glyphs">Marks: <span class="unchanged">';
|
3288 |
+
$MarkRecord = array();
|
3289 |
+
for ($i=0;$i<count($MarkGlyphs);$i++) {
|
3290 |
+
if ($level==2 && strpos($lcoverage, $MarkGlyphs[$i])===false) { continue; }
|
3291 |
+
else {
|
3292 |
+
if (!$firstMark) { $firstMark = $MarkGlyphs[$i]; }
|
3293 |
+
}
|
3294 |
+
// Get the relevant MarkRecord
|
3295 |
+
$MarkRecord[$i] = $this->_getMarkRecord($MarkArray, $i);
|
3296 |
+
//Mark Class is = $MarkRecord[$i]['Class']
|
3297 |
+
$html .= ' '.$this->formatEntity($MarkGlyphs[$i]).' ';
|
3298 |
+
}
|
3299 |
+
$html .= '</span></div>';
|
3300 |
+
if (!$firstMark) { return; }
|
3301 |
+
|
3302 |
+
$this->seek($LigatureArray);
|
3303 |
+
$LigatureCount = $this->read_ushort();
|
3304 |
+
$LigatureAttach = array();
|
3305 |
+
$html .= '<div class="glyphs">Ligatures: <span class="unchanged">';
|
3306 |
+
for ($j=0;$j<count($LigatureGlyphs);$j++) {
|
3307 |
+
// Get the relevant LigatureRecord
|
3308 |
+
$LigatureAttach[$j] = $LigatureArray + $this->read_ushort();
|
3309 |
+
$html .= ' '.$this->formatEntity($LigatureGlyphs[$j]).' ';
|
3310 |
+
}
|
3311 |
+
$html .= '</span></div>';
|
3312 |
+
|
3313 |
+
/*
|
3314 |
+
for ($i=0;$i<count($MarkGlyphs);$i++) {
|
3315 |
+
$html .= '<div class="glyphs">';
|
3316 |
+
$html .= '<span class="unchanged">'.$this->formatEntity($MarkGlyphs[$i]).'</span>';
|
3317 |
+
|
3318 |
+
for ($j=0;$j<count($LigatureGlyphs);$j++) {
|
3319 |
+
$this->seek($LigatureAttach[$j]);
|
3320 |
+
$ComponentCount = $this->read_ushort();
|
3321 |
+
$html .= '<span class="unchanged">'.$this->formatEntity($LigatureGlyphs[$j]).'</span>';
|
3322 |
+
$offsets = array();
|
3323 |
+
for ($comp=0;$comp<$ComponentCount;$comp++) {
|
3324 |
+
// ComponentRecords
|
3325 |
+
for ($class=0;$class<$ClassCount;$class++) {
|
3326 |
+
$offset = $this->read_ushort();
|
3327 |
+
if ($offset!= 0 && $class == $MarkRecord[$i]['Class']) {
|
3328 |
+
|
3329 |
+
$html .= ' ['.$comp.'] ';
|
3330 |
+
|
3331 |
+
}
|
3332 |
+
}
|
3333 |
+
}
|
3334 |
+
}
|
3335 |
+
$html .= '</span></div>';
|
3336 |
+
}
|
3337 |
+
*/
|
3338 |
+
|
3339 |
+
|
3340 |
+
}
|
3341 |
+
////////////////////////////////////////////////////////////////////////////////
|
3342 |
+
// LookupType 6: MarkToMark attachment Attach a combining mark to another mark
|
3343 |
+
////////////////////////////////////////////////////////////////////////////////
|
3344 |
+
else if ($Lookup[$luli]['Type'] == 6) {
|
3345 |
+
$html .= '<div class="lookuptype">LookupType 6: MarkToMark attachment </div>';
|
3346 |
+
$Mark1Coverage = $subtable_offset + $this->read_ushort(); // Combining Mark
|
3347 |
+
//$Mark1Coverage is already set in $LuCoverage 0065|0073 etc
|
3348 |
+
$Mark2Coverage = $subtable_offset + $this->read_ushort(); // Base Mark
|
3349 |
+
$ClassCount = $this->read_ushort(); // Number of classes defined for marks = No. of Combining mark1 glyphs in the MarkCoverage table
|
3350 |
+
$this->seek($Mark1Coverage);
|
3351 |
+
$Mark1Glyphs = $this->_getCoverage();
|
3352 |
+
$this->seek($Mark2Coverage);
|
3353 |
+
$Mark2Glyphs = $this->_getCoverage();
|
3354 |
+
|
3355 |
+
|
3356 |
+
$firstMark = '';
|
3357 |
+
$html .= '<div class="glyphs">Marks: <span class="unchanged">';
|
3358 |
+
for($i=0;$i<count($Mark1Glyphs);$i++) {
|
3359 |
+
if ($level==2 && strpos($lcoverage, $Mark1Glyphs[$i])===false) { continue; }
|
3360 |
+
else {
|
3361 |
+
if (!$firstMark) { $firstMark = $Mark1Glyphs[$i]; }
|
3362 |
+
}
|
3363 |
+
$html .= ' '.$this->formatEntity($Mark1Glyphs[$i]).' ';
|
3364 |
+
}
|
3365 |
+
$html .= '</span></div>';
|
3366 |
+
|
3367 |
+
if ($firstMark) {
|
3368 |
+
|
3369 |
+
$html .= '<div class="glyphs">Bases: <span class="unchanged">';
|
3370 |
+
for($j=0;$j<count($Mark2Glyphs);$j++) {
|
3371 |
+
$html .= ' '.$this->formatEntity($Mark2Glyphs[$j]).' ';
|
3372 |
+
}
|
3373 |
+
$html .= '</span></div>';
|
3374 |
+
|
3375 |
+
// Example
|
3376 |
+
$html .= '<div class="glyphs" style="font-feature-settings:\''.$tag.'\' 1;">Example(s): <span class="changed">';
|
3377 |
+
for ($j=0;$j<min(count($Mark2Glyphs),20);$j++) {
|
3378 |
+
$html .= ' '.$this->formatEntity($Mark2Glyphs[$j]).$this->formatEntity($firstMark,true).' ';
|
3379 |
+
}
|
3380 |
+
$html .= '</span></div>';
|
3381 |
+
}
|
3382 |
+
|
3383 |
+
}
|
3384 |
+
////////////////////////////////////////////////////////////////////////////////
|
3385 |
+
// LookupType 7: Context positioning Position one or more glyphs in context
|
3386 |
+
////////////////////////////////////////////////////////////////////////////////
|
3387 |
+
else if ($Lookup[$luli]['Type'] == 7) {
|
3388 |
+
$html .= '<div class="lookuptype">LookupType 7: Context positioning [Format '.$PosFormat.']</div>';
|
3389 |
+
//===========
|
3390 |
+
// Format 1:
|
3391 |
+
//===========
|
3392 |
+
if ($PosFormat==1) {
|
3393 |
+
die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not YET TESTED.");
|
3394 |
+
}
|
3395 |
+
//===========
|
3396 |
+
// Format 2:
|
3397 |
+
//===========
|
3398 |
+
else if ($PosFormat==2) {
|
3399 |
+
die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not YET TESTED.");
|
3400 |
+
}
|
3401 |
+
//===========
|
3402 |
+
// Format 3:
|
3403 |
+
//===========
|
3404 |
+
else if ($PosFormat==3) {
|
3405 |
+
die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not YET TESTED.");
|
3406 |
+
}
|
3407 |
+
else {
|
3408 |
+
die("GPOS Lookup Type ".$Type.", Format ".$PosFormat." not supported.");
|
3409 |
+
}
|
3410 |
+
}
|
3411 |
+
////////////////////////////////////////////////////////////////////////////////
|
3412 |
+
// LookupType 8: Chained Context positioning Position one or more glyphs in chained context
|
3413 |
+
////////////////////////////////////////////////////////////////////////////////
|
3414 |
+
else if ($Lookup[$luli]['Type'] == 8) {
|
3415 |
+
$html .= '<div class="lookuptype">LookupType 8: Chained Context positioning [Format '.$PosFormat.']</div>';
|
3416 |
+
//===========
|
3417 |
+
// Format 1:
|
3418 |
+
//===========
|
3419 |
+
if ($PosFormat==1) {
|
3420 |
+
die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not TESTED YET.");
|
3421 |
+
}
|
3422 |
+
//===========
|
3423 |
+
// Format 2:
|
3424 |
+
//===========
|
3425 |
+
else if ($PosFormat==2) {
|
3426 |
+
$html .= '<div>GPOS Lookup Type 8: Format 2 not yet supported in OTL dump</div>';
|
3427 |
+
continue;
|
3428 |
+
/* NB When developing - cf. GSUB 6.2 */
|
3429 |
+
die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not TESTED YET.");
|
3430 |
+
}
|
3431 |
+
//===========
|
3432 |
+
// Format 3:
|
3433 |
+
//===========
|
3434 |
+
else if ($PosFormat==3) {
|
3435 |
+
$BacktrackGlyphCount = $this->read_ushort();
|
3436 |
+
$CoverageBacktrackOffset = array();
|
3437 |
+
for ($b=0;$b<$BacktrackGlyphCount;$b++) {
|
3438 |
+
$CoverageBacktrackOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order
|
3439 |
+
}
|
3440 |
+
$InputGlyphCount = $this->read_ushort();
|
3441 |
+
$CoverageInputOffset = array();
|
3442 |
+
for ($b=0;$b<$InputGlyphCount;$b++) {
|
3443 |
+
$CoverageInputOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order
|
3444 |
+
}
|
3445 |
+
$LookaheadGlyphCount = $this->read_ushort();
|
3446 |
+
$CoverageLookaheadOffset = array();
|
3447 |
+
for ($b=0;$b<$LookaheadGlyphCount;$b++) {
|
3448 |
+
$CoverageLookaheadOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order
|
3449 |
+
}
|
3450 |
+
$PosCount = $this->read_ushort();
|
3451 |
+
|
3452 |
+
$PosLookupRecord = array();
|
3453 |
+
for ($p=0;$p<$PosCount;$p++) {
|
3454 |
+
// PosLookupRecord
|
3455 |
+
$PosLookupRecord[$p]['SequenceIndex'] = $this->read_ushort();
|
3456 |
+
$PosLookupRecord[$p]['LookupListIndex'] = $this->read_ushort();
|
3457 |
+
}
|
3458 |
+
|
3459 |
+
$backtrackGlyphs = array();
|
3460 |
+
for ($b=0;$b<$BacktrackGlyphCount;$b++) {
|
3461 |
+
$this->seek($CoverageBacktrackOffset[$b]);
|
3462 |
+
$backtrackGlyphs[$b] = implode('|',$this->_getCoverage());
|
3463 |
+
}
|
3464 |
+
$inputGlyphs = array();
|
3465 |
+
for ($b=0;$b<$InputGlyphCount;$b++) {
|
3466 |
+
$this->seek($CoverageInputOffset[$b]);
|
3467 |
+
$inputGlyphs[$b] = implode('|',$this->_getCoverage());
|
3468 |
+
}
|
3469 |
+
$lookaheadGlyphs = array();
|
3470 |
+
for ($b=0;$b<$LookaheadGlyphCount;$b++) {
|
3471 |
+
$this->seek($CoverageLookaheadOffset[$b]);
|
3472 |
+
$lookaheadGlyphs[$b] = implode('|',$this->_getCoverage());
|
3473 |
+
}
|
3474 |
+
|
3475 |
+
$exampleB = array();
|
3476 |
+
$exampleI = array();
|
3477 |
+
$exampleL = array();
|
3478 |
+
$html .= '<div class="context">CONTEXT: ';
|
3479 |
+
for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) {
|
3480 |
+
$html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>';
|
3481 |
+
$exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]);
|
3482 |
+
}
|
3483 |
+
for ($ff=0;$ff<count($inputGlyphs);$ff++) {
|
3484 |
+
$html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>';
|
3485 |
+
$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
|
3486 |
+
}
|
3487 |
+
for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) {
|
3488 |
+
$html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>';
|
3489 |
+
$exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]);
|
3490 |
+
}
|
3491 |
+
$html .= '</div>';
|
3492 |
+
|
3493 |
+
|
3494 |
+
for ($p=0;$p<$PosCount;$p++) {
|
3495 |
+
$lup = $PosLookupRecord[$p]['LookupListIndex'] ;
|
3496 |
+
$seqIndex = $PosLookupRecord[$p]['SequenceIndex'] ;
|
3497 |
+
|
3498 |
+
// GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n]
|
3499 |
+
$exB = '';
|
3500 |
+
$exL = '';
|
3501 |
+
if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('‍',$exampleB).'</span>'; }
|
3502 |
+
|
3503 |
+
if ($seqIndex>0) {
|
3504 |
+
$exB .= '<span class="inputother">';
|
3505 |
+
for($ip=0;$ip<$seqIndex;$ip++) {
|
3506 |
+
$exB .= $exampleI[$ip].'‍';
|
3507 |
+
}
|
3508 |
+
$exB .= '</span>';
|
3509 |
+
}
|
3510 |
+
|
3511 |
+
if (count($inputGlyphs)>($seqIndex+1)) {
|
3512 |
+
$exL .= '<span class="inputother">';
|
3513 |
+
for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
|
3514 |
+
$exL .= '‍'.$exampleI[$ip];
|
3515 |
+
}
|
3516 |
+
$exL .= '</span>';
|
3517 |
+
}
|
3518 |
+
|
3519 |
+
if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('‍',$exampleL).'</span>'; }
|
3520 |
+
|
3521 |
+
$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>';
|
3522 |
+
|
3523 |
+
$lul2 = array($lup=>$tag);
|
3524 |
+
|
3525 |
+
// Only apply if the (first) 'Replace' glyph from the
|
3526 |
+
// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
|
3527 |
+
// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
|
3528 |
+
// to level 2 and only apply if first Replace glyph is in this list
|
3529 |
+
$html .= $this->_getGPOSarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
|
3530 |
+
|
3531 |
+
}
|
3532 |
+
}
|
3533 |
+
}
|
3534 |
+
|
3535 |
+
}
|
3536 |
+
$html .= '</div>';
|
3537 |
+
}
|
3538 |
+
if ($level ==1) { $this->mpdf->WriteHTML($html); }
|
3539 |
+
else { return $html; }
|
3540 |
+
//print_r($Lookup); exit;
|
3541 |
+
}
|
3542 |
+
//=====================================================================================
|
3543 |
+
//=====================================================================================
|
3544 |
+
// GPOS FUNCTIONS
|
3545 |
+
//=====================================================================================
|
3546 |
+
|
3547 |
+
function count_bits($n) {
|
3548 |
+
for ($c=0; $n; $c++) {
|
3549 |
+
$n &= $n - 1; // clear the least significant bit set
|
3550 |
+
}
|
3551 |
+
return $c;
|
3552 |
+
}
|
3553 |
+
|
3554 |
+
function _getValueRecord($ValueFormat) { // Common ValueRecord for GPOS
|
3555 |
+
// Only returns 3 possible: $vra['XPlacement'] $vra['YPlacement'] $vra['XAdvance']
|
3556 |
+
$vra = array();
|
3557 |
+
// Horizontal adjustment for placement-in design units
|
3558 |
+
if (($ValueFormat & 0x0001) == 0x0001) { $vra['XPlacement'] = $this->read_short(); }
|
3559 |
+
// Vertical adjustment for placement-in design units
|
3560 |
+
if (($ValueFormat & 0x0002) == 0x0002) { $vra['YPlacement'] = $this->read_short(); }
|
3561 |
+
// Horizontal adjustment for advance-in design units (only used for horizontal writing)
|
3562 |
+
if (($ValueFormat & 0x0004) == 0x0004) { $vra['XAdvance'] = $this->read_short(); }
|
3563 |
+
// Vertical adjustment for advance-in design units (only used for vertical writing)
|
3564 |
+
if (($ValueFormat & 0x0008) == 0x0008) { $this->read_short(); }
|
3565 |
+
// Offset to Device table for horizontal placement-measured from beginning of PosTable (may be NULL)
|
3566 |
+
if (($ValueFormat & 0x0010) == 0x0010) { $this->read_ushort(); }
|
3567 |
+
// Offset to Device table for vertical placement-measured from beginning of PosTable (may be NULL)
|
3568 |
+
if (($ValueFormat & 0x0020) == 0x0020) { $this->read_ushort(); }
|
3569 |
+
// Offset to Device table for horizontal advance-measured from beginning of PosTable (may be NULL)
|
3570 |
+
if (($ValueFormat & 0x0040) == 0x0040) { $this->read_ushort(); }
|
3571 |
+
// Offset to Device table for vertical advance-measured from beginning of PosTable (may be NULL)
|
3572 |
+
if (($ValueFormat & 0x0080) == 0x0080) { $this->read_ushort(); }
|
3573 |
+
return $vra;
|
3574 |
+
}
|
3575 |
+
|
3576 |
+
function _getAnchorTable($offset=0) {
|
3577 |
+
if ($offset) { $this->seek($offset); }
|
3578 |
+
$AnchorFormat = $this->read_ushort();
|
3579 |
+
$XCoordinate = $this->read_short();
|
3580 |
+
$YCoordinate = $this->read_short();
|
3581 |
+
// Format 2 specifies additional link to contour point; Format 3 additional Device table
|
3582 |
+
return array($XCoordinate, $YCoordinate);
|
3583 |
+
}
|
3584 |
+
|
3585 |
+
function _getMarkRecord($offset, $MarkPos) {
|
3586 |
+
$this->seek($offset);
|
3587 |
+
$MarkCount = $this->read_ushort();
|
3588 |
+
$this->skip($MarkPos*4);
|
3589 |
+
$Class = $this->read_ushort();
|
3590 |
+
$MarkAnchor = $offset + $this->read_ushort(); // = Offset to anchor table
|
3591 |
+
list($x,$y) = $this->_getAnchorTable($MarkAnchor );
|
3592 |
+
$MarkRecord = array('Class'=>$Class, 'AnchorX'=>$x, 'AnchorY'=>$y);
|
3593 |
+
return $MarkRecord;
|
3594 |
+
}
|
3595 |
+
|
3596 |
+
|
3597 |
+
//////////////////////////////////////////////////////////////////////////////////
|
3598 |
+
// Recursively get composite glyph data
|
3599 |
+
function getGlyphData($originalGlyphIdx, &$maxdepth, &$depth, &$points, &$contours) {
|
3600 |
+
$depth++;
|
3601 |
+
$maxdepth = max($maxdepth, $depth);
|
3602 |
+
if (count($this->glyphdata[$originalGlyphIdx]['compGlyphs'])) {
|
3603 |
+
foreach($this->glyphdata[$originalGlyphIdx]['compGlyphs'] AS $glyphIdx) {
|
3604 |
+
$this->getGlyphData($glyphIdx, $maxdepth, $depth, $points, $contours);
|
3605 |
+
}
|
3606 |
+
}
|
3607 |
+
else if (($this->glyphdata[$originalGlyphIdx]['nContours'] > 0) && $depth > 0) { // simple
|
3608 |
+
$contours += $this->glyphdata[$originalGlyphIdx]['nContours'];
|
3609 |
+
$points += $this->glyphdata[$originalGlyphIdx]['nPoints'];
|
3610 |
+
}
|
3611 |
+
$depth--;
|
3612 |
+
}
|
3613 |
+
|
3614 |
+
|
3615 |
+
//////////////////////////////////////////////////////////////////////////////////
|
3616 |
+
// Recursively get composite glyphs
|
3617 |
+
function getGlyphs($originalGlyphIdx, &$start, &$glyphSet, &$subsetglyphs) {
|
3618 |
+
$glyphPos = $this->glyphPos[$originalGlyphIdx];
|
3619 |
+
$glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
|
3620 |
+
if (!$glyphLen) {
|
3621 |
+
return;
|
3622 |
+
}
|
3623 |
+
$this->seek($start + $glyphPos);
|
3624 |
+
$numberOfContours = $this->read_short();
|
3625 |
+
if ($numberOfContours < 0) {
|
3626 |
+
$this->skip(8);
|
3627 |
+
$flags = GF_MORE;
|
3628 |
+
while ($flags & GF_MORE) {
|
3629 |
+
$flags = $this->read_ushort();
|
3630 |
+
$glyphIdx = $this->read_ushort();
|
3631 |
+
if (!isset($glyphSet[$glyphIdx])) {
|
3632 |
+
$glyphSet[$glyphIdx] = count($subsetglyphs); // old glyphID to new glyphID
|
3633 |
+
$subsetglyphs[$glyphIdx] = true;
|
3634 |
+
}
|
3635 |
+
$savepos = ftell($this->fh);
|
3636 |
+
$this->getGlyphs($glyphIdx, $start, $glyphSet, $subsetglyphs);
|
3637 |
+
$this->seek($savepos);
|
3638 |
+
if ($flags & GF_WORDS)
|
3639 |
+
$this->skip(4);
|
3640 |
+
else
|
3641 |
+
$this->skip(2);
|
3642 |
+
if ($flags & GF_SCALE)
|
3643 |
+
$this->skip(2);
|
3644 |
+
else if ($flags & GF_XYSCALE)
|
3645 |
+
$this->skip(4);
|
3646 |
+
else if ($flags & GF_TWOBYTWO)
|
3647 |
+
$this->skip(8);
|
3648 |
+
}
|
3649 |
+
}
|
3650 |
+
}
|
3651 |
+
|
3652 |
+
//////////////////////////////////////////////////////////////////////////////////
|
3653 |
+
|
3654 |
+
function getHMTX($numberOfHMetrics, $numGlyphs, &$glyphToChar, $scale) {
|
3655 |
+
$start = $this->seek_table("hmtx");
|
3656 |
+
$aw = 0;
|
3657 |
+
$this->charWidths = str_pad('', 256*256*2, "\x00");
|
3658 |
+
if ($this->maxUniChar > 65536) { $this->charWidths .= str_pad('', 256*256*2, "\x00"); } // Plane 1 SMP
|
3659 |
+
if ($this->maxUniChar > 131072) { $this->charWidths .= str_pad('', 256*256*2, "\x00"); } // Plane 2 SMP
|
3660 |
+
$nCharWidths = 0;
|
3661 |
+
if (($numberOfHMetrics*4) < $this->maxStrLenRead) {
|
3662 |
+
$data = $this->get_chunk($start,($numberOfHMetrics*4));
|
3663 |
+
$arr = unpack("n*", $data);
|
3664 |
+
}
|
3665 |
+
else { $this->seek($start); }
|
3666 |
+
for( $glyph=0; $glyph<$numberOfHMetrics; $glyph++) {
|
3667 |
+
if (($numberOfHMetrics*4) < $this->maxStrLenRead) {
|
3668 |
+
$aw = $arr[($glyph*2)+1];
|
3669 |
+
}
|
3670 |
+
else {
|
3671 |
+
$aw = $this->read_ushort();
|
3672 |
+
$lsb = $this->read_ushort();
|
3673 |
+
}
|
3674 |
+
if (isset($glyphToChar[$glyph]) || $glyph == 0) {
|
3675 |
+
|
3676 |
+
if ($aw >= (1 << 15) ) { $aw = 0; } // 1.03 Some (arabic) fonts have -ve values for width
|
3677 |
+
// although should be unsigned value - comes out as e.g. 65108 (intended -50)
|
3678 |
+
if ($glyph == 0) {
|
3679 |
+
$this->defaultWidth = $scale*$aw;
|
3680 |
+
continue;
|
3681 |
+
}
|
3682 |
+
foreach($glyphToChar[$glyph] AS $char) {
|
3683 |
+
//$this->charWidths[$char] = intval(round($scale*$aw));
|
3684 |
+
if ($char != 0 && $char != 65535) {
|
3685 |
+
$w = intval(round($scale*$aw));
|
3686 |
+
if ($w == 0) { $w = 65535; }
|
3687 |
+
if ($char < 196608) {
|
3688 |
+
$this->charWidths[$char*2] = chr($w >> 8);
|
3689 |
+
$this->charWidths[$char*2 + 1] = chr($w & 0xFF);
|
3690 |
+
$nCharWidths++;
|
3691 |
+
}
|
3692 |
+
}
|
3693 |
+
}
|
3694 |
+
}
|
3695 |
+
}
|
3696 |
+
$data = $this->get_chunk(($start+$numberOfHMetrics*4),($numGlyphs*2));
|
3697 |
+
$arr = unpack("n*", $data);
|
3698 |
+
$diff = $numGlyphs-$numberOfHMetrics;
|
3699 |
+
$w = intval(round($scale*$aw));
|
3700 |
+
if ($w == 0) { $w = 65535; }
|
3701 |
+
for( $pos=0; $pos<$diff; $pos++) {
|
3702 |
+
$glyph = $pos + $numberOfHMetrics;
|
3703 |
+
if (isset($glyphToChar[$glyph])) {
|
3704 |
+
foreach($glyphToChar[$glyph] AS $char) {
|
3705 |
+
if ($char != 0 && $char != 65535) {
|
3706 |
+
if ($char < 196608) {
|
3707 |
+
$this->charWidths[$char*2] = chr($w >> 8);
|
3708 |
+
$this->charWidths[$char*2 + 1] = chr($w & 0xFF);
|
3709 |
+
$nCharWidths++;
|
3710 |
+
}
|
3711 |
+
}
|
3712 |
+
}
|
3713 |
+
}
|
3714 |
+
}
|
3715 |
+
// NB 65535 is a set width of 0
|
3716 |
+
// First bytes define number of chars in font
|
3717 |
+
$this->charWidths[0] = chr($nCharWidths >> 8);
|
3718 |
+
$this->charWidths[1] = chr($nCharWidths & 0xFF);
|
3719 |
+
}
|
3720 |
+
|
3721 |
+
|
3722 |
+
|
3723 |
+
|
3724 |
+
|
3725 |
+
function getHMetric($numberOfHMetrics, $gid) {
|
3726 |
+
$start = $this->seek_table("hmtx");
|
3727 |
+
if ($gid < $numberOfHMetrics) {
|
3728 |
+
$this->seek($start+($gid*4));
|
3729 |
+
$hm = fread($this->fh,4);
|
3730 |
+
}
|
3731 |
+
else {
|
3732 |
+
$this->seek($start+(($numberOfHMetrics-1)*4));
|
3733 |
+
$hm = fread($this->fh,2);
|
3734 |
+
$this->seek($start+($numberOfHMetrics*2)+($gid*2));
|
3735 |
+
$hm .= fread($this->fh,2);
|
3736 |
+
}
|
3737 |
+
return $hm;
|
3738 |
+
}
|
3739 |
+
|
3740 |
+
function getLOCA($indexToLocFormat, $numGlyphs) {
|
3741 |
+
$start = $this->seek_table('loca');
|
3742 |
+
$this->glyphPos = array();
|
3743 |
+
if ($indexToLocFormat == 0) {
|
3744 |
+
$data = $this->get_chunk($start,($numGlyphs*2)+2);
|
3745 |
+
$arr = unpack("n*", $data);
|
3746 |
+
for ($n=0; $n<=$numGlyphs; $n++) {
|
3747 |
+
$this->glyphPos[] = ($arr[$n+1] * 2);
|
3748 |
+
}
|
3749 |
+
}
|
3750 |
+
else if ($indexToLocFormat == 1) {
|
3751 |
+
$data = $this->get_chunk($start,($numGlyphs*4)+4);
|
3752 |
+
$arr = unpack("N*", $data);
|
3753 |
+
for ($n=0; $n<=$numGlyphs; $n++) {
|
3754 |
+
$this->glyphPos[] = ($arr[$n+1]);
|
3755 |
+
}
|
3756 |
+
}
|
3757 |
+
else
|
3758 |
+
die('Unknown location table format '.$indexToLocFormat);
|
3759 |
+
}
|
3760 |
+
|
3761 |
+
|
3762 |
+
// CMAP Format 4
|
3763 |
+
function getCMAP4($unicode_cmap_offset, &$glyphToChar, &$charToGlyph ) {
|
3764 |
+
$this->maxUniChar = 0;
|
3765 |
+
$this->seek($unicode_cmap_offset + 2);
|
3766 |
+
$length = $this->read_ushort();
|
3767 |
+
$limit = $unicode_cmap_offset + $length;
|
3768 |
+
$this->skip(2);
|
3769 |
+
|
3770 |
+
$segCount = $this->read_ushort() / 2;
|
3771 |
+
$this->skip(6);
|
3772 |
+
$endCount = array();
|
3773 |
+
for($i=0; $i<$segCount; $i++) { $endCount[] = $this->read_ushort(); }
|
3774 |
+
$this->skip(2);
|
3775 |
+
$startCount = array();
|
3776 |
+
for($i=0; $i<$segCount; $i++) { $startCount[] = $this->read_ushort(); }
|
3777 |
+
$idDelta = array();
|
3778 |
+
for($i=0; $i<$segCount; $i++) { $idDelta[] = $this->read_short(); } // ???? was unsigned short
|
3779 |
+
$idRangeOffset_start = $this->_pos;
|
3780 |
+
$idRangeOffset = array();
|
3781 |
+
for($i=0; $i<$segCount; $i++) { $idRangeOffset[] = $this->read_ushort(); }
|
3782 |
+
|
3783 |
+
for ($n=0;$n<$segCount;$n++) {
|
3784 |
+
$endpoint = ($endCount[$n] + 1);
|
3785 |
+
for ($unichar=$startCount[$n];$unichar<$endpoint;$unichar++) {
|
3786 |
+
if ($idRangeOffset[$n] == 0)
|
3787 |
+
$glyph = ($unichar + $idDelta[$n]) & 0xFFFF;
|
3788 |
+
else {
|
3789 |
+
$offset = ($unichar - $startCount[$n]) * 2 + $idRangeOffset[$n];
|
3790 |
+
$offset = $idRangeOffset_start + 2 * $n + $offset;
|
3791 |
+
if ($offset >= $limit)
|
3792 |
+
$glyph = 0;
|
3793 |
+
else {
|
3794 |
+
$glyph = $this->get_ushort($offset);
|
3795 |
+
if ($glyph != 0)
|
3796 |
+
$glyph = ($glyph + $idDelta[$n]) & 0xFFFF;
|
3797 |
+
}
|
3798 |
+
}
|
3799 |
+
$charToGlyph[$unichar] = $glyph;
|
3800 |
+
if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
|
3801 |
+
$glyphToChar[$glyph][] = $unichar;
|
3802 |
+
}
|
3803 |
+
}
|
3804 |
+
|
3805 |
+
}
|
3806 |
+
|
3807 |
+
function formatUni($char) {
|
3808 |
+
$x = preg_replace('/^[0]*/','',$char);
|
3809 |
+
$x = str_pad($x, 4, '0', STR_PAD_LEFT);
|
3810 |
+
$d = hexdec($x);
|
3811 |
+
if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; } // E000 - F8FF, 1E000-1F000
|
3812 |
+
else { $id = 'U'; }
|
3813 |
+
return $id .'+'.$x;
|
3814 |
+
}
|
3815 |
+
function formatEntity($char, $allowjoining=false) {
|
3816 |
+
$char = preg_replace('/^[0]/','',$char);
|
3817 |
+
$x = '&#x'.$char.';';
|
3818 |
+
if (strpos($this->GlyphClassMarks, $char)!==false) {
|
3819 |
+
if (!$allowjoining) {
|
3820 |
+
$x = '◌'.$x;
|
3821 |
+
}
|
3822 |
+
}
|
3823 |
+
return $x;
|
3824 |
+
}
|
3825 |
+
function formatUniArr($arr) {
|
3826 |
+
$s = array();
|
3827 |
+
foreach($arr AS $c) {
|
3828 |
+
$x = preg_replace('/^[0]*/','',$c);
|
3829 |
+
$d = hexdec($x);
|
3830 |
+
if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; } // E000 - F8FF, 1E000-1F000
|
3831 |
+
else { $id = 'U'; }
|
3832 |
+
$s[] = $id .'+'.str_pad($x, 4, '0', STR_PAD_LEFT);
|
3833 |
+
}
|
3834 |
+
return implode(', ',$s);
|
3835 |
+
}
|
3836 |
+
function formatEntityArr($arr) {
|
3837 |
+
$s = array();
|
3838 |
+
foreach($arr AS $c) {
|
3839 |
+
$c = preg_replace('/^[0]/','',$c);
|
3840 |
+
$x = '&#x'.$c.';';
|
3841 |
+
if (strpos($this->GlyphClassMarks, $c)!==false) {
|
3842 |
+
$x = '◌'.$x;
|
3843 |
+
}
|
3844 |
+
$s[] = $x;
|
3845 |
+
}
|
3846 |
+
return implode(' ',$s); // ZWNJ? ‍
|
3847 |
+
}
|
3848 |
+
function formatClassArr($arr) {
|
3849 |
+
$s = array();
|
3850 |
+
foreach($arr AS $c) {
|
3851 |
+
$x = preg_replace('/^[0]*/','',$c);
|
3852 |
+
$d = hexdec($x);
|
3853 |
+
if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; } // E000 - F8FF, 1E000-1F000
|
3854 |
+
else { $id = 'U'; }
|
3855 |
+
$s[] = $id .'+'.str_pad($x, 4, '0', STR_PAD_LEFT);
|
3856 |
+
}
|
3857 |
+
return implode(', ',$s);
|
3858 |
+
}
|
3859 |
+
function formatUniStr($str) {
|
3860 |
+
$s = array();
|
3861 |
+
$arr = explode('|',$str);
|
3862 |
+
foreach($arr AS $c) {
|
3863 |
+
$x = preg_replace('/^[0]*/','',$c);
|
3864 |
+
$d = hexdec($x);
|
3865 |
+
if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; } // E000 - F8FF, 1E000-1F000
|
3866 |
+
else { $id = 'U'; }
|
3867 |
+
$s[] = $id .'+'.str_pad($x, 4, '0', STR_PAD_LEFT);
|
3868 |
+
}
|
3869 |
+
return implode(', ',$s);
|
3870 |
+
}
|
3871 |
+
function formatEntityStr($str) {
|
3872 |
+
$s = array();
|
3873 |
+
$arr = explode('|',$str);
|
3874 |
+
foreach($arr AS $c) {
|
3875 |
+
$c = preg_replace('/^[0]/','',$c);
|
3876 |
+
$x = '&#x'.$c.';';
|
3877 |
+
if (strpos($this->GlyphClassMarks, $c)!==false) {
|
3878 |
+
$x = '◌'.$x;
|
3879 |
+
}
|
3880 |
+
$s[] = $x;
|
3881 |
+
}
|
3882 |
+
return implode(' ',$s); // ZWNJ? ‍
|
3883 |
+
}
|
3884 |
+
function formatEntityFirst($str) {
|
3885 |
+
$arr = explode('|',$str);
|
3886 |
+
$char = preg_replace('/^[0]/','',$arr[0]);
|
3887 |
+
$x = '&#x'.$char.';';
|
3888 |
+
if (strpos($this->GlyphClassMarks, $char)!==false) {
|
3889 |
+
$x = '◌'.$x;
|
3890 |
+
}
|
3891 |
+
return $x;
|
3892 |
+
}
|
3893 |
+
|
3894 |
+
}
|
3895 |
+
|
3896 |
+
|
3897 |
+
?>
|
lib/mpdf/classes/sea.php
ADDED
@@ -0,0 +1,349 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
|
4 |
+
class SEA {
|
5 |
+
|
6 |
+
// South East Asian shaper
|
7 |
+
|
8 |
+
// sea_category
|
9 |
+
const OT_X = 0;
|
10 |
+
const OT_C = 1;
|
11 |
+
const OT_IV = 2; # Independent Vowel
|
12 |
+
const OT_T = 3; # Tone Marks
|
13 |
+
const OT_H = 4; # Halant
|
14 |
+
const OT_A = 10; # Anusvara
|
15 |
+
const OT_GB = 12; # Generic Base (OT_DOTTEDCIRCLE in Indic)
|
16 |
+
const OT_CM = 17; # Consonant Medial
|
17 |
+
const OT_MR = 22; # Medial Ra
|
18 |
+
const OT_VAbv = 26;
|
19 |
+
const OT_VBlw = 27;
|
20 |
+
const OT_VPre = 28;
|
21 |
+
const OT_VPst = 29;
|
22 |
+
// ? From Indic categories
|
23 |
+
const OT_ZWNJ = 5;
|
24 |
+
const OT_ZWJ = 6;
|
25 |
+
const OT_M = 7;
|
26 |
+
const OT_SM = 8;
|
27 |
+
const OT_VD = 9;
|
28 |
+
const OT_NBSP = 11;
|
29 |
+
const OT_RS = 13;
|
30 |
+
const OT_Coeng = 14;
|
31 |
+
const OT_Repha = 15;
|
32 |
+
const OT_Ra = 16;
|
33 |
+
|
34 |
+
// Based on sea_category used to make string to find syllables
|
35 |
+
// OT_ to string character (using e.g. OT_C from INDIC) hb-ot-shape-complex-sea-private.hh
|
36 |
+
public static $sea_category_char = array(
|
37 |
+
'x',
|
38 |
+
'C',
|
39 |
+
'V',
|
40 |
+
'T',
|
41 |
+
'H',
|
42 |
+
'x',
|
43 |
+
'x',
|
44 |
+
'x',
|
45 |
+
'x',
|
46 |
+
'x',
|
47 |
+
'A',
|
48 |
+
'x',
|
49 |
+
'G',
|
50 |
+
'x',
|
51 |
+
'x',
|
52 |
+
'x',
|
53 |
+
'x',
|
54 |
+
'M',
|
55 |
+
'x',
|
56 |
+
'x',
|
57 |
+
'x',
|
58 |
+
'x',
|
59 |
+
'R',
|
60 |
+
'x',
|
61 |
+
'x',
|
62 |
+
'x',
|
63 |
+
'a',
|
64 |
+
'b',
|
65 |
+
'p',
|
66 |
+
't',
|
67 |
+
);
|
68 |
+
|
69 |
+
|
70 |
+
/* Visual positions in a syllable from left to right. */
|
71 |
+
// sea_position
|
72 |
+
const POS_START = 0;
|
73 |
+
|
74 |
+
const POS_RA_TO_BECOME_REPH = 1;
|
75 |
+
const POS_PRE_M = 2;
|
76 |
+
const POS_PRE_C = 3;
|
77 |
+
|
78 |
+
const POS_BASE_C = 4;
|
79 |
+
const POS_AFTER_MAIN = 5;
|
80 |
+
|
81 |
+
const POS_ABOVE_C = 6;
|
82 |
+
|
83 |
+
const POS_BEFORE_SUB = 7;
|
84 |
+
const POS_BELOW_C = 8;
|
85 |
+
const POS_AFTER_SUB = 9;
|
86 |
+
|
87 |
+
const POS_BEFORE_POST = 10;
|
88 |
+
const POS_POST_C = 11;
|
89 |
+
const POS_AFTER_POST = 12;
|
90 |
+
|
91 |
+
const POS_FINAL_C = 13;
|
92 |
+
const POS_SMVD = 14;
|
93 |
+
|
94 |
+
const POS_END = 15;
|
95 |
+
|
96 |
+
|
97 |
+
|
98 |
+
public static function set_sea_properties(&$info, $scriptblock ) {
|
99 |
+
$u = $info['uni'];
|
100 |
+
$type = self::sea_get_categories($u);
|
101 |
+
$cat = ($type & 0x7F);
|
102 |
+
$pos = ($type >> 8);
|
103 |
+
|
104 |
+
/*
|
105 |
+
* Re-assign category
|
106 |
+
*/
|
107 |
+
// Medial Ra
|
108 |
+
if ($u == 0x1A55 || $u == 0xAA34) { $cat = self::OT_MR; }
|
109 |
+
|
110 |
+
/*
|
111 |
+
* Re-assign position.
|
112 |
+
*/
|
113 |
+
if ($cat == self::OT_M) { // definitely "OT_M" in HarfBuzz - although this does not seem to have been defined ? should be OT_MR
|
114 |
+
switch ($pos) {
|
115 |
+
case self::POS_PRE_C: $cat = self::OT_VPre; break;
|
116 |
+
case self::POS_ABOVE_C: $cat = self::OT_VAbv; break;
|
117 |
+
case self::POS_BELOW_C: $cat = self::OT_VBlw; break;
|
118 |
+
case self::POS_POST_C: $cat = self::OT_VPst; break;
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
$info['sea_category'] = $cat;
|
123 |
+
$info['sea_position'] = $pos;
|
124 |
+
}
|
125 |
+
|
126 |
+
// syllable_type
|
127 |
+
const CONSONANT_SYLLABLE = 0;
|
128 |
+
const BROKEN_CLUSTER = 1;
|
129 |
+
const NON_SEA_CLUSTER = 2;
|
130 |
+
|
131 |
+
|
132 |
+
|
133 |
+
public static function set_syllables(&$o, $s, &$broken_syllables) {
|
134 |
+
$ptr = 0;
|
135 |
+
$syllable_serial = 1;
|
136 |
+
$broken_syllables = false;
|
137 |
+
while($ptr < strlen($s)) {
|
138 |
+
$match = '';
|
139 |
+
$syllable_length = 1;
|
140 |
+
$syllable_type = self::NON_SEA_CLUSTER ;
|
141 |
+
|
142 |
+
// CONSONANT_SYLLABLE Consonant syllable
|
143 |
+
if (preg_match('/^(C|V|G)(p|a|b|t|HC|M|R|T|A)*/', substr($s,$ptr), $ma)) {
|
144 |
+
$syllable_length = strlen($ma[0]);
|
145 |
+
$syllable_type = self::CONSONANT_SYLLABLE ;
|
146 |
+
}
|
147 |
+
// BROKEN_CLUSTER syllable
|
148 |
+
else if (preg_match('/^(p|a|b|t|HC|M|R|T|A)+/', substr($s,$ptr), $ma)) {
|
149 |
+
$syllable_length = strlen($ma[0]);
|
150 |
+
$syllable_type = self::BROKEN_CLUSTER ;
|
151 |
+
$broken_syllables = true;
|
152 |
+
}
|
153 |
+
|
154 |
+
for ($i = $ptr; $i < $ptr+$syllable_length; $i++) { $o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type; }
|
155 |
+
$ptr += $syllable_length ;
|
156 |
+
$syllable_serial++;
|
157 |
+
if ($syllable_serial == 16) $syllable_serial = 1;
|
158 |
+
}
|
159 |
+
}
|
160 |
+
|
161 |
+
public static function initial_reordering(&$info, $GSUBdata, $broken_syllables, $scriptblock, $dottedcircle) {
|
162 |
+
|
163 |
+
if ($broken_syllables && $dottedcircle) { self::insert_dotted_circles ($info, $dottedcircle); }
|
164 |
+
|
165 |
+
$count = count($info);
|
166 |
+
if (!$count) return;
|
167 |
+
$last = 0;
|
168 |
+
$last_syllable = $info[0]['syllable'];
|
169 |
+
for ($i = 1; $i < $count; $i++) {
|
170 |
+
if ($last_syllable != $info[$i]['syllable']) {
|
171 |
+
self::initial_reordering_syllable ($info, $GSUBdata, $scriptblock, $last, $i);
|
172 |
+
$last = $i;
|
173 |
+
$last_syllable = $info[$last]['syllable'];
|
174 |
+
}
|
175 |
+
}
|
176 |
+
self::initial_reordering_syllable($info, $GSUBdata, $scriptblock, $last, $count);
|
177 |
+
}
|
178 |
+
|
179 |
+
public static function insert_dotted_circles(&$info, $dottedcircle) {
|
180 |
+
$idx = 0;
|
181 |
+
$last_syllable = 0;
|
182 |
+
while ($idx < count($info)) {
|
183 |
+
$syllable = $info[$idx]['syllable'];
|
184 |
+
$syllable_type = ($syllable & 0x0F);
|
185 |
+
if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
|
186 |
+
$last_syllable = $syllable;
|
187 |
+
$dottedcircle[0]['syllable'] = $info[$idx]['syllable'];
|
188 |
+
array_splice($info, $idx, 0, $dottedcircle);
|
189 |
+
}
|
190 |
+
else
|
191 |
+
$idx++;
|
192 |
+
}
|
193 |
+
}
|
194 |
+
|
195 |
+
|
196 |
+
|
197 |
+
|
198 |
+
public static function initial_reordering_syllable (&$info, $GSUBdata, $scriptblock, $start, $end) {
|
199 |
+
/* broken_cluster: We already inserted dotted-circles, so just call the standalone_cluster. */
|
200 |
+
|
201 |
+
$syllable_type = ($info[$start]['syllable'] & 0x0F);
|
202 |
+
if ($syllable_type==self::NON_SEA_CLUSTER ) { return; }
|
203 |
+
if ($syllable_type==self::BROKEN_CLUSTER) {
|
204 |
+
/* For dotted-circle, this is what Uniscribe does:
|
205 |
+
* If dotted-circle is the last glyph, it just does nothing. */
|
206 |
+
if ($info[$end - 1]['sea_category'] == self::OT_GB) {
|
207 |
+
return;
|
208 |
+
}
|
209 |
+
}
|
210 |
+
|
211 |
+
$base = $start;
|
212 |
+
$i = $start;
|
213 |
+
for (; $i < $base; $i++)
|
214 |
+
$info[$i]['sea_position'] = self::POS_PRE_C;
|
215 |
+
if ($i < $end) {
|
216 |
+
$info[$i]['sea_position'] = self::POS_BASE_C;
|
217 |
+
$i++;
|
218 |
+
}
|
219 |
+
for (; $i < $end; $i++) {
|
220 |
+
if ($info[$i]['sea_category'] == self::OT_MR) { /* Pre-base reordering */
|
221 |
+
$info[$i]['sea_position'] = self::POS_PRE_C;
|
222 |
+
continue;
|
223 |
+
}
|
224 |
+
if ($info[$i]['sea_category'] == self::OT_VPre) { /* Left matra */
|
225 |
+
$info[$i]['sea_position'] = self::POS_PRE_M;
|
226 |
+
continue;
|
227 |
+
}
|
228 |
+
$info[$i]['sea_position'] = self::POS_AFTER_MAIN;
|
229 |
+
}
|
230 |
+
|
231 |
+
/* Sit tight, rock 'n roll! */
|
232 |
+
self::bubble_sort ($info, $start, $end - $start);
|
233 |
+
|
234 |
+
}
|
235 |
+
|
236 |
+
public static function final_reordering (&$info, $GSUBdata, $scriptblock) {
|
237 |
+
$count = count($info);
|
238 |
+
if (!$count) return;
|
239 |
+
$last = 0;
|
240 |
+
$last_syllable = $info[0]['syllable'];
|
241 |
+
for ($i = 1; $i < $count; $i++) {
|
242 |
+
if ($last_syllable != $info[$i]['syllable']) {
|
243 |
+
self::final_reordering_syllable ($info, $GSUBdata, $scriptblock, $last, $i);
|
244 |
+
$last = $i;
|
245 |
+
$last_syllable = $info[$last]['syllable'];
|
246 |
+
}
|
247 |
+
}
|
248 |
+
self::final_reordering_syllable ($info, $GSUBdata, $scriptblock, $last, $count);
|
249 |
+
|
250 |
+
}
|
251 |
+
|
252 |
+
public static function final_reordering_syllable (&$info, $GSUBdata, $scriptblock, $start, $end) {
|
253 |
+
/*
|
254 |
+
* Nothing to do here at present!
|
255 |
+
*/
|
256 |
+
|
257 |
+
}
|
258 |
+
|
259 |
+
|
260 |
+
|
261 |
+
|
262 |
+
public static $sea_table = array(
|
263 |
+
|
264 |
+
/* New Tai Lue (1980..19DF) */
|
265 |
+
|
266 |
+
/* 1980 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
267 |
+
/* 1988 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
268 |
+
/* 1990 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
269 |
+
/* 1998 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
270 |
+
/* 19A0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
271 |
+
/* 19A8 */ 3841, 3841, 3841, 3841, 3840, 3840, 3840, 3840,
|
272 |
+
/* 19B0 */ 2823, 2823, 2823, 2823, 2823, 775, 775, 775,
|
273 |
+
/* 19B8 */ 2823, 2823, 775, 2823, 2823, 2823, 2823, 2823,
|
274 |
+
/* 19C0 */ 2823, 3857, 3857, 3857, 3857, 3857, 3857, 3857,
|
275 |
+
/* 19C8 */ 3843, 3843, 3840, 3840, 3840, 3840, 3840, 3840,
|
276 |
+
/* 19D0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
277 |
+
/* 19D8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
278 |
+
|
279 |
+
/* Tai Tham (1A20..1AAF) */
|
280 |
+
|
281 |
+
/* 1A20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
282 |
+
/* 1A28 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
283 |
+
/* 1A30 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
284 |
+
/* 1A38 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
285 |
+
/* 1A40 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
286 |
+
/* 1A48 */ 3841, 3841, 3841, 3841, 3841, 3842, 3842, 3842,
|
287 |
+
/* 1A50 */3842, 3842, 3842, 3841, 3841, 3857, 3857, 3857,
|
288 |
+
/* 1A58 */ 3857, 3857, 3857, 3857, 3857, 3857, 3857, 3840,
|
289 |
+
/* 1A60 */ 3844, 2823, 1543, 2823, 2823, 1543, 1543, 1543,
|
290 |
+
/* 1A68 */ 1543, 2055, 2055, 1543, 2055, 2823, 775, 775,
|
291 |
+
/* 1A70 */ 775, 775, 775, 1543, 1543, 3843, 3843, 3843,
|
292 |
+
/* 1A78 */ 3843, 3843, 3840, 3840, 3840, 3840, 3840, 3840,
|
293 |
+
/* 1A80 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
294 |
+
/* 1A88 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
295 |
+
/* 1A90 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
296 |
+
/* 1A98 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
297 |
+
/* 1AA0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
298 |
+
/* 1AA8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
299 |
+
|
300 |
+
/* Cham (AA00..AA5F) */
|
301 |
+
|
302 |
+
/* AA00 */ 3842, 3842, 3842, 3842, 3842, 3842, 3841, 3841,
|
303 |
+
/* AA08 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
304 |
+
/* AA10 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
305 |
+
/* AA18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
306 |
+
/* AA20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
|
307 |
+
/* AA28 */ 3841, 1543, 1543, 1543, 1543, 2055, 1543, 775,
|
308 |
+
/* AA30 */ 775, 1543, 2055, 3857, 3857, 3857, 3857, 3840,
|
309 |
+
/* AA38 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
310 |
+
/* AA40 */ 3857, 3857, 3857, 3857, 3857, 3857, 3857, 3857,
|
311 |
+
/* AA48 */ 3857, 3857, 3857, 3857, 3857, 3857, 3840, 3840,
|
312 |
+
/* AA50 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
313 |
+
/* AA58 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
|
314 |
+
|
315 |
+
);
|
316 |
+
|
317 |
+
|
318 |
+
|
319 |
+
public static function sea_get_categories ($u) {
|
320 |
+
if (0x1980 <= $u && $u <= 0x19DF) return self::$sea_table[$u - 0x1980]; // offset 0 for New Tai Lue
|
321 |
+
if (0x1A20 <= $u && $u <= 0x1AAF) return self::$sea_table[$u - 0x1A20 + 96]; // offset for Tai Tham
|
322 |
+
if (0xAA00 <= $u && $u <= 0xAA5F) return self::$sea_table[$u - 0xAA00 + 96 + 144]; // Cham
|
323 |
+
if ($u == 0x00A0) return 3851; // (ISC_CP | (IMC_x << 8))
|
324 |
+
if ($u == 0x25CC) return 3851; // (ISC_CP | (IMC_x << 8))
|
325 |
+
return 3840; // (ISC_x | (IMC_x << 8))
|
326 |
+
}
|
327 |
+
|
328 |
+
|
329 |
+
public static function bubble_sort(&$arr, $start, $len) {
|
330 |
+
if ($len<2) { return;}
|
331 |
+
$k = $start+$len-2;
|
332 |
+
while ($k >= $start) {
|
333 |
+
for ($j=$start; $j<=$k; $j++) {
|
334 |
+
if ($arr[$j]['sea_position'] > $arr[$j + 1]['sea_position']) {
|
335 |
+
$t = $arr[$j];
|
336 |
+
$arr[$j] = $arr[$j + 1];
|
337 |
+
$arr[$j + 1] = $t;
|
338 |
+
}
|
339 |
+
}
|
340 |
+
$k--;
|
341 |
+
}
|
342 |
+
}
|
343 |
+
|
344 |
+
|
345 |
+
|
346 |
+
|
347 |
+
} // end Class
|
348 |
+
|
349 |
+
?>
|
lib/mpdf/classes/svg.php
ADDED
@@ -0,0 +1,3441 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
// svg class modified for mPDF version 6.0 by Ian Back: based on -
|
3 |
+
// svg2pdf fpdf class
|
4 |
+
// sylvain briand (syb@godisaduck.com), modified by rick trevino (rtrevino1@yahoo.com)
|
5 |
+
// http://www.godisaduck.com/svg2pdf_with_fpdf
|
6 |
+
// http://rhodopsin.blogspot.com
|
7 |
+
//
|
8 |
+
// cette class etendue est open source, toute modification devra cependant etre repertori�e~
|
9 |
+
|
10 |
+
|
11 |
+
// If you wish to use Automatic Font selection within SVG's. change this definition to true.
|
12 |
+
// This selects different fonts for different scripts used in text.
|
13 |
+
// This can be enabled/disabled independently of the use of Automatic Font selection within mPDF generally.
|
14 |
+
// Choice of font is determined by the config_script2lang.php and config_lang2fonts.php files, the same as for mPDF generally.
|
15 |
+
if (!defined("_SVG_AUTOFONT")) { define("_SVG_AUTOFONT", false); }
|
16 |
+
|
17 |
+
// Enable a limited use of classes within SVG <text> elements by setting this to true.
|
18 |
+
// This allows recognition of a "class" attribute on a <text> element.
|
19 |
+
// The CSS style for that class should be outside the SVG, and cannot use any other selectors (i.e. only .class {} can be defined)
|
20 |
+
// <style> definitions within the SVG code will be recognised if the SVG is included as an inline item within the HTML code passed to mPDF.
|
21 |
+
// The style property should be pertinent to SVG e.g. use fill:red rather than color:red
|
22 |
+
// Only the properties currently supported for SVG text can be specified:
|
23 |
+
// fill, fill-opacity, stroke, stroke-opacity, stroke-linecap, stroke-linejoin, stroke-width, stroke-dasharray, stroke-dashoffset
|
24 |
+
// font-family, font-size, font-weight, font-variant, font-style, opacity, text-anchor
|
25 |
+
if (!defined("_SVG_CLASSES")) { define("_SVG_CLASSES", false); }
|
26 |
+
|
27 |
+
|
28 |
+
|
29 |
+
|
30 |
+
// NB UNITS - Works in pixels as main units - converting to PDF units when outputing to PDF string
|
31 |
+
// and on returning size
|
32 |
+
|
33 |
+
class SVG {
|
34 |
+
|
35 |
+
var $svg_font; // array - holds content of SVG fonts defined in image // mPDF 6
|
36 |
+
var $svg_gradient; // array - contient les infos sur les gradient fill du svg class� par id du svg
|
37 |
+
var $svg_shadinglist; // array - contient les ids des objet shading
|
38 |
+
var $svg_info; // array contenant les infos du svg voulue par l'utilisateur
|
39 |
+
var $svg_attribs; // array - holds all attributes of root <svg> tag
|
40 |
+
var $svg_style; // array contenant les style de groupes du svg
|
41 |
+
var $svg_string; // String contenant le tracage du svg en lui m�me.
|
42 |
+
var $txt_data; // array - holds string info to write txt to image
|
43 |
+
var $txt_style; // array - current text style
|
44 |
+
var $mpdf_ref;
|
45 |
+
var $xbase;
|
46 |
+
var $ybase;
|
47 |
+
var $svg_error;
|
48 |
+
var $subPathInit;
|
49 |
+
var $spxstart;
|
50 |
+
var $spystart;
|
51 |
+
var $kp; // convert pixels to PDF units
|
52 |
+
var $pathBBox;
|
53 |
+
|
54 |
+
var $script2lang;
|
55 |
+
var $viet;
|
56 |
+
var $pashto;
|
57 |
+
var $urdu;
|
58 |
+
var $persian;
|
59 |
+
var $sindhi;
|
60 |
+
|
61 |
+
var $textlength; // mPDF 5.7.4
|
62 |
+
var $texttotallength; // mPDF 5.7.4
|
63 |
+
var $textoutput; // mPDF 5.7.4
|
64 |
+
var $textanchor; // mPDF 5.7.4
|
65 |
+
var $textXorigin; // mPDF 5.7.4
|
66 |
+
var $textYorigin; // mPDF 5.7.4
|
67 |
+
var $textjuststarted; // mPDF 5.7.4
|
68 |
+
var $intext; // mPDF 5.7.4
|
69 |
+
|
70 |
+
function SVG(&$mpdf){
|
71 |
+
$this->svg_font = array(); // mPDF 6
|
72 |
+
$this->svg_gradient = array();
|
73 |
+
$this->svg_shadinglist = array();
|
74 |
+
$this->txt_data = array();
|
75 |
+
$this->svg_string = '';
|
76 |
+
$this->svg_info = array();
|
77 |
+
$this->svg_attribs = array();
|
78 |
+
$this->xbase = 0;
|
79 |
+
$this->ybase = 0;
|
80 |
+
$this->svg_error = false;
|
81 |
+
$this->subPathInit = false;
|
82 |
+
$this->dashesUsed = false;
|
83 |
+
$this->mpdf_ref =& $mpdf;
|
84 |
+
|
85 |
+
$this->textlength = 0; // mPDF 5.7.4
|
86 |
+
$this->texttotallength = 0; // mPDF 5.7.4
|
87 |
+
$this->textoutput = ''; // mPDF 5.7.4
|
88 |
+
$this->textanchor = 'start'; // mPDF 5.7.4
|
89 |
+
$this->textXorigin = 0; // mPDF 5.7.4
|
90 |
+
$this->textYorigin = 0; // mPDF 5.7.4
|
91 |
+
$this->textjuststarted = false; // mPDF 5.7.4
|
92 |
+
$this->intext = false; // mPDF 5.7.4
|
93 |
+
|
94 |
+
$this->kp = 72 / $mpdf->img_dpi; // constant To convert pixels to pts/PDF units
|
95 |
+
$this->kf = 1; // constant To convert font size if re-mapped
|
96 |
+
$this->pathBBox = array();
|
97 |
+
|
98 |
+
$this->svg_style = array(
|
99 |
+
array(
|
100 |
+
'fill' => 'black',
|
101 |
+
'fill-opacity' => 1, // remplissage opaque par defaut
|
102 |
+
'fill-rule' => 'nonzero', // mode de remplissage par defaut
|
103 |
+
'stroke' => 'none', // pas de trait par defaut
|
104 |
+
'stroke-linecap' => 'butt', // style de langle par defaut
|
105 |
+
'stroke-linejoin' => 'miter',
|
106 |
+
'stroke-miterlimit' => 4, // limite de langle par defaut
|
107 |
+
'stroke-opacity' => 1, // trait opaque par defaut
|
108 |
+
'stroke-width' => 1,
|
109 |
+
'stroke-dasharray' => 0,
|
110 |
+
'stroke-dashoffset' => 0,
|
111 |
+
'color' => ''
|
112 |
+
)
|
113 |
+
);
|
114 |
+
|
115 |
+
$this->txt_style = array(
|
116 |
+
array(
|
117 |
+
'fill' => 'black', // pas de remplissage par defaut
|
118 |
+
'font-family' => $mpdf->default_font,
|
119 |
+
'font-size' => $mpdf->default_font_size, // ****** this is pts
|
120 |
+
'font-weight' => 'normal', // normal | bold
|
121 |
+
'font-style' => 'normal', // italic | normal
|
122 |
+
'text-anchor' => 'start', // alignment: start, middle, end
|
123 |
+
'fill-opacity' => 1, // remplissage opaque par defaut
|
124 |
+
'fill-rule' => 'nonzero', // mode de remplissage par defaut
|
125 |
+
'stroke' => 'none', // pas de trait par defaut
|
126 |
+
'stroke-opacity' => 1, // trait opaque par defaut
|
127 |
+
'stroke-width' => 1,
|
128 |
+
'color' => ''
|
129 |
+
)
|
130 |
+
);
|
131 |
+
|
132 |
+
|
133 |
+
|
134 |
+
}
|
135 |
+
|
136 |
+
// mPDF 5.7.4 Embedded image
|
137 |
+
function svgImage($attribs) {
|
138 |
+
// x and y are coordinates
|
139 |
+
$x = (isset($attribs['x']) ? $attribs['x'] : 0);
|
140 |
+
$y = (isset($attribs['y']) ? $attribs['y'] : 0);
|
141 |
+
// preserveAspectRatio
|
142 |
+
$par = (isset($attribs['preserveAspectRatio']) ? $attribs['preserveAspectRatio'] : 'xMidYMid meet');
|
143 |
+
// width and height are <lengths> - Required attributes
|
144 |
+
$wset = (isset($attribs['width']) ? $attribs['width'] : 0);
|
145 |
+
$hset = (isset($attribs['height']) ? $attribs['height'] : 0);
|
146 |
+
$w = $this->mpdf_ref->ConvertSize($wset,$this->svg_info['w']*(25.4/$this->mpdf_ref->dpi),$this->mpdf_ref->FontSize,false);
|
147 |
+
$h = $this->mpdf_ref->ConvertSize($hset,$this->svg_info['h']*(25.4/$this->mpdf_ref->dpi),$this->mpdf_ref->FontSize,false);
|
148 |
+
if ($w==0 || $h==0) { return; }
|
149 |
+
// Convert to pixels = SVG units
|
150 |
+
$w *= 1/(25.4/$this->mpdf_ref->dpi);
|
151 |
+
$h *= 1/(25.4/$this->mpdf_ref->dpi);
|
152 |
+
|
153 |
+
$srcpath = $attribs['xlink:href'];
|
154 |
+
$orig_srcpath = '';
|
155 |
+
if (trim($srcpath) != '' && substr($srcpath,0,4)=='var:') {
|
156 |
+
$orig_srcpath = $srcpath;
|
157 |
+
$this->mpdf_ref->GetFullPath($srcpath);
|
158 |
+
}
|
159 |
+
|
160 |
+
// Image file (does not allow vector images i.e. WMF/SVG)
|
161 |
+
// mPDF 6 Added $this->mpdf_ref->interpolateImages
|
162 |
+
$info = $this->mpdf_ref->_getImage($srcpath, true, false, $orig_srcpath, $this->mpdf_ref->interpolateImages);
|
163 |
+
if(!$info) return;
|
164 |
+
|
165 |
+
// x,y,w,h define the reference rectangle
|
166 |
+
$img_h = $h;
|
167 |
+
$img_w = $w;
|
168 |
+
$img_x = $x;
|
169 |
+
$img_y = $y;
|
170 |
+
$meetOrSlice = 'meet';
|
171 |
+
|
172 |
+
// preserveAspectRatio
|
173 |
+
$ar = preg_split('/\s+/', strtolower($par));
|
174 |
+
if ($ar[0]!='none') { // If "none" need to do nothing
|
175 |
+
// Force uniform scaling
|
176 |
+
if (isset($ar[1]) && $ar[1]=='slice') { $meetOrSlice = 'slice'; }
|
177 |
+
else { $meetOrSlice = 'meet'; }
|
178 |
+
if ($info['h']/$info['w'] > $h/$w) {
|
179 |
+
if ($meetOrSlice == 'meet') { // the entire viewBox is visible within the viewport
|
180 |
+
$img_w = $img_h * $info['w']/$info['h'];
|
181 |
+
}
|
182 |
+
else { // the entire viewport is covered by the viewBox
|
183 |
+
$img_h = $img_w * $info['h']/$info['w'];
|
184 |
+
}
|
185 |
+
}
|
186 |
+
else if ($info['h']/$info['w'] < $h/$w) {
|
187 |
+
if ($meetOrSlice == 'meet') { // the entire viewBox is visible within the viewport
|
188 |
+
$img_h = $img_w * $info['h']/$info['w'];
|
189 |
+
}
|
190 |
+
else { // the entire viewport is covered by the viewBox
|
191 |
+
$img_w = $img_h * $info['w']/$info['h'];
|
192 |
+
}
|
193 |
+
}
|
194 |
+
if ($ar[0]=='xminymin') {
|
195 |
+
// do nothing to x
|
196 |
+
// do nothing to y
|
197 |
+
}
|
198 |
+
else if ($ar[0]=='xmidymin') {
|
199 |
+
$img_x += $w/2 - $img_w/2; // xMid
|
200 |
+
// do nothing to y
|
201 |
+
}
|
202 |
+
else if ($ar[0]=='xmaxymin') {
|
203 |
+
$img_x += $w - $img_w; // xMax
|
204 |
+
// do nothing to y
|
205 |
+
}
|
206 |
+
else if ($ar[0]=='xminymid') {
|
207 |
+
// do nothing to x
|
208 |
+
$img_y += $h/2 - $img_h/2; // yMid
|
209 |
+
}
|
210 |
+
else if ($ar[0]=='xmaxymid') {
|
211 |
+
$img_x += $w - $img_w; // xMax
|
212 |
+
$img_y += $h/2 - $img_h/2; // yMid
|
213 |
+
}
|
214 |
+
else if ($ar[0]=='xminymax') {
|
215 |
+
// do nothing to x
|
216 |
+
$img_y += $h - $img_h; // yMax
|
217 |
+
}
|
218 |
+
else if ($ar[0]=='xmidymax') {
|
219 |
+
$img_x += $w/2 - $img_w/2; // xMid
|
220 |
+
$img_y += $h - $img_h; // yMax
|
221 |
+
}
|
222 |
+
else if ($ar[0]=='xmaxymax') {
|
223 |
+
$img_x += $w - $img_w; // xMax
|
224 |
+
$img_y += $h - $img_h; // yMax
|
225 |
+
}
|
226 |
+
else { // xMidYMid (the default)
|
227 |
+
$img_x += $w/2 - $img_w/2; // xMid
|
228 |
+
$img_y += $h/2 - $img_h/2; // yMid
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
// Output
|
233 |
+
if ($meetOrSlice == 'slice') { // need to add a clipping path to reference rectangle
|
234 |
+
$s = ' q 0 w '; // Line width=0
|
235 |
+
$s .= sprintf('%.3F %.3F m ', ($x)*$this->kp, (-($y+$h))*$this->kp); // start point TL before the arc
|
236 |
+
$s .= sprintf('%.3F %.3F l ', ($x)*$this->kp, (-($y))*$this->kp); // line to BL
|
237 |
+
$s .= sprintf('%.3F %.3F l ', ($x+$w)*$this->kp, (-($y))*$this->kp); // line to BR
|
238 |
+
$s .= sprintf('%.3F %.3F l ', ($x+$w)*$this->kp, (-($y+$h))*$this->kp); // line to TR
|
239 |
+
$s .= sprintf('%.3F %.3F l ', ($x)*$this->kp, (-($y+$h))*$this->kp); // line to TL
|
240 |
+
$s .= ' W n '; // Ends path no-op & Sets the clipping path
|
241 |
+
$this->svgWriteString($s);
|
242 |
+
}
|
243 |
+
|
244 |
+
$outstring = sprintf(" q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q ",$img_w*$this->kp, $img_h*$this->kp, $img_x*$this->kp, -($img_y+$img_h)*$this->kp, $info['i'] );
|
245 |
+
$this->svgWriteString($outstring);
|
246 |
+
|
247 |
+
if ($meetOrSlice == 'slice') { // need to end clipping path
|
248 |
+
$this->svgWriteString(' Q ');
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
|
253 |
+
function svgGradient($gradient_info, $attribs, $element){
|
254 |
+
$n = count($this->mpdf_ref->gradients)+1;
|
255 |
+
|
256 |
+
// Get bounding dimensions of element
|
257 |
+
$w = 100;
|
258 |
+
$h = 100;
|
259 |
+
$x_offset = 0;
|
260 |
+
$y_offset = 0;
|
261 |
+
if ($element=='rect') {
|
262 |
+
$w = $attribs['width'];
|
263 |
+
$h = $attribs['height'];
|
264 |
+
$x_offset = $attribs['x'];
|
265 |
+
$y_offset = $attribs['y'];
|
266 |
+
}
|
267 |
+
else if ($element=='ellipse') {
|
268 |
+
$w = $attribs['rx']*2;
|
269 |
+
$h = $attribs['ry']*2;
|
270 |
+
$x_offset = $attribs['cx']-$attribs['rx'];
|
271 |
+
$y_offset = $attribs['cy']-$attribs['ry'];
|
272 |
+
}
|
273 |
+
else if ($element=='circle') {
|
274 |
+
$w = $attribs['r']*2;
|
275 |
+
$h = $attribs['r']*2;
|
276 |
+
$x_offset = $attribs['cx']-$attribs['r'];
|
277 |
+
$y_offset = $attribs['cy']-$attribs['r'];
|
278 |
+
}
|
279 |
+
else if ($element=='polygon') {
|
280 |
+
$pts = preg_split('/[ ,]+/', trim($attribs['points']));
|
281 |
+
$maxr=$maxb=0;
|
282 |
+
$minl=$mint=999999;
|
283 |
+
for ($i=0;$i<count($pts); $i++) {
|
284 |
+
if ($i % 2 == 0) { // x values
|
285 |
+
$minl = min($minl,$pts[$i]);
|
286 |
+
$maxr = max($maxr,$pts[$i]);
|
287 |
+
}
|
288 |
+
else { // y values
|
289 |
+
$mint = min($mint,$pts[$i]);
|
290 |
+
$maxb = max($maxb,$pts[$i]);
|
291 |
+
}
|
292 |
+
}
|
293 |
+
$w = $maxr-$minl;
|
294 |
+
$h = $maxb-$mint;
|
295 |
+
$x_offset = $minl;
|
296 |
+
$y_offset = $mint;
|
297 |
+
}
|
298 |
+
else if ($element=='path') {
|
299 |
+
if (is_array($this->pathBBox) && $this->pathBBox[2]>0) {
|
300 |
+
$w = $this->pathBBox[2];
|
301 |
+
$h = $this->pathBBox[3];
|
302 |
+
$x_offset = $this->pathBBox[0];
|
303 |
+
$y_offset = $this->pathBBox[1];
|
304 |
+
}
|
305 |
+
else {
|
306 |
+
preg_match_all('/([a-z]|[A-Z])([ ,\-.\d]+)*/', $attribs['d'], $commands, PREG_SET_ORDER);
|
307 |
+
$maxr=$maxb=0;
|
308 |
+
$minl=$mint=999999;
|
309 |
+
foreach($commands as $c){
|
310 |
+
if(count($c)==3){
|
311 |
+
list($tmp, $cmd, $arg) = $c;
|
312 |
+
if ($cmd=='M' || $cmd=='L' || $cmd=='C' || $cmd=='S' || $cmd=='Q' || $cmd=='T') {
|
313 |
+
$pts = preg_split('/[ ,]+/', trim($arg));
|
314 |
+
for ($i=0;$i<count($pts); $i++) {
|
315 |
+
if ($i % 2 == 0) { // x values
|
316 |
+
$minl = min($minl,$pts[$i]);
|
317 |
+
$maxr = max($maxr,$pts[$i]);
|
318 |
+
}
|
319 |
+
else { // y values
|
320 |
+
$mint = min($mint,$pts[$i]);
|
321 |
+
$maxb = max($maxb,$pts[$i]);
|
322 |
+
}
|
323 |
+
}
|
324 |
+
}
|
325 |
+
if ($cmd=='H') { // sets new x
|
326 |
+
$minl = min($minl,$arg);
|
327 |
+
$maxr = max($maxr,$arg);
|
328 |
+
}
|
329 |
+
if ($cmd=='V') { // sets new y
|
330 |
+
$mint = min($mint,$arg);
|
331 |
+
$maxb = max($maxb,$arg);
|
332 |
+
}
|
333 |
+
}
|
334 |
+
}
|
335 |
+
$w = $maxr-$minl;
|
336 |
+
$h = $maxb-$mint;
|
337 |
+
$x_offset = $minl;
|
338 |
+
$y_offset = $mint;
|
339 |
+
}
|
340 |
+
}
|
341 |
+
if (!$w || $w==-999999) { $w = 100; }
|
342 |
+
if (!$h || $h==-999999) { $h = 100; }
|
343 |
+
if ($x_offset==999999) { $x_offset = 0; }
|
344 |
+
if ($y_offset==999999) { $y_offset = 0; }
|
345 |
+
|
346 |
+
// TRANSFORMATIONS
|
347 |
+
$transformations = '';
|
348 |
+
if (isset($gradient_info['transform'])){
|
349 |
+
preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is',$gradient_info['transform'],$m);
|
350 |
+
if (count($m[0])) {
|
351 |
+
for($i=0; $i<count($m[0]); $i++) {
|
352 |
+
$c = strtolower($m[1][$i]);
|
353 |
+
$v = trim($m[2][$i]);
|
354 |
+
$vv = preg_split('/[ ,]+/',$v);
|
355 |
+
if ($c=='matrix' && count($vv)==6) {
|
356 |
+
// Note angle of rotation is reversed (from SVG to PDF), so vv[1] and vv[2] are negated
|
357 |
+
// cf svgDefineStyle()
|
358 |
+
$transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $vv[0], -$vv[1], -$vv[2], $vv[3], $vv[4]*$this->kp, -$vv[5]*$this->kp);
|
359 |
+
}
|
360 |
+
else if ($c=='translate' && count($vv)) {
|
361 |
+
$tm[4] = $vv[0];
|
362 |
+
if (count($vv)==2) { $t_y = -$vv[1]; }
|
363 |
+
else { $t_y = 0; }
|
364 |
+
$tm[5] = $t_y;
|
365 |
+
$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $tm[4]*$this->kp, $tm[5]*$this->kp);
|
366 |
+
}
|
367 |
+
else if ($c=='scale' && count($vv)) {
|
368 |
+
if (count($vv)==2) { $s_y = $vv[1]; }
|
369 |
+
else { $s_y = $vv[0]; }
|
370 |
+
$tm[0] = $vv[0];
|
371 |
+
$tm[3] = $s_y;
|
372 |
+
$transformations .= sprintf(' %.3F 0 0 %.3F 0 0 cm ', $tm[0], $tm[3]);
|
373 |
+
}
|
374 |
+
else if ($c=='rotate' && count($vv)) {
|
375 |
+
$tm[0] = cos(deg2rad(-$vv[0]));
|
376 |
+
$tm[1] = sin(deg2rad(-$vv[0]));
|
377 |
+
$tm[2] = -$tm[1];
|
378 |
+
$tm[3] = $tm[0];
|
379 |
+
if (count($vv)==3) {
|
380 |
+
$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $vv[1]*$this->kp, -$vv[2]*$this->kp);
|
381 |
+
}
|
382 |
+
$transformations .= sprintf(' %.3F %.3F %.3F %.3F 0 0 cm ', $tm[0], $tm[1], $tm[2], $tm[3]);
|
383 |
+
if (count($vv)==3) {
|
384 |
+
$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', -$vv[1]*$this->kp, $vv[2]*$this->kp);
|
385 |
+
}
|
386 |
+
}
|
387 |
+
else if ($c=='skewx' && count($vv)) {
|
388 |
+
$tm[2] = tan(deg2rad(-$vv[0]));
|
389 |
+
$transformations .= sprintf(' 1 0 %.3F 1 0 0 cm ', $tm[2]);
|
390 |
+
}
|
391 |
+
else if ($c=='skewy' && count($vv)) {
|
392 |
+
$tm[1] = tan(deg2rad(-$vv[0]));
|
393 |
+
$transformations .= sprintf(' 1 %.3F 0 1 0 0 cm ', $tm[1]);
|
394 |
+
}
|
395 |
+
|
396 |
+
}
|
397 |
+
}
|
398 |
+
}
|
399 |
+
|
400 |
+
|
401 |
+
$return = "";
|
402 |
+
|
403 |
+
if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
|
404 |
+
if ($transformations) { $return .= $transformations; }
|
405 |
+
}
|
406 |
+
$spread = 'P'; // pad
|
407 |
+
if (isset($gradient_info['spread'])) {
|
408 |
+
if (strtolower($gradient_info['spread'])=='reflect') { $spread = 'F'; } // reflect
|
409 |
+
else if (strtolower($gradient_info['spread'])=='repeat') { $spread = 'R'; } // repeat
|
410 |
+
}
|
411 |
+
|
412 |
+
|
413 |
+
for ($i=0; $i<(count($gradient_info['color'])); $i++) {
|
414 |
+
if (stristr($gradient_info['color'][$i]['offset'], '%')!== false) { $gradient_info['color'][$i]['offset'] = ($gradient_info['color'][$i]['offset']+0)/100; }
|
415 |
+
if (isset($gradient_info['color'][($i+1)]['offset']) && stristr($gradient_info['color'][($i+1)]['offset'], '%')!== false) { $gradient_info['color'][($i+1)]['offset'] = ($gradient_info['color'][($i+1)]['offset']+0)/100; }
|
416 |
+
if ($gradient_info['color'][$i]['offset']<0) { $gradient_info['color'][$i]['offset'] = 0; }
|
417 |
+
if ($gradient_info['color'][$i]['offset']>1) { $gradient_info['color'][$i]['offset'] = 1; }
|
418 |
+
if ($i>0) {
|
419 |
+
if ($gradient_info['color'][$i]['offset']<$gradient_info['color'][($i-1)]['offset']) {
|
420 |
+
$gradient_info['color'][$i]['offset']=$gradient_info['color'][($i-1)]['offset'];
|
421 |
+
}
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
if (isset($gradient_info['color'][0]['offset']) && $gradient_info['color'][0]['offset']>0) {
|
426 |
+
array_unshift($gradient_info['color'], $gradient_info['color'][0]);
|
427 |
+
$gradient_info['color'][0]['offset'] = 0;
|
428 |
+
}
|
429 |
+
$ns = count($gradient_info['color']);
|
430 |
+
if (isset($gradient_info['color'][($ns-1)]['offset']) && $gradient_info['color'][($ns-1)]['offset']<1) {
|
431 |
+
$gradient_info['color'][] = $gradient_info['color'][($ns-1)];
|
432 |
+
$gradient_info['color'][($ns)]['offset'] = 1;
|
433 |
+
}
|
434 |
+
$ns = count($gradient_info['color']);
|
435 |
+
|
436 |
+
|
437 |
+
|
438 |
+
|
439 |
+
if ($gradient_info['type'] == 'linear'){
|
440 |
+
// mPDF 4.4.003
|
441 |
+
if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
|
442 |
+
if (isset($gradient_info['info']['x1'])) { $gradient_info['info']['x1'] = ($gradient_info['info']['x1']-$x_offset) / $w; }
|
443 |
+
if (isset($gradient_info['info']['y1'])) { $gradient_info['info']['y1'] = ($gradient_info['info']['y1']-$y_offset) / $h; }
|
444 |
+
if (isset($gradient_info['info']['x2'])) { $gradient_info['info']['x2'] = ($gradient_info['info']['x2']-$x_offset) / $w; }
|
445 |
+
if (isset($gradient_info['info']['y2'])) { $gradient_info['info']['y2'] = ($gradient_info['info']['y2']-$y_offset) / $h; }
|
446 |
+
}
|
447 |
+
if (isset($gradient_info['info']['x1'])) { $x1 = $gradient_info['info']['x1']; }
|
448 |
+
else { $x1 = 0; }
|
449 |
+
if (isset($gradient_info['info']['y1'])) { $y1 = $gradient_info['info']['y1']; }
|
450 |
+
else { $y1 = 0; }
|
451 |
+
if (isset($gradient_info['info']['x2'])) { $x2 = $gradient_info['info']['x2']; }
|
452 |
+
else { $x2 = 1; }
|
453 |
+
if (isset($gradient_info['info']['y2'])) { $y2 = $gradient_info['info']['y2']; }
|
454 |
+
else { $y2 = 0; } // mPDF 6
|
455 |
+
|
456 |
+
if (stristr($x1, '%')!== false) { $x1 = ($x1+0)/100; }
|
457 |
+
if (stristr($x2, '%')!== false) { $x2 = ($x2+0)/100; }
|
458 |
+
if (stristr($y1, '%')!== false) { $y1 = ($y1+0)/100; }
|
459 |
+
if (stristr($y2, '%')!== false) { $y2 = ($y2+0)/100; }
|
460 |
+
|
461 |
+
// mPDF 5.0.042
|
462 |
+
$bboxw = $w;
|
463 |
+
$bboxh = $h;
|
464 |
+
$usex = $x_offset;
|
465 |
+
$usey = $y_offset;
|
466 |
+
$usew = $bboxw;
|
467 |
+
$useh = $bboxh;
|
468 |
+
if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
|
469 |
+
$angle = rad2deg(atan2(($gradient_info['info']['y2']-$gradient_info['info']['y1']), ($gradient_info['info']['x2']-$gradient_info['info']['x1'])));
|
470 |
+
if ($angle < 0) { $angle += 360; }
|
471 |
+
else if ($angle > 360) { $angle -= 360; }
|
472 |
+
if ($angle!=0 && $angle!=360 && $angle!=90 && $angle!=180 && $angle!=270) {
|
473 |
+
if ($w >= $h) {
|
474 |
+
$y1 *= $h/$w ;
|
475 |
+
$y2 *= $h/$w ;
|
476 |
+
$usew = $useh = $bboxw;
|
477 |
+
}
|
478 |
+
else {
|
479 |
+
$x1 *= $w/$h ;
|
480 |
+
$x2 *= $w/$h ;
|
481 |
+
$usew = $useh = $bboxh;
|
482 |
+
}
|
483 |
+
}
|
484 |
+
}
|
485 |
+
$a = $usew; // width
|
486 |
+
$d = -$useh; // height
|
487 |
+
$e = $usex; // x- offset
|
488 |
+
$f = -$usey; // -y-offset
|
489 |
+
|
490 |
+
$return .= sprintf('%.3F 0 0 %.3F %.3F %.3F cm ', $a*$this->kp, $d*$this->kp, $e*$this->kp, $f*$this->kp);
|
491 |
+
|
492 |
+
if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='objectboundingbox') {
|
493 |
+
if ($transformations) { $return .= $transformations; }
|
494 |
+
}
|
495 |
+
|
496 |
+
$trans = false;
|
497 |
+
|
498 |
+
if ($spread=='R' || $spread=='F') { // Repeat / Reflect
|
499 |
+
$offs = array();
|
500 |
+
for($i=0;$i<$ns;$i++) {
|
501 |
+
$offs[$i] = $gradient_info['color'][$i]['offset'];
|
502 |
+
}
|
503 |
+
$gp = 0;
|
504 |
+
$inside=true;
|
505 |
+
while($inside) {
|
506 |
+
$gp++;
|
507 |
+
for($i=0;$i<$ns;$i++) {
|
508 |
+
if ($spread=='F' && ($gp % 2) == 1) { // Reflect
|
509 |
+
$gradient_info['color'][(($ns*$gp)+$i)] = $gradient_info['color'][(($ns*($gp-1))+($ns-$i-1))];
|
510 |
+
$tmp = $gp+(1-$offs[($ns-$i-1)]) ;
|
511 |
+
$gradient_info['color'][(($ns*$gp)+$i)]['offset'] = $tmp;
|
512 |
+
}
|
513 |
+
else { // Reflect
|
514 |
+
$gradient_info['color'][(($ns*$gp)+$i)] = $gradient_info['color'][$i];
|
515 |
+
$tmp = $gp+$offs[$i] ;
|
516 |
+
$gradient_info['color'][(($ns*$gp)+$i)]['offset'] = $tmp;
|
517 |
+
}
|
518 |
+
// IF STILL INSIDE BOX OR STILL VALID
|
519 |
+
// Point on axis to test
|
520 |
+
$px1 = $x1 + ($x2-$x1)*$tmp;
|
521 |
+
$py1 = $y1 + ($y2-$y1)*$tmp;
|
522 |
+
// Get perpendicular axis
|
523 |
+
$alpha = atan2($y2-$y1, $x2-$x1);
|
524 |
+
$alpha += M_PI/2; // rotate 90 degrees
|
525 |
+
// Get arbitrary point to define line perpendicular to axis
|
526 |
+
$px2 = $px1+cos($alpha);
|
527 |
+
$py2 = $py1+sin($alpha);
|
528 |
+
|
529 |
+
$res1 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 0, 1); // $x=0 vert axis
|
530 |
+
$res2 = _testIntersect($px1, $py1, $px2, $py2, 1, 0, 1, 1); // $x=1 vert axis
|
531 |
+
$res3 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 1, 0); // $y=0 horiz axis
|
532 |
+
$res4 = _testIntersect($px1, $py1, $px2, $py2, 0, 1, 1, 1); // $y=1 horiz axis
|
533 |
+
if (!$res1 && !$res2 && !$res3 && !$res4) { $inside = false; }
|
534 |
+
}
|
535 |
+
}
|
536 |
+
|
537 |
+
$inside=true;
|
538 |
+
$gp = 0;
|
539 |
+
while($inside) {
|
540 |
+
$gp++;
|
541 |
+
$newarr = array();
|
542 |
+
for($i=0;$i<$ns;$i++) {
|
543 |
+
if ($spread=='F') { // Reflect
|
544 |
+
$newarr[$i] = $gradient_info['color'][($ns-$i-1)];
|
545 |
+
if (($gp % 2) == 1) {
|
546 |
+
$tmp = -$gp+(1-$offs[($ns-$i-1)]);
|
547 |
+
$newarr[$i]['offset'] = $tmp;
|
548 |
+
}
|
549 |
+
else {
|
550 |
+
$tmp = -$gp+$offs[$i];
|
551 |
+
$newarr[$i]['offset'] = $tmp;
|
552 |
+
}
|
553 |
+
}
|
554 |
+
else { // Reflect
|
555 |
+
$newarr[$i] = $gradient_info['color'][$i];
|
556 |
+
$tmp = -$gp+$offs[$i];
|
557 |
+
$newarr[$i]['offset'] = $tmp;
|
558 |
+
}
|
559 |
+
|
560 |
+
// IF STILL INSIDE BOX OR STILL VALID
|
561 |
+
// Point on axis to test
|
562 |
+
$px1 = $x1 + ($x2-$x1)*$tmp;
|
563 |
+
$py1 = $y1 + ($y2-$y1)*$tmp;
|
564 |
+
// Get perpendicular axis
|
565 |
+
$alpha = atan2($y2-$y1, $x2-$x1);
|
566 |
+
$alpha += M_PI/2; // rotate 90 degrees
|
567 |
+
// Get arbitrary point to define line perpendicular to axis
|
568 |
+
$px2 = $px1+cos($alpha);
|
569 |
+
$py2 = $py1+sin($alpha);
|
570 |
+
|
571 |
+
$res1 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 0, 1); // $x=0 vert axis
|
572 |
+
$res2 = _testIntersect($px1, $py1, $px2, $py2, 1, 0, 1, 1); // $x=1 vert axis
|
573 |
+
$res3 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 1, 0); // $y=0 horiz axis
|
574 |
+
$res4 = _testIntersect($px1, $py1, $px2, $py2, 0, 1, 1, 1); // $y=1 horiz axis
|
575 |
+
if (!$res1 && !$res2 && !$res3 && !$res4) { $inside = false; }
|
576 |
+
}
|
577 |
+
for($i=($ns-1);$i>=0;$i--) {
|
578 |
+
if (isset($newarr[$i]['offset'])) array_unshift($gradient_info['color'], $newarr[$i]);
|
579 |
+
}
|
580 |
+
}
|
581 |
+
}
|
582 |
+
|
583 |
+
// Gradient STOPs
|
584 |
+
$stops = count($gradient_info['color']);
|
585 |
+
if ($stops < 2) { return ''; }
|
586 |
+
|
587 |
+
$range = $gradient_info['color'][count($gradient_info['color'])-1]['offset']-$gradient_info['color'][0]['offset'];
|
588 |
+
$min = $gradient_info['color'][0]['offset'];
|
589 |
+
|
590 |
+
for ($i=0; $i<($stops); $i++) {
|
591 |
+
if (!$gradient_info['color'][$i]['color']) {
|
592 |
+
if ($gradient_info['colorspace']=='RGB') $gradient_info['color'][$i]['color'] = '0 0 0';
|
593 |
+
else if ($gradient_info['colorspace']=='Gray') $gradient_info['color'][$i]['color'] = '0';
|
594 |
+
else if ($gradient_info['colorspace']=='CMYK') $gradient_info['color'][$i]['color'] = '1 1 1 1';
|
595 |
+
}
|
596 |
+
$offset = ($gradient_info['color'][$i]['offset'] - $min)/$range;
|
597 |
+
$this->mpdf_ref->gradients[$n]['stops'][] = array(
|
598 |
+
'col' => $gradient_info['color'][$i]['color'],
|
599 |
+
'opacity' => $gradient_info['color'][$i]['opacity'],
|
600 |
+
'offset' => $offset);
|
601 |
+
if ($gradient_info['color'][$i]['opacity']<1) { $trans = true; }
|
602 |
+
}
|
603 |
+
$grx1 = $x1 + ($x2-$x1)*$gradient_info['color'][0]['offset'];
|
604 |
+
$gry1 = $y1 + ($y2-$y1)*$gradient_info['color'][0]['offset'];
|
605 |
+
$grx2 = $x1 + ($x2-$x1)*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
|
606 |
+
$gry2 = $y1 + ($y2-$y1)*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
|
607 |
+
|
608 |
+
$this->mpdf_ref->gradients[$n]['coords']=array($grx1, $gry1, $grx2, $gry2);
|
609 |
+
|
610 |
+
$this->mpdf_ref->gradients[$n]['colorspace'] = $gradient_info['colorspace'];
|
611 |
+
|
612 |
+
$this->mpdf_ref->gradients[$n]['type'] = 2;
|
613 |
+
$this->mpdf_ref->gradients[$n]['fo'] = true;
|
614 |
+
|
615 |
+
$this->mpdf_ref->gradients[$n]['extend']=array('true','true');
|
616 |
+
if ($trans) {
|
617 |
+
$this->mpdf_ref->gradients[$n]['trans'] = true;
|
618 |
+
$return .= ' /TGS'.($n).' gs ';
|
619 |
+
}
|
620 |
+
$return .= ' /Sh'.($n).' sh ';
|
621 |
+
$return .= " Q\n";
|
622 |
+
}
|
623 |
+
else if ($gradient_info['type'] == 'radial'){
|
624 |
+
if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
|
625 |
+
if ($w > $h) { $h = $w; }
|
626 |
+
else { $w = $h; }
|
627 |
+
if (isset($gradient_info['info']['x0'])) { $gradient_info['info']['x0'] = ($gradient_info['info']['x0']-$x_offset) / $w; }
|
628 |
+
if (isset($gradient_info['info']['y0'])) { $gradient_info['info']['y0'] = ($gradient_info['info']['y0']-$y_offset) / $h; }
|
629 |
+
if (isset($gradient_info['info']['x1'])) { $gradient_info['info']['x1'] = ($gradient_info['info']['x1']-$x_offset) / $w; }
|
630 |
+
if (isset($gradient_info['info']['y1'])) { $gradient_info['info']['y1'] = ($gradient_info['info']['y1']-$y_offset) / $h; }
|
631 |
+
if (isset($gradient_info['info']['r'])) { $gradient_info['info']['rx'] = $gradient_info['info']['r'] / $w; }
|
632 |
+
if (isset($gradient_info['info']['r'])) { $gradient_info['info']['ry'] = $gradient_info['info']['r'] / $h; }
|
633 |
+
}
|
634 |
+
|
635 |
+
if (isset($gradient_info['info']['x0'])) { $x0 = $gradient_info['info']['x0']; }
|
636 |
+
else { $x0 = 0.5; }
|
637 |
+
if (isset($gradient_info['info']['y0'])) { $y0 = $gradient_info['info']['y0']; }
|
638 |
+
else { $y0 = 0.5; }
|
639 |
+
if (isset($gradient_info['info']['rx'])) { $rx = $gradient_info['info']['rx']; }
|
640 |
+
else if (isset($gradient_info['info']['r'])) { $rx = $gradient_info['info']['r']; }
|
641 |
+
else { $rx = 0.5; }
|
642 |
+
if (isset($gradient_info['info']['ry'])) { $ry = $gradient_info['info']['ry']; }
|
643 |
+
else if (isset($gradient_info['info']['r'])) { $ry = $gradient_info['info']['r']; }
|
644 |
+
else { $ry = 0.5; }
|
645 |
+
if (isset($gradient_info['info']['x1'])) { $x1 = $gradient_info['info']['x1']; }
|
646 |
+
else { $x1 = $x0; }
|
647 |
+
if (isset($gradient_info['info']['y1'])) { $y1 = $gradient_info['info']['y1']; }
|
648 |
+
else { $y1 = $y0; }
|
649 |
+
|
650 |
+
if (stristr($x1, '%')!== false) { $x1 = ($x1+0)/100; }
|
651 |
+
if (stristr($x0, '%')!== false) { $x0 = ($x0+0)/100; }
|
652 |
+
if (stristr($y1, '%')!== false) { $y1 = ($y1+0)/100; }
|
653 |
+
if (stristr($y0, '%')!== false) { $y0 = ($y0+0)/100; }
|
654 |
+
if (stristr($rx, '%')!== false) { $rx = ($rx+0)/100; }
|
655 |
+
if (stristr($ry, '%')!== false) { $ry = ($ry+0)/100; }
|
656 |
+
|
657 |
+
$bboxw = $w;
|
658 |
+
$bboxh = $h;
|
659 |
+
$usex = $x_offset;
|
660 |
+
$usey = $y_offset;
|
661 |
+
$usew = $bboxw;
|
662 |
+
$useh = $bboxh;
|
663 |
+
if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
|
664 |
+
$angle = rad2deg(atan2(($gradient_info['info']['y0']-$gradient_info['info']['y1']), ($gradient_info['info']['x0']-$gradient_info['info']['x1'])));
|
665 |
+
if ($angle < 0) { $angle += 360; }
|
666 |
+
else if ($angle > 360) { $angle -= 360; }
|
667 |
+
if ($angle!=0 && $angle!=360 && $angle!=90 && $angle!=180 && $angle!=270) {
|
668 |
+
if ($w >= $h) {
|
669 |
+
$y1 *= $h/$w ;
|
670 |
+
$y0 *= $h/$w ;
|
671 |
+
$rx *= $h/$w ;
|
672 |
+
$ry *= $h/$w ;
|
673 |
+
$usew = $useh = $bboxw;
|
674 |
+
}
|
675 |
+
else {
|
676 |
+
$x1 *= $w/$h ;
|
677 |
+
$x0 *= $w/$h ;
|
678 |
+
$rx *= $w/$h ;
|
679 |
+
$ry *= $w/$h ;
|
680 |
+
$usew = $useh = $bboxh;
|
681 |
+
}
|
682 |
+
}
|
683 |
+
}
|
684 |
+
$a = $usew; // width
|
685 |
+
$d = -$useh; // height
|
686 |
+
$e = $usex; // x- offset
|
687 |
+
$f = -$usey; // -y-offset
|
688 |
+
|
689 |
+
$r = $rx;
|
690 |
+
|
691 |
+
|
692 |
+
$return .= sprintf('%.3F 0 0 %.3F %.3F %.3F cm ', $a*$this->kp, $d*$this->kp, $e*$this->kp, $f*$this->kp);
|
693 |
+
|
694 |
+
// mPDF 5.0.039
|
695 |
+
if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='objectboundingbox') {
|
696 |
+
if ($transformations) { $return .= $transformations; }
|
697 |
+
}
|
698 |
+
|
699 |
+
// mPDF 5.7.4
|
700 |
+
// x1 and y1 (fx, fy) should be inside the circle defined by x0 y0 (cx, cy)
|
701 |
+
// "If the point defined by fx and fy lies outside the circle defined by cx, cy and r, then the user agent shall set
|
702 |
+
// the focal point to the intersection of the line from (cx, cy) to (fx, fy) with the circle defined by cx, cy and r."
|
703 |
+
while (pow(($x1-$x0),2) + pow(($y1 - $y0),2) >= pow($r,2)) {
|
704 |
+
// Gradually move along fx,fy towards cx,cy in 100'ths until meets criteria
|
705 |
+
$x1 -= ($x1-$x0)/100;
|
706 |
+
$y1 -= ($y1-$y0)/100;
|
707 |
+
}
|
708 |
+
|
709 |
+
|
710 |
+
if ($spread=='R' || $spread=='F') { // Repeat / Reflect
|
711 |
+
$offs = array();
|
712 |
+
for($i=0;$i<$ns;$i++) {
|
713 |
+
$offs[$i] = $gradient_info['color'][$i]['offset'];
|
714 |
+
}
|
715 |
+
$gp = 0;
|
716 |
+
$inside=true;
|
717 |
+
while($inside) {
|
718 |
+
$gp++;
|
719 |
+
for($i=0;$i<$ns;$i++) {
|
720 |
+
if ($spread=='F' && ($gp % 2) == 1) { // Reflect
|
721 |
+
$gradient_info['color'][(($ns*$gp)+$i)] = $gradient_info['color'][(($ns*($gp-1))+($ns-$i-1))];
|
722 |
+
$tmp = $gp+(1-$offs[($ns-$i-1)]) ;
|
723 |
+
$gradient_info['color'][(($ns*$gp)+$i)]['offset'] = $tmp;
|
724 |
+
}
|
725 |
+
else { // Reflect
|
726 |
+
$gradient_info['color'][(($ns*$gp)+$i)] = $gradient_info['color'][$i];
|
727 |
+
$tmp = $gp+$offs[$i] ;
|
728 |
+
$gradient_info['color'][(($ns*$gp)+$i)]['offset'] = $tmp;
|
729 |
+
}
|
730 |
+
// IF STILL INSIDE BOX OR STILL VALID
|
731 |
+
// TEST IF circle (perimeter) intersects with
|
732 |
+
// or is enclosed
|
733 |
+
// Point on axis to test
|
734 |
+
$px = $x1 + ($x0-$x1)*$tmp;
|
735 |
+
$py = $y1 + ($y0-$y1)*$tmp;
|
736 |
+
$pr = $r*$tmp;
|
737 |
+
$res = _testIntersectCircle($px, $py, $pr);
|
738 |
+
if (!$res) { $inside = false; }
|
739 |
+
}
|
740 |
+
}
|
741 |
+
}
|
742 |
+
|
743 |
+
// Gradient STOPs
|
744 |
+
$stops = count($gradient_info['color']);
|
745 |
+
if ($stops < 2) { return ''; }
|
746 |
+
|
747 |
+
$range = $gradient_info['color'][count($gradient_info['color'])-1]['offset']-$gradient_info['color'][0]['offset'];
|
748 |
+
$min = $gradient_info['color'][0]['offset'];
|
749 |
+
|
750 |
+
for ($i=0; $i<($stops); $i++) {
|
751 |
+
if (!$gradient_info['color'][$i]['color']) {
|
752 |
+
if ($gradient_info['colorspace']=='RGB') $gradient_info['color'][$i]['color'] = '0 0 0';
|
753 |
+
else if ($gradient_info['colorspace']=='Gray') $gradient_info['color'][$i]['color'] = '0';
|
754 |
+
else if ($gradient_info['colorspace']=='CMYK') $gradient_info['color'][$i]['color'] = '1 1 1 1';
|
755 |
+
}
|
756 |
+
$offset = ($gradient_info['color'][$i]['offset'] - $min)/$range;
|
757 |
+
$this->mpdf_ref->gradients[$n]['stops'][] = array(
|
758 |
+
'col' => $gradient_info['color'][$i]['color'],
|
759 |
+
'opacity' => $gradient_info['color'][$i]['opacity'],
|
760 |
+
'offset' => $offset);
|
761 |
+
if ($gradient_info['color'][$i]['opacity']<1) { $trans = true; }
|
762 |
+
}
|
763 |
+
$grx1 = $x1 + ($x0-$x1)*$gradient_info['color'][0]['offset'];
|
764 |
+
$gry1 = $y1 + ($y0-$y1)*$gradient_info['color'][0]['offset'];
|
765 |
+
$grx2 = $x1 + ($x0-$x1)*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
|
766 |
+
$gry2 = $y1 + ($y0-$y1)*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
|
767 |
+
$grir = $r*$gradient_info['color'][0]['offset'];
|
768 |
+
$grr = $r*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
|
769 |
+
|
770 |
+
$this->mpdf_ref->gradients[$n]['coords']=array($grx1, $gry1, $grx2, $gry2, abs($grr), abs($grir) );
|
771 |
+
|
772 |
+
$this->mpdf_ref->gradients[$n]['colorspace'] = $gradient_info['colorspace'];
|
773 |
+
|
774 |
+
$this->mpdf_ref->gradients[$n]['type'] = 3;
|
775 |
+
$this->mpdf_ref->gradients[$n]['fo'] = true;
|
776 |
+
|
777 |
+
$this->mpdf_ref->gradients[$n]['extend']=array('true','true');
|
778 |
+
if (isset($trans) && $trans) {
|
779 |
+
$this->mpdf_ref->gradients[$n]['trans'] = true;
|
780 |
+
$return .= ' /TGS'.($n).' gs ';
|
781 |
+
}
|
782 |
+
$return .= ' /Sh'.($n).' sh ';
|
783 |
+
$return .= " Q\n";
|
784 |
+
|
785 |
+
|
786 |
+
}
|
787 |
+
|
788 |
+
return $return;
|
789 |
+
}
|
790 |
+
|
791 |
+
|
792 |
+
function svgOffset ($attribs){
|
793 |
+
// save all <svg> tag attributes
|
794 |
+
$this->svg_attribs = $attribs;
|
795 |
+
if(isset($this->svg_attribs['viewBox'])) {
|
796 |
+
$vb = preg_split('/\s+/is', trim($this->svg_attribs['viewBox']));
|
797 |
+
if (count($vb)==4) {
|
798 |
+
$this->svg_info['x'] = $vb[0];
|
799 |
+
$this->svg_info['y'] = $vb[1];
|
800 |
+
$this->svg_info['w'] = $vb[2];
|
801 |
+
$this->svg_info['h'] = $vb[3];
|
802 |
+
// return;
|
803 |
+
}
|
804 |
+
}
|
805 |
+
$svg_w = 0;
|
806 |
+
$svg_h = 0;
|
807 |
+
if (isset($attribs['width']) && $attribs['width']) $svg_w = $this->mpdf_ref->ConvertSize($attribs['width']); // mm (interprets numbers as pixels)
|
808 |
+
if (isset($attribs['height']) && $attribs['height']) $svg_h = $this->mpdf_ref->ConvertSize($attribs['height']); // mm
|
809 |
+
|
810 |
+
///*
|
811 |
+
// mPDF 5.0.005
|
812 |
+
if (isset($this->svg_info['w']) && $this->svg_info['w']) { // if 'w' set by viewBox
|
813 |
+
if ($svg_w) { // if width also set, use these values to determine to set size of "pixel"
|
814 |
+
$this->kp *= ($svg_w/0.2645) / $this->svg_info['w'];
|
815 |
+
$this->kf = ($svg_w/0.2645) / $this->svg_info['w'];
|
816 |
+
}
|
817 |
+
else if ($svg_h) {
|
818 |
+
$this->kp *= ($svg_h/0.2645) / $this->svg_info['h'];
|
819 |
+
$this->kf = ($svg_h/0.2645) / $this->svg_info['h'];
|
820 |
+
}
|
821 |
+
return;
|
822 |
+
}
|
823 |
+
//*/
|
824 |
+
|
825 |
+
// Added to handle file without height or width specified
|
826 |
+
if (!$svg_w && !$svg_h) { $svg_w = $svg_h = $this->mpdf_ref->blk[$this->mpdf_ref->blklvl]['inner_width'] ; } // DEFAULT
|
827 |
+
if (!$svg_w) { $svg_w = $svg_h; }
|
828 |
+
if (!$svg_h) { $svg_h = $svg_w; }
|
829 |
+
|
830 |
+
$this->svg_info['x'] = 0;
|
831 |
+
$this->svg_info['y'] = 0;
|
832 |
+
$this->svg_info['w'] = $svg_w/0.2645; // mm->pixels
|
833 |
+
$this->svg_info['h'] = $svg_h/0.2645; // mm->pixels
|
834 |
+
|
835 |
+
}
|
836 |
+
|
837 |
+
|
838 |
+
//
|
839 |
+
// check if points are within svg, if not, set to max
|
840 |
+
function svg_overflow($x,$y)
|
841 |
+
{
|
842 |
+
$x2 = $x;
|
843 |
+
$y2 = $y;
|
844 |
+
if(isset($this->svg_attribs['overflow']))
|
845 |
+
{
|
846 |
+
if($this->svg_attribs['overflow'] == 'hidden')
|
847 |
+
{
|
848 |
+
// Not sure if this is supposed to strip off units, but since I dont use any I will omlt this step
|
849 |
+
$svg_w = preg_replace("/([0-9\.]*)(.*)/i","$1",$this->svg_attribs['width']);
|
850 |
+
$svg_h = preg_replace("/([0-9\.]*)(.*)/i","$1",$this->svg_attribs['height']);
|
851 |
+
|
852 |
+
// $xmax = floor($this->svg_attribs['width']);
|
853 |
+
$xmax = floor($svg_w);
|
854 |
+
$xmin = 0;
|
855 |
+
// $ymax = floor(($this->svg_attribs['height'] * -1));
|
856 |
+
$ymax = floor(($svg_h * -1));
|
857 |
+
$ymin = 0;
|
858 |
+
|
859 |
+
if($x > $xmax) $x2 = $xmax; // right edge
|
860 |
+
if($x < $xmin) $x2 = $xmin; // left edge
|
861 |
+
if($y < $ymax) $y2 = $ymax; // bottom
|
862 |
+
if($y > $ymin) $y2 = $ymin; // top
|
863 |
+
|
864 |
+
}
|
865 |
+
}
|
866 |
+
|
867 |
+
|
868 |
+
return array( 'x' => $x2, 'y' => $y2);
|
869 |
+
}
|
870 |
+
|
871 |
+
|
872 |
+
|
873 |
+
function svgDefineStyle($critere_style){
|
874 |
+
|
875 |
+
$tmp = count($this->svg_style)-1;
|
876 |
+
$current_style = $this->svg_style[$tmp];
|
877 |
+
|
878 |
+
unset($current_style['transformations']);
|
879 |
+
|
880 |
+
// TRANSFORM SCALE
|
881 |
+
$transformations = '';
|
882 |
+
if (isset($critere_style['transform'])){
|
883 |
+
preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is',$critere_style['transform'],$m);
|
884 |
+
if (count($m[0])) {
|
885 |
+
for($i=0; $i<count($m[0]); $i++) {
|
886 |
+
$c = strtolower($m[1][$i]);
|
887 |
+
$v = trim($m[2][$i]);
|
888 |
+
$vv = preg_split('/[ ,]+/',$v);
|
889 |
+
if ($c=='matrix' && count($vv)==6) {
|
890 |
+
// mPDF 5.0.039
|
891 |
+
// Note angle of rotation is reversed (from SVG to PDF), so vv[1] and vv[2] are negated
|
892 |
+
$transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $vv[0], -$vv[1], -$vv[2], $vv[3], $vv[4]*$this->kp, -$vv[5]*$this->kp);
|
893 |
+
|
894 |
+
/*
|
895 |
+
// The long way of doing this??
|
896 |
+
// need to reverse angle of rotation from SVG to PDF
|
897 |
+
$sx=sqrt(pow($vv[0],2)+pow($vv[2],2));
|
898 |
+
if ($vv[0] < 0) { $sx *= -1; } // change sign
|
899 |
+
$sy=sqrt(pow($vv[1],2)+pow($vv[3],2));
|
900 |
+
if ($vv[3] < 0) { $sy *= -1; } // change sign
|
901 |
+
|
902 |
+
// rotation angle is
|
903 |
+
$t=atan2($vv[1],$vv[3]);
|
904 |
+
$t=atan2(-$vv[2],$vv[0]); // Should be the same value or skew has been applied
|
905 |
+
|
906 |
+
// Reverse angle
|
907 |
+
$t *= -1;
|
908 |
+
|
909 |
+
// Rebuild matrix
|
910 |
+
$ma = $sx * cos($t);
|
911 |
+
$mb = $sy * sin($t);
|
912 |
+
$mc = -$sx * sin($t);
|
913 |
+
$md = $sy * cos($t);
|
914 |
+
|
915 |
+
// $transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $ma, $mb, $mc, $md, $vv[4]*$this->kp, -$vv[5]*$this->kp);
|
916 |
+
*/
|
917 |
+
|
918 |
+
}
|
919 |
+
else if ($c=='translate' && count($vv)) {
|
920 |
+
$tm[4] = $vv[0];
|
921 |
+
if (count($vv)==2) { $t_y = -$vv[1]; }
|
922 |
+
else { $t_y = 0; }
|
923 |
+
$tm[5] = $t_y;
|
924 |
+
$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $tm[4]*$this->kp, $tm[5]*$this->kp);
|
925 |
+
}
|
926 |
+
else if ($c=='scale' && count($vv)) {
|
927 |
+
if (count($vv)==2) { $s_y = $vv[1]; }
|
928 |
+
else { $s_y = $vv[0]; }
|
929 |
+
$tm[0] = $vv[0];
|
930 |
+
$tm[3] = $s_y;
|
931 |
+
$transformations .= sprintf(' %.3F 0 0 %.3F 0 0 cm ', $tm[0], $tm[3]);
|
932 |
+
}
|
933 |
+
else if ($c=='rotate' && count($vv)) {
|
934 |
+
$tm[0] = cos(deg2rad(-$vv[0]));
|
935 |
+
$tm[1] = sin(deg2rad(-$vv[0]));
|
936 |
+
$tm[2] = -$tm[1];
|
937 |
+
$tm[3] = $tm[0];
|
938 |
+
if (count($vv)==3) {
|
939 |
+
$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $vv[1]*$this->kp, -$vv[2]*$this->kp);
|
940 |
+
}
|
941 |
+
$transformations .= sprintf(' %.3F %.3F %.3F %.3F 0 0 cm ', $tm[0], $tm[1], $tm[2], $tm[3]);
|
942 |
+
if (count($vv)==3) {
|
943 |
+
$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', -$vv[1]*$this->kp, $vv[2]*$this->kp);
|
944 |
+
}
|
945 |
+
}
|
946 |
+
else if ($c=='skewx' && count($vv)) {
|
947 |
+
$tm[2] = tan(deg2rad(-$vv[0]));
|
948 |
+
$transformations .= sprintf(' 1 0 %.3F 1 0 0 cm ', $tm[2]);
|
949 |
+
}
|
950 |
+
else if ($c=='skewy' && count($vv)) {
|
951 |
+
$tm[1] = tan(deg2rad(-$vv[0]));
|
952 |
+
$transformations .= sprintf(' 1 %.3F 0 1 0 0 cm ', $tm[1]);
|
953 |
+
}
|
954 |
+
|
955 |
+
}
|
956 |
+
}
|
957 |
+
$current_style['transformations'] = $transformations;
|
958 |
+
}
|
959 |
+
|
960 |
+
if (isset($critere_style['style'])){
|
961 |
+
if (preg_match('/fill:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/i',$critere_style['style'], $m)) { // mPDF 5.7.2
|
962 |
+
$current_style['fill'] = '#'.str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
|
963 |
+
}
|
964 |
+
else { $tmp = preg_replace("/(.*)fill:\s*([a-z0-9#_()]*|none)(.*)/i","$2",$critere_style['style']);
|
965 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['fill'] = $tmp; }
|
966 |
+
}
|
967 |
+
|
968 |
+
// mPDF 5.7.2
|
969 |
+
if ((preg_match("/[^-]opacity:\s*([a-z0-9.]*|none)/i",$critere_style['style'], $m) ||
|
970 |
+
preg_match("/^opacity:\s*([a-z0-9.]*|none)/i",$critere_style['style'], $m)) && $m[1]!='inherit') {
|
971 |
+
$current_style['fill-opacity'] = $m[1];
|
972 |
+
$current_style['stroke-opacity'] = $m[1];
|
973 |
+
}
|
974 |
+
|
975 |
+
$tmp = preg_replace("/(.*)fill-opacity:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
|
976 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['fill-opacity'] = $tmp;}
|
977 |
+
|
978 |
+
$tmp = preg_replace("/(.*)fill-rule:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
979 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['fill-rule'] = $tmp;}
|
980 |
+
|
981 |
+
if (preg_match('/stroke:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/',$critere_style['style'], $m)) {
|
982 |
+
$current_style['stroke'] = '#'.str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
|
983 |
+
}
|
984 |
+
else { $tmp = preg_replace("/(.*)stroke:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
985 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['stroke'] = $tmp; }
|
986 |
+
}
|
987 |
+
|
988 |
+
$tmp = preg_replace("/(.*)stroke-linecap:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
989 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-linecap'] = $tmp;}
|
990 |
+
|
991 |
+
$tmp = preg_replace("/(.*)stroke-linejoin:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
992 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-linejoin'] = $tmp;}
|
993 |
+
|
994 |
+
$tmp = preg_replace("/(.*)stroke-miterlimit:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
995 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-miterlimit'] = $tmp;}
|
996 |
+
|
997 |
+
$tmp = preg_replace("/(.*)stroke-opacity:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
|
998 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-opacity'] = $tmp; }
|
999 |
+
|
1000 |
+
$tmp = preg_replace("/(.*)stroke-width:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
|
1001 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-width'] = $tmp;}
|
1002 |
+
|
1003 |
+
$tmp = preg_replace("/(.*)stroke-dasharray:\s*([a-z0-9., ]*|none)(.*)/i","$2",$critere_style['style']);
|
1004 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-dasharray'] = $tmp;}
|
1005 |
+
|
1006 |
+
$tmp = preg_replace("/(.*)stroke-dashoffset:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
|
1007 |
+
if ($tmp && $tmp!='inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-dashoffset'] = $tmp;}
|
1008 |
+
|
1009 |
+
}
|
1010 |
+
// mPDF 5.7.2
|
1011 |
+
if(isset($critere_style['opacity']) && $critere_style['opacity']!= 'inherit'){
|
1012 |
+
$current_style['fill-opacity'] = $critere_style['opacity'];
|
1013 |
+
$current_style['stroke-opacity'] = $critere_style['opacity'];
|
1014 |
+
}
|
1015 |
+
|
1016 |
+
if(isset($critere_style['fill']) && $critere_style['fill']!= 'inherit'){
|
1017 |
+
$current_style['fill'] = $critere_style['fill'];
|
1018 |
+
}
|
1019 |
+
|
1020 |
+
if(isset($critere_style['fill-opacity']) && $critere_style['fill-opacity']!= 'inherit'){
|
1021 |
+
$current_style['fill-opacity'] = $critere_style['fill-opacity'];
|
1022 |
+
}
|
1023 |
+
|
1024 |
+
if(isset($critere_style['fill-rule']) && $critere_style['fill-rule']!= 'inherit'){
|
1025 |
+
$current_style['fill-rule'] = $critere_style['fill-rule'];
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
if(isset($critere_style['stroke']) && $critere_style['stroke']!= 'inherit'){
|
1029 |
+
$current_style['stroke'] = $critere_style['stroke'];
|
1030 |
+
}
|
1031 |
+
|
1032 |
+
if(isset($critere_style['stroke-linecap']) && $critere_style['stroke-linecap']!= 'inherit'){
|
1033 |
+
$current_style['stroke-linecap'] = $critere_style['stroke-linecap'];
|
1034 |
+
}
|
1035 |
+
|
1036 |
+
if(isset($critere_style['stroke-linejoin']) && $critere_style['stroke-linejoin']!= 'inherit'){
|
1037 |
+
$current_style['stroke-linejoin'] = $critere_style['stroke-linejoin'];
|
1038 |
+
}
|
1039 |
+
|
1040 |
+
if(isset($critere_style['stroke-miterlimit']) && $critere_style['stroke-miterlimit']!= 'inherit'){
|
1041 |
+
$current_style['stroke-miterlimit'] = $critere_style['stroke-miterlimit'];
|
1042 |
+
}
|
1043 |
+
|
1044 |
+
if(isset($critere_style['stroke-opacity']) && $critere_style['stroke-opacity']!= 'inherit'){
|
1045 |
+
$current_style['stroke-opacity'] = $critere_style['stroke-opacity'];
|
1046 |
+
}
|
1047 |
+
|
1048 |
+
if(isset($critere_style['stroke-width']) && $critere_style['stroke-width']!= 'inherit'){
|
1049 |
+
$current_style['stroke-width'] = $critere_style['stroke-width'];
|
1050 |
+
}
|
1051 |
+
|
1052 |
+
if(isset($critere_style['stroke-dasharray']) && $critere_style['stroke-dasharray']!= 'inherit'){
|
1053 |
+
$current_style['stroke-dasharray'] = $critere_style['stroke-dasharray'];
|
1054 |
+
}
|
1055 |
+
if(isset($critere_style['stroke-dashoffset']) && $critere_style['stroke-dashoffset']!= 'inherit'){
|
1056 |
+
$current_style['stroke-dashoffset'] = $critere_style['stroke-dashoffset'];
|
1057 |
+
}
|
1058 |
+
|
1059 |
+
// Used as indirect setting for currentColor
|
1060 |
+
if(isset($critere_style['color']) && $critere_style['color'] != 'inherit'){
|
1061 |
+
$current_style['color'] = $critere_style['color'];
|
1062 |
+
}
|
1063 |
+
|
1064 |
+
return $current_style;
|
1065 |
+
|
1066 |
+
}
|
1067 |
+
|
1068 |
+
//
|
1069 |
+
// Cette fonction ecrit le style dans le stream svg.
|
1070 |
+
function svgStyle($critere_style, $attribs, $element){
|
1071 |
+
$path_style = '';
|
1072 |
+
$fill_gradient = '';
|
1073 |
+
$w = '';
|
1074 |
+
$style = '';
|
1075 |
+
if (substr_count($critere_style['fill'],'url')>0 && $element != 'line'){
|
1076 |
+
//
|
1077 |
+
// couleur degrad�
|
1078 |
+
$id_gradient = preg_replace("/url\(#([\w_]*)\)/i","$1",$critere_style['fill']);
|
1079 |
+
if ($id_gradient != $critere_style['fill']) {
|
1080 |
+
if (isset($this->svg_gradient[$id_gradient])) {
|
1081 |
+
$fill_gradient = $this->svgGradient($this->svg_gradient[$id_gradient], $attribs, $element);
|
1082 |
+
if ($fill_gradient) {
|
1083 |
+
$path_style = "q ";
|
1084 |
+
$w = "W";
|
1085 |
+
$style .= 'N';
|
1086 |
+
}
|
1087 |
+
}
|
1088 |
+
}
|
1089 |
+
|
1090 |
+
}
|
1091 |
+
// Used as indirect setting for currentColor
|
1092 |
+
else if (strtolower($critere_style['fill']) == 'currentcolor' && $element != 'line'){
|
1093 |
+
$col = $this->mpdf_ref->ConvertColor($critere_style['color']);
|
1094 |
+
if ($col) {
|
1095 |
+
if ($col{0}==5) { $critere_style['fill-opacity'] = ord($col{4}/100); } // RGBa
|
1096 |
+
if ($col{0}==6) { $critere_style['fill-opacity'] = ord($col{5}/100); } // CMYKa
|
1097 |
+
$path_style .= $this->mpdf_ref->SetFColor($col, true).' ';
|
1098 |
+
$style .= 'F';
|
1099 |
+
}
|
1100 |
+
}
|
1101 |
+
else if ($critere_style['fill'] != 'none' && $element != 'line'){
|
1102 |
+
$col = $this->mpdf_ref->ConvertColor($critere_style['fill']);
|
1103 |
+
if ($col) {
|
1104 |
+
if ($col{0}==5) { $critere_style['fill-opacity'] = ord($col{4}/100); } // RGBa
|
1105 |
+
if ($col{0}==6) { $critere_style['fill-opacity'] = ord($col{5}/100); } // CMYKa
|
1106 |
+
$path_style .= $this->mpdf_ref->SetFColor($col, true).' ';
|
1107 |
+
$style .= 'F';
|
1108 |
+
}
|
1109 |
+
}
|
1110 |
+
if (substr_count($critere_style['stroke'],'url')>0){
|
1111 |
+
/*
|
1112 |
+
// Cannot put a gradient on a "stroke" in PDF?
|
1113 |
+
$id_gradient = preg_replace("/url\(#([\w_]*)\)/i","$1",$critere_style['stroke']);
|
1114 |
+
if ($id_gradient != $critere_style['stroke']) {
|
1115 |
+
if (isset($this->svg_gradient[$id_gradient])) {
|
1116 |
+
$fill_gradient = $this->svgGradient($this->svg_gradient[$id_gradient], $attribs, $element);
|
1117 |
+
if ($fill_gradient) {
|
1118 |
+
$path_style = "q ";
|
1119 |
+
$w = "W";
|
1120 |
+
$style .= 'D';
|
1121 |
+
}
|
1122 |
+
}
|
1123 |
+
}
|
1124 |
+
*/
|
1125 |
+
}
|
1126 |
+
// Used as indirect setting for currentColor
|
1127 |
+
else if (strtolower($critere_style['stroke']) == 'currentcolor'){
|
1128 |
+
$col = $this->mpdf_ref->ConvertColor($critere_style['color']);
|
1129 |
+
if ($col) {
|
1130 |
+
if ($col{0}==5) { $critere_style['stroke-opacity'] = ord($col{4}/100); } // RGBa
|
1131 |
+
if ($col{0}==6) { $critere_style['stroke-opacity'] = ord($col{5}/100); } // CMYKa
|
1132 |
+
$path_style .= $this->mpdf_ref->SetDColor($col, true).' ';
|
1133 |
+
$style .= 'D';
|
1134 |
+
$lw = $this->ConvertSVGSizePixels($critere_style['stroke-width']);
|
1135 |
+
$path_style .= sprintf('%.3F w ',$lw*$this->kp);
|
1136 |
+
}
|
1137 |
+
}
|
1138 |
+
else if ($critere_style['stroke'] != 'none'){
|
1139 |
+
$col = $this->mpdf_ref->ConvertColor($critere_style['stroke']);
|
1140 |
+
if ($col) {
|
1141 |
+
// mPDF 5.0.051
|
1142 |
+
// mPDF 5.3.74
|
1143 |
+
if ($col{0}==5) { $critere_style['stroke-opacity'] = ord($col{4}/100); } // RGBa
|
1144 |
+
if ($col{0}==6) { $critere_style['stroke-opacity'] = ord($col{5}/100); } // CMYKa
|
1145 |
+
$path_style .= $this->mpdf_ref->SetDColor($col, true).' ';
|
1146 |
+
$style .= 'D';
|
1147 |
+
$lw = $this->ConvertSVGSizePixels($critere_style['stroke-width']);
|
1148 |
+
$path_style .= sprintf('%.3F w ',$lw*$this->kp);
|
1149 |
+
}
|
1150 |
+
}
|
1151 |
+
|
1152 |
+
|
1153 |
+
if ($critere_style['stroke'] != 'none'){
|
1154 |
+
if ($critere_style['stroke-linejoin'] == 'miter'){
|
1155 |
+
$path_style .= ' 0 j ';
|
1156 |
+
}
|
1157 |
+
else if ($critere_style['stroke-linejoin'] == 'round'){
|
1158 |
+
$path_style .= ' 1 j ';
|
1159 |
+
}
|
1160 |
+
else if ($critere_style['stroke-linejoin'] == 'bevel'){
|
1161 |
+
$path_style .= ' 2 j ';
|
1162 |
+
}
|
1163 |
+
|
1164 |
+
if ($critere_style['stroke-linecap'] == 'butt'){
|
1165 |
+
$path_style .= ' 0 J ';
|
1166 |
+
}
|
1167 |
+
else if ($critere_style['stroke-linecap'] == 'round'){
|
1168 |
+
$path_style .= ' 1 J ';
|
1169 |
+
}
|
1170 |
+
else if ($critere_style['stroke-linecap'] == 'square'){
|
1171 |
+
$path_style .= ' 2 J ';
|
1172 |
+
}
|
1173 |
+
|
1174 |
+
if (isset($critere_style['stroke-miterlimit'])){
|
1175 |
+
if ($critere_style['stroke-miterlimit'] == 'none'){
|
1176 |
+
}
|
1177 |
+
else if (preg_match('/^[\d.]+$/',$critere_style['stroke-miterlimit'])) {
|
1178 |
+
$path_style .= sprintf('%.2F M ',$critere_style['stroke-miterlimit']);
|
1179 |
+
}
|
1180 |
+
}
|
1181 |
+
if (isset($critere_style['stroke-dasharray'])){
|
1182 |
+
$off = 0;
|
1183 |
+
$d = preg_split('/[ ,]/',$critere_style['stroke-dasharray']);
|
1184 |
+
if (count($d) == 1 && $d[0]==0) {
|
1185 |
+
$path_style .= '[] 0 d ';
|
1186 |
+
}
|
1187 |
+
else {
|
1188 |
+
if (count($d) % 2 == 1) { $d = array_merge($d, $d); } // 5, 3, 1 => 5,3,1,5,3,1 OR 3 => 3,3
|
1189 |
+
$arr = '';
|
1190 |
+
for($i=0; $i<count($d); $i+=2) {
|
1191 |
+
$arr .= sprintf('%.3F %.3F ', $d[$i]*$this->kp, $d[$i+1]*$this->kp);
|
1192 |
+
}
|
1193 |
+
if (isset($critere_style['stroke-dashoffset'])){ $off = $critere_style['stroke-dashoffset'] + 0; }
|
1194 |
+
$path_style .= sprintf('[%s] %.3F d ', $arr, $off*$this->kp);
|
1195 |
+
}
|
1196 |
+
}
|
1197 |
+
}
|
1198 |
+
|
1199 |
+
if ($critere_style['fill-rule']=='evenodd') { $fr = '*'; }
|
1200 |
+
else { $fr = ''; }
|
1201 |
+
|
1202 |
+
if (isset($critere_style['fill-opacity'])) {
|
1203 |
+
$opacity = 1;
|
1204 |
+
if ($critere_style['fill-opacity'] == 0) { $opacity = 0; }
|
1205 |
+
else if ($critere_style['fill-opacity'] > 1) { $opacity = 1; }
|
1206 |
+
else if ($critere_style['fill-opacity'] > 0) { $opacity = $critere_style['fill-opacity']; }
|
1207 |
+
else if ($critere_style['fill-opacity'] < 0) { $opacity = 0; }
|
1208 |
+
$gs = $this->mpdf_ref->AddExtGState(array('ca'=>$opacity, 'BM'=>'/Normal'));
|
1209 |
+
$this->mpdf_ref->extgstates[$gs]['fo'] = true;
|
1210 |
+
$path_style .= sprintf(' /GS%d gs ', $gs);
|
1211 |
+
}
|
1212 |
+
|
1213 |
+
if (isset($critere_style['stroke-opacity'])) {
|
1214 |
+
$opacity = 1;
|
1215 |
+
if ($critere_style['stroke-opacity'] == 0) { $opacity = 0; }
|
1216 |
+
else if ($critere_style['stroke-opacity'] > 1) { $opacity = 1; }
|
1217 |
+
else if ($critere_style['stroke-opacity'] > 0) { $opacity = $critere_style['stroke-opacity']; }
|
1218 |
+
else if ($critere_style['stroke-opacity'] < 0) { $opacity = 0; }
|
1219 |
+
$gs = $this->mpdf_ref->AddExtGState(array('CA'=>$opacity, 'BM'=>'/Normal'));
|
1220 |
+
$this->mpdf_ref->extgstates[$gs]['fo'] = true;
|
1221 |
+
$path_style .= sprintf(' /GS%d gs ', $gs);
|
1222 |
+
}
|
1223 |
+
|
1224 |
+
switch ($style){
|
1225 |
+
case 'F':
|
1226 |
+
$op = 'f';
|
1227 |
+
break;
|
1228 |
+
case 'FD':
|
1229 |
+
$op = 'B';
|
1230 |
+
break;
|
1231 |
+
case 'ND':
|
1232 |
+
$op = 'S';
|
1233 |
+
break;
|
1234 |
+
case 'D':
|
1235 |
+
$op = 'S';
|
1236 |
+
break;
|
1237 |
+
default:
|
1238 |
+
$op = 'n';
|
1239 |
+
}
|
1240 |
+
|
1241 |
+
$prestyle = $path_style.' ';
|
1242 |
+
$poststyle = $w.' '. $op.$fr.' '.$fill_gradient."\n";
|
1243 |
+
return array($prestyle,$poststyle);
|
1244 |
+
|
1245 |
+
}
|
1246 |
+
|
1247 |
+
// fonction retracant les <path />
|
1248 |
+
function svgPath($command, $arguments){
|
1249 |
+
$path_cmd = '';
|
1250 |
+
$newsubpath = false;
|
1251 |
+
// mPDF 5.0.039
|
1252 |
+
$minl = $this->pathBBox[0];
|
1253 |
+
$mint = $this->pathBBox[1];
|
1254 |
+
$maxr = $this->pathBBox[2]+$this->pathBBox[0];
|
1255 |
+
$maxb = $this->pathBBox[3]+$this->pathBBox[1];
|
1256 |
+
|
1257 |
+
$start = array($this->xbase, -$this->ybase);
|
1258 |
+
|
1259 |
+
preg_match_all('/[\-^]?[\d.]+(e[\-]?[\d]+){0,1}/i', $arguments, $a, PREG_SET_ORDER);
|
1260 |
+
|
1261 |
+
// if the command is a capital letter, the coords go absolute, otherwise relative
|
1262 |
+
if(strtolower($command) == $command) $relative = true;
|
1263 |
+
else $relative = false;
|
1264 |
+
|
1265 |
+
|
1266 |
+
$ile_argumentow = count($a);
|
1267 |
+
|
1268 |
+
// each command may have different needs for arguments [1 to 8]
|
1269 |
+
|
1270 |
+
switch(strtolower($command)){
|
1271 |
+
case 'm': // move
|
1272 |
+
for($i = 0; $i<$ile_argumentow; $i+=2){
|
1273 |
+
$x = $a[$i][0];
|
1274 |
+
$y = $a[$i+1][0];
|
1275 |
+
if($relative){
|
1276 |
+
$pdfx = ($this->xbase + $x);
|
1277 |
+
$pdfy = ($this->ybase - $y);
|
1278 |
+
$this->xbase += $x;
|
1279 |
+
$this->ybase += -$y;
|
1280 |
+
}
|
1281 |
+
else{
|
1282 |
+
$pdfx = $x;
|
1283 |
+
$pdfy = -$y ;
|
1284 |
+
$this->xbase = $x;
|
1285 |
+
$this->ybase = -$y;
|
1286 |
+
}
|
1287 |
+
$pdf_pt = $this->svg_overflow($pdfx,$pdfy);
|
1288 |
+
$minl = min($minl,$pdf_pt['x']);
|
1289 |
+
$maxr = max($maxr,$pdf_pt['x']);
|
1290 |
+
$mint = min($mint,-$pdf_pt['y']);
|
1291 |
+
$maxb = max($maxb,-$pdf_pt['y']);
|
1292 |
+
if($i == 0) $path_cmd .= sprintf('%.3F %.3F m ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
|
1293 |
+
else $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
|
1294 |
+
// mPDF 4.4.003 Save start points of subpath
|
1295 |
+
if ($this->subPathInit) {
|
1296 |
+
$this->spxstart = $this->xbase;
|
1297 |
+
$this->spystart = $this->ybase;
|
1298 |
+
$this->subPathInit = false;
|
1299 |
+
}
|
1300 |
+
}
|
1301 |
+
break;
|
1302 |
+
case 'l': // a simple line
|
1303 |
+
for($i = 0; $i<$ile_argumentow; $i+=2){
|
1304 |
+
$x = ($a[$i][0]);
|
1305 |
+
$y = ($a[$i+1][0]);
|
1306 |
+
if($relative){
|
1307 |
+
$pdfx = ($this->xbase + $x);
|
1308 |
+
$pdfy = ($this->ybase - $y);
|
1309 |
+
$this->xbase += $x;
|
1310 |
+
$this->ybase += -$y;
|
1311 |
+
}
|
1312 |
+
else{
|
1313 |
+
$pdfx = $x ;
|
1314 |
+
$pdfy = -$y ;
|
1315 |
+
$this->xbase = $x;
|
1316 |
+
$this->ybase = -$y;
|
1317 |
+
}
|
1318 |
+
$pdf_pt = $this->svg_overflow($pdfx,$pdfy);
|
1319 |
+
$minl = min($minl,$pdf_pt['x']);
|
1320 |
+
$maxr = max($maxr,$pdf_pt['x']);
|
1321 |
+
$mint = min($mint,-$pdf_pt['y']);
|
1322 |
+
$maxb = max($maxb,-$pdf_pt['y']);
|
1323 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
|
1324 |
+
}
|
1325 |
+
break;
|
1326 |
+
case 'h': // a very simple horizontal line
|
1327 |
+
for($i = 0; $i<$ile_argumentow; $i++){
|
1328 |
+
$x = ($a[$i][0]);
|
1329 |
+
if($relative){
|
1330 |
+
$y = 0;
|
1331 |
+
$pdfx = ($this->xbase + $x) ;
|
1332 |
+
$pdfy = ($this->ybase - $y) ;
|
1333 |
+
$this->xbase += $x;
|
1334 |
+
$this->ybase += -$y;
|
1335 |
+
}
|
1336 |
+
else{
|
1337 |
+
$y = -$this->ybase;
|
1338 |
+
$pdfx = $x;
|
1339 |
+
$pdfy = -$y;
|
1340 |
+
$this->xbase = $x;
|
1341 |
+
$this->ybase = -$y;
|
1342 |
+
}
|
1343 |
+
$pdf_pt = $this->svg_overflow($pdfx,$pdfy);
|
1344 |
+
$minl = min($minl,$pdf_pt['x']);
|
1345 |
+
$maxr = max($maxr,$pdf_pt['x']);
|
1346 |
+
$mint = min($mint,-$pdf_pt['y']);
|
1347 |
+
$maxb = max($maxb,-$pdf_pt['y']);
|
1348 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
|
1349 |
+
}
|
1350 |
+
break;
|
1351 |
+
case 'v': // the simplest line, vertical
|
1352 |
+
for($i = 0; $i<$ile_argumentow; $i++){
|
1353 |
+
$y = ($a[$i][0]);
|
1354 |
+
if($relative){
|
1355 |
+
$x = 0;
|
1356 |
+
$pdfx = ($this->xbase + $x);
|
1357 |
+
$pdfy = ($this->ybase - $y);
|
1358 |
+
$this->xbase += $x;
|
1359 |
+
$this->ybase += -$y;
|
1360 |
+
}
|
1361 |
+
else{
|
1362 |
+
$x = $this->xbase;
|
1363 |
+
$pdfx = $x;
|
1364 |
+
$pdfy = -$y;
|
1365 |
+
$this->xbase = $x;
|
1366 |
+
$this->ybase = -$y;
|
1367 |
+
}
|
1368 |
+
$pdf_pt = $this->svg_overflow($pdfx,$pdfy);
|
1369 |
+
$minl = min($minl,$pdf_pt['x']);
|
1370 |
+
$maxr = max($maxr,$pdf_pt['x']);
|
1371 |
+
$mint = min($mint,-$pdf_pt['y']);
|
1372 |
+
$maxb = max($maxb,-$pdf_pt['y']);
|
1373 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
|
1374 |
+
}
|
1375 |
+
break;
|
1376 |
+
case 's': // bezier with first vertex equal first control
|
1377 |
+
// mPDF 4.4.003
|
1378 |
+
if (!($this->lastcommand == 'C' || $this->lastcommand == 'c' || $this->lastcommand == 'S' || $this->lastcommand == 's')) {
|
1379 |
+
$this->lastcontrolpoints = array(0,0);
|
1380 |
+
}
|
1381 |
+
for($i = 0; $i<$ile_argumentow; $i += 4){
|
1382 |
+
$x1 = $this->lastcontrolpoints[0];
|
1383 |
+
$y1 = $this->lastcontrolpoints[1];
|
1384 |
+
$x2 = ($a[$i][0]);
|
1385 |
+
$y2 = ($a[$i+1][0]);
|
1386 |
+
$x = ($a[$i+2][0]);
|
1387 |
+
$y = ($a[$i+3][0]);
|
1388 |
+
if($relative){
|
1389 |
+
$pdfx1 = ($this->xbase + $x1);
|
1390 |
+
$pdfy1 = ($this->ybase - $y1);
|
1391 |
+
$pdfx2 = ($this->xbase + $x2);
|
1392 |
+
$pdfy2 = ($this->ybase - $y2);
|
1393 |
+
$pdfx = ($this->xbase + $x);
|
1394 |
+
$pdfy = ($this->ybase - $y);
|
1395 |
+
$this->xbase += $x;
|
1396 |
+
$this->ybase += -$y;
|
1397 |
+
}
|
1398 |
+
else{
|
1399 |
+
$pdfx1 = $this->xbase + $x1;
|
1400 |
+
$pdfy1 = $this->ybase -$y1;
|
1401 |
+
$pdfx2 = $x2;
|
1402 |
+
$pdfy2 = -$y2;
|
1403 |
+
$pdfx = $x;
|
1404 |
+
$pdfy = -$y;
|
1405 |
+
$this->xbase = $x;
|
1406 |
+
$this->ybase = -$y;
|
1407 |
+
}
|
1408 |
+
$this->lastcontrolpoints = array(($pdfx-$pdfx2),-($pdfy-$pdfy2)); // mPDF 4.4.003 always relative
|
1409 |
+
|
1410 |
+
$pdf_pt = $this->svg_overflow($pdfx,$pdfy);
|
1411 |
+
|
1412 |
+
$curves = array($pdfx1,-$pdfy1,$pdfx2,-$pdfy2,$pdfx,-$pdfy);
|
1413 |
+
$bx = calc_bezier_bbox($start, $curves);
|
1414 |
+
$minl = min($minl,$bx[0]);
|
1415 |
+
$maxr = max($maxr,$bx[2]);
|
1416 |
+
$mint = min($mint,$bx[1]);
|
1417 |
+
$maxb = max($maxb,$bx[3]);
|
1418 |
+
|
1419 |
+
if( ($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy) )
|
1420 |
+
{
|
1421 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
|
1422 |
+
}
|
1423 |
+
else
|
1424 |
+
{
|
1425 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1*$this->kp, $pdfy1*$this->kp, $pdfx2*$this->kp, $pdfy2*$this->kp, $pdfx*$this->kp, $pdfy*$this->kp);
|
1426 |
+
}
|
1427 |
+
|
1428 |
+
}
|
1429 |
+
break;
|
1430 |
+
case 'c': // bezier with second vertex equal second control
|
1431 |
+
for($i = 0; $i<$ile_argumentow; $i += 6){
|
1432 |
+
$x1 = ($a[$i][0]);
|
1433 |
+
$y1 = ($a[$i+1][0]);
|
1434 |
+
$x2 = ($a[$i+2][0]);
|
1435 |
+
$y2 = ($a[$i+3][0]);
|
1436 |
+
$x = ($a[$i+4][0]);
|
1437 |
+
$y = ($a[$i+5][0]);
|
1438 |
+
|
1439 |
+
|
1440 |
+
if($relative){
|
1441 |
+
$pdfx1 = ($this->xbase + $x1);
|
1442 |
+
$pdfy1 = ($this->ybase - $y1);
|
1443 |
+
$pdfx2 = ($this->xbase + $x2);
|
1444 |
+
$pdfy2 = ($this->ybase - $y2);
|
1445 |
+
$pdfx = ($this->xbase + $x);
|
1446 |
+
$pdfy = ($this->ybase - $y);
|
1447 |
+
$this->xbase += $x;
|
1448 |
+
$this->ybase += -$y;
|
1449 |
+
}
|
1450 |
+
else{
|
1451 |
+
$pdfx1 = $x1;
|
1452 |
+
$pdfy1 = -$y1;
|
1453 |
+
$pdfx2 = $x2;
|
1454 |
+
$pdfy2 = -$y2;
|
1455 |
+
$pdfx = $x;
|
1456 |
+
$pdfy = -$y;
|
1457 |
+
$this->xbase = $x;
|
1458 |
+
$this->ybase = -$y;
|
1459 |
+
}
|
1460 |
+
$this->lastcontrolpoints = array(($pdfx-$pdfx2),-($pdfy-$pdfy2)); // mPDF 4.4.003 always relative
|
1461 |
+
// $pdf_pt2 = $this->svg_overflow($pdfx2,$pdfy2);
|
1462 |
+
// $pdf_pt1 = $this->svg_overflow($pdfx1,$pdfy1);
|
1463 |
+
$pdf_pt = $this->svg_overflow($pdfx,$pdfy);
|
1464 |
+
|
1465 |
+
$curves = array($pdfx1,-$pdfy1,$pdfx2,-$pdfy2,$pdfx,-$pdfy);
|
1466 |
+
$bx = calc_bezier_bbox($start, $curves);
|
1467 |
+
$minl = min($minl,$bx[0]);
|
1468 |
+
$maxr = max($maxr,$bx[2]);
|
1469 |
+
$mint = min($mint,$bx[1]);
|
1470 |
+
$maxb = max($maxb,$bx[3]);
|
1471 |
+
|
1472 |
+
if( ($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy) )
|
1473 |
+
{
|
1474 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
|
1475 |
+
}
|
1476 |
+
else
|
1477 |
+
{
|
1478 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1*$this->kp, $pdfy1*$this->kp, $pdfx2*$this->kp, $pdfy2*$this->kp, $pdfx*$this->kp, $pdfy*$this->kp);
|
1479 |
+
}
|
1480 |
+
|
1481 |
+
}
|
1482 |
+
break;
|
1483 |
+
|
1484 |
+
case 'q': // bezier quadratic avec point de control
|
1485 |
+
for($i = 0; $i<$ile_argumentow; $i += 4){
|
1486 |
+
$x1 = ($a[$i][0]);
|
1487 |
+
$y1 = ($a[$i+1][0]);
|
1488 |
+
$x = ($a[$i+2][0]);
|
1489 |
+
$y = ($a[$i+3][0]);
|
1490 |
+
if($relative){
|
1491 |
+
$pdfx = ($this->xbase + $x);
|
1492 |
+
$pdfy = ($this->ybase - $y);
|
1493 |
+
|
1494 |
+
$pdfx1 = ($this->xbase + ($x1*2/3));
|
1495 |
+
$pdfy1 = ($this->ybase - ($y1*2/3));
|
1496 |
+
// mPDF 4.4.003
|
1497 |
+
$pdfx2 = $pdfx1 + 1/3 *($x);
|
1498 |
+
$pdfy2 = $pdfy1 + 1/3 *(-$y) ;
|
1499 |
+
|
1500 |
+
$this->xbase += $x;
|
1501 |
+
$this->ybase += -$y;
|
1502 |
+
}
|
1503 |
+
else{
|
1504 |
+
$pdfx = $x;
|
1505 |
+
$pdfy = -$y;
|
1506 |
+
|
1507 |
+
$pdfx1 = ($this->xbase+(($x1-$this->xbase)*2/3));
|
1508 |
+
$pdfy1 = ($this->ybase-(($y1+$this->ybase)*2/3));
|
1509 |
+
|
1510 |
+
$pdfx2 = ($x+(($x1-$x)*2/3));
|
1511 |
+
$pdfy2 = (-$y-(($y1-$y)*2/3));
|
1512 |
+
|
1513 |
+
// mPDF 4.4.003
|
1514 |
+
$pdfx2 = $pdfx1 + 1/3 *($x - $this->xbase);
|
1515 |
+
$pdfy2 = $pdfy1 + 1/3 *(-$y - $this->ybase) ;
|
1516 |
+
|
1517 |
+
$this->xbase = $x;
|
1518 |
+
$this->ybase = -$y;
|
1519 |
+
}
|
1520 |
+
$this->lastcontrolpoints = array(($pdfx-$pdfx2),-($pdfy-$pdfy2)); // mPDF 4.4.003 always relative
|
1521 |
+
|
1522 |
+
$pdf_pt = $this->svg_overflow($pdfx,$pdfy);
|
1523 |
+
|
1524 |
+
$curves = array($pdfx1,-$pdfy1,$pdfx2,-$pdfy2,$pdfx,-$pdfy);
|
1525 |
+
$bx = calc_bezier_bbox($start, $curves);
|
1526 |
+
$minl = min($minl,$bx[0]);
|
1527 |
+
$maxr = max($maxr,$bx[2]);
|
1528 |
+
$mint = min($mint,$bx[1]);
|
1529 |
+
$maxb = max($maxb,$bx[3]);
|
1530 |
+
|
1531 |
+
if( ($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy) )
|
1532 |
+
{
|
1533 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
|
1534 |
+
}
|
1535 |
+
else
|
1536 |
+
{
|
1537 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1*$this->kp, $pdfy1*$this->kp, $pdfx2*$this->kp, $pdfy2*$this->kp, $pdfx*$this->kp, $pdfy*$this->kp);
|
1538 |
+
}
|
1539 |
+
}
|
1540 |
+
break;
|
1541 |
+
case 't': // bezier quadratic avec point de control simetrique a lancien point de control
|
1542 |
+
if (!($this->lastcommand == 'Q' || $this->lastcommand == 'q' || $this->lastcommand == 'T' || $this->lastcommand == 't')) {
|
1543 |
+
$this->lastcontrolpoints = array(0,0);
|
1544 |
+
}
|
1545 |
+
for($i = 0; $i<$ile_argumentow; $i += 2){
|
1546 |
+
$x = ($a[$i][0]);
|
1547 |
+
$y = ($a[$i+1][0]);
|
1548 |
+
|
1549 |
+
$x1 = $this->lastcontrolpoints[0];
|
1550 |
+
$y1 = $this->lastcontrolpoints[1];
|
1551 |
+
|
1552 |
+
if($relative){
|
1553 |
+
$pdfx = ($this->xbase + $x);
|
1554 |
+
$pdfy = ($this->ybase - $y);
|
1555 |
+
|
1556 |
+
$pdfx1 = ($this->xbase + ($x1));
|
1557 |
+
$pdfy1 = ($this->ybase - ($y1));
|
1558 |
+
// mPDF 4.4.003
|
1559 |
+
$pdfx2 = $pdfx1 + 1/3 *($x);
|
1560 |
+
$pdfy2 = $pdfy1 + 1/3 *(-$y) ;
|
1561 |
+
|
1562 |
+
$this->xbase += $x;
|
1563 |
+
$this->ybase += -$y;
|
1564 |
+
}
|
1565 |
+
else{
|
1566 |
+
$pdfx = $x;
|
1567 |
+
$pdfy = -$y;
|
1568 |
+
|
1569 |
+
$pdfx1 = ($this->xbase + ($x1));
|
1570 |
+
$pdfy1 = ($this->ybase - ($y1));
|
1571 |
+
// mPDF 4.4.003
|
1572 |
+
$pdfx2 = $pdfx1 + 1/3 *($x - $this->xbase);
|
1573 |
+
$pdfy2 = $pdfy1 + 1/3 *(-$y - $this->ybase) ;
|
1574 |
+
|
1575 |
+
$this->xbase = $x;
|
1576 |
+
$this->ybase = -$y;
|
1577 |
+
}
|
1578 |
+
|
1579 |
+
$this->lastcontrolpoints = array(($pdfx-$pdfx2),-($pdfy-$pdfy2)); // mPDF 4.4.003 always relative
|
1580 |
+
|
1581 |
+
$curves = array($pdfx1,-$pdfy1,$pdfx2,-$pdfy2,$pdfx,-$pdfy);
|
1582 |
+
$bx = calc_bezier_bbox($start, $curves);
|
1583 |
+
$minl = min($minl,$bx[0]);
|
1584 |
+
$maxr = max($maxr,$bx[2]);
|
1585 |
+
$mint = min($mint,$bx[1]);
|
1586 |
+
$maxb = max($maxb,$bx[3]);
|
1587 |
+
|
1588 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1*$this->kp, $pdfy1*$this->kp, $pdfx2*$this->kp, $pdfy2*$this->kp, $pdfx*$this->kp, $pdfy*$this->kp);
|
1589 |
+
}
|
1590 |
+
|
1591 |
+
break;
|
1592 |
+
case 'a': // Elliptical arc
|
1593 |
+
for($i = 0; $i<$ile_argumentow; $i += 7){
|
1594 |
+
$rx = ($a[$i][0]);
|
1595 |
+
$ry = ($a[$i+1][0]);
|
1596 |
+
$angle = ($a[$i+2][0]); //x-axis-rotation
|
1597 |
+
$largeArcFlag = ($a[$i+3][0]);
|
1598 |
+
$sweepFlag = ($a[$i+4][0]);
|
1599 |
+
$x2 = ($a[$i+5][0]);
|
1600 |
+
$y2 = ($a[$i+6][0]);
|
1601 |
+
$x1 = $this->xbase;
|
1602 |
+
$y1 = -$this->ybase;
|
1603 |
+
if($relative){
|
1604 |
+
$x2 = $this->xbase + $x2;
|
1605 |
+
$y2 = -$this->ybase + $y2;
|
1606 |
+
$this->xbase += ($a[$i+5][0]);
|
1607 |
+
$this->ybase += -($a[$i+6][0]);
|
1608 |
+
}
|
1609 |
+
else{
|
1610 |
+
$this->xbase = $x2;
|
1611 |
+
$this->ybase = -$y2;
|
1612 |
+
}
|
1613 |
+
list($pcmd, $bounds) = $this->Arcto($x1, $y1, $x2, $y2, $rx, $ry, $angle, $largeArcFlag, $sweepFlag);
|
1614 |
+
$minl = min($minl,$x2,min($bounds[0]));
|
1615 |
+
$maxr = max($maxr,$x2,max($bounds[0]));
|
1616 |
+
$mint = min($mint,$y2,min($bounds[1]));
|
1617 |
+
$maxb = max($maxb,$y2,max($bounds[1]));
|
1618 |
+
$path_cmd .= $pcmd;
|
1619 |
+
|
1620 |
+
}
|
1621 |
+
break;
|
1622 |
+
case'z':
|
1623 |
+
$path_cmd .= 'h ';
|
1624 |
+
$this->subPathInit = true;
|
1625 |
+
$newsubpath = true;
|
1626 |
+
$this->xbase = $this->spxstart;
|
1627 |
+
$this->ybase = $this->spystart;
|
1628 |
+
break;
|
1629 |
+
default:
|
1630 |
+
break;
|
1631 |
+
}
|
1632 |
+
|
1633 |
+
if (!$newsubpath) { $this->subPathInit = false; }
|
1634 |
+
$this->lastcommand = $command;
|
1635 |
+
// mPDF 5.0.039
|
1636 |
+
$this->pathBBox[0] = $minl;
|
1637 |
+
$this->pathBBox[1] = $mint;
|
1638 |
+
$this->pathBBox[2] = $maxr - $this->pathBBox[0];
|
1639 |
+
$this->pathBBox[3] = $maxb - $this->pathBBox[1];
|
1640 |
+
return $path_cmd;
|
1641 |
+
|
1642 |
+
}
|
1643 |
+
|
1644 |
+
function Arcto($x1, $y1, $x2, $y2, $rx, $ry, $angle, $largeArcFlag, $sweepFlag) {
|
1645 |
+
|
1646 |
+
$bounds = array(0=>array($x1,$x2),1=>array($y1,$y2));
|
1647 |
+
// 1. Treat out-of-range parameters as described in
|
1648 |
+
// http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
1649 |
+
// If the endpoints (x1, y1) and (x2, y2) are identical, then this
|
1650 |
+
// is equivalent to omitting the elliptical arc segment entirely
|
1651 |
+
if ($x1 == $x2 && $y1 == $y2) return array('', $bounds); // mPD 5.0.040
|
1652 |
+
|
1653 |
+
// If rX = 0 or rY = 0 then this arc is treated as a straight line
|
1654 |
+
// segment (a "lineto") joining the endpoints.
|
1655 |
+
if ($rx == 0.0 || $ry == 0.0) {
|
1656 |
+
// return array(Lineto(x2, y2), $bounds);
|
1657 |
+
}
|
1658 |
+
|
1659 |
+
// If rX or rY have negative signs, these are dropped; the absolute
|
1660 |
+
// value is used instead.
|
1661 |
+
if ($rx<0.0) $rx = -$rx;
|
1662 |
+
if ($ry<0.0) $ry = -$ry;
|
1663 |
+
|
1664 |
+
// 2. convert to center parameterization as shown in
|
1665 |
+
// http://www.w3.org/TR/SVG/implnote.html
|
1666 |
+
$sinPhi = sin(deg2rad($angle));
|
1667 |
+
$cosPhi = cos(deg2rad($angle));
|
1668 |
+
|
1669 |
+
$x1dash = $cosPhi * ($x1-$x2)/2.0 + $sinPhi * ($y1-$y2)/2.0;
|
1670 |
+
$y1dash = -$sinPhi * ($x1-$x2)/2.0 + $cosPhi * ($y1-$y2)/2.0;
|
1671 |
+
|
1672 |
+
|
1673 |
+
$numerator = $rx*$rx*$ry*$ry - $rx*$rx*$y1dash*$y1dash - $ry*$ry*$x1dash*$x1dash;
|
1674 |
+
|
1675 |
+
if ($numerator < 0.0) {
|
1676 |
+
// If rX , rY and are such that there is no solution (basically,
|
1677 |
+
// the ellipse is not big enough to reach from (x1, y1) to (x2,
|
1678 |
+
// y2)) then the ellipse is scaled up uniformly until there is
|
1679 |
+
// exactly one solution (until the ellipse is just big enough).
|
1680 |
+
|
1681 |
+
// -> find factor s, such that numerator' with rx'=s*rx and
|
1682 |
+
// ry'=s*ry becomes 0 :
|
1683 |
+
$s = sqrt(1.0 - $numerator/($rx*$rx*$ry*$ry));
|
1684 |
+
|
1685 |
+
$rx *= $s;
|
1686 |
+
$ry *= $s;
|
1687 |
+
$root = 0.0;
|
1688 |
+
|
1689 |
+
}
|
1690 |
+
else {
|
1691 |
+
$root = ($largeArcFlag == $sweepFlag ? -1.0 : 1.0) * sqrt( $numerator/($rx*$rx*$y1dash*$y1dash+$ry*$ry*$x1dash*$x1dash) );
|
1692 |
+
}
|
1693 |
+
|
1694 |
+
$cxdash = $root*$rx*$y1dash/$ry;
|
1695 |
+
$cydash = -$root*$ry*$x1dash/$rx;
|
1696 |
+
|
1697 |
+
$cx = $cosPhi * $cxdash - $sinPhi * $cydash + ($x1+$x2)/2.0;
|
1698 |
+
$cy = $sinPhi * $cxdash + $cosPhi * $cydash + ($y1+$y2)/2.0;
|
1699 |
+
|
1700 |
+
|
1701 |
+
$theta1 = $this->CalcVectorAngle(1.0, 0.0, ($x1dash-$cxdash)/$rx, ($y1dash-$cydash)/$ry);
|
1702 |
+
$dtheta = $this->CalcVectorAngle(($x1dash-$cxdash)/$rx, ($y1dash-$cydash)/$ry, (-$x1dash-$cxdash)/$rx, (-$y1dash-$cydash)/$ry);
|
1703 |
+
if (!$sweepFlag && $dtheta>0)
|
1704 |
+
$dtheta -= 2.0*M_PI;
|
1705 |
+
else if ($sweepFlag && $dtheta<0)
|
1706 |
+
$dtheta += 2.0*M_PI;
|
1707 |
+
|
1708 |
+
// 3. convert into cubic bezier segments <= 90deg
|
1709 |
+
$segments = ceil(abs($dtheta/(M_PI/2.0)));
|
1710 |
+
$delta = $dtheta/$segments;
|
1711 |
+
$t = 8.0/3.0 * sin($delta/4.0) * sin($delta/4.0) / sin($delta/2.0);
|
1712 |
+
$coords = array();
|
1713 |
+
for ($i = 0; $i < $segments; $i++) {
|
1714 |
+
$cosTheta1 = cos($theta1);
|
1715 |
+
$sinTheta1 = sin($theta1);
|
1716 |
+
$theta2 = $theta1 + $delta;
|
1717 |
+
$cosTheta2 = cos($theta2);
|
1718 |
+
$sinTheta2 = sin($theta2);
|
1719 |
+
|
1720 |
+
// a) calculate endpoint of the segment:
|
1721 |
+
$xe = $cosPhi * $rx*$cosTheta2 - $sinPhi * $ry*$sinTheta2 + $cx;
|
1722 |
+
$ye = $sinPhi * $rx*$cosTheta2 + $cosPhi * $ry*$sinTheta2 + $cy;
|
1723 |
+
|
1724 |
+
// b) calculate gradients at start/end points of segment:
|
1725 |
+
$dx1 = $t * ( - $cosPhi * $rx*$sinTheta1 - $sinPhi * $ry*$cosTheta1);
|
1726 |
+
$dy1 = $t * ( - $sinPhi * $rx*$sinTheta1 + $cosPhi * $ry*$cosTheta1);
|
1727 |
+
|
1728 |
+
$dxe = $t * ( $cosPhi * $rx*$sinTheta2 + $sinPhi * $ry*$cosTheta2);
|
1729 |
+
$dye = $t * ( $sinPhi * $rx*$sinTheta2 - $cosPhi * $ry*$cosTheta2);
|
1730 |
+
|
1731 |
+
// c) draw the cubic bezier:
|
1732 |
+
$coords[$i] = array(($x1+$dx1), ($y1+$dy1), ($xe+$dxe), ($ye+$dye), $xe, $ye);
|
1733 |
+
|
1734 |
+
// do next segment
|
1735 |
+
$theta1 = $theta2;
|
1736 |
+
$x1 = $xe;
|
1737 |
+
$y1 = $ye;
|
1738 |
+
}
|
1739 |
+
$path = ' ';
|
1740 |
+
foreach($coords AS $c) {
|
1741 |
+
$cpx1 = $c[0];
|
1742 |
+
$cpy1 = $c[1];
|
1743 |
+
$cpx2 = $c[2];
|
1744 |
+
$cpy2 = $c[3];
|
1745 |
+
$x2 = $c[4];
|
1746 |
+
$y2 = $c[5];
|
1747 |
+
$path .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $cpx1*$this->kp, -$cpy1*$this->kp, $cpx2*$this->kp, -$cpy2*$this->kp, $x2*$this->kp, -$y2*$this->kp) ."\n";
|
1748 |
+
|
1749 |
+
// mPDF 5.0.040
|
1750 |
+
$bounds[0][] = $c[4];
|
1751 |
+
$bounds[1][] = $c[5];
|
1752 |
+
}
|
1753 |
+
return array($path, $bounds); // mPD 5.0.040
|
1754 |
+
}
|
1755 |
+
|
1756 |
+
|
1757 |
+
function CalcVectorAngle($ux, $uy, $vx, $vy) {
|
1758 |
+
$ta = atan2($uy, $ux);
|
1759 |
+
$tb = atan2($vy, $vx);
|
1760 |
+
if ($tb >= $ta)
|
1761 |
+
return ($tb-$ta);
|
1762 |
+
return (6.28318530718 - ($ta-$tb));
|
1763 |
+
}
|
1764 |
+
|
1765 |
+
|
1766 |
+
function ConvertSVGSizePixels($size=5,$maxsize='x'){
|
1767 |
+
// maxsize in pixels (user units) or 'y' or 'x'
|
1768 |
+
// e.g. $w = $this->ConvertSVGSizePixels($arguments['w'],$this->svg_info['w']*(25.4/$this->mpdf_ref->dpi));
|
1769 |
+
// usefontsize - setfalse for e.g. margins - will ignore fontsize for % values
|
1770 |
+
// Depends of maxsize value to make % work properly. Usually maxsize == pagewidth
|
1771 |
+
// For text $maxsize = Fontsize
|
1772 |
+
// Setting e.g. margin % will use maxsize (pagewidth) and em will use fontsize
|
1773 |
+
|
1774 |
+
if ($maxsize == 'y') { $maxsize = $this->svg_info['h']; }
|
1775 |
+
else if ($maxsize == 'x') { $maxsize = $this->svg_info['w']; }
|
1776 |
+
$maxsize *= (25.4/$this->mpdf_ref->dpi); // convert pixels to mm
|
1777 |
+
$fontsize=$this->mpdf_ref->FontSize / $this->kf;
|
1778 |
+
//Return as pixels
|
1779 |
+
$size = $this->mpdf_ref->ConvertSize($size,$maxsize,$fontsize,false) * 1/(25.4/$this->mpdf_ref->dpi);
|
1780 |
+
return $size;
|
1781 |
+
}
|
1782 |
+
|
1783 |
+
function ConvertSVGSizePts($size=5){
|
1784 |
+
// usefontsize - setfalse for e.g. margins - will ignore fontsize for % values
|
1785 |
+
// Depends of maxsize value to make % work properly. Usually maxsize == pagewidth
|
1786 |
+
// For text $maxsize = Fontsize
|
1787 |
+
// Setting e.g. margin % will use maxsize (pagewidth) and em will use fontsize
|
1788 |
+
$maxsize=$this->mpdf_ref->FontSize;
|
1789 |
+
//Return as pts
|
1790 |
+
$size = $this->mpdf_ref->ConvertSize($size,$maxsize,false,true) * 72/25.4;
|
1791 |
+
return $size;
|
1792 |
+
}
|
1793 |
+
|
1794 |
+
|
1795 |
+
//
|
1796 |
+
// fonction retracant les <rect />
|
1797 |
+
function svgRect($arguments){
|
1798 |
+
|
1799 |
+
if ($arguments['h']==0 || $arguments['w']==0) { return ''; }
|
1800 |
+
|
1801 |
+
$x = $this->ConvertSVGSizePixels($arguments['x'],'x'); // mPDF 4.4.003
|
1802 |
+
$y = $this->ConvertSVGSizePixels($arguments['y'],'y'); // mPDF 4.4.003
|
1803 |
+
$h = $this->ConvertSVGSizePixels($arguments['h'],'y'); // mPDF 4.4.003
|
1804 |
+
$w = $this->ConvertSVGSizePixels($arguments['w'],'x'); // mPDF 4.4.003
|
1805 |
+
$rx = $this->ConvertSVGSizePixels($arguments['rx'],'x'); // mPDF 4.4.003
|
1806 |
+
$ry = $this->ConvertSVGSizePixels($arguments['ry'],'y'); // mPDF 4.4.003
|
1807 |
+
|
1808 |
+
if ($rx > $w/2) { $rx = $w/2; } // mPDF 4.4.003
|
1809 |
+
if ($ry > $h/2) { $ry = $h/2; } // mPDF 4.4.003
|
1810 |
+
|
1811 |
+
if ($rx>0 and $ry == 0){$ry = $rx;}
|
1812 |
+
if ($ry>0 and $rx == 0){$rx = $ry;}
|
1813 |
+
|
1814 |
+
if ($rx == 0 and $ry == 0){
|
1815 |
+
// trace un rectangle sans angle arrondit
|
1816 |
+
$path_cmd = sprintf('%.3F %.3F m ', ($x*$this->kp), -($y*$this->kp));
|
1817 |
+
$path_cmd .= sprintf('%.3F %.3F l ', (($x+$w)*$this->kp), -($y*$this->kp));
|
1818 |
+
$path_cmd .= sprintf('%.3F %.3F l ', (($x+$w)*$this->kp), -(($y+$h)*$this->kp));
|
1819 |
+
$path_cmd .= sprintf('%.3F %.3F l ', ($x)*$this->kp, -(($y+$h)*$this->kp));
|
1820 |
+
$path_cmd .= sprintf('%.3F %.3F l h ', ($x*$this->kp), -($y*$this->kp));
|
1821 |
+
|
1822 |
+
|
1823 |
+
}
|
1824 |
+
else {
|
1825 |
+
// trace un rectangle avec les arrondit
|
1826 |
+
// les points de controle du bezier sont deduis grace a la constante kappa
|
1827 |
+
$kappa = 4*(sqrt(2)-1)/3;
|
1828 |
+
|
1829 |
+
$kx = $kappa*$rx;
|
1830 |
+
$ky = $kappa*$ry;
|
1831 |
+
|
1832 |
+
$path_cmd = sprintf('%.3F %.3F m ', ($x+$rx)*$this->kp, -$y*$this->kp);
|
1833 |
+
$path_cmd .= sprintf('%.3F %.3F l ', ($x+($w-$rx))*$this->kp, -$y*$this->kp);
|
1834 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x+($w-$rx+$kx))*$this->kp, -$y*$this->kp, ($x+$w)*$this->kp, (-$y+(-$ry+$ky))*$this->kp, ($x+$w)*$this->kp, (-$y+(-$ry))*$this->kp );
|
1835 |
+
$path_cmd .= sprintf('%.3F %.3F l ', ($x+$w)*$this->kp, (-$y+(-$h+$ry))*$this->kp);
|
1836 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x+$w)*$this->kp, (-$y+(-$h-$ky+$ry))*$this->kp, ($x+($w-$rx+$kx))*$this->kp, (-$y+(-$h))*$this->kp, ($x+($w-$rx))*$this->kp, (-$y+(-$h))*$this->kp );
|
1837 |
+
|
1838 |
+
$path_cmd .= sprintf('%.3F %.3F l ', ($x+$rx)*$this->kp, (-$y+(-$h))*$this->kp);
|
1839 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x+($rx-$kx))*$this->kp, (-$y+(-$h))*$this->kp, $x*$this->kp, (-$y+(-$h-$ky+$ry))*$this->kp, $x*$this->kp, (-$y+(-$h+$ry))*$this->kp );
|
1840 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $x*$this->kp, (-$y+(-$ry))*$this->kp);
|
1841 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c h ', $x*$this->kp, (-$y+(-$ry+$ky))*$this->kp, ($x+($rx-$kx))*$this->kp, -$y*$this->kp, ($x+$rx)*$this->kp, -$y*$this->kp );
|
1842 |
+
|
1843 |
+
|
1844 |
+
}
|
1845 |
+
return $path_cmd;
|
1846 |
+
}
|
1847 |
+
|
1848 |
+
//
|
1849 |
+
// fonction retracant les <ellipse /> et <circle />
|
1850 |
+
// le cercle est trac� grave a 4 bezier cubic, les poitn de controles
|
1851 |
+
// sont deduis grace a la constante kappa * rayon
|
1852 |
+
function svgEllipse($arguments){
|
1853 |
+
if ($arguments['rx']==0 || $arguments['ry']==0) { return ''; }
|
1854 |
+
|
1855 |
+
$kappa = 4*(sqrt(2)-1)/3;
|
1856 |
+
|
1857 |
+
$cx = $this->ConvertSVGSizePixels($arguments['cx'],'x');
|
1858 |
+
$cy = $this->ConvertSVGSizePixels($arguments['cy'],'y');
|
1859 |
+
$rx = $this->ConvertSVGSizePixels($arguments['rx'],'x');
|
1860 |
+
$ry = $this->ConvertSVGSizePixels($arguments['ry'],'y');
|
1861 |
+
|
1862 |
+
$x1 = $cx;
|
1863 |
+
$y1 = -$cy+$ry;
|
1864 |
+
|
1865 |
+
$x2 = $cx+$rx;
|
1866 |
+
$y2 = -$cy;
|
1867 |
+
|
1868 |
+
$x3 = $cx;
|
1869 |
+
$y3 = -$cy-$ry;
|
1870 |
+
|
1871 |
+
$x4 = $cx-$rx;
|
1872 |
+
$y4 = -$cy;
|
1873 |
+
|
1874 |
+
$path_cmd = sprintf('%.3F %.3F m ', $x1*$this->kp, $y1*$this->kp);
|
1875 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x1+($rx*$kappa))*$this->kp, $y1*$this->kp, $x2*$this->kp, ($y2+($ry*$kappa))*$this->kp, $x2*$this->kp, $y2*$this->kp);
|
1876 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x2*$this->kp, ($y2-($ry*$kappa))*$this->kp, ($x3+($rx*$kappa))*$this->kp, $y3*$this->kp, $x3*$this->kp, $y3*$this->kp);
|
1877 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x3-($rx*$kappa))*$this->kp, $y3*$this->kp, $x4*$this->kp, ($y4-($ry*$kappa))*$this->kp, $x4*$this->kp, $y4*$this->kp);
|
1878 |
+
$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x4*$this->kp, ($y4+($ry*$kappa))*$this->kp, ($x1-($rx*$kappa))*$this->kp, $y1*$this->kp, $x1*$this->kp, $y1*$this->kp);
|
1879 |
+
$path_cmd .= 'h ';
|
1880 |
+
|
1881 |
+
return $path_cmd;
|
1882 |
+
|
1883 |
+
}
|
1884 |
+
|
1885 |
+
//
|
1886 |
+
// fonction retracant les <polyline /> et les <line />
|
1887 |
+
function svgPolyline($arguments,$ispolyline=true){
|
1888 |
+
if ($ispolyline) {
|
1889 |
+
$xbase = $arguments[0] ;
|
1890 |
+
$ybase = - $arguments[1] ;
|
1891 |
+
}
|
1892 |
+
else {
|
1893 |
+
if ($arguments[0]==$arguments[2] && $arguments[1]==$arguments[3]) { return ''; } // Zero length line
|
1894 |
+
$xbase = $this->ConvertSVGSizePixels($arguments[0],'x');
|
1895 |
+
$ybase = - $this->ConvertSVGSizePixels($arguments[1],'y');
|
1896 |
+
}
|
1897 |
+
$path_cmd = sprintf('%.3F %.3F m ', $xbase*$this->kp, $ybase*$this->kp);
|
1898 |
+
for ($i = 2; $i<count($arguments);$i += 2) {
|
1899 |
+
if ($ispolyline) {
|
1900 |
+
$tmp_x = $arguments[$i] ;
|
1901 |
+
$tmp_y = - $arguments[($i+1)] ;
|
1902 |
+
}
|
1903 |
+
else {
|
1904 |
+
$tmp_x = $this->ConvertSVGSizePixels($arguments[$i],'x') ;
|
1905 |
+
$tmp_y = - $this->ConvertSVGSizePixels($arguments[($i+1)],'y') ;
|
1906 |
+
}
|
1907 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $tmp_x*$this->kp, $tmp_y*$this->kp);
|
1908 |
+
}
|
1909 |
+
|
1910 |
+
// $path_cmd .= 'h '; // ?? In error - don't close subpath here
|
1911 |
+
return $path_cmd;
|
1912 |
+
|
1913 |
+
}
|
1914 |
+
|
1915 |
+
//
|
1916 |
+
// fonction retracant les <polygone />
|
1917 |
+
function svgPolygon($arguments){
|
1918 |
+
$xbase = $arguments[0] ;
|
1919 |
+
$ybase = - $arguments[1] ;
|
1920 |
+
$path_cmd = sprintf('%.3F %.3F m ', $xbase*$this->kp, $ybase*$this->kp);
|
1921 |
+
for ($i = 2; $i<count($arguments);$i += 2) {
|
1922 |
+
$tmp_x = $arguments[$i] ;
|
1923 |
+
$tmp_y = - $arguments[($i+1)] ;
|
1924 |
+
|
1925 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $tmp_x*$this->kp, $tmp_y*$this->kp);
|
1926 |
+
|
1927 |
+
}
|
1928 |
+
$path_cmd .= sprintf('%.3F %.3F l ', $xbase*$this->kp, $ybase*$this->kp);
|
1929 |
+
$path_cmd .= 'h ';
|
1930 |
+
return $path_cmd;
|
1931 |
+
|
1932 |
+
}
|
1933 |
+
|
1934 |
+
//
|
1935 |
+
// write string to image
|
1936 |
+
function svgText() {
|
1937 |
+
// $tmp = count($this->txt_style)-1;
|
1938 |
+
$current_style = $this->txt_style[count($this->txt_style)-1]; // mPDF 5.7.4
|
1939 |
+
$style = '';
|
1940 |
+
$op = '';
|
1941 |
+
$render = -1;
|
1942 |
+
if(isset($this->txt_data[2])) {
|
1943 |
+
// mPDF 6
|
1944 |
+
// If using SVG Font
|
1945 |
+
if (isset($this->svg_font[$current_style['font-family']])) {
|
1946 |
+
// select font
|
1947 |
+
$style = 'R';
|
1948 |
+
$style .= (isset($current_style['font-weight']) && $current_style['font-weight'] == 'bold')?'B':'';
|
1949 |
+
$style .= (isset($current_style['font-style']) && $current_style['font-style'] == 'italic')?'I':'';
|
1950 |
+
$style .= (isset($current_style['font-variant']) && $current_style['font-variant'] == 'small-caps')?'S':'';
|
1951 |
+
|
1952 |
+
$fontsize = $current_style['font-size'] * $this->mpdf_ref->dpi / 72;
|
1953 |
+
if (isset($this->svg_font[$current_style['font-family']][$style])) {
|
1954 |
+
$svg_font = $this->svg_font[$current_style['font-family']][$style];
|
1955 |
+
}
|
1956 |
+
else if (isset($this->svg_font[$current_style['font-family']]['R'])) {
|
1957 |
+
$svg_font = $this->svg_font[$current_style['font-family']]['R'];
|
1958 |
+
}
|
1959 |
+
|
1960 |
+
if (!isset($svg_font['units-per-em']) || $svg_font['units-per-em']<1) { $svg_font['units-per-em'] = 1000; }
|
1961 |
+
$units_per_em = $svg_font['units-per-em'];
|
1962 |
+
$scale = $fontsize / $units_per_em;
|
1963 |
+
$stroke_width = $current_style['stroke-width'];
|
1964 |
+
$stroke_width /= $scale;
|
1965 |
+
|
1966 |
+
$opacitystr = '';
|
1967 |
+
$fopacity = 1;
|
1968 |
+
if (isset($current_style['fill-opacity'])) {
|
1969 |
+
if ($current_style['fill-opacity'] == 0) { $fopacity = 0; }
|
1970 |
+
else if ($current_style['fill-opacity'] > 1) { $fopacity = 1; }
|
1971 |
+
else if ($current_style['fill-opacity'] > 0) { $fopacity = $current_style['fill-opacity']; }
|
1972 |
+
else if ($current_style['fill-opacity'] < 0) { $fopacity = 0; }
|
1973 |
+
}
|
1974 |
+
$sopacity = 1;
|
1975 |
+
if (isset($current_style['stroke-opacity'])) {
|
1976 |
+
if ($current_style['stroke-opacity'] == 0) { $sopacity = 0; }
|
1977 |
+
else if ($current_style['stroke-opacity'] > 1) { $sopacity = 1; }
|
1978 |
+
else if ($current_style['stroke-opacity'] > 0) { $sopacity = $current_style['stroke-opacity']; }
|
1979 |
+
else if ($current_style['stroke-opacity'] < 0) { $sopacity = 0; }
|
1980 |
+
}
|
1981 |
+
$gs = $this->mpdf_ref->AddExtGState(array('ca'=>$fopacity, 'CA'=>$sopacity, 'BM'=>'/Normal'));
|
1982 |
+
$this->mpdf_ref->extgstates[$gs]['fo'] = true;
|
1983 |
+
$opacitystr = sprintf(' /GS%d gs ', $gs);
|
1984 |
+
|
1985 |
+
$fillstr = '';
|
1986 |
+
if (isset($current_style['fill']) && $current_style['fill']!='none') {
|
1987 |
+
$col = $this->mpdf_ref->ConvertColor($current_style['fill']);
|
1988 |
+
$fillstr = $this->mpdf_ref->SetFColor($col, true);
|
1989 |
+
$render = "0"; // Fill (only)
|
1990 |
+
$op = 'f';
|
1991 |
+
}
|
1992 |
+
$strokestr = '';
|
1993 |
+
if ($stroke_width>0 && $current_style['stroke']!='none') {
|
1994 |
+
$scol = $this->mpdf_ref->ConvertColor($current_style['stroke']);
|
1995 |
+
if ($scol) {
|
1996 |
+
$strokestr .= $this->mpdf_ref->SetDColor($scol, true).' ';
|
1997 |
+
}
|
1998 |
+
$linewidth = $this->ConvertSVGSizePixels($stroke_width);
|
1999 |
+
if ($linewidth > 0) {
|
2000 |
+
$strokestr .= sprintf('%.3F w 0 J 0 j ',$linewidth*$this->kp);
|
2001 |
+
if ($render == -1) { $render = "1"; } // stroke only
|
2002 |
+
else { $render = "2"; } // fill and stroke
|
2003 |
+
$op .= 'S';
|
2004 |
+
}
|
2005 |
+
}
|
2006 |
+
if ($render == -1) { return ''; }
|
2007 |
+
if ($op == 'fS') { $op = 'B'; }
|
2008 |
+
|
2009 |
+
$x = $this->txt_data[0]; // mPDF 5.7.4
|
2010 |
+
$y = $this->txt_data[1]; // mPDF 5.7.4
|
2011 |
+
$txt = $this->txt_data[2];
|
2012 |
+
|
2013 |
+
$txt = preg_replace('/\f/','',$txt);
|
2014 |
+
$txt = preg_replace('/\r/','',$txt);
|
2015 |
+
$txt = preg_replace('/\n/',' ',$txt);
|
2016 |
+
$txt = preg_replace('/\t/',' ',$txt);
|
2017 |
+
$txt = preg_replace("/[ ]+/u",' ',$txt);
|
2018 |
+
|
2019 |
+
if ($this->textjuststarted) { $txt = ltrim($txt); } // mPDF 5.7.4
|
2020 |
+
$this->textjuststarted = false; // mPDF 5.7.4
|
2021 |
+
|
2022 |
+
$txt = $this->mpdf_ref->purify_utf8_text($txt);
|
2023 |
+
if ($this->mpdf_ref->text_input_as_HTML) {
|
2024 |
+
$txt = $this->mpdf_ref->all_entities_to_utf8($txt);
|
2025 |
+
}
|
2026 |
+
|
2027 |
+
$nb=mb_strlen($txt, 'UTF-8');
|
2028 |
+
$i=0;
|
2029 |
+
$sw = 0;
|
2030 |
+
$subpath_cmd = '';
|
2031 |
+
while($i<$nb) {
|
2032 |
+
//Get next character
|
2033 |
+
$char = mb_substr($txt,$i,1,'UTF-8');
|
2034 |
+
|
2035 |
+
|
2036 |
+
if (isset($svg_font['glyphs'][$char])) {
|
2037 |
+
$d = $svg_font['glyphs'][$char]['d'];
|
2038 |
+
if (isset($svg_font['glyphs'][$char]['horiz-adv-x'])) { $horiz_adv_x = $svg_font['glyphs'][$char]['horiz-adv-x']; }
|
2039 |
+
else { $horiz_adv_x = $svg_font['horiz-adv-x']; } // missing glyph width
|
2040 |
+
}
|
2041 |
+
else {
|
2042 |
+
$d = $svg_font['d'];
|
2043 |
+
$horiz_adv_x = $svg_font['horiz-adv-x']; // missing glyph width
|
2044 |
+
}
|
2045 |
+
preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $d, $commands, PREG_SET_ORDER);
|
2046 |
+
$subpath_cmd .= sprintf('q %.4F 0 0 %.4F mPDF-AXS(%.4F) %.4F cm ', $scale, -$scale, ($x + $sw*$scale)*$this->kp, -$y*$this->kp);
|
2047 |
+
|
2048 |
+
$this->subPathInit = true;
|
2049 |
+
$this->pathBBox = array(999999,999999,-999999,-999999);
|
2050 |
+
foreach($commands as $cmd){
|
2051 |
+
if(count($cmd)==3 || (isset($cmd[2]) && $cmd[2]=='')){
|
2052 |
+
list($tmp, $command, $arguments) = $cmd;
|
2053 |
+
}
|
2054 |
+
else{
|
2055 |
+
list($tmp, $command) = $cmd;
|
2056 |
+
$arguments = '';
|
2057 |
+
}
|
2058 |
+
|
2059 |
+
$subpath_cmd .= $this->svgPath($command, $arguments);
|
2060 |
+
}
|
2061 |
+
$subpath_cmd .= $op . ' Q ';
|
2062 |
+
if ($this->pathBBox[2]==-1999998) { $this->pathBBox[2] = 100; }
|
2063 |
+
if ($this->pathBBox[3]==-1999998) { $this->pathBBox[3] = 100; }
|
2064 |
+
if ($this->pathBBox[0]==999999) { $this->pathBBox[0] = 0; }
|
2065 |
+
if ($this->pathBBox[1]==999999) { $this->pathBBox[1] = 0; }
|
2066 |
+
|
2067 |
+
|
2068 |
+
$sw += $horiz_adv_x;
|
2069 |
+
$i++;
|
2070 |
+
}
|
2071 |
+
|
2072 |
+
$sw *= $scale; // convert stringwidth to units
|
2073 |
+
|
2074 |
+
// mPDF 5.7.4
|
2075 |
+
$this->textlength = $sw;
|
2076 |
+
$this->texttotallength += $this->textlength;
|
2077 |
+
|
2078 |
+
$path_cmd = sprintf('q %s %s Tr %s %s ',$opacitystr,$render,$fillstr,$strokestr);
|
2079 |
+
$path_cmd .= $subpath_cmd;
|
2080 |
+
$path_cmd .= 'Q ';
|
2081 |
+
|
2082 |
+
unset($this->txt_data[0], $this->txt_data[1], $this->txt_data[2]);
|
2083 |
+
return $path_cmd;
|
2084 |
+
}
|
2085 |
+
|
2086 |
+
// select font
|
2087 |
+
$style .= ($current_style['font-weight'] == 'bold')?'B':'';
|
2088 |
+
$style .= ($current_style['font-style'] == 'italic')?'I':'';
|
2089 |
+
$size = $current_style['font-size']*$this->kf;
|
2090 |
+
|
2091 |
+
$current_style['font-family'] = $this->mpdf_ref->SetFont($current_style['font-family'],$style,$size,false);
|
2092 |
+
$this->mpdf_ref->CurrentFont['fo'] = true;
|
2093 |
+
|
2094 |
+
$opacitystr = '';
|
2095 |
+
// mPDF 6
|
2096 |
+
$fopacity = 1;
|
2097 |
+
if (isset($current_style['fill-opacity'])) {
|
2098 |
+
if ($current_style['fill-opacity'] == 0) { $fopacity = 0; }
|
2099 |
+
else if ($current_style['fill-opacity'] > 1) { $fopacity = 1; }
|
2100 |
+
else if ($current_style['fill-opacity'] > 0) { $fopacity = $current_style['fill-opacity']; }
|
2101 |
+
else if ($current_style['fill-opacity'] < 0) { $fopacity = 0; }
|
2102 |
+
}
|
2103 |
+
$sopacity = 1;
|
2104 |
+
if (isset($current_style['stroke-opacity'])) {
|
2105 |
+
if ($current_style['stroke-opacity'] == 0) { $sopacity = 0; }
|
2106 |
+
else if ($current_style['stroke-opacity'] > 1) { $sopacity = 1; }
|
2107 |
+
else if ($current_style['stroke-opacity'] > 0) { $sopacity = $current_style['stroke-opacity']; }
|
2108 |
+
else if ($current_style['stroke-opacity'] < 0) { $sopacity = 0; }
|
2109 |
+
}
|
2110 |
+
$gs = $this->mpdf_ref->AddExtGState(array('ca'=>$fopacity, 'CA'=>$sopacity, 'BM'=>'/Normal'));
|
2111 |
+
$this->mpdf_ref->extgstates[$gs]['fo'] = true;
|
2112 |
+
$opacitystr = sprintf(' /GS%d gs ', $gs);
|
2113 |
+
|
2114 |
+
$fillstr = '';
|
2115 |
+
if (isset($current_style['fill']) && $current_style['fill']!='none') {
|
2116 |
+
$col = $this->mpdf_ref->ConvertColor($current_style['fill']);
|
2117 |
+
$fillstr = $this->mpdf_ref->SetFColor($col, true);
|
2118 |
+
$render = "0"; // Fill (only)
|
2119 |
+
}
|
2120 |
+
$strokestr = '';
|
2121 |
+
if (isset($current_style['stroke-width']) && $current_style['stroke-width']>0 && $current_style['stroke']!='none') {
|
2122 |
+
$scol = $this->mpdf_ref->ConvertColor($current_style['stroke']);
|
2123 |
+
if ($scol) {
|
2124 |
+
$strokestr .= $this->mpdf_ref->SetDColor($scol, true).' ';
|
2125 |
+
}
|
2126 |
+
$linewidth = $this->ConvertSVGSizePixels($current_style['stroke-width']);
|
2127 |
+
if ($linewidth > 0) {
|
2128 |
+
$strokestr .= sprintf('%.3F w 1 J 1 j ',$linewidth*$this->kp);
|
2129 |
+
if ($render == -1) { $render = "1"; } // stroke only
|
2130 |
+
else { $render = "2"; } // fill and stroke
|
2131 |
+
}
|
2132 |
+
}
|
2133 |
+
if ($render == -1) { return ''; }
|
2134 |
+
|
2135 |
+
$x = $this->txt_data[0]; // mPDF 5.7.4
|
2136 |
+
$y = $this->txt_data[1]; // mPDF 5.7.4
|
2137 |
+
$txt = $this->txt_data[2];
|
2138 |
+
|
2139 |
+
$txt = preg_replace('/\f/','',$txt);
|
2140 |
+
$txt = preg_replace('/\r/','',$txt);
|
2141 |
+
$txt = preg_replace('/\n/',' ',$txt);
|
2142 |
+
$txt = preg_replace('/\t/',' ',$txt);
|
2143 |
+
$txt = preg_replace("/[ ]+/u",' ',$txt);
|
2144 |
+
|
2145 |
+
if ($this->textjuststarted) { $txt = ltrim($txt); } // mPDF 5.7.4
|
2146 |
+
$this->textjuststarted = false; // mPDF 5.7.4
|
2147 |
+
|
2148 |
+
$txt = $this->mpdf_ref->purify_utf8_text($txt);
|
2149 |
+
if ($this->mpdf_ref->text_input_as_HTML) {
|
2150 |
+
$txt = $this->mpdf_ref->all_entities_to_utf8($txt);
|
2151 |
+
}
|
2152 |
+
|
2153 |
+
if ($this->mpdf_ref->usingCoreFont) { $txt = mb_convert_encoding($txt,$this->mpdf_ref->mb_enc,'UTF-8'); }
|
2154 |
+
if (preg_match("/([".$this->mpdf_ref->pregRTLchars."])/u", $txt)) { $this->mpdf_ref->biDirectional = true; }
|
2155 |
+
|
2156 |
+
$textvar = 0;
|
2157 |
+
$save_OTLtags = $this->mpdf_ref->OTLtags;
|
2158 |
+
$this->mpdf_ref->OTLtags = array();
|
2159 |
+
if ($this->mpdf_ref->useKerning) {
|
2160 |
+
if ($this->mpdf_ref->CurrentFont['haskernGPOS']) {
|
2161 |
+
if (isset($this->mpdf_ref->OTLtags['Plus'])) { $this->mpdf_ref->OTLtags['Plus'] .= ' kern'; }
|
2162 |
+
else { $this->mpdf_ref->OTLtags['Plus'] = ' kern'; }
|
2163 |
+
}
|
2164 |
+
else { $textvar = ($textvar | FC_KERNING); }
|
2165 |
+
}
|
2166 |
+
|
2167 |
+
// Use OTL OpenType Table Layout - GSUB & GPOS
|
2168 |
+
if (isset($this->mpdf_ref->CurrentFont['useOTL']) && $this->mpdf_ref->CurrentFont['useOTL']) {
|
2169 |
+
$txt = $this->mpdf_ref->otl->applyOTL($txt, $this->mpdf_ref->CurrentFont['useOTL']);
|
2170 |
+
$OTLdata = $this->mpdf_ref->otl->OTLdata;
|
2171 |
+
}
|
2172 |
+
$this->mpdf_ref->OTLtags = $save_OTLtags ;
|
2173 |
+
|
2174 |
+
$this->mpdf_ref->magic_reverse_dir($txt, $this->mpdf_ref->directionality, $OTLdata);
|
2175 |
+
|
2176 |
+
$this->mpdf_ref->CurrentFont['used']= true;
|
2177 |
+
|
2178 |
+
$sw = $this->mpdf_ref->GetStringWidth($txt, true, $OTLdata, $textvar); // also adds characters to subset
|
2179 |
+
|
2180 |
+
// mPDF 5.7.4
|
2181 |
+
$this->textlength = $sw * 1/(25.4/$this->mpdf_ref->dpi);
|
2182 |
+
$this->texttotallength += $this->textlength;
|
2183 |
+
|
2184 |
+
$pdfx = $x *$this->kp;
|
2185 |
+
$pdfy = -$y *$this->kp;
|
2186 |
+
|
2187 |
+
$aixextra = sprintf(' /F%d %.3F Tf %s %s Tr %s %s ',$this->mpdf_ref->CurrentFont['i'],$this->mpdf_ref->FontSizePt,$opacitystr,$render,$fillstr,$strokestr);
|
2188 |
+
|
2189 |
+
$path_cmd = 'q 1 0 0 1 mPDF-AXS(0.00) 0 cm '; // Align X-shift
|
2190 |
+
$path_cmd .= $this->mpdf_ref->Text($pdfx,$pdfy,$txt,$OTLdata,$textvar,$aixextra,'SVG',true);
|
2191 |
+
$path_cmd .= " Q\n";
|
2192 |
+
|
2193 |
+
unset($this->txt_data[0], $this->txt_data[1], $this->txt_data[2]);
|
2194 |
+
|
2195 |
+
if (isset($current_style['font-size-parent'])) {
|
2196 |
+
$this->mpdf_ref->SetFontSize($current_style['font-size-parent']);
|
2197 |
+
}
|
2198 |
+
}
|
2199 |
+
else { return ' '; }
|
2200 |
+
// Reset font // mPDF 5.7.4
|
2201 |
+
$prev_style = $this->txt_style[count($this->txt_style)-1];
|
2202 |
+
$style = '';
|
2203 |
+
$style .= ($prev_style['font-weight'] == 'bold')?'B':'';
|
2204 |
+
$style .= ($prev_style['font-style'] == 'italic')?'I':'';
|
2205 |
+
$size = $prev_style['font-size']*$this->kf;
|
2206 |
+
$this->mpdf_ref->SetFont($prev_style['font-family'],$style,$size,false);
|
2207 |
+
|
2208 |
+
return $path_cmd;
|
2209 |
+
}
|
2210 |
+
|
2211 |
+
|
2212 |
+
function svgDefineTxtStyle($critere_style)
|
2213 |
+
{
|
2214 |
+
// get copy of current/default txt style, and modify it with supplied attributes
|
2215 |
+
$tmp = count($this->txt_style)-1;
|
2216 |
+
$current_style = $this->txt_style[$tmp];
|
2217 |
+
if (isset($critere_style['style'])){
|
2218 |
+
if (preg_match('/fill:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/',$critere_style['style'], $m)) {
|
2219 |
+
$current_style['fill'] = '#'.str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
|
2220 |
+
}
|
2221 |
+
else { $tmp = preg_replace("/(.*)fill:\s*([a-z0-9#_()]*|none)(.*)/i","$2",$critere_style['style']);
|
2222 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['fill'] = $tmp; }
|
2223 |
+
}
|
2224 |
+
|
2225 |
+
// mPDF 6
|
2226 |
+
if (preg_match("/[^-]opacity:\s*([a-z0-9.]*|none)/i",$critere_style['style'], $m) ||
|
2227 |
+
preg_match("/^opacity:\s*([a-z0-9.]*|none)/i",$critere_style['style'], $m)) {
|
2228 |
+
$current_style['fill-opacity'] = $m[1];
|
2229 |
+
$current_style['stroke-opacity'] = $m[1];
|
2230 |
+
}
|
2231 |
+
|
2232 |
+
$tmp = preg_replace("/(.*)fill-opacity:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
|
2233 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['fill-opacity'] = $tmp;}
|
2234 |
+
|
2235 |
+
$tmp = preg_replace("/(.*)fill-rule:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
2236 |
+
if ($tmp != $critere_style['style'] && $tmp!=$critere_style['style']){ $current_style['fill-rule'] = $tmp;}
|
2237 |
+
|
2238 |
+
if (preg_match('/stroke:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/',$critere_style['style'], $m)) {
|
2239 |
+
$current_style['stroke'] = '#'.str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
|
2240 |
+
}
|
2241 |
+
else { $tmp = preg_replace("/(.*)stroke:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
2242 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['stroke'] = $tmp; }
|
2243 |
+
}
|
2244 |
+
|
2245 |
+
$tmp = preg_replace("/(.*)stroke-linecap:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
2246 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-linecap'] = $tmp;}
|
2247 |
+
|
2248 |
+
$tmp = preg_replace("/(.*)stroke-linejoin:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
2249 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-linejoin'] = $tmp;}
|
2250 |
+
|
2251 |
+
$tmp = preg_replace("/(.*)stroke-miterlimit:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
|
2252 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-miterlimit'] = $tmp;}
|
2253 |
+
|
2254 |
+
$tmp = preg_replace("/(.*)stroke-opacity:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
|
2255 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-opacity'] = $tmp; }
|
2256 |
+
|
2257 |
+
$tmp = preg_replace("/(.*)stroke-width:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
|
2258 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-width'] = $tmp;}
|
2259 |
+
|
2260 |
+
$tmp = preg_replace("/(.*)stroke-dasharray:\s*([a-z0-9., ]*|none)(.*)/i","$2",$critere_style['style']);
|
2261 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-dasharray'] = $tmp;}
|
2262 |
+
|
2263 |
+
$tmp = preg_replace("/(.*)stroke-dashoffset:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
|
2264 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $current_style['stroke-dashoffset'] = $tmp;}
|
2265 |
+
|
2266 |
+
$tmp = preg_replace("/(.*)font-family:\s*([a-z0-9.\"' ,\-]*|none)(.*)/i","$2",$critere_style['style']);
|
2267 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $critere_style['font-family'] = $tmp;}
|
2268 |
+
|
2269 |
+
$tmp = preg_replace("/(.*)font-size:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
|
2270 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $critere_style['font-size'] = $tmp;}
|
2271 |
+
|
2272 |
+
$tmp = preg_replace("/(.*)font-weight:\s*([a-z0-9.]*|normal)(.*)/i","$2",$critere_style['style']);
|
2273 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $critere_style['font-weight'] = $tmp;}
|
2274 |
+
|
2275 |
+
$tmp = preg_replace("/(.*)font-style:\s*([a-z0-9.]*|normal)(.*)/i","$2",$critere_style['style']);
|
2276 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $critere_style['font-style'] = $tmp;}
|
2277 |
+
|
2278 |
+
$tmp = preg_replace("/(.*)font-variant:\s*([a-z0-9.]*|normal)(.*)/i","$2",$critere_style['style']);
|
2279 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $critere_style['font-variant'] = $tmp;}
|
2280 |
+
|
2281 |
+
$tmp = preg_replace("/(.*)text-anchor:\s*(start|middle|end)(.*)/i","$2",$critere_style['style']);
|
2282 |
+
if ($tmp && $tmp != 'inherit' && $tmp!=$critere_style['style']){ $critere_style['text-anchor'] = $tmp;}
|
2283 |
+
|
2284 |
+
}
|
2285 |
+
if (isset($critere_style['font'])){
|
2286 |
+
|
2287 |
+
// [ [ <'font-style'> || <'font-variant'> || <'font-weight'> ]?<'font-size'> [ / <'line-height'> ]? <'font-family'> ]
|
2288 |
+
|
2289 |
+
$tmp = preg_replace("/(.*)(italic|oblique)(.*)/i","$2",$critere_style['font']);
|
2290 |
+
if ($tmp != $critere_style['font']){
|
2291 |
+
if($tmp == 'oblique'){
|
2292 |
+
$tmp = 'italic';
|
2293 |
+
}
|
2294 |
+
$current_style['font-style'] = $tmp;
|
2295 |
+
}
|
2296 |
+
$tmp = preg_replace("/(.*)(bold|bolder)(.*)/i","$2",$critere_style['font']);
|
2297 |
+
if ($tmp != $critere_style['font']){
|
2298 |
+
if($tmp == 'bolder'){
|
2299 |
+
$tmp = 'bold';
|
2300 |
+
}
|
2301 |
+
$current_style['font-weight'] = $tmp;
|
2302 |
+
}
|
2303 |
+
|
2304 |
+
$tmp = preg_replace("/(.*)(small\-caps)(.*)/i","$2",$critere_style['font']);
|
2305 |
+
if ($tmp != $critere_style['font']){
|
2306 |
+
$current_style['font-variant'] = $tmp;
|
2307 |
+
}
|
2308 |
+
|
2309 |
+
// select digits not followed by percent sign nor preceeded by forward slash
|
2310 |
+
$tmp = preg_replace("/(.*)\b(\d+)[\b|\/](.*)/i","$2",$critere_style['font']);
|
2311 |
+
if ($tmp != $critere_style['font']){
|
2312 |
+
$current_style['font-size'] = $this->ConvertSVGSizePts($tmp);
|
2313 |
+
$this->mpdf_ref->SetFont('','',$current_style['font-size'],false);
|
2314 |
+
}
|
2315 |
+
|
2316 |
+
}
|
2317 |
+
|
2318 |
+
// mPDF 6
|
2319 |
+
if(isset($critere_style['opacity']) && $critere_style['opacity']!='inherit'){
|
2320 |
+
$current_style['fill-opacity'] = $critere_style['opacity'];
|
2321 |
+
$current_style['stroke-opacity'] = $critere_style['opacity'];
|
2322 |
+
}
|
2323 |
+
// mPDF 6
|
2324 |
+
if(isset($critere_style['stroke-opacity']) && $critere_style['stroke-opacity']!='inherit'){
|
2325 |
+
$current_style['stroke-opacity'] = $critere_style['stroke-opacity'];
|
2326 |
+
}
|
2327 |
+
// mPDF 6
|
2328 |
+
if(isset($critere_style['fill-opacity']) && $critere_style['fill-opacity']!='inherit'){
|
2329 |
+
$current_style['fill-opacity'] = $critere_style['fill-opacity'];
|
2330 |
+
}
|
2331 |
+
if(isset($critere_style['fill']) && $critere_style['fill']!='inherit'){
|
2332 |
+
$current_style['fill'] = $critere_style['fill'];
|
2333 |
+
}
|
2334 |
+
if(isset($critere_style['stroke']) && $critere_style['stroke']!='inherit'){
|
2335 |
+
$current_style['stroke'] = $critere_style['stroke'];
|
2336 |
+
}
|
2337 |
+
if(isset($critere_style['stroke-width']) && $critere_style['stroke-width']!='inherit'){
|
2338 |
+
$current_style['stroke-width'] = $critere_style['stroke-width'];
|
2339 |
+
}
|
2340 |
+
|
2341 |
+
if(isset($critere_style['font-style']) && $critere_style['font-style']!='inherit'){
|
2342 |
+
if(strtolower($critere_style['font-style']) == 'oblique')
|
2343 |
+
{
|
2344 |
+
$critere_style['font-style'] = 'italic';
|
2345 |
+
}
|
2346 |
+
$current_style['font-style'] = $critere_style['font-style'];
|
2347 |
+
}
|
2348 |
+
|
2349 |
+
if(isset($critere_style['font-weight']) && $critere_style['font-weight']!='inherit'){
|
2350 |
+
if(strtolower($critere_style['font-weight']) == 'bolder')
|
2351 |
+
{
|
2352 |
+
$critere_style['font-weight'] = 'bold';
|
2353 |
+
}
|
2354 |
+
$current_style['font-weight'] = $critere_style['font-weight'];
|
2355 |
+
}
|
2356 |
+
|
2357 |
+
if(isset($critere_style['font-variant']) && $critere_style['font-variant']!='inherit'){
|
2358 |
+
$current_style['font-variant'] = $critere_style['font-variant'];
|
2359 |
+
}
|
2360 |
+
|
2361 |
+
if(isset($critere_style['font-size']) && $critere_style['font-size']!='inherit'){
|
2362 |
+
if (strpos($critere_style['font-size'], '%')!==false) {
|
2363 |
+
$current_style['font-size-parent'] = $current_style['font-size'];
|
2364 |
+
}
|
2365 |
+
$current_style['font-size'] = $this->ConvertSVGSizePts($critere_style['font-size']);
|
2366 |
+
$this->mpdf_ref->SetFont('','',$current_style['font-size'],false);
|
2367 |
+
}
|
2368 |
+
|
2369 |
+
if(isset($critere_style['font-family']) && $critere_style['font-family']!='inherit'){
|
2370 |
+
$v = $critere_style['font-family'];
|
2371 |
+
$aux_fontlist = explode(",",$v);
|
2372 |
+
$found = 0;
|
2373 |
+
|
2374 |
+
$svgfontstyle = 'R';
|
2375 |
+
$svgfontstyle .= (isset($current_style['font-weight']) && $current_style['font-weight'] == 'bold')?'B':'';
|
2376 |
+
$svgfontstyle .= (isset($current_style['font-style']) && $current_style['font-style'] == 'italic')?'I':'';
|
2377 |
+
$svgfontstyle .= (isset($current_style['font-variant']) && $current_style['font-variant'] == 'small-caps')?'S':'';
|
2378 |
+
|
2379 |
+
foreach($aux_fontlist AS $f) {
|
2380 |
+
$fonttype = trim($f);
|
2381 |
+
$fonttype = preg_replace('/["\']*(.*?)["\']*/','\\1',$fonttype);
|
2382 |
+
$fonttype = preg_replace('/ /','',$fonttype);
|
2383 |
+
$v = strtolower(trim($fonttype));
|
2384 |
+
if (isset($this->mpdf_ref->fonttrans[$v]) && $this->mpdf_ref->fonttrans[$v]) { $v = $this->mpdf_ref->fonttrans[$v]; }
|
2385 |
+
if ((!$this->mpdf_ref->usingCoreFont && in_array($v,$this->mpdf_ref->available_unifonts)) ||
|
2386 |
+
($this->mpdf_ref->usingCoreFont && in_array($v,array('courier','times','helvetica','arial'))) ||
|
2387 |
+
in_array($v, array('sjis','uhc','big5','gb')) || isset($this->svg_font[$v][$svgfontstyle ])) { // mPDF 6
|
2388 |
+
$current_style['font-family'] = $v;
|
2389 |
+
$found = 1;
|
2390 |
+
break;
|
2391 |
+
}
|
2392 |
+
}
|
2393 |
+
if (!$found) {
|
2394 |
+
foreach($aux_fontlist AS $f) {
|
2395 |
+
$fonttype = trim($f);
|
2396 |
+
$fonttype = preg_replace('/["\']*(.*?)["\']*/','\\1',$fonttype);
|
2397 |
+
$fonttype = preg_replace('/ /','',$fonttype);
|
2398 |
+
$v = strtolower(trim($fonttype));
|
2399 |
+
if (isset($this->mpdf_ref->fonttrans[$v]) && $this->mpdf_ref->fonttrans[$v]) { $v = $this->mpdf_ref->fonttrans[$v]; }
|
2400 |
+
if (in_array($v,$this->mpdf_ref->sans_fonts) || in_array($v,$this->mpdf_ref->serif_fonts) || in_array($v,$this->mpdf_ref->mono_fonts) || isset($this->svg_font[$v][$svgfontstyle ])) { // mPDF 6
|
2401 |
+
$current_style['font-family'] = $v;
|
2402 |
+
break;
|
2403 |
+
}
|
2404 |
+
}
|
2405 |
+
}
|
2406 |
+
}
|
2407 |
+
if(isset($critere_style['text-anchor']) && $critere_style['text-anchor']!='inherit'){
|
2408 |
+
$current_style['text-anchor'] = $critere_style['text-anchor'];
|
2409 |
+
}
|
2410 |
+
|
2411 |
+
// add current style to text style array (will remove it later after writing text to svg_string)
|
2412 |
+
array_push($this->txt_style,$current_style);
|
2413 |
+
}
|
2414 |
+
|
2415 |
+
|
2416 |
+
|
2417 |
+
//
|
2418 |
+
// fonction ajoutant un gradient
|
2419 |
+
function svgAddGradient($id,$array_gradient){
|
2420 |
+
|
2421 |
+
$this->svg_gradient[$id] = $array_gradient;
|
2422 |
+
|
2423 |
+
}
|
2424 |
+
//
|
2425 |
+
// Ajoute une couleur dans le gradient correspondant
|
2426 |
+
|
2427 |
+
//
|
2428 |
+
// function ecrivant dans le svgstring
|
2429 |
+
function svgWriteString($content){
|
2430 |
+
|
2431 |
+
$this->svg_string .= $content;
|
2432 |
+
|
2433 |
+
}
|
2434 |
+
|
2435 |
+
|
2436 |
+
|
2437 |
+
// analise le svg et renvoie aux fonctions precedente our le traitement
|
2438 |
+
function ImageSVG($data){
|
2439 |
+
// Try to clean up the start of the file
|
2440 |
+
// removing DOCTYPE fails with this:
|
2441 |
+
/*
|
2442 |
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd"
|
2443 |
+
[
|
2444 |
+
<!ELEMENT Paragraph (#PCDATA)>
|
2445 |
+
]>
|
2446 |
+
*/
|
2447 |
+
//$data = preg_replace('/<!DOCTYPE.*? >/is', '', $data);
|
2448 |
+
//$data = preg_replace('/<\?xml.*? >/is', '', $data);
|
2449 |
+
|
2450 |
+
$data = preg_replace('/^.*?<svg([> ])/is', '<svg\\1', $data); // mPDF 5.7.4
|
2451 |
+
|
2452 |
+
$data = preg_replace('/<!--.*?-->/is', '', $data); // mPDF 5.7.4
|
2453 |
+
|
2454 |
+
// Converts < to < when not a tag
|
2455 |
+
$data = preg_replace('/<([^!?\/a-zA-Z_:])/i','<\\1',$data ); // mPDF 5.7.4
|
2456 |
+
|
2457 |
+
if (_SVG_AUTOFONT) { $data = $this->markScriptToLang($data); }
|
2458 |
+
|
2459 |
+
$this->svg_info = array();
|
2460 |
+
$last_gradid = ''; // mPDF 6
|
2461 |
+
$last_svg_fontid = ''; // mPDF 6
|
2462 |
+
$last_svg_fontdefw = ''; // mPDF 6
|
2463 |
+
$last_svg_fontstyle = ''; // mPDF 6
|
2464 |
+
|
2465 |
+
if (preg_match('/<!ENTITY/si',$data)) {
|
2466 |
+
// Get User-defined entities
|
2467 |
+
preg_match_all('/<!ENTITY\s+([a-z]+)\s+\"(.*?)\">/si',$data, $ent);
|
2468 |
+
// Replace entities
|
2469 |
+
for ($i=0; $i<count($ent[0]); $i++) {
|
2470 |
+
$data = preg_replace('/&'.preg_quote($ent[1][$i],'/').';/is', $ent[2][$i], $data);
|
2471 |
+
}
|
2472 |
+
}
|
2473 |
+
|
2474 |
+
if (preg_match('/xlink:href\s*=/si',$data)) {
|
2475 |
+
// GRADIENTS
|
2476 |
+
// Get links
|
2477 |
+
preg_match_all('/(<(linearGradient|radialgradient)[^>]*)xlink:href\s*=\s*["\']#(.*?)["\'](.*?)\/>/si',$data, $links);
|
2478 |
+
if (count($links[0])) { $links[5] = array(); }
|
2479 |
+
// Delete links from data - keeping in $links
|
2480 |
+
for ($i=0; $i<count($links[0]); $i++) {
|
2481 |
+
$links[5][$i] = 'tmpLink'.RAND(100000,9999999);
|
2482 |
+
$data = preg_replace('/'.preg_quote($links[0][$i],'/').'/is', '<MYLINKS'.$links[5][$i].'>' , $data);
|
2483 |
+
}
|
2484 |
+
// Get targets
|
2485 |
+
preg_match_all('/<(linearGradient|radialgradient)([^>]*)id\s*=\s*["\'](.*?)["\'](.*?)>(.*?)<\/(linearGradient|radialgradient)>/si',$data, $m);
|
2486 |
+
$targets = array();
|
2487 |
+
$stops = array();
|
2488 |
+
// keeping in $targets
|
2489 |
+
for ($i=0; $i<count($m[0]); $i++) {
|
2490 |
+
$stops[$m[3][$i]] = $m[5][$i];
|
2491 |
+
}
|
2492 |
+
// Add back links this time as targets (gradients)
|
2493 |
+
for ($i=0; $i<count($links[0]); $i++) {
|
2494 |
+
$def = $links[1][$i] .' '.$links[4][$i].'>'. $stops[$links[3][$i]].'</'.$links[2][$i] .'>' ;
|
2495 |
+
$data = preg_replace('/<MYLINKS'.$links[5][$i].'>/is', $def , $data);
|
2496 |
+
}
|
2497 |
+
|
2498 |
+
// mPDF 5.7.4
|
2499 |
+
// <TREF>
|
2500 |
+
preg_match_all('/<tref ([^>]*)xlink:href\s*=\s*["\']#([^>]*?)["\']([^>]*)\/>/si',$data, $links);
|
2501 |
+
for ($i=0; $i<count($links[0]); $i++) {
|
2502 |
+
|
2503 |
+
// Get the item to use from defs
|
2504 |
+
$insert = '';
|
2505 |
+
if (preg_match('/<text [^>]*id\s*=\s*["\']'.$links[2][$i].'["\'][^>]*>(.*?)<\/text>/si',$data, $m)) {
|
2506 |
+
$insert = $m[1];
|
2507 |
+
}
|
2508 |
+
if ($insert) {
|
2509 |
+
$data = preg_replace('/'.preg_quote($links[0][$i],'/').'/is', $insert, $data);
|
2510 |
+
}
|
2511 |
+
}
|
2512 |
+
|
2513 |
+
// mPDF 5.7.2
|
2514 |
+
// <USE>
|
2515 |
+
preg_match_all('/<use ([^>]*)xlink:href\s*=\s*["\']#([^>]*?)["\']([^>]*)\/>/si',$data, $links);
|
2516 |
+
for ($i=0; $i<count($links[0]); $i++) {
|
2517 |
+
|
2518 |
+
// Get the item to use from defs
|
2519 |
+
$insert = '';
|
2520 |
+
if (preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']'.$links[2][$i].'["\'][^>]*\/>/si',$data, $m)) {
|
2521 |
+
$insert = $m[0];
|
2522 |
+
}
|
2523 |
+
if (!$insert && preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']'.$links[2][$i].'["\']/si',$data, $m)) {
|
2524 |
+
|
2525 |
+
if (preg_match('/<'.$m[1].'[^>]*id\s*=\s*["\']'.$links[2][$i].'["\'][^>]*>.*?<\/'.$m[1].'>/si',$data, $m)) {
|
2526 |
+
$insert = $m[0];
|
2527 |
+
}
|
2528 |
+
}
|
2529 |
+
|
2530 |
+
if ($insert) {
|
2531 |
+
|
2532 |
+
$inners = $links[1][$i] . ' ' . $links[3][$i];
|
2533 |
+
// Change x,y coords to translate()
|
2534 |
+
if (preg_match('/y\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) { $y = $m[1]; }
|
2535 |
+
else { $y = 0; }
|
2536 |
+
if (preg_match('/x\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) { $x = $m[1]; }
|
2537 |
+
else { $x = 0; }
|
2538 |
+
if ($x || $y) {
|
2539 |
+
$inners = preg_replace('/(y|x)\s*=\s*["\']([^>]*?)["\']/', '', $inners);
|
2540 |
+
if (preg_match('/transform\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
|
2541 |
+
if (preg_match('/translate\(\s*([0-9\.]+)\s*,\s*([0-9\.]+)\s*\)/', $m[1], $mm)) {
|
2542 |
+
$transform = $m[1]; // transform="...."
|
2543 |
+
$x += $mm[1];
|
2544 |
+
$y += $mm[2];
|
2545 |
+
$transform = preg_replace('/'.preg_quote($mm[0],'/').'/', '', $transform);
|
2546 |
+
$transform = 'transform="'.$transform.' translate('.$x.', '.$y.')"';
|
2547 |
+
$inners = preg_replace('/'.preg_quote($m[0],'/').'/is', $transform, $inners);
|
2548 |
+
}
|
2549 |
+
else {
|
2550 |
+
$inners = preg_replace('/'.preg_quote($m[0],'/').'/is', 'transform="'.$m[1].' translate('.$x.', '.$y.')"', $inners);
|
2551 |
+
}
|
2552 |
+
}
|
2553 |
+
else {
|
2554 |
+
$inners .= ' transform="translate('.$x.', '.$y.')"';
|
2555 |
+
}
|
2556 |
+
}
|
2557 |
+
}
|
2558 |
+
$replacement = '<g '.$inners.'>'.$insert.'</g>';
|
2559 |
+
$data = preg_replace('/'.preg_quote($links[0][$i],'/').'/is', $replacement, $data);
|
2560 |
+
}
|
2561 |
+
preg_match_all('/<use ([^>]*)xlink:href\s*=\s*["\']#([^>]*?)["\']([^>]*)>\s*<\/use>/si',$data, $links);
|
2562 |
+
for ($i=0; $i<count($links[0]); $i++) {
|
2563 |
+
|
2564 |
+
// Get the item to use from defs
|
2565 |
+
$insert = '';
|
2566 |
+
if (preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']'.$links[2][$i].'["\'][^>]*\/>/si',$data, $m)) {
|
2567 |
+
$insert = $m[0];
|
2568 |
+
}
|
2569 |
+
if (!$insert && preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']'.$links[2][$i].'["\']/si',$data, $m)) {
|
2570 |
+
|
2571 |
+
if (preg_match('/<'.$m[1].'[^>]*id\s*=\s*["\']'.$links[2][$i].'["\'][^>]*>.*?<\/'.$m[1].'>/si',$data, $m)) {
|
2572 |
+
$insert = $m[0];
|
2573 |
+
}
|
2574 |
+
}
|
2575 |
+
|
2576 |
+
if ($insert) {
|
2577 |
+
|
2578 |
+
$inners = $links[1][$i] . ' ' . $links[3][$i];
|
2579 |
+
// Change x,y coords to translate()
|
2580 |
+
if (preg_match('/y\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) { $y = $m[1]; }
|
2581 |
+
else { $y = 0; }
|
2582 |
+
if (preg_match('/x\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) { $x = $m[1]; }
|
2583 |
+
else { $x = 0; }
|
2584 |
+
if ($x || $y) {
|
2585 |
+
$inners = preg_replace('/(y|x)\s*=\s*["\']([^>]*?)["\']/', '', $inners);
|
2586 |
+
if (preg_match('/transform\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
|
2587 |
+
if (preg_match('/translate\(\s*([0-9\.]+)\s*,\s*([0-9\.]+)\s*\)/', $m[1], $mm)) {
|
2588 |
+
$transform = $m[1]; // transform="...."
|
2589 |
+
$x += $mm[1];
|
2590 |
+
$y += $mm[2];
|
2591 |
+
$transform = preg_replace('/'.preg_quote($mm[0],'/').'/', '', $transform);
|
2592 |
+
$transform = 'transform="'.$transform.' translate('.$x.', '.$y.')"';
|
2593 |
+
$inners = preg_replace('/'.preg_quote($m[0],'/').'/is', $transform, $inners);
|
2594 |
+
}
|
2595 |
+
else {
|
2596 |
+
$inners = preg_replace('/'.preg_quote($m[0],'/').'/is', 'transform="'.$m[1].' translate('.$x.', '.$y.')"', $inners);
|
2597 |
+
}
|
2598 |
+
}
|
2599 |
+
else {
|
2600 |
+
$inners .= ' transform="translate('.$x.', '.$y.')"';
|
2601 |
+
}
|
2602 |
+
}
|
2603 |
+
$replacement = '<g '.$inners.'>'.$insert.'</g>';
|
2604 |
+
$data = preg_replace('/'.preg_quote($links[0][$i],'/').'/is', $replacement, $data);
|
2605 |
+
|
2606 |
+
|
2607 |
+
}
|
2608 |
+
}
|
2609 |
+
}
|
2610 |
+
|
2611 |
+
// Removes <pattern>
|
2612 |
+
$data = preg_replace('/<pattern.*?<\/pattern>/is', '', $data);
|
2613 |
+
// Removes <marker>
|
2614 |
+
$data = preg_replace('/<marker.*?<\/marker>/is', '', $data);
|
2615 |
+
|
2616 |
+
$this->svg_info['data'] = $data;
|
2617 |
+
|
2618 |
+
$this->svg_string = '';
|
2619 |
+
|
2620 |
+
//
|
2621 |
+
// chargement unique des fonctions
|
2622 |
+
if(!function_exists("xml_svg2pdf_start")){
|
2623 |
+
|
2624 |
+
function xml_svg2pdf_start($parser, $name, $attribs){
|
2625 |
+
//
|
2626 |
+
// definition
|
2627 |
+
global $svg_class, $last_gradid, $last_svg_fontid, $last_svg_fontdefw, $last_svg_fontstyle; // mPDF 6
|
2628 |
+
|
2629 |
+
// mPDF 6
|
2630 |
+
if (strtolower($name) == 'font'){
|
2631 |
+
$last_svg_fontid = '';
|
2632 |
+
if (isset($attribs['horiz-adv-x']) && $attribs['horiz-adv-x']) {
|
2633 |
+
$last_svg_fontdefw = $attribs['horiz-adv-x'];
|
2634 |
+
}
|
2635 |
+
return;
|
2636 |
+
}
|
2637 |
+
// mPDF 6
|
2638 |
+
else if (strtolower($name) == 'font-face'){
|
2639 |
+
$last_svg_fontstyle = 'R';
|
2640 |
+
$last_svg_fontstyle .= (isset($attribs['font-weight']) && $attribs['font-weight'] == 'bold')?'B':'';
|
2641 |
+
$last_svg_fontstyle .= (isset($attribs['font-style']) && $attribs['font-style'] == 'italic')?'I':'';
|
2642 |
+
$last_svg_fontstyle .= (isset($attribs['font-variant']) && $attribs['font-variant'] == 'small-caps')?'S':'';
|
2643 |
+
|
2644 |
+
if (isset($attribs['font-family']) && $attribs['font-family']) {
|
2645 |
+
$tmp_svg_font = array(
|
2646 |
+
'units-per-em' => (isset($attribs['units-per-em']) ? $attribs['units-per-em'] : ''),
|
2647 |
+
'd' => '',
|
2648 |
+
'glyphs' => array()
|
2649 |
+
);
|
2650 |
+
$last_svg_fontid = strtolower($attribs['font-family']);
|
2651 |
+
if ($last_svg_fontdefw) { $tmp_svg_font['horiz-adv-x'] = $last_svg_fontdefw; }
|
2652 |
+
else { $tmp_svg_font['horiz-adv-x'] = 500; }
|
2653 |
+
$svg_class->svg_font[$last_svg_fontid][$last_svg_fontstyle] = $tmp_svg_font;
|
2654 |
+
}
|
2655 |
+
return;
|
2656 |
+
}
|
2657 |
+
// mPDF 6
|
2658 |
+
else if (strtolower($name) == 'missing-glyph'){
|
2659 |
+
if ($last_svg_fontid && isset($attribs['horiz-adv-x'])) {
|
2660 |
+
$svg_class->svg_font[$last_svg_fontid][$last_svg_fontstyle]['horiz-adv-x'] = (isset($attribs['horiz-adv-x']) ? $attribs['horiz-adv-x'] : '');
|
2661 |
+
$svg_class->svg_font[$last_svg_fontid][$last_svg_fontstyle]['d'] = (isset($attribs['d']) ? $attribs['d'] : '');
|
2662 |
+
}
|
2663 |
+
return;
|
2664 |
+
}
|
2665 |
+
// mPDF 6
|
2666 |
+
else if (strtolower($name) == 'glyph'){
|
2667 |
+
if ($last_svg_fontid && isset($attribs['unicode'])) {
|
2668 |
+
$svg_class->svg_font[$last_svg_fontid][$last_svg_fontstyle]['glyphs'][$attribs['unicode']] = array(
|
2669 |
+
'horiz-adv-x' => (isset($attribs['horiz-adv-x']) ? $attribs['horiz-adv-x'] : $last_svg_fontdefw),
|
2670 |
+
'd' => (isset($attribs['d']) ? $attribs['d'] : ''),
|
2671 |
+
);
|
2672 |
+
}
|
2673 |
+
return;
|
2674 |
+
}
|
2675 |
+
// mPDF 5.7.2
|
2676 |
+
else if (strtolower($name) == 'lineargradient'){
|
2677 |
+
$tmp_gradient = array(
|
2678 |
+
'type' => 'linear',
|
2679 |
+
'transform' => (isset($attribs['gradientTransform']) ? $attribs['gradientTransform'] : ''),
|
2680 |
+
'units' => (isset($attribs['gradientUnits']) ? $attribs['gradientUnits'] : ''),
|
2681 |
+
'spread' => (isset($attribs['spreadMethod']) ? $attribs['spreadMethod'] : ''),
|
2682 |
+
'color' => array()
|
2683 |
+
);
|
2684 |
+
if (isset($attribs['x1'])) $tmp_gradient['info']['x1'] = $attribs['x1'] ;
|
2685 |
+
if (isset($attribs['y1'])) $tmp_gradient['info']['y1'] = $attribs['y1'] ;
|
2686 |
+
if (isset($attribs['x2'])) $tmp_gradient['info']['x2'] = $attribs['x2'] ;
|
2687 |
+
if (isset($attribs['y2'])) $tmp_gradient['info']['y2'] = $attribs['y2'] ;
|
2688 |
+
$last_gradid = $attribs['id'];
|
2689 |
+
$svg_class->svgAddGradient($attribs['id'],$tmp_gradient);
|
2690 |
+
return;
|
2691 |
+
}
|
2692 |
+
else if (strtolower($name) == 'radialgradient'){
|
2693 |
+
$tmp_gradient = array(
|
2694 |
+
'type' => 'radial',
|
2695 |
+
'transform' => (isset($attribs['gradientTransform']) ? $attribs['gradientTransform'] : ''),
|
2696 |
+
'units' => (isset($attribs['gradientUnits']) ? $attribs['gradientUnits'] : ''),
|
2697 |
+
'spread' => (isset($attribs['spreadMethod']) ? $attribs['spreadMethod'] : ''),
|
2698 |
+
'color' => array()
|
2699 |
+
);
|
2700 |
+
if (isset($attribs['cx'])) $tmp_gradient['info']['x0'] = $attribs['cx'] ;
|
2701 |
+
if (isset($attribs['cy'])) $tmp_gradient['info']['y0'] = $attribs['cy'] ;
|
2702 |
+
if (isset($attribs['fx'])) $tmp_gradient['info']['x1'] = $attribs['fx'] ;
|
2703 |
+
if (isset($attribs['fy'])) $tmp_gradient['info']['y1'] = $attribs['fy'] ;
|
2704 |
+
if (isset($attribs['r'])) $tmp_gradient['info']['r'] = $attribs['r'] ;
|
2705 |
+
$last_gradid = $attribs['id'];
|
2706 |
+
$svg_class->svgAddGradient($attribs['id'],$tmp_gradient);
|
2707 |
+
return;
|
2708 |
+
}
|
2709 |
+
else if (strtolower($name) == 'stop'){
|
2710 |
+
if (!$last_gradid) break;
|
2711 |
+
$color = '#000000';
|
2712 |
+
if (isset($attribs['style']) AND preg_match('/stop-color:\s*([^;]*)/i',$attribs['style'],$m)) {
|
2713 |
+
$color = trim($m[1]);
|
2714 |
+
} else if (isset($attribs['stop-color']) && $attribs['stop-color']) {
|
2715 |
+
$color = $attribs['stop-color'];
|
2716 |
+
}
|
2717 |
+
$col = $svg_class->mpdf_ref->ConvertColor($color);
|
2718 |
+
if (!$col) { $col = $svg_class->mpdf_ref->ConvertColor('#000000'); } // In case "transparent" or "inherit" returned
|
2719 |
+
if ($col{0}==3 || $col{0}==5) { // RGB
|
2720 |
+
$color_final = sprintf('%.3F %.3F %.3F',ord($col{1})/255,ord($col{2})/255,ord($col{3})/255);
|
2721 |
+
$svg_class->svg_gradient[$last_gradid]['colorspace']='RGB';
|
2722 |
+
}
|
2723 |
+
else if ($col{0}==4 || $col{0}==6) { // CMYK
|
2724 |
+
$color_final = sprintf('%.3F %.3F %.3F %.3F',ord($col{1})/100,ord($col{2})/100,ord($col{3})/100,ord($col{4})/100);
|
2725 |
+
$svg_class->svg_gradient[$last_gradid]['colorspace']='CMYK';
|
2726 |
+
}
|
2727 |
+
else if ($col{0}==1) { // Grayscale
|
2728 |
+
$color_final = sprintf('%.3F',ord($col{1})/255);
|
2729 |
+
$svg_class->svg_gradient[$last_gradid]['colorspace']='Gray';
|
2730 |
+
}
|
2731 |
+
|
2732 |
+
$stop_opacity = 1;
|
2733 |
+
if (isset($attribs['style']) AND preg_match('/stop-opacity:\s*([0-9.]*)/i',$attribs['style'],$m)) {
|
2734 |
+
$stop_opacity = $m[1];
|
2735 |
+
} else if (isset($attribs['stop-opacity'])) {
|
2736 |
+
$stop_opacity = $attribs['stop-opacity'];
|
2737 |
+
}
|
2738 |
+
else if ($col{0}==5) { // RGBa
|
2739 |
+
$stop_opacity = ord($col{4}/100);
|
2740 |
+
}
|
2741 |
+
else if ($col{0}==6) { // CMYKa
|
2742 |
+
$stop_opacity = ord($col{5}/100);
|
2743 |
+
}
|
2744 |
+
|
2745 |
+
$tmp_color = array(
|
2746 |
+
'color' => $color_final,
|
2747 |
+
'offset' => (isset($attribs['offset']) ? $attribs['offset'] : ''),
|
2748 |
+
'opacity' => $stop_opacity
|
2749 |
+
);
|
2750 |
+
array_push($svg_class->svg_gradient[$last_gradid]['color'],$tmp_color);
|
2751 |
+
return;
|
2752 |
+
}
|
2753 |
+
if ($svg_class->inDefs) { return; }
|
2754 |
+
|
2755 |
+
$svg_class->xbase = 0;
|
2756 |
+
$svg_class->ybase = 0;
|
2757 |
+
switch (strtolower($name)){
|
2758 |
+
|
2759 |
+
// Don't output stuff inside <defs>
|
2760 |
+
case 'defs':
|
2761 |
+
$svg_class->inDefs = true;
|
2762 |
+
return;
|
2763 |
+
|
2764 |
+
case 'svg':
|
2765 |
+
$svg_class->svgOffset($attribs);
|
2766 |
+
break;
|
2767 |
+
|
2768 |
+
case 'path':
|
2769 |
+
$path = $attribs['d'];
|
2770 |
+
preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $path, $commands, PREG_SET_ORDER);
|
2771 |
+
$path_cmd = '';
|
2772 |
+
$svg_class->subPathInit = true;
|
2773 |
+
$svg_class->pathBBox = array(999999,999999,-999999,-999999);
|
2774 |
+
foreach($commands as $c){
|
2775 |
+
if((isset($c) && count($c)==3) || (isset($c[2]) && $c[2]=='')){
|
2776 |
+
list($tmp, $command, $arguments) = $c;
|
2777 |
+
}
|
2778 |
+
else{
|
2779 |
+
list($tmp, $command) = $c;
|
2780 |
+
$arguments = '';
|
2781 |
+
}
|
2782 |
+
|
2783 |
+
$path_cmd .= $svg_class->svgPath($command, $arguments);
|
2784 |
+
}
|
2785 |
+
if ($svg_class->pathBBox[2]==-1999998) { $svg_class->pathBBox[2] = 100; }
|
2786 |
+
if ($svg_class->pathBBox[3]==-1999998) { $svg_class->pathBBox[3] = 100; }
|
2787 |
+
if ($svg_class->pathBBox[0]==999999) { $svg_class->pathBBox[0] = 0; }
|
2788 |
+
if ($svg_class->pathBBox[1]==999999) { $svg_class->pathBBox[1] = 0; }
|
2789 |
+
$critere_style = $attribs;
|
2790 |
+
unset($critere_style['d']);
|
2791 |
+
$path_style = $svg_class->svgDefineStyle($critere_style);
|
2792 |
+
break;
|
2793 |
+
|
2794 |
+
case 'rect':
|
2795 |
+
if (!isset($attribs['x'])) {$attribs['x'] = 0;}
|
2796 |
+
if (!isset($attribs['y'])) {$attribs['y'] = 0;}
|
2797 |
+
if (!isset($attribs['rx'])) {$attribs['rx'] = 0;}
|
2798 |
+
if (!isset($attribs['ry'])) {$attribs['ry'] = 0;}
|
2799 |
+
$arguments = array();
|
2800 |
+
if (isset($attribs['x'])) $arguments['x'] = $attribs['x'];
|
2801 |
+
if (isset($attribs['y'])) $arguments['y'] = $attribs['y'];
|
2802 |
+
if (isset($attribs['width'])) $arguments['w'] = $attribs['width'];
|
2803 |
+
if (isset($attribs['height'])) $arguments['h'] = $attribs['height'];
|
2804 |
+
if (isset($attribs['rx'])) $arguments['rx'] = $attribs['rx'];
|
2805 |
+
if (isset($attribs['ry'])) $arguments['ry'] = $attribs['ry'];
|
2806 |
+
$path_cmd = $svg_class->svgRect($arguments);
|
2807 |
+
$critere_style = $attribs;
|
2808 |
+
unset($critere_style['x'],$critere_style['y'],$critere_style['rx'],$critere_style['ry'],$critere_style['height'],$critere_style['width']);
|
2809 |
+
$path_style = $svg_class->svgDefineStyle($critere_style);
|
2810 |
+
break;
|
2811 |
+
|
2812 |
+
case 'circle':
|
2813 |
+
if (!isset($attribs['cx'])) {$attribs['cx'] = 0;}
|
2814 |
+
if (!isset($attribs['cy'])) {$attribs['cy'] = 0;}
|
2815 |
+
$arguments = array();
|
2816 |
+
if (isset($attribs['cx'])) $arguments['cx'] = $attribs['cx'];
|
2817 |
+
if (isset($attribs['cy'])) $arguments['cy'] = $attribs['cy'];
|
2818 |
+
if (isset($attribs['r'])) $arguments['rx'] = $attribs['r'];
|
2819 |
+
if (isset($attribs['r'])) $arguments['ry'] = $attribs['r'];
|
2820 |
+
$path_cmd = $svg_class->svgEllipse($arguments);
|
2821 |
+
$critere_style = $attribs;
|
2822 |
+
unset($critere_style['cx'],$critere_style['cy'],$critere_style['r']);
|
2823 |
+
$path_style = $svg_class->svgDefineStyle($critere_style);
|
2824 |
+
break;
|
2825 |
+
|
2826 |
+
case 'ellipse':
|
2827 |
+
if (!isset($attribs['cx'])) {$attribs['cx'] = 0;}
|
2828 |
+
if (!isset($attribs['cy'])) {$attribs['cy'] = 0;}
|
2829 |
+
$arguments = array();
|
2830 |
+
if (isset($attribs['cx'])) $arguments['cx'] = $attribs['cx'];
|
2831 |
+
if (isset($attribs['cy'])) $arguments['cy'] = $attribs['cy'];
|
2832 |
+
if (isset($attribs['rx'])) $arguments['rx'] = $attribs['rx'];
|
2833 |
+
if (isset($attribs['ry'])) $arguments['ry'] = $attribs['ry'];
|
2834 |
+
$path_cmd = $svg_class->svgEllipse($arguments);
|
2835 |
+
$critere_style = $attribs;
|
2836 |
+
unset($critere_style['cx'],$critere_style['cy'],$critere_style['rx'],$critere_style['ry']);
|
2837 |
+
$path_style = $svg_class->svgDefineStyle($critere_style);
|
2838 |
+
break;
|
2839 |
+
|
2840 |
+
case 'line':
|
2841 |
+
$arguments = array();
|
2842 |
+
$arguments[0] = (isset($attribs['x1']) ? $attribs['x1'] : '');
|
2843 |
+
$arguments[1] = (isset($attribs['y1']) ? $attribs['y1'] : '');
|
2844 |
+
$arguments[2] = (isset($attribs['x2']) ? $attribs['x2'] : '');
|
2845 |
+
$arguments[3] = (isset($attribs['y2']) ? $attribs['y2'] : '');
|
2846 |
+
$path_cmd = $svg_class->svgPolyline($arguments,false);
|
2847 |
+
$critere_style = $attribs;
|
2848 |
+
unset($critere_style['x1'],$critere_style['y1'],$critere_style['x2'],$critere_style['y2']);
|
2849 |
+
$path_style = $svg_class->svgDefineStyle($critere_style);
|
2850 |
+
break;
|
2851 |
+
|
2852 |
+
case 'polyline':
|
2853 |
+
$path = $attribs['points'];
|
2854 |
+
preg_match_all('/[0-9\-\.]*/',$path, $tmp, PREG_SET_ORDER);
|
2855 |
+
$arguments = array();
|
2856 |
+
for ($i=0;$i<count($tmp);$i++){
|
2857 |
+
if ($tmp[$i][0] !=''){
|
2858 |
+
array_push($arguments, $tmp[$i][0]);
|
2859 |
+
}
|
2860 |
+
}
|
2861 |
+
$path_cmd = $svg_class->svgPolyline($arguments);
|
2862 |
+
$critere_style = $attribs;
|
2863 |
+
unset($critere_style['points']);
|
2864 |
+
$path_style = $svg_class->svgDefineStyle($critere_style);
|
2865 |
+
break;
|
2866 |
+
|
2867 |
+
case 'polygon':
|
2868 |
+
$path = $attribs['points'];
|
2869 |
+
preg_match_all('/([\-]*[0-9\.]+)/',$path, $tmp);
|
2870 |
+
$arguments = array();
|
2871 |
+
for ($i=0;$i<count($tmp[0]);$i++){
|
2872 |
+
if ($tmp[0][$i] !=''){
|
2873 |
+
array_push($arguments, $tmp[0][$i]);
|
2874 |
+
}
|
2875 |
+
}
|
2876 |
+
$path_cmd = $svg_class->svgPolygon($arguments);
|
2877 |
+
// definition du style de la forme:
|
2878 |
+
$critere_style = $attribs;
|
2879 |
+
unset($critere_style['points']);
|
2880 |
+
$path_style = $svg_class->svgDefineStyle($critere_style);
|
2881 |
+
break;
|
2882 |
+
|
2883 |
+
// mPDF 5.7.4 Embedded image
|
2884 |
+
case 'image':
|
2885 |
+
if (isset($attribs['xlink:href']) && $attribs['xlink:href']) {
|
2886 |
+
$svg_class->svgImage($attribs);
|
2887 |
+
}
|
2888 |
+
break;
|
2889 |
+
|
2890 |
+
|
2891 |
+
case 'a':
|
2892 |
+
if (isset($attribs['xlink:href'])) {
|
2893 |
+
unset($attribs['xlink:href']); // this should be a hyperlink
|
2894 |
+
// not handled like a xlink:href in other elements
|
2895 |
+
} // then continue like a <g>
|
2896 |
+
case 'g':
|
2897 |
+
$array_style = $svg_class->svgDefineStyle($attribs);
|
2898 |
+
if (isset($array_style['transformations']) && $array_style['transformations']) {
|
2899 |
+
// If in the middle of <text> element, add to textoutput, else WriteString
|
2900 |
+
if ($svg_class->intext) { $svg_class->textoutput .= ' q '.$array_style['transformations']; } // mPDF 5.7.4
|
2901 |
+
else { $svg_class->svgWriteString(' q '.$array_style['transformations']); }
|
2902 |
+
}
|
2903 |
+
array_push($svg_class->svg_style,$array_style);
|
2904 |
+
|
2905 |
+
$svg_class->svgDefineTxtStyle($attribs);
|
2906 |
+
|
2907 |
+
break;
|
2908 |
+
|
2909 |
+
case 'text':
|
2910 |
+
$svg_class->textlength = 0; // mPDF 5.7.4
|
2911 |
+
$svg_class->texttotallength = 0; // mPDF 5.7.4
|
2912 |
+
$svg_class->textoutput = ''; // mPDF 5.7.4
|
2913 |
+
$svg_class->textanchor = 'start'; // mPDF 5.7.4
|
2914 |
+
$svg_class->textXorigin = 0; // mPDF 5.7.4
|
2915 |
+
$svg_class->textYorigin = 0; // mPDF 5.7.4
|
2916 |
+
|
2917 |
+
$svg_class->intext = true; // mPDF 5.7.4
|
2918 |
+
|
2919 |
+
$styl = '';
|
2920 |
+
if (_SVG_CLASSES && isset($attribs['class']) && $attribs['class']) {
|
2921 |
+
$classes = preg_split('/\s+/',trim($attribs['class']));
|
2922 |
+
foreach($classes AS $class) {
|
2923 |
+
if (isset($svg_class->mpdf_ref->cssmgr->CSS['CLASS>>'.strtoupper($class)])) {
|
2924 |
+
$c = $svg_class->mpdf_ref->cssmgr->CSS['CLASS>>'.strtoupper($class)];
|
2925 |
+
foreach($c AS $prop=>$val) {
|
2926 |
+
$styl .= strtolower($prop).':'.$val.';';
|
2927 |
+
}
|
2928 |
+
}
|
2929 |
+
}
|
2930 |
+
}
|
2931 |
+
|
2932 |
+
if (_SVG_AUTOFONT && isset($attribs['lang']) && $attribs['lang']) {
|
2933 |
+
if (!$svg_class->mpdf_ref->usingCoreFont) {
|
2934 |
+
if ($attribs['lang'] != $svg_class->mpdf_ref->default_lang) {
|
2935 |
+
list ($coreSuitable,$mpdf_unifont) = GetLangOpts($attribs['lang'], $svg_class->mpdf_ref->useAdobeCJK, $svg_class->mpdf_ref->fontdata);
|
2936 |
+
if ($mpdf_unifont) { $styl .= 'font-family:'.$mpdf_unifont.';'; }
|
2937 |
+
}
|
2938 |
+
}
|
2939 |
+
}
|
2940 |
+
|
2941 |
+
if ($styl) {
|
2942 |
+
if (isset($attribs['style'])) { $attribs['style'] = $styl . $attribs['style']; }
|
2943 |
+
else { $attribs['style'] = $styl; }
|
2944 |
+
}
|
2945 |
+
|
2946 |
+
$array_style = $svg_class->svgDefineStyle($attribs);
|
2947 |
+
if ($array_style['transformations']) {
|
2948 |
+
$svg_class->textoutput .= ' q '.$array_style['transformations']; // mPDF 5.7.4
|
2949 |
+
}
|
2950 |
+
array_push($svg_class->svg_style,$array_style);
|
2951 |
+
|
2952 |
+
$svg_class->txt_data = array();
|
2953 |
+
$x = isset($attribs['x']) ? $svg_class->ConvertSVGSizePixels($attribs['x'],'x') : 0; // mPDF 5.7.4
|
2954 |
+
$y = isset($attribs['y']) ? $svg_class->ConvertSVGSizePixels($attribs['y'],'y') : 0; // mPDF 5.7.4
|
2955 |
+
$x += isset($attribs['dx']) ? $svg_class->ConvertSVGSizePixels($attribs['dx'],'x') : 0; // mPDF 5.7.4
|
2956 |
+
$y += isset($attribs['dy']) ? $svg_class->ConvertSVGSizePixels($attribs['dy'],'y') : 0; // mPDF 5.7.4
|
2957 |
+
|
2958 |
+
$svg_class->txt_data[0] = $x; // mPDF 5.7.4
|
2959 |
+
$svg_class->txt_data[1] = $y; // mPDF 5.7.4
|
2960 |
+
$critere_style = $attribs;
|
2961 |
+
unset($critere_style['x'], $critere_style['y']);
|
2962 |
+
$svg_class->svgDefineTxtStyle($critere_style);
|
2963 |
+
|
2964 |
+
$svg_class->textanchor = $svg_class->txt_style[count($svg_class->txt_style)-1]['text-anchor']; // mPDF 5.7.4
|
2965 |
+
$svg_class->textXorigin = $svg_class->txt_data[0]; // mPDF 5.7.4
|
2966 |
+
$svg_class->textYorigin = $svg_class->txt_data[1]; // mPDF 5.7.4
|
2967 |
+
$svg_class->textjuststarted = true; // mPDF 5.7.4
|
2968 |
+
|
2969 |
+
break;
|
2970 |
+
|
2971 |
+
// mPDF 5.7.4
|
2972 |
+
case 'tspan':
|
2973 |
+
|
2974 |
+
// OUTPUT CHUNK(s) UP To NOW (svgText updates $svg_class->textlength)
|
2975 |
+
$p_cmd = $svg_class->svgText();
|
2976 |
+
$svg_class->textoutput .= $p_cmd;
|
2977 |
+
$tmp = count($svg_class->svg_style)-1;
|
2978 |
+
$current_style = $svg_class->svg_style[$tmp];
|
2979 |
+
|
2980 |
+
$styl = '';
|
2981 |
+
if (_SVG_CLASSES && isset($attribs['class']) && $attribs['class']) {
|
2982 |
+
$classes = preg_split('/\s+/',trim($attribs['class']));
|
2983 |
+
foreach($classes AS $class) {
|
2984 |
+
if (isset($svg_class->mpdf_ref->cssmgr->CSS['CLASS>>'.strtoupper($class)])) {
|
2985 |
+
$c = $svg_class->mpdf_ref->cssmgr->CSS['CLASS>>'.strtoupper($class)];
|
2986 |
+
foreach($c AS $prop=>$val) {
|
2987 |
+
$styl .= strtolower($prop).':'.$val.';';
|
2988 |
+
}
|
2989 |
+
}
|
2990 |
+
}
|
2991 |
+
}
|
2992 |
+
|
2993 |
+
if (_SVG_AUTOFONT && isset($attribs['lang']) && $attribs['lang']) {
|
2994 |
+
if (!$svg_class->mpdf_ref->usingCoreFont) {
|
2995 |
+
if ($attribs['lang'] != $svg_class->mpdf_ref->default_lang) {
|
2996 |
+
list ($coreSuitable,$mpdf_unifont) = GetLangOpts($attribs['lang'], $svg_class->mpdf_ref->useAdobeCJK, $svg_class->mpdf_ref->fontdata);
|
2997 |
+
if ($mpdf_unifont) { $styl .= 'font-family:'.$mpdf_unifont.';'; }
|
2998 |
+
}
|
2999 |
+
}
|
3000 |
+
}
|
3001 |
+
|
3002 |
+
if ($styl) {
|
3003 |
+
if (isset($attribs['style'])) { $attribs['style'] = $styl . $attribs['style']; }
|
3004 |
+
else { $attribs['style'] = $styl; }
|
3005 |
+
}
|
3006 |
+
|
3007 |
+
$array_style = $svg_class->svgDefineStyle($attribs);
|
3008 |
+
|
3009 |
+
$svg_class->txt_data = array();
|
3010 |
+
|
3011 |
+
|
3012 |
+
// If absolute position adjustment (x or y), creates new block of text for text-alignment
|
3013 |
+
if (isset($attribs['x']) || isset($attribs['y'])) {
|
3014 |
+
// If text-anchor middle|end, adjust
|
3015 |
+
if ($svg_class->textanchor == 'end') { $tx = -$svg_class->texttotallength; }
|
3016 |
+
else if ($svg_class->textanchor == 'middle') { $tx = -$svg_class->texttotallength/2; }
|
3017 |
+
else { $tx = 0; }
|
3018 |
+
while(preg_match('/mPDF-AXS\((.*?)\)/',$svg_class->textoutput,$m)) {
|
3019 |
+
if ($tx) {
|
3020 |
+
$txk = $m[1] + ($tx*$svg_class->kp);
|
3021 |
+
$svg_class->textoutput = preg_replace('/mPDF-AXS\((.*?)\)/', sprintf('%.4F', $txk) ,$svg_class->textoutput,1);
|
3022 |
+
}
|
3023 |
+
else {
|
3024 |
+
$svg_class->textoutput = preg_replace('/mPDF-AXS\((.*?)\)/','\\1',$svg_class->textoutput,1);
|
3025 |
+
}
|
3026 |
+
}
|
3027 |
+
|
3028 |
+
$svg_class->svgWriteString($svg_class->textoutput);
|
3029 |
+
|
3030 |
+
$svg_class->textXorigin += $svg_class->textlength;
|
3031 |
+
$currentX = $svg_class->textXorigin;
|
3032 |
+
$currentY = $svg_class->textYorigin;
|
3033 |
+
$svg_class->textlength = 0;
|
3034 |
+
$svg_class->texttotallength = 0;
|
3035 |
+
$svg_class->textoutput = '';
|
3036 |
+
|
3037 |
+
$x = isset($attribs['x']) ? $svg_class->ConvertSVGSizePixels($attribs['x'],'x') : $currentX;
|
3038 |
+
$y = isset($attribs['y']) ? $svg_class->ConvertSVGSizePixels($attribs['y'],'y') : $currentY;
|
3039 |
+
|
3040 |
+
$svg_class->txt_data[0] = $x;
|
3041 |
+
$svg_class->txt_data[1] = $y;
|
3042 |
+
$critere_style = $attribs;
|
3043 |
+
unset($critere_style['x'], $critere_style['y']);
|
3044 |
+
$svg_class->svgDefineTxtStyle($critere_style);
|
3045 |
+
|
3046 |
+
$svg_class->textanchor = $svg_class->txt_style[count($svg_class->txt_style)-1]['text-anchor'];
|
3047 |
+
$svg_class->textXorigin = $x;
|
3048 |
+
$svg_class->textYorigin = $y;
|
3049 |
+
|
3050 |
+
}
|
3051 |
+
else {
|
3052 |
+
|
3053 |
+
$svg_class->textXorigin += $svg_class->textlength;
|
3054 |
+
$currentX = $svg_class->textXorigin;
|
3055 |
+
$currentY = $svg_class->textYorigin;
|
3056 |
+
|
3057 |
+
$currentX += isset($attribs['dx']) ? $svg_class->ConvertSVGSizePixels($attribs['dx'],'x') : 0;
|
3058 |
+
$currentY += isset($attribs['dy']) ? $svg_class->ConvertSVGSizePixels($attribs['dy'],'y') : 0;
|
3059 |
+
|
3060 |
+
$svg_class->txt_data[0] = $currentX;
|
3061 |
+
$svg_class->txt_data[1] = $currentY;
|
3062 |
+
$critere_style = $attribs;
|
3063 |
+
unset($critere_style['x'], $critere_style['y']);
|
3064 |
+
$svg_class->svgDefineTxtStyle($critere_style);
|
3065 |
+
$svg_class->textXorigin = $currentX;
|
3066 |
+
$svg_class->textYorigin = $currentY;
|
3067 |
+
|
3068 |
+
}
|
3069 |
+
|
3070 |
+
if ($array_style['transformations']) {
|
3071 |
+
$svg_class->textoutput .= ' q '.$array_style['transformations'];
|
3072 |
+
}
|
3073 |
+
array_push($svg_class->svg_style,$array_style);
|
3074 |
+
|
3075 |
+
break;
|
3076 |
+
}
|
3077 |
+
|
3078 |
+
//
|
3079 |
+
//insertion des path et du style dans le flux de donn� general.
|
3080 |
+
if (isset($path_cmd) && $path_cmd) {
|
3081 |
+
// mPDF 5.0
|
3082 |
+
list($prestyle,$poststyle) = $svg_class->svgStyle($path_style, $attribs, strtolower($name));
|
3083 |
+
if (isset($path_style['transformations']) && $path_style['transformations']) { // transformation on an element
|
3084 |
+
$svg_class->svgWriteString(" q ".$path_style['transformations']. $prestyle . $path_cmd . $poststyle . " Q\n");
|
3085 |
+
}
|
3086 |
+
else {
|
3087 |
+
$svg_class->svgWriteString(" q ".$prestyle . $path_cmd . $poststyle ." Q\n"); // mPDF 5.7.4
|
3088 |
+
}
|
3089 |
+
}
|
3090 |
+
}
|
3091 |
+
|
3092 |
+
function characterData($parser, $data)
|
3093 |
+
{
|
3094 |
+
global $svg_class;
|
3095 |
+
if ($svg_class->inDefs) { return; } // mPDF 5.7.2
|
3096 |
+
if(isset($svg_class->txt_data[2])) {
|
3097 |
+
$svg_class->txt_data[2] .= $data;
|
3098 |
+
}
|
3099 |
+
else {
|
3100 |
+
$svg_class->txt_data[2] = $data;
|
3101 |
+
$svg_class->txt_data[0] = $svg_class->textXorigin ;
|
3102 |
+
$svg_class->txt_data[1] = $svg_class->textYorigin ; }
|
3103 |
+
}
|
3104 |
+
|
3105 |
+
|
3106 |
+
function xml_svg2pdf_end($parser, $name){
|
3107 |
+
global $svg_class;
|
3108 |
+
// mPDF 5.7.2
|
3109 |
+
// Don't output stuff inside <defs>
|
3110 |
+
if ($name == 'defs') {
|
3111 |
+
$svg_class->inDefs = false;
|
3112 |
+
return;
|
3113 |
+
}
|
3114 |
+
if ($svg_class->inDefs) { return; }
|
3115 |
+
switch($name){
|
3116 |
+
|
3117 |
+
case "g":
|
3118 |
+
case "a":
|
3119 |
+
if ($svg_class->intext) {
|
3120 |
+
$p_cmd = $svg_class->svgText();
|
3121 |
+
$svg_class->textoutput .= $p_cmd;
|
3122 |
+
}
|
3123 |
+
|
3124 |
+
$tmp = count($svg_class->svg_style)-1;
|
3125 |
+
$current_style = $svg_class->svg_style[$tmp];
|
3126 |
+
if ($current_style['transformations']) {
|
3127 |
+
// If in the middle of <text> element, add to textoutput, else WriteString
|
3128 |
+
if ($svg_class->intext) { $svg_class->textoutput .= " Q\n"; } // mPDF 5.7.4
|
3129 |
+
else { $svg_class->svgWriteString(" Q\n"); }
|
3130 |
+
}
|
3131 |
+
array_pop($svg_class->svg_style);
|
3132 |
+
array_pop($svg_class->txt_style);
|
3133 |
+
if ($svg_class->intext) {
|
3134 |
+
$svg_class->textXorigin += $svg_class->textlength;
|
3135 |
+
$svg_class->textlength = 0;
|
3136 |
+
}
|
3137 |
+
|
3138 |
+
break;
|
3139 |
+
case 'font':
|
3140 |
+
$last_svg_fontdefw = '';
|
3141 |
+
break;
|
3142 |
+
case 'font-face':
|
3143 |
+
$last_svg_fontid = '';
|
3144 |
+
$last_svg_fontstyle = '';
|
3145 |
+
break;
|
3146 |
+
case 'radialgradient':
|
3147 |
+
case 'lineargradient':
|
3148 |
+
$last_gradid = '';
|
3149 |
+
break;
|
3150 |
+
case "text":
|
3151 |
+
$svg_class->txt_data[2] = rtrim($svg_class->txt_data[2]); // mPDF 5.7.4
|
3152 |
+
$path_cmd = $svg_class->svgText();
|
3153 |
+
$svg_class->textoutput .= $path_cmd; // mPDF 5.7.4
|
3154 |
+
$tmp = count($svg_class->svg_style)-1;
|
3155 |
+
$current_style = $svg_class->svg_style[$tmp];
|
3156 |
+
if ($current_style['transformations']) {
|
3157 |
+
$svg_class->textoutput .= " Q\n"; // mPDF 5.7.4
|
3158 |
+
}
|
3159 |
+
array_pop($svg_class->svg_style);
|
3160 |
+
array_pop($svg_class->txt_style); // mPDF 5.7.4
|
3161 |
+
|
3162 |
+
// mPDF 5.7.4
|
3163 |
+
// If text-anchor middle|end, adjust
|
3164 |
+
if ($svg_class->textanchor == 'end') { $tx = -$svg_class->texttotallength; }
|
3165 |
+
else if ($svg_class->textanchor == 'middle') { $tx = -$svg_class->texttotallength/2; }
|
3166 |
+
else { $tx = 0; }
|
3167 |
+
while(preg_match('/mPDF-AXS\((.*?)\)/',$svg_class->textoutput,$m)) {
|
3168 |
+
if ($tx) {
|
3169 |
+
$txk = $m[1] + ($tx*$svg_class->kp);
|
3170 |
+
$svg_class->textoutput = preg_replace('/mPDF-AXS\((.*?)\)/', sprintf('%.4F', $txk) ,$svg_class->textoutput,1);
|
3171 |
+
}
|
3172 |
+
else {
|
3173 |
+
$svg_class->textoutput = preg_replace('/mPDF-AXS\((.*?)\)/','\\1',$svg_class->textoutput,1);
|
3174 |
+
}
|
3175 |
+
}
|
3176 |
+
|
3177 |
+
$svg_class->svgWriteString($svg_class->textoutput);
|
3178 |
+
$svg_class->textlength = 0;
|
3179 |
+
$svg_class->texttotallength = 0;
|
3180 |
+
$svg_class->textoutput = '';
|
3181 |
+
$svg_class->intext = false; // mPDF 5.7.4
|
3182 |
+
|
3183 |
+
break;
|
3184 |
+
// mPDF 5.7.4
|
3185 |
+
case "tspan":
|
3186 |
+
$p_cmd = $svg_class->svgText();
|
3187 |
+
$svg_class->textoutput .= $p_cmd;
|
3188 |
+
$tmp = count($svg_class->svg_style)-1;
|
3189 |
+
$current_style = $svg_class->svg_style[$tmp];
|
3190 |
+
if ($current_style['transformations']) {
|
3191 |
+
$svg_class->textoutput .= " Q\n";
|
3192 |
+
}
|
3193 |
+
array_pop($svg_class->svg_style);
|
3194 |
+
array_pop($svg_class->txt_style);
|
3195 |
+
|
3196 |
+
$svg_class->textXorigin += $svg_class->textlength;
|
3197 |
+
$svg_class->textlength = 0;
|
3198 |
+
|
3199 |
+
break;
|
3200 |
+
}
|
3201 |
+
|
3202 |
+
}
|
3203 |
+
|
3204 |
+
}
|
3205 |
+
|
3206 |
+
$svg2pdf_xml='';
|
3207 |
+
global $svg_class;
|
3208 |
+
$svg_class = $this;
|
3209 |
+
// Don't output stuff inside <defs>
|
3210 |
+
$svg_class->inDefs = false;
|
3211 |
+
$svg2pdf_xml_parser = xml_parser_create("utf-8");
|
3212 |
+
xml_parser_set_option($svg2pdf_xml_parser, XML_OPTION_CASE_FOLDING, false);
|
3213 |
+
xml_set_element_handler($svg2pdf_xml_parser, "xml_svg2pdf_start", "xml_svg2pdf_end");
|
3214 |
+
xml_set_character_data_handler($svg2pdf_xml_parser, "characterData");
|
3215 |
+
xml_parse($svg2pdf_xml_parser, $data);
|
3216 |
+
if ($this->svg_error) { return false; }
|
3217 |
+
else {
|
3218 |
+
return array('x'=>$this->svg_info['x']*$this->kp,'y'=>-$this->svg_info['y']*$this->kp,'w'=>$this->svg_info['w']*$this->kp,'h'=>-$this->svg_info['h']*$this->kp,'data'=>$svg_class->svg_string);
|
3219 |
+
}
|
3220 |
+
|
3221 |
+
}
|
3222 |
+
|
3223 |
+
|
3224 |
+
|
3225 |
+
// AUTOFONT =========================
|
3226 |
+
function markScriptToLang($html) {
|
3227 |
+
if ($this->mpdf_ref->onlyCoreFonts) { return $html; }
|
3228 |
+
if (empty($this->script2lang)) {
|
3229 |
+
if (!empty($this->mpdf_ref->script2lang)) {
|
3230 |
+
$this->script2lang = $this->mpdf_ref->script2lang;
|
3231 |
+
$this->viet = $this->mpdf_ref->viet;
|
3232 |
+
$this->pashto = $this->mpdf_ref->pashto;
|
3233 |
+
$this->urdu = $this->mpdf_ref->urdu;
|
3234 |
+
$this->persian = $this->mpdf_ref->persian;
|
3235 |
+
$this->sindhi = $this->mpdf_ref->sindhi;
|
3236 |
+
}
|
3237 |
+
else {
|
3238 |
+
include(_MPDF_PATH.'config_script2lang.php');
|
3239 |
+
}
|
3240 |
+
}
|
3241 |
+
|
3242 |
+
$n = '';
|
3243 |
+
$a=preg_split('/<(.*?)>/ms',$html,-1,PREG_SPLIT_DELIM_CAPTURE);
|
3244 |
+
foreach($a as $i => $e) {
|
3245 |
+
if($i%2==0) {
|
3246 |
+
$e = strcode2utf($e);
|
3247 |
+
$e = $this->mpdf_ref->lesser_entity_decode($e);
|
3248 |
+
|
3249 |
+
$earr = $this->mpdf_ref->UTF8StringToArray($e, false);
|
3250 |
+
|
3251 |
+
$scriptblock = 0;
|
3252 |
+
$scriptblocks = array();
|
3253 |
+
$scriptblocks[0] = 0;
|
3254 |
+
$chardata = array();
|
3255 |
+
$subchunk = 0;
|
3256 |
+
$charctr = 0;
|
3257 |
+
foreach($earr as $char) {
|
3258 |
+
$ucd_record = UCDN::get_ucd_record($char);
|
3259 |
+
$sbl = $ucd_record[6];
|
3260 |
+
|
3261 |
+
if ($sbl && $sbl != 40 && $sbl != 102) {
|
3262 |
+
if ($scriptblock == 0) { $scriptblock = $sbl; $scriptblocks[$subchunk] = $scriptblock; }
|
3263 |
+
else if ($scriptblock > 0 && $scriptblock != $sbl) {
|
3264 |
+
// NEW (non-common) Script encountered in this chunk.
|
3265 |
+
// Start a new subchunk
|
3266 |
+
$subchunk++;
|
3267 |
+
$scriptblock = $sbl;
|
3268 |
+
$charctr = 0;
|
3269 |
+
$scriptblocks[$subchunk] = $scriptblock;
|
3270 |
+
}
|
3271 |
+
}
|
3272 |
+
|
3273 |
+
$chardata[$subchunk][$charctr]['script'] = $sbl;
|
3274 |
+
$chardata[$subchunk][$charctr]['uni'] = $char;
|
3275 |
+
$charctr++;
|
3276 |
+
}
|
3277 |
+
|
3278 |
+
// If scriptblock[x] = common & non-baseScript
|
3279 |
+
// and scriptblock[x+1] = baseScript
|
3280 |
+
// Move common script from end of x to start of x+1
|
3281 |
+
for($sch=0;$sch<$subchunk;$sch++) {
|
3282 |
+
if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->mpdf_ref->baseScript && $scriptblocks[$sch+1] == $this->mpdf_ref->baseScript) {
|
3283 |
+
$end = count($chardata[$sch])-1;
|
3284 |
+
while($chardata[$sch][$end]['script'] == 0 && $end > 1) { // common script
|
3285 |
+
$tmp = array_pop($chardata[$sch]);
|
3286 |
+
array_unshift($chardata[$sch+1],$tmp);
|
3287 |
+
$end--;
|
3288 |
+
}
|
3289 |
+
}
|
3290 |
+
}
|
3291 |
+
|
3292 |
+
$o = '';
|
3293 |
+
for($sch=0;$sch<=$subchunk;$sch++) {
|
3294 |
+
if (isset($chardata[$sch])) {
|
3295 |
+
$s = '';
|
3296 |
+
for ($j=0;$j<count($chardata[$sch]);$j++) {
|
3297 |
+
$s.=code2utf($chardata[$sch][$j]['uni']);
|
3298 |
+
}
|
3299 |
+
// ZZZ99 Undo lesser_entity_decode as above - but only for <>&
|
3300 |
+
$s = str_replace("&","&",$s);
|
3301 |
+
$s = str_replace("<","<",$s);
|
3302 |
+
$s = str_replace(">",">",$s);
|
3303 |
+
|
3304 |
+
if (substr($a[$i-1],0,5)!='<text' && substr($a[$i-1],0,5)!='<tspa') { continue; } // <tspan> or <text> only
|
3305 |
+
|
3306 |
+
$lang = '';
|
3307 |
+
// Check Vietnamese if Latin script - even if Basescript
|
3308 |
+
if ($scriptblocks[$sch] == UCDN::SCRIPT_LATIN && $this->mpdf_ref->autoVietnamese && preg_match("/([".$this->viet."])/u", $s)) {
|
3309 |
+
$lang="vi";
|
3310 |
+
}
|
3311 |
+
// Check Arabic for different languages if Arabic script - even if Basescript
|
3312 |
+
else if ($scriptblocks[$sch] == UCDN::SCRIPT_ARABIC && $this->mpdf_ref->autoArabic) {
|
3313 |
+
if (preg_match("/[".$this->sindhi ."]/u", $s) ) {
|
3314 |
+
$lang="sd";
|
3315 |
+
}
|
3316 |
+
else if (preg_match("/[".$this->urdu ."]/u", $s) ) {
|
3317 |
+
$lang="ur";
|
3318 |
+
}
|
3319 |
+
else if (preg_match("/[".$this->pashto ."]/u", $s) ) {
|
3320 |
+
$lang="ps";
|
3321 |
+
}
|
3322 |
+
else if (preg_match("/[".$this->persian ."]/u", $s) ) {
|
3323 |
+
$lang="fa";
|
3324 |
+
}
|
3325 |
+
else if ($this->mpdf_ref->baseScript != UCDN::SCRIPT_ARABIC && isset($this->script2lang[$scriptblocks[$sch]])) {
|
3326 |
+
$lang="'.$this->script2lang[$scriptblocks[$sch]].'";
|
3327 |
+
}
|
3328 |
+
}
|
3329 |
+
// Identify Script block if not Basescript, and mark up as language
|
3330 |
+
else if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->mpdf_ref->baseScript && isset($this->script2lang[$scriptblocks[$sch]])) {
|
3331 |
+
$lang=$this->script2lang[$scriptblocks[$sch]];
|
3332 |
+
}
|
3333 |
+
if ($lang) {
|
3334 |
+
$o .= '<tspan lang="'.$lang.'">'.$s.'</tspan>';
|
3335 |
+
}
|
3336 |
+
else { $o .= $s; }
|
3337 |
+
|
3338 |
+
}
|
3339 |
+
}
|
3340 |
+
|
3341 |
+
$a[$i] = $o;
|
3342 |
+
}
|
3343 |
+
else {
|
3344 |
+
$a[$i] = '<'.$e.'>';
|
3345 |
+
}
|
3346 |
+
}
|
3347 |
+
$n = implode('',$a);
|
3348 |
+
return $n;
|
3349 |
+
}
|
3350 |
+
|
3351 |
+
|
3352 |
+
}
|
3353 |
+
|
3354 |
+
// END OF CLASS
|
3355 |
+
// END OF CLASS
|
3356 |
+
// END OF CLASS
|
3357 |
+
|
3358 |
+
|
3359 |
+
function calc_bezier_bbox($start, $c) {
|
3360 |
+
$P0 = array($start[0],$start[1]);
|
3361 |
+
$P1 = array($c[0],$c[1]);
|
3362 |
+
$P2 = array($c[2],$c[3]);
|
3363 |
+
$P3 = array($c[4],$c[5]);
|
3364 |
+
$bounds = array();
|
3365 |
+
$bounds[0][] = $P0[0];
|
3366 |
+
$bounds[1][] = $P0[1];
|
3367 |
+
$bounds[0][] = $P3[0];
|
3368 |
+
$bounds[1][] = $P3[1];
|
3369 |
+
for ($i=0;$i<=1;$i++) {
|
3370 |
+
$b = 6 * $P0[$i] - 12 * $P1[$i] + 6 * $P2[$i];
|
3371 |
+
$a = -3 * $P0[$i] + 9 * $P1[$i] - 9 * $P2[$i] + 3 * $P3[$i];
|
3372 |
+
$c = 3 * $P1[$i] - 3 * $P0[$i];
|
3373 |
+
if ($a == 0) {
|
3374 |
+
if ($b == 0) { continue; }
|
3375 |
+
$t = -$c / $b;
|
3376 |
+
if ($t>0 && $t<1) {
|
3377 |
+
$bounds[$i][] = (pow((1-$t),3) * $P0[$i] + 3 * pow((1-$t),2) * $t * $P1[$i] + 3 * (1-$t) * pow($t,2) * $P2[$i] + pow($t,3) * $P3[$i]);
|
3378 |
+
}
|
3379 |
+
continue;
|
3380 |
+
}
|
3381 |
+
$b2ac = pow($b, 2) - 4 * $c * $a;
|
3382 |
+
if ($b2ac < 0) { continue; }
|
3383 |
+
$t1 = (-$b + sqrt($b2ac))/(2 * $a);
|
3384 |
+
if ($t1>0 && $t1<1) {
|
3385 |
+
$bounds[$i][] = (pow((1-$t1),3) * $P0[$i] + 3 * pow((1-$t1),2) * $t1 * $P1[$i] + 3 * (1-$t1) * pow($t1,2) * $P2[$i] + pow($t1,3) * $P3[$i]);
|
3386 |
+
}
|
3387 |
+
$t2 = (-$b - sqrt($b2ac))/(2 * $a);
|
3388 |
+
if ($t2>0 && $t2<1) {
|
3389 |
+
$bounds[$i][] = (pow((1-$t2),3) * $P0[$i] + 3 * pow((1-$t2),2) * $t2 * $P1[$i] + 3 * (1-$t2) * pow($t2,2) * $P2[$i] + pow($t2,3) * $P3[$i]);
|
3390 |
+
}
|
3391 |
+
}
|
3392 |
+
$x = min($bounds[0]);
|
3393 |
+
$x2 = max($bounds[0]);
|
3394 |
+
$y = min($bounds[1]);
|
3395 |
+
$y2 = max($bounds[1]);
|
3396 |
+
return array($x, $y, $x2, $y2);
|
3397 |
+
}
|
3398 |
+
|
3399 |
+
function _testIntersectCircle($cx, $cy, $cr) {
|
3400 |
+
// Tests whether a circle fully encloses a rectangle 0,0,1,1
|
3401 |
+
// to see if any further radial gradients need adding (SVG)
|
3402 |
+
// If centre of circle is inside 0,0,1,1 square
|
3403 |
+
if ($cx >= 0 && $cx <= 1 && $cy >= 0 && $cy <= 1) {
|
3404 |
+
$maxd = 1.5;
|
3405 |
+
}
|
3406 |
+
// distance to four corners
|
3407 |
+
else {
|
3408 |
+
$d1 = sqrt(pow(($cy-0),2) + pow(($cx-0),2));
|
3409 |
+
$d2 = sqrt(pow(($cy-1),2) + pow(($cx-0),2));
|
3410 |
+
$d3 = sqrt(pow(($cy-0),2) + pow(($cx-1),2));
|
3411 |
+
$d4 = sqrt(pow(($cy-1),2) + pow(($cx-1),2));
|
3412 |
+
$maxd = max($d1,$d2,$d3,$d4);
|
3413 |
+
}
|
3414 |
+
if ($cr < $maxd) { return true; }
|
3415 |
+
else { return false; }
|
3416 |
+
}
|
3417 |
+
|
3418 |
+
function _testIntersect($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4) {
|
3419 |
+
// Tests whether line (x1, y1) and (x2, y2) [a gradient axis (perpendicular)]
|
3420 |
+
// intersects with a specific line segment (x3, y3) and (x4, y4)
|
3421 |
+
$a1 = $y2-$y1;
|
3422 |
+
$b1 = $x1-$x2;
|
3423 |
+
$c1 = $a1*$x1+$b1*$y1;
|
3424 |
+
$a2 = $y4-$y3;
|
3425 |
+
$b2 = $x3-$x4;
|
3426 |
+
$c2 = $a2*$x3+$b2*$y3;
|
3427 |
+
$det = $a1*$b2 - $a2*$b1;
|
3428 |
+
if($det == 0){ //Lines are parallel
|
3429 |
+
return false;
|
3430 |
+
}
|
3431 |
+
else{
|
3432 |
+
$x = ($b2*$c1 - $b1*$c2)/$det;
|
3433 |
+
$y = ($a1*$c2 - $a2*$c1)/$det;
|
3434 |
+
if ($x >= $x3 && $x <= $x4 && $y >= $y3 && $y <= $y4) { return true; }
|
3435 |
+
}
|
3436 |
+
return false;
|
3437 |
+
}
|
3438 |
+
|
3439 |
+
|
3440 |
+
|
3441 |
+
?>
|
lib/mpdf/classes/tocontents.php
ADDED
@@ -0,0 +1,509 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class tocontents {
|
4 |
+
|
5 |
+
var $mpdf = null;
|
6 |
+
var $_toc;
|
7 |
+
var $TOCmark;
|
8 |
+
var $TOCoutdent; // mPDF 5.6.31
|
9 |
+
var $TOCpreHTML;
|
10 |
+
var $TOCpostHTML;
|
11 |
+
var $TOCbookmarkText;
|
12 |
+
var $TOCusePaging;
|
13 |
+
var $TOCuseLinking;
|
14 |
+
var $TOCorientation;
|
15 |
+
var $TOC_margin_left;
|
16 |
+
var $TOC_margin_right;
|
17 |
+
var $TOC_margin_top;
|
18 |
+
var $TOC_margin_bottom;
|
19 |
+
var $TOC_margin_header;
|
20 |
+
var $TOC_margin_footer;
|
21 |
+
var $TOC_odd_header_name;
|
22 |
+
var $TOC_even_header_name;
|
23 |
+
var $TOC_odd_footer_name;
|
24 |
+
var $TOC_even_footer_name;
|
25 |
+
var $TOC_odd_header_value;
|
26 |
+
var $TOC_even_header_value;
|
27 |
+
var $TOC_odd_footer_value;
|
28 |
+
var $TOC_even_footer_value;
|
29 |
+
var $TOC_page_selector;
|
30 |
+
var $TOC_resetpagenum; // mPDF 6
|
31 |
+
var $TOC_pagenumstyle; // mPDF 6
|
32 |
+
var $TOC_suppress; // mPDF 6
|
33 |
+
var $m_TOC;
|
34 |
+
|
35 |
+
function tocontents(&$mpdf) {
|
36 |
+
$this->mpdf = $mpdf;
|
37 |
+
$this->_toc=array();
|
38 |
+
$this->TOCmark = 0;
|
39 |
+
$this->m_TOC=array();
|
40 |
+
}
|
41 |
+
|
42 |
+
function TOCpagebreak($tocfont='', $tocfontsize='', $tocindent='', $TOCusePaging=true, $TOCuseLinking='', $toc_orientation='', $toc_mgl='',$toc_mgr='',$toc_mgt='',$toc_mgb='',$toc_mgh='',$toc_mgf='',$toc_ohname='',$toc_ehname='',$toc_ofname='',$toc_efname='',$toc_ohvalue=0,$toc_ehvalue=0,$toc_ofvalue=0, $toc_efvalue=0, $toc_preHTML='', $toc_postHTML='', $toc_bookmarkText='', $resetpagenum='', $pagenumstyle='', $suppress='', $orientation='', $mgl='',$mgr='',$mgt='',$mgb='',$mgh='',$mgf='',$ohname='',$ehname='',$ofname='',$efname='',$ohvalue=0,$ehvalue=0,$ofvalue=0,$efvalue=0, $toc_id=0, $pagesel='', $toc_pagesel='', $sheetsize='', $toc_sheetsize='', $tocoutdent='', $toc_resetpagenum='', $toc_pagenumstyle='', $toc_suppress='') { // mPDF 5.6.19 // mPDF 6
|
43 |
+
if (strtoupper($toc_id)=='ALL') { $toc_id = '_mpdf_all'; }
|
44 |
+
else if (!$toc_id) { $toc_id = 0; }
|
45 |
+
else { $toc_id = strtolower($toc_id); }
|
46 |
+
|
47 |
+
if ($TOCusePaging === false || strtolower($TOCusePaging) == "off" || $TOCusePaging === 0 || $TOCusePaging === "0" || $TOCusePaging === "") { $TOCusePaging = false; }
|
48 |
+
else { $TOCusePaging = true; }
|
49 |
+
if (!$TOCuseLinking) { $TOCuseLinking = false; }
|
50 |
+
if ($toc_id) {
|
51 |
+
$this->m_TOC[$toc_id]['TOCmark'] = $this->mpdf->page;
|
52 |
+
$this->m_TOC[$toc_id]['TOCoutdent'] = $tocoutdent;
|
53 |
+
$this->m_TOC[$toc_id]['TOCorientation'] = $toc_orientation;
|
54 |
+
$this->m_TOC[$toc_id]['TOCuseLinking'] = $TOCuseLinking;
|
55 |
+
$this->m_TOC[$toc_id]['TOCusePaging'] = $TOCusePaging;
|
56 |
+
|
57 |
+
if ($toc_preHTML) { $this->m_TOC[$toc_id]['TOCpreHTML'] = $toc_preHTML; }
|
58 |
+
if ($toc_postHTML) { $this->m_TOC[$toc_id]['TOCpostHTML'] = $toc_postHTML; }
|
59 |
+
if ($toc_bookmarkText) { $this->m_TOC[$toc_id]['TOCbookmarkText'] = $toc_bookmarkText; }
|
60 |
+
|
61 |
+
$this->m_TOC[$toc_id]['TOC_margin_left'] = $toc_mgl;
|
62 |
+
$this->m_TOC[$toc_id]['TOC_margin_right'] = $toc_mgr;
|
63 |
+
$this->m_TOC[$toc_id]['TOC_margin_top'] = $toc_mgt;
|
64 |
+
$this->m_TOC[$toc_id]['TOC_margin_bottom'] = $toc_mgb;
|
65 |
+
$this->m_TOC[$toc_id]['TOC_margin_header'] = $toc_mgh;
|
66 |
+
$this->m_TOC[$toc_id]['TOC_margin_footer'] = $toc_mgf;
|
67 |
+
$this->m_TOC[$toc_id]['TOC_odd_header_name'] = $toc_ohname;
|
68 |
+
$this->m_TOC[$toc_id]['TOC_even_header_name'] = $toc_ehname;
|
69 |
+
$this->m_TOC[$toc_id]['TOC_odd_footer_name'] = $toc_ofname;
|
70 |
+
$this->m_TOC[$toc_id]['TOC_even_footer_name'] = $toc_efname;
|
71 |
+
$this->m_TOC[$toc_id]['TOC_odd_header_value'] = $toc_ohvalue;
|
72 |
+
$this->m_TOC[$toc_id]['TOC_even_header_value'] = $toc_ehvalue;
|
73 |
+
$this->m_TOC[$toc_id]['TOC_odd_footer_value'] = $toc_ofvalue;
|
74 |
+
$this->m_TOC[$toc_id]['TOC_even_footer_value'] = $toc_efvalue;
|
75 |
+
$this->m_TOC[$toc_id]['TOC_page_selector'] = $toc_pagesel;
|
76 |
+
$this->m_TOC[$toc_id]['TOC_resetpagenum'] = $toc_resetpagenum; // mPDF 6
|
77 |
+
$this->m_TOC[$toc_id]['TOC_pagenumstyle'] = $toc_pagenumstyle; // mPDF 6
|
78 |
+
$this->m_TOC[$toc_id]['TOC_suppress'] = $toc_suppress; // mPDF 6
|
79 |
+
$this->m_TOC[$toc_id]['TOCsheetsize'] = $toc_sheetsize;
|
80 |
+
}
|
81 |
+
else {
|
82 |
+
$this->TOCmark = $this->mpdf->page;
|
83 |
+
$this->TOCoutdent = $tocoutdent;
|
84 |
+
$this->TOCorientation = $toc_orientation;
|
85 |
+
$this->TOCuseLinking = $TOCuseLinking;
|
86 |
+
$this->TOCusePaging = $TOCusePaging;
|
87 |
+
|
88 |
+
if ($toc_preHTML) { $this->TOCpreHTML = $toc_preHTML; }
|
89 |
+
if ($toc_postHTML) { $this->TOCpostHTML = $toc_postHTML; }
|
90 |
+
if ($toc_bookmarkText) { $this->TOCbookmarkText = $toc_bookmarkText; }
|
91 |
+
|
92 |
+
$this->TOC_margin_left = $toc_mgl;
|
93 |
+
$this->TOC_margin_right = $toc_mgr;
|
94 |
+
$this->TOC_margin_top = $toc_mgt;
|
95 |
+
$this->TOC_margin_bottom = $toc_mgb;
|
96 |
+
$this->TOC_margin_header = $toc_mgh;
|
97 |
+
$this->TOC_margin_footer = $toc_mgf;
|
98 |
+
$this->TOC_odd_header_name = $toc_ohname;
|
99 |
+
$this->TOC_even_header_name = $toc_ehname;
|
100 |
+
$this->TOC_odd_footer_name = $toc_ofname;
|
101 |
+
$this->TOC_even_footer_name = $toc_efname;
|
102 |
+
$this->TOC_odd_header_value = $toc_ohvalue;
|
103 |
+
$this->TOC_even_header_value = $toc_ehvalue;
|
104 |
+
$this->TOC_odd_footer_value = $toc_ofvalue;
|
105 |
+
$this->TOC_even_footer_value = $toc_efvalue;
|
106 |
+
$this->TOC_page_selector = $toc_pagesel;
|
107 |
+
$this->TOC_resetpagenum = $toc_resetpagenum; // mPDF 6
|
108 |
+
$this->TOC_pagenumstyle = $toc_pagenumstyle; // mPDF 6
|
109 |
+
$this->TOC_suppress = $toc_suppress; // mPDF 6
|
110 |
+
$this->TOCsheetsize = $toc_sheetsize;
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
// Initiate, and Mark a place for the Table of Contents to be inserted
|
115 |
+
function TOC($tocfont='', $tocfontsize=0, $tocindent=0, $resetpagenum='', $pagenumstyle='', $suppress='', $toc_orientation='', $TOCusePaging=true, $TOCuseLinking=false, $toc_id=0, $tocoutdent='', $toc_resetpagenum='', $toc_pagenumstyle='', $toc_suppress='') { // mPDF 5.6.19 // mPDF 6
|
116 |
+
if (strtoupper($toc_id)=='ALL') { $toc_id = '_mpdf_all'; }
|
117 |
+
else if (!$toc_id) { $toc_id = 0; }
|
118 |
+
else { $toc_id = strtolower($toc_id); }
|
119 |
+
// To use odd and even pages
|
120 |
+
// Cannot start table of contents on an even page
|
121 |
+
if (($this->mpdf->mirrorMargins) && (($this->mpdf->page)%2==0)) { // EVEN
|
122 |
+
if ($this->mpdf->ColActive) {
|
123 |
+
if (count($this->mpdf->columnbuffer)) { $this->mpdf->printcolumnbuffer(); }
|
124 |
+
}
|
125 |
+
$this->mpdf->AddPage($this->mpdf->CurOrientation,'',$resetpagenum, $pagenumstyle, $suppress);
|
126 |
+
}
|
127 |
+
else {
|
128 |
+
$this->mpdf->PageNumSubstitutions[] = array('from'=>$this->mpdf->page, 'reset'=> $resetpagenum, 'type'=>$pagenumstyle, 'suppress'=>$suppress);
|
129 |
+
}
|
130 |
+
if ($toc_id) {
|
131 |
+
$this->m_TOC[$toc_id]['TOCmark'] = $this->mpdf->page;
|
132 |
+
$this->m_TOC[$toc_id]['TOCoutdent'] = $tocoutdent;
|
133 |
+
$this->m_TOC[$toc_id]['TOCorientation'] = $toc_orientation;
|
134 |
+
$this->m_TOC[$toc_id]['TOCuseLinking'] = $TOCuseLinking;
|
135 |
+
$this->m_TOC[$toc_id]['TOCusePaging'] = $TOCusePaging;
|
136 |
+
$this->m_TOC[$toc_id]['TOC_resetpagenum'] = $toc_resetpagenum; // mPDF 6
|
137 |
+
$this->m_TOC[$toc_id]['TOC_pagenumstyle'] = $toc_pagenumstyle; // mPDF 6
|
138 |
+
$this->m_TOC[$toc_id]['TOC_suppress'] = $toc_suppress; // mPDF 6
|
139 |
+
}
|
140 |
+
else {
|
141 |
+
$this->TOCmark = $this->mpdf->page;
|
142 |
+
$this->TOCoutdent = $tocoutdent;
|
143 |
+
$this->TOCorientation = $toc_orientation;
|
144 |
+
$this->TOCuseLinking = $TOCuseLinking;
|
145 |
+
$this->TOCusePaging = $TOCusePaging;
|
146 |
+
$this->TOC_resetpagenum = $toc_resetpagenum; // mPDF 6
|
147 |
+
$this->TOC_pagenumstyle = $toc_pagenumstyle; // mPDF 6
|
148 |
+
$this->TOC_suppress = $toc_suppress; // mPDF 6
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
|
153 |
+
function insertTOC() {
|
154 |
+
$notocs = 0;
|
155 |
+
if ($this->TOCmark) { $notocs = 1; }
|
156 |
+
$notocs += count($this->m_TOC);
|
157 |
+
|
158 |
+
if ($notocs==0) { return; }
|
159 |
+
|
160 |
+
if (count($this->m_TOC)) { reset($this->m_TOC); }
|
161 |
+
$added_toc_pages = 0;
|
162 |
+
|
163 |
+
if ($this->mpdf->ColActive) { $this->mpdf->SetColumns(0); }
|
164 |
+
if (($this->mpdf->mirrorMargins) && (($this->mpdf->page)%2==1)) { // ODD
|
165 |
+
$this->mpdf->AddPage($this->mpdf->CurOrientation);
|
166 |
+
$extrapage = true;
|
167 |
+
}
|
168 |
+
else { $extrapage = false; }
|
169 |
+
|
170 |
+
for ($toci = 0; $toci<$notocs; $toci++) {
|
171 |
+
if ($toci==0 && $this->TOCmark) {
|
172 |
+
$toc_id = 0;
|
173 |
+
$toc_page = $this->TOCmark;
|
174 |
+
$tocoutdent = $this->TOCoutdent;
|
175 |
+
$toc_orientation = $this->TOCorientation;
|
176 |
+
$TOCuseLinking = $this->TOCuseLinking;
|
177 |
+
$TOCusePaging = $this->TOCusePaging;
|
178 |
+
$toc_preHTML = $this->TOCpreHTML;
|
179 |
+
$toc_postHTML = $this->TOCpostHTML;
|
180 |
+
$toc_bookmarkText = $this->TOCbookmarkText;
|
181 |
+
$toc_mgl = $this->TOC_margin_left;
|
182 |
+
$toc_mgr = $this->TOC_margin_right;
|
183 |
+
$toc_mgt = $this->TOC_margin_top;
|
184 |
+
$toc_mgb = $this->TOC_margin_bottom;
|
185 |
+
$toc_mgh = $this->TOC_margin_header;
|
186 |
+
$toc_mgf = $this->TOC_margin_footer;
|
187 |
+
$toc_ohname = $this->TOC_odd_header_name;
|
188 |
+
$toc_ehname = $this->TOC_even_header_name;
|
189 |
+
$toc_ofname = $this->TOC_odd_footer_name;
|
190 |
+
$toc_efname = $this->TOC_even_footer_name;
|
191 |
+
$toc_ohvalue = $this->TOC_odd_header_value;
|
192 |
+
$toc_ehvalue = $this->TOC_even_header_value;
|
193 |
+
$toc_ofvalue = $this->TOC_odd_footer_value;
|
194 |
+
$toc_efvalue = $this->TOC_even_footer_value;
|
195 |
+
$toc_page_selector = $this->TOC_page_selector;
|
196 |
+
$toc_resetpagenum = $this->TOC_resetpagenum; // mPDF 6
|
197 |
+
$toc_pagenumstyle = $this->TOC_pagenumstyle; // mPDF 6
|
198 |
+
$toc_suppress = $this->TOC_suppress; // mPDF 6
|
199 |
+
$toc_sheet_size = (isset($this->TOCsheetsize) ? $this->TOCsheetsize : '');
|
200 |
+
}
|
201 |
+
else {
|
202 |
+
$arr = current($this->m_TOC);
|
203 |
+
|
204 |
+
$toc_id = key($this->m_TOC);
|
205 |
+
$toc_page = $this->m_TOC[$toc_id]['TOCmark'];
|
206 |
+
$tocoutdent = $this->m_TOC[$toc_id]['TOCoutdent'];
|
207 |
+
$toc_orientation = $this->m_TOC[$toc_id]['TOCorientation'];
|
208 |
+
$TOCuseLinking = $this->m_TOC[$toc_id]['TOCuseLinking'];
|
209 |
+
$TOCusePaging = $this->m_TOC[$toc_id]['TOCusePaging'];
|
210 |
+
if (isset($this->m_TOC[$toc_id]['TOCpreHTML'])) { $toc_preHTML = $this->m_TOC[$toc_id]['TOCpreHTML']; }
|
211 |
+
else { $toc_preHTML = ''; }
|
212 |
+
if (isset($this->m_TOC[$toc_id]['TOCpostHTML'])) { $toc_postHTML = $this->m_TOC[$toc_id]['TOCpostHTML']; }
|
213 |
+
else { $toc_postHTML = ''; }
|
214 |
+
if (isset($this->m_TOC[$toc_id]['TOCbookmarkText'])) { $toc_bookmarkText = $this->m_TOC[$toc_id]['TOCbookmarkText']; }
|
215 |
+
else { $toc_bookmarkText = ''; } // *BOOKMARKS*
|
216 |
+
$toc_mgl = $this->m_TOC[$toc_id]['TOC_margin_left'];
|
217 |
+
$toc_mgr = $this->m_TOC[$toc_id]['TOC_margin_right'];
|
218 |
+
$toc_mgt = $this->m_TOC[$toc_id]['TOC_margin_top'];
|
219 |
+
$toc_mgb = $this->m_TOC[$toc_id]['TOC_margin_bottom'];
|
220 |
+
$toc_mgh = $this->m_TOC[$toc_id]['TOC_margin_header'];
|
221 |
+
$toc_mgf = $this->m_TOC[$toc_id]['TOC_margin_footer'];
|
222 |
+
$toc_ohname = $this->m_TOC[$toc_id]['TOC_odd_header_name'];
|
223 |
+
$toc_ehname = $this->m_TOC[$toc_id]['TOC_even_header_name'];
|
224 |
+
$toc_ofname = $this->m_TOC[$toc_id]['TOC_odd_footer_name'];
|
225 |
+
$toc_efname = $this->m_TOC[$toc_id]['TOC_even_footer_name'];
|
226 |
+
$toc_ohvalue = $this->m_TOC[$toc_id]['TOC_odd_header_value'];
|
227 |
+
$toc_ehvalue = $this->m_TOC[$toc_id]['TOC_even_header_value'];
|
228 |
+
$toc_ofvalue = $this->m_TOC[$toc_id]['TOC_odd_footer_value'];
|
229 |
+
$toc_efvalue = $this->m_TOC[$toc_id]['TOC_even_footer_value'];
|
230 |
+
$toc_page_selector = $this->m_TOC[$toc_id]['TOC_page_selector'];
|
231 |
+
$toc_resetpagenum = $this->m_TOC[$toc_id]['TOC_resetpagenum']; // mPDF 6
|
232 |
+
$toc_pagenumstyle = $this->m_TOC[$toc_id]['TOC_pagenumstyle']; // mPDF 6
|
233 |
+
$toc_suppress = $this->m_TOC[$toc_id]['TOC_suppress']; // mPDF 6
|
234 |
+
$toc_sheet_size = (isset($this->m_TOC[$toc_id]['TOCsheetsize']) ? $this->m_TOC[$toc_id]['TOCsheetsize'] : '');
|
235 |
+
next($this->m_TOC);
|
236 |
+
}
|
237 |
+
|
238 |
+
// mPDF 5.6.31
|
239 |
+
if (!$toc_orientation) { $toc_orientation= $this->mpdf->DefOrientation; }
|
240 |
+
|
241 |
+
// mPDF 6 number style and suppress now picked up from section preceding ToC
|
242 |
+
list($tp_pagenumstyle, $tp_suppress, $tp_reset) = $this->mpdf->docPageSettings($toc_page-1);
|
243 |
+
|
244 |
+
if ($toc_resetpagenum) $tp_reset = $toc_resetpagenum; // mPDF 6
|
245 |
+
if ($toc_pagenumstyle) $tp_pagenumstyle = $toc_pagenumstyle; // mPDF 6
|
246 |
+
if ($toc_suppress || $toc_suppress==='0') $tp_suppress = $toc_suppress; // mPDF 6
|
247 |
+
|
248 |
+
$this->mpdf->AddPage($toc_orientation, '', $tp_reset, $tp_pagenumstyle, $tp_suppress, $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_page_selector, $toc_sheet_size ); // mPDF 6
|
249 |
+
|
250 |
+
|
251 |
+
$this->mpdf->writingToC = true; // mPDF 5.6.38
|
252 |
+
// mPDF 5.6.31
|
253 |
+
$tocstart=count($this->mpdf->pages);
|
254 |
+
if (isset($toc_preHTML) && $toc_preHTML) { $this->mpdf->WriteHTML($toc_preHTML); }
|
255 |
+
|
256 |
+
|
257 |
+
// mPDF 5.6.19
|
258 |
+
$html ='<div class="mpdf_toc" id="mpdf_toc_'.$toc_id.'">';
|
259 |
+
foreach($this->_toc as $t) {
|
260 |
+
if ($t['toc_id']==='_mpdf_all' || $t['toc_id']===$toc_id ) {
|
261 |
+
$html .= '<div class="mpdf_toc_level_'.$t['l'].'">';
|
262 |
+
if ($TOCuseLinking) { $html .= '<a class="mpdf_toc_a" href="#__mpdfinternallink_'.$t['link'].'">'; }
|
263 |
+
$html .= '<span class="mpdf_toc_t_level_'.$t['l'].'">'.$t['t'].'</span>';
|
264 |
+
if ($TOCuseLinking) { $html .= '</a>'; }
|
265 |
+
if (!$tocoutdent) { $tocoutdent = '0'; }
|
266 |
+
if ($TOCusePaging) { $html .= ' <dottab outdent="'.$tocoutdent.'" /> ';
|
267 |
+
if ($TOCuseLinking) { $html .= '<a class="mpdf_toc_a" href="#__mpdfinternallink_'.$t['link'].'">'; }
|
268 |
+
$html .= '<span class="mpdf_toc_p_level_'.$t['l'].'">'.$this->mpdf->docPageNum($t['p']).'</span>';
|
269 |
+
if ($TOCuseLinking) { $html .= '</a>'; }
|
270 |
+
}
|
271 |
+
$html .= '</div>';
|
272 |
+
}
|
273 |
+
}
|
274 |
+
$html .= '</div>';
|
275 |
+
$this->mpdf->WriteHTML($html);
|
276 |
+
|
277 |
+
if (isset($toc_postHTML) && $toc_postHTML) { $this->mpdf->WriteHTML($toc_postHTML); }
|
278 |
+
$this->mpdf->writingToC = false; // mPDF 5.6.38
|
279 |
+
$this->mpdf->AddPage($toc_orientation,'E');
|
280 |
+
|
281 |
+
$n_toc = $this->mpdf->page - $tocstart + 1;
|
282 |
+
|
283 |
+
if ($toci==0 && $this->TOCmark) {
|
284 |
+
$TOC_start = $tocstart ;
|
285 |
+
$TOC_end = $this->mpdf->page;
|
286 |
+
$TOC_npages = $n_toc;
|
287 |
+
}
|
288 |
+
else {
|
289 |
+
$this->m_TOC[$toc_id]['start'] = $tocstart ;
|
290 |
+
$this->m_TOC[$toc_id]['end'] = $this->mpdf->page;
|
291 |
+
$this->m_TOC[$toc_id]['npages'] = $n_toc;
|
292 |
+
}
|
293 |
+
}
|
294 |
+
|
295 |
+
$s = '';
|
296 |
+
|
297 |
+
$s .= $this->mpdf->PrintBodyBackgrounds();
|
298 |
+
|
299 |
+
$s .= $this->mpdf->PrintPageBackgrounds();
|
300 |
+
$this->mpdf->pages[$this->mpdf->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->mpdf->uniqstr.')/', "\n".$s."\n".'\\1', $this->mpdf->pages[$this->mpdf->page]);
|
301 |
+
$this->mpdf->pageBackgrounds = array();
|
302 |
+
|
303 |
+
//Page footer
|
304 |
+
$this->mpdf->InFooter=true;
|
305 |
+
$this->mpdf->Footer();
|
306 |
+
$this->mpdf->InFooter=false;
|
307 |
+
|
308 |
+
// 2nd time through to move pages etc.
|
309 |
+
$added_toc_pages = 0;
|
310 |
+
if (count($this->m_TOC)) { reset($this->m_TOC); }
|
311 |
+
|
312 |
+
for ($toci = 0; $toci<$notocs; $toci++) {
|
313 |
+
if ($toci==0 && $this->TOCmark) {
|
314 |
+
$toc_id = 0;
|
315 |
+
$toc_page = $this->TOCmark + $added_toc_pages;
|
316 |
+
$toc_orientation = $this->TOCorientation;
|
317 |
+
$TOCuseLinking = $this->TOCuseLinking;
|
318 |
+
$TOCusePaging = $this->TOCusePaging;
|
319 |
+
$toc_bookmarkText = $this->TOCbookmarkText; // *BOOKMARKS*
|
320 |
+
|
321 |
+
$tocstart = $TOC_start ;
|
322 |
+
$tocend = $n = $TOC_end;
|
323 |
+
$n_toc = $TOC_npages;
|
324 |
+
}
|
325 |
+
else {
|
326 |
+
$arr = current($this->m_TOC);
|
327 |
+
|
328 |
+
$toc_id = key($this->m_TOC);
|
329 |
+
$toc_page = $this->m_TOC[$toc_id]['TOCmark'] + $added_toc_pages;
|
330 |
+
$toc_orientation = $this->m_TOC[$toc_id]['TOCorientation'];
|
331 |
+
$TOCuseLinking = $this->m_TOC[$toc_id]['TOCuseLinking'];
|
332 |
+
$TOCusePaging = $this->m_TOC[$toc_id]['TOCusePaging'];
|
333 |
+
$toc_bookmarkText = $this->m_TOC[$toc_id]['TOCbookmarkText']; // *BOOKMARKS*
|
334 |
+
|
335 |
+
$tocstart = $this->m_TOC[$toc_id]['start'] ;
|
336 |
+
$tocend = $n = $this->m_TOC[$toc_id]['end'] ;
|
337 |
+
$n_toc = $this->m_TOC[$toc_id]['npages'] ;
|
338 |
+
|
339 |
+
next($this->m_TOC);
|
340 |
+
}
|
341 |
+
|
342 |
+
// Now pages moved
|
343 |
+
$added_toc_pages += $n_toc;
|
344 |
+
|
345 |
+
$this->mpdf->MovePages($toc_page, $tocstart, $tocend) ;
|
346 |
+
$this->mpdf->pgsIns[$toc_page] = $tocend - $tocstart + 1;
|
347 |
+
|
348 |
+
/*-- BOOKMARKS --*/
|
349 |
+
// Insert new Bookmark for Bookmark
|
350 |
+
if ($toc_bookmarkText) {
|
351 |
+
$insert = -1;
|
352 |
+
foreach($this->mpdf->BMoutlines as $i=>$o) {
|
353 |
+
if($o['p']<$toc_page) { // i.e. before point of insertion
|
354 |
+
$insert = $i;
|
355 |
+
}
|
356 |
+
}
|
357 |
+
$txt = $this->mpdf->purify_utf8_text($toc_bookmarkText);
|
358 |
+
if ($this->mpdf->text_input_as_HTML) {
|
359 |
+
$txt = $this->mpdf->all_entities_to_utf8($txt);
|
360 |
+
}
|
361 |
+
$newBookmark[0] = array('t'=>$txt,'l'=>0,'y'=>0,'p'=>$toc_page );
|
362 |
+
array_splice($this->mpdf->BMoutlines,($insert+1),0,$newBookmark);
|
363 |
+
}
|
364 |
+
/*-- END BOOKMARKS --*/
|
365 |
+
|
366 |
+
}
|
367 |
+
|
368 |
+
// Delete empty page that was inserted earlier
|
369 |
+
if ($extrapage) {
|
370 |
+
unset($this->mpdf->pages[count($this->mpdf->pages)]);
|
371 |
+
$this->mpdf->page--; // Reset page pointer
|
372 |
+
}
|
373 |
+
|
374 |
+
|
375 |
+
}
|
376 |
+
|
377 |
+
|
378 |
+
function openTagTOC($attr) {
|
379 |
+
if (isset($attr['OUTDENT']) && $attr['OUTDENT']) { $tocoutdent = $attr['OUTDENT']; } else { $tocoutdent = ''; } // mPDF 5.6.19
|
380 |
+
if (isset($attr['RESETPAGENUM']) && $attr['RESETPAGENUM']) { $resetpagenum = $attr['RESETPAGENUM']; } else { $resetpagenum = ''; }
|
381 |
+
if (isset($attr['PAGENUMSTYLE']) && $attr['PAGENUMSTYLE']) { $pagenumstyle = $attr['PAGENUMSTYLE']; } else { $pagenumstyle= ''; }
|
382 |
+
if (isset($attr['SUPPRESS']) && $attr['SUPPRESS']) { $suppress = $attr['SUPPRESS']; } else { $suppress = ''; }
|
383 |
+
if (isset($attr['TOC-ORIENTATION']) && $attr['TOC-ORIENTATION']) { $toc_orientation = $attr['TOC-ORIENTATION']; } else { $toc_orientation = ''; }
|
384 |
+
if (isset($attr['PAGING']) && (strtoupper($attr['PAGING'])=='OFF' || $attr['PAGING']==='0')) { $paging = false; }
|
385 |
+
else { $paging = true; }
|
386 |
+
if (isset($attr['LINKS']) && (strtoupper($attr['LINKS'])=='ON' || $attr['LINKS']==1)) { $links = true; }
|
387 |
+
else { $links = false; }
|
388 |
+
if (isset($attr['NAME']) && $attr['NAME']) { $toc_id = strtolower($attr['NAME']); } else { $toc_id = 0; }
|
389 |
+
$this->TOC('',0,0,$resetpagenum, $pagenumstyle, $suppress, $toc_orientation, $paging, $links, $toc_id, $tocoutdent); // mPDF 5.6.19 5.6.31
|
390 |
+
}
|
391 |
+
|
392 |
+
|
393 |
+
function openTagTOCPAGEBREAK($attr) {
|
394 |
+
if (isset($attr['NAME']) && $attr['NAME']) { $toc_id = strtolower($attr['NAME']); } else { $toc_id = 0; }
|
395 |
+
if ($toc_id) {
|
396 |
+
if (isset($attr['OUTDENT']) && $attr['OUTDENT']) { $this->m_TOC[$toc_id]['TOCoutdent'] = $attr['OUTDENT']; } else { $this->m_TOC[$toc_id]['TOCoutdent'] = ''; } // mPDF 5.6.19
|
397 |
+
if (isset($attr['TOC-ORIENTATION']) && $attr['TOC-ORIENTATION']) { $this->m_TOC[$toc_id]['TOCorientation'] = $attr['TOC-ORIENTATION']; } else { $this->m_TOC[$toc_id]['TOCorientation'] = ''; }
|
398 |
+
if (isset($attr['PAGING']) && (strtoupper($attr['PAGING'])=='OFF' || $attr['PAGING']==='0')) { $this->m_TOC[$toc_id]['TOCusePaging'] = false; }
|
399 |
+
else { $this->m_TOC[$toc_id]['TOCusePaging'] = true; }
|
400 |
+
if (isset($attr['LINKS']) && (strtoupper($attr['LINKS'])=='ON' || $attr['LINKS']==1)) { $this->m_TOC[$toc_id]['TOCuseLinking'] = true; }
|
401 |
+
else { $this->m_TOC[$toc_id]['TOCuseLinking'] = false; }
|
402 |
+
|
403 |
+
$this->m_TOC[$toc_id]['TOC_margin_left'] = $this->m_TOC[$toc_id]['TOC_margin_right'] = $this->m_TOC[$toc_id]['TOC_margin_top'] = $this->m_TOC[$toc_id]['TOC_margin_bottom'] = $this->m_TOC[$toc_id]['TOC_margin_header'] = $this->m_TOC[$toc_id]['TOC_margin_footer'] = '';
|
404 |
+
if (isset($attr['TOC-MARGIN-RIGHT'])) { $this->m_TOC[$toc_id]['TOC_margin_right'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-RIGHT'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
405 |
+
if (isset($attr['TOC-MARGIN-LEFT'])) { $this->m_TOC[$toc_id]['TOC_margin_left'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-LEFT'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
406 |
+
if (isset($attr['TOC-MARGIN-TOP'])) { $this->m_TOC[$toc_id]['TOC_margin_top'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-TOP'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
407 |
+
if (isset($attr['TOC-MARGIN-BOTTOM'])) { $this->m_TOC[$toc_id]['TOC_margin_bottom'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-BOTTOM'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
408 |
+
if (isset($attr['TOC-MARGIN-HEADER'])) { $this->m_TOC[$toc_id]['TOC_margin_header'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-HEADER'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
409 |
+
if (isset($attr['TOC-MARGIN-FOOTER'])) { $this->m_TOC[$toc_id]['TOC_margin_footer'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-FOOTER'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
410 |
+
$this->m_TOC[$toc_id]['TOC_odd_header_name'] = $this->m_TOC[$toc_id]['TOC_even_header_name'] = $this->m_TOC[$toc_id]['TOC_odd_footer_name'] = $this->m_TOC[$toc_id]['TOC_even_footer_name'] = '';
|
411 |
+
if (isset($attr['TOC-ODD-HEADER-NAME']) && $attr['TOC-ODD-HEADER-NAME']) { $this->m_TOC[$toc_id]['TOC_odd_header_name'] = $attr['TOC-ODD-HEADER-NAME']; }
|
412 |
+
if (isset($attr['TOC-EVEN-HEADER-NAME']) && $attr['TOC-EVEN-HEADER-NAME']) { $this->m_TOC[$toc_id]['TOC_even_header_name'] = $attr['TOC-EVEN-HEADER-NAME']; }
|
413 |
+
if (isset($attr['TOC-ODD-FOOTER-NAME']) && $attr['TOC-ODD-FOOTER-NAME']) { $this->m_TOC[$toc_id]['TOC_odd_footer_name'] = $attr['TOC-ODD-FOOTER-NAME']; }
|
414 |
+
if (isset($attr['TOC-EVEN-FOOTER-NAME']) && $attr['TOC-EVEN-FOOTER-NAME']) { $this->m_TOC[$toc_id]['TOC_even_footer_name'] = $attr['TOC-EVEN-FOOTER-NAME']; }
|
415 |
+
$this->m_TOC[$toc_id]['TOC_odd_header_value'] = $this->m_TOC[$toc_id]['TOC_even_header_value'] = $this->m_TOC[$toc_id]['TOC_odd_footer_value'] = $this->m_TOC[$toc_id]['TOC_even_footer_value'] = 0;
|
416 |
+
if (isset($attr['TOC-ODD-HEADER-VALUE']) && ($attr['TOC-ODD-HEADER-VALUE']=='1' || strtoupper($attr['TOC-ODD-HEADER-VALUE'])=='ON')) { $this->m_TOC[$toc_id]['TOC_odd_header_value'] = 1; }
|
417 |
+
else if (isset($attr['TOC-ODD-HEADER-VALUE']) && ($attr['TOC-ODD-HEADER-VALUE']=='-1' || strtoupper($attr['TOC-ODD-HEADER-VALUE'])=='OFF')) { $this->m_TOC[$toc_id]['TOC_odd_header_value'] = -1; }
|
418 |
+
if (isset($attr['TOC-EVEN-HEADER-VALUE']) && ($attr['TOC-EVEN-HEADER-VALUE']=='1' || strtoupper($attr['TOC-EVEN-HEADER-VALUE'])=='ON')) { $this->m_TOC[$toc_id]['TOC_even_header_value'] = 1; }
|
419 |
+
else if (isset($attr['TOC-EVEN-HEADER-VALUE']) && ($attr['TOC-EVEN-HEADER-VALUE']=='-1' || strtoupper($attr['TOC-EVEN-HEADER-VALUE'])=='OFF')) { $this->m_TOC[$toc_id]['TOC_even_header_value'] = -1; }
|
420 |
+
if (isset($attr['TOC-ODD-FOOTER-VALUE']) && ($attr['TOC-ODD-FOOTER-VALUE']=='1' || strtoupper($attr['TOC-ODD-FOOTER-VALUE'])=='ON')) { $this->m_TOC[$toc_id]['TOC_odd_footer_value'] = 1; }
|
421 |
+
else if (isset($attr['TOC-ODD-FOOTER-VALUE']) && ($attr['TOC-ODD-FOOTER-VALUE']=='-1' || strtoupper($attr['TOC-ODD-FOOTER-VALUE'])=='OFF')) { $this->m_TOC[$toc_id]['TOC_odd_footer_value'] = -1; }
|
422 |
+
if (isset($attr['TOC-EVEN-FOOTER-VALUE']) && ($attr['TOC-EVEN-FOOTER-VALUE']=='1' || strtoupper($attr['TOC-EVEN-FOOTER-VALUE'])=='ON')) { $this->m_TOC[$toc_id]['TOC_even_footer_value'] = 1; }
|
423 |
+
else if (isset($attr['TOC-EVEN-FOOTER-VALUE']) && ($attr['TOC-EVEN-FOOTER-VALUE']=='-1' || strtoupper($attr['TOC-EVEN-FOOTER-VALUE'])=='OFF')) { $this->m_TOC[$toc_id]['TOC_even_footer_value'] = -1; }
|
424 |
+
if (isset($attr['TOC-RESETPAGENUM']) && $attr['TOC-RESETPAGENUM']) { $this->m_TOC[$toc_id]['TOC_resetpagenum'] = $attr['TOC-RESETPAGENUM']; }
|
425 |
+
else { $this->m_TOC[$toc_id]['TOC_resetpagenum'] = ''; } // mPDF 6
|
426 |
+
if (isset($attr['TOC-PAGENUMSTYLE']) && $attr['TOC-PAGENUMSTYLE']) { $this->m_TOC[$toc_id]['TOC_pagenumstyle'] = $attr['TOC-PAGENUMSTYLE']; }
|
427 |
+
else { $this->m_TOC[$toc_id]['TOC_pagenumstyle'] = ''; } // mPDF 6
|
428 |
+
if (isset($attr['TOC-SUPPRESS']) && ($attr['TOC-SUPPRESS'] || $attr['TOC-SUPPRESS']==='0')) { $this->m_TOC[$toc_id]['TOC_suppress'] = $attr['TOC-SUPPRESS']; }
|
429 |
+
else { $this->m_TOC[$toc_id]['TOC_suppress'] = ''; } // mPDF 6
|
430 |
+
if (isset($attr['TOC-PAGE-SELECTOR']) && $attr['TOC-PAGE-SELECTOR']) { $this->m_TOC[$toc_id]['TOC_page_selector'] = $attr['TOC-PAGE-SELECTOR']; }
|
431 |
+
else { $this->m_TOC[$toc_id]['TOC_page_selector'] = ''; }
|
432 |
+
if (isset($attr['TOC-SHEET-SIZE']) && $attr['TOC-SHEET-SIZE']) { $this->m_TOC[$toc_id]['TOCsheetsize'] = $attr['TOC-SHEET-SIZE']; } else { $this->m_TOC[$toc_id]['TOCsheetsize'] = ''; }
|
433 |
+
|
434 |
+
|
435 |
+
if (isset($attr['TOC-PREHTML']) && $attr['TOC-PREHTML']) { $this->m_TOC[$toc_id]['TOCpreHTML'] = htmlspecialchars_decode($attr['TOC-PREHTML'],ENT_QUOTES); }
|
436 |
+
if (isset($attr['TOC-POSTHTML']) && $attr['TOC-POSTHTML']) { $this->m_TOC[$toc_id]['TOCpostHTML'] = htmlspecialchars_decode($attr['TOC-POSTHTML'],ENT_QUOTES); }
|
437 |
+
|
438 |
+
if (isset($attr['TOC-BOOKMARKTEXT']) && $attr['TOC-BOOKMARKTEXT']) { $this->m_TOC[$toc_id]['TOCbookmarkText'] = htmlspecialchars_decode($attr['TOC-BOOKMARKTEXT'],ENT_QUOTES); } // *BOOKMARKS*
|
439 |
+
}
|
440 |
+
else {
|
441 |
+
if (isset($attr['OUTDENT']) && $attr['OUTDENT']) { $this->TOCoutdent = $attr['OUTDENT']; } else { $this->TOCoutdent = ''; } // mPDF 5.6.19
|
442 |
+
if (isset($attr['TOC-ORIENTATION']) && $attr['TOC-ORIENTATION']) { $this->TOCorientation = $attr['TOC-ORIENTATION']; } else { $this->TOCorientation = ''; }
|
443 |
+
if (isset($attr['PAGING']) && (strtoupper($attr['PAGING'])=='OFF' || $attr['PAGING']==='0')) { $this->TOCusePaging = false; }
|
444 |
+
else { $this->TOCusePaging = true; }
|
445 |
+
if (isset($attr['LINKS']) && (strtoupper($attr['LINKS'])=='ON' || $attr['LINKS']==1)) { $this->TOCuseLinking = true; }
|
446 |
+
else { $this->TOCuseLinking = false; }
|
447 |
+
|
448 |
+
$this->TOC_margin_left = $this->TOC_margin_right = $this->TOC_margin_top = $this->TOC_margin_bottom = $this->TOC_margin_header = $this->TOC_margin_footer = '';
|
449 |
+
if (isset($attr['TOC-MARGIN-RIGHT'])) { $this->TOC_margin_right = $this->mpdf->ConvertSize($attr['TOC-MARGIN-RIGHT'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
450 |
+
if (isset($attr['TOC-MARGIN-LEFT'])) { $this->TOC_margin_left = $this->mpdf->ConvertSize($attr['TOC-MARGIN-LEFT'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
451 |
+
if (isset($attr['TOC-MARGIN-TOP'])) { $this->TOC_margin_top = $this->mpdf->ConvertSize($attr['TOC-MARGIN-TOP'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
452 |
+
if (isset($attr['TOC-MARGIN-BOTTOM'])) { $this->TOC_margin_bottom = $this->mpdf->ConvertSize($attr['TOC-MARGIN-BOTTOM'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
453 |
+
if (isset($attr['TOC-MARGIN-HEADER'])) { $this->TOC_margin_header = $this->mpdf->ConvertSize($attr['TOC-MARGIN-HEADER'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
454 |
+
if (isset($attr['TOC-MARGIN-FOOTER'])) { $this->TOC_margin_footer = $this->mpdf->ConvertSize($attr['TOC-MARGIN-FOOTER'],$this->mpdf->w,$this->mpdf->FontSize,false); }
|
455 |
+
$this->TOC_odd_header_name = $this->TOC_even_header_name = $this->TOC_odd_footer_name = $this->TOC_even_footer_name = '';
|
456 |
+
if (isset($attr['TOC-ODD-HEADER-NAME']) && $attr['TOC-ODD-HEADER-NAME']) { $this->TOC_odd_header_name = $attr['TOC-ODD-HEADER-NAME']; }
|
457 |
+
if (isset($attr['TOC-EVEN-HEADER-NAME']) && $attr['TOC-EVEN-HEADER-NAME']) { $this->TOC_even_header_name = $attr['TOC-EVEN-HEADER-NAME']; }
|
458 |
+
if (isset($attr['TOC-ODD-FOOTER-NAME']) && $attr['TOC-ODD-FOOTER-NAME']) { $this->TOC_odd_footer_name = $attr['TOC-ODD-FOOTER-NAME']; }
|
459 |
+
if (isset($attr['TOC-EVEN-FOOTER-NAME']) && $attr['TOC-EVEN-FOOTER-NAME']) { $this->TOC_even_footer_name = $attr['TOC-EVEN-FOOTER-NAME']; }
|
460 |
+
$this->TOC_odd_header_value = $this->TOC_even_header_value = $this->TOC_odd_footer_value = $this->TOC_even_footer_value = 0;
|
461 |
+
if (isset($attr['TOC-ODD-HEADER-VALUE']) && ($attr['TOC-ODD-HEADER-VALUE']=='1' || strtoupper($attr['TOC-ODD-HEADER-VALUE'])=='ON')) { $this->TOC_odd_header_value = 1; }
|
462 |
+
else if (isset($attr['TOC-ODD-HEADER-VALUE']) && ($attr['TOC-ODD-HEADER-VALUE']=='-1' || strtoupper($attr['TOC-ODD-HEADER-VALUE'])=='OFF'
|