Version Description
- Fix: ImageMagick version conflict
- Translations: Updated POT
Download this release
Release Info
Developer | pomegranate |
Plugin | WooCommerce PDF Invoices & Packing Slips |
Version | 2.5.2 |
Comparing to | |
See all releases |
Code changes from version 2.5.1 to 2.5.2
- includes/class-wcpdf-main.php +1 -1
- includes/class-wcpdf-settings-debug.php +264 -262
- includes/documents/abstract-wcpdf-order-document.php +1 -1
- includes/views/wcpdf-extensions.php +131 -131
- languages/woocommerce-pdf-invoices-packing-slips.pot +1202 -1057
- readme.txt +5 -1
- vendor/dompdf/dompdf/lib/Cpdf.php +5644 -5640
- woocommerce-pdf-invoices-packingslips.php +2 -2
includes/class-wcpdf-main.php
CHANGED
@@ -36,7 +36,7 @@ class Main {
|
|
36 |
add_filter( 'wpo_wcpdf_document_use_historical_settings', array( $this, 'test_mode_settings' ), 15, 2 );
|
37 |
|
38 |
// page numbers & currency filters
|
39 |
-
|
40 |
add_action( 'wpo_wcpdf_after_dompdf_render', array($this, 'page_number_replacements' ), 9, 2 );
|
41 |
if ( isset( WPO_WCPDF()->settings->general_settings['currency_font'] ) ) {
|
42 |
add_action( 'wpo_wcpdf_before_pdf', array($this, 'use_currency_font' ), 10, 2 );
|
36 |
add_filter( 'wpo_wcpdf_document_use_historical_settings', array( $this, 'test_mode_settings' ), 15, 2 );
|
37 |
|
38 |
// page numbers & currency filters
|
39 |
+
add_filter( 'wpo_wcpdf_get_html', array($this, 'format_page_number_placeholders' ), 10, 2 );
|
40 |
add_action( 'wpo_wcpdf_after_dompdf_render', array($this, 'page_number_replacements' ), 9, 2 );
|
41 |
if ( isset( WPO_WCPDF()->settings->general_settings['currency_font'] ) ) {
|
42 |
add_action( 'wpo_wcpdf_before_pdf', array($this, 'use_currency_font' ), 10, 2 );
|
includes/class-wcpdf-settings-debug.php
CHANGED
@@ -1,263 +1,265 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
-
exit; // Exit if accessed directly
|
6 |
-
}
|
7 |
-
|
8 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_Debug' ) ) :
|
9 |
-
|
10 |
-
class Settings_Debug {
|
11 |
-
|
12 |
-
function __construct() {
|
13 |
-
add_action( 'admin_init', array( $this, 'init_settings' ) );
|
14 |
-
add_action( 'wpo_wcpdf_settings_output_debug', array( $this, 'output' ), 10, 1 );
|
15 |
-
add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'debug_tools' ), 10, 2 );
|
16 |
-
|
17 |
-
// yes, we're hiring!
|
18 |
-
if (defined('WP_DEBUG') && WP_DEBUG) {
|
19 |
-
add_action( 'wpo_wcpdf_before_settings_page', array( $this, 'work_at_wpovernight' ), 10, 2 );
|
20 |
-
} else {
|
21 |
-
add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'work_at_wpovernight' ), 30, 2 );
|
22 |
-
}
|
23 |
-
|
24 |
-
add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'dompdf_status' ), 20, 2 );
|
25 |
-
}
|
26 |
-
|
27 |
-
public function output( $section ) {
|
28 |
-
settings_fields( "wpo_wcpdf_settings_debug" );
|
29 |
-
do_settings_sections( "wpo_wcpdf_settings_debug" );
|
30 |
-
|
31 |
-
submit_button();
|
32 |
-
}
|
33 |
-
|
34 |
-
public function debug_tools( $tab, $section ) {
|
35 |
-
if ($tab !== 'debug') {
|
36 |
-
return;
|
37 |
-
}
|
38 |
-
?>
|
39 |
-
<form method="post">
|
40 |
-
<?php wp_nonce_field( 'wpo_wcpdf_debug_tools_action', 'security' ); ?>
|
41 |
-
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="install_fonts">
|
42 |
-
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Reinstall fonts', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
43 |
-
<?php
|
44 |
-
if ( !empty($_POST) && isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'install_fonts' ) {
|
45 |
-
// check permissions
|
46 |
-
if ( !check_admin_referer( 'wpo_wcpdf_debug_tools_action', 'security' ) ) {
|
47 |
-
return;
|
48 |
-
}
|
49 |
-
|
50 |
-
$font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
|
51 |
-
|
52 |
-
// clear folder first
|
53 |
-
if ( function_exists("glob") && $files = glob( $font_path.'/*.*' ) ) {
|
54 |
-
$exclude_files = array( 'index.php', '.htaccess' );
|
55 |
-
foreach($files as $file) {
|
56 |
-
if( is_file($file) && !in_array( basename($file), $exclude_files ) ) {
|
57 |
-
unlink($file);
|
58 |
-
}
|
59 |
-
}
|
60 |
-
}
|
61 |
-
|
62 |
-
WPO_WCPDF()->main->copy_fonts( $font_path );
|
63 |
-
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Fonts reinstalled!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
64 |
-
}
|
65 |
-
?>
|
66 |
-
</form>
|
67 |
-
<form method="post">
|
68 |
-
<?php wp_nonce_field( 'wpo_wcpdf_debug_tools_action', 'security' ); ?>
|
69 |
-
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="clear_tmp">
|
70 |
-
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Remove temporary files', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
71 |
-
<?php
|
72 |
-
if ( !empty($_POST) && isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'clear_tmp' ) {
|
73 |
-
// check permissions
|
74 |
-
if ( !check_admin_referer( 'wpo_wcpdf_debug_tools_action', 'security' ) ) {
|
75 |
-
return;
|
76 |
-
}
|
77 |
-
$tmp_path = WPO_WCPDF()->main->get_tmp_path('attachments');
|
78 |
-
|
79 |
-
if ( !function_exists("glob") ) {
|
80 |
-
// glob is disabled
|
81 |
-
printf('<div class="notice notice-error"><p>%s<br><code>%s</code></p></div>', __( "Unable to read temporary folder contents!", 'woocommerce-pdf-invoices-packing-slips' ), $tmp_path);
|
82 |
-
} else {
|
83 |
-
$success = 0;
|
84 |
-
$error = 0;
|
85 |
-
if ( $files = glob($tmp_path.'*.pdf') ) { // get all pdf files
|
86 |
-
foreach($files as $file) {
|
87 |
-
if(is_file($file)) {
|
88 |
-
// delete file
|
89 |
-
if ( unlink($file) === true ) {
|
90 |
-
$success++;
|
91 |
-
} else {
|
92 |
-
$error++;
|
93 |
-
}
|
94 |
-
}
|
95 |
-
}
|
96 |
-
|
97 |
-
if ($error > 0) {
|
98 |
-
$message = sprintf( __( 'Unable to delete %d files! (deleted %d)', 'woocommerce-pdf-invoices-packing-slips' ), $error, $success);
|
99 |
-
printf('<div class="notice notice-error"><p>%s</p></div>', $message);
|
100 |
-
} else {
|
101 |
-
$message = sprintf( __( 'Successfully deleted %d files!', 'woocommerce-pdf-invoices-packing-slips' ), $success );
|
102 |
-
printf('<div class="notice notice-success"><p>%s</p></div>', $message);
|
103 |
-
}
|
104 |
-
} else {
|
105 |
-
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Nothing to delete!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
106 |
-
}
|
107 |
-
}
|
108 |
-
}
|
109 |
-
?>
|
110 |
-
</form>
|
111 |
-
<form method="post">
|
112 |
-
<?php wp_nonce_field( 'wpo_wcpdf_debug_tools_action', 'security' ); ?>
|
113 |
-
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="delete_legacy_settings">
|
114 |
-
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Delete legacy (1.X) settings', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
115 |
-
<?php
|
116 |
-
if ( !empty($_POST) && isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'delete_legacy_settings' ) {
|
117 |
-
// check permissions
|
118 |
-
if ( !check_admin_referer( 'wpo_wcpdf_debug_tools_action', 'security' ) ) {
|
119 |
-
return;
|
120 |
-
}
|
121 |
-
// delete options
|
122 |
-
delete_option( 'wpo_wcpdf_general_settings' );
|
123 |
-
delete_option( 'wpo_wcpdf_template_settings' );
|
124 |
-
delete_option( 'wpo_wcpdf_debug_settings' );
|
125 |
-
// and delete cache of these options, just in case...
|
126 |
-
wp_cache_delete( 'wpo_wcpdf_general_settings','options' );
|
127 |
-
wp_cache_delete( 'wpo_wcpdf_template_settings','options' );
|
128 |
-
wp_cache_delete( 'wpo_wcpdf_debug_settings','options' );
|
129 |
-
|
130 |
-
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Legacy settings deleted!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
131 |
-
}
|
132 |
-
?>
|
133 |
-
</form>
|
134 |
-
<?php
|
135 |
-
}
|
136 |
-
|
137 |
-
public function work_at_wpovernight( $tab, $section ) {
|
138 |
-
if ($tab === 'debug') {
|
139 |
-
include( WPO_WCPDF()->plugin_path() . '/includes/views/work-at-wpovernight.php' );
|
140 |
-
}
|
141 |
-
}
|
142 |
-
|
143 |
-
public function dompdf_status( $tab, $section ) {
|
144 |
-
if ($tab === 'debug') {
|
145 |
-
include( WPO_WCPDF()->plugin_path() . '/includes/views/dompdf-status.php' );
|
146 |
-
}
|
147 |
-
}
|
148 |
-
|
149 |
-
public function init_settings() {
|
150 |
-
// Register settings.
|
151 |
-
$page = $option_group = $option_name = 'wpo_wcpdf_settings_debug';
|
152 |
-
|
153 |
-
$settings_fields = array(
|
154 |
-
array(
|
155 |
-
'type' => 'section',
|
156 |
-
'id' => 'debug_settings',
|
157 |
-
'title' => __( 'Debug settings', 'woocommerce-pdf-invoices-packing-slips' ),
|
158 |
-
'callback' => 'section',
|
159 |
-
),
|
160 |
-
array(
|
161 |
-
'type' => 'setting',
|
162 |
-
'id' => 'legacy_mode',
|
163 |
-
'title' => __( 'Legacy mode', 'woocommerce-pdf-invoices-packing-slips' ),
|
164 |
-
'callback' => 'checkbox',
|
165 |
-
'section' => 'debug_settings',
|
166 |
-
'args' => array(
|
167 |
-
'option_name' => $option_name,
|
168 |
-
'id' => 'legacy_mode',
|
169 |
-
'description' => __( "Legacy mode ensures compatibility with templates and filters from previous versions.", 'woocommerce-pdf-invoices-packing-slips' ),
|
170 |
-
)
|
171 |
-
),
|
172 |
-
array(
|
173 |
-
'type' => 'setting',
|
174 |
-
'id' => 'guest_access',
|
175 |
-
'title' => __( 'Allow guest access', 'woocommerce-pdf-invoices-packing-slips' ),
|
176 |
-
'callback' => 'checkbox',
|
177 |
-
'section' => 'debug_settings',
|
178 |
-
'args' => array(
|
179 |
-
'option_name' => $option_name,
|
180 |
-
'id' => 'guest_access',
|
181 |
-
'description' => __( 'Enable this to allow customers that purchase without an account to access their PDF with a unique key', 'woocommerce-pdf-invoices-packing-slips' ),
|
182 |
-
)
|
183 |
-
),
|
184 |
-
array(
|
185 |
-
'type' => 'setting',
|
186 |
-
'id' => 'calculate_document_numbers',
|
187 |
-
'title' => __( 'Calculate document numbers (slow)', 'woocommerce-pdf-invoices-packing-slips' ),
|
188 |
-
'callback' => 'checkbox',
|
189 |
-
'section' => 'debug_settings',
|
190 |
-
'args' => array(
|
191 |
-
'option_name' => $option_name,
|
192 |
-
'id' => 'calculate_document_numbers',
|
193 |
-
'description' => __( "Document numbers (such as invoice numbers) are generated using AUTO_INCREMENT by default. Use this setting if your database auto increments with more than 1.", 'woocommerce-pdf-invoices-packing-slips' ),
|
194 |
-
)
|
195 |
-
),
|
196 |
-
array(
|
197 |
-
'type' => 'setting',
|
198 |
-
'id' => 'enable_debug',
|
199 |
-
'title' => __( 'Enable debug output', 'woocommerce-pdf-invoices-packing-slips' ),
|
200 |
-
'callback' => 'checkbox',
|
201 |
-
'section' => 'debug_settings',
|
202 |
-
'args' => array(
|
203 |
-
'option_name' => $option_name,
|
204 |
-
'id' => 'enable_debug',
|
205 |
-
'description' => __( "Enable this option to output plugin errors if you're getting a blank page or other PDF generation issues", 'woocommerce-pdf-invoices-packing-slips' ) . '<br>' .
|
206 |
-
__( '<b>Caution!</b> This setting may reveal errors (from other plugins) in other places on your site too, therefor this is not recommended to leave it enabled on live sites.', 'woocommerce-pdf-invoices-packing-slips' )
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
'
|
212 |
-
'
|
213 |
-
'
|
214 |
-
'
|
215 |
-
'
|
216 |
-
|
217 |
-
'
|
218 |
-
'
|
219 |
-
'
|
220 |
-
'
|
221 |
-
'
|
222 |
-
'
|
223 |
-
'
|
224 |
-
|
225 |
-
__(
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
'
|
231 |
-
'
|
232 |
-
'
|
233 |
-
'
|
234 |
-
'
|
235 |
-
|
236 |
-
'
|
237 |
-
'
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
'
|
244 |
-
'
|
245 |
-
'
|
246 |
-
'
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
}
|
260 |
-
|
261 |
-
|
262 |
-
|
|
|
|
|
263 |
return new Settings_Debug();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
+
exit; // Exit if accessed directly
|
6 |
+
}
|
7 |
+
|
8 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_Debug' ) ) :
|
9 |
+
|
10 |
+
class Settings_Debug {
|
11 |
+
|
12 |
+
function __construct() {
|
13 |
+
add_action( 'admin_init', array( $this, 'init_settings' ) );
|
14 |
+
add_action( 'wpo_wcpdf_settings_output_debug', array( $this, 'output' ), 10, 1 );
|
15 |
+
add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'debug_tools' ), 10, 2 );
|
16 |
+
|
17 |
+
// yes, we're hiring!
|
18 |
+
if (defined('WP_DEBUG') && WP_DEBUG) {
|
19 |
+
add_action( 'wpo_wcpdf_before_settings_page', array( $this, 'work_at_wpovernight' ), 10, 2 );
|
20 |
+
} else {
|
21 |
+
add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'work_at_wpovernight' ), 30, 2 );
|
22 |
+
}
|
23 |
+
|
24 |
+
add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'dompdf_status' ), 20, 2 );
|
25 |
+
}
|
26 |
+
|
27 |
+
public function output( $section ) {
|
28 |
+
settings_fields( "wpo_wcpdf_settings_debug" );
|
29 |
+
do_settings_sections( "wpo_wcpdf_settings_debug" );
|
30 |
+
|
31 |
+
submit_button();
|
32 |
+
}
|
33 |
+
|
34 |
+
public function debug_tools( $tab, $section ) {
|
35 |
+
if ($tab !== 'debug') {
|
36 |
+
return;
|
37 |
+
}
|
38 |
+
?>
|
39 |
+
<form method="post">
|
40 |
+
<?php wp_nonce_field( 'wpo_wcpdf_debug_tools_action', 'security' ); ?>
|
41 |
+
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="install_fonts">
|
42 |
+
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Reinstall fonts', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
43 |
+
<?php
|
44 |
+
if ( !empty($_POST) && isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'install_fonts' ) {
|
45 |
+
// check permissions
|
46 |
+
if ( !check_admin_referer( 'wpo_wcpdf_debug_tools_action', 'security' ) ) {
|
47 |
+
return;
|
48 |
+
}
|
49 |
+
|
50 |
+
$font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
|
51 |
+
|
52 |
+
// clear folder first
|
53 |
+
if ( function_exists("glob") && $files = glob( $font_path.'/*.*' ) ) {
|
54 |
+
$exclude_files = array( 'index.php', '.htaccess' );
|
55 |
+
foreach($files as $file) {
|
56 |
+
if( is_file($file) && !in_array( basename($file), $exclude_files ) ) {
|
57 |
+
unlink($file);
|
58 |
+
}
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
WPO_WCPDF()->main->copy_fonts( $font_path );
|
63 |
+
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Fonts reinstalled!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
64 |
+
}
|
65 |
+
?>
|
66 |
+
</form>
|
67 |
+
<form method="post">
|
68 |
+
<?php wp_nonce_field( 'wpo_wcpdf_debug_tools_action', 'security' ); ?>
|
69 |
+
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="clear_tmp">
|
70 |
+
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Remove temporary files', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
71 |
+
<?php
|
72 |
+
if ( !empty($_POST) && isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'clear_tmp' ) {
|
73 |
+
// check permissions
|
74 |
+
if ( !check_admin_referer( 'wpo_wcpdf_debug_tools_action', 'security' ) ) {
|
75 |
+
return;
|
76 |
+
}
|
77 |
+
$tmp_path = WPO_WCPDF()->main->get_tmp_path('attachments');
|
78 |
+
|
79 |
+
if ( !function_exists("glob") ) {
|
80 |
+
// glob is disabled
|
81 |
+
printf('<div class="notice notice-error"><p>%s<br><code>%s</code></p></div>', __( "Unable to read temporary folder contents!", 'woocommerce-pdf-invoices-packing-slips' ), $tmp_path);
|
82 |
+
} else {
|
83 |
+
$success = 0;
|
84 |
+
$error = 0;
|
85 |
+
if ( $files = glob($tmp_path.'*.pdf') ) { // get all pdf files
|
86 |
+
foreach($files as $file) {
|
87 |
+
if(is_file($file)) {
|
88 |
+
// delete file
|
89 |
+
if ( unlink($file) === true ) {
|
90 |
+
$success++;
|
91 |
+
} else {
|
92 |
+
$error++;
|
93 |
+
}
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
if ($error > 0) {
|
98 |
+
$message = sprintf( __( 'Unable to delete %d files! (deleted %d)', 'woocommerce-pdf-invoices-packing-slips' ), $error, $success);
|
99 |
+
printf('<div class="notice notice-error"><p>%s</p></div>', $message);
|
100 |
+
} else {
|
101 |
+
$message = sprintf( __( 'Successfully deleted %d files!', 'woocommerce-pdf-invoices-packing-slips' ), $success );
|
102 |
+
printf('<div class="notice notice-success"><p>%s</p></div>', $message);
|
103 |
+
}
|
104 |
+
} else {
|
105 |
+
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Nothing to delete!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
106 |
+
}
|
107 |
+
}
|
108 |
+
}
|
109 |
+
?>
|
110 |
+
</form>
|
111 |
+
<form method="post">
|
112 |
+
<?php wp_nonce_field( 'wpo_wcpdf_debug_tools_action', 'security' ); ?>
|
113 |
+
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="delete_legacy_settings">
|
114 |
+
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Delete legacy (1.X) settings', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
115 |
+
<?php
|
116 |
+
if ( !empty($_POST) && isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'delete_legacy_settings' ) {
|
117 |
+
// check permissions
|
118 |
+
if ( !check_admin_referer( 'wpo_wcpdf_debug_tools_action', 'security' ) ) {
|
119 |
+
return;
|
120 |
+
}
|
121 |
+
// delete options
|
122 |
+
delete_option( 'wpo_wcpdf_general_settings' );
|
123 |
+
delete_option( 'wpo_wcpdf_template_settings' );
|
124 |
+
delete_option( 'wpo_wcpdf_debug_settings' );
|
125 |
+
// and delete cache of these options, just in case...
|
126 |
+
wp_cache_delete( 'wpo_wcpdf_general_settings','options' );
|
127 |
+
wp_cache_delete( 'wpo_wcpdf_template_settings','options' );
|
128 |
+
wp_cache_delete( 'wpo_wcpdf_debug_settings','options' );
|
129 |
+
|
130 |
+
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Legacy settings deleted!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
131 |
+
}
|
132 |
+
?>
|
133 |
+
</form>
|
134 |
+
<?php
|
135 |
+
}
|
136 |
+
|
137 |
+
public function work_at_wpovernight( $tab, $section ) {
|
138 |
+
if ($tab === 'debug') {
|
139 |
+
include( WPO_WCPDF()->plugin_path() . '/includes/views/work-at-wpovernight.php' );
|
140 |
+
}
|
141 |
+
}
|
142 |
+
|
143 |
+
public function dompdf_status( $tab, $section ) {
|
144 |
+
if ($tab === 'debug') {
|
145 |
+
include( WPO_WCPDF()->plugin_path() . '/includes/views/dompdf-status.php' );
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
public function init_settings() {
|
150 |
+
// Register settings.
|
151 |
+
$page = $option_group = $option_name = 'wpo_wcpdf_settings_debug';
|
152 |
+
|
153 |
+
$settings_fields = array(
|
154 |
+
array(
|
155 |
+
'type' => 'section',
|
156 |
+
'id' => 'debug_settings',
|
157 |
+
'title' => __( 'Debug settings', 'woocommerce-pdf-invoices-packing-slips' ),
|
158 |
+
'callback' => 'section',
|
159 |
+
),
|
160 |
+
array(
|
161 |
+
'type' => 'setting',
|
162 |
+
'id' => 'legacy_mode',
|
163 |
+
'title' => __( 'Legacy mode', 'woocommerce-pdf-invoices-packing-slips' ),
|
164 |
+
'callback' => 'checkbox',
|
165 |
+
'section' => 'debug_settings',
|
166 |
+
'args' => array(
|
167 |
+
'option_name' => $option_name,
|
168 |
+
'id' => 'legacy_mode',
|
169 |
+
'description' => __( "Legacy mode ensures compatibility with templates and filters from previous versions.", 'woocommerce-pdf-invoices-packing-slips' ),
|
170 |
+
)
|
171 |
+
),
|
172 |
+
array(
|
173 |
+
'type' => 'setting',
|
174 |
+
'id' => 'guest_access',
|
175 |
+
'title' => __( 'Allow guest access', 'woocommerce-pdf-invoices-packing-slips' ),
|
176 |
+
'callback' => 'checkbox',
|
177 |
+
'section' => 'debug_settings',
|
178 |
+
'args' => array(
|
179 |
+
'option_name' => $option_name,
|
180 |
+
'id' => 'guest_access',
|
181 |
+
'description' => __( 'Enable this to allow customers that purchase without an account to access their PDF with a unique key', 'woocommerce-pdf-invoices-packing-slips' ),
|
182 |
+
)
|
183 |
+
),
|
184 |
+
array(
|
185 |
+
'type' => 'setting',
|
186 |
+
'id' => 'calculate_document_numbers',
|
187 |
+
'title' => __( 'Calculate document numbers (slow)', 'woocommerce-pdf-invoices-packing-slips' ),
|
188 |
+
'callback' => 'checkbox',
|
189 |
+
'section' => 'debug_settings',
|
190 |
+
'args' => array(
|
191 |
+
'option_name' => $option_name,
|
192 |
+
'id' => 'calculate_document_numbers',
|
193 |
+
'description' => __( "Document numbers (such as invoice numbers) are generated using AUTO_INCREMENT by default. Use this setting if your database auto increments with more than 1.", 'woocommerce-pdf-invoices-packing-slips' ),
|
194 |
+
)
|
195 |
+
),
|
196 |
+
array(
|
197 |
+
'type' => 'setting',
|
198 |
+
'id' => 'enable_debug',
|
199 |
+
'title' => __( 'Enable debug output', 'woocommerce-pdf-invoices-packing-slips' ),
|
200 |
+
'callback' => 'checkbox',
|
201 |
+
'section' => 'debug_settings',
|
202 |
+
'args' => array(
|
203 |
+
'option_name' => $option_name,
|
204 |
+
'id' => 'enable_debug',
|
205 |
+
'description' => __( "Enable this option to output plugin errors if you're getting a blank page or other PDF generation issues", 'woocommerce-pdf-invoices-packing-slips' ) . '<br>' .
|
206 |
+
__( '<b>Caution!</b> This setting may reveal errors (from other plugins) in other places on your site too, therefor this is not recommended to leave it enabled on live sites.', 'woocommerce-pdf-invoices-packing-slips' ) . ' ' .
|
207 |
+
__( 'You can also add <code>&debug=true</code> to the URL to apply this on a per-order basis.', 'woocommerce-pdf-invoices-packing-slips' ),
|
208 |
+
)
|
209 |
+
),
|
210 |
+
array(
|
211 |
+
'type' => 'setting',
|
212 |
+
'id' => 'enable_cleanup',
|
213 |
+
'title' => __( 'Enable automatic cleanup', 'woocommerce-pdf-invoices-packing-slips' ),
|
214 |
+
'callback' => 'checkbox_text_input',
|
215 |
+
'section' => 'debug_settings',
|
216 |
+
'args' => array(
|
217 |
+
'option_name' => $option_name,
|
218 |
+
'id' => 'enable_cleanup',
|
219 |
+
'disabled' => ( !function_exists("glob") || !function_exists('filemtime') ) ? 1 : NULL,
|
220 |
+
'text_input_wrap' => __( "every %s days", 'woocommerce-pdf-invoices-packing-slips' ),
|
221 |
+
'text_input_size' => 4,
|
222 |
+
'text_input_id' => 'cleanup_days',
|
223 |
+
'text_input_default'=> 7,
|
224 |
+
'description' => ( function_exists("glob") && function_exists('filemtime') ) ?
|
225 |
+
__( "Automatically clean up PDF files stored in the temporary folder (used for email attachments)", 'woocommerce-pdf-invoices-packing-slips' ) :
|
226 |
+
__( '<b>Disabled:</b> The PHP functions glob and filemtime are required for automatic cleanup but not enabled on your server.', 'woocommerce-pdf-invoices-packing-slips' ),
|
227 |
+
)
|
228 |
+
),
|
229 |
+
array(
|
230 |
+
'type' => 'setting',
|
231 |
+
'id' => 'html_output',
|
232 |
+
'title' => __( 'Output to HTML', 'woocommerce-pdf-invoices-packing-slips' ),
|
233 |
+
'callback' => 'checkbox',
|
234 |
+
'section' => 'debug_settings',
|
235 |
+
'args' => array(
|
236 |
+
'option_name' => $option_name,
|
237 |
+
'id' => 'html_output',
|
238 |
+
'description' => __( 'Send the template output as HTML to the browser instead of creating a PDF.', 'woocommerce-pdf-invoices-packing-slips' ) . ' ' .
|
239 |
+
__( 'You can also add <code>&output=html</code> to the URL to apply this on a per-order basis.', 'woocommerce-pdf-invoices-packing-slips' ),
|
240 |
+
)
|
241 |
+
),
|
242 |
+
array(
|
243 |
+
'type' => 'setting',
|
244 |
+
'id' => 'use_html5_parser',
|
245 |
+
'title' => __( 'Use alternative HTML5 parser to parse HTML', 'woocommerce-pdf-invoices-packing-slips' ),
|
246 |
+
'callback' => 'checkbox',
|
247 |
+
'section' => 'debug_settings',
|
248 |
+
'args' => array(
|
249 |
+
'option_name' => $option_name,
|
250 |
+
'id' => 'use_html5_parser',
|
251 |
+
)
|
252 |
+
),
|
253 |
+
);
|
254 |
+
|
255 |
+
// allow plugins to alter settings fields
|
256 |
+
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_debug', $settings_fields, $page, $option_group, $option_name );
|
257 |
+
WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
|
258 |
+
return;
|
259 |
+
}
|
260 |
+
|
261 |
+
}
|
262 |
+
|
263 |
+
endif; // class_exists
|
264 |
+
|
265 |
return new Settings_Debug();
|
includes/documents/abstract-wcpdf-order-document.php
CHANGED
@@ -320,7 +320,7 @@ abstract class Order_Document {
|
|
320 |
$refund_id = $order->get_id();
|
321 |
$parent_order = wc_get_order( $order->get_parent_id() );
|
322 |
}
|
323 |
-
$note = $refund_id ? sprintf( __( '%s (refund #%s) was regenerated.', '
|
324 |
$parent_order ? $parent_order->add_order_note( $note ) : $order->add_order_note( $note );
|
325 |
|
326 |
do_action( 'wpo_wcpdf_regenerate_document', $this );
|
320 |
$refund_id = $order->get_id();
|
321 |
$parent_order = wc_get_order( $order->get_parent_id() );
|
322 |
}
|
323 |
+
$note = $refund_id ? sprintf( __( '%s (refund #%s) was regenerated.', 'woocommerce-pdf-invoices-packing-slips' ), ucfirst( $this->get_title() ), $refund_id ) : sprintf( __( '%s was regenerated', 'woocommerce-pdf-invoices-packing-slips' ), ucfirst( $this->get_title() ) );
|
324 |
$parent_order ? $parent_order->add_order_note( $note ) : $order->add_order_note( $note );
|
325 |
|
326 |
do_action( 'wpo_wcpdf_regenerate_document', $this );
|
includes/views/wcpdf-extensions.php
CHANGED
@@ -1,132 +1,132 @@
|
|
1 |
-
<?php defined( 'ABSPATH' ) or exit; ?>
|
2 |
-
<script type="text/javascript">
|
3 |
-
jQuery(document).ready(function() {
|
4 |
-
jQuery('.extensions .more').hide();
|
5 |
-
|
6 |
-
jQuery('.extensions > li').click(function() {
|
7 |
-
jQuery(this).toggleClass('expanded');
|
8 |
-
jQuery(this).find('.more').slideToggle();
|
9 |
-
});
|
10 |
-
});
|
11 |
-
</script>
|
12 |
-
|
13 |
-
<div class="wcpdf-extensions-ad">
|
14 |
-
<?php $no_pro = !class_exists('WooCommerce_PDF_IPS_Pro') && !class_exists('WooCommerce_PDF_IPS_Dropbox') && !class_exists('
|
15 |
-
<img src="<?php echo WPO_WCPDF()->plugin_url() . '/assets/images/wpo-helper.png'; ?>" class="wpo-helper">
|
16 |
-
<h3><?php _e( 'Check out these premium extensions!', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
17 |
-
<i>(<?php _e( 'click items to read more', 'woocommerce-pdf-invoices-packing-slips' ); ?>)</i>
|
18 |
-
<ul class="extensions">
|
19 |
-
<?php if ( $no_pro ): ?>
|
20 |
-
<!-- No Pro extensions: Ad for PDF bundle -->
|
21 |
-
<li>
|
22 |
-
<?php _e('Premium PDF Invoice bundle: Everything you need for a perfect invoicing system', 'woocommerce-pdf-invoices-packing-slips' )?>
|
23 |
-
<div class="more" style="display:none;">
|
24 |
-
<h4><?php _e( 'Supercharge WooCommerce PDF Invoices & Packing Slips with the all our premium extensions:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h4>
|
25 |
-
<?php _e( 'Professional features:', 'woocommerce-pdf-invoices-packing-slips' ); ?>
|
26 |
-
<ul>
|
27 |
-
<li><?php _e( 'Email/print/download <b>PDF Credit Notes & Proforma invoices</b>', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
28 |
-
<li><?php _e( 'Send out a separate <b>notification email</b> with (or without) PDF invoices/packing slips, for example to a drop-shipper or a supplier.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
29 |
-
<li><?php _e( 'Attach <b>up to 3 static files</b> (for example a terms & conditions document) to the WooCommerce emails of your choice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
30 |
-
<li><?php _e( 'Use <b>separate numbering systems</b> and/or format for proforma invoices and credit notes or utilize the main invoice numbering system', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
31 |
-
<li><?php _e( '<b>Customize</b> the <b>shipping & billing address</b> format to include additional custom fields, font sizes etc. without the need to create a custom template.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
32 |
-
<li><?php _e( 'Use the plugin in multilingual <b>WPML</b> setups', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
33 |
-
</ul>
|
34 |
-
<?php _e('Advanced, customizable templates', 'woocommerce-pdf-invoices-packing-slips' )?>
|
35 |
-
<ul>
|
36 |
-
<li><?php _e( 'Completely customize the invoice contents (prices, taxes, thumbnails) to your needs with a drag & drop customizer', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
37 |
-
<li><?php _e( 'Two extra stylish premade templates (Modern & Business)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
38 |
-
</ul>
|
39 |
-
<?php _e('Upload automatically to dropbox', 'woocommerce-pdf-invoices-packing-slips' )?>
|
40 |
-
<ul>
|
41 |
-
<li><?php _e( 'This extension conveniently uploads all the invoices (and other pdf documents from the professional extension) that are emailed to your customers to Dropbox. The best way to keep your invoice administration up to date!', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
42 |
-
</ul>
|
43 |
-
<br>
|
44 |
-
<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/" target="_blank"><?php _e("Get WooCommerce PDF Invoices & Packing Slips Bundle", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
45 |
-
</div>
|
46 |
-
</li>
|
47 |
-
<?php endif; ?>
|
48 |
-
<?php
|
49 |
-
// NO BUNDLE: separate ads
|
50 |
-
if (!class_exists('WooCommerce_PDF_IPS_Pro') && !$no_pro) {
|
51 |
-
?>
|
52 |
-
<li>
|
53 |
-
<?php _e('Go Pro: Proforma invoices, credit notes (=refunds) & more!', 'woocommerce-pdf-invoices-packing-slips' )?>
|
54 |
-
<div class="more" style="display:none;">
|
55 |
-
<?php _e( 'Supercharge WooCommerce PDF Invoices & Packing Slips with the following features:', 'woocommerce-pdf-invoices-packing-slips' ); ?>
|
56 |
-
<ul>
|
57 |
-
<li><?php _e( 'Email/print/download <b>PDF Credit Notes & Proforma invoices</b>', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
58 |
-
<li><?php _e( 'Send out a separate <b>notification email</b> with (or without) PDF invoices/packing slips, for example to a drop-shipper or a supplier.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
59 |
-
<li><?php _e( 'Attach <b>up to 3 static files</b> (for example a terms & conditions document) to the WooCommerce emails of your choice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
60 |
-
<li><?php _e( 'Use <b>separate numbering systems</b> and/or format for proforma invoices and credit notes or utilize the main invoice numbering system', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
61 |
-
<li><?php _e( '<b>Customize</b> the <b>shipping & billing address</b> format to include additional custom fields, font sizes etc. without the need to create a custom template.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
62 |
-
<li><?php _e( 'Use the plugin in multilingual <b>WPML</b> setups', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
63 |
-
<li><?php _e( 'Upload automatically to dropbox', 'woocommerce-pdf-invoices-packing-slips' )?></li>
|
64 |
-
</ul>
|
65 |
-
<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/" target="_blank"><?php _e("Get WooCommerce PDF Invoices & Packing Slips Professional!", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
66 |
-
</li>
|
67 |
-
<?php } ?>
|
68 |
-
|
69 |
-
<?php
|
70 |
-
if (!class_exists('WPO_WC_Smart_Reminder_Emails')) {
|
71 |
-
?>
|
72 |
-
<li>
|
73 |
-
<?php _e('Automatically send payment reminders to your customers', 'woocommerce-pdf-invoices-packing-slips' )?>
|
74 |
-
<div class="more" style="display:none;">
|
75 |
-
<?php _e('WooCommerce Smart Reminder emails', 'woocommerce-pdf-invoices-packing-slips' )?>
|
76 |
-
<ul>
|
77 |
-
<li><?php _e( '<b>Completely automatic</b> scheduled emails', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
78 |
-
<li><?php _e( '<b>Rich text editor</b> for the email text, including placeholders for data from the order (name, order total, etc)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
79 |
-
<li><?php _e( 'Configure the exact requirements for sending an email (time after order, order status, payment method)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
80 |
-
<li><?php _e( 'Fully <b>WPML Compatible</b> – emails will be automatically sent in the order language.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
81 |
-
<li><?php _e( '<b>Super versatile!</b> Can be used for any kind of reminder email (review reminders, repeat purchases)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
82 |
-
<li><b><?php _e( 'Integrates seamlessly with the PDF Invoices & Packing Slips plugin', 'woocommerce-pdf-invoices-packing-slips' ); ?></b></li>
|
83 |
-
</ul>
|
84 |
-
<a href="https://wpovernight.com/downloads/woocommerce-reminder-emails-payment-reminders/" target="_blank"><?php _e("Get WooCommerce Smart Reminder Emails", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
85 |
-
</div>
|
86 |
-
</li>
|
87 |
-
<?php } ?>
|
88 |
-
|
89 |
-
<?php
|
90 |
-
if (!class_exists('WooCommerce_Ext_PrintOrders')) {
|
91 |
-
?>
|
92 |
-
<li>
|
93 |
-
<?php _e('Automatically send new orders or packing slips to your printer, as soon as the customer orders!', 'woocommerce-pdf-invoices-packing-slips' )?>
|
94 |
-
<div class="more" style="display:none;">
|
95 |
-
<table>
|
96 |
-
<tr>
|
97 |
-
<td><img src="<?php echo WPO_WCPDF()->plugin_url() . '/assets/images/cloud-print.png'; ?>" class="cloud-logo"></td>
|
98 |
-
<td>
|
99 |
-
<?php _e( 'Check out the WooCommerce Automatic Order Printing extension from our partners at Simba Hosting', 'woocommerce-pdf-invoices-packing-slips' ); ?><br/>
|
100 |
-
<a href="https://www.simbahosting.co.uk/s3/product/woocommerce-automatic-order-printing/?affiliates=2" target="_blank"><?php _e("WooCommerce Automatic Order Printing", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
101 |
-
</td>
|
102 |
-
</tr>
|
103 |
-
</table>
|
104 |
-
</div>
|
105 |
-
</li>
|
106 |
-
<?php } ?>
|
107 |
-
|
108 |
-
<?php
|
109 |
-
if (!class_exists('WooCommerce_PDF_IPS_Templates') && !class_exists('WPO_WCPDF_Templates') && !$no_pro) {
|
110 |
-
$template_link = '<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/" target="_blank">wpovernight.com</a>';
|
111 |
-
$email_link = '<a href="mailto:support@wpovernight.com">support@wpovernight.com</a>'
|
112 |
-
?>
|
113 |
-
<li>
|
114 |
-
<?php _e('Advanced, customizable templates', 'woocommerce-pdf-invoices-packing-slips' )?>
|
115 |
-
<div class="more" style="display:none;">
|
116 |
-
<ul>
|
117 |
-
<li><?php _e( 'Completely customize the invoice contents (prices, taxes, thumbnails) to your needs with a drag & drop customizer', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
118 |
-
<li><?php _e( 'Two extra stylish premade templates (Modern & Business)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
119 |
-
<li><?php printf( __("Check out the Premium PDF Invoice & Packing Slips templates at %s.", 'woocommerce-pdf-invoices-packing-slips' ), $template_link );?></li>
|
120 |
-
<li><?php printf( __("For custom templates, contact us at %s.", 'woocommerce-pdf-invoices-packing-slips' ), $email_link );?></li>
|
121 |
-
</ul>
|
122 |
-
</div>
|
123 |
-
</li>
|
124 |
-
<?php } ?>
|
125 |
-
</ul>
|
126 |
-
<?php
|
127 |
-
// link to hide message when one of the premium extensions is installed
|
128 |
-
if ( class_exists('WooCommerce_PDF_IPS_Pro') || class_exists('WooCommerce_PDF_IPS_Dropbox') || class_exists('WPO_WCPDF_Templates') || class_exists('WooCommerce_PDF_IPS_Templates') || class_exists('WooCommerce_Ext_PrintOrders') || class_exists('WPO_WC_Smart_Reminder_Emails') ) {
|
129 |
-
printf('<a href="%s" style="display:inline-block; margin-top: 10px;">%s</a>', add_query_arg( 'wpo_wcpdf_hide_extensions_ad', 'true' ), __( 'Hide this message', 'woocommerce-pdf-invoices-packing-slips' ) );
|
130 |
-
}
|
131 |
-
?>
|
132 |
</div>
|
1 |
+
<?php defined( 'ABSPATH' ) or exit; ?>
|
2 |
+
<script type="text/javascript">
|
3 |
+
jQuery(document).ready(function() {
|
4 |
+
jQuery('.extensions .more').hide();
|
5 |
+
|
6 |
+
jQuery('.extensions > li').click(function() {
|
7 |
+
jQuery(this).toggleClass('expanded');
|
8 |
+
jQuery(this).find('.more').slideToggle();
|
9 |
+
});
|
10 |
+
});
|
11 |
+
</script>
|
12 |
+
|
13 |
+
<div class="wcpdf-extensions-ad">
|
14 |
+
<?php $no_pro = !class_exists('WooCommerce_PDF_IPS_Pro') && !class_exists('WooCommerce_PDF_IPS_Dropbox') && !class_exists('WPO_WCPDF_Templates'); ?>
|
15 |
+
<img src="<?php echo WPO_WCPDF()->plugin_url() . '/assets/images/wpo-helper.png'; ?>" class="wpo-helper">
|
16 |
+
<h3><?php _e( 'Check out these premium extensions!', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
17 |
+
<i>(<?php _e( 'click items to read more', 'woocommerce-pdf-invoices-packing-slips' ); ?>)</i>
|
18 |
+
<ul class="extensions">
|
19 |
+
<?php if ( $no_pro ): ?>
|
20 |
+
<!-- No Pro extensions: Ad for PDF bundle -->
|
21 |
+
<li>
|
22 |
+
<?php _e('Premium PDF Invoice bundle: Everything you need for a perfect invoicing system', 'woocommerce-pdf-invoices-packing-slips' )?>
|
23 |
+
<div class="more" style="display:none;">
|
24 |
+
<h4><?php _e( 'Supercharge WooCommerce PDF Invoices & Packing Slips with the all our premium extensions:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h4>
|
25 |
+
<?php _e( 'Professional features:', 'woocommerce-pdf-invoices-packing-slips' ); ?>
|
26 |
+
<ul>
|
27 |
+
<li><?php _e( 'Email/print/download <b>PDF Credit Notes & Proforma invoices</b>', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
28 |
+
<li><?php _e( 'Send out a separate <b>notification email</b> with (or without) PDF invoices/packing slips, for example to a drop-shipper or a supplier.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
29 |
+
<li><?php _e( 'Attach <b>up to 3 static files</b> (for example a terms & conditions document) to the WooCommerce emails of your choice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
30 |
+
<li><?php _e( 'Use <b>separate numbering systems</b> and/or format for proforma invoices and credit notes or utilize the main invoice numbering system', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
31 |
+
<li><?php _e( '<b>Customize</b> the <b>shipping & billing address</b> format to include additional custom fields, font sizes etc. without the need to create a custom template.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
32 |
+
<li><?php _e( 'Use the plugin in multilingual <b>WPML</b> setups', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
33 |
+
</ul>
|
34 |
+
<?php _e('Advanced, customizable templates', 'woocommerce-pdf-invoices-packing-slips' )?>
|
35 |
+
<ul>
|
36 |
+
<li><?php _e( 'Completely customize the invoice contents (prices, taxes, thumbnails) to your needs with a drag & drop customizer', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
37 |
+
<li><?php _e( 'Two extra stylish premade templates (Modern & Business)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
38 |
+
</ul>
|
39 |
+
<?php _e('Upload automatically to dropbox', 'woocommerce-pdf-invoices-packing-slips' )?>
|
40 |
+
<ul>
|
41 |
+
<li><?php _e( 'This extension conveniently uploads all the invoices (and other pdf documents from the professional extension) that are emailed to your customers to Dropbox. The best way to keep your invoice administration up to date!', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
42 |
+
</ul>
|
43 |
+
<br>
|
44 |
+
<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/" target="_blank"><?php _e("Get WooCommerce PDF Invoices & Packing Slips Bundle", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
45 |
+
</div>
|
46 |
+
</li>
|
47 |
+
<?php endif; ?>
|
48 |
+
<?php
|
49 |
+
// NO BUNDLE: separate ads
|
50 |
+
if (!class_exists('WooCommerce_PDF_IPS_Pro') && !$no_pro) {
|
51 |
+
?>
|
52 |
+
<li>
|
53 |
+
<?php _e('Go Pro: Proforma invoices, credit notes (=refunds) & more!', 'woocommerce-pdf-invoices-packing-slips' )?>
|
54 |
+
<div class="more" style="display:none;">
|
55 |
+
<?php _e( 'Supercharge WooCommerce PDF Invoices & Packing Slips with the following features:', 'woocommerce-pdf-invoices-packing-slips' ); ?>
|
56 |
+
<ul>
|
57 |
+
<li><?php _e( 'Email/print/download <b>PDF Credit Notes & Proforma invoices</b>', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
58 |
+
<li><?php _e( 'Send out a separate <b>notification email</b> with (or without) PDF invoices/packing slips, for example to a drop-shipper or a supplier.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
59 |
+
<li><?php _e( 'Attach <b>up to 3 static files</b> (for example a terms & conditions document) to the WooCommerce emails of your choice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
60 |
+
<li><?php _e( 'Use <b>separate numbering systems</b> and/or format for proforma invoices and credit notes or utilize the main invoice numbering system', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
61 |
+
<li><?php _e( '<b>Customize</b> the <b>shipping & billing address</b> format to include additional custom fields, font sizes etc. without the need to create a custom template.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
62 |
+
<li><?php _e( 'Use the plugin in multilingual <b>WPML</b> setups', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
63 |
+
<li><?php _e( 'Upload automatically to dropbox', 'woocommerce-pdf-invoices-packing-slips' )?></li>
|
64 |
+
</ul>
|
65 |
+
<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/" target="_blank"><?php _e("Get WooCommerce PDF Invoices & Packing Slips Professional!", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
66 |
+
</li>
|
67 |
+
<?php } ?>
|
68 |
+
|
69 |
+
<?php
|
70 |
+
if (!class_exists('WPO_WC_Smart_Reminder_Emails')) {
|
71 |
+
?>
|
72 |
+
<li>
|
73 |
+
<?php _e('Automatically send payment reminders to your customers', 'woocommerce-pdf-invoices-packing-slips' )?>
|
74 |
+
<div class="more" style="display:none;">
|
75 |
+
<?php _e('WooCommerce Smart Reminder emails', 'woocommerce-pdf-invoices-packing-slips' )?>
|
76 |
+
<ul>
|
77 |
+
<li><?php _e( '<b>Completely automatic</b> scheduled emails', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
78 |
+
<li><?php _e( '<b>Rich text editor</b> for the email text, including placeholders for data from the order (name, order total, etc)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
79 |
+
<li><?php _e( 'Configure the exact requirements for sending an email (time after order, order status, payment method)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
80 |
+
<li><?php _e( 'Fully <b>WPML Compatible</b> – emails will be automatically sent in the order language.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
81 |
+
<li><?php _e( '<b>Super versatile!</b> Can be used for any kind of reminder email (review reminders, repeat purchases)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
82 |
+
<li><b><?php _e( 'Integrates seamlessly with the PDF Invoices & Packing Slips plugin', 'woocommerce-pdf-invoices-packing-slips' ); ?></b></li>
|
83 |
+
</ul>
|
84 |
+
<a href="https://wpovernight.com/downloads/woocommerce-reminder-emails-payment-reminders/" target="_blank"><?php _e("Get WooCommerce Smart Reminder Emails", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
85 |
+
</div>
|
86 |
+
</li>
|
87 |
+
<?php } ?>
|
88 |
+
|
89 |
+
<?php
|
90 |
+
if (!class_exists('WooCommerce_Ext_PrintOrders')) {
|
91 |
+
?>
|
92 |
+
<li>
|
93 |
+
<?php _e('Automatically send new orders or packing slips to your printer, as soon as the customer orders!', 'woocommerce-pdf-invoices-packing-slips' )?>
|
94 |
+
<div class="more" style="display:none;">
|
95 |
+
<table>
|
96 |
+
<tr>
|
97 |
+
<td><img src="<?php echo WPO_WCPDF()->plugin_url() . '/assets/images/cloud-print.png'; ?>" class="cloud-logo"></td>
|
98 |
+
<td>
|
99 |
+
<?php _e( 'Check out the WooCommerce Automatic Order Printing extension from our partners at Simba Hosting', 'woocommerce-pdf-invoices-packing-slips' ); ?><br/>
|
100 |
+
<a href="https://www.simbahosting.co.uk/s3/product/woocommerce-printnode-automatic-order-printing/?affiliates=2" target="_blank"><?php _e("WooCommerce Automatic Order Printing", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
101 |
+
</td>
|
102 |
+
</tr>
|
103 |
+
</table>
|
104 |
+
</div>
|
105 |
+
</li>
|
106 |
+
<?php } ?>
|
107 |
+
|
108 |
+
<?php
|
109 |
+
if (!class_exists('WooCommerce_PDF_IPS_Templates') && !class_exists('WPO_WCPDF_Templates') && !$no_pro) {
|
110 |
+
$template_link = '<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/" target="_blank">wpovernight.com</a>';
|
111 |
+
$email_link = '<a href="mailto:support@wpovernight.com">support@wpovernight.com</a>'
|
112 |
+
?>
|
113 |
+
<li>
|
114 |
+
<?php _e('Advanced, customizable templates', 'woocommerce-pdf-invoices-packing-slips' )?>
|
115 |
+
<div class="more" style="display:none;">
|
116 |
+
<ul>
|
117 |
+
<li><?php _e( 'Completely customize the invoice contents (prices, taxes, thumbnails) to your needs with a drag & drop customizer', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
118 |
+
<li><?php _e( 'Two extra stylish premade templates (Modern & Business)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
119 |
+
<li><?php printf( __("Check out the Premium PDF Invoice & Packing Slips templates at %s.", 'woocommerce-pdf-invoices-packing-slips' ), $template_link );?></li>
|
120 |
+
<li><?php printf( __("For custom templates, contact us at %s.", 'woocommerce-pdf-invoices-packing-slips' ), $email_link );?></li>
|
121 |
+
</ul>
|
122 |
+
</div>
|
123 |
+
</li>
|
124 |
+
<?php } ?>
|
125 |
+
</ul>
|
126 |
+
<?php
|
127 |
+
// link to hide message when one of the premium extensions is installed
|
128 |
+
if ( class_exists('WooCommerce_PDF_IPS_Pro') || class_exists('WooCommerce_PDF_IPS_Dropbox') || class_exists('WPO_WCPDF_Templates') || class_exists('WooCommerce_PDF_IPS_Templates') || class_exists('WooCommerce_Ext_PrintOrders') || class_exists('WPO_WC_Smart_Reminder_Emails') ) {
|
129 |
+
printf('<a href="%s" style="display:inline-block; margin-top: 10px;">%s</a>', add_query_arg( 'wpo_wcpdf_hide_extensions_ad', 'true' ), __( 'Hide this message', 'woocommerce-pdf-invoices-packing-slips' ) );
|
130 |
+
}
|
131 |
+
?>
|
132 |
</div>
|
languages/woocommerce-pdf-invoices-packing-slips.pot
CHANGED
@@ -1,1057 +1,1202 @@
|
|
1 |
-
#, fuzzy
|
2 |
-
msgid ""
|
3 |
-
msgstr ""
|
4 |
-
"Project-Id-Version: WooCommerce PDF Invoices & Packing Slips\n"
|
5 |
-
"POT-Creation-Date:
|
6 |
-
"PO-Revision-Date: 2015-04-29 08:58+0100\n"
|
7 |
-
"Last-Translator: \n"
|
8 |
-
"Language-Team: WP Overnight <support@wpovernight.com>\n"
|
9 |
-
"Language: en_US\n"
|
10 |
-
"MIME-Version: 1.0\n"
|
11 |
-
"Content-Type: text/plain; charset=UTF-8\n"
|
12 |
-
"Content-Transfer-Encoding: 8bit\n"
|
13 |
-
"X-Generator: Poedit
|
14 |
-
"X-Poedit-Basepath: ..\n"
|
15 |
-
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
16 |
-
"X-Poedit-SourceCharset: UTF-8\n"
|
17 |
-
"X-Poedit-KeywordsList: __;_e;_x;_n:1,2\n"
|
18 |
-
"X-Poedit-SearchPath-0: .\n"
|
19 |
-
|
20 |
-
#: includes/class-wcpdf-admin.php:
|
21 |
-
#, php-format
|
22 |
-
msgid "Wow, you have created more than %d invoices with our plugin!"
|
23 |
-
msgstr ""
|
24 |
-
|
25 |
-
#: includes/class-wcpdf-admin.php:
|
26 |
-
msgid ""
|
27 |
-
"It would mean a lot to us if you would quickly give our plugin a 5-star "
|
28 |
-
"rating. Help us spread the word and boost our motivation!"
|
29 |
-
msgstr ""
|
30 |
-
|
31 |
-
#: includes/class-wcpdf-admin.php:
|
32 |
-
msgid "Yes you deserve it!"
|
33 |
-
msgstr ""
|
34 |
-
|
35 |
-
#: includes/class-wcpdf-admin.php:
|
36 |
-
#: includes/views/attachment-settings-hint.php:
|
37 |
-
#: includes/views/wcpdf-extensions.php:
|
38 |
-
msgid "Hide this message"
|
39 |
-
msgstr ""
|
40 |
-
|
41 |
-
#: includes/class-wcpdf-admin.php:
|
42 |
-
msgid "Already did!"
|
43 |
-
msgstr ""
|
44 |
-
|
45 |
-
#: includes/class-wcpdf-admin.php:
|
46 |
-
msgid "Actually, I have a complaint..."
|
47 |
-
msgstr ""
|
48 |
-
|
49 |
-
#: includes/class-wcpdf-admin.php:
|
50 |
-
msgid "New to WooCommerce PDF Invoices & Packing Slips?"
|
51 |
-
msgstr ""
|
52 |
-
|
53 |
-
#: includes/class-wcpdf-admin.php:
|
54 |
-
msgid "Jumpstart the plugin by following our wizard!"
|
55 |
-
msgstr ""
|
56 |
-
|
57 |
-
#: includes/class-wcpdf-admin.php:
|
58 |
-
msgid "Run the Setup Wizard"
|
59 |
-
msgstr ""
|
60 |
-
|
61 |
-
#: includes/class-wcpdf-admin.php:
|
62 |
-
msgid "I am the wizard"
|
63 |
-
msgstr ""
|
64 |
-
|
65 |
-
#: includes/class-wcpdf-admin.php:
|
66 |
-
#: includes/class-wcpdf-main.php:
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
#:
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
#: includes/class-wcpdf-admin.php:
|
109 |
-
#, php-format
|
110 |
-
msgid "%s email notification manually sent."
|
111 |
-
msgstr ""
|
112 |
-
|
113 |
-
#: includes/class-wcpdf-
|
114 |
-
msgid "
|
115 |
-
msgstr ""
|
116 |
-
|
117 |
-
#: includes/class-wcpdf-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
"
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
msgstr ""
|
167 |
-
|
168 |
-
#: includes/class-wcpdf-settings-
|
169 |
-
msgid "
|
170 |
-
msgstr ""
|
171 |
-
|
172 |
-
#: includes/class-wcpdf-settings-
|
173 |
-
msgid "
|
174 |
-
msgstr ""
|
175 |
-
|
176 |
-
#: includes/class-wcpdf-settings-debug.php:
|
177 |
-
msgid "
|
178 |
-
msgstr ""
|
179 |
-
|
180 |
-
#: includes/class-wcpdf-settings-debug.php:
|
181 |
-
msgid "
|
182 |
-
msgstr ""
|
183 |
-
|
184 |
-
#: includes/class-wcpdf-settings-debug.php:
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
msgid "
|
200 |
-
msgstr ""
|
201 |
-
|
202 |
-
#: includes/class-wcpdf-settings-debug.php:
|
203 |
-
msgid "
|
204 |
-
msgstr ""
|
205 |
-
|
206 |
-
#: includes/class-wcpdf-settings-debug.php:
|
207 |
-
msgid "
|
208 |
-
msgstr ""
|
209 |
-
|
210 |
-
#: includes/class-wcpdf-settings-debug.php:
|
211 |
-
msgid "Legacy
|
212 |
-
msgstr ""
|
213 |
-
|
214 |
-
#: includes/class-wcpdf-settings-debug.php:
|
215 |
-
msgid ""
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
"
|
245 |
-
"
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
msgid "
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
msgstr ""
|
286 |
-
|
287 |
-
#: includes/class-wcpdf-settings-
|
288 |
-
msgid ""
|
289 |
-
"
|
290 |
-
"
|
291 |
-
msgstr ""
|
292 |
-
|
293 |
-
#: includes/class-wcpdf-settings-
|
294 |
-
msgid "
|
295 |
-
msgstr ""
|
296 |
-
|
297 |
-
#: includes/class-wcpdf-settings-
|
298 |
-
msgid "
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
#: includes/class-wcpdf-settings
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
#: includes/class-wcpdf-
|
374 |
-
msgid "
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
msgstr ""
|
404 |
-
|
405 |
-
#: includes/class-wcpdf-settings-general.php:
|
406 |
-
msgid "
|
407 |
-
msgstr ""
|
408 |
-
|
409 |
-
#: includes/class-wcpdf-settings-general.php:
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
#: includes/
|
448 |
-
msgid "
|
449 |
-
msgstr ""
|
450 |
-
|
451 |
-
#: includes/class-wcpdf-
|
452 |
-
msgid "
|
453 |
-
msgstr ""
|
454 |
-
|
455 |
-
#: includes/class-wcpdf-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
msgid "
|
506 |
-
msgstr ""
|
507 |
-
|
508 |
-
#: includes/
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
#: includes/
|
529 |
-
msgid "
|
530 |
-
msgstr ""
|
531 |
-
|
532 |
-
#: includes/
|
533 |
-
msgid "
|
534 |
-
msgstr ""
|
535 |
-
|
536 |
-
#: includes/
|
537 |
-
msgid "
|
538 |
-
msgstr ""
|
539 |
-
|
540 |
-
#: includes/
|
541 |
-
msgid "
|
542 |
-
msgstr ""
|
543 |
-
|
544 |
-
#: includes/
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
#: includes/documents/
|
558 |
-
msgid "
|
559 |
-
msgstr ""
|
560 |
-
|
561 |
-
#: includes/documents/
|
562 |
-
msgid "
|
563 |
-
msgstr ""
|
564 |
-
|
565 |
-
#: includes/documents/
|
566 |
-
#, php-format
|
567 |
-
msgid ""
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
#: includes/documents/
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
"
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
#: includes/documents/class-wcpdf-invoice.php:
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
"
|
629 |
-
"
|
630 |
-
msgstr ""
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
"
|
647 |
-
"
|
648 |
-
msgstr ""
|
649 |
-
|
650 |
-
#: includes/documents/class-wcpdf-invoice.php:
|
651 |
-
msgid "
|
652 |
-
msgstr ""
|
653 |
-
|
654 |
-
#: includes/documents/class-wcpdf-invoice.php:
|
655 |
-
msgid "
|
656 |
-
msgstr ""
|
657 |
-
|
658 |
-
#: includes/documents/class-wcpdf-invoice.php:
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
#: includes/documents/class-wcpdf-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
#: includes/
|
683 |
-
msgid ""
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
#: includes/documents/class-wcpdf-invoice.php:
|
688 |
-
msgid "
|
689 |
-
msgstr ""
|
690 |
-
|
691 |
-
#: includes/documents/class-wcpdf-invoice.php:
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
#: includes/
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
msgstr
|
707 |
-
|
708 |
-
#: includes/documents/class-wcpdf-
|
709 |
-
msgid "
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
"
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
msgstr ""
|
762 |
-
|
763 |
-
#: includes/
|
764 |
-
msgid "
|
765 |
-
msgstr ""
|
766 |
-
|
767 |
-
#: includes/
|
768 |
-
msgid "
|
769 |
-
msgstr ""
|
770 |
-
|
771 |
-
#: includes/
|
772 |
-
msgid "
|
773 |
-
msgstr ""
|
774 |
-
|
775 |
-
#: includes/
|
776 |
-
msgid "
|
777 |
-
msgstr ""
|
778 |
-
|
779 |
-
#: includes/
|
780 |
-
msgid "
|
781 |
-
msgstr ""
|
782 |
-
|
783 |
-
#: includes/
|
784 |
-
msgid ""
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
"
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
msgstr ""
|
817 |
-
|
818 |
-
#: includes/
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
#: includes/
|
834 |
-
msgid ""
|
835 |
-
|
836 |
-
|
837 |
-
msgstr ""
|
838 |
-
|
839 |
-
#: includes/
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
#: includes/
|
854 |
-
msgid "
|
855 |
-
msgstr ""
|
856 |
-
|
857 |
-
#: includes/
|
858 |
-
|
859 |
-
|
860 |
-
"
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
msgstr ""
|
873 |
-
|
874 |
-
#: includes/views/
|
875 |
-
msgid ""
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
"
|
880 |
-
msgstr ""
|
881 |
-
|
882 |
-
#: includes/views/
|
883 |
-
msgid "
|
884 |
-
msgstr ""
|
885 |
-
|
886 |
-
#: includes/views/
|
887 |
-
msgid "
|
888 |
-
msgstr ""
|
889 |
-
|
890 |
-
#: includes/views/
|
891 |
-
msgid ""
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
"
|
928 |
-
msgstr ""
|
929 |
-
|
930 |
-
#: includes/views/
|
931 |
-
msgid ""
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
|
966 |
-
|
967 |
-
|
968 |
-
msgstr ""
|
969 |
-
|
970 |
-
#: includes/views/wcpdf-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
#:
|
979 |
-
msgid "
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
#:
|
999 |
-
msgid "
|
1000 |
-
msgstr ""
|
1001 |
-
|
1002 |
-
#:
|
1003 |
-
|
1004 |
-
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
#:
|
1011 |
-
msgid "
|
1012 |
-
msgstr ""
|
1013 |
-
|
1014 |
-
#:
|
1015 |
-
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
-
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
"
|
1047 |
-
msgstr ""
|
1048 |
-
|
1049 |
-
#:
|
1050 |
-
msgid ""
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#, fuzzy
|
2 |
+
msgid ""
|
3 |
+
msgstr ""
|
4 |
+
"Project-Id-Version: WooCommerce PDF Invoices & Packing Slips\n"
|
5 |
+
"POT-Creation-Date: 2020-06-04 16:08+0200\n"
|
6 |
+
"PO-Revision-Date: 2015-04-29 08:58+0100\n"
|
7 |
+
"Last-Translator: \n"
|
8 |
+
"Language-Team: WP Overnight <support@wpovernight.com>\n"
|
9 |
+
"Language: en_US\n"
|
10 |
+
"MIME-Version: 1.0\n"
|
11 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
12 |
+
"Content-Transfer-Encoding: 8bit\n"
|
13 |
+
"X-Generator: Poedit 2.2.1\n"
|
14 |
+
"X-Poedit-Basepath: ..\n"
|
15 |
+
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
16 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
17 |
+
"X-Poedit-KeywordsList: __;_e;_x;_n:1,2\n"
|
18 |
+
"X-Poedit-SearchPath-0: .\n"
|
19 |
+
|
20 |
+
#: includes/class-wcpdf-admin.php:90
|
21 |
+
#, php-format
|
22 |
+
msgid "Wow, you have created more than %d invoices with our plugin!"
|
23 |
+
msgstr ""
|
24 |
+
|
25 |
+
#: includes/class-wcpdf-admin.php:91
|
26 |
+
msgid ""
|
27 |
+
"It would mean a lot to us if you would quickly give our plugin a 5-star "
|
28 |
+
"rating. Help us spread the word and boost our motivation!"
|
29 |
+
msgstr ""
|
30 |
+
|
31 |
+
#: includes/class-wcpdf-admin.php:93
|
32 |
+
msgid "Yes you deserve it!"
|
33 |
+
msgstr ""
|
34 |
+
|
35 |
+
#: includes/class-wcpdf-admin.php:94
|
36 |
+
#: includes/views/attachment-settings-hint.php:23
|
37 |
+
#: includes/views/wcpdf-extensions.php:129
|
38 |
+
msgid "Hide this message"
|
39 |
+
msgstr ""
|
40 |
+
|
41 |
+
#: includes/class-wcpdf-admin.php:94
|
42 |
+
msgid "Already did!"
|
43 |
+
msgstr ""
|
44 |
+
|
45 |
+
#: includes/class-wcpdf-admin.php:95
|
46 |
+
msgid "Actually, I have a complaint..."
|
47 |
+
msgstr ""
|
48 |
+
|
49 |
+
#: includes/class-wcpdf-admin.php:130
|
50 |
+
msgid "New to WooCommerce PDF Invoices & Packing Slips?"
|
51 |
+
msgstr ""
|
52 |
+
|
53 |
+
#: includes/class-wcpdf-admin.php:130
|
54 |
+
msgid "Jumpstart the plugin by following our wizard!"
|
55 |
+
msgstr ""
|
56 |
+
|
57 |
+
#: includes/class-wcpdf-admin.php:131
|
58 |
+
msgid "Run the Setup Wizard"
|
59 |
+
msgstr ""
|
60 |
+
|
61 |
+
#: includes/class-wcpdf-admin.php:131
|
62 |
+
msgid "I am the wizard"
|
63 |
+
msgstr ""
|
64 |
+
|
65 |
+
#: includes/class-wcpdf-admin.php:219 includes/class-wcpdf-admin.php:391
|
66 |
+
#: includes/class-wcpdf-main.php:700
|
67 |
+
#: includes/documents/class-wcpdf-invoice.php:271
|
68 |
+
msgid "Invoice Number"
|
69 |
+
msgstr ""
|
70 |
+
|
71 |
+
#: includes/class-wcpdf-admin.php:256
|
72 |
+
msgid "Send order email"
|
73 |
+
msgstr ""
|
74 |
+
|
75 |
+
#: includes/class-wcpdf-admin.php:267
|
76 |
+
msgid "Create PDF"
|
77 |
+
msgstr ""
|
78 |
+
|
79 |
+
#: includes/class-wcpdf-admin.php:277
|
80 |
+
msgid "PDF Invoice data"
|
81 |
+
msgstr ""
|
82 |
+
|
83 |
+
#: includes/class-wcpdf-admin.php:317
|
84 |
+
msgid "Send email"
|
85 |
+
msgstr ""
|
86 |
+
|
87 |
+
#: includes/class-wcpdf-admin.php:400 includes/class-wcpdf-admin.php:424
|
88 |
+
#: templates/Simple/invoice.php:61
|
89 |
+
msgid "Invoice Date:"
|
90 |
+
msgstr ""
|
91 |
+
|
92 |
+
#: includes/class-wcpdf-admin.php:409
|
93 |
+
msgid "Set invoice number & date"
|
94 |
+
msgstr ""
|
95 |
+
|
96 |
+
#: includes/class-wcpdf-admin.php:416
|
97 |
+
msgid "Invoice Number (unformatted!)"
|
98 |
+
msgstr ""
|
99 |
+
|
100 |
+
#: includes/class-wcpdf-admin.php:426 includes/class-wcpdf-admin.php:428
|
101 |
+
msgid "h"
|
102 |
+
msgstr ""
|
103 |
+
|
104 |
+
#: includes/class-wcpdf-admin.php:426 includes/class-wcpdf-admin.php:428
|
105 |
+
msgid "m"
|
106 |
+
msgstr ""
|
107 |
+
|
108 |
+
#: includes/class-wcpdf-admin.php:570
|
109 |
+
#, php-format
|
110 |
+
msgid "%s email notification manually sent."
|
111 |
+
msgstr ""
|
112 |
+
|
113 |
+
#: includes/class-wcpdf-admin.php:734
|
114 |
+
msgid "DEBUG output enabled"
|
115 |
+
msgstr ""
|
116 |
+
|
117 |
+
#: includes/class-wcpdf-assets.php:91
|
118 |
+
msgid "Are you sure you want to delete this document? This cannot be undone."
|
119 |
+
msgstr ""
|
120 |
+
|
121 |
+
#: includes/class-wcpdf-assets.php:92
|
122 |
+
msgid ""
|
123 |
+
"Are you sure you want to regenerate this document? This will make the "
|
124 |
+
"document reflect the most current settings (such as footer text, document "
|
125 |
+
"name, etc.) rather than using historical settings."
|
126 |
+
msgstr ""
|
127 |
+
|
128 |
+
#: includes/class-wcpdf-frontend.php:110
|
129 |
+
msgid "Download invoice (PDF)"
|
130 |
+
msgstr ""
|
131 |
+
|
132 |
+
#: includes/class-wcpdf-main.php:241 includes/class-wcpdf-main.php:246
|
133 |
+
#: includes/class-wcpdf-main.php:316
|
134 |
+
msgid "You do not have sufficient permissions to access this page."
|
135 |
+
msgstr ""
|
136 |
+
|
137 |
+
#: includes/class-wcpdf-main.php:255
|
138 |
+
msgid "You haven't selected any orders"
|
139 |
+
msgstr ""
|
140 |
+
|
141 |
+
#: includes/class-wcpdf-main.php:259
|
142 |
+
msgid "Some of the export parameters are missing."
|
143 |
+
msgstr ""
|
144 |
+
|
145 |
+
#: includes/class-wcpdf-main.php:344
|
146 |
+
#, php-format
|
147 |
+
msgid "Document of type '%s' for the selected order(s) could not be generated"
|
148 |
+
msgstr ""
|
149 |
+
|
150 |
+
#: includes/class-wcpdf-main.php:701
|
151 |
+
#: includes/documents/class-wcpdf-invoice.php:255
|
152 |
+
msgid "Invoice Date"
|
153 |
+
msgstr ""
|
154 |
+
|
155 |
+
#: includes/class-wcpdf-settings-callbacks.php:27
|
156 |
+
msgid ""
|
157 |
+
"<b>Warning!</b> The settings below are meant for debugging/development only. "
|
158 |
+
"Do not use them on a live website!"
|
159 |
+
msgstr ""
|
160 |
+
|
161 |
+
#: includes/class-wcpdf-settings-callbacks.php:36
|
162 |
+
msgid ""
|
163 |
+
"These are used for the (optional) footer columns in the <em>Modern "
|
164 |
+
"(Premium)</em> template, but can also be used for other elements in your "
|
165 |
+
"custom template"
|
166 |
+
msgstr ""
|
167 |
+
|
168 |
+
#: includes/class-wcpdf-settings-callbacks.php:378
|
169 |
+
msgid "Image resolution"
|
170 |
+
msgstr ""
|
171 |
+
|
172 |
+
#: includes/class-wcpdf-settings-callbacks.php:405
|
173 |
+
msgid "Save"
|
174 |
+
msgstr ""
|
175 |
+
|
176 |
+
#: includes/class-wcpdf-settings-debug.php:42
|
177 |
+
msgid "Reinstall fonts"
|
178 |
+
msgstr ""
|
179 |
+
|
180 |
+
#: includes/class-wcpdf-settings-debug.php:63
|
181 |
+
msgid "Fonts reinstalled!"
|
182 |
+
msgstr ""
|
183 |
+
|
184 |
+
#: includes/class-wcpdf-settings-debug.php:70
|
185 |
+
msgid "Remove temporary files"
|
186 |
+
msgstr ""
|
187 |
+
|
188 |
+
#: includes/class-wcpdf-settings-debug.php:81
|
189 |
+
msgid "Unable to read temporary folder contents!"
|
190 |
+
msgstr ""
|
191 |
+
|
192 |
+
#: includes/class-wcpdf-settings-debug.php:98
|
193 |
+
#, php-format
|
194 |
+
msgid "Unable to delete %d files! (deleted %d)"
|
195 |
+
msgstr ""
|
196 |
+
|
197 |
+
#: includes/class-wcpdf-settings-debug.php:101
|
198 |
+
#, php-format
|
199 |
+
msgid "Successfully deleted %d files!"
|
200 |
+
msgstr ""
|
201 |
+
|
202 |
+
#: includes/class-wcpdf-settings-debug.php:105
|
203 |
+
msgid "Nothing to delete!"
|
204 |
+
msgstr ""
|
205 |
+
|
206 |
+
#: includes/class-wcpdf-settings-debug.php:114
|
207 |
+
msgid "Delete legacy (1.X) settings"
|
208 |
+
msgstr ""
|
209 |
+
|
210 |
+
#: includes/class-wcpdf-settings-debug.php:130
|
211 |
+
msgid "Legacy settings deleted!"
|
212 |
+
msgstr ""
|
213 |
+
|
214 |
+
#: includes/class-wcpdf-settings-debug.php:157
|
215 |
+
msgid "Debug settings"
|
216 |
+
msgstr ""
|
217 |
+
|
218 |
+
#: includes/class-wcpdf-settings-debug.php:163
|
219 |
+
msgid "Legacy mode"
|
220 |
+
msgstr ""
|
221 |
+
|
222 |
+
#: includes/class-wcpdf-settings-debug.php:169
|
223 |
+
msgid ""
|
224 |
+
"Legacy mode ensures compatibility with templates and filters from previous "
|
225 |
+
"versions."
|
226 |
+
msgstr ""
|
227 |
+
|
228 |
+
#: includes/class-wcpdf-settings-debug.php:175
|
229 |
+
msgid "Allow guest access"
|
230 |
+
msgstr ""
|
231 |
+
|
232 |
+
#: includes/class-wcpdf-settings-debug.php:181
|
233 |
+
msgid ""
|
234 |
+
"Enable this to allow customers that purchase without an account to access "
|
235 |
+
"their PDF with a unique key"
|
236 |
+
msgstr ""
|
237 |
+
|
238 |
+
#: includes/class-wcpdf-settings-debug.php:187
|
239 |
+
msgid "Calculate document numbers (slow)"
|
240 |
+
msgstr ""
|
241 |
+
|
242 |
+
#: includes/class-wcpdf-settings-debug.php:193
|
243 |
+
msgid ""
|
244 |
+
"Document numbers (such as invoice numbers) are generated using "
|
245 |
+
"AUTO_INCREMENT by default. Use this setting if your database auto increments "
|
246 |
+
"with more than 1."
|
247 |
+
msgstr ""
|
248 |
+
|
249 |
+
#: includes/class-wcpdf-settings-debug.php:199
|
250 |
+
msgid "Enable debug output"
|
251 |
+
msgstr ""
|
252 |
+
|
253 |
+
#: includes/class-wcpdf-settings-debug.php:205
|
254 |
+
msgid ""
|
255 |
+
"Enable this option to output plugin errors if you're getting a blank page or "
|
256 |
+
"other PDF generation issues"
|
257 |
+
msgstr ""
|
258 |
+
|
259 |
+
#: includes/class-wcpdf-settings-debug.php:206
|
260 |
+
msgid ""
|
261 |
+
"<b>Caution!</b> This setting may reveal errors (from other plugins) in other "
|
262 |
+
"places on your site too, therefor this is not recommended to leave it "
|
263 |
+
"enabled on live sites."
|
264 |
+
msgstr ""
|
265 |
+
|
266 |
+
#: includes/class-wcpdf-settings-debug.php:207
|
267 |
+
msgid ""
|
268 |
+
"You can also add <code>&debug=true</code> to the URL to apply this on a per-"
|
269 |
+
"order basis."
|
270 |
+
msgstr ""
|
271 |
+
|
272 |
+
#: includes/class-wcpdf-settings-debug.php:213
|
273 |
+
msgid "Enable automatic cleanup"
|
274 |
+
msgstr ""
|
275 |
+
|
276 |
+
#: includes/class-wcpdf-settings-debug.php:220
|
277 |
+
#, php-format
|
278 |
+
msgid "every %s days"
|
279 |
+
msgstr ""
|
280 |
+
|
281 |
+
#: includes/class-wcpdf-settings-debug.php:225
|
282 |
+
msgid ""
|
283 |
+
"Automatically clean up PDF files stored in the temporary folder (used for "
|
284 |
+
"email attachments)"
|
285 |
+
msgstr ""
|
286 |
+
|
287 |
+
#: includes/class-wcpdf-settings-debug.php:226
|
288 |
+
msgid ""
|
289 |
+
"<b>Disabled:</b> The PHP functions glob and filemtime are required for "
|
290 |
+
"automatic cleanup but not enabled on your server."
|
291 |
+
msgstr ""
|
292 |
+
|
293 |
+
#: includes/class-wcpdf-settings-debug.php:232
|
294 |
+
msgid "Output to HTML"
|
295 |
+
msgstr ""
|
296 |
+
|
297 |
+
#: includes/class-wcpdf-settings-debug.php:238
|
298 |
+
msgid ""
|
299 |
+
"Send the template output as HTML to the browser instead of creating a PDF."
|
300 |
+
msgstr ""
|
301 |
+
|
302 |
+
#: includes/class-wcpdf-settings-debug.php:239
|
303 |
+
msgid ""
|
304 |
+
"You can also add <code>&output=html</code> to the URL to apply this on a per-"
|
305 |
+
"order basis."
|
306 |
+
msgstr ""
|
307 |
+
|
308 |
+
#: includes/class-wcpdf-settings-debug.php:245
|
309 |
+
msgid "Use alternative HTML5 parser to parse HTML"
|
310 |
+
msgstr ""
|
311 |
+
|
312 |
+
#: includes/class-wcpdf-settings-documents.php:30
|
313 |
+
#: includes/class-wcpdf-settings.php:96
|
314 |
+
msgid "Documents"
|
315 |
+
msgstr ""
|
316 |
+
|
317 |
+
#: includes/class-wcpdf-settings-documents.php:36
|
318 |
+
#: includes/class-wcpdf-settings-documents.php:57
|
319 |
+
msgid "untitled"
|
320 |
+
msgstr ""
|
321 |
+
|
322 |
+
#: includes/class-wcpdf-settings-documents.php:50
|
323 |
+
msgid ""
|
324 |
+
"All available documents are listed below. Click on a document to configure "
|
325 |
+
"it."
|
326 |
+
msgstr ""
|
327 |
+
|
328 |
+
#: includes/class-wcpdf-settings-general.php:40
|
329 |
+
msgid "General settings"
|
330 |
+
msgstr ""
|
331 |
+
|
332 |
+
#: includes/class-wcpdf-settings-general.php:46
|
333 |
+
msgid "How do you want to view the PDF?"
|
334 |
+
msgstr ""
|
335 |
+
|
336 |
+
#: includes/class-wcpdf-settings-general.php:53
|
337 |
+
msgid "Download the PDF"
|
338 |
+
msgstr ""
|
339 |
+
|
340 |
+
#: includes/class-wcpdf-settings-general.php:54
|
341 |
+
msgid "Open the PDF in a new browser tab/window"
|
342 |
+
msgstr ""
|
343 |
+
|
344 |
+
#: includes/class-wcpdf-settings-general.php:61
|
345 |
+
msgid "Choose a template"
|
346 |
+
msgstr ""
|
347 |
+
|
348 |
+
#: includes/class-wcpdf-settings-general.php:68
|
349 |
+
#, php-format
|
350 |
+
msgid ""
|
351 |
+
"Want to use your own template? Copy all the files from <code>%s</code> to "
|
352 |
+
"your (child) theme in <code>%s</code> to customize them"
|
353 |
+
msgstr ""
|
354 |
+
|
355 |
+
#: includes/class-wcpdf-settings-general.php:74
|
356 |
+
msgid "Paper size"
|
357 |
+
msgstr ""
|
358 |
+
|
359 |
+
#: includes/class-wcpdf-settings-general.php:81
|
360 |
+
#: includes/views/setup-wizard/paper-format.php:12
|
361 |
+
msgid "A4"
|
362 |
+
msgstr ""
|
363 |
+
|
364 |
+
#: includes/class-wcpdf-settings-general.php:82
|
365 |
+
#: includes/views/setup-wizard/paper-format.php:13
|
366 |
+
msgid "Letter"
|
367 |
+
msgstr ""
|
368 |
+
|
369 |
+
#: includes/class-wcpdf-settings-general.php:89
|
370 |
+
msgid "Test mode"
|
371 |
+
msgstr ""
|
372 |
+
|
373 |
+
#: includes/class-wcpdf-settings-general.php:95
|
374 |
+
msgid ""
|
375 |
+
"With test mode enabled, any document generated will always use the latest "
|
376 |
+
"settings, rather than using the settings as configured at the time the "
|
377 |
+
"document was first created."
|
378 |
+
msgstr ""
|
379 |
+
|
380 |
+
#: includes/class-wcpdf-settings-general.php:95
|
381 |
+
msgid ""
|
382 |
+
"<strong>Note:</strong> invoice numbers and dates are not affected by this "
|
383 |
+
"setting and will still be generated."
|
384 |
+
msgstr ""
|
385 |
+
|
386 |
+
#: includes/class-wcpdf-settings-general.php:101
|
387 |
+
msgid "Extended currency symbol support"
|
388 |
+
msgstr ""
|
389 |
+
|
390 |
+
#: includes/class-wcpdf-settings-general.php:107
|
391 |
+
msgid "Enable this if your currency symbol is not displaying properly"
|
392 |
+
msgstr ""
|
393 |
+
|
394 |
+
#: includes/class-wcpdf-settings-general.php:113
|
395 |
+
msgid "Enable font subsetting"
|
396 |
+
msgstr ""
|
397 |
+
|
398 |
+
#: includes/class-wcpdf-settings-general.php:119
|
399 |
+
msgid ""
|
400 |
+
"Font subsetting can reduce file size by only including the characters that "
|
401 |
+
"are used in the PDF, but limits the ability to edit PDF files later. "
|
402 |
+
"Recommended if you're using an Asian font."
|
403 |
+
msgstr ""
|
404 |
+
|
405 |
+
#: includes/class-wcpdf-settings-general.php:125
|
406 |
+
msgid "Shop header/logo"
|
407 |
+
msgstr ""
|
408 |
+
|
409 |
+
#: includes/class-wcpdf-settings-general.php:131
|
410 |
+
#: includes/views/setup-wizard/logo.php:20
|
411 |
+
msgid "Select or upload your invoice header/logo"
|
412 |
+
msgstr ""
|
413 |
+
|
414 |
+
#: includes/class-wcpdf-settings-general.php:132
|
415 |
+
#: includes/views/setup-wizard/logo.php:20
|
416 |
+
msgid "Set image"
|
417 |
+
msgstr ""
|
418 |
+
|
419 |
+
#: includes/class-wcpdf-settings-general.php:133
|
420 |
+
#: includes/views/setup-wizard/logo.php:20
|
421 |
+
msgid "Remove image"
|
422 |
+
msgstr ""
|
423 |
+
|
424 |
+
#: includes/class-wcpdf-settings-general.php:140
|
425 |
+
msgid "Logo height"
|
426 |
+
msgstr ""
|
427 |
+
|
428 |
+
#: includes/class-wcpdf-settings-general.php:148
|
429 |
+
msgid ""
|
430 |
+
"Enter the total height of the logo in mm, cm or in and use a dot for "
|
431 |
+
"decimals.<br/>For example: 1.15in or 40mm"
|
432 |
+
msgstr ""
|
433 |
+
|
434 |
+
#: includes/class-wcpdf-settings-general.php:154
|
435 |
+
#: includes/class-wcpdf-setup-wizard.php:42
|
436 |
+
msgid "Shop Name"
|
437 |
+
msgstr ""
|
438 |
+
|
439 |
+
#: includes/class-wcpdf-settings-general.php:167
|
440 |
+
msgid "Shop Address"
|
441 |
+
msgstr ""
|
442 |
+
|
443 |
+
#: includes/class-wcpdf-settings-general.php:182
|
444 |
+
msgid "Footer: terms & conditions, policies, etc."
|
445 |
+
msgstr ""
|
446 |
+
|
447 |
+
#: includes/class-wcpdf-settings-general.php:197
|
448 |
+
msgid "Extra template fields"
|
449 |
+
msgstr ""
|
450 |
+
|
451 |
+
#: includes/class-wcpdf-settings-general.php:203
|
452 |
+
msgid "Extra field 1"
|
453 |
+
msgstr ""
|
454 |
+
|
455 |
+
#: includes/class-wcpdf-settings-general.php:211
|
456 |
+
msgid "This is footer column 1 in the <i>Modern (Premium)</i> template"
|
457 |
+
msgstr ""
|
458 |
+
|
459 |
+
#: includes/class-wcpdf-settings-general.php:218
|
460 |
+
msgid "Extra field 2"
|
461 |
+
msgstr ""
|
462 |
+
|
463 |
+
#: includes/class-wcpdf-settings-general.php:226
|
464 |
+
msgid "This is footer column 2 in the <i>Modern (Premium)</i> template"
|
465 |
+
msgstr ""
|
466 |
+
|
467 |
+
#: includes/class-wcpdf-settings-general.php:233
|
468 |
+
msgid "Extra field 3"
|
469 |
+
msgstr ""
|
470 |
+
|
471 |
+
#: includes/class-wcpdf-settings-general.php:241
|
472 |
+
msgid "This is footer column 3 in the <i>Modern (Premium)</i> template"
|
473 |
+
msgstr ""
|
474 |
+
|
475 |
+
#: includes/class-wcpdf-settings.php:48 includes/class-wcpdf-settings.php:49
|
476 |
+
msgid "PDF Invoices"
|
477 |
+
msgstr ""
|
478 |
+
|
479 |
+
#: includes/class-wcpdf-settings.php:61
|
480 |
+
msgid "Settings"
|
481 |
+
msgstr ""
|
482 |
+
|
483 |
+
#: includes/class-wcpdf-settings.php:74
|
484 |
+
msgid "Documentation"
|
485 |
+
msgstr ""
|
486 |
+
|
487 |
+
#: includes/class-wcpdf-settings.php:75
|
488 |
+
msgid "Support Forum"
|
489 |
+
msgstr ""
|
490 |
+
|
491 |
+
#: includes/class-wcpdf-settings.php:87
|
492 |
+
#, php-format
|
493 |
+
msgid ""
|
494 |
+
"<strong>Warning!</strong> Your database has an AUTO_INCREMENT step size of "
|
495 |
+
"%s, your invoice numbers may not be sequential. Enable the 'Calculate "
|
496 |
+
"document numbers (slow)' setting in the Status tab to use an alternate "
|
497 |
+
"method."
|
498 |
+
msgstr ""
|
499 |
+
|
500 |
+
#: includes/class-wcpdf-settings.php:95
|
501 |
+
msgid "General"
|
502 |
+
msgstr ""
|
503 |
+
|
504 |
+
#: includes/class-wcpdf-settings.php:101
|
505 |
+
msgid "Status"
|
506 |
+
msgstr ""
|
507 |
+
|
508 |
+
#: includes/class-wcpdf-setup-wizard.php:46
|
509 |
+
#: includes/views/setup-wizard/logo.php:3
|
510 |
+
msgid "Your logo"
|
511 |
+
msgstr ""
|
512 |
+
|
513 |
+
#: includes/class-wcpdf-setup-wizard.php:50
|
514 |
+
msgid "Attachments"
|
515 |
+
msgstr ""
|
516 |
+
|
517 |
+
#: includes/class-wcpdf-setup-wizard.php:54
|
518 |
+
#: includes/views/setup-wizard/display-options.php:3
|
519 |
+
msgid "Display options"
|
520 |
+
msgstr ""
|
521 |
+
|
522 |
+
#: includes/class-wcpdf-setup-wizard.php:58
|
523 |
+
#: includes/views/setup-wizard/paper-format.php:3
|
524 |
+
msgid "Paper format"
|
525 |
+
msgstr ""
|
526 |
+
|
527 |
+
#: includes/class-wcpdf-setup-wizard.php:62
|
528 |
+
#: includes/views/setup-wizard/show-action-buttons.php:3
|
529 |
+
msgid "Action buttons"
|
530 |
+
msgstr ""
|
531 |
+
|
532 |
+
#: includes/class-wcpdf-setup-wizard.php:66
|
533 |
+
msgid "Ready!"
|
534 |
+
msgstr ""
|
535 |
+
|
536 |
+
#: includes/class-wcpdf-setup-wizard.php:180
|
537 |
+
msgid "Previous"
|
538 |
+
msgstr ""
|
539 |
+
|
540 |
+
#: includes/class-wcpdf-setup-wizard.php:186
|
541 |
+
msgid "Skip this step"
|
542 |
+
msgstr ""
|
543 |
+
|
544 |
+
#: includes/class-wcpdf-setup-wizard.php:188
|
545 |
+
msgid "Finish"
|
546 |
+
msgstr ""
|
547 |
+
|
548 |
+
#: includes/compatibility/class-wc-core-compatibility.php:222
|
549 |
+
msgid "WooCommerce"
|
550 |
+
msgstr ""
|
551 |
+
|
552 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:116
|
553 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:183
|
554 |
+
msgid "N/A"
|
555 |
+
msgstr ""
|
556 |
+
|
557 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:424
|
558 |
+
msgid "Payment method"
|
559 |
+
msgstr ""
|
560 |
+
|
561 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:445
|
562 |
+
msgid "Shipping method"
|
563 |
+
msgstr ""
|
564 |
+
|
565 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:888
|
566 |
+
#, php-format
|
567 |
+
msgid "(includes %s)"
|
568 |
+
msgstr ""
|
569 |
+
|
570 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:891
|
571 |
+
#, php-format
|
572 |
+
msgid "(Includes %s)"
|
573 |
+
msgstr ""
|
574 |
+
|
575 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:921
|
576 |
+
msgid "Subtotal"
|
577 |
+
msgstr ""
|
578 |
+
|
579 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:946
|
580 |
+
msgid "Shipping"
|
581 |
+
msgstr ""
|
582 |
+
|
583 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:1009
|
584 |
+
msgid "Discount"
|
585 |
+
msgstr ""
|
586 |
+
|
587 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:1050
|
588 |
+
msgid "VAT"
|
589 |
+
msgstr ""
|
590 |
+
|
591 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:1051
|
592 |
+
msgid "Tax rate"
|
593 |
+
msgstr ""
|
594 |
+
|
595 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:1095
|
596 |
+
msgid "Total ex. VAT"
|
597 |
+
msgstr ""
|
598 |
+
|
599 |
+
#: includes/documents/abstract-wcpdf-order-document-methods.php:1098
|
600 |
+
msgid "Total"
|
601 |
+
msgstr ""
|
602 |
+
|
603 |
+
#: includes/documents/abstract-wcpdf-order-document.php:323
|
604 |
+
#, php-format
|
605 |
+
msgid "%s (refund #%s) was regenerated."
|
606 |
+
msgstr ""
|
607 |
+
|
608 |
+
#: includes/documents/abstract-wcpdf-order-document.php:323
|
609 |
+
#, php-format
|
610 |
+
msgid "%s was regenerated"
|
611 |
+
msgstr ""
|
612 |
+
|
613 |
+
#: includes/documents/abstract-wcpdf-order-document.php:837
|
614 |
+
msgid "Admin email"
|
615 |
+
msgstr ""
|
616 |
+
|
617 |
+
#: includes/documents/abstract-wcpdf-order-document.php:840
|
618 |
+
msgid "Manual email"
|
619 |
+
msgstr ""
|
620 |
+
|
621 |
+
#: includes/documents/class-wcpdf-invoice.php:32
|
622 |
+
#: includes/documents/class-wcpdf-invoice.php:56
|
623 |
+
#: includes/legacy/class-wcpdf-legacy-functions.php:22
|
624 |
+
msgid "Invoice"
|
625 |
+
msgstr ""
|
626 |
+
|
627 |
+
#: includes/documents/class-wcpdf-invoice.php:129
|
628 |
+
msgid "invoice"
|
629 |
+
msgid_plural "invoices"
|
630 |
+
msgstr[0] ""
|
631 |
+
msgstr[1] ""
|
632 |
+
|
633 |
+
#: includes/documents/class-wcpdf-invoice.php:174
|
634 |
+
#: includes/documents/class-wcpdf-packing-slip.php:88
|
635 |
+
msgid "Enable"
|
636 |
+
msgstr ""
|
637 |
+
|
638 |
+
#: includes/documents/class-wcpdf-invoice.php:185
|
639 |
+
msgid "Attach to:"
|
640 |
+
msgstr ""
|
641 |
+
|
642 |
+
#: includes/documents/class-wcpdf-invoice.php:192
|
643 |
+
#, php-format
|
644 |
+
msgid ""
|
645 |
+
"It looks like the temp folder (<code>%s</code>) is not writable, check the "
|
646 |
+
"permissions for this folder! Without having write access to this folder, the "
|
647 |
+
"plugin will not be able to email invoices."
|
648 |
+
msgstr ""
|
649 |
+
|
650 |
+
#: includes/documents/class-wcpdf-invoice.php:198
|
651 |
+
msgid "Disable for:"
|
652 |
+
msgstr ""
|
653 |
+
|
654 |
+
#: includes/documents/class-wcpdf-invoice.php:207
|
655 |
+
msgid "Select one or more statuses"
|
656 |
+
msgstr ""
|
657 |
+
|
658 |
+
#: includes/documents/class-wcpdf-invoice.php:213
|
659 |
+
#: includes/views/setup-wizard/display-options.php:17
|
660 |
+
msgid "Display shipping address"
|
661 |
+
msgstr ""
|
662 |
+
|
663 |
+
#: includes/documents/class-wcpdf-invoice.php:219
|
664 |
+
msgid ""
|
665 |
+
"Display shipping address (in addition to the default billing address) if "
|
666 |
+
"different from billing address"
|
667 |
+
msgstr ""
|
668 |
+
|
669 |
+
#: includes/documents/class-wcpdf-invoice.php:225
|
670 |
+
#: includes/documents/class-wcpdf-packing-slip.php:111
|
671 |
+
#: includes/views/setup-wizard/display-options.php:26
|
672 |
+
msgid "Display email address"
|
673 |
+
msgstr ""
|
674 |
+
|
675 |
+
#: includes/documents/class-wcpdf-invoice.php:236
|
676 |
+
#: includes/documents/class-wcpdf-packing-slip.php:122
|
677 |
+
#: includes/views/setup-wizard/display-options.php:35
|
678 |
+
msgid "Display phone number"
|
679 |
+
msgstr ""
|
680 |
+
|
681 |
+
#: includes/documents/class-wcpdf-invoice.php:247
|
682 |
+
#: includes/views/setup-wizard/display-options.php:44
|
683 |
+
msgid "Display invoice date"
|
684 |
+
msgstr ""
|
685 |
+
|
686 |
+
#: includes/documents/class-wcpdf-invoice.php:254
|
687 |
+
#: includes/documents/class-wcpdf-invoice.php:270
|
688 |
+
msgid "No"
|
689 |
+
msgstr ""
|
690 |
+
|
691 |
+
#: includes/documents/class-wcpdf-invoice.php:256
|
692 |
+
msgid "Order Date"
|
693 |
+
msgstr ""
|
694 |
+
|
695 |
+
#: includes/documents/class-wcpdf-invoice.php:263
|
696 |
+
#: includes/views/setup-wizard/display-options.php:53
|
697 |
+
msgid "Display invoice number"
|
698 |
+
msgstr ""
|
699 |
+
|
700 |
+
#: includes/documents/class-wcpdf-invoice.php:272
|
701 |
+
msgid "Order Number"
|
702 |
+
msgstr ""
|
703 |
+
|
704 |
+
#: includes/documents/class-wcpdf-invoice.php:276
|
705 |
+
msgid "Warning!"
|
706 |
+
msgstr ""
|
707 |
+
|
708 |
+
#: includes/documents/class-wcpdf-invoice.php:277
|
709 |
+
msgid ""
|
710 |
+
"Using the Order Number as invoice number is not recommended as this may lead "
|
711 |
+
"to gaps in the invoice number sequence (even when order numbers are "
|
712 |
+
"sequential)."
|
713 |
+
msgstr ""
|
714 |
+
|
715 |
+
#: includes/documents/class-wcpdf-invoice.php:278
|
716 |
+
msgid "More information"
|
717 |
+
msgstr ""
|
718 |
+
|
719 |
+
#: includes/documents/class-wcpdf-invoice.php:285
|
720 |
+
msgid "Next invoice number (without prefix/suffix etc.)"
|
721 |
+
msgstr ""
|
722 |
+
|
723 |
+
#: includes/documents/class-wcpdf-invoice.php:291
|
724 |
+
msgid ""
|
725 |
+
"This is the number that will be used for the next document. By default, "
|
726 |
+
"numbering starts from 1 and increases for every new document. Note that if "
|
727 |
+
"you override this and set it lower than the current/highest number, this "
|
728 |
+
"could create duplicate numbers!"
|
729 |
+
msgstr ""
|
730 |
+
|
731 |
+
#: includes/documents/class-wcpdf-invoice.php:297
|
732 |
+
msgid "Number format"
|
733 |
+
msgstr ""
|
734 |
+
|
735 |
+
#: includes/documents/class-wcpdf-invoice.php:305
|
736 |
+
msgid "Prefix"
|
737 |
+
msgstr ""
|
738 |
+
|
739 |
+
#: includes/documents/class-wcpdf-invoice.php:307
|
740 |
+
msgid ""
|
741 |
+
"to use the invoice year and/or month, use [invoice_year] or [invoice_month] "
|
742 |
+
"respectively"
|
743 |
+
msgstr ""
|
744 |
+
|
745 |
+
#: includes/documents/class-wcpdf-invoice.php:310
|
746 |
+
msgid "Suffix"
|
747 |
+
msgstr ""
|
748 |
+
|
749 |
+
#: includes/documents/class-wcpdf-invoice.php:315
|
750 |
+
msgid "Padding"
|
751 |
+
msgstr ""
|
752 |
+
|
753 |
+
#: includes/documents/class-wcpdf-invoice.php:318
|
754 |
+
msgid "enter the number of digits here - enter \"6\" to display 42 as 000042"
|
755 |
+
msgstr ""
|
756 |
+
|
757 |
+
#: includes/documents/class-wcpdf-invoice.php:321
|
758 |
+
msgid ""
|
759 |
+
"note: if you have already created a custom invoice number format with a "
|
760 |
+
"filter, the above settings will be ignored"
|
761 |
+
msgstr ""
|
762 |
+
|
763 |
+
#: includes/documents/class-wcpdf-invoice.php:327
|
764 |
+
msgid "Reset invoice number yearly"
|
765 |
+
msgstr ""
|
766 |
+
|
767 |
+
#: includes/documents/class-wcpdf-invoice.php:338
|
768 |
+
msgid "Allow My Account invoice download"
|
769 |
+
msgstr ""
|
770 |
+
|
771 |
+
#: includes/documents/class-wcpdf-invoice.php:345
|
772 |
+
msgid "Only when an invoice is already created/emailed"
|
773 |
+
msgstr ""
|
774 |
+
|
775 |
+
#: includes/documents/class-wcpdf-invoice.php:346
|
776 |
+
msgid "Only for specific order statuses (define below)"
|
777 |
+
msgstr ""
|
778 |
+
|
779 |
+
#: includes/documents/class-wcpdf-invoice.php:347
|
780 |
+
msgid "Always"
|
781 |
+
msgstr ""
|
782 |
+
|
783 |
+
#: includes/documents/class-wcpdf-invoice.php:348
|
784 |
+
msgid "Never"
|
785 |
+
msgstr ""
|
786 |
+
|
787 |
+
#: includes/documents/class-wcpdf-invoice.php:363
|
788 |
+
msgid "Enable invoice number column in the orders list"
|
789 |
+
msgstr ""
|
790 |
+
|
791 |
+
#: includes/documents/class-wcpdf-invoice.php:374
|
792 |
+
msgid "Disable for free orders"
|
793 |
+
msgstr ""
|
794 |
+
|
795 |
+
#: includes/documents/class-wcpdf-invoice.php:380
|
796 |
+
#, php-format
|
797 |
+
msgid "Disable automatic creation/attachment when the order total is %s"
|
798 |
+
msgstr ""
|
799 |
+
|
800 |
+
#: includes/documents/class-wcpdf-invoice.php:386
|
801 |
+
msgid "Always use most current settings"
|
802 |
+
msgstr ""
|
803 |
+
|
804 |
+
#: includes/documents/class-wcpdf-invoice.php:392
|
805 |
+
msgid ""
|
806 |
+
"When enabled, the document will always reflect the most current settings "
|
807 |
+
"(such as footer text, document name, etc.) rather than using historical "
|
808 |
+
"settings."
|
809 |
+
msgstr ""
|
810 |
+
|
811 |
+
#: includes/documents/class-wcpdf-invoice.php:394
|
812 |
+
msgid ""
|
813 |
+
"<strong>Caution:</strong> enabling this will also mean that if you change "
|
814 |
+
"your company name or address in the future, previously generated documents "
|
815 |
+
"will also be affected."
|
816 |
+
msgstr ""
|
817 |
+
|
818 |
+
#: includes/documents/class-wcpdf-invoice.php:407
|
819 |
+
msgid "Invoice numbers are created by a third-party extension."
|
820 |
+
msgstr ""
|
821 |
+
|
822 |
+
#: includes/documents/class-wcpdf-invoice.php:409
|
823 |
+
#, php-format
|
824 |
+
msgid "Configure it <a href=\"%s\">here</a>."
|
825 |
+
msgstr ""
|
826 |
+
|
827 |
+
#: includes/documents/class-wcpdf-packing-slip.php:32
|
828 |
+
#: includes/documents/class-wcpdf-packing-slip.php:41
|
829 |
+
#: includes/legacy/class-wcpdf-legacy-functions.php:25
|
830 |
+
msgid "Packing Slip"
|
831 |
+
msgstr ""
|
832 |
+
|
833 |
+
#: includes/documents/class-wcpdf-packing-slip.php:47
|
834 |
+
msgid "packing-slip"
|
835 |
+
msgid_plural "packing-slips"
|
836 |
+
msgstr[0] ""
|
837 |
+
msgstr[1] ""
|
838 |
+
|
839 |
+
#: includes/documents/class-wcpdf-packing-slip.php:99
|
840 |
+
msgid "Display billing address"
|
841 |
+
msgstr ""
|
842 |
+
|
843 |
+
#: includes/documents/class-wcpdf-packing-slip.php:105
|
844 |
+
msgid ""
|
845 |
+
"Display billing address (in addition to the default shipping address) if "
|
846 |
+
"different from shipping address"
|
847 |
+
msgstr ""
|
848 |
+
|
849 |
+
#: includes/legacy/class-wcpdf-legacy-document.php:32
|
850 |
+
msgid "Legacy Document"
|
851 |
+
msgstr ""
|
852 |
+
|
853 |
+
#: includes/legacy/class-wcpdf-legacy.php:72
|
854 |
+
msgid "Error"
|
855 |
+
msgstr ""
|
856 |
+
|
857 |
+
#: includes/legacy/class-wcpdf-legacy.php:73
|
858 |
+
msgid ""
|
859 |
+
"An outdated template or action hook was used to generate the PDF. Legacy "
|
860 |
+
"mode has been activated, please try again by reloading this page."
|
861 |
+
msgstr ""
|
862 |
+
|
863 |
+
#: includes/legacy/class-wcpdf-legacy.php:76
|
864 |
+
msgid "The following function was called"
|
865 |
+
msgstr ""
|
866 |
+
|
867 |
+
#: includes/views/attachment-settings-hint.php:22
|
868 |
+
#, php-format
|
869 |
+
msgid ""
|
870 |
+
"It looks like you haven't setup any email attachments yet, check the "
|
871 |
+
"settings under <b>%sDocuments > Invoice%s</b>"
|
872 |
+
msgstr ""
|
873 |
+
|
874 |
+
#: includes/views/setup-wizard/attach-to.php:3
|
875 |
+
msgid "Attach too..."
|
876 |
+
msgstr ""
|
877 |
+
|
878 |
+
#: includes/views/setup-wizard/attach-to.php:4
|
879 |
+
msgid "Select to which emails you would like to attach your invoice."
|
880 |
+
msgstr ""
|
881 |
+
|
882 |
+
#: includes/views/setup-wizard/display-options.php:4
|
883 |
+
msgid "Select some additional display options for your invoice."
|
884 |
+
msgstr ""
|
885 |
+
|
886 |
+
#: includes/views/setup-wizard/good-to-go.php:3
|
887 |
+
msgid "You are good to go!"
|
888 |
+
msgstr ""
|
889 |
+
|
890 |
+
#: includes/views/setup-wizard/good-to-go.php:4
|
891 |
+
msgid "If you have any questions please have a look at our documentation:"
|
892 |
+
msgstr ""
|
893 |
+
|
894 |
+
#: includes/views/setup-wizard/good-to-go.php:5
|
895 |
+
msgid "Invoices & Packing Slips"
|
896 |
+
msgstr ""
|
897 |
+
|
898 |
+
#: includes/views/setup-wizard/good-to-go.php:6
|
899 |
+
msgid "Happy selling!"
|
900 |
+
msgstr ""
|
901 |
+
|
902 |
+
#: includes/views/setup-wizard/logo.php:4
|
903 |
+
msgid "Set the header image that will display on your invoice."
|
904 |
+
msgstr ""
|
905 |
+
|
906 |
+
#: includes/views/setup-wizard/paper-format.php:4
|
907 |
+
msgid "Select the paper format for your invoice."
|
908 |
+
msgstr ""
|
909 |
+
|
910 |
+
#: includes/views/setup-wizard/shop-name.php:3
|
911 |
+
msgid "Enter your shop name"
|
912 |
+
msgstr ""
|
913 |
+
|
914 |
+
#: includes/views/setup-wizard/shop-name.php:4
|
915 |
+
msgid ""
|
916 |
+
"Lets quickly setup your invoice. Please enter the name and address of your "
|
917 |
+
"shop in the fields on the right."
|
918 |
+
msgstr ""
|
919 |
+
|
920 |
+
#: includes/views/setup-wizard/show-action-buttons.php:4
|
921 |
+
msgid ""
|
922 |
+
"Would you like to display the action buttons in your WooCommerce order list? "
|
923 |
+
"The action buttons allow you to manually create a PDF."
|
924 |
+
msgstr ""
|
925 |
+
|
926 |
+
#: includes/views/setup-wizard/show-action-buttons.php:5
|
927 |
+
msgid "(You can always change this setting later via the Screen Options menu)"
|
928 |
+
msgstr ""
|
929 |
+
|
930 |
+
#: includes/views/setup-wizard/show-action-buttons.php:18
|
931 |
+
msgid "Show action buttons"
|
932 |
+
msgstr ""
|
933 |
+
|
934 |
+
#: includes/views/wcpdf-extensions.php:16
|
935 |
+
msgid "Check out these premium extensions!"
|
936 |
+
msgstr ""
|
937 |
+
|
938 |
+
#: includes/views/wcpdf-extensions.php:17
|
939 |
+
msgid "click items to read more"
|
940 |
+
msgstr ""
|
941 |
+
|
942 |
+
#: includes/views/wcpdf-extensions.php:22
|
943 |
+
msgid ""
|
944 |
+
"Premium PDF Invoice bundle: Everything you need for a perfect invoicing "
|
945 |
+
"system"
|
946 |
+
msgstr ""
|
947 |
+
|
948 |
+
#: includes/views/wcpdf-extensions.php:24
|
949 |
+
msgid ""
|
950 |
+
"Supercharge WooCommerce PDF Invoices & Packing Slips with the all our "
|
951 |
+
"premium extensions:"
|
952 |
+
msgstr ""
|
953 |
+
|
954 |
+
#: includes/views/wcpdf-extensions.php:25
|
955 |
+
msgid "Professional features:"
|
956 |
+
msgstr ""
|
957 |
+
|
958 |
+
#: includes/views/wcpdf-extensions.php:27
|
959 |
+
#: includes/views/wcpdf-extensions.php:57
|
960 |
+
msgid "Email/print/download <b>PDF Credit Notes & Proforma invoices</b>"
|
961 |
+
msgstr ""
|
962 |
+
|
963 |
+
#: includes/views/wcpdf-extensions.php:28
|
964 |
+
#: includes/views/wcpdf-extensions.php:58
|
965 |
+
msgid ""
|
966 |
+
"Send out a separate <b>notification email</b> with (or without) PDF invoices/"
|
967 |
+
"packing slips, for example to a drop-shipper or a supplier."
|
968 |
+
msgstr ""
|
969 |
+
|
970 |
+
#: includes/views/wcpdf-extensions.php:29
|
971 |
+
#: includes/views/wcpdf-extensions.php:59
|
972 |
+
msgid ""
|
973 |
+
"Attach <b>up to 3 static files</b> (for example a terms & conditions "
|
974 |
+
"document) to the WooCommerce emails of your choice."
|
975 |
+
msgstr ""
|
976 |
+
|
977 |
+
#: includes/views/wcpdf-extensions.php:30
|
978 |
+
#: includes/views/wcpdf-extensions.php:60
|
979 |
+
msgid ""
|
980 |
+
"Use <b>separate numbering systems</b> and/or format for proforma invoices "
|
981 |
+
"and credit notes or utilize the main invoice numbering system"
|
982 |
+
msgstr ""
|
983 |
+
|
984 |
+
#: includes/views/wcpdf-extensions.php:31
|
985 |
+
#: includes/views/wcpdf-extensions.php:61
|
986 |
+
msgid ""
|
987 |
+
"<b>Customize</b> the <b>shipping & billing address</b> format to include "
|
988 |
+
"additional custom fields, font sizes etc. without the need to create a "
|
989 |
+
"custom template."
|
990 |
+
msgstr ""
|
991 |
+
|
992 |
+
#: includes/views/wcpdf-extensions.php:32
|
993 |
+
#: includes/views/wcpdf-extensions.php:62
|
994 |
+
msgid "Use the plugin in multilingual <b>WPML</b> setups"
|
995 |
+
msgstr ""
|
996 |
+
|
997 |
+
#: includes/views/wcpdf-extensions.php:34
|
998 |
+
#: includes/views/wcpdf-extensions.php:114
|
999 |
+
msgid "Advanced, customizable templates"
|
1000 |
+
msgstr ""
|
1001 |
+
|
1002 |
+
#: includes/views/wcpdf-extensions.php:36
|
1003 |
+
#: includes/views/wcpdf-extensions.php:117
|
1004 |
+
msgid ""
|
1005 |
+
"Completely customize the invoice contents (prices, taxes, thumbnails) to "
|
1006 |
+
"your needs with a drag & drop customizer"
|
1007 |
+
msgstr ""
|
1008 |
+
|
1009 |
+
#: includes/views/wcpdf-extensions.php:37
|
1010 |
+
#: includes/views/wcpdf-extensions.php:118
|
1011 |
+
msgid "Two extra stylish premade templates (Modern & Business)"
|
1012 |
+
msgstr ""
|
1013 |
+
|
1014 |
+
#: includes/views/wcpdf-extensions.php:39
|
1015 |
+
#: includes/views/wcpdf-extensions.php:63
|
1016 |
+
msgid "Upload automatically to dropbox"
|
1017 |
+
msgstr ""
|
1018 |
+
|
1019 |
+
#: includes/views/wcpdf-extensions.php:41
|
1020 |
+
msgid ""
|
1021 |
+
"This extension conveniently uploads all the invoices (and other pdf "
|
1022 |
+
"documents from the professional extension) that are emailed to your "
|
1023 |
+
"customers to Dropbox. The best way to keep your invoice administration up to "
|
1024 |
+
"date!"
|
1025 |
+
msgstr ""
|
1026 |
+
|
1027 |
+
#: includes/views/wcpdf-extensions.php:44
|
1028 |
+
msgid "Get WooCommerce PDF Invoices & Packing Slips Bundle"
|
1029 |
+
msgstr ""
|
1030 |
+
|
1031 |
+
#: includes/views/wcpdf-extensions.php:53
|
1032 |
+
msgid "Go Pro: Proforma invoices, credit notes (=refunds) & more!"
|
1033 |
+
msgstr ""
|
1034 |
+
|
1035 |
+
#: includes/views/wcpdf-extensions.php:55
|
1036 |
+
msgid ""
|
1037 |
+
"Supercharge WooCommerce PDF Invoices & Packing Slips with the following "
|
1038 |
+
"features:"
|
1039 |
+
msgstr ""
|
1040 |
+
|
1041 |
+
#: includes/views/wcpdf-extensions.php:65
|
1042 |
+
msgid "Get WooCommerce PDF Invoices & Packing Slips Professional!"
|
1043 |
+
msgstr ""
|
1044 |
+
|
1045 |
+
#: includes/views/wcpdf-extensions.php:73
|
1046 |
+
msgid "Automatically send payment reminders to your customers"
|
1047 |
+
msgstr ""
|
1048 |
+
|
1049 |
+
#: includes/views/wcpdf-extensions.php:75
|
1050 |
+
msgid "WooCommerce Smart Reminder emails"
|
1051 |
+
msgstr ""
|
1052 |
+
|
1053 |
+
#: includes/views/wcpdf-extensions.php:77
|
1054 |
+
msgid "<b>Completely automatic</b> scheduled emails"
|
1055 |
+
msgstr ""
|
1056 |
+
|
1057 |
+
#: includes/views/wcpdf-extensions.php:78
|
1058 |
+
msgid ""
|
1059 |
+
"<b>Rich text editor</b> for the email text, including placeholders for data "
|
1060 |
+
"from the order (name, order total, etc)"
|
1061 |
+
msgstr ""
|
1062 |
+
|
1063 |
+
#: includes/views/wcpdf-extensions.php:79
|
1064 |
+
msgid ""
|
1065 |
+
"Configure the exact requirements for sending an email (time after order, "
|
1066 |
+
"order status, payment method)"
|
1067 |
+
msgstr ""
|
1068 |
+
|
1069 |
+
#: includes/views/wcpdf-extensions.php:80
|
1070 |
+
msgid ""
|
1071 |
+
"Fully <b>WPML Compatible</b> – emails will be automatically sent in the "
|
1072 |
+
"order language."
|
1073 |
+
msgstr ""
|
1074 |
+
|
1075 |
+
#: includes/views/wcpdf-extensions.php:81
|
1076 |
+
msgid ""
|
1077 |
+
"<b>Super versatile!</b> Can be used for any kind of reminder email (review "
|
1078 |
+
"reminders, repeat purchases)"
|
1079 |
+
msgstr ""
|
1080 |
+
|
1081 |
+
#: includes/views/wcpdf-extensions.php:82
|
1082 |
+
msgid "Integrates seamlessly with the PDF Invoices & Packing Slips plugin"
|
1083 |
+
msgstr ""
|
1084 |
+
|
1085 |
+
#: includes/views/wcpdf-extensions.php:84
|
1086 |
+
msgid "Get WooCommerce Smart Reminder Emails"
|
1087 |
+
msgstr ""
|
1088 |
+
|
1089 |
+
#: includes/views/wcpdf-extensions.php:93
|
1090 |
+
msgid ""
|
1091 |
+
"Automatically send new orders or packing slips to your printer, as soon as "
|
1092 |
+
"the customer orders!"
|
1093 |
+
msgstr ""
|
1094 |
+
|
1095 |
+
#: includes/views/wcpdf-extensions.php:99
|
1096 |
+
msgid ""
|
1097 |
+
"Check out the WooCommerce Automatic Order Printing extension from our "
|
1098 |
+
"partners at Simba Hosting"
|
1099 |
+
msgstr ""
|
1100 |
+
|
1101 |
+
#: includes/views/wcpdf-extensions.php:100
|
1102 |
+
msgid "WooCommerce Automatic Order Printing"
|
1103 |
+
msgstr ""
|
1104 |
+
|
1105 |
+
#: includes/views/wcpdf-extensions.php:119
|
1106 |
+
#, php-format
|
1107 |
+
msgid "Check out the Premium PDF Invoice & Packing Slips templates at %s."
|
1108 |
+
msgstr ""
|
1109 |
+
|
1110 |
+
#: includes/views/wcpdf-extensions.php:120
|
1111 |
+
#, php-format
|
1112 |
+
msgid "For custom templates, contact us at %s."
|
1113 |
+
msgstr ""
|
1114 |
+
|
1115 |
+
#: includes/views/wcpdf-settings-page.php:9
|
1116 |
+
msgid "WooCommerce PDF Invoices"
|
1117 |
+
msgstr ""
|
1118 |
+
|
1119 |
+
#: includes/wcpdf-functions.php:208
|
1120 |
+
msgid "Error creating PDF, please contact the site owner."
|
1121 |
+
msgstr ""
|
1122 |
+
|
1123 |
+
#: templates/Simple/invoice.php:31 templates/Simple/packing-slip.php:44
|
1124 |
+
msgid "Billing Address:"
|
1125 |
+
msgstr ""
|
1126 |
+
|
1127 |
+
#: templates/Simple/invoice.php:44
|
1128 |
+
msgid "Ship To:"
|
1129 |
+
msgstr ""
|
1130 |
+
|
1131 |
+
#: templates/Simple/invoice.php:55
|
1132 |
+
msgid "Invoice Number:"
|
1133 |
+
msgstr ""
|
1134 |
+
|
1135 |
+
#: templates/Simple/invoice.php:66 templates/Simple/packing-slip.php:54
|
1136 |
+
msgid "Order Number:"
|
1137 |
+
msgstr ""
|
1138 |
+
|
1139 |
+
#: templates/Simple/invoice.php:70 templates/Simple/packing-slip.php:58
|
1140 |
+
msgid "Order Date:"
|
1141 |
+
msgstr ""
|
1142 |
+
|
1143 |
+
#: templates/Simple/invoice.php:74
|
1144 |
+
msgid "Payment Method:"
|
1145 |
+
msgstr ""
|
1146 |
+
|
1147 |
+
#: templates/Simple/invoice.php:88 templates/Simple/packing-slip.php:76
|
1148 |
+
msgid "Product"
|
1149 |
+
msgstr ""
|
1150 |
+
|
1151 |
+
#: templates/Simple/invoice.php:89 templates/Simple/packing-slip.php:77
|
1152 |
+
msgid "Quantity"
|
1153 |
+
msgstr ""
|
1154 |
+
|
1155 |
+
#: templates/Simple/invoice.php:90
|
1156 |
+
msgid "Price"
|
1157 |
+
msgstr ""
|
1158 |
+
|
1159 |
+
#: templates/Simple/invoice.php:97 templates/Simple/packing-slip.php:84
|
1160 |
+
msgid "Description"
|
1161 |
+
msgstr ""
|
1162 |
+
|
1163 |
+
#: templates/Simple/invoice.php:102 templates/Simple/packing-slip.php:89
|
1164 |
+
msgid "SKU"
|
1165 |
+
msgstr ""
|
1166 |
+
|
1167 |
+
#: templates/Simple/invoice.php:103 templates/Simple/packing-slip.php:90
|
1168 |
+
msgid "SKU:"
|
1169 |
+
msgstr ""
|
1170 |
+
|
1171 |
+
#: templates/Simple/invoice.php:104 templates/Simple/packing-slip.php:91
|
1172 |
+
msgid "Weight:"
|
1173 |
+
msgstr ""
|
1174 |
+
|
1175 |
+
#: templates/Simple/invoice.php:119 templates/Simple/packing-slip.php:108
|
1176 |
+
msgid "Customer Notes"
|
1177 |
+
msgstr ""
|
1178 |
+
|
1179 |
+
#: templates/Simple/packing-slip.php:31
|
1180 |
+
msgid "Shipping Address:"
|
1181 |
+
msgstr ""
|
1182 |
+
|
1183 |
+
#: templates/Simple/packing-slip.php:62
|
1184 |
+
msgid "Shipping Method:"
|
1185 |
+
msgstr ""
|
1186 |
+
|
1187 |
+
#: woocommerce-pdf-invoices-packingslips.php:249
|
1188 |
+
#, php-format
|
1189 |
+
msgid ""
|
1190 |
+
"WooCommerce PDF Invoices & Packing Slips requires %sWooCommerce%s to be "
|
1191 |
+
"installed & activated!"
|
1192 |
+
msgstr ""
|
1193 |
+
|
1194 |
+
#: woocommerce-pdf-invoices-packingslips.php:261
|
1195 |
+
msgid ""
|
1196 |
+
"WooCommerce PDF Invoices & Packing Slips requires PHP 5.3 or higher (5.6 or "
|
1197 |
+
"higher recommended)."
|
1198 |
+
msgstr ""
|
1199 |
+
|
1200 |
+
#: woocommerce-pdf-invoices-packingslips.php:262
|
1201 |
+
msgid "How to update your PHP version"
|
1202 |
+
msgstr ""
|
readme.txt
CHANGED
@@ -5,7 +5,7 @@ Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice,
|
|
5 |
Requires at least: 3.5
|
6 |
Tested up to: 5.4
|
7 |
Requires PHP: 5.3
|
8 |
-
Stable tag: 2.5.
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
@@ -102,6 +102,10 @@ There's a setting on the Status tab of the settings page that allows you to togg
|
|
102 |
|
103 |
== Changelog ==
|
104 |
|
|
|
|
|
|
|
|
|
105 |
= 2.5.1 =
|
106 |
* Fix: Correct integration with permalink settings for `[wcpdf_download_invoice]` shortcode
|
107 |
* Fix: Plugin assets versioning
|
5 |
Requires at least: 3.5
|
6 |
Tested up to: 5.4
|
7 |
Requires PHP: 5.3
|
8 |
+
Stable tag: 2.5.2
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
102 |
|
103 |
== Changelog ==
|
104 |
|
105 |
+
= 2.5.2 =
|
106 |
+
* Fix: ImageMagick version conflict
|
107 |
+
* Translations: Updated POT
|
108 |
+
|
109 |
= 2.5.1 =
|
110 |
* Fix: Correct integration with permalink settings for `[wcpdf_download_invoice]` shortcode
|
111 |
* Fix: Plugin assets versioning
|
vendor/dompdf/dompdf/lib/Cpdf.php
CHANGED
@@ -1,5640 +1,5644 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* A PHP class to provide the basic functionality to create a pdf document without
|
4 |
-
* any requirement for additional modules.
|
5 |
-
*
|
6 |
-
* Extended by Orion Richardson to support Unicode / UTF-8 characters using
|
7 |
-
* TCPDF and others as a guide.
|
8 |
-
*
|
9 |
-
* @author Wayne Munro <pdf@ros.co.nz>
|
10 |
-
* @author Orion Richardson <orionr@yahoo.com>
|
11 |
-
* @author Helmut Tischer <htischer@weihenstephan.org>
|
12 |
-
* @author Ryan H. Masten <ryan.masten@gmail.com>
|
13 |
-
* @author Brian Sweeney <eclecticgeek@gmail.com>
|
14 |
-
* @author Fabien Ménager <fabien.menager@gmail.com>
|
15 |
-
* @license Public Domain http://creativecommons.org/licenses/publicdomain/
|
16 |
-
* @package Cpdf
|
17 |
-
*/
|
18 |
-
use FontLib\Font;
|
19 |
-
use FontLib\BinaryStream;
|
20 |
-
|
21 |
-
class Cpdf
|
22 |
-
{
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @var integer The current number of pdf objects in the document
|
26 |
-
*/
|
27 |
-
public $numObj = 0;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* @var array This array contains all of the pdf objects, ready for final assembly
|
31 |
-
*/
|
32 |
-
public $objects = array();
|
33 |
-
|
34 |
-
/**
|
35 |
-
* @var integer The objectId (number within the objects array) of the document catalog
|
36 |
-
*/
|
37 |
-
public $catalogId;
|
38 |
-
|
39 |
-
/**
|
40 |
-
* @var array Array carrying information about the fonts that the system currently knows about
|
41 |
-
* Used to ensure that a font is not loaded twice, among other things
|
42 |
-
*/
|
43 |
-
public $fonts = array();
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @var string The default font metrics file to use if no other font has been loaded.
|
47 |
-
* The path to the directory containing the font metrics should be included
|
48 |
-
*/
|
49 |
-
public $defaultFont = './fonts/Helvetica.afm';
|
50 |
-
|
51 |
-
/**
|
52 |
-
* @string A record of the current font
|
53 |
-
*/
|
54 |
-
public $currentFont = '';
|
55 |
-
|
56 |
-
/**
|
57 |
-
* @var string The current base font
|
58 |
-
*/
|
59 |
-
public $currentBaseFont = '';
|
60 |
-
|
61 |
-
/**
|
62 |
-
* @var integer The number of the current font within the font array
|
63 |
-
*/
|
64 |
-
public $currentFontNum = 0;
|
65 |
-
|
66 |
-
/**
|
67 |
-
* @var integer
|
68 |
-
*/
|
69 |
-
public $currentNode;
|
70 |
-
|
71 |
-
/**
|
72 |
-
* @var integer Object number of the current page
|
73 |
-
*/
|
74 |
-
public $currentPage;
|
75 |
-
|
76 |
-
/**
|
77 |
-
* @var integer Object number of the currently active contents block
|
78 |
-
*/
|
79 |
-
public $currentContents;
|
80 |
-
|
81 |
-
/**
|
82 |
-
* @var integer Number of fonts within the system
|
83 |
-
*/
|
84 |
-
public $numFonts = 0;
|
85 |
-
|
86 |
-
/**
|
87 |
-
* @var integer Number of graphic state resources used
|
88 |
-
*/
|
89 |
-
private $numStates = 0;
|
90 |
-
|
91 |
-
/**
|
92 |
-
* @var array Number of graphic state resources used
|
93 |
-
*/
|
94 |
-
private $gstates = array();
|
95 |
-
|
96 |
-
/**
|
97 |
-
* @var array Current color for fill operations, defaults to inactive value,
|
98 |
-
* all three components should be between 0 and 1 inclusive when active
|
99 |
-
*/
|
100 |
-
public $currentColor = null;
|
101 |
-
|
102 |
-
/**
|
103 |
-
* @var array Current color for stroke operations (lines etc.)
|
104 |
-
*/
|
105 |
-
public $currentStrokeColor = null;
|
106 |
-
|
107 |
-
/**
|
108 |
-
* @var string Fill rule (nonzero or evenodd)
|
109 |
-
*/
|
110 |
-
public $fillRule = "nonzero";
|
111 |
-
|
112 |
-
/**
|
113 |
-
* @var string Current style that lines are drawn in
|
114 |
-
*/
|
115 |
-
public $currentLineStyle = '';
|
116 |
-
|
117 |
-
/**
|
118 |
-
* @var array Current line transparency (partial graphics state)
|
119 |
-
*/
|
120 |
-
public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
121 |
-
|
122 |
-
/**
|
123 |
-
* array Current fill transparency (partial graphics state)
|
124 |
-
*/
|
125 |
-
public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
126 |
-
|
127 |
-
/**
|
128 |
-
* @var array An array which is used to save the state of the document, mainly the colors and styles
|
129 |
-
* it is used to temporarily change to another state, then change back to what it was before
|
130 |
-
*/
|
131 |
-
public $stateStack = array();
|
132 |
-
|
133 |
-
/**
|
134 |
-
* @var integer Number of elements within the state stack
|
135 |
-
*/
|
136 |
-
public $nStateStack = 0;
|
137 |
-
|
138 |
-
/**
|
139 |
-
* @var integer Number of page objects within the document
|
140 |
-
*/
|
141 |
-
public $numPages = 0;
|
142 |
-
|
143 |
-
/**
|
144 |
-
* @var array Object Id storage stack
|
145 |
-
*/
|
146 |
-
public $stack = array();
|
147 |
-
|
148 |
-
/**
|
149 |
-
* @var integer Number of elements within the object Id storage stack
|
150 |
-
*/
|
151 |
-
public $nStack = 0;
|
152 |
-
|
153 |
-
/**
|
154 |
-
* an array which contains information about the objects which are not firmly attached to pages
|
155 |
-
* these have been added with the addObject function
|
156 |
-
*/
|
157 |
-
public $looseObjects = array();
|
158 |
-
|
159 |
-
/**
|
160 |
-
* array contains information about how the loose objects are to be added to the document
|
161 |
-
*/
|
162 |
-
public $addLooseObjects = array();
|
163 |
-
|
164 |
-
/**
|
165 |
-
* @var integer The objectId of the information object for the document
|
166 |
-
* this contains authorship, title etc.
|
167 |
-
*/
|
168 |
-
public $infoObject = 0;
|
169 |
-
|
170 |
-
/**
|
171 |
-
* @var integer Number of images being tracked within the document
|
172 |
-
*/
|
173 |
-
public $numImages = 0;
|
174 |
-
|
175 |
-
/**
|
176 |
-
* @var array An array containing options about the document
|
177 |
-
* it defaults to turning on the compression of the objects
|
178 |
-
*/
|
179 |
-
public $options = array('compression' => true);
|
180 |
-
|
181 |
-
/**
|
182 |
-
* @var integer The objectId of the first page of the document
|
183 |
-
*/
|
184 |
-
public $firstPageId;
|
185 |
-
|
186 |
-
/**
|
187 |
-
* @var integer The object Id of the procset object
|
188 |
-
*/
|
189 |
-
public $procsetObjectId;
|
190 |
-
|
191 |
-
/**
|
192 |
-
* @var array Store the information about the relationship between font families
|
193 |
-
* this used so that the code knows which font is the bold version of another font, etc.
|
194 |
-
* the value of this array is initialised in the constructor function.
|
195 |
-
*/
|
196 |
-
public $fontFamilies = array();
|
197 |
-
|
198 |
-
/**
|
199 |
-
* @var string Folder for php serialized formats of font metrics files.
|
200 |
-
* If empty string, use same folder as original metrics files.
|
201 |
-
* This can be passed in from class creator.
|
202 |
-
* If this folder does not exist or is not writable, Cpdf will be **much** slower.
|
203 |
-
* Because of potential trouble with php safe mode, folder cannot be created at runtime.
|
204 |
-
*/
|
205 |
-
public $fontcache = '';
|
206 |
-
|
207 |
-
/**
|
208 |
-
* @var integer The version of the font metrics cache file.
|
209 |
-
* This value must be manually incremented whenever the internal font data structure is modified.
|
210 |
-
*/
|
211 |
-
public $fontcacheVersion = 6;
|
212 |
-
|
213 |
-
/**
|
214 |
-
* @var string Temporary folder.
|
215 |
-
* If empty string, will attempt system tmp folder.
|
216 |
-
* This can be passed in from class creator.
|
217 |
-
*/
|
218 |
-
public $tmp = '';
|
219 |
-
|
220 |
-
/**
|
221 |
-
* @var string Track if the current font is bolded or italicised
|
222 |
-
*/
|
223 |
-
public $currentTextState = '';
|
224 |
-
|
225 |
-
/**
|
226 |
-
* @var string Messages are stored here during processing, these can be selected afterwards to give some useful debug information
|
227 |
-
*/
|
228 |
-
public $messages = '';
|
229 |
-
|
230 |
-
/**
|
231 |
-
* @var string The encryption array for the document encryption is stored here
|
232 |
-
*/
|
233 |
-
public $arc4 = '';
|
234 |
-
|
235 |
-
/**
|
236 |
-
* @var integer The object Id of the encryption information
|
237 |
-
*/
|
238 |
-
public $arc4_objnum = 0;
|
239 |
-
|
240 |
-
/**
|
241 |
-
* @var string The file identifier, used to uniquely identify a pdf document
|
242 |
-
*/
|
243 |
-
public $fileIdentifier = '';
|
244 |
-
|
245 |
-
/**
|
246 |
-
* @var boolean A flag to say if a document is to be encrypted or not
|
247 |
-
*/
|
248 |
-
public $encrypted = false;
|
249 |
-
|
250 |
-
/**
|
251 |
-
* @var string The encryption key for the encryption of all the document content (structure is not encrypted)
|
252 |
-
*/
|
253 |
-
public $encryptionKey = '';
|
254 |
-
|
255 |
-
/**
|
256 |
-
* @var array Array which forms a stack to keep track of nested callback functions
|
257 |
-
*/
|
258 |
-
public $callback = array();
|
259 |
-
|
260 |
-
/**
|
261 |
-
* @var integer The number of callback functions in the callback array
|
262 |
-
*/
|
263 |
-
public $nCallback = 0;
|
264 |
-
|
265 |
-
/**
|
266 |
-
* @var array Store label->id pairs for named destinations, these will be used to replace internal links
|
267 |
-
* done this way so that destinations can be defined after the location that links to them
|
268 |
-
*/
|
269 |
-
public $destinations = array();
|
270 |
-
|
271 |
-
/**
|
272 |
-
* @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
|
273 |
-
* publiciables within the class, so that the user can rollback at will (from each 'start' command)
|
274 |
-
* note that this includes the objects array, so these can be large.
|
275 |
-
*/
|
276 |
-
public $checkpoint = '';
|
277 |
-
|
278 |
-
/**
|
279 |
-
* @var array Table of Image origin filenames and image labels which were already added with o_image().
|
280 |
-
* Allows to merge identical images
|
281 |
-
*/
|
282 |
-
public $imagelist = array();
|
283 |
-
|
284 |
-
/**
|
285 |
-
* @var boolean Whether the text passed in should be treated as Unicode or just local character set.
|
286 |
-
*/
|
287 |
-
public $isUnicode = false;
|
288 |
-
|
289 |
-
/**
|
290 |
-
* @var string the JavaScript code of the document
|
291 |
-
*/
|
292 |
-
public $javascript = '';
|
293 |
-
|
294 |
-
/**
|
295 |
-
* @var boolean whether the compression is possible
|
296 |
-
*/
|
297 |
-
protected $compressionReady = false;
|
298 |
-
|
299 |
-
/**
|
300 |
-
* @var array Current page size
|
301 |
-
*/
|
302 |
-
protected $currentPageSize = array("width" => 0, "height" => 0);
|
303 |
-
|
304 |
-
/**
|
305 |
-
* @var array All the chars that will be required in the font subsets
|
306 |
-
*/
|
307 |
-
protected $stringSubsets = array();
|
308 |
-
|
309 |
-
/**
|
310 |
-
* @var string The target internal encoding
|
311 |
-
*/
|
312 |
-
static protected $targetEncoding = 'Windows-1252';
|
313 |
-
|
314 |
-
/**
|
315 |
-
* @var array The list of the core fonts
|
316 |
-
*/
|
317 |
-
static protected $coreFonts = array(
|
318 |
-
'courier',
|
319 |
-
'courier-bold',
|
320 |
-
'courier-oblique',
|
321 |
-
'courier-boldoblique',
|
322 |
-
'helvetica',
|
323 |
-
'helvetica-bold',
|
324 |
-
'helvetica-oblique',
|
325 |
-
'helvetica-boldoblique',
|
326 |
-
'times-roman',
|
327 |
-
'times-bold',
|
328 |
-
'times-italic',
|
329 |
-
'times-bolditalic',
|
330 |
-
'symbol',
|
331 |
-
'zapfdingbats'
|
332 |
-
);
|
333 |
-
|
334 |
-
/**
|
335 |
-
* Class constructor
|
336 |
-
* This will start a new document
|
337 |
-
*
|
338 |
-
* @param array $pageSize Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
|
339 |
-
* @param boolean $isUnicode Whether text will be treated as Unicode or not.
|
340 |
-
* @param string $fontcache The font cache folder
|
341 |
-
* @param string $tmp The temporary folder
|
342 |
-
*/
|
343 |
-
function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '')
|
344 |
-
{
|
345 |
-
$this->isUnicode = $isUnicode;
|
346 |
-
$this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\");
|
347 |
-
$this->tmp = ($tmp !== '' ? $tmp : sys_get_temp_dir());
|
348 |
-
$this->newDocument($pageSize);
|
349 |
-
|
350 |
-
$this->compressionReady = function_exists('gzcompress');
|
351 |
-
|
352 |
-
if (in_array('Windows-1252', mb_list_encodings())) {
|
353 |
-
self::$targetEncoding = 'Windows-1252';
|
354 |
-
}
|
355 |
-
|
356 |
-
// also initialize the font families that are known about already
|
357 |
-
$this->setFontFamily('init');
|
358 |
-
}
|
359 |
-
|
360 |
-
/**
|
361 |
-
* Document object methods (internal use only)
|
362 |
-
*
|
363 |
-
* There is about one object method for each type of object in the pdf document
|
364 |
-
* Each function has the same call list ($id,$action,$options).
|
365 |
-
* $id = the object ID of the object, or what it is to be if it is being created
|
366 |
-
* $action = a string specifying the action to be performed, though ALL must support:
|
367 |
-
* 'new' - create the object with the id $id
|
368 |
-
* 'out' - produce the output for the pdf object
|
369 |
-
* $options = optional, a string or array containing the various parameters for the object
|
370 |
-
*
|
371 |
-
* These, in conjunction with the output function are the ONLY way for output to be produced
|
372 |
-
* within the pdf 'file'.
|
373 |
-
*/
|
374 |
-
|
375 |
-
/**
|
376 |
-
* Destination object, used to specify the location for the user to jump to, presently on opening
|
377 |
-
*
|
378 |
-
* @param $id
|
379 |
-
* @param $action
|
380 |
-
* @param string $options
|
381 |
-
* @return string|null
|
382 |
-
*/
|
383 |
-
protected function o_destination($id, $action, $options = '')
|
384 |
-
{
|
385 |
-
switch ($action) {
|
386 |
-
case 'new':
|
387 |
-
$this->objects[$id] = array('t' => 'destination', 'info' => array());
|
388 |
-
$tmp = '';
|
389 |
-
switch ($options['type']) {
|
390 |
-
case 'XYZ':
|
391 |
-
/** @noinspection PhpMissingBreakStatementInspection */
|
392 |
-
case 'FitR':
|
393 |
-
$tmp = ' ' . $options['p3'] . $tmp;
|
394 |
-
case 'FitH':
|
395 |
-
case 'FitV':
|
396 |
-
case 'FitBH':
|
397 |
-
/** @noinspection PhpMissingBreakStatementInspection */
|
398 |
-
case 'FitBV':
|
399 |
-
$tmp = ' ' . $options['p1'] . ' ' . $options['p2'] . $tmp;
|
400 |
-
case 'Fit':
|
401 |
-
case 'FitB':
|
402 |
-
$tmp = $options['type'] . $tmp;
|
403 |
-
$this->objects[$id]['info']['string'] = $tmp;
|
404 |
-
$this->objects[$id]['info']['page'] = $options['page'];
|
405 |
-
}
|
406 |
-
break;
|
407 |
-
|
408 |
-
case 'out':
|
409 |
-
$o = &$this->objects[$id];
|
410 |
-
|
411 |
-
$tmp = $o['info'];
|
412 |
-
$res = "\n$id 0 obj\n" . '[' . $tmp['page'] . ' 0 R /' . $tmp['string'] . "]\nendobj";
|
413 |
-
|
414 |
-
return $res;
|
415 |
-
}
|
416 |
-
|
417 |
-
return null;
|
418 |
-
}
|
419 |
-
|
420 |
-
/**
|
421 |
-
* set the viewer preferences
|
422 |
-
*
|
423 |
-
* @param $id
|
424 |
-
* @param $action
|
425 |
-
* @param string|array $options
|
426 |
-
* @return string|null
|
427 |
-
*/
|
428 |
-
protected function o_viewerPreferences($id, $action, $options = '')
|
429 |
-
{
|
430 |
-
switch ($action) {
|
431 |
-
case 'new':
|
432 |
-
$this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array());
|
433 |
-
break;
|
434 |
-
|
435 |
-
case 'add':
|
436 |
-
$o = &$this->objects[$id];
|
437 |
-
|
438 |
-
foreach ($options as $k => $v) {
|
439 |
-
switch ($k) {
|
440 |
-
// Boolean keys
|
441 |
-
case 'HideToolbar':
|
442 |
-
case 'HideMenubar':
|
443 |
-
case 'HideWindowUI':
|
444 |
-
case 'FitWindow':
|
445 |
-
case 'CenterWindow':
|
446 |
-
case 'DisplayDocTitle':
|
447 |
-
case 'PickTrayByPDFSize':
|
448 |
-
$o['info'][$k] = (bool)$v;
|
449 |
-
break;
|
450 |
-
|
451 |
-
// Integer keys
|
452 |
-
case 'NumCopies':
|
453 |
-
$o['info'][$k] = (int)$v;
|
454 |
-
break;
|
455 |
-
|
456 |
-
// Name keys
|
457 |
-
case 'ViewArea':
|
458 |
-
case 'ViewClip':
|
459 |
-
case 'PrintClip':
|
460 |
-
case 'PrintArea':
|
461 |
-
$o['info'][$k] = (string)$v;
|
462 |
-
break;
|
463 |
-
|
464 |
-
// Named with limited valid values
|
465 |
-
case 'NonFullScreenPageMode':
|
466 |
-
if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) {
|
467 |
-
break;
|
468 |
-
}
|
469 |
-
$o['info'][$k] = $v;
|
470 |
-
break;
|
471 |
-
|
472 |
-
case 'Direction':
|
473 |
-
if (!in_array($v, array('L2R', 'R2L'))) {
|
474 |
-
break;
|
475 |
-
}
|
476 |
-
$o['info'][$k] = $v;
|
477 |
-
break;
|
478 |
-
|
479 |
-
case 'PrintScaling':
|
480 |
-
if (!in_array($v, array('None', 'AppDefault'))) {
|
481 |
-
break;
|
482 |
-
}
|
483 |
-
$o['info'][$k] = $v;
|
484 |
-
break;
|
485 |
-
|
486 |
-
case 'Duplex':
|
487 |
-
if (!in_array($v, array('None', 'AppDefault'))) {
|
488 |
-
break;
|
489 |
-
}
|
490 |
-
$o['info'][$k] = $v;
|
491 |
-
break;
|
492 |
-
|
493 |
-
// Integer array
|
494 |
-
case 'PrintPageRange':
|
495 |
-
// Cast to integer array
|
496 |
-
foreach ($v as $vK => $vV) {
|
497 |
-
$v[$vK] = (int)$vV;
|
498 |
-
}
|
499 |
-
$o['info'][$k] = array_values($v);
|
500 |
-
break;
|
501 |
-
}
|
502 |
-
}
|
503 |
-
break;
|
504 |
-
|
505 |
-
case 'out':
|
506 |
-
$o = &$this->objects[$id];
|
507 |
-
$res = "\n$id 0 obj\n<< ";
|
508 |
-
|
509 |
-
foreach ($o['info'] as $k => $v) {
|
510 |
-
if (is_string($v)) {
|
511 |
-
$v = '/' . $v;
|
512 |
-
} elseif (is_int($v)) {
|
513 |
-
$v = (string) $v;
|
514 |
-
} elseif (is_bool($v)) {
|
515 |
-
$v = ($v ? 'true' : 'false');
|
516 |
-
} elseif (is_array($v)) {
|
517 |
-
$v = '[' . implode(' ', $v) . ']';
|
518 |
-
}
|
519 |
-
$res .= "\n/$k $v";
|
520 |
-
}
|
521 |
-
$res .= "\n>>\n";
|
522 |
-
|
523 |
-
return $res;
|
524 |
-
}
|
525 |
-
|
526 |
-
return null;
|
527 |
-
}
|
528 |
-
|
529 |
-
/**
|
530 |
-
* define the document catalog, the overall controller for the document
|
531 |
-
*
|
532 |
-
* @param $id
|
533 |
-
* @param $action
|
534 |
-
* @param string|array $options
|
535 |
-
* @return string|null
|
536 |
-
*/
|
537 |
-
protected function o_catalog($id, $action, $options = '')
|
538 |
-
{
|
539 |
-
if ($action !== 'new') {
|
540 |
-
$o = &$this->objects[$id];
|
541 |
-
}
|
542 |
-
|
543 |
-
switch ($action) {
|
544 |
-
case 'new':
|
545 |
-
$this->objects[$id] = array('t' => 'catalog', 'info' => array());
|
546 |
-
$this->catalogId = $id;
|
547 |
-
break;
|
548 |
-
|
549 |
-
case 'outlines':
|
550 |
-
case 'pages':
|
551 |
-
case 'openHere':
|
552 |
-
case 'javascript':
|
553 |
-
$o['info'][$action] = $options;
|
554 |
-
break;
|
555 |
-
|
556 |
-
case 'viewerPreferences':
|
557 |
-
if (!isset($o['info']['viewerPreferences'])) {
|
558 |
-
$this->numObj++;
|
559 |
-
$this->o_viewerPreferences($this->numObj, 'new');
|
560 |
-
$o['info']['viewerPreferences'] = $this->numObj;
|
561 |
-
}
|
562 |
-
|
563 |
-
$vp = $o['info']['viewerPreferences'];
|
564 |
-
$this->o_viewerPreferences($vp, 'add', $options);
|
565 |
-
|
566 |
-
break;
|
567 |
-
|
568 |
-
case 'out':
|
569 |
-
$res = "\n$id 0 obj\n<< /Type /Catalog";
|
570 |
-
|
571 |
-
foreach ($o['info'] as $k => $v) {
|
572 |
-
switch ($k) {
|
573 |
-
case 'outlines':
|
574 |
-
$res .= "\n/Outlines $v 0 R";
|
575 |
-
break;
|
576 |
-
|
577 |
-
case 'pages':
|
578 |
-
$res .= "\n/Pages $v 0 R";
|
579 |
-
break;
|
580 |
-
|
581 |
-
case 'viewerPreferences':
|
582 |
-
$res .= "\n/ViewerPreferences $v 0 R";
|
583 |
-
break;
|
584 |
-
|
585 |
-
case 'openHere':
|
586 |
-
$res .= "\n/OpenAction $v 0 R";
|
587 |
-
break;
|
588 |
-
|
589 |
-
case 'javascript':
|
590 |
-
$res .= "\n/Names <</JavaScript $v 0 R>>";
|
591 |
-
break;
|
592 |
-
}
|
593 |
-
}
|
594 |
-
|
595 |
-
$res .= " >>\nendobj";
|
596 |
-
|
597 |
-
return $res;
|
598 |
-
}
|
599 |
-
|
600 |
-
return null;
|
601 |
-
}
|
602 |
-
|
603 |
-
/**
|
604 |
-
* object which is a parent to the pages in the document
|
605 |
-
*
|
606 |
-
* @param $id
|
607 |
-
* @param $action
|
608 |
-
* @param string $options
|
609 |
-
* @return string|null
|
610 |
-
*/
|
611 |
-
protected function o_pages($id, $action, $options = '')
|
612 |
-
{
|
613 |
-
if ($action !== 'new') {
|
614 |
-
$o = &$this->objects[$id];
|
615 |
-
}
|
616 |
-
|
617 |
-
switch ($action) {
|
618 |
-
case 'new':
|
619 |
-
$this->objects[$id] = array('t' => 'pages', 'info' => array());
|
620 |
-
$this->o_catalog($this->catalogId, 'pages', $id);
|
621 |
-
break;
|
622 |
-
|
623 |
-
case 'page':
|
624 |
-
if (!is_array($options)) {
|
625 |
-
// then it will just be the id of the new page
|
626 |
-
$o['info']['pages'][] = $options;
|
627 |
-
} else {
|
628 |
-
// then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
|
629 |
-
// and pos is either 'before' or 'after', saying where this page will fit.
|
630 |
-
if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
|
631 |
-
$i = array_search($options['rid'], $o['info']['pages']);
|
632 |
-
if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) {
|
633 |
-
|
634 |
-
// then there is a match
|
635 |
-
// make a space
|
636 |
-
switch ($options['pos']) {
|
637 |
-
case 'before':
|
638 |
-
$k = $i;
|
639 |
-
break;
|
640 |
-
|
641 |
-
case 'after':
|
642 |
-
$k = $i + 1;
|
643 |
-
break;
|
644 |
-
|
645 |
-
default:
|
646 |
-
$k = -1;
|
647 |
-
break;
|
648 |
-
}
|
649 |
-
|
650 |
-
if ($k >= 0) {
|
651 |
-
for ($j = count($o['info']['pages']) - 1; $j >= $k; $j--) {
|
652 |
-
$o['info']['pages'][$j + 1] = $o['info']['pages'][$j];
|
653 |
-
}
|
654 |
-
|
655 |
-
$o['info']['pages'][$k] = $options['id'];
|
656 |
-
}
|
657 |
-
}
|
658 |
-
}
|
659 |
-
}
|
660 |
-
break;
|
661 |
-
|
662 |
-
case 'procset':
|
663 |
-
$o['info']['procset'] = $options;
|
664 |
-
break;
|
665 |
-
|
666 |
-
case 'mediaBox':
|
667 |
-
$o['info']['mediaBox'] = $options;
|
668 |
-
// which should be an array of 4 numbers
|
669 |
-
$this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
|
670 |
-
break;
|
671 |
-
|
672 |
-
case 'font':
|
673 |
-
$o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']);
|
674 |
-
break;
|
675 |
-
|
676 |
-
case 'extGState':
|
677 |
-
$o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
|
678 |
-
break;
|
679 |
-
|
680 |
-
case 'xObject':
|
681 |
-
$o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']);
|
682 |
-
break;
|
683 |
-
|
684 |
-
case 'out':
|
685 |
-
if (count($o['info']['pages'])) {
|
686 |
-
$res = "\n$id 0 obj\n<< /Type /Pages\n/Kids [";
|
687 |
-
foreach ($o['info']['pages'] as $v) {
|
688 |
-
$res .= "$v 0 R\n";
|
689 |
-
}
|
690 |
-
|
691 |
-
$res .= "]\n/Count " . count($this->objects[$id]['info']['pages']);
|
692 |
-
|
693 |
-
if ((isset($o['info']['fonts']) && count($o['info']['fonts'])) ||
|
694 |
-
isset($o['info']['procset']) ||
|
695 |
-
(isset($o['info']['extGStates']) && count($o['info']['extGStates']))
|
696 |
-
) {
|
697 |
-
$res .= "\n/Resources <<";
|
698 |
-
|
699 |
-
if (isset($o['info']['procset'])) {
|
700 |
-
$res .= "\n/ProcSet " . $o['info']['procset'] . " 0 R";
|
701 |
-
}
|
702 |
-
|
703 |
-
if (isset($o['info']['fonts']) && count($o['info']['fonts'])) {
|
704 |
-
$res .= "\n/Font << ";
|
705 |
-
foreach ($o['info']['fonts'] as $finfo) {
|
706 |
-
$res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R";
|
707 |
-
}
|
708 |
-
$res .= "\n>>";
|
709 |
-
}
|
710 |
-
|
711 |
-
if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
|
712 |
-
$res .= "\n/XObject << ";
|
713 |
-
foreach ($o['info']['xObjects'] as $finfo) {
|
714 |
-
$res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
|
715 |
-
}
|
716 |
-
$res .= "\n>>";
|
717 |
-
}
|
718 |
-
|
719 |
-
if (isset($o['info']['extGStates']) && count($o['info']['extGStates'])) {
|
720 |
-
$res .= "\n/ExtGState << ";
|
721 |
-
foreach ($o['info']['extGStates'] as $gstate) {
|
722 |
-
$res .= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R";
|
723 |
-
}
|
724 |
-
$res .= "\n>>";
|
725 |
-
}
|
726 |
-
|
727 |
-
$res .= "\n>>";
|
728 |
-
if (isset($o['info']['mediaBox'])) {
|
729 |
-
$tmp = $o['info']['mediaBox'];
|
730 |
-
$res .= "\n/MediaBox [" . sprintf(
|
731 |
-
'%.3F %.3F %.3F %.3F',
|
732 |
-
$tmp[0],
|
733 |
-
$tmp[1],
|
734 |
-
$tmp[2],
|
735 |
-
$tmp[3]
|
736 |
-
) . ']';
|
737 |
-
}
|
738 |
-
}
|
739 |
-
|
740 |
-
$res .= "\n >>\nendobj";
|
741 |
-
} else {
|
742 |
-
$res = "\n$id 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
|
743 |
-
}
|
744 |
-
|
745 |
-
return $res;
|
746 |
-
}
|
747 |
-
|
748 |
-
return null;
|
749 |
-
}
|
750 |
-
|
751 |
-
/**
|
752 |
-
* define the outlines in the doc, empty for now
|
753 |
-
*
|
754 |
-
* @param $id
|
755 |
-
* @param $action
|
756 |
-
* @param string $options
|
757 |
-
* @return string|null
|
758 |
-
*/
|
759 |
-
protected function o_outlines($id, $action, $options = '')
|
760 |
-
{
|
761 |
-
if ($action !== 'new') {
|
762 |
-
$o = &$this->objects[$id];
|
763 |
-
}
|
764 |
-
|
765 |
-
switch ($action) {
|
766 |
-
case 'new':
|
767 |
-
$this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array()));
|
768 |
-
$this->o_catalog($this->catalogId, 'outlines', $id);
|
769 |
-
break;
|
770 |
-
|
771 |
-
case 'outline':
|
772 |
-
$o['info']['outlines'][] = $options;
|
773 |
-
break;
|
774 |
-
|
775 |
-
case 'out':
|
776 |
-
if (count($o['info']['outlines'])) {
|
777 |
-
$res = "\n$id 0 obj\n<< /Type /Outlines /Kids [";
|
778 |
-
foreach ($o['info']['outlines'] as $v) {
|
779 |
-
$res .= "$v 0 R ";
|
780 |
-
}
|
781 |
-
|
782 |
-
$res .= "] /Count " . count($o['info']['outlines']) . " >>\nendobj";
|
783 |
-
} else {
|
784 |
-
$res = "\n$id 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
|
785 |
-
}
|
786 |
-
|
787 |
-
return $res;
|
788 |
-
}
|
789 |
-
|
790 |
-
return null;
|
791 |
-
}
|
792 |
-
|
793 |
-
/**
|
794 |
-
* an object to hold the font description
|
795 |
-
*
|
796 |
-
* @param $id
|
797 |
-
* @param $action
|
798 |
-
* @param string|array $options
|
799 |
-
* @return string|null
|
800 |
-
*/
|
801 |
-
protected function o_font($id, $action, $options = '')
|
802 |
-
{
|
803 |
-
if ($action !== 'new') {
|
804 |
-
$o = &$this->objects[$id];
|
805 |
-
}
|
806 |
-
|
807 |
-
switch ($action) {
|
808 |
-
case 'new':
|
809 |
-
$this->objects[$id] = array(
|
810 |
-
't' => 'font',
|
811 |
-
'info' => array(
|
812 |
-
'name' => $options['name'],
|
813 |
-
'fontFileName' => $options['fontFileName'],
|
814 |
-
'SubType' => 'Type1'
|
815 |
-
)
|
816 |
-
);
|
817 |
-
$fontNum = $this->numFonts;
|
818 |
-
$this->objects[$id]['info']['fontNum'] = $fontNum;
|
819 |
-
|
820 |
-
// deal with the encoding and the differences
|
821 |
-
if (isset($options['differences'])) {
|
822 |
-
// then we'll need an encoding dictionary
|
823 |
-
$this->numObj++;
|
824 |
-
$this->o_fontEncoding($this->numObj, 'new', $options);
|
825 |
-
$this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
|
826 |
-
} else {
|
827 |
-
if (isset($options['encoding'])) {
|
828 |
-
// we can specify encoding here
|
829 |
-
switch ($options['encoding']) {
|
830 |
-
case 'WinAnsiEncoding':
|
831 |
-
case 'MacRomanEncoding':
|
832 |
-
case 'MacExpertEncoding':
|
833 |
-
$this->objects[$id]['info']['encoding'] = $options['encoding'];
|
834 |
-
break;
|
835 |
-
|
836 |
-
case 'none':
|
837 |
-
break;
|
838 |
-
|
839 |
-
default:
|
840 |
-
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
841 |
-
break;
|
842 |
-
}
|
843 |
-
} else {
|
844 |
-
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
845 |
-
}
|
846 |
-
}
|
847 |
-
|
848 |
-
if ($this->fonts[$options['fontFileName']]['isUnicode']) {
|
849 |
-
// For Unicode fonts, we need to incorporate font data into
|
850 |
-
// sub-sections that are linked from the primary font section.
|
851 |
-
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
852 |
-
// for more information.
|
853 |
-
//
|
854 |
-
// All of this code is adapted from the excellent changes made to
|
855 |
-
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
856 |
-
|
857 |
-
$toUnicodeId = ++$this->numObj;
|
858 |
-
$this->o_toUnicode($toUnicodeId, 'new');
|
859 |
-
$this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
|
860 |
-
|
861 |
-
$cidFontId = ++$this->numObj;
|
862 |
-
$this->o_fontDescendentCID($cidFontId, 'new', $options);
|
863 |
-
$this->objects[$id]['info']['cidFont'] = $cidFontId;
|
864 |
-
}
|
865 |
-
|
866 |
-
// also tell the pages node about the new font
|
867 |
-
$this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
|
868 |
-
break;
|
869 |
-
|
870 |
-
case 'add':
|
871 |
-
foreach ($options as $k => $v) {
|
872 |
-
switch ($k) {
|
873 |
-
case 'BaseFont':
|
874 |
-
$o['info']['name'] = $v;
|
875 |
-
break;
|
876 |
-
case 'FirstChar':
|
877 |
-
case 'LastChar':
|
878 |
-
case 'Widths':
|
879 |
-
case 'FontDescriptor':
|
880 |
-
case 'SubType':
|
881 |
-
$this->addMessage('o_font ' . $k . " : " . $v);
|
882 |
-
$o['info'][$k] = $v;
|
883 |
-
break;
|
884 |
-
}
|
885 |
-
}
|
886 |
-
|
887 |
-
// pass values down to descendent font
|
888 |
-
if (isset($o['info']['cidFont'])) {
|
889 |
-
$this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
|
890 |
-
}
|
891 |
-
break;
|
892 |
-
|
893 |
-
case 'out':
|
894 |
-
if ($this->fonts[$this->objects[$id]['info']['fontFileName']]['isUnicode']) {
|
895 |
-
// For Unicode fonts, we need to incorporate font data into
|
896 |
-
// sub-sections that are linked from the primary font section.
|
897 |
-
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
898 |
-
// for more information.
|
899 |
-
//
|
900 |
-
// All of this code is adapted from the excellent changes made to
|
901 |
-
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
902 |
-
|
903 |
-
$res = "\n$id 0 obj\n<</Type /Font\n/Subtype /Type0\n";
|
904 |
-
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
905 |
-
|
906 |
-
// The horizontal identity mapping for 2-byte CIDs; may be used
|
907 |
-
// with CIDFonts using any Registry, Ordering, and Supplement values.
|
908 |
-
$res .= "/Encoding /Identity-H\n";
|
909 |
-
$res .= "/DescendantFonts [" . $o['info']['cidFont'] . " 0 R]\n";
|
910 |
-
$res .= "/ToUnicode " . $o['info']['toUnicode'] . " 0 R\n";
|
911 |
-
$res .= ">>\n";
|
912 |
-
$res .= "endobj";
|
913 |
-
} else {
|
914 |
-
$res = "\n$id 0 obj\n<< /Type /Font\n/Subtype /" . $o['info']['SubType'] . "\n";
|
915 |
-
$res .= "/Name /F" . $o['info']['fontNum'] . "\n";
|
916 |
-
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
917 |
-
|
918 |
-
if (isset($o['info']['encodingDictionary'])) {
|
919 |
-
// then place a reference to the dictionary
|
920 |
-
$res .= "/Encoding " . $o['info']['encodingDictionary'] . " 0 R\n";
|
921 |
-
} else {
|
922 |
-
if (isset($o['info']['encoding'])) {
|
923 |
-
// use the specified encoding
|
924 |
-
$res .= "/Encoding /" . $o['info']['encoding'] . "\n";
|
925 |
-
}
|
926 |
-
}
|
927 |
-
|
928 |
-
if (isset($o['info']['FirstChar'])) {
|
929 |
-
$res .= "/FirstChar " . $o['info']['FirstChar'] . "\n";
|
930 |
-
}
|
931 |
-
|
932 |
-
if (isset($o['info']['LastChar'])) {
|
933 |
-
$res .= "/LastChar " . $o['info']['LastChar'] . "\n";
|
934 |
-
}
|
935 |
-
|
936 |
-
if (isset($o['info']['Widths'])) {
|
937 |
-
$res .= "/Widths " . $o['info']['Widths'] . " 0 R\n";
|
938 |
-
}
|
939 |
-
|
940 |
-
if (isset($o['info']['FontDescriptor'])) {
|
941 |
-
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
942 |
-
}
|
943 |
-
|
944 |
-
$res .= ">>\n";
|
945 |
-
$res .= "endobj";
|
946 |
-
}
|
947 |
-
|
948 |
-
return $res;
|
949 |
-
}
|
950 |
-
|
951 |
-
return null;
|
952 |
-
}
|
953 |
-
|
954 |
-
/**
|
955 |
-
* A toUnicode section, needed for unicode fonts
|
956 |
-
*
|
957 |
-
* @param $id
|
958 |
-
* @param $action
|
959 |
-
* @return null|string
|
960 |
-
*/
|
961 |
-
protected function o_toUnicode($id, $action)
|
962 |
-
{
|
963 |
-
switch ($action) {
|
964 |
-
case 'new':
|
965 |
-
$this->objects[$id] = array(
|
966 |
-
't' => 'toUnicode'
|
967 |
-
);
|
968 |
-
break;
|
969 |
-
case 'add':
|
970 |
-
break;
|
971 |
-
case 'out':
|
972 |
-
$ordering = '(UCS)';
|
973 |
-
$registry = '(Adobe)';
|
974 |
-
|
975 |
-
if ($this->encrypted) {
|
976 |
-
$this->encryptInit($id);
|
977 |
-
$ordering = $this->ARC4($ordering);
|
978 |
-
$registry = $this->ARC4($registry);
|
979 |
-
}
|
980 |
-
|
981 |
-
$stream = <<<EOT
|
982 |
-
/CIDInit /ProcSet findresource begin
|
983 |
-
12 dict begin
|
984 |
-
begincmap
|
985 |
-
/CIDSystemInfo
|
986 |
-
<</Registry $registry
|
987 |
-
/Ordering $ordering
|
988 |
-
/Supplement 0
|
989 |
-
>> def
|
990 |
-
/CMapName /Adobe-Identity-UCS def
|
991 |
-
/CMapType 2 def
|
992 |
-
1 begincodespacerange
|
993 |
-
<0000> <FFFF>
|
994 |
-
endcodespacerange
|
995 |
-
1 beginbfrange
|
996 |
-
<0000> <FFFF> <0000>
|
997 |
-
endbfrange
|
998 |
-
endcmap
|
999 |
-
CMapName currentdict /CMap defineresource pop
|
1000 |
-
end
|
1001 |
-
end
|
1002 |
-
EOT;
|
1003 |
-
|
1004 |
-
$res = "\n$id 0 obj\n";
|
1005 |
-
$res .= "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
|
1006 |
-
$res .= "stream\n" . $stream . "\nendstream" . "\nendobj";;
|
1007 |
-
|
1008 |
-
return $res;
|
1009 |
-
}
|
1010 |
-
|
1011 |
-
return null;
|
1012 |
-
}
|
1013 |
-
|
1014 |
-
/**
|
1015 |
-
* a font descriptor, needed for including additional fonts
|
1016 |
-
*
|
1017 |
-
* @param $id
|
1018 |
-
* @param $action
|
1019 |
-
* @param string $options
|
1020 |
-
* @return null|string
|
1021 |
-
*/
|
1022 |
-
protected function o_fontDescriptor($id, $action, $options = '')
|
1023 |
-
{
|
1024 |
-
if ($action !== 'new') {
|
1025 |
-
$o = &$this->objects[$id];
|
1026 |
-
}
|
1027 |
-
|
1028 |
-
switch ($action) {
|
1029 |
-
case 'new':
|
1030 |
-
$this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options);
|
1031 |
-
break;
|
1032 |
-
|
1033 |
-
case 'out':
|
1034 |
-
$res = "\n$id 0 obj\n<< /Type /FontDescriptor\n";
|
1035 |
-
foreach ($o['info'] as $label => $value) {
|
1036 |
-
switch ($label) {
|
1037 |
-
case 'Ascent':
|
1038 |
-
case 'CapHeight':
|
1039 |
-
case 'Descent':
|
1040 |
-
case 'Flags':
|
1041 |
-
case 'ItalicAngle':
|
1042 |
-
case 'StemV':
|
1043 |
-
case 'AvgWidth':
|
1044 |
-
case 'Leading':
|
1045 |
-
case 'MaxWidth':
|
1046 |
-
case 'MissingWidth':
|
1047 |
-
case 'StemH':
|
1048 |
-
case 'XHeight':
|
1049 |
-
case 'CharSet':
|
1050 |
-
if (mb_strlen($value, '8bit')) {
|
1051 |
-
$res .= "/$label $value\n";
|
1052 |
-
}
|
1053 |
-
|
1054 |
-
break;
|
1055 |
-
case 'FontFile':
|
1056 |
-
case 'FontFile2':
|
1057 |
-
case 'FontFile3':
|
1058 |
-
$res .= "/$label $value 0 R\n";
|
1059 |
-
break;
|
1060 |
-
|
1061 |
-
case 'FontBBox':
|
1062 |
-
$res .= "/$label [$value[0] $value[1] $value[2] $value[3]]\n";
|
1063 |
-
break;
|
1064 |
-
|
1065 |
-
case 'FontName':
|
1066 |
-
$res .= "/$label /$value\n";
|
1067 |
-
break;
|
1068 |
-
}
|
1069 |
-
}
|
1070 |
-
|
1071 |
-
$res .= ">>\nendobj";
|
1072 |
-
|
1073 |
-
return $res;
|
1074 |
-
}
|
1075 |
-
|
1076 |
-
return null;
|
1077 |
-
}
|
1078 |
-
|
1079 |
-
/**
|
1080 |
-
* the font encoding
|
1081 |
-
*
|
1082 |
-
* @param $id
|
1083 |
-
* @param $action
|
1084 |
-
* @param string $options
|
1085 |
-
* @return null|string
|
1086 |
-
*/
|
1087 |
-
protected function o_fontEncoding($id, $action, $options = '')
|
1088 |
-
{
|
1089 |
-
if ($action !== 'new') {
|
1090 |
-
$o = &$this->objects[$id];
|
1091 |
-
}
|
1092 |
-
|
1093 |
-
switch ($action) {
|
1094 |
-
case 'new':
|
1095 |
-
// the options array should contain 'differences' and maybe 'encoding'
|
1096 |
-
$this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options);
|
1097 |
-
break;
|
1098 |
-
|
1099 |
-
case 'out':
|
1100 |
-
$res = "\n$id 0 obj\n<< /Type /Encoding\n";
|
1101 |
-
if (!isset($o['info']['encoding'])) {
|
1102 |
-
$o['info']['encoding'] = 'WinAnsiEncoding';
|
1103 |
-
}
|
1104 |
-
|
1105 |
-
if ($o['info']['encoding'] !== 'none') {
|
1106 |
-
$res .= "/BaseEncoding /" . $o['info']['encoding'] . "\n";
|
1107 |
-
}
|
1108 |
-
|
1109 |
-
$res .= "/Differences \n[";
|
1110 |
-
|
1111 |
-
$onum = -100;
|
1112 |
-
|
1113 |
-
foreach ($o['info']['differences'] as $num => $label) {
|
1114 |
-
if ($num != $onum + 1) {
|
1115 |
-
// we cannot make use of consecutive numbering
|
1116 |
-
$res .= "\n$num /$label";
|
1117 |
-
} else {
|
1118 |
-
$res .= " /$label";
|
1119 |
-
}
|
1120 |
-
|
1121 |
-
$onum = $num;
|
1122 |
-
}
|
1123 |
-
|
1124 |
-
$res .= "\n]\n>>\nendobj";
|
1125 |
-
|
1126 |
-
return $res;
|
1127 |
-
}
|
1128 |
-
|
1129 |
-
return null;
|
1130 |
-
}
|
1131 |
-
|
1132 |
-
/**
|
1133 |
-
* a descendent cid font, needed for unicode fonts
|
1134 |
-
*
|
1135 |
-
* @param $id
|
1136 |
-
* @param $action
|
1137 |
-
* @param string|array $options
|
1138 |
-
* @return null|string
|
1139 |
-
*/
|
1140 |
-
protected function o_fontDescendentCID($id, $action, $options = '')
|
1141 |
-
{
|
1142 |
-
if ($action !== 'new') {
|
1143 |
-
$o = &$this->objects[$id];
|
1144 |
-
}
|
1145 |
-
|
1146 |
-
switch ($action) {
|
1147 |
-
case 'new':
|
1148 |
-
$this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
|
1149 |
-
|
1150 |
-
// we need a CID system info section
|
1151 |
-
$cidSystemInfoId = ++$this->numObj;
|
1152 |
-
$this->o_cidSystemInfo($cidSystemInfoId, 'new');
|
1153 |
-
$this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
|
1154 |
-
|
1155 |
-
// and a CID to GID map
|
1156 |
-
$cidToGidMapId = ++$this->numObj;
|
1157 |
-
$this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
|
1158 |
-
$this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
|
1159 |
-
break;
|
1160 |
-
|
1161 |
-
case 'add':
|
1162 |
-
foreach ($options as $k => $v) {
|
1163 |
-
switch ($k) {
|
1164 |
-
case 'BaseFont':
|
1165 |
-
$o['info']['name'] = $v;
|
1166 |
-
break;
|
1167 |
-
|
1168 |
-
case 'FirstChar':
|
1169 |
-
case 'LastChar':
|
1170 |
-
case 'MissingWidth':
|
1171 |
-
case 'FontDescriptor':
|
1172 |
-
case 'SubType':
|
1173 |
-
$this->addMessage("o_fontDescendentCID $k : $v");
|
1174 |
-
$o['info'][$k] = $v;
|
1175 |
-
break;
|
1176 |
-
}
|
1177 |
-
}
|
1178 |
-
|
1179 |
-
// pass values down to cid to gid map
|
1180 |
-
$this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
|
1181 |
-
break;
|
1182 |
-
|
1183 |
-
case 'out':
|
1184 |
-
$res = "\n$id 0 obj\n";
|
1185 |
-
$res .= "<</Type /Font\n";
|
1186 |
-
$res .= "/Subtype /CIDFontType2\n";
|
1187 |
-
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
1188 |
-
$res .= "/CIDSystemInfo " . $o['info']['cidSystemInfo'] . " 0 R\n";
|
1189 |
-
// if (isset($o['info']['FirstChar'])) {
|
1190 |
-
// $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
|
1191 |
-
// }
|
1192 |
-
|
1193 |
-
// if (isset($o['info']['LastChar'])) {
|
1194 |
-
// $res.= "/LastChar ".$o['info']['LastChar']."\n";
|
1195 |
-
// }
|
1196 |
-
if (isset($o['info']['FontDescriptor'])) {
|
1197 |
-
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
1198 |
-
}
|
1199 |
-
|
1200 |
-
if (isset($o['info']['MissingWidth'])) {
|
1201 |
-
$res .= "/DW " . $o['info']['MissingWidth'] . "\n";
|
1202 |
-
}
|
1203 |
-
|
1204 |
-
if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
|
1205 |
-
$cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
|
1206 |
-
$w = '';
|
1207 |
-
foreach ($cid_widths as $cid => $width) {
|
1208 |
-
$w .= "$cid [$width] ";
|
1209 |
-
}
|
1210 |
-
$res .= "/W [$w]\n";
|
1211 |
-
}
|
1212 |
-
|
1213 |
-
$res .= "/CIDToGIDMap " . $o['info']['cidToGidMap'] . " 0 R\n";
|
1214 |
-
$res .= ">>\n";
|
1215 |
-
$res .= "endobj";
|
1216 |
-
|
1217 |
-
return $res;
|
1218 |
-
}
|
1219 |
-
|
1220 |
-
return null;
|
1221 |
-
}
|
1222 |
-
|
1223 |
-
/**
|
1224 |
-
* CID system info section, needed for unicode fonts
|
1225 |
-
*
|
1226 |
-
* @param $id
|
1227 |
-
* @param $action
|
1228 |
-
* @return null|string
|
1229 |
-
*/
|
1230 |
-
protected function o_cidSystemInfo($id, $action)
|
1231 |
-
{
|
1232 |
-
switch ($action) {
|
1233 |
-
case 'new':
|
1234 |
-
$this->objects[$id] = array(
|
1235 |
-
't' => 'cidSystemInfo'
|
1236 |
-
);
|
1237 |
-
break;
|
1238 |
-
case 'add':
|
1239 |
-
break;
|
1240 |
-
case 'out':
|
1241 |
-
$ordering = '(UCS)';
|
1242 |
-
$registry = '(Adobe)';
|
1243 |
-
|
1244 |
-
if ($this->encrypted) {
|
1245 |
-
$this->encryptInit($id);
|
1246 |
-
$ordering = $this->ARC4($ordering);
|
1247 |
-
$registry = $this->ARC4($registry);
|
1248 |
-
}
|
1249 |
-
|
1250 |
-
|
1251 |
-
$res = "\n$id 0 obj\n";
|
1252 |
-
|
1253 |
-
$res .= '<</Registry ' . $registry . "\n"; // A string identifying an issuer of character collections
|
1254 |
-
$res .= '/Ordering ' . $ordering . "\n"; // A string that uniquely names a character collection issued by a specific registry
|
1255 |
-
$res .= "/Supplement 0\n"; // The supplement number of the character collection.
|
1256 |
-
$res .= ">>";
|
1257 |
-
|
1258 |
-
$res .= "\nendobj";;
|
1259 |
-
|
1260 |
-
return $res;
|
1261 |
-
}
|
1262 |
-
|
1263 |
-
return null;
|
1264 |
-
}
|
1265 |
-
|
1266 |
-
/**
|
1267 |
-
* a font glyph to character map, needed for unicode fonts
|
1268 |
-
*
|
1269 |
-
* @param $id
|
1270 |
-
* @param $action
|
1271 |
-
* @param string $options
|
1272 |
-
* @return null|string
|
1273 |
-
*/
|
1274 |
-
protected function o_fontGIDtoCIDMap($id, $action, $options = '')
|
1275 |
-
{
|
1276 |
-
if ($action !== 'new') {
|
1277 |
-
$o = &$this->objects[$id];
|
1278 |
-
}
|
1279 |
-
|
1280 |
-
switch ($action) {
|
1281 |
-
case 'new':
|
1282 |
-
$this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options);
|
1283 |
-
break;
|
1284 |
-
|
1285 |
-
case 'out':
|
1286 |
-
$res = "\n$id 0 obj\n";
|
1287 |
-
$fontFileName = $o['info']['fontFileName'];
|
1288 |
-
$tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']);
|
1289 |
-
|
1290 |
-
$compressed = isset($this->fonts[$fontFileName]['CIDtoGID_Compressed']) &&
|
1291 |
-
$this->fonts[$fontFileName]['CIDtoGID_Compressed'];
|
1292 |
-
|
1293 |
-
if (!$compressed && isset($o['raw'])) {
|
1294 |
-
$res .= $tmp;
|
1295 |
-
} else {
|
1296 |
-
$res .= "<<";
|
1297 |
-
|
1298 |
-
if (!$compressed && $this->compressionReady && $this->options['compression']) {
|
1299 |
-
// then implement ZLIB based compression on this content stream
|
1300 |
-
$compressed = true;
|
1301 |
-
$tmp = gzcompress($tmp, 6);
|
1302 |
-
}
|
1303 |
-
if ($compressed) {
|
1304 |
-
$res .= "\n/Filter /FlateDecode";
|
1305 |
-
}
|
1306 |
-
|
1307 |
-
if ($this->encrypted) {
|
1308 |
-
$this->encryptInit($id);
|
1309 |
-
$tmp = $this->ARC4($tmp);
|
1310 |
-
}
|
1311 |
-
|
1312 |
-
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream";
|
1313 |
-
}
|
1314 |
-
|
1315 |
-
$res .= "\nendobj";
|
1316 |
-
|
1317 |
-
return $res;
|
1318 |
-
}
|
1319 |
-
|
1320 |
-
return null;
|
1321 |
-
}
|
1322 |
-
|
1323 |
-
/**
|
1324 |
-
* the document procset, solves some problems with printing to old PS printers
|
1325 |
-
*
|
1326 |
-
* @param $id
|
1327 |
-
* @param $action
|
1328 |
-
* @param string $options
|
1329 |
-
* @return null|string
|
1330 |
-
*/
|
1331 |
-
protected function o_procset($id, $action, $options = '')
|
1332 |
-
{
|
1333 |
-
if ($action !== 'new') {
|
1334 |
-
$o = &$this->objects[$id];
|
1335 |
-
}
|
1336 |
-
|
1337 |
-
switch ($action) {
|
1338 |
-
case 'new':
|
1339 |
-
$this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1));
|
1340 |
-
$this->o_pages($this->currentNode, 'procset', $id);
|
1341 |
-
$this->procsetObjectId = $id;
|
1342 |
-
break;
|
1343 |
-
|
1344 |
-
case 'add':
|
1345 |
-
// this is to add new items to the procset list, despite the fact that this is considered
|
1346 |
-
// obsolete, the items are required for printing to some postscript printers
|
1347 |
-
switch ($options) {
|
1348 |
-
case 'ImageB':
|
1349 |
-
case 'ImageC':
|
1350 |
-
case 'ImageI':
|
1351 |
-
$o['info'][$options] = 1;
|
1352 |
-
break;
|
1353 |
-
}
|
1354 |
-
break;
|
1355 |
-
|
1356 |
-
case 'out':
|
1357 |
-
$res = "\n$id 0 obj\n[";
|
1358 |
-
foreach ($o['info'] as $label => $val) {
|
1359 |
-
$res .= "/$label ";
|
1360 |
-
}
|
1361 |
-
$res .= "]\nendobj";
|
1362 |
-
|
1363 |
-
return $res;
|
1364 |
-
}
|
1365 |
-
|
1366 |
-
return null;
|
1367 |
-
}
|
1368 |
-
|
1369 |
-
/**
|
1370 |
-
* define the document information
|
1371 |
-
*
|
1372 |
-
* @param $id
|
1373 |
-
* @param $action
|
1374 |
-
* @param string $options
|
1375 |
-
* @return null|string
|
1376 |
-
*/
|
1377 |
-
protected function o_info($id, $action, $options = '')
|
1378 |
-
{
|
1379 |
-
switch ($action) {
|
1380 |
-
case 'new':
|
1381 |
-
$this->infoObject = $id;
|
1382 |
-
$date = 'D:' . @date('Ymd');
|
1383 |
-
$this->objects[$id] = array(
|
1384 |
-
't' => 'info',
|
1385 |
-
'info' => array(
|
1386 |
-
'Producer' => 'CPDF (dompdf)',
|
1387 |
-
'CreationDate' => $date
|
1388 |
-
)
|
1389 |
-
);
|
1390 |
-
break;
|
1391 |
-
case 'Title':
|
1392 |
-
case 'Author':
|
1393 |
-
case 'Subject':
|
1394 |
-
case 'Keywords':
|
1395 |
-
case 'Creator':
|
1396 |
-
case 'Producer':
|
1397 |
-
case 'CreationDate':
|
1398 |
-
case 'ModDate':
|
1399 |
-
case 'Trapped':
|
1400 |
-
$this->objects[$id]['info'][$action] = $options;
|
1401 |
-
break;
|
1402 |
-
|
1403 |
-
case 'out':
|
1404 |
-
$encrypted = $this->encrypted;
|
1405 |
-
if ($encrypted) {
|
1406 |
-
$this->encryptInit($id);
|
1407 |
-
}
|
1408 |
-
|
1409 |
-
$res = "\n$id 0 obj\n<<\n";
|
1410 |
-
$o = &$this->objects[$id];
|
1411 |
-
foreach ($o['info'] as $k => $v) {
|
1412 |
-
$res .= "/$k (";
|
1413 |
-
|
1414 |
-
// dates must be outputted as-is, without Unicode transformations
|
1415 |
-
if ($k !== 'CreationDate' && $k !== 'ModDate') {
|
1416 |
-
$v = $this->filterText($v, true, false);
|
1417 |
-
}
|
1418 |
-
|
1419 |
-
if ($encrypted) {
|
1420 |
-
$v = $this->ARC4($v);
|
1421 |
-
}
|
1422 |
-
|
1423 |
-
$res .= $v;
|
1424 |
-
$res .= ")\n";
|
1425 |
-
}
|
1426 |
-
|
1427 |
-
$res .= ">>\nendobj";
|
1428 |
-
|
1429 |
-
return $res;
|
1430 |
-
}
|
1431 |
-
|
1432 |
-
return null;
|
1433 |
-
}
|
1434 |
-
|
1435 |
-
/**
|
1436 |
-
* an action object, used to link to URLS initially
|
1437 |
-
*
|
1438 |
-
* @param $id
|
1439 |
-
* @param $action
|
1440 |
-
* @param string $options
|
1441 |
-
* @return null|string
|
1442 |
-
*/
|
1443 |
-
protected function o_action($id, $action, $options = '')
|
1444 |
-
{
|
1445 |
-
if ($action !== 'new') {
|
1446 |
-
$o = &$this->objects[$id];
|
1447 |
-
}
|
1448 |
-
|
1449 |
-
switch ($action) {
|
1450 |
-
case 'new':
|
1451 |
-
if (is_array($options)) {
|
1452 |
-
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']);
|
1453 |
-
} else {
|
1454 |
-
// then assume a URI action
|
1455 |
-
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI');
|
1456 |
-
}
|
1457 |
-
break;
|
1458 |
-
|
1459 |
-
case 'out':
|
1460 |
-
if ($this->encrypted) {
|
1461 |
-
$this->encryptInit($id);
|
1462 |
-
}
|
1463 |
-
|
1464 |
-
$res = "\n$id 0 obj\n<< /Type /Action";
|
1465 |
-
switch ($o['type']) {
|
1466 |
-
case 'ilink':
|
1467 |
-
if (!isset($this->destinations[(string)$o['info']['label']])) {
|
1468 |
-
break;
|
1469 |
-
}
|
1470 |
-
|
1471 |
-
// there will be an 'label' setting, this is the name of the destination
|
1472 |
-
$res .= "\n/S /GoTo\n/D " . $this->destinations[(string)$o['info']['label']] . " 0 R";
|
1473 |
-
break;
|
1474 |
-
|
1475 |
-
case 'URI':
|
1476 |
-
$res .= "\n/S /URI\n/URI (";
|
1477 |
-
if ($this->encrypted) {
|
1478 |
-
$res .= $this->filterText($this->ARC4($o['info']), false, false);
|
1479 |
-
} else {
|
1480 |
-
$res .= $this->filterText($o['info'], false, false);
|
1481 |
-
}
|
1482 |
-
|
1483 |
-
$res .= ")";
|
1484 |
-
break;
|
1485 |
-
}
|
1486 |
-
|
1487 |
-
$res .= "\n>>\nendobj";
|
1488 |
-
|
1489 |
-
return $res;
|
1490 |
-
}
|
1491 |
-
|
1492 |
-
return null;
|
1493 |
-
}
|
1494 |
-
|
1495 |
-
/**
|
1496 |
-
* an annotation object, this will add an annotation to the current page.
|
1497 |
-
* initially will support just link annotations
|
1498 |
-
*
|
1499 |
-
* @param $id
|
1500 |
-
* @param $action
|
1501 |
-
* @param string $options
|
1502 |
-
* @return null|string
|
1503 |
-
*/
|
1504 |
-
protected function o_annotation($id, $action, $options = '')
|
1505 |
-
{
|
1506 |
-
if ($action !== 'new') {
|
1507 |
-
$o = &$this->objects[$id];
|
1508 |
-
}
|
1509 |
-
|
1510 |
-
switch ($action) {
|
1511 |
-
case 'new':
|
1512 |
-
// add the annotation to the current page
|
1513 |
-
$pageId = $this->currentPage;
|
1514 |
-
$this->o_page($pageId, 'annot', $id);
|
1515 |
-
|
1516 |
-
// and add the action object which is going to be required
|
1517 |
-
switch ($options['type']) {
|
1518 |
-
case 'link':
|
1519 |
-
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1520 |
-
$this->numObj++;
|
1521 |
-
$this->o_action($this->numObj, 'new', $options['url']);
|
1522 |
-
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1523 |
-
break;
|
1524 |
-
|
1525 |
-
case 'ilink':
|
1526 |
-
// this is to a named internal link
|
1527 |
-
$label = $options['label'];
|
1528 |
-
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1529 |
-
$this->numObj++;
|
1530 |
-
$this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label));
|
1531 |
-
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1532 |
-
break;
|
1533 |
-
}
|
1534 |
-
break;
|
1535 |
-
|
1536 |
-
case 'out':
|
1537 |
-
$res = "\n$id 0 obj\n<< /Type /Annot";
|
1538 |
-
switch ($o['info']['type']) {
|
1539 |
-
case 'link':
|
1540 |
-
case 'ilink':
|
1541 |
-
$res .= "\n/Subtype /Link";
|
1542 |
-
break;
|
1543 |
-
}
|
1544 |
-
$res .= "\n/A " . $o['info']['actionId'] . " 0 R";
|
1545 |
-
$res .= "\n/Border [0 0 0]";
|
1546 |
-
$res .= "\n/H /I";
|
1547 |
-
$res .= "\n/Rect [ ";
|
1548 |
-
|
1549 |
-
foreach ($o['info']['rect'] as $v) {
|
1550 |
-
$res .= sprintf("%.4F ", $v);
|
1551 |
-
}
|
1552 |
-
|
1553 |
-
$res .= "]";
|
1554 |
-
$res .= "\n>>\nendobj";
|
1555 |
-
|
1556 |
-
return $res;
|
1557 |
-
}
|
1558 |
-
|
1559 |
-
return null;
|
1560 |
-
}
|
1561 |
-
|
1562 |
-
/**
|
1563 |
-
* a page object, it also creates a contents object to hold its contents
|
1564 |
-
*
|
1565 |
-
* @param $id
|
1566 |
-
* @param $action
|
1567 |
-
* @param string $options
|
1568 |
-
* @return null|string
|
1569 |
-
*/
|
1570 |
-
protected function o_page($id, $action, $options = '')
|
1571 |
-
{
|
1572 |
-
if ($action !== 'new') {
|
1573 |
-
$o = &$this->objects[$id];
|
1574 |
-
}
|
1575 |
-
|
1576 |
-
switch ($action) {
|
1577 |
-
case 'new':
|
1578 |
-
$this->numPages++;
|
1579 |
-
$this->objects[$id] = array(
|
1580 |
-
't' => 'page',
|
1581 |
-
'info' => array(
|
1582 |
-
'parent' => $this->currentNode,
|
1583 |
-
'pageNum' => $this->numPages,
|
1584 |
-
'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox']
|
1585 |
-
)
|
1586 |
-
);
|
1587 |
-
|
1588 |
-
if (is_array($options)) {
|
1589 |
-
// then this must be a page insertion, array should contain 'rid','pos'=[before|after]
|
1590 |
-
$options['id'] = $id;
|
1591 |
-
$this->o_pages($this->currentNode, 'page', $options);
|
1592 |
-
} else {
|
1593 |
-
$this->o_pages($this->currentNode, 'page', $id);
|
1594 |
-
}
|
1595 |
-
|
1596 |
-
$this->currentPage = $id;
|
1597 |
-
//make a contents object to go with this page
|
1598 |
-
$this->numObj++;
|
1599 |
-
$this->o_contents($this->numObj, 'new', $id);
|
1600 |
-
$this->currentContents = $this->numObj;
|
1601 |
-
$this->objects[$id]['info']['contents'] = array();
|
1602 |
-
$this->objects[$id]['info']['contents'][] = $this->numObj;
|
1603 |
-
|
1604 |
-
$match = ($this->numPages % 2 ? 'odd' : 'even');
|
1605 |
-
foreach ($this->addLooseObjects as $oId => $target) {
|
1606 |
-
if ($target === 'all' || $match === $target) {
|
1607 |
-
$this->objects[$id]['info']['contents'][] = $oId;
|
1608 |
-
}
|
1609 |
-
}
|
1610 |
-
break;
|
1611 |
-
|
1612 |
-
case 'content':
|
1613 |
-
$o['info']['contents'][] = $options;
|
1614 |
-
break;
|
1615 |
-
|
1616 |
-
case 'annot':
|
1617 |
-
// add an annotation to this page
|
1618 |
-
if (!isset($o['info']['annot'])) {
|
1619 |
-
$o['info']['annot'] = array();
|
1620 |
-
}
|
1621 |
-
|
1622 |
-
// $options should contain the id of the annotation dictionary
|
1623 |
-
$o['info']['annot'][] = $options;
|
1624 |
-
break;
|
1625 |
-
|
1626 |
-
case 'out':
|
1627 |
-
$res = "\n$id 0 obj\n<< /Type /Page";
|
1628 |
-
if (isset($o['info']['mediaBox'])) {
|
1629 |
-
$tmp = $o['info']['mediaBox'];
|
1630 |
-
$res .= "\n/MediaBox [" . sprintf(
|
1631 |
-
'%.3F %.3F %.3F %.3F',
|
1632 |
-
$tmp[0],
|
1633 |
-
$tmp[1],
|
1634 |
-
$tmp[2],
|
1635 |
-
$tmp[3]
|
1636 |
-
) . ']';
|
1637 |
-
}
|
1638 |
-
$res .= "\n/Parent " . $o['info']['parent'] . " 0 R";
|
1639 |
-
|
1640 |
-
if (isset($o['info']['annot'])) {
|
1641 |
-
$res .= "\n/Annots [";
|
1642 |
-
foreach ($o['info']['annot'] as $aId) {
|
1643 |
-
$res .= " $aId 0 R";
|
1644 |
-
}
|
1645 |
-
$res .= " ]";
|
1646 |
-
}
|
1647 |
-
|
1648 |
-
$count = count($o['info']['contents']);
|
1649 |
-
if ($count == 1) {
|
1650 |
-
$res .= "\n/Contents " . $o['info']['contents'][0] . " 0 R";
|
1651 |
-
} else {
|
1652 |
-
if ($count > 1) {
|
1653 |
-
$res .= "\n/Contents [\n";
|
1654 |
-
|
1655 |
-
// reverse the page contents so added objects are below normal content
|
1656 |
-
//foreach (array_reverse($o['info']['contents']) as $cId) {
|
1657 |
-
// Back to normal now that I've got transparency working --Benj
|
1658 |
-
foreach ($o['info']['contents'] as $cId) {
|
1659 |
-
$res .= "$cId 0 R\n";
|
1660 |
-
}
|
1661 |
-
$res .= "]";
|
1662 |
-
}
|
1663 |
-
}
|
1664 |
-
|
1665 |
-
$res .= "\n>>\nendobj";
|
1666 |
-
|
1667 |
-
return $res;
|
1668 |
-
}
|
1669 |
-
|
1670 |
-
return null;
|
1671 |
-
}
|
1672 |
-
|
1673 |
-
/**
|
1674 |
-
* the contents objects hold all of the content which appears on pages
|
1675 |
-
*
|
1676 |
-
* @param $id
|
1677 |
-
* @param $action
|
1678 |
-
* @param string|array $options
|
1679 |
-
* @return null|string
|
1680 |
-
*/
|
1681 |
-
protected function o_contents($id, $action, $options = '')
|
1682 |
-
{
|
1683 |
-
if ($action !== 'new') {
|
1684 |
-
$o = &$this->objects[$id];
|
1685 |
-
}
|
1686 |
-
|
1687 |
-
switch ($action) {
|
1688 |
-
case 'new':
|
1689 |
-
$this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array());
|
1690 |
-
if (mb_strlen($options, '8bit') && intval($options)) {
|
1691 |
-
// then this contents is the primary for a page
|
1692 |
-
$this->objects[$id]['onPage'] = $options;
|
1693 |
-
} else {
|
1694 |
-
if ($options === 'raw') {
|
1695 |
-
// then this page contains some other type of system object
|
1696 |
-
$this->objects[$id]['raw'] = 1;
|
1697 |
-
}
|
1698 |
-
}
|
1699 |
-
break;
|
1700 |
-
|
1701 |
-
case 'add':
|
1702 |
-
// add more options to the declaration
|
1703 |
-
foreach ($options as $k => $v) {
|
1704 |
-
$o['info'][$k] = $v;
|
1705 |
-
}
|
1706 |
-
|
1707 |
-
case 'out':
|
1708 |
-
$tmp = $o['c'];
|
1709 |
-
$res = "\n$id 0 obj\n";
|
1710 |
-
|
1711 |
-
if (isset($this->objects[$id]['raw'])) {
|
1712 |
-
$res .= $tmp;
|
1713 |
-
} else {
|
1714 |
-
$res .= "<<";
|
1715 |
-
if ($this->compressionReady && $this->options['compression']) {
|
1716 |
-
// then implement ZLIB based compression on this content stream
|
1717 |
-
$res .= " /Filter /FlateDecode";
|
1718 |
-
$tmp = gzcompress($tmp, 6);
|
1719 |
-
}
|
1720 |
-
|
1721 |
-
if ($this->encrypted) {
|
1722 |
-
$this->encryptInit($id);
|
1723 |
-
$tmp = $this->ARC4($tmp);
|
1724 |
-
}
|
1725 |
-
|
1726 |
-
foreach ($o['info'] as $k => $v) {
|
1727 |
-
$res .= "\n/$k $v";
|
1728 |
-
}
|
1729 |
-
|
1730 |
-
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . " >>\nstream\n$tmp\nendstream";
|
1731 |
-
}
|
1732 |
-
|
1733 |
-
$res .= "\nendobj";
|
1734 |
-
|
1735 |
-
return $res;
|
1736 |
-
}
|
1737 |
-
|
1738 |
-
return null;
|
1739 |
-
}
|
1740 |
-
|
1741 |
-
/**
|
1742 |
-
* @param $id
|
1743 |
-
* @param $action
|
1744 |
-
* @return string|null
|
1745 |
-
*/
|
1746 |
-
protected function o_embedjs($id, $action)
|
1747 |
-
{
|
1748 |
-
switch ($action) {
|
1749 |
-
case 'new':
|
1750 |
-
$this->objects[$id] = array(
|
1751 |
-
't' => 'embedjs',
|
1752 |
-
'info' => array(
|
1753 |
-
'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]'
|
1754 |
-
)
|
1755 |
-
);
|
1756 |
-
break;
|
1757 |
-
|
1758 |
-
case 'out':
|
1759 |
-
$o = &$this->objects[$id];
|
1760 |
-
$res = "\n$id 0 obj\n<< ";
|
1761 |
-
foreach ($o['info'] as $k => $v) {
|
1762 |
-
$res .= "\n/$k $v";
|
1763 |
-
}
|
1764 |
-
$res .= "\n>>\nendobj";
|
1765 |
-
|
1766 |
-
return $res;
|
1767 |
-
}
|
1768 |
-
|
1769 |
-
return null;
|
1770 |
-
}
|
1771 |
-
|
1772 |
-
/**
|
1773 |
-
* @param $id
|
1774 |
-
* @param $action
|
1775 |
-
* @param string $code
|
1776 |
-
* @return null|string
|
1777 |
-
*/
|
1778 |
-
protected function o_javascript($id, $action, $code = '')
|
1779 |
-
{
|
1780 |
-
switch ($action) {
|
1781 |
-
case 'new':
|
1782 |
-
$this->objects[$id] = array(
|
1783 |
-
't' => 'javascript',
|
1784 |
-
'info' => array(
|
1785 |
-
'S' => '/JavaScript',
|
1786 |
-
'JS' => '(' . $this->filterText($code, true, false) . ')',
|
1787 |
-
)
|
1788 |
-
);
|
1789 |
-
break;
|
1790 |
-
|
1791 |
-
case 'out':
|
1792 |
-
$o = &$this->objects[$id];
|
1793 |
-
$res = "\n$id 0 obj\n<< ";
|
1794 |
-
|
1795 |
-
foreach ($o['info'] as $k => $v) {
|
1796 |
-
$res .= "\n/$k $v";
|
1797 |
-
}
|
1798 |
-
$res .= "\n>>\nendobj";
|
1799 |
-
|
1800 |
-
return $res;
|
1801 |
-
}
|
1802 |
-
|
1803 |
-
return null;
|
1804 |
-
}
|
1805 |
-
|
1806 |
-
/**
|
1807 |
-
* an image object, will be an XObject in the document, includes description and data
|
1808 |
-
*
|
1809 |
-
* @param $id
|
1810 |
-
* @param $action
|
1811 |
-
* @param string $options
|
1812 |
-
* @return null|string
|
1813 |
-
*/
|
1814 |
-
protected function o_image($id, $action, $options = '')
|
1815 |
-
{
|
1816 |
-
switch ($action) {
|
1817 |
-
case 'new':
|
1818 |
-
// make the new object
|
1819 |
-
$this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array());
|
1820 |
-
|
1821 |
-
$info =& $this->objects[$id]['info'];
|
1822 |
-
|
1823 |
-
$info['Type'] = '/XObject';
|
1824 |
-
$info['Subtype'] = '/Image';
|
1825 |
-
$info['Width'] = $options['iw'];
|
1826 |
-
$info['Height'] = $options['ih'];
|
1827 |
-
|
1828 |
-
if (isset($options['masked']) && $options['masked']) {
|
1829 |
-
$info['SMask'] = ($this->numObj - 1) . ' 0 R';
|
1830 |
-
}
|
1831 |
-
|
1832 |
-
if (!isset($options['type']) || $options['type'] === 'jpg') {
|
1833 |
-
if (!isset($options['channels'])) {
|
1834 |
-
$options['channels'] = 3;
|
1835 |
-
}
|
1836 |
-
|
1837 |
-
switch ($options['channels']) {
|
1838 |
-
case 1:
|
1839 |
-
$info['ColorSpace'] = '/DeviceGray';
|
1840 |
-
break;
|
1841 |
-
case 4:
|
1842 |
-
$info['ColorSpace'] = '/DeviceCMYK';
|
1843 |
-
break;
|
1844 |
-
default:
|
1845 |
-
$info['ColorSpace'] = '/DeviceRGB';
|
1846 |
-
break;
|
1847 |
-
}
|
1848 |
-
|
1849 |
-
if ($info['ColorSpace'] === '/DeviceCMYK') {
|
1850 |
-
$info['Decode'] = '[1 0 1 0 1 0 1 0]';
|
1851 |
-
}
|
1852 |
-
|
1853 |
-
$info['Filter'] = '/DCTDecode';
|
1854 |
-
$info['BitsPerComponent'] = 8;
|
1855 |
-
} else {
|
1856 |
-
if ($options['type'] === 'png') {
|
1857 |
-
$info['Filter'] = '/FlateDecode';
|
1858 |
-
$info['DecodeParms'] = '<< /Predictor 15 /Colors ' . $options['ncolor'] . ' /Columns ' . $options['iw'] . ' /BitsPerComponent ' . $options['bitsPerComponent'] . '>>';
|
1859 |
-
|
1860 |
-
if ($options['isMask']) {
|
1861 |
-
$info['ColorSpace'] = '/DeviceGray';
|
1862 |
-
} else {
|
1863 |
-
if (mb_strlen($options['pdata'], '8bit')) {
|
1864 |
-
$tmp = ' [ /Indexed /DeviceRGB ' . (mb_strlen($options['pdata'], '8bit') / 3 - 1) . ' ';
|
1865 |
-
$this->numObj++;
|
1866 |
-
$this->o_contents($this->numObj, 'new');
|
1867 |
-
$this->objects[$this->numObj]['c'] = $options['pdata'];
|
1868 |
-
$tmp .= $this->numObj . ' 0 R';
|
1869 |
-
$tmp .= ' ]';
|
1870 |
-
$info['ColorSpace'] = $tmp;
|
1871 |
-
|
1872 |
-
if (isset($options['transparency'])) {
|
1873 |
-
$transparency = $options['transparency'];
|
1874 |
-
switch ($transparency['type']) {
|
1875 |
-
case 'indexed':
|
1876 |
-
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1877 |
-
$info['Mask'] = $tmp;
|
1878 |
-
break;
|
1879 |
-
|
1880 |
-
case 'color-key':
|
1881 |
-
$tmp = ' [ ' .
|
1882 |
-
$transparency['r'] . ' ' . $transparency['r'] .
|
1883 |
-
$transparency['g'] . ' ' . $transparency['g'] .
|
1884 |
-
$transparency['b'] . ' ' . $transparency['b'] .
|
1885 |
-
' ] ';
|
1886 |
-
$info['Mask'] = $tmp;
|
1887 |
-
break;
|
1888 |
-
}
|
1889 |
-
}
|
1890 |
-
} else {
|
1891 |
-
if (isset($options['transparency'])) {
|
1892 |
-
$transparency = $options['transparency'];
|
1893 |
-
|
1894 |
-
switch ($transparency['type']) {
|
1895 |
-
case 'indexed':
|
1896 |
-
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1897 |
-
$info['Mask'] = $tmp;
|
1898 |
-
break;
|
1899 |
-
|
1900 |
-
case 'color-key':
|
1901 |
-
$tmp = ' [ ' .
|
1902 |
-
$transparency['r'] . ' ' . $transparency['r'] . ' ' .
|
1903 |
-
$transparency['g'] . ' ' . $transparency['g'] . ' ' .
|
1904 |
-
$transparency['b'] . ' ' . $transparency['b'] .
|
1905 |
-
' ] ';
|
1906 |
-
$info['Mask'] = $tmp;
|
1907 |
-
break;
|
1908 |
-
}
|
1909 |
-
}
|
1910 |
-
$info['ColorSpace'] = '/' . $options['color'];
|
1911 |
-
}
|
1912 |
-
}
|
1913 |
-
|
1914 |
-
$info['BitsPerComponent'] = $options['bitsPerComponent'];
|
1915 |
-
}
|
1916 |
-
}
|
1917 |
-
|
1918 |
-
// assign it a place in the named resource dictionary as an external object, according to
|
1919 |
-
// the label passed in with it.
|
1920 |
-
$this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id));
|
1921 |
-
|
1922 |
-
// also make sure that we have the right procset object for it.
|
1923 |
-
$this->o_procset($this->procsetObjectId, 'add', 'ImageC');
|
1924 |
-
break;
|
1925 |
-
|
1926 |
-
case 'out':
|
1927 |
-
$o = &$this->objects[$id];
|
1928 |
-
$tmp = &$o['data'];
|
1929 |
-
$res = "\n$id 0 obj\n<<";
|
1930 |
-
|
1931 |
-
foreach ($o['info'] as $k => $v) {
|
1932 |
-
$res .= "\n/$k $v";
|
1933 |
-
}
|
1934 |
-
|
1935 |
-
if ($this->encrypted) {
|
1936 |
-
$this->encryptInit($id);
|
1937 |
-
$tmp = $this->ARC4($tmp);
|
1938 |
-
}
|
1939 |
-
|
1940 |
-
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream\nendobj";
|
1941 |
-
|
1942 |
-
return $res;
|
1943 |
-
}
|
1944 |
-
|
1945 |
-
return null;
|
1946 |
-
}
|
1947 |
-
|
1948 |
-
/**
|
1949 |
-
* graphics state object
|
1950 |
-
*
|
1951 |
-
* @param $id
|
1952 |
-
* @param $action
|
1953 |
-
* @param string $options
|
1954 |
-
* @return null|string
|
1955 |
-
*/
|
1956 |
-
protected function o_extGState($id, $action, $options = "")
|
1957 |
-
{
|
1958 |
-
static $valid_params = array(
|
1959 |
-
"LW",
|
1960 |
-
"LC",
|
1961 |
-
"LC",
|
1962 |
-
"LJ",
|
1963 |
-
"ML",
|
1964 |
-
"D",
|
1965 |
-
"RI",
|
1966 |
-
"OP",
|
1967 |
-
"op",
|
1968 |
-
"OPM",
|
1969 |
-
"Font",
|
1970 |
-
"BG",
|
1971 |
-
"BG2",
|
1972 |
-
"UCR",
|
1973 |
-
"TR",
|
1974 |
-
"TR2",
|
1975 |
-
"HT",
|
1976 |
-
"FL",
|
1977 |
-
"SM",
|
1978 |
-
"SA",
|
1979 |
-
"BM",
|
1980 |
-
"SMask",
|
1981 |
-
"CA",
|
1982 |
-
"ca",
|
1983 |
-
"AIS",
|
1984 |
-
"TK"
|
1985 |
-
);
|
1986 |
-
|
1987 |
-
switch ($action) {
|
1988 |
-
case "new":
|
1989 |
-
$this->objects[$id] = array('t' => 'extGState', 'info' => $options);
|
1990 |
-
|
1991 |
-
// Tell the pages about the new resource
|
1992 |
-
$this->numStates++;
|
1993 |
-
$this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
|
1994 |
-
break;
|
1995 |
-
|
1996 |
-
case "out":
|
1997 |
-
$o = &$this->objects[$id];
|
1998 |
-
$res = "\n$id 0 obj\n<< /Type /ExtGState\n";
|
1999 |
-
|
2000 |
-
foreach ($o["info"] as $k => $v) {
|
2001 |
-
if (!in_array($k, $valid_params)) {
|
2002 |
-
continue;
|
2003 |
-
}
|
2004 |
-
$res .= "/$k $v\n";
|
2005 |
-
}
|
2006 |
-
|
2007 |
-
$res .= ">>\nendobj";
|
2008 |
-
|
2009 |
-
return $res;
|
2010 |
-
}
|
2011 |
-
|
2012 |
-
return null;
|
2013 |
-
}
|
2014 |
-
|
2015 |
-
/**
|
2016 |
-
* encryption object.
|
2017 |
-
*
|
2018 |
-
* @param $id
|
2019 |
-
* @param $action
|
2020 |
-
* @param string $options
|
2021 |
-
* @return string|null
|
2022 |
-
*/
|
2023 |
-
protected function o_encryption($id, $action, $options = '')
|
2024 |
-
{
|
2025 |
-
switch ($action) {
|
2026 |
-
case 'new':
|
2027 |
-
// make the new object
|
2028 |
-
$this->objects[$id] = array('t' => 'encryption', 'info' => $options);
|
2029 |
-
$this->arc4_objnum = $id;
|
2030 |
-
break;
|
2031 |
-
|
2032 |
-
case 'keys':
|
2033 |
-
// figure out the additional parameters required
|
2034 |
-
$pad = chr(0x28) . chr(0xBF) . chr(0x4E) . chr(0x5E) . chr(0x4E) . chr(0x75) . chr(0x8A) . chr(0x41)
|
2035 |
-
. chr(0x64) . chr(0x00) . chr(0x4E) . chr(0x56) . chr(0xFF) . chr(0xFA) . chr(0x01) . chr(0x08)
|
2036 |
-
. chr(0x2E) . chr(0x2E) . chr(0x00) . chr(0xB6) . chr(0xD0) . chr(0x68) . chr(0x3E) . chr(0x80)
|
2037 |
-
. chr(0x2F) . chr(0x0C) . chr(0xA9) . chr(0xFE) . chr(0x64) . chr(0x53) . chr(0x69) . chr(0x7A);
|
2038 |
-
|
2039 |
-
$info = $this->objects[$id]['info'];
|
2040 |
-
|
2041 |
-
$len = mb_strlen($info['owner'], '8bit');
|
2042 |
-
|
2043 |
-
if ($len > 32) {
|
2044 |
-
$owner = substr($info['owner'], 0, 32);
|
2045 |
-
} else {
|
2046 |
-
if ($len < 32) {
|
2047 |
-
$owner = $info['owner'] . substr($pad, 0, 32 - $len);
|
2048 |
-
} else {
|
2049 |
-
$owner = $info['owner'];
|
2050 |
-
}
|
2051 |
-
}
|
2052 |
-
|
2053 |
-
$len = mb_strlen($info['user'], '8bit');
|
2054 |
-
if ($len > 32) {
|
2055 |
-
$user = substr($info['user'], 0, 32);
|
2056 |
-
} else {
|
2057 |
-
if ($len < 32) {
|
2058 |
-
$user = $info['user'] . substr($pad, 0, 32 - $len);
|
2059 |
-
} else {
|
2060 |
-
$user = $info['user'];
|
2061 |
-
}
|
2062 |
-
}
|
2063 |
-
|
2064 |
-
$tmp = $this->md5_16($owner);
|
2065 |
-
$okey = substr($tmp, 0, 5);
|
2066 |
-
$this->ARC4_init($okey);
|
2067 |
-
$ovalue = $this->ARC4($user);
|
2068 |
-
$this->objects[$id]['info']['O'] = $ovalue;
|
2069 |
-
|
2070 |
-
// now make the u value, phew.
|
2071 |
-
$tmp = $this->md5_16(
|
2072 |
-
$user . $ovalue . chr($info['p']) . chr(255) . chr(255) . chr(255) . hex2bin($this->fileIdentifier)
|
2073 |
-
);
|
2074 |
-
|
2075 |
-
$ukey = substr($tmp, 0, 5);
|
2076 |
-
$this->ARC4_init($ukey);
|
2077 |
-
$this->encryptionKey = $ukey;
|
2078 |
-
$this->encrypted = true;
|
2079 |
-
$uvalue = $this->ARC4($pad);
|
2080 |
-
$this->objects[$id]['info']['U'] = $uvalue;
|
2081 |
-
// initialize the arc4 array
|
2082 |
-
break;
|
2083 |
-
|
2084 |
-
case 'out':
|
2085 |
-
$o = &$this->objects[$id];
|
2086 |
-
|
2087 |
-
$res = "\n$id 0 obj\n<<";
|
2088 |
-
$res .= "\n/Filter /Standard";
|
2089 |
-
$res .= "\n/V 1";
|
2090 |
-
$res .= "\n/R 2";
|
2091 |
-
$res .= "\n/O (" . $this->filterText($o['info']['O'], false, false) . ')';
|
2092 |
-
$res .= "\n/U (" . $this->filterText($o['info']['U'], false, false) . ')';
|
2093 |
-
// and the p-value needs to be converted to account for the twos-complement approach
|
2094 |
-
$o['info']['p'] = (($o['info']['p'] ^ 255) + 1) * -1;
|
2095 |
-
$res .= "\n/P " . ($o['info']['p']);
|
2096 |
-
$res .= "\n>>\nendobj";
|
2097 |
-
|
2098 |
-
return $res;
|
2099 |
-
}
|
2100 |
-
|
2101 |
-
return null;
|
2102 |
-
}
|
2103 |
-
|
2104 |
-
/**
|
2105 |
-
* ARC4 functions
|
2106 |
-
* A series of function to implement ARC4 encoding in PHP
|
2107 |
-
*/
|
2108 |
-
|
2109 |
-
/**
|
2110 |
-
* calculate the 16 byte version of the 128 bit md5 digest of the string
|
2111 |
-
*
|
2112 |
-
* @param $string
|
2113 |
-
* @return string
|
2114 |
-
*/
|
2115 |
-
function md5_16($string)
|
2116 |
-
{
|
2117 |
-
$tmp = md5($string);
|
2118 |
-
$out = '';
|
2119 |
-
for ($i = 0; $i <= 30; $i = $i + 2) {
|
2120 |
-
$out .= chr(hexdec(substr($tmp, $i, 2)));
|
2121 |
-
}
|
2122 |
-
|
2123 |
-
return $out;
|
2124 |
-
}
|
2125 |
-
|
2126 |
-
/**
|
2127 |
-
* initialize the encryption for processing a particular object
|
2128 |
-
*
|
2129 |
-
* @param $id
|
2130 |
-
*/
|
2131 |
-
function encryptInit($id)
|
2132 |
-
{
|
2133 |
-
$tmp = $this->encryptionKey;
|
2134 |
-
$hex = dechex($id);
|
2135 |
-
if (mb_strlen($hex, '8bit') < 6) {
|
2136 |
-
$hex = substr('000000', 0, 6 - mb_strlen($hex, '8bit')) . $hex;
|
2137 |
-
}
|
2138 |
-
$tmp .= chr(hexdec(substr($hex, 4, 2)))
|
2139 |
-
. chr(hexdec(substr($hex, 2, 2)))
|
2140 |
-
. chr(hexdec(substr($hex, 0, 2)))
|
2141 |
-
. chr(0)
|
2142 |
-
. chr(0)
|
2143 |
-
;
|
2144 |
-
$key = $this->md5_16($tmp);
|
2145 |
-
$this->ARC4_init(substr($key, 0, 10));
|
2146 |
-
}
|
2147 |
-
|
2148 |
-
/**
|
2149 |
-
* initialize the ARC4 encryption
|
2150 |
-
*
|
2151 |
-
* @param string $key
|
2152 |
-
*/
|
2153 |
-
function ARC4_init($key = '')
|
2154 |
-
{
|
2155 |
-
$this->arc4 = '';
|
2156 |
-
|
2157 |
-
// setup the control array
|
2158 |
-
if (mb_strlen($key, '8bit') == 0) {
|
2159 |
-
return;
|
2160 |
-
}
|
2161 |
-
|
2162 |
-
$k = '';
|
2163 |
-
while (mb_strlen($k, '8bit') < 256) {
|
2164 |
-
$k .= $key;
|
2165 |
-
}
|
2166 |
-
|
2167 |
-
$k = substr($k, 0, 256);
|
2168 |
-
for ($i = 0; $i < 256; $i++) {
|
2169 |
-
$this->arc4 .= chr($i);
|
2170 |
-
}
|
2171 |
-
|
2172 |
-
$j = 0;
|
2173 |
-
|
2174 |
-
for ($i = 0; $i < 256; $i++) {
|
2175 |
-
$t = $this->arc4[$i];
|
2176 |
-
$j = ($j + ord($t) + ord($k[$i])) % 256;
|
2177 |
-
$this->arc4[$i] = $this->arc4[$j];
|
2178 |
-
$this->arc4[$j] = $t;
|
2179 |
-
}
|
2180 |
-
}
|
2181 |
-
|
2182 |
-
/**
|
2183 |
-
* ARC4 encrypt a text string
|
2184 |
-
*
|
2185 |
-
* @param $text
|
2186 |
-
* @return string
|
2187 |
-
*/
|
2188 |
-
function ARC4($text)
|
2189 |
-
{
|
2190 |
-
$len = mb_strlen($text, '8bit');
|
2191 |
-
$a = 0;
|
2192 |
-
$b = 0;
|
2193 |
-
$c = $this->arc4;
|
2194 |
-
$out = '';
|
2195 |
-
for ($i = 0; $i < $len; $i++) {
|
2196 |
-
$a = ($a + 1) % 256;
|
2197 |
-
$t = $c[$a];
|
2198 |
-
$b = ($b + ord($t)) % 256;
|
2199 |
-
$c[$a] = $c[$b];
|
2200 |
-
$c[$b] = $t;
|
2201 |
-
$k = ord($c[(ord($c[$a]) + ord($c[$b])) % 256]);
|
2202 |
-
$out .= chr(ord($text[$i]) ^ $k);
|
2203 |
-
}
|
2204 |
-
|
2205 |
-
return $out;
|
2206 |
-
}
|
2207 |
-
|
2208 |
-
/**
|
2209 |
-
* functions which can be called to adjust or add to the document
|
2210 |
-
*/
|
2211 |
-
|
2212 |
-
/**
|
2213 |
-
* add a link in the document to an external URL
|
2214 |
-
*
|
2215 |
-
* @param $url
|
2216 |
-
* @param $x0
|
2217 |
-
* @param $y0
|
2218 |
-
* @param $x1
|
2219 |
-
* @param $y1
|
2220 |
-
*/
|
2221 |
-
function addLink($url, $x0, $y0, $x1, $y1)
|
2222 |
-
{
|
2223 |
-
$this->numObj++;
|
2224 |
-
$info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1));
|
2225 |
-
$this->o_annotation($this->numObj, 'new', $info);
|
2226 |
-
}
|
2227 |
-
|
2228 |
-
/**
|
2229 |
-
* add a link in the document to an internal destination (ie. within the document)
|
2230 |
-
*
|
2231 |
-
* @param $label
|
2232 |
-
* @param $x0
|
2233 |
-
* @param $y0
|
2234 |
-
* @param $x1
|
2235 |
-
* @param $y1
|
2236 |
-
*/
|
2237 |
-
function addInternalLink($label, $x0, $y0, $x1, $y1)
|
2238 |
-
{
|
2239 |
-
$this->numObj++;
|
2240 |
-
$info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1));
|
2241 |
-
$this->o_annotation($this->numObj, 'new', $info);
|
2242 |
-
}
|
2243 |
-
|
2244 |
-
/**
|
2245 |
-
* set the encryption of the document
|
2246 |
-
* can be used to turn it on and/or set the passwords which it will have.
|
2247 |
-
* also the functions that the user will have are set here, such as print, modify, add
|
2248 |
-
*
|
2249 |
-
* @param string $userPass
|
2250 |
-
* @param string $ownerPass
|
2251 |
-
* @param array $pc
|
2252 |
-
*/
|
2253 |
-
function setEncryption($userPass = '', $ownerPass = '', $pc = array())
|
2254 |
-
{
|
2255 |
-
$p = bindec("11000000");
|
2256 |
-
|
2257 |
-
$options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32);
|
2258 |
-
|
2259 |
-
foreach ($pc as $k => $v) {
|
2260 |
-
if ($v && isset($options[$k])) {
|
2261 |
-
$p += $options[$k];
|
2262 |
-
} else {
|
2263 |
-
if (isset($options[$v])) {
|
2264 |
-
$p += $options[$v];
|
2265 |
-
}
|
2266 |
-
}
|
2267 |
-
}
|
2268 |
-
|
2269 |
-
// implement encryption on the document
|
2270 |
-
if ($this->arc4_objnum == 0) {
|
2271 |
-
// then the block does not exist already, add it.
|
2272 |
-
$this->numObj++;
|
2273 |
-
if (mb_strlen($ownerPass) == 0) {
|
2274 |
-
$ownerPass = $userPass;
|
2275 |
-
}
|
2276 |
-
|
2277 |
-
$this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p));
|
2278 |
-
}
|
2279 |
-
}
|
2280 |
-
|
2281 |
-
/**
|
2282 |
-
* should be used for internal checks, not implemented as yet
|
2283 |
-
*/
|
2284 |
-
function checkAllHere()
|
2285 |
-
{
|
2286 |
-
}
|
2287 |
-
|
2288 |
-
/**
|
2289 |
-
* return the pdf stream as a string returned from the function
|
2290 |
-
*
|
2291 |
-
* @param bool $debug
|
2292 |
-
* @return string
|
2293 |
-
*/
|
2294 |
-
function output($debug = false)
|
2295 |
-
{
|
2296 |
-
if ($debug) {
|
2297 |
-
// turn compression off
|
2298 |
-
$this->options['compression'] = false;
|
2299 |
-
}
|
2300 |
-
|
2301 |
-
if ($this->javascript) {
|
2302 |
-
$this->numObj++;
|
2303 |
-
|
2304 |
-
$js_id = $this->numObj;
|
2305 |
-
$this->o_embedjs($js_id, 'new');
|
2306 |
-
$this->o_javascript(++$this->numObj, 'new', $this->javascript);
|
2307 |
-
|
2308 |
-
$id = $this->catalogId;
|
2309 |
-
|
2310 |
-
$this->o_catalog($id, 'javascript', $js_id);
|
2311 |
-
}
|
2312 |
-
|
2313 |
-
if ($this->fileIdentifier === '') {
|
2314 |
-
$tmp = implode('', $this->objects[$this->infoObject]['info']);
|
2315 |
-
$this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand());
|
2316 |
-
}
|
2317 |
-
|
2318 |
-
if ($this->arc4_objnum) {
|
2319 |
-
$this->o_encryption($this->arc4_objnum, 'keys');
|
2320 |
-
$this->ARC4_init($this->encryptionKey);
|
2321 |
-
}
|
2322 |
-
|
2323 |
-
$this->checkAllHere();
|
2324 |
-
|
2325 |
-
$xref = array();
|
2326 |
-
$content = '%PDF-1.3';
|
2327 |
-
$pos = mb_strlen($content, '8bit');
|
2328 |
-
|
2329 |
-
foreach ($this->objects as $k => $v) {
|
2330 |
-
$tmp = 'o_' . $v['t'];
|
2331 |
-
$cont = $this->$tmp($k, 'out');
|
2332 |
-
$content .= $cont;
|
2333 |
-
$xref[] = $pos + 1; //+1 to account for \n at the start of each object
|
2334 |
-
$pos += mb_strlen($cont, '8bit');
|
2335 |
-
}
|
2336 |
-
|
2337 |
-
$content .= "\nxref\n0 " . (count($xref) + 1) . "\n0000000000 65535 f \n";
|
2338 |
-
|
2339 |
-
foreach ($xref as $p) {
|
2340 |
-
$content .= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n";
|
2341 |
-
}
|
2342 |
-
|
2343 |
-
$content .= "trailer\n<<\n" .
|
2344 |
-
'/Size ' . (count($xref) + 1) . "\n" .
|
2345 |
-
'/Root 1 0 R' . "\n" .
|
2346 |
-
'/Info ' . $this->infoObject . " 0 R\n"
|
2347 |
-
;
|
2348 |
-
|
2349 |
-
// if encryption has been applied to this document then add the marker for this dictionary
|
2350 |
-
if ($this->arc4_objnum > 0) {
|
2351 |
-
$content .= '/Encrypt ' . $this->arc4_objnum . " 0 R\n";
|
2352 |
-
}
|
2353 |
-
|
2354 |
-
$content .= '/ID[<' . $this->fileIdentifier . '><' . $this->fileIdentifier . ">]\n";
|
2355 |
-
|
2356 |
-
// account for \n added at start of xref table
|
2357 |
-
$pos++;
|
2358 |
-
|
2359 |
-
$content .= ">>\nstartxref\n$pos\n%%EOF\n";
|
2360 |
-
|
2361 |
-
return $content;
|
2362 |
-
}
|
2363 |
-
|
2364 |
-
/**
|
2365 |
-
* initialize a new document
|
2366 |
-
* if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
|
2367 |
-
* this function is called automatically by the constructor function
|
2368 |
-
*
|
2369 |
-
* @param array $pageSize
|
2370 |
-
*/
|
2371 |
-
private function newDocument($pageSize = array(0, 0, 612, 792))
|
2372 |
-
{
|
2373 |
-
$this->numObj = 0;
|
2374 |
-
$this->objects = array();
|
2375 |
-
|
2376 |
-
$this->numObj++;
|
2377 |
-
$this->o_catalog($this->numObj, 'new');
|
2378 |
-
|
2379 |
-
$this->numObj++;
|
2380 |
-
$this->o_outlines($this->numObj, 'new');
|
2381 |
-
|
2382 |
-
$this->numObj++;
|
2383 |
-
$this->o_pages($this->numObj, 'new');
|
2384 |
-
|
2385 |
-
$this->o_pages($this->numObj, 'mediaBox', $pageSize);
|
2386 |
-
$this->currentNode = 3;
|
2387 |
-
|
2388 |
-
$this->numObj++;
|
2389 |
-
$this->o_procset($this->numObj, 'new');
|
2390 |
-
|
2391 |
-
$this->numObj++;
|
2392 |
-
$this->o_info($this->numObj, 'new');
|
2393 |
-
|
2394 |
-
$this->numObj++;
|
2395 |
-
$this->o_page($this->numObj, 'new');
|
2396 |
-
|
2397 |
-
// need to store the first page id as there is no way to get it to the user during
|
2398 |
-
// startup
|
2399 |
-
$this->firstPageId = $this->currentContents;
|
2400 |
-
}
|
2401 |
-
|
2402 |
-
/**
|
2403 |
-
* open the font file and return a php structure containing it.
|
2404 |
-
* first check if this one has been done before and saved in a form more suited to php
|
2405 |
-
* note that if a php serialized version does not exist it will try and make one, but will
|
2406 |
-
* require write access to the directory to do it... it is MUCH faster to have these serialized
|
2407 |
-
* files.
|
2408 |
-
*
|
2409 |
-
* @param $font
|
2410 |
-
*/
|
2411 |
-
private function openFont($font)
|
2412 |
-
{
|
2413 |
-
// assume that $font contains the path and file but not the extension
|
2414 |
-
$name = basename($font);
|
2415 |
-
$dir = dirname($font) . '/';
|
2416 |
-
|
2417 |
-
$fontcache = $this->fontcache;
|
2418 |
-
if ($fontcache == '') {
|
2419 |
-
$fontcache = rtrim($dir, DIRECTORY_SEPARATOR."/\\");
|
2420 |
-
}
|
2421 |
-
|
2422 |
-
//$name filename without folder and extension of font metrics
|
2423 |
-
//$dir folder of font metrics
|
2424 |
-
//$fontcache folder of runtime created php serialized version of font metrics.
|
2425 |
-
// If this is not given, the same folder as the font metrics will be used.
|
2426 |
-
// Storing and reusing serialized versions improves speed much
|
2427 |
-
|
2428 |
-
$this->addMessage("openFont: $font - $name");
|
2429 |
-
|
2430 |
-
if (!$this->isUnicode || in_array(mb_strtolower(basename($name)), self::$coreFonts)) {
|
2431 |
-
$metrics_name = "$name.afm";
|
2432 |
-
} else {
|
2433 |
-
$metrics_name = "$name.ufm";
|
2434 |
-
}
|
2435 |
-
|
2436 |
-
$cache_name = "$metrics_name.php";
|
2437 |
-
$this->addMessage("metrics: $metrics_name, cache: $cache_name");
|
2438 |
-
|
2439 |
-
if (file_exists($fontcache . '/' . $cache_name)) {
|
2440 |
-
$this->addMessage("openFont: php file exists $fontcache/$cache_name");
|
2441 |
-
$this->fonts[$font] = require($fontcache . '/' . $cache_name);
|
2442 |
-
|
2443 |
-
if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) {
|
2444 |
-
// if the font file is old, then clear it out and prepare for re-creation
|
2445 |
-
$this->addMessage('openFont: clear out, make way for new version.');
|
2446 |
-
$this->fonts[$font] = null;
|
2447 |
-
unset($this->fonts[$font]);
|
2448 |
-
}
|
2449 |
-
} else {
|
2450 |
-
$old_cache_name = "php_$metrics_name";
|
2451 |
-
if (file_exists($fontcache . '/' . $old_cache_name)) {
|
2452 |
-
$this->addMessage(
|
2453 |
-
"openFont: php file doesn't exist $fontcache/$cache_name, creating it from the old format"
|
2454 |
-
);
|
2455 |
-
$old_cache = file_get_contents($fontcache . '/' . $old_cache_name);
|
2456 |
-
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . $old_cache . ';');
|
2457 |
-
|
2458 |
-
$this->openFont($font);
|
2459 |
-
return;
|
2460 |
-
}
|
2461 |
-
}
|
2462 |
-
|
2463 |
-
if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
|
2464 |
-
// then rebuild the php_<font>.afm file from the <font>.afm file
|
2465 |
-
$this->addMessage("openFont: build php file from $dir$metrics_name");
|
2466 |
-
$data = array();
|
2467 |
-
|
2468 |
-
// 20 => 'space'
|
2469 |
-
$data['codeToName'] = array();
|
2470 |
-
|
2471 |
-
// Since we're not going to enable Unicode for the core fonts we need to use a font-based
|
2472 |
-
// setting for Unicode support rather than a global setting.
|
2473 |
-
$data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm');
|
2474 |
-
|
2475 |
-
$cidtogid = '';
|
2476 |
-
if ($data['isUnicode']) {
|
2477 |
-
$cidtogid = str_pad('', 256 * 256 * 2, "\x00");
|
2478 |
-
}
|
2479 |
-
|
2480 |
-
$file = file($dir . $metrics_name);
|
2481 |
-
|
2482 |
-
foreach ($file as $rowA) {
|
2483 |
-
$row = trim($rowA);
|
2484 |
-
$pos = strpos($row, ' ');
|
2485 |
-
|
2486 |
-
if ($pos) {
|
2487 |
-
// then there must be some keyword
|
2488 |
-
$key = substr($row, 0, $pos);
|
2489 |
-
switch ($key) {
|
2490 |
-
case 'FontName':
|
2491 |
-
case 'FullName':
|
2492 |
-
case 'FamilyName':
|
2493 |
-
case 'PostScriptName':
|
2494 |
-
case 'Weight':
|
2495 |
-
case 'ItalicAngle':
|
2496 |
-
case 'IsFixedPitch':
|
2497 |
-
case 'CharacterSet':
|
2498 |
-
case 'UnderlinePosition':
|
2499 |
-
case 'UnderlineThickness':
|
2500 |
-
case 'Version':
|
2501 |
-
case 'EncodingScheme':
|
2502 |
-
case 'CapHeight':
|
2503 |
-
case 'XHeight':
|
2504 |
-
case 'Ascender':
|
2505 |
-
case 'Descender':
|
2506 |
-
case 'StdHW':
|
2507 |
-
case 'StdVW':
|
2508 |
-
case 'StartCharMetrics':
|
2509 |
-
case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font. Otherwise it's too big.
|
2510 |
-
$data[$key] = trim(substr($row, $pos));
|
2511 |
-
break;
|
2512 |
-
|
2513 |
-
case 'FontBBox':
|
2514 |
-
$data[$key] = explode(' ', trim(substr($row, $pos)));
|
2515 |
-
break;
|
2516 |
-
|
2517 |
-
//C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
|
2518 |
-
case 'C': // Found in AFM files
|
2519 |
-
$bits = explode(';', trim($row));
|
2520 |
-
$dtmp = array();
|
2521 |
-
|
2522 |
-
foreach ($bits as $bit) {
|
2523 |
-
$bits2 = explode(' ', trim($bit));
|
2524 |
-
if (mb_strlen($bits2[0], '8bit') == 0) {
|
2525 |
-
continue;
|
2526 |
-
}
|
2527 |
-
|
2528 |
-
if (count($bits2) > 2) {
|
2529 |
-
$dtmp[$bits2[0]] = array();
|
2530 |
-
for ($i = 1; $i < count($bits2); $i++) {
|
2531 |
-
$dtmp[$bits2[0]][] = $bits2[$i];
|
2532 |
-
}
|
2533 |
-
} else {
|
2534 |
-
if (count($bits2) == 2) {
|
2535 |
-
$dtmp[$bits2[0]] = $bits2[1];
|
2536 |
-
}
|
2537 |
-
}
|
2538 |
-
}
|
2539 |
-
|
2540 |
-
$c = (int)$dtmp['C'];
|
2541 |
-
$n = $dtmp['N'];
|
2542 |
-
$width = floatval($dtmp['WX']);
|
2543 |
-
|
2544 |
-
if ($c >= 0) {
|
2545 |
-
if ($c != hexdec($n)) {
|
2546 |
-
$data['codeToName'][$c] = $n;
|
2547 |
-
}
|
2548 |
-
$data['C'][$c] = $width;
|
2549 |
-
} else {
|
2550 |
-
$data['C'][$n] = $width;
|
2551 |
-
}
|
2552 |
-
|
2553 |
-
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2554 |
-
$data['MissingWidth'] = $width;
|
2555 |
-
}
|
2556 |
-
|
2557 |
-
break;
|
2558 |
-
|
2559 |
-
// U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
|
2560 |
-
case 'U': // Found in UFM files
|
2561 |
-
if (!$data['isUnicode']) {
|
2562 |
-
break;
|
2563 |
-
}
|
2564 |
-
|
2565 |
-
$bits = explode(';', trim($row));
|
2566 |
-
$dtmp = array();
|
2567 |
-
|
2568 |
-
foreach ($bits as $bit) {
|
2569 |
-
$bits2 = explode(' ', trim($bit));
|
2570 |
-
if (mb_strlen($bits2[0], '8bit') === 0) {
|
2571 |
-
continue;
|
2572 |
-
}
|
2573 |
-
|
2574 |
-
if (count($bits2) > 2) {
|
2575 |
-
$dtmp[$bits2[0]] = array();
|
2576 |
-
for ($i = 1; $i < count($bits2); $i++) {
|
2577 |
-
$dtmp[$bits2[0]][] = $bits2[$i];
|
2578 |
-
}
|
2579 |
-
} else {
|
2580 |
-
if (count($bits2) == 2) {
|
2581 |
-
$dtmp[$bits2[0]] = $bits2[1];
|
2582 |
-
}
|
2583 |
-
}
|
2584 |
-
}
|
2585 |
-
|
2586 |
-
$c = (int)$dtmp['U'];
|
2587 |
-
$n = $dtmp['N'];
|
2588 |
-
$glyph = $dtmp['G'];
|
2589 |
-
$width = floatval($dtmp['WX']);
|
2590 |
-
|
2591 |
-
if ($c >= 0) {
|
2592 |
-
// Set values in CID to GID map
|
2593 |
-
if ($c >= 0 && $c < 0xFFFF && $glyph) {
|
2594 |
-
$cidtogid[$c * 2] = chr($glyph >> 8);
|
2595 |
-
$cidtogid[$c * 2 + 1] = chr($glyph & 0xFF);
|
2596 |
-
}
|
2597 |
-
|
2598 |
-
if ($c != hexdec($n)) {
|
2599 |
-
$data['codeToName'][$c] = $n;
|
2600 |
-
}
|
2601 |
-
$data['C'][$c] = $width;
|
2602 |
-
} else {
|
2603 |
-
$data['C'][$n] = $width;
|
2604 |
-
}
|
2605 |
-
|
2606 |
-
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2607 |
-
$data['MissingWidth'] = $width;
|
2608 |
-
}
|
2609 |
-
|
2610 |
-
break;
|
2611 |
-
|
2612 |
-
case 'KPX':
|
2613 |
-
break; // don't include them as they are not used yet
|
2614 |
-
//KPX Adieresis yacute -40
|
2615 |
-
/*$bits = explode(' ', trim($row));
|
2616 |
-
$data['KPX'][$bits[1]][$bits[2]] = $bits[3];
|
2617 |
-
break;*/
|
2618 |
-
}
|
2619 |
-
}
|
2620 |
-
}
|
2621 |
-
|
2622 |
-
if ($this->compressionReady && $this->options['compression']) {
|
2623 |
-
// then implement ZLIB based compression on CIDtoGID string
|
2624 |
-
$data['CIDtoGID_Compressed'] = true;
|
2625 |
-
$cidtogid = gzcompress($cidtogid, 6);
|
2626 |
-
}
|
2627 |
-
$data['CIDtoGID'] = base64_encode($cidtogid);
|
2628 |
-
$data['_version_'] = $this->fontcacheVersion;
|
2629 |
-
$this->fonts[$font] = $data;
|
2630 |
-
|
2631 |
-
//Because of potential trouble with php safe mode, expect that the folder already exists.
|
2632 |
-
//If not existing, this will hit performance because of missing cached results.
|
2633 |
-
if (is_dir($fontcache) && is_writable($fontcache)) {
|
2634 |
-
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . var_export($data, true) . ';');
|
2635 |
-
}
|
2636 |
-
$data = null;
|
2637 |
-
}
|
2638 |
-
|
2639 |
-
if (!isset($this->fonts[$font])) {
|
2640 |
-
$this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
|
2641 |
-
}
|
2642 |
-
|
2643 |
-
//pre_r($this->messages);
|
2644 |
-
}
|
2645 |
-
|
2646 |
-
/**
|
2647 |
-
* if the font is not loaded then load it and make the required object
|
2648 |
-
* else just make it the current font
|
2649 |
-
* the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding'
|
2650 |
-
* note that encoding='none' will need to be used for symbolic fonts
|
2651 |
-
* and 'differences' => an array of mappings between numbers 0->255 and character names.
|
2652 |
-
*
|
2653 |
-
* @param $fontName
|
2654 |
-
* @param string $encoding
|
2655 |
-
* @param bool $set
|
2656 |
-
* @return int
|
2657 |
-
*/
|
2658 |
-
function selectFont($fontName, $encoding = '', $set = true)
|
2659 |
-
{
|
2660 |
-
$ext = substr($fontName, -4);
|
2661 |
-
if ($ext === '.afm' || $ext === '.ufm') {
|
2662 |
-
$fontName = substr($fontName, 0, mb_strlen($fontName) - 4);
|
2663 |
-
}
|
2664 |
-
|
2665 |
-
if (!isset($this->fonts[$fontName])) {
|
2666 |
-
$this->addMessage("selectFont: selecting - $fontName - $encoding, $set");
|
2667 |
-
|
2668 |
-
// load the file
|
2669 |
-
$this->openFont($fontName);
|
2670 |
-
|
2671 |
-
if (isset($this->fonts[$fontName])) {
|
2672 |
-
$this->numObj++;
|
2673 |
-
$this->numFonts++;
|
2674 |
-
|
2675 |
-
$font = &$this->fonts[$fontName];
|
2676 |
-
|
2677 |
-
$name = basename($fontName);
|
2678 |
-
$dir = dirname($fontName) . '/';
|
2679 |
-
$options = array('name' => $name, 'fontFileName' => $fontName);
|
2680 |
-
|
2681 |
-
if (is_array($encoding)) {
|
2682 |
-
// then encoding and differences might be set
|
2683 |
-
if (isset($encoding['encoding'])) {
|
2684 |
-
$options['encoding'] = $encoding['encoding'];
|
2685 |
-
}
|
2686 |
-
|
2687 |
-
if (isset($encoding['differences'])) {
|
2688 |
-
$options['differences'] = $encoding['differences'];
|
2689 |
-
}
|
2690 |
-
} else {
|
2691 |
-
if (mb_strlen($encoding, '8bit')) {
|
2692 |
-
// then perhaps only the encoding has been set
|
2693 |
-
$options['encoding'] = $encoding;
|
2694 |
-
}
|
2695 |
-
}
|
2696 |
-
|
2697 |
-
$fontObj = $this->numObj;
|
2698 |
-
$this->o_font($this->numObj, 'new', $options);
|
2699 |
-
$font['fontNum'] = $this->numFonts;
|
2700 |
-
|
2701 |
-
// if this is a '.afm' font, and there is a '.pfa' file to go with it (as there
|
2702 |
-
// should be for all non-basic fonts), then load it into an object and put the
|
2703 |
-
// references into the font object
|
2704 |
-
$basefile = $fontName;
|
2705 |
-
|
2706 |
-
$fbtype = '';
|
2707 |
-
if (file_exists("$basefile.ttf")) {
|
2708 |
-
$fbtype = 'ttf';
|
2709 |
-
} elseif (file_exists("$basefile.TTF")) {
|
2710 |
-
$fbtype = 'TTF';
|
2711 |
-
} elseif (file_exists("$basefile.pfb")) {
|
2712 |
-
$fbtype = 'pfb';
|
2713 |
-
} elseif (file_exists("$basefile.PFB")) {
|
2714 |
-
$fbtype = 'PFB';
|
2715 |
-
}
|
2716 |
-
|
2717 |
-
$fbfile = "$basefile.$fbtype";
|
2718 |
-
|
2719 |
-
// $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
|
2720 |
-
// $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
|
2721 |
-
$this->addMessage('selectFont: checking for - ' . $fbfile);
|
2722 |
-
|
2723 |
-
// OAR - I don't understand this old check
|
2724 |
-
// if (substr($fontName, -4) === '.afm' && strlen($fbtype)) {
|
2725 |
-
if ($fbtype) {
|
2726 |
-
$adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
|
2727 |
-
// $fontObj = $this->numObj;
|
2728 |
-
$this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
|
2729 |
-
|
2730 |
-
// find the array of font widths, and put that into an object.
|
2731 |
-
$firstChar = -1;
|
2732 |
-
$lastChar = 0;
|
2733 |
-
$widths = array();
|
2734 |
-
$cid_widths = array();
|
2735 |
-
|
2736 |
-
foreach ($font['C'] as $num => $d) {
|
2737 |
-
if (intval($num) > 0 || $num == '0') {
|
2738 |
-
if (!$font['isUnicode']) {
|
2739 |
-
// With Unicode, widths array isn't used
|
2740 |
-
if ($lastChar > 0 && $num > $lastChar + 1) {
|
2741 |
-
for ($i = $lastChar + 1; $i < $num; $i++) {
|
2742 |
-
$widths[] = 0;
|
2743 |
-
}
|
2744 |
-
}
|
2745 |
-
}
|
2746 |
-
|
2747 |
-
$widths[] = $d;
|
2748 |
-
|
2749 |
-
if ($font['isUnicode']) {
|
2750 |
-
$cid_widths[$num] = $d;
|
2751 |
-
}
|
2752 |
-
|
2753 |
-
if ($firstChar == -1) {
|
2754 |
-
$firstChar = $num;
|
2755 |
-
}
|
2756 |
-
|
2757 |
-
$lastChar = $num;
|
2758 |
-
}
|
2759 |
-
}
|
2760 |
-
|
2761 |
-
// also need to adjust the widths for the differences array
|
2762 |
-
if (isset($options['differences'])) {
|
2763 |
-
foreach ($options['differences'] as $charNum => $charName) {
|
2764 |
-
if ($charNum > $lastChar) {
|
2765 |
-
if (!$font['isUnicode']) {
|
2766 |
-
// With Unicode, widths array isn't used
|
2767 |
-
for ($i = $lastChar + 1; $i <= $charNum; $i++) {
|
2768 |
-
$widths[] = 0;
|
2769 |
-
}
|
2770 |
-
}
|
2771 |
-
|
2772 |
-
$lastChar = $charNum;
|
2773 |
-
}
|
2774 |
-
|
2775 |
-
if (isset($font['C'][$charName])) {
|
2776 |
-
$widths[$charNum - $firstChar] = $font['C'][$charName];
|
2777 |
-
if ($font['isUnicode']) {
|
2778 |
-
$cid_widths[$charName] = $font['C'][$charName];
|
2779 |
-
}
|
2780 |
-
}
|
2781 |
-
}
|
2782 |
-
}
|
2783 |
-
|
2784 |
-
if ($font['isUnicode']) {
|
2785 |
-
$font['CIDWidths'] = $cid_widths;
|
2786 |
-
}
|
2787 |
-
|
2788 |
-
$this->addMessage('selectFont: FirstChar = ' . $firstChar);
|
2789 |
-
$this->addMessage('selectFont: LastChar = ' . $lastChar);
|
2790 |
-
|
2791 |
-
$widthid = -1;
|
2792 |
-
|
2793 |
-
if (!$font['isUnicode']) {
|
2794 |
-
// With Unicode, widths array isn't used
|
2795 |
-
|
2796 |
-
$this->numObj++;
|
2797 |
-
$this->o_contents($this->numObj, 'new', 'raw');
|
2798 |
-
$this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
|
2799 |
-
$widthid = $this->numObj;
|
2800 |
-
}
|
2801 |
-
|
2802 |
-
$missing_width = 500;
|
2803 |
-
$stemV = 70;
|
2804 |
-
|
2805 |
-
if (isset($font['MissingWidth'])) {
|
2806 |
-
$missing_width = $font['MissingWidth'];
|
2807 |
-
}
|
2808 |
-
if (isset($font['StdVW'])) {
|
2809 |
-
$stemV = $font['StdVW'];
|
2810 |
-
} else {
|
2811 |
-
if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
|
2812 |
-
$stemV = 120;
|
2813 |
-
}
|
2814 |
-
}
|
2815 |
-
|
2816 |
-
// load the pfb file, and put that into an object too.
|
2817 |
-
// note that pdf supports only binary format type 1 font files, though there is a
|
2818 |
-
// simple utility to convert them from pfa to pfb.
|
2819 |
-
// FIXME: should we move font subset creation to CPDF::output? See notes in issue #750.
|
2820 |
-
if (!$this->isUnicode || strtolower($fbtype) !== 'ttf' || empty($this->stringSubsets)) {
|
2821 |
-
$data = file_get_contents($fbfile);
|
2822 |
-
} else {
|
2823 |
-
$this->stringSubsets[$fontName][] = 32; // Force space if not in yet
|
2824 |
-
|
2825 |
-
$subset = $this->stringSubsets[$fontName];
|
2826 |
-
sort($subset);
|
2827 |
-
|
2828 |
-
// Load font
|
2829 |
-
$font_obj = Font::load($fbfile);
|
2830 |
-
$font_obj->parse();
|
2831 |
-
|
2832 |
-
// Define subset
|
2833 |
-
$font_obj->setSubset($subset);
|
2834 |
-
$font_obj->reduce();
|
2835 |
-
|
2836 |
-
// Write new font
|
2837 |
-
$tmp_name = $this->tmp . "/" . basename($fbfile) . ".tmp." . uniqid();
|
2838 |
-
$font_obj->open($tmp_name, BinaryStream::modeWrite);
|
2839 |
-
$font_obj->encode(array("OS/2"));
|
2840 |
-
$font_obj->close();
|
2841 |
-
|
2842 |
-
// Parse the new font to get cid2gid and widths
|
2843 |
-
$font_obj = Font::load($tmp_name);
|
2844 |
-
|
2845 |
-
// Find Unicode char map table
|
2846 |
-
$subtable = null;
|
2847 |
-
foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
|
2848 |
-
if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
|
2849 |
-
$subtable = $_subtable;
|
2850 |
-
break;
|
2851 |
-
}
|
2852 |
-
}
|
2853 |
-
|
2854 |
-
if ($subtable) {
|
2855 |
-
$glyphIndexArray = $subtable["glyphIndexArray"];
|
2856 |
-
$hmtx = $font_obj->getData("hmtx");
|
2857 |
-
|
2858 |
-
unset($glyphIndexArray[0xFFFF]);
|
2859 |
-
|
2860 |
-
$cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00");
|
2861 |
-
$font['CIDWidths'] = array();
|
2862 |
-
foreach ($glyphIndexArray as $cid => $gid) {
|
2863 |
-
if ($cid >= 0 && $cid < 0xFFFF && $gid) {
|
2864 |
-
$cidtogid[$cid * 2] = chr($gid >> 8);
|
2865 |
-
$cidtogid[$cid * 2 + 1] = chr($gid & 0xFF);
|
2866 |
-
}
|
2867 |
-
|
2868 |
-
$width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
|
2869 |
-
$font['CIDWidths'][$cid] = $width;
|
2870 |
-
}
|
2871 |
-
|
2872 |
-
$font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
|
2873 |
-
$font['CIDtoGID_Compressed'] = true;
|
2874 |
-
|
2875 |
-
$data = file_get_contents($tmp_name);
|
2876 |
-
} else {
|
2877 |
-
$data = file_get_contents($fbfile);
|
2878 |
-
}
|
2879 |
-
|
2880 |
-
$font_obj->close();
|
2881 |
-
unlink($tmp_name);
|
2882 |
-
}
|
2883 |
-
|
2884 |
-
// create the font descriptor
|
2885 |
-
$this->numObj++;
|
2886 |
-
$fontDescriptorId = $this->numObj;
|
2887 |
-
|
2888 |
-
$this->numObj++;
|
2889 |
-
$pfbid = $this->numObj;
|
2890 |
-
|
2891 |
-
// determine flags (more than a little flakey, hopefully will not matter much)
|
2892 |
-
$flags = 0;
|
2893 |
-
|
2894 |
-
if ($font['ItalicAngle'] != 0) {
|
2895 |
-
$flags += pow(2, 6);
|
2896 |
-
}
|
2897 |
-
|
2898 |
-
if ($font['IsFixedPitch'] === 'true') {
|
2899 |
-
$flags += 1;
|
2900 |
-
}
|
2901 |
-
|
2902 |
-
$flags += pow(2, 5); // assume non-sybolic
|
2903 |
-
$list = array(
|
2904 |
-
'Ascent' => 'Ascender',
|
2905 |
-
'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight'
|
2906 |
-
'MissingWidth' => 'MissingWidth',
|
2907 |
-
'Descent' => 'Descender',
|
2908 |
-
'FontBBox' => 'FontBBox',
|
2909 |
-
'ItalicAngle' => 'ItalicAngle'
|
2910 |
-
);
|
2911 |
-
$fdopt = array(
|
2912 |
-
'Flags' => $flags,
|
2913 |
-
'FontName' => $adobeFontName,
|
2914 |
-
'StemV' => $stemV
|
2915 |
-
);
|
2916 |
-
|
2917 |
-
foreach ($list as $k => $v) {
|
2918 |
-
if (isset($font[$v])) {
|
2919 |
-
$fdopt[$k] = $font[$v];
|
2920 |
-
}
|
2921 |
-
}
|
2922 |
-
|
2923 |
-
if (strtolower($fbtype) === 'pfb') {
|
2924 |
-
$fdopt['FontFile'] = $pfbid;
|
2925 |
-
} elseif (strtolower($fbtype) === 'ttf') {
|
2926 |
-
$fdopt['FontFile2'] = $pfbid;
|
2927 |
-
}
|
2928 |
-
|
2929 |
-
$this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
|
2930 |
-
|
2931 |
-
// embed the font program
|
2932 |
-
$this->o_contents($this->numObj, 'new');
|
2933 |
-
$this->objects[$pfbid]['c'] .= $data;
|
2934 |
-
|
2935 |
-
// determine the cruicial lengths within this file
|
2936 |
-
if (strtolower($fbtype) === 'pfb') {
|
2937 |
-
$l1 = strpos($data, 'eexec') + 6;
|
2938 |
-
$l2 = strpos($data, '00000000') - $l1;
|
2939 |
-
$l3 = mb_strlen($data, '8bit') - $l2 - $l1;
|
2940 |
-
$this->o_contents(
|
2941 |
-
$this->numObj,
|
2942 |
-
'add',
|
2943 |
-
array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)
|
2944 |
-
);
|
2945 |
-
} elseif (strtolower($fbtype) == 'ttf') {
|
2946 |
-
$l1 = mb_strlen($data, '8bit');
|
2947 |
-
$this->o_contents($this->numObj, 'add', array('Length1' => $l1));
|
2948 |
-
}
|
2949 |
-
|
2950 |
-
// tell the font object about all this new stuff
|
2951 |
-
$tmp = array(
|
2952 |
-
'BaseFont' => $adobeFontName,
|
2953 |
-
'MissingWidth' => $missing_width,
|
2954 |
-
'Widths' => $widthid,
|
2955 |
-
'FirstChar' => $firstChar,
|
2956 |
-
'LastChar' => $lastChar,
|
2957 |
-
'FontDescriptor' => $fontDescriptorId
|
2958 |
-
);
|
2959 |
-
|
2960 |
-
if (strtolower($fbtype) === 'ttf') {
|
2961 |
-
$tmp['SubType'] = 'TrueType';
|
2962 |
-
}
|
2963 |
-
|
2964 |
-
$this->addMessage("adding extra info to font.($fontObj)");
|
2965 |
-
|
2966 |
-
foreach ($tmp as $fk => $fv) {
|
2967 |
-
$this->addMessage("$fk : $fv");
|
2968 |
-
}
|
2969 |
-
|
2970 |
-
$this->o_font($fontObj, 'add', $tmp);
|
2971 |
-
} else {
|
2972 |
-
$this->addMessage(
|
2973 |
-
'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
|
2974 |
-
);
|
2975 |
-
}
|
2976 |
-
|
2977 |
-
// also set the differences here, note that this means that these will take effect only the
|
2978 |
-
//first time that a font is selected, else they are ignored
|
2979 |
-
if (isset($options['differences'])) {
|
2980 |
-
$font['differences'] = $options['differences'];
|
2981 |
-
}
|
2982 |
-
}
|
2983 |
-
}
|
2984 |
-
|
2985 |
-
if ($set && isset($this->fonts[$fontName])) {
|
2986 |
-
// so if for some reason the font was not set in the last one then it will not be selected
|
2987 |
-
$this->currentBaseFont = $fontName;
|
2988 |
-
|
2989 |
-
// the next lines mean that if a new font is selected, then the current text state will be
|
2990 |
-
// applied to it as well.
|
2991 |
-
$this->currentFont = $this->currentBaseFont;
|
2992 |
-
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
2993 |
-
|
2994 |
-
//$this->setCurrentFont();
|
2995 |
-
}
|
2996 |
-
|
2997 |
-
return $this->currentFontNum;
|
2998 |
-
//return $this->numObj;
|
2999 |
-
}
|
3000 |
-
|
3001 |
-
/**
|
3002 |
-
* sets up the current font, based on the font families, and the current text state
|
3003 |
-
* note that this system is quite flexible, a bold-italic font can be completely different to a
|
3004 |
-
* italic-bold font, and even bold-bold will have to be defined within the family to have meaning
|
3005 |
-
* This function is to be called whenever the currentTextState is changed, it will update
|
3006 |
-
* the currentFont setting to whatever the appropriate family one is.
|
3007 |
-
* If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
|
3008 |
-
* This function will change the currentFont to whatever it should be, but will not change the
|
3009 |
-
* currentBaseFont.
|
3010 |
-
*/
|
3011 |
-
private function setCurrentFont()
|
3012 |
-
{
|
3013 |
-
// if (strlen($this->currentBaseFont) == 0){
|
3014 |
-
// // then assume an initial font
|
3015 |
-
// $this->selectFont($this->defaultFont);
|
3016 |
-
// }
|
3017 |
-
// $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1);
|
3018 |
-
// if (strlen($this->currentTextState)
|
3019 |
-
// && isset($this->fontFamilies[$cf])
|
3020 |
-
// && isset($this->fontFamilies[$cf][$this->currentTextState])){
|
3021 |
-
// // then we are in some state or another
|
3022 |
-
// // and this font has a family, and the current setting exists within it
|
3023 |
-
// // select the font, then return it
|
3024 |
-
// $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState];
|
3025 |
-
// $this->selectFont($nf,'',0);
|
3026 |
-
// $this->currentFont = $nf;
|
3027 |
-
// $this->currentFontNum = $this->fonts[$nf]['fontNum'];
|
3028 |
-
// } else {
|
3029 |
-
// // the this font must not have the right family member for the current state
|
3030 |
-
// // simply assume the base font
|
3031 |
-
$this->currentFont = $this->currentBaseFont;
|
3032 |
-
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
3033 |
-
// }
|
3034 |
-
}
|
3035 |
-
|
3036 |
-
/**
|
3037 |
-
* function for the user to find out what the ID is of the first page that was created during
|
3038 |
-
* startup - useful if they wish to add something to it later.
|
3039 |
-
*
|
3040 |
-
* @return int
|
3041 |
-
*/
|
3042 |
-
function getFirstPageId()
|
3043 |
-
{
|
3044 |
-
return $this->firstPageId;
|
3045 |
-
}
|
3046 |
-
|
3047 |
-
/**
|
3048 |
-
* add content to the currently active object
|
3049 |
-
*
|
3050 |
-
* @param $content
|
3051 |
-
*/
|
3052 |
-
private function addContent($content)
|
3053 |
-
{
|
3054 |
-
$this->objects[$this->currentContents]['c'] .= $content;
|
3055 |
-
}
|
3056 |
-
|
3057 |
-
/**
|
3058 |
-
* sets the color for fill operations
|
3059 |
-
*
|
3060 |
-
* @param $color
|
3061 |
-
* @param bool $force
|
3062 |
-
*/
|
3063 |
-
function setColor($color, $force = false)
|
3064 |
-
{
|
3065 |
-
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3066 |
-
|
3067 |
-
if (!$force && $this->currentColor == $new_color) {
|
3068 |
-
return;
|
3069 |
-
}
|
3070 |
-
|
3071 |
-
if (isset($new_color[3])) {
|
3072 |
-
$this->currentColor = $new_color;
|
3073 |
-
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColor));
|
3074 |
-
} else {
|
3075 |
-
if (isset($new_color[2])) {
|
3076 |
-
$this->currentColor = $new_color;
|
3077 |
-
$this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColor));
|
3078 |
-
}
|
3079 |
-
}
|
3080 |
-
}
|
3081 |
-
|
3082 |
-
/**
|
3083 |
-
* sets the color for fill operations
|
3084 |
-
*
|
3085 |
-
* @param $fillRule
|
3086 |
-
*/
|
3087 |
-
function setFillRule($fillRule)
|
3088 |
-
{
|
3089 |
-
if (!in_array($fillRule, array("nonzero", "evenodd"))) {
|
3090 |
-
return;
|
3091 |
-
}
|
3092 |
-
|
3093 |
-
$this->fillRule = $fillRule;
|
3094 |
-
}
|
3095 |
-
|
3096 |
-
/**
|
3097 |
-
* sets the color for stroke operations
|
3098 |
-
*
|
3099 |
-
* @param $color
|
3100 |
-
* @param bool $force
|
3101 |
-
*/
|
3102 |
-
function setStrokeColor($color, $force = false)
|
3103 |
-
{
|
3104 |
-
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3105 |
-
|
3106 |
-
if (!$force && $this->currentStrokeColor == $new_color) {
|
3107 |
-
return;
|
3108 |
-
}
|
3109 |
-
|
3110 |
-
if (isset($new_color[3])) {
|
3111 |
-
$this->currentStrokeColor = $new_color;
|
3112 |
-
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColor));
|
3113 |
-
} else {
|
3114 |
-
if (isset($new_color[2])) {
|
3115 |
-
$this->currentStrokeColor = $new_color;
|
3116 |
-
$this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColor));
|
3117 |
-
}
|
3118 |
-
}
|
3119 |
-
}
|
3120 |
-
|
3121 |
-
/**
|
3122 |
-
* Set the graphics state for compositions
|
3123 |
-
*
|
3124 |
-
* @param $parameters
|
3125 |
-
*/
|
3126 |
-
function setGraphicsState($parameters)
|
3127 |
-
{
|
3128 |
-
// Create a new graphics state object if necessary
|
3129 |
-
if (($gstate = array_search($parameters, $this->gstates)) === false) {
|
3130 |
-
$this->numObj++;
|
3131 |
-
$this->o_extGState($this->numObj, 'new', $parameters);
|
3132 |
-
$gstate = $this->numStates;
|
3133 |
-
$this->gstates[$gstate] = $parameters;
|
3134 |
-
}
|
3135 |
-
$this->addContent("\n/GS$gstate gs");
|
3136 |
-
}
|
3137 |
-
|
3138 |
-
/**
|
3139 |
-
* Set current blend mode & opacity for lines.
|
3140 |
-
*
|
3141 |
-
* Valid blend modes are:
|
3142 |
-
*
|
3143 |
-
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3144 |
-
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3145 |
-
* Exclusion
|
3146 |
-
*
|
3147 |
-
* @param string $mode the blend mode to use
|
3148 |
-
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3149 |
-
*/
|
3150 |
-
function setLineTransparency($mode, $opacity)
|
3151 |
-
{
|
3152 |
-
static $blend_modes = array(
|
3153 |
-
"Normal",
|
3154 |
-
"Multiply",
|
3155 |
-
"Screen",
|
3156 |
-
"Overlay",
|
3157 |
-
"Darken",
|
3158 |
-
"Lighten",
|
3159 |
-
"ColorDogde",
|
3160 |
-
"ColorBurn",
|
3161 |
-
"HardLight",
|
3162 |
-
"SoftLight",
|
3163 |
-
"Difference",
|
3164 |
-
"Exclusion"
|
3165 |
-
);
|
3166 |
-
|
3167 |
-
if (!in_array($mode, $blend_modes)) {
|
3168 |
-
$mode = "Normal";
|
3169 |
-
}
|
3170 |
-
|
3171 |
-
// Only create a new graphics state if required
|
3172 |
-
if ($mode === $this->currentLineTransparency["mode"] &&
|
3173 |
-
$opacity == $this->currentLineTransparency["opacity"]
|
3174 |
-
) {
|
3175 |
-
return;
|
3176 |
-
}
|
3177 |
-
|
3178 |
-
$this->currentLineTransparency["mode"] = $mode;
|
3179 |
-
$this->currentLineTransparency["opacity"] = $opacity;
|
3180 |
-
|
3181 |
-
$options = array(
|
3182 |
-
"BM" => "/$mode",
|
3183 |
-
"CA" => (float)$opacity
|
3184 |
-
);
|
3185 |
-
|
3186 |
-
$this->setGraphicsState($options);
|
3187 |
-
}
|
3188 |
-
|
3189 |
-
/**
|
3190 |
-
* Set current blend mode & opacity for filled objects.
|
3191 |
-
*
|
3192 |
-
* Valid blend modes are:
|
3193 |
-
*
|
3194 |
-
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3195 |
-
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3196 |
-
* Exclusion
|
3197 |
-
*
|
3198 |
-
* @param string $mode the blend mode to use
|
3199 |
-
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3200 |
-
*/
|
3201 |
-
function setFillTransparency($mode, $opacity)
|
3202 |
-
{
|
3203 |
-
static $blend_modes = array(
|
3204 |
-
"Normal",
|
3205 |
-
"Multiply",
|
3206 |
-
"Screen",
|
3207 |
-
"Overlay",
|
3208 |
-
"Darken",
|
3209 |
-
"Lighten",
|
3210 |
-
"ColorDogde",
|
3211 |
-
"ColorBurn",
|
3212 |
-
"HardLight",
|
3213 |
-
"SoftLight",
|
3214 |
-
"Difference",
|
3215 |
-
"Exclusion"
|
3216 |
-
);
|
3217 |
-
|
3218 |
-
if (!in_array($mode, $blend_modes)) {
|
3219 |
-
$mode = "Normal";
|
3220 |
-
}
|
3221 |
-
|
3222 |
-
if ($mode === $this->currentFillTransparency["mode"] &&
|
3223 |
-
$opacity == $this->currentFillTransparency["opacity"]
|
3224 |
-
) {
|
3225 |
-
return;
|
3226 |
-
}
|
3227 |
-
|
3228 |
-
$this->currentFillTransparency["mode"] = $mode;
|
3229 |
-
$this->currentFillTransparency["opacity"] = $opacity;
|
3230 |
-
|
3231 |
-
$options = array(
|
3232 |
-
"BM" => "/$mode",
|
3233 |
-
"ca" => (float)$opacity,
|
3234 |
-
);
|
3235 |
-
|
3236 |
-
$this->setGraphicsState($options);
|
3237 |
-
}
|
3238 |
-
|
3239 |
-
/**
|
3240 |
-
* draw a line from one set of coordinates to another
|
3241 |
-
*
|
3242 |
-
* @param $x1
|
3243 |
-
* @param $y1
|
3244 |
-
* @param $x2
|
3245 |
-
* @param $y2
|
3246 |
-
* @param bool $stroke
|
3247 |
-
*/
|
3248 |
-
function line($x1, $y1, $x2, $y2, $stroke = true)
|
3249 |
-
{
|
3250 |
-
$this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2));
|
3251 |
-
|
3252 |
-
if ($stroke) {
|
3253 |
-
$this->addContent(' S');
|
3254 |
-
}
|
3255 |
-
}
|
3256 |
-
|
3257 |
-
/**
|
3258 |
-
* draw a bezier curve based on 4 control points
|
3259 |
-
*
|
3260 |
-
* @param $x0
|
3261 |
-
* @param $y0
|
3262 |
-
* @param $x1
|
3263 |
-
* @param $y1
|
3264 |
-
* @param $x2
|
3265 |
-
* @param $y2
|
3266 |
-
* @param $x3
|
3267 |
-
* @param $y3
|
3268 |
-
*/
|
3269 |
-
function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3270 |
-
{
|
3271 |
-
// in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
|
3272 |
-
// as the control points for the curve.
|
3273 |
-
$this->addContent(
|
3274 |
-
sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3275 |
-
);
|
3276 |
-
}
|
3277 |
-
|
3278 |
-
/**
|
3279 |
-
* draw a part of an ellipse
|
3280 |
-
*
|
3281 |
-
* @param $x0
|
3282 |
-
* @param $y0
|
3283 |
-
* @param $astart
|
3284 |
-
* @param $afinish
|
3285 |
-
* @param $r1
|
3286 |
-
* @param int $r2
|
3287 |
-
* @param int $angle
|
3288 |
-
* @param int $nSeg
|
3289 |
-
*/
|
3290 |
-
function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
|
3291 |
-
{
|
3292 |
-
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false);
|
3293 |
-
}
|
3294 |
-
|
3295 |
-
/**
|
3296 |
-
* draw a filled ellipse
|
3297 |
-
*
|
3298 |
-
* @param $x0
|
3299 |
-
* @param $y0
|
3300 |
-
* @param $r1
|
3301 |
-
* @param int $r2
|
3302 |
-
* @param int $angle
|
3303 |
-
* @param int $nSeg
|
3304 |
-
* @param int $astart
|
3305 |
-
* @param int $afinish
|
3306 |
-
*/
|
3307 |
-
function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
|
3308 |
-
{
|
3309 |
-
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true);
|
3310 |
-
}
|
3311 |
-
|
3312 |
-
/**
|
3313 |
-
* @param $x
|
3314 |
-
* @param $y
|
3315 |
-
*/
|
3316 |
-
function lineTo($x, $y)
|
3317 |
-
{
|
3318 |
-
$this->addContent(sprintf("\n%.3F %.3F l", $x, $y));
|
3319 |
-
}
|
3320 |
-
|
3321 |
-
/**
|
3322 |
-
* @param $x
|
3323 |
-
* @param $y
|
3324 |
-
*/
|
3325 |
-
function moveTo($x, $y)
|
3326 |
-
{
|
3327 |
-
$this->addContent(sprintf("\n%.3F %.3F m", $x, $y));
|
3328 |
-
}
|
3329 |
-
|
3330 |
-
/**
|
3331 |
-
* draw a bezier curve based on 4 control points
|
3332 |
-
*
|
3333 |
-
* @param $x1
|
3334 |
-
* @param $y1
|
3335 |
-
* @param $x2
|
3336 |
-
* @param $y2
|
3337 |
-
* @param $x3
|
3338 |
-
* @param $y3
|
3339 |
-
*/
|
3340 |
-
function curveTo($x1, $y1, $x2, $y2, $x3, $y3)
|
3341 |
-
{
|
3342 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F %.3F %.3F c", $x1, $y1, $x2, $y2, $x3, $y3));
|
3343 |
-
}
|
3344 |
-
|
3345 |
-
/**
|
3346 |
-
* draw a bezier curve based on 4 control points
|
3347 |
-
*/ function quadTo($cpx, $cpy, $x, $y)
|
3348 |
-
{
|
3349 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F v", $cpx, $cpy, $x, $y));
|
3350 |
-
}
|
3351 |
-
|
3352 |
-
function closePath()
|
3353 |
-
{
|
3354 |
-
$this->addContent(' h');
|
3355 |
-
}
|
3356 |
-
|
3357 |
-
function endPath()
|
3358 |
-
{
|
3359 |
-
$this->addContent(' n');
|
3360 |
-
}
|
3361 |
-
|
3362 |
-
/**
|
3363 |
-
* draw an ellipse
|
3364 |
-
* note that the part and filled ellipse are just special cases of this function
|
3365 |
-
*
|
3366 |
-
* draws an ellipse in the current line style
|
3367 |
-
* centered at $x0,$y0, radii $r1,$r2
|
3368 |
-
* if $r2 is not set, then a circle is drawn
|
3369 |
-
* from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse.
|
3370 |
-
* nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
|
3371 |
-
* pretty crappy shape at 2, as we are approximating with bezier curves.
|
3372 |
-
*
|
3373 |
-
* @param $x0
|
3374 |
-
* @param $y0
|
3375 |
-
* @param $r1
|
3376 |
-
* @param int $r2
|
3377 |
-
* @param int $angle
|
3378 |
-
* @param int $nSeg
|
3379 |
-
* @param int $astart
|
3380 |
-
* @param int $afinish
|
3381 |
-
* @param bool $close
|
3382 |
-
* @param bool $fill
|
3383 |
-
* @param bool $stroke
|
3384 |
-
* @param bool $incomplete
|
3385 |
-
*/
|
3386 |
-
function ellipse(
|
3387 |
-
$x0,
|
3388 |
-
$y0,
|
3389 |
-
$r1,
|
3390 |
-
$r2 = 0,
|
3391 |
-
$angle = 0,
|
3392 |
-
$nSeg = 8,
|
3393 |
-
$astart = 0,
|
3394 |
-
$afinish = 360,
|
3395 |
-
$close = true,
|
3396 |
-
$fill = false,
|
3397 |
-
$stroke = true,
|
3398 |
-
$incomplete = false
|
3399 |
-
) {
|
3400 |
-
if ($r1 == 0) {
|
3401 |
-
return;
|
3402 |
-
}
|
3403 |
-
|
3404 |
-
if ($r2 == 0) {
|
3405 |
-
$r2 = $r1;
|
3406 |
-
}
|
3407 |
-
|
3408 |
-
if ($nSeg < 2) {
|
3409 |
-
$nSeg = 2;
|
3410 |
-
}
|
3411 |
-
|
3412 |
-
$astart = deg2rad((float)$astart);
|
3413 |
-
$afinish = deg2rad((float)$afinish);
|
3414 |
-
$totalAngle = $afinish - $astart;
|
3415 |
-
|
3416 |
-
$dt = $totalAngle / $nSeg;
|
3417 |
-
$dtm = $dt / 3;
|
3418 |
-
|
3419 |
-
if ($angle != 0) {
|
3420 |
-
$a = -1 * deg2rad((float)$angle);
|
3421 |
-
|
3422 |
-
$this->addContent(
|
3423 |
-
sprintf("\n q %.3F %.3F %.3F %.3F %.3F %.3F cm", cos($a), -sin($a), sin($a), cos($a), $x0, $y0)
|
3424 |
-
);
|
3425 |
-
|
3426 |
-
$x0 = 0;
|
3427 |
-
$y0 = 0;
|
3428 |
-
}
|
3429 |
-
|
3430 |
-
$t1 = $astart;
|
3431 |
-
$a0 = $x0 + $r1 * cos($t1);
|
3432 |
-
$b0 = $y0 + $r2 * sin($t1);
|
3433 |
-
$c0 = -$r1 * sin($t1);
|
3434 |
-
$d0 = $r2 * cos($t1);
|
3435 |
-
|
3436 |
-
if (!$incomplete) {
|
3437 |
-
$this->addContent(sprintf("\n%.3F %.3F m ", $a0, $b0));
|
3438 |
-
}
|
3439 |
-
|
3440 |
-
for ($i = 1; $i <= $nSeg; $i++) {
|
3441 |
-
// draw this bit of the total curve
|
3442 |
-
$t1 = $i * $dt + $astart;
|
3443 |
-
$a1 = $x0 + $r1 * cos($t1);
|
3444 |
-
$b1 = $y0 + $r2 * sin($t1);
|
3445 |
-
$c1 = -$r1 * sin($t1);
|
3446 |
-
$d1 = $r2 * cos($t1);
|
3447 |
-
|
3448 |
-
$this->addContent(
|
3449 |
-
sprintf(
|
3450 |
-
"\n%.3F %.3F %.3F %.3F %.3F %.3F c",
|
3451 |
-
($a0 + $c0 * $dtm),
|
3452 |
-
($b0 + $d0 * $dtm),
|
3453 |
-
($a1 - $c1 * $dtm),
|
3454 |
-
($b1 - $d1 * $dtm),
|
3455 |
-
$a1,
|
3456 |
-
$b1
|
3457 |
-
)
|
3458 |
-
);
|
3459 |
-
|
3460 |
-
$a0 = $a1;
|
3461 |
-
$b0 = $b1;
|
3462 |
-
$c0 = $c1;
|
3463 |
-
$d0 = $d1;
|
3464 |
-
}
|
3465 |
-
|
3466 |
-
if (!$incomplete) {
|
3467 |
-
if ($fill) {
|
3468 |
-
$this->addContent(' f');
|
3469 |
-
}
|
3470 |
-
|
3471 |
-
if ($stroke) {
|
3472 |
-
if ($close) {
|
3473 |
-
$this->addContent(' s'); // small 's' signifies closing the path as well
|
3474 |
-
} else {
|
3475 |
-
$this->addContent(' S');
|
3476 |
-
}
|
3477 |
-
}
|
3478 |
-
}
|
3479 |
-
|
3480 |
-
if ($angle != 0) {
|
3481 |
-
$this->addContent(' Q');
|
3482 |
-
}
|
3483 |
-
}
|
3484 |
-
|
3485 |
-
/**
|
3486 |
-
* this sets the line drawing style.
|
3487 |
-
* width, is the thickness of the line in user units
|
3488 |
-
* cap is the type of cap to put on the line, values can be 'butt','round','square'
|
3489 |
-
* where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the
|
3490 |
-
* end of the line.
|
3491 |
-
* join can be 'miter', 'round', 'bevel'
|
3492 |
-
* dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the
|
3493 |
-
* on and off dashes.
|
3494 |
-
* (2) represents 2 on, 2 off, 2 on , 2 off ...
|
3495 |
-
* (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
|
3496 |
-
* phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
|
3497 |
-
*
|
3498 |
-
* @param int $width
|
3499 |
-
* @param string $cap
|
3500 |
-
* @param string $join
|
3501 |
-
* @param string $dash
|
3502 |
-
* @param int $phase
|
3503 |
-
*/
|
3504 |
-
function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
|
3505 |
-
{
|
3506 |
-
// this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
|
3507 |
-
$string = '';
|
3508 |
-
|
3509 |
-
if ($width > 0) {
|
3510 |
-
$string .= "$width w";
|
3511 |
-
}
|
3512 |
-
|
3513 |
-
$ca = array('butt' => 0, 'round' => 1, 'square' => 2);
|
3514 |
-
|
3515 |
-
if (isset($ca[$cap])) {
|
3516 |
-
$string .= " $ca[$cap] J";
|
3517 |
-
}
|
3518 |
-
|
3519 |
-
$ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
|
3520 |
-
|
3521 |
-
if (isset($ja[$join])) {
|
3522 |
-
$string .= " $ja[$join] j";
|
3523 |
-
}
|
3524 |
-
|
3525 |
-
if (is_array($dash)) {
|
3526 |
-
$string .= ' [ ' . implode(' ', $dash) . " ] $phase d";
|
3527 |
-
}
|
3528 |
-
|
3529 |
-
$this->currentLineStyle = $string;
|
3530 |
-
$this->addContent("\n$string");
|
3531 |
-
}
|
3532 |
-
|
3533 |
-
/**
|
3534 |
-
* draw a polygon, the syntax for this is similar to the GD polygon command
|
3535 |
-
*
|
3536 |
-
* @param $p
|
3537 |
-
* @param $np
|
3538 |
-
* @param bool $f
|
3539 |
-
*/
|
3540 |
-
function polygon($p, $np, $f = false)
|
3541 |
-
{
|
3542 |
-
$this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
|
3543 |
-
|
3544 |
-
for ($i = 2; $i < $np * 2; $i = $i + 2) {
|
3545 |
-
$this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
|
3546 |
-
}
|
3547 |
-
|
3548 |
-
if ($f) {
|
3549 |
-
$this->addContent(' f');
|
3550 |
-
} else {
|
3551 |
-
$this->addContent(' S');
|
3552 |
-
}
|
3553 |
-
}
|
3554 |
-
|
3555 |
-
/**
|
3556 |
-
* a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3557 |
-
* the coordinates of the upper-right corner
|
3558 |
-
*
|
3559 |
-
* @param $x1
|
3560 |
-
* @param $y1
|
3561 |
-
* @param $width
|
3562 |
-
* @param $height
|
3563 |
-
*/
|
3564 |
-
function filledRectangle($x1, $y1, $width, $height)
|
3565 |
-
{
|
3566 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height));
|
3567 |
-
}
|
3568 |
-
|
3569 |
-
/**
|
3570 |
-
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3571 |
-
* the coordinates of the upper-right corner
|
3572 |
-
*
|
3573 |
-
* @param $x1
|
3574 |
-
* @param $y1
|
3575 |
-
* @param $width
|
3576 |
-
* @param $height
|
3577 |
-
*/
|
3578 |
-
function rectangle($x1, $y1, $width, $height)
|
3579 |
-
{
|
3580 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height));
|
3581 |
-
}
|
3582 |
-
|
3583 |
-
/**
|
3584 |
-
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3585 |
-
* the coordinates of the upper-right corner
|
3586 |
-
*
|
3587 |
-
* @param $x1
|
3588 |
-
* @param $y1
|
3589 |
-
* @param $width
|
3590 |
-
* @param $height
|
3591 |
-
*/
|
3592 |
-
function rect($x1, $y1, $width, $height)
|
3593 |
-
{
|
3594 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height));
|
3595 |
-
}
|
3596 |
-
|
3597 |
-
function stroke()
|
3598 |
-
{
|
3599 |
-
$this->addContent("\nS");
|
3600 |
-
}
|
3601 |
-
|
3602 |
-
function fill()
|
3603 |
-
{
|
3604 |
-
$this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3605 |
-
}
|
3606 |
-
|
3607 |
-
function fillStroke()
|
3608 |
-
{
|
3609 |
-
$this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3610 |
-
}
|
3611 |
-
|
3612 |
-
/**
|
3613 |
-
* save the current graphic state
|
3614 |
-
*/
|
3615 |
-
function save()
|
3616 |
-
{
|
3617 |
-
// we must reset the color cache or it will keep bad colors after clipping
|
3618 |
-
$this->currentColor = null;
|
3619 |
-
$this->currentStrokeColor = null;
|
3620 |
-
$this->addContent("\nq");
|
3621 |
-
}
|
3622 |
-
|
3623 |
-
/**
|
3624 |
-
* restore the last graphic state
|
3625 |
-
*/
|
3626 |
-
function restore()
|
3627 |
-
{
|
3628 |
-
// we must reset the color cache or it will keep bad colors after clipping
|
3629 |
-
$this->currentColor = null;
|
3630 |
-
$this->currentStrokeColor = null;
|
3631 |
-
$this->addContent("\nQ");
|
3632 |
-
}
|
3633 |
-
|
3634 |
-
/**
|
3635 |
-
* draw a clipping rectangle, all the elements added after this will be clipped
|
3636 |
-
*
|
3637 |
-
* @param $x1
|
3638 |
-
* @param $y1
|
3639 |
-
* @param $width
|
3640 |
-
* @param $height
|
3641 |
-
*/
|
3642 |
-
function clippingRectangle($x1, $y1, $width, $height)
|
3643 |
-
{
|
3644 |
-
$this->save();
|
3645 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height));
|
3646 |
-
}
|
3647 |
-
|
3648 |
-
/**
|
3649 |
-
* draw a clipping rounded rectangle, all the elements added after this will be clipped
|
3650 |
-
*
|
3651 |
-
* @param $x1
|
3652 |
-
* @param $y1
|
3653 |
-
* @param $w
|
3654 |
-
* @param $h
|
3655 |
-
* @param $rTL
|
3656 |
-
* @param $rTR
|
3657 |
-
* @param $rBR
|
3658 |
-
* @param $rBL
|
3659 |
-
*/
|
3660 |
-
function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
3661 |
-
{
|
3662 |
-
$this->save();
|
3663 |
-
|
3664 |
-
// start: top edge, left end
|
3665 |
-
$this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h));
|
3666 |
-
|
3667 |
-
// line: bottom edge, left end
|
3668 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1, $y1 + $rBL));
|
3669 |
-
|
3670 |
-
// curve: bottom-left corner
|
3671 |
-
$this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true);
|
3672 |
-
|
3673 |
-
// line: right edge, bottom end
|
3674 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1));
|
3675 |
-
|
3676 |
-
// curve: bottom-right corner
|
3677 |
-
$this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true);
|
3678 |
-
|
3679 |
-
// line: right edge, top end
|
3680 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR));
|
3681 |
-
|
3682 |
-
// curve: bottom-right corner
|
3683 |
-
$this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true);
|
3684 |
-
|
3685 |
-
// line: bottom edge, right end
|
3686 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h));
|
3687 |
-
|
3688 |
-
// curve: top-right corner
|
3689 |
-
$this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true);
|
3690 |
-
|
3691 |
-
// line: top edge, left end
|
3692 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1));
|
3693 |
-
|
3694 |
-
// Close & clip
|
3695 |
-
$this->addContent(" W n");
|
3696 |
-
}
|
3697 |
-
|
3698 |
-
/**
|
3699 |
-
* ends the last clipping shape
|
3700 |
-
*/
|
3701 |
-
function clippingEnd()
|
3702 |
-
{
|
3703 |
-
$this->restore();
|
3704 |
-
}
|
3705 |
-
|
3706 |
-
/**
|
3707 |
-
* scale
|
3708 |
-
*
|
3709 |
-
* @param float $s_x scaling factor for width as percent
|
3710 |
-
* @param float $s_y scaling factor for height as percent
|
3711 |
-
* @param float $x Origin abscissa
|
3712 |
-
* @param float $y Origin ordinate
|
3713 |
-
*/
|
3714 |
-
function scale($s_x, $s_y, $x, $y)
|
3715 |
-
{
|
3716 |
-
$y = $this->currentPageSize["height"] - $y;
|
3717 |
-
|
3718 |
-
$tm = array(
|
3719 |
-
$s_x,
|
3720 |
-
0,
|
3721 |
-
0,
|
3722 |
-
$s_y,
|
3723 |
-
$x * (1 - $s_x),
|
3724 |
-
$y * (1 - $s_y)
|
3725 |
-
);
|
3726 |
-
|
3727 |
-
$this->transform($tm);
|
3728 |
-
}
|
3729 |
-
|
3730 |
-
/**
|
3731 |
-
* translate
|
3732 |
-
*
|
3733 |
-
* @param float $t_x movement to the right
|
3734 |
-
* @param float $t_y movement to the bottom
|
3735 |
-
*/
|
3736 |
-
function translate($t_x, $t_y)
|
3737 |
-
{
|
3738 |
-
$tm = array(
|
3739 |
-
1,
|
3740 |
-
0,
|
3741 |
-
0,
|
3742 |
-
1,
|
3743 |
-
$t_x,
|
3744 |
-
-$t_y
|
3745 |
-
);
|
3746 |
-
|
3747 |
-
$this->transform($tm);
|
3748 |
-
}
|
3749 |
-
|
3750 |
-
/**
|
3751 |
-
* rotate
|
3752 |
-
*
|
3753 |
-
* @param float $angle angle in degrees for counter-clockwise rotation
|
3754 |
-
* @param float $x Origin abscissa
|
3755 |
-
* @param float $y Origin ordinate
|
3756 |
-
*/
|
3757 |
-
function rotate($angle, $x, $y)
|
3758 |
-
{
|
3759 |
-
$y = $this->currentPageSize["height"] - $y;
|
3760 |
-
|
3761 |
-
$a = deg2rad($angle);
|
3762 |
-
$cos_a = cos($a);
|
3763 |
-
$sin_a = sin($a);
|
3764 |
-
|
3765 |
-
$tm = array(
|
3766 |
-
$cos_a,
|
3767 |
-
-$sin_a,
|
3768 |
-
$sin_a,
|
3769 |
-
$cos_a,
|
3770 |
-
$x - $sin_a * $y - $cos_a * $x,
|
3771 |
-
$y - $cos_a * $y + $sin_a * $x,
|
3772 |
-
);
|
3773 |
-
|
3774 |
-
$this->transform($tm);
|
3775 |
-
}
|
3776 |
-
|
3777 |
-
/**
|
3778 |
-
* skew
|
3779 |
-
*
|
3780 |
-
* @param float $angle_x
|
3781 |
-
* @param float $angle_y
|
3782 |
-
* @param float $x Origin abscissa
|
3783 |
-
* @param float $y Origin ordinate
|
3784 |
-
*/
|
3785 |
-
function skew($angle_x, $angle_y, $x, $y)
|
3786 |
-
{
|
3787 |
-
$y = $this->currentPageSize["height"] - $y;
|
3788 |
-
|
3789 |
-
$tan_x = tan(deg2rad($angle_x));
|
3790 |
-
$tan_y = tan(deg2rad($angle_y));
|
3791 |
-
|
3792 |
-
$tm = array(
|
3793 |
-
1,
|
3794 |
-
-$tan_y,
|
3795 |
-
-$tan_x,
|
3796 |
-
1,
|
3797 |
-
$tan_x * $y,
|
3798 |
-
$tan_y * $x,
|
3799 |
-
);
|
3800 |
-
|
3801 |
-
$this->transform($tm);
|
3802 |
-
}
|
3803 |
-
|
3804 |
-
/**
|
3805 |
-
* apply graphic transformations
|
3806 |
-
*
|
3807 |
-
* @param array $tm transformation matrix
|
3808 |
-
*/
|
3809 |
-
function transform($tm)
|
3810 |
-
{
|
3811 |
-
$this->addContent(vsprintf("\n %.3F %.3F %.3F %.3F %.3F %.3F cm", $tm));
|
3812 |
-
}
|
3813 |
-
|
3814 |
-
/**
|
3815 |
-
* add a new page to the document
|
3816 |
-
* this also makes the new page the current active object
|
3817 |
-
*
|
3818 |
-
* @param int $insert
|
3819 |
-
* @param int $id
|
3820 |
-
* @param string $pos
|
3821 |
-
* @return int
|
3822 |
-
*/
|
3823 |
-
function newPage($insert = 0, $id = 0, $pos = 'after')
|
3824 |
-
{
|
3825 |
-
// if there is a state saved, then go up the stack closing them
|
3826 |
-
// then on the new page, re-open them with the right setings
|
3827 |
-
|
3828 |
-
if ($this->nStateStack) {
|
3829 |
-
for ($i = $this->nStateStack; $i >= 1; $i--) {
|
3830 |
-
$this->restoreState($i);
|
3831 |
-
}
|
3832 |
-
}
|
3833 |
-
|
3834 |
-
$this->numObj++;
|
3835 |
-
|
3836 |
-
if ($insert) {
|
3837 |
-
// the id from the ezPdf class is the id of the contents of the page, not the page object itself
|
3838 |
-
// query that object to find the parent
|
3839 |
-
$rid = $this->objects[$id]['onPage'];
|
3840 |
-
$opt = array('rid' => $rid, 'pos' => $pos);
|
3841 |
-
$this->o_page($this->numObj, 'new', $opt);
|
3842 |
-
} else {
|
3843 |
-
$this->o_page($this->numObj, 'new');
|
3844 |
-
}
|
3845 |
-
|
3846 |
-
// if there is a stack saved, then put that onto the page
|
3847 |
-
if ($this->nStateStack) {
|
3848 |
-
for ($i = 1; $i <= $this->nStateStack; $i++) {
|
3849 |
-
$this->saveState($i);
|
3850 |
-
}
|
3851 |
-
}
|
3852 |
-
|
3853 |
-
// and if there has been a stroke or fill color set, then transfer them
|
3854 |
-
if (isset($this->currentColor)) {
|
3855 |
-
$this->setColor($this->currentColor, true);
|
3856 |
-
}
|
3857 |
-
|
3858 |
-
if (isset($this->currentStrokeColor)) {
|
3859 |
-
$this->setStrokeColor($this->currentStrokeColor, true);
|
3860 |
-
}
|
3861 |
-
|
3862 |
-
// if there is a line style set, then put this in too
|
3863 |
-
if (mb_strlen($this->currentLineStyle, '8bit')) {
|
3864 |
-
$this->addContent("\n$this->currentLineStyle");
|
3865 |
-
}
|
3866 |
-
|
3867 |
-
// the call to the o_page object set currentContents to the present page, so this can be returned as the page id
|
3868 |
-
return $this->currentContents;
|
3869 |
-
}
|
3870 |
-
|
3871 |
-
/**
|
3872 |
-
* Streams the PDF to the client.
|
3873 |
-
*
|
3874 |
-
* @param string $filename The filename to present to the client.
|
3875 |
-
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
|
3876 |
-
*/
|
3877 |
-
function stream($filename = "document.pdf", $options = array())
|
3878 |
-
{
|
3879 |
-
if (headers_sent()) {
|
3880 |
-
die("Unable to stream pdf: headers already sent");
|
3881 |
-
}
|
3882 |
-
|
3883 |
-
if (!isset($options["compress"])) $options["compress"] = true;
|
3884 |
-
if (!isset($options["Attachment"])) $options["Attachment"] = true;
|
3885 |
-
|
3886 |
-
$debug = !$options['compress'];
|
3887 |
-
$tmp = ltrim($this->output($debug));
|
3888 |
-
|
3889 |
-
header("Cache-Control: private");
|
3890 |
-
header("Content-Type: application/pdf");
|
3891 |
-
header("Content-Length: " . mb_strlen($tmp, "8bit"));
|
3892 |
-
|
3893 |
-
$filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
|
3894 |
-
$attachment = $options["Attachment"] ? "attachment" : "inline";
|
3895 |
-
|
3896 |
-
$encoding = mb_detect_encoding($filename);
|
3897 |
-
$fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding);
|
3898 |
-
$fallbackfilename = str_replace("\"", "", $fallbackfilename);
|
3899 |
-
$encodedfilename = rawurlencode($filename);
|
3900 |
-
|
3901 |
-
$contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\"";
|
3902 |
-
if ($fallbackfilename !== $filename) {
|
3903 |
-
$contentDisposition .= "; filename*=UTF-8''$encodedfilename";
|
3904 |
-
}
|
3905 |
-
header($contentDisposition);
|
3906 |
-
|
3907 |
-
echo $tmp;
|
3908 |
-
flush();
|
3909 |
-
}
|
3910 |
-
|
3911 |
-
/**
|
3912 |
-
* return the height in units of the current font in the given size
|
3913 |
-
*
|
3914 |
-
* @param $size
|
3915 |
-
* @return float|int
|
3916 |
-
*/
|
3917 |
-
function getFontHeight($size)
|
3918 |
-
{
|
3919 |
-
if (!$this->numFonts) {
|
3920 |
-
$this->selectFont($this->defaultFont);
|
3921 |
-
}
|
3922 |
-
|
3923 |
-
$font = $this->fonts[$this->currentFont];
|
3924 |
-
|
3925 |
-
// for the current font, and the given size, what is the height of the font in user units
|
3926 |
-
if (isset($font['Ascender']) && isset($font['Descender'])) {
|
3927 |
-
$h = $font['Ascender'] - $font['Descender'];
|
3928 |
-
} else {
|
3929 |
-
$h = $font['FontBBox'][3] - $font['FontBBox'][1];
|
3930 |
-
}
|
3931 |
-
|
3932 |
-
// have to adjust by a font offset for Windows fonts. unfortunately it looks like
|
3933 |
-
// the bounding box calculations are wrong and I don't know why.
|
3934 |
-
if (isset($font['FontHeightOffset'])) {
|
3935 |
-
// For CourierNew from Windows this needs to be -646 to match the
|
3936 |
-
// Adobe native Courier font.
|
3937 |
-
//
|
3938 |
-
// For FreeMono from GNU this needs to be -337 to match the
|
3939 |
-
// Courier font.
|
3940 |
-
//
|
3941 |
-
// Both have been added manually to the .afm and .ufm files.
|
3942 |
-
$h += (int)$font['FontHeightOffset'];
|
3943 |
-
}
|
3944 |
-
|
3945 |
-
return $size * $h / 1000;
|
3946 |
-
}
|
3947 |
-
|
3948 |
-
/**
|
3949 |
-
* @param $size
|
3950 |
-
* @return float|int
|
3951 |
-
*/
|
3952 |
-
function getFontXHeight($size)
|
3953 |
-
{
|
3954 |
-
if (!$this->numFonts) {
|
3955 |
-
$this->selectFont($this->defaultFont);
|
3956 |
-
}
|
3957 |
-
|
3958 |
-
$font = $this->fonts[$this->currentFont];
|
3959 |
-
|
3960 |
-
// for the current font, and the given size, what is the height of the font in user units
|
3961 |
-
if (isset($font['XHeight'])) {
|
3962 |
-
$xh = $font['Ascender'] - $font['Descender'];
|
3963 |
-
} else {
|
3964 |
-
$xh = $this->getFontHeight($size) / 2;
|
3965 |
-
}
|
3966 |
-
|
3967 |
-
return $size * $xh / 1000;
|
3968 |
-
}
|
3969 |
-
|
3970 |
-
/**
|
3971 |
-
* return the font descender, this will normally return a negative number
|
3972 |
-
* if you add this number to the baseline, you get the level of the bottom of the font
|
3973 |
-
* it is in the pdf user units
|
3974 |
-
*
|
3975 |
-
* @param $size
|
3976 |
-
* @return float|int
|
3977 |
-
*/
|
3978 |
-
function getFontDescender($size)
|
3979 |
-
{
|
3980 |
-
// note that this will most likely return a negative value
|
3981 |
-
if (!$this->numFonts) {
|
3982 |
-
$this->selectFont($this->defaultFont);
|
3983 |
-
}
|
3984 |
-
|
3985 |
-
//$h = $this->fonts[$this->currentFont]['FontBBox'][1];
|
3986 |
-
$h = $this->fonts[$this->currentFont]['Descender'];
|
3987 |
-
|
3988 |
-
return $size * $h / 1000;
|
3989 |
-
}
|
3990 |
-
|
3991 |
-
/**
|
3992 |
-
* filter the text, this is applied to all text just before being inserted into the pdf document
|
3993 |
-
* it escapes the various things that need to be escaped, and so on
|
3994 |
-
*
|
3995 |
-
* @access private
|
3996 |
-
*
|
3997 |
-
* @param $text
|
3998 |
-
* @param bool $bom
|
3999 |
-
* @param bool $convert_encoding
|
4000 |
-
* @return string
|
4001 |
-
*/
|
4002 |
-
function filterText($text, $bom = true, $convert_encoding = true)
|
4003 |
-
{
|
4004 |
-
if (!$this->numFonts) {
|
4005 |
-
$this->selectFont($this->defaultFont);
|
4006 |
-
}
|
4007 |
-
|
4008 |
-
if ($convert_encoding) {
|
4009 |
-
$cf = $this->currentFont;
|
4010 |
-
if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
|
4011 |
-
$text = $this->utf8toUtf16BE($text, $bom);
|
4012 |
-
} else {
|
4013 |
-
//$text = html_entity_decode($text, ENT_QUOTES);
|
4014 |
-
$text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
|
4015 |
-
}
|
4016 |
-
} else if ($bom) {
|
4017 |
-
$text = $this->utf8toUtf16BE($text, $bom);
|
4018 |
-
}
|
4019 |
-
|
4020 |
-
// the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
|
4021 |
-
return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
|
4022 |
-
}
|
4023 |
-
|
4024 |
-
/**
|
4025 |
-
* return array containing codepoints (UTF-8 character values) for the
|
4026 |
-
* string passed in.
|
4027 |
-
*
|
4028 |
-
* based on the excellent TCPDF code by Nicola Asuni and the
|
4029 |
-
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4030 |
-
*
|
4031 |
-
* @access private
|
4032 |
-
* @author Orion Richardson
|
4033 |
-
* @since January 5, 2008
|
4034 |
-
*
|
4035 |
-
* @param string $text UTF-8 string to process
|
4036 |
-
*
|
4037 |
-
* @return array UTF-8 codepoints array for the string
|
4038 |
-
*/
|
4039 |
-
function utf8toCodePointsArray(&$text)
|
4040 |
-
{
|
4041 |
-
$length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
|
4042 |
-
$unicode = array(); // array containing unicode values
|
4043 |
-
$bytes = array(); // array containing single character byte sequences
|
4044 |
-
$numbytes = 1; // number of octets needed to represent the UTF-8 character
|
4045 |
-
|
4046 |
-
for ($i = 0; $i < $length; $i++) {
|
4047 |
-
$c = ord($text[$i]); // get one string character at time
|
4048 |
-
if (count($bytes) === 0) { // get starting octect
|
4049 |
-
if ($c <= 0x7F) {
|
4050 |
-
$unicode[] = $c; // use the character "as is" because is ASCII
|
4051 |
-
$numbytes = 1;
|
4052 |
-
} elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
|
4053 |
-
$bytes[] = ($c - 0xC0) << 0x06;
|
4054 |
-
$numbytes = 2;
|
4055 |
-
} elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
|
4056 |
-
$bytes[] = ($c - 0xE0) << 0x0C;
|
4057 |
-
$numbytes = 3;
|
4058 |
-
} elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
|
4059 |
-
$bytes[] = ($c - 0xF0) << 0x12;
|
4060 |
-
$numbytes = 4;
|
4061 |
-
} else {
|
4062 |
-
// use replacement character for other invalid sequences
|
4063 |
-
$unicode[] = 0xFFFD;
|
4064 |
-
$bytes = array();
|
4065 |
-
$numbytes = 1;
|
4066 |
-
}
|
4067 |
-
} elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
|
4068 |
-
$bytes[] = $c - 0x80;
|
4069 |
-
if (count($bytes) === $numbytes) {
|
4070 |
-
// compose UTF-8 bytes to a single unicode value
|
4071 |
-
$c = $bytes[0];
|
4072 |
-
for ($j = 1; $j < $numbytes; $j++) {
|
4073 |
-
$c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
|
4074 |
-
}
|
4075 |
-
if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) {
|
4076 |
-
// The definition of UTF-8 prohibits encoding character numbers between
|
4077 |
-
// U+D800 and U+DFFF, which are reserved for use with the UTF-16
|
4078 |
-
// encoding form (as surrogate pairs) and do not directly represent
|
4079 |
-
// characters.
|
4080 |
-
$unicode[] = 0xFFFD; // use replacement character
|
4081 |
-
} else {
|
4082 |
-
$unicode[] = $c; // add char to array
|
4083 |
-
}
|
4084 |
-
// reset data for next char
|
4085 |
-
$bytes = array();
|
4086 |
-
$numbytes = 1;
|
4087 |
-
}
|
4088 |
-
} else {
|
4089 |
-
// use replacement character for other invalid sequences
|
4090 |
-
$unicode[] = 0xFFFD;
|
4091 |
-
$bytes = array();
|
4092 |
-
$numbytes = 1;
|
4093 |
-
}
|
4094 |
-
}
|
4095 |
-
|
4096 |
-
return $unicode;
|
4097 |
-
}
|
4098 |
-
|
4099 |
-
/**
|
4100 |
-
* convert UTF-8 to UTF-16 with an additional byte order marker
|
4101 |
-
* at the front if required.
|
4102 |
-
*
|
4103 |
-
* based on the excellent TCPDF code by Nicola Asuni and the
|
4104 |
-
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4105 |
-
*
|
4106 |
-
* @access private
|
4107 |
-
* @author Orion Richardson
|
4108 |
-
* @since January 5, 2008
|
4109 |
-
*
|
4110 |
-
* @param string $text UTF-8 string to process
|
4111 |
-
* @param boolean $bom whether to add the byte order marker
|
4112 |
-
*
|
4113 |
-
* @return string UTF-16 result string
|
4114 |
-
*/
|
4115 |
-
function utf8toUtf16BE(&$text, $bom = true)
|
4116 |
-
{
|
4117 |
-
$out = $bom ? "\xFE\xFF" : '';
|
4118 |
-
|
4119 |
-
$unicode = $this->utf8toCodePointsArray($text);
|
4120 |
-
foreach ($unicode as $c) {
|
4121 |
-
if ($c === 0xFFFD) {
|
4122 |
-
$out .= "\xFF\xFD"; // replacement character
|
4123 |
-
} elseif ($c < 0x10000) {
|
4124 |
-
$out .= chr($c >> 0x08) . chr($c & 0xFF);
|
4125 |
-
} else {
|
4126 |
-
$c -= 0x10000;
|
4127 |
-
$w1 = 0xD800 | ($c >> 0x10);
|
4128 |
-
$w2 = 0xDC00 | ($c & 0x3FF);
|
4129 |
-
$out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
|
4130 |
-
}
|
4131 |
-
}
|
4132 |
-
|
4133 |
-
return $out;
|
4134 |
-
}
|
4135 |
-
|
4136 |
-
/**
|
4137 |
-
* given a start position and information about how text is to be laid out, calculate where
|
4138 |
-
* on the page the text will end
|
4139 |
-
*
|
4140 |
-
* @param $x
|
4141 |
-
* @param $y
|
4142 |
-
* @param $angle
|
4143 |
-
* @param $size
|
4144 |
-
* @param $wa
|
4145 |
-
* @param $text
|
4146 |
-
* @return array
|
4147 |
-
*/
|
4148 |
-
private function getTextPosition($x, $y, $angle, $size, $wa, $text)
|
4149 |
-
{
|
4150 |
-
// given this information return an array containing x and y for the end position as elements 0 and 1
|
4151 |
-
$w = $this->getTextWidth($size, $text);
|
4152 |
-
|
4153 |
-
// need to adjust for the number of spaces in this text
|
4154 |
-
$words = explode(' ', $text);
|
4155 |
-
$nspaces = count($words) - 1;
|
4156 |
-
$w += $wa * $nspaces;
|
4157 |
-
$a = deg2rad((float)$angle);
|
4158 |
-
|
4159 |
-
return array(cos($a) * $w + $x, -sin($a) * $w + $y);
|
4160 |
-
}
|
4161 |
-
|
4162 |
-
/**
|
4163 |
-
* Callback method used by smallCaps
|
4164 |
-
*
|
4165 |
-
* @param array $matches
|
4166 |
-
*
|
4167 |
-
* @return string
|
4168 |
-
*/
|
4169 |
-
function toUpper($matches)
|
4170 |
-
{
|
4171 |
-
return mb_strtoupper($matches[0]);
|
4172 |
-
}
|
4173 |
-
|
4174 |
-
function concatMatches($matches)
|
4175 |
-
{
|
4176 |
-
$str = "";
|
4177 |
-
foreach ($matches as $match) {
|
4178 |
-
$str .= $match[0];
|
4179 |
-
}
|
4180 |
-
|
4181 |
-
return $str;
|
4182 |
-
}
|
4183 |
-
|
4184 |
-
/**
|
4185 |
-
* register text for font subsetting
|
4186 |
-
*
|
4187 |
-
* @param $font
|
4188 |
-
* @param $text
|
4189 |
-
*/
|
4190 |
-
function registerText($font, $text)
|
4191 |
-
{
|
4192 |
-
if (!$this->isUnicode || in_array(mb_strtolower(basename($font)), self::$coreFonts)) {
|
4193 |
-
return;
|
4194 |
-
}
|
4195 |
-
|
4196 |
-
if (!isset($this->stringSubsets[$font])) {
|
4197 |
-
$this->stringSubsets[$font] = array();
|
4198 |
-
}
|
4199 |
-
|
4200 |
-
$this->stringSubsets[$font] = array_unique(
|
4201 |
-
array_merge($this->stringSubsets[$font], $this->utf8toCodePointsArray($text))
|
4202 |
-
);
|
4203 |
-
}
|
4204 |
-
|
4205 |
-
/**
|
4206 |
-
* add text to the document, at a specified location, size and angle on the page
|
4207 |
-
*
|
4208 |
-
* @param $x
|
4209 |
-
* @param $y
|
4210 |
-
* @param $size
|
4211 |
-
* @param $text
|
4212 |
-
* @param int $angle
|
4213 |
-
* @param int $wordSpaceAdjust
|
4214 |
-
* @param int $charSpaceAdjust
|
4215 |
-
* @param bool $smallCaps
|
4216 |
-
*/
|
4217 |
-
function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false)
|
4218 |
-
{
|
4219 |
-
if (!$this->numFonts) {
|
4220 |
-
$this->selectFont($this->defaultFont);
|
4221 |
-
}
|
4222 |
-
|
4223 |
-
$text = str_replace(array("\r", "\n"), "", $text);
|
4224 |
-
|
4225 |
-
if ($smallCaps) {
|
4226 |
-
preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4227 |
-
$lower = $this->concatMatches($matches);
|
4228 |
-
d($lower);
|
4229 |
-
|
4230 |
-
preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4231 |
-
$other = $this->concatMatches($matches);
|
4232 |
-
d($other);
|
4233 |
-
|
4234 |
-
//$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
|
4235 |
-
}
|
4236 |
-
|
4237 |
-
// if there are any open callbacks, then they should be called, to show the start of the line
|
4238 |
-
if ($this->nCallback > 0) {
|
4239 |
-
for ($i = $this->nCallback; $i > 0; $i--) {
|
4240 |
-
// call each function
|
4241 |
-
$info = array(
|
4242 |
-
'x' => $x,
|
4243 |
-
'y' => $y,
|
4244 |
-
'angle' => $angle,
|
4245 |
-
'status' => 'sol',
|
4246 |
-
'p' => $this->callback[$i]['p'],
|
4247 |
-
'nCallback' => $this->callback[$i]['nCallback'],
|
4248 |
-
'height' => $this->callback[$i]['height'],
|
4249 |
-
'descender' => $this->callback[$i]['descender']
|
4250 |
-
);
|
4251 |
-
|
4252 |
-
$func = $this->callback[$i]['f'];
|
4253 |
-
$this->$func($info);
|
4254 |
-
}
|
4255 |
-
}
|
4256 |
-
|
4257 |
-
if ($angle == 0) {
|
4258 |
-
$this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y));
|
4259 |
-
} else {
|
4260 |
-
$a = deg2rad((float)$angle);
|
4261 |
-
$this->addContent(
|
4262 |
-
sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y)
|
4263 |
-
);
|
4264 |
-
}
|
4265 |
-
|
4266 |
-
if ($wordSpaceAdjust != 0) {
|
4267 |
-
$this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
|
4268 |
-
}
|
4269 |
-
|
4270 |
-
if ($charSpaceAdjust != 0) {
|
4271 |
-
$this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust));
|
4272 |
-
}
|
4273 |
-
|
4274 |
-
$len = mb_strlen($text);
|
4275 |
-
$start = 0;
|
4276 |
-
|
4277 |
-
if ($start < $len) {
|
4278 |
-
$part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start);
|
4279 |
-
$place_text = $this->filterText($part, false);
|
4280 |
-
// modify unicode text so that extra word spacing is manually implemented (bug #)
|
4281 |
-
$cf = $this->currentFont;
|
4282 |
-
if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
|
4283 |
-
$space_scale = 1000 / $size;
|
4284 |
-
$place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text);
|
4285 |
-
}
|
4286 |
-
$this->addContent(" /F$this->currentFontNum " . sprintf('%.1F Tf ', $size));
|
4287 |
-
$this->addContent(" [($place_text)] TJ");
|
4288 |
-
}
|
4289 |
-
|
4290 |
-
if ($wordSpaceAdjust != 0) {
|
4291 |
-
$this->addContent(sprintf(" %.3F Tw", 0));
|
4292 |
-
}
|
4293 |
-
|
4294 |
-
if ($charSpaceAdjust != 0) {
|
4295 |
-
$this->addContent(sprintf(" %.3F Tc", 0));
|
4296 |
-
}
|
4297 |
-
|
4298 |
-
$this->addContent(' ET');
|
4299 |
-
|
4300 |
-
// if there are any open callbacks, then they should be called, to show the end of the line
|
4301 |
-
if ($this->nCallback > 0) {
|
4302 |
-
for ($i = $this->nCallback; $i > 0; $i--) {
|
4303 |
-
// call each function
|
4304 |
-
$tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
|
4305 |
-
$info = array(
|
4306 |
-
'x' => $tmp[0],
|
4307 |
-
'y' => $tmp[1],
|
4308 |
-
'angle' => $angle,
|
4309 |
-
'status' => 'eol',
|
4310 |
-
'p' => $this->callback[$i]['p'],
|
4311 |
-
'nCallback' => $this->callback[$i]['nCallback'],
|
4312 |
-
'height' => $this->callback[$i]['height'],
|
4313 |
-
'descender' => $this->callback[$i]['descender']
|
4314 |
-
);
|
4315 |
-
$func = $this->callback[$i]['f'];
|
4316 |
-
$this->$func($info);
|
4317 |
-
}
|
4318 |
-
}
|
4319 |
-
}
|
4320 |
-
|
4321 |
-
/**
|
4322 |
-
* calculate how wide a given text string will be on a page, at a given size.
|
4323 |
-
* this can be called externally, but is also used by the other class functions
|
4324 |
-
*
|
4325 |
-
* @param $size
|
4326 |
-
* @param $text
|
4327 |
-
* @param int $word_spacing
|
4328 |
-
* @param int $char_spacing
|
4329 |
-
* @return float|int
|
4330 |
-
*/
|
4331 |
-
function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0)
|
4332 |
-
{
|
4333 |
-
static $ord_cache = array();
|
4334 |
-
|
4335 |
-
// this function should not change any of the settings, though it will need to
|
4336 |
-
// track any directives which change during calculation, so copy them at the start
|
4337 |
-
// and put them back at the end.
|
4338 |
-
$store_currentTextState = $this->currentTextState;
|
4339 |
-
|
4340 |
-
if (!$this->numFonts) {
|
4341 |
-
$this->selectFont($this->defaultFont);
|
4342 |
-
}
|
4343 |
-
|
4344 |
-
$text = str_replace(array("\r", "\n"), "", $text);
|
4345 |
-
|
4346 |
-
// converts a number or a float to a string so it can get the width
|
4347 |
-
$text = "$text";
|
4348 |
-
|
4349 |
-
// hmm, this is where it all starts to get tricky - use the font information to
|
4350 |
-
// calculate the width of each character, add them up and convert to user units
|
4351 |
-
$w = 0;
|
4352 |
-
$cf = $this->currentFont;
|
4353 |
-
$current_font = $this->fonts[$cf];
|
4354 |
-
$space_scale = 1000 / ($size > 0 ? $size : 1);
|
4355 |
-
$n_spaces = 0;
|
4356 |
-
|
4357 |
-
if ($current_font['isUnicode']) {
|
4358 |
-
// for Unicode, use the code points array to calculate width rather
|
4359 |
-
// than just the string itself
|
4360 |
-
$unicode = $this->utf8toCodePointsArray($text);
|
4361 |
-
|
4362 |
-
foreach ($unicode as $char) {
|
4363 |
-
// check if we have to replace character
|
4364 |
-
if (isset($current_font['differences'][$char])) {
|
4365 |
-
$char = $current_font['differences'][$char];
|
4366 |
-
}
|
4367 |
-
|
4368 |
-
if (isset($current_font['C'][$char])) {
|
4369 |
-
$char_width = $current_font['C'][$char];
|
4370 |
-
|
4371 |
-
// add the character width
|
4372 |
-
$w += $char_width;
|
4373 |
-
|
4374 |
-
// add additional padding for space
|
4375 |
-
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4376 |
-
$w += $word_spacing * $space_scale;
|
4377 |
-
$n_spaces++;
|
4378 |
-
}
|
4379 |
-
}
|
4380 |
-
}
|
4381 |
-
|
4382 |
-
// add additional char spacing
|
4383 |
-
if ($char_spacing != 0) {
|
4384 |
-
$w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
|
4385 |
-
}
|
4386 |
-
|
4387 |
-
} else {
|
4388 |
-
// If CPDF is in Unicode mode but the current font does not support Unicode we need to convert the character set to Windows-1252
|
4389 |
-
if ($this->isUnicode) {
|
4390 |
-
$text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
|
4391 |
-
}
|
4392 |
-
|
4393 |
-
$len = mb_strlen($text, 'Windows-1252');
|
4394 |
-
|
4395 |
-
for ($i = 0; $i < $len; $i++) {
|
4396 |
-
$c = $text[$i];
|
4397 |
-
$char = isset($ord_cache[$c]) ? $ord_cache[$c] : ($ord_cache[$c] = ord($c));
|
4398 |
-
|
4399 |
-
// check if we have to replace character
|
4400 |
-
if (isset($current_font['differences'][$char])) {
|
4401 |
-
$char = $current_font['differences'][$char];
|
4402 |
-
}
|
4403 |
-
|
4404 |
-
if (isset($current_font['C'][$char])) {
|
4405 |
-
$char_width = $current_font['C'][$char];
|
4406 |
-
|
4407 |
-
// add the character width
|
4408 |
-
$w += $char_width;
|
4409 |
-
|
4410 |
-
// add additional padding for space
|
4411 |
-
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4412 |
-
$w += $word_spacing * $space_scale;
|
4413 |
-
$n_spaces++;
|
4414 |
-
}
|
4415 |
-
}
|
4416 |
-
}
|
4417 |
-
|
4418 |
-
// add additional char spacing
|
4419 |
-
if ($char_spacing != 0) {
|
4420 |
-
$w += $char_spacing * $space_scale * ($len + $n_spaces);
|
4421 |
-
}
|
4422 |
-
}
|
4423 |
-
|
4424 |
-
$this->currentTextState = $store_currentTextState;
|
4425 |
-
$this->setCurrentFont();
|
4426 |
-
|
4427 |
-
return $w * $size / 1000;
|
4428 |
-
}
|
4429 |
-
|
4430 |
-
/**
|
4431 |
-
* this will be called at a new page to return the state to what it was on the
|
4432 |
-
* end of the previous page, before the stack was closed down
|
4433 |
-
* This is to get around not being able to have open 'q' across pages
|
4434 |
-
*
|
4435 |
-
* @param int $pageEnd
|
4436 |
-
*/
|
4437 |
-
function saveState($pageEnd = 0)
|
4438 |
-
{
|
4439 |
-
if ($pageEnd) {
|
4440 |
-
// this will be called at a new page to return the state to what it was on the
|
4441 |
-
// end of the previous page, before the stack was closed down
|
4442 |
-
// This is to get around not being able to have open 'q' across pages
|
4443 |
-
$opt = $this->stateStack[$pageEnd];
|
4444 |
-
// ok to use this as stack starts numbering at 1
|
4445 |
-
$this->setColor($opt['col'], true);
|
4446 |
-
$this->setStrokeColor($opt['str'], true);
|
4447 |
-
$this->addContent("\n" . $opt['lin']);
|
4448 |
-
// $this->currentLineStyle = $opt['lin'];
|
4449 |
-
} else {
|
4450 |
-
$this->nStateStack++;
|
4451 |
-
$this->stateStack[$this->nStateStack] = array(
|
4452 |
-
'col' => $this->currentColor,
|
4453 |
-
'str' => $this->currentStrokeColor,
|
4454 |
-
'lin' => $this->currentLineStyle
|
4455 |
-
);
|
4456 |
-
}
|
4457 |
-
|
4458 |
-
$this->save();
|
4459 |
-
}
|
4460 |
-
|
4461 |
-
/**
|
4462 |
-
* restore a previously saved state
|
4463 |
-
*
|
4464 |
-
* @param int $pageEnd
|
4465 |
-
*/
|
4466 |
-
function restoreState($pageEnd = 0)
|
4467 |
-
{
|
4468 |
-
if (!$pageEnd) {
|
4469 |
-
$n = $this->nStateStack;
|
4470 |
-
$this->currentColor = $this->stateStack[$n]['col'];
|
4471 |
-
$this->currentStrokeColor = $this->stateStack[$n]['str'];
|
4472 |
-
$this->addContent("\n" . $this->stateStack[$n]['lin']);
|
4473 |
-
$this->currentLineStyle = $this->stateStack[$n]['lin'];
|
4474 |
-
$this->stateStack[$n] = null;
|
4475 |
-
unset($this->stateStack[$n]);
|
4476 |
-
$this->nStateStack--;
|
4477 |
-
}
|
4478 |
-
|
4479 |
-
$this->restore();
|
4480 |
-
}
|
4481 |
-
|
4482 |
-
/**
|
4483 |
-
* make a loose object, the output will go into this object, until it is closed, then will revert to
|
4484 |
-
* the current one.
|
4485 |
-
* this object will not appear until it is included within a page.
|
4486 |
-
* the function will return the object number
|
4487 |
-
*
|
4488 |
-
* @return int
|
4489 |
-
*/
|
4490 |
-
function openObject()
|
4491 |
-
{
|
4492 |
-
$this->nStack++;
|
4493 |
-
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4494 |
-
// add a new object of the content type, to hold the data flow
|
4495 |
-
$this->numObj++;
|
4496 |
-
$this->o_contents($this->numObj, 'new');
|
4497 |
-
$this->currentContents = $this->numObj;
|
4498 |
-
$this->looseObjects[$this->numObj] = 1;
|
4499 |
-
|
4500 |
-
return $this->numObj;
|
4501 |
-
}
|
4502 |
-
|
4503 |
-
/**
|
4504 |
-
* open an existing object for editing
|
4505 |
-
*
|
4506 |
-
* @param $id
|
4507 |
-
*/
|
4508 |
-
function reopenObject($id)
|
4509 |
-
{
|
4510 |
-
$this->nStack++;
|
4511 |
-
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4512 |
-
$this->currentContents = $id;
|
4513 |
-
|
4514 |
-
// also if this object is the primary contents for a page, then set the current page to its parent
|
4515 |
-
if (isset($this->objects[$id]['onPage'])) {
|
4516 |
-
$this->currentPage = $this->objects[$id]['onPage'];
|
4517 |
-
}
|
4518 |
-
}
|
4519 |
-
|
4520 |
-
/**
|
4521 |
-
* close an object
|
4522 |
-
*/
|
4523 |
-
function closeObject()
|
4524 |
-
{
|
4525 |
-
// close the object, as long as there was one open in the first place, which will be indicated by
|
4526 |
-
// an objectId on the stack.
|
4527 |
-
if ($this->nStack > 0) {
|
4528 |
-
$this->currentContents = $this->stack[$this->nStack]['c'];
|
4529 |
-
$this->currentPage = $this->stack[$this->nStack]['p'];
|
4530 |
-
$this->nStack--;
|
4531 |
-
// easier to probably not worry about removing the old entries, they will be overwritten
|
4532 |
-
// if there are new ones.
|
4533 |
-
}
|
4534 |
-
}
|
4535 |
-
|
4536 |
-
/**
|
4537 |
-
* stop an object from appearing on pages from this point on
|
4538 |
-
*
|
4539 |
-
* @param $id
|
4540 |
-
*/
|
4541 |
-
function stopObject($id)
|
4542 |
-
{
|
4543 |
-
// if an object has been appearing on pages up to now, then stop it, this page will
|
4544 |
-
// be the last one that could contain it.
|
4545 |
-
if (isset($this->addLooseObjects[$id])) {
|
4546 |
-
$this->addLooseObjects[$id] = '';
|
4547 |
-
}
|
4548 |
-
}
|
4549 |
-
|
4550 |
-
/**
|
4551 |
-
* after an object has been created, it wil only show if it has been added, using this function.
|
4552 |
-
*
|
4553 |
-
* @param $id
|
4554 |
-
* @param string $options
|
4555 |
-
*/
|
4556 |
-
function addObject($id, $options = 'add')
|
4557 |
-
{
|
4558 |
-
// add the specified object to the page
|
4559 |
-
if (isset($this->looseObjects[$id]) && $this->currentContents != $id) {
|
4560 |
-
// then it is a valid object, and it is not being added to itself
|
4561 |
-
switch ($options) {
|
4562 |
-
case 'all':
|
4563 |
-
// then this object is to be added to this page (done in the next block) and
|
4564 |
-
// all future new pages.
|
4565 |
-
$this->addLooseObjects[$id] = 'all';
|
4566 |
-
|
4567 |
-
case 'add':
|
4568 |
-
if (isset($this->objects[$this->currentContents]['onPage'])) {
|
4569 |
-
// then the destination contents is the primary for the page
|
4570 |
-
// (though this object is actually added to that page)
|
4571 |
-
$this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id);
|
4572 |
-
}
|
4573 |
-
break;
|
4574 |
-
|
4575 |
-
case 'even':
|
4576 |
-
$this->addLooseObjects[$id] = 'even';
|
4577 |
-
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4578 |
-
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 0) {
|
4579 |
-
$this->addObject($id);
|
4580 |
-
// hacky huh :)
|
4581 |
-
}
|
4582 |
-
break;
|
4583 |
-
|
4584 |
-
case 'odd':
|
4585 |
-
$this->addLooseObjects[$id] = 'odd';
|
4586 |
-
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4587 |
-
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 1) {
|
4588 |
-
$this->addObject($id);
|
4589 |
-
// hacky huh :)
|
4590 |
-
}
|
4591 |
-
break;
|
4592 |
-
|
4593 |
-
case 'next':
|
4594 |
-
$this->addLooseObjects[$id] = 'all';
|
4595 |
-
break;
|
4596 |
-
|
4597 |
-
case 'nexteven':
|
4598 |
-
$this->addLooseObjects[$id] = 'even';
|
4599 |
-
break;
|
4600 |
-
|
4601 |
-
case 'nextodd':
|
4602 |
-
$this->addLooseObjects[$id] = 'odd';
|
4603 |
-
break;
|
4604 |
-
}
|
4605 |
-
}
|
4606 |
-
}
|
4607 |
-
|
4608 |
-
/**
|
4609 |
-
* return a storable representation of a specific object
|
4610 |
-
*
|
4611 |
-
* @param $id
|
4612 |
-
* @return string|null
|
4613 |
-
*/
|
4614 |
-
function serializeObject($id)
|
4615 |
-
{
|
4616 |
-
if (array_key_exists($id, $this->objects)) {
|
4617 |
-
return serialize($this->objects[$id]);
|
4618 |
-
}
|
4619 |
-
|
4620 |
-
return null;
|
4621 |
-
}
|
4622 |
-
|
4623 |
-
/**
|
4624 |
-
* restore an object from its stored representation. returns its new object id.
|
4625 |
-
*
|
4626 |
-
* @param $obj
|
4627 |
-
* @return int
|
4628 |
-
*/
|
4629 |
-
function restoreSerializedObject($obj)
|
4630 |
-
{
|
4631 |
-
$obj_id = $this->openObject();
|
4632 |
-
$this->objects[$obj_id] = unserialize($obj);
|
4633 |
-
$this->closeObject();
|
4634 |
-
|
4635 |
-
return $obj_id;
|
4636 |
-
}
|
4637 |
-
|
4638 |
-
/**
|
4639 |
-
* add content to the documents info object
|
4640 |
-
*
|
4641 |
-
* @param $label
|
4642 |
-
* @param int $value
|
4643 |
-
*/
|
4644 |
-
function addInfo($label, $value = 0)
|
4645 |
-
{
|
4646 |
-
// this will only work if the label is one of the valid ones.
|
4647 |
-
// modify this so that arrays can be passed as well.
|
4648 |
-
// if $label is an array then assume that it is key => value pairs
|
4649 |
-
// else assume that they are both scalar, anything else will probably error
|
4650 |
-
if (is_array($label)) {
|
4651 |
-
foreach ($label as $l => $v) {
|
4652 |
-
$this->o_info($this->infoObject, $l, $v);
|
4653 |
-
}
|
4654 |
-
} else {
|
4655 |
-
$this->o_info($this->infoObject, $label, $value);
|
4656 |
-
}
|
4657 |
-
}
|
4658 |
-
|
4659 |
-
/**
|
4660 |
-
* set the viewer preferences of the document, it is up to the browser to obey these.
|
4661 |
-
*
|
4662 |
-
* @param $label
|
4663 |
-
* @param int $value
|
4664 |
-
*/
|
4665 |
-
function setPreferences($label, $value = 0)
|
4666 |
-
{
|
4667 |
-
// this will only work if the label is one of the valid ones.
|
4668 |
-
if (is_array($label)) {
|
4669 |
-
foreach ($label as $l => $v) {
|
4670 |
-
$this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
|
4671 |
-
}
|
4672 |
-
} else {
|
4673 |
-
$this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
|
4674 |
-
}
|
4675 |
-
}
|
4676 |
-
|
4677 |
-
/**
|
4678 |
-
* extract an integer from a position in a byte stream
|
4679 |
-
*
|
4680 |
-
* @param $data
|
4681 |
-
* @param $pos
|
4682 |
-
* @param $num
|
4683 |
-
* @return int
|
4684 |
-
*/
|
4685 |
-
private function getBytes(&$data, $pos, $num)
|
4686 |
-
{
|
4687 |
-
// return the integer represented by $num bytes from $pos within $data
|
4688 |
-
$ret = 0;
|
4689 |
-
for ($i = 0; $i < $num; $i++) {
|
4690 |
-
$ret *= 256;
|
4691 |
-
$ret += ord($data[$pos + $i]);
|
4692 |
-
}
|
4693 |
-
|
4694 |
-
return $ret;
|
4695 |
-
}
|
4696 |
-
|
4697 |
-
/**
|
4698 |
-
* Check if image already added to pdf image directory.
|
4699 |
-
* If yes, need not to create again (pass empty data)
|
4700 |
-
*
|
4701 |
-
* @param $imgname
|
4702 |
-
* @return bool
|
4703 |
-
*/
|
4704 |
-
function image_iscached($imgname)
|
4705 |
-
{
|
4706 |
-
return isset($this->imagelist[$imgname]);
|
4707 |
-
}
|
4708 |
-
|
4709 |
-
/**
|
4710 |
-
* add a PNG image into the document, from a GD object
|
4711 |
-
* this should work with remote files
|
4712 |
-
*
|
4713 |
-
* @param string $file The PNG file
|
4714 |
-
* @param float $x X position
|
4715 |
-
* @param float $y Y position
|
4716 |
-
* @param float $w Width
|
4717 |
-
* @param float $h Height
|
4718 |
-
* @param resource $img A GD resource
|
4719 |
-
* @param bool $is_mask true if the image is a mask
|
4720 |
-
* @param bool $mask true if the image is masked
|
4721 |
-
* @throws Exception
|
4722 |
-
*/
|
4723 |
-
function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null)
|
4724 |
-
{
|
4725 |
-
if (!function_exists("imagepng")) {
|
4726 |
-
throw new \Exception("The PHP GD extension is required, but is not installed.");
|
4727 |
-
}
|
4728 |
-
|
4729 |
-
//if already cached, need not to read again
|
4730 |
-
if (isset($this->imagelist[$file])) {
|
4731 |
-
$data = null;
|
4732 |
-
} else {
|
4733 |
-
// Example for transparency handling on new image. Retain for current image
|
4734 |
-
// $tIndex = imagecolortransparent($img);
|
4735 |
-
// if ($tIndex > 0) {
|
4736 |
-
// $tColor = imagecolorsforindex($img, $tIndex);
|
4737 |
-
// $new_tIndex = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']);
|
4738 |
-
// imagefill($new_img, 0, 0, $new_tIndex);
|
4739 |
-
// imagecolortransparent($new_img, $new_tIndex);
|
4740 |
-
// }
|
4741 |
-
// blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn
|
4742 |
-
//imagealphablending($img, true);
|
4743 |
-
|
4744 |
-
//default, but explicitely set to ensure pdf compatibility
|
4745 |
-
imagesavealpha($img, false/*!$is_mask && !$mask*/);
|
4746 |
-
|
4747 |
-
$error = 0;
|
4748 |
-
//DEBUG_IMG_TEMP
|
4749 |
-
//debugpng
|
4750 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4751 |
-
print '[addImagePng ' . $file . ']';
|
4752 |
-
}
|
4753 |
-
|
4754 |
-
ob_start();
|
4755 |
-
@imagepng($img);
|
4756 |
-
$data = ob_get_clean();
|
4757 |
-
|
4758 |
-
if ($data == '') {
|
4759 |
-
$error = 1;
|
4760 |
-
$errormsg = 'trouble writing file from GD';
|
4761 |
-
//DEBUG_IMG_TEMP
|
4762 |
-
//debugpng
|
4763 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4764 |
-
print 'trouble writing file from GD';
|
4765 |
-
}
|
4766 |
-
}
|
4767 |
-
|
4768 |
-
if ($error) {
|
4769 |
-
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
|
4770 |
-
|
4771 |
-
return;
|
4772 |
-
}
|
4773 |
-
} //End isset($this->imagelist[$file]) (png Duplicate removal)
|
4774 |
-
|
4775 |
-
$this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
|
4776 |
-
}
|
4777 |
-
|
4778 |
-
/**
|
4779 |
-
* @param $file
|
4780 |
-
* @param $x
|
4781 |
-
* @param $y
|
4782 |
-
* @param $w
|
4783 |
-
* @param $h
|
4784 |
-
* @param $byte
|
4785 |
-
*/
|
4786 |
-
protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte)
|
4787 |
-
{
|
4788 |
-
// generate images
|
4789 |
-
$img = imagecreatefrompng($file);
|
4790 |
-
|
4791 |
-
if ($img === false) {
|
4792 |
-
return;
|
4793 |
-
}
|
4794 |
-
|
4795 |
-
// FIXME The pixel transformation doesn't work well with 8bit PNGs
|
4796 |
-
$eight_bit = ($byte & 4) !== 4;
|
4797 |
-
|
4798 |
-
$wpx = imagesx($img);
|
4799 |
-
$hpx = imagesy($img);
|
4800 |
-
|
4801 |
-
imagesavealpha($img, false);
|
4802 |
-
|
4803 |
-
// create temp alpha file
|
4804 |
-
$tempfile_alpha = @tempnam($this->tmp, "cpdf_img_");
|
4805 |
-
@unlink($tempfile_alpha);
|
4806 |
-
$tempfile_alpha = "$tempfile_alpha.png";
|
4807 |
-
|
4808 |
-
// create temp plain file
|
4809 |
-
$tempfile_plain = @tempnam($this->tmp, "cpdf_img_");
|
4810 |
-
@unlink($tempfile_plain);
|
4811 |
-
$tempfile_plain = "$tempfile_plain.png";
|
4812 |
-
|
4813 |
-
$imgalpha = imagecreate($wpx, $hpx);
|
4814 |
-
imagesavealpha($imgalpha, false);
|
4815 |
-
|
4816 |
-
// generate gray scale palette (0 -> 255)
|
4817 |
-
for ($c = 0; $c < 256; ++$c) {
|
4818 |
-
imagecolorallocate($imgalpha, $c, $c, $c);
|
4819 |
-
}
|
4820 |
-
|
4821 |
-
// Use PECL gmagick + Graphics Magic to process transparent PNG images
|
4822 |
-
if (extension_loaded("gmagick")) {
|
4823 |
-
$gmagick = new \Gmagick($file);
|
4824 |
-
$gmagick->setimageformat('png');
|
4825 |
-
|
4826 |
-
// Get opacity channel (negative of alpha channel)
|
4827 |
-
$alpha_channel_neg = clone $gmagick;
|
4828 |
-
$alpha_channel_neg->separateimagechannel(\Gmagick::CHANNEL_OPACITY);
|
4829 |
-
|
4830 |
-
// Negate opacity channel
|
4831 |
-
$alpha_channel = new \Gmagick();
|
4832 |
-
$alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4833 |
-
$alpha_channel->compositeimage($alpha_channel_neg, \Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
|
4834 |
-
$alpha_channel->separateimagechannel(\Gmagick::CHANNEL_RED);
|
4835 |
-
$alpha_channel->writeimage($tempfile_alpha);
|
4836 |
-
|
4837 |
-
// Cast to 8bit+palette
|
4838 |
-
$imgalpha_ = imagecreatefrompng($tempfile_alpha);
|
4839 |
-
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
|
4840 |
-
imagedestroy($imgalpha_);
|
4841 |
-
imagepng($imgalpha, $tempfile_alpha);
|
4842 |
-
|
4843 |
-
// Make opaque image
|
4844 |
-
$color_channels = new \Gmagick();
|
4845 |
-
$color_channels->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4846 |
-
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYRED, 0, 0);
|
4847 |
-
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYGREEN, 0, 0);
|
4848 |
-
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYBLUE, 0, 0);
|
4849 |
-
$color_channels->writeimage($tempfile_plain);
|
4850 |
-
|
4851 |
-
$imgplain = imagecreatefrompng($tempfile_plain);
|
4852 |
-
}
|
4853 |
-
// Use PECL imagick + ImageMagic to process transparent PNG images
|
4854 |
-
elseif (extension_loaded("imagick")) {
|
4855 |
-
// Native cloning was added to pecl-imagick in svn commit 263814
|
4856 |
-
// the first version containing it was 3.0.1RC1
|
4857 |
-
static $imagickClonable = null;
|
4858 |
-
if ($imagickClonable === null) {
|
4859 |
-
$imagickClonable = version_compare(Imagick::IMAGICK_EXTVER, '3.0.1rc1') > 0;
|
4860 |
-
}
|
4861 |
-
|
4862 |
-
$imagick = new \Imagick($file);
|
4863 |
-
$imagick->setFormat('png');
|
4864 |
-
|
4865 |
-
// Get opacity channel (negative of alpha channel)
|
4866 |
-
if ($imagick->getImageAlphaChannel() !== 0) {
|
4867 |
-
$alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
|
4868 |
-
$alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA);
|
4869 |
-
|
4870 |
-
$
|
4871 |
-
|
4872 |
-
|
4873 |
-
|
4874 |
-
|
4875 |
-
|
4876 |
-
|
4877 |
-
|
4878 |
-
$
|
4879 |
-
|
4880 |
-
|
4881 |
-
|
4882 |
-
|
4883 |
-
|
4884 |
-
|
4885 |
-
|
4886 |
-
$color_channels
|
4887 |
-
$color_channels->
|
4888 |
-
|
4889 |
-
$
|
4890 |
-
|
4891 |
-
|
4892 |
-
|
4893 |
-
|
4894 |
-
|
4895 |
-
|
4896 |
-
|
4897 |
-
|
4898 |
-
|
4899 |
-
|
4900 |
-
|
4901 |
-
|
4902 |
-
|
4903 |
-
|
4904 |
-
|
4905 |
-
|
4906 |
-
//
|
4907 |
-
$
|
4908 |
-
|
4909 |
-
|
4910 |
-
|
4911 |
-
|
4912 |
-
|
4913 |
-
|
4914 |
-
|
4915 |
-
|
4916 |
-
|
4917 |
-
|
4918 |
-
|
4919 |
-
|
4920 |
-
|
4921 |
-
|
4922 |
-
|
4923 |
-
|
4924 |
-
|
4925 |
-
|
4926 |
-
|
4927 |
-
|
4928 |
-
|
4929 |
-
|
4930 |
-
|
4931 |
-
|
4932 |
-
|
4933 |
-
|
4934 |
-
|
4935 |
-
|
4936 |
-
|
4937 |
-
|
4938 |
-
|
4939 |
-
|
4940 |
-
|
4941 |
-
|
4942 |
-
|
4943 |
-
|
4944 |
-
//
|
4945 |
-
|
4946 |
-
|
4947 |
-
|
4948 |
-
|
4949 |
-
|
4950 |
-
|
4951 |
-
|
4952 |
-
|
4953 |
-
|
4954 |
-
|
4955 |
-
|
4956 |
-
*
|
4957 |
-
*
|
4958 |
-
*
|
4959 |
-
* @param
|
4960 |
-
* @
|
4961 |
-
|
4962 |
-
|
4963 |
-
|
4964 |
-
|
4965 |
-
|
4966 |
-
|
4967 |
-
|
4968 |
-
|
4969 |
-
|
4970 |
-
|
4971 |
-
|
4972 |
-
|
4973 |
-
|
4974 |
-
$
|
4975 |
-
|
4976 |
-
|
4977 |
-
|
4978 |
-
|
4979 |
-
|
4980 |
-
|
4981 |
-
|
4982 |
-
|
4983 |
-
|
4984 |
-
|
4985 |
-
|
4986 |
-
|
4987 |
-
|
4988 |
-
|
4989 |
-
|
4990 |
-
|
4991 |
-
|
4992 |
-
//
|
4993 |
-
//
|
4994 |
-
//
|
4995 |
-
//
|
4996 |
-
//
|
4997 |
-
//image
|
4998 |
-
|
4999 |
-
|
5000 |
-
|
5001 |
-
|
5002 |
-
$
|
5003 |
-
|
5004 |
-
|
5005 |
-
|
5006 |
-
|
5007 |
-
|
5008 |
-
$
|
5009 |
-
|
5010 |
-
|
5011 |
-
|
5012 |
-
|
5013 |
-
|
5014 |
-
|
5015 |
-
|
5016 |
-
|
5017 |
-
|
5018 |
-
|
5019 |
-
|
5020 |
-
|
5021 |
-
|
5022 |
-
|
5023 |
-
|
5024 |
-
|
5025 |
-
|
5026 |
-
|
5027 |
-
|
5028 |
-
|
5029 |
-
|
5030 |
-
|
5031 |
-
|
5032 |
-
|
5033 |
-
*
|
5034 |
-
*
|
5035 |
-
*
|
5036 |
-
* @param
|
5037 |
-
|
5038 |
-
|
5039 |
-
|
5040 |
-
|
5041 |
-
|
5042 |
-
|
5043 |
-
|
5044 |
-
$
|
5045 |
-
|
5046 |
-
$
|
5047 |
-
|
5048 |
-
$
|
5049 |
-
|
5050 |
-
|
5051 |
-
|
5052 |
-
|
5053 |
-
|
5054 |
-
|
5055 |
-
|
5056 |
-
|
5057 |
-
|
5058 |
-
|
5059 |
-
*
|
5060 |
-
*
|
5061 |
-
* @param
|
5062 |
-
* @param $
|
5063 |
-
* @param
|
5064 |
-
* @param
|
5065 |
-
|
5066 |
-
|
5067 |
-
|
5068 |
-
|
5069 |
-
|
5070 |
-
|
5071 |
-
|
5072 |
-
|
5073 |
-
|
5074 |
-
|
5075 |
-
|
5076 |
-
|
5077 |
-
|
5078 |
-
|
5079 |
-
|
5080 |
-
|
5081 |
-
|
5082 |
-
|
5083 |
-
|
5084 |
-
|
5085 |
-
|
5086 |
-
|
5087 |
-
|
5088 |
-
|
5089 |
-
|
5090 |
-
|
5091 |
-
|
5092 |
-
|
5093 |
-
|
5094 |
-
|
5095 |
-
|
5096 |
-
|
5097 |
-
|
5098 |
-
|
5099 |
-
|
5100 |
-
|
5101 |
-
//
|
5102 |
-
$
|
5103 |
-
$
|
5104 |
-
|
5105 |
-
|
5106 |
-
|
5107 |
-
|
5108 |
-
|
5109 |
-
|
5110 |
-
|
5111 |
-
|
5112 |
-
|
5113 |
-
|
5114 |
-
|
5115 |
-
|
5116 |
-
|
5117 |
-
|
5118 |
-
$info['
|
5119 |
-
$info['
|
5120 |
-
$info['
|
5121 |
-
|
5122 |
-
|
5123 |
-
$
|
5124 |
-
|
5125 |
-
|
5126 |
-
|
5127 |
-
|
5128 |
-
|
5129 |
-
|
5130 |
-
|
5131 |
-
|
5132 |
-
|
5133 |
-
|
5134 |
-
|
5135 |
-
|
5136 |
-
$
|
5137 |
-
|
5138 |
-
|
5139 |
-
|
5140 |
-
|
5141 |
-
|
5142 |
-
|
5143 |
-
|
5144 |
-
|
5145 |
-
|
5146 |
-
|
5147 |
-
|
5148 |
-
|
5149 |
-
break;
|
5150 |
-
|
5151 |
-
case '
|
5152 |
-
$
|
5153 |
-
break;
|
5154 |
-
|
5155 |
-
case '
|
5156 |
-
|
5157 |
-
|
5158 |
-
|
5159 |
-
|
5160 |
-
|
5161 |
-
|
5162 |
-
|
5163 |
-
|
5164 |
-
|
5165 |
-
|
5166 |
-
|
5167 |
-
|
5168 |
-
|
5169 |
-
|
5170 |
-
|
5171 |
-
|
5172 |
-
|
5173 |
-
|
5174 |
-
|
5175 |
-
|
5176 |
-
|
5177 |
-
|
5178 |
-
|
5179 |
-
|
5180 |
-
|
5181 |
-
|
5182 |
-
|
5183 |
-
|
5184 |
-
|
5185 |
-
|
5186 |
-
|
5187 |
-
|
5188 |
-
|
5189 |
-
|
5190 |
-
|
5191 |
-
|
5192 |
-
|
5193 |
-
|
5194 |
-
|
5195 |
-
|
5196 |
-
|
5197 |
-
|
5198 |
-
|
5199 |
-
|
5200 |
-
|
5201 |
-
|
5202 |
-
|
5203 |
-
$transparency['
|
5204 |
-
//
|
5205 |
-
|
5206 |
-
|
5207 |
-
|
5208 |
-
|
5209 |
-
|
5210 |
-
|
5211 |
-
|
5212 |
-
|
5213 |
-
|
5214 |
-
|
5215 |
-
|
5216 |
-
|
5217 |
-
|
5218 |
-
|
5219 |
-
|
5220 |
-
|
5221 |
-
|
5222 |
-
|
5223 |
-
|
5224 |
-
|
5225 |
-
|
5226 |
-
|
5227 |
-
|
5228 |
-
$
|
5229 |
-
|
5230 |
-
|
5231 |
-
|
5232 |
-
|
5233 |
-
|
5234 |
-
|
5235 |
-
|
5236 |
-
|
5237 |
-
|
5238 |
-
|
5239 |
-
$
|
5240 |
-
|
5241 |
-
|
5242 |
-
|
5243 |
-
|
5244 |
-
|
5245 |
-
|
5246 |
-
|
5247 |
-
|
5248 |
-
|
5249 |
-
|
5250 |
-
|
5251 |
-
|
5252 |
-
|
5253 |
-
|
5254 |
-
|
5255 |
-
|
5256 |
-
|
5257 |
-
|
5258 |
-
|
5259 |
-
|
5260 |
-
|
5261 |
-
|
5262 |
-
|
5263 |
-
|
5264 |
-
|
5265 |
-
|
5266 |
-
|
5267 |
-
|
5268 |
-
|
5269 |
-
$
|
5270 |
-
|
5271 |
-
|
5272 |
-
|
5273 |
-
|
5274 |
-
$
|
5275 |
-
|
5276 |
-
|
5277 |
-
|
5278 |
-
|
5279 |
-
$
|
5280 |
-
|
5281 |
-
|
5282 |
-
|
5283 |
-
|
5284 |
-
|
5285 |
-
|
5286 |
-
|
5287 |
-
|
5288 |
-
|
5289 |
-
|
5290 |
-
|
5291 |
-
|
5292 |
-
|
5293 |
-
|
5294 |
-
|
5295 |
-
|
5296 |
-
|
5297 |
-
|
5298 |
-
|
5299 |
-
|
5300 |
-
$
|
5301 |
-
|
5302 |
-
|
5303 |
-
|
5304 |
-
$
|
5305 |
-
|
5306 |
-
|
5307 |
-
|
5308 |
-
|
5309 |
-
'
|
5310 |
-
'
|
5311 |
-
'
|
5312 |
-
'
|
5313 |
-
'
|
5314 |
-
'
|
5315 |
-
'
|
5316 |
-
|
5317 |
-
|
5318 |
-
|
5319 |
-
|
5320 |
-
|
5321 |
-
|
5322 |
-
|
5323 |
-
|
5324 |
-
|
5325 |
-
|
5326 |
-
|
5327 |
-
|
5328 |
-
}
|
5329 |
-
|
5330 |
-
if ($
|
5331 |
-
|
5332 |
-
|
5333 |
-
|
5334 |
-
|
5335 |
-
|
5336 |
-
$
|
5337 |
-
}
|
5338 |
-
|
5339 |
-
if ($
|
5340 |
-
$
|
5341 |
-
}
|
5342 |
-
|
5343 |
-
|
5344 |
-
|
5345 |
-
|
5346 |
-
|
5347 |
-
|
5348 |
-
|
5349 |
-
|
5350 |
-
|
5351 |
-
*
|
5352 |
-
*
|
5353 |
-
* @param
|
5354 |
-
|
5355 |
-
|
5356 |
-
|
5357 |
-
|
5358 |
-
|
5359 |
-
|
5360 |
-
|
5361 |
-
|
5362 |
-
|
5363 |
-
|
5364 |
-
if (
|
5365 |
-
|
5366 |
-
|
5367 |
-
|
5368 |
-
|
5369 |
-
|
5370 |
-
$
|
5371 |
-
$
|
5372 |
-
$
|
5373 |
-
|
5374 |
-
|
5375 |
-
|
5376 |
-
|
5377 |
-
|
5378 |
-
|
5379 |
-
|
5380 |
-
|
5381 |
-
|
5382 |
-
|
5383 |
-
|
5384 |
-
$
|
5385 |
-
}
|
5386 |
-
|
5387 |
-
if ($w
|
5388 |
-
$w = $
|
5389 |
-
}
|
5390 |
-
|
5391 |
-
if ($
|
5392 |
-
$
|
5393 |
-
}
|
5394 |
-
|
5395 |
-
|
5396 |
-
|
5397 |
-
|
5398 |
-
|
5399 |
-
|
5400 |
-
|
5401 |
-
|
5402 |
-
|
5403 |
-
*
|
5404 |
-
* @param
|
5405 |
-
* @param $
|
5406 |
-
* @param $
|
5407 |
-
* @param int $
|
5408 |
-
* @param $
|
5409 |
-
|
5410 |
-
|
5411 |
-
|
5412 |
-
|
5413 |
-
|
5414 |
-
|
5415 |
-
|
5416 |
-
$
|
5417 |
-
$
|
5418 |
-
$
|
5419 |
-
$
|
5420 |
-
|
5421 |
-
|
5422 |
-
|
5423 |
-
|
5424 |
-
|
5425 |
-
|
5426 |
-
|
5427 |
-
|
5428 |
-
|
5429 |
-
|
5430 |
-
|
5431 |
-
|
5432 |
-
|
5433 |
-
|
5434 |
-
|
5435 |
-
|
5436 |
-
|
5437 |
-
|
5438 |
-
|
5439 |
-
|
5440 |
-
$this->
|
5441 |
-
|
5442 |
-
|
5443 |
-
|
5444 |
-
|
5445 |
-
|
5446 |
-
|
5447 |
-
|
5448 |
-
'
|
5449 |
-
|
5450 |
-
|
5451 |
-
|
5452 |
-
|
5453 |
-
|
5454 |
-
|
5455 |
-
|
5456 |
-
|
5457 |
-
|
5458 |
-
|
5459 |
-
|
5460 |
-
|
5461 |
-
|
5462 |
-
|
5463 |
-
|
5464 |
-
|
5465 |
-
|
5466 |
-
|
5467 |
-
|
5468 |
-
*
|
5469 |
-
*
|
5470 |
-
|
5471 |
-
|
5472 |
-
|
5473 |
-
|
5474 |
-
|
5475 |
-
|
5476 |
-
|
5477 |
-
//
|
5478 |
-
//
|
5479 |
-
// '
|
5480 |
-
// '
|
5481 |
-
// '
|
5482 |
-
// '
|
5483 |
-
|
5484 |
-
|
5485 |
-
|
5486 |
-
|
5487 |
-
|
5488 |
-
|
5489 |
-
|
5490 |
-
|
5491 |
-
|
5492 |
-
|
5493 |
-
|
5494 |
-
|
5495 |
-
|
5496 |
-
|
5497 |
-
|
5498 |
-
|
5499 |
-
|
5500 |
-
|
5501 |
-
|
5502 |
-
|
5503 |
-
|
5504 |
-
|
5505 |
-
|
5506 |
-
|
5507 |
-
|
5508 |
-
*
|
5509 |
-
*
|
5510 |
-
* @param
|
5511 |
-
|
5512 |
-
|
5513 |
-
|
5514 |
-
|
5515 |
-
|
5516 |
-
|
5517 |
-
|
5518 |
-
|
5519 |
-
|
5520 |
-
|
5521 |
-
|
5522 |
-
|
5523 |
-
|
5524 |
-
|
5525 |
-
|
5526 |
-
|
5527 |
-
|
5528 |
-
|
5529 |
-
|
5530 |
-
|
5531 |
-
|
5532 |
-
|
5533 |
-
|
5534 |
-
*
|
5535 |
-
*
|
5536 |
-
|
5537 |
-
|
5538 |
-
|
5539 |
-
|
5540 |
-
|
5541 |
-
|
5542 |
-
|
5543 |
-
|
5544 |
-
|
5545 |
-
|
5546 |
-
|
5547 |
-
|
5548 |
-
|
5549 |
-
|
5550 |
-
|
5551 |
-
|
5552 |
-
|
5553 |
-
|
5554 |
-
|
5555 |
-
|
5556 |
-
|
5557 |
-
|
5558 |
-
|
5559 |
-
|
5560 |
-
|
5561 |
-
|
5562 |
-
|
5563 |
-
|
5564 |
-
|
5565 |
-
|
5566 |
-
|
5567 |
-
|
5568 |
-
|
5569 |
-
|
5570 |
-
|
5571 |
-
|
5572 |
-
|
5573 |
-
|
5574 |
-
|
5575 |
-
|
5576 |
-
|
5577 |
-
|
5578 |
-
|
5579 |
-
|
5580 |
-
|
5581 |
-
|
5582 |
-
|
5583 |
-
|
5584 |
-
|
5585 |
-
|
5586 |
-
|
5587 |
-
|
5588 |
-
|
5589 |
-
|
5590 |
-
|
5591 |
-
|
5592 |
-
|
5593 |
-
|
5594 |
-
|
5595 |
-
|
5596 |
-
|
5597 |
-
|
5598 |
-
|
5599 |
-
|
5600 |
-
|
5601 |
-
|
5602 |
-
|
5603 |
-
|
5604 |
-
|
5605 |
-
|
5606 |
-
|
5607 |
-
|
5608 |
-
|
5609 |
-
$this->checkpoint
|
5610 |
-
|
5611 |
-
|
5612 |
-
|
5613 |
-
|
5614 |
-
|
5615 |
-
|
5616 |
-
|
5617 |
-
|
5618 |
-
|
5619 |
-
|
5620 |
-
|
5621 |
-
|
5622 |
-
|
5623 |
-
|
5624 |
-
|
5625 |
-
|
5626 |
-
|
5627 |
-
|
5628 |
-
|
5629 |
-
|
5630 |
-
|
5631 |
-
|
5632 |
-
|
5633 |
-
|
5634 |
-
|
5635 |
-
|
5636 |
-
|
5637 |
-
|
5638 |
-
|
5639 |
-
|
5640 |
-
}
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* A PHP class to provide the basic functionality to create a pdf document without
|
4 |
+
* any requirement for additional modules.
|
5 |
+
*
|
6 |
+
* Extended by Orion Richardson to support Unicode / UTF-8 characters using
|
7 |
+
* TCPDF and others as a guide.
|
8 |
+
*
|
9 |
+
* @author Wayne Munro <pdf@ros.co.nz>
|
10 |
+
* @author Orion Richardson <orionr@yahoo.com>
|
11 |
+
* @author Helmut Tischer <htischer@weihenstephan.org>
|
12 |
+
* @author Ryan H. Masten <ryan.masten@gmail.com>
|
13 |
+
* @author Brian Sweeney <eclecticgeek@gmail.com>
|
14 |
+
* @author Fabien Ménager <fabien.menager@gmail.com>
|
15 |
+
* @license Public Domain http://creativecommons.org/licenses/publicdomain/
|
16 |
+
* @package Cpdf
|
17 |
+
*/
|
18 |
+
use FontLib\Font;
|
19 |
+
use FontLib\BinaryStream;
|
20 |
+
|
21 |
+
class Cpdf
|
22 |
+
{
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var integer The current number of pdf objects in the document
|
26 |
+
*/
|
27 |
+
public $numObj = 0;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var array This array contains all of the pdf objects, ready for final assembly
|
31 |
+
*/
|
32 |
+
public $objects = array();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @var integer The objectId (number within the objects array) of the document catalog
|
36 |
+
*/
|
37 |
+
public $catalogId;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @var array Array carrying information about the fonts that the system currently knows about
|
41 |
+
* Used to ensure that a font is not loaded twice, among other things
|
42 |
+
*/
|
43 |
+
public $fonts = array();
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @var string The default font metrics file to use if no other font has been loaded.
|
47 |
+
* The path to the directory containing the font metrics should be included
|
48 |
+
*/
|
49 |
+
public $defaultFont = './fonts/Helvetica.afm';
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @string A record of the current font
|
53 |
+
*/
|
54 |
+
public $currentFont = '';
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @var string The current base font
|
58 |
+
*/
|
59 |
+
public $currentBaseFont = '';
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @var integer The number of the current font within the font array
|
63 |
+
*/
|
64 |
+
public $currentFontNum = 0;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @var integer
|
68 |
+
*/
|
69 |
+
public $currentNode;
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @var integer Object number of the current page
|
73 |
+
*/
|
74 |
+
public $currentPage;
|
75 |
+
|
76 |
+
/**
|
77 |
+
* @var integer Object number of the currently active contents block
|
78 |
+
*/
|
79 |
+
public $currentContents;
|
80 |
+
|
81 |
+
/**
|
82 |
+
* @var integer Number of fonts within the system
|
83 |
+
*/
|
84 |
+
public $numFonts = 0;
|
85 |
+
|
86 |
+
/**
|
87 |
+
* @var integer Number of graphic state resources used
|
88 |
+
*/
|
89 |
+
private $numStates = 0;
|
90 |
+
|
91 |
+
/**
|
92 |
+
* @var array Number of graphic state resources used
|
93 |
+
*/
|
94 |
+
private $gstates = array();
|
95 |
+
|
96 |
+
/**
|
97 |
+
* @var array Current color for fill operations, defaults to inactive value,
|
98 |
+
* all three components should be between 0 and 1 inclusive when active
|
99 |
+
*/
|
100 |
+
public $currentColor = null;
|
101 |
+
|
102 |
+
/**
|
103 |
+
* @var array Current color for stroke operations (lines etc.)
|
104 |
+
*/
|
105 |
+
public $currentStrokeColor = null;
|
106 |
+
|
107 |
+
/**
|
108 |
+
* @var string Fill rule (nonzero or evenodd)
|
109 |
+
*/
|
110 |
+
public $fillRule = "nonzero";
|
111 |
+
|
112 |
+
/**
|
113 |
+
* @var string Current style that lines are drawn in
|
114 |
+
*/
|
115 |
+
public $currentLineStyle = '';
|
116 |
+
|
117 |
+
/**
|
118 |
+
* @var array Current line transparency (partial graphics state)
|
119 |
+
*/
|
120 |
+
public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
121 |
+
|
122 |
+
/**
|
123 |
+
* array Current fill transparency (partial graphics state)
|
124 |
+
*/
|
125 |
+
public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
126 |
+
|
127 |
+
/**
|
128 |
+
* @var array An array which is used to save the state of the document, mainly the colors and styles
|
129 |
+
* it is used to temporarily change to another state, then change back to what it was before
|
130 |
+
*/
|
131 |
+
public $stateStack = array();
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @var integer Number of elements within the state stack
|
135 |
+
*/
|
136 |
+
public $nStateStack = 0;
|
137 |
+
|
138 |
+
/**
|
139 |
+
* @var integer Number of page objects within the document
|
140 |
+
*/
|
141 |
+
public $numPages = 0;
|
142 |
+
|
143 |
+
/**
|
144 |
+
* @var array Object Id storage stack
|
145 |
+
*/
|
146 |
+
public $stack = array();
|
147 |
+
|
148 |
+
/**
|
149 |
+
* @var integer Number of elements within the object Id storage stack
|
150 |
+
*/
|
151 |
+
public $nStack = 0;
|
152 |
+
|
153 |
+
/**
|
154 |
+
* an array which contains information about the objects which are not firmly attached to pages
|
155 |
+
* these have been added with the addObject function
|
156 |
+
*/
|
157 |
+
public $looseObjects = array();
|
158 |
+
|
159 |
+
/**
|
160 |
+
* array contains information about how the loose objects are to be added to the document
|
161 |
+
*/
|
162 |
+
public $addLooseObjects = array();
|
163 |
+
|
164 |
+
/**
|
165 |
+
* @var integer The objectId of the information object for the document
|
166 |
+
* this contains authorship, title etc.
|
167 |
+
*/
|
168 |
+
public $infoObject = 0;
|
169 |
+
|
170 |
+
/**
|
171 |
+
* @var integer Number of images being tracked within the document
|
172 |
+
*/
|
173 |
+
public $numImages = 0;
|
174 |
+
|
175 |
+
/**
|
176 |
+
* @var array An array containing options about the document
|
177 |
+
* it defaults to turning on the compression of the objects
|
178 |
+
*/
|
179 |
+
public $options = array('compression' => true);
|
180 |
+
|
181 |
+
/**
|
182 |
+
* @var integer The objectId of the first page of the document
|
183 |
+
*/
|
184 |
+
public $firstPageId;
|
185 |
+
|
186 |
+
/**
|
187 |
+
* @var integer The object Id of the procset object
|
188 |
+
*/
|
189 |
+
public $procsetObjectId;
|
190 |
+
|
191 |
+
/**
|
192 |
+
* @var array Store the information about the relationship between font families
|
193 |
+
* this used so that the code knows which font is the bold version of another font, etc.
|
194 |
+
* the value of this array is initialised in the constructor function.
|
195 |
+
*/
|
196 |
+
public $fontFamilies = array();
|
197 |
+
|
198 |
+
/**
|
199 |
+
* @var string Folder for php serialized formats of font metrics files.
|
200 |
+
* If empty string, use same folder as original metrics files.
|
201 |
+
* This can be passed in from class creator.
|
202 |
+
* If this folder does not exist or is not writable, Cpdf will be **much** slower.
|
203 |
+
* Because of potential trouble with php safe mode, folder cannot be created at runtime.
|
204 |
+
*/
|
205 |
+
public $fontcache = '';
|
206 |
+
|
207 |
+
/**
|
208 |
+
* @var integer The version of the font metrics cache file.
|
209 |
+
* This value must be manually incremented whenever the internal font data structure is modified.
|
210 |
+
*/
|
211 |
+
public $fontcacheVersion = 6;
|
212 |
+
|
213 |
+
/**
|
214 |
+
* @var string Temporary folder.
|
215 |
+
* If empty string, will attempt system tmp folder.
|
216 |
+
* This can be passed in from class creator.
|
217 |
+
*/
|
218 |
+
public $tmp = '';
|
219 |
+
|
220 |
+
/**
|
221 |
+
* @var string Track if the current font is bolded or italicised
|
222 |
+
*/
|
223 |
+
public $currentTextState = '';
|
224 |
+
|
225 |
+
/**
|
226 |
+
* @var string Messages are stored here during processing, these can be selected afterwards to give some useful debug information
|
227 |
+
*/
|
228 |
+
public $messages = '';
|
229 |
+
|
230 |
+
/**
|
231 |
+
* @var string The encryption array for the document encryption is stored here
|
232 |
+
*/
|
233 |
+
public $arc4 = '';
|
234 |
+
|
235 |
+
/**
|
236 |
+
* @var integer The object Id of the encryption information
|
237 |
+
*/
|
238 |
+
public $arc4_objnum = 0;
|
239 |
+
|
240 |
+
/**
|
241 |
+
* @var string The file identifier, used to uniquely identify a pdf document
|
242 |
+
*/
|
243 |
+
public $fileIdentifier = '';
|
244 |
+
|
245 |
+
/**
|
246 |
+
* @var boolean A flag to say if a document is to be encrypted or not
|
247 |
+
*/
|
248 |
+
public $encrypted = false;
|
249 |
+
|
250 |
+
/**
|
251 |
+
* @var string The encryption key for the encryption of all the document content (structure is not encrypted)
|
252 |
+
*/
|
253 |
+
public $encryptionKey = '';
|
254 |
+
|
255 |
+
/**
|
256 |
+
* @var array Array which forms a stack to keep track of nested callback functions
|
257 |
+
*/
|
258 |
+
public $callback = array();
|
259 |
+
|
260 |
+
/**
|
261 |
+
* @var integer The number of callback functions in the callback array
|
262 |
+
*/
|
263 |
+
public $nCallback = 0;
|
264 |
+
|
265 |
+
/**
|
266 |
+
* @var array Store label->id pairs for named destinations, these will be used to replace internal links
|
267 |
+
* done this way so that destinations can be defined after the location that links to them
|
268 |
+
*/
|
269 |
+
public $destinations = array();
|
270 |
+
|
271 |
+
/**
|
272 |
+
* @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
|
273 |
+
* publiciables within the class, so that the user can rollback at will (from each 'start' command)
|
274 |
+
* note that this includes the objects array, so these can be large.
|
275 |
+
*/
|
276 |
+
public $checkpoint = '';
|
277 |
+
|
278 |
+
/**
|
279 |
+
* @var array Table of Image origin filenames and image labels which were already added with o_image().
|
280 |
+
* Allows to merge identical images
|
281 |
+
*/
|
282 |
+
public $imagelist = array();
|
283 |
+
|
284 |
+
/**
|
285 |
+
* @var boolean Whether the text passed in should be treated as Unicode or just local character set.
|
286 |
+
*/
|
287 |
+
public $isUnicode = false;
|
288 |
+
|
289 |
+
/**
|
290 |
+
* @var string the JavaScript code of the document
|
291 |
+
*/
|
292 |
+
public $javascript = '';
|
293 |
+
|
294 |
+
/**
|
295 |
+
* @var boolean whether the compression is possible
|
296 |
+
*/
|
297 |
+
protected $compressionReady = false;
|
298 |
+
|
299 |
+
/**
|
300 |
+
* @var array Current page size
|
301 |
+
*/
|
302 |
+
protected $currentPageSize = array("width" => 0, "height" => 0);
|
303 |
+
|
304 |
+
/**
|
305 |
+
* @var array All the chars that will be required in the font subsets
|
306 |
+
*/
|
307 |
+
protected $stringSubsets = array();
|
308 |
+
|
309 |
+
/**
|
310 |
+
* @var string The target internal encoding
|
311 |
+
*/
|
312 |
+
static protected $targetEncoding = 'Windows-1252';
|
313 |
+
|
314 |
+
/**
|
315 |
+
* @var array The list of the core fonts
|
316 |
+
*/
|
317 |
+
static protected $coreFonts = array(
|
318 |
+
'courier',
|
319 |
+
'courier-bold',
|
320 |
+
'courier-oblique',
|
321 |
+
'courier-boldoblique',
|
322 |
+
'helvetica',
|
323 |
+
'helvetica-bold',
|
324 |
+
'helvetica-oblique',
|
325 |
+
'helvetica-boldoblique',
|
326 |
+
'times-roman',
|
327 |
+
'times-bold',
|
328 |
+
'times-italic',
|
329 |
+
'times-bolditalic',
|
330 |
+
'symbol',
|
331 |
+
'zapfdingbats'
|
332 |
+
);
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Class constructor
|
336 |
+
* This will start a new document
|
337 |
+
*
|
338 |
+
* @param array $pageSize Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
|
339 |
+
* @param boolean $isUnicode Whether text will be treated as Unicode or not.
|
340 |
+
* @param string $fontcache The font cache folder
|
341 |
+
* @param string $tmp The temporary folder
|
342 |
+
*/
|
343 |
+
function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '')
|
344 |
+
{
|
345 |
+
$this->isUnicode = $isUnicode;
|
346 |
+
$this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\");
|
347 |
+
$this->tmp = ($tmp !== '' ? $tmp : sys_get_temp_dir());
|
348 |
+
$this->newDocument($pageSize);
|
349 |
+
|
350 |
+
$this->compressionReady = function_exists('gzcompress');
|
351 |
+
|
352 |
+
if (in_array('Windows-1252', mb_list_encodings())) {
|
353 |
+
self::$targetEncoding = 'Windows-1252';
|
354 |
+
}
|
355 |
+
|
356 |
+
// also initialize the font families that are known about already
|
357 |
+
$this->setFontFamily('init');
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* Document object methods (internal use only)
|
362 |
+
*
|
363 |
+
* There is about one object method for each type of object in the pdf document
|
364 |
+
* Each function has the same call list ($id,$action,$options).
|
365 |
+
* $id = the object ID of the object, or what it is to be if it is being created
|
366 |
+
* $action = a string specifying the action to be performed, though ALL must support:
|
367 |
+
* 'new' - create the object with the id $id
|
368 |
+
* 'out' - produce the output for the pdf object
|
369 |
+
* $options = optional, a string or array containing the various parameters for the object
|
370 |
+
*
|
371 |
+
* These, in conjunction with the output function are the ONLY way for output to be produced
|
372 |
+
* within the pdf 'file'.
|
373 |
+
*/
|
374 |
+
|
375 |
+
/**
|
376 |
+
* Destination object, used to specify the location for the user to jump to, presently on opening
|
377 |
+
*
|
378 |
+
* @param $id
|
379 |
+
* @param $action
|
380 |
+
* @param string $options
|
381 |
+
* @return string|null
|
382 |
+
*/
|
383 |
+
protected function o_destination($id, $action, $options = '')
|
384 |
+
{
|
385 |
+
switch ($action) {
|
386 |
+
case 'new':
|
387 |
+
$this->objects[$id] = array('t' => 'destination', 'info' => array());
|
388 |
+
$tmp = '';
|
389 |
+
switch ($options['type']) {
|
390 |
+
case 'XYZ':
|
391 |
+
/** @noinspection PhpMissingBreakStatementInspection */
|
392 |
+
case 'FitR':
|
393 |
+
$tmp = ' ' . $options['p3'] . $tmp;
|
394 |
+
case 'FitH':
|
395 |
+
case 'FitV':
|
396 |
+
case 'FitBH':
|
397 |
+
/** @noinspection PhpMissingBreakStatementInspection */
|
398 |
+
case 'FitBV':
|
399 |
+
$tmp = ' ' . $options['p1'] . ' ' . $options['p2'] . $tmp;
|
400 |
+
case 'Fit':
|
401 |
+
case 'FitB':
|
402 |
+
$tmp = $options['type'] . $tmp;
|
403 |
+
$this->objects[$id]['info']['string'] = $tmp;
|
404 |
+
$this->objects[$id]['info']['page'] = $options['page'];
|
405 |
+
}
|
406 |
+
break;
|
407 |
+
|
408 |
+
case 'out':
|
409 |
+
$o = &$this->objects[$id];
|
410 |
+
|
411 |
+
$tmp = $o['info'];
|
412 |
+
$res = "\n$id 0 obj\n" . '[' . $tmp['page'] . ' 0 R /' . $tmp['string'] . "]\nendobj";
|
413 |
+
|
414 |
+
return $res;
|
415 |
+
}
|
416 |
+
|
417 |
+
return null;
|
418 |
+
}
|
419 |
+
|
420 |
+
/**
|
421 |
+
* set the viewer preferences
|
422 |
+
*
|
423 |
+
* @param $id
|
424 |
+
* @param $action
|
425 |
+
* @param string|array $options
|
426 |
+
* @return string|null
|
427 |
+
*/
|
428 |
+
protected function o_viewerPreferences($id, $action, $options = '')
|
429 |
+
{
|
430 |
+
switch ($action) {
|
431 |
+
case 'new':
|
432 |
+
$this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array());
|
433 |
+
break;
|
434 |
+
|
435 |
+
case 'add':
|
436 |
+
$o = &$this->objects[$id];
|
437 |
+
|
438 |
+
foreach ($options as $k => $v) {
|
439 |
+
switch ($k) {
|
440 |
+
// Boolean keys
|
441 |
+
case 'HideToolbar':
|
442 |
+
case 'HideMenubar':
|
443 |
+
case 'HideWindowUI':
|
444 |
+
case 'FitWindow':
|
445 |
+
case 'CenterWindow':
|
446 |
+
case 'DisplayDocTitle':
|
447 |
+
case 'PickTrayByPDFSize':
|
448 |
+
$o['info'][$k] = (bool)$v;
|
449 |
+
break;
|
450 |
+
|
451 |
+
// Integer keys
|
452 |
+
case 'NumCopies':
|
453 |
+
$o['info'][$k] = (int)$v;
|
454 |
+
break;
|
455 |
+
|
456 |
+
// Name keys
|
457 |
+
case 'ViewArea':
|
458 |
+
case 'ViewClip':
|
459 |
+
case 'PrintClip':
|
460 |
+
case 'PrintArea':
|
461 |
+
$o['info'][$k] = (string)$v;
|
462 |
+
break;
|
463 |
+
|
464 |
+
// Named with limited valid values
|
465 |
+
case 'NonFullScreenPageMode':
|
466 |
+
if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) {
|
467 |
+
break;
|
468 |
+
}
|
469 |
+
$o['info'][$k] = $v;
|
470 |
+
break;
|
471 |
+
|
472 |
+
case 'Direction':
|
473 |
+
if (!in_array($v, array('L2R', 'R2L'))) {
|
474 |
+
break;
|
475 |
+
}
|
476 |
+
$o['info'][$k] = $v;
|
477 |
+
break;
|
478 |
+
|
479 |
+
case 'PrintScaling':
|
480 |
+
if (!in_array($v, array('None', 'AppDefault'))) {
|
481 |
+
break;
|
482 |
+
}
|
483 |
+
$o['info'][$k] = $v;
|
484 |
+
break;
|
485 |
+
|
486 |
+
case 'Duplex':
|
487 |
+
if (!in_array($v, array('None', 'AppDefault'))) {
|
488 |
+
break;
|
489 |
+
}
|
490 |
+
$o['info'][$k] = $v;
|
491 |
+
break;
|
492 |
+
|
493 |
+
// Integer array
|
494 |
+
case 'PrintPageRange':
|
495 |
+
// Cast to integer array
|
496 |
+
foreach ($v as $vK => $vV) {
|
497 |
+
$v[$vK] = (int)$vV;
|
498 |
+
}
|
499 |
+
$o['info'][$k] = array_values($v);
|
500 |
+
break;
|
501 |
+
}
|
502 |
+
}
|
503 |
+
break;
|
504 |
+
|
505 |
+
case 'out':
|
506 |
+
$o = &$this->objects[$id];
|
507 |
+
$res = "\n$id 0 obj\n<< ";
|
508 |
+
|
509 |
+
foreach ($o['info'] as $k => $v) {
|
510 |
+
if (is_string($v)) {
|
511 |
+
$v = '/' . $v;
|
512 |
+
} elseif (is_int($v)) {
|
513 |
+
$v = (string) $v;
|
514 |
+
} elseif (is_bool($v)) {
|
515 |
+
$v = ($v ? 'true' : 'false');
|
516 |
+
} elseif (is_array($v)) {
|
517 |
+
$v = '[' . implode(' ', $v) . ']';
|
518 |
+
}
|
519 |
+
$res .= "\n/$k $v";
|
520 |
+
}
|
521 |
+
$res .= "\n>>\n";
|
522 |
+
|
523 |
+
return $res;
|
524 |
+
}
|
525 |
+
|
526 |
+
return null;
|
527 |
+
}
|
528 |
+
|
529 |
+
/**
|
530 |
+
* define the document catalog, the overall controller for the document
|
531 |
+
*
|
532 |
+
* @param $id
|
533 |
+
* @param $action
|
534 |
+
* @param string|array $options
|
535 |
+
* @return string|null
|
536 |
+
*/
|
537 |
+
protected function o_catalog($id, $action, $options = '')
|
538 |
+
{
|
539 |
+
if ($action !== 'new') {
|
540 |
+
$o = &$this->objects[$id];
|
541 |
+
}
|
542 |
+
|
543 |
+
switch ($action) {
|
544 |
+
case 'new':
|
545 |
+
$this->objects[$id] = array('t' => 'catalog', 'info' => array());
|
546 |
+
$this->catalogId = $id;
|
547 |
+
break;
|
548 |
+
|
549 |
+
case 'outlines':
|
550 |
+
case 'pages':
|
551 |
+
case 'openHere':
|
552 |
+
case 'javascript':
|
553 |
+
$o['info'][$action] = $options;
|
554 |
+
break;
|
555 |
+
|
556 |
+
case 'viewerPreferences':
|
557 |
+
if (!isset($o['info']['viewerPreferences'])) {
|
558 |
+
$this->numObj++;
|
559 |
+
$this->o_viewerPreferences($this->numObj, 'new');
|
560 |
+
$o['info']['viewerPreferences'] = $this->numObj;
|
561 |
+
}
|
562 |
+
|
563 |
+
$vp = $o['info']['viewerPreferences'];
|
564 |
+
$this->o_viewerPreferences($vp, 'add', $options);
|
565 |
+
|
566 |
+
break;
|
567 |
+
|
568 |
+
case 'out':
|
569 |
+
$res = "\n$id 0 obj\n<< /Type /Catalog";
|
570 |
+
|
571 |
+
foreach ($o['info'] as $k => $v) {
|
572 |
+
switch ($k) {
|
573 |
+
case 'outlines':
|
574 |
+
$res .= "\n/Outlines $v 0 R";
|
575 |
+
break;
|
576 |
+
|
577 |
+
case 'pages':
|
578 |
+
$res .= "\n/Pages $v 0 R";
|
579 |
+
break;
|
580 |
+
|
581 |
+
case 'viewerPreferences':
|
582 |
+
$res .= "\n/ViewerPreferences $v 0 R";
|
583 |
+
break;
|
584 |
+
|
585 |
+
case 'openHere':
|
586 |
+
$res .= "\n/OpenAction $v 0 R";
|
587 |
+
break;
|
588 |
+
|
589 |
+
case 'javascript':
|
590 |
+
$res .= "\n/Names <</JavaScript $v 0 R>>";
|
591 |
+
break;
|
592 |
+
}
|
593 |
+
}
|
594 |
+
|
595 |
+
$res .= " >>\nendobj";
|
596 |
+
|
597 |
+
return $res;
|
598 |
+
}
|
599 |
+
|
600 |
+
return null;
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* object which is a parent to the pages in the document
|
605 |
+
*
|
606 |
+
* @param $id
|
607 |
+
* @param $action
|
608 |
+
* @param string $options
|
609 |
+
* @return string|null
|
610 |
+
*/
|
611 |
+
protected function o_pages($id, $action, $options = '')
|
612 |
+
{
|
613 |
+
if ($action !== 'new') {
|
614 |
+
$o = &$this->objects[$id];
|
615 |
+
}
|
616 |
+
|
617 |
+
switch ($action) {
|
618 |
+
case 'new':
|
619 |
+
$this->objects[$id] = array('t' => 'pages', 'info' => array());
|
620 |
+
$this->o_catalog($this->catalogId, 'pages', $id);
|
621 |
+
break;
|
622 |
+
|
623 |
+
case 'page':
|
624 |
+
if (!is_array($options)) {
|
625 |
+
// then it will just be the id of the new page
|
626 |
+
$o['info']['pages'][] = $options;
|
627 |
+
} else {
|
628 |
+
// then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
|
629 |
+
// and pos is either 'before' or 'after', saying where this page will fit.
|
630 |
+
if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
|
631 |
+
$i = array_search($options['rid'], $o['info']['pages']);
|
632 |
+
if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) {
|
633 |
+
|
634 |
+
// then there is a match
|
635 |
+
// make a space
|
636 |
+
switch ($options['pos']) {
|
637 |
+
case 'before':
|
638 |
+
$k = $i;
|
639 |
+
break;
|
640 |
+
|
641 |
+
case 'after':
|
642 |
+
$k = $i + 1;
|
643 |
+
break;
|
644 |
+
|
645 |
+
default:
|
646 |
+
$k = -1;
|
647 |
+
break;
|
648 |
+
}
|
649 |
+
|
650 |
+
if ($k >= 0) {
|
651 |
+
for ($j = count($o['info']['pages']) - 1; $j >= $k; $j--) {
|
652 |
+
$o['info']['pages'][$j + 1] = $o['info']['pages'][$j];
|
653 |
+
}
|
654 |
+
|
655 |
+
$o['info']['pages'][$k] = $options['id'];
|
656 |
+
}
|
657 |
+
}
|
658 |
+
}
|
659 |
+
}
|
660 |
+
break;
|
661 |
+
|
662 |
+
case 'procset':
|
663 |
+
$o['info']['procset'] = $options;
|
664 |
+
break;
|
665 |
+
|
666 |
+
case 'mediaBox':
|
667 |
+
$o['info']['mediaBox'] = $options;
|
668 |
+
// which should be an array of 4 numbers
|
669 |
+
$this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
|
670 |
+
break;
|
671 |
+
|
672 |
+
case 'font':
|
673 |
+
$o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']);
|
674 |
+
break;
|
675 |
+
|
676 |
+
case 'extGState':
|
677 |
+
$o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
|
678 |
+
break;
|
679 |
+
|
680 |
+
case 'xObject':
|
681 |
+
$o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']);
|
682 |
+
break;
|
683 |
+
|
684 |
+
case 'out':
|
685 |
+
if (count($o['info']['pages'])) {
|
686 |
+
$res = "\n$id 0 obj\n<< /Type /Pages\n/Kids [";
|
687 |
+
foreach ($o['info']['pages'] as $v) {
|
688 |
+
$res .= "$v 0 R\n";
|
689 |
+
}
|
690 |
+
|
691 |
+
$res .= "]\n/Count " . count($this->objects[$id]['info']['pages']);
|
692 |
+
|
693 |
+
if ((isset($o['info']['fonts']) && count($o['info']['fonts'])) ||
|
694 |
+
isset($o['info']['procset']) ||
|
695 |
+
(isset($o['info']['extGStates']) && count($o['info']['extGStates']))
|
696 |
+
) {
|
697 |
+
$res .= "\n/Resources <<";
|
698 |
+
|
699 |
+
if (isset($o['info']['procset'])) {
|
700 |
+
$res .= "\n/ProcSet " . $o['info']['procset'] . " 0 R";
|
701 |
+
}
|
702 |
+
|
703 |
+
if (isset($o['info']['fonts']) && count($o['info']['fonts'])) {
|
704 |
+
$res .= "\n/Font << ";
|
705 |
+
foreach ($o['info']['fonts'] as $finfo) {
|
706 |
+
$res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R";
|
707 |
+
}
|
708 |
+
$res .= "\n>>";
|
709 |
+
}
|
710 |
+
|
711 |
+
if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
|
712 |
+
$res .= "\n/XObject << ";
|
713 |
+
foreach ($o['info']['xObjects'] as $finfo) {
|
714 |
+
$res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
|
715 |
+
}
|
716 |
+
$res .= "\n>>";
|
717 |
+
}
|
718 |
+
|
719 |
+
if (isset($o['info']['extGStates']) && count($o['info']['extGStates'])) {
|
720 |
+
$res .= "\n/ExtGState << ";
|
721 |
+
foreach ($o['info']['extGStates'] as $gstate) {
|
722 |
+
$res .= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R";
|
723 |
+
}
|
724 |
+
$res .= "\n>>";
|
725 |
+
}
|
726 |
+
|
727 |
+
$res .= "\n>>";
|
728 |
+
if (isset($o['info']['mediaBox'])) {
|
729 |
+
$tmp = $o['info']['mediaBox'];
|
730 |
+
$res .= "\n/MediaBox [" . sprintf(
|
731 |
+
'%.3F %.3F %.3F %.3F',
|
732 |
+
$tmp[0],
|
733 |
+
$tmp[1],
|
734 |
+
$tmp[2],
|
735 |
+
$tmp[3]
|
736 |
+
) . ']';
|
737 |
+
}
|
738 |
+
}
|
739 |
+
|
740 |
+
$res .= "\n >>\nendobj";
|
741 |
+
} else {
|
742 |
+
$res = "\n$id 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
|
743 |
+
}
|
744 |
+
|
745 |
+
return $res;
|
746 |
+
}
|
747 |
+
|
748 |
+
return null;
|
749 |
+
}
|
750 |
+
|
751 |
+
/**
|
752 |
+
* define the outlines in the doc, empty for now
|
753 |
+
*
|
754 |
+
* @param $id
|
755 |
+
* @param $action
|
756 |
+
* @param string $options
|
757 |
+
* @return string|null
|
758 |
+
*/
|
759 |
+
protected function o_outlines($id, $action, $options = '')
|
760 |
+
{
|
761 |
+
if ($action !== 'new') {
|
762 |
+
$o = &$this->objects[$id];
|
763 |
+
}
|
764 |
+
|
765 |
+
switch ($action) {
|
766 |
+
case 'new':
|
767 |
+
$this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array()));
|
768 |
+
$this->o_catalog($this->catalogId, 'outlines', $id);
|
769 |
+
break;
|
770 |
+
|
771 |
+
case 'outline':
|
772 |
+
$o['info']['outlines'][] = $options;
|
773 |
+
break;
|
774 |
+
|
775 |
+
case 'out':
|
776 |
+
if (count($o['info']['outlines'])) {
|
777 |
+
$res = "\n$id 0 obj\n<< /Type /Outlines /Kids [";
|
778 |
+
foreach ($o['info']['outlines'] as $v) {
|
779 |
+
$res .= "$v 0 R ";
|
780 |
+
}
|
781 |
+
|
782 |
+
$res .= "] /Count " . count($o['info']['outlines']) . " >>\nendobj";
|
783 |
+
} else {
|
784 |
+
$res = "\n$id 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
|
785 |
+
}
|
786 |
+
|
787 |
+
return $res;
|
788 |
+
}
|
789 |
+
|
790 |
+
return null;
|
791 |
+
}
|
792 |
+
|
793 |
+
/**
|
794 |
+
* an object to hold the font description
|
795 |
+
*
|
796 |
+
* @param $id
|
797 |
+
* @param $action
|
798 |
+
* @param string|array $options
|
799 |
+
* @return string|null
|
800 |
+
*/
|
801 |
+
protected function o_font($id, $action, $options = '')
|
802 |
+
{
|
803 |
+
if ($action !== 'new') {
|
804 |
+
$o = &$this->objects[$id];
|
805 |
+
}
|
806 |
+
|
807 |
+
switch ($action) {
|
808 |
+
case 'new':
|
809 |
+
$this->objects[$id] = array(
|
810 |
+
't' => 'font',
|
811 |
+
'info' => array(
|
812 |
+
'name' => $options['name'],
|
813 |
+
'fontFileName' => $options['fontFileName'],
|
814 |
+
'SubType' => 'Type1'
|
815 |
+
)
|
816 |
+
);
|
817 |
+
$fontNum = $this->numFonts;
|
818 |
+
$this->objects[$id]['info']['fontNum'] = $fontNum;
|
819 |
+
|
820 |
+
// deal with the encoding and the differences
|
821 |
+
if (isset($options['differences'])) {
|
822 |
+
// then we'll need an encoding dictionary
|
823 |
+
$this->numObj++;
|
824 |
+
$this->o_fontEncoding($this->numObj, 'new', $options);
|
825 |
+
$this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
|
826 |
+
} else {
|
827 |
+
if (isset($options['encoding'])) {
|
828 |
+
// we can specify encoding here
|
829 |
+
switch ($options['encoding']) {
|
830 |
+
case 'WinAnsiEncoding':
|
831 |
+
case 'MacRomanEncoding':
|
832 |
+
case 'MacExpertEncoding':
|
833 |
+
$this->objects[$id]['info']['encoding'] = $options['encoding'];
|
834 |
+
break;
|
835 |
+
|
836 |
+
case 'none':
|
837 |
+
break;
|
838 |
+
|
839 |
+
default:
|
840 |
+
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
841 |
+
break;
|
842 |
+
}
|
843 |
+
} else {
|
844 |
+
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
845 |
+
}
|
846 |
+
}
|
847 |
+
|
848 |
+
if ($this->fonts[$options['fontFileName']]['isUnicode']) {
|
849 |
+
// For Unicode fonts, we need to incorporate font data into
|
850 |
+
// sub-sections that are linked from the primary font section.
|
851 |
+
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
852 |
+
// for more information.
|
853 |
+
//
|
854 |
+
// All of this code is adapted from the excellent changes made to
|
855 |
+
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
856 |
+
|
857 |
+
$toUnicodeId = ++$this->numObj;
|
858 |
+
$this->o_toUnicode($toUnicodeId, 'new');
|
859 |
+
$this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
|
860 |
+
|
861 |
+
$cidFontId = ++$this->numObj;
|
862 |
+
$this->o_fontDescendentCID($cidFontId, 'new', $options);
|
863 |
+
$this->objects[$id]['info']['cidFont'] = $cidFontId;
|
864 |
+
}
|
865 |
+
|
866 |
+
// also tell the pages node about the new font
|
867 |
+
$this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
|
868 |
+
break;
|
869 |
+
|
870 |
+
case 'add':
|
871 |
+
foreach ($options as $k => $v) {
|
872 |
+
switch ($k) {
|
873 |
+
case 'BaseFont':
|
874 |
+
$o['info']['name'] = $v;
|
875 |
+
break;
|
876 |
+
case 'FirstChar':
|
877 |
+
case 'LastChar':
|
878 |
+
case 'Widths':
|
879 |
+
case 'FontDescriptor':
|
880 |
+
case 'SubType':
|
881 |
+
$this->addMessage('o_font ' . $k . " : " . $v);
|
882 |
+
$o['info'][$k] = $v;
|
883 |
+
break;
|
884 |
+
}
|
885 |
+
}
|
886 |
+
|
887 |
+
// pass values down to descendent font
|
888 |
+
if (isset($o['info']['cidFont'])) {
|
889 |
+
$this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
|
890 |
+
}
|
891 |
+
break;
|
892 |
+
|
893 |
+
case 'out':
|
894 |
+
if ($this->fonts[$this->objects[$id]['info']['fontFileName']]['isUnicode']) {
|
895 |
+
// For Unicode fonts, we need to incorporate font data into
|
896 |
+
// sub-sections that are linked from the primary font section.
|
897 |
+
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
898 |
+
// for more information.
|
899 |
+
//
|
900 |
+
// All of this code is adapted from the excellent changes made to
|
901 |
+
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
902 |
+
|
903 |
+
$res = "\n$id 0 obj\n<</Type /Font\n/Subtype /Type0\n";
|
904 |
+
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
905 |
+
|
906 |
+
// The horizontal identity mapping for 2-byte CIDs; may be used
|
907 |
+
// with CIDFonts using any Registry, Ordering, and Supplement values.
|
908 |
+
$res .= "/Encoding /Identity-H\n";
|
909 |
+
$res .= "/DescendantFonts [" . $o['info']['cidFont'] . " 0 R]\n";
|
910 |
+
$res .= "/ToUnicode " . $o['info']['toUnicode'] . " 0 R\n";
|
911 |
+
$res .= ">>\n";
|
912 |
+
$res .= "endobj";
|
913 |
+
} else {
|
914 |
+
$res = "\n$id 0 obj\n<< /Type /Font\n/Subtype /" . $o['info']['SubType'] . "\n";
|
915 |
+
$res .= "/Name /F" . $o['info']['fontNum'] . "\n";
|
916 |
+
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
917 |
+
|
918 |
+
if (isset($o['info']['encodingDictionary'])) {
|
919 |
+
// then place a reference to the dictionary
|
920 |
+
$res .= "/Encoding " . $o['info']['encodingDictionary'] . " 0 R\n";
|
921 |
+
} else {
|
922 |
+
if (isset($o['info']['encoding'])) {
|
923 |
+
// use the specified encoding
|
924 |
+
$res .= "/Encoding /" . $o['info']['encoding'] . "\n";
|
925 |
+
}
|
926 |
+
}
|
927 |
+
|
928 |
+
if (isset($o['info']['FirstChar'])) {
|
929 |
+
$res .= "/FirstChar " . $o['info']['FirstChar'] . "\n";
|
930 |
+
}
|
931 |
+
|
932 |
+
if (isset($o['info']['LastChar'])) {
|
933 |
+
$res .= "/LastChar " . $o['info']['LastChar'] . "\n";
|
934 |
+
}
|
935 |
+
|
936 |
+
if (isset($o['info']['Widths'])) {
|
937 |
+
$res .= "/Widths " . $o['info']['Widths'] . " 0 R\n";
|
938 |
+
}
|
939 |
+
|
940 |
+
if (isset($o['info']['FontDescriptor'])) {
|
941 |
+
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
942 |
+
}
|
943 |
+
|
944 |
+
$res .= ">>\n";
|
945 |
+
$res .= "endobj";
|
946 |
+
}
|
947 |
+
|
948 |
+
return $res;
|
949 |
+
}
|
950 |
+
|
951 |
+
return null;
|
952 |
+
}
|
953 |
+
|
954 |
+
/**
|
955 |
+
* A toUnicode section, needed for unicode fonts
|
956 |
+
*
|
957 |
+
* @param $id
|
958 |
+
* @param $action
|
959 |
+
* @return null|string
|
960 |
+
*/
|
961 |
+
protected function o_toUnicode($id, $action)
|
962 |
+
{
|
963 |
+
switch ($action) {
|
964 |
+
case 'new':
|
965 |
+
$this->objects[$id] = array(
|
966 |
+
't' => 'toUnicode'
|
967 |
+
);
|
968 |
+
break;
|
969 |
+
case 'add':
|
970 |
+
break;
|
971 |
+
case 'out':
|
972 |
+
$ordering = '(UCS)';
|
973 |
+
$registry = '(Adobe)';
|
974 |
+
|
975 |
+
if ($this->encrypted) {
|
976 |
+
$this->encryptInit($id);
|
977 |
+
$ordering = $this->ARC4($ordering);
|
978 |
+
$registry = $this->ARC4($registry);
|
979 |
+
}
|
980 |
+
|
981 |
+
$stream = <<<EOT
|
982 |
+
/CIDInit /ProcSet findresource begin
|
983 |
+
12 dict begin
|
984 |
+
begincmap
|
985 |
+
/CIDSystemInfo
|
986 |
+
<</Registry $registry
|
987 |
+
/Ordering $ordering
|
988 |
+
/Supplement 0
|
989 |
+
>> def
|
990 |
+
/CMapName /Adobe-Identity-UCS def
|
991 |
+
/CMapType 2 def
|
992 |
+
1 begincodespacerange
|
993 |
+
<0000> <FFFF>
|
994 |
+
endcodespacerange
|
995 |
+
1 beginbfrange
|
996 |
+
<0000> <FFFF> <0000>
|
997 |
+
endbfrange
|
998 |
+
endcmap
|
999 |
+
CMapName currentdict /CMap defineresource pop
|
1000 |
+
end
|
1001 |
+
end
|
1002 |
+
EOT;
|
1003 |
+
|
1004 |
+
$res = "\n$id 0 obj\n";
|
1005 |
+
$res .= "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
|
1006 |
+
$res .= "stream\n" . $stream . "\nendstream" . "\nendobj";;
|
1007 |
+
|
1008 |
+
return $res;
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
return null;
|
1012 |
+
}
|
1013 |
+
|
1014 |
+
/**
|
1015 |
+
* a font descriptor, needed for including additional fonts
|
1016 |
+
*
|
1017 |
+
* @param $id
|
1018 |
+
* @param $action
|
1019 |
+
* @param string $options
|
1020 |
+
* @return null|string
|
1021 |
+
*/
|
1022 |
+
protected function o_fontDescriptor($id, $action, $options = '')
|
1023 |
+
{
|
1024 |
+
if ($action !== 'new') {
|
1025 |
+
$o = &$this->objects[$id];
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
switch ($action) {
|
1029 |
+
case 'new':
|
1030 |
+
$this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options);
|
1031 |
+
break;
|
1032 |
+
|
1033 |
+
case 'out':
|
1034 |
+
$res = "\n$id 0 obj\n<< /Type /FontDescriptor\n";
|
1035 |
+
foreach ($o['info'] as $label => $value) {
|
1036 |
+
switch ($label) {
|
1037 |
+
case 'Ascent':
|
1038 |
+
case 'CapHeight':
|
1039 |
+
case 'Descent':
|
1040 |
+
case 'Flags':
|
1041 |
+
case 'ItalicAngle':
|
1042 |
+
case 'StemV':
|
1043 |
+
case 'AvgWidth':
|
1044 |
+
case 'Leading':
|
1045 |
+
case 'MaxWidth':
|
1046 |
+
case 'MissingWidth':
|
1047 |
+
case 'StemH':
|
1048 |
+
case 'XHeight':
|
1049 |
+
case 'CharSet':
|
1050 |
+
if (mb_strlen($value, '8bit')) {
|
1051 |
+
$res .= "/$label $value\n";
|
1052 |
+
}
|
1053 |
+
|
1054 |
+
break;
|
1055 |
+
case 'FontFile':
|
1056 |
+
case 'FontFile2':
|
1057 |
+
case 'FontFile3':
|
1058 |
+
$res .= "/$label $value 0 R\n";
|
1059 |
+
break;
|
1060 |
+
|
1061 |
+
case 'FontBBox':
|
1062 |
+
$res .= "/$label [$value[0] $value[1] $value[2] $value[3]]\n";
|
1063 |
+
break;
|
1064 |
+
|
1065 |
+
case 'FontName':
|
1066 |
+
$res .= "/$label /$value\n";
|
1067 |
+
break;
|
1068 |
+
}
|
1069 |
+
}
|
1070 |
+
|
1071 |
+
$res .= ">>\nendobj";
|
1072 |
+
|
1073 |
+
return $res;
|
1074 |
+
}
|
1075 |
+
|
1076 |
+
return null;
|
1077 |
+
}
|
1078 |
+
|
1079 |
+
/**
|
1080 |
+
* the font encoding
|
1081 |
+
*
|
1082 |
+
* @param $id
|
1083 |
+
* @param $action
|
1084 |
+
* @param string $options
|
1085 |
+
* @return null|string
|
1086 |
+
*/
|
1087 |
+
protected function o_fontEncoding($id, $action, $options = '')
|
1088 |
+
{
|
1089 |
+
if ($action !== 'new') {
|
1090 |
+
$o = &$this->objects[$id];
|
1091 |
+
}
|
1092 |
+
|
1093 |
+
switch ($action) {
|
1094 |
+
case 'new':
|
1095 |
+
// the options array should contain 'differences' and maybe 'encoding'
|
1096 |
+
$this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options);
|
1097 |
+
break;
|
1098 |
+
|
1099 |
+
case 'out':
|
1100 |
+
$res = "\n$id 0 obj\n<< /Type /Encoding\n";
|
1101 |
+
if (!isset($o['info']['encoding'])) {
|
1102 |
+
$o['info']['encoding'] = 'WinAnsiEncoding';
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
if ($o['info']['encoding'] !== 'none') {
|
1106 |
+
$res .= "/BaseEncoding /" . $o['info']['encoding'] . "\n";
|
1107 |
+
}
|
1108 |
+
|
1109 |
+
$res .= "/Differences \n[";
|
1110 |
+
|
1111 |
+
$onum = -100;
|
1112 |
+
|
1113 |
+
foreach ($o['info']['differences'] as $num => $label) {
|
1114 |
+
if ($num != $onum + 1) {
|
1115 |
+
// we cannot make use of consecutive numbering
|
1116 |
+
$res .= "\n$num /$label";
|
1117 |
+
} else {
|
1118 |
+
$res .= " /$label";
|
1119 |
+
}
|
1120 |
+
|
1121 |
+
$onum = $num;
|
1122 |
+
}
|
1123 |
+
|
1124 |
+
$res .= "\n]\n>>\nendobj";
|
1125 |
+
|
1126 |
+
return $res;
|
1127 |
+
}
|
1128 |
+
|
1129 |
+
return null;
|
1130 |
+
}
|
1131 |
+
|
1132 |
+
/**
|
1133 |
+
* a descendent cid font, needed for unicode fonts
|
1134 |
+
*
|
1135 |
+
* @param $id
|
1136 |
+
* @param $action
|
1137 |
+
* @param string|array $options
|
1138 |
+
* @return null|string
|
1139 |
+
*/
|
1140 |
+
protected function o_fontDescendentCID($id, $action, $options = '')
|
1141 |
+
{
|
1142 |
+
if ($action !== 'new') {
|
1143 |
+
$o = &$this->objects[$id];
|
1144 |
+
}
|
1145 |
+
|
1146 |
+
switch ($action) {
|
1147 |
+
case 'new':
|
1148 |
+
$this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
|
1149 |
+
|
1150 |
+
// we need a CID system info section
|
1151 |
+
$cidSystemInfoId = ++$this->numObj;
|
1152 |
+
$this->o_cidSystemInfo($cidSystemInfoId, 'new');
|
1153 |
+
$this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
|
1154 |
+
|
1155 |
+
// and a CID to GID map
|
1156 |
+
$cidToGidMapId = ++$this->numObj;
|
1157 |
+
$this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
|
1158 |
+
$this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
|
1159 |
+
break;
|
1160 |
+
|
1161 |
+
case 'add':
|
1162 |
+
foreach ($options as $k => $v) {
|
1163 |
+
switch ($k) {
|
1164 |
+
case 'BaseFont':
|
1165 |
+
$o['info']['name'] = $v;
|
1166 |
+
break;
|
1167 |
+
|
1168 |
+
case 'FirstChar':
|
1169 |
+
case 'LastChar':
|
1170 |
+
case 'MissingWidth':
|
1171 |
+
case 'FontDescriptor':
|
1172 |
+
case 'SubType':
|
1173 |
+
$this->addMessage("o_fontDescendentCID $k : $v");
|
1174 |
+
$o['info'][$k] = $v;
|
1175 |
+
break;
|
1176 |
+
}
|
1177 |
+
}
|
1178 |
+
|
1179 |
+
// pass values down to cid to gid map
|
1180 |
+
$this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
|
1181 |
+
break;
|
1182 |
+
|
1183 |
+
case 'out':
|
1184 |
+
$res = "\n$id 0 obj\n";
|
1185 |
+
$res .= "<</Type /Font\n";
|
1186 |
+
$res .= "/Subtype /CIDFontType2\n";
|
1187 |
+
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
1188 |
+
$res .= "/CIDSystemInfo " . $o['info']['cidSystemInfo'] . " 0 R\n";
|
1189 |
+
// if (isset($o['info']['FirstChar'])) {
|
1190 |
+
// $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
|
1191 |
+
// }
|
1192 |
+
|
1193 |
+
// if (isset($o['info']['LastChar'])) {
|
1194 |
+
// $res.= "/LastChar ".$o['info']['LastChar']."\n";
|
1195 |
+
// }
|
1196 |
+
if (isset($o['info']['FontDescriptor'])) {
|
1197 |
+
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
1198 |
+
}
|
1199 |
+
|
1200 |
+
if (isset($o['info']['MissingWidth'])) {
|
1201 |
+
$res .= "/DW " . $o['info']['MissingWidth'] . "\n";
|
1202 |
+
}
|
1203 |
+
|
1204 |
+
if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
|
1205 |
+
$cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
|
1206 |
+
$w = '';
|
1207 |
+
foreach ($cid_widths as $cid => $width) {
|
1208 |
+
$w .= "$cid [$width] ";
|
1209 |
+
}
|
1210 |
+
$res .= "/W [$w]\n";
|
1211 |
+
}
|
1212 |
+
|
1213 |
+
$res .= "/CIDToGIDMap " . $o['info']['cidToGidMap'] . " 0 R\n";
|
1214 |
+
$res .= ">>\n";
|
1215 |
+
$res .= "endobj";
|
1216 |
+
|
1217 |
+
return $res;
|
1218 |
+
}
|
1219 |
+
|
1220 |
+
return null;
|
1221 |
+
}
|
1222 |
+
|
1223 |
+
/**
|
1224 |
+
* CID system info section, needed for unicode fonts
|
1225 |
+
*
|
1226 |
+
* @param $id
|
1227 |
+
* @param $action
|
1228 |
+
* @return null|string
|
1229 |
+
*/
|
1230 |
+
protected function o_cidSystemInfo($id, $action)
|
1231 |
+
{
|
1232 |
+
switch ($action) {
|
1233 |
+
case 'new':
|
1234 |
+
$this->objects[$id] = array(
|
1235 |
+
't' => 'cidSystemInfo'
|
1236 |
+
);
|
1237 |
+
break;
|
1238 |
+
case 'add':
|
1239 |
+
break;
|
1240 |
+
case 'out':
|
1241 |
+
$ordering = '(UCS)';
|
1242 |
+
$registry = '(Adobe)';
|
1243 |
+
|
1244 |
+
if ($this->encrypted) {
|
1245 |
+
$this->encryptInit($id);
|
1246 |
+
$ordering = $this->ARC4($ordering);
|
1247 |
+
$registry = $this->ARC4($registry);
|
1248 |
+
}
|
1249 |
+
|
1250 |
+
|
1251 |
+
$res = "\n$id 0 obj\n";
|
1252 |
+
|
1253 |
+
$res .= '<</Registry ' . $registry . "\n"; // A string identifying an issuer of character collections
|
1254 |
+
$res .= '/Ordering ' . $ordering . "\n"; // A string that uniquely names a character collection issued by a specific registry
|
1255 |
+
$res .= "/Supplement 0\n"; // The supplement number of the character collection.
|
1256 |
+
$res .= ">>";
|
1257 |
+
|
1258 |
+
$res .= "\nendobj";;
|
1259 |
+
|
1260 |
+
return $res;
|
1261 |
+
}
|
1262 |
+
|
1263 |
+
return null;
|
1264 |
+
}
|
1265 |
+
|
1266 |
+
/**
|
1267 |
+
* a font glyph to character map, needed for unicode fonts
|
1268 |
+
*
|
1269 |
+
* @param $id
|
1270 |
+
* @param $action
|
1271 |
+
* @param string $options
|
1272 |
+
* @return null|string
|
1273 |
+
*/
|
1274 |
+
protected function o_fontGIDtoCIDMap($id, $action, $options = '')
|
1275 |
+
{
|
1276 |
+
if ($action !== 'new') {
|
1277 |
+
$o = &$this->objects[$id];
|
1278 |
+
}
|
1279 |
+
|
1280 |
+
switch ($action) {
|
1281 |
+
case 'new':
|
1282 |
+
$this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options);
|
1283 |
+
break;
|
1284 |
+
|
1285 |
+
case 'out':
|
1286 |
+
$res = "\n$id 0 obj\n";
|
1287 |
+
$fontFileName = $o['info']['fontFileName'];
|
1288 |
+
$tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']);
|
1289 |
+
|
1290 |
+
$compressed = isset($this->fonts[$fontFileName]['CIDtoGID_Compressed']) &&
|
1291 |
+
$this->fonts[$fontFileName]['CIDtoGID_Compressed'];
|
1292 |
+
|
1293 |
+
if (!$compressed && isset($o['raw'])) {
|
1294 |
+
$res .= $tmp;
|
1295 |
+
} else {
|
1296 |
+
$res .= "<<";
|
1297 |
+
|
1298 |
+
if (!$compressed && $this->compressionReady && $this->options['compression']) {
|
1299 |
+
// then implement ZLIB based compression on this content stream
|
1300 |
+
$compressed = true;
|
1301 |
+
$tmp = gzcompress($tmp, 6);
|
1302 |
+
}
|
1303 |
+
if ($compressed) {
|
1304 |
+
$res .= "\n/Filter /FlateDecode";
|
1305 |
+
}
|
1306 |
+
|
1307 |
+
if ($this->encrypted) {
|
1308 |
+
$this->encryptInit($id);
|
1309 |
+
$tmp = $this->ARC4($tmp);
|
1310 |
+
}
|
1311 |
+
|
1312 |
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream";
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
$res .= "\nendobj";
|
1316 |
+
|
1317 |
+
return $res;
|
1318 |
+
}
|
1319 |
+
|
1320 |
+
return null;
|
1321 |
+
}
|
1322 |
+
|
1323 |
+
/**
|
1324 |
+
* the document procset, solves some problems with printing to old PS printers
|
1325 |
+
*
|
1326 |
+
* @param $id
|
1327 |
+
* @param $action
|
1328 |
+
* @param string $options
|
1329 |
+
* @return null|string
|
1330 |
+
*/
|
1331 |
+
protected function o_procset($id, $action, $options = '')
|
1332 |
+
{
|
1333 |
+
if ($action !== 'new') {
|
1334 |
+
$o = &$this->objects[$id];
|
1335 |
+
}
|
1336 |
+
|
1337 |
+
switch ($action) {
|
1338 |
+
case 'new':
|
1339 |
+
$this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1));
|
1340 |
+
$this->o_pages($this->currentNode, 'procset', $id);
|
1341 |
+
$this->procsetObjectId = $id;
|
1342 |
+
break;
|
1343 |
+
|
1344 |
+
case 'add':
|
1345 |
+
// this is to add new items to the procset list, despite the fact that this is considered
|
1346 |
+
// obsolete, the items are required for printing to some postscript printers
|
1347 |
+
switch ($options) {
|
1348 |
+
case 'ImageB':
|
1349 |
+
case 'ImageC':
|
1350 |
+
case 'ImageI':
|
1351 |
+
$o['info'][$options] = 1;
|
1352 |
+
break;
|
1353 |
+
}
|
1354 |
+
break;
|
1355 |
+
|
1356 |
+
case 'out':
|
1357 |
+
$res = "\n$id 0 obj\n[";
|
1358 |
+
foreach ($o['info'] as $label => $val) {
|
1359 |
+
$res .= "/$label ";
|
1360 |
+
}
|
1361 |
+
$res .= "]\nendobj";
|
1362 |
+
|
1363 |
+
return $res;
|
1364 |
+
}
|
1365 |
+
|
1366 |
+
return null;
|
1367 |
+
}
|
1368 |
+
|
1369 |
+
/**
|
1370 |
+
* define the document information
|
1371 |
+
*
|
1372 |
+
* @param $id
|
1373 |
+
* @param $action
|
1374 |
+
* @param string $options
|
1375 |
+
* @return null|string
|
1376 |
+
*/
|
1377 |
+
protected function o_info($id, $action, $options = '')
|
1378 |
+
{
|
1379 |
+
switch ($action) {
|
1380 |
+
case 'new':
|
1381 |
+
$this->infoObject = $id;
|
1382 |
+
$date = 'D:' . @date('Ymd');
|
1383 |
+
$this->objects[$id] = array(
|
1384 |
+
't' => 'info',
|
1385 |
+
'info' => array(
|
1386 |
+
'Producer' => 'CPDF (dompdf)',
|
1387 |
+
'CreationDate' => $date
|
1388 |
+
)
|
1389 |
+
);
|
1390 |
+
break;
|
1391 |
+
case 'Title':
|
1392 |
+
case 'Author':
|
1393 |
+
case 'Subject':
|
1394 |
+
case 'Keywords':
|
1395 |
+
case 'Creator':
|
1396 |
+
case 'Producer':
|
1397 |
+
case 'CreationDate':
|
1398 |
+
case 'ModDate':
|
1399 |
+
case 'Trapped':
|
1400 |
+
$this->objects[$id]['info'][$action] = $options;
|
1401 |
+
break;
|
1402 |
+
|
1403 |
+
case 'out':
|
1404 |
+
$encrypted = $this->encrypted;
|
1405 |
+
if ($encrypted) {
|
1406 |
+
$this->encryptInit($id);
|
1407 |
+
}
|
1408 |
+
|
1409 |
+
$res = "\n$id 0 obj\n<<\n";
|
1410 |
+
$o = &$this->objects[$id];
|
1411 |
+
foreach ($o['info'] as $k => $v) {
|
1412 |
+
$res .= "/$k (";
|
1413 |
+
|
1414 |
+
// dates must be outputted as-is, without Unicode transformations
|
1415 |
+
if ($k !== 'CreationDate' && $k !== 'ModDate') {
|
1416 |
+
$v = $this->filterText($v, true, false);
|
1417 |
+
}
|
1418 |
+
|
1419 |
+
if ($encrypted) {
|
1420 |
+
$v = $this->ARC4($v);
|
1421 |
+
}
|
1422 |
+
|
1423 |
+
$res .= $v;
|
1424 |
+
$res .= ")\n";
|
1425 |
+
}
|
1426 |
+
|
1427 |
+
$res .= ">>\nendobj";
|
1428 |
+
|
1429 |
+
return $res;
|
1430 |
+
}
|
1431 |
+
|
1432 |
+
return null;
|
1433 |
+
}
|
1434 |
+
|
1435 |
+
/**
|
1436 |
+
* an action object, used to link to URLS initially
|
1437 |
+
*
|
1438 |
+
* @param $id
|
1439 |
+
* @param $action
|
1440 |
+
* @param string $options
|
1441 |
+
* @return null|string
|
1442 |
+
*/
|
1443 |
+
protected function o_action($id, $action, $options = '')
|
1444 |
+
{
|
1445 |
+
if ($action !== 'new') {
|
1446 |
+
$o = &$this->objects[$id];
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
switch ($action) {
|
1450 |
+
case 'new':
|
1451 |
+
if (is_array($options)) {
|
1452 |
+
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']);
|
1453 |
+
} else {
|
1454 |
+
// then assume a URI action
|
1455 |
+
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI');
|
1456 |
+
}
|
1457 |
+
break;
|
1458 |
+
|
1459 |
+
case 'out':
|
1460 |
+
if ($this->encrypted) {
|
1461 |
+
$this->encryptInit($id);
|
1462 |
+
}
|
1463 |
+
|
1464 |
+
$res = "\n$id 0 obj\n<< /Type /Action";
|
1465 |
+
switch ($o['type']) {
|
1466 |
+
case 'ilink':
|
1467 |
+
if (!isset($this->destinations[(string)$o['info']['label']])) {
|
1468 |
+
break;
|
1469 |
+
}
|
1470 |
+
|
1471 |
+
// there will be an 'label' setting, this is the name of the destination
|
1472 |
+
$res .= "\n/S /GoTo\n/D " . $this->destinations[(string)$o['info']['label']] . " 0 R";
|
1473 |
+
break;
|
1474 |
+
|
1475 |
+
case 'URI':
|
1476 |
+
$res .= "\n/S /URI\n/URI (";
|
1477 |
+
if ($this->encrypted) {
|
1478 |
+
$res .= $this->filterText($this->ARC4($o['info']), false, false);
|
1479 |
+
} else {
|
1480 |
+
$res .= $this->filterText($o['info'], false, false);
|
1481 |
+
}
|
1482 |
+
|
1483 |
+
$res .= ")";
|
1484 |
+
break;
|
1485 |
+
}
|
1486 |
+
|
1487 |
+
$res .= "\n>>\nendobj";
|
1488 |
+
|
1489 |
+
return $res;
|
1490 |
+
}
|
1491 |
+
|
1492 |
+
return null;
|
1493 |
+
}
|
1494 |
+
|
1495 |
+
/**
|
1496 |
+
* an annotation object, this will add an annotation to the current page.
|
1497 |
+
* initially will support just link annotations
|
1498 |
+
*
|
1499 |
+
* @param $id
|
1500 |
+
* @param $action
|
1501 |
+
* @param string $options
|
1502 |
+
* @return null|string
|
1503 |
+
*/
|
1504 |
+
protected function o_annotation($id, $action, $options = '')
|
1505 |
+
{
|
1506 |
+
if ($action !== 'new') {
|
1507 |
+
$o = &$this->objects[$id];
|
1508 |
+
}
|
1509 |
+
|
1510 |
+
switch ($action) {
|
1511 |
+
case 'new':
|
1512 |
+
// add the annotation to the current page
|
1513 |
+
$pageId = $this->currentPage;
|
1514 |
+
$this->o_page($pageId, 'annot', $id);
|
1515 |
+
|
1516 |
+
// and add the action object which is going to be required
|
1517 |
+
switch ($options['type']) {
|
1518 |
+
case 'link':
|
1519 |
+
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1520 |
+
$this->numObj++;
|
1521 |
+
$this->o_action($this->numObj, 'new', $options['url']);
|
1522 |
+
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1523 |
+
break;
|
1524 |
+
|
1525 |
+
case 'ilink':
|
1526 |
+
// this is to a named internal link
|
1527 |
+
$label = $options['label'];
|
1528 |
+
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1529 |
+
$this->numObj++;
|
1530 |
+
$this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label));
|
1531 |
+
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1532 |
+
break;
|
1533 |
+
}
|
1534 |
+
break;
|
1535 |
+
|
1536 |
+
case 'out':
|
1537 |
+
$res = "\n$id 0 obj\n<< /Type /Annot";
|
1538 |
+
switch ($o['info']['type']) {
|
1539 |
+
case 'link':
|
1540 |
+
case 'ilink':
|
1541 |
+
$res .= "\n/Subtype /Link";
|
1542 |
+
break;
|
1543 |
+
}
|
1544 |
+
$res .= "\n/A " . $o['info']['actionId'] . " 0 R";
|
1545 |
+
$res .= "\n/Border [0 0 0]";
|
1546 |
+
$res .= "\n/H /I";
|
1547 |
+
$res .= "\n/Rect [ ";
|
1548 |
+
|
1549 |
+
foreach ($o['info']['rect'] as $v) {
|
1550 |
+
$res .= sprintf("%.4F ", $v);
|
1551 |
+
}
|
1552 |
+
|
1553 |
+
$res .= "]";
|
1554 |
+
$res .= "\n>>\nendobj";
|
1555 |
+
|
1556 |
+
return $res;
|
1557 |
+
}
|
1558 |
+
|
1559 |
+
return null;
|
1560 |
+
}
|
1561 |
+
|
1562 |
+
/**
|
1563 |
+
* a page object, it also creates a contents object to hold its contents
|
1564 |
+
*
|
1565 |
+
* @param $id
|
1566 |
+
* @param $action
|
1567 |
+
* @param string $options
|
1568 |
+
* @return null|string
|
1569 |
+
*/
|
1570 |
+
protected function o_page($id, $action, $options = '')
|
1571 |
+
{
|
1572 |
+
if ($action !== 'new') {
|
1573 |
+
$o = &$this->objects[$id];
|
1574 |
+
}
|
1575 |
+
|
1576 |
+
switch ($action) {
|
1577 |
+
case 'new':
|
1578 |
+
$this->numPages++;
|
1579 |
+
$this->objects[$id] = array(
|
1580 |
+
't' => 'page',
|
1581 |
+
'info' => array(
|
1582 |
+
'parent' => $this->currentNode,
|
1583 |
+
'pageNum' => $this->numPages,
|
1584 |
+
'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox']
|
1585 |
+
)
|
1586 |
+
);
|
1587 |
+
|
1588 |
+
if (is_array($options)) {
|
1589 |
+
// then this must be a page insertion, array should contain 'rid','pos'=[before|after]
|
1590 |
+
$options['id'] = $id;
|
1591 |
+
$this->o_pages($this->currentNode, 'page', $options);
|
1592 |
+
} else {
|
1593 |
+
$this->o_pages($this->currentNode, 'page', $id);
|
1594 |
+
}
|
1595 |
+
|
1596 |
+
$this->currentPage = $id;
|
1597 |
+
//make a contents object to go with this page
|
1598 |
+
$this->numObj++;
|
1599 |
+
$this->o_contents($this->numObj, 'new', $id);
|
1600 |
+
$this->currentContents = $this->numObj;
|
1601 |
+
$this->objects[$id]['info']['contents'] = array();
|
1602 |
+
$this->objects[$id]['info']['contents'][] = $this->numObj;
|
1603 |
+
|
1604 |
+
$match = ($this->numPages % 2 ? 'odd' : 'even');
|
1605 |
+
foreach ($this->addLooseObjects as $oId => $target) {
|
1606 |
+
if ($target === 'all' || $match === $target) {
|
1607 |
+
$this->objects[$id]['info']['contents'][] = $oId;
|
1608 |
+
}
|
1609 |
+
}
|
1610 |
+
break;
|
1611 |
+
|
1612 |
+
case 'content':
|
1613 |
+
$o['info']['contents'][] = $options;
|
1614 |
+
break;
|
1615 |
+
|
1616 |
+
case 'annot':
|
1617 |
+
// add an annotation to this page
|
1618 |
+
if (!isset($o['info']['annot'])) {
|
1619 |
+
$o['info']['annot'] = array();
|
1620 |
+
}
|
1621 |
+
|
1622 |
+
// $options should contain the id of the annotation dictionary
|
1623 |
+
$o['info']['annot'][] = $options;
|
1624 |
+
break;
|
1625 |
+
|
1626 |
+
case 'out':
|
1627 |
+
$res = "\n$id 0 obj\n<< /Type /Page";
|
1628 |
+
if (isset($o['info']['mediaBox'])) {
|
1629 |
+
$tmp = $o['info']['mediaBox'];
|
1630 |
+
$res .= "\n/MediaBox [" . sprintf(
|
1631 |
+
'%.3F %.3F %.3F %.3F',
|
1632 |
+
$tmp[0],
|
1633 |
+
$tmp[1],
|
1634 |
+
$tmp[2],
|
1635 |
+
$tmp[3]
|
1636 |
+
) . ']';
|
1637 |
+
}
|
1638 |
+
$res .= "\n/Parent " . $o['info']['parent'] . " 0 R";
|
1639 |
+
|
1640 |
+
if (isset($o['info']['annot'])) {
|
1641 |
+
$res .= "\n/Annots [";
|
1642 |
+
foreach ($o['info']['annot'] as $aId) {
|
1643 |
+
$res .= " $aId 0 R";
|
1644 |
+
}
|
1645 |
+
$res .= " ]";
|
1646 |
+
}
|
1647 |
+
|
1648 |
+
$count = count($o['info']['contents']);
|
1649 |
+
if ($count == 1) {
|
1650 |
+
$res .= "\n/Contents " . $o['info']['contents'][0] . " 0 R";
|
1651 |
+
} else {
|
1652 |
+
if ($count > 1) {
|
1653 |
+
$res .= "\n/Contents [\n";
|
1654 |
+
|
1655 |
+
// reverse the page contents so added objects are below normal content
|
1656 |
+
//foreach (array_reverse($o['info']['contents']) as $cId) {
|
1657 |
+
// Back to normal now that I've got transparency working --Benj
|
1658 |
+
foreach ($o['info']['contents'] as $cId) {
|
1659 |
+
$res .= "$cId 0 R\n";
|
1660 |
+
}
|
1661 |
+
$res .= "]";
|
1662 |
+
}
|
1663 |
+
}
|
1664 |
+
|
1665 |
+
$res .= "\n>>\nendobj";
|
1666 |
+
|
1667 |
+
return $res;
|
1668 |
+
}
|
1669 |
+
|
1670 |
+
return null;
|
1671 |
+
}
|
1672 |
+
|
1673 |
+
/**
|
1674 |
+
* the contents objects hold all of the content which appears on pages
|
1675 |
+
*
|
1676 |
+
* @param $id
|
1677 |
+
* @param $action
|
1678 |
+
* @param string|array $options
|
1679 |
+
* @return null|string
|
1680 |
+
*/
|
1681 |
+
protected function o_contents($id, $action, $options = '')
|
1682 |
+
{
|
1683 |
+
if ($action !== 'new') {
|
1684 |
+
$o = &$this->objects[$id];
|
1685 |
+
}
|
1686 |
+
|
1687 |
+
switch ($action) {
|
1688 |
+
case 'new':
|
1689 |
+
$this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array());
|
1690 |
+
if (mb_strlen($options, '8bit') && intval($options)) {
|
1691 |
+
// then this contents is the primary for a page
|
1692 |
+
$this->objects[$id]['onPage'] = $options;
|
1693 |
+
} else {
|
1694 |
+
if ($options === 'raw') {
|
1695 |
+
// then this page contains some other type of system object
|
1696 |
+
$this->objects[$id]['raw'] = 1;
|
1697 |
+
}
|
1698 |
+
}
|
1699 |
+
break;
|
1700 |
+
|
1701 |
+
case 'add':
|
1702 |
+
// add more options to the declaration
|
1703 |
+
foreach ($options as $k => $v) {
|
1704 |
+
$o['info'][$k] = $v;
|
1705 |
+
}
|
1706 |
+
|
1707 |
+
case 'out':
|
1708 |
+
$tmp = $o['c'];
|
1709 |
+
$res = "\n$id 0 obj\n";
|
1710 |
+
|
1711 |
+
if (isset($this->objects[$id]['raw'])) {
|
1712 |
+
$res .= $tmp;
|
1713 |
+
} else {
|
1714 |
+
$res .= "<<";
|
1715 |
+
if ($this->compressionReady && $this->options['compression']) {
|
1716 |
+
// then implement ZLIB based compression on this content stream
|
1717 |
+
$res .= " /Filter /FlateDecode";
|
1718 |
+
$tmp = gzcompress($tmp, 6);
|
1719 |
+
}
|
1720 |
+
|
1721 |
+
if ($this->encrypted) {
|
1722 |
+
$this->encryptInit($id);
|
1723 |
+
$tmp = $this->ARC4($tmp);
|
1724 |
+
}
|
1725 |
+
|
1726 |
+
foreach ($o['info'] as $k => $v) {
|
1727 |
+
$res .= "\n/$k $v";
|
1728 |
+
}
|
1729 |
+
|
1730 |
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . " >>\nstream\n$tmp\nendstream";
|
1731 |
+
}
|
1732 |
+
|
1733 |
+
$res .= "\nendobj";
|
1734 |
+
|
1735 |
+
return $res;
|
1736 |
+
}
|
1737 |
+
|
1738 |
+
return null;
|
1739 |
+
}
|
1740 |
+
|
1741 |
+
/**
|
1742 |
+
* @param $id
|
1743 |
+
* @param $action
|
1744 |
+
* @return string|null
|
1745 |
+
*/
|
1746 |
+
protected function o_embedjs($id, $action)
|
1747 |
+
{
|
1748 |
+
switch ($action) {
|
1749 |
+
case 'new':
|
1750 |
+
$this->objects[$id] = array(
|
1751 |
+
't' => 'embedjs',
|
1752 |
+
'info' => array(
|
1753 |
+
'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]'
|
1754 |
+
)
|
1755 |
+
);
|
1756 |
+
break;
|
1757 |
+
|
1758 |
+
case 'out':
|
1759 |
+
$o = &$this->objects[$id];
|
1760 |
+
$res = "\n$id 0 obj\n<< ";
|
1761 |
+
foreach ($o['info'] as $k => $v) {
|
1762 |
+
$res .= "\n/$k $v";
|
1763 |
+
}
|
1764 |
+
$res .= "\n>>\nendobj";
|
1765 |
+
|
1766 |
+
return $res;
|
1767 |
+
}
|
1768 |
+
|
1769 |
+
return null;
|
1770 |
+
}
|
1771 |
+
|
1772 |
+
/**
|
1773 |
+
* @param $id
|
1774 |
+
* @param $action
|
1775 |
+
* @param string $code
|
1776 |
+
* @return null|string
|
1777 |
+
*/
|
1778 |
+
protected function o_javascript($id, $action, $code = '')
|
1779 |
+
{
|
1780 |
+
switch ($action) {
|
1781 |
+
case 'new':
|
1782 |
+
$this->objects[$id] = array(
|
1783 |
+
't' => 'javascript',
|
1784 |
+
'info' => array(
|
1785 |
+
'S' => '/JavaScript',
|
1786 |
+
'JS' => '(' . $this->filterText($code, true, false) . ')',
|
1787 |
+
)
|
1788 |
+
);
|
1789 |
+
break;
|
1790 |
+
|
1791 |
+
case 'out':
|
1792 |
+
$o = &$this->objects[$id];
|
1793 |
+
$res = "\n$id 0 obj\n<< ";
|
1794 |
+
|
1795 |
+
foreach ($o['info'] as $k => $v) {
|
1796 |
+
$res .= "\n/$k $v";
|
1797 |
+
}
|
1798 |
+
$res .= "\n>>\nendobj";
|
1799 |
+
|
1800 |
+
return $res;
|
1801 |
+
}
|
1802 |
+
|
1803 |
+
return null;
|
1804 |
+
}
|
1805 |
+
|
1806 |
+
/**
|
1807 |
+
* an image object, will be an XObject in the document, includes description and data
|
1808 |
+
*
|
1809 |
+
* @param $id
|
1810 |
+
* @param $action
|
1811 |
+
* @param string $options
|
1812 |
+
* @return null|string
|
1813 |
+
*/
|
1814 |
+
protected function o_image($id, $action, $options = '')
|
1815 |
+
{
|
1816 |
+
switch ($action) {
|
1817 |
+
case 'new':
|
1818 |
+
// make the new object
|
1819 |
+
$this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array());
|
1820 |
+
|
1821 |
+
$info =& $this->objects[$id]['info'];
|
1822 |
+
|
1823 |
+
$info['Type'] = '/XObject';
|
1824 |
+
$info['Subtype'] = '/Image';
|
1825 |
+
$info['Width'] = $options['iw'];
|
1826 |
+
$info['Height'] = $options['ih'];
|
1827 |
+
|
1828 |
+
if (isset($options['masked']) && $options['masked']) {
|
1829 |
+
$info['SMask'] = ($this->numObj - 1) . ' 0 R';
|
1830 |
+
}
|
1831 |
+
|
1832 |
+
if (!isset($options['type']) || $options['type'] === 'jpg') {
|
1833 |
+
if (!isset($options['channels'])) {
|
1834 |
+
$options['channels'] = 3;
|
1835 |
+
}
|
1836 |
+
|
1837 |
+
switch ($options['channels']) {
|
1838 |
+
case 1:
|
1839 |
+
$info['ColorSpace'] = '/DeviceGray';
|
1840 |
+
break;
|
1841 |
+
case 4:
|
1842 |
+
$info['ColorSpace'] = '/DeviceCMYK';
|
1843 |
+
break;
|
1844 |
+
default:
|
1845 |
+
$info['ColorSpace'] = '/DeviceRGB';
|
1846 |
+
break;
|
1847 |
+
}
|
1848 |
+
|
1849 |
+
if ($info['ColorSpace'] === '/DeviceCMYK') {
|
1850 |
+
$info['Decode'] = '[1 0 1 0 1 0 1 0]';
|
1851 |
+
}
|
1852 |
+
|
1853 |
+
$info['Filter'] = '/DCTDecode';
|
1854 |
+
$info['BitsPerComponent'] = 8;
|
1855 |
+
} else {
|
1856 |
+
if ($options['type'] === 'png') {
|
1857 |
+
$info['Filter'] = '/FlateDecode';
|
1858 |
+
$info['DecodeParms'] = '<< /Predictor 15 /Colors ' . $options['ncolor'] . ' /Columns ' . $options['iw'] . ' /BitsPerComponent ' . $options['bitsPerComponent'] . '>>';
|
1859 |
+
|
1860 |
+
if ($options['isMask']) {
|
1861 |
+
$info['ColorSpace'] = '/DeviceGray';
|
1862 |
+
} else {
|
1863 |
+
if (mb_strlen($options['pdata'], '8bit')) {
|
1864 |
+
$tmp = ' [ /Indexed /DeviceRGB ' . (mb_strlen($options['pdata'], '8bit') / 3 - 1) . ' ';
|
1865 |
+
$this->numObj++;
|
1866 |
+
$this->o_contents($this->numObj, 'new');
|
1867 |
+
$this->objects[$this->numObj]['c'] = $options['pdata'];
|
1868 |
+
$tmp .= $this->numObj . ' 0 R';
|
1869 |
+
$tmp .= ' ]';
|
1870 |
+
$info['ColorSpace'] = $tmp;
|
1871 |
+
|
1872 |
+
if (isset($options['transparency'])) {
|
1873 |
+
$transparency = $options['transparency'];
|
1874 |
+
switch ($transparency['type']) {
|
1875 |
+
case 'indexed':
|
1876 |
+
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1877 |
+
$info['Mask'] = $tmp;
|
1878 |
+
break;
|
1879 |
+
|
1880 |
+
case 'color-key':
|
1881 |
+
$tmp = ' [ ' .
|
1882 |
+
$transparency['r'] . ' ' . $transparency['r'] .
|
1883 |
+
$transparency['g'] . ' ' . $transparency['g'] .
|
1884 |
+
$transparency['b'] . ' ' . $transparency['b'] .
|
1885 |
+
' ] ';
|
1886 |
+
$info['Mask'] = $tmp;
|
1887 |
+
break;
|
1888 |
+
}
|
1889 |
+
}
|
1890 |
+
} else {
|
1891 |
+
if (isset($options['transparency'])) {
|
1892 |
+
$transparency = $options['transparency'];
|
1893 |
+
|
1894 |
+
switch ($transparency['type']) {
|
1895 |
+
case 'indexed':
|
1896 |
+
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1897 |
+
$info['Mask'] = $tmp;
|
1898 |
+
break;
|
1899 |
+
|
1900 |
+
case 'color-key':
|
1901 |
+
$tmp = ' [ ' .
|
1902 |
+
$transparency['r'] . ' ' . $transparency['r'] . ' ' .
|
1903 |
+
$transparency['g'] . ' ' . $transparency['g'] . ' ' .
|
1904 |
+
$transparency['b'] . ' ' . $transparency['b'] .
|
1905 |
+
' ] ';
|
1906 |
+
$info['Mask'] = $tmp;
|
1907 |
+
break;
|
1908 |
+
}
|
1909 |
+
}
|
1910 |
+
$info['ColorSpace'] = '/' . $options['color'];
|
1911 |
+
}
|
1912 |
+
}
|
1913 |
+
|
1914 |
+
$info['BitsPerComponent'] = $options['bitsPerComponent'];
|
1915 |
+
}
|
1916 |
+
}
|
1917 |
+
|
1918 |
+
// assign it a place in the named resource dictionary as an external object, according to
|
1919 |
+
// the label passed in with it.
|
1920 |
+
$this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id));
|
1921 |
+
|
1922 |
+
// also make sure that we have the right procset object for it.
|
1923 |
+
$this->o_procset($this->procsetObjectId, 'add', 'ImageC');
|
1924 |
+
break;
|
1925 |
+
|
1926 |
+
case 'out':
|
1927 |
+
$o = &$this->objects[$id];
|
1928 |
+
$tmp = &$o['data'];
|
1929 |
+
$res = "\n$id 0 obj\n<<";
|
1930 |
+
|
1931 |
+
foreach ($o['info'] as $k => $v) {
|
1932 |
+
$res .= "\n/$k $v";
|
1933 |
+
}
|
1934 |
+
|
1935 |
+
if ($this->encrypted) {
|
1936 |
+
$this->encryptInit($id);
|
1937 |
+
$tmp = $this->ARC4($tmp);
|
1938 |
+
}
|
1939 |
+
|
1940 |
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream\nendobj";
|
1941 |
+
|
1942 |
+
return $res;
|
1943 |
+
}
|
1944 |
+
|
1945 |
+
return null;
|
1946 |
+
}
|
1947 |
+
|
1948 |
+
/**
|
1949 |
+
* graphics state object
|
1950 |
+
*
|
1951 |
+
* @param $id
|
1952 |
+
* @param $action
|
1953 |
+
* @param string $options
|
1954 |
+
* @return null|string
|
1955 |
+
*/
|
1956 |
+
protected function o_extGState($id, $action, $options = "")
|
1957 |
+
{
|
1958 |
+
static $valid_params = array(
|
1959 |
+
"LW",
|
1960 |
+
"LC",
|
1961 |
+
"LC",
|
1962 |
+
"LJ",
|
1963 |
+
"ML",
|
1964 |
+
"D",
|
1965 |
+
"RI",
|
1966 |
+
"OP",
|
1967 |
+
"op",
|
1968 |
+
"OPM",
|
1969 |
+
"Font",
|
1970 |
+
"BG",
|
1971 |
+
"BG2",
|
1972 |
+
"UCR",
|
1973 |
+
"TR",
|
1974 |
+
"TR2",
|
1975 |
+
"HT",
|
1976 |
+
"FL",
|
1977 |
+
"SM",
|
1978 |
+
"SA",
|
1979 |
+
"BM",
|
1980 |
+
"SMask",
|
1981 |
+
"CA",
|
1982 |
+
"ca",
|
1983 |
+
"AIS",
|
1984 |
+
"TK"
|
1985 |
+
);
|
1986 |
+
|
1987 |
+
switch ($action) {
|
1988 |
+
case "new":
|
1989 |
+
$this->objects[$id] = array('t' => 'extGState', 'info' => $options);
|
1990 |
+
|
1991 |
+
// Tell the pages about the new resource
|
1992 |
+
$this->numStates++;
|
1993 |
+
$this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
|
1994 |
+
break;
|
1995 |
+
|
1996 |
+
case "out":
|
1997 |
+
$o = &$this->objects[$id];
|
1998 |
+
$res = "\n$id 0 obj\n<< /Type /ExtGState\n";
|
1999 |
+
|
2000 |
+
foreach ($o["info"] as $k => $v) {
|
2001 |
+
if (!in_array($k, $valid_params)) {
|
2002 |
+
continue;
|
2003 |
+
}
|
2004 |
+
$res .= "/$k $v\n";
|
2005 |
+
}
|
2006 |
+
|
2007 |
+
$res .= ">>\nendobj";
|
2008 |
+
|
2009 |
+
return $res;
|
2010 |
+
}
|
2011 |
+
|
2012 |
+
return null;
|
2013 |
+
}
|
2014 |
+
|
2015 |
+
/**
|
2016 |
+
* encryption object.
|
2017 |
+
*
|
2018 |
+
* @param $id
|
2019 |
+
* @param $action
|
2020 |
+
* @param string $options
|
2021 |
+
* @return string|null
|
2022 |
+
*/
|
2023 |
+
protected function o_encryption($id, $action, $options = '')
|
2024 |
+
{
|
2025 |
+
switch ($action) {
|
2026 |
+
case 'new':
|
2027 |
+
// make the new object
|
2028 |
+
$this->objects[$id] = array('t' => 'encryption', 'info' => $options);
|
2029 |
+
$this->arc4_objnum = $id;
|
2030 |
+
break;
|
2031 |
+
|
2032 |
+
case 'keys':
|
2033 |
+
// figure out the additional parameters required
|
2034 |
+
$pad = chr(0x28) . chr(0xBF) . chr(0x4E) . chr(0x5E) . chr(0x4E) . chr(0x75) . chr(0x8A) . chr(0x41)
|
2035 |
+
. chr(0x64) . chr(0x00) . chr(0x4E) . chr(0x56) . chr(0xFF) . chr(0xFA) . chr(0x01) . chr(0x08)
|
2036 |
+
. chr(0x2E) . chr(0x2E) . chr(0x00) . chr(0xB6) . chr(0xD0) . chr(0x68) . chr(0x3E) . chr(0x80)
|
2037 |
+
. chr(0x2F) . chr(0x0C) . chr(0xA9) . chr(0xFE) . chr(0x64) . chr(0x53) . chr(0x69) . chr(0x7A);
|
2038 |
+
|
2039 |
+
$info = $this->objects[$id]['info'];
|
2040 |
+
|
2041 |
+
$len = mb_strlen($info['owner'], '8bit');
|
2042 |
+
|
2043 |
+
if ($len > 32) {
|
2044 |
+
$owner = substr($info['owner'], 0, 32);
|
2045 |
+
} else {
|
2046 |
+
if ($len < 32) {
|
2047 |
+
$owner = $info['owner'] . substr($pad, 0, 32 - $len);
|
2048 |
+
} else {
|
2049 |
+
$owner = $info['owner'];
|
2050 |
+
}
|
2051 |
+
}
|
2052 |
+
|
2053 |
+
$len = mb_strlen($info['user'], '8bit');
|
2054 |
+
if ($len > 32) {
|
2055 |
+
$user = substr($info['user'], 0, 32);
|
2056 |
+
} else {
|
2057 |
+
if ($len < 32) {
|
2058 |
+
$user = $info['user'] . substr($pad, 0, 32 - $len);
|
2059 |
+
} else {
|
2060 |
+
$user = $info['user'];
|
2061 |
+
}
|
2062 |
+
}
|
2063 |
+
|
2064 |
+
$tmp = $this->md5_16($owner);
|
2065 |
+
$okey = substr($tmp, 0, 5);
|
2066 |
+
$this->ARC4_init($okey);
|
2067 |
+
$ovalue = $this->ARC4($user);
|
2068 |
+
$this->objects[$id]['info']['O'] = $ovalue;
|
2069 |
+
|
2070 |
+
// now make the u value, phew.
|
2071 |
+
$tmp = $this->md5_16(
|
2072 |
+
$user . $ovalue . chr($info['p']) . chr(255) . chr(255) . chr(255) . hex2bin($this->fileIdentifier)
|
2073 |
+
);
|
2074 |
+
|
2075 |
+
$ukey = substr($tmp, 0, 5);
|
2076 |
+
$this->ARC4_init($ukey);
|
2077 |
+
$this->encryptionKey = $ukey;
|
2078 |
+
$this->encrypted = true;
|
2079 |
+
$uvalue = $this->ARC4($pad);
|
2080 |
+
$this->objects[$id]['info']['U'] = $uvalue;
|
2081 |
+
// initialize the arc4 array
|
2082 |
+
break;
|
2083 |
+
|
2084 |
+
case 'out':
|
2085 |
+
$o = &$this->objects[$id];
|
2086 |
+
|
2087 |
+
$res = "\n$id 0 obj\n<<";
|
2088 |
+
$res .= "\n/Filter /Standard";
|
2089 |
+
$res .= "\n/V 1";
|
2090 |
+
$res .= "\n/R 2";
|
2091 |
+
$res .= "\n/O (" . $this->filterText($o['info']['O'], false, false) . ')';
|
2092 |
+
$res .= "\n/U (" . $this->filterText($o['info']['U'], false, false) . ')';
|
2093 |
+
// and the p-value needs to be converted to account for the twos-complement approach
|
2094 |
+
$o['info']['p'] = (($o['info']['p'] ^ 255) + 1) * -1;
|
2095 |
+
$res .= "\n/P " . ($o['info']['p']);
|
2096 |
+
$res .= "\n>>\nendobj";
|
2097 |
+
|
2098 |
+
return $res;
|
2099 |
+
}
|
2100 |
+
|
2101 |
+
return null;
|
2102 |
+
}
|
2103 |
+
|
2104 |
+
/**
|
2105 |
+
* ARC4 functions
|
2106 |
+
* A series of function to implement ARC4 encoding in PHP
|
2107 |
+
*/
|
2108 |
+
|
2109 |
+
/**
|
2110 |
+
* calculate the 16 byte version of the 128 bit md5 digest of the string
|
2111 |
+
*
|
2112 |
+
* @param $string
|
2113 |
+
* @return string
|
2114 |
+
*/
|
2115 |
+
function md5_16($string)
|
2116 |
+
{
|
2117 |
+
$tmp = md5($string);
|
2118 |
+
$out = '';
|
2119 |
+
for ($i = 0; $i <= 30; $i = $i + 2) {
|
2120 |
+
$out .= chr(hexdec(substr($tmp, $i, 2)));
|
2121 |
+
}
|
2122 |
+
|
2123 |
+
return $out;
|
2124 |
+
}
|
2125 |
+
|
2126 |
+
/**
|
2127 |
+
* initialize the encryption for processing a particular object
|
2128 |
+
*
|
2129 |
+
* @param $id
|
2130 |
+
*/
|
2131 |
+
function encryptInit($id)
|
2132 |
+
{
|
2133 |
+
$tmp = $this->encryptionKey;
|
2134 |
+
$hex = dechex($id);
|
2135 |
+
if (mb_strlen($hex, '8bit') < 6) {
|
2136 |
+
$hex = substr('000000', 0, 6 - mb_strlen($hex, '8bit')) . $hex;
|
2137 |
+
}
|
2138 |
+
$tmp .= chr(hexdec(substr($hex, 4, 2)))
|
2139 |
+
. chr(hexdec(substr($hex, 2, 2)))
|
2140 |
+
. chr(hexdec(substr($hex, 0, 2)))
|
2141 |
+
. chr(0)
|
2142 |
+
. chr(0)
|
2143 |
+
;
|
2144 |
+
$key = $this->md5_16($tmp);
|
2145 |
+
$this->ARC4_init(substr($key, 0, 10));
|
2146 |
+
}
|
2147 |
+
|
2148 |
+
/**
|
2149 |
+
* initialize the ARC4 encryption
|
2150 |
+
*
|
2151 |
+
* @param string $key
|
2152 |
+
*/
|
2153 |
+
function ARC4_init($key = '')
|
2154 |
+
{
|
2155 |
+
$this->arc4 = '';
|
2156 |
+
|
2157 |
+
// setup the control array
|
2158 |
+
if (mb_strlen($key, '8bit') == 0) {
|
2159 |
+
return;
|
2160 |
+
}
|
2161 |
+
|
2162 |
+
$k = '';
|
2163 |
+
while (mb_strlen($k, '8bit') < 256) {
|
2164 |
+
$k .= $key;
|
2165 |
+
}
|
2166 |
+
|
2167 |
+
$k = substr($k, 0, 256);
|
2168 |
+
for ($i = 0; $i < 256; $i++) {
|
2169 |
+
$this->arc4 .= chr($i);
|
2170 |
+
}
|
2171 |
+
|
2172 |
+
$j = 0;
|
2173 |
+
|
2174 |
+
for ($i = 0; $i < 256; $i++) {
|
2175 |
+
$t = $this->arc4[$i];
|
2176 |
+
$j = ($j + ord($t) + ord($k[$i])) % 256;
|
2177 |
+
$this->arc4[$i] = $this->arc4[$j];
|
2178 |
+
$this->arc4[$j] = $t;
|
2179 |
+
}
|
2180 |
+
}
|
2181 |
+
|
2182 |
+
/**
|
2183 |
+
* ARC4 encrypt a text string
|
2184 |
+
*
|
2185 |
+
* @param $text
|
2186 |
+
* @return string
|
2187 |
+
*/
|
2188 |
+
function ARC4($text)
|
2189 |
+
{
|
2190 |
+
$len = mb_strlen($text, '8bit');
|
2191 |
+
$a = 0;
|
2192 |
+
$b = 0;
|
2193 |
+
$c = $this->arc4;
|
2194 |
+
$out = '';
|
2195 |
+
for ($i = 0; $i < $len; $i++) {
|
2196 |
+
$a = ($a + 1) % 256;
|
2197 |
+
$t = $c[$a];
|
2198 |
+
$b = ($b + ord($t)) % 256;
|
2199 |
+
$c[$a] = $c[$b];
|
2200 |
+
$c[$b] = $t;
|
2201 |
+
$k = ord($c[(ord($c[$a]) + ord($c[$b])) % 256]);
|
2202 |
+
$out .= chr(ord($text[$i]) ^ $k);
|
2203 |
+
}
|
2204 |
+
|
2205 |
+
return $out;
|
2206 |
+
}
|
2207 |
+
|
2208 |
+
/**
|
2209 |
+
* functions which can be called to adjust or add to the document
|
2210 |
+
*/
|
2211 |
+
|
2212 |
+
/**
|
2213 |
+
* add a link in the document to an external URL
|
2214 |
+
*
|
2215 |
+
* @param $url
|
2216 |
+
* @param $x0
|
2217 |
+
* @param $y0
|
2218 |
+
* @param $x1
|
2219 |
+
* @param $y1
|
2220 |
+
*/
|
2221 |
+
function addLink($url, $x0, $y0, $x1, $y1)
|
2222 |
+
{
|
2223 |
+
$this->numObj++;
|
2224 |
+
$info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1));
|
2225 |
+
$this->o_annotation($this->numObj, 'new', $info);
|
2226 |
+
}
|
2227 |
+
|
2228 |
+
/**
|
2229 |
+
* add a link in the document to an internal destination (ie. within the document)
|
2230 |
+
*
|
2231 |
+
* @param $label
|
2232 |
+
* @param $x0
|
2233 |
+
* @param $y0
|
2234 |
+
* @param $x1
|
2235 |
+
* @param $y1
|
2236 |
+
*/
|
2237 |
+
function addInternalLink($label, $x0, $y0, $x1, $y1)
|
2238 |
+
{
|
2239 |
+
$this->numObj++;
|
2240 |
+
$info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1));
|
2241 |
+
$this->o_annotation($this->numObj, 'new', $info);
|
2242 |
+
}
|
2243 |
+
|
2244 |
+
/**
|
2245 |
+
* set the encryption of the document
|
2246 |
+
* can be used to turn it on and/or set the passwords which it will have.
|
2247 |
+
* also the functions that the user will have are set here, such as print, modify, add
|
2248 |
+
*
|
2249 |
+
* @param string $userPass
|
2250 |
+
* @param string $ownerPass
|
2251 |
+
* @param array $pc
|
2252 |
+
*/
|
2253 |
+
function setEncryption($userPass = '', $ownerPass = '', $pc = array())
|
2254 |
+
{
|
2255 |
+
$p = bindec("11000000");
|
2256 |
+
|
2257 |
+
$options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32);
|
2258 |
+
|
2259 |
+
foreach ($pc as $k => $v) {
|
2260 |
+
if ($v && isset($options[$k])) {
|
2261 |
+
$p += $options[$k];
|
2262 |
+
} else {
|
2263 |
+
if (isset($options[$v])) {
|
2264 |
+
$p += $options[$v];
|
2265 |
+
}
|
2266 |
+
}
|
2267 |
+
}
|
2268 |
+
|
2269 |
+
// implement encryption on the document
|
2270 |
+
if ($this->arc4_objnum == 0) {
|
2271 |
+
// then the block does not exist already, add it.
|
2272 |
+
$this->numObj++;
|
2273 |
+
if (mb_strlen($ownerPass) == 0) {
|
2274 |
+
$ownerPass = $userPass;
|
2275 |
+
}
|
2276 |
+
|
2277 |
+
$this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p));
|
2278 |
+
}
|
2279 |
+
}
|
2280 |
+
|
2281 |
+
/**
|
2282 |
+
* should be used for internal checks, not implemented as yet
|
2283 |
+
*/
|
2284 |
+
function checkAllHere()
|
2285 |
+
{
|
2286 |
+
}
|
2287 |
+
|
2288 |
+
/**
|
2289 |
+
* return the pdf stream as a string returned from the function
|
2290 |
+
*
|
2291 |
+
* @param bool $debug
|
2292 |
+
* @return string
|
2293 |
+
*/
|
2294 |
+
function output($debug = false)
|
2295 |
+
{
|
2296 |
+
if ($debug) {
|
2297 |
+
// turn compression off
|
2298 |
+
$this->options['compression'] = false;
|
2299 |
+
}
|
2300 |
+
|
2301 |
+
if ($this->javascript) {
|
2302 |
+
$this->numObj++;
|
2303 |
+
|
2304 |
+
$js_id = $this->numObj;
|
2305 |
+
$this->o_embedjs($js_id, 'new');
|
2306 |
+
$this->o_javascript(++$this->numObj, 'new', $this->javascript);
|
2307 |
+
|
2308 |
+
$id = $this->catalogId;
|
2309 |
+
|
2310 |
+
$this->o_catalog($id, 'javascript', $js_id);
|
2311 |
+
}
|
2312 |
+
|
2313 |
+
if ($this->fileIdentifier === '') {
|
2314 |
+
$tmp = implode('', $this->objects[$this->infoObject]['info']);
|
2315 |
+
$this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand());
|
2316 |
+
}
|
2317 |
+
|
2318 |
+
if ($this->arc4_objnum) {
|
2319 |
+
$this->o_encryption($this->arc4_objnum, 'keys');
|
2320 |
+
$this->ARC4_init($this->encryptionKey);
|
2321 |
+
}
|
2322 |
+
|
2323 |
+
$this->checkAllHere();
|
2324 |
+
|
2325 |
+
$xref = array();
|
2326 |
+
$content = '%PDF-1.3';
|
2327 |
+
$pos = mb_strlen($content, '8bit');
|
2328 |
+
|
2329 |
+
foreach ($this->objects as $k => $v) {
|
2330 |
+
$tmp = 'o_' . $v['t'];
|
2331 |
+
$cont = $this->$tmp($k, 'out');
|
2332 |
+
$content .= $cont;
|
2333 |
+
$xref[] = $pos + 1; //+1 to account for \n at the start of each object
|
2334 |
+
$pos += mb_strlen($cont, '8bit');
|
2335 |
+
}
|
2336 |
+
|
2337 |
+
$content .= "\nxref\n0 " . (count($xref) + 1) . "\n0000000000 65535 f \n";
|
2338 |
+
|
2339 |
+
foreach ($xref as $p) {
|
2340 |
+
$content .= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n";
|
2341 |
+
}
|
2342 |
+
|
2343 |
+
$content .= "trailer\n<<\n" .
|
2344 |
+
'/Size ' . (count($xref) + 1) . "\n" .
|
2345 |
+
'/Root 1 0 R' . "\n" .
|
2346 |
+
'/Info ' . $this->infoObject . " 0 R\n"
|
2347 |
+
;
|
2348 |
+
|
2349 |
+
// if encryption has been applied to this document then add the marker for this dictionary
|
2350 |
+
if ($this->arc4_objnum > 0) {
|
2351 |
+
$content .= '/Encrypt ' . $this->arc4_objnum . " 0 R\n";
|
2352 |
+
}
|
2353 |
+
|
2354 |
+
$content .= '/ID[<' . $this->fileIdentifier . '><' . $this->fileIdentifier . ">]\n";
|
2355 |
+
|
2356 |
+
// account for \n added at start of xref table
|
2357 |
+
$pos++;
|
2358 |
+
|
2359 |
+
$content .= ">>\nstartxref\n$pos\n%%EOF\n";
|
2360 |
+
|
2361 |
+
return $content;
|
2362 |
+
}
|
2363 |
+
|
2364 |
+
/**
|
2365 |
+
* initialize a new document
|
2366 |
+
* if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
|
2367 |
+
* this function is called automatically by the constructor function
|
2368 |
+
*
|
2369 |
+
* @param array $pageSize
|
2370 |
+
*/
|
2371 |
+
private function newDocument($pageSize = array(0, 0, 612, 792))
|
2372 |
+
{
|
2373 |
+
$this->numObj = 0;
|
2374 |
+
$this->objects = array();
|
2375 |
+
|
2376 |
+
$this->numObj++;
|
2377 |
+
$this->o_catalog($this->numObj, 'new');
|
2378 |
+
|
2379 |
+
$this->numObj++;
|
2380 |
+
$this->o_outlines($this->numObj, 'new');
|
2381 |
+
|
2382 |
+
$this->numObj++;
|
2383 |
+
$this->o_pages($this->numObj, 'new');
|
2384 |
+
|
2385 |
+
$this->o_pages($this->numObj, 'mediaBox', $pageSize);
|
2386 |
+
$this->currentNode = 3;
|
2387 |
+
|
2388 |
+
$this->numObj++;
|
2389 |
+
$this->o_procset($this->numObj, 'new');
|
2390 |
+
|
2391 |
+
$this->numObj++;
|
2392 |
+
$this->o_info($this->numObj, 'new');
|
2393 |
+
|
2394 |
+
$this->numObj++;
|
2395 |
+
$this->o_page($this->numObj, 'new');
|
2396 |
+
|
2397 |
+
// need to store the first page id as there is no way to get it to the user during
|
2398 |
+
// startup
|
2399 |
+
$this->firstPageId = $this->currentContents;
|
2400 |
+
}
|
2401 |
+
|
2402 |
+
/**
|
2403 |
+
* open the font file and return a php structure containing it.
|
2404 |
+
* first check if this one has been done before and saved in a form more suited to php
|
2405 |
+
* note that if a php serialized version does not exist it will try and make one, but will
|
2406 |
+
* require write access to the directory to do it... it is MUCH faster to have these serialized
|
2407 |
+
* files.
|
2408 |
+
*
|
2409 |
+
* @param $font
|
2410 |
+
*/
|
2411 |
+
private function openFont($font)
|
2412 |
+
{
|
2413 |
+
// assume that $font contains the path and file but not the extension
|
2414 |
+
$name = basename($font);
|
2415 |
+
$dir = dirname($font) . '/';
|
2416 |
+
|
2417 |
+
$fontcache = $this->fontcache;
|
2418 |
+
if ($fontcache == '') {
|
2419 |
+
$fontcache = rtrim($dir, DIRECTORY_SEPARATOR."/\\");
|
2420 |
+
}
|
2421 |
+
|
2422 |
+
//$name filename without folder and extension of font metrics
|
2423 |
+
//$dir folder of font metrics
|
2424 |
+
//$fontcache folder of runtime created php serialized version of font metrics.
|
2425 |
+
// If this is not given, the same folder as the font metrics will be used.
|
2426 |
+
// Storing and reusing serialized versions improves speed much
|
2427 |
+
|
2428 |
+
$this->addMessage("openFont: $font - $name");
|
2429 |
+
|
2430 |
+
if (!$this->isUnicode || in_array(mb_strtolower(basename($name)), self::$coreFonts)) {
|
2431 |
+
$metrics_name = "$name.afm";
|
2432 |
+
} else {
|
2433 |
+
$metrics_name = "$name.ufm";
|
2434 |
+
}
|
2435 |
+
|
2436 |
+
$cache_name = "$metrics_name.php";
|
2437 |
+
$this->addMessage("metrics: $metrics_name, cache: $cache_name");
|
2438 |
+
|
2439 |
+
if (file_exists($fontcache . '/' . $cache_name)) {
|
2440 |
+
$this->addMessage("openFont: php file exists $fontcache/$cache_name");
|
2441 |
+
$this->fonts[$font] = require($fontcache . '/' . $cache_name);
|
2442 |
+
|
2443 |
+
if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) {
|
2444 |
+
// if the font file is old, then clear it out and prepare for re-creation
|
2445 |
+
$this->addMessage('openFont: clear out, make way for new version.');
|
2446 |
+
$this->fonts[$font] = null;
|
2447 |
+
unset($this->fonts[$font]);
|
2448 |
+
}
|
2449 |
+
} else {
|
2450 |
+
$old_cache_name = "php_$metrics_name";
|
2451 |
+
if (file_exists($fontcache . '/' . $old_cache_name)) {
|
2452 |
+
$this->addMessage(
|
2453 |
+
"openFont: php file doesn't exist $fontcache/$cache_name, creating it from the old format"
|
2454 |
+
);
|
2455 |
+
$old_cache = file_get_contents($fontcache . '/' . $old_cache_name);
|
2456 |
+
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . $old_cache . ';');
|
2457 |
+
|
2458 |
+
$this->openFont($font);
|
2459 |
+
return;
|
2460 |
+
}
|
2461 |
+
}
|
2462 |
+
|
2463 |
+
if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
|
2464 |
+
// then rebuild the php_<font>.afm file from the <font>.afm file
|
2465 |
+
$this->addMessage("openFont: build php file from $dir$metrics_name");
|
2466 |
+
$data = array();
|
2467 |
+
|
2468 |
+
// 20 => 'space'
|
2469 |
+
$data['codeToName'] = array();
|
2470 |
+
|
2471 |
+
// Since we're not going to enable Unicode for the core fonts we need to use a font-based
|
2472 |
+
// setting for Unicode support rather than a global setting.
|
2473 |
+
$data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm');
|
2474 |
+
|
2475 |
+
$cidtogid = '';
|
2476 |
+
if ($data['isUnicode']) {
|
2477 |
+
$cidtogid = str_pad('', 256 * 256 * 2, "\x00");
|
2478 |
+
}
|
2479 |
+
|
2480 |
+
$file = file($dir . $metrics_name);
|
2481 |
+
|
2482 |
+
foreach ($file as $rowA) {
|
2483 |
+
$row = trim($rowA);
|
2484 |
+
$pos = strpos($row, ' ');
|
2485 |
+
|
2486 |
+
if ($pos) {
|
2487 |
+
// then there must be some keyword
|
2488 |
+
$key = substr($row, 0, $pos);
|
2489 |
+
switch ($key) {
|
2490 |
+
case 'FontName':
|
2491 |
+
case 'FullName':
|
2492 |
+
case 'FamilyName':
|
2493 |
+
case 'PostScriptName':
|
2494 |
+
case 'Weight':
|
2495 |
+
case 'ItalicAngle':
|
2496 |
+
case 'IsFixedPitch':
|
2497 |
+
case 'CharacterSet':
|
2498 |
+
case 'UnderlinePosition':
|
2499 |
+
case 'UnderlineThickness':
|
2500 |
+
case 'Version':
|
2501 |
+
case 'EncodingScheme':
|
2502 |
+
case 'CapHeight':
|
2503 |
+
case 'XHeight':
|
2504 |
+
case 'Ascender':
|
2505 |
+
case 'Descender':
|
2506 |
+
case 'StdHW':
|
2507 |
+
case 'StdVW':
|
2508 |
+
case 'StartCharMetrics':
|
2509 |
+
case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font. Otherwise it's too big.
|
2510 |
+
$data[$key] = trim(substr($row, $pos));
|
2511 |
+
break;
|
2512 |
+
|
2513 |
+
case 'FontBBox':
|
2514 |
+
$data[$key] = explode(' ', trim(substr($row, $pos)));
|
2515 |
+
break;
|
2516 |
+
|
2517 |
+
//C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
|
2518 |
+
case 'C': // Found in AFM files
|
2519 |
+
$bits = explode(';', trim($row));
|
2520 |
+
$dtmp = array();
|
2521 |
+
|
2522 |
+
foreach ($bits as $bit) {
|
2523 |
+
$bits2 = explode(' ', trim($bit));
|
2524 |
+
if (mb_strlen($bits2[0], '8bit') == 0) {
|
2525 |
+
continue;
|
2526 |
+
}
|
2527 |
+
|
2528 |
+
if (count($bits2) > 2) {
|
2529 |
+
$dtmp[$bits2[0]] = array();
|
2530 |
+
for ($i = 1; $i < count($bits2); $i++) {
|
2531 |
+
$dtmp[$bits2[0]][] = $bits2[$i];
|
2532 |
+
}
|
2533 |
+
} else {
|
2534 |
+
if (count($bits2) == 2) {
|
2535 |
+
$dtmp[$bits2[0]] = $bits2[1];
|
2536 |
+
}
|
2537 |
+
}
|
2538 |
+
}
|
2539 |
+
|
2540 |
+
$c = (int)$dtmp['C'];
|
2541 |
+
$n = $dtmp['N'];
|
2542 |
+
$width = floatval($dtmp['WX']);
|
2543 |
+
|
2544 |
+
if ($c >= 0) {
|
2545 |
+
if ($c != hexdec($n)) {
|
2546 |
+
$data['codeToName'][$c] = $n;
|
2547 |
+
}
|
2548 |
+
$data['C'][$c] = $width;
|
2549 |
+
} else {
|
2550 |
+
$data['C'][$n] = $width;
|
2551 |
+
}
|
2552 |
+
|
2553 |
+
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2554 |
+
$data['MissingWidth'] = $width;
|
2555 |
+
}
|
2556 |
+
|
2557 |
+
break;
|
2558 |
+
|
2559 |
+
// U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
|
2560 |
+
case 'U': // Found in UFM files
|
2561 |
+
if (!$data['isUnicode']) {
|
2562 |
+
break;
|
2563 |
+
}
|
2564 |
+
|
2565 |
+
$bits = explode(';', trim($row));
|
2566 |
+
$dtmp = array();
|
2567 |
+
|
2568 |
+
foreach ($bits as $bit) {
|
2569 |
+
$bits2 = explode(' ', trim($bit));
|
2570 |
+
if (mb_strlen($bits2[0], '8bit') === 0) {
|
2571 |
+
continue;
|
2572 |
+
}
|
2573 |
+
|
2574 |
+
if (count($bits2) > 2) {
|
2575 |
+
$dtmp[$bits2[0]] = array();
|
2576 |
+
for ($i = 1; $i < count($bits2); $i++) {
|
2577 |
+
$dtmp[$bits2[0]][] = $bits2[$i];
|
2578 |
+
}
|
2579 |
+
} else {
|
2580 |
+
if (count($bits2) == 2) {
|
2581 |
+
$dtmp[$bits2[0]] = $bits2[1];
|
2582 |
+
}
|
2583 |
+
}
|
2584 |
+
}
|
2585 |
+
|
2586 |
+
$c = (int)$dtmp['U'];
|
2587 |
+
$n = $dtmp['N'];
|
2588 |
+
$glyph = $dtmp['G'];
|
2589 |
+
$width = floatval($dtmp['WX']);
|
2590 |
+
|
2591 |
+
if ($c >= 0) {
|
2592 |
+
// Set values in CID to GID map
|
2593 |
+
if ($c >= 0 && $c < 0xFFFF && $glyph) {
|
2594 |
+
$cidtogid[$c * 2] = chr($glyph >> 8);
|
2595 |
+
$cidtogid[$c * 2 + 1] = chr($glyph & 0xFF);
|
2596 |
+
}
|
2597 |
+
|
2598 |
+
if ($c != hexdec($n)) {
|
2599 |
+
$data['codeToName'][$c] = $n;
|
2600 |
+
}
|
2601 |
+
$data['C'][$c] = $width;
|
2602 |
+
} else {
|
2603 |
+
$data['C'][$n] = $width;
|
2604 |
+
}
|
2605 |
+
|
2606 |
+
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2607 |
+
$data['MissingWidth'] = $width;
|
2608 |
+
}
|
2609 |
+
|
2610 |
+
break;
|
2611 |
+
|
2612 |
+
case 'KPX':
|
2613 |
+
break; // don't include them as they are not used yet
|
2614 |
+
//KPX Adieresis yacute -40
|
2615 |
+
/*$bits = explode(' ', trim($row));
|
2616 |
+
$data['KPX'][$bits[1]][$bits[2]] = $bits[3];
|
2617 |
+
break;*/
|
2618 |
+
}
|
2619 |
+
}
|
2620 |
+
}
|
2621 |
+
|
2622 |
+
if ($this->compressionReady && $this->options['compression']) {
|
2623 |
+
// then implement ZLIB based compression on CIDtoGID string
|
2624 |
+
$data['CIDtoGID_Compressed'] = true;
|
2625 |
+
$cidtogid = gzcompress($cidtogid, 6);
|
2626 |
+
}
|
2627 |
+
$data['CIDtoGID'] = base64_encode($cidtogid);
|
2628 |
+
$data['_version_'] = $this->fontcacheVersion;
|
2629 |
+
$this->fonts[$font] = $data;
|
2630 |
+
|
2631 |
+
//Because of potential trouble with php safe mode, expect that the folder already exists.
|
2632 |
+
//If not existing, this will hit performance because of missing cached results.
|
2633 |
+
if (is_dir($fontcache) && is_writable($fontcache)) {
|
2634 |
+
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . var_export($data, true) . ';');
|
2635 |
+
}
|
2636 |
+
$data = null;
|
2637 |
+
}
|
2638 |
+
|
2639 |
+
if (!isset($this->fonts[$font])) {
|
2640 |
+
$this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
|
2641 |
+
}
|
2642 |
+
|
2643 |
+
//pre_r($this->messages);
|
2644 |
+
}
|
2645 |
+
|
2646 |
+
/**
|
2647 |
+
* if the font is not loaded then load it and make the required object
|
2648 |
+
* else just make it the current font
|
2649 |
+
* the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding'
|
2650 |
+
* note that encoding='none' will need to be used for symbolic fonts
|
2651 |
+
* and 'differences' => an array of mappings between numbers 0->255 and character names.
|
2652 |
+
*
|
2653 |
+
* @param $fontName
|
2654 |
+
* @param string $encoding
|
2655 |
+
* @param bool $set
|
2656 |
+
* @return int
|
2657 |
+
*/
|
2658 |
+
function selectFont($fontName, $encoding = '', $set = true)
|
2659 |
+
{
|
2660 |
+
$ext = substr($fontName, -4);
|
2661 |
+
if ($ext === '.afm' || $ext === '.ufm') {
|
2662 |
+
$fontName = substr($fontName, 0, mb_strlen($fontName) - 4);
|
2663 |
+
}
|
2664 |
+
|
2665 |
+
if (!isset($this->fonts[$fontName])) {
|
2666 |
+
$this->addMessage("selectFont: selecting - $fontName - $encoding, $set");
|
2667 |
+
|
2668 |
+
// load the file
|
2669 |
+
$this->openFont($fontName);
|
2670 |
+
|
2671 |
+
if (isset($this->fonts[$fontName])) {
|
2672 |
+
$this->numObj++;
|
2673 |
+
$this->numFonts++;
|
2674 |
+
|
2675 |
+
$font = &$this->fonts[$fontName];
|
2676 |
+
|
2677 |
+
$name = basename($fontName);
|
2678 |
+
$dir = dirname($fontName) . '/';
|
2679 |
+
$options = array('name' => $name, 'fontFileName' => $fontName);
|
2680 |
+
|
2681 |
+
if (is_array($encoding)) {
|
2682 |
+
// then encoding and differences might be set
|
2683 |
+
if (isset($encoding['encoding'])) {
|
2684 |
+
$options['encoding'] = $encoding['encoding'];
|
2685 |
+
}
|
2686 |
+
|
2687 |
+
if (isset($encoding['differences'])) {
|
2688 |
+
$options['differences'] = $encoding['differences'];
|
2689 |
+
}
|
2690 |
+
} else {
|
2691 |
+
if (mb_strlen($encoding, '8bit')) {
|
2692 |
+
// then perhaps only the encoding has been set
|
2693 |
+
$options['encoding'] = $encoding;
|
2694 |
+
}
|
2695 |
+
}
|
2696 |
+
|
2697 |
+
$fontObj = $this->numObj;
|
2698 |
+
$this->o_font($this->numObj, 'new', $options);
|
2699 |
+
$font['fontNum'] = $this->numFonts;
|
2700 |
+
|
2701 |
+
// if this is a '.afm' font, and there is a '.pfa' file to go with it (as there
|
2702 |
+
// should be for all non-basic fonts), then load it into an object and put the
|
2703 |
+
// references into the font object
|
2704 |
+
$basefile = $fontName;
|
2705 |
+
|
2706 |
+
$fbtype = '';
|
2707 |
+
if (file_exists("$basefile.ttf")) {
|
2708 |
+
$fbtype = 'ttf';
|
2709 |
+
} elseif (file_exists("$basefile.TTF")) {
|
2710 |
+
$fbtype = 'TTF';
|
2711 |
+
} elseif (file_exists("$basefile.pfb")) {
|
2712 |
+
$fbtype = 'pfb';
|
2713 |
+
} elseif (file_exists("$basefile.PFB")) {
|
2714 |
+
$fbtype = 'PFB';
|
2715 |
+
}
|
2716 |
+
|
2717 |
+
$fbfile = "$basefile.$fbtype";
|
2718 |
+
|
2719 |
+
// $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
|
2720 |
+
// $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
|
2721 |
+
$this->addMessage('selectFont: checking for - ' . $fbfile);
|
2722 |
+
|
2723 |
+
// OAR - I don't understand this old check
|
2724 |
+
// if (substr($fontName, -4) === '.afm' && strlen($fbtype)) {
|
2725 |
+
if ($fbtype) {
|
2726 |
+
$adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
|
2727 |
+
// $fontObj = $this->numObj;
|
2728 |
+
$this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
|
2729 |
+
|
2730 |
+
// find the array of font widths, and put that into an object.
|
2731 |
+
$firstChar = -1;
|
2732 |
+
$lastChar = 0;
|
2733 |
+
$widths = array();
|
2734 |
+
$cid_widths = array();
|
2735 |
+
|
2736 |
+
foreach ($font['C'] as $num => $d) {
|
2737 |
+
if (intval($num) > 0 || $num == '0') {
|
2738 |
+
if (!$font['isUnicode']) {
|
2739 |
+
// With Unicode, widths array isn't used
|
2740 |
+
if ($lastChar > 0 && $num > $lastChar + 1) {
|
2741 |
+
for ($i = $lastChar + 1; $i < $num; $i++) {
|
2742 |
+
$widths[] = 0;
|
2743 |
+
}
|
2744 |
+
}
|
2745 |
+
}
|
2746 |
+
|
2747 |
+
$widths[] = $d;
|
2748 |
+
|
2749 |
+
if ($font['isUnicode']) {
|
2750 |
+
$cid_widths[$num] = $d;
|
2751 |
+
}
|
2752 |
+
|
2753 |
+
if ($firstChar == -1) {
|
2754 |
+
$firstChar = $num;
|
2755 |
+
}
|
2756 |
+
|
2757 |
+
$lastChar = $num;
|
2758 |
+
}
|
2759 |
+
}
|
2760 |
+
|
2761 |
+
// also need to adjust the widths for the differences array
|
2762 |
+
if (isset($options['differences'])) {
|
2763 |
+
foreach ($options['differences'] as $charNum => $charName) {
|
2764 |
+
if ($charNum > $lastChar) {
|
2765 |
+
if (!$font['isUnicode']) {
|
2766 |
+
// With Unicode, widths array isn't used
|
2767 |
+
for ($i = $lastChar + 1; $i <= $charNum; $i++) {
|
2768 |
+
$widths[] = 0;
|
2769 |
+
}
|
2770 |
+
}
|
2771 |
+
|
2772 |
+
$lastChar = $charNum;
|
2773 |
+
}
|
2774 |
+
|
2775 |
+
if (isset($font['C'][$charName])) {
|
2776 |
+
$widths[$charNum - $firstChar] = $font['C'][$charName];
|
2777 |
+
if ($font['isUnicode']) {
|
2778 |
+
$cid_widths[$charName] = $font['C'][$charName];
|
2779 |
+
}
|
2780 |
+
}
|
2781 |
+
}
|
2782 |
+
}
|
2783 |
+
|
2784 |
+
if ($font['isUnicode']) {
|
2785 |
+
$font['CIDWidths'] = $cid_widths;
|
2786 |
+
}
|
2787 |
+
|
2788 |
+
$this->addMessage('selectFont: FirstChar = ' . $firstChar);
|
2789 |
+
$this->addMessage('selectFont: LastChar = ' . $lastChar);
|
2790 |
+
|
2791 |
+
$widthid = -1;
|
2792 |
+
|
2793 |
+
if (!$font['isUnicode']) {
|
2794 |
+
// With Unicode, widths array isn't used
|
2795 |
+
|
2796 |
+
$this->numObj++;
|
2797 |
+
$this->o_contents($this->numObj, 'new', 'raw');
|
2798 |
+
$this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
|
2799 |
+
$widthid = $this->numObj;
|
2800 |
+
}
|
2801 |
+
|
2802 |
+
$missing_width = 500;
|
2803 |
+
$stemV = 70;
|
2804 |
+
|
2805 |
+
if (isset($font['MissingWidth'])) {
|
2806 |
+
$missing_width = $font['MissingWidth'];
|
2807 |
+
}
|
2808 |
+
if (isset($font['StdVW'])) {
|
2809 |
+
$stemV = $font['StdVW'];
|
2810 |
+
} else {
|
2811 |
+
if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
|
2812 |
+
$stemV = 120;
|
2813 |
+
}
|
2814 |
+
}
|
2815 |
+
|
2816 |
+
// load the pfb file, and put that into an object too.
|
2817 |
+
// note that pdf supports only binary format type 1 font files, though there is a
|
2818 |
+
// simple utility to convert them from pfa to pfb.
|
2819 |
+
// FIXME: should we move font subset creation to CPDF::output? See notes in issue #750.
|
2820 |
+
if (!$this->isUnicode || strtolower($fbtype) !== 'ttf' || empty($this->stringSubsets)) {
|
2821 |
+
$data = file_get_contents($fbfile);
|
2822 |
+
} else {
|
2823 |
+
$this->stringSubsets[$fontName][] = 32; // Force space if not in yet
|
2824 |
+
|
2825 |
+
$subset = $this->stringSubsets[$fontName];
|
2826 |
+
sort($subset);
|
2827 |
+
|
2828 |
+
// Load font
|
2829 |
+
$font_obj = Font::load($fbfile);
|
2830 |
+
$font_obj->parse();
|
2831 |
+
|
2832 |
+
// Define subset
|
2833 |
+
$font_obj->setSubset($subset);
|
2834 |
+
$font_obj->reduce();
|
2835 |
+
|
2836 |
+
// Write new font
|
2837 |
+
$tmp_name = $this->tmp . "/" . basename($fbfile) . ".tmp." . uniqid();
|
2838 |
+
$font_obj->open($tmp_name, BinaryStream::modeWrite);
|
2839 |
+
$font_obj->encode(array("OS/2"));
|
2840 |
+
$font_obj->close();
|
2841 |
+
|
2842 |
+
// Parse the new font to get cid2gid and widths
|
2843 |
+
$font_obj = Font::load($tmp_name);
|
2844 |
+
|
2845 |
+
// Find Unicode char map table
|
2846 |
+
$subtable = null;
|
2847 |
+
foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
|
2848 |
+
if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
|
2849 |
+
$subtable = $_subtable;
|
2850 |
+
break;
|
2851 |
+
}
|
2852 |
+
}
|
2853 |
+
|
2854 |
+
if ($subtable) {
|
2855 |
+
$glyphIndexArray = $subtable["glyphIndexArray"];
|
2856 |
+
$hmtx = $font_obj->getData("hmtx");
|
2857 |
+
|
2858 |
+
unset($glyphIndexArray[0xFFFF]);
|
2859 |
+
|
2860 |
+
$cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00");
|
2861 |
+
$font['CIDWidths'] = array();
|
2862 |
+
foreach ($glyphIndexArray as $cid => $gid) {
|
2863 |
+
if ($cid >= 0 && $cid < 0xFFFF && $gid) {
|
2864 |
+
$cidtogid[$cid * 2] = chr($gid >> 8);
|
2865 |
+
$cidtogid[$cid * 2 + 1] = chr($gid & 0xFF);
|
2866 |
+
}
|
2867 |
+
|
2868 |
+
$width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
|
2869 |
+
$font['CIDWidths'][$cid] = $width;
|
2870 |
+
}
|
2871 |
+
|
2872 |
+
$font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
|
2873 |
+
$font['CIDtoGID_Compressed'] = true;
|
2874 |
+
|
2875 |
+
$data = file_get_contents($tmp_name);
|
2876 |
+
} else {
|
2877 |
+
$data = file_get_contents($fbfile);
|
2878 |
+
}
|
2879 |
+
|
2880 |
+
$font_obj->close();
|
2881 |
+
unlink($tmp_name);
|
2882 |
+
}
|
2883 |
+
|
2884 |
+
// create the font descriptor
|
2885 |
+
$this->numObj++;
|
2886 |
+
$fontDescriptorId = $this->numObj;
|
2887 |
+
|
2888 |
+
$this->numObj++;
|
2889 |
+
$pfbid = $this->numObj;
|
2890 |
+
|
2891 |
+
// determine flags (more than a little flakey, hopefully will not matter much)
|
2892 |
+
$flags = 0;
|
2893 |
+
|
2894 |
+
if ($font['ItalicAngle'] != 0) {
|
2895 |
+
$flags += pow(2, 6);
|
2896 |
+
}
|
2897 |
+
|
2898 |
+
if ($font['IsFixedPitch'] === 'true') {
|
2899 |
+
$flags += 1;
|
2900 |
+
}
|
2901 |
+
|
2902 |
+
$flags += pow(2, 5); // assume non-sybolic
|
2903 |
+
$list = array(
|
2904 |
+
'Ascent' => 'Ascender',
|
2905 |
+
'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight'
|
2906 |
+
'MissingWidth' => 'MissingWidth',
|
2907 |
+
'Descent' => 'Descender',
|
2908 |
+
'FontBBox' => 'FontBBox',
|
2909 |
+
'ItalicAngle' => 'ItalicAngle'
|
2910 |
+
);
|
2911 |
+
$fdopt = array(
|
2912 |
+
'Flags' => $flags,
|
2913 |
+
'FontName' => $adobeFontName,
|
2914 |
+
'StemV' => $stemV
|
2915 |
+
);
|
2916 |
+
|
2917 |
+
foreach ($list as $k => $v) {
|
2918 |
+
if (isset($font[$v])) {
|
2919 |
+
$fdopt[$k] = $font[$v];
|
2920 |
+
}
|
2921 |
+
}
|
2922 |
+
|
2923 |
+
if (strtolower($fbtype) === 'pfb') {
|
2924 |
+
$fdopt['FontFile'] = $pfbid;
|
2925 |
+
} elseif (strtolower($fbtype) === 'ttf') {
|
2926 |
+
$fdopt['FontFile2'] = $pfbid;
|
2927 |
+
}
|
2928 |
+
|
2929 |
+
$this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
|
2930 |
+
|
2931 |
+
// embed the font program
|
2932 |
+
$this->o_contents($this->numObj, 'new');
|
2933 |
+
$this->objects[$pfbid]['c'] .= $data;
|
2934 |
+
|
2935 |
+
// determine the cruicial lengths within this file
|
2936 |
+
if (strtolower($fbtype) === 'pfb') {
|
2937 |
+
$l1 = strpos($data, 'eexec') + 6;
|
2938 |
+
$l2 = strpos($data, '00000000') - $l1;
|
2939 |
+
$l3 = mb_strlen($data, '8bit') - $l2 - $l1;
|
2940 |
+
$this->o_contents(
|
2941 |
+
$this->numObj,
|
2942 |
+
'add',
|
2943 |
+
array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)
|
2944 |
+
);
|
2945 |
+
} elseif (strtolower($fbtype) == 'ttf') {
|
2946 |
+
$l1 = mb_strlen($data, '8bit');
|
2947 |
+
$this->o_contents($this->numObj, 'add', array('Length1' => $l1));
|
2948 |
+
}
|
2949 |
+
|
2950 |
+
// tell the font object about all this new stuff
|
2951 |
+
$tmp = array(
|
2952 |
+
'BaseFont' => $adobeFontName,
|
2953 |
+
'MissingWidth' => $missing_width,
|
2954 |
+
'Widths' => $widthid,
|
2955 |
+
'FirstChar' => $firstChar,
|
2956 |
+
'LastChar' => $lastChar,
|
2957 |
+
'FontDescriptor' => $fontDescriptorId
|
2958 |
+
);
|
2959 |
+
|
2960 |
+
if (strtolower($fbtype) === 'ttf') {
|
2961 |
+
$tmp['SubType'] = 'TrueType';
|
2962 |
+
}
|
2963 |
+
|
2964 |
+
$this->addMessage("adding extra info to font.($fontObj)");
|
2965 |
+
|
2966 |
+
foreach ($tmp as $fk => $fv) {
|
2967 |
+
$this->addMessage("$fk : $fv");
|
2968 |
+
}
|
2969 |
+
|
2970 |
+
$this->o_font($fontObj, 'add', $tmp);
|
2971 |
+
} else {
|
2972 |
+
$this->addMessage(
|
2973 |
+
'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
|
2974 |
+
);
|
2975 |
+
}
|
2976 |
+
|
2977 |
+
// also set the differences here, note that this means that these will take effect only the
|
2978 |
+
//first time that a font is selected, else they are ignored
|
2979 |
+
if (isset($options['differences'])) {
|
2980 |
+
$font['differences'] = $options['differences'];
|
2981 |
+
}
|
2982 |
+
}
|
2983 |
+
}
|
2984 |
+
|
2985 |
+
if ($set && isset($this->fonts[$fontName])) {
|
2986 |
+
// so if for some reason the font was not set in the last one then it will not be selected
|
2987 |
+
$this->currentBaseFont = $fontName;
|
2988 |
+
|
2989 |
+
// the next lines mean that if a new font is selected, then the current text state will be
|
2990 |
+
// applied to it as well.
|
2991 |
+
$this->currentFont = $this->currentBaseFont;
|
2992 |
+
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
2993 |
+
|
2994 |
+
//$this->setCurrentFont();
|
2995 |
+
}
|
2996 |
+
|
2997 |
+
return $this->currentFontNum;
|
2998 |
+
//return $this->numObj;
|
2999 |
+
}
|
3000 |
+
|
3001 |
+
/**
|
3002 |
+
* sets up the current font, based on the font families, and the current text state
|
3003 |
+
* note that this system is quite flexible, a bold-italic font can be completely different to a
|
3004 |
+
* italic-bold font, and even bold-bold will have to be defined within the family to have meaning
|
3005 |
+
* This function is to be called whenever the currentTextState is changed, it will update
|
3006 |
+
* the currentFont setting to whatever the appropriate family one is.
|
3007 |
+
* If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
|
3008 |
+
* This function will change the currentFont to whatever it should be, but will not change the
|
3009 |
+
* currentBaseFont.
|
3010 |
+
*/
|
3011 |
+
private function setCurrentFont()
|
3012 |
+
{
|
3013 |
+
// if (strlen($this->currentBaseFont) == 0){
|
3014 |
+
// // then assume an initial font
|
3015 |
+
// $this->selectFont($this->defaultFont);
|
3016 |
+
// }
|
3017 |
+
// $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1);
|
3018 |
+
// if (strlen($this->currentTextState)
|
3019 |
+
// && isset($this->fontFamilies[$cf])
|
3020 |
+
// && isset($this->fontFamilies[$cf][$this->currentTextState])){
|
3021 |
+
// // then we are in some state or another
|
3022 |
+
// // and this font has a family, and the current setting exists within it
|
3023 |
+
// // select the font, then return it
|
3024 |
+
// $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState];
|
3025 |
+
// $this->selectFont($nf,'',0);
|
3026 |
+
// $this->currentFont = $nf;
|
3027 |
+
// $this->currentFontNum = $this->fonts[$nf]['fontNum'];
|
3028 |
+
// } else {
|
3029 |
+
// // the this font must not have the right family member for the current state
|
3030 |
+
// // simply assume the base font
|
3031 |
+
$this->currentFont = $this->currentBaseFont;
|
3032 |
+
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
3033 |
+
// }
|
3034 |
+
}
|
3035 |
+
|
3036 |
+
/**
|
3037 |
+
* function for the user to find out what the ID is of the first page that was created during
|
3038 |
+
* startup - useful if they wish to add something to it later.
|
3039 |
+
*
|
3040 |
+
* @return int
|
3041 |
+
*/
|
3042 |
+
function getFirstPageId()
|
3043 |
+
{
|
3044 |
+
return $this->firstPageId;
|
3045 |
+
}
|
3046 |
+
|
3047 |
+
/**
|
3048 |
+
* add content to the currently active object
|
3049 |
+
*
|
3050 |
+
* @param $content
|
3051 |
+
*/
|
3052 |
+
private function addContent($content)
|
3053 |
+
{
|
3054 |
+
$this->objects[$this->currentContents]['c'] .= $content;
|
3055 |
+
}
|
3056 |
+
|
3057 |
+
/**
|
3058 |
+
* sets the color for fill operations
|
3059 |
+
*
|
3060 |
+
* @param $color
|
3061 |
+
* @param bool $force
|
3062 |
+
*/
|
3063 |
+
function setColor($color, $force = false)
|
3064 |
+
{
|
3065 |
+
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3066 |
+
|
3067 |
+
if (!$force && $this->currentColor == $new_color) {
|
3068 |
+
return;
|
3069 |
+
}
|
3070 |
+
|
3071 |
+
if (isset($new_color[3])) {
|
3072 |
+
$this->currentColor = $new_color;
|
3073 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColor));
|
3074 |
+
} else {
|
3075 |
+
if (isset($new_color[2])) {
|
3076 |
+
$this->currentColor = $new_color;
|
3077 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColor));
|
3078 |
+
}
|
3079 |
+
}
|
3080 |
+
}
|
3081 |
+
|
3082 |
+
/**
|
3083 |
+
* sets the color for fill operations
|
3084 |
+
*
|
3085 |
+
* @param $fillRule
|
3086 |
+
*/
|
3087 |
+
function setFillRule($fillRule)
|
3088 |
+
{
|
3089 |
+
if (!in_array($fillRule, array("nonzero", "evenodd"))) {
|
3090 |
+
return;
|
3091 |
+
}
|
3092 |
+
|
3093 |
+
$this->fillRule = $fillRule;
|
3094 |
+
}
|
3095 |
+
|
3096 |
+
/**
|
3097 |
+
* sets the color for stroke operations
|
3098 |
+
*
|
3099 |
+
* @param $color
|
3100 |
+
* @param bool $force
|
3101 |
+
*/
|
3102 |
+
function setStrokeColor($color, $force = false)
|
3103 |
+
{
|
3104 |
+
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3105 |
+
|
3106 |
+
if (!$force && $this->currentStrokeColor == $new_color) {
|
3107 |
+
return;
|
3108 |
+
}
|
3109 |
+
|
3110 |
+
if (isset($new_color[3])) {
|
3111 |
+
$this->currentStrokeColor = $new_color;
|
3112 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColor));
|
3113 |
+
} else {
|
3114 |
+
if (isset($new_color[2])) {
|
3115 |
+
$this->currentStrokeColor = $new_color;
|
3116 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColor));
|
3117 |
+
}
|
3118 |
+
}
|
3119 |
+
}
|
3120 |
+
|
3121 |
+
/**
|
3122 |
+
* Set the graphics state for compositions
|
3123 |
+
*
|
3124 |
+
* @param $parameters
|
3125 |
+
*/
|
3126 |
+
function setGraphicsState($parameters)
|
3127 |
+
{
|
3128 |
+
// Create a new graphics state object if necessary
|
3129 |
+
if (($gstate = array_search($parameters, $this->gstates)) === false) {
|
3130 |
+
$this->numObj++;
|
3131 |
+
$this->o_extGState($this->numObj, 'new', $parameters);
|
3132 |
+
$gstate = $this->numStates;
|
3133 |
+
$this->gstates[$gstate] = $parameters;
|
3134 |
+
}
|
3135 |
+
$this->addContent("\n/GS$gstate gs");
|
3136 |
+
}
|
3137 |
+
|
3138 |
+
/**
|
3139 |
+
* Set current blend mode & opacity for lines.
|
3140 |
+
*
|
3141 |
+
* Valid blend modes are:
|
3142 |
+
*
|
3143 |
+
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3144 |
+
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3145 |
+
* Exclusion
|
3146 |
+
*
|
3147 |
+
* @param string $mode the blend mode to use
|
3148 |
+
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3149 |
+
*/
|
3150 |
+
function setLineTransparency($mode, $opacity)
|
3151 |
+
{
|
3152 |
+
static $blend_modes = array(
|
3153 |
+
"Normal",
|
3154 |
+
"Multiply",
|
3155 |
+
"Screen",
|
3156 |
+
"Overlay",
|
3157 |
+
"Darken",
|
3158 |
+
"Lighten",
|
3159 |
+
"ColorDogde",
|
3160 |
+
"ColorBurn",
|
3161 |
+
"HardLight",
|
3162 |
+
"SoftLight",
|
3163 |
+
"Difference",
|
3164 |
+
"Exclusion"
|
3165 |
+
);
|
3166 |
+
|
3167 |
+
if (!in_array($mode, $blend_modes)) {
|
3168 |
+
$mode = "Normal";
|
3169 |
+
}
|
3170 |
+
|
3171 |
+
// Only create a new graphics state if required
|
3172 |
+
if ($mode === $this->currentLineTransparency["mode"] &&
|
3173 |
+
$opacity == $this->currentLineTransparency["opacity"]
|
3174 |
+
) {
|
3175 |
+
return;
|
3176 |
+
}
|
3177 |
+
|
3178 |
+
$this->currentLineTransparency["mode"] = $mode;
|
3179 |
+
$this->currentLineTransparency["opacity"] = $opacity;
|
3180 |
+
|
3181 |
+
$options = array(
|
3182 |
+
"BM" => "/$mode",
|
3183 |
+
"CA" => (float)$opacity
|
3184 |
+
);
|
3185 |
+
|
3186 |
+
$this->setGraphicsState($options);
|
3187 |
+
}
|
3188 |
+
|
3189 |
+
/**
|
3190 |
+
* Set current blend mode & opacity for filled objects.
|
3191 |
+
*
|
3192 |
+
* Valid blend modes are:
|
3193 |
+
*
|
3194 |
+
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3195 |
+
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3196 |
+
* Exclusion
|
3197 |
+
*
|
3198 |
+
* @param string $mode the blend mode to use
|
3199 |
+
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3200 |
+
*/
|
3201 |
+
function setFillTransparency($mode, $opacity)
|
3202 |
+
{
|
3203 |
+
static $blend_modes = array(
|
3204 |
+
"Normal",
|
3205 |
+
"Multiply",
|
3206 |
+
"Screen",
|
3207 |
+
"Overlay",
|
3208 |
+
"Darken",
|
3209 |
+
"Lighten",
|
3210 |
+
"ColorDogde",
|
3211 |
+
"ColorBurn",
|
3212 |
+
"HardLight",
|
3213 |
+
"SoftLight",
|
3214 |
+
"Difference",
|
3215 |
+
"Exclusion"
|
3216 |
+
);
|
3217 |
+
|
3218 |
+
if (!in_array($mode, $blend_modes)) {
|
3219 |
+
$mode = "Normal";
|
3220 |
+
}
|
3221 |
+
|
3222 |
+
if ($mode === $this->currentFillTransparency["mode"] &&
|
3223 |
+
$opacity == $this->currentFillTransparency["opacity"]
|
3224 |
+
) {
|
3225 |
+
return;
|
3226 |
+
}
|
3227 |
+
|
3228 |
+
$this->currentFillTransparency["mode"] = $mode;
|
3229 |
+
$this->currentFillTransparency["opacity"] = $opacity;
|
3230 |
+
|
3231 |
+
$options = array(
|
3232 |
+
"BM" => "/$mode",
|
3233 |
+
"ca" => (float)$opacity,
|
3234 |
+
);
|
3235 |
+
|
3236 |
+
$this->setGraphicsState($options);
|
3237 |
+
}
|
3238 |
+
|
3239 |
+
/**
|
3240 |
+
* draw a line from one set of coordinates to another
|
3241 |
+
*
|
3242 |
+
* @param $x1
|
3243 |
+
* @param $y1
|
3244 |
+
* @param $x2
|
3245 |
+
* @param $y2
|
3246 |
+
* @param bool $stroke
|
3247 |
+
*/
|
3248 |
+
function line($x1, $y1, $x2, $y2, $stroke = true)
|
3249 |
+
{
|
3250 |
+
$this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2));
|
3251 |
+
|
3252 |
+
if ($stroke) {
|
3253 |
+
$this->addContent(' S');
|
3254 |
+
}
|
3255 |
+
}
|
3256 |
+
|
3257 |
+
/**
|
3258 |
+
* draw a bezier curve based on 4 control points
|
3259 |
+
*
|
3260 |
+
* @param $x0
|
3261 |
+
* @param $y0
|
3262 |
+
* @param $x1
|
3263 |
+
* @param $y1
|
3264 |
+
* @param $x2
|
3265 |
+
* @param $y2
|
3266 |
+
* @param $x3
|
3267 |
+
* @param $y3
|
3268 |
+
*/
|
3269 |
+
function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3270 |
+
{
|
3271 |
+
// in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
|
3272 |
+
// as the control points for the curve.
|
3273 |
+
$this->addContent(
|
3274 |
+
sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3275 |
+
);
|
3276 |
+
}
|
3277 |
+
|
3278 |
+
/**
|
3279 |
+
* draw a part of an ellipse
|
3280 |
+
*
|
3281 |
+
* @param $x0
|
3282 |
+
* @param $y0
|
3283 |
+
* @param $astart
|
3284 |
+
* @param $afinish
|
3285 |
+
* @param $r1
|
3286 |
+
* @param int $r2
|
3287 |
+
* @param int $angle
|
3288 |
+
* @param int $nSeg
|
3289 |
+
*/
|
3290 |
+
function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
|
3291 |
+
{
|
3292 |
+
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false);
|
3293 |
+
}
|
3294 |
+
|
3295 |
+
/**
|
3296 |
+
* draw a filled ellipse
|
3297 |
+
*
|
3298 |
+
* @param $x0
|
3299 |
+
* @param $y0
|
3300 |
+
* @param $r1
|
3301 |
+
* @param int $r2
|
3302 |
+
* @param int $angle
|
3303 |
+
* @param int $nSeg
|
3304 |
+
* @param int $astart
|
3305 |
+
* @param int $afinish
|
3306 |
+
*/
|
3307 |
+
function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
|
3308 |
+
{
|
3309 |
+
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true);
|
3310 |
+
}
|
3311 |
+
|
3312 |
+
/**
|
3313 |
+
* @param $x
|
3314 |
+
* @param $y
|
3315 |
+
*/
|
3316 |
+
function lineTo($x, $y)
|
3317 |
+
{
|
3318 |
+
$this->addContent(sprintf("\n%.3F %.3F l", $x, $y));
|
3319 |
+
}
|
3320 |
+
|
3321 |
+
/**
|
3322 |
+
* @param $x
|
3323 |
+
* @param $y
|
3324 |
+
*/
|
3325 |
+
function moveTo($x, $y)
|
3326 |
+
{
|
3327 |
+
$this->addContent(sprintf("\n%.3F %.3F m", $x, $y));
|
3328 |
+
}
|
3329 |
+
|
3330 |
+
/**
|
3331 |
+
* draw a bezier curve based on 4 control points
|
3332 |
+
*
|
3333 |
+
* @param $x1
|
3334 |
+
* @param $y1
|
3335 |
+
* @param $x2
|
3336 |
+
* @param $y2
|
3337 |
+
* @param $x3
|
3338 |
+
* @param $y3
|
3339 |
+
*/
|
3340 |
+
function curveTo($x1, $y1, $x2, $y2, $x3, $y3)
|
3341 |
+
{
|
3342 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F %.3F %.3F c", $x1, $y1, $x2, $y2, $x3, $y3));
|
3343 |
+
}
|
3344 |
+
|
3345 |
+
/**
|
3346 |
+
* draw a bezier curve based on 4 control points
|
3347 |
+
*/ function quadTo($cpx, $cpy, $x, $y)
|
3348 |
+
{
|
3349 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F v", $cpx, $cpy, $x, $y));
|
3350 |
+
}
|
3351 |
+
|
3352 |
+
function closePath()
|
3353 |
+
{
|
3354 |
+
$this->addContent(' h');
|
3355 |
+
}
|
3356 |
+
|
3357 |
+
function endPath()
|
3358 |
+
{
|
3359 |
+
$this->addContent(' n');
|
3360 |
+
}
|
3361 |
+
|
3362 |
+
/**
|
3363 |
+
* draw an ellipse
|
3364 |
+
* note that the part and filled ellipse are just special cases of this function
|
3365 |
+
*
|
3366 |
+
* draws an ellipse in the current line style
|
3367 |
+
* centered at $x0,$y0, radii $r1,$r2
|
3368 |
+
* if $r2 is not set, then a circle is drawn
|
3369 |
+
* from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse.
|
3370 |
+
* nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
|
3371 |
+
* pretty crappy shape at 2, as we are approximating with bezier curves.
|
3372 |
+
*
|
3373 |
+
* @param $x0
|
3374 |
+
* @param $y0
|
3375 |
+
* @param $r1
|
3376 |
+
* @param int $r2
|
3377 |
+
* @param int $angle
|
3378 |
+
* @param int $nSeg
|
3379 |
+
* @param int $astart
|
3380 |
+
* @param int $afinish
|
3381 |
+
* @param bool $close
|
3382 |
+
* @param bool $fill
|
3383 |
+
* @param bool $stroke
|
3384 |
+
* @param bool $incomplete
|
3385 |
+
*/
|
3386 |
+
function ellipse(
|
3387 |
+
$x0,
|
3388 |
+
$y0,
|
3389 |
+
$r1,
|
3390 |
+
$r2 = 0,
|
3391 |
+
$angle = 0,
|
3392 |
+
$nSeg = 8,
|
3393 |
+
$astart = 0,
|
3394 |
+
$afinish = 360,
|
3395 |
+
$close = true,
|
3396 |
+
$fill = false,
|
3397 |
+
$stroke = true,
|
3398 |
+
$incomplete = false
|
3399 |
+
) {
|
3400 |
+
if ($r1 == 0) {
|
3401 |
+
return;
|
3402 |
+
}
|
3403 |
+
|
3404 |
+
if ($r2 == 0) {
|
3405 |
+
$r2 = $r1;
|
3406 |
+
}
|
3407 |
+
|
3408 |
+
if ($nSeg < 2) {
|
3409 |
+
$nSeg = 2;
|
3410 |
+
}
|
3411 |
+
|
3412 |
+
$astart = deg2rad((float)$astart);
|
3413 |
+
$afinish = deg2rad((float)$afinish);
|
3414 |
+
$totalAngle = $afinish - $astart;
|
3415 |
+
|
3416 |
+
$dt = $totalAngle / $nSeg;
|
3417 |
+
$dtm = $dt / 3;
|
3418 |
+
|
3419 |
+
if ($angle != 0) {
|
3420 |
+
$a = -1 * deg2rad((float)$angle);
|
3421 |
+
|
3422 |
+
$this->addContent(
|
3423 |
+
sprintf("\n q %.3F %.3F %.3F %.3F %.3F %.3F cm", cos($a), -sin($a), sin($a), cos($a), $x0, $y0)
|
3424 |
+
);
|
3425 |
+
|
3426 |
+
$x0 = 0;
|
3427 |
+
$y0 = 0;
|
3428 |
+
}
|
3429 |
+
|
3430 |
+
$t1 = $astart;
|
3431 |
+
$a0 = $x0 + $r1 * cos($t1);
|
3432 |
+
$b0 = $y0 + $r2 * sin($t1);
|
3433 |
+
$c0 = -$r1 * sin($t1);
|
3434 |
+
$d0 = $r2 * cos($t1);
|
3435 |
+
|
3436 |
+
if (!$incomplete) {
|
3437 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $a0, $b0));
|
3438 |
+
}
|
3439 |
+
|
3440 |
+
for ($i = 1; $i <= $nSeg; $i++) {
|
3441 |
+
// draw this bit of the total curve
|
3442 |
+
$t1 = $i * $dt + $astart;
|
3443 |
+
$a1 = $x0 + $r1 * cos($t1);
|
3444 |
+
$b1 = $y0 + $r2 * sin($t1);
|
3445 |
+
$c1 = -$r1 * sin($t1);
|
3446 |
+
$d1 = $r2 * cos($t1);
|
3447 |
+
|
3448 |
+
$this->addContent(
|
3449 |
+
sprintf(
|
3450 |
+
"\n%.3F %.3F %.3F %.3F %.3F %.3F c",
|
3451 |
+
($a0 + $c0 * $dtm),
|
3452 |
+
($b0 + $d0 * $dtm),
|
3453 |
+
($a1 - $c1 * $dtm),
|
3454 |
+
($b1 - $d1 * $dtm),
|
3455 |
+
$a1,
|
3456 |
+
$b1
|
3457 |
+
)
|
3458 |
+
);
|
3459 |
+
|
3460 |
+
$a0 = $a1;
|
3461 |
+
$b0 = $b1;
|
3462 |
+
$c0 = $c1;
|
3463 |
+
$d0 = $d1;
|
3464 |
+
}
|
3465 |
+
|
3466 |
+
if (!$incomplete) {
|
3467 |
+
if ($fill) {
|
3468 |
+
$this->addContent(' f');
|
3469 |
+
}
|
3470 |
+
|
3471 |
+
if ($stroke) {
|
3472 |
+
if ($close) {
|
3473 |
+
$this->addContent(' s'); // small 's' signifies closing the path as well
|
3474 |
+
} else {
|
3475 |
+
$this->addContent(' S');
|
3476 |
+
}
|
3477 |
+
}
|
3478 |
+
}
|
3479 |
+
|
3480 |
+
if ($angle != 0) {
|
3481 |
+
$this->addContent(' Q');
|
3482 |
+
}
|
3483 |
+
}
|
3484 |
+
|
3485 |
+
/**
|
3486 |
+
* this sets the line drawing style.
|
3487 |
+
* width, is the thickness of the line in user units
|
3488 |
+
* cap is the type of cap to put on the line, values can be 'butt','round','square'
|
3489 |
+
* where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the
|
3490 |
+
* end of the line.
|
3491 |
+
* join can be 'miter', 'round', 'bevel'
|
3492 |
+
* dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the
|
3493 |
+
* on and off dashes.
|
3494 |
+
* (2) represents 2 on, 2 off, 2 on , 2 off ...
|
3495 |
+
* (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
|
3496 |
+
* phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
|
3497 |
+
*
|
3498 |
+
* @param int $width
|
3499 |
+
* @param string $cap
|
3500 |
+
* @param string $join
|
3501 |
+
* @param string $dash
|
3502 |
+
* @param int $phase
|
3503 |
+
*/
|
3504 |
+
function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
|
3505 |
+
{
|
3506 |
+
// this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
|
3507 |
+
$string = '';
|
3508 |
+
|
3509 |
+
if ($width > 0) {
|
3510 |
+
$string .= "$width w";
|
3511 |
+
}
|
3512 |
+
|
3513 |
+
$ca = array('butt' => 0, 'round' => 1, 'square' => 2);
|
3514 |
+
|
3515 |
+
if (isset($ca[$cap])) {
|
3516 |
+
$string .= " $ca[$cap] J";
|
3517 |
+
}
|
3518 |
+
|
3519 |
+
$ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
|
3520 |
+
|
3521 |
+
if (isset($ja[$join])) {
|
3522 |
+
$string .= " $ja[$join] j";
|
3523 |
+
}
|
3524 |
+
|
3525 |
+
if (is_array($dash)) {
|
3526 |
+
$string .= ' [ ' . implode(' ', $dash) . " ] $phase d";
|
3527 |
+
}
|
3528 |
+
|
3529 |
+
$this->currentLineStyle = $string;
|
3530 |
+
$this->addContent("\n$string");
|
3531 |
+
}
|
3532 |
+
|
3533 |
+
/**
|
3534 |
+
* draw a polygon, the syntax for this is similar to the GD polygon command
|
3535 |
+
*
|
3536 |
+
* @param $p
|
3537 |
+
* @param $np
|
3538 |
+
* @param bool $f
|
3539 |
+
*/
|
3540 |
+
function polygon($p, $np, $f = false)
|
3541 |
+
{
|
3542 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
|
3543 |
+
|
3544 |
+
for ($i = 2; $i < $np * 2; $i = $i + 2) {
|
3545 |
+
$this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
|
3546 |
+
}
|
3547 |
+
|
3548 |
+
if ($f) {
|
3549 |
+
$this->addContent(' f');
|
3550 |
+
} else {
|
3551 |
+
$this->addContent(' S');
|
3552 |
+
}
|
3553 |
+
}
|
3554 |
+
|
3555 |
+
/**
|
3556 |
+
* a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3557 |
+
* the coordinates of the upper-right corner
|
3558 |
+
*
|
3559 |
+
* @param $x1
|
3560 |
+
* @param $y1
|
3561 |
+
* @param $width
|
3562 |
+
* @param $height
|
3563 |
+
*/
|
3564 |
+
function filledRectangle($x1, $y1, $width, $height)
|
3565 |
+
{
|
3566 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height));
|
3567 |
+
}
|
3568 |
+
|
3569 |
+
/**
|
3570 |
+
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3571 |
+
* the coordinates of the upper-right corner
|
3572 |
+
*
|
3573 |
+
* @param $x1
|
3574 |
+
* @param $y1
|
3575 |
+
* @param $width
|
3576 |
+
* @param $height
|
3577 |
+
*/
|
3578 |
+
function rectangle($x1, $y1, $width, $height)
|
3579 |
+
{
|
3580 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height));
|
3581 |
+
}
|
3582 |
+
|
3583 |
+
/**
|
3584 |
+
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3585 |
+
* the coordinates of the upper-right corner
|
3586 |
+
*
|
3587 |
+
* @param $x1
|
3588 |
+
* @param $y1
|
3589 |
+
* @param $width
|
3590 |
+
* @param $height
|
3591 |
+
*/
|
3592 |
+
function rect($x1, $y1, $width, $height)
|
3593 |
+
{
|
3594 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height));
|
3595 |
+
}
|
3596 |
+
|
3597 |
+
function stroke()
|
3598 |
+
{
|
3599 |
+
$this->addContent("\nS");
|
3600 |
+
}
|
3601 |
+
|
3602 |
+
function fill()
|
3603 |
+
{
|
3604 |
+
$this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3605 |
+
}
|
3606 |
+
|
3607 |
+
function fillStroke()
|
3608 |
+
{
|
3609 |
+
$this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3610 |
+
}
|
3611 |
+
|
3612 |
+
/**
|
3613 |
+
* save the current graphic state
|
3614 |
+
*/
|
3615 |
+
function save()
|
3616 |
+
{
|
3617 |
+
// we must reset the color cache or it will keep bad colors after clipping
|
3618 |
+
$this->currentColor = null;
|
3619 |
+
$this->currentStrokeColor = null;
|
3620 |
+
$this->addContent("\nq");
|
3621 |
+
}
|
3622 |
+
|
3623 |
+
/**
|
3624 |
+
* restore the last graphic state
|
3625 |
+
*/
|
3626 |
+
function restore()
|
3627 |
+
{
|
3628 |
+
// we must reset the color cache or it will keep bad colors after clipping
|
3629 |
+
$this->currentColor = null;
|
3630 |
+
$this->currentStrokeColor = null;
|
3631 |
+
$this->addContent("\nQ");
|
3632 |
+
}
|
3633 |
+
|
3634 |
+
/**
|
3635 |
+
* draw a clipping rectangle, all the elements added after this will be clipped
|
3636 |
+
*
|
3637 |
+
* @param $x1
|
3638 |
+
* @param $y1
|
3639 |
+
* @param $width
|
3640 |
+
* @param $height
|
3641 |
+
*/
|
3642 |
+
function clippingRectangle($x1, $y1, $width, $height)
|
3643 |
+
{
|
3644 |
+
$this->save();
|
3645 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height));
|
3646 |
+
}
|
3647 |
+
|
3648 |
+
/**
|
3649 |
+
* draw a clipping rounded rectangle, all the elements added after this will be clipped
|
3650 |
+
*
|
3651 |
+
* @param $x1
|
3652 |
+
* @param $y1
|
3653 |
+
* @param $w
|
3654 |
+
* @param $h
|
3655 |
+
* @param $rTL
|
3656 |
+
* @param $rTR
|
3657 |
+
* @param $rBR
|
3658 |
+
* @param $rBL
|
3659 |
+
*/
|
3660 |
+
function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
3661 |
+
{
|
3662 |
+
$this->save();
|
3663 |
+
|
3664 |
+
// start: top edge, left end
|
3665 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h));
|
3666 |
+
|
3667 |
+
// line: bottom edge, left end
|
3668 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1, $y1 + $rBL));
|
3669 |
+
|
3670 |
+
// curve: bottom-left corner
|
3671 |
+
$this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true);
|
3672 |
+
|
3673 |
+
// line: right edge, bottom end
|
3674 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1));
|
3675 |
+
|
3676 |
+
// curve: bottom-right corner
|
3677 |
+
$this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true);
|
3678 |
+
|
3679 |
+
// line: right edge, top end
|
3680 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR));
|
3681 |
+
|
3682 |
+
// curve: bottom-right corner
|
3683 |
+
$this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true);
|
3684 |
+
|
3685 |
+
// line: bottom edge, right end
|
3686 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h));
|
3687 |
+
|
3688 |
+
// curve: top-right corner
|
3689 |
+
$this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true);
|
3690 |
+
|
3691 |
+
// line: top edge, left end
|
3692 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1));
|
3693 |
+
|
3694 |
+
// Close & clip
|
3695 |
+
$this->addContent(" W n");
|
3696 |
+
}
|
3697 |
+
|
3698 |
+
/**
|
3699 |
+
* ends the last clipping shape
|
3700 |
+
*/
|
3701 |
+
function clippingEnd()
|
3702 |
+
{
|
3703 |
+
$this->restore();
|
3704 |
+
}
|
3705 |
+
|
3706 |
+
/**
|
3707 |
+
* scale
|
3708 |
+
*
|
3709 |
+
* @param float $s_x scaling factor for width as percent
|
3710 |
+
* @param float $s_y scaling factor for height as percent
|
3711 |
+
* @param float $x Origin abscissa
|
3712 |
+
* @param float $y Origin ordinate
|
3713 |
+
*/
|
3714 |
+
function scale($s_x, $s_y, $x, $y)
|
3715 |
+
{
|
3716 |
+
$y = $this->currentPageSize["height"] - $y;
|
3717 |
+
|
3718 |
+
$tm = array(
|
3719 |
+
$s_x,
|
3720 |
+
0,
|
3721 |
+
0,
|
3722 |
+
$s_y,
|
3723 |
+
$x * (1 - $s_x),
|
3724 |
+
$y * (1 - $s_y)
|
3725 |
+
);
|
3726 |
+
|
3727 |
+
$this->transform($tm);
|
3728 |
+
}
|
3729 |
+
|
3730 |
+
/**
|
3731 |
+
* translate
|
3732 |
+
*
|
3733 |
+
* @param float $t_x movement to the right
|
3734 |
+
* @param float $t_y movement to the bottom
|
3735 |
+
*/
|
3736 |
+
function translate($t_x, $t_y)
|
3737 |
+
{
|
3738 |
+
$tm = array(
|
3739 |
+
1,
|
3740 |
+
0,
|
3741 |
+
0,
|
3742 |
+
1,
|
3743 |
+
$t_x,
|
3744 |
+
-$t_y
|
3745 |
+
);
|
3746 |
+
|
3747 |
+
$this->transform($tm);
|
3748 |
+
}
|
3749 |
+
|
3750 |
+
/**
|
3751 |
+
* rotate
|
3752 |
+
*
|
3753 |
+
* @param float $angle angle in degrees for counter-clockwise rotation
|
3754 |
+
* @param float $x Origin abscissa
|
3755 |
+
* @param float $y Origin ordinate
|
3756 |
+
*/
|
3757 |
+
function rotate($angle, $x, $y)
|
3758 |
+
{
|
3759 |
+
$y = $this->currentPageSize["height"] - $y;
|
3760 |
+
|
3761 |
+
$a = deg2rad($angle);
|
3762 |
+
$cos_a = cos($a);
|
3763 |
+
$sin_a = sin($a);
|
3764 |
+
|
3765 |
+
$tm = array(
|
3766 |
+
$cos_a,
|
3767 |
+
-$sin_a,
|
3768 |
+
$sin_a,
|
3769 |
+
$cos_a,
|
3770 |
+
$x - $sin_a * $y - $cos_a * $x,
|
3771 |
+
$y - $cos_a * $y + $sin_a * $x,
|
3772 |
+
);
|
3773 |
+
|
3774 |
+
$this->transform($tm);
|
3775 |
+
}
|
3776 |
+
|
3777 |
+
/**
|
3778 |
+
* skew
|
3779 |
+
*
|
3780 |
+
* @param float $angle_x
|
3781 |
+
* @param float $angle_y
|
3782 |
+
* @param float $x Origin abscissa
|
3783 |
+
* @param float $y Origin ordinate
|
3784 |
+
*/
|
3785 |
+
function skew($angle_x, $angle_y, $x, $y)
|
3786 |
+
{
|
3787 |
+
$y = $this->currentPageSize["height"] - $y;
|
3788 |
+
|
3789 |
+
$tan_x = tan(deg2rad($angle_x));
|
3790 |
+
$tan_y = tan(deg2rad($angle_y));
|
3791 |
+
|
3792 |
+
$tm = array(
|
3793 |
+
1,
|
3794 |
+
-$tan_y,
|
3795 |
+
-$tan_x,
|
3796 |
+
1,
|
3797 |
+
$tan_x * $y,
|
3798 |
+
$tan_y * $x,
|
3799 |
+
);
|
3800 |
+
|
3801 |
+
$this->transform($tm);
|
3802 |
+
}
|
3803 |
+
|
3804 |
+
/**
|
3805 |
+
* apply graphic transformations
|
3806 |
+
*
|
3807 |
+
* @param array $tm transformation matrix
|
3808 |
+
*/
|
3809 |
+
function transform($tm)
|
3810 |
+
{
|
3811 |
+
$this->addContent(vsprintf("\n %.3F %.3F %.3F %.3F %.3F %.3F cm", $tm));
|
3812 |
+
}
|
3813 |
+
|
3814 |
+
/**
|
3815 |
+
* add a new page to the document
|
3816 |
+
* this also makes the new page the current active object
|
3817 |
+
*
|
3818 |
+
* @param int $insert
|
3819 |
+
* @param int $id
|
3820 |
+
* @param string $pos
|
3821 |
+
* @return int
|
3822 |
+
*/
|
3823 |
+
function newPage($insert = 0, $id = 0, $pos = 'after')
|
3824 |
+
{
|
3825 |
+
// if there is a state saved, then go up the stack closing them
|
3826 |
+
// then on the new page, re-open them with the right setings
|
3827 |
+
|
3828 |
+
if ($this->nStateStack) {
|
3829 |
+
for ($i = $this->nStateStack; $i >= 1; $i--) {
|
3830 |
+
$this->restoreState($i);
|
3831 |
+
}
|
3832 |
+
}
|
3833 |
+
|
3834 |
+
$this->numObj++;
|
3835 |
+
|
3836 |
+
if ($insert) {
|
3837 |
+
// the id from the ezPdf class is the id of the contents of the page, not the page object itself
|
3838 |
+
// query that object to find the parent
|
3839 |
+
$rid = $this->objects[$id]['onPage'];
|
3840 |
+
$opt = array('rid' => $rid, 'pos' => $pos);
|
3841 |
+
$this->o_page($this->numObj, 'new', $opt);
|
3842 |
+
} else {
|
3843 |
+
$this->o_page($this->numObj, 'new');
|
3844 |
+
}
|
3845 |
+
|
3846 |
+
// if there is a stack saved, then put that onto the page
|
3847 |
+
if ($this->nStateStack) {
|
3848 |
+
for ($i = 1; $i <= $this->nStateStack; $i++) {
|
3849 |
+
$this->saveState($i);
|
3850 |
+
}
|
3851 |
+
}
|
3852 |
+
|
3853 |
+
// and if there has been a stroke or fill color set, then transfer them
|
3854 |
+
if (isset($this->currentColor)) {
|
3855 |
+
$this->setColor($this->currentColor, true);
|
3856 |
+
}
|
3857 |
+
|
3858 |
+
if (isset($this->currentStrokeColor)) {
|
3859 |
+
$this->setStrokeColor($this->currentStrokeColor, true);
|
3860 |
+
}
|
3861 |
+
|
3862 |
+
// if there is a line style set, then put this in too
|
3863 |
+
if (mb_strlen($this->currentLineStyle, '8bit')) {
|
3864 |
+
$this->addContent("\n$this->currentLineStyle");
|
3865 |
+
}
|
3866 |
+
|
3867 |
+
// the call to the o_page object set currentContents to the present page, so this can be returned as the page id
|
3868 |
+
return $this->currentContents;
|
3869 |
+
}
|
3870 |
+
|
3871 |
+
/**
|
3872 |
+
* Streams the PDF to the client.
|
3873 |
+
*
|
3874 |
+
* @param string $filename The filename to present to the client.
|
3875 |
+
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
|
3876 |
+
*/
|
3877 |
+
function stream($filename = "document.pdf", $options = array())
|
3878 |
+
{
|
3879 |
+
if (headers_sent()) {
|
3880 |
+
die("Unable to stream pdf: headers already sent");
|
3881 |
+
}
|
3882 |
+
|
3883 |
+
if (!isset($options["compress"])) $options["compress"] = true;
|
3884 |
+
if (!isset($options["Attachment"])) $options["Attachment"] = true;
|
3885 |
+
|
3886 |
+
$debug = !$options['compress'];
|
3887 |
+
$tmp = ltrim($this->output($debug));
|
3888 |
+
|
3889 |
+
header("Cache-Control: private");
|
3890 |
+
header("Content-Type: application/pdf");
|
3891 |
+
header("Content-Length: " . mb_strlen($tmp, "8bit"));
|
3892 |
+
|
3893 |
+
$filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
|
3894 |
+
$attachment = $options["Attachment"] ? "attachment" : "inline";
|
3895 |
+
|
3896 |
+
$encoding = mb_detect_encoding($filename);
|
3897 |
+
$fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding);
|
3898 |
+
$fallbackfilename = str_replace("\"", "", $fallbackfilename);
|
3899 |
+
$encodedfilename = rawurlencode($filename);
|
3900 |
+
|
3901 |
+
$contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\"";
|
3902 |
+
if ($fallbackfilename !== $filename) {
|
3903 |
+
$contentDisposition .= "; filename*=UTF-8''$encodedfilename";
|
3904 |
+
}
|
3905 |
+
header($contentDisposition);
|
3906 |
+
|
3907 |
+
echo $tmp;
|
3908 |
+
flush();
|
3909 |
+
}
|
3910 |
+
|
3911 |
+
/**
|
3912 |
+
* return the height in units of the current font in the given size
|
3913 |
+
*
|
3914 |
+
* @param $size
|
3915 |
+
* @return float|int
|
3916 |
+
*/
|
3917 |
+
function getFontHeight($size)
|
3918 |
+
{
|
3919 |
+
if (!$this->numFonts) {
|
3920 |
+
$this->selectFont($this->defaultFont);
|
3921 |
+
}
|
3922 |
+
|
3923 |
+
$font = $this->fonts[$this->currentFont];
|
3924 |
+
|
3925 |
+
// for the current font, and the given size, what is the height of the font in user units
|
3926 |
+
if (isset($font['Ascender']) && isset($font['Descender'])) {
|
3927 |
+
$h = $font['Ascender'] - $font['Descender'];
|
3928 |
+
} else {
|
3929 |
+
$h = $font['FontBBox'][3] - $font['FontBBox'][1];
|
3930 |
+
}
|
3931 |
+
|
3932 |
+
// have to adjust by a font offset for Windows fonts. unfortunately it looks like
|
3933 |
+
// the bounding box calculations are wrong and I don't know why.
|
3934 |
+
if (isset($font['FontHeightOffset'])) {
|
3935 |
+
// For CourierNew from Windows this needs to be -646 to match the
|
3936 |
+
// Adobe native Courier font.
|
3937 |
+
//
|
3938 |
+
// For FreeMono from GNU this needs to be -337 to match the
|
3939 |
+
// Courier font.
|
3940 |
+
//
|
3941 |
+
// Both have been added manually to the .afm and .ufm files.
|
3942 |
+
$h += (int)$font['FontHeightOffset'];
|
3943 |
+
}
|
3944 |
+
|
3945 |
+
return $size * $h / 1000;
|
3946 |
+
}
|
3947 |
+
|
3948 |
+
/**
|
3949 |
+
* @param $size
|
3950 |
+
* @return float|int
|
3951 |
+
*/
|
3952 |
+
function getFontXHeight($size)
|
3953 |
+
{
|
3954 |
+
if (!$this->numFonts) {
|
3955 |
+
$this->selectFont($this->defaultFont);
|
3956 |
+
}
|
3957 |
+
|
3958 |
+
$font = $this->fonts[$this->currentFont];
|
3959 |
+
|
3960 |
+
// for the current font, and the given size, what is the height of the font in user units
|
3961 |
+
if (isset($font['XHeight'])) {
|
3962 |
+
$xh = $font['Ascender'] - $font['Descender'];
|
3963 |
+
} else {
|
3964 |
+
$xh = $this->getFontHeight($size) / 2;
|
3965 |
+
}
|
3966 |
+
|
3967 |
+
return $size * $xh / 1000;
|
3968 |
+
}
|
3969 |
+
|
3970 |
+
/**
|
3971 |
+
* return the font descender, this will normally return a negative number
|
3972 |
+
* if you add this number to the baseline, you get the level of the bottom of the font
|
3973 |
+
* it is in the pdf user units
|
3974 |
+
*
|
3975 |
+
* @param $size
|
3976 |
+
* @return float|int
|
3977 |
+
*/
|
3978 |
+
function getFontDescender($size)
|
3979 |
+
{
|
3980 |
+
// note that this will most likely return a negative value
|
3981 |
+
if (!$this->numFonts) {
|
3982 |
+
$this->selectFont($this->defaultFont);
|
3983 |
+
}
|
3984 |
+
|
3985 |
+
//$h = $this->fonts[$this->currentFont]['FontBBox'][1];
|
3986 |
+
$h = $this->fonts[$this->currentFont]['Descender'];
|
3987 |
+
|
3988 |
+
return $size * $h / 1000;
|
3989 |
+
}
|
3990 |
+
|
3991 |
+
/**
|
3992 |
+
* filter the text, this is applied to all text just before being inserted into the pdf document
|
3993 |
+
* it escapes the various things that need to be escaped, and so on
|
3994 |
+
*
|
3995 |
+
* @access private
|
3996 |
+
*
|
3997 |
+
* @param $text
|
3998 |
+
* @param bool $bom
|
3999 |
+
* @param bool $convert_encoding
|
4000 |
+
* @return string
|
4001 |
+
*/
|
4002 |
+
function filterText($text, $bom = true, $convert_encoding = true)
|
4003 |
+
{
|
4004 |
+
if (!$this->numFonts) {
|
4005 |
+
$this->selectFont($this->defaultFont);
|
4006 |
+
}
|
4007 |
+
|
4008 |
+
if ($convert_encoding) {
|
4009 |
+
$cf = $this->currentFont;
|
4010 |
+
if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
|
4011 |
+
$text = $this->utf8toUtf16BE($text, $bom);
|
4012 |
+
} else {
|
4013 |
+
//$text = html_entity_decode($text, ENT_QUOTES);
|
4014 |
+
$text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
|
4015 |
+
}
|
4016 |
+
} else if ($bom) {
|
4017 |
+
$text = $this->utf8toUtf16BE($text, $bom);
|
4018 |
+
}
|
4019 |
+
|
4020 |
+
// the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
|
4021 |
+
return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
|
4022 |
+
}
|
4023 |
+
|
4024 |
+
/**
|
4025 |
+
* return array containing codepoints (UTF-8 character values) for the
|
4026 |
+
* string passed in.
|
4027 |
+
*
|
4028 |
+
* based on the excellent TCPDF code by Nicola Asuni and the
|
4029 |
+
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4030 |
+
*
|
4031 |
+
* @access private
|
4032 |
+
* @author Orion Richardson
|
4033 |
+
* @since January 5, 2008
|
4034 |
+
*
|
4035 |
+
* @param string $text UTF-8 string to process
|
4036 |
+
*
|
4037 |
+
* @return array UTF-8 codepoints array for the string
|
4038 |
+
*/
|
4039 |
+
function utf8toCodePointsArray(&$text)
|
4040 |
+
{
|
4041 |
+
$length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
|
4042 |
+
$unicode = array(); // array containing unicode values
|
4043 |
+
$bytes = array(); // array containing single character byte sequences
|
4044 |
+
$numbytes = 1; // number of octets needed to represent the UTF-8 character
|
4045 |
+
|
4046 |
+
for ($i = 0; $i < $length; $i++) {
|
4047 |
+
$c = ord($text[$i]); // get one string character at time
|
4048 |
+
if (count($bytes) === 0) { // get starting octect
|
4049 |
+
if ($c <= 0x7F) {
|
4050 |
+
$unicode[] = $c; // use the character "as is" because is ASCII
|
4051 |
+
$numbytes = 1;
|
4052 |
+
} elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
|
4053 |
+
$bytes[] = ($c - 0xC0) << 0x06;
|
4054 |
+
$numbytes = 2;
|
4055 |
+
} elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
|
4056 |
+
$bytes[] = ($c - 0xE0) << 0x0C;
|
4057 |
+
$numbytes = 3;
|
4058 |
+
} elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
|
4059 |
+
$bytes[] = ($c - 0xF0) << 0x12;
|
4060 |
+
$numbytes = 4;
|
4061 |
+
} else {
|
4062 |
+
// use replacement character for other invalid sequences
|
4063 |
+
$unicode[] = 0xFFFD;
|
4064 |
+
$bytes = array();
|
4065 |
+
$numbytes = 1;
|
4066 |
+
}
|
4067 |
+
} elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
|
4068 |
+
$bytes[] = $c - 0x80;
|
4069 |
+
if (count($bytes) === $numbytes) {
|
4070 |
+
// compose UTF-8 bytes to a single unicode value
|
4071 |
+
$c = $bytes[0];
|
4072 |
+
for ($j = 1; $j < $numbytes; $j++) {
|
4073 |
+
$c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
|
4074 |
+
}
|
4075 |
+
if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) {
|
4076 |
+
// The definition of UTF-8 prohibits encoding character numbers between
|
4077 |
+
// U+D800 and U+DFFF, which are reserved for use with the UTF-16
|
4078 |
+
// encoding form (as surrogate pairs) and do not directly represent
|
4079 |
+
// characters.
|
4080 |
+
$unicode[] = 0xFFFD; // use replacement character
|
4081 |
+
} else {
|
4082 |
+
$unicode[] = $c; // add char to array
|
4083 |
+
}
|
4084 |
+
// reset data for next char
|
4085 |
+
$bytes = array();
|
4086 |
+
$numbytes = 1;
|
4087 |
+
}
|
4088 |
+
} else {
|
4089 |
+
// use replacement character for other invalid sequences
|
4090 |
+
$unicode[] = 0xFFFD;
|
4091 |
+
$bytes = array();
|
4092 |
+
$numbytes = 1;
|
4093 |
+
}
|
4094 |
+
}
|
4095 |
+
|
4096 |
+
return $unicode;
|
4097 |
+
}
|
4098 |
+
|
4099 |
+
/**
|
4100 |
+
* convert UTF-8 to UTF-16 with an additional byte order marker
|
4101 |
+
* at the front if required.
|
4102 |
+
*
|
4103 |
+
* based on the excellent TCPDF code by Nicola Asuni and the
|
4104 |
+
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4105 |
+
*
|
4106 |
+
* @access private
|
4107 |
+
* @author Orion Richardson
|
4108 |
+
* @since January 5, 2008
|
4109 |
+
*
|
4110 |
+
* @param string $text UTF-8 string to process
|
4111 |
+
* @param boolean $bom whether to add the byte order marker
|
4112 |
+
*
|
4113 |
+
* @return string UTF-16 result string
|
4114 |
+
*/
|
4115 |
+
function utf8toUtf16BE(&$text, $bom = true)
|
4116 |
+
{
|
4117 |
+
$out = $bom ? "\xFE\xFF" : '';
|
4118 |
+
|
4119 |
+
$unicode = $this->utf8toCodePointsArray($text);
|
4120 |
+
foreach ($unicode as $c) {
|
4121 |
+
if ($c === 0xFFFD) {
|
4122 |
+
$out .= "\xFF\xFD"; // replacement character
|
4123 |
+
} elseif ($c < 0x10000) {
|
4124 |
+
$out .= chr($c >> 0x08) . chr($c & 0xFF);
|
4125 |
+
} else {
|
4126 |
+
$c -= 0x10000;
|
4127 |
+
$w1 = 0xD800 | ($c >> 0x10);
|
4128 |
+
$w2 = 0xDC00 | ($c & 0x3FF);
|
4129 |
+
$out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
|
4130 |
+
}
|
4131 |
+
}
|
4132 |
+
|
4133 |
+
return $out;
|
4134 |
+
}
|
4135 |
+
|
4136 |
+
/**
|
4137 |
+
* given a start position and information about how text is to be laid out, calculate where
|
4138 |
+
* on the page the text will end
|
4139 |
+
*
|
4140 |
+
* @param $x
|
4141 |
+
* @param $y
|
4142 |
+
* @param $angle
|
4143 |
+
* @param $size
|
4144 |
+
* @param $wa
|
4145 |
+
* @param $text
|
4146 |
+
* @return array
|
4147 |
+
*/
|
4148 |
+
private function getTextPosition($x, $y, $angle, $size, $wa, $text)
|
4149 |
+
{
|
4150 |
+
// given this information return an array containing x and y for the end position as elements 0 and 1
|
4151 |
+
$w = $this->getTextWidth($size, $text);
|
4152 |
+
|
4153 |
+
// need to adjust for the number of spaces in this text
|
4154 |
+
$words = explode(' ', $text);
|
4155 |
+
$nspaces = count($words) - 1;
|
4156 |
+
$w += $wa * $nspaces;
|
4157 |
+
$a = deg2rad((float)$angle);
|
4158 |
+
|
4159 |
+
return array(cos($a) * $w + $x, -sin($a) * $w + $y);
|
4160 |
+
}
|
4161 |
+
|
4162 |
+
/**
|
4163 |
+
* Callback method used by smallCaps
|
4164 |
+
*
|
4165 |
+
* @param array $matches
|
4166 |
+
*
|
4167 |
+
* @return string
|
4168 |
+
*/
|
4169 |
+
function toUpper($matches)
|
4170 |
+
{
|
4171 |
+
return mb_strtoupper($matches[0]);
|
4172 |
+
}
|
4173 |
+
|
4174 |
+
function concatMatches($matches)
|
4175 |
+
{
|
4176 |
+
$str = "";
|
4177 |
+
foreach ($matches as $match) {
|
4178 |
+
$str .= $match[0];
|
4179 |
+
}
|
4180 |
+
|
4181 |
+
return $str;
|
4182 |
+
}
|
4183 |
+
|
4184 |
+
/**
|
4185 |
+
* register text for font subsetting
|
4186 |
+
*
|
4187 |
+
* @param $font
|
4188 |
+
* @param $text
|
4189 |
+
*/
|
4190 |
+
function registerText($font, $text)
|
4191 |
+
{
|
4192 |
+
if (!$this->isUnicode || in_array(mb_strtolower(basename($font)), self::$coreFonts)) {
|
4193 |
+
return;
|
4194 |
+
}
|
4195 |
+
|
4196 |
+
if (!isset($this->stringSubsets[$font])) {
|
4197 |
+
$this->stringSubsets[$font] = array();
|
4198 |
+
}
|
4199 |
+
|
4200 |
+
$this->stringSubsets[$font] = array_unique(
|
4201 |
+
array_merge($this->stringSubsets[$font], $this->utf8toCodePointsArray($text))
|
4202 |
+
);
|
4203 |
+
}
|
4204 |
+
|
4205 |
+
/**
|
4206 |
+
* add text to the document, at a specified location, size and angle on the page
|
4207 |
+
*
|
4208 |
+
* @param $x
|
4209 |
+
* @param $y
|
4210 |
+
* @param $size
|
4211 |
+
* @param $text
|
4212 |
+
* @param int $angle
|
4213 |
+
* @param int $wordSpaceAdjust
|
4214 |
+
* @param int $charSpaceAdjust
|
4215 |
+
* @param bool $smallCaps
|
4216 |
+
*/
|
4217 |
+
function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false)
|
4218 |
+
{
|
4219 |
+
if (!$this->numFonts) {
|
4220 |
+
$this->selectFont($this->defaultFont);
|
4221 |
+
}
|
4222 |
+
|
4223 |
+
$text = str_replace(array("\r", "\n"), "", $text);
|
4224 |
+
|
4225 |
+
if ($smallCaps) {
|
4226 |
+
preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4227 |
+
$lower = $this->concatMatches($matches);
|
4228 |
+
d($lower);
|
4229 |
+
|
4230 |
+
preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4231 |
+
$other = $this->concatMatches($matches);
|
4232 |
+
d($other);
|
4233 |
+
|
4234 |
+
//$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
|
4235 |
+
}
|
4236 |
+
|
4237 |
+
// if there are any open callbacks, then they should be called, to show the start of the line
|
4238 |
+
if ($this->nCallback > 0) {
|
4239 |
+
for ($i = $this->nCallback; $i > 0; $i--) {
|
4240 |
+
// call each function
|
4241 |
+
$info = array(
|
4242 |
+
'x' => $x,
|
4243 |
+
'y' => $y,
|
4244 |
+
'angle' => $angle,
|
4245 |
+
'status' => 'sol',
|
4246 |
+
'p' => $this->callback[$i]['p'],
|
4247 |
+
'nCallback' => $this->callback[$i]['nCallback'],
|
4248 |
+
'height' => $this->callback[$i]['height'],
|
4249 |
+
'descender' => $this->callback[$i]['descender']
|
4250 |
+
);
|
4251 |
+
|
4252 |
+
$func = $this->callback[$i]['f'];
|
4253 |
+
$this->$func($info);
|
4254 |
+
}
|
4255 |
+
}
|
4256 |
+
|
4257 |
+
if ($angle == 0) {
|
4258 |
+
$this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y));
|
4259 |
+
} else {
|
4260 |
+
$a = deg2rad((float)$angle);
|
4261 |
+
$this->addContent(
|
4262 |
+
sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y)
|
4263 |
+
);
|
4264 |
+
}
|
4265 |
+
|
4266 |
+
if ($wordSpaceAdjust != 0) {
|
4267 |
+
$this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
|
4268 |
+
}
|
4269 |
+
|
4270 |
+
if ($charSpaceAdjust != 0) {
|
4271 |
+
$this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust));
|
4272 |
+
}
|
4273 |
+
|
4274 |
+
$len = mb_strlen($text);
|
4275 |
+
$start = 0;
|
4276 |
+
|
4277 |
+
if ($start < $len) {
|
4278 |
+
$part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start);
|
4279 |
+
$place_text = $this->filterText($part, false);
|
4280 |
+
// modify unicode text so that extra word spacing is manually implemented (bug #)
|
4281 |
+
$cf = $this->currentFont;
|
4282 |
+
if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
|
4283 |
+
$space_scale = 1000 / $size;
|
4284 |
+
$place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text);
|
4285 |
+
}
|
4286 |
+
$this->addContent(" /F$this->currentFontNum " . sprintf('%.1F Tf ', $size));
|
4287 |
+
$this->addContent(" [($place_text)] TJ");
|
4288 |
+
}
|
4289 |
+
|
4290 |
+
if ($wordSpaceAdjust != 0) {
|
4291 |
+
$this->addContent(sprintf(" %.3F Tw", 0));
|
4292 |
+
}
|
4293 |
+
|
4294 |
+
if ($charSpaceAdjust != 0) {
|
4295 |
+
$this->addContent(sprintf(" %.3F Tc", 0));
|
4296 |
+
}
|
4297 |
+
|
4298 |
+
$this->addContent(' ET');
|
4299 |
+
|
4300 |
+
// if there are any open callbacks, then they should be called, to show the end of the line
|
4301 |
+
if ($this->nCallback > 0) {
|
4302 |
+
for ($i = $this->nCallback; $i > 0; $i--) {
|
4303 |
+
// call each function
|
4304 |
+
$tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
|
4305 |
+
$info = array(
|
4306 |
+
'x' => $tmp[0],
|
4307 |
+
'y' => $tmp[1],
|
4308 |
+
'angle' => $angle,
|
4309 |
+
'status' => 'eol',
|
4310 |
+
'p' => $this->callback[$i]['p'],
|
4311 |
+
'nCallback' => $this->callback[$i]['nCallback'],
|
4312 |
+
'height' => $this->callback[$i]['height'],
|
4313 |
+
'descender' => $this->callback[$i]['descender']
|
4314 |
+
);
|
4315 |
+
$func = $this->callback[$i]['f'];
|
4316 |
+
$this->$func($info);
|
4317 |
+
}
|
4318 |
+
}
|
4319 |
+
}
|
4320 |
+
|
4321 |
+
/**
|
4322 |
+
* calculate how wide a given text string will be on a page, at a given size.
|
4323 |
+
* this can be called externally, but is also used by the other class functions
|
4324 |
+
*
|
4325 |
+
* @param $size
|
4326 |
+
* @param $text
|
4327 |
+
* @param int $word_spacing
|
4328 |
+
* @param int $char_spacing
|
4329 |
+
* @return float|int
|
4330 |
+
*/
|
4331 |
+
function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0)
|
4332 |
+
{
|
4333 |
+
static $ord_cache = array();
|
4334 |
+
|
4335 |
+
// this function should not change any of the settings, though it will need to
|
4336 |
+
// track any directives which change during calculation, so copy them at the start
|
4337 |
+
// and put them back at the end.
|
4338 |
+
$store_currentTextState = $this->currentTextState;
|
4339 |
+
|
4340 |
+
if (!$this->numFonts) {
|
4341 |
+
$this->selectFont($this->defaultFont);
|
4342 |
+
}
|
4343 |
+
|
4344 |
+
$text = str_replace(array("\r", "\n"), "", $text);
|
4345 |
+
|
4346 |
+
// converts a number or a float to a string so it can get the width
|
4347 |
+
$text = "$text";
|
4348 |
+
|
4349 |
+
// hmm, this is where it all starts to get tricky - use the font information to
|
4350 |
+
// calculate the width of each character, add them up and convert to user units
|
4351 |
+
$w = 0;
|
4352 |
+
$cf = $this->currentFont;
|
4353 |
+
$current_font = $this->fonts[$cf];
|
4354 |
+
$space_scale = 1000 / ($size > 0 ? $size : 1);
|
4355 |
+
$n_spaces = 0;
|
4356 |
+
|
4357 |
+
if ($current_font['isUnicode']) {
|
4358 |
+
// for Unicode, use the code points array to calculate width rather
|
4359 |
+
// than just the string itself
|
4360 |
+
$unicode = $this->utf8toCodePointsArray($text);
|
4361 |
+
|
4362 |
+
foreach ($unicode as $char) {
|
4363 |
+
// check if we have to replace character
|
4364 |
+
if (isset($current_font['differences'][$char])) {
|
4365 |
+
$char = $current_font['differences'][$char];
|
4366 |
+
}
|
4367 |
+
|
4368 |
+
if (isset($current_font['C'][$char])) {
|
4369 |
+
$char_width = $current_font['C'][$char];
|
4370 |
+
|
4371 |
+
// add the character width
|
4372 |
+
$w += $char_width;
|
4373 |
+
|
4374 |
+
// add additional padding for space
|
4375 |
+
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4376 |
+
$w += $word_spacing * $space_scale;
|
4377 |
+
$n_spaces++;
|
4378 |
+
}
|
4379 |
+
}
|
4380 |
+
}
|
4381 |
+
|
4382 |
+
// add additional char spacing
|
4383 |
+
if ($char_spacing != 0) {
|
4384 |
+
$w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
|
4385 |
+
}
|
4386 |
+
|
4387 |
+
} else {
|
4388 |
+
// If CPDF is in Unicode mode but the current font does not support Unicode we need to convert the character set to Windows-1252
|
4389 |
+
if ($this->isUnicode) {
|
4390 |
+
$text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
|
4391 |
+
}
|
4392 |
+
|
4393 |
+
$len = mb_strlen($text, 'Windows-1252');
|
4394 |
+
|
4395 |
+
for ($i = 0; $i < $len; $i++) {
|
4396 |
+
$c = $text[$i];
|
4397 |
+
$char = isset($ord_cache[$c]) ? $ord_cache[$c] : ($ord_cache[$c] = ord($c));
|
4398 |
+
|
4399 |
+
// check if we have to replace character
|
4400 |
+
if (isset($current_font['differences'][$char])) {
|
4401 |
+
$char = $current_font['differences'][$char];
|
4402 |
+
}
|
4403 |
+
|
4404 |
+
if (isset($current_font['C'][$char])) {
|
4405 |
+
$char_width = $current_font['C'][$char];
|
4406 |
+
|
4407 |
+
// add the character width
|
4408 |
+
$w += $char_width;
|
4409 |
+
|
4410 |
+
// add additional padding for space
|
4411 |
+
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4412 |
+
$w += $word_spacing * $space_scale;
|
4413 |
+
$n_spaces++;
|
4414 |
+
}
|
4415 |
+
}
|
4416 |
+
}
|
4417 |
+
|
4418 |
+
// add additional char spacing
|
4419 |
+
if ($char_spacing != 0) {
|
4420 |
+
$w += $char_spacing * $space_scale * ($len + $n_spaces);
|
4421 |
+
}
|
4422 |
+
}
|
4423 |
+
|
4424 |
+
$this->currentTextState = $store_currentTextState;
|
4425 |
+
$this->setCurrentFont();
|
4426 |
+
|
4427 |
+
return $w * $size / 1000;
|
4428 |
+
}
|
4429 |
+
|
4430 |
+
/**
|
4431 |
+
* this will be called at a new page to return the state to what it was on the
|
4432 |
+
* end of the previous page, before the stack was closed down
|
4433 |
+
* This is to get around not being able to have open 'q' across pages
|
4434 |
+
*
|
4435 |
+
* @param int $pageEnd
|
4436 |
+
*/
|
4437 |
+
function saveState($pageEnd = 0)
|
4438 |
+
{
|
4439 |
+
if ($pageEnd) {
|
4440 |
+
// this will be called at a new page to return the state to what it was on the
|
4441 |
+
// end of the previous page, before the stack was closed down
|
4442 |
+
// This is to get around not being able to have open 'q' across pages
|
4443 |
+
$opt = $this->stateStack[$pageEnd];
|
4444 |
+
// ok to use this as stack starts numbering at 1
|
4445 |
+
$this->setColor($opt['col'], true);
|
4446 |
+
$this->setStrokeColor($opt['str'], true);
|
4447 |
+
$this->addContent("\n" . $opt['lin']);
|
4448 |
+
// $this->currentLineStyle = $opt['lin'];
|
4449 |
+
} else {
|
4450 |
+
$this->nStateStack++;
|
4451 |
+
$this->stateStack[$this->nStateStack] = array(
|
4452 |
+
'col' => $this->currentColor,
|
4453 |
+
'str' => $this->currentStrokeColor,
|
4454 |
+
'lin' => $this->currentLineStyle
|
4455 |
+
);
|
4456 |
+
}
|
4457 |
+
|
4458 |
+
$this->save();
|
4459 |
+
}
|
4460 |
+
|
4461 |
+
/**
|
4462 |
+
* restore a previously saved state
|
4463 |
+
*
|
4464 |
+
* @param int $pageEnd
|
4465 |
+
*/
|
4466 |
+
function restoreState($pageEnd = 0)
|
4467 |
+
{
|
4468 |
+
if (!$pageEnd) {
|
4469 |
+
$n = $this->nStateStack;
|
4470 |
+
$this->currentColor = $this->stateStack[$n]['col'];
|
4471 |
+
$this->currentStrokeColor = $this->stateStack[$n]['str'];
|
4472 |
+
$this->addContent("\n" . $this->stateStack[$n]['lin']);
|
4473 |
+
$this->currentLineStyle = $this->stateStack[$n]['lin'];
|
4474 |
+
$this->stateStack[$n] = null;
|
4475 |
+
unset($this->stateStack[$n]);
|
4476 |
+
$this->nStateStack--;
|
4477 |
+
}
|
4478 |
+
|
4479 |
+
$this->restore();
|
4480 |
+
}
|
4481 |
+
|
4482 |
+
/**
|
4483 |
+
* make a loose object, the output will go into this object, until it is closed, then will revert to
|
4484 |
+
* the current one.
|
4485 |
+
* this object will not appear until it is included within a page.
|
4486 |
+
* the function will return the object number
|
4487 |
+
*
|
4488 |
+
* @return int
|
4489 |
+
*/
|
4490 |
+
function openObject()
|
4491 |
+
{
|
4492 |
+
$this->nStack++;
|
4493 |
+
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4494 |
+
// add a new object of the content type, to hold the data flow
|
4495 |
+
$this->numObj++;
|
4496 |
+
$this->o_contents($this->numObj, 'new');
|
4497 |
+
$this->currentContents = $this->numObj;
|
4498 |
+
$this->looseObjects[$this->numObj] = 1;
|
4499 |
+
|
4500 |
+
return $this->numObj;
|
4501 |
+
}
|
4502 |
+
|
4503 |
+
/**
|
4504 |
+
* open an existing object for editing
|
4505 |
+
*
|
4506 |
+
* @param $id
|
4507 |
+
*/
|
4508 |
+
function reopenObject($id)
|
4509 |
+
{
|
4510 |
+
$this->nStack++;
|
4511 |
+
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4512 |
+
$this->currentContents = $id;
|
4513 |
+
|
4514 |
+
// also if this object is the primary contents for a page, then set the current page to its parent
|
4515 |
+
if (isset($this->objects[$id]['onPage'])) {
|
4516 |
+
$this->currentPage = $this->objects[$id]['onPage'];
|
4517 |
+
}
|
4518 |
+
}
|
4519 |
+
|
4520 |
+
/**
|
4521 |
+
* close an object
|
4522 |
+
*/
|
4523 |
+
function closeObject()
|
4524 |
+
{
|
4525 |
+
// close the object, as long as there was one open in the first place, which will be indicated by
|
4526 |
+
// an objectId on the stack.
|
4527 |
+
if ($this->nStack > 0) {
|
4528 |
+
$this->currentContents = $this->stack[$this->nStack]['c'];
|
4529 |
+
$this->currentPage = $this->stack[$this->nStack]['p'];
|
4530 |
+
$this->nStack--;
|
4531 |
+
// easier to probably not worry about removing the old entries, they will be overwritten
|
4532 |
+
// if there are new ones.
|
4533 |
+
}
|
4534 |
+
}
|
4535 |
+
|
4536 |
+
/**
|
4537 |
+
* stop an object from appearing on pages from this point on
|
4538 |
+
*
|
4539 |
+
* @param $id
|
4540 |
+
*/
|
4541 |
+
function stopObject($id)
|
4542 |
+
{
|
4543 |
+
// if an object has been appearing on pages up to now, then stop it, this page will
|
4544 |
+
// be the last one that could contain it.
|
4545 |
+
if (isset($this->addLooseObjects[$id])) {
|
4546 |
+
$this->addLooseObjects[$id] = '';
|
4547 |
+
}
|
4548 |
+
}
|
4549 |
+
|
4550 |
+
/**
|
4551 |
+
* after an object has been created, it wil only show if it has been added, using this function.
|
4552 |
+
*
|
4553 |
+
* @param $id
|
4554 |
+
* @param string $options
|
4555 |
+
*/
|
4556 |
+
function addObject($id, $options = 'add')
|
4557 |
+
{
|
4558 |
+
// add the specified object to the page
|
4559 |
+
if (isset($this->looseObjects[$id]) && $this->currentContents != $id) {
|
4560 |
+
// then it is a valid object, and it is not being added to itself
|
4561 |
+
switch ($options) {
|
4562 |
+
case 'all':
|
4563 |
+
// then this object is to be added to this page (done in the next block) and
|
4564 |
+
// all future new pages.
|
4565 |
+
$this->addLooseObjects[$id] = 'all';
|
4566 |
+
|
4567 |
+
case 'add':
|
4568 |
+
if (isset($this->objects[$this->currentContents]['onPage'])) {
|
4569 |
+
// then the destination contents is the primary for the page
|
4570 |
+
// (though this object is actually added to that page)
|
4571 |
+
$this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id);
|
4572 |
+
}
|
4573 |
+
break;
|
4574 |
+
|
4575 |
+
case 'even':
|
4576 |
+
$this->addLooseObjects[$id] = 'even';
|
4577 |
+
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4578 |
+
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 0) {
|
4579 |
+
$this->addObject($id);
|
4580 |
+
// hacky huh :)
|
4581 |
+
}
|
4582 |
+
break;
|
4583 |
+
|
4584 |
+
case 'odd':
|
4585 |
+
$this->addLooseObjects[$id] = 'odd';
|
4586 |
+
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4587 |
+
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 1) {
|
4588 |
+
$this->addObject($id);
|
4589 |
+
// hacky huh :)
|
4590 |
+
}
|
4591 |
+
break;
|
4592 |
+
|
4593 |
+
case 'next':
|
4594 |
+
$this->addLooseObjects[$id] = 'all';
|
4595 |
+
break;
|
4596 |
+
|
4597 |
+
case 'nexteven':
|
4598 |
+
$this->addLooseObjects[$id] = 'even';
|
4599 |
+
break;
|
4600 |
+
|
4601 |
+
case 'nextodd':
|
4602 |
+
$this->addLooseObjects[$id] = 'odd';
|
4603 |
+
break;
|
4604 |
+
}
|
4605 |
+
}
|
4606 |
+
}
|
4607 |
+
|
4608 |
+
/**
|
4609 |
+
* return a storable representation of a specific object
|
4610 |
+
*
|
4611 |
+
* @param $id
|
4612 |
+
* @return string|null
|
4613 |
+
*/
|
4614 |
+
function serializeObject($id)
|
4615 |
+
{
|
4616 |
+
if (array_key_exists($id, $this->objects)) {
|
4617 |
+
return serialize($this->objects[$id]);
|
4618 |
+
}
|
4619 |
+
|
4620 |
+
return null;
|
4621 |
+
}
|
4622 |
+
|
4623 |
+
/**
|
4624 |
+
* restore an object from its stored representation. returns its new object id.
|
4625 |
+
*
|
4626 |
+
* @param $obj
|
4627 |
+
* @return int
|
4628 |
+
*/
|
4629 |
+
function restoreSerializedObject($obj)
|
4630 |
+
{
|
4631 |
+
$obj_id = $this->openObject();
|
4632 |
+
$this->objects[$obj_id] = unserialize($obj);
|
4633 |
+
$this->closeObject();
|
4634 |
+
|
4635 |
+
return $obj_id;
|
4636 |
+
}
|
4637 |
+
|
4638 |
+
/**
|
4639 |
+
* add content to the documents info object
|
4640 |
+
*
|
4641 |
+
* @param $label
|
4642 |
+
* @param int $value
|
4643 |
+
*/
|
4644 |
+
function addInfo($label, $value = 0)
|
4645 |
+
{
|
4646 |
+
// this will only work if the label is one of the valid ones.
|
4647 |
+
// modify this so that arrays can be passed as well.
|
4648 |
+
// if $label is an array then assume that it is key => value pairs
|
4649 |
+
// else assume that they are both scalar, anything else will probably error
|
4650 |
+
if (is_array($label)) {
|
4651 |
+
foreach ($label as $l => $v) {
|
4652 |
+
$this->o_info($this->infoObject, $l, $v);
|
4653 |
+
}
|
4654 |
+
} else {
|
4655 |
+
$this->o_info($this->infoObject, $label, $value);
|
4656 |
+
}
|
4657 |
+
}
|
4658 |
+
|
4659 |
+
/**
|
4660 |
+
* set the viewer preferences of the document, it is up to the browser to obey these.
|
4661 |
+
*
|
4662 |
+
* @param $label
|
4663 |
+
* @param int $value
|
4664 |
+
*/
|
4665 |
+
function setPreferences($label, $value = 0)
|
4666 |
+
{
|
4667 |
+
// this will only work if the label is one of the valid ones.
|
4668 |
+
if (is_array($label)) {
|
4669 |
+
foreach ($label as $l => $v) {
|
4670 |
+
$this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
|
4671 |
+
}
|
4672 |
+
} else {
|
4673 |
+
$this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
|
4674 |
+
}
|
4675 |
+
}
|
4676 |
+
|
4677 |
+
/**
|
4678 |
+
* extract an integer from a position in a byte stream
|
4679 |
+
*
|
4680 |
+
* @param $data
|
4681 |
+
* @param $pos
|
4682 |
+
* @param $num
|
4683 |
+
* @return int
|
4684 |
+
*/
|
4685 |
+
private function getBytes(&$data, $pos, $num)
|
4686 |
+
{
|
4687 |
+
// return the integer represented by $num bytes from $pos within $data
|
4688 |
+
$ret = 0;
|
4689 |
+
for ($i = 0; $i < $num; $i++) {
|
4690 |
+
$ret *= 256;
|
4691 |
+
$ret += ord($data[$pos + $i]);
|
4692 |
+
}
|
4693 |
+
|
4694 |
+
return $ret;
|
4695 |
+
}
|
4696 |
+
|
4697 |
+
/**
|
4698 |
+
* Check if image already added to pdf image directory.
|
4699 |
+
* If yes, need not to create again (pass empty data)
|
4700 |
+
*
|
4701 |
+
* @param $imgname
|
4702 |
+
* @return bool
|
4703 |
+
*/
|
4704 |
+
function image_iscached($imgname)
|
4705 |
+
{
|
4706 |
+
return isset($this->imagelist[$imgname]);
|
4707 |
+
}
|
4708 |
+
|
4709 |
+
/**
|
4710 |
+
* add a PNG image into the document, from a GD object
|
4711 |
+
* this should work with remote files
|
4712 |
+
*
|
4713 |
+
* @param string $file The PNG file
|
4714 |
+
* @param float $x X position
|
4715 |
+
* @param float $y Y position
|
4716 |
+
* @param float $w Width
|
4717 |
+
* @param float $h Height
|
4718 |
+
* @param resource $img A GD resource
|
4719 |
+
* @param bool $is_mask true if the image is a mask
|
4720 |
+
* @param bool $mask true if the image is masked
|
4721 |
+
* @throws Exception
|
4722 |
+
*/
|
4723 |
+
function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null)
|
4724 |
+
{
|
4725 |
+
if (!function_exists("imagepng")) {
|
4726 |
+
throw new \Exception("The PHP GD extension is required, but is not installed.");
|
4727 |
+
}
|
4728 |
+
|
4729 |
+
//if already cached, need not to read again
|
4730 |
+
if (isset($this->imagelist[$file])) {
|
4731 |
+
$data = null;
|
4732 |
+
} else {
|
4733 |
+
// Example for transparency handling on new image. Retain for current image
|
4734 |
+
// $tIndex = imagecolortransparent($img);
|
4735 |
+
// if ($tIndex > 0) {
|
4736 |
+
// $tColor = imagecolorsforindex($img, $tIndex);
|
4737 |
+
// $new_tIndex = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']);
|
4738 |
+
// imagefill($new_img, 0, 0, $new_tIndex);
|
4739 |
+
// imagecolortransparent($new_img, $new_tIndex);
|
4740 |
+
// }
|
4741 |
+
// blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn
|
4742 |
+
//imagealphablending($img, true);
|
4743 |
+
|
4744 |
+
//default, but explicitely set to ensure pdf compatibility
|
4745 |
+
imagesavealpha($img, false/*!$is_mask && !$mask*/);
|
4746 |
+
|
4747 |
+
$error = 0;
|
4748 |
+
//DEBUG_IMG_TEMP
|
4749 |
+
//debugpng
|
4750 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4751 |
+
print '[addImagePng ' . $file . ']';
|
4752 |
+
}
|
4753 |
+
|
4754 |
+
ob_start();
|
4755 |
+
@imagepng($img);
|
4756 |
+
$data = ob_get_clean();
|
4757 |
+
|
4758 |
+
if ($data == '') {
|
4759 |
+
$error = 1;
|
4760 |
+
$errormsg = 'trouble writing file from GD';
|
4761 |
+
//DEBUG_IMG_TEMP
|
4762 |
+
//debugpng
|
4763 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4764 |
+
print 'trouble writing file from GD';
|
4765 |
+
}
|
4766 |
+
}
|
4767 |
+
|
4768 |
+
if ($error) {
|
4769 |
+
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
|
4770 |
+
|
4771 |
+
return;
|
4772 |
+
}
|
4773 |
+
} //End isset($this->imagelist[$file]) (png Duplicate removal)
|
4774 |
+
|
4775 |
+
$this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
|
4776 |
+
}
|
4777 |
+
|
4778 |
+
/**
|
4779 |
+
* @param $file
|
4780 |
+
* @param $x
|
4781 |
+
* @param $y
|
4782 |
+
* @param $w
|
4783 |
+
* @param $h
|
4784 |
+
* @param $byte
|
4785 |
+
*/
|
4786 |
+
protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte)
|
4787 |
+
{
|
4788 |
+
// generate images
|
4789 |
+
$img = imagecreatefrompng($file);
|
4790 |
+
|
4791 |
+
if ($img === false) {
|
4792 |
+
return;
|
4793 |
+
}
|
4794 |
+
|
4795 |
+
// FIXME The pixel transformation doesn't work well with 8bit PNGs
|
4796 |
+
$eight_bit = ($byte & 4) !== 4;
|
4797 |
+
|
4798 |
+
$wpx = imagesx($img);
|
4799 |
+
$hpx = imagesy($img);
|
4800 |
+
|
4801 |
+
imagesavealpha($img, false);
|
4802 |
+
|
4803 |
+
// create temp alpha file
|
4804 |
+
$tempfile_alpha = @tempnam($this->tmp, "cpdf_img_");
|
4805 |
+
@unlink($tempfile_alpha);
|
4806 |
+
$tempfile_alpha = "$tempfile_alpha.png";
|
4807 |
+
|
4808 |
+
// create temp plain file
|
4809 |
+
$tempfile_plain = @tempnam($this->tmp, "cpdf_img_");
|
4810 |
+
@unlink($tempfile_plain);
|
4811 |
+
$tempfile_plain = "$tempfile_plain.png";
|
4812 |
+
|
4813 |
+
$imgalpha = imagecreate($wpx, $hpx);
|
4814 |
+
imagesavealpha($imgalpha, false);
|
4815 |
+
|
4816 |
+
// generate gray scale palette (0 -> 255)
|
4817 |
+
for ($c = 0; $c < 256; ++$c) {
|
4818 |
+
imagecolorallocate($imgalpha, $c, $c, $c);
|
4819 |
+
}
|
4820 |
+
|
4821 |
+
// Use PECL gmagick + Graphics Magic to process transparent PNG images
|
4822 |
+
if (extension_loaded("gmagick")) {
|
4823 |
+
$gmagick = new \Gmagick($file);
|
4824 |
+
$gmagick->setimageformat('png');
|
4825 |
+
|
4826 |
+
// Get opacity channel (negative of alpha channel)
|
4827 |
+
$alpha_channel_neg = clone $gmagick;
|
4828 |
+
$alpha_channel_neg->separateimagechannel(\Gmagick::CHANNEL_OPACITY);
|
4829 |
+
|
4830 |
+
// Negate opacity channel
|
4831 |
+
$alpha_channel = new \Gmagick();
|
4832 |
+
$alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4833 |
+
$alpha_channel->compositeimage($alpha_channel_neg, \Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
|
4834 |
+
$alpha_channel->separateimagechannel(\Gmagick::CHANNEL_RED);
|
4835 |
+
$alpha_channel->writeimage($tempfile_alpha);
|
4836 |
+
|
4837 |
+
// Cast to 8bit+palette
|
4838 |
+
$imgalpha_ = imagecreatefrompng($tempfile_alpha);
|
4839 |
+
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
|
4840 |
+
imagedestroy($imgalpha_);
|
4841 |
+
imagepng($imgalpha, $tempfile_alpha);
|
4842 |
+
|
4843 |
+
// Make opaque image
|
4844 |
+
$color_channels = new \Gmagick();
|
4845 |
+
$color_channels->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4846 |
+
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYRED, 0, 0);
|
4847 |
+
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYGREEN, 0, 0);
|
4848 |
+
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYBLUE, 0, 0);
|
4849 |
+
$color_channels->writeimage($tempfile_plain);
|
4850 |
+
|
4851 |
+
$imgplain = imagecreatefrompng($tempfile_plain);
|
4852 |
+
}
|
4853 |
+
// Use PECL imagick + ImageMagic to process transparent PNG images
|
4854 |
+
elseif (extension_loaded("imagick")) {
|
4855 |
+
// Native cloning was added to pecl-imagick in svn commit 263814
|
4856 |
+
// the first version containing it was 3.0.1RC1
|
4857 |
+
static $imagickClonable = null;
|
4858 |
+
if ($imagickClonable === null) {
|
4859 |
+
$imagickClonable = version_compare(Imagick::IMAGICK_EXTVER, '3.0.1rc1') > 0;
|
4860 |
+
}
|
4861 |
+
|
4862 |
+
$imagick = new \Imagick($file);
|
4863 |
+
$imagick->setFormat('png');
|
4864 |
+
|
4865 |
+
// Get opacity channel (negative of alpha channel)
|
4866 |
+
if ($imagick->getImageAlphaChannel() !== 0) {
|
4867 |
+
$alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
|
4868 |
+
$alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA);
|
4869 |
+
// Since ImageMagick7 negate invert transparency as default
|
4870 |
+
$imagick_version = \Imagick::getVersion();
|
4871 |
+
if ( !empty($imagick_version) && is_array($imagick_version) && $imagick_version['versionNumber'] < 1800 ) {
|
4872 |
+
$alpha_channel->negateImage(true);
|
4873 |
+
}
|
4874 |
+
$alpha_channel->writeImage($tempfile_alpha);
|
4875 |
+
|
4876 |
+
// Cast to 8bit+palette
|
4877 |
+
$imgalpha_ = imagecreatefrompng($tempfile_alpha);
|
4878 |
+
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
|
4879 |
+
imagedestroy($imgalpha_);
|
4880 |
+
imagepng($imgalpha, $tempfile_alpha);
|
4881 |
+
} else {
|
4882 |
+
$tempfile_alpha = null;
|
4883 |
+
}
|
4884 |
+
|
4885 |
+
// Make opaque image
|
4886 |
+
$color_channels = new \Imagick();
|
4887 |
+
$color_channels->newImage($wpx, $hpx, "#FFFFFF", "png");
|
4888 |
+
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYRED, 0, 0);
|
4889 |
+
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYGREEN, 0, 0);
|
4890 |
+
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYBLUE, 0, 0);
|
4891 |
+
$color_channels->writeImage($tempfile_plain);
|
4892 |
+
|
4893 |
+
$imgplain = imagecreatefrompng($tempfile_plain);
|
4894 |
+
} else {
|
4895 |
+
// allocated colors cache
|
4896 |
+
$allocated_colors = array();
|
4897 |
+
|
4898 |
+
// extract alpha channel
|
4899 |
+
for ($xpx = 0; $xpx < $wpx; ++$xpx) {
|
4900 |
+
for ($ypx = 0; $ypx < $hpx; ++$ypx) {
|
4901 |
+
$color = imagecolorat($img, $xpx, $ypx);
|
4902 |
+
$col = imagecolorsforindex($img, $color);
|
4903 |
+
$alpha = $col['alpha'];
|
4904 |
+
|
4905 |
+
if ($eight_bit) {
|
4906 |
+
// with gamma correction
|
4907 |
+
$gammacorr = 2.2;
|
4908 |
+
$pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255;
|
4909 |
+
} else {
|
4910 |
+
// without gamma correction
|
4911 |
+
$pixel = (127 - $alpha) * 2;
|
4912 |
+
|
4913 |
+
$key = $col['red'] . $col['green'] . $col['blue'];
|
4914 |
+
|
4915 |
+
if (!isset($allocated_colors[$key])) {
|
4916 |
+
$pixel_img = imagecolorallocate($img, $col['red'], $col['green'], $col['blue']);
|
4917 |
+
$allocated_colors[$key] = $pixel_img;
|
4918 |
+
} else {
|
4919 |
+
$pixel_img = $allocated_colors[$key];
|
4920 |
+
}
|
4921 |
+
|
4922 |
+
imagesetpixel($img, $xpx, $ypx, $pixel_img);
|
4923 |
+
}
|
4924 |
+
|
4925 |
+
imagesetpixel($imgalpha, $xpx, $ypx, $pixel);
|
4926 |
+
}
|
4927 |
+
}
|
4928 |
+
|
4929 |
+
// extract image without alpha channel
|
4930 |
+
$imgplain = imagecreatetruecolor($wpx, $hpx);
|
4931 |
+
imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
|
4932 |
+
imagedestroy($img);
|
4933 |
+
|
4934 |
+
imagepng($imgalpha, $tempfile_alpha);
|
4935 |
+
imagepng($imgplain, $tempfile_plain);
|
4936 |
+
}
|
4937 |
+
|
4938 |
+
// embed mask image
|
4939 |
+
if ($tempfile_alpha) {
|
4940 |
+
$this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true);
|
4941 |
+
imagedestroy($imgalpha);
|
4942 |
+
}
|
4943 |
+
|
4944 |
+
// embed image, masked with previously embedded mask
|
4945 |
+
$this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, ($tempfile_alpha !== null));
|
4946 |
+
imagedestroy($imgplain);
|
4947 |
+
|
4948 |
+
// remove temp files
|
4949 |
+
if ($tempfile_alpha) {
|
4950 |
+
unlink($tempfile_alpha);
|
4951 |
+
}
|
4952 |
+
unlink($tempfile_plain);
|
4953 |
+
}
|
4954 |
+
|
4955 |
+
/**
|
4956 |
+
* add a PNG image into the document, from a file
|
4957 |
+
* this should work with remote files
|
4958 |
+
*
|
4959 |
+
* @param $file
|
4960 |
+
* @param $x
|
4961 |
+
* @param $y
|
4962 |
+
* @param int $w
|
4963 |
+
* @param int $h
|
4964 |
+
* @throws Exception
|
4965 |
+
*/
|
4966 |
+
function addPngFromFile($file, $x, $y, $w = 0, $h = 0)
|
4967 |
+
{
|
4968 |
+
if (!function_exists("imagecreatefrompng")) {
|
4969 |
+
throw new \Exception("The PHP GD extension is required, but is not installed.");
|
4970 |
+
}
|
4971 |
+
|
4972 |
+
//if already cached, need not to read again
|
4973 |
+
if (isset($this->imagelist[$file])) {
|
4974 |
+
$img = null;
|
4975 |
+
} else {
|
4976 |
+
$info = file_get_contents($file, false, null, 24, 5);
|
4977 |
+
$meta = unpack("CbitDepth/CcolorType/CcompressionMethod/CfilterMethod/CinterlaceMethod", $info);
|
4978 |
+
$bit_depth = $meta["bitDepth"];
|
4979 |
+
$color_type = $meta["colorType"];
|
4980 |
+
|
4981 |
+
// http://www.w3.org/TR/PNG/#11IHDR
|
4982 |
+
// 3 => indexed
|
4983 |
+
// 4 => greyscale with alpha
|
4984 |
+
// 6 => fullcolor with alpha
|
4985 |
+
$is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4);
|
4986 |
+
|
4987 |
+
if ($is_alpha) { // exclude grayscale alpha
|
4988 |
+
$this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
|
4989 |
+
return;
|
4990 |
+
}
|
4991 |
+
|
4992 |
+
//png files typically contain an alpha channel.
|
4993 |
+
//pdf file format or class.pdf does not support alpha blending.
|
4994 |
+
//on alpha blended images, more transparent areas have a color near black.
|
4995 |
+
//This appears in the result on not storing the alpha channel.
|
4996 |
+
//Correct would be the box background image or its parent when transparent.
|
4997 |
+
//But this would make the image dependent on the background.
|
4998 |
+
//Therefore create an image with white background and copy in
|
4999 |
+
//A more natural background than black is white.
|
5000 |
+
//Therefore create an empty image with white background and merge the
|
5001 |
+
//image in with alpha blending.
|
5002 |
+
$imgtmp = @imagecreatefrompng($file);
|
5003 |
+
if (!$imgtmp) {
|
5004 |
+
return;
|
5005 |
+
}
|
5006 |
+
$sx = imagesx($imgtmp);
|
5007 |
+
$sy = imagesy($imgtmp);
|
5008 |
+
$img = imagecreatetruecolor($sx, $sy);
|
5009 |
+
imagealphablending($img, true);
|
5010 |
+
|
5011 |
+
// @todo is it still needed ??
|
5012 |
+
$ti = imagecolortransparent($imgtmp);
|
5013 |
+
if ($ti >= 0) {
|
5014 |
+
$tc = imagecolorsforindex($imgtmp, $ti);
|
5015 |
+
$ti = imagecolorallocate($img, $tc['red'], $tc['green'], $tc['blue']);
|
5016 |
+
imagefill($img, 0, 0, $ti);
|
5017 |
+
imagecolortransparent($img, $ti);
|
5018 |
+
} else {
|
5019 |
+
imagefill($img, 1, 1, imagecolorallocate($img, 255, 255, 255));
|
5020 |
+
}
|
5021 |
+
|
5022 |
+
imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy);
|
5023 |
+
imagedestroy($imgtmp);
|
5024 |
+
}
|
5025 |
+
$this->addImagePng($file, $x, $y, $w, $h, $img);
|
5026 |
+
|
5027 |
+
if ($img) {
|
5028 |
+
imagedestroy($img);
|
5029 |
+
}
|
5030 |
+
}
|
5031 |
+
|
5032 |
+
/**
|
5033 |
+
* add a PNG image into the document, from a file
|
5034 |
+
* this should work with remote files
|
5035 |
+
*
|
5036 |
+
* @param $file
|
5037 |
+
* @param $x
|
5038 |
+
* @param $y
|
5039 |
+
* @param int $w
|
5040 |
+
* @param int $h
|
5041 |
+
*/
|
5042 |
+
function addSvgFromFile($file, $x, $y, $w = 0, $h = 0)
|
5043 |
+
{
|
5044 |
+
$doc = new \Svg\Document();
|
5045 |
+
$doc->loadFile($file);
|
5046 |
+
$dimensions = $doc->getDimensions();
|
5047 |
+
|
5048 |
+
$this->save();
|
5049 |
+
|
5050 |
+
$this->transform(array($w / $dimensions["width"], 0, 0, $h / $dimensions["height"], $x, $y));
|
5051 |
+
|
5052 |
+
$surface = new \Svg\Surface\SurfaceCpdf($doc, $this);
|
5053 |
+
$doc->render($surface);
|
5054 |
+
|
5055 |
+
$this->restore();
|
5056 |
+
}
|
5057 |
+
|
5058 |
+
/**
|
5059 |
+
* add a PNG image into the document, from a memory buffer of the file
|
5060 |
+
*
|
5061 |
+
* @param $file
|
5062 |
+
* @param $x
|
5063 |
+
* @param $y
|
5064 |
+
* @param float $w
|
5065 |
+
* @param float $h
|
5066 |
+
* @param $data
|
5067 |
+
* @param bool $is_mask
|
5068 |
+
* @param null $mask
|
5069 |
+
*/
|
5070 |
+
function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null)
|
5071 |
+
{
|
5072 |
+
if (isset($this->imagelist[$file])) {
|
5073 |
+
$data = null;
|
5074 |
+
$info['width'] = $this->imagelist[$file]['w'];
|
5075 |
+
$info['height'] = $this->imagelist[$file]['h'];
|
5076 |
+
$label = $this->imagelist[$file]['label'];
|
5077 |
+
} else {
|
5078 |
+
if ($data == null) {
|
5079 |
+
$this->addMessage('addPngFromBuf error - data not present!');
|
5080 |
+
|
5081 |
+
return;
|
5082 |
+
}
|
5083 |
+
|
5084 |
+
$error = 0;
|
5085 |
+
|
5086 |
+
if (!$error) {
|
5087 |
+
$header = chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10);
|
5088 |
+
|
5089 |
+
if (mb_substr($data, 0, 8, '8bit') != $header) {
|
5090 |
+
$error = 1;
|
5091 |
+
|
5092 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5093 |
+
print '[addPngFromFile this file does not have a valid header ' . $file . ']';
|
5094 |
+
}
|
5095 |
+
|
5096 |
+
$errormsg = 'this file does not have a valid header';
|
5097 |
+
}
|
5098 |
+
}
|
5099 |
+
|
5100 |
+
if (!$error) {
|
5101 |
+
// set pointer
|
5102 |
+
$p = 8;
|
5103 |
+
$len = mb_strlen($data, '8bit');
|
5104 |
+
|
5105 |
+
// cycle through the file, identifying chunks
|
5106 |
+
$haveHeader = 0;
|
5107 |
+
$info = array();
|
5108 |
+
$idata = '';
|
5109 |
+
$pdata = '';
|
5110 |
+
|
5111 |
+
while ($p < $len) {
|
5112 |
+
$chunkLen = $this->getBytes($data, $p, 4);
|
5113 |
+
$chunkType = mb_substr($data, $p + 4, 4, '8bit');
|
5114 |
+
|
5115 |
+
switch ($chunkType) {
|
5116 |
+
case 'IHDR':
|
5117 |
+
// this is where all the file information comes from
|
5118 |
+
$info['width'] = $this->getBytes($data, $p + 8, 4);
|
5119 |
+
$info['height'] = $this->getBytes($data, $p + 12, 4);
|
5120 |
+
$info['bitDepth'] = ord($data[$p + 16]);
|
5121 |
+
$info['colorType'] = ord($data[$p + 17]);
|
5122 |
+
$info['compressionMethod'] = ord($data[$p + 18]);
|
5123 |
+
$info['filterMethod'] = ord($data[$p + 19]);
|
5124 |
+
$info['interlaceMethod'] = ord($data[$p + 20]);
|
5125 |
+
|
5126 |
+
//print_r($info);
|
5127 |
+
$haveHeader = 1;
|
5128 |
+
if ($info['compressionMethod'] != 0) {
|
5129 |
+
$error = 1;
|
5130 |
+
|
5131 |
+
//debugpng
|
5132 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5133 |
+
print '[addPngFromFile unsupported compression method ' . $file . ']';
|
5134 |
+
}
|
5135 |
+
|
5136 |
+
$errormsg = 'unsupported compression method';
|
5137 |
+
}
|
5138 |
+
|
5139 |
+
if ($info['filterMethod'] != 0) {
|
5140 |
+
$error = 1;
|
5141 |
+
|
5142 |
+
//debugpng
|
5143 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5144 |
+
print '[addPngFromFile unsupported filter method ' . $file . ']';
|
5145 |
+
}
|
5146 |
+
|
5147 |
+
$errormsg = 'unsupported filter method';
|
5148 |
+
}
|
5149 |
+
break;
|
5150 |
+
|
5151 |
+
case 'PLTE':
|
5152 |
+
$pdata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
|
5153 |
+
break;
|
5154 |
+
|
5155 |
+
case 'IDAT':
|
5156 |
+
$idata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
|
5157 |
+
break;
|
5158 |
+
|
5159 |
+
case 'tRNS':
|
5160 |
+
//this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
|
5161 |
+
//print "tRNS found, color type = ".$info['colorType']."\n";
|
5162 |
+
$transparency = array();
|
5163 |
+
|
5164 |
+
switch ($info['colorType']) {
|
5165 |
+
// indexed color, rbg
|
5166 |
+
case 3:
|
5167 |
+
/* corresponding to entries in the plte chunk
|
5168 |
+
Alpha for palette index 0: 1 byte
|
5169 |
+
Alpha for palette index 1: 1 byte
|
5170 |
+
...etc...
|
5171 |
+
*/
|
5172 |
+
// there will be one entry for each palette entry. up until the last non-opaque entry.
|
5173 |
+
// set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
|
5174 |
+
$transparency['type'] = 'indexed';
|
5175 |
+
$trans = 0;
|
5176 |
+
|
5177 |
+
for ($i = $chunkLen; $i >= 0; $i--) {
|
5178 |
+
if (ord($data[$p + 8 + $i]) == 0) {
|
5179 |
+
$trans = $i;
|
5180 |
+
}
|
5181 |
+
}
|
5182 |
+
|
5183 |
+
$transparency['data'] = $trans;
|
5184 |
+
break;
|
5185 |
+
|
5186 |
+
// grayscale
|
5187 |
+
case 0:
|
5188 |
+
/* corresponding to entries in the plte chunk
|
5189 |
+
Gray: 2 bytes, range 0 .. (2^bitdepth)-1
|
5190 |
+
*/
|
5191 |
+
// $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale
|
5192 |
+
$transparency['type'] = 'indexed';
|
5193 |
+
$transparency['data'] = ord($data[$p + 8 + 1]);
|
5194 |
+
break;
|
5195 |
+
|
5196 |
+
// truecolor
|
5197 |
+
case 2:
|
5198 |
+
/* corresponding to entries in the plte chunk
|
5199 |
+
Red: 2 bytes, range 0 .. (2^bitdepth)-1
|
5200 |
+
Green: 2 bytes, range 0 .. (2^bitdepth)-1
|
5201 |
+
Blue: 2 bytes, range 0 .. (2^bitdepth)-1
|
5202 |
+
*/
|
5203 |
+
$transparency['r'] = $this->getBytes($data, $p + 8, 2);
|
5204 |
+
// r from truecolor
|
5205 |
+
$transparency['g'] = $this->getBytes($data, $p + 10, 2);
|
5206 |
+
// g from truecolor
|
5207 |
+
$transparency['b'] = $this->getBytes($data, $p + 12, 2);
|
5208 |
+
// b from truecolor
|
5209 |
+
|
5210 |
+
$transparency['type'] = 'color-key';
|
5211 |
+
break;
|
5212 |
+
|
5213 |
+
//unsupported transparency type
|
5214 |
+
default:
|
5215 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5216 |
+
print '[addPngFromFile unsupported transparency type ' . $file . ']';
|
5217 |
+
}
|
5218 |
+
break;
|
5219 |
+
}
|
5220 |
+
|
5221 |
+
// KS End new code
|
5222 |
+
break;
|
5223 |
+
|
5224 |
+
default:
|
5225 |
+
break;
|
5226 |
+
}
|
5227 |
+
|
5228 |
+
$p += $chunkLen + 12;
|
5229 |
+
}
|
5230 |
+
|
5231 |
+
if (!$haveHeader) {
|
5232 |
+
$error = 1;
|
5233 |
+
|
5234 |
+
//debugpng
|
5235 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5236 |
+
print '[addPngFromFile information header is missing ' . $file . ']';
|
5237 |
+
}
|
5238 |
+
|
5239 |
+
$errormsg = 'information header is missing';
|
5240 |
+
}
|
5241 |
+
|
5242 |
+
if (isset($info['interlaceMethod']) && $info['interlaceMethod']) {
|
5243 |
+
$error = 1;
|
5244 |
+
|
5245 |
+
//debugpng
|
5246 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5247 |
+
print '[addPngFromFile no support for interlaced images in pdf ' . $file . ']';
|
5248 |
+
}
|
5249 |
+
|
5250 |
+
$errormsg = 'There appears to be no support for interlaced images in pdf.';
|
5251 |
+
}
|
5252 |
+
}
|
5253 |
+
|
5254 |
+
if (!$error && $info['bitDepth'] > 8) {
|
5255 |
+
$error = 1;
|
5256 |
+
|
5257 |
+
//debugpng
|
5258 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5259 |
+
print '[addPngFromFile bit depth of 8 or less is supported ' . $file . ']';
|
5260 |
+
}
|
5261 |
+
|
5262 |
+
$errormsg = 'only bit depth of 8 or less is supported';
|
5263 |
+
}
|
5264 |
+
|
5265 |
+
if (!$error) {
|
5266 |
+
switch ($info['colorType']) {
|
5267 |
+
case 3:
|
5268 |
+
$color = 'DeviceRGB';
|
5269 |
+
$ncolor = 1;
|
5270 |
+
break;
|
5271 |
+
|
5272 |
+
case 2:
|
5273 |
+
$color = 'DeviceRGB';
|
5274 |
+
$ncolor = 3;
|
5275 |
+
break;
|
5276 |
+
|
5277 |
+
case 0:
|
5278 |
+
$color = 'DeviceGray';
|
5279 |
+
$ncolor = 1;
|
5280 |
+
break;
|
5281 |
+
|
5282 |
+
default:
|
5283 |
+
$error = 1;
|
5284 |
+
|
5285 |
+
//debugpng
|
5286 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5287 |
+
print '[addPngFromFile alpha channel not supported: ' . $info['colorType'] . ' ' . $file . ']';
|
5288 |
+
}
|
5289 |
+
|
5290 |
+
$errormsg = 'transparency alpha channel not supported, transparency only supported for palette images.';
|
5291 |
+
}
|
5292 |
+
}
|
5293 |
+
|
5294 |
+
if ($error) {
|
5295 |
+
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
|
5296 |
+
|
5297 |
+
return;
|
5298 |
+
}
|
5299 |
+
|
5300 |
+
//print_r($info);
|
5301 |
+
// so this image is ok... add it in.
|
5302 |
+
$this->numImages++;
|
5303 |
+
$im = $this->numImages;
|
5304 |
+
$label = "I$im";
|
5305 |
+
$this->numObj++;
|
5306 |
+
|
5307 |
+
// $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
|
5308 |
+
$options = array(
|
5309 |
+
'label' => $label,
|
5310 |
+
'data' => $idata,
|
5311 |
+
'bitsPerComponent' => $info['bitDepth'],
|
5312 |
+
'pdata' => $pdata,
|
5313 |
+
'iw' => $info['width'],
|
5314 |
+
'ih' => $info['height'],
|
5315 |
+
'type' => 'png',
|
5316 |
+
'color' => $color,
|
5317 |
+
'ncolor' => $ncolor,
|
5318 |
+
'masked' => $mask,
|
5319 |
+
'isMask' => $is_mask
|
5320 |
+
);
|
5321 |
+
|
5322 |
+
if (isset($transparency)) {
|
5323 |
+
$options['transparency'] = $transparency;
|
5324 |
+
}
|
5325 |
+
|
5326 |
+
$this->o_image($this->numObj, 'new', $options);
|
5327 |
+
$this->imagelist[$file] = array('label' => $label, 'w' => $info['width'], 'h' => $info['height']);
|
5328 |
+
}
|
5329 |
+
|
5330 |
+
if ($is_mask) {
|
5331 |
+
return;
|
5332 |
+
}
|
5333 |
+
|
5334 |
+
if ($w <= 0 && $h <= 0) {
|
5335 |
+
$w = $info['width'];
|
5336 |
+
$h = $info['height'];
|
5337 |
+
}
|
5338 |
+
|
5339 |
+
if ($w <= 0) {
|
5340 |
+
$w = $h / $info['height'] * $info['width'];
|
5341 |
+
}
|
5342 |
+
|
5343 |
+
if ($h <= 0) {
|
5344 |
+
$h = $w * $info['height'] / $info['width'];
|
5345 |
+
}
|
5346 |
+
|
5347 |
+
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ", $w, $h, $x, $y, $label));
|
5348 |
+
}
|
5349 |
+
|
5350 |
+
/**
|
5351 |
+
* add a JPEG image into the document, from a file
|
5352 |
+
*
|
5353 |
+
* @param $img
|
5354 |
+
* @param $x
|
5355 |
+
* @param $y
|
5356 |
+
* @param int $w
|
5357 |
+
* @param int $h
|
5358 |
+
*/
|
5359 |
+
function addJpegFromFile($img, $x, $y, $w = 0, $h = 0)
|
5360 |
+
{
|
5361 |
+
// attempt to add a jpeg image straight from a file, using no GD commands
|
5362 |
+
// note that this function is unable to operate on a remote file.
|
5363 |
+
|
5364 |
+
if (!file_exists($img)) {
|
5365 |
+
return;
|
5366 |
+
}
|
5367 |
+
|
5368 |
+
if ($this->image_iscached($img)) {
|
5369 |
+
$data = null;
|
5370 |
+
$imageWidth = $this->imagelist[$img]['w'];
|
5371 |
+
$imageHeight = $this->imagelist[$img]['h'];
|
5372 |
+
$channels = $this->imagelist[$img]['c'];
|
5373 |
+
} else {
|
5374 |
+
$tmp = getimagesize($img);
|
5375 |
+
$imageWidth = $tmp[0];
|
5376 |
+
$imageHeight = $tmp[1];
|
5377 |
+
|
5378 |
+
if (isset($tmp['channels'])) {
|
5379 |
+
$channels = $tmp['channels'];
|
5380 |
+
} else {
|
5381 |
+
$channels = 3;
|
5382 |
+
}
|
5383 |
+
|
5384 |
+
$data = file_get_contents($img);
|
5385 |
+
}
|
5386 |
+
|
5387 |
+
if ($w <= 0 && $h <= 0) {
|
5388 |
+
$w = $imageWidth;
|
5389 |
+
}
|
5390 |
+
|
5391 |
+
if ($w == 0) {
|
5392 |
+
$w = $h / $imageHeight * $imageWidth;
|
5393 |
+
}
|
5394 |
+
|
5395 |
+
if ($h == 0) {
|
5396 |
+
$h = $w * $imageHeight / $imageWidth;
|
5397 |
+
}
|
5398 |
+
|
5399 |
+
$this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img);
|
5400 |
+
}
|
5401 |
+
|
5402 |
+
/**
|
5403 |
+
* common code used by the two JPEG adding functions
|
5404 |
+
* @param $data
|
5405 |
+
* @param $x
|
5406 |
+
* @param $y
|
5407 |
+
* @param int $w
|
5408 |
+
* @param int $h
|
5409 |
+
* @param $imageWidth
|
5410 |
+
* @param $imageHeight
|
5411 |
+
* @param int $channels
|
5412 |
+
* @param $imgname
|
5413 |
+
*/
|
5414 |
+
private function addJpegImage_common(
|
5415 |
+
&$data,
|
5416 |
+
$x,
|
5417 |
+
$y,
|
5418 |
+
$w = 0,
|
5419 |
+
$h = 0,
|
5420 |
+
$imageWidth,
|
5421 |
+
$imageHeight,
|
5422 |
+
$channels = 3,
|
5423 |
+
$imgname
|
5424 |
+
) {
|
5425 |
+
if ($this->image_iscached($imgname)) {
|
5426 |
+
$label = $this->imagelist[$imgname]['label'];
|
5427 |
+
//debugpng
|
5428 |
+
//if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']';
|
5429 |
+
|
5430 |
+
} else {
|
5431 |
+
if ($data == null) {
|
5432 |
+
$this->addMessage('addJpegImage_common error - (' . $imgname . ') data not present!');
|
5433 |
+
|
5434 |
+
return;
|
5435 |
+
}
|
5436 |
+
|
5437 |
+
// note that this function is not to be called externally
|
5438 |
+
// it is just the common code between the GD and the file options
|
5439 |
+
$this->numImages++;
|
5440 |
+
$im = $this->numImages;
|
5441 |
+
$label = "I$im";
|
5442 |
+
$this->numObj++;
|
5443 |
+
|
5444 |
+
$this->o_image(
|
5445 |
+
$this->numObj,
|
5446 |
+
'new',
|
5447 |
+
array(
|
5448 |
+
'label' => $label,
|
5449 |
+
'data' => &$data,
|
5450 |
+
'iw' => $imageWidth,
|
5451 |
+
'ih' => $imageHeight,
|
5452 |
+
'channels' => $channels
|
5453 |
+
)
|
5454 |
+
);
|
5455 |
+
|
5456 |
+
$this->imagelist[$imgname] = array(
|
5457 |
+
'label' => $label,
|
5458 |
+
'w' => $imageWidth,
|
5459 |
+
'h' => $imageHeight,
|
5460 |
+
'c' => $channels
|
5461 |
+
);
|
5462 |
+
}
|
5463 |
+
|
5464 |
+
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
|
5465 |
+
}
|
5466 |
+
|
5467 |
+
/**
|
5468 |
+
* specify where the document should open when it first starts
|
5469 |
+
*
|
5470 |
+
* @param $style
|
5471 |
+
* @param int $a
|
5472 |
+
* @param int $b
|
5473 |
+
* @param int $c
|
5474 |
+
*/
|
5475 |
+
function openHere($style, $a = 0, $b = 0, $c = 0)
|
5476 |
+
{
|
5477 |
+
// this function will open the document at a specified page, in a specified style
|
5478 |
+
// the values for style, and the required parameters are:
|
5479 |
+
// 'XYZ' left, top, zoom
|
5480 |
+
// 'Fit'
|
5481 |
+
// 'FitH' top
|
5482 |
+
// 'FitV' left
|
5483 |
+
// 'FitR' left,bottom,right
|
5484 |
+
// 'FitB'
|
5485 |
+
// 'FitBH' top
|
5486 |
+
// 'FitBV' left
|
5487 |
+
$this->numObj++;
|
5488 |
+
$this->o_destination(
|
5489 |
+
$this->numObj,
|
5490 |
+
'new',
|
5491 |
+
array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
|
5492 |
+
);
|
5493 |
+
$id = $this->catalogId;
|
5494 |
+
$this->o_catalog($id, 'openHere', $this->numObj);
|
5495 |
+
}
|
5496 |
+
|
5497 |
+
/**
|
5498 |
+
* Add JavaScript code to the PDF document
|
5499 |
+
*
|
5500 |
+
* @param string $code
|
5501 |
+
*/
|
5502 |
+
function addJavascript($code)
|
5503 |
+
{
|
5504 |
+
$this->javascript .= $code;
|
5505 |
+
}
|
5506 |
+
|
5507 |
+
/**
|
5508 |
+
* create a labelled destination within the document
|
5509 |
+
*
|
5510 |
+
* @param $label
|
5511 |
+
* @param $style
|
5512 |
+
* @param int $a
|
5513 |
+
* @param int $b
|
5514 |
+
* @param int $c
|
5515 |
+
*/
|
5516 |
+
function addDestination($label, $style, $a = 0, $b = 0, $c = 0)
|
5517 |
+
{
|
5518 |
+
// associates the given label with the destination, it is done this way so that a destination can be specified after
|
5519 |
+
// it has been linked to
|
5520 |
+
// styles are the same as the 'openHere' function
|
5521 |
+
$this->numObj++;
|
5522 |
+
$this->o_destination(
|
5523 |
+
$this->numObj,
|
5524 |
+
'new',
|
5525 |
+
array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
|
5526 |
+
);
|
5527 |
+
$id = $this->numObj;
|
5528 |
+
|
5529 |
+
// store the label->idf relationship, note that this means that labels can be used only once
|
5530 |
+
$this->destinations["$label"] = $id;
|
5531 |
+
}
|
5532 |
+
|
5533 |
+
/**
|
5534 |
+
* define font families, this is used to initialize the font families for the default fonts
|
5535 |
+
* and for the user to add new ones for their fonts. The default bahavious can be overridden should
|
5536 |
+
* that be desired.
|
5537 |
+
*
|
5538 |
+
* @param $family
|
5539 |
+
* @param string $options
|
5540 |
+
*/
|
5541 |
+
function setFontFamily($family, $options = '')
|
5542 |
+
{
|
5543 |
+
if (!is_array($options)) {
|
5544 |
+
if ($family === 'init') {
|
5545 |
+
// set the known family groups
|
5546 |
+
// these font families will be used to enable bold and italic markers to be included
|
5547 |
+
// within text streams. html forms will be used... <b></b> <i></i>
|
5548 |
+
$this->fontFamilies['Helvetica.afm'] =
|
5549 |
+
array(
|
5550 |
+
'b' => 'Helvetica-Bold.afm',
|
5551 |
+
'i' => 'Helvetica-Oblique.afm',
|
5552 |
+
'bi' => 'Helvetica-BoldOblique.afm',
|
5553 |
+
'ib' => 'Helvetica-BoldOblique.afm'
|
5554 |
+
);
|
5555 |
+
|
5556 |
+
$this->fontFamilies['Courier.afm'] =
|
5557 |
+
array(
|
5558 |
+
'b' => 'Courier-Bold.afm',
|
5559 |
+
'i' => 'Courier-Oblique.afm',
|
5560 |
+
'bi' => 'Courier-BoldOblique.afm',
|
5561 |
+
'ib' => 'Courier-BoldOblique.afm'
|
5562 |
+
);
|
5563 |
+
|
5564 |
+
$this->fontFamilies['Times-Roman.afm'] =
|
5565 |
+
array(
|
5566 |
+
'b' => 'Times-Bold.afm',
|
5567 |
+
'i' => 'Times-Italic.afm',
|
5568 |
+
'bi' => 'Times-BoldItalic.afm',
|
5569 |
+
'ib' => 'Times-BoldItalic.afm'
|
5570 |
+
);
|
5571 |
+
}
|
5572 |
+
} else {
|
5573 |
+
|
5574 |
+
// the user is trying to set a font family
|
5575 |
+
// note that this can also be used to set the base ones to something else
|
5576 |
+
if (mb_strlen($family)) {
|
5577 |
+
$this->fontFamilies[$family] = $options;
|
5578 |
+
}
|
5579 |
+
}
|
5580 |
+
}
|
5581 |
+
|
5582 |
+
/**
|
5583 |
+
* used to add messages for use in debugging
|
5584 |
+
*
|
5585 |
+
* @param $message
|
5586 |
+
*/
|
5587 |
+
function addMessage($message)
|
5588 |
+
{
|
5589 |
+
$this->messages .= $message . "\n";
|
5590 |
+
}
|
5591 |
+
|
5592 |
+
/**
|
5593 |
+
* a few functions which should allow the document to be treated transactionally.
|
5594 |
+
*
|
5595 |
+
* @param $action
|
5596 |
+
*/
|
5597 |
+
function transaction($action)
|
5598 |
+
{
|
5599 |
+
switch ($action) {
|
5600 |
+
case 'start':
|
5601 |
+
// store all the data away into the checkpoint variable
|
5602 |
+
$data = get_object_vars($this);
|
5603 |
+
$this->checkpoint = $data;
|
5604 |
+
unset($data);
|
5605 |
+
break;
|
5606 |
+
|
5607 |
+
case 'commit':
|
5608 |
+
if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) {
|
5609 |
+
$tmp = $this->checkpoint['checkpoint'];
|
5610 |
+
$this->checkpoint = $tmp;
|
5611 |
+
unset($tmp);
|
5612 |
+
} else {
|
5613 |
+
$this->checkpoint = '';
|
5614 |
+
}
|
5615 |
+
break;
|
5616 |
+
|
5617 |
+
case 'rewind':
|
5618 |
+
// do not destroy the current checkpoint, but move us back to the state then, so that we can try again
|
5619 |
+
if (is_array($this->checkpoint)) {
|
5620 |
+
// can only abort if were inside a checkpoint
|
5621 |
+
$tmp = $this->checkpoint;
|
5622 |
+
|
5623 |
+
foreach ($tmp as $k => $v) {
|
5624 |
+
if ($k !== 'checkpoint') {
|
5625 |
+
$this->$k = $v;
|
5626 |
+
}
|
5627 |
+
}
|
5628 |
+
unset($tmp);
|
5629 |
+
}
|
5630 |
+
break;
|
5631 |
+
|
5632 |
+
case 'abort':
|
5633 |
+
if (is_array($this->checkpoint)) {
|
5634 |
+
// can only abort if were inside a checkpoint
|
5635 |
+
$tmp = $this->checkpoint;
|
5636 |
+
foreach ($tmp as $k => $v) {
|
5637 |
+
$this->$k = $v;
|
5638 |
+
}
|
5639 |
+
unset($tmp);
|
5640 |
+
}
|
5641 |
+
break;
|
5642 |
+
}
|
5643 |
+
}
|
5644 |
+
}
|
woocommerce-pdf-invoices-packingslips.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
* Plugin Name: WooCommerce PDF Invoices & Packing Slips
|
4 |
* Plugin URI: http://www.wpovernight.com
|
5 |
* Description: Create, print & email PDF invoices & packing slips for WooCommerce orders.
|
6 |
-
* Version: 2.5.
|
7 |
* Author: Ewout Fernhout
|
8 |
* Author URI: http://www.wpovernight.com
|
9 |
* License: GPLv2 or later
|
@@ -21,7 +21,7 @@ if ( !class_exists( 'WPO_WCPDF' ) ) :
|
|
21 |
|
22 |
class WPO_WCPDF {
|
23 |
|
24 |
-
public $version = '2.5.
|
25 |
public $plugin_basename;
|
26 |
public $legacy_mode;
|
27 |
|
3 |
* Plugin Name: WooCommerce PDF Invoices & Packing Slips
|
4 |
* Plugin URI: http://www.wpovernight.com
|
5 |
* Description: Create, print & email PDF invoices & packing slips for WooCommerce orders.
|
6 |
+
* Version: 2.5.2
|
7 |
* Author: Ewout Fernhout
|
8 |
* Author URI: http://www.wpovernight.com
|
9 |
* License: GPLv2 or later
|
21 |
|
22 |
class WPO_WCPDF {
|
23 |
|
24 |
+
public $version = '2.5.2';
|
25 |
public $plugin_basename;
|
26 |
public $legacy_mode;
|
27 |
|