]> _ Git - cubist_pdf.git/commitdiff
wip #6188 @3
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Thu, 7 Sep 2023 14:16:31 +0000 (16:16 +0200)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Thu, 7 Sep 2023 14:16:31 +0000 (16:16 +0200)
.idea/deployment.xml
.idea/misc.xml
resources/tools/fwstk/.idea/deployment.xml
resources/tools/fwstk/.idea/workspace.xml
resources/tools/fwstk/bin/com/fluidbook/fwstk/Main.class
resources/tools/fwstk/out/artifacts/fwstk_jar/fwstk.jar
resources/tools/fwstk/src/com/fluidbook/fwstk/Main.java
src/PDFTools.php

index 922bffea3bc281db1f777f46dfabf8fd1b81f9ac..6b9787362a7ae563e1cedaa78dca08267f841288 100644 (file)
@@ -1,13 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
-  <component name="PublishConfigData" autoUpload="Always" serverName="toolbox.fluidbook.com" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
+  <component name="PublishConfigData" autoUpload="Always" serverName="toolbox.fluidbook.com" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false" showAutoUploadSettingsWarning="false">
     <option name="confirmBeforeUploading" value="false" />
     <serverData>
-      <paths name="ccv-montpellier.fr">
+      <paths name="alphaville.cubedesigners.com">
         <serverdata>
           <mappings>
-            <mapping local="$PROJECT_DIR$" web="/" />
+            <mapping deploy="/app/vendor/cubist/pdf" local="$PROJECT_DIR$" web="/" />
           </mappings>
+          <excludedPaths>
+            <excludedPath local="true" path="$PROJECT_DIR$/vendor" />
+          </excludedPaths>
         </serverdata>
       </paths>
       <paths name="demo1.cubedesigners.com">
           </mappings>
         </serverdata>
       </paths>
+      <paths name="dev.ccgm.fr">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
       <paths name="dev.cubedesigners.fr">
         <serverdata>
           <mappings>
           </mappings>
         </serverdata>
       </paths>
+      <paths name="dev.toolbox.fluidbook.com">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
+      <paths name="dev.toolbox.fluidbook.com (Host)">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
       <paths name="digitaltoolbox.danone.com">
         <serverdata>
           <mappings>
           </mappings>
         </serverdata>
       </paths>
+      <paths name="kingkong.cubedesigners.com">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
       <paths name="m.cubjeans.com">
         <serverdata>
           <mappings>
           </mappings>
         </serverdata>
       </paths>
+      <paths name="songbook.enhydra.fr">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
       <paths name="stats3.fluidbook.com">
         <serverdata>
           <mappings>
           </excludedPaths>
         </serverdata>
       </paths>
+      <paths name="toolbox.fluidbook.com (Host)">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
+      <paths name="vps-e87c4d02.vps.ovh.net">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
       <paths name="workshop.fluidbook.com">
         <serverdata>
           <mappings>
index ad68c4157ee962017343e406db10929127702773..3761743f2ec011700e2045b4e2ce856b27661577 100644 (file)
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="AhkProjectSettings">
     <option name="defaultAhkSdk" value="AutoHotkey" />
index d64fd48c12ccc45ece48569f102bf04f42e30a43..574024daf10ea6e2ba885087091122c745d0160d 100644 (file)
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
-  <component name="PublishConfigData" serverName="workshop.fluidbook.com" confirmBeforeUploading="false">
+  <component name="PublishConfigData" autoUpload="Always" serverName="toolbox.fluidbook.com" confirmBeforeUploading="false" autoUploadExternalChanges="true">
     <option name="confirmBeforeUploading" value="false" />
     <serverData>
-      <paths name="ccv-montpellier.fr">
+      <paths name="alphaville.cubedesigners.com">
         <serverdata>
           <mappings>
             <mapping local="$PROJECT_DIR$" web="/" />
           </mappings>
         </serverdata>
       </paths>
+      <paths name="dev.ccgm.fr">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
       <paths name="dev.cubedesigners.fr">
         <serverdata>
           <mappings>
           </mappings>
         </serverdata>
       </paths>
+      <paths name="dev.toolbox.fluidbook.com">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
+      <paths name="dev.toolbox.fluidbook.com (Host)">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
       <paths name="digitaltoolbox.danone.com">
         <serverdata>
           <mappings>
           </mappings>
         </serverdata>
       </paths>
+      <paths name="kingkong.cubedesigners.com">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
       <paths name="m.cubjeans.com">
         <serverdata>
           <mappings>
           </mappings>
         </serverdata>
       </paths>
+      <paths name="songbook.enhydra.fr">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
       <paths name="stats3.fluidbook.com">
         <serverdata>
           <mappings>
         </serverdata>
       </paths>
       <paths name="toolbox.fluidbook.com">
+        <serverdata>
+          <mappings>
+            <mapping deploy="/vendor/cubist/pdf/resources/tools/fwstk" local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
+      <paths name="toolbox.fluidbook.com (Host)">
+        <serverdata>
+          <mappings>
+            <mapping local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
+      <paths name="vps-e87c4d02.vps.ovh.net">
         <serverdata>
           <mappings>
             <mapping local="$PROJECT_DIR$" web="/" />
         </serverdata>
       </paths>
     </serverData>
+    <option name="myAutoUpload" value="ALWAYS" />
   </component>
 </project>
\ No newline at end of file
index 382d81c58e2fc25081e099ba4929036ce0e683b2..d20d359b4f7ba5a48b0325f6fb2969dec9a901e0 100644 (file)
@@ -5,8 +5,15 @@
       <artifact name="fwstk:jar" />
     </artifacts-to-build>
   </component>
+  <component name="AutoImportSettings">
+    <option name="autoReloadType" value="SELECTIVE" />
+  </component>
   <component name="ChangeListManager">
-    <list default="true" id="f146bc67-2578-4de3-9db2-94d2d43e9e83" name="Default" comment="wip #5410" />
+    <list default="true" id="f146bc67-2578-4de3-9db2-94d2d43e9e83" name="Default" comment="wip #5410">
+      <change beforePath="$PROJECT_DIR$/../../../.idea/deployment.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../../../.idea/deployment.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/../../../.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../../../.idea/misc.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/../../../src/PDFTools.php" beforeDir="false" afterPath="$PROJECT_DIR$/../../../src/PDFTools.php" afterDir="false" />
+    </list>
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
     <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
   <component name="MarkdownSettingsMigration">
     <option name="stateVersion" value="1" />
   </component>
-  <component name="PhpServers">
-    <servers />
-  </component>
+  <component name="PhpWorkspaceProjectConfiguration" interpreter_name="PHP 8.2" />
+  <component name="ProjectColorInfo">{
+  &quot;associatedIndex&quot;: 8
+}</component>
   <component name="ProjectId" id="1S3xbFxrVuUoNhkuaZ10sq6V8Ta" />
   <component name="ProjectLevelVcsManager" settingsEditedManually="true">
     <ConfirmationsSetting value="1" id="Add" />
     &quot;WebServerToolWindowPanel.toolwindow.show.date&quot;: &quot;false&quot;,
     &quot;WebServerToolWindowPanel.toolwindow.show.permissions&quot;: &quot;false&quot;,
     &quot;WebServerToolWindowPanel.toolwindow.show.size&quot;: &quot;false&quot;,
