]> BookStack Code Mirror - bookstack/blob - app/Services/ExportService.php
Merge pull request #3 from OsmosysSoftware/revert-1-issue-181
[bookstack] / app / Services / ExportService.php
1 <?php namespace BookStack\Services;
2
3 use BookStack\Book;
4 use BookStack\Chapter;
5 use BookStack\Page;
6 use BookStack\Repos\EntityRepo;
7
8 class ExportService
9 {
10
11     protected $entityRepo;
12
13     /**
14      * ExportService constructor.
15      * @param $entityRepo
16      */
17     public function __construct(EntityRepo $entityRepo)
18     {
19         $this->entityRepo = $entityRepo;
20     }
21
22     /**
23      * Convert a page to a self-contained HTML file.
24      * Includes required CSS & image content. Images are base64 encoded into the HTML.
25      * @param Page $page
26      * @return mixed|string
27      */
28     public function pageToContainedHtml(Page $page)
29     {
30         $pageHtml = view('pages/export', [
31             'page' => $page,
32             'pageContent' => $this->entityRepo->renderPage($page)
33         ])->render();
34         return $this->containHtml($pageHtml);
35     }
36
37     /**
38      * Convert a chapter to a self-contained HTML file.
39      * @param Chapter $chapter
40      * @return mixed|string
41      */
42     public function chapterToContainedHtml(Chapter $chapter)
43     {
44         $pages = $this->entityRepo->getChapterChildren($chapter);
45         $pages->each(function($page) {
46             $page->html = $this->entityRepo->renderPage($page);
47         });
48         $html = view('chapters/export', [
49             'chapter' => $chapter,
50             'pages' => $pages
51         ])->render();
52         return $this->containHtml($html);
53     }
54
55     /**
56      * Convert a book to a self-contained HTML file.
57      * @param Book $book
58      * @return mixed|string
59      */
60     public function bookToContainedHtml(Book $book)
61     {
62         $bookTree = $this->entityRepo->getBookChildren($book, true, true);
63         $html = view('books/export', [
64             'book' => $book,
65             'bookChildren' => $bookTree
66         ])->render();
67         return $this->containHtml($html);
68     }
69
70     /**
71      * Convert a page to a PDF file.
72      * @param Page $page
73      * @return mixed|string
74      */
75     public function pageToPdf(Page $page)
76     {
77         $html = view('pages/pdf', [
78             'page' => $page,
79             'pageContent' => $this->entityRepo->renderPage($page)
80         ])->render();
81         return $this->htmlToPdf($html);
82     }
83
84     /**
85      * Convert a chapter to a PDF file.
86      * @param Chapter $chapter
87      * @return mixed|string
88      */
89     public function chapterToPdf(Chapter $chapter)
90     {
91         $pages = $this->entityRepo->getChapterChildren($chapter);
92         $pages->each(function($page) {
93             $page->html = $this->entityRepo->renderPage($page);
94         });
95         $html = view('chapters/export', [
96             'chapter' => $chapter,
97             'pages' => $pages
98         ])->render();
99         return $this->htmlToPdf($html);
100     }
101
102     /**
103      * Convert a book to a PDF file
104      * @param Book $book
105      * @return string
106      */
107     public function bookToPdf(Book $book)
108     {
109         $bookTree = $this->entityRepo->getBookChildren($book, true, true);
110         $html = view('books/export', [
111             'book' => $book,
112             'bookChildren' => $bookTree
113         ])->render();
114         return $this->htmlToPdf($html);
115     }
116
117     /**
118      * Convert normal webpage HTML to a PDF.
119      * @param $html
120      * @return string
121      */
122     protected function htmlToPdf($html)
123     {
124         $containedHtml = $this->containHtml($html);
125         $useWKHTML = config('snappy.pdf.binary') !== false;
126         if ($useWKHTML) {
127             $pdf = \SnappyPDF::loadHTML($containedHtml);
128             $pdf->setOption('print-media-type', true);
129         } else {
130             $pdf = \PDF::loadHTML($containedHtml);
131         }
132         return $pdf->output();
133     }
134
135     /**
136      * Bundle of the contents of a html file to be self-contained.
137      * @param $htmlContent
138      * @return mixed|string
139      */
140     protected function containHtml($htmlContent)
141     {
142         $imageTagsOutput = [];
143         preg_match_all("/\<img.*src\=(\'|\")(.*?)(\'|\").*?\>/i", $htmlContent, $imageTagsOutput);
144
145         // Replace image src with base64 encoded image strings
146         if (isset($imageTagsOutput[0]) && count($imageTagsOutput[0]) > 0) {
147             foreach ($imageTagsOutput[0] as $index => $imgMatch) {
148                 $oldImgString = $imgMatch;
149                 $srcString = $imageTagsOutput[2][$index];
150                 $isLocal = strpos(trim($srcString), 'http') !== 0;
151                 if ($isLocal) {
152                     $pathString = public_path(trim($srcString, '/'));
153                 } else {
154                     $pathString = $srcString;
155                 }
156                 if ($isLocal && !file_exists($pathString)) continue;
157                 try {
158                     $imageContent = file_get_contents($pathString);
159                     $imageEncoded = 'data:image/' . pathinfo($pathString, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageContent);
160                     $newImageString = str_replace($srcString, $imageEncoded, $oldImgString);
161                 } catch (\ErrorException $e) {
162                     $newImageString = '';
163                 }
164                 $htmlContent = str_replace($oldImgString, $newImageString, $htmlContent);
165             }
166         }
167
168         $linksOutput = [];
169         preg_match_all("/\<a.*href\=(\'|\")(.*?)(\'|\").*?\>/i", $htmlContent, $linksOutput);
170
171         // Replace image src with base64 encoded image strings
172         if (isset($linksOutput[0]) && count($linksOutput[0]) > 0) {
173             foreach ($linksOutput[0] as $index => $linkMatch) {
174                 $oldLinkString = $linkMatch;
175                 $srcString = $linksOutput[2][$index];
176                 if (strpos(trim($srcString), 'http') !== 0) {
177                     $newSrcString = url($srcString);
178                     $newLinkString = str_replace($srcString, $newSrcString, $oldLinkString);
179                     $htmlContent = str_replace($oldLinkString, $newLinkString, $htmlContent);
180                 }
181             }
182         }
183
184         // Replace any relative links with system domain
185         return $htmlContent;
186     }
187
188     /**
189      * Converts the page contents into simple plain text.
190      * This method filters any bad looking content to provide a nice final output.
191      * @param Page $page
192      * @return mixed
193      */
194     public function pageToPlainText(Page $page)
195     {
196         $html = $this->entityRepo->renderPage($page);
197         $text = strip_tags($html);
198         // Replace multiple spaces with single spaces
199         $text = preg_replace('/\ {2,}/', ' ', $text);
200         // Reduce multiple horrid whitespace characters.
201         $text = preg_replace('/(\x0A|\xA0|\x0A|\r|\n){2,}/su', "\n\n", $text);
202         $text = html_entity_decode($text);
203         // Add title
204         $text = $page->name . "\n\n" . $text;
205         return $text;
206     }
207
208     /**
209      * Convert a chapter into a plain text string.
210      * @param Chapter $chapter
211      * @return string
212      */
213     public function chapterToPlainText(Chapter $chapter)
214     {
215         $text = $chapter->name . "\n\n";
216         $text .= $chapter->description . "\n\n";
217         foreach ($chapter->pages as $page) {
218             $text .= $this->pageToPlainText($page);
219         }
220         return $text;
221     }
222
223     /**
224      * Convert a book into a plain text string.
225      * @param Book $book
226      * @return string
227      */
228     public function bookToPlainText(Book $book)
229     {
230         $bookTree = $this->entityRepo->getBookChildren($book, true, true);
231         $text = $book->name . "\n\n";
232         foreach ($bookTree as $bookChild) {
233             if ($bookChild->isA('chapter')) {
234                 $text .= $this->chapterToPlainText($bookChild);
235             } else {
236                 $text .= $this->pageToPlainText($bookChild);
237             }
238         }
239         return $text;
240     }
241
242 }
243
244
245
246
247
248
249
250
251
252
253
254
Morty Proxy This is a proxified and sanitized view of the page, visit original site.