diff options
Diffstat (limited to 'lib/classes/exportdocument/ExportPDF.php')
| -rw-r--r-- | lib/classes/exportdocument/ExportPDF.php | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/lib/classes/exportdocument/ExportPDF.php b/lib/classes/exportdocument/ExportPDF.php new file mode 100644 index 0000000..f676c45 --- /dev/null +++ b/lib/classes/exportdocument/ExportPDF.php @@ -0,0 +1,369 @@ +<?php +# Lifter010: TODO +/** + * ExportPDF.php - create and export or save a pdf with simple HTML-Data + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * @author Rasmus Fuhse & Peter Thienel + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + * @category Stud.IP + */ + +define('K_PATH_IMAGES', $GLOBALS['STUDIP_BASE_PATH'] . '/public/assets/images/'); + +/** + * Class to create an PDF by putting in Stud.IP-formatted code. + * Usage: + * + * $doc = new ExportPDF(); + * $doc->addPage(); + * $doc->addContent('Hallo, %%wir%% benutzen :studip:-Formatierung.'); + * $doc->dispatch("test_pdf"); + * //lines following dispatch won't be accessed anymor, because dispatch + * //cancels all other output. + * + */ +class ExportPDF extends TCPDF implements ExportDocument +{ + private $media_proxy = NULL; + private $config; + private $defaults = false; + private $page_added = false; + private $h_title = ''; + private $h_string = ''; + private $domains; + static protected $countEndnote = 0; + + /** + * Create a basic document (without any content so far). + * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul> + * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. + * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). + * @param boolean $unicode TRUE means that the input text is unicode (default = true) + * @param String $encoding charset encoding; default is UTF-8 + */ + public function __construct($orientation = 'P', $unit = 'mm', $format = 'A4', $unicode = true, $encoding = 'UTF-8') + { + $this->config = Config::GetInstance(); + if ($this->config->getValue('LOAD_EXTERNAL_MEDIA') == 'proxy') { + $this->media_proxy = new MediaProxy(); + } + parent::__construct($orientation, $unit, $format, $unicode, $encoding, false); + $this->getDomains(); + $this->setDefaults(); + } + + /** + * Adding a new page to the document. This page can contain even more content + * than for just one page. The pagebreak will be managed by tcpdf. But this function + * will create a new pagebreak. Needs to be called at least once to addContent. + * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul> + * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). + * @param boolean $keepmargins if true overwrites the default page margins with the current margins + * @param boolean $tocpage if true set the tocpage state to true (the added page will be used to display Table Of Content). + */ + public function addPage($orientation = '', $format = '', $keepmargins = false, $tocpage = false) + { + $this->page_added = true; + parent::AddPage($orientation, $format, $keepmargins, $tocpage); + } + + /** + * Adding Stud.IP formatted code to the current page of the pdf. + * Remember to call addPage first. + * @param string $content Stud.IP formatted code + */ + public function addContent($content) + { + $endnote = ""; + preg_match_all("#\[comment(=.*)?\](.*)\[/comment\]#msU", $content, $matches); + if (count($matches[0])) { + $endnote .= "<br><br>"._("Kommentare")."<hr>"; + for ($i=0; $i < count($matches[0]); $i++) { + $endnote .= ($i+1).") ".htmlReady(mb_substr($matches[1][$i], 1)).": ".htmlReady($matches[2][$i])."<br>"; + } + } + $content = preg_replace_callback("#\[comment(=.*)?\](.*)\[/comment\]#msU", function ($m) {return $this->addEndnote($m[1], $m[2]);}, $content); + $content = formatReady($content, true, true, true, null); + $content = str_replace("<table", "<table border=\"1\"", $content); + + // Since TCPDF cannot handle missing images at all, the content needs + // to be cleaned from those (see tickets #2957, #3329 and #3688) + $content = preg_replace_callback('/<img[^>]+src="(.*?)"[^>]*>/', function ($match) { + $url = $match[1]; + + // Detect possible html entities in url and remove them + if (mb_strpos($url, '&') !== false) { + $url = html_entity_decode($url); + } + + // Handle optional media proxy + if (Config::GetInstance()->LOAD_EXTERNAL_MEDIA) { + $parsed = parse_url($url); + // Detect media proxy + if (mb_strpos($parsed['path'], 'media_proxy') !== false && mb_strpos($parsed['query'], 'url=') !== false) { + // Remove media proxy + parse_str($parsed['query'], $parameters); + $url = $parameters['url']; + } + } + + // Fetch headers from url, handle possible redirects + do { + $headers = get_headers($url, true, get_default_http_stream_context($url)); + if (!$headers) { + break; + } + list(, $status) = explode(' ', $headers[0]); + + $url = $headers['Location'] ?? $headers['location'] ?? $url; + } while (in_array($status, [300, 301, 302, 303, 305, 307])); + + $status = $status ?? 404; + + // Replace image with link on error (and not internal), otherwise return sainitized + // url + return ((!is_internal_url($url) || $status == 404) && $status >= 400) + ? sprintf('[<a href="%s">%s</a>]', $url, basename($url)) + : str_replace($match[1], $url, $match[0]); + }, $content); + + $this->writeHTML($content.$endnote); + } + + /** + * + * @param <type> $commented_by + * @param <type> $text + * @return <type> + */ + public function addEndnote($commented_by, $text) + { + self::$countEndnote++; + return ">>"._("Kommentar")." ".self::$countEndnote.">>"; + } + + /** + * Dispatches the PDF to the user and cancels all other output of Stud.IP. + * @param string $filename name of the future file without the extension. + */ + public function dispatch($filename) + { + $this->Output($filename.".pdf", 'I'); + } + + /** + * Saves the content as a file in the filesystem and returns a FileRef object. + * + * @param string $filename name of the future file without the extension. + * @param mixed $folder_id md5-id of a given folder in database or null for nothing + * @return FileRef of the exported file or false if creation of the FileRef or its associated File object failed. + */ + public function save($filename, $folder_id = null) + { + global $user; + + //get folder: + $folder = Folder::find($folder_id); + if(!$folder) { + return false; + } + $folder = $folder->getTypedFolder(); + + //Create a File: + $file = new File(); + $file->user_id = $user->id; + $file->mime_type = 'application/pdf'; + $file->name = FileManager::cleanFileName($filename); + $file->storage = 'disk'; + if(!$file->store()) { + return false; + } + + //...and a FileRef: + $file_ref = new FileRef(); + $file_ref->file_id = $file->id; + $file_ref->folder_id = $folder->getId(); + $file_ref->user_id = $user->id; + $file_ref->name = $file->name; + if(!$file_ref->store()) { + return false; + } + + //Now we can create the PDF file and store it in the file's path: + $path = $file->getPath(); + $this->Output($path, 'F'); + $file->size = filesize($path); + if($file->store()) { + return $file_ref; + } + + return false; + } + + /** + * Sets some default-values for the document, that tcpdf needs. + */ + private function setDefaults () + { + $this->defaults = true; + + // setting defaults + $this->SetCreator('Stud.IP - ' . $this->config->getValue('UNI_NAME_CLEAN')); + // set header and footer fonts + $this->setHeaderFont([PDF_FONT_NAME_MAIN, '', 8]); + $this->setFooterFont([PDF_FONT_NAME_DATA, '', 8]); + // set default monospaced font + $this->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED); + //set margins + $this->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); + $this->SetHeaderMargin(PDF_MARGIN_HEADER); + $this->SetFooterMargin(PDF_MARGIN_FOOTER); + //set auto page breaks + $this->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); + //set image scale factor + $this->setImageScale(PDF_IMAGE_SCALE_RATIO); + // set default font subsetting mode + $this->setFontSubsetting(true); + // Set font + //$this->SetFont('helvetica', '', 10, '', true); + + // set default page header + $this->setHeaderData(); + + } + + /** + * Sets the title of the header of each page. + * @param string $title title of the head + */ + public function setHeaderTitle ($title) + { + $this->h_title = $title; + $this->setHeaderData(); + } + + /** + * Sets the subtitle of the header of each page. + * @param string $subtitle subtitle of the head + */ + public function setHeaderSubtitle ($subtitle) + { + $this->h_string = $subtitle; + $this->setHeaderData(); + } + + /** + * Creates a header for each page with a custom logo defined + * @param string $ln header image logo + * @param int $lw header image logo width in mm + * @param string $ht string to print as title on document header + * @param string $hs string to print on document header + */ + public function setHeaderData($ln = '', $lw = 0, $ht = '', $hs = '', $tc = [], $lc = []) { + if (!$ln) { + $ln = Config::get()->PDF_LOGO ?: 'logos/logoklein.png'; + } + $lw = 30; + $ht = ($ht == '' ? $this->h_title : $ht); + $hs = ($hs == '' ? $this->h_string : $hs); + + parent::resetHeaderTemplate(); + + parent::setHeaderData($ln, $lw, $ht, $hs); + } + + /** + * Overrides writeHTML-method of tcpdf to convert image-urls, so that they + * aren't accessed via proxy but directly. + * @param string $html text to display + * @param boolean $ln if true add a new line after text (default = true) + * @param boolean $fill Indicates if the background must be painted (true) or transparent (false). + * @param boolean $reseth if true reset the last cell height (default false). + * @param boolean $cell if true add the current left (or right for RTL) padding to each Write (default false). + * @param string $align Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> + */ + public function writeHTML ($html, $ln = true, $fill = false, $reseth = false, $cell = false, $align = '') + { + $html = preg_replace_callback('/src="([^@].*)"/U', function ($m) {return $this->convertURL($m[1]);}, $html); + parent::writeHTML($html, $ln, $fill, $reseth, $cell, $align); + } + + /** + * Converts URLs in images so that the webserver can access them without proxy. + * @param string $url of an image + * @return string " src=\"".$converted_url."\"" + */ + protected function convertURL($url) + { + $convurl = $url; + $url_elements = @parse_url($url); + $url = $url_elements['path']; + if (isset($url_elements['query'])) { + $url .= "?{$url_elements['query']}"; + } + if (mb_strpos(implode('#', $this->domains), $url_elements['host']) !== false) { + if (mb_strpos($url, 'dispatch.php/media_proxy?url=') !== false) { + $targeturl = urldecode(mb_substr($url, 4)); + try { + // is file in cache? + if (!$metadata = $this->media_proxy->getMetaData($targeturl)) { + $convurl = $targeturl; + } else { + $convurl = $this->config->getValue('MEDIA_CACHE_PATH') . '/' . md5($targeturl); + } + } catch (Exception $e) { + $convurl = ''; + } + } else if (mb_stripos($url, 'dispatch.php/document/download') !== false) { + if (preg_match('#([a-f0-9]{32})#', $url, $matches)) { + $file_ref = FileRef::find($matches[1]); + $folder = $file_ref->folder->getTypedFolder(); + if($folder->isFileDownloadable($file_ref->id, $GLOBALS['user']->id)) { + $convurl = $file_ref->file->getPath(); + } + } + } else if (mb_stripos($url, 'download') !== false + || mb_stripos($url, 'sendfile.php') !== false) { + //// get file id + if (preg_match('#([a-f0-9]{32})#', $url, $matches)) { + $file_ref = FileRef::find($matches[1]); + $folder = $file_ref->folder->getTypedFolder(); + if($folder->isFileDownloadable($file_ref->id, $GLOBALS['user']->id)) { + $convurl = $file_ref->file->getPath(); + } else { + $convurl = Assets::image_path('messagebox/exception.png'); + } + } + } + } + + $src = 'src=""'; + $file_content = @file_get_contents($convurl, false, get_default_http_stream_context($convurl)); + if ($file_content) { + $img_size = @getimagesizefromstring($file_content); + if (is_array($img_size) && $img_size[0] > 0) { + $src = 'src="@' . base64_encode($file_content) . '"'; + } + } + return $src; + } + + /** + * finds an array with all domains of this Stud.IP and stores it in $this->domains + */ + protected function getDomains() + { + $this->domains = []; + $host_url_parsed = @parse_url($GLOBALS['ABSOLUTE_URI_STUDIP']); + if (isset($GLOBALS['STUDIP_DOMAINS'])) { + $this->domains = $GLOBALS['STUDIP_DOMAINS']; + } + $this->domains[] = $host_url_parsed['host']; + } + +} |
