Advanced Order Export For WooCommerce - Version 3.0.2

Version Description

  • 2019-08-20 =
  • Added "Summary by customers" report
  • Format PDF supports UTF8 chars
  • Added filter "Exclude products" to section "Filter by product"
  • New tab "Other items" (in section "Setup fields") allows to export tax/fee/shipping
  • Fixed bug - XLS export stops at wrong dates
  • Fixed bug - button "ESC" doesn't abort export (Safari only)
Download this release

Release Info

Developer algol.plus
Plugin Icon 128x128 Advanced Order Export For WooCommerce
Version 3.0.2
Comparing to
See all releases

Code changes from version 3.0.1 to 3.0.2

Files changed (42) hide show
  1. assets/css/export.css +19 -0
  2. assets/js/buttons.js +36 -32
  3. assets/js/export-fields.js +287 -10
  4. assets/js/filters.js +11 -2
  5. assets/js/settings-form.js +3 -1
  6. classes/FPDF/class-woe-fpdf-tt-font-file.php +1458 -0
  7. classes/FPDF/class-woe-fpdf.php +3046 -1807
  8. classes/FPDF/class-woe-pdf-mc-table.php +187 -54
  9. classes/FPDF/exception/class-woe-fpdf-exception.php +53 -0
  10. classes/FPDF/font/OpenSans-Bold.ttf +0 -0
  11. classes/FPDF/font/OpenSans-Regular.ttf +0 -0
  12. classes/FPDF/font/courier.php +0 -10
  13. classes/FPDF/font/courierb.php +0 -10
  14. classes/FPDF/font/courierbi.php +0 -10
  15. classes/FPDF/font/courieri.php +0 -10
  16. classes/FPDF/font/helvetica.php +0 -21
  17. classes/FPDF/font/helveticab.php +0 -21
  18. classes/FPDF/font/helveticabi.php +0 -21
  19. classes/FPDF/font/helveticai.php +0 -21
  20. classes/FPDF/font/symbol.php +0 -20
  21. classes/FPDF/font/times.php +0 -21
  22. classes/FPDF/font/timesb.php +0 -21
  23. classes/FPDF/font/timesbi.php +0 -21
  24. classes/FPDF/font/timesi.php +0 -21
  25. classes/FPDF/font/zapfdingbats.php +0 -20
  26. classes/PHPExcel/Shared/PCLZip/pclzip.lib.php +3 -3
  27. classes/admin/class-wc-order-export-labels.php +1 -2
  28. classes/admin/class-wc-order-export-manage.php +24 -15
  29. classes/admin/class-wc-order-export-settings.php +6 -0
  30. classes/admin/tabs/ajax/trait-wc-order-export-admin-tab-abstract-ajax-filters.php +27 -0
  31. classes/class-wc-order-export-admin.php +11 -4
  32. classes/core/class-wc-order-export-data-extractor-ui.php +49 -30
  33. classes/core/class-wc-order-export-data-extractor.php +159 -8
  34. classes/core/class-wc-order-export-engine.php +1 -1
  35. classes/formats/abstract-class-woe-formatter-plain-format.php +144 -12
  36. classes/formats/class-woe-formatter-pdf.php +11 -2
  37. classes/formats/class-woe-formatter-xls.php +5 -2
  38. classes/formats/trait-woe-plain-format.php +1 -1
  39. i18n/languages/woo-order-export-lite.pot +969 -906
  40. readme.txt +9 -1
  41. view/settings-form.php +155 -19
  42. woo-order-export-lite.php +3 -3