+    &quot;git-widget-placeholder&quot;: &quot;master&quot;,
+    &quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
     &quot;last_opened_file_path&quot;: &quot;D:/Works/cubist_pdf/resources/tools/fwstk&quot;,
+    &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
+    &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
+    &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
+    &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
     &quot;project.structure.last.edited&quot;: &quot;SDKs&quot;,
     &quot;project.structure.proportion&quot;: &quot;0.15&quot;,
     &quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
       <option name="USE_PATTERN" value="false" />
       <method />
     </configuration>
+    <configuration default="true" type="tests" factoryName="Nosetests">
+      <module name="fwstk" />
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+      <option name="_new_regexPattern" value="&quot;&quot;" />
+      <option name="_new_additionalArguments" value="&quot;&quot;" />
+      <option name="_new_target" value="&quot;.&quot;" />
+      <option name="_new_targetType" value="&quot;PATH&quot;" />
+      <method v="2" />
+    </configuration>
+    <configuration default="true" type="tests" factoryName="Unittests">
+      <module name="fwstk" />
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+      <option name="_new_additionalArguments" value="&quot;&quot;" />
+      <option name="_new_target" value="&quot;.&quot;" />
+      <option name="_new_targetType" value="&quot;PATH&quot;" />
+      <method v="2" />
+    </configuration>
+    <configuration default="true" type="tests" factoryName="py.test">
+      <module name="fwstk" />
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+      <option name="_new_keywords" value="&quot;&quot;" />
+      <option name="_new_parameters" value="&quot;&quot;" />
+      <option name="_new_additionalArguments" value="&quot;&quot;" />
+      <option name="_new_target" value="&quot;.&quot;" />
+      <option name="_new_targetType" value="&quot;PATH&quot;" />
+      <method v="2" />
+    </configuration>
     <configuration default="true" type="AndroidRunConfigurationType" factoryName="Android App">
       <option name="DEPLOY" value="true" />
       <option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
       <option name="PM_INSTALL_OPTIONS" value="" />
       <option name="ALL_USERS" value="false" />
       <option name="ALWAYS_INSTALL_WITH_PM" value="false" />
-      <option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
+      <option name="CLEAR_APP_STORAGE" value="false" />
       <option name="ACTIVITY_EXTRA_FLAGS" value="" />
       <option name="MODE" value="default_activity" />
       <option name="CLEAR_LOGCAT" value="false" />
       <option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
       <option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
       <option name="DEBUGGER_TYPE" value="Java" />
-      <Java />
+      <Java>
+        <option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
+        <option name="DEBUG_SANDBOX_SDK" value="false" />
+      </Java>
       <Profilers>
         <option name="ADVANCED_PROFILING_ENABLED" value="false" />
         <option name="STARTUP_PROFILING_ENABLED" value="false" />
       <option name="METHOD_NAME" value="" />
       <option name="CLASS_NAME" value="" />
       <option name="PACKAGE_NAME" value="" />
+      <option name="TEST_NAME_REGEX" value="" />
       <option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
       <option name="EXTRA_OPTIONS" value="" />
-      <option name="INCLUDE_GRADLE_EXTRA_OPTIONS" value="true" />
       <option name="RETENTION_ENABLED" value="No" />
       <option name="RETENTION_MAX_SNAPSHOTS" value="2" />
       <option name="RETENTION_COMPRESS_SNAPSHOTS" value="false" />
       <option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
       <option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
       <option name="DEBUGGER_TYPE" value="Java" />
-      <Java />
+      <Java>
+        <option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
+        <option name="DEBUG_SANDBOX_SDK" value="false" />
+      </Java>
       <Profilers>
         <option name="ADVANCED_PROFILING_ENABLED" value="false" />
         <option name="STARTUP_PROFILING_ENABLED" value="false" />
       <envs />
       <method v="2" />
     </configuration>
-    <configuration default="true" type="tests" factoryName="Nosetests">
-      <module name="fwstk" />
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-      <option name="_new_regexPattern" value="&quot;&quot;" />
-      <option name="_new_additionalArguments" value="&quot;&quot;" />
-      <option name="_new_target" value="&quot;.&quot;" />
-      <option name="_new_targetType" value="&quot;PATH&quot;" />
-      <method v="2" />
-    </configuration>
-    <configuration default="true" type="tests" factoryName="Unittests">
-      <module name="fwstk" />
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-      <option name="_new_additionalArguments" value="&quot;&quot;" />
-      <option name="_new_target" value="&quot;.&quot;" />
-      <option name="_new_targetType" value="&quot;PATH&quot;" />
-      <method v="2" />
-    </configuration>
-    <configuration default="true" type="tests" factoryName="py.test">
-      <module name="fwstk" />
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-      <option name="_new_keywords" value="&quot;&quot;" />
-      <option name="_new_parameters" value="&quot;&quot;" />
-      <option name="_new_additionalArguments" value="&quot;&quot;" />
-      <option name="_new_target" value="&quot;.&quot;" />
-      <option name="_new_targetType" value="&quot;PATH&quot;" />
-      <method v="2" />
-    </configuration>
     <list>
       <item itemvalue="Application.extract links" />
       <item itemvalue="Application.extract texts" />
       <workItem from="1661198842786" duration="416000" />
       <workItem from="1661239352893" duration="103000" />
       <workItem from="1661240120256" duration="206000" />
+      <workItem from="1692895286414" duration="2259000" />
+      <workItem from="1692974658841" duration="8000" />
+      <workItem from="1692974700537" duration="688000" />
+      <workItem from="1694090487471" duration="2806000" />
     </task>
     <task id="LOCAL-00001" summary="wip #1111 @0.5">
       <created>1487172253077</created>
