-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtbs_plugin_opentbs.php
executable file
·3454 lines (2924 loc) · 114 KB
/
tbs_plugin_opentbs.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
/* OpenTBS version 1.7.6 (2012-06-06)
Author : Skrol29 (email: http://www.tinybutstrong.com/onlyyou.html)
Licence : LGPL
This class can open a zip file, read the central directory, and retrieve the content of a zipped file which is not compressed.
Site: http://www.tinybutstrong.com/plugins.php
*/
// Constants to drive the plugin.
define('OPENTBS_PLUGIN','clsOpenTBS');
define('OPENTBS_DOWNLOAD',1); // download (default) = TBS_OUTPUT
define('OPENTBS_NOHEADER',4); // option to use with DOWNLOAD: no header is sent
define('OPENTBS_FILE',8); // output to file = TBSZIP_FILE
define('OPENTBS_DEBUG_XML',16); // display the result of the current subfile
define('OPENTBS_STRING',32); // output to string = TBSZIP_STRING
define('OPENTBS_DEBUG_AVOIDAUTOFIELDS',64); // avoit auto field merging during the Show() method
define('OPENTBS_INFO','clsOpenTBS.Info'); // command to display the archive info
define('OPENTBS_RESET','clsOpenTBS.Reset'); // command to reset the changes in the current archive
define('OPENTBS_ADDFILE','clsOpenTBS.AddFile'); // command to add a new file in the archive
define('OPENTBS_DELETEFILE','clsOpenTBS.DeleteFile'); // command to delete a file in the archive
define('OPENTBS_REPLACEFILE','clsOpenTBS.ReplaceFile'); // command to replace a file in the archive
define('OPENTBS_FILEEXISTS','clsOpenTBS.FileExists');
define('OPENTBS_CHART','clsOpenTBS.Chart'); // command to delete a file in the archive
define('OPENTBS_DEFAULT',''); // Charset
define('OPENTBS_ALREADY_XML',false);
define('OPENTBS_ALREADY_UTF8','already_utf8');
define('OPENTBS_DEBUG_XML_SHOW','clsOpenTBS.DebugXmlShow');
define('OPENTBS_DEBUG_XML_CURRENT','clsOpenTBS.DebugXmlCurrent');
define('OPENTBS_DEBUG_INFO','clsOpenTBS.DebugInfo');
define('OPENTBS_DEBUG_CHART_LIST','clsOpenTBS.DebugInfo'); // deprecated
define('OPENTBS_FORCE_DOCTYPE','clsOpenTBS.ForceDocType');
define('OPENTBS_DELETE_ELEMENTS','clsOpenTBS.DeleteElements');
define('OPENTBS_SELECT_SHEET','clsOpenTBS.SelectSheet');
define('OPENTBS_SELECT_MAIN','clsOpenTBS.SelectMain');
define('OPENTBS_DISPLAY_SHEETS','clsOpenTBS.DisplaySheets');
define('OPENTBS_DELETE_SHEETS','clsOpenTBS.DeleteSheets');
define('OPENTBS_DELETE_COMMENTS','clsOpenTBS.DeleteComments');
class clsOpenTBS extends clsTbsZip {
function OnInstall() {
$TBS =& $this->TBS;
if (!isset($TBS->OtbsAutoLoad)) $TBS->OtbsAutoLoad = true; // TBS will load the subfile regarding to the extension of the archive
if (!isset($TBS->OtbsConvBr)) $TBS->OtbsConvBr = false; // string for NewLine conversion
if (!isset($TBS->OtbsAutoUncompress)) $TBS->OtbsAutoUncompress = $this->Meth8Ok;
if (!isset($TBS->OtbsConvertApostrophes)) $TBS->OtbsConvertApostrophes = true;
if (!isset($TBS->OtbsSpacePreserve)) $TBS->OtbsSpacePreserve = true;
$this->Version = '1.7.6';
$this->DebugLst = false; // deactivate the debug mode
$this->ExtInfo = false;
$TBS->TbsZip = &$this; // a shortcut
return array('BeforeLoadTemplate','BeforeShow', 'OnCommand', 'OnOperation', 'OnCacheField');
}
function BeforeLoadTemplate(&$File,&$Charset) {
$TBS =& $this->TBS;
if ($TBS->_Mode!=0) return; // If we are in subtemplate mode, the we use the TBS default process
// Decompose the file path. The syntaxe is 'Archive.ext#subfile', or 'Archive.ext', or '#subfile'
$p = strpos($File, '#');
if ($p===false) {
$FilePath = $File;
$SubFileLst = false;
} else {
$FilePath = substr($File,0,$p);
$SubFileLst = substr($File,$p+1);
}
// Open the archive
if ($FilePath!=='') {
$this->Open($FilePath); // Open the archive
$this->Ext_PrepareInfo(); // Set extension information
if ($TBS->OtbsAutoLoad && ($this->ExtInfo!==false) && ($SubFileLst===false)) {
// auto load files from the archive
$SubFileLst = $this->ExtInfo['load'];
$TBS->OtbsConvBr = $this->ExtInfo['br'];
}
$TBS->OtbsCurrFile = false;
$TBS->OtbsSubFileLst = $SubFileLst;
$this->TbsStoreLst = array();
$this->TbsCurrIdx = false;
} elseif ($this->ArchFile==='') {
$this->RaiseError('Cannot read file(s) "'.$SubFileLst.'" because no archive is opened.');
}
// Change the Charset if a new archive is opended, or if LoadTemplate is called explicitely for that
if (($FilePath!=='') || ($File==='')) {
if ($Charset===OPENTBS_ALREADY_XML) {
$TBS->LoadTemplate('', false); // Define the function for string conversion
} elseif ($Charset===OPENTBS_ALREADY_UTF8) {
$TBS->LoadTemplate('', array(&$this,'ConvXmlOnly')); // Define the function for string conversion
} else {
$TBS->LoadTemplate('', array(&$this,'ConvXmlUtf8')); // Define the function for string conversion
}
}
$TbsLoadTemplate = (($TBS->Render & OPENTBS_DEBUG_AVOIDAUTOFIELDS)!=OPENTBS_DEBUG_AVOIDAUTOFIELDS);
// Load the subfile(s)
if (($SubFileLst!=='') && ($SubFileLst!==false)) {
if (is_string($SubFileLst)) $SubFileLst = explode(';',$SubFileLst);
$ModeSave = $TBS->_Mode;
$TBS->_Mode++; // deactivate TplVars[] reset and Charset reset.
$TBS->Plugin(-4); // deactivate other plugins
foreach ($SubFileLst as $SubFile) {
$idx = $this->FileGetIdx($SubFile);
if ($idx===false) {
$this->RaiseError('The file "'.$SubFile.'" is not found in the archive "'.$this->ArchFile.'".');
} elseif ($idx!==$this->TbsCurrIdx) {
// Save the current loaded subfile if any
$this->TbsStorePark();
// load the subfile
$TBS->Source = $this->TbsStoreGet($idx, false);
if ($this->LastReadNotStored) {
if ($this->LastReadComp<=0) { // the contents is not compressed
if ($this->ExtInfo!==false) {
$i = $this->ExtInfo;
if (isset($i['rpl_what'])) $TBS->Source = str_replace($i['rpl_what'], $i['rpl_with'], $TBS->Source); // auto replace strings in the loaded file
if (($i['ext']==='docx') && isset($TBS->OtbsClearMsWord) && $TBS->OtbsClearMsWord) $this->MsWord_Clean($TBS->Source);
if (($i['ext']==='xlsx') && isset($TBS->OtbsMsExcelConsistent) && isset($TBS->OtbsMsExcelConsistent) ) {
$this->MsExcel_DeleteFormulaResults($TBS->Source);
$this->MsExcel_ConvertToRelative($TBS->Source);
}
}
// apply default TBS behaviors on the uncompressed content: other plug-ins + [onload] fields
if ($TbsLoadTemplate) $TBS->LoadTemplate(null,'+');
}
}
$TBS->OtbsCurrFile = $SubFile;
$this->TbsCurrIdx = $idx;
}
}
// Reactivate default configuration
$TBS->_Mode = $ModeSave;
$TBS->Plugin(-10); // reactivate other plugins
}
if ($FilePath!=='') $TBS->_LastFile = $FilePath;
return false; // default LoadTemplate() process is not executed
}
function BeforeShow(&$Render, $File='') {
$TBS =& $this->TBS;
if ($TBS->_Mode!=0) return; // If we are in subtemplate mode, the we use the TBS default process
$this->TbsStorePark(); // Save the current loaded subfile if any
$TBS->Plugin(-4); // deactivate other plugins
$Debug = (($Render & OPENTBS_DEBUG_XML)==OPENTBS_DEBUG_XML);
if ($Debug) $this->DebugLst = array();
$TbsShow = (($Render & OPENTBS_DEBUG_AVOIDAUTOFIELDS)!=OPENTBS_DEBUG_AVOIDAUTOFIELDS);
if (isset($this->OtbsSheetODS)) $this->OpenDoc_SheetDeleteAndDisplay();
if (isset($this->OtbsSheetXLSX)) $this->MsExcel_SheetDeleteAndDisplay();
if ($this->Ext_Get()=='docx') $this->MsWord_RenumDocPr();
// Merges all modified subfiles
$idx_lst = array_keys($this->TbsStoreLst);
foreach ($idx_lst as $idx) {
$TBS->Source = $this->TbsStoreLst[$idx]['src'];
$onshow = $this->TbsStoreLst[$idx]['onshow'];
unset($this->TbsStoreLst[$idx]); // save memory space
$TBS->OtbsCurrFile = $this->TbsGetFileName($idx); // usefull for TbsPicAdd()
$this->TbsCurrIdx = $idx; // usefull for debug mode
if ($TbsShow && $onshow) $TBS->Show(TBS_NOTHING);
if ($Debug) $this->DebugLst[$this->TbsGetFileName($idx)] = $TBS->Source;
$this->FileReplace($idx, $TBS->Source, TBSZIP_STRING, $TBS->OtbsAutoUncompress);
}
$TBS->Plugin(-10); // reactivate other plugins
$this->TbsCurrIdx = false;
if (isset($this->OpenXmlRid)) $this->OpenXML_RidCommit($Debug); // Commit special OpenXML features if any
if (isset($this->OpenXmlCTypes)) $this->OpenXML_CTypesCommit($Debug); // Commit special OpenXML features if any
if (isset($this->OpenDocManif)) $this->OpenDoc_ManifestCommit($Debug); // Commit special OpenDocument features if any
if ( ($TBS->ErrCount>0) && (!$TBS->NoErr) && (!$Debug)) {
$TBS->meth_Misc_Alert('Show() Method', 'The output is cancelled by the OpenTBS plugin because at least one error has occured.');
exit;
}
if ($Debug) {
// Do the debug even if other options are used
$this->TbsDebug_Merge(true, false);
} elseif (($Render & TBS_OUTPUT)==TBS_OUTPUT) { // notice that TBS_OUTPUT = OPENTBS_DOWNLOAD
// download
$ContentType = (isset($this->ExtInfo['ctype'])) ? $this->ExtInfo['ctype'] : '';
$this->Flush($Render, $File, $ContentType); // $Render is used because it can contain options OPENTBS_DOWNLOAD and OPENTBS_NOHEADER.
$Render = $Render - TBS_OUTPUT; //prevent TBS from an extra output.
} elseif(($Render & OPENTBS_FILE)==OPENTBS_FILE) {
// to file
$this->Flush(TBSZIP_FILE, $File);
} elseif(($Render & OPENTBS_STRING)==OPENTBS_STRING) {
// to string
$this->Flush(TBSZIP_STRING);
$TBS->Source = $this->OutputSrc;
$this->OutputSrc = '';
}
if (($Render & TBS_EXIT)==TBS_EXIT) {
$this->Close();
exit;
}
return false; // cancel the default Show() process
}
function OnCacheField($BlockName,&$Loc,&$Txt,$PrmProc) {
if (isset($Loc->PrmLst['ope'])) {
// Prepare to change picture
$ope_lst = explode(',', $Loc->PrmLst['ope']); // in this event, ope is not exploded
if (in_array('changepic', $ope_lst)) {
$this->TbsPicFound($Txt, $Loc); // add parameter "att" which will be processed just after this event, when the field is cached
$Loc->PrmLst['pic_change'] = true;
}
// Change cell type in ODS files
if (strpos(','.$Loc->PrmLst['ope'],',ods')!==false) {
foreach($ope_lst as $ope) {
if (substr($ope,0,3)==='ods') {
$x = '';
$this->OpenDoc_ChangeCellType($Txt, $Loc, $ope, false, $x);
return; // do it only once
}
}
}
// Change cell type in XLSX files
if (strpos(','.$Loc->PrmLst['ope'],',xlsx')!==false) {
foreach($ope_lst as $ope) {
if (substr($ope,0,4)==='xlsx') {
$x = '';
$this->MsExcel_ChangeCellType($Txt, $Loc, $ope);
return; // do it only once
}
}
}
}
}
function OnOperation($FieldName,&$Value,&$PrmLst,&$Txt,$PosBeg,$PosEnd,&$Loc) {
// in this event, ope is exploded, there is one function call for each ope command
$ope = $PrmLst['ope'];
if ($ope==='addpic') {
$this->TbsPicAdd($Value, $PrmLst, $Txt, $Loc, 'ope=addpic');
} elseif ($ope==='changepic') {
if (!isset($PrmLst['pic_change'])) {
$this->TbsPicFound($Txt, $Loc); // add parameter "att" which will be processed just before the value is merged
$PrmLst['pic_change'] = true;
}
$this->TbsPicAdd($Value, $PrmLst, $Txt, $Loc, 'ope=changepic');
} elseif(substr($ope,0,4)==='xlsx') {
if (!isset($Loc->PrmLst['xlsxok'])) $this->MsExcel_ChangeCellType($Txt, $Loc, $ope);
switch ($Loc->PrmLst['xlsxok']) {
case 'xlsxNum':
if (is_numeric($Value)) {
// we have to check contents in order to avoid Excel errors. Note that value '0.00000000000000' makes an Excel error.
if (strpos($Value,'e')!==false) { // exponential representation
$Value = (float) $Value;
} elseif (strpos($Value,'x')!==false) { // hexa representation
$Value = hexdec($Value);
} elseif (strpos($Value,'.')===false) {
$Value = (integer) $Value;
} else {
$Value = (float) $Value;
}
} else {
$Value = '';
}
$Value = (is_numeric($Value)) ? ''.$Value : '';
break;
case 'xlsxBool':
$Value = ($Value) ? 1 : 0;
break;
case 'xlsxDate':
if (is_string($Value)) {
$t = strtotime($Value); // We look if it's a date
} else {
$t = $Value;
}
if (($t===-1) or ($t===false)) { // Date not recognized
$Value = '';
} elseif ($t===943916400) { // Date to zero
$Value = '';
} else { // It's a date
$Value = ($t/86400.00)+25569; // unix: 1 means 01/01/1970, xls: 1 means 01/01/1900
}
break;
default:
// do nothing
}
} elseif(substr($ope,0,3)==='ods') {
if (!isset($Loc->PrmLst['odsok'])) $this->OpenDoc_ChangeCellType($Txt, $Loc, $ope, true, $Value);
}
}
function OnCommand($Cmd, $x1=null, $x2=null, $x3=null, $x4=null, $x5=null) {
if ($Cmd==OPENTBS_INFO) {
// Display debug information
echo "<strong>OpenTBS plugin Information</strong><br>\r\n";
return $this->Debug();
} elseif ($Cmd==OPENTBS_RESET) {
// Reset all mergings
$this->ArchCancelModif();
$this->TbsStoreLst = array();
$TBS =& $this->TBS;
$TBS->Source = '';
$TBS->OtbsCurrFile = false;
if (is_string($TBS->OtbsSubFileLst)) {
$f = '#'.$TBS->OtbsSubFileLst;
$h = '';
$this->BeforeLoadTemplate($f,$h);
}
return true;
} elseif ( ($Cmd==OPENTBS_ADDFILE) || ($Cmd==OPENTBS_REPLACEFILE) ) {
// Add a new file or cancel a previous add
$Name = (is_null($x1)) ? false : $x1;
$Data = (is_null($x2)) ? false : $x2;
$DataType = (is_null($x3)) ? TBSZIP_STRING : $x3;
$Compress = (is_null($x4)) ? true : $x4;
if ($Cmd==OPENTBS_ADDFILE) {
return $this->FileAdd($Name, $Data, $DataType, $Compress);
} else {
return $this->FileReplace($Name, $Data, $DataType, $Compress);
}
} elseif ($Cmd==OPENTBS_DELETEFILE) {
// Delete an existing file in the archive
$Name = (is_null($x1)) ? false : $x1;
$this->FileCancelModif($Name, false); // cancel added files
return $this->FileReplace($Name, false); // mark the file as to be deleted
} elseif ($Cmd==OPENTBS_FILEEXISTS) {
return $this->FileExists($x1);
} elseif ($Cmd==OPENTBS_CHART) {
$ChartNameOrNum = $x1;
$SeriesNameOrNum = $x2;
$NewValues = (is_null($x3)) ? false : $x3;
$NewLegend = (is_null($x4)) ? false : $x4;
$CopyFromSeries = (is_null($x5)) ? false : $x5;
return $this->OpenXML_ChartChangeSeries($ChartNameOrNum, $SeriesNameOrNum, $NewValues, $NewLegend, $CopyFromSeries);
} elseif ( ($Cmd==OPENTBS_DEBUG_INFO) || ($Cmd==OPENTBS_DEBUG_CHART_LIST) ) {
if (is_null($x1)) $x1 = true;
$this->TbsDebug_Info($x1);
} elseif ($Cmd==OPENTBS_DEBUG_XML_SHOW) {
$this->TBS->Show(OPENTBS_DEBUG_XML);
} elseif ($Cmd==OPENTBS_DEBUG_XML_CURRENT) {
$this->TbsStorePark();
$this->DebugLst = array();
foreach ($this->TbsStoreLst as $idx=>$park) $this->DebugLst[$this->TbsGetFileName($idx)] = $park['src'];
$this->TbsDebug_Merge(true, true);
} elseif($Cmd==OPENTBS_FORCE_DOCTYPE) {
return $this->Ext_PrepareInfo($x1);
} elseif ($Cmd==OPENTBS_DELETE_ELEMENTS) {
if (is_string($x1)) $x1 = explode(',', $x1);
if (is_null($x2)) $x2 = false; // OnlyInner
return $this->XML_DeleteElements($this->TBS->Source, $x1, $x2);
} elseif ($Cmd==OPENTBS_SELECT_MAIN) {
if ( ($this->ExtInfo!==false) && isset($this->ExtInfo['main']) ) {
$this->TBS->LoadTemplate('#'.$this->ExtInfo['main']);
return true;
} else {
return false;
}
} elseif ($Cmd==OPENTBS_SELECT_SHEET) {
// Only XLSX files have sheets in separated subfiles.
if ($this->Ext_Get()==='xlsx') {
$loc = $this->MsExcel_SheetGet($x1, $Cmd, true);
if ($loc==false) return;
if ($this->FileExists($loc->xlsxTarget)) {
$this->TBS->LoadTemplate('#'.$loc->xlsxTarget);
} else {
return $this->RaiseError("($Cmd) sub-file '".$loc->xlsxTarget."' is not found inside the Workbook.");
}
}
return true;
} elseif ( ($Cmd==OPENTBS_DELETE_SHEETS) || ($Cmd==OPENTBS_DISPLAY_SHEETS) ) {
if (is_null($x2)) $x2 = true; // default value
$delete = ($Cmd==OPENTBS_DELETE_SHEETS);
$ext = $this->Ext_Get();
if (!isset($this->OtbsSheetOk)) {
if ($ext=='xlsx') $this->OtbsSheetXLSX = true;
if ($ext=='ods') $this->OtbsSheetODS = true;
$this->OtbsSheetDelete = array();
$this->OtbsSheetVisible = array();
$this->OtbsSheetOk = true;
}
$x2 = (boolean) $x2;
if (!is_array($x1)) $x1 = array($x1);
foreach ($x1 as $sheet=>$action) {
if (!is_bool($action)) {
$sheet = $action;
$action = $x2;
}
$sheet_ref = (is_string($sheet)) ? 'n:'.htmlspecialchars($sheet) : 'i:'.$sheet; // help to make the difference beetween id and name
if ($delete) {
if ($x2) {
$this->OtbsSheetDelete[$sheet_ref] = $sheet;
} else {
unset($this->OtbsSheetDelete[$sheet_ref]);
}
} else {
$this->OtbsSheetVisible[$sheet_ref] = $x2;
}
}
} elseif ($Cmd==OPENTBS_DELETE_COMMENTS) {
// Default values
$MainTags = false;
$CommFiles = false;
$CommTags = false;
$Inner = false;
if ($this->Ext_GetFrm()=='odf') {
$MainTags = array('office:annotation', 'officeooo:annotation'); // officeooo:annotation is used in ODP Presentations
} else {
switch ($this->Ext_Get()) {
case 'docx':
$MainTags = array('w:commentRangeStart', 'w:commentRangeEnd', 'w:commentReference');
$CommFiles = array('wordprocessingml.comments+xml');
$CommTags = array('w:comment');
$Inner = true;
break;
case 'xlsx':
$CommFiles = array('spreadsheetml.comments+xml');
$CommTags = array('comment', 'author');
break;
case 'pptx':
$CommFiles = array('presentationml.comments+xml');
$CommTags = array('p:cm');
break;
default:
return 0;
}
}
return $this->TbsDeleteComments($MainTags, $CommFiles, $CommTags, $Inner);
}
}
function TbsStorePark() {
// save the last opened subfile into the store, and close the subfile
if ($this->TbsCurrIdx!==false) {
$this->TbsStoreLst[$this->TbsCurrIdx] = array('src'=>$this->TBS->Source, 'onshow'=>true);
$this->TBS->Source = '';
$this->TbsCurrIdx = false;
}
}
function TbsStorePut($idx, $src, $onshow = null) {
// Save a given source in the store. If $onshow is null, then it stay unchanged
if ($idx===$this->TbsCurrIdx) {
$this->TBS->Source = $src;
} else {
if (is_null($onshow)) {
if (isset($this->TbsStoreLst[$idx])) {
$onshow = $this->TbsStoreLst[$idx]['onshow'];
} else {
$onshow = false;
}
}
$this->TbsStoreLst[$idx] = array('src'=>$src, 'onshow'=>$onshow);
}
}
function TbsStoreGet($idx, $caller) {
// retrieve a source from the merging, the store, or the archive
// the file is not stored yet if it comes from the archive
$this->LastReadNotStored = false;
if ($idx===$this->TbsCurrIdx) {
return $this->TBS->Source;
} elseif (isset($this->TbsStoreLst[$idx])) {
return $this->TbsStoreLst[$idx]['src'];
} else {
$this->LastReadNotStored = true;
$txt = $this->FileRead($idx, true);
if ($this->LastReadComp>0) {
if ($caller===false) {
return $txt; // return the uncompressed contents
} else {
return $this->RaiseError("(".$caller.") unable to uncompress '".$this->TbsGetFileName($idx)."'.");
}
} else {
return $txt;
}
}
}
function TbsGetFileName($idx) {
if (isset($this->CdFileLst[$idx])) {
return $this->CdFileLst[$idx]['v_name'];
} else {
return '(id='.$idx.')';
}
}
function TbsDebug_Init(&$nl, &$sep, &$bull, $type) {
// display the header of debug mode
$nl = "\n";
$sep = str_repeat('-',30);
$bull = $nl.' - ';
if (isset($this->DebugInit)) return;
$this->DebugInit = true;
if (!headers_sent()) header('Content-Type: text/plain; charset="UTF-8"');
echo "* OPENTBS DEBUG MODE: if the star, (*) on the left before the word OPENTBS, is not the very first character of this page, then your
merged Document will be corrupted when you use the OPENTBS_DOWNLOAD option. If there is a PHP error message, then you have to fix it.
If they are blank spaces, line beaks, or other unexpected characters, then you have to check your code in order to avoid them.";
echo $nl;
echo $nl.$sep.$nl.'INFORMATION'.$nl.$sep;
echo $nl.'* Debug command: '.$type;
echo $nl.'* OpenTBS version: '.$this->Version;
echo $nl.'* TinyButStrong version: '.$this->TBS->Version;
echo $nl.'* PHP version: '.PHP_VERSION;
echo $nl.'* Opened document: '.$this->ArchFile;
echo $nl.'* Activated features for document type: '.(($this->ExtInfo===false) ? '(none)' : $this->ExtInfo['frm'].'/'.$this->ExtInfo['ext']);
}
function TbsDebug_Info($Exit) {
$this->TbsDebug_Init($nl, $sep, $bull, 'OPENTBS_DEBUG_INFO');
if ($this->Ext_Get()==='xlsx') $this->MsExcel_SheetDebug($nl, $sep, $bull);
if ($this->Ext_Get()==='ods') $this->OpenDoc_SheetDebug($nl, $sep, $bull);
if ($this->Ext_GetFrm()==='openxml') {
$this->OpenXML_ChartDebug($nl, $sep, $bull);
}
if ($Exit) exit;
}
function TbsDebug_Merge($XmlFormat = true, $Current) {
// display modified and added files
$this->TbsDebug_Init($nl, $sep, $bull, ($Current ? 'OPENTBS_DEBUG_XML_CURRENT' :'OPENTBS_DEBUG_XML_SHOW'));
// scann files for collecting information
$mod_lst = ''; // id of modified files
$del_lst = ''; // id of deleted files
$add_lst = ''; // id of added files
// files marked as replaced in TbsZip
$idx_lst = array_keys($this->ReplInfo);
foreach ($idx_lst as $idx) {
$name = $this->TbsGetFileName($idx);
if ($this->ReplInfo[$idx]===false) {
$del_lst .= $bull.$name;
} else {
$mod_lst .= $bull.$name;
}
}
// files marked as modified in the Park
$idx_lst = array_keys($this->TbsStoreLst);
foreach ($idx_lst as $idx) {
if (!isset($this->ReplInfo[$idx])) {
$mod_lst .= $bull.$this->TbsGetFileName($idx);
}
}
// files marked as added in TbsZip
$idx_lst = array_keys($this->AddInfo);
foreach ($idx_lst as $idx) {
$name = $this->AddInfo[$idx]['name'];
$add_lst .= $bull.$name;
}
if ($mod_lst==='') $mod_lst = ' none';
if ($del_lst==='') $del_lst = ' none';
if ($add_lst==='') $add_lst = ' none';
echo $nl.'* Deleted files in the archive:'.$del_lst;
echo $nl.'* Added files in the archive:'.$add_lst;
echo $nl.'* Modified files in the archive:'.$mod_lst;
echo $nl;
// display contents merged with OpenTBS
foreach ($this->DebugLst as $name=>$src) {
$x = trim($src);
$info = '';
$xml = ((strlen($x)>0) && $x[0]==='<');
if ($XmlFormat && $xml) {
$info = ' (XML reformated for debuging only)';
$src = $this->XmlFormat($src);
}
echo $nl.$sep;
echo $nl.'File merged with OpenTBS'.$info.': '.$name;
echo $nl.$sep;
echo $nl.$src;
}
}
function ConvXmlOnly($Txt, $ConvBr) {
// Used by TBS to convert special chars and new lines.
$x = htmlspecialchars($Txt);
if ($ConvBr) $this->ConvBr($x);
return $x;
}
function ConvXmlUtf8($Txt, $ConvBr) {
// Used by TBS to convert special chars and new lines.
$x = htmlspecialchars(utf8_encode($Txt));
if ($ConvBr) $this->ConvBr($x);
return $x;
}
function ConvBr(&$x) {
$z = $this->TBS->OtbsConvBr;
if ($z===false) return;
$x = nl2br($x); // Convert any type of line break
$x = str_replace("\r", '' ,$x);
$x = str_replace("\n", '' ,$x);
$x = str_replace('<br />',$z ,$x);
}
function XmlFormat($Txt) {
// format an XML source the be nicely aligned
// delete line breaks
$Txt = str_replace("\r",'',$Txt);
$Txt = str_replace("\n",'',$Txt);
// init values
$p = 0;
$lev = 0;
$Res = '';
$to = true;
while ($to!==false) {
$to = strpos($Txt,'<',$p);
if ($to!==false) {
$tc = strpos($Txt,'>',$to);
if ($to===false) {
$to = false; // anomaly
} else {
// get text between the tags
$x = trim(substr($Txt, $p, $to-$p),' ');
if ($x!=='') $Res .= "\n".str_repeat(' ',max($lev,0)).$x;
// get the tag
$x = substr($Txt, $to, $tc-$to+1);
if ($Txt[$to+1]==='/') $lev--;
$Res .= "\n".str_repeat(' ',max($lev,0)).$x;
// change the level
if (($Txt[$to+1]!=='?') && ($Txt[$to+1]!=='/') && ($Txt[$tc-1]!=='/')) $lev++;
// next position
$p = $tc + 1;
}
}
}
$Res = substr($Res, 1); // delete the first line break
if ($p<strlen($Txt)) $Res .= trim(substr($Txt, $p), ' '); // complete the end
return $Res;
}
function RaiseError($Msg, $NoErrMsg=false) {
// Overwrite the parent RaiseError() method.
$exit = (!$this->TBS->NoErr);
if ($exit) $Msg .= ' The process is ending, unless you set NoErr property to true.';
$this->TBS->meth_Misc_Alert('OpenTBS Plugin', $Msg, $NoErrMsg);
if ($exit) {
if ($this->DebugLst!==false) {
if ($this->TbsCurrIdx!==false) $this->DebugLst[$this->TbsGetFileName($this->TbsCurrIdx)] = $this->TBS->Source;
$this->TbsDebug_Merge(true, false);
}
exit;
}
return false;
}
function TbsPicFound($Txt, &$Loc) {
// Found the relevent attribute for the image source, and then add parameter 'att' to the TBS locator.
$att = false;
if (isset($this->ExtInfo['frm'])) {
if ($this->ExtInfo['frm']==='odf') {
$att = 'draw:image#xlink:href';
if (isset($Loc->PrmLst['adjust'])) $Loc->otbsDim = $this->TbsPicGetDim_ODF($Txt, $Loc->PosBeg);
} elseif ($this->ExtInfo['frm']==='openxml') {
$att = $this->OpenXML_FirstPicAtt($Txt, $Loc->PosBeg, true);
if ($att===false) return $this->RaiseError('Parameter ope=changepic used in the field ['.$Loc->FullName.'] has failed to found the picture.');
if (isset($Loc->PrmLst['adjust'])) {
if (strpos($att,'v:imagedata')!==false) {
$Loc->otbsDim = $this->TbsPicGetDim_OpenXML_vml($Txt, $Loc->PosBeg);
} else {
$Loc->otbsDim = $this->TbsPicGetDim_OpenXML_dml($Txt, $Loc->PosBeg);
}
}
}
} else {
return $this->RaiseError('Parameter ope=changepic used in the field ['.$Loc->FullName.'] is not supported with the current document type.');
}
if ($att!==false) {
if (isset($Loc->PrmLst['att'])) {
return $this->RaiseError('Parameter att is used with parameter ope=changepic in the field ['.$Loc->FullName.']. changepic will be ignored');
} else {
$Loc->PrmLst['att'] = $att;
}
}
return true;
}
function TbsPicAdjust(&$Txt, &$Loc, &$File) {
// Adjust the dimensions if the picture
$fDim = @getimagesize($File); // file dimensions
if (!is_array($fDim)) return;
$w = (float) $fDim[0];
$h = (float) $fDim[1];
$r = ($w/$h);
$delta = 0;
$adjust = $Loc->PrmLst['adjust'];
if ( (!is_string($adjust)) || ($adjust=='') ) $adjust = 'inside';
if (strpos($adjust, '%')!==false) {
$adjust_coef = floatval(str_replace('%','',$adjust))/100.0;
$adjust = '%';
}
foreach ($Loc->otbsDim as $tDim) { // template dimensions. They must be sorted in reverse order of location
if ($tDim!==false) {
// find what dimensions should be edited
if ($adjust=='%') {
if ($tDim['wb']>$tDim['hb']) { // the last attribute must be processed first
$edit_lst = array('w' => $adjust_coef * $w, 'h' => $adjust_coef * $h );
} else {
$edit_lst = array('h' => $adjust_coef * $h, 'w' => $adjust_coef * $w );
}
} elseif ($adjust=='samewidth') {
$edit_lst = array('h' => $tDim['wv'] * $h / $w );
} elseif ($adjust=='sameheight') {
$edit_lst = array('w' => $r * $tDim['hv'] );
} else { // default value
if ($tDim['r']>=$r) {
$edit_lst = array('w' => $r * $tDim['hv'] ); // adjust width
} else {
$edit_lst = array('h' => $tDim['wv'] * $h / $w ); // adjust height
}
}
// edit dimensions
foreach ($edit_lst as $what=>$new) {
$beg = $tDim[$what.'b'];
$len = $tDim[$what.'l'];
$unit = $tDim[$what.'u'];
if ($adjust=='%') {
if ($tDim['cpt']!==false) $new = $new * $tDim['cpt']; // apply the coef to Point conversion if any
if ($unit!=='') { // force unit to pt, if units are allowed
$unit = 'pt';
}
}
$new = number_format($new, $tDim['dec'], '.', '').$unit;
$Txt = substr_replace($Txt, $new, $beg, $len);
if ($Loc->PosBeg>$beg) $delta = $delta + strlen($new) - $len;
}
}
}
if ($delta<>0) {
$Loc->PosBeg = $Loc->PosBeg + $delta;
$Loc->PosEnd = $Loc->PosEnd + $delta;
}
}
function TbsPicGetDim_ODF($Txt, $Pos) {
// Found the attributes for the image dimensions, in an ODF file
// unit (can be: mm, cm, in, pi, pt)
$dim = $this->TbsPicGetDim_Any($Txt, $Pos, 'draw:frame', 'svg:width="', 'svg:height="', 3, false);
return array($dim);
}
function TbsPicGetDim_OpenXML_vml($Txt, $Pos) {
$dim = $this->TbsPicGetDim_Any($Txt, $Pos, 'v:shape', 'width:', 'height:', 2, false);
return array($dim);
}
function TbsPicGetDim_OpenXML_dml($Txt, $Pos) {
$dim_shape = $this->TbsPicGetDim_Any($Txt, $Pos, 'wp:extent', 'cx="', 'cy="', 0, 12700);
$dim_inner = $this->TbsPicGetDim_Any($Txt, $Pos, 'a:ext' , 'cx="', 'cy="', 0, 12700);
if ( ($dim_inner!==false) && ($dim_inner['wb']<$dim_shape['wb']) ) $dim_inner = false; // <a:ext> isoptional but must always be after the corresponding <wp:extent>, otherwise it may be the <a:ext> of another picture
return array($dim_inner, $dim_shape); // dims must be soerted in reverse order of location
}
function TbsPicGetDim_Any($Txt, $Pos, $Element, $AttW, $AttH, $AllowedDec, $CoefToPt) {
// Found the attributes for the image dimensions, in an ODF file
$p = clsTinyButStrong::f_Xml_FindTagStart($Txt, $Element, true, $Pos, false, true);
if ($p===false) return false;
$pe = strpos($Txt, '>', $p);
if ($pe===false) return false;
$x = substr($Txt, $p, $pe -$p);
$att_lst = array('w'=>$AttW, 'h'=>$AttH);
$res_lst = array();
foreach ($att_lst as $i=>$att) {
$l = strlen($att);
$b = strpos($x, $att);
if ($b===false) return false;
$b = $b + $l;
$e = strpos($x, '"', $b);
$e2 = strpos($x, ';', $b); // in case of VML format, width and height are styles separted by ;
if ($e2!==false) $e = min($e, $e2);
if ($e===false) return false;
$lt = $e - $b;
$t = substr($x, $b, $lt);
$pu = $lt; // unit first char
while ( ($pu>1) && (!is_numeric($t[$pu-1])) ) $pu--;
$u = ($pu>=$lt) ? '' : substr($t, $pu);
$v = floatval(substr($t, 0, $pu));
$res_lst[$i.'b'] = ($p+$b); // start
$res_lst[$i.'l'] = $lt; // length of the text
$res_lst[$i.'u'] = $u; // unit
$res_lst[$i.'v'] = $v; // value
$res_lst[$i.'t'] = $t; // text
}
$res_lst['r'] = ($res_lst['hv']==0) ? 0.0 : $res_lst['wv']/$res_lst['hv']; // ratio W/H
$res_lst['dec'] = $AllowedDec; // save the allowed decimal for this attribute
$res_lst['cpt'] = $CoefToPt;
return $res_lst;
}
function TbsPicAdd(&$Value, &$PrmLst, &$Txt, &$Loc, $Prm) {
// Add a picture inside the archive, use parameters 'from' and 'as'.
// Argument $Prm is only used for error messages.
$TBS = &$this->TBS;
// set the path where files should be taken
if (isset($PrmLst['from'])) {
if (!isset($PrmLst['pic_prepared'])) $TBS->meth_Merge_AutoVar($PrmLst['from'],true); // merge automatic TBS fields in the path
$FullPath = str_replace($TBS->_ChrVal,$Value,$PrmLst['from']); // merge [val] fields in the path
} else {
$FullPath = $Value;
}
if ( (!isset($PrmLst['pic_prepared'])) && isset($PrmLst['default']) ) $TBS->meth_Merge_AutoVar($PrmLst['default'],true); // merge automatic TBS fields in the path
$ok = true; // true if the picture file is actually inserted and ready to be changed
// check if the picture exists, and eventually use the default picture
if (!file_exists($FullPath)) {
if (isset($PrmLst['default'])) {
$x = $PrmLst['default'];
if ($x==='current') {
$ok = false;
} elseif (file_exists($x)) {
$FullPath = $x;
} else {
$ok = $this->RaiseError('The default picture "'.$x.'" defined by parameter "default" of the field ['.$Loc->FullName.'] is not found.');
}
} else {
$ok = $this->RaiseError('The picture "'.$FullPath.'" that is supposed to be added because of parameter "'.$Prm.'" of the field ['.$Loc->FullName.'] is not found. You can use parameter default=current to cancel this message');
}
}
// set the name of the internal file
if (isset($PrmLst['as'])) {
if (!isset($PrmLst['pic_prepared'])) $TBS->meth_Merge_AutoVar($PrmLst['as'],true); // merge automatic TBS fields in the path
$InternalPath = str_replace($TBS->_ChrVal,$Value,$PrmLst['as']); // merge [val] fields in the path
} else {
$InternalPath = basename($FullPath);
}
if ($ok) {
// the value of the current TBS field becomes the full internal path
if (isset($this->ExtInfo['pic_path'])) $InternalPath = $this->ExtInfo['pic_path'].$InternalPath;
// actually add the picture inside the archive
if ($this->FileGetIdxAdd($InternalPath)===false) $this->FileAdd($InternalPath, $FullPath, TBSZIP_FILE, true);
// preparation for others file in the archive
$Rid = false;
$Frm = $this->Ext_GetFrm();
if ($Frm==='odf') {
// OpenOffice document
$this->OpenDoc_ManifestChange($InternalPath,'');
} elseif ($Frm==='openxml') {
// Microsoft Office document
$this->OpenXML_CTypesPrepare($InternalPath, '');
$Rid = $this->OpenXml_RidPrepare($TBS->OtbsCurrFile, basename($InternalPath));
}
// change the value of the field for the merging process
if ($Rid===false) {
$Value = $InternalPath;
} else {
$Value = $Rid; // the Rid is used instead of the file name for the merging
}
}
// Change the dimensions of the picture
if (isset($Loc->otbsDim)) {
if (isset($Loc->AttForward)) { // the field has been already moved by parameter att
if (!isset($Loc->otbsRealBeg)) { // save the real position of the field
$Loc->otbsRealBeg = $Loc->PosBeg;
$Loc->otbsRealEnd = $Loc->PosEnd;
} else { // restore the real position of the field
$Loc->PosBeg = $Loc->otbsRealBeg;
$Loc->PosEnd = $Loc->otbsRealEnd;
}
}
if ($ok) $this->TbsPicAdjust($Txt, $Loc, $FullPath);
}
// Unchanged value (must be done after redim)
if (!$ok) $Value = substr($Txt, $Loc->PosBeg, $Loc->PosEnd - $Loc->PosBeg + 1);
$PrmLst['pic_prepared'] = true; // mark the locator as Picture prepared
return $ok;
}
// Check after the sheet process
function TbsSheetCheck() {
if (count($this->OtbsSheetDelete)>0) $this->RaiseError("Unable to delete the following sheets because they are not found in the workbook: ".(str_replace(array('i:','n:'),'',implode(', ',$this->OtbsSheetDelete))).'.');
if (count($this->OtbsSheetVisible)>0) $this->RaiseError("Unable to change visibility of the following sheets because they are not found in the workbook: ".(str_replace(array('i:','n:'),'',implode(', ',array_keys($this->OtbsSheetVisible)))).'.');
}
function TbsDeleteComments($MainTags, $CommFiles, $CommTags, $Inner) {
$nbr = 0;
// Retrieve the Comment sub-file (OpenXML only)
if ($CommFiles!==false) {
$Files = $this->OpenXML_MapGetFiles($CommFiles);
foreach ($Files as $file) {
$idx = $this->FileGetIdx($file);
if ($idx!==false) {
// Delete inner text of the comments to be sure that contents is deleted
// we only empty the comment elements in case some comments are referenced in other special part of the document
$Txt = $this->TbsStoreGet($idx, "Delete Comments");
$nbr = $nbr + $this->XML_DeleteElements($Txt, $CommTags, $Inner);
$this->TbsStorePut($idx, $Txt);