Overview

Namespaces

  • Alpha
    • Controller
      • Front
    • Exception
    • Model
      • Type
    • Task
    • Util
      • Backup
      • Cache
      • Code
        • Highlight
        • Metric
      • Config
      • Convertor
      • Email
      • Extension
      • Feed
      • File
      • Graph
      • Helper
      • Http
        • Filter
        • Session
      • Image
      • Logging
      • Search
      • Security
    • View
      • Renderer
        • Html
        • Json
      • Widget

Classes

  • Markdown
  • MarkdownFacade
  • TCPDF
  • TCPDFFacade
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: namespace Alpha\Util\Extension;
  4: 
  5: use Alpha\Util\Config\ConfigProvider;
  6: use Alpha\Util\Logging\Logger;
  7: use Alpha\View\Widget\Image;
  8: use Alpha\Exception\AlphaException;
  9: 
 10: /**
 11:  * A facade class for the TCPDF library which is used to convert some HTML content provided by the
 12:  * Markdown library to a PDF file using FPDF.
 13:  *
 14:  * @since 1.0
 15:  *
 16:  * @author John Collins <dev@alphaframework.org>
 17:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
 18:  * @copyright Copyright (c) 2015, John Collins (founder of Alpha Framework).
 19:  * All rights reserved.
 20:  *
 21:  * <pre>
 22:  * Redistribution and use in source and binary forms, with or
 23:  * without modification, are permitted provided that the
 24:  * following conditions are met:
 25:  *
 26:  * * Redistributions of source code must retain the above
 27:  *   copyright notice, this list of conditions and the
 28:  *   following disclaimer.
 29:  * * Redistributions in binary form must reproduce the above
 30:  *   copyright notice, this list of conditions and the
 31:  *   following disclaimer in the documentation and/or other
 32:  *   materials provided with the distribution.
 33:  * * Neither the name of the Alpha Framework nor the names
 34:  *   of its contributors may be used to endorse or promote
 35:  *   products derived from this software without specific
 36:  *   prior written permission.
 37:  *
 38:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 39:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 40:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 41:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 42:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 43:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 44:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 45:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 46:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 47:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 48:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 49:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 50:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 51:  * </pre>
 52:  */
 53: class TCPDFFacade
 54: {
 55:     /**
 56:      * The HTML-format content that we will render as a PDF.
 57:      *
 58:      * @var string
 59:      *
 60:      * @since 1.0
 61:      */
 62:     private $content;
 63: 
 64:     /**
 65:      * The PDF object that will be generated from the Markdown HTML content.
 66:      *
 67:      * @var Alpha\Util\Extension\TCPDF
 68:      *
 69:      * @since 1.0
 70:      */
 71:     private $pdf;
 72: 
 73:     /**
 74:      * The business object that stores the content will be rendered to Markdown.
 75:      *
 76:      * @var Alpha\Model\ActiveRecord
 77:      *
 78:      * @since 1.0
 79:      */
 80:     private $BO = null;
 81: 
 82:     /**
 83:      * The auto-generated name of the PDF cache file for the BO.
 84:      *
 85:      * @var string
 86:      *
 87:      * @since 1.0
 88:      */
 89:     private $PDFFilename;
 90: 
 91:     /**
 92:      * The auto-generated name of the HTML cache file for the BO generated by Markdown.
 93:      *
 94:      * @var string
 95:      *
 96:      * @since 1.0
 97:      */
 98:     private $HTMLFilename;
 99: 
100:     /**
101:      * Trace logger.
102:      *
103:      * @var Alpha\Util\Logging\Logger
104:      *
105:      * @since 1.0
106:      */
107:     private static $logger = null;
108: 
109:     /**
110:      * The constructor.
111:      *
112:      * @param Alpha\Model\ActiveRecord $BO the business object that stores the content will be rendered to Markdown
113:      *
114:      * @since 1.0
115:      */
116:     public function __construct($BO)
117:     {
118:         self::$logger = new Logger('TCPDFFacade');
119:         self::$logger->debug('>>__construct()');
120: 
121:         $config = ConfigProvider::getInstance();
122: 
123:         $this->BO = $BO;
124: 
125:         $reflect = new \ReflectionClass($this->BO);
126:         $classname = $reflect->getShortName();
127: 
128:         $this->PDFFilename = $config->get('app.file.store.dir').'cache/pdf/'.$classname.'_'.$this->BO->getID().'_'.$this->BO->getVersion().'.pdf';
129:         $PDFDownloadName = str_replace(' ', '_', $this->BO->get('title').'.pdf');
130:         $this->HTMLFilename = $config->get('app.file.store.dir').'cache/html/'.$classname.'_'.$this->BO->getID().'_'.$this->BO->getVersion().'.html';
131: 
132:         // first check the PDF cache
133:         if ($this->checkPDFCache()) {
134:             return;
135:         }
136: 
137:         if (method_exists($this->BO, 'getAttachmentsURL')) {
138:             $attachURL = $this->BO->getAttachmentsURL();
139:         } else {
140:             $attachURL = '';
141:         }
142: 
143:         if ($this->checkHTMLCache()) {
144:             $this->loadHTMLCache();
145:         } else {
146:             $this->content = $this->markdown($this->BO->get('content', true), $attachURL);
147:             $this->HTMLCache();
148:         }
149: 
150:         // Replace all instances of $attachURL in link tags to links to the ViewAttachment controller
151:         $attachments = array();
152:         preg_match_all('/href\=\"\$attachURL\/.*\"/', $this->content, $attachments);
153: 
154:         foreach ($attachments[0] as $attachmentURL) {
155:             $start = mb_strpos($attachmentURL, '/');
156:             $end = mb_strrpos($attachmentURL, '"');
157:             $fileName = mb_substr($attachmentURL, $start + 1, $end - ($start + 1));
158: 
159:             if (method_exists($this->BO, 'getAttachmentSecureURL')) {
160:                 $this->content = str_replace($attachmentURL, 'href='.$this->BO->getAttachmentSecureURL($fileName), $this->content);
161:             }
162:         }
163: 
164:         // Handle image attachments
165:         $attachments = array();
166:         preg_match_all('/\<img\ src\=\"\$attachURL\/.*\".*\>/', $this->content, $attachments);
167: 
168:         foreach ($attachments[0] as $attachmentURL) {
169:             $start = mb_strpos($attachmentURL, '/');
170:             $end = mb_strrpos($attachmentURL, '" alt');
171:             $fileName = mb_substr($attachmentURL, $start + 1, $end - ($start + 1));
172: 
173:             if ($config->get('cms.images.widget')) {
174:                 // get the details of the source image
175:                 $path = $this->BO->getAttachmentsLocation().'/'.$fileName;
176:                 $image_details = getimagesize($path);
177:                 $imgType = $image_details[2];
178:                 if ($imgType == 1) {
179:                     $type = 'gif';
180:                 } elseif ($imgType == 2) {
181:                     $type = 'jpg';
182:                 } elseif ($imgType == 3) {
183:                     $type = 'png';
184:                 }
185: 
186:                 $img = new Image($path, $image_details[0], $image_details[1], $type, 0.95, false, (boolean) $config->get('cms.images.widget.secure'));
187:                 $this->content = str_replace($attachmentURL, $img->renderHTMLLink(), $this->content);
188:             } else {
189:                 // render a normal image link to the ViewAttachment controller
190:                 if (method_exists($this->BO, 'getAttachmentSecureURL')) {
191:                     $this->content = str_replace($attachmentURL, '<img src="'.$this->BO->getAttachmentSecureURL($fileName).'">', $this->content);
192:                 }
193:             }
194:         }
195: 
196:         $this->pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
197:         $this->pdf->SetCreator(PDF_CREATOR);
198:         $this->pdf->SetAuthor($this->BO->get('author'));
199:         $this->pdf->SetTitle($this->BO->get('title'));
200:         $this->pdf->SetSubject($this->BO->get('description'));
201: 
202:         //set margins
203:         $this->pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
204:         $this->pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
205:         $this->pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
206: 
207:         //set auto page breaks
208:         $this->pdf->SetAutoPageBreak(true, PDF_MARGIN_BOTTOM);
209: 
210:         //set image scale factor
211:         $this->pdf->setImageScale(2.5);
212: 
213:         // add a page
214:         $this->pdf->AddPage();
215: 
216:         // add the title
217:         $title = '<h1>'.$this->BO->get('title').'</h1>';
218:         // add some custom footer info about the article
219:         $footer = '<br><p>Article URL: <a href="'.$this->BO->get('URL').'">'.$this->BO->get('URL').'</a><br>Title: '.$this->BO->get('title').'<br>Author: '.$this->BO->get('author').'</p>';
220: 
221:         // write the title
222:         self::$logger->debug('Writing the title ['.$title.'] to the PDF');
223:         $this->pdf->writeHTML(utf8_encode($title), true, false, true, false, '');
224:         // output the HTML content
225:         self::$logger->debug('Writing the content ['.$this->content.'] to the PDF');
226:         $this->pdf->writeHTML(utf8_encode($this->content), true, false, true, false, '');
227:         // write the article footer
228:         $this->pdf->writeHTML(utf8_encode($footer), true, false, true, false, '');
229:         self::$logger->debug('Writing the footer ['.$footer.'] to the PDF');
230: 
231:         // save this PDF to the cache
232:         $this->pdf->Output($this->PDFFilename, 'F');
233: 
234:         self::$logger->debug('<<__construct()');
235:     }
236: 
237:      /**
238:       * Facade method which will invoke our custom markdown class rather than the standard one.
239:       *
240:       * @since 1.0
241:       */
242:      private function markdown($text, $attachURL = '')
243:      {
244:          $config = ConfigProvider::getInstance();
245: 
246:         /*
247:          * Initialize the parser and return the result of its transform method.
248:          *
249:          */
250:         static $parser;
251: 
252:          if (!isset($parser)) {
253:              $parser = new \Alpha\Util\Extension\Markdown();
254:          }
255: 
256:         /*
257:          * Replace all instances of $sysURL in the text with the app.url setting from config
258:          *
259:          */
260:         $text = str_replace('$sysURL', $config->get('app.url'), $text);
261: 
262:         // transform text using parser.
263:         return $parser->transform($text);
264:      }
265: 
266:     /**
267:      * Fetter for the content.
268:      *
269:      * @return string HTML rendered the content
270:      *
271:      * @since 1.0
272:      */
273:     public function getContent()
274:     {
275:         return $this->content;
276:     }
277: 
278:     /**
279:      * Saves the HTML generated by Markdown to the cache directory.
280:      *
281:      * @throws Alpha\Exception\AlphaException
282:      *
283:      * @since 1.0
284:      */
285:     private function HTMLCache()
286:     {
287:         // check to ensure that the article is not transient before caching it
288:         if ($this->BO->getID() != '00000000000') {
289:             $fp = fopen($this->HTMLFilename, 'w');
290:             if (!$fp) {
291:                 throw new AlphaException('Failed to open the cache file for writing, directory permissions my not be set correctly!');
292:             } else {
293:                 flock($fp, 2); // locks the file for writting
294:                 fwrite($fp, $this->content);
295:                 flock($fp, 3); // unlocks the file
296:                 fclose($fp); //closes the file
297:             }
298:         }
299:     }
300: 
301:     /**
302:      * Used to check the HTML cache for the BO cache file.
303:      *
304:      * @return bool true if the file exists, false otherwise
305:      *
306:      * @since 1.0
307:      */
308:     private function checkHTMLCache()
309:     {
310:         $config = ConfigProvider::getInstance();
311: 
312:         return file_exists($this->HTMLFilename);
313:     }
314: 
315:     /**
316:      * Method to load the content of the cache file to the $content attribute of this object.
317:      *
318:      * @throws Alpha\Exception\AlphaException
319:      *
320:      * @since 1.0
321:      */
322:     private function loadHTMLCache()
323:     {
324:         $fp = fopen($this->HTMLFilename, 'r');
325: 
326:         if (!$fp) {
327:             throw new AlphaException('Failed to open the cache file for reading, directory permissions my not be set correctly!', 'loadHTMLCache()');
328:         } else {
329:             $this->content = fread($fp, filesize($this->HTMLFilename));
330:             fclose($fp); //closes the file
331:         }
332:     }
333: 
334:     /**
335:      * Used to check the PDF cache for the BO cache file.
336:      *
337:      * @return bool true if the file exists, false otherwise
338:      *
339:      * @since 1.0
340:      */
341:     private function checkPDFCache()
342:     {
343:         return file_exists($this->PDFFilename);
344:     }
345: 
346:     /**
347:      * Used to serve the cached PDF file as a download.
348:      *
349:      * @since 1.0
350:      * @deprecated
351:      */
352:     private function serveCachedPDF($name)
353:     {
354:         // first load the file
355:         $handle = fopen($this->PDFFilename, 'r');
356:         $data = fread($handle, filesize($this->PDFFilename));
357:         fclose($handle);
358: 
359:         $filesize = strlen($data);
360:         $mimetype = 'application/octet-stream';
361: 
362:         // Start sending headers
363:         header('Pragma: public'); // required
364:         header('Expires: 0');
365:         header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
366:         header('Cache-Control: private', false); // required for certain browsers
367:         header('Content-Transfer-Encoding: binary');
368:         header('Content-Type: '.$mimetype);
369:         header('Content-Length: '.$filesize);
370:         header('Content-Disposition: attachment; filename="'.$name.'";');
371: 
372:         // Send data
373:         echo $data;
374:         die();
375:     }
376: 
377:     /**
378:      * Returns the raw PDF data stream.
379:      *
380:      * @return string
381:      *
382:      * @since 2.0
383:      */
384:     public function getPDFData()
385:     {
386:         $PDFDownloadName = str_replace(' ', '-', $this->BO->get('title').'.pdf');
387:         // first load the file
388:         $handle = fopen($this->PDFFilename, 'r');
389:         $data = fread($handle, filesize($this->PDFFilename));
390:         fclose($handle);
391: 
392:         return $data;
393:     }
394: }
395: 
Alpha Framework 2.0.4 API Documentation API documentation generated by ApiGen 2.8.0