index 04a9a3b12ded488615d868df6dfb2cc703c79bac..15e5d0f251c32460cd8b77f4ef488c482b291d95 100644 (file)
Binary files a/resources/tools/fwstk/bin/com/fluidbook/fwstk/Main.class and b/resources/tools/fwstk/bin/com/fluidbook/fwstk/Main.class differ
index 536d8f5499a8a76f399d15344e303aee32b440de..44662489275bb472ae21626b030eb5addcd5ffa4 100644 (file)
Binary files a/resources/tools/fwstk/out/artifacts/fwstk_jar/fwstk.jar and b/resources/tools/fwstk/out/artifacts/fwstk_jar/fwstk.jar differ
index 4871f86d753ce183e69197dec12d6ea0b09c71f8..b9b5d96a87180e615d13694e08a551ddba5982eb 100644 (file)
@@ -128,7 +128,7 @@ public class Main {
                     imageOutput = args[i].trim();
                 } else if (args[i].trim().compareTo("--ignoreSeparators") == 0) {
                     i++;
-                    ignoredSeparators = args[i].trim();
+                    ignoredSeparators = args[i].trim().replace("{SPACE}"," ");
                 } else if (args[i].trim().compareTo("--linkOffsetX") == 0) {
                     i++;
                     linkOffsetX = Float.parseFloat(args[i].trim());
index e43ee8b094cc30dfd26087d30bdeae6fbf060518..60d98f77a2b78ac1f0c3a640fcfc1659afc031be 100644 (file)
@@ -12,532 +12,563 @@ use DOMNode;
 use DOMXPath;
 use Cubist\PDF\CommandLine\FWSTK;
 
-class PDFTools {
-       /**
-        * @param $path string
-        * @return string
-        */
-
-       public static function resource_path($path) {
-               return __DIR__ . '/../resources/' . self::_cleanPath($path);
-       }
-
-       /**
-        * @param $path string
-        * @return string
-        */
-       public static function tools_path($path, $chmod = false) {
-               $res = self::resource_path('tools/' . self::_cleanPath($path));
-               if ($chmod) {
-                       self::chmodExec($res);
-               }
-               return $res;
-       }
-
-       public static function chmodExec($path) {
-               if (is_file($path)) {
-                       @chmod($path, 0755);
-               }
-       }
-
-
-       public static function getDimensions($pdf) {
-               $infos = self::infos($pdf);
-               $res = ['size' => $infos['infos']['size'], 'max' => [0, 0], 'totalHeight' => 0];
-               foreach ($infos['infos']['page'] as $page) {
-                       $res['max'][0] = max($res['max'][0], $page['size'][0]);
-                       $res['max'][1] = max($res['max'][1], $page['size'][1]);
-                       $res['totalHeight'] += $page['size'][1];
-               }
-               return $res;
-       }
-
-       protected static function parseInfos($data) {
-               $res = [];
-
-               // This function get general infos (pages sizes, boxes, number sections and
-               // bookmarks
-               // Init arrays
-               $res['raw'] = $data;
-               $res['infos'] = [];
-               $res['infos']['size'] = [0, 0];
-               $res['bookmarks'] = [];
-               $res['numberSections'] = '';
-               $bookmark_id = 0;
-
-               $res['size'] = array(0, 0);
-               $lines = explode("\n", $data);
-               foreach ($lines as $line) {
-                       $line = trim(Text::condenseWhite($line));
-                       $e = explode(':', $line, 2);
-                       $k = trim($e[0]);
-                       if (count($e) < 2) {
-                               continue;
-                       }
-                       $v = trim($e[1]);
-                       if ($k == 'Pages' || $k == 'NumberOfPages') {
-                               $res['pages'] = $res['infos']['pages'] = $v;
-                               $res['infos']['page'] = [];
-                               for ($i = 1; $i <= $res['pages']; $i++) {
-                                       $res['infos']['page'][$i] = [];
-                               }
-                       } elseif (preg_match('|Page\s+([0-9]+)\s+(.*)Box:\s+([0-9.]*)\s+([0-9.]*)\s+([0-9.]*)\s+([0-9.]*)|iu', $line, $m)) {
-                               $res['infos']['page'][$m[1]][strtolower($m[2])] = [$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)) {
-                               $res['infos']['page'][$m[1]]['size'] = array($m[2], $m[3]);
-                               if ($m[1] == 1) {
-                                       $res['infos']['size'][0] = $m[2];
-                                       $res['infos']['size'][1] = $m[3];
-                               }
-                       } elseif ($k == 'BookmarkTitle') {
-                               $res['bookmarks'][$bookmark_id] = array('titre' => str_replace('&#13;', '', trim($v)));
-                       } elseif ($k == 'BookmarkLevel') {
-                               $res['bookmarks'][$bookmark_id]['level'] = $v;
-                       } elseif ($k == 'BookmarkPage') {
-                               $res['bookmarks'][$bookmark_id]['page'] = $v;
-                               $bookmark_id++;
-                       } elseif ($k == 'NumberSections') {
-                               $res['numberSections'] = $v;
-                               $res['infos']['pagenumbers'] = $v;
-                       }
-               }
-               return $res;
-       }
-
-       /**
-        * @throws \Exception
-        */
-       public static function infos($pdf) {
-               if (!file_exists($pdf)) {
-                       throw new \Exception('Unable to parse infos of ' . $pdf . ' : file not found');
-               }
-               $fwstk = new FWSTK();
-               $fwstk->setArg('--input ' . $pdf);
-               $fwstk->setArg('--infos');
-               $fwstk->execute();
-               $out = $fwstk->getOutput();
-
-               $pdfinfo = new CommandLine('pdfinfo');
-               $pdfinfo->setArg('-box');
-               $pdfinfo->setArg('f', 1);
-               $pdfinfo->setArg('l', 100000);
-               $pdfinfo->setArg(null, $pdf);
-               $pdfinfo->execute();
-               $out .= "\n";
-               $out .= $pdfinfo->getOutput();
-
-               return self::parseInfos($out);
-       }
-
-       /**
-        * @param $path string
-        * @return string
-        */
-       protected static function _cleanPath($path) {
-               return trim($path, '/');
-       }
-
-       public static function makeMiniShot($in, $out, $page, $format = 'jpg', $quality = 85) {
-               self::makeShotFixedWidth($in, $out, $page, 'p', 500, max(0, min(100, $quality - 15)), 4, 'PNM', $format);
-       }
-
-       public static function makeShotFixedWidth($in, $out, $page, $prefix = '', $w = 100, $quality = 90, $antialiasing = 4, $method = 'PNM', $format = 'jpg') {
-               // Make thumbs of $w width
-               self::makeShot($in, $out, $page, $prefix, null, $quality, $antialiasing, $method, $w, -1, $format);
-       }
-
-       public static function makeShotFixedHeight($in, $out, $page, $prefix = '', $h = '', $quality = 90, $antialiasing = 4, $method = 'PNM', $format = 'jpg') {
-               // Make thumbs of $h height
-               self::makeShot($in, $out, $page, $prefix, null, $quality, $antialiasing, $method, -1, $h, $format);
-       }
-
-       public static function makeSWF($in, $out, $page, $resolution = 100, $quality = 90) {
-               if (file_exists($out)) {
-                       unlink($out);
-               }
-               $pdf2swf = new CommandLine('pdf2swf', null, true);
-               $pdf2swf->setArg('p', $page);
-               $pdf2swf->setArg('T', 10);
-               $pdf2swf->setArg('Q', 30);
-               $pdf2swf->setArg('set reordertags', '0');
-               $pdf2swf->setArg('fonts');
-               $pdf2swf->setArg('set storeallcharacters');
-               $pdf2swf->setArg('set subpixels', $resolution / 72);
-               $pdf2swf->setArg('set jpegquality', $quality);
-               $pdf2swf->setArg('set disablelinks');
-               $pdf2swf->setArg('set dots');
-               $pdf2swf->setArg(null, $in);
-               $pdf2swf->setArg('output', $out);
-               $pdf2swf->execute();
-               $pdf2swf->debug();
-
-               if (file_exists($out)) {
-                       return;
-               }
-               $pdf2swf = new CommandLine('pdf2swf', null, true);
-               $pdf2swf->setArg('p', $page);
-               $pdf2swf->setArg('T', 10);
-               $pdf2swf->setArg('Q', 120);
-               $pdf2swf->setArg('set poly2bitmap');
-               $pdf2swf->setArg('set storeallcharacters');
-               $pdf2swf->setArg('set reordertags', '0');
-               $pdf2swf->setArg('fonts');
-               $pdf2swf->setArg('set subpixels', $resolution / 72);
-               $pdf2swf->setArg('set jpegquality', $quality);
-               $pdf2swf->setArg('set disablelinks');
-               $pdf2swf->setArg('set dots');
-               $pdf2swf->setArg(null, $in);
-               $pdf2swf->setArg('output', $out);
-               $pdf2swf->execute();
-               $pdf2swf->debug();
-               if (file_exists($out)) {
-                       return;
-               }
-               $pdf2swf = new CommandLine('pdf2swf', null, true);
-               $pdf2swf->setArg('p', $page);
-               $pdf2swf->setArg('T', 10);
-               $pdf2swf->setArg('set reordertags', '0');
-               $pdf2swf->setArg('fonts');
-               $pdf2swf->setArg('set bitmap');
-               $pdf2swf->setArg('set storeallcharacters');
-               $pdf2swf->setArg('set subpixels', $resolution / 72);
-               $pdf2swf->setArg('set jpegquality', $quality);
-               $pdf2swf->setArg('set disablelinks');
-               $pdf2swf->setArg('set dots');
-               $pdf2swf->setArg(null, $in);
-               $pdf2swf->setArg('output', $out);
-               $pdf2swf->execute();
-               $pdf2swf->debug();
-       }
-
-
-       public static function makeBaseSVGFile($in, $out, $page) {
-               $pdftocairo = new CommandLine('pdftocairo');
-               $pdftocairo->setArg('f', $page);
-               $pdftocairo->setArg('l', $page);
-               $pdftocairo->setArg('r', 300);
-               $pdftocairo->setArg(null, '-expand');
-               $pdftocairo->setArg(null, '-svg');
-               $pdftocairo->setArg(null, $in);
-               $pdftocairo->setArg(null, $out);
-               $pdftocairo->execute();
-       }
-
-       public static function makeTextSVGFile($in, $out) {
-               $svg = new DOMDocument();
-               $svg->preserveWhiteSpace = false;
-               $svg->load($in, 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");
-               $toDelete = [
-                       '//svg:defs/svg:g[starts-with(@id, "surface")]//svg:path',
-                       '//svg:defs/svg:g[starts-with(@id, "surface")]//svg:rect',
-                       '//svg:defs/svg:g[starts-with(@id, "surface")]//svg:use[starts-with(@xlink:href, "#image")]',
-                       '/svg:svg/svg:g[@id="surface1"]//svg:path',
-                       '/svg:svg/svg:g[@id="surface1"]//svg:rect',
-                       '/svg:svg/svg:g[@id="surface1"]//svg:filter',
-                       '/svg:svg/svg:g[@id="surface1"]//svg:use[starts-with(@xlink:href, "#image")]',
-                       '//svg:svg/svg:g[@id="surface1"]//svg:use[starts-with(@xlink:href, "#image")]',
-               ];
-               $toDeleteIfOrphan = [
-                       '//svg:image',
-               ];
-
-               foreach ($toDelete as $q) {
-                       $list = $xpath->query($q);
-                       if (count($list)) {
-                               foreach ($list as $node) {
-                                       /* @var $node DOMNode */
-                                       $parent = $node->parentNode;
-                                       $parent->removeChild($node);
-                               }
-                       }
-               }
-
-               foreach ($toDeleteIfOrphan as $q) {
-                       $list = $xpath->query($q);
-                       if (count($list)) {
-                               foreach ($list as $node) {
-                                       /* @var $node DOMElement */
-                                       $id = $node->getAttribute('id');
-                                       if ($xpath->query('//*[@id="' . $id . '"]')->count() > 0) {
-                                               $parent = $node->parentNode;
-                                               $parent->removeChild($node);
-                                       }
-                               }
-                       }
-               }
-               $res = $svg->saveXML();
-               $res = preg_replace('/<g clip-path="url\(#clip\d+\)" clip-rule="nonzero"\/>/', '', $res);
-               while (true) {
-                       $res = preg_replace('/<g clip-path="url\(#clip\d+\)" clip-rule="nonzero"><\/g>/', '', $res, -1, $count);
-                       if (!$count) {
-                               break;
-                       }
-               }
-
-               file_put_contents($out, $res);
-       }
-
-       public static function makeShot($in, $out, $page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $method = 'PNM', $width = null, $height = null, $format = 'jpg') {
-               $error = false;
-               if ($method === 'GS') {
-                       self::makeShotGS($in, $out, $page, $prefix, $resolution, $quality, $antialiasing, $width, $height, $format);
-               } elseif ($method === 'PNM') {
-                       self::makeShotPNM($in, $out, $page, $prefix, $resolution, $quality, $antialiasing, true, $width, $height, $format);
-               }
-               // Test the result by checking all files
-               if (!file_exists($out)) {
-                       $error = true;
-               }
-               // If error, we try to make thumbs with other method
-               if ($error) {
-                       if ($method === 'GS') {
-                               self::makeShotPNM($in, $out, $page, $prefix, $resolution, $quality, $antialiasing, true, $width, $height, $format);
-                       } elseif ($method === 'PNM') {
-                               self::makeShotGS($in, $out, $page, $prefix, $resolution, $quality, $antialiasing, $width, $height, $format);
-                       }
-               }
-       }
-
-       protected static function makeShotGS($in, $out, $page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $width = null, $height = null, $format = 'jpg') {
-               // Fabrication des thumbnails avec ghostscript
-               $gs = new CommandLine('gs', null, true);
-               $gs->setArg('-dBATCH');
-               $gs->setArg('-dNOPAUSE');
-               $gs->setArg('-dNOPROMPT');
-               // Antialias
-               $gs->setArg('-dDOINTERPOLATE');
-               $gs->setArg('-dTextAlphaBits=' . $antialiasing);
-               $gs->setArg('-dGraphicsAlphaBits=' . $antialiasing);
-               // Device
-               $device = $format === 'jpg' ? 'jpeg' : 'png16m';
-               $gs->setArg('-sDEVICE=' . $device);
-               // Dispotion & colors
-               // $gs->setArg('-dUseCIEColor');
-               $gs->setArg('-dAutoRotatePages=/None');
-               $gs->setArg('-dUseCropBox');
-               // Resolution & Quality
-               $gs->setArg('-r' . round($resolution));
-               if ($format === 'jpg') {
-                       $gs->setArg('-dJPEGQ=' . $quality);
-               }
-               // Performances
-               $gs->setArg('-dNumRenderingThreads=4');
-               // Page range
-               $gs->setArg('-dFirstPage=' . $page);
-               $gs->setArg('-dLastPage=' . $page);
-               // Files
-               $gs->setArg('-sOutputFile=' . $out);
-
-               $gs->setArg(null, $in);
-               $gs->execute();
-       }
-
-       public static function makeShotPNM($in, $out, $page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $texts = true, $width = null, $height = null, $format = 'jpg') {
-               $tmp = Files::tempnam();
-
-               $antialiasing = $antialiasing ? 'yes' : 'no';
-               $freetype = $texts ? 'yes' : 'no';
-               // Exporte les fichiers
-               $pdftoppm = new CommandLine('pdftoppm', null, true);
-               $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);
-               if (null !== $resolution) {
-                       $pdftoppm->setArg('r', $resolution);
-               }
-               if (null !== $width) {
-                       $pdftoppm->setArg('-scale-to-x ' . $width);
-               }
-               if (null !== $height) {
-                       $pdftoppm->setArg('-scale-to-y ' . $height);
-               }
-               $pdftoppm->setArg(null, $in);
-               $pdftoppm->setArg(null, $tmp);
-               $pdftoppm->execute();
-               $tmp .= '.ppm';
-
-
-               if (file_exists($tmp)) {
-                       if ($format === 'jpg') {
-                               $cjpeg = new CommandLine('cjpeg', null, true);
-                               $cjpeg->setArg('-quality ' . ($quality + 6));
-                               $cjpeg->setArg('-outfile ' . $out);
-                               $cjpeg->setArg(null, $tmp);
-                               $cjpeg->execute();
-                       } else if ($format === 'png') {
-                               $pnmtopng = new CommandLine('pnmtopng', $out, false);
-                               $pnmtopng->setArg('-background white');
-                               $pnmtopng->setArg(null, $tmp);
-                               $pnmtopng->execute();
-                       }
-                       unlink($tmp);
-               } else {
-                       $pdftoppm->debug();
-               }
-       }
-
-       public static function getThumbFromPDF($pdf, $page, $format = 'jpg') {
-               if (!file_exists($pdf)) {
-                       return false;
-               }
-               $dir = WS_CACHE . '/thumbs/' . sha1($pdf) . '/';
-               if (!file_exists($dir)) {
-                       mkdir($dir, 0777, true);
-               }
-               $image = $dir . '/p' . $page . '.' . $format;
-               $mtime = filemtime($image);
-
-               if (!file_exists($image) || $mtime < filemtime(__FILE__) || $mtime < filemtime($pdf)) {
-                       self::makeMiniShot($pdf, $image, $page, $format);
-               }
-
-               return $image;
-       }
-
-       public static function extractLinks($pdf, $out) {
-               $out .= 'links/';
-               Files::mkdir($out);
-
-               if (file_exists($out . '/p1.csv')) {
-                       return;
-               }
-               $fwstk = new FWSTK();
-               $fwstk->setArg('--input ' . $pdf);
-               $fwstk->setArg('--extractLinks ' . $out . 'p%d.csv');
-               $fwstk->setArg('--threads 1');
-               $fwstk->execute();
-       }
-
-       public static function extractTexts($pdf, $out, $textExtraction = 'fluidbook', $ignoreSeparators = '') {
-               $out .= 'texts';
-               if ($ignoreSeparators) {
-                       $out .= '/sep_' . md5($ignoreSeparators);
-               }
-               $out = Files::mkdir($out);
-
-               $fwstk = new FWSTK();
-               $fwstk->setArg('--input ' . $pdf);
-               $fwstk->setArg('--extractTexts ' . $out . '%s%d.txt');
-               $fwstk->setArg('--extractTextsMethod ' . $textExtraction);
-               $fwstk->setArg('--threads 1');
-               if ($ignoreSeparators) {
-                       $fwstk->setArg('--ignoreSeparators ' . $ignoreSeparators);
-               }
-               $fwstk->execute();
-       }
-
-
-       public static function extractHighlightsData($pdf, $out) {
-               $out .= 'texts/';
-               Files::mkdir($out);
-
-               $fwstk = new FWSTK();
-               $fwstk->setArg('--input ' . $pdf);
-               $fwstk->setArg('--layout ' . $out . 'p%d.fby');
-               $fwstk->setArg('--cmaps ' . $out);
-               $fwstk->setArg('--fonts' . $out . 'fonts/web/');
-               $fwstk->execute();
-       }
-
-       public static function fixPDF($in, $out) {
-               if (file_exists($out)) {
-                       unlink($out);
-               }
-
-               $pdftk = new CommandLine('pdftk');
-               $pdftk->setArg(null, $in);
-               $pdftk->setArg(null, 'output');
-               $pdftk->setArg(null, $out);
-               $pdftk->execute();
-
-               if (!file_exists($out)) {
-                       $pdftocairo = new CommandLine('pdftocairo');
-                       $pdftocairo->setPath(CONVERTER_PATH);
-                       $pdftocairo->setArg(null, '-pdf');
-                       $pdftocairo->setArg(null, $in);
-                       $pdftocairo->setArg(null, $out);
-                       $pdftocairo->execute();
-               }
-       }
-
-       public static function split($pdf, $out) {
-
-               $lock = $pdf . '.split.lock';
-
-               $returnAfterSleep = false;
-
-               usleep(rand(100000, 2000000));
-
-               while (file_exists($lock)) {
-                       if (filemtime($lock) < time() - 300) {
-                               unlink($lock);
-                       }
-                       $returnAfterSleep = true;
-                       sleep(5);
-               }
-               if ($returnAfterSleep) {
-                       return;
-               }
-
-               touch($lock);
-
-               try {
-                       Files::mkdir($out);
-                       $pdftk = new CommandLine('pdftk');
-                       $pdftk->setArg(null, $pdf);
-                       $pdftk->setArg(null, 'burst');
-                       $pdftk->setArg(null, 'uncompress');
-                       $pdftk->setArg(null, 'output');
-                       $pdftk->setArg(null, $out . '/p%d.pdf');
-                       $pdftk->execute();
-
-
-                       for ($i = 1; true; $i++) {
-                               // Remove annotations : https://gist.github.com/stefanschmidt/5248592
-                               $file = sprintf($out . '/p%d.pdf', $i);
-                               if (!file_exists($file)) {
-                                       break;
-                               }
-                               $to = sprintf($out . '/s%d.pdf', $i);
-                               `LANG=C LC_CTYPE=C sed -n '/^\/Annots/!p' $file > $to`;
-                               if (file_exists($to)) {
-                                       if (filesize($to) > 0) {
-                                               unlink($file);
-                                               rename($to, $file);
-                                       } else {
-                                               unlink($to);
-                                       }
-                               }
-                       }
-               } catch (\Exception $e) {
-
-               }
-               unlink($lock);
-       }
-
-       public static function compressPDF($source, $dest, $resolution = 72) {
-               $gs = new CommandLine('gs');
-               $gs->setArg('-dBATCH');
-               $gs->setArg('-dNOPAUSE');
-               $gs->setArg('-dNOPROMPT');
-               $gs->setArg('-sOutputFile=' . $dest);
-               $gs->setArg('-sDEVICE=pdfwrite');
-               $gs->setArg('-dPDFSETTINGS=/ebook');
-               $gs->setArg('-dColorImageResolution=' . $resolution);
-               $gs->setArg('-dAutoRotatePages=/None');
-               $gs->setArg('-dColorConversionStrategy=/LeaveColorUnchanged');
-               $gs->setArg(null, $source);
-               $gs->execute();
-       }
+class PDFTools
+{
+    /**
+     * @param $path string
+     * @return string
+     */
+
+    public static function resource_path($path)
+    {
+        return __DIR__ . '/../resources/' . self::_cleanPath($path);
+    }
+
+    /**
+     * @param $path string
+     * @return string
+     */
+    public static function tools_path($path, $chmod = false)
+    {
+        $res = self::resource_path('tools/' . self::_cleanPath($path));
+        if ($chmod) {
+            self::chmodExec($res);
+        }
+        return $res;
+    }
+
+    public static function chmodExec($path)
+    {
+        if (is_file($path)) {
+            @chmod($path, 0755);
+        }
+    }
+
+
+    public static function getDimensions($pdf)
+    {
+        $infos = self::infos($pdf);
+        $res = ['size' => $infos['infos']['size'], 'max' => [0, 0], 'totalHeight' => 0];
+        foreach ($infos['infos']['page'] as $page) {
+            $res['max'][0] = max($res['max'][0], $page['size'][0]);
+            $res['max'][1] = max($res['max'][1], $page['size'][1]);
+            $res['totalHeight'] += $page['size'][1];
+        }
+        return $res;
+    }
+
+    protected static function parseInfos($data)
+    {
+        $res = [];
+
+        // This function get general infos (pages sizes, boxes, number sections and
+        // bookmarks
+        // Init arrays
+        $res['raw'] = $data;
+        $res['infos'] = [];
+        $res['infos']['size'] = [0, 0];
+        $res['bookmarks'] = [];
+        $res['numberSections'] = '';
+        $bookmark_id = 0;
+
+        $res['size'] = array(0, 0);
+        $lines = explode("\n", $data);
+        foreach ($lines as $line) {
+            $line = trim(Text::condenseWhite($line));
+            $e = explode(':', $line, 2);
+            $k = trim($e[0]);
+            if (count($e) < 2) {
+                continue;
+            }
+            $v = trim($e[1]);
+            if ($k == 'Pages' || $k == 'NumberOfPages') {
+                $res['pages'] = $res['infos']['pages'] = $v;
+                $res['infos']['page'] = [];
+                for ($i = 1; $i <= $res['pages']; $i++) {
+                    $res['infos']['page'][$i] = [];
+                }
+            } elseif (preg_match('|Page\s+([0-9]+)\s+(.*)Box:\s+([0-9.]*)\s+([0-9.]*)\s+([0-9.]*)\s+([0-9.]*)|iu', $line, $m)) {
+                $res['infos']['page'][$m[1]][strtolower($m[2])] = [$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)) {
+                $res['infos']['page'][$m[1]]['size'] = array($m[2], $m[3]);
+                if ($m[1] == 1) {
+                    $res['infos']['size'][0] = $m[2];
+                    $res['infos']['size'][1] = $m[3];
+                }
+            } elseif ($k == 'BookmarkTitle') {
+                $res['bookmarks'][$bookmark_id] = array('titre' => str_replace('&#13;', '', trim($v)));
+            } elseif ($k == 'BookmarkLevel') {
+                $res['bookmarks'][$bookmark_id]['level'] = $v;
+            } elseif ($k == 'BookmarkPage') {
+                $res['bookmarks'][$bookmark_id]['page'] = $v;
+                $bookmark_id++;
+            } elseif ($k == 'NumberSections') {
+                $res['numberSections'] = $v;
+                $res['infos']['pagenumbers'] = $v;
+            }
+        }
+        return $res;
+    }
+
+    /**
+     * @throws \Exception
+     */
+    public static function infos($pdf)
+    {
+        if (!file_exists($pdf)) {
+            throw new \Exception('Unable to parse infos of ' . $pdf . ' : file not found');
+        }
+        $fwstk = new FWSTK();
+        $fwstk->setArg('--input ' . $pdf);
+        $fwstk->setArg('--infos');
+        $fwstk->execute();
+        $out = $fwstk->getOutput();
+
+        $pdfinfo = new CommandLine('pdfinfo');
+        $pdfinfo->setArg('-box');
+        $pdfinfo->setArg('f', 1);
+        $pdfinfo->setArg('l', 100000);
+        $pdfinfo->setArg(null, $pdf);
+        $pdfinfo->execute();
+        $out .= "\n";
+        $out .= $pdfinfo->getOutput();
+
+        return self::parseInfos($out);
+    }
+
+    /**
+     * @param $path string
+     * @return string
+     */
+    protected static function _cleanPath($path)
+    {
+        return trim($path, '/');
+    }
+
+    public static function makeMiniShot($in, $out, $page, $format = 'jpg', $quality = 85)
+    {
+        self::makeShotFixedWidth($in, $out, $page, 'p', 500, max(0, min(100, $quality - 15)), 4, 'PNM', $format);
+    }
+
+    public static function makeShotFixedWidth($in, $out, $page, $prefix = '', $w = 100, $quality = 90, $antialiasing = 4, $method = 'PNM', $format = 'jpg')
+    {
+        // Make thumbs of $w width
+        self::makeShot($in, $out, $page, $prefix, null, $quality, $antialiasing, $method, $w, -1, $format);
+    }
+
+    public static function makeShotFixedHeight($in, $out, $page, $prefix = '', $h = '', $quality = 90, $antialiasing = 4, $method = 'PNM', $format = 'jpg')
+    {
+        // Make thumbs of $h height
+        self::makeShot($in, $out, $page, $prefix, null, $quality, $antialiasing, $method, -1, $h, $format);
+    }
+
+    public static function makeSWF($in, $out, $page, $resolution = 100, $quality = 90)
+    {
+        if (file_exists($out)) {
+            unlink($out);
+        }
+        $pdf2swf = new CommandLine('pdf2swf', null, true);
+        $pdf2swf->setArg('p', $page);
+        $pdf2swf->setArg('T', 10);
+        $pdf2swf->setArg('Q', 30);
+        $pdf2swf->setArg('set reordertags', '0');
+        $pdf2swf->setArg('fonts');
+        $pdf2swf->setArg('set storeallcharacters');
+        $pdf2swf->setArg('set subpixels', $resolution / 72);
+        $pdf2swf->setArg('set jpegquality', $quality);
+        $pdf2swf->setArg('set disablelinks');
+        $pdf2swf->setArg('set dots');
+        $pdf2swf->setArg(null, $in);
+        $pdf2swf->setArg('output', $out);
+        $pdf2swf->execute();
+        $pdf2swf->debug();
+
+        if (file_exists($out)) {
+            return;
+        }
+        $pdf2swf = new CommandLine('pdf2swf', null, true);
+        $pdf2swf->setArg('p', $page);
+        $pdf2swf->setArg('T', 10);
+        $pdf2swf->setArg('Q', 120);
+        $pdf2swf->setArg('set poly2bitmap');
+        $pdf2swf->setArg('set storeallcharacters');
+        $pdf2swf->setArg('set reordertags', '0');
+        $pdf2swf->setArg('fonts');
+        $pdf2swf->setArg('set subpixels', $resolution / 72);
+        $pdf2swf->setArg('set jpegquality', $quality);
+        $pdf2swf->setArg('set disablelinks');
+        $pdf2swf->setArg('set dots');
+        $pdf2swf->setArg(null, $in);
+        $pdf2swf->setArg('output', $out);
+        $pdf2swf->execute();
+        $pdf2swf->debug();
+        if (file_exists($out)) {
+            return;
+        }
+        $pdf2swf = new CommandLine('pdf2swf', null, true);
+        $pdf2swf->setArg('p', $page);
+        $pdf2swf->setArg('T', 10);
+        $pdf2swf->setArg('set reordertags', '0');
+        $pdf2swf->setArg('fonts');
+        $pdf2swf->setArg('set bitmap');
+        $pdf2swf->setArg('set storeallcharacters');
+        $pdf2swf->setArg('set subpixels', $resolution / 72);
+        $pdf2swf->setArg('set jpegquality', $quality);
+        $pdf2swf->setArg('set disablelinks');
+        $pdf2swf->setArg('set dots');
+        $pdf2swf->setArg(null, $in);
+        $pdf2swf->setArg('output', $out);
+        $pdf2swf->execute();
+        $pdf2swf->debug();
+    }
+
+
+    public static function makeBaseSVGFile($in, $out, $page)
+    {
+        $pdftocairo = new CommandLine('pdftocairo');
+        $pdftocairo->setArg('f', $page);
+        $pdftocairo->setArg('l', $page);
+        $pdftocairo->setArg('r', 300);
+        $pdftocairo->setArg(null, '-expand');
+        $pdftocairo->setArg(null, '-svg');
+        $pdftocairo->setArg(null, $in);
+        $pdftocairo->setArg(null, $out);
+        $pdftocairo->execute();
+    }
+
+    public static function makeTextSVGFile($in, $out)
+    {
+        set_time_limit(0);
+
+        $svg = new DOMDocument();
+        $svg->preserveWhiteSpace = false;
+        $svg->load($in, 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");
+        $toDelete = [
+            '//svg:defs/svg:g[starts-with(@id, "surface")]//svg:path',
+            '//svg:defs/svg:g[starts-with(@id, "surface")]//svg:rect',
+            '//svg:defs/svg:g[starts-with(@id, "surface")]//svg:use[starts-with(@xlink:href, "#image")]',
+            '/svg:svg/svg:g[@id="surface1"]//svg:path',
+            '/svg:svg/svg:g[@id="surface1"]//svg:rect',
+            '/svg:svg/svg:g[@id="surface1"]//svg:filter',
+            '/svg:svg/svg:g[@id="surface1"]//svg:use[starts-with(@xlink:href, "#image")]',
+            '//svg:svg/svg:g[@id="surface1"]//svg:use[starts-with(@xlink:href, "#image")]',
+        ];
+        $toDeleteIfOrphan = [
+            '//svg:image',
+        ];
+
+        foreach ($toDelete as $q) {
+            $list = $xpath->query($q);
+            if (count($list)) {
+                foreach ($list as $node) {
+                    /* @var $node DOMNode */
+                    $parent = $node->parentNode;
+                    $parent->removeChild($node);
+                }
+            }
+        }
+
+        foreach ($toDeleteIfOrphan as $q) {
+            $list = $xpath->query($q);
+            if (count($list)) {
+                foreach ($list as $node) {
+                    /* @var $node DOMElement */
+                    $id = $node->getAttribute('id');
+                    if ($xpath->query('//*[@id="' . $id . '"]')->count() > 0) {
+                        $parent = $node->parentNode;
+                        $parent->removeChild($node);
+                    }
+                }
+            }
+        }
+        $res = $svg->saveXML();
+        $res = preg_replace('/<g clip-path="url\(#clip\d+\)" clip-rule="nonzero"\/>/', '', $res);
+        while (true) {
+            $res = preg_replace('/<g clip-path="url\(#clip\d+\)" clip-rule="nonzero"><\/g>/', '', $res, -1, $count);
+            if (!$count) {
+                break;
+            }
+        }
+
+        file_put_contents($out, $res);
+    }
+
+    public static function makeShot($in, $out, $page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $method = 'PNM', $width = null, $height = null, $format = 'jpg')
+    {
+        $error = false;
+        if ($method === 'GS') {
+            self::makeShotGS($in, $out, $page, $prefix, $resolution, $quality, $antialiasing, $width, $height, $format);
+        } elseif ($method === 'PNM') {
+            self::makeShotPNM($in, $out, $page, $prefix, $resolution, $quality, $antialiasing, true, $width, $height, $format);
+        }
+        // Test the result by checking all files
+        if (!file_exists($out)) {
+            $error = true;
+        }
+        // If error, we try to make thumbs with other method
+        if ($error) {
+            if ($method === 'GS') {
+                self::makeShotPNM($in, $out, $page, $prefix, $resolution, $quality, $antialiasing, true, $width, $height, $format);
+            } elseif ($method === 'PNM') {
+                self::makeShotGS($in, $out, $page, $prefix, $resolution, $quality, $antialiasing, $width, $height, $format);
+            }
+        }
+    }
+
+    protected static function makeShotGS($in, $out, $page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $width = null, $height = null, $format = 'jpg')
+    {
+        // Fabrication des thumbnails avec ghostscript
+        $gs = new CommandLine('gs', null, true);
+        $gs->setArg('-dBATCH');
+        $gs->setArg('-dNOPAUSE');
+        $gs->setArg('-dNOPROMPT');
+        // Antialias
+        $gs->setArg('-dDOINTERPOLATE');
+        $gs->setArg('-dTextAlphaBits=' . $antialiasing);
+        $gs->setArg('-dGraphicsAlphaBits=' . $antialiasing);
+        // Device
+        $device = $format === 'jpg' ? 'jpeg' : 'png16m';
+        $gs->setArg('-sDEVICE=' . $device);
+        // Dispotion & colors
+        // $gs->setArg('-dUseCIEColor');
+        $gs->setArg('-dAutoRotatePages=/None');
+        $gs->setArg('-dUseCropBox');
+        // Resolution & Quality
+        $gs->setArg('-r' . round($resolution));
+        if ($format === 'jpg') {
+            $gs->setArg('-dJPEGQ=' . $quality);
+        }
+        // Performances
+        $gs->setArg('-dNumRenderingThreads=4');
+        // Page range
+        $gs->setArg('-dFirstPage=' . $page);
+        $gs->setArg('-dLastPage=' . $page);
+        // Files
+        $gs->setArg('-sOutputFile=' . $out);
+
+        $gs->setArg(null, $in);
+        $gs->execute();
+    }
+
+    public static function makeShotPNM($in, $out, $page, $prefix = '', $resolution = 72, $quality = 90, $antialiasing = 4, $texts = true, $width = null, $height = null, $format = 'jpg')
+    {
+        $tmp = Files::tempnam();
+
+        $antialiasing = $antialiasing ? 'yes' : 'no';
+        $freetype = $texts ? 'yes' : 'no';
+        // Exporte les fichiers
+        $pdftoppm = new CommandLine('pdftoppm', null, true);
+        $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);
+        if (null !== $resolution) {
+            $pdftoppm->setArg('r', $resolution);
+        }
+        if (null !== $width) {
+            $pdftoppm->setArg('-scale-to-x ' . $width);
+        }
+        if (null !== $height) {
+            $pdftoppm->setArg('-scale-to-y ' . $height);
+        }
+        $pdftoppm->setArg(null, $in);
+        $pdftoppm->setArg(null, $tmp);
+        $pdftoppm->execute();
+        $tmp .= '.ppm';
+
+
+        if (file_exists($tmp)) {
+            if ($format === 'jpg') {
+                $cjpeg = new CommandLine('cjpeg', null, true);
+                $cjpeg->setArg('-quality ' . ($quality + 6));
+                $cjpeg->setArg('-outfile ' . $out);
+                $cjpeg->setArg(null, $tmp);
+                $cjpeg->execute();
+            } else if ($format === 'png') {
+                $pnmtopng = new CommandLine('pnmtopng', $out, false);
+                $pnmtopng->setArg('-background white');
+                $pnmtopng->setArg(null, $tmp);
+                $pnmtopng->execute();
+            }
+            unlink($tmp);
+        } else {
+            $pdftoppm->debug();
+        }
+    }
+
+    public static function getThumbFromPDF($pdf, $page, $format = 'jpg')
+    {
+        if (!file_exists($pdf)) {
+            return false;
+        }
+        $dir = WS_CACHE . '/thumbs/' . sha1($pdf) . '/';
+        if (!file_exists($dir)) {
+            mkdir($dir, 0777, true);
+        }
+        $image = $dir . '/p' . $page . '.' . $format;
+        $mtime = filemtime($image);
+
+        if (!file_exists($image) || $mtime < filemtime(__FILE__) || $mtime < filemtime($pdf)) {
+            self::makeMiniShot($pdf, $image, $page, $format);
+        }
+
+        return $image;
+    }
+
+    public static function extractLinks($pdf, $out)
+    {
+        $out .= 'links/';
+        Files::mkdir($out);
+
+        if (file_exists($out . '/p1.csv')) {
+            return;
+        }
+        $fwstk = new FWSTK();
+        $fwstk->setArg('--input ' . $pdf);
+        $fwstk->setArg('--extractLinks ' . $out . 'p%d.csv');
+        $fwstk->setArg('--threads 1');
+        $fwstk->execute();
+    }
+
+    public static function extractTexts($pdf, $out, $mode = 'standard', $textExtraction = 'fluidbook', $ignoreSeparators = '')
+    {
+        $out .= 'texts';
+        if ($ignoreSeparators) {
+            $out .= '/sep_' . md5($ignoreSeparators);
+        }
+        $out = Files::mkdir($out);
+
+        $fwstk = new FWSTK();
+        $fwstk->setArg('--input ' . $pdf);
+        $fwstk->setArg('--extractTexts ' . $out . '%s%d.txt');
+        $fwstk->setArg('--extractTextsMethod ' . $textExtraction);
+        $fwstk->setArg('--threads 1');
+        if ($ignoreSeparators) {
+            $fwstk->setArg('--ignoreSeparators "' . $ignoreSeparators . '"');
+        }
+        $fwstk->execute();
+    }
+
+
+    public static function extractHighlightsData($pdf, $out, $mode = 'standard')
+    {
+        $out .= 'texts/';
+        Files::mkdir($out);
+
+        $fwstk = new FWSTK();
+        $fwstk->setArg('--input ' . $pdf);
+        $fwstk->setArg('--layout ' . $out . 'p%d.fby');
+        $fwstk->setArg('--cmaps ' . $out);
+        $fwstk->setArg('--fonts' . $out . 'fonts/web/');
+        $fwstk->execute();
+    }
+
+    public static function fixPDF($in, $out)
+    {
+        if (file_exists($out)) {
+            unlink($out);
+        }
+
+        $pdftk = new CommandLine('pdftk');
+        $pdftk->setArg(null, $in);
+        $pdftk->setArg(null, 'output');
+        $pdftk->setArg(null, $out);
+        $pdftk->execute();
+
+        if (!file_exists($out)) {
+            $pdftocairo = new CommandLine('pdftocairo');
+            $pdftocairo->setPath(CONVERTER_PATH);
+            $pdftocairo->setArg(null, '-pdf');
+            $pdftocairo->setArg(null, $in);
+            $pdftocairo->setArg(null, $out);
+            $pdftocairo->execute();
+        }
+    }
+
+    public static function split($pdf, $out)
+    {
+
+        if (!$pdf) {
+            throw new \Exception('Try to split empty pdf (out : ' . $out . ')');
+        }
+
+        $lock = $pdf . '.split.lock';
+
+
+        $returnAfterSleep = false;
+
+        usleep(rand(100000, 2000000));
+
+        while (file_exists($lock)) {
+            if (filemtime($lock) < time() - 300) {
+                unlink($lock);
+            }
+            $returnAfterSleep = true;
+            sleep(5);
+        }
+        if ($returnAfterSleep) {
+            return;
+        }
+
+        @touch($lock);
+
+        try {
+            Files::mkdir($out);
+            $pdftk = new CommandLine('pdftk');
+            $pdftk->setArg(null, $pdf);
+            $pdftk->setArg(null, 'burst');
+            $pdftk->setArg(null, 'uncompress');
+            $pdftk->setArg(null, 'output');
+            $pdftk->setArg(null, $out . '/p%d.pdf');
+            $pdftk->execute();
+
+
+            for ($i = 1; true; $i++) {
+                // Remove annotations : https://gist.github.com/stefanschmidt/5248592
+                $file = sprintf($out . '/p%d.pdf', $i);
+                if (!file_exists($file)) {
+                    break;
+                }
+                $to = sprintf($out . '/s%d.pdf', $i);
+                `LANG=C LC_CTYPE=C sed -n '/^\/Annots/!p' $file > $to`;
+                if (file_exists($to)) {
+                    if (filesize($to) > 0) {
+                        unlink($file);
+                        rename($to, $file);
+                    } else {
+                        unlink($to);
+                    }
+                }
+            }
+        } catch (\Exception $e) {
+
+        }
+        unlink($lock);
+    }
+
+    public static function compressPDF($source, $dest, $resolution = 72)
+    {
+        $gs = new CommandLine('gs');
+        $gs->setArg('-dBATCH');
+        $gs->setArg('-dNOPAUSE');
+        $gs->setArg('-dNOPROMPT');
+        $gs->setArg('-sOutputFile=' . $dest);
+        $gs->setArg('-sDEVICE=pdfwrite');
+        $gs->setArg('-dPDFSETTINGS=/ebook');
+        $gs->setArg('-dColorImageResolution=' . $resolution);
+        $gs->setArg('-dAutoRotatePages=/None');
+        $gs->setArg('-dColorConversionStrategy=/LeaveColorUnchanged');
+        $gs->setArg(null, $source);
+        $gs->execute();
+    }
 
 }