packages feed

ghostscript-parallel 0.0 → 0.0.1

raw patch · 3 files changed

+89/−15 lines, 3 filesdep +non-emptydep ~optparse-applicative

Dependencies added: non-empty

Dependency ranges changed: optparse-applicative

Files

+ ReadMe.md view
@@ -0,0 +1,38 @@+# Usage++The most simple call is:+~~~~+gs-parallel input.pdf page%04d.png -j4 -- -sDEVICE=png16m -dNOPAUSE -dBATCH+~~~~+This converts the document `input.pdf` to a series of PNGs, one for each page.+It splits the range of all pages in four almost equally sized ranges+and forks a ghostscript instance for each of these ranges.+++If there is a cluster of pages that requires an especially long render time,+then it might be useful to chop the page set into more smaller chunks.+~~~~+gs-parallel input.pdf page%04d.ppm -j4 --chunk-size 100 \+    -- -sDEVICE=ppmraw -dNOPAUSE -dBATCH+~~~~+This will process a document with 910 pages+in chunks of sizes 100, 100, ..., 100, 10.++If you want a more balanced series of chunk sizes,+use the option `--max-chunk-size` instead:+~~~~+gs-parallel input.pdf page%04d.tif -j4 --max-chunk-size 100 \+    -- -sDEVICE=tif24nc -dNOPAUSE -dBATCH+~~~~+It will process 10 chunks of size 91.+++For selecting a subrange of pages,+you must use the options `--first-page` and `--last-page`+instead of the `gs` options `-dFirstPage` and `-dLastPage`,+because `gs-parallel` must know the selected pages.+++# Known issues++We do not escape the pdf file path for page counting, this may cause problems.
ghostscript-parallel.cabal view
@@ -1,5 +1,5 @@ Name:                ghostscript-parallel-Version:             0.0+Version:             0.0.1 Synopsis:            Let Ghostscript render pages in parallel Description:   This is intended for rendering PostScript or PDF documents using Ghostscript.@@ -16,8 +16,7 @@   .   Run it like so:   .-  > gs-parallel input.pdf frames-%04d.png \-  >     -- -sDEVICE=png16m -dNOPAUSE -dBATCH+  > gs-parallel input.pdf page%04d.png -j4 -- -sDEVICE=png16m -dNOPAUSE -dBATCH Homepage:            https://hub.darcs.net/thielema/ghostscript-parallel License:             BSD3 License-File:        LICENSE@@ -28,9 +27,10 @@ Cabal-Version:       >=1.10 Extra-Source-Files:   Makefile+  ReadMe.md  Source-Repository this-  Tag:         0.0+  Tag:         0.0.1   Type:        darcs   Location:    https://hub.darcs.net/thielema/ghostscript-parallel @@ -45,6 +45,7 @@     optparse-applicative >=0.11 && <0.19,     directory >=1.3 && <1.4,     process >=1.6 && <1.7,+    non-empty >=0.3.4 && <0.4,     utility-ht >=0.0.10 && <0.1,     base >=4.5 && <5   Hs-Source-Dirs:      src
src/Main.hs view
@@ -12,12 +12,13 @@ import System.Process (callProcess, readProcess)  import qualified Data.Foldable as Fold-import qualified Data.List.HT as ListHT+import qualified Data.NonEmpty.Class as NonEmptyC+import qualified Data.NonEmpty as NonEmpty import qualified Data.Ix as Ix import Data.Tuple.HT (mapSnd) import Data.Monoid ((<>)) -import Control.Applicative (pure, (<*>))+import Control.Applicative (pure, (<*>), (<|>))  import Text.Printf (printf) @@ -45,18 +46,31 @@ divUp :: (Integral a) => a -> a -> a divUp x y = - div (-x) y +splitPages :: Int -> Int -> NonEmpty.T [] Int+splitPages numJobs numPages =+   fmap (flip divUp numJobs) $+   NonEmpty.mapTail (take numJobs) $ NonEmptyC.iterate (numPages+) 0++splitInChunks :: Int -> Int -> NonEmpty.T [] Int+splitInChunks chunkSize numPages =+   flip NonEmpty.snoc numPages $ takeWhile (<numPages) $ iterate (chunkSize+) 0++splitInBoundedChunks :: Int -> Int -> NonEmpty.T [] Int+splitInBoundedChunks chunkSize numPages =+   splitPages (divUp numPages chunkSize) numPages+ run ::    Int -> Int -> Maybe Int ->+   (Int -> Int -> NonEmpty.T [] Int) ->    FilePath -> FilePath -> [String] -> IO ()-run numJobs firstPage mLastPage input output gsArgs = do+run numJobs firstPage mLastPage split input output gsArgs = do    lastPage <- maybe (countPages input) return mLastPage    let num = Ix.rangeSize (firstPage, lastPage)    let ranges =          zip [0::Integer ..] $-         ListHT.mapAdjacent+         NonEmpty.mapAdjacent             (\p0 p1 -> (p0,p1-1))-            (map ((firstPage+) . flip divUp numJobs) $-             take (numJobs+1) $ iterate (num+) 0)+            (fmap (firstPage+) $ split numJobs num)    Pool.runException (Just numJobs) $       flip map ranges $ \(job,(p0,p1)) -> do          callProcess "gs" $@@ -75,30 +89,51 @@          (printf output dstPage)  +parsePositiveNumber :: (Read a, Ord a, Num a) => String -> OP.ReadM a+parsePositiveNumber name =+   OP.eitherReader $ parseNumber name (0<) "positive"+ parser :: OP.Parser ([String] -> IO ()) parser =    pure run    <*>-      (OP.option-            (OP.eitherReader $ parseNumber "number of jobs" (0<) "positive") $+      (OP.option (parsePositiveNumber "number of jobs") $          OP.short 'j' <>          OP.long "jobs" <>          OP.metavar "NUMBER" <>          OP.value 1 <>          OP.help "Number of parallel threads")    <*>-      (OP.option (OP.eitherReader $ parseNumber "page" (0<) "positive") $+      (OP.option (parsePositiveNumber "page") $          OP.long "first-page" <>          OP.metavar "ONEBASED" <>          OP.value 1 <>          OP.help "First page to render")    <*>-      (OP.option-            (fmap Just $ OP.eitherReader $ parseNumber "page" (0<) "positive") $+      (OP.option (fmap Just $ parsePositiveNumber "page") $          OP.long "last-page" <>          OP.metavar "ONEBASED" <>          OP.value Nothing <>          OP.help "Last page to render")+   <*>+      (+         (OP.option+               (fmap (\chunkSize _numJobs numPages ->+                        splitInChunks chunkSize numPages) $+                parsePositiveNumber "number of pages") $+            OP.long "chunk-size" <>+            OP.metavar "NUMBER" <>+            OP.help "Split pages into chunks of NUMBER pages, except the last chunk")+         <|>+         (OP.option+               (fmap (\chunkSize _numJobs numPages ->+                        splitInBoundedChunks chunkSize numPages) $+                parsePositiveNumber "number of pages") $+            OP.long "max-chunk-size" <>+            OP.metavar "NUMBER" <>+            OP.value splitPages <>+            OP.help "Split pages into chunks of balanced size containing at most NUMBER pages")+      )    <*>       OP.strArgument          (OP.metavar "INPUT" <>