@@ -62,6 +62,27 @@ class TemplateProcessor
62
62
*/
63
63
protected $ tempDocumentFooters = array ();
64
64
65
+ /**
66
+ * Document relations (in XML format) of the temporary document.
67
+ *
68
+ * @var string[]
69
+ */
70
+ protected $ tempDocumentRelations = array ();
71
+
72
+ /**
73
+ * Document content types (in XML format) of the temporary document.
74
+ *
75
+ * @var string
76
+ */
77
+ protected $ tempDocumentContentTypes = "" ;
78
+
79
+ /**
80
+ * new inserted images list
81
+ *
82
+ * @var string[]
83
+ */
84
+ protected $ tempDocumentNewImages = array ();
85
+
65
86
/**
66
87
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception.
67
88
*
@@ -88,19 +109,32 @@ public function __construct($documentTemplate)
88
109
$ this ->zipClass ->open ($ this ->tempDocumentFilename );
89
110
$ index = 1 ;
90
111
while (false !== $ this ->zipClass ->locateName ($ this ->getHeaderName ($ index ))) {
91
- $ this ->tempDocumentHeaders [$ index ] = $ this ->fixBrokenMacros (
92
- $ this ->zipClass ->getFromName ($ this ->getHeaderName ($ index ))
93
- );
112
+ $ this ->tempDocumentHeaders [$ index ] = $ this ->readPartWithRels ($ this ->getHeaderName ($ index ));
94
113
$ index ++;
95
114
}
96
115
$ index = 1 ;
97
116
while (false !== $ this ->zipClass ->locateName ($ this ->getFooterName ($ index ))) {
98
- $ this ->tempDocumentFooters [$ index ] = $ this ->fixBrokenMacros (
99
- $ this ->zipClass ->getFromName ($ this ->getFooterName ($ index ))
100
- );
117
+ $ this ->tempDocumentFooters [$ index ] = $ this ->readPartWithRels ($ this ->getFooterName ($ index ));
101
118
$ index ++;
102
119
}
103
- $ this ->tempDocumentMainPart = $ this ->fixBrokenMacros ($ this ->zipClass ->getFromName ($ this ->getMainPartName ()));
120
+
121
+ $ this ->tempDocumentMainPart = $ this ->readPartWithRels ($ this ->getMainPartName ());
122
+ $ this ->tempDocumentContentTypes = $ this ->zipClass ->getFromName ($ this ->getDocumentContentTypesName ());
123
+ }
124
+
125
+ /**
126
+ * @param string $fileName
127
+ *
128
+ * @return string
129
+ */
130
+ protected function readPartWithRels ($ fileName )
131
+ {
132
+ $ relsFileName = $ this ->getRelationsName ($ fileName );
133
+ $ partRelations = $ this ->zipClass ->getFromName ($ relsFileName );
134
+ if ($ partRelations !== false ) {
135
+ $ this ->tempDocumentRelations [$ fileName ] = $ partRelations ;
136
+ }
137
+ return $ this ->fixBrokenMacros ($ this ->zipClass ->getFromName ($ fileName ));
104
138
}
105
139
106
140
/**
@@ -236,6 +270,130 @@ public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_
236
270
$ this ->tempDocumentFooters = $ this ->setValueForPart ($ search , $ replace , $ this ->tempDocumentFooters , $ limit );
237
271
}
238
272
273
+ /**
274
+ * @param mixed $search
275
+ * @param mixed $replace Path to image, or array("path" => xx, "width" => yy, "height" => zz)
276
+ * @param integer $limit
277
+ *
278
+ * @return void
279
+ */
280
+ public function setImageValue ($ search , $ replace , $ limit = self ::MAXIMUM_REPLACEMENTS_DEFAULT )
281
+ {
282
+ // prepare $search_replace
283
+ if (!is_array ($ search )) {
284
+ $ search = array ($ search );
285
+ }
286
+
287
+ $ replacesList = array ();
288
+ if (!is_array ($ replace ) || isset ($ replace ["path " ])) {
289
+ $ replacesList [] = $ replace ;
290
+ } else {
291
+ $ replacesList = array_values ($ replace );
292
+ }
293
+
294
+ $ searchReplace = array ();
295
+ foreach ($ search as $ searchIdx => $ searchString ) {
296
+ $ searchReplace [$ searchString ] = isset ($ replacesList [$ searchIdx ]) ? $ replacesList [$ searchIdx ] : $ replacesList [0 ];
297
+ }
298
+ //
299
+
300
+ // define templates
301
+ // result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425)
302
+ $ imgTpl = '<w:pict><v:shape type="#_x0000_t75" style="width:{WIDTH}px;height:{HEIGHT}px"><v:imagedata r:id="{RID}" o:title=""/></v:shape></w:pict> ' ;
303
+ $ typeTpl = '<Override PartName="/word/media/{IMG}" ContentType="image/{EXT}"/> ' ;
304
+ $ relationTpl = '<Relationship Id="{RID}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/{IMG}"/> ' ;
305
+ $ newRelationsTpl = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?> ' ."\n" .'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships> ' ;
306
+ $ newRelationsTypeTpl = '<Override PartName="/{RELS}" ContentType="application/vnd.openxmlformats-package.relationships+xml"/> ' ;
307
+ $ extTransform = array (
308
+ "jpg " => "jpeg " ,
309
+ "JPG " => "jpeg " ,
310
+ "png " => "png " ,
311
+ "PNG " => "png " ,
312
+ );
313
+ //
314
+
315
+ $ searchParts = array (
316
+ $ this ->getMainPartName () => &$ this ->tempDocumentMainPart ,
317
+ );
318
+ foreach (array_keys ($ this ->tempDocumentHeaders ) as $ headerIndex ) {
319
+ $ searchParts [ $ this ->getHeaderName ($ headerIndex ) ] = &$ this ->tempDocumentHeaders [$ headerIndex ];
320
+ }
321
+ foreach (array_keys ($ this ->tempDocumentFooters ) as $ headerIndex ) {
322
+ $ searchParts [ $ this ->getFooterName ($ headerIndex ) ] = &$ this ->tempDocumentFooters [$ headerIndex ];
323
+ }
324
+
325
+ foreach ($ searchParts as $ partFileName => &$ partContent ) {
326
+ $ partVariables = $ this ->getVariablesForPart ($ partContent );
327
+
328
+ $ partSearchReplaces = array ();
329
+ foreach ($ searchReplace as $ search => $ replace ) {
330
+ if (!in_array ($ search , $ partVariables )) {
331
+ continue ;
332
+ }
333
+
334
+ // get image path and size
335
+ $ width = 115 ;
336
+ $ height = 70 ;
337
+ if (is_array ($ replace ) && isset ($ replace ["path " ])) {
338
+ $ imgPath = $ replace ["path " ];
339
+ if (isset ($ replace ["width " ])) {
340
+ $ width = $ replace ["width " ];
341
+ }
342
+ if (isset ($ replace ["height " ])) {
343
+ $ height = $ replace ["height " ];
344
+ }
345
+ } else {
346
+ $ imgPath = $ replace ;
347
+ }
348
+
349
+ // get image index
350
+ $ imgIndex = $ this ->getNextRelationsIndex ($ partFileName );
351
+ $ rid = 'rId ' . $ imgIndex ;
352
+
353
+ // get image embed name
354
+ if (isset ($ this ->tempDocumentNewImages [$ imgPath ])) {
355
+ $ imgName = $ this ->tempDocumentNewImages [$ imgPath ];
356
+ } else {
357
+ // transform extension
358
+ $ imgExt = pathinfo ($ imgPath , PATHINFO_EXTENSION );
359
+ if (isset ($ extTransform )) {
360
+ $ imgExt = $ extTransform [$ imgExt ];
361
+ }
362
+
363
+ // add image to document
364
+ $ imgName = 'image ' . $ imgIndex . '_ ' . pathinfo ($ partFileName , PATHINFO_FILENAME ) . '. ' . $ imgExt ;
365
+ $ this ->zipClass ->pclzipAddFile ($ imgPath , 'word/media/ ' . $ imgName );
366
+ $ this ->tempDocumentNewImages [$ imgPath ] = $ imgName ;
367
+
368
+ // setup type for image
369
+ $ xmlImageType = str_replace (array ('{IMG} ' , '{EXT} ' ), array ($ imgName , $ imgExt ), $ typeTpl ) ;
370
+ $ this ->tempDocumentContentTypes = str_replace ('</Types> ' , $ xmlImageType , $ this ->tempDocumentContentTypes ) . '</Types> ' ;
371
+ }
372
+
373
+ $ xmlImage = str_replace (array ('{RID} ' , '{WIDTH} ' , '{HEIGHT} ' ), array ($ rid , $ width , $ height ), $ imgTpl ) ;
374
+ $ xmlImageRelation = str_replace (array ('{RID} ' , '{IMG} ' ), array ($ rid , $ imgName ), $ relationTpl );
375
+
376
+ if (!isset ($ this ->tempDocumentRelations [$ partFileName ])) {
377
+ // create new relations file
378
+ $ this ->tempDocumentRelations [$ partFileName ] = $ newRelationsTpl ;
379
+ // and add it to content types
380
+ $ xmlRelationsType = str_replace ('{RELS} ' , $ this ->getRelationsName ($ partFileName ), $ newRelationsTypeTpl );
381
+ $ this ->tempDocumentContentTypes = str_replace ('</Types> ' , $ xmlRelationsType , $ this ->tempDocumentContentTypes ) . '</Types> ' ;
382
+ }
383
+
384
+ // add image to relations
385
+ $ this ->tempDocumentRelations [$ partFileName ] = str_replace ('</Relationships> ' , $ xmlImageRelation , $ this ->tempDocumentRelations [$ partFileName ]) . '</Relationships> ' ;
386
+
387
+ // collect prepared replaces
388
+ $ partSearchReplaces ["<w:t> " .self ::ensureMacroCompleted ($ search )."</w:t> " ] = $ xmlImage ;
389
+ }
390
+
391
+ if ($ partSearchReplaces ) {
392
+ $ partContent = $ this ->setValueForPart (array_keys ($ partSearchReplaces ), $ partSearchReplaces , $ partContent , $ limit );
393
+ }
394
+ }
395
+ }
396
+
239
397
/**
240
398
* Returns array of all variables in template.
241
399
*
@@ -399,15 +557,17 @@ public function deleteBlock($blockname)
399
557
public function save ()
400
558
{
401
559
foreach ($ this ->tempDocumentHeaders as $ index => $ xml ) {
402
- $ this ->zipClass -> addFromString ($ this ->getHeaderName ($ index ), $ xml );
560
+ $ this ->savePartWithRels ($ this ->getHeaderName ($ index ), $ xml );
403
561
}
404
562
405
- $ this ->zipClass -> addFromString ($ this ->getMainPartName (), $ this ->tempDocumentMainPart );
563
+ $ this ->savePartWithRels ($ this ->getMainPartName (), $ this ->tempDocumentMainPart );
406
564
407
565
foreach ($ this ->tempDocumentFooters as $ index => $ xml ) {
408
- $ this ->zipClass -> addFromString ($ this ->getFooterName ($ index ), $ xml );
566
+ $ this ->savePartWithRels ($ this ->getFooterName ($ index ), $ xml );
409
567
}
410
568
569
+ $ this ->zipClass ->addFromString ($ this ->getDocumentContentTypesName (), $ this ->tempDocumentContentTypes );
570
+
411
571
// Close zip file
412
572
if (false === $ this ->zipClass ->close ()) {
413
573
throw new Exception ('Could not close zip file. ' );
@@ -416,6 +576,21 @@ public function save()
416
576
return $ this ->tempDocumentFilename ;
417
577
}
418
578
579
+ /**
580
+ * @param string $fileName
581
+ * @param string $xml
582
+ *
583
+ * @return void
584
+ */
585
+ protected function savePartWithRels ($ fileName , $ xml )
586
+ {
587
+ $ this ->zipClass ->addFromString ($ fileName , $ xml );
588
+ if (isset ($ this ->tempDocumentRelations [$ fileName ])) {
589
+ $ relsFileName = $ this ->getRelationsName ($ fileName );
590
+ $ this ->zipClass ->addFromString ($ relsFileName , $ this ->tempDocumentRelations [$ fileName ]);
591
+ }
592
+ }
593
+
419
594
/**
420
595
* Saves the result document to the user defined file.
421
596
*
@@ -533,6 +708,34 @@ protected function getFooterName($index)
533
708
return sprintf ('word/footer%d.xml ' , $ index );
534
709
}
535
710
711
+ /**
712
+ * Get the name of the relations file for document part.
713
+ *
714
+ * @param string $docuemntPartName
715
+ *
716
+ * @return string
717
+ */
718
+ protected function getRelationsName ($ documentPartName )
719
+ {
720
+ return 'word/_rels/ ' .pathinfo ($ documentPartName , PATHINFO_BASENAME ).'.rels ' ;
721
+ }
722
+
723
+ protected function getNextRelationsIndex ($ documentPartName )
724
+ {
725
+ if (isset ($ this ->tempDocumentRelations [$ documentPartName ])) {
726
+ return substr_count ($ this ->tempDocumentRelations [$ documentPartName ], '<Relationship ' );
727
+ }
728
+ return 1 ;
729
+ }
730
+
731
+ /**
732
+ * @return string
733
+ */
734
+ protected function getDocumentContentTypesName ()
735
+ {
736
+ return '[Content_Types].xml ' ;
737
+ }
738
+
536
739
/**
537
740
* Find the start position of the nearest table row before $offset.
538
741
*
0 commit comments