assets/css/export.css CHANGED
@@ -748,4 +748,23 @@ input.date {
748
749
.default-html-css-label {
750
display: block;
751
}
748
749
.default-html-css-label {
750
display: block;
751
+ }
752
+
753
+ #manage_fields.summary-customers-report .fields-control-block,
754
+ #manage_fields.summary-customers-report #order_fields .mapping_row,
755
+ #manage_fields.summary-customers-report #unselected_fields .subsubsub .block-segment-choice,
756
+ #manage_fields.summary-customers-report #unselected_fields .subsubsub .block-segment-choice[data-segment="shipping"] .divider {
757
+ display: none;
758
+ }
759
+
760
+ #manage_fields.summary-customers-report #order_fields .mapping_row.segment_user,
761
+ #manage_fields.summary-customers-report #order_fields .mapping_row.segment_billing,
762
+ #manage_fields.summary-customers-report #order_fields .mapping_row.segment_shipping {
763
+ display: block;
764
+ }
765
+
766
+ #manage_fields.summary-customers-report #unselected_fields .subsubsub .block-segment-choice[data-segment="user"],
767
+ #manage_fields.summary-customers-report #unselected_fields .subsubsub .block-segment-choice[data-segment="billing"],
768
+ #manage_fields.summary-customers-report #unselected_fields .subsubsub .block-segment-choice[data-segment="shipping"] {
769
+ display: inline;
770
}
assets/js/buttons.js CHANGED
@@ -102,38 +102,42 @@ function woe_waiting_dialog() {
102
103
jQuery( "#background" ).addClass( "loading" );
104
105
- jQuery( '#wpbody-content' ).keydown( function ( event ) {
106
- if ( event.keyCode == 27 ) {
107
- if ( ! window.cancelling ) {
108
- event.preventDefault();
109
- window.cancelling = true;
110
-
111
- jQuery.ajax( {
112
- type: "post",
113
- data: {
114
- action: 'order_exporter',
115
- method: 'cancel_export',
116
- file_id: window.file_id,
117
- },
118
- cache: false,
119
- url: ajaxurl,
120
- dataType: "json",
121
- error: function ( xhr, status, error ) {
122
- alert( xhr.responseText );
123
- woe_export_progress( 100, jQuery( '#progressBar' ) );
124
- },
125
- success: function ( response ) {
126
- woe_export_progress( 100, jQuery( '#progressBar' ) );
127
- }
128
- } );
129
-
130
- window.count = 0;
131
- window.file_id = '';
132
- jQuery( '#wpbody-content' ).off( 'keydown' );
133
- }
134
- return false;
135
- }
136
- } );
137
}
138
139
function woe_export_progress( percent, $element ) {
102
103
jQuery( "#background" ).addClass( "loading" );
104
105
+ jQuery( document ).on ('keydown', stop_export );
106
+ }
107
+
108
+ function stop_export(event) {
109
+ if ( event.keyCode == 27 ) {
110
+ if ( ! window.cancelling ) {
111
+ event.preventDefault();
112
+ window.cancelling = true;
113
+
114
+ jQuery.ajax( {
115
+ type: "post",
116
+ data: {
117
+ action: 'order_exporter',
118
+ method: 'cancel_export',
119
+ file_id: window.file_id,
120
+ woe_nonce: settings_form.woe_nonce,
121
+ tab: settings_form.woe_active_tab,
122
+ },
123
+ cache: false,
124
+ url: ajaxurl,
125
+ dataType: "json",
126
+ error: function ( xhr, status, error ) {
127
+ alert( xhr.responseText );
128
+ woe_export_progress( 100, jQuery( '#progressBar' ) );
129
+ },
130
+ success: function ( response ) {
131
+ woe_export_progress( 100, jQuery( '#progressBar' ) );
132
+ }
133
+ } );
134
+
135
+ window.count = 0;
136
+ window.file_id = '';
137
+ jQuery( document ).off( 'keydown', stop_export );
138
+ }
139
+ return false;
140
+ }
141
}
142
143
function woe_export_progress( percent, $element ) {
assets/js/export-fields.js CHANGED
@@ -506,7 +506,10 @@ function woe_activate_draggable_field( el, segment, format ) {
506
woe_moving_products_and_coupons_group_blocks_to_first_item( format );
507
508
// change static field key index to prevent fields with identical keys
509
- var tmp_prefix = ['products', 'coupons'].indexOf( segment ) === - 1 ? '' : 'plain_' + segment + '_';
510
if ( jQuery( ui.helper[0] ).find( 'input[value*="' + tmp_prefix + 'static_field"]' ).length > 0 ) {
511
var suffix = 0;
512
jQuery( '#order_fields input[value*="' + tmp_prefix + 'static_field_"]' ).each( function () {
@@ -524,10 +527,13 @@ function woe_activate_draggable_field( el, segment, format ) {
524
}
525
} );
526
527
- var field_key = tmp_prefix + 'static_field_' + (
528
- suffix + 1
529
- );
530
- jQuery( ui.helper[0] ).find( 'input[name="orders[][key]"]' ).first().val( field_key );
531
}
532
// end change static field key
533
@@ -935,9 +941,14 @@ function woe_add_custom_meta( to, index_p, format, label, colname, segment, form
935
}
936
937
function woe_reset_field_contorls() {
938
- jQuery( '.tab-actions-forms' )
939
- .find( 'input,select' )
940
- .val( '' );
941
}
942
943
function woe_escape_str( str ) {
@@ -972,6 +983,16 @@ jQuery( document ).ready( function ( $ ) {
972
973
$( '.segment_choice' ).click( function () {
974
975
$( '.segment_choice' ).removeClass( 'active' );
976
$( this ).addClass( 'active' );
977
@@ -980,11 +1001,11 @@ jQuery( document ).ready( function ( $ ) {
980
981
window.location.href = $( this ).attr( 'href' );
982
983
- jQuery( '.tab-actions-forms .segment-form' ).removeClass( 'active' ).find( 'input,select' ).val( '' );
984
} );
985
986
setTimeout( function () {
987
- if ( summary_mode ) {
988
$( '.segment_choice[href="products"]' ).click()
989
} else if ( window.location.hash.indexOf( 'segment' ) !== - 1 ) {
990
$( '.segment_choice[href="' + window.location.hash + '"]' ).click()
@@ -1080,6 +1101,12 @@ jQuery( document ).ready( function ( $ ) {
1080
.removeClass( 'active' )
1081
.find( 'input,select' ).val( '' );
1082
1083
return false;
1084
} );
1085
@@ -1172,6 +1199,183 @@ jQuery( document ).ready( function ( $ ) {
1172
return false;
1173
});
1174
1175
///*CUSTOM FIELDS BINDS
1176
1177
jQuery( 'input[name=custom_meta_order_mode]' ).change( function () {
@@ -1331,6 +1535,8 @@ jQuery( document ).ready( function ( $ ) {
1331
1332
jQuery( '#manage_fields' ).toggleClass( 'summary-products-report', ! ! jQuery( this ).prop( 'checked' ) );
1333
1334
$( '#unselected_fields .segment_choice' ).removeClass( 'active' );
1335
$( '#unselected_fields_list .settings-segment' ).removeClass( 'active' );
1336
@@ -1400,8 +1606,79 @@ jQuery( document ).ready( function ( $ ) {
1400
1401
} );
1402
1403
setTimeout( function () {
1404
jQuery( "#summary_report_by_products_checkbox" ).trigger( 'change', 'onstart' );
1405
}, 1 )
1406
1407
} );
506
woe_moving_products_and_coupons_group_blocks_to_first_item( format );
507
508
// change static field key index to prevent fields with identical keys
509
+ var tmp_prefix = '';
510
+ if ( woe_is_flat_format( format ) && ['products', 'coupons'].indexOf( segment ) > - 1 ) {
511
+ tmp_prefix = 'plain_' + segment + '_';
512
+ }
513
if ( jQuery( ui.helper[0] ).find( 'input[value*="' + tmp_prefix + 'static_field"]' ).length > 0 ) {
514
var suffix = 0;
515
jQuery( '#order_fields input[value*="' + tmp_prefix + 'static_field_"]' ).each( function () {
527
}
528
} );
529
530
+ var field_key = tmp_prefix + 'static_field_' + (suffix + 1);
531
+
532
+ if ( ! woe_is_flat_format( format ) && ['products', 'coupons'].indexOf( segment ) > - 1 ) {
533
+ jQuery( ui.helper[0] ).find( 'input[name="' + segment + '[][key]"]' ).first().val( field_key );
534
+ } else {
535
+ jQuery( ui.helper[0] ).find( 'input[name="orders[][key]"]' ).first().val( field_key );
536
+ }
537
}
538
// end change static field key
539
941
}
942
943
function woe_reset_field_contorls() {
944
+
945
+ jQuery( '.tab-actions-forms .segment-form' ).removeClass( 'active' ).find( 'input,select' ).val( '' )
946
+
947
+ jQuery( '.tab-actions-forms .segment-form select' ).each(function () {
948
+ if (jQuery(this).find('option[selected]')) {
949
+ jQuery(this).val(jQuery(this).find('option[selected]').attr('value'));
950
+ }
951
+ });
952
}
953
954
function woe_escape_str( str ) {
983
984
$( '.segment_choice' ).click( function () {
985
986
+ var segment = $( this ).data( 'segment' );
987
+
988
+ $('.tab-actions-buttons').hide();
989
+
990
+ if ($('.tab-actions-buttons.' + segment + '-actions-buttons').length) {
991
+ $('.tab-actions-buttons.' + segment + '-actions-buttons').show();
992
+ } else {
993
+ $('.tab-actions-buttons.default-actions').show();
994
+ }
995
+
996
$( '.segment_choice' ).removeClass( 'active' );
997
$( this ).addClass( 'active' );
998
1001
1002
window.location.href = $( this ).attr( 'href' );
1003
1004
+ woe_reset_field_contorls();
1005
} );
1006
1007
setTimeout( function () {
1008
+ if ( summary_mode_by_products ) {
1009
$( '.segment_choice[href="products"]' ).click()
1010
} else if ( window.location.hash.indexOf( 'segment' ) !== - 1 ) {
1011
$( '.segment_choice[href="' + window.location.hash + '"]' ).click()
1101
.removeClass( 'active' )
1102
.find( 'input,select' ).val( '' );
1103
1104
+ jQuery( this ).closest( '.segment-form' ).find( 'select' ).each(function () {
1105
+ if (jQuery(this).find('option[selected]')) {
1106
+ jQuery(this).val(jQuery(this).find('option[selected]').attr('value'));
1107
+ }
1108
+ });
1109
+
1110
return false;
1111
} );
1112
1199
return false;
1200
});
1201
1202
+ jQuery( '.tab-controls .other_items-actions-buttons .add-fee' ).on( 'click', function () {
1203
+ jQuery( '.tab-actions-forms .segment-form' ).removeClass( 'active' );
1204
+ jQuery( '.tab-actions-forms .segment-form.other-items-add-fee-form' ).addClass( 'active' );
1205
+ return false;
1206
+ } );
1207
+
1208
+ jQuery( '.other-items-add-fee-form #button_other_items_add_fee_field' ).on( 'click', function () {
1209
+
1210
+ var segment = jQuery( '.segment_choice.active' ).attr( 'data-segment' );
1211
+
1212
+ var label = jQuery( '#select_fee_items' ).val();
1213
+ var colname = jQuery( '#colname_fee_item_other_items' ).val();
1214
+
1215
+ if ( ! label ) {
1216
+ alert( export_messages.empty_meta_key );
1217
+ jQuery( '#select_fee_items' ).focus();
1218
+ return false
1219
+ }
1220
+
1221
+ if ( ! colname ) {
1222
+ alert( export_messages.empty_column_name );
1223
+ jQuery( '#colname_fee_item_other_items' ).focus();
1224
+ return false
1225
+ }
1226
+
1227
+ var format_field = jQuery( '#format_fee_item_other_items' ).val();
1228
+
1229
+ woe_add_custom_meta(jQuery("#" + segment + '_unselected_segment'), 'orders', output_format, label, colname, segment, format_field);
1230
+
1231
+ jQuery( this ).siblings( '.button-cancel' ).trigger( 'click' );
1232
+
1233
+ return false;
1234
+ } );
1235
+
1236
+ function load_order_fee_items() {
1237
+
1238
+ jQuery.post(
1239
+ ajaxurl,
1240
+ {
1241
+ action: 'order_exporter',
1242
+ method: 'get_used_order_fee_items',
1243
+ woe_nonce: settings_form.woe_nonce,
1244
+ tab: settings_form.woe_active_tab,
1245
+ },
1246
+ function ( response ) {
1247
+ if ( response ) {
1248
+ var options = '';
1249
+ jQuery.each( response, function ( index, value ) {
1250
+ options += '<option value="' + woe_escape_str( value ) + '">' + value.replace('FEE_', '') + '</option>';
1251
+ } );
1252
+ jQuery( '#select_fee_items' ).html( options );
1253
+ }
1254
+ },
1255
+ 'json'
1256
+ );
1257
+ }
1258
+
1259
+ load_order_fee_items();
1260
+
1261
+ jQuery( '.tab-controls .other_items-actions-buttons .add-shipping' ).on( 'click', function () {
1262
+ jQuery( '.tab-actions-forms .segment-form' ).removeClass( 'active' );
1263
+ jQuery( '.tab-actions-forms .segment-form.other-items-add-shipping-form' ).addClass( 'active' );
1264
+ return false;
1265
+ } );
1266
+
1267
+ jQuery( '.other-items-add-shipping-form #button_other_items_add_shipping_field' ).on( 'click', function () {
1268
+
1269
+ var segment = jQuery( '.segment_choice.active' ).attr( 'data-segment' );
1270
+
1271
+ var label = jQuery( '#select_shipping_items' ).val();
1272
+ var colname = jQuery( '#colname_shipping_item_other_items' ).val();
1273
+
1274
+ if ( ! label ) {
1275
+ alert( export_messages.empty_meta_key );
1276
+ jQuery( '#select_shipping_items' ).focus();
1277
+ return false
1278
+ }
1279
+
1280
+ if ( ! colname ) {
1281
+ alert( export_messages.empty_column_name );
1282
+ jQuery( '#colname_shipping_item_other_items' ).focus();
1283
+ return false
1284
+ }
1285
+
1286
+ var format_field = jQuery( '#format_shipping_item_other_items' ).val();
1287
+
1288
+ woe_add_custom_meta(jQuery("#" + segment + '_unselected_segment'), 'orders', output_format, label, colname, segment, format_field);
1289
+
1290
+ jQuery( this ).siblings( '.button-cancel' ).trigger( 'click' );
1291
+
1292
+ return false;
1293
+ } );
1294
+
1295
+ function load_order_shipping_items() {
1296
+
1297
+ jQuery.post(
1298
+ ajaxurl,
1299
+ {
1300
+ action: 'order_exporter',
1301
+ method: 'get_used_order_shipping_items',
1302
+ woe_nonce: settings_form.woe_nonce,
1303
+ tab: settings_form.woe_active_tab,
1304
+ },
1305
+ function ( response ) {
1306
+ if ( response ) {
1307
+ var options = '';
1308
+ jQuery.each( response, function ( index, value ) {
1309
+ options += '<option value="' + woe_escape_str( value ) + '">' + value.replace('SHIPPING_', '') + '</option>';
1310
+ } );
1311
+ jQuery( '#select_shipping_items' ).html( options );
1312
+ }
1313
+ },
1314
+ 'json'
1315
+ );
1316
+ }
1317
+
1318
+ load_order_shipping_items();
1319
+
1320
+ jQuery( '.tab-controls .other_items-actions-buttons .add-tax' ).on( 'click', function () {
1321
+ jQuery( '.tab-actions-forms .segment-form' ).removeClass( 'active' );
1322
+ jQuery( '.tab-actions-forms .segment-form.other-items-add-tax-form' ).addClass( 'active' );
1323
+ return false;
1324
+ } );
1325
+
1326
+ jQuery( '.other-items-add-tax-form #button_other_items_add_tax_field' ).on( 'click', function () {
1327
+
1328
+ var segment = jQuery( '.segment_choice.active' ).attr( 'data-segment' );
1329
+
1330
+ var label = jQuery( '#select_tax_items' ).val();
1331
+ var colname = jQuery( '#colname_tax_item_other_items' ).val();
1332
+
1333
+ if ( ! label ) {
1334
+ alert( export_messages.empty_meta_key );
1335
+ jQuery( '#select_tax_items' ).focus();
1336
+ return false
1337
+ }
1338
+
1339
+ if ( ! colname ) {
1340
+ alert( export_messages.empty_column_name );
1341
+ jQuery( '#colname_tax_item_other_items' ).focus();
1342
+ return false
1343
+ }
1344
+
1345
+ var format_field = jQuery( '#format_tax_item_other_items' ).val();
1346
+
1347
+ woe_add_custom_meta(jQuery("#" + segment + '_unselected_segment'), 'orders', output_format, label, colname, segment, format_field);
1348
+
1349
+ jQuery( this ).siblings( '.button-cancel' ).trigger( 'click' );
1350
+
1351
+ return false;
1352
+ } );
1353
+
1354
+ function load_order_tax_items() {
1355
+
1356
+ jQuery.post(
1357
+ ajaxurl,
1358
+ {
1359
+ action: 'order_exporter',
1360
+ method: 'get_used_order_tax_items',
1361
+ woe_nonce: settings_form.woe_nonce,
1362
+ tab: settings_form.woe_active_tab,
1363
+ },
1364
+ function ( response ) {
1365
+ if ( response ) {
1366
+ var options = '';
1367
+ jQuery.each( response, function ( index, value ) {
1368
+ options += '<option value="' + woe_escape_str( value ) + '">' + value.replace('TAX_', '') + '</option>';
1369
+ } );
1370
+ jQuery( '#select_tax_items' ).html( options );
1371
+ }
1372
+ },
1373
+ 'json'
1374
+ );
1375
+ }
1376
+
1377
+ load_order_tax_items();
1378
+
1379
///*CUSTOM FIELDS BINDS
1380
1381
jQuery( 'input[name=custom_meta_order_mode]' ).change( function () {
1535
1536
jQuery( '#manage_fields' ).toggleClass( 'summary-products-report', ! ! jQuery( this ).prop( 'checked' ) );
1537
1538
+ $('#summary_report_by_customers_checkbox').prop( 'disabled', !!jQuery( this ).prop( 'checked' ) );
1539
+
1540
$( '#unselected_fields .segment_choice' ).removeClass( 'active' );
1541
$( '#unselected_fields_list .settings-segment' ).removeClass( 'active' );
1542
1606
1607
} );
1608
1609
+ //logic for setup link
1610
+ jQuery( "#summary_report_by_customers_checkbox" ).change( function ( e, action ) {
1611
+
1612
+ var summary_report_fields = [];
1613
+
1614
+ summary_report_fields.push( $( '#user_unselected_segment input[value="summary_report_total_count"]' ).parents( 'li' ) );
1615
+ summary_report_fields.push( $( '#user_unselected_segment input[value="summary_report_total_amount"]' ).parents( 'li' ) );
1616
+ summary_report_fields.push( $( '#user_unselected_segment input[value="summary_report_total_amount_paid"]' ).parents( 'li' ) );
1617
+ summary_report_fields.push( $( '#user_unselected_segment input[value="summary_report_total_refund_count"]' ).parents( 'li' ) );
1618
+ summary_report_fields.push( $( '#user_unselected_segment input[value="summary_report_total_refund_amount"]' ).parents( 'li' ) );
1619
+
1620
+ jQuery( '#manage_fields' ).toggleClass( 'summary-customers-report', ! ! jQuery( this ).prop( 'checked' ) );
1621
+
1622
+ $('#summary_report_by_products_checkbox').prop( 'disabled', !!jQuery( this ).prop( 'checked' ) );
1623
+
1624
+ $( '#unselected_fields .segment_choice' ).removeClass( 'active' );
1625
+ $( '#unselected_fields_list .settings-segment' ).removeClass( 'active' );
1626
+
1627
+ if ( jQuery( this ).prop( 'checked' ) ) {
1628
+
1629
+ var segment = 'user';
1630
+
1631
+ if ( 'onstart' !== action ) {
1632
+ // purge summary report fields before insert
1633
+ $( '#order_fields .segment_user input[value="summary_report_total_count"]' ).closest( '.mapping_row' ).remove();
1634
+ $( '#order_fields .segment_user input[value="summary_report_total_amount"]' ).closest( '.mapping_row' ).remove();
1635
+ $( '#order_fields .segment_user input[value="summary_report_total_amount_paid"]' ).closest( '.mapping_row' ).remove();
1636
+ $( '#order_fields .segment_user input[value="summary_report_total_refund_count"]' ).closest( '.mapping_row' ).remove();
1637
+ $( '#order_fields .segment_user input[value="summary_report_total_refund_amount"]' ).closest( '.mapping_row' ).remove();
1638
+
1639
+ // insert summary report fields
1640
+ jQuery.each( summary_report_fields, function ( i, value ) {
1641
+ $( value ).show();
1642
+ var $field_to_copy = $( value ).clone();
1643
+ $field_to_copy
1644
+ .attr( 'style', '' )
1645
+ .addClass( 'ui-draggabled' )
1646
+ .removeClass( 'segment_field' )
1647
+ .find( 'input' ).prop( 'disabled', false );
1648
+
1649
+ jQuery( '#manage_fields #order_fields' ).append( $field_to_copy );
1650
+ } );
1651
+ }
1652
+
1653
+ $( '#unselected_fields .segment_choice[data-segment="' + segment + '"]' ).addClass( 'active' );
1654
+ $( '#unselected_fields_list .settings-segment#' + segment + '_unselected_segment' ).addClass( 'active' );
1655
+
1656
+ } else {
1657
+ var segment = window.location.hash.replace( '#segment=', '' );
1658
+
1659
+ // purge summary report fields
1660
+ $( '#order_fields .segment_user input[value="summary_report_total_count"]' ).closest( '.mapping_row' ).remove();
1661
+ $( '#order_fields .segment_user input[value="summary_report_total_amount"]' ).closest( '.mapping_row' ).remove();
1662
+ $( '#order_fields .segment_user input[value="summary_report_total_amount_paid"]' ).closest( '.mapping_row' ).remove();
1663
+ $( '#order_fields .segment_user input[value="summary_report_total_refund_count"]' ).closest( '.mapping_row' ).remove();
1664
+ $( '#order_fields .segment_user input[value="summary_report_total_refund_amount"]' ).closest( '.mapping_row' ).remove();
1665
+
1666
+ jQuery.each( summary_report_fields, function ( i, value ) {
1667
+ $( value ).hide();
1668
+ } );
1669
+ }
1670
+
1671
+ $( '#unselected_fields .segment_choice[data-segment="' + segment + '"]' ).addClass( 'active' );
1672
+ $( '#unselected_fields_list .settings-segment#' + segment + '_unselected_segment' ).addClass( 'active' );
1673
+
1674
+ } );
1675
+
1676
setTimeout( function () {
1677
jQuery( "#summary_report_by_products_checkbox" ).trigger( 'change', 'onstart' );
1678
}, 1 )
1679
1680
+ setTimeout( function () {
1681
+ jQuery( "#summary_report_by_customers_checkbox" ).trigger( 'change', 'onstart' );
1682
+ }, 1 )
1683
+
1684
} );
assets/js/filters.js CHANGED
@@ -27,6 +27,7 @@ jQuery( document ).ready( function ( $ ) {
27
action: "order_exporter",
28
woe_nonce: settings_form.woe_nonce,
29
tab: settings_form.woe_active_tab,
30
};
31
32
var val_op = jQuery( '#attributes_compare' ).val();
@@ -119,6 +120,7 @@ jQuery( document ).ready( function ( $ ) {
119
action: "order_exporter",
120
woe_nonce: settings_form.woe_nonce,
121
tab: settings_form.woe_active_tab,
122
};
123
124
var val_op = jQuery( '#itemmeta_compare' ).val();
@@ -237,6 +239,7 @@ jQuery( document ).ready( function ( $ ) {
237
action: "order_exporter",
238
woe_nonce: settings_form.woe_nonce,
239
tab: settings_form.woe_active_tab,
240
};
241
242
var val_op = jQuery( '#taxonomies_compare' ).val();
@@ -328,6 +331,7 @@ jQuery( document ).ready( function ( $ ) {
328
action: "order_exporter",
329
woe_nonce: settings_form.woe_nonce,
330
tab: settings_form.woe_active_tab,
331
};
332
333
var val_op = jQuery( '#product_custom_fields_compare' ).val();
@@ -419,6 +423,7 @@ jQuery( document ).ready( function ( $ ) {
419
action: "order_exporter",
420
woe_nonce: settings_form.woe_nonce,
421
tab: settings_form.woe_active_tab,
422
};
423
424
jQuery.post( ajaxurl, data, function ( response ) {
@@ -480,6 +485,7 @@ jQuery( document ).ready( function ( $ ) {
480
action: "order_exporter",
481
woe_nonce: settings_form.woe_nonce,
482
tab: settings_form.woe_active_tab,
483
};
484
485
jQuery.post( ajaxurl, data, function ( response ) {
@@ -542,6 +548,7 @@ jQuery( document ).ready( function ( $ ) {
542
action: "order_exporter",
543
woe_nonce: settings_form.woe_nonce,
544
tab: settings_form.woe_active_tab,
545
};
546
547
jQuery.post( ajaxurl, data, function ( response ) {
@@ -612,6 +619,7 @@ jQuery( document ).ready( function ( $ ) {
612
action: "order_exporter",
613
woe_nonce: settings_form.woe_nonce,
614
tab: settings_form.woe_active_tab,
615
};
616
617
jQuery.post( ajaxurl, data, function ( response ) {
@@ -642,8 +650,7 @@ jQuery( document ).ready( function ( $ ) {
642
} ).trigger( 'change' );
643
644
jQuery( '#add_item_metadata' ).click( function () {
645
-
646
- var val = jQuery( "#text_item_metadata" ).val();
647
var val2 = jQuery( '#item_metadata' ).val();
648
var val_op = jQuery( '#item_metadata_compare' ).val();
649
if ( val != null && val2 != null && val.length && val2.length ) {
@@ -681,6 +688,7 @@ jQuery( document ).ready( function ( $ ) {
681
action: "order_exporter",
682
woe_nonce: settings_form.woe_nonce,
683
tab: settings_form.woe_active_tab,
684
};
685
var val_op = jQuery( '#select_user_custom_fields' ).val();
686
jQuery( '#text_user_custom_fields' ).val( '' );
@@ -778,6 +786,7 @@ jQuery( document ).ready( function ( $ ) {
778
action: "order_exporter",
779
woe_nonce: settings_form.woe_nonce,
780
tab: settings_form.woe_active_tab,
781
};
782
783
var val_op = jQuery( '#custom_fields_compare' ).val();
27
action: "order_exporter",
28
woe_nonce: settings_form.woe_nonce,
29
tab: settings_form.woe_active_tab,
30
+ woe_order_post_type: woe_order_post_type,
31
};
32
33
var val_op = jQuery( '#attributes_compare' ).val();
120
action: "order_exporter",
121
woe_nonce: settings_form.woe_nonce,
122
tab: settings_form.woe_active_tab,
123
+ woe_order_post_type: woe_order_post_type,
124
};
125
126
var val_op = jQuery( '#itemmeta_compare' ).val();
239
action: "order_exporter",
240
woe_nonce: settings_form.woe_nonce,
241
tab: settings_form.woe_active_tab,
242
+ woe_order_post_type: woe_order_post_type,
243
};
244
245
var val_op = jQuery( '#taxonomies_compare' ).val();
331
action: "order_exporter",
332
woe_nonce: settings_form.woe_nonce,
333
tab: settings_form.woe_active_tab,
334
+ woe_order_post_type: woe_order_post_type,
335
};
336
337
var val_op = jQuery( '#product_custom_fields_compare' ).val();
423
action: "order_exporter",
424
woe_nonce: settings_form.woe_nonce,
425
tab: settings_form.woe_active_tab,
426
+ woe_order_post_type: woe_order_post_type,
427
};
428
429
jQuery.post( ajaxurl, data, function ( response ) {
485
action: "order_exporter",
486
woe_nonce: settings_form.woe_nonce,
487
tab: settings_form.woe_active_tab,
488
+ woe_order_post_type: woe_order_post_type,
489
};
490
491
jQuery.post( ajaxurl, data, function ( response ) {
548
action: "order_exporter",
549
woe_nonce: settings_form.woe_nonce,
550
tab: settings_form.woe_active_tab,
551
+ woe_order_post_type: woe_order_post_type,
552
};
553
554
jQuery.post( ajaxurl, data, function ( response ) {
619
action: "order_exporter",
620
woe_nonce: settings_form.woe_nonce,
621
tab: settings_form.woe_active_tab,
622
+ woe_order_post_type: woe_order_post_type,
623
};
624
625
jQuery.post( ajaxurl, data, function ( response ) {
650
} ).trigger( 'change' );
651
652
jQuery( '#add_item_metadata' ).click( function () {
653
+ var val = ! jQuery( "#text_item_metadata" ).is( ':disabled' ) ? jQuery( "#text_item_metadata" ).val() : jQuery( "#text_order_itemmetadata" ).val();
654
var val2 = jQuery( '#item_metadata' ).val();
655
var val_op = jQuery( '#item_metadata_compare' ).val();
656
if ( val != null && val2 != null && val.length && val2.length ) {
688
action: "order_exporter",
689
woe_nonce: settings_form.woe_nonce,
690
tab: settings_form.woe_active_tab,
691
+ woe_order_post_type: woe_order_post_type,
692
};
693
var val_op = jQuery( '#select_user_custom_fields' ).val();
694
jQuery( '#text_user_custom_fields' ).val( '' );
786
action: "order_exporter",
787
woe_nonce: settings_form.woe_nonce,
788
tab: settings_form.woe_active_tab,
789
+ woe_order_post_type: woe_order_post_type,
790
};
791
792
var val_op = jQuery( '#custom_fields_compare' ).val();
assets/js/settings-form.js CHANGED
@@ -45,8 +45,10 @@ function woe_change_filename_ext() {
45
function woe_show_summary_report( ext ) {
46
if ( woe_is_flat_format( ext ) ) {
47
jQuery( '#summary_report_by_products' ).show();
48
} else {
49
jQuery( '#summary_report_by_products' ).hide();
50
jQuery( '#summary_setup_fields' ).hide();
51
jQuery( '#summary_report_by_products_checkbox' ).prop( 'checked', false ).trigger( 'change' );
52
}
@@ -224,7 +226,7 @@ jQuery( document ).ready( function ( $ ) {
224
225
woe_show_summary_report( output_format );
226
227
- if ( ! summary_mode ) {
228
jQuery( '#summary_setup_fields' ).hide();
229
}
230
45
function woe_show_summary_report( ext ) {
46
if ( woe_is_flat_format( ext ) ) {
47
jQuery( '#summary_report_by_products' ).show();
48
+ jQuery( '#summary_report_by_customers' ).show();
49
} else {
50
jQuery( '#summary_report_by_products' ).hide();
51
+ jQuery( '#summary_report_by_customers' ).hide();
52
jQuery( '#summary_setup_fields' ).hide();
53
jQuery( '#summary_report_by_products_checkbox' ).prop( 'checked', false ).trigger( 'change' );
54
}
226
227
woe_show_summary_report( output_format );
228
229
+ if ( ! summary_mode_by_products ) {
230
jQuery( '#summary_setup_fields' ).hide();
231
}
232
classes/FPDF/class-woe-fpdf-tt-font-file.php ADDED
@@ -0,0 +1,1458 @@
1
+ <?php
2
+ mb_internal_encoding('utf-8'); // @important
3
+
4
+
5
+ class WOE_FPDF_TT_Font_File
6
+ {
7
+ private $gfWords = 0b1;
8
+ private $gfScale = 0b1000;
9
+ private $gfMore = 0b100000;
10
+ private $gfXYScale = 0b1000000;
11
+ private $gfTwoByTwo = 0b10000000;
12
+
13
+ // Define the value used in the "head" table of a created TTF file
14
+ // 0x74727565 "true" for Mac
15
+ // 0x00010000 for Windows
16
+ // Either seems to work for a font embedded in a PDF file
17
+ // when read by Adobe Reader on a Windows PC(!)
18
+ private $ttf_mac_header = false;
19
+
20
+ private $maxUni;
21
+ private $_pos;
22
+ private $numTables;
23
+ private $searchRange;
24
+ private $entrySelector;
25
+ private $rangeShift;
26
+ private $tables;
27
+ private $otables;
28
+ private $filename;
29
+ private $fh;
30
+ private $glyphPos;
31
+ private $charToGlyph;
32
+ private $codeToGlyph;
33
+ private $ascent;
34
+ private $descent;
35
+ private $name;
36
+ private $familyName;
37
+ private $styleName;
38
+ private $fullName;
39
+ private $uniqueFontID;
40
+ private $unitsPerEm;
41
+ private $bbox;
42
+ private $capHeight;
43
+ private $stemV;
44
+ private $italicAngle;
45
+ private $flags;
46
+ private $underlinePosition;
47
+ private $underlineThickness;
48
+ private $charWidths;
49
+ private $defaultWidth;
50
+ private $maxStrLenRead;
51
+
52
+ public function __construct()
53
+ {
54
+ $this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
55
+ }
56
+
57
+ public function getMetrics($file)
58
+ {
59
+ if (!file_exists($file)) {
60
+ throw new \RuntimeException('Can\'t open file ' . $file);
61
+ }
62
+
63
+ $this->filename = $file;
64
+ $this->fh = fopen($file, 'rb');
65
+ $this->_pos = 0;
66
+ $this->charWidths = '';
67
+ $this->glyphPos = array();
68
+ $this->charToGlyph = array();
69
+ $this->tables = array();
70
+ $this->otables = array();
71
+ $this->ascent = 0;
72
+ $this->descent = 0;
73
+ $this->TTCFonts = array();
74
+ $this->version = $version = $this->read_ulong();
75
+
76
+ if ($version === 0x4F54544F) {
77
+ throw new \RuntimeException('Postscript outlines are not supported');
78
+ }
79
+
80
+ if ($version === 0x74746366) {
81
+ throw new \RuntimeException('ERROR - TrueType Fonts Collections not supported');
82
+ }
83
+
84
+ if (!in_array($version, array(0x00010000, 0x74727565))) {
85
+ throw new \RuntimeException("Not a TrueType font: version=" . $version);
86
+ }
87
+
88
+ $this->readTableDirectory();
89
+ $this->extractInfo();
90
+ fclose($this->fh);
91
+ }
92
+
93
+ public function getCharWidths()
94
+ {
95
+ return $this->charWidths;
96
+ }
97
+
98
+ private function readTableDirectory()
99
+ {
100
+ $this->numTables = $this->read_ushort();
101
+ $this->searchRange = $this->read_ushort();
102
+ $this->entrySelector = $this->read_ushort();
103
+ $this->rangeShift = $this->read_ushort();
104
+ $this->tables = array();
105
+ for ($i = 0; $i < $this->numTables; $i++) {
106
+ $record = array();
107
+ $record['tag'] = $this->read_tag();
108
+ $record['checksum'] = array($this->read_ushort(), $this->read_ushort());
109
+ $record['offset'] = $this->read_ulong();
110
+ $record['length'] = $this->read_ulong();
111
+ $this->tables[$record['tag']] = $record;
112
+ }
113
+ }
114
+
115
+
116
+ private function sub32($x, $y)
117
+ {
118
+ $xlo = $x[1];
119
+ $xhi = $x[0];
120
+ $ylo = $y[1];
121
+ $yhi = $y[0];
122
+ if ($ylo > $xlo) {
123
+ $xlo += 1 << 16;
124
+ $yhi += 1;
125
+ }
126
+ $reslo = $xlo - $ylo;
127
+ if ($yhi > $xhi) {
128
+ $xhi += 1 << 16;
129
+ }
130
+ $reshi = $xhi - $yhi;
131
+ $reshi = $reshi & 0xFFFF;
132
+ return array($reshi, $reslo);
133
+ }
134
+
135
+ private function calcChecksum($data)
136
+ {
137
+ if (strlen($data) % 4) {
138
+ $data .= str_repeat("\0", (4 - (strlen($data) % 4)));
139
+ }
140
+ $hi = 0x0000;
141
+ $lo = 0x0000;
142
+ for ($i = 0; $i < strlen($data); $i += 4) {
143
+ $hi += (ord($data[$i]) << 8) + ord($data[$i + 1]);
144
+ $lo += (ord($data[$i + 2]) << 8) + ord($data[$i + 3]);
145
+ $hi += $lo >> 16;
146
+ $lo = $lo & 0xFFFF;
147
+ $hi = $hi & 0xFFFF;
148
+ }
149
+ return array($hi, $lo);
150
+ }
151
+
152
+ private function get_table_pos($tag)
153
+ {
154
+ $offset = $this->tables[$tag]['offset'];
155
+ $length = $this->tables[$tag]['length'];
156
+ return array($offset, $length);
157
+ }
158
+
159
+ private function seek($pos)
160
+ {
161
+ $this->_pos = $pos;
162
+ fseek($this->fh, $this->_pos);
163
+ }
164
+
165
+ private function skip($delta)
166
+ {
167
+ $this->_pos = $this->_pos + $delta;
168
+ fseek($this->fh, $this->_pos);
169
+ }
170
+
171
+ private function seek_table($tag, $offset_in_table = 0)
172
+ {
173
+ $tpos = $this->get_table_pos($tag);
174
+ $this->_pos = $tpos[0] + $offset_in_table;
175
+ fseek($this->fh, $this->_pos);
176
+ return $this->_pos;
177
+ }
178
+
179
+ private function read_tag()
180
+ {
181
+ $this->_pos += 4;
182
+ return fread($this->fh, 4);
183
+ }
184
+
185
+ private function read_short()
186
+ {
187
+ $this->_pos += 2;
188
+ $s = fread($this->fh, 2);
189
+ $a = (ord($s[0]) << 8) + ord($s[1]);
190
+ if ($a & (1 << 15)) {
191
+ $a = ($a - (1 << 16));
192
+ }
193
+ return $a;
194
+ }
195
+
196
+ private function unpack_short($s)
197
+ {
198
+ $a = (ord($s[0]) << 8) + ord($s[1]);
199
+ if ($a & (1 << 15)) {
200
+ $a = ($a - (1 << 16));
201
+ }
202
+ return $a;
203
+ }
204
+
205
+ private function read_ushort()
206
+ {
207
+ $this->_pos += 2;
208
+ $s = fread($this->fh, 2);
209
+ return (ord($s[0]) << 8) + ord($s[1]);
210
+ }
211
+
212
+ private function read_ulong()
213
+ {
214
+ $this->_pos += 4;
215
+ $s = fread($this->fh, 4);
216
+ // if large uInt32 as an integer, PHP converts it to -ve
217
+ return (ord($s[0]) * 16777216) + (ord($s[1]) << 16) + (ord($s[2]) << 8) + ord($s[3]); // 16777216 = 1<<24
218
+ }
219
+
220
+ private function get_ushort($pos)
221
+ {
222
+ fseek($this->fh, $pos);
223
+ $s = fread($this->fh, 2);
224
+ return (ord($s[0]) << 8) + ord($s[1]);
225
+ }
226
+
227
+ private function get_ulong($pos)
228
+ {
229
+ fseek($this->fh, $pos);
230
+ $s = fread($this->fh, 4);
231
+ // iF large uInt32 as an integer, PHP converts it to -ve
232
+ return (ord($s[0]) * 16777216) + (ord($s[1]) << 16) + (ord($s[2]) << 8) + ord($s[3]); // 16777216 = 1<<24
233
+ }
234
+
235
+ private function pack_short($val)
236
+ {
237
+ if ($val < 0) {
238
+ $val = abs($val);
239
+ $val = ~$val;
240
+ $val += 1;
241
+ }
242
+ return pack("n", $val);
243
+ }
244
+
245
+ private function splice($stream, $offset, $value)
246
+ {
247
+ return substr($stream, 0, $offset) . $value . substr($stream, $offset + strlen($value));
248
+ }
249
+
250
+ private function _set_ushort($stream, $offset, $value)
251
+ {
252
+ $up = pack("n", $value);
253
+ return $this->splice($stream, $offset, $up);
254
+ }
255
+
256
+ private function _set_short($stream, $offset, $val)
257
+ {
258
+ if ($val < 0) {
259
+ $val = abs($val);
260
+ $val = ~$val;
261
+ $val += 1;
262
+ }
263
+ $up = pack("n", $val);
264
+ return $this->splice($stream, $offset, $up);
265
+ }
266
+
267
+ private function get_chunk($pos, $length)
268
+ {
269
+ fseek($this->fh, $pos);
270
+ if ($length < 1) {
271
+ return '';
272
+ }
273
+ return (fread($this->fh, $length));
274
+ }
275
+
276
+ private function get_table($tag)
277
+ {
278
+ list($pos, $length) = $this->get_table_pos($tag);
279
+ if ($length == 0) {
280
+ throw new \RuntimeException('Truetype font (' . $this->filename . '): error reading table: ' . $tag);
281
+ }
282
+ fseek($this->fh, $pos);
283
+ return (fread($this->fh, $length));
284
+ }
285
+
286
+ private function add($tag, $data)
287
+ {
288
+ if ($tag == 'head') {
289
+ $data = $this->splice($data, 8, "\0\0\0\0");
290
+ }
291
+ $this->otables[$tag] = $data;
292
+ }
293
+
294
+
295
+
296
+ /////////////////////////////////////////////////////////////////////////////////////////
297
+ /////////////////////////////////////////////////////////////////////////////////////////
298
+
299
+ /////////////////////////////////////////////////////////////////////////////////////////
300
+
301
+ private function extractInfo()
302
+ {
303
+ ///////////////////////////////////
304
+ // name - Naming table
305
+ ///////////////////////////////////
306
+ $this->sFamilyClass = 0;
307
+ $this->sFamilySubClass = 0;
308
+
309
+ $name_offset = $this->seek_table('name');
310
+ $format = $this->read_ushort();
311
+ if ($format != 0)
312
+ throw new \RuntimeException('Unknown name table format ' . $format);
313
+ $numRecords = $this->read_ushort();
314
+ $string_data_offset = $name_offset + $this->read_ushort();
315
+ $names = array(1 => '', 2 => '', 3 => '', 4 => '', 6 => '');
316
+ $K = array_keys($names);
317
+ $nameCount = count($names);
318
+ for ($i = 0; $i < $numRecords; $i++) {
319
+ $platformId = $this->read_ushort();
320
+ $encodingId = $this->read_ushort();
321
+ $languageId = $this->read_ushort();
322
+ $nameId = $this->read_ushort();
323
+ $length = $this->read_ushort();
324
+ $offset = $this->read_ushort();
325
+ if (!in_array($nameId, $K)) continue;
326
+ $N = '';
327
+ if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name
328
+ $opos = $this->_pos;
329
+ $this->seek($string_data_offset + $offset);
330
+ if ($length % 2 != 0)
331
+ throw new \RuntimeException("PostScript name is UTF-16BE string of odd length");
332
+ $length /= 2;
333
+ $N = '';
334
+ while ($length > 0) {
335
+ $char = $this->read_ushort();
336
+ $N .= (chr($char));
337
+ $length -= 1;
338
+ }
339
+ $this->_pos = $opos;
340
+ $this->seek($opos);
341
+ } else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name
342
+ $opos = $this->_pos;
343
+ $N = $this->get_chunk($string_data_offset + $offset, $length);
344
+ $this->_pos = $opos;
345
+ $this->seek($opos);
346
+ }
347
+ if ($N && $names[$nameId] == '') {
348
+ $names[$nameId] = $N;
349
+ $nameCount -= 1;
350
+ if ($nameCount == 0) break;
351
+ }
352
+ }
353
+ if ($names[6])
354
+ $psName = $names[6];
355
+ else if ($names[4])
356
+ $psName = preg_replace('/ /', '-', $names[4]);
357
+ else if ($names[1])
358
+ $psName = preg_replace('/ /', '-', $names[1]);
359
+ else
360
+ $psName = '';
361
+ if (!$psName)
362
+ throw new \RuntimeException("Could not find PostScript font name");
363
+ $this->name = $psName;
364
+ if ($names[1]) {
365
+ $this->familyName = $names[1];
366
+ } else {
367
+ $this->familyName = $psName;
368
+ }
369
+ if ($names[2]) {
370
+ $this->styleName = $names[2];
371
+ } else {
372
+ $this->styleName = 'Regular';
373
+ }
374
+ if ($names[4]) {
375
+ $this->fullName = $names[4];
376
+ } else {
377
+ $this->fullName = $psName;
378
+ }
379
+ if ($names[3]) {
380
+ $this->uniqueFontID = $names[3];
381
+ } else {
382
+ $this->uniqueFontID = $psName;
383
+ }
384
+ if ($names[6]) {
385
+ $this->fullName = $names[6];
386
+ }
387
+
388
+ ///////////////////////////////////
389
+ // head - Font header table
390
+ ///////////////////////////////////
391
+ $this->seek_table("head");
392
+ $this->skip(18);
393
+ $this->unitsPerEm = $unitsPerEm = $this->read_ushort();
394
+ $scale = 1000 / $unitsPerEm;
395
+ $this->skip(16);
396
+ $xMin = $this->read_short();
397
+ $yMin = $this->read_short();
398
+ $xMax = $this->read_short();
399
+ $yMax = $this->read_short();
400
+ $this->bbox = array(($xMin * $scale), ($yMin * $scale), ($xMax * $scale), ($yMax * $scale));
401
+ $this->skip(3 * 2);
402
+ $indexToLocFormat = $this->read_ushort();
403
+ $glyphDataFormat = $this->read_ushort();
404
+ if ($glyphDataFormat != 0)
405
+ throw new \RuntimeException('Unknown glyph data format ' . $glyphDataFormat);
406
+
407
+ ///////////////////////////////////
408
+ // hhea metrics table
409
+ ///////////////////////////////////
410
+ // ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility
411
+ if (isset($this->tables["hhea"])) {
412
+ $this->seek_table("hhea");
413
+ $this->skip(4);
414
+ $hheaAscender = $this->read_short();
415
+ $hheaDescender = $this->read_short();
416
+ $this->ascent = ($hheaAscender * $scale);
417
+ $this->descent = ($hheaDescender * $scale);
418
+ }
419
+
420
+ ///////////////////////////////////
421
+ // OS/2 - OS/2 and Windows metrics table
422
+ ///////////////////////////////////
423
+ if (isset($this->tables["OS/2"])) {
424
+ $this->seek_table("OS/2");
425
+ $version = $this->read_ushort();
426
+ $this->skip(2);
427
+ $usWeightClass = $this->read_ushort();
428
+ $this->skip(2);
429
+ $fsType = $this->read_ushort();
430
+ if ($fsType == 0x0002 || ($fsType & 0x0300) != 0) {
431
+ throw new \RuntimeException('ERROR - Font file ' . $this->filename . ' cannot be embedded due to copyright restrictions.');
432
+ }
433
+ $this->skip(20);
434
+ $sF = $this->read_short();
435
+ $this->sFamilyClass = ($sF >> 8);
436
+ $this->sFamilySubClass = ($sF & 0xFF);
437
+ $this->_pos += 10; //PANOSE = 10 byte length
438
+ $panose = fread($this->fh, 10);
439
+ $this->skip(26);
440
+ $sTypoAscender = $this->read_short();
441
+ $sTypoDescender = $this->read_short();
442
+ if (!$this->ascent) $this->ascent = ($sTypoAscender * $scale);
443
+ if (!$this->descent) $this->descent = ($sTypoDescender * $scale);
444
+ if ($version > 1) {
445
+ $this->skip(16);
446
+ $sCapHeight = $this->read_short();
447
+ $this->capHeight = ($sCapHeight * $scale);
448
+ } else {
449
+ $this->capHeight = $this->ascent;
450
+ }
451
+ } else {
452
+ $usWeightClass = 500;
453
+ if (!$this->ascent) $this->ascent = ($yMax * $scale);
454
+ if (!$this->descent) $this->descent = ($yMin * $scale);
455
+ $this->capHeight = $this->ascent;
456
+ }
457
+ $this->stemV = 50 + (int) pow($usWeightClass / 65.0, 2);
458
+
459
+ ///////////////////////////////////
460
+ // post - PostScript table
461
+ ///////////////////////////////////
462
+ $this->seek_table('post');
463
+ $this->skip(4);
464
+ $this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0;
465
+ $this->underlinePosition = $this->read_short() * $scale;
466
+ $this->underlineThickness = $this->read_short() * $scale;
467
+ $isFixedPitch = $this->read_ulong();
468
+
469
+ $this->flags = 4;
470
+
471
+ if ($this->italicAngle != 0)
472
+ $this->flags = $this->flags | 64;
473
+ if ($usWeightClass >= 600)
474
+ $this->flags = $this->flags | 262144;
475
+ if ($isFixedPitch)
476
+ $this->flags = $this->flags | 1;
477
+
478
+ ///////////////////////////////////
479
+ // hhea - Horizontal header table
480
+ ///////////////////////////////////
481
+ $this->seek_table("hhea");
482
+ $this->skip(32);
483
+ $metricDataFormat = $this->read_ushort();
484
+ if ($metricDataFormat != 0)
485
+ throw new \RuntimeException('Unknown horizontal metric data format ' . $metricDataFormat);
486
+ $numberOfHMetrics = $this->read_ushort();
487
+ if ($numberOfHMetrics == 0)
488
+ throw new \RuntimeException('Number of horizontal metrics is 0');
489
+
490
+ ///////////////////////////////////
491
+ // maxp - Maximum profile table
492
+ ///////////////////////////////////
493
+ $this->seek_table("maxp");
494
+ $this->skip(4);
495
+ $numGlyphs = $this->read_ushort();
496
+
497
+
498
+ ///////////////////////////////////
499
+ // cmap - Character to glyph index mapping table
500
+ ///////////////////////////////////
501
+ $cmap_offset = $this->seek_table("cmap");
502
+ $this->skip(2);
503
+ $cmapTableCount = $this->read_ushort();
504
+ $unicode_cmap_offset = 0;
505
+ for ($i = 0; $i < $cmapTableCount; $i++) {
506
+ $platformID = $this->read_ushort();
507
+ $encodingID = $this->read_ushort();
508
+ $offset = $this->read_ulong();
509
+ $save_pos = $this->_pos;
510
+ if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
511
+ $format = $this->get_ushort($cmap_offset + $offset);
512
+ if ($format == 4) {
513
+ if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset;
514
+ break;
515
+ }
516
+ }
517
+ $this->seek($save_pos);
518
+ }
519
+ if (!$unicode_cmap_offset)
520
+ throw new \RuntimeException('Font (' . $this->filename . ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
521
+
522
+
523
+ $glyphToChar = array();
524
+ $charToGlyph = array();
525
+ $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph);
526
+
527
+ ///////////////////////////////////
528
+ // hmtx - Horizontal metrics table
529
+ ///////////////////////////////////
530
+ $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
531
+
532
+ }
533
+
534
+
535
+ /////////////////////////////////////////////////////////////////////////////////////////
536
+ /////////////////////////////////////////////////////////////////////////////////////////
537
+
538
+
539
+ public function makeSubset($file, &$subset)
540
+ {
541
+ if (!file_exists($file)) {
542
+ throw new \RuntimeException('Can\'t open file to create subset');
543
+ }
544
+
545
+ $this->filename = $file;
546
+ $this->fh = fopen($file, 'rb');
547
+ $this->_pos = 0;
548
+ $this->charWidths = '';
549
+ $this->glyphPos = array();
550
+ $this->charToGlyph = array();
551
+ $this->tables = array();
552
+ $this->otables = array();
553
+ $this->ascent = 0;
554
+ $this->descent = 0;
555
+ $this->skip(4);
556
+ $this->maxUni = 0;
557
+ $this->readTableDirectory();
558
+
559
+
560
+ ///////////////////////////////////
561
+ // head - Font header table
562
+ ///////////////////////////////////
563
+ $this->seek_table("head");
564
+ $this->skip(50);
565
+ $indexToLocFormat = $this->read_ushort();
566
+ $glyphDataFormat = $this->read_ushort();
567
+
568
+ ///////////////////////////////////
569
+ // hhea - Horizontal header table
570
+ ///////////////////////////////////
571
+ $this->seek_table("hhea");
572
+ $this->skip(32);
573
+ $metricDataFormat = $this->read_ushort();
574
+ $orignHmetrics = $numberOfHMetrics = $this->read_ushort();
575
+
576
+ ///////////////////////////////////
577
+ // maxp - Maximum profile table
578
+ ///////////////////////////////////
579
+ $this->seek_table("maxp");
580
+ $this->skip(4);
581
+ $numGlyphs = $this->read_ushort();
582
+
583
+
584
+ ///////////////////////////////////
585
+ // cmap - Character to glyph index mapping table
586
+ ///////////////////////////////////
587
+ $cmap_offset = $this->seek_table("cmap");
588
+ $this->skip(2);
589
+ $cmapTableCount = $this->read_ushort();
590
+ $unicode_cmap_offset = 0;
591
+ for ($i = 0; $i < $cmapTableCount; $i++) {
592
+ $platformID = $this->read_ushort();
593
+ $encodingID = $this->read_ushort();
594
+ $offset = $this->read_ulong();
595
+ $save_pos = $this->_pos;
596
+ if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
597
+ $format = $this->get_ushort($cmap_offset + $offset);
598
+ if ($format == 4) {
599
+ $unicode_cmap_offset = $cmap_offset + $offset;
600
+ break;
601
+ }
602
+ }
603
+ $this->seek($save_pos);
604
+ }
605
+
606
+ if (!$unicode_cmap_offset)
607
+ throw new \RuntimeException('Font (' . $this->filename . ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
608
+
609
+
610
+ $glyphToChar = array();
611
+ $charToGlyph = array();
612
+ $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph);
613
+
614
+ $this->charToGlyph = $charToGlyph;
615
+
616
+ ///////////////////////////////////
617
+ // hmtx - Horizontal metrics table
618
+ ///////////////////////////////////
619
+ $scale = 1; // not used
620
+ $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
621
+
622
+ ///////////////////////////////////
623
+ // loca - Index to location
624
+ ///////////////////////////////////
625
+ $this->getLOCA($indexToLocFormat, $numGlyphs);
626
+
627
+ $subsetglyphs = array(0 => 0);
628
+ $subsetCharToGlyph = array();
629
+ foreach ($subset AS $code) {
630
+ if (isset($this->charToGlyph[$code])) {
631
+ $subsetglyphs[$this->charToGlyph[$code]] = $code; // Old Glyph ID => Unicode
632
+ $subsetCharToGlyph[$code] = $this->charToGlyph[$code]; // Unicode to old GlyphID
633
+
634
+ }
635
+ $this->maxUni = max($this->maxUni, $code);
636
+ }
637
+
638
+ list($start, $dummy) = $this->get_table_pos('glyf');
639
+
640
+ $glyphSet = array();
641
+ ksort($subsetglyphs);
642
+ $n = 0;
643
+ $fsLastCharIndex = 0; // maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and platform- specific encoding ID 0 or 1.
644
+ foreach ($subsetglyphs AS $originalGlyphIdx => $uni) {
645
+ $fsLastCharIndex = max($fsLastCharIndex, $uni);
646
+ $glyphSet[$originalGlyphIdx] = $n; // old glyphID to new glyphID
647
+ $n++;
648
+ }
649
+
650
+ ksort($subsetCharToGlyph);
651
+ foreach ($subsetCharToGlyph AS $uni => $originalGlyphIdx) {
652
+ $codeToGlyph[$uni] = $glyphSet[$originalGlyphIdx];
653
+ }
654
+ $this->codeToGlyph = $codeToGlyph;
655
+
656
+ ksort($subsetglyphs);
657
+ foreach ($subsetglyphs AS $originalGlyphIdx => $uni) {
658
+ $this->getGlyphs($originalGlyphIdx, $start, $glyphSet, $subsetglyphs);
659
+ }
660
+
661
+ $numGlyphs = $numberOfHMetrics = count($subsetglyphs);
662
+
663
+ //tables copied from the original
664
+ $tags = array('name');
665
+ foreach ($tags AS $tag) {
666
+ $this->add($tag, $this->get_table($tag));
667
+ }
668
+ $tags = array('cvt ', 'fpgm', 'prep', 'gasp');
669
+ foreach ($tags AS $tag) {
670
+ if (isset($this->tables[$tag])) {
671
+ $this->add($tag, $this->get_table($tag));
672
+ }
673
+ }
674
+
675
+ // post - PostScript
676
+ $opost = $this->get_table('post');
677
+ $post = "\x00\x03\x00\x00" . substr($opost, 4, 12) . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
678
+ $this->add('post', $post);
679
+
680
+ // Sort CID2GID map into segments of contiguous codes
681
+ ksort($codeToGlyph);
682
+ unset($codeToGlyph[0]);
683
+ //unset($codeToGlyph[65535]);
684
+ $rangeid = 0;
685
+ $range = array();
686
+ $prevcid = -2;
687
+ $prevglidx = -1;
688
+ // for each character
689
+ foreach ($codeToGlyph as $cid => $glidx) {
690
+ if ($cid == ($prevcid + 1) && $glidx == ($prevglidx + 1)) {
691
+ $range[$rangeid][] = $glidx;
692
+ } else {
693
+ // new range
694
+ $rangeid = $cid;
695
+ $range[$rangeid] = array();
696
+ $range[$rangeid][] = $glidx;
697
+ }
698
+ $prevcid = $cid;
699
+ $prevglidx = $glidx;
700
+ }
701
+
702
+ // cmap - Character to glyph mapping - Format 4 (MS / )
703
+ $segCount = count($range) + 1; // + 1 Last segment has missing character 0xFFFF
704
+ $searchRange = 1;
705
+ $entrySelector = 0;
706
+ while ($searchRange * 2 <= $segCount) {
707
+ $searchRange = $searchRange * 2;
708
+ $entrySelector = $entrySelector + 1;
709
+ }
710
+ $searchRange = $searchRange * 2;
711
+ $rangeShift = $segCount * 2 - $searchRange;
712
+ $length = 16 + (8 * $segCount) + ($numGlyphs + 1);
713
+ $cmap = array(0, 1, // Index : version, number of encoding subtables
714
+ 3, 1, // Encoding Subtable : platform (MS=3), encoding (Unicode)
715
+ 0, 12, // Encoding Subtable : offset (hi,lo)
716
+ 4, $length, 0, // Format 4 Mapping subtable: format, length, language
717
+ $segCount * 2,
718
+ $searchRange,
719
+ $entrySelector,
720
+ $rangeShift);
721
+
722
+ // endCode(s)
723
+ foreach ($range AS $start => $subrange) {
724
+ $endCode = $start + (count($subrange) - 1);
725
+ $cmap[] = $endCode; // endCode(s)
726
+ }
727
+ $cmap[] = 0xFFFF; // endCode of last Segment
728
+ $cmap[] = 0; // reservedPad
729
+
730
+ // startCode(s)
731
+ foreach ($range AS $start => $subrange) {
732
+ $cmap[] = $start; // startCode(s)
733
+ }
734
+ $cmap[] = 0xFFFF; // startCode of last Segment
735
+ // idDelta(s)
736
+ foreach ($range AS $start => $subrange) {
737
+ $idDelta = -($start - $subrange[0]);
738
+ $n += count($subrange);
739
+ $cmap[] = $idDelta; // idDelta(s)
740
+ }
741
+ $cmap[] = 1; // idDelta of last Segment
742
+ // idRangeOffset(s)
743
+ foreach ($range AS $subrange) {
744
+ $cmap[] = 0; // idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0
745
+
746
+ }
747
+ $cmap[] = 0; // idRangeOffset of last Segment
748
+ foreach ($range AS $subrange) {
749
+ foreach ($subrange AS $glidx) {
750
+ $cmap[] = $glidx;
751
+ }
752
+ }
753
+ $cmap[] = 0; // Mapping for last character
754
+ $cmapstr = '';
755
+ foreach ($cmap AS $cm) {
756
+ $cmapstr .= pack("n", $cm);
757
+ }
758
+ $this->add('cmap', $cmapstr);
759
+
760
+
761
+ // glyf - Glyph data
762
+ list($glyfOffset, $glyfLength) = $this->get_table_pos('glyf');
763
+ if ($glyfLength < $this->maxStrLenRead) {
764
+ $glyphData = $this->get_table('glyf');
765
+ }
766
+
767
+ $offsets = array();
768
+ $glyf = '';
769
+ $pos = 0;
770
+
771
+ $hmtxstr = '';
772
+ $xMinT = 0;
773
+ $yMinT = 0;
774
+ $xMaxT = 0;
775
+ $yMaxT = 0;
776
+ $advanceWidthMax = 0;
777
+ $minLeftSideBearing = 0;
778
+ $minRightSideBearing = 0;
779
+ $xMaxExtent = 0;
780
+ $maxPoints = 0; // points in non-compound glyph
781
+ $maxContours = 0; // contours in non-compound glyph
782
+ $maxComponentPoints = 0; // points in compound glyph
783
+ $maxComponentContours = 0; // contours in compound glyph
784
+ $maxComponentElements = 0; // number of glyphs referenced at top level
785
+ $maxComponentDepth = 0; // levels of recursion, set to 0 if font has only simple glyphs
786
+ $this->glyphdata = array();
787
+
788
+ foreach ($subsetglyphs AS $originalGlyphIdx => $uni) {
789
+ // hmtx - Horizontal Metrics
790
+ $hm = $this->getHMetric($orignHmetrics, $originalGlyphIdx);
791
+ $hmtxstr .= $hm;
792
+
793
+ $offsets[] = $pos;
794
+ $glyphPos = $this->glyphPos[$originalGlyphIdx];
795
+ $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
796
+ if ($glyfLength < $this->maxStrLenRead) {
797
+ $data = substr($glyphData, $glyphPos, $glyphLen);
798
+ } else {
799
+ if ($glyphLen > 0) $data = $this->get_chunk($glyfOffset + $glyphPos, $glyphLen);
800
+ else $data = '';
801
+ }
802
+
803
+ if ($glyphLen > 0) {
804
+ $up = unpack("n", substr($data, 0, 2));
805
+ }
806
+
807
+ if ($glyphLen > 2 && ($up[1] & (1 << 15))) { // If number of contours <= -1 i.e. composiste glyph
808
+ $pos_in_glyph = 10;
809
+ $flags = $this->gfMore;
810
+ $nComponentElements = 0;
811
+ while ($flags & $this->gfMore) {
812
+ $nComponentElements += 1; // number of glyphs referenced at top level
813
+ $up = unpack("n", substr($data, $pos_in_glyph, 2));
814
+ $flags = $up[1];
815
+ $up = unpack("n", substr($data, $pos_in_glyph + 2, 2));
816
+ $glyphIdx = $up[1];
817
+ $this->glyphdata[$originalGlyphIdx]['compGlyphs'][] = $glyphIdx;
818
+ $data = $this->_set_ushort($data, $pos_in_glyph + 2, $glyphSet[$glyphIdx]);
819
+ $pos_in_glyph += 4;
820
+ if ($flags & $this->gfWords) {
821
+ $pos_in_glyph += 4;
822
+ } else {
823
+ $pos_in_glyph += 2;
824
+ }
825
+ if ($flags & $this->gfScale) {
826
+ $pos_in_glyph += 2;
827
+ } else if ($flags & $this->gfXYScale) {
828
+ $pos_in_glyph += 4;
829
+ } else if ($flags & $this->gfTwoByTwo) {
830
+ $pos_in_glyph += 8;
831
+ }
832
+ }
833
+ $maxComponentElements = max($maxComponentElements, $nComponentElements);
834
+ }
835
+
836
+ $glyf .= $data;
837
+ $pos += $glyphLen;
838
+ if ($pos % 4 != 0) {
839
+ $padding = 4 - ($pos % 4);
840
+ $glyf .= str_repeat("\0", $padding);
841
+ $pos += $padding;
842
+ }
843
+ }
844
+
845
+ $offsets[] = $pos;
846
+ $this->add('glyf', $glyf);
847
+
848
+ // hmtx - Horizontal Metrics
849
+ $this->add('hmtx', $hmtxstr);
850
+
851
+ // loca - Index to location
852
+ $locastr = '';
853
+ if ((($pos + 1) >> 1) > 0xFFFF) {
854
+ $indexToLocFormat = 1; // long format
855
+ foreach ($offsets AS $offset) {
856
+ $locastr .= pack("N", $offset);
857
+ }
858
+ } else {
859
+ $indexToLocFormat = 0; // short format
860
+ foreach ($offsets AS $offset) {
861
+ $locastr .= pack("n", ($offset / 2));
862
+ }
863
+ }
864
+ $this->add('loca', $locastr);
865
+
866
+ // head - Font header
867
+ $head = $this->get_table('head');
868
+ $head = $this->_set_ushort($head, 50, $indexToLocFormat);
869
+ $this->add('head', $head);
870
+
871
+
872
+ // hhea - Horizontal Header
873
+ $hhea = $this->get_table('hhea');
874
+ $hhea = $this->_set_ushort($hhea, 34, $numberOfHMetrics);
875
+ $this->add('hhea', $hhea);
876
+
877
+ // maxp - Maximum Profile
878
+ $maxp = $this->get_table('maxp');
879
+ $maxp = $this->_set_ushort($maxp, 4, $numGlyphs);
880
+ $this->add('maxp', $maxp);
881
+
882
+
883
+ // OS/2 - OS/2
884
+ $os2 = $this->get_table('OS/2');
885
+ $this->add('OS/2', $os2);
886
+
887
+ fclose($this->fh);
888
+
889
+ // Put the TTF file together
890
+ $stm = '';
891
+ $this->endTTFile($stm);
892
+ return $stm;
893
+ }
894
+
895
+ //////////////////////////////////////////////////////////////////////////////////
896
+ // Recursively get composite glyph data
897
+ private function getGlyphData($originalGlyphIdx, &$maxdepth, &$depth, &$points, &$contours)
898
+ {
899
+ $depth++;
900
+ $maxdepth = max($maxdepth, $depth);
901
+ if (count($this->glyphdata[$originalGlyphIdx]['compGlyphs'])) {
902
+ foreach ($this->glyphdata[$originalGlyphIdx]['compGlyphs'] AS $glyphIdx) {
903
+ $this->getGlyphData($glyphIdx, $maxdepth, $depth, $points, $contours);
904
+ }
905
+ } else if (($this->glyphdata[$originalGlyphIdx]['nContours'] > 0) && $depth > 0) { // simple
906
+ $contours += $this->glyphdata[$originalGlyphIdx]['nContours'];
907
+ $points += $this->glyphdata[$originalGlyphIdx]['nPoints'];
908
+ }
909
+ $depth--;
910
+ }
911
+
912
+
913
+ //////////////////////////////////////////////////////////////////////////////////
914
+ // Recursively get composite glyphs
915
+ private function getGlyphs($originalGlyphIdx, &$start, &$glyphSet, &$subsetglyphs)
916
+ {
917
+ $glyphPos = $this->glyphPos[$originalGlyphIdx];
918
+ $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
919
+ if (!$glyphLen) {
920
+ return;
921
+ }
922
+ $this->seek($start + $glyphPos);
923
+ $numberOfContours = $this->read_short();
924
+ if ($numberOfContours < 0) {
925
+ $this->skip(8);
926
+ $flags = $this->gfMore;
927
+ while ($flags & $this->gfMore) {
928
+ $flags = $this->read_ushort();
929
+ $glyphIdx = $this->read_ushort();
930
+ if (!isset($glyphSet[$glyphIdx])) {
931
+ $glyphSet[$glyphIdx] = count($subsetglyphs); // old glyphID to new glyphID
932
+ $subsetglyphs[$glyphIdx] = true;
933
+ }
934
+ $savepos = ftell($this->fh);
935
+ $this->getGlyphs($glyphIdx, $start, $glyphSet, $subsetglyphs);
936
+ $this->seek($savepos);
937
+ if ($flags & $this->gfWords)
938
+ $this->skip(4);
939
+ else
940
+ $this->skip(2);
941
+ if ($flags & $this->gfScale)
942
+ $this->skip(2);
943
+ else if ($flags & $this->gfXYScale)
944
+ $this->skip(4);
945
+ else if ($flags & $this->gfTwoByTwo)
946
+ $this->skip(8);
947
+ }
948
+ }
949
+ }
950
+
951
+
952
+ //////////////////////////////////////////////////////////////////////////////////
953
+
954
+ private function getHMTX($numberOfHMetrics, $numGlyphs, &$glyphToChar, $scale)
955
+ {
956
+ $start = $this->seek_table("hmtx");
957
+ $aw = 0;
958
+ $this->charWidths = str_pad('', 256 * 256 * 2, "\x00");
959
+ $nCharWidths = 0;
960
+ if (($numberOfHMetrics * 4) < $this->maxStrLenRead) {
961
+ $data = $this->get_chunk($start, ($numberOfHMetrics * 4));
962
+ $arr = unpack("n*", $data);
963
+ } else {
964
+ $this->seek($start);
965
+ }
966
+ for ($glyph = 0; $glyph < $numberOfHMetrics; $glyph++) {
967
+
968
+ if (($numberOfHMetrics * 4) < $this->maxStrLenRead) {
969
+ $aw = $arr[($glyph * 2) + 1];
970
+ } else {
971
+ $aw = $this->read_ushort();
972
+ $lsb = $this->read_ushort();
973
+ }
974
+ if (isset($glyphToChar[$glyph]) || $glyph == 0) {
975
+
976
+ if ($aw >= (1 << 15)) {
977
+ $aw = 0;
978
+ } // 1.03 Some (arabic) fonts have -ve values for width
979
+ // although should be unsigned value - comes out as e.g. 65108 (intended -50)
980
+ if ($glyph == 0) {
981
+ $this->defaultWidth = $scale * $aw;
982
+ continue;
983
+ }
984
+ foreach ($glyphToChar[$glyph] AS $char) {
985
+ if ($char != 0 && $char != 65535) {
986
+ $w = intval(round($scale * $aw));
987
+ if ($w == 0) {
988
+ $w = 65535;
989
+ }
990
+ if ($char < 196608) {
991
+ $this->charWidths[$char * 2] = chr($w >> 8);
992
+ $this->charWidths[$char * 2 + 1] = chr($w & 0xFF);
993
+ $nCharWidths++;
994
+ }
995
+ }
996
+ }
997
+ }
998
+ }
999
+ $data = $this->get_chunk(($start + $numberOfHMetrics * 4), ($numGlyphs * 2));
1000
+ $arr = unpack("n*", $data);
1001
+ $diff = $numGlyphs - $numberOfHMetrics;
1002
+ for ($pos = 0; $pos < $diff; $pos++) {
1003
+ $glyph = $pos + $numberOfHMetrics;
1004
+ if (isset($glyphToChar[$glyph])) {
1005
+ foreach ($glyphToChar[$glyph] AS $char) {
1006
+ if ($char != 0 && $char != 65535) {
1007
+ $w = intval(round($scale * $aw));
1008
+ if ($w == 0) {
1009
+ $w = 65535;
1010
+ }
1011
+ if ($char < 196608) {
1012
+ $this->charWidths[$char * 2] = chr($w >> 8);
1013
+ $this->charWidths[$char * 2 + 1] = chr($w & 0xFF);
1014
+ $nCharWidths++;
1015
+ }
1016
+ }
1017
+ }
1018
+ }
1019
+ }
1020
+ // NB 65535 is a set width of 0
1021
+ // First bytes define number of chars in font
1022
+ $this->charWidths[0] = chr($nCharWidths >> 8);
1023
+ $this->charWidths[1] = chr($nCharWidths & 0xFF);
1024
+ }
1025
+
1026
+ /**
1027
+ * @return int
1028
+ */
1029
+ public function getGfWords()
1030
+ {
1031
+ return $this->gfWords;
1032
+ }
1033
+
1034
+ /**
1035
+ * @return int
1036
+ */
1037
+ public function getGfScale()
1038
+ {
1039
+ return $this->gfScale;
1040
+ }
1041
+
1042
+ /**
1043
+ * @return int
1044
+ */
1045
+ public function getGfMore()
1046
+ {
1047
+ return $this->gfMore;
1048
+ }
1049
+
1050
+ /**
1051
+ * @return int
1052
+ */
1053
+ public function getGfXYScale()
1054
+ {
1055
+ return $this->gfXYScale;
1056
+ }
1057
+
1058
+ /**
1059
+ * @return int
1060
+ */
1061
+ public function getGfTwoByTwo()
1062
+ {
1063
+ return $this->gfTwoByTwo;
1064
+ }
1065
+
1066
+ /**
1067
+ * @return boolean
1068
+ */
1069
+ public function isTtfMacHeader()
1070
+ {
1071
+ return $this->ttf_mac_header;
1072
+ }
1073
+
1074
+ /**
1075
+ * @return mixed
1076
+ */
1077
+ public function getMaxUni()
1078
+ {
1079
+ return $this->maxUni;
1080
+ }
1081
+
1082
+ /**
1083
+ * @return mixed
1084
+ */
1085
+ public function getPos()
1086
+ {
1087
+ return $this->_pos;
1088
+ }
1089
+
1090
+ /**
1091
+ * @return mixed
1092
+ */
1093
+ public function getNumTables()
1094
+ {
1095
+ return $this->numTables;
1096
+ }
1097
+
1098
+ /**
1099
+ * @return mixed
1100
+ */
1101
+ public function getSearchRange()
1102
+ {
1103
+ return $this->searchRange;
1104
+ }
1105
+
1106
+ /**
1107
+ * @return mixed
1108
+ */
1109
+ public function getEntrySelector()
1110
+ {
1111
+ return $this->entrySelector;
1112
+ }
1113
+
1114
+ /**
1115
+ * @return mixed
1116
+ */
1117
+ public function getRangeShift()
1118
+ {
1119
+ return $this->rangeShift;
1120
+ }
1121
+
1122
+ /**
1123
+ * @return mixed
1124
+ */
1125
+ public function getTables()
1126
+ {
1127
+ return $this->tables;
1128
+ }
1129
+
1130
+ /**
1131
+ * @return mixed
1132
+ */
1133
+ public function getOtables()
1134
+ {
1135
+ return $this->otables;
1136
+ }
1137
+
1138
+ /**
1139
+ * @return mixed
1140
+ */
1141
+ public function getFilename()
1142
+ {
1143
+ return $this->filename;
1144
+ }
1145
+
1146
+ /**
1147
+ * @return mixed
1148
+ */
1149
+ public function getFh()
1150
+ {
1151
+ return $this->fh;
1152
+ }
1153
+
1154
+ /**
1155
+ * @return mixed
1156
+ */
1157
+ public function getGlyphPos()
1158
+ {
1159
+ return $this->glyphPos;
1160
+ }
1161
+
1162
+ /**
1163
+ * @return mixed
1164
+ */
1165
+ public function getCharToGlyph()
1166
+ {
1167
+ return $this->charToGlyph;
1168
+ }
1169
+
1170
+ /**
1171
+ * @return mixed
1172
+ */
1173
+ public function getAscent()
1174
+ {
1175
+ return $this->ascent;
1176
+ }
1177
+
1178
+ /**
1179
+ * @return mixed
1180
+ */
1181
+ public function getDescent()
1182
+ {
1183
+ return $this->descent;
1184
+ }
1185
+
1186
+ /**
1187
+ * @return mixed
1188
+ */
1189
+ public function getName()
1190
+ {
1191
+ return $this->name;
1192
+ }
1193
+
1194
+ /**
1195
+ * @return mixed
1196
+ */
1197
+ public function getFamilyName()
1198
+ {
1199
+ return $this->familyName;
1200
+ }
1201
+
1202
+ /**
1203
+ * @return mixed
1204
+ */
1205
+ public function getStyleName()
1206
+ {
1207
+ return $this->styleName;
1208
+ }
1209
+
1210
+ /**
1211
+ * @return mixed
1212
+ */
1213
+ public function getFullName()
1214
+ {
1215
+ return $this->fullName;
1216
+ }
1217
+
1218
+ /**
1219
+ * @return mixed
1220
+ */
1221
+ public function getUniqueFontID()
1222
+ {
1223
+ return $this->uniqueFontID;
1224
+ }
1225
+
1226
+ /**
1227
+ * @return mixed
1228
+ */
1229
+ public function getUnitsPerEm()
1230
+ {
1231
+ return $this->unitsPerEm;
1232
+ }
1233
+
1234
+ /**
1235
+ * @return mixed
1236
+ */
1237
+ public function getBbox()
1238
+ {
1239
+ return $this->bbox;
1240
+ }
1241
+
1242
+ /**
1243
+ * @return mixed
1244
+ */
1245
+ public function getCapHeight()
1246
+ {
1247
+ return $this->capHeight;
1248
+ }
1249
+
1250
+ /**
1251
+ * @return mixed
1252
+ */
1253
+ public function getStemV()
1254
+ {
1255
+ return $this->stemV;
1256
+ }
1257
+
1258
+ /**
1259
+ * @return mixed
1260
+ */
1261
+ public function getItalicAngle()
1262
+ {
1263
+ return $this->italicAngle;
1264
+ }
1265
+
1266
+ /**
1267
+ * @return mixed
1268
+ */
1269
+ public function getFlags()
1270
+ {
1271
+ return $this->flags;
1272
+ }
1273
+
1274
+ /**
1275
+ * @return mixed
1276
+ */
1277
+ public function getUnderlinePosition()
1278
+ {
1279
+ return $this->underlinePosition;
1280
+ }
1281
+
1282
+ /**
1283
+ * @return mixed
1284
+ */
1285
+ public function getUnderlineThickness()
1286
+ {
1287
+ return $this->underlineThickness;
1288
+ }
1289
+
1290
+ /**
1291
+ * @return mixed
1292
+ */
1293
+ public function getDefaultWidth()
1294
+ {
1295
+ return $this->defaultWidth;
1296
+ }
1297
+
1298
+ /**
1299
+ * @return int
1300
+ */
1301
+ public function getMaxStrLenRead()
1302
+ {
1303
+ return $this->maxStrLenRead;
1304
+ }
1305
+
1306
+ /**
1307
+ * @return mixed
1308
+ */
1309
+ public function getCodeToGlyph()
1310
+ {
1311
+ return $this->codeToGlyph;
1312
+ }
1313
+
1314
+ private function getHMetric($numberOfHMetrics, $gid)
1315
+ {
1316
+ $start = $this->seek_table("hmtx");
1317
+ if ($gid < $numberOfHMetrics) {
1318
+ $this->seek($start + ($gid * 4));
1319
+ $hm = fread($this->fh, 4);
1320
+ } else {
1321
+ $this->seek($start + (($numberOfHMetrics - 1) * 4));
1322
+ $hm = fread($this->fh, 2);
1323
+ $this->seek($start + ($numberOfHMetrics * 2) + ($gid * 2));
1324
+ $hm .= fread($this->fh, 2);
1325
+ }
1326
+ return $hm;
1327
+ }
1328
+
1329
+ private function getLOCA($indexToLocFormat, $numGlyphs)
1330
+ {
1331
+ $start = $this->seek_table('loca');
1332
+ $this->glyphPos = array();
1333
+ if ($indexToLocFormat == 0) {
1334
+ $data = $this->get_chunk($start, ($numGlyphs * 2) + 2);
1335
+ $arr = unpack("n*", $data);
1336
+ for ($n = 0; $n <= $numGlyphs; $n++) {
1337
+ $this->glyphPos[] = ($arr[$n + 1] * 2);
1338
+ }
1339
+ } else if ($indexToLocFormat == 1) {
1340
+ $data = $this->get_chunk($start, ($numGlyphs * 4) + 4);
1341
+ $arr = unpack("N*", $data);
1342
+ for ($n = 0; $n <= $numGlyphs; $n++) {
1343
+ $this->glyphPos[] = ($arr[$n + 1]);
1344
+ }
1345
+ } else
1346
+ throw new \RuntimeException('Unknown location table format ' . $indexToLocFormat);