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