|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace WrkLst\DocxMustache; |
|
4
|
|
|
|
|
5
|
|
|
use Exception; |
|
6
|
|
|
use Illuminate\Support\Facades\Log; |
|
7
|
|
|
|
|
8
|
|
|
//Custom DOCX template class to change content based on mustache templating engine. |
|
9
|
|
|
class DocxMustache |
|
10
|
|
|
{ |
|
11
|
|
|
public $items; |
|
12
|
|
|
public $word_doc; |
|
13
|
|
|
public $template_file_name; |
|
14
|
|
|
public $template_file; |
|
15
|
|
|
public $local_path; |
|
16
|
|
|
public $storageDisk; |
|
17
|
|
|
public $storagePathPrefix; |
|
18
|
|
|
public $zipper; |
|
19
|
|
|
public $imageManipulation; |
|
20
|
|
|
public $verbose; |
|
21
|
|
|
|
|
22
|
|
|
public function __construct($items, $local_template_file) |
|
23
|
|
|
{ |
|
24
|
|
|
$this->items = $items; |
|
25
|
|
|
$this->template_file_name = basename($local_template_file); |
|
26
|
|
|
$this->template_file = $local_template_file; |
|
27
|
|
|
$this->word_doc = false; |
|
28
|
|
|
$this->zipper = new \Chumper\Zipper\Zipper(); |
|
29
|
|
|
|
|
30
|
|
|
//name of disk for storage |
|
31
|
|
|
$this->storageDisk = 'local'; |
|
32
|
|
|
|
|
33
|
|
|
//prefix within your storage path |
|
34
|
|
|
$this->storagePathPrefix = 'app/'; |
|
35
|
|
|
|
|
36
|
|
|
//if you use img urls that support manipulation via parameter |
|
37
|
|
|
$this->imageManipulation = ''; //'&w=1800'; |
|
38
|
|
|
|
|
39
|
|
|
$this->verbose = false; |
|
40
|
|
|
} |
|
41
|
|
|
|
|
42
|
|
|
public function Execute() |
|
43
|
|
|
{ |
|
44
|
|
|
$this->CopyTmplate(); |
|
45
|
|
|
$this->ReadTeamplate(); |
|
46
|
|
|
} |
|
47
|
|
|
|
|
48
|
|
|
/** |
|
49
|
|
|
* @param string $file |
|
50
|
|
|
*/ |
|
51
|
|
|
public function StoragePath($file) |
|
52
|
|
|
{ |
|
53
|
|
|
return storage_path($file); |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
/** |
|
57
|
|
|
* @param string $msg |
|
58
|
|
|
*/ |
|
59
|
|
|
protected function Log($msg) |
|
60
|
|
|
{ |
|
61
|
|
|
//introduce logging method here to keep track of process |
|
62
|
|
|
// can be overwritten in extended class to log with custom preocess logger |
|
63
|
|
|
if ($this->verbose) { |
|
64
|
|
|
Log::error($msg); |
|
65
|
|
|
} |
|
66
|
|
|
} |
|
67
|
|
|
|
|
68
|
|
|
public function CleanUpTmpDirs() |
|
69
|
|
|
{ |
|
70
|
|
|
$now = time(); |
|
71
|
|
|
$isExpired = ($now - (60 * 240)); |
|
72
|
|
|
$disk = \Storage::disk($this->storageDisk); |
|
73
|
|
|
$all_dirs = $disk->directories($this->storagePathPrefix.'DocxMustache'); |
|
74
|
|
|
foreach ($all_dirs as $dir) { |
|
75
|
|
|
//delete dirs older than 20min |
|
76
|
|
|
if ($disk->lastModified($dir) < $isExpired) { |
|
77
|
|
|
$disk->deleteDirectory($dir); |
|
78
|
|
|
} |
|
79
|
|
|
} |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
public function GetTmpDir() |
|
83
|
|
|
{ |
|
84
|
|
|
$this->CleanUpTmpDirs(); |
|
85
|
|
|
$path = $this->storagePathPrefix.'DocxMustache/'.uniqid($this->template_file).'/'; |
|
86
|
|
|
\File::makeDirectory($this->StoragePath($path), 0775, true); |
|
87
|
|
|
|
|
88
|
|
|
return $path; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
public function CopyTmplate() |
|
92
|
|
|
{ |
|
93
|
|
|
$this->Log('Get Copy of Template'); |
|
94
|
|
|
$this->local_path = $this->GetTmpDir(); |
|
95
|
|
|
\Storage::disk($this->storageDisk)->copy($this->storagePathPrefix.$this->template_file, $this->local_path.$this->template_file_name); |
|
96
|
|
|
} |
|
97
|
|
|
|
|
98
|
|
|
protected function exctractOpenXmlFile($file) |
|
99
|
|
|
{ |
|
100
|
|
|
$this->zipper->make($this->StoragePath($this->local_path.$this->template_file_name)) |
|
101
|
|
|
->extractTo($this->StoragePath($this->local_path), [$file], \Chumper\Zipper\Zipper::WHITELIST); |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
protected function ReadOpenXmlFile($file, $type = 'file') |
|
105
|
|
|
{ |
|
106
|
|
|
$this->exctractOpenXmlFile($file); |
|
107
|
|
|
if ($type == 'file') { |
|
108
|
|
|
if ($file_contents = \Storage::disk($this->storageDisk)->get($this->local_path.$file)) { |
|
109
|
|
|
return $file_contents; |
|
110
|
|
|
} else { |
|
111
|
|
|
throw new Exception('Cannot not read file '.$file); |
|
112
|
|
|
} |
|
113
|
|
|
} else { |
|
114
|
|
|
if ($xml_object = simplexml_load_file($this->StoragePath($this->local_path.$file))) { |
|
115
|
|
|
return $xml_object; |
|
116
|
|
|
} else { |
|
117
|
|
|
throw new Exception('Cannot load XML Object from file '.$file); |
|
118
|
|
|
} |
|
119
|
|
|
} |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
protected function SaveOpenXmlFile($file, $folder, $content) |
|
123
|
|
|
{ |
|
124
|
|
|
\Storage::disk($this->storageDisk) |
|
125
|
|
|
->put($this->local_path.$file, $content); |
|
126
|
|
|
//add new content to word doc |
|
127
|
|
|
if ($folder) { |
|
128
|
|
|
$this->zipper->folder($folder) |
|
129
|
|
|
->add($this->StoragePath($this->local_path.$file)); |
|
130
|
|
|
} else { |
|
131
|
|
|
$this->zipper |
|
132
|
|
|
->add($this->StoragePath($this->local_path.$file)); |
|
133
|
|
|
} |
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
protected function SaveOpenXmlObjectToFile($xmlObject, $file, $folder) |
|
137
|
|
|
{ |
|
138
|
|
|
if ($xmlString = $xmlObject->asXML()) { |
|
139
|
|
|
$this->SaveOpenXmlFile($file, $folder, $xmlString); |
|
140
|
|
|
} else { |
|
141
|
|
|
throw new Exception('Cannot generate xml for '.$file); |
|
142
|
|
|
} |
|
143
|
|
|
} |
|
144
|
|
|
|
|
145
|
|
|
public function ReadTeamplate() |
|
146
|
|
|
{ |
|
147
|
|
|
$this->Log('Analyze Template'); |
|
148
|
|
|
//get the main document out of the docx archive |
|
149
|
|
|
$this->word_doc = $this->ReadOpenXmlFile('word/document.xml', 'file'); |
|
150
|
|
|
|
|
151
|
|
|
$this->Log('Merge Data into Template'); |
|
152
|
|
|
|
|
153
|
|
|
$this->word_doc = MustacheRender::render($this->items, $this->word_doc); |
|
154
|
|
|
|
|
155
|
|
|
$this->word_doc = HtmlConversion::convert($this->word_doc); |
|
156
|
|
|
|
|
157
|
|
|
$this->ImageReplacer(); |
|
158
|
|
|
|
|
159
|
|
|
$this->Log('Compact Template with Data'); |
|
160
|
|
|
|
|
161
|
|
|
$this->SaveOpenXmlFile('word/document.xml', 'word', $this->word_doc); |
|
162
|
|
|
$this->zipper->close(); |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
protected function AddContentType($imageCt = 'jpeg') |
|
166
|
|
|
{ |
|
167
|
|
|
$ct_file = $this->ReadOpenXmlFile('[Content_Types].xml', 'object'); |
|
168
|
|
|
|
|
169
|
|
|
if (! ($ct_file instanceof \Traversable)) { |
|
170
|
|
|
throw new Exception('Cannot traverse through [Content_Types].xml.'); |
|
171
|
|
|
} |
|
172
|
|
|
|
|
173
|
|
|
//check if content type for jpg has been set |
|
174
|
|
|
$i = 0; |
|
175
|
|
|
$ct_already_set = false; |
|
176
|
|
|
foreach ($ct_file as $ct) { |
|
177
|
|
|
if ((string) $ct_file->Default[$i]['Extension'] == $imageCt) { |
|
178
|
|
|
$ct_already_set = true; |
|
179
|
|
|
} |
|
180
|
|
|
$i++; |
|
181
|
|
|
} |
|
182
|
|
|
|
|
183
|
|
|
//if content type for jpg has not been set, add it to xml |
|
184
|
|
|
// and save xml to file and add it to the archive |
|
185
|
|
|
if (! $ct_already_set) { |
|
186
|
|
|
$sxe = $ct_file->addChild('Default'); |
|
187
|
|
|
$sxe->addAttribute('Extension', $imageCt); |
|
188
|
|
|
$sxe->addAttribute('ContentType', 'image/'.$imageCt); |
|
189
|
|
|
$this->SaveOpenXmlObjectToFile($ct_file, '[Content_Types].xml', false); |
|
190
|
|
|
} |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
|
|
protected function FetchReplaceableImages(&$main_file, $ns) |
|
194
|
|
|
{ |
|
195
|
|
|
//set up basic arrays to keep track of imgs |
|
196
|
|
|
$imgs = []; |
|
197
|
|
|
$imgs_replaced = []; // so they can later be removed from media and relation file. |
|
198
|
|
|
$newIdCounter = 1; |
|
199
|
|
|
|
|
200
|
|
|
//iterate through all drawing containers of the xml document |
|
201
|
|
|
foreach ($main_file->xpath('//w:drawing') as $k=>$drawing) { |
|
202
|
|
|
//figure out if there is a URL saved in the description field of the img |
|
203
|
|
|
$img_url = $this->AnalyseImgUrlString($drawing->children($ns['wp'])->xpath('wp:docPr')[0]->attributes()['descr']); |
|
204
|
|
|
$main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->xpath('wp:docPr')[0]->attributes()['descr'] = $img_url['rest']; |
|
205
|
|
|
|
|
206
|
|
|
//if there is a url, save this img as a img to be replaced |
|
207
|
|
|
if ($img_url['valid']) { |
|
208
|
|
|
$ueid = 'wrklstId'.$newIdCounter; |
|
209
|
|
|
$wasId = (string) $main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->children($ns['a'])->graphic->graphicData->children($ns['pic'])->pic->blipFill->children($ns['a'])->blip->attributes($ns['r'])['embed']; |
|
210
|
|
|
|
|
211
|
|
|
//get dimensions |
|
212
|
|
|
$cx = (int) $main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->children($ns['a'])->graphic->graphicData->children($ns['pic'])->pic->spPr->children($ns['a'])->xfrm->ext->attributes()['cx']; |
|
213
|
|
|
$cy = (int) $main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->children($ns['a'])->graphic->graphicData->children($ns['pic'])->pic->spPr->children($ns['a'])->xfrm->ext->attributes()['cy']; |
|
214
|
|
|
|
|
215
|
|
|
//remember img as being replaced |
|
216
|
|
|
$imgs_replaced[$wasId] = $wasId; |
|
217
|
|
|
|
|
218
|
|
|
//set new img id |
|
219
|
|
|
$main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->children($ns['a'])->graphic->graphicData->children($ns['pic'])->pic->blipFill->children($ns['a'])->blip->attributes($ns['r'])['embed'] = $ueid; |
|
220
|
|
|
|
|
221
|
|
|
$imgs[] = [ |
|
222
|
|
|
'cx' => (int) $cx, |
|
223
|
|
|
'cy' => (int) $cy, |
|
224
|
|
|
'wasId' => $wasId, |
|
225
|
|
|
'id' => $ueid, |
|
226
|
|
|
'url' => $img_url['url'], |
|
227
|
|
|
'path' => $img_url['path'], |
|
228
|
|
|
'mode' => $img_url['mode'], |
|
229
|
|
|
]; |
|
230
|
|
|
|
|
231
|
|
|
$newIdCounter++; |
|
232
|
|
|
} |
|
233
|
|
|
} |
|
234
|
|
|
|
|
235
|
|
|
return [ |
|
236
|
|
|
'imgs' => $imgs, |
|
237
|
|
|
'imgs_replaced' => $imgs_replaced, |
|
238
|
|
|
]; |
|
239
|
|
|
} |
|
240
|
|
|
|
|
241
|
|
|
protected function RemoveReplaceImages($imgs_replaced, &$rels_file) |
|
242
|
|
|
{ |
|
243
|
|
|
//TODO: check if the same img is used at a different position int he file as well, as otherwise broken images are produced. |
|
244
|
|
|
//iterate through replaced images and clean rels files from them |
|
245
|
|
|
foreach ($imgs_replaced as $img_replaced) { |
|
246
|
|
|
$i = 0; |
|
247
|
|
|
foreach ($rels_file as $rel) { |
|
248
|
|
|
if ((string) $rel->attributes()['Id'] == $img_replaced) { |
|
249
|
|
|
$this->zipper->remove('word/'.(string) $rel->attributes()['Target']); |
|
250
|
|
|
unset($rels_file->Relationship[$i]); |
|
251
|
|
|
} |
|
252
|
|
|
$i++; |
|
253
|
|
|
} |
|
254
|
|
|
} |
|
255
|
|
|
} |
|
256
|
|
|
|
|
257
|
|
|
protected function InsertImages($ns, &$imgs, &$rels_file, &$main_file) |
|
258
|
|
|
{ |
|
259
|
|
|
$docimage = new DocImage(); |
|
260
|
|
|
$allowed_imgs = $docimage->AllowedContentTypeImages(); |
|
261
|
|
|
$image_i = 1; |
|
262
|
|
|
//iterate through replacable images |
|
263
|
|
|
foreach ($imgs as $k=>$img) { |
|
264
|
|
|
$this->Log('Merge Images into Template - '.round($image_i / count($imgs) * 100).'%'); |
|
265
|
|
|
//get file type of img and test it against supported imgs |
|
266
|
|
|
if ($imgageData = $docimage->GetImageFromUrl($img['mode'] == 'url' ? $img['url'] : $img['path'], $img['mode'] == 'url' ? $this->imageManipulation : '')) { |
|
267
|
|
|
$imgs[$k]['img_file_src'] = str_replace('wrklstId', 'wrklst_image', $img['id']).$allowed_imgs[$imgageData['mime']]; |
|
268
|
|
|
$imgs[$k]['img_file_dest'] = str_replace('wrklstId', 'wrklst_image', $img['id']).'.jpeg'; |
|
269
|
|
|
|
|
270
|
|
|
$resampled_img = $docimage->ResampleImage($this, $imgs, $k, $imgageData['data']); |
|
271
|
|
|
|
|
272
|
|
|
$sxe = $rels_file->addChild('Relationship'); |
|
273
|
|
|
$sxe->addAttribute('Id', $img['id']); |
|
274
|
|
|
$sxe->addAttribute('Type', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image'); |
|
275
|
|
|
$sxe->addAttribute('Target', 'media/'.$imgs[$k]['img_file_dest']); |
|
276
|
|
|
|
|
277
|
|
|
foreach ($main_file->xpath('//w:drawing') as $k=>$drawing) { |
|
278
|
|
|
if (null !== $main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->children($ns['a']) |
|
279
|
|
|
->graphic->graphicData->children($ns['pic'])->pic->blipFill && |
|
280
|
|
|
$img['id'] == $main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->children($ns['a']) |
|
281
|
|
|
->graphic->graphicData->children($ns['pic'])->pic->blipFill->children($ns['a']) |
|
282
|
|
|
->blip->attributes($ns['r'])['embed']) { |
|
283
|
|
|
$main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->children($ns['a']) |
|
284
|
|
|
->graphic->graphicData->children($ns['pic'])->pic->spPr->children($ns['a']) |
|
285
|
|
|
->xfrm->ext->attributes()['cx'] = $resampled_img['width_emus']; |
|
286
|
|
|
$main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->children($ns['a']) |
|
287
|
|
|
->graphic->graphicData->children($ns['pic'])->pic->spPr->children($ns['a']) |
|
288
|
|
|
->xfrm->ext->attributes()['cy'] = $resampled_img['height_emus']; |
|
289
|
|
|
//anchor images |
|
290
|
|
|
if (isset($main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->anchor)) { |
|
291
|
|
|
$main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->anchor->extent->attributes()['cx'] = $resampled_img['width_emus']; |
|
292
|
|
|
$main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->anchor->extent->attributes()['cy'] = $resampled_img['height_emus']; |
|
293
|
|
|
} |
|
294
|
|
|
//inline images |
|
295
|
|
|
elseif (isset($main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->inline)) { |
|
296
|
|
|
$main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->inline->extent->attributes()['cx'] = $resampled_img['width_emus']; |
|
297
|
|
|
$main_file->xpath('//w:drawing')[$k]->children($ns['wp'])->inline->extent->attributes()['cy'] = $resampled_img['height_emus']; |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
break; |
|
301
|
|
|
} |
|
302
|
|
|
} |
|
303
|
|
|
} |
|
304
|
|
|
$image_i++; |
|
305
|
|
|
} |
|
306
|
|
|
} |
|
307
|
|
|
|
|
308
|
|
|
protected function ImageReplacer() |
|
309
|
|
|
{ |
|
310
|
|
|
$this->Log('Load XML Document to Merge Images'); |
|
311
|
|
|
|
|
312
|
|
|
//load main doc xml |
|
313
|
|
|
libxml_use_internal_errors(true); |
|
314
|
|
|
$main_file = simplexml_load_string($this->word_doc); |
|
315
|
|
|
|
|
316
|
|
|
if(gettype($main_file) == "object") |
|
317
|
|
|
{ |
|
318
|
|
|
$this->Log('Merge Images into Template'); |
|
319
|
|
|
|
|
320
|
|
|
//get all namespaces of the document |
|
321
|
|
|
$ns = $main_file->getNamespaces(true); |
|
322
|
|
|
|
|
323
|
|
|
$replaceableImage = $this->FetchReplaceableImages($main_file, $ns); |
|
324
|
|
|
$imgs = $replaceableImage['imgs']; |
|
325
|
|
|
$imgs_replaced = $replaceableImage['imgs_replaced']; |
|
|
|
|
|
|
326
|
|
|
|
|
327
|
|
|
$rels_file = $this->ReadOpenXmlFile('word/_rels/document.xml.rels', 'object'); |
|
328
|
|
|
|
|
329
|
|
|
//do not remove until it is checked if the same img is used at a different position int he file as well, as otherwise broken images are produced. |
|
330
|
|
|
//$this->RemoveReplaceImages($imgs_replaced, $rels_file); |
|
|
|
|
|
|
331
|
|
|
|
|
332
|
|
|
//add jpg content type if not set |
|
333
|
|
|
$this->AddContentType('jpeg'); |
|
334
|
|
|
|
|
335
|
|
|
$this->InsertImages($ns, $imgs, $rels_file, $main_file); |
|
336
|
|
|
|
|
337
|
|
|
$this->SaveOpenXmlObjectToFile($rels_file, 'word/_rels/document.xml.rels', 'word/_rels'); |
|
338
|
|
|
|
|
339
|
|
|
if ($main_file_xml = $main_file->asXML()) { |
|
340
|
|
|
$this->word_doc = $main_file_xml; |
|
341
|
|
|
} else { |
|
342
|
|
|
throw new Exception('Cannot generate xml for word/document.xml.'); |
|
343
|
|
|
} |
|
344
|
|
|
} |
|
345
|
|
|
else |
|
346
|
|
|
{ |
|
347
|
|
|
$xmlerror = ''; |
|
348
|
|
|
foreach (libxml_get_errors() as $error) { |
|
349
|
|
|
// handle errors here |
|
350
|
|
|
$xmlerror .= $error; |
|
351
|
|
|
} |
|
352
|
|
|
$this->Log('Error: Could not load XML file. '.$xmlerror); |
|
353
|
|
|
libxml_clear_errors(); |
|
354
|
|
|
} |
|
355
|
|
|
} |
|
356
|
|
|
|
|
357
|
|
|
/** |
|
358
|
|
|
* @param string $string |
|
359
|
|
|
*/ |
|
360
|
|
|
protected function AnalyseImgUrlString($string) |
|
361
|
|
|
{ |
|
362
|
|
|
$string = (string) $string; |
|
363
|
|
|
$start = '[IMG-REPLACE]'; |
|
364
|
|
|
$end = '[/IMG-REPLACE]'; |
|
365
|
|
|
$start_local = '[LOCAL_IMG_REPLACE]'; |
|
366
|
|
|
$end_local = '[/LOCAL_IMG_REPLACE]'; |
|
367
|
|
|
$valid = false; |
|
368
|
|
|
$url = ''; |
|
369
|
|
|
$path = ''; |
|
370
|
|
|
|
|
371
|
|
|
if ($string != str_replace($start, '', $string) && $string == str_replace($start.$end, '', $string)) { |
|
372
|
|
|
$string = ' '.$string; |
|
373
|
|
|
$ini = strpos($string, $start); |
|
374
|
|
View Code Duplication |
if ($ini == 0) { |
|
|
|
|
|
|
375
|
|
|
$url = ''; |
|
376
|
|
|
$rest = $string; |
|
377
|
|
|
} else { |
|
378
|
|
|
$ini += strlen($start); |
|
379
|
|
|
$len = ((strpos($string, $end, $ini)) - $ini); |
|
380
|
|
|
$url = substr($string, $ini, $len); |
|
381
|
|
|
|
|
382
|
|
|
$ini = strpos($string, $start); |
|
383
|
|
|
$len = strpos($string, $end, $ini + strlen($start)) + strlen($end); |
|
384
|
|
|
$rest = substr($string, 0, $ini).substr($string, $len); |
|
385
|
|
|
} |
|
386
|
|
|
|
|
387
|
|
|
$valid = true; |
|
388
|
|
|
|
|
389
|
|
|
//TODO: create a better url validity check |
|
390
|
|
|
if (! trim(str_replace(['http', 'https', ':', ' '], '', $url)) || $url == str_replace('http', '', $url)) { |
|
391
|
|
|
$valid = false; |
|
392
|
|
|
} |
|
393
|
|
|
$mode = 'url'; |
|
394
|
|
|
} elseif ($string != str_replace($start_local, '', $string) && $string == str_replace($start_local.$end_local, '', $string)) { |
|
395
|
|
|
$string = ' '.$string; |
|
396
|
|
|
$ini = strpos($string, $start_local); |
|
397
|
|
View Code Duplication |
if ($ini == 0) { |
|
|
|
|
|
|
398
|
|
|
$path = ''; |
|
399
|
|
|
$rest = $string; |
|
400
|
|
|
} else { |
|
401
|
|
|
$ini += strlen($start_local); |
|
402
|
|
|
$len = ((strpos($string, $end_local, $ini)) - $ini); |
|
403
|
|
|
$path = str_replace('..', '', substr($string, $ini, $len)); |
|
404
|
|
|
|
|
405
|
|
|
$ini = strpos($string, $start_local); |
|
406
|
|
|
$len = strpos($string, $end_local, $ini + strlen($start)) + strlen($end_local); |
|
407
|
|
|
$rest = substr($string, 0, $ini).substr($string, $len); |
|
408
|
|
|
} |
|
409
|
|
|
|
|
410
|
|
|
$valid = true; |
|
411
|
|
|
|
|
412
|
|
|
//check if path starts with storage path |
|
413
|
|
|
if (! starts_with($path, storage_path())) { |
|
414
|
|
|
$valid = false; |
|
415
|
|
|
} |
|
416
|
|
|
$mode = 'path'; |
|
417
|
|
|
} else { |
|
418
|
|
|
$mode = 'nothing'; |
|
419
|
|
|
$url = ''; |
|
420
|
|
|
$path = ''; |
|
421
|
|
|
$rest = str_replace([$start, $end, $start_local, $end_local], '', $string); |
|
422
|
|
|
} |
|
423
|
|
|
|
|
424
|
|
|
return [ |
|
425
|
|
|
'mode' => $mode, |
|
426
|
|
|
'url' => trim($url), |
|
427
|
|
|
'path' => trim($path), |
|
428
|
|
|
'rest' => trim($rest), |
|
429
|
|
|
'valid' => $valid, |
|
430
|
|
|
]; |
|
431
|
|
|
} |
|
432
|
|
|
|
|
433
|
|
|
public function SaveAsPdf() |
|
434
|
|
|
{ |
|
435
|
|
|
$this->Log('Converting DOCX to PDF'); |
|
436
|
|
|
//convert to pdf with libre office |
|
437
|
|
|
$process = new \Symfony\Component\Process\Process([ |
|
438
|
|
|
'soffice', |
|
439
|
|
|
'--headless', |
|
440
|
|
|
'--convert-to', |
|
441
|
|
|
'pdf', |
|
442
|
|
|
$this->StoragePath($this->local_path.$this->template_file_name), |
|
443
|
|
|
'--outdir', |
|
444
|
|
|
$this->StoragePath($this->local_path), |
|
445
|
|
|
]); |
|
446
|
|
|
$process->start(); |
|
447
|
|
|
while ($process->isRunning()) { |
|
448
|
|
|
//wait until process is ready |
|
449
|
|
|
} |
|
450
|
|
|
// executes after the command finishes |
|
451
|
|
|
if (! $process->isSuccessful()) { |
|
452
|
|
|
throw new \Symfony\Component\Process\Exception\ProcessFailedException($process); |
|
453
|
|
|
} else { |
|
454
|
|
|
$path_parts = pathinfo($this->StoragePath($this->local_path.$this->template_file_name)); |
|
455
|
|
|
|
|
456
|
|
|
return $this->StoragePath($this->local_path.$path_parts['filename'].'pdf'); |
|
457
|
|
|
} |
|
458
|
|
|
} |
|
459
|
|
|
} |
|
460
|
|
|
|
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
Both the
$myVarassignment in line 1 and the$higherassignment in line 2 are dead. The first because$myVaris never used and the second because$higheris always overwritten for every possible time line.