tmpl = WBClass::create('patTemplate'); $this->tmpl->setType('tex'); $this->tmpl->setDefaultAttribute('whitespace', 'keep'); $this->tmpl->setRoot('resource/latex'); if (empty($parameter['mandator'])) { $parameter['mandator'] = WBClass::create('WBMandator', $parameter); } $this->mandator = $parameter['mandator']; WBClass::load('patTemplate_Function_Config'); patTemplate_Function_Config::setMandator($this->mandator); } /** * Compress Output File * * @param bool whether or not compress the output */ public function useCompression($comp = true) { $this->outputCompression = $comp; } /** * Load Templates * * Auxiliary function for convinience * @param string $tmpl */ public function loadTemplates($tmpl) { $tmpl = sprintf(self::TEMPLATE_FILE_FORMAT, $tmpl); $this->tmpl->readTemplatesFromInput($tmpl); } /** * Check if Named Template Exists * * @see patTemplate:exists() * @return bool */ public function templateExists($name) { return $this->tmpl->exists($name); } /** * Add Var * * Quote values and add to template engine * * @use patTemplate::addVar() * @param string $tmpl * @param string $name * @param mixed * @return bool */ public function addVar($tmpl, $name, $var) { $this->quote($var); return $this->tmpl->addVar($tmpl, $name, $var); } /** * Add Vars * * Data to fill placeholders, proxy for template engine. * * @see patTemplate::addVars() * @param string $tmpl * @param array $vars * @param string $prefix * @return bool */ public function addVars($tmpl, $vars, $prefix = '') { if (!is_array($vars)) { return; } $this->quote($vars); return $this->tmpl->addVars($tmpl, $vars, $prefix); } /** * Add Rows * * Quote values and add to template engine * * @use patTemplate::addRows() * @param string $tmpl * @param array */ public function addRows($tmpl, $rows) { $this->quote($rows); $this->tmpl->addRows($tmpl, $rows); } /** * Add Global Vars * * Data to fill placeholders, proxy for template engine. * * @see patTemplate::addGlobalVar() * @param string $name * @param string $var * @return bool */ public function addGlobalVar($name, $var) { $this->quote($var); return $this->tmpl->addGlobalVar($name, $var); } /** * Add Global Vars * * Data to fill placeholders, proxy for template engine. * * @see patTemplate::addGlobalVars() * @param array $vars * @param string $prefix * @return bool */ public function addGlobalVars($vars, $prefix = '') { if (!is_array($vars)) { return; } $this->quote($vars); return $this->tmpl->addGlobalVars($vars, $prefix); } /** * Add VFSFile As Template Var * * Add vfsfile to list for template * * @see renderVfsFileList() * @param string $tmpl * @param string $name * @param string $vfsfid */ public function addVFSFile($tmpl, $name, $vfsfid) { $this->vfsFileList[] = array( 'tmpl' => $tmpl, 'name' => $name, 'id' => $vfsfid ); } /** * Add VFSFile As Global Var * * Add vfsfile to list, mark as global * * @see renderVfsFileList() * @param string $name * @param string $vfsfid */ public function addGlobalVFSFile($name, $vfsfid) { $this->vfsFileList[] = array( 'tmpl' => '__global', 'name' => $name, 'id' => $vfsfid ); } /** * Actually Resolve VFS File List * * Resolve vfsfid to file names and add to template variable */ private function renderVfsFileList() { foreach ($this->vfsFileList as $l) { $file = $this->resolveVFSFile($l['id']); if ('__global' == $l['tmpl']) { return $this->addGlobalVar($l['name'], $file); } else { $this->addVar($l['tmpl'], $l['name'], $file); } } } /** * Fill List in Template * * Standard method to add rows to list template * * @param array list * @param string template name stump */ protected function list2Template($list, $name = 'list') { $this->tmpl->addGlobalVar($name . '_count', count($list)); if (!$this->tmpl->exists($name . '_entry')) { return; } $this->addRows($name . '_entry', $list); $max = WBParam::get('wb/template/listtemplatemax', 10); for ($i = 1; $i < $max; ++$i) { if (!$this->tmpl->exists($name . '_entry_' . $i)) { break; } $this->addRows($name . '_entry_' . $i, $list); } } /** * Quote Value For TeX * * @param mixed $var */ private function quote(&$var) { if (is_array($var)) { // recursive foreach ($var as &$v) { $this->quote($v); } return; } // don't force string replace on numbers if (is_numeric($var)) { return; } // string replace $replace = array( '<=>' => '$\\Leftrightarrow$', '<=' => '$\\Leftarrow$', '=>' => '$\\Rightarrow$', '"' => "''", "\r\n" => "\n", '%' => '\\%', ' ' => '~', '&' => '\\&', '_' => '\\_', // '€' => '\\EUR', '°' => '{\\textdegree}', '®' => '®', '<' => '<', '>' => '>', '≥' => '$\ge$', '≤' => '$\le$', '­' => '\\-', json_decode("\u200b") => '\\-' ); $sear = array_keys($replace); $repl = array_values($replace); $var = str_replace($sear, $repl, $var); } /** * Render PDF * * Prepare LaTeX work dir, render content, use post processor * and return path to PDF file. (Design oattern: Template Function) * * @return string */ public function render() { $this->tmpDir = WBClass::create('WBFile'); // $this->tmpDir->mkdir('var/tmp/tex-test'); $this->tmpDir->tempdir('tex-'); $this->renderBefore(); $this->addUserInfo(); $this->renderVfsFileList(); $this->renderMandator(); $this->renderContent(); $tex = $this->tmpl->getParsedTemplate('texdoc'); $tex = WBString::replaceSuperPlaceholders($tex); file_put_contents($this->tmpDir->realpath() . '/doc.tex', $tex); $this->renderAfter(); $pdf = $this->processRenderedPdf($this->runLaTeX()); $this->cleanup(); return $pdf; } /** * Add Current User's Info * * */ private function addUserInfo() { WBClass::load('WBUser'); $this->user = WBUser::getCurrent(); if (!$this->user || !$this->user->isAuthenticated()) { return; } // add user data and group membership $this->tmpl->addGlobalVars($this->user->getData(), 'user_current_'); $groups = array(); foreach ($this->user->getGroups() as $gid => $gname) { $groups[$gname] = '1'; } $this->tmpl->addGlobalVars($groups, 'user_current_group_'); } /** * Do something with */ protected function processRenderedPdf($pdf) { return $pdf; } /** * Before Rendering Starts * * */ protected function renderBefore() { } /** * Actually Render Mandator Data * * Fill Templates LaTeX Like */ protected function renderMandator() { $data = $this->mandator->get(); $this->addGlobalVars($data, 'mandator_'); } /** * Actually Render Content * * Fill Templates LaTeX Like */ protected function renderContent() { } /** * After Rendering Is Done * * */ protected function renderAfter() { } /** * Execute LaTeX Commands * * Run LaTeX (ignore return codes) and GhostScript to optimize file size with nice image resolution * @return string path of rendered file */ protected function runLaTeX() { $cwd = getcwd(); $path = $this->tmpDir->realpath(); chdir($path); $resource = array(); $resource[] = '.'; $resource[] = WBParam::get('wb/dir/base') . '/resource/latex'; foreach (WBParam::get('wb/dir/resource', array()) as $r) { $resource[] = $r . '/resource/latex'; } // fake home folder /** @var WBFile */ $home = WBClass::create('WBFile'); $home->mkdir('var/cache/home/laTeX'); // run ĹaTeX and ignore return code $cmdPpdfLaTeX = WBParam::get('wb/latex/pdflatex/command', 'pdflatex'); $cmd = sprintf('HOME=%s TEXINPUTS=%s: %s --interaction=nonstopmode doc.tex', $home->realpath(), implode(':', $resource), $cmdPpdfLaTeX); file_put_contents($path . '/cmd.log', $cmd . "\n"); $res = $this->exec($cmd); // run twice to collect all references file_put_contents($path . '/cmd.log', $cmd . "\n", FILE_APPEND); $res = $this->exec($cmd); if ($this->useCompression()) { $cmdGS = WBParam::get('wb/latex/gs/command', 'gs'); // run ghostscript to reduce file size, ignore return code // -dPDFSETTINGS=/screen (screen-view-only quality, 72 dpi images) // -dPDFSETTINGS=/ebook (low quality, 150 dpi images) // -dPDFSETTINGS=/printer (high quality, 300 dpi images) // -dPDFSETTINGS=/prepress (high quality, color preserving, 300 dpi imgs) // -dPDFSETTINGS=/default (almost identical to /screen) $cmd = sprintf('%s -sDEVICE=pdfwrite -dCompatibilityLevel=1.7 -dPDFSETTINGS=/prepress -dNOPAUSE -dQUIET -dBATCH -sOutputFile=doc.small.pdf doc.pdf', $cmdGS); file_put_contents($path . '/cmd.log', $cmd . "\n", FILE_APPEND); $res = $this->exec($cmd); rename($path . '/doc.small.pdf', $path . '/doc.pdf'); } chdir($cwd); rename($path . '/doc.pdf', $path . '.pdf'); return $path . '.pdf'; } /** * Tidy Up * * Remove temporary file */ public function cleanup() { if (0 < WBParam::get('wb/debug', 0)) { return; } $this->tmpDir->removeDir(); } /** * Run Programm * * @param string * @return bool true on success */ protected function exec($cmd) { // echo $cmd . "\n"; exec($cmd, $out, $ret); if (0 < $ret) { //echo "FAILED $cmd
\n"; //echo "
" . implode("\n", $out) . "
"; return false; } return true; } /** * Resolve VFSFile To render image * * Create symlinks for actual image VFS file * * @see symlink() * @param string fileid * @return string filename or empty of not valid */ protected function resolveVFSFile($vfsfid) { if (0 == $vfsfid) { return; } if (!$this->vfsFile) { $this->vfsFile = WBClass::create('WBVFS_File'); } /** @var WBVFS_File */ $file = $this->vfsFile->loadById($vfsfid); if ('image' != $file->getMime(WBVFS_File::MIME_MAJOR)) { return ''; } $mime = $file->getMime(WBVFS_File::MIME_MINOR); $fileName = 'image-' . $vfsfid . '.' . $mime; switch ($mime) { case 'jpeg': case 'jpg': case 'png': if (file_exists($this->tmpDir->realpath() . '/' . $fileName)) { break; } symlink($file->getPath() . '.org', $this->tmpDir->realpath() . '/' . $fileName); break; default: $fileName = 'image-' . $vfsfid . '.png'; if (file_exists($this->tmpDir->realpath() . '/' . $fileName)) { break; } $cmd = sprintf('convert %s.org %s/image-%s.png', $file->getPath(), $this->tmpDir->realpath(), $vfsfid); $this->exec($cmd); break; } return $fileName; } }