From 347caf500a07ff94ac40ff8694a15e3aba4354ab Mon Sep 17 00:00:00 2001 From: "vincent@cubedesigners.com" Date: Thu, 29 Aug 2019 12:07:58 +0000 Subject: [PATCH] wait #2439 @0:10 --- inc/ws/Metier/class.ws.book.parametres.php | 3 +- inc/ws/Metier/class.ws.document.php | 2119 ++++++++++---------- 2 files changed, 1064 insertions(+), 1058 deletions(-) diff --git a/inc/ws/Metier/class.ws.book.parametres.php b/inc/ws/Metier/class.ws.book.parametres.php index 276a24caa..38ef59199 100644 --- a/inc/ws/Metier/class.ws.book.parametres.php +++ b/inc/ws/Metier/class.ws.book.parametres.php @@ -595,9 +595,10 @@ class wsBookParametres extends wsParametres $this->fields['scorm_quiz_as_questionnaire'] = array('type' => 'boolean', 'default' => false, 'editable' => true, 'label' => __('Traiter les quiz comme des questionnaires (toutes les réponses sont considérées comme correctes)'), 'grade' => 5); $this->fields['scorm_quizdata'] = array('type' => 'freefile', 'editable' => true, 'default' => '', 'label' => __('Données de quiz')); $this->fields['scorm_complete_on_exit'] = ['type' => 'boolean', 'default' => false, 'label' => 'Marquer le cours comme "Complete" à la fermeture de la fenêtre', 'editable' => true]; + $this->fields['scorm_complete_on_last_page'] = ['type' => 'boolean', 'default' => false, 'label' => 'Marquer le cours comme "Complete" lorsque le visiteur atteint la dernière page', 'editable' => true]; $this->forms['scorm'] = array('label' => __('SCORM'), - 'fieldsnames' => array('scorm_enable', 'scorm_version', 'scorm_id', 'scorm_org', 'scorm_title', 'scorm_variables', '|', 'scorm_complete_on_exit', '|', 'scorm_quizdata', 'scorm_score', 'scorm_score_min', 'scorm_quiz_as_questionnaire')); + 'fieldsnames' => array('scorm_enable', 'scorm_version', 'scorm_id', 'scorm_org', 'scorm_title', 'scorm_variables', '|', 'scorm_complete_on_exit', 'scorm_complete_on_last_page', '|', 'scorm_quizdata', 'scorm_score', 'scorm_score_min', 'scorm_quiz_as_questionnaire')); $versions = wsUrl::getFluidbookVersions(false); $ignore = ['v1', 'v2', 'phonegap']; diff --git a/inc/ws/Metier/class.ws.document.php b/inc/ws/Metier/class.ws.document.php index d694452ee..9d2477c4c 100644 --- a/inc/ws/Metier/class.ws.document.php +++ b/inc/ws/Metier/class.ws.document.php @@ -12,1061 +12,1066 @@ class wsDocument extends cubeMetier { - protected $document_id; - protected $file; - protected $proprietaire; - protected $pages; - protected $trim; - protected $date; - protected $localInfos; - protected $generalInfos; - protected $conversionInfos; - protected $bookmarks; - protected $numberSections; - protected $localHash; - protected $version; - // Crop & cut - protected $autocrop; - protected $manualcrop; - protected $autocut; - protected $manualcut; - // Files - protected $out; - protected $in; - protected $html; - protected $uncompressed; - protected $log; - protected $common_log_pointer; - protected $pages_log_pointers; - protected $infos; - protected $cropped; - protected $rgb; - - protected static $_docsDir; - - const NORMAL = 0; - const FLATTEN = 1; - const POLY2BITMAP = 2; - const BITMAP = 3; - const BARBARE_PNM = 4; - const BARBARE_GS = 5; - const MAX = 5; - const PNM_FILL = 2; - - protected static $resolution2multiply = array(72 => 2, 100 => 2, 150 => 3, 200 => 3, 300 => 3, 450 => 4, 600 => 5); - // Number section styles - protected static - $numberStyles = array('NoNumber' => 'no', 'DecimalArabicNumerals' => 'decimal', - 'UppercaseRomanNumerals' => 'roman_up', 'LowercaseRomanNumerals' => 'roman_low', - 'UppercaseLetters' => 'letters_up', 'LowercaseLetters' => 'letters_low'); - - public function init() - { - $this->out = wsDocument::getDir($this->document_id); - $this->log = $this->out . '/logs/'; - $this->html = $this->out . '/html/'; - $this->in = $this->out . 'original.pdf'; - $this->uncompressed = $this->out . 'uncompressed.pdf'; - $this->infos = $this->out . 'infos.txt'; - if (!file_exists($this->out)) { - mkdir($this->out, 0755, true); - mkdir($this->log, 0755); - } - if (!file_exists($this->html)) { - mkdir($this->html, 0755); - } - $this->cropped = $this->out . 'crop.pdf'; - $this->pages_log_pointers = array(); - - if (is_null($this->conversionInfos)) { - $this->conversionInfos = new wsDocumentConversionInfos(); - } - } - - public function getCroppedPDF(){ - if(!file_exists($this->cropped)){ - return $this->in; - } - return $this->cropped; - } - - public function copyOriginalFromUpload($tmp_file) - { - move_uploaded_file($tmp_file, $this->in); - } - - public function copyOriginalFromOlderVersion() - { - if (!file_exists($this->in)) { - copy('https://ws.fluidbook.com/docs/' . $this->document_id . '/original.pdf', $this->in); - } - } - - public function extractFonts() - { - $extractor = new wsPDFFontExtractor($this->out, $this); - $extractor->extract(); - } - - public function getInfos($in = null, $force = false) - { - if (is_null($in)) { - $in = $this->in; - } - - $fwstk = new cubeCommandLine('fwstk.sh'); - $fwstk->setPath(CONVERTER_PATH); - $fwstk->setArg('--input ' . $in); - $fwstk->setArg('--infos'); - $fwstk->execute(); - $this->addToLog($fwstk); - $out = $fwstk->output; - - $pdfinfo = new cubeCommandLine('pdfinfo'); - $pdfinfo->setPath(CONVERTER_PATH); - $pdfinfo->setArg('-box'); - $pdfinfo->setArg('f', 1); - $pdfinfo->setArg('l', 100000); - $pdfinfo->setArg(null, $in); - $pdfinfo->execute(); - $this->addToLog($pdfinfo); - $out .= "\n"; - $out .= $pdfinfo->output; - - $this->addToLog('Parse infos'); - $this->parseInfos($out); - - $this->addToLog('Set Page Infos'); - $this->conversionInfos->setPageNumber($this->generalInfos['pages']); - - $this->addToLog('Write infos'); - file_put_contents($this->infos, $out); - - $this->addToLog('Find cut disposition'); - - $this->findCutDisposition(); - } - - public function findCutDisposition() - { - $this->detectSpreads(); - $this->detectPageDifferences(); - } - - protected function detectPageDifferences() - { - $this->addToLog('Detect page differences'); - // Vérifie si la cropbox et la trimbox sont identiques pour toutes les pages - $difference = false; - foreach ($this->generalInfos['page'] as $page => $infos) { - if (!isset($infos['crop']) || !isset($infos['crop'])) { - continue; - } - if ($infos['crop'] != $infos['trim']) { - $difference = true; - } - } - if (!$difference) { - return false; - } - // Vérifie si la trimbox définie toutes les pages de la même taille - $heights = array(); - $widths = array(); - foreach ($this->generalInfos['page'] as $page => $infos) { - $heights[] = round($infos['trim']->height); - $widths[] = round($infos['trim']->width); - } - $heights = array_unique($heights); - $widths = array_unique($widths); - if (count($heights) == 1 && count($widths) == 1) { - $this->autocrop = 'trim'; - $this->manualcrop = false; - } else { - $this->autocrop = false; - $this->manualcrop = true; - } - } - - protected function detectSpreads() - { - $this->addToLog('Detect spreads'); - // Détection des spreads - - $this->autocut = false; - $this->manualcut = false; - if ($this->generalInfos['pages'] <= 2) { - return; - } - - foreach ($this->generalInfos['page'] as $page => $infos) { - if ($page == 1) { - $first = $infos['size']; - } elseif ($page == $this->generalInfos['pages']) { - $last = $infos['size']; - } elseif ($page == 2) { - $second = $infos['size']; - } - } - - if ($first == $last && $last == $second) { - $ratio = $first[0] / $first[1]; - $this->autocut = false; - if ($ratio <= 1) { - $this->manualcut = false; - } elseif ($ratio >= 6) { - $this->manualcut = 'L8'; - } elseif ($ratio >= 3) { - $this->manualcut = 'L4'; - } elseif ($ratio >= 2) { - $this->manualcut = 'L3'; - } else { - $this->manualcut = '14-23'; - } - return; - } - $this->manualcut = false; - if (self::compareSizes($last, $first) && cubeMath::compare($first[0] * 2, $second[0], 0.9)) { - $this->autocut = '1-23-4'; - } - if (cubeMath::compare($first[0] * 2, $second[0], 0.9) && self::compareSizes($last, $second)) { - $this->autocut = '1-23'; - } - - $this->addToLog('Detect Spreads : Manual cut ' . $this->manualcut . ' ; Auto cut : ' . $this->autocut); - } - - public static function compareSizes($x, $y, $tolerance = 0.9) - { - return cubeMath::compare($x[0], $y[0], $tolerance) && cubeMath::compare($x[1], $y[1], $tolerance); - } - - public function lnCrop() - { - $root = dirname($this->cropped); - `cd $root;ln -s original.pdf crop.pdf`; - } - - public function parseInfos($data) - { - cubePHP::set_memory('4G'); - // This function get general infos (pages sizes, boxes, number sections and - // bookmarks - // Init arrays - $this->generalInfos = array(); - $this->generalInfos['size'] = array(0, 0); - $this->bookmarks = array(); - $this->numberSections = ''; - $bookmark_id = 0; - - $res['size'] = array(0, 0); - $lines = explode("\n", $data); - foreach ($lines as $line) { - $line = trim(cubeText::condenseWhite($line)); - $e = explode(':', $line, 2); - $k = trim($e[0]); - if (count($e) < 2) { - continue; - } - $v = trim($e[1]); - if ($k == 'Pages' || $k == 'NumberOfPages') { - $this->pages = $this->generalInfos['pages'] = $v; - $this->generalInfos['page'] = array(); - for ($i = 1; $i <= $this->pages; $i++) { - $this->generalInfos['page'][$i] = array(); - } - } elseif (preg_match('|Page\s+([0-9]+)\s+(.*)Box:\s+([0-9.]*)\s+([0-9.]*)\s+([0-9.]*)\s+([0-9.]*)|iu', $line, $m)) { - $this->generalInfos['page'][$m[1]][strtolower($m[2])] = new wsBox($m[3], $m[4], $m[5], $m[6]); - } elseif (preg_match('|Page\s+([0-9]+)\s+size:\s+([0-9.]*)[pts[:space:]]+x\s+([0-9.]*)\s+pts|iu', $line, $m)) { - $this->generalInfos['page'][$m[1]]['size'] = array($m[2], $m[3]); - if ($m[1] == 1) { - $this->generalInfos['size'][0] = $m[2]; - $this->generalInfos['size'][1] = $m[3]; - } - } elseif ($k == 'BookmarkTitle') { - $this->bookmarks[$bookmark_id] = array('titre' => str_replace(' ', '', trim($v))); - } elseif ($k == 'BookmarkLevel') { - $this->bookmarks[$bookmark_id]['level'] = $v; - } elseif ($k == 'BookmarkPageNumber') { - $this->bookmarks[$bookmark_id]['page'] = $v; - $bookmark_id++; - } elseif ($k == 'NumberSections') { - $this->numberSections = $v; - } - } - return $res; - } - - public function getPagesNumber() - { - $this->getInfos(); - return $this->generalInfos['pages']; - } - - public function globalOperations() - { - $this->addToLog('Get infos'); - $this->getInfos(); - if ($this->CropAndCut()) { - $this->addToLog('Get infos after crop'); - $this->getInfos($this->cropped, true); - } - $this->addToLog('Get links'); - $this->getLinks(); - $this->addToLog('End of global ops'); - $this->splitDoc(); - } - - public function splitDoc() - { - $this->addToLog('Split document'); - mkdir($this->out . '/pdf'); - $pdftk = new cubeCommandLine('pdftk'); - $pdftk->setPath(CONVERTER_PATH); - $pdftk->setArg(null, $this->getCroppedPDF()); - $pdftk->setArg(null, 'burst'); - $pdftk->setArg(null, 'output'); - $pdftk->setArg(null, $this->out . 'pdf/p%d.pdf'); - $pdftk->execute(); - $this->addToLog($pdftk); - } - - public function CropAndCut() - { - $this->addToLog('Crop And Cut'); - if (!$this->isCropped()) { - $this->lnCrop(); - return false; - } - if ($this->autocrop == 'trim') { - $this->trimDocument(); - } else { - $this->lnCrop(); - } - - if ($this->autocut) { - $this->cutDocument($this->autocut); - return true; - } - return false; - } - - public function cutDocument($mode) - { - $fwstk = new cubeCommandLine('fwstk.sh'); - $fwstk->setPath(CONVERTER_PATH); - $fwstk->setArg('--input ' . $this->in); - $fwstk->setArg('--cut ' . $mode); - $fwstk->setArg('--output ' . $this->cropped); - $fwstk->execute(); - $this->addToLog($fwstk); - } - - public function trimDocument() - { - $fwstk = new cubeCommandLine('fwstk.sh'); - $fwstk->setPath(CONVERTER_PATH); - $fwstk->setArg('--input ' . $this->in); - $fwstk->setArg('--trim'); - $fwstk->setArg('--output ' . $this->cropped); - $fwstk->execute(); - $this->addToLog($fwstk); - } - - public function processOnePage($page, $force = true) - { - if ($force) { - $this->addToLog('Processing page #' . $page); - $this->makeMiniShot($page); - $this->makeSWFFiles($page); - $this->makeHTML5Files($page); - } - } - - public function processAllPages() - { - for ($i = 1; $i <= $this->generalInfos['pages']; $i++) { - $this->processOnePage($i); - } - } - - public function processRange($pages) - { - foreach ($pages as $i) { - $this->processOnePage($i); - } - } - - public function getLinks() - { - $fwstk = new cubeCommandLine('fwstk.sh'); - $fwstk->setPath(CONVERTER_PATH); - $fwstk->setArg('--input ' . $this->getCroppedPDF()); - $fwstk->setArg('--extractLinks ' . $this->out . 'p%d.csv'); - $fwstk->setArg('--threads 1'); - $fwstk->execute(); - $this->addToLog($fwstk); - } - - public function getHighlightTextsData() - { - $fwstk = new cubeCommandLine('fwstk.sh'); - $fwstk->setPath(CONVERTER_PATH); - $fwstk->setArg('--input ' . $this->getCroppedPDF()); - $fwstk->setArg('--layout ' . $this->html . 'p%d.fby'); - $fwstk->setArg('--cmaps ' . $this->html); - $fwstk->setArg('--fonts' . $this->out . 'fonts/web/'); - $fwstk->execute(); - $this->addToLog($fwstk); - } - - public function getResolutionRatio() - { - $a4surface = 500990; // en pt² - $docSurface = $this->generalInfos['size'][0] * $this->generalInfos['size'][1]; // en pt² - // to have the same surface resulting in px, we have to sqrt the ratio between the two surfaces defined above - return sqrt($a4surface / $docSurface); - } - - public function makeMiniShot($page) - { - $this->makeShotFixedWidth($page, 'p', 500, 65, 4, 'PNM'); - } - - public function makeShotFixedWidth($page, $prefix = '', $w = 100, $quality = 90, $antialiasing = 4, $method = 'PNM') - { - // Make thumbs of $w width - // resolution 72 make 1pt=1px - $width = $this->generalInfos['size'][0]; - $ratio = $width / $w; - $this->makeShot($page, $prefix, round(72 / $ratio, 2), $quality, $antialiasing, $method); - } - - public function makeShotFixedHeight($page, $prefix = '', $h = '', $quality = 90, $antialiasing = 4, $method = 'PNM') - { - // Make thumbs of $w height - // resolution 72 make 1pt=1px - $height = $this->generalInfos['size'][1]; - $ratio = $height / $h; - $this->makeShot($page, $prefix, round(72 / $ratio, 2), $quality, $antialiasing, $method); - } - - public function makeShot($page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $method = 'PNM', $in = null) - { - $error = false; - if (is_null($in)) { - $in = $this->getCroppedPDF(); - } - // Delete all old files - $res = $this->out . $prefix . $page . '.jpg'; - if (file_exists($res)) { - @unlink($res); - } - - if ($method == 'GS') { - $this->makeShotGS($page, $prefix, $resolution, $quality, $antialiasing, $in); - } elseif ($method == 'PNM') { - $this->makeShotPNM($page, $prefix, $resolution, $quality, $antialiasing, $in); - } - // Test the result by checking all files - if (!file_exists($res)) { - $error = true; - } - // If error, we try to make thumbs with other method - if ($error) { - if ($method == 'GS') { - $this->makeShotPNM($page, $prefix, $resolution, $quality, $antialiasing, $in); - } elseif ($method == 'PNM') { - $this->makeShotGS($page, $prefix, $resolution, $quality, $antialiasing, $in); - } - } - } - - protected function makeShotGS($page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $in = null) - { - if (is_null($in)) { - $in = $this->getCroppedPDF(); - } - // Fabrication des thumbanails avec ghostscript - $gs = new cubeCommandLine('gs', null, true); - $gs->setPath(CONVERTER_PATH); - $gs->setEnv('GS_FONTPATH', FONT_PATH); - $gs->setArg('-dBATCH'); - $gs->setArg('-dNOPAUSE'); - $gs->setArg('-dNOPROMPT'); - // Antialias - $gs->setArg('-dDOINTERPOLATE'); - $gs->setArg('-dTextAlphaBits=' . $antialiasing); - $gs->setArg('-dGraphicsAlphaBits=' . $antialiasing); - // Device - $gs->setArg('-sDEVICE=jpeg'); - // Dispotion & colors - // $gs->setArg('-dUseCIEColor'); - $gs->setArg('-dAutoRotatePages=/None'); - $gs->setArg('-dUseCropBox'); - // Resolution & Quality - $gs->setArg('-r' . round($resolution)); - $gs->setArg('-dJPEGQ=' . $quality); - // Performances - $gs->setArg('-dNumRenderingThreads=4'); - // Page range - $gs->setArg('-dFirstPage=' . $page); - $gs->setArg('-dLastPage=' . $page); - // Files - $gs->setArg('-sOutputFile=' . $this->out . '/' . $prefix . $page . '.jpg'); - - $gs->setArg(null, $in); - $gs->execute(); - $this->addToLog($gs, true, $page); - } - - protected function makeShotPNM($page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $in = null, $texts = true) - { - if (is_null($in)) { - $in = $this->getCroppedPDF(); - } - - $tmp = cubeFiles::tempnam(); - - $antialiasing = $antialiasing ? 'yes' : 'no'; - $freetype = $texts ? 'yes' : 'no'; - // Exporte les fichiers - $pdftoppm = new cubeCommandLine('pdftoppm', null, true); - $pdftoppm->setPath(CONVERTER_PATH); - - $pdftoppm->setArg('f', $page); - $pdftoppm->setArg('l', $page); - $pdftoppm->setArg('-cropbox'); - $pdftoppm->setArg('-freetype ' . $freetype); - $pdftoppm->setArg('-singlefile'); - $pdftoppm->setArg('-aa ' . $antialiasing); - $pdftoppm->setArg('-aaVector ' . $antialiasing); - $pdftoppm->setArg('r', $resolution); - $pdftoppm->setArg(null, $in); - $pdftoppm->setArg(null, $tmp); - $pdftoppm->execute(); - $this->addToLog($pdftoppm, true, $page); - - $tmp .= '.ppm'; - - $jpegfile = $this->out . $prefix . $page . '.jpg'; - - if (file_exists($tmp)) { - // $pnmtojpeg = new cubeCommandLine('pnmtojpeg', $jpegfile, false); - // $pnmtojpeg->setArg('-quality ' . $quality); - // $pnmtojpeg->setArg(null, $tmp); - // $pnmtojpeg->execute(); - // $this->addToLog($pnmtojpeg, false, $page); - - - $cjpeg = new cubeCommandLine('cjpeg', null, true); - $cjpeg->setArg('-quality ' . ($quality + 6)); - $cjpeg->setArg('-outfile ' . $jpegfile); - $cjpeg->setArg(null, $tmp); - $cjpeg->execute(); - $this->addToLog($cjpeg, false, $page); - - unlink($tmp); - } - } - - protected function isCropped() - { - return $this->autocrop || $this->manualcrop || $this->autocut || $this->manualcut; - } - - public function makeSWFFiles($page, $resolution = null, $quality = null, $storeAllChars = null, $maxObjects = null, $method = null, $version = null) - { - $conversionSettings = $this->conversionInfos->pages[$page]; - if (is_null($storeAllChars)) { - $storeAllChars = true; - } - if (is_null($resolution)) { - $resolution = $conversionSettings->resolution; - } - if (is_null($method)) { - $method = $conversionSettings->method; - } - if (is_null($quality)) { - $quality = $conversionSettings->quality; - } - if (is_null($maxObjects)) { - $maxObjects = $conversionSettings->objects; - } - if (is_null($version)) { - $version = isset($conversionSettings->version) ? $conversionSettings->version : 'stable'; - } - - if ($maxObjects <= 1) { - $method = self::POLY2BITMAP; - } - - // Pour les fichiers croppés, on utilise la méthode flatten qui ne prends - // pas en compte les objets hors de la box - if ($this->isCropped()) { - // $method = max($method, self::FLATTEN); - } - - $out = $this->pdf2swf($page, $resolution * $this->getResolutionRatio(), $quality, $storeAllChars, $method, 'p', $version); - if ($method < self::BARBARE_PNM) { - // Analyse de la sortie pour détecter des typos manquantes - $overflow = false; - $overflowObjects = false; - $written = false; - $missing_fonts = array(); - if (file_exists($out)) { - $fp = fopen($out, 'rb'); - while ($line = fgets($fp)) { - if (preg_match('|Try putting a TTF version of that font \(named \"([A-Z-_0-9.]*)\"\)|Uui', trim($line), $matches)) { - $missing_fonts[] = $matches[1]; - } elseif (stristr($line, 'ID Table overflow')) { - $overflow = true; - } elseif (stristr($line, 'NOTICE SWF written')) { - $written = true; - } elseif (stristr($line, 'NOTICE Writing SWF file')) { - $written = true; - } - } - } - if (!is_null($page) && file_exists($this->out . 'p' . $page . '.swf')) { - $written = true; - } - // On teste si le fichier est écrit et qu'il a été généré par le premier niveau - if ($method < self::POLY2BITMAP && $written) { - $overflowObjects = $this->checkObjectsNumber($this->out . 'p' . $page . '.swf', $maxObjects, $page); - } - - if (!$written || $overflow || $overflowObjects) { - if ($method == self::MAX) { - return; - } - $nextMethod = $method + 1; - return $this->makeSWFFiles($page, $resolution, $quality, $storeAllChars, $maxObjects, $nextMethod, $version); - } - } - } - - public function makeHTML5Files($page) - { - // Then make HD background shots - $resolutions = array(300 => 85, 150 => 85, 36 => 85); - $rratio = $this->getResolutionRatio(); - foreach ($resolutions as $r => $q) { - $this->makeShotPNM($page, 'html/h' . $r . '-', $r * $rratio, $q, 4, null, false); - $this->makeShotPNM($page, 'html/t' . $r . '-', $r * $rratio, $q, 4, null, true); - } - $this->makeSVGFile($page); - } - - public function makeSVGFile($page) - { - $svgFile = $this->out . '/html/fp' . $page . '.svg'; - $source = $this->getCroppedPDF(); - - $pdftocairo = new cubeCommandLine('pdftocairo'); - $pdftocairo->setPath(CONVERTER_PATH); - $pdftocairo->setArg('f', $page); - $pdftocairo->setArg('l', $page); - $pdftocairo->setArg('r', 300); - $pdftocairo->setArg(null, '-expand'); - $pdftocairo->setArg(null, '-svg'); - $pdftocairo->setArg(null, $source); - $pdftocairo->setArg(null, $svgFile); - $pdftocairo->execute(); - - $this->addToLog($pdftocairo, true, $page); - } - - public static function extractTexts($svgFile, $textFile, $force = false) - { - $do = $force || !file_exists($textFile) || filesize($textFile) < 100 || filemtime($svgFile) > filemtime($textFile) || filemtime($textFile) < filemtime(__FILE__); - if (!$do) { - return; - } - - $svg = new DOMDocument(); - $svg->preserveWhiteSpace = false; - $svg->load($svgFile, LIBXML_PARSEHUGE); - - // Operations to delete - $xpath = new DOMXPath($svg); - $xpath->registerNamespace('svg', 'http://www.w3.org/2000/svg'); - $xpath->registerNamespace('xlink', 'http://www.w3.org/1999/xlink'); - $xpath->registerNamespace("php", "http://php.net/xpath"); - $xpath->registerPhpFunctions('has_not_text'); - $toDelete = array('//svg:defs/svg:clipPath', - '//svg:defs/svg:image', - '//svg:defs/svg:path', - '//svg:defs/svg:pattern', - '//svg:defs/svg:g[starts-with(@id, "surface")]//svg:path', - '/svg:svg/svg:g//svg:path', - '/svg:svg/svg:g//svg:rect', - '//svg:use[starts-with(@xlink:href, "#image")]' - ); - - //global $svglog; - //$svglog = array('XPATH : ' . print_r($xpath, true)); - foreach ($toDelete as $q) { - $list = $xpath->query($q); - // $svglog[] = "Evaluate xpath query " . $q; - // $svglog[] = 'Give ' . $list->length . ' results'; - // $svglog[] = 'Deleting Nodes in ' . print_r($list, true); - if (count($list)) { - foreach ($list as $node) { - /* @var $node DOMNode */ - $parent = $node->parentNode; - $parent->removeChild($node); - } - } - } - file_put_contents($textFile, $svg->saveXML()); - } - - protected function checkObjectsNumber($file, $maxObjects, $page) - { - $swfdump = new cubeCommandLine('swfdump', null, true); - $swfdump->setPath(CONVERTER_PATH); - $swfdump->setArg(null, $file); - $swfdump->execute(); - $this->addToLog($swfdump, true, $page); - - str_replace('[01a]', '', $swfdump->output, $nbObjects); - if ($nbObjects > $maxObjects) { - return true; - } - return false; - } - - protected function dumpSWF($page, $prefix = 'p') - { - $swfdump = new cubeCommandLine('/usr/local/swftools/special-swfdump/bin/swfdump', null, true); - $swfdump->setPath(CONVERTER_PATH); - $swfdump->setArg('t'); - $swfdump->setArg('p'); - $swfdump->setArg('F'); - $swfdump->setArg(null, $this->out . $prefix . $page . '.swf'); - $swfdump->execute(); - $this->addToLog($swfdump, false, $page); - return $swfdump->output; - } - - /** - * wsDocument::pdf2swf() - * - * @param mixed $page - * @param integer $resolution - * @param integer $quality - * @param mixed $storeAllChars - * @param integer $method - * @return - */ - protected function pdf2swf($page, $resolution = 150, $quality = 90, $storeAllChars = true, $method = 0, $prefix = 'p', $version = 'stable') - { - /* - -h , --help Print short help message and exit - -V , --version Print version info and exit - -o , --output file.swf Direct output to file.swf. If file.swf contains '%' (file%.swf), then each page goes to a seperate file. - -p , --pages range Convert only pages in range with range e.g. 1-20 or 1,4,6,9-11 or - -P , --password password Use password for deciphering the pdf. - -v , --verbose Be verbose. Use more than one -v for greater effect. - -z , --zlib Use Flash 6 (MX) zlib compression. - -i , --ignore Allows pdf2swf to change the draw order of the pdf. This may make the generated - -j , --jpegquality quality Set quality of embedded jpeg pictures to quality. 0 is worst (small), 100 is best (big). (default:85) - -s , --set param=value Set a SWF encoder specific parameter. See pdf2swf -s help for more information. - -w , --samewindow When converting pdf hyperlinks, don't make the links open a new window. - -t , --stop Insert a stop() command in each page. - -T , --flashversion num Set Flash Version in the SWF header to num. - -F , --fontdir directory Add directory to the font search path. - -b , --defaultviewer Link a standard viewer to the swf file. - -l , --defaultloader Link a standard preloader to the swf file which will be displayed while the main swf is loading. - -B , --viewer filename Link viewer filename to the swf file. - -L , --preloader filename Link preloader filename to the swf file. - -q , --quiet Suppress normal messages. Use -qq to suppress warnings, also. - -S , --shapes Don't use SWF Fonts, but store everything as shape. - -f , --fonts Store full fonts in SWF. (Don't reduce to used characters). - -G , --flatten Remove as many clip layers from file as possible. - -I , --info Don't do actual conversion, just display a list of all pages in the PDF. - -Q , --maxtime n Abort conversion after n seconds. Only availableon Unix. - - PDF device global parameters: - ----------------------------- - fontdir= a directory with additional fonts - font= an additional font filename - pages= the range of pages to convert (example: pages=1-100,210-) - zoom= the resultion (default: 72) - languagedir= Add an xpdf language directory - multiply= Render everything at the resolution - poly2bitmap Convert graphics to bitmaps - bitmap Convert everything to bitmaps - - SWF Parameters : - ---------------- - SWF layer options : - ------------------- - - jpegsubpixels= resolution adjustment for jpeg images (same as jpegdpi, but in pixels) - ppmsubpixels= shortcut for setting both jpegsubpixels and ppmsubpixels - drawonlyshapes convert everything to shapes (currently broken) - ignoredraworder allow to perform a few optimizations for creating smaller SWFs - linksopennewwindow make links open a new browser window - linktarget target window name of new links - linkcolor==7) - bboxvars store the bounding box of the SWF file in actionscript variables - dots Take care to handle dots correctly - reordertags=0/1 (default: 1) perform some tag optimizations - internallinkfunction= when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called - externallinkfunction= when the user clicks an external link (e.g. http://www.foo.bar/) on the converted file, this actionscript function is called - disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles) - caplinewidth= the minimum thichness a line needs to have so that capstyles become visible (and are converted) - insertstop put an ActionScript "STOP" tag in every frame - protect add a "protect" tag to the file, to prevent loading in the Flash editor - flashversion= the SWF fileversion (6) - framerate= SWF framerate - minlinewidth= convert horizontal/vertical boxes smaller than this width to lines (0.05) - simpleviewer Add next/previous buttons to the SWF - animate insert a showframe tag after each placeobject (animate draw order of PDF files) - jpegquality= set compression quality of jpeg images - splinequality= Set the quality of spline convertion to value (0-100, default: 100). - disablelinks Disable links. - */ - - if (file_exists($this->out . $prefix . $page . '.swf')) { - unlink($this->out . $prefix . $page . '.swf'); - } - - if (!in_array($method, array(self::BARBARE_PNM, self::BARBARE_GS))) { - if (in_array($version, array('legacy', 'stable', 'latest', 'git'))) { - $program = '/usr/local/swftools/' . $version . '/bin/pdf2swf'; - } else { - $program = 'pdf2swf'; - } - - $pdf2swf = new cubeCommandLine($program, null, true); - $pdf2swf->setPath(CONVERTER_PATH); - - $pdf2swf->setArg('p', 1); - $flashversion = 10; - - if ($method == self::NORMAL) { - // Default - $multiply = 1; - } elseif ($method == self::FLATTEN) { - $multiply = 1; - $pdf2swf->setArg('flatten'); - } elseif ($method == self::POLY2BITMAP) { - // Raster graphics, keep texts - $pdf2swf->setArg('set poly2bitmap'); - $pdf2swf->setArg('set multiply', $this->_findMultiply($resolution)); - } elseif ($method == self::BITMAP) { - // Raster all - $pdf2swf->setArg('set bitmap'); - $pdf2swf->setArg('set multiply', $this->_findMultiply($resolution)); - } - // $pdf2swf->setManualArg('-v'); - $pdf2swf->setArg('T', $flashversion); - $pdf2swf->setArg('set reordertags', '0'); - if ($storeAllChars) { - $pdf2swf->setArg('fonts'); - $pdf2swf->setArg('set storeallcharacters'); - } - if (DEV) { - $pdf2swf->setArg('F', 'C:/Windows/Fonts'); - } else { - $pdf2swf->setArg('F', '/home/typo/fonts'); - } - $pdf2swf->setArg('set subpixels', $resolution / 72); - $pdf2swf->setArg('set jpegquality', $quality); - $pdf2swf->setArg('set disablelinks'); - $pdf2swf->setArg('set dots'); - if ($version == 'git') { - $pdf2swf->setArg(null, '-T7'); - $pdf2swf->setArg('set alignfonts'); - } - $pdf2swf->setArg(null, $this->out . 'pdf/p' . $page . '.pdf'); - $pdf2swf->setArg('output', $this->out . $prefix . $page . '.swf'); - $pdf2swf->execute(); - - if ($version == 'git') { - $f = $this->out . $prefix . $page . '.swf'; - $combine = new cubeCommandLine('swfcombine'); - $combine->setArg('d'); - $combine->setArg(null, '-F9'); - $combine->setArg(null, $f); - $combine->setArg('o', $f); - $combine->execute(); - } - - $this->addToLog($pdf2swf, true, $page); - } else { - $this->pdf2swfBarbare($page, $resolution, $quality, $method); - } - } - - protected function _findMultiply($resolution) - { - $resolution /= $this->getResolutionRatio(); - return self::$resolution2multiply[$resolution]; - } - - protected function makeAS3($page) - { - $swffile = $this->out . 'p' . $page . '.swf'; - - $swfcombine = new cubeCommandLine('swfcombine'); - $swfcombine->setPath(CONVERTER_PATH); - $swfcombine->setArg('merge'); - $swfcombine->setArg('stack1'); - $swfcombine->setArg('z'); - $swfcombine->setManualArg('-v'); - $swfcombine->setArg('o', $swffile); - $swfcombine->setArg(null, ROOT . '/swf/as3Container.swf'); - $swfcombine->setManualArg('content=' . $swffile); - $swfcombine->execute(); - $this->addToLog($swfcombine, true, $page); - } - - protected function pdf2swfBarbare($page, $resolution = 150, $quality = 85, $method = 4) - { - // Fabrique les images - - $this->addToLog('Making barbare swf', true, $page); - - if ($method == self::BARBARE_PNM) { - $this->makeShot($page, 'barbare', $resolution, $quality, 4, 'PNM'); - } elseif ($method == self::BARBARE_GS) { - $this->makeShot($page, 'barbare', $resolution, $quality, 4, 'GS'); - } - - $dim = getimagesize($this->out . 'barbare' . $page . '.jpg'); - - // A partir des images, on crée les swf - $jpeg2swf = new cubeCommandLine('jpeg2swf'); - $jpeg2swf->setPath(CONVERTER_PATH); - $jpeg2swf->setArg('--quality', $quality); - $jpeg2swf->setArg('--output', $this->out . 'p' . $page . '.swf'); - $jpeg2swf->setArg('--flashversion', 10); - $jpeg2swf->setArg('--width', $dim[0] * (72 / $resolution)); - $jpeg2swf->setArg('--height', $dim[1] * (72 / $resolution)); - $jpeg2swf->setArg('--fit-to-movie'); - $jpeg2swf->setArg(null, $this->out . 'barbare' . $page . '.jpg'); - $jpeg2swf->execute(); - $this->addToLog($jpeg2swf, true, $page); - // Suppression du jpeg - @unlink($this->out . '/barbare' . $page . '.jpg', true, $page); - - $pdf2swf = new cubeCommandLine('pdf2swf', null, true); - $pdf2swf->setPath(CONVERTER_PATH); - $pdf2swf->setArg('set poly2bitmap'); - $pdf2swf->setArg('p', 1); - $pdf2swf->setArg('stop'); - $pdf2swf->setArg('T', 10); - $pdf2swf->setArg('set reordertags', '0'); - $pdf2swf->setArg('fonts'); - $pdf2swf->setArg('set storeallcharacters'); - if (DEV) { - $pdf2swf->setArg('F', 'C:/Windows/Fonts'); - } else { - $pdf2swf->setArg('F', '/home/typo/fonts'); - } - if (file_exists($this->out . 't' . $page . '.swf')) { - unlink($this->out . 't' . $page . '.swf'); - } - $pdf2swf->setArg('set subpixels', '0.01'); - $pdf2swf->setArg('set jpegquality', '1'); - $pdf2swf->setArg('set disablelinks'); - $pdf2swf->setArg(null, $this->out . 'pdf/p' . $page . '.pdf'); - $pdf2swf->setArg('output', $this->out . 't' . $page . '.swf'); - $pdf2swf->execute(); - $this->addToLog($pdf2swf, true, $page); - - return ''; - } - - public function resetLog() - { - $f=$this->log . '/commons.log.gz'; - if(file_exists($f)) { - unlink($f); - } - } - - public function addToLog($cl, $output = true, $page = null) - { - if ($cl instanceof cubeCommandLine) { - $c = '--- Exécuté en ' . $cl->execTime . " s\n" . $cl->commande . "\n\n"; - if ($output) { - $c .= $cl->output . "\n\n"; - } - } elseif (is_string($cl)) { - $c = '--- ' . "\n\n"; - $c .= $cl . "\n\n"; - } - - if (!file_exists($this->log)) { - mkdir($this->log, 0777, true); - } - - - if (is_null($page)) { - $fname = 'commons.log'; - } else { - $fname = 'p' . $page . '.log'; - } - - $file = $this->log . '/' . $fname; - if (!file_exists($file) && file_exists($fname . '.gz')) { - $fopen = 'gzopen'; - $fwrite = 'gzwrite'; - $fclose = 'gzclose'; - $file .= '.gz'; - } else { - $fopen = 'fopen'; - $fwrite = 'fwrite'; - $fclose = 'fclose'; - } - - $pointer = $fopen($file, 'ab'); - $fwrite($pointer, $c); - $fclose($pointer); - } - - public function __destruct() - { - - } - - public static function getDir($id) - { - - if (!is_array(self::$_docsDir)) { - self::$_docsDir = array(); - } - - if (isset(self::$_docsDir[$id])) { - return self::$_docsDir[$id]; - } - - $new = WS_FILES . '/docs/' . $id . '/'; - $old = WS_FILES . '/docs1/' . $id . '/'; - $veryold = WS_FILES . '/docs2/' . $id . '/'; - - if (file_exists($new . 'p1.swf')) { - self::$_docsDir[$id] = $new; - return $new; - } elseif (file_exists($old . 'p1.swf')) { - self::$_docsDir[$id] = $old; - return $old; - } else if (file_exists($veryold . 'p1.swf')) { - self::$_docsDir[$id] = $veryold; - return $veryold; - } - if (!file_exists($new)) { - mkdir($new, 0777, true); - } - self::$_docsDir[$id] = $new; - return $new; - } + protected $document_id; + protected $file; + protected $proprietaire; + protected $pages; + protected $trim; + protected $date; + protected $localInfos; + protected $generalInfos; + protected $conversionInfos; + protected $bookmarks; + protected $numberSections; + protected $localHash; + protected $version; + // Crop & cut + protected $autocrop; + protected $manualcrop; + protected $autocut; + protected $manualcut; + // Files + protected $out; + protected $in; + protected $html; + protected $uncompressed; + protected $log; + protected $common_log_pointer; + protected $pages_log_pointers; + protected $infos; + protected $cropped; + protected $rgb; + + protected static $_docsDir; + + const NORMAL = 0; + const FLATTEN = 1; + const POLY2BITMAP = 2; + const BITMAP = 3; + const BARBARE_PNM = 4; + const BARBARE_GS = 5; + const MAX = 5; + const PNM_FILL = 2; + + protected static $resolution2multiply = array(72 => 2, 100 => 2, 150 => 3, 200 => 3, 300 => 3, 450 => 4, 600 => 5); + // Number section styles + protected static + $numberStyles = array('NoNumber' => 'no', 'DecimalArabicNumerals' => 'decimal', + 'UppercaseRomanNumerals' => 'roman_up', 'LowercaseRomanNumerals' => 'roman_low', + 'UppercaseLetters' => 'letters_up', 'LowercaseLetters' => 'letters_low'); + + public function init() + { + $this->out = wsDocument::getDir($this->document_id); + $this->log = $this->out . '/logs/'; + $this->html = $this->out . '/html/'; + $this->in = $this->out . 'original.pdf'; + $this->uncompressed = $this->out . 'uncompressed.pdf'; + $this->infos = $this->out . 'infos.txt'; + if (!file_exists($this->out)) { + mkdir($this->out, 0755, true); + mkdir($this->log, 0755); + } + if (!file_exists($this->html)) { + mkdir($this->html, 0755); + } + $this->cropped = $this->out . 'crop.pdf'; + $this->pages_log_pointers = array(); + + if (is_null($this->conversionInfos)) { + $this->conversionInfos = new wsDocumentConversionInfos(); + } + } + + public function getCroppedPDF() + { + if (!file_exists($this->cropped)) { + return $this->in; + } + return $this->cropped; + } + + public function copyOriginalFromUpload($tmp_file) + { + move_uploaded_file($tmp_file, $this->in); + } + + public function copyOriginalFromOlderVersion() + { + if (!file_exists($this->in)) { + copy('https://ws.fluidbook.com/docs/' . $this->document_id . '/original.pdf', $this->in); + } + } + + public function extractFonts() + { + $extractor = new wsPDFFontExtractor($this->out, $this); + $extractor->extract(); + } + + public function getInfos($in = null, $force = false) + { + if (is_null($in)) { + $in = $this->in; + } + + $fwstk = new cubeCommandLine('fwstk.sh'); + $fwstk->setPath(CONVERTER_PATH); + $fwstk->setArg('--input ' . $in); + $fwstk->setArg('--infos'); + $fwstk->execute(); + $this->addToLog($fwstk); + $out = $fwstk->output; + + $pdfinfo = new cubeCommandLine('pdfinfo'); + $pdfinfo->setPath(CONVERTER_PATH); + $pdfinfo->setArg('-box'); + $pdfinfo->setArg('f', 1); + $pdfinfo->setArg('l', 100000); + $pdfinfo->setArg(null, $in); + $pdfinfo->execute(); + $this->addToLog($pdfinfo); + $out .= "\n"; + $out .= $pdfinfo->output; + + $this->addToLog('Parse infos'); + $this->parseInfos($out); + + $this->addToLog('Set Page Infos'); + $this->conversionInfos->setPageNumber($this->generalInfos['pages']); + + $this->addToLog('Write infos'); + file_put_contents($this->infos, $out); + + $this->addToLog('Find cut disposition'); + + $this->findCutDisposition(); + } + + public function findCutDisposition() + { + $this->detectSpreads(); + $this->detectPageDifferences(); + } + + protected function detectPageDifferences() + { + $this->addToLog('Detect page differences'); + // Vérifie si la cropbox et la trimbox sont identiques pour toutes les pages + $difference = false; + foreach ($this->generalInfos['page'] as $page => $infos) { + if (!isset($infos['crop']) || !isset($infos['crop'])) { + continue; + } + if ($infos['crop'] != $infos['trim']) { + $difference = true; + } + } + if (!$difference) { + return false; + } + // Vérifie si la trimbox définie toutes les pages de la même taille + $heights = array(); + $widths = array(); + foreach ($this->generalInfos['page'] as $page => $infos) { + $heights[] = round($infos['trim']->height); + $widths[] = round($infos['trim']->width); + } + $heights = array_unique($heights); + $widths = array_unique($widths); + if (count($heights) == 1 && count($widths) == 1) { + $this->autocrop = 'trim'; + $this->manualcrop = false; + } else { + $this->autocrop = false; + $this->manualcrop = true; + } + } + + protected function detectSpreads() + { + $this->addToLog('Detect spreads'); + // Détection des spreads + + $this->autocut = false; + $this->manualcut = false; + if ($this->generalInfos['pages'] <= 2) { + return; + } + + foreach ($this->generalInfos['page'] as $page => $infos) { + if ($page == 1) { + $first = $infos['size']; + } elseif ($page == $this->generalInfos['pages']) { + $last = $infos['size']; + } elseif ($page == 2) { + $second = $infos['size']; + } + } + + if ($first == $last && $last == $second) { + $ratio = $first[0] / $first[1]; + $this->autocut = false; + if ($ratio <= 1) { + $this->manualcut = false; + } elseif ($ratio >= 6) { + $this->manualcut = 'L8'; + } elseif ($ratio >= 3) { + $this->manualcut = 'L4'; + } elseif ($ratio >= 2) { + $this->manualcut = 'L3'; + } else { + $this->manualcut = '14-23'; + } + return; + } + $this->manualcut = false; + if (self::compareSizes($last, $first) && cubeMath::compare($first[0] * 2, $second[0], 0.9)) { + $this->autocut = '1-23-4'; + } + if (cubeMath::compare($first[0] * 2, $second[0], 0.9) && self::compareSizes($last, $second)) { + $this->autocut = '1-23'; + } + + $this->addToLog('Detect Spreads : Manual cut ' . $this->manualcut . ' ; Auto cut : ' . $this->autocut); + } + + public static function compareSizes($x, $y, $tolerance = 0.9) + { + return cubeMath::compare($x[0], $y[0], $tolerance) && cubeMath::compare($x[1], $y[1], $tolerance); + } + + public function lnCrop() + { + $root = dirname($this->cropped); + `cd $root;ln -s original.pdf crop.pdf`; + } + + public function parseInfos($data) + { + cubePHP::set_memory('4G'); + // This function get general infos (pages sizes, boxes, number sections and + // bookmarks + // Init arrays + $this->generalInfos = array(); + $this->generalInfos['size'] = array(0, 0); + $this->bookmarks = array(); + $this->numberSections = ''; + $bookmark_id = 0; + + $res['size'] = array(0, 0); + $lines = explode("\n", $data); + foreach ($lines as $line) { + $line = trim(cubeText::condenseWhite($line)); + $e = explode(':', $line, 2); + $k = trim($e[0]); + if (count($e) < 2) { + continue; + } + $v = trim($e[1]); + if ($k == 'Pages' || $k == 'NumberOfPages') { + $this->pages = $this->generalInfos['pages'] = $v; + $this->generalInfos['page'] = array(); + for ($i = 1; $i <= $this->pages; $i++) { + $this->generalInfos['page'][$i] = array(); + } + } elseif (preg_match('|Page\s+([0-9]+)\s+(.*)Box:\s+([0-9.]*)\s+([0-9.]*)\s+([0-9.]*)\s+([0-9.]*)|iu', $line, $m)) { + $this->generalInfos['page'][$m[1]][strtolower($m[2])] = new wsBox($m[3], $m[4], $m[5], $m[6]); + } elseif (preg_match('|Page\s+([0-9]+)\s+size:\s+([0-9.]*)[pts[:space:]]+x\s+([0-9.]*)\s+pts|iu', $line, $m)) { + $this->generalInfos['page'][$m[1]]['size'] = array($m[2], $m[3]); + if ($m[1] == 1) { + $this->generalInfos['size'][0] = $m[2]; + $this->generalInfos['size'][1] = $m[3]; + } + } elseif ($k == 'BookmarkTitle') { + $this->bookmarks[$bookmark_id] = array('titre' => str_replace(' ', '', trim($v))); + } elseif ($k == 'BookmarkLevel') { + $this->bookmarks[$bookmark_id]['level'] = $v; + } elseif ($k == 'BookmarkPageNumber') { + $this->bookmarks[$bookmark_id]['page'] = $v; + $bookmark_id++; + } elseif ($k == 'NumberSections') { + $this->numberSections = $v; + } + } + return $res; + } + + public function getPagesNumber() + { + $this->getInfos(); + return $this->generalInfos['pages']; + } + + public function globalOperations() + { + $this->addToLog('Get infos'); + $this->getInfos(); + if ($this->CropAndCut()) { + $this->addToLog('Get infos after crop'); + $this->getInfos($this->cropped, true); + } + $this->addToLog('Get links'); + $this->getLinks(); + $this->addToLog('End of global ops'); + $this->splitDoc(); + } + + public function splitDoc() + { + $this->addToLog('Split document'); + mkdir($this->out . '/pdf'); + $pdftk = new cubeCommandLine('pdftk'); + $pdftk->setPath(CONVERTER_PATH); + $pdftk->setArg(null, $this->getCroppedPDF()); + $pdftk->setArg(null, 'burst'); + $pdftk->setArg(null, 'output'); + $pdftk->setArg(null, $this->out . 'pdf/p%d.pdf'); + $pdftk->execute(); + $this->addToLog($pdftk); + } + + public function CropAndCut() + { + $this->addToLog('Crop And Cut'); + if (!$this->isCropped()) { + $this->lnCrop(); + return false; + } + if ($this->autocrop == 'trim') { + $this->trimDocument(); + } else { + $this->lnCrop(); + } + + if ($this->autocut) { + $this->cutDocument($this->autocut); + return true; + } + return false; + } + + public function cutDocument($mode) + { + $fwstk = new cubeCommandLine('fwstk.sh'); + $fwstk->setPath(CONVERTER_PATH); + $fwstk->setArg('--input ' . $this->in); + $fwstk->setArg('--cut ' . $mode); + $fwstk->setArg('--output ' . $this->cropped); + $fwstk->execute(); + $this->addToLog($fwstk); + } + + public function trimDocument() + { + $fwstk = new cubeCommandLine('fwstk.sh'); + $fwstk->setPath(CONVERTER_PATH); + $fwstk->setArg('--input ' . $this->in); + $fwstk->setArg('--trim'); + $fwstk->setArg('--output ' . $this->cropped); + $fwstk->execute(); + $this->addToLog($fwstk); + } + + public function processOnePage($page, $force = true) + { + if ($force) { + $this->addToLog('Processing page #' . $page); + $this->makeMiniShot($page); + $this->makeSWFFiles($page); + $this->makeHTML5Files($page); + } + } + + public function processAllPages() + { + for ($i = 1; $i <= $this->generalInfos['pages']; $i++) { + $this->processOnePage($i); + } + } + + public function processRange($pages) + { + foreach ($pages as $i) { + $this->processOnePage($i); + } + } + + public function getLinks() + { + $fwstk = new cubeCommandLine('fwstk.sh'); + $fwstk->setPath(CONVERTER_PATH); + $fwstk->setArg('--input ' . $this->getCroppedPDF()); + $fwstk->setArg('--extractLinks ' . $this->out . 'p%d.csv'); + $fwstk->setArg('--threads 1'); + $fwstk->execute(); + $this->addToLog($fwstk); + } + + public function getHighlightTextsData() + { + $fwstk = new cubeCommandLine('fwstk.sh'); + $fwstk->setPath(CONVERTER_PATH); + $fwstk->setArg('--input ' . $this->getCroppedPDF()); + $fwstk->setArg('--layout ' . $this->html . 'p%d.fby'); + $fwstk->setArg('--cmaps ' . $this->html); + $fwstk->setArg('--fonts' . $this->out . 'fonts/web/'); + $fwstk->execute(); + $this->addToLog($fwstk); + } + + public function getResolutionRatio() + { + $a4surface = 500990; // en pt² + $docSurface = $this->generalInfos['size'][0] * $this->generalInfos['size'][1]; // en pt² + // to have the same surface resulting in px, we have to sqrt the ratio between the two surfaces defined above + return sqrt($a4surface / $docSurface); + } + + public function makeMiniShot($page) + { + $this->makeShotFixedWidth($page, 'p', 500, 65, 4, 'PNM'); + } + + public function makeShotFixedWidth($page, $prefix = '', $w = 100, $quality = 90, $antialiasing = 4, $method = 'PNM') + { + // Make thumbs of $w width + // resolution 72 make 1pt=1px + $width = $this->generalInfos['size'][0]; + $ratio = $width / $w; + $this->makeShot($page, $prefix, round(72 / $ratio, 2), $quality, $antialiasing, $method); + } + + public function makeShotFixedHeight($page, $prefix = '', $h = '', $quality = 90, $antialiasing = 4, $method = 'PNM') + { + // Make thumbs of $w height + // resolution 72 make 1pt=1px + $height = $this->generalInfos['size'][1]; + $ratio = $height / $h; + $this->makeShot($page, $prefix, round(72 / $ratio, 2), $quality, $antialiasing, $method); + } + + public function makeShot($page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $method = 'PNM', $in = null) + { + $error = false; + if (is_null($in)) { + $in = $this->getCroppedPDF(); + } + // Delete all old files + $res = $this->out . $prefix . $page . '.jpg'; + if (file_exists($res)) { + @unlink($res); + } + + if ($method == 'GS') { + $this->makeShotGS($page, $prefix, $resolution, $quality, $antialiasing, $in); + } elseif ($method == 'PNM') { + $this->makeShotPNM($page, $prefix, $resolution, $quality, $antialiasing, $in); + } + // Test the result by checking all files + if (!file_exists($res)) { + $error = true; + } + // If error, we try to make thumbs with other method + if ($error) { + if ($method == 'GS') { + $this->makeShotPNM($page, $prefix, $resolution, $quality, $antialiasing, $in); + } elseif ($method == 'PNM') { + $this->makeShotGS($page, $prefix, $resolution, $quality, $antialiasing, $in); + } + } + } + + protected function makeShotGS($page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $in = null) + { + if (is_null($in)) { + $in = $this->getCroppedPDF(); + } + // Fabrication des thumbanails avec ghostscript + $gs = new cubeCommandLine('gs', null, true); + $gs->setPath(CONVERTER_PATH); + $gs->setEnv('GS_FONTPATH', FONT_PATH); + $gs->setArg('-dBATCH'); + $gs->setArg('-dNOPAUSE'); + $gs->setArg('-dNOPROMPT'); + // Antialias + $gs->setArg('-dDOINTERPOLATE'); + $gs->setArg('-dTextAlphaBits=' . $antialiasing); + $gs->setArg('-dGraphicsAlphaBits=' . $antialiasing); + // Device + $gs->setArg('-sDEVICE=jpeg'); + // Dispotion & colors + // $gs->setArg('-dUseCIEColor'); + $gs->setArg('-dAutoRotatePages=/None'); + $gs->setArg('-dUseCropBox'); + // Resolution & Quality + $gs->setArg('-r' . round($resolution)); + $gs->setArg('-dJPEGQ=' . $quality); + // Performances + $gs->setArg('-dNumRenderingThreads=4'); + // Page range + $gs->setArg('-dFirstPage=' . $page); + $gs->setArg('-dLastPage=' . $page); + // Files + $gs->setArg('-sOutputFile=' . $this->out . '/' . $prefix . $page . '.jpg'); + + $gs->setArg(null, $in); + $gs->execute(); + $this->addToLog($gs, true, $page); + } + + protected function makeShotPNM($page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $in = null, $texts = true) + { + if (is_null($in)) { + $in = $this->getCroppedPDF(); + } + + $tmp = cubeFiles::tempnam(); + + $antialiasing = $antialiasing ? 'yes' : 'no'; + $freetype = $texts ? 'yes' : 'no'; + // Exporte les fichiers + $pdftoppm = new cubeCommandLine('pdftoppm', null, true); + $pdftoppm->setPath(CONVERTER_PATH); + + $pdftoppm->setArg('f', $page); + $pdftoppm->setArg('l', $page); + $pdftoppm->setArg('-cropbox'); + $pdftoppm->setArg('-freetype ' . $freetype); + $pdftoppm->setArg('-singlefile'); + $pdftoppm->setArg('-aa ' . $antialiasing); + $pdftoppm->setArg('-aaVector ' . $antialiasing); + $pdftoppm->setArg('r', $resolution); + $pdftoppm->setArg(null, $in); + $pdftoppm->setArg(null, $tmp); + $pdftoppm->execute(); + $this->addToLog($pdftoppm, true, $page); + + $tmp .= '.ppm'; + + $jpegfile = $this->out . $prefix . $page . '.jpg'; + + if (file_exists($tmp)) { + // $pnmtojpeg = new cubeCommandLine('pnmtojpeg', $jpegfile, false); + // $pnmtojpeg->setArg('-quality ' . $quality); + // $pnmtojpeg->setArg(null, $tmp); + // $pnmtojpeg->execute(); + // $this->addToLog($pnmtojpeg, false, $page); + + + $cjpeg = new cubeCommandLine('cjpeg', null, true); + $cjpeg->setArg('-quality ' . ($quality + 6)); + $cjpeg->setArg('-outfile ' . $jpegfile); + $cjpeg->setArg(null, $tmp); + $cjpeg->execute(); + $this->addToLog($cjpeg, false, $page); + + unlink($tmp); + } + } + + protected function isCropped() + { + return $this->autocrop || $this->manualcrop || $this->autocut || $this->manualcut; + } + + public function makeSWFFiles($page, $resolution = null, $quality = null, $storeAllChars = null, $maxObjects = null, $method = null, $version = null) + { + $conversionSettings = $this->conversionInfos->pages[$page]; + if (is_null($storeAllChars)) { + $storeAllChars = true; + } + if (is_null($resolution)) { + $resolution = $conversionSettings->resolution; + } + if (is_null($method)) { + $method = $conversionSettings->method; + } + if (is_null($quality)) { + $quality = $conversionSettings->quality; + } + if (is_null($maxObjects)) { + $maxObjects = $conversionSettings->objects; + } + if (is_null($version)) { + $version = isset($conversionSettings->version) ? $conversionSettings->version : 'stable'; + } + + if ($maxObjects <= 1) { + $method = self::POLY2BITMAP; + } + + // Pour les fichiers croppés, on utilise la méthode flatten qui ne prends + // pas en compte les objets hors de la box + if ($this->isCropped()) { + // $method = max($method, self::FLATTEN); + } + + $out = $this->pdf2swf($page, $resolution * $this->getResolutionRatio(), $quality, $storeAllChars, $method, 'p', $version); + if ($method < self::BARBARE_PNM) { + // Analyse de la sortie pour détecter des typos manquantes + $overflow = false; + $overflowObjects = false; + $written = false; + $missing_fonts = array(); + if (file_exists($out)) { + $fp = fopen($out, 'rb'); + while ($line = fgets($fp)) { + if (preg_match('|Try putting a TTF version of that font \(named \"([A-Z-_0-9.]*)\"\)|Uui', trim($line), $matches)) { + $missing_fonts[] = $matches[1]; + } elseif (stristr($line, 'ID Table overflow')) { + $overflow = true; + } elseif (stristr($line, 'NOTICE SWF written')) { + $written = true; + } elseif (stristr($line, 'NOTICE Writing SWF file')) { + $written = true; + } + } + } + if (!is_null($page) && file_exists($this->out . 'p' . $page . '.swf')) { + $written = true; + } + // On teste si le fichier est écrit et qu'il a été généré par le premier niveau + if ($method < self::POLY2BITMAP && $written) { + $overflowObjects = $this->checkObjectsNumber($this->out . 'p' . $page . '.swf', $maxObjects, $page); + } + + if (!$written || $overflow || $overflowObjects) { + if ($method == self::MAX) { + return; + } + $nextMethod = $method + 1; + return $this->makeSWFFiles($page, $resolution, $quality, $storeAllChars, $maxObjects, $nextMethod, $version); + } + } + } + + public function makeHTML5Files($page) + { + // Then make HD background shots + $resolutions = array(300 => 85, 150 => 85, 36 => 85); + $rratio = $this->getResolutionRatio(); + foreach ($resolutions as $r => $q) { + $this->makeShotPNM($page, 'html/h' . $r . '-', $r * $rratio, $q, 4, null, false); + $this->makeShotPNM($page, 'html/t' . $r . '-', $r * $rratio, $q, 4, null, true); + } + $this->makeSVGFile($page); + } + + public function makeSVGFile($page) + { + $svgFile = $this->out . '/html/fp' . $page . '.svg'; + $source = $this->getCroppedPDF(); + + $pdftocairo = new cubeCommandLine('pdftocairo'); + $pdftocairo->setPath(CONVERTER_PATH); + $pdftocairo->setArg('f', $page); + $pdftocairo->setArg('l', $page); + $pdftocairo->setArg('r', 300); + $pdftocairo->setArg(null, '-expand'); + $pdftocairo->setArg(null, '-svg'); + $pdftocairo->setArg(null, $source); + $pdftocairo->setArg(null, $svgFile); + $pdftocairo->execute(); + + $this->addToLog($pdftocairo, true, $page); + } + + public static function extractTexts($svgFile, $textFile, $force = false) + { + $do = $force || !file_exists($textFile) || filesize($textFile) < 100 || filemtime($svgFile) > filemtime($textFile) || filemtime($textFile) < filemtime(__FILE__); + if (!$do) { + return; + } + + $svg = new DOMDocument(); + $svg->preserveWhiteSpace = false; + $svg->load($svgFile, LIBXML_PARSEHUGE); + + // Operations to delete + $xpath = new DOMXPath($svg); + $xpath->registerNamespace('svg', 'http://www.w3.org/2000/svg'); + $xpath->registerNamespace('xlink', 'http://www.w3.org/1999/xlink'); + $xpath->registerNamespace("php", "http://php.net/xpath"); + $xpath->registerPhpFunctions('has_not_text'); + $toDelete = array('//svg:defs/svg:clipPath', + '//svg:defs/svg:image', + '//svg:defs/svg:path', + '//svg:defs/svg:pattern', + '//svg:defs/svg:g[starts-with(@id, "surface")]//svg:path', + '/svg:svg/svg:g//svg:path', + '/svg:svg/svg:g//svg:rect', + '//svg:use[starts-with(@xlink:href, "#image")]' + ); + + //global $svglog; + //$svglog = array('XPATH : ' . print_r($xpath, true)); + foreach ($toDelete as $q) { + $list = $xpath->query($q); + // $svglog[] = "Evaluate xpath query " . $q; + // $svglog[] = 'Give ' . $list->length . ' results'; + // $svglog[] = 'Deleting Nodes in ' . print_r($list, true); + if (count($list)) { + foreach ($list as $node) { + /* @var $node DOMNode */ + $parent = $node->parentNode; + $parent->removeChild($node); + } + } + } + file_put_contents($textFile, $svg->saveXML()); + } + + protected function checkObjectsNumber($file, $maxObjects, $page) + { + $swfdump = new cubeCommandLine('swfdump', null, true); + $swfdump->setPath(CONVERTER_PATH); + $swfdump->setArg(null, $file); + $swfdump->execute(); + $this->addToLog($swfdump, true, $page); + + str_replace('[01a]', '', $swfdump->output, $nbObjects); + if ($nbObjects > $maxObjects) { + return true; + } + return false; + } + + protected function dumpSWF($page, $prefix = 'p') + { + $swfdump = new cubeCommandLine('/usr/local/swftools/special-swfdump/bin/swfdump', null, true); + $swfdump->setPath(CONVERTER_PATH); + $swfdump->setArg('t'); + $swfdump->setArg('p'); + $swfdump->setArg('F'); + $swfdump->setArg(null, $this->out . $prefix . $page . '.swf'); + $swfdump->execute(); + $this->addToLog($swfdump, false, $page); + return $swfdump->output; + } + + /** + * wsDocument::pdf2swf() + * + * @param mixed $page + * @param integer $resolution + * @param integer $quality + * @param mixed $storeAllChars + * @param integer $method + * @return + */ + protected function pdf2swf($page, $resolution = 150, $quality = 90, $storeAllChars = true, $method = 0, $prefix = 'p', $version = 'stable') + { + /* + -h , --help Print short help message and exit + -V , --version Print version info and exit + -o , --output file.swf Direct output to file.swf. If file.swf contains '%' (file%.swf), then each page goes to a seperate file. + -p , --pages range Convert only pages in range with range e.g. 1-20 or 1,4,6,9-11 or + -P , --password password Use password for deciphering the pdf. + -v , --verbose Be verbose. Use more than one -v for greater effect. + -z , --zlib Use Flash 6 (MX) zlib compression. + -i , --ignore Allows pdf2swf to change the draw order of the pdf. This may make the generated + -j , --jpegquality quality Set quality of embedded jpeg pictures to quality. 0 is worst (small), 100 is best (big). (default:85) + -s , --set param=value Set a SWF encoder specific parameter. See pdf2swf -s help for more information. + -w , --samewindow When converting pdf hyperlinks, don't make the links open a new window. + -t , --stop Insert a stop() command in each page. + -T , --flashversion num Set Flash Version in the SWF header to num. + -F , --fontdir directory Add directory to the font search path. + -b , --defaultviewer Link a standard viewer to the swf file. + -l , --defaultloader Link a standard preloader to the swf file which will be displayed while the main swf is loading. + -B , --viewer filename Link viewer filename to the swf file. + -L , --preloader filename Link preloader filename to the swf file. + -q , --quiet Suppress normal messages. Use -qq to suppress warnings, also. + -S , --shapes Don't use SWF Fonts, but store everything as shape. + -f , --fonts Store full fonts in SWF. (Don't reduce to used characters). + -G , --flatten Remove as many clip layers from file as possible. + -I , --info Don't do actual conversion, just display a list of all pages in the PDF. + -Q , --maxtime n Abort conversion after n seconds. Only availableon Unix. + + PDF device global parameters: + ----------------------------- + fontdir= a directory with additional fonts + font= an additional font filename + pages= the range of pages to convert (example: pages=1-100,210-) + zoom= the resultion (default: 72) + languagedir= Add an xpdf language directory + multiply= Render everything at the resolution + poly2bitmap Convert graphics to bitmaps + bitmap Convert everything to bitmaps + + SWF Parameters : + ---------------- + SWF layer options : + ------------------- + + jpegsubpixels= resolution adjustment for jpeg images (same as jpegdpi, but in pixels) + ppmsubpixels= shortcut for setting both jpegsubpixels and ppmsubpixels + drawonlyshapes convert everything to shapes (currently broken) + ignoredraworder allow to perform a few optimizations for creating smaller SWFs + linksopennewwindow make links open a new browser window + linktarget target window name of new links + linkcolor==7) + bboxvars store the bounding box of the SWF file in actionscript variables + dots Take care to handle dots correctly + reordertags=0/1 (default: 1) perform some tag optimizations + internallinkfunction= when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called + externallinkfunction= when the user clicks an external link (e.g. http://www.foo.bar/) on the converted file, this actionscript function is called + disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles) + caplinewidth= the minimum thichness a line needs to have so that capstyles become visible (and are converted) + insertstop put an ActionScript "STOP" tag in every frame + protect add a "protect" tag to the file, to prevent loading in the Flash editor + flashversion= the SWF fileversion (6) + framerate= SWF framerate + minlinewidth= convert horizontal/vertical boxes smaller than this width to lines (0.05) + simpleviewer Add next/previous buttons to the SWF + animate insert a showframe tag after each placeobject (animate draw order of PDF files) + jpegquality= set compression quality of jpeg images + splinequality= Set the quality of spline convertion to value (0-100, default: 100). + disablelinks Disable links. + */ + + if (file_exists($this->out . $prefix . $page . '.swf')) { + unlink($this->out . $prefix . $page . '.swf'); + } + + if (!in_array($method, array(self::BARBARE_PNM, self::BARBARE_GS))) { + if (in_array($version, array('legacy', 'stable', 'latest', 'git'))) { + $program = '/usr/local/swftools/' . $version . '/bin/pdf2swf'; + } else { + $program = 'pdf2swf'; + } + + $pdf2swf = new cubeCommandLine($program, null, true); + $pdf2swf->setPath(CONVERTER_PATH); + + $pdf2swf->setArg('p', 1); + $flashversion = 10; + + if ($method == self::NORMAL) { + // Default + $multiply = 1; + } elseif ($method == self::FLATTEN) { + $multiply = 1; + $pdf2swf->setArg('flatten'); + } elseif ($method == self::POLY2BITMAP) { + // Raster graphics, keep texts + $pdf2swf->setArg('set poly2bitmap'); + $pdf2swf->setArg('set multiply', $this->_findMultiply($resolution)); + } elseif ($method == self::BITMAP) { + // Raster all + $pdf2swf->setArg('set bitmap'); + $pdf2swf->setArg('set multiply', $this->_findMultiply($resolution)); + } + // $pdf2swf->setManualArg('-v'); + $pdf2swf->setArg('T', $flashversion); + $pdf2swf->setArg('set reordertags', '0'); + if ($storeAllChars) { + $pdf2swf->setArg('fonts'); + $pdf2swf->setArg('set storeallcharacters'); + } + if (DEV) { + $pdf2swf->setArg('F', 'C:/Windows/Fonts'); + } else { + $pdf2swf->setArg('F', '/home/typo/fonts'); + } + $pdf2swf->setArg('set subpixels', $resolution / 72); + $pdf2swf->setArg('set jpegquality', $quality); + $pdf2swf->setArg('set disablelinks'); + $pdf2swf->setArg('set dots'); + if ($version == 'git') { + $pdf2swf->setArg(null, '-T7'); + $pdf2swf->setArg('set alignfonts'); + } + $pdf2swf->setArg(null, $this->out . 'pdf/p' . $page . '.pdf'); + $pdf2swf->setArg('output', $this->out . $prefix . $page . '.swf'); + $pdf2swf->execute(); + + if ($version == 'git') { + $f = $this->out . $prefix . $page . '.swf'; + $combine = new cubeCommandLine('swfcombine'); + $combine->setArg('d'); + $combine->setArg(null, '-F9'); + $combine->setArg(null, $f); + $combine->setArg('o', $f); + $combine->execute(); + } + + $this->addToLog($pdf2swf, true, $page); + } else { + $this->pdf2swfBarbare($page, $resolution, $quality, $method); + } + } + + protected function _findMultiply($resolution) + { + $resolution /= $this->getResolutionRatio(); + return self::$resolution2multiply[$resolution]; + } + + protected function makeAS3($page) + { + $swffile = $this->out . 'p' . $page . '.swf'; + + $swfcombine = new cubeCommandLine('swfcombine'); + $swfcombine->setPath(CONVERTER_PATH); + $swfcombine->setArg('merge'); + $swfcombine->setArg('stack1'); + $swfcombine->setArg('z'); + $swfcombine->setManualArg('-v'); + $swfcombine->setArg('o', $swffile); + $swfcombine->setArg(null, ROOT . '/swf/as3Container.swf'); + $swfcombine->setManualArg('content=' . $swffile); + $swfcombine->execute(); + $this->addToLog($swfcombine, true, $page); + } + + protected function pdf2swfBarbare($page, $resolution = 150, $quality = 85, $method = 4) + { + // Fabrique les images + + $this->addToLog('Making barbare swf', true, $page); + + if ($method == self::BARBARE_PNM) { + $this->makeShot($page, 'barbare', $resolution, $quality, 4, 'PNM'); + } elseif ($method == self::BARBARE_GS) { + $this->makeShot($page, 'barbare', $resolution, $quality, 4, 'GS'); + } + + $dim = getimagesize($this->out . 'barbare' . $page . '.jpg'); + + // A partir des images, on crée les swf + $jpeg2swf = new cubeCommandLine('jpeg2swf'); + $jpeg2swf->setPath(CONVERTER_PATH); + $jpeg2swf->setArg('--quality', $quality); + $jpeg2swf->setArg('--output', $this->out . 'p' . $page . '.swf'); + $jpeg2swf->setArg('--flashversion', 10); + $jpeg2swf->setArg('--width', $dim[0] * (72 / $resolution)); + $jpeg2swf->setArg('--height', $dim[1] * (72 / $resolution)); + $jpeg2swf->setArg('--fit-to-movie'); + $jpeg2swf->setArg(null, $this->out . 'barbare' . $page . '.jpg'); + $jpeg2swf->execute(); + $this->addToLog($jpeg2swf, true, $page); + // Suppression du jpeg + @unlink($this->out . '/barbare' . $page . '.jpg', true, $page); + + $pdf2swf = new cubeCommandLine('pdf2swf', null, true); + $pdf2swf->setPath(CONVERTER_PATH); + $pdf2swf->setArg('set poly2bitmap'); + $pdf2swf->setArg('p', 1); + $pdf2swf->setArg('stop'); + $pdf2swf->setArg('T', 10); + $pdf2swf->setArg('set reordertags', '0'); + $pdf2swf->setArg('fonts'); + $pdf2swf->setArg('set storeallcharacters'); + if (DEV) { + $pdf2swf->setArg('F', 'C:/Windows/Fonts'); + } else { + $pdf2swf->setArg('F', '/home/typo/fonts'); + } + if (file_exists($this->out . 't' . $page . '.swf')) { + unlink($this->out . 't' . $page . '.swf'); + } + $pdf2swf->setArg('set subpixels', '0.01'); + $pdf2swf->setArg('set jpegquality', '1'); + $pdf2swf->setArg('set disablelinks'); + $pdf2swf->setArg(null, $this->out . 'pdf/p' . $page . '.pdf'); + $pdf2swf->setArg('output', $this->out . 't' . $page . '.swf'); + $pdf2swf->execute(); + $this->addToLog($pdf2swf, true, $page); + + return ''; + } + + public function resetLog() + { + $f = $this->log . '/commons.log.gz'; + if (file_exists($f)) { + unlink($f); + } + } + + public function addToLog($cl, $output = true, $page = null) + { + if ($cl instanceof cubeCommandLine) { + $c = '--- Exécuté en ' . $cl->execTime . " s\n" . $cl->commande . "\n\n"; + if ($output) { + $c .= $cl->output . "\n\n"; + } + } elseif (is_string($cl)) { + $c = '--- ' . "\n\n"; + $c .= $cl . "\n\n"; + } + + if (!file_exists($this->log)) { + mkdir($this->log, 0777, true); + } + + + if (is_null($page)) { + $fname = 'commons.log'; + } else { + $fname = 'p' . $page . '.log'; + } + + $file = $this->log . '/' . $fname; + if (!file_exists($file) && file_exists($fname . '.gz')) { + $fopen = 'gzopen'; + $fwrite = 'gzwrite'; + $fclose = 'gzclose'; + $file .= '.gz'; + } else { + $fopen = 'fopen'; + $fwrite = 'fwrite'; + $fclose = 'fclose'; + } + + $pointer = $fopen($file, 'ab'); + $fwrite($pointer, $c); + $fclose($pointer); + } + + public function __destruct() + { + + } + + public static function getDir($id) + { + + if (!is_array(self::$_docsDir)) { + self::$_docsDir = array(); + } + + if (isset(self::$_docsDir[$id])) { + return self::$_docsDir[$id]; + } + + $new = WS_FILES . '/docs/' . $id . '/'; + $old = WS_FILES . '/docs1/' . $id . '/'; + $veryold = WS_FILES . '/docs2/' . $id . '/'; + $vveryold = WS_FILES . '/docs3/' . $id . '/'; + + if (file_exists($new . 'p1.swf')) { + self::$_docsDir[$id] = $new; + return $new; + } elseif (file_exists($old . 'p1.swf')) { + self::$_docsDir[$id] = $old; + return $old; + } else if (file_exists($veryold . 'p1.swf')) { + self::$_docsDir[$id] = $veryold; + return $veryold; + } else if (file_exists($vveryold . 'p1.swf')) { + self::$_docsDir[$id] = $vveryold; + return $vveryold; + } + if (!file_exists($new)) { + mkdir($new, 0777, true); + } + self::$_docsDir[$id] = $new; + return $new; + } } \ No newline at end of file -- 2.39.5