packages feed

pdf-slave 1.2.3.0 → 1.3.0.0

raw patch · 5 files changed

+139/−65 lines, 5 filesdep +directoryPVP ok

version bump matches the API change (PVP)

Dependencies added: directory

API changes (from Hackage documentation)

- Text.PDF.Slave: renderBundleOrTemplateFromFile :: FilePath -> Maybe Value -> Sh PDFContent
+ Text.PDF.Slave: renderBundleOrTemplateFromFile :: FilePath -> Maybe Value -> Bool -> Sh PDFContent
- Text.PDF.Slave: renderBundleToPDF :: Template -> Sh PDFContent
+ Text.PDF.Slave: renderBundleToPDF :: Template -> Bool -> Sh PDFContent
- Text.PDF.Slave: renderFromFileBundleToPDF :: FilePath -> Maybe Value -> Sh PDFContent
+ Text.PDF.Slave: renderFromFileBundleToPDF :: FilePath -> Maybe Value -> Bool -> Sh PDFContent
- Text.PDF.Slave: renderFromFileToPDF :: FilePath -> Sh PDFContent
+ Text.PDF.Slave: renderFromFileToPDF :: FilePath -> Bool -> Sh PDFContent
- Text.PDF.Slave: renderTemplateToPDF :: TemplateFile -> FilePath -> Sh PDFContent
+ Text.PDF.Slave: renderTemplateToPDF :: TemplateFile -> FilePath -> Bool -> Sh PDFContent
- Text.PDF.Slave.Render: renderBundleOrTemplateFromFile :: FilePath -> Maybe Value -> Sh PDFContent
+ Text.PDF.Slave.Render: renderBundleOrTemplateFromFile :: FilePath -> Maybe Value -> Bool -> Sh PDFContent
- Text.PDF.Slave.Render: renderBundleToPDF :: Template -> Sh PDFContent
+ Text.PDF.Slave.Render: renderBundleToPDF :: Template -> Bool -> Sh PDFContent
- Text.PDF.Slave.Render: renderFromFileBundleToPDF :: FilePath -> Maybe Value -> Sh PDFContent
+ Text.PDF.Slave.Render: renderFromFileBundleToPDF :: FilePath -> Maybe Value -> Bool -> Sh PDFContent
- Text.PDF.Slave.Render: renderFromFileToPDF :: FilePath -> Sh PDFContent
+ Text.PDF.Slave.Render: renderFromFileToPDF :: FilePath -> Bool -> Sh PDFContent
- Text.PDF.Slave.Render: renderTemplateToPDF :: TemplateFile -> FilePath -> Sh PDFContent
+ Text.PDF.Slave.Render: renderTemplateToPDF :: TemplateFile -> FilePath -> Bool -> Sh PDFContent

Files

CHANGELOG.md view
@@ -1,3 +1,12 @@+1.3.0.0+=======++* Default input file name is now `input.json`, not `<template name>_input.json`.++* Changed recommended way of reading input via `Helper.hs`++* Added flag `preserve-temp` to not nuke temporary files after execution.+ 1.2.3.0 ======= 
README.md view
@@ -46,68 +46,88 @@  * `template_input.json` - Input data for template in JSON format. -``` JSON-{-  "line-width": 2,-  "spiral-precision": 0.01,-  "spiral-interval": [0,4],-  "spiral-a": 0.1,-  "spiral-b": 4-}-```+  ``` JSON+  {+    "line-width": 2,+    "spiral-precision": 0.01,+    "spiral-interval": [0,4],+    "spiral-a": 0.1,+    "spiral-b": 4+  }+  ```  * `template.htex` - TeX/LaTeX with embedded Haskell that reads input data from file `template.json`. You have to provide code at the beginning of file that reads the inputs, like that: -``` Haskell-\begin{document}+  Make a `Helper.hs` file in directory with template:+  ``` haskell+  module Helper where -\begin{haskellpragmas}-{-# LANGUAGE OverloadedStrings #-}-\end{haskellpragmas}-\begin{writehaskell}-import Data.Aeson-import System.IO.Unsafe (unsafePerformIO)-import qualified Data.ByteString.Lazy as BS+  import qualified Data.ByteString.Lazy as BS+  import Data.Aeson+  import System.IO.Unsafe -data Input = Input {-  lineWidth       :: Double-, spiralPrecision :: Double-, spiralInterval  :: (Double, Double)-, spiralA         :: Double-, spiralB         :: Double-}+  defaultInput :: FromJSON a => a+  {-# NOINLINE defaultInput #-}+  defaultInput = case unsafePerformIO $ fmap eitherDecode' $ BS.readFile "input.json" of+    Left e -> error (show e)+    Right a -> a+  ``` -instance FromJSON Input where-  parseJSON (Object o) = Input-    <$> o .: "line-width"-    <*> o .: "spiral-precision"-    <*> o .: "spiral-interval"-    <*> o .: "spiral-a"-    <*> o .: "spiral-b"+  Next in your `.htex` file:+  ``` Haskell+  \begin{document} -inpt :: Input-inpt = case unsafePerformIO $ fmap eitherDecode' $ BS.readFile "template_input.json" of-  Left e -> error (show e)-  Right a -> a+  \begin{haskellpragmas}+  {-# LANGUAGE OverloadedStrings #-}+  \end{haskellpragmas}+  \begin{writehaskell}+  import Data.Aeson+  import Helper -\end{writehaskell}-```+  data Input = Input {+    lineWidth       :: Double+  , spiralPrecision :: Double+  , spiralInterval  :: (Double, Double)+  , spiralA         :: Double+  , spiralB         :: Double+  } -Note: The tool copies JSON with inputs to build folder with .htex twice, once with-name specified at `input` key in template description and secondly at fixed `<template_name>_input.json`.+  instance FromJSON Input where+    parseJSON (Object o) = Input+      <$> o .: "line-width"+      <*> o .: "spiral-precision"+      <*> o .: "spiral-interval"+      <*> o .: "spiral-a"+      <*> o .: "spiral-b" +  inpt :: Input+  inpt = defaultInput+  \end{writehaskell}+  ```++  Note: The tool copies JSON with inputs to build folder with .htex twice, once with+  name specified at `input` key in template description and secondly at fixed `input.json`.+ * `template.yaml` - description of template and its dependencies. -``` YAML-name:  template01             # name of template-input: template01_input.json  # name of input file-body:  template01.htex        # name of .htex file-dependencies: {}              # dependency tree (see below)-haskintex-opts: []            # additional flags to haskintex-```+  ``` YAML+  name:  template01             # name of template+  input: template01_input.json  # name of input file+  body:  template01.htex        # name of .htex file+  dependencies: {}              # dependency tree (see below)+  haskintex-opts: []            # additional flags to haskintex+  ``` +  For input `Helper.hs` you should make a record to inform `pdf-slave` that the module should be copied to build directory:++  ``` YAML+  dependencies:+    Helper.hs:+      type: other+  ```+ ## Dependencies  There are 4 different types of dependencies:@@ -310,3 +330,11 @@   ```  3. Or download [precompiled container](https://hub.docker.com/r/ncrashed/pdf-slave/) from Docker Hub.+++Credits+=======++* Daniel Díaz - author of `haskintex` tool that is core of the package.++* Alexander Vershilov aka qnikst - help with debugging and new `Helper.hs` module.
app/Main.hs view
@@ -33,13 +33,15 @@ -- | CLI options data Options = Options {   -- | Path to template file, if missing, stdin is used-  templatePath  :: Maybe FilePath+  templatePath       :: Maybe FilePath   -- | Path to output pdf file, if missing, stdout is used-, pdfOutputPath :: Maybe FilePath+, pdfOutputPath      :: Maybe FilePath   -- | Path to JSON input file for overwritting input data of bundle , inputOverwritePath :: Maybe FilePath+  -- | If the flag is set, temporary dirs won't be nuked after execution+, preserveTempDirs   :: Bool   -- | CLI action-, cliCommand    ::  Command+, cliCommand         :: Command }  -- | Same as 'strOption' but parses Text@@ -65,6 +67,10 @@       <> " for bundle or template input file" )     <> metavar "INPUT_JSON_PATH"     )+  <*> switch (+       long "preserve-temp"+    <> help "If the flag is set, temporary dirs won't be nuked after execution"+    )   <*> commandParser  -- | Parser of CLI commands@@ -110,12 +116,12 @@         bs <- case res of           Left bundle -> do             let bundle' = fromMaybe bundle $ (\i -> bundle { templateInput = i }) <$> optInput-            renderBundleToPDF bundle'+            renderBundleToPDF bundle' nuke           Right template -> do             inputOverwrite <- sequence $ fmap toTextWarn inputOverwritePath             let template' = fromMaybe template $                   (const $ template { templateFileInput = inputOverwrite }) <$> optInput-            renderTemplateToPDF template' baseDir+            renderTemplateToPDF template' baseDir nuke         -- output results         case pdfOutputPath of           Nothing -> liftIO $ BS.putStr bs@@ -150,6 +156,8 @@             outputFolder' <- toTextWarn outputFolder             echo $ "Bundle is unpacked to " <> outputFolder'       _ -> return ()++  nuke = not preserveTempDirs  main :: IO () main = execParser opts >>= pdfSlave
pdf-slave.cabal view
@@ -1,5 +1,5 @@ name:                pdf-slave-version:             1.2.3.0+version:             1.3.0.0 synopsis:            Tool to generate PDF from haskintex templates and YAML input description:         Please see README.md homepage:            https://github.com/NCrashed/pdf-slave#readme@@ -31,6 +31,7 @@     , base64-bytestring        >= 1.0      && < 1.1     , bytestring               >= 0.10     && < 0.11     , containers               >= 0.5      && < 0.6+    , directory                >= 1.2      && < 1.3     , exceptions               >= 0.8      && < 0.9     , haskintex                >= 0.6      && < 0.8     , HaTeX                    >= 3.16     && < 3.18@@ -39,6 +40,7 @@     , system-filepath          >= 0.4      && < 0.5     , unordered-containers     >= 0.2      && < 0.3     , yaml                     >= 0.8      && < 0.9+   default-extensions:     DeriveGeneric     LambdaCase
src/Text/PDF/Slave/Render.hs view
@@ -20,19 +20,25 @@   , parseBundleOrTemplateFromFile   ) where +import Control.Concurrent import Control.Monad (join, forM_) import Control.Monad.Catch import Data.Aeson (Value(..)) import Data.ByteString (ByteString)+import Data.Char import Data.Maybe (fromMaybe) import Data.Monoid import Data.Set (Set) import Data.Yaml (ParseException, decodeEither') import Filesystem.Path (dropExtension, directory)+import Filesystem.Path.CurrentOS (decodeString) import GHC.Generics import Prelude hiding (FilePath) import Shelly+import System.IO (hClose, openTempFile) +import System.Directory (getTemporaryDirectory)+ import qualified Data.Aeson as A import qualified Data.ByteString.Lazy as BZ import qualified Data.Foldable as F@@ -73,15 +79,16 @@ renderBundleOrTemplateFromFile ::      FilePath -- ^ Path to either bundle 'Template' or template 'TemplateFile'   -> Maybe Value -- ^ Overwrite of input JSON for bundle+  -> Bool -- ^ Nuke temp folder?   -> Sh PDFContent-renderBundleOrTemplateFromFile filename bundleInput = do+renderBundleOrTemplateFromFile filename bundleInput nuke = do   res <- parseBundleOrTemplateFromFile filename   let baseDir = directory filename   case res of     Left bundle -> do       let bundle' = fromMaybe bundle $ fmap (\i -> bundle { templateInput = Just i }) bundleInput-      renderBundleToPDF bundle'-    Right template -> renderTemplateToPDF template baseDir+      renderBundleToPDF bundle' nuke+    Right template -> renderTemplateToPDF template baseDir nuke  -- | Try to parse either a bundle or template file parseBundleOrTemplateFromFile :: FilePath -- ^ Path to either 'Template' or 'TemplateFile'@@ -102,36 +109,40 @@ -- | Helper to render from all-in bundle template renderFromFileBundleToPDF :: FilePath -- ^ Path to 'Template' all-in bundle   -> Maybe Value -- ^ Overwrite of input JSON for bundle+  -> Bool -- ^ Nuke temp folder?   -> Sh PDFContent-renderFromFileBundleToPDF filename bundleInput = do+renderFromFileBundleToPDF filename bundleInput nuke = do   cnt <- readBinary filename   case decodeEither' cnt of     Left e -> throwM $ BundleFormatError filename e     Right bundle -> do       let bundle' = fromMaybe bundle $ fmap (\i -> bundle { templateInput = Just i }) bundleInput-      renderBundleToPDF bundle'+      renderBundleToPDF bundle' nuke  -- | Helper to render from template file renderFromFileToPDF :: FilePath -- ^ Path to 'TemplateFile'+  -> Bool -- ^ Nuke temp folder?   -> Sh PDFContent-renderFromFileToPDF filename = do+renderFromFileToPDF filename nuke = do   cnt <- readBinary filename   case decodeEither' cnt of     Left e -> throwM $ TemplateFormatError filename e-    Right template -> renderTemplateToPDF template (directory filename)+    Right template -> renderTemplateToPDF template (directory filename) nuke  -- | Unpack bundle, render the template, cleanup and return PDF renderBundleToPDF :: Template -- ^ Input all-in template+  -> Bool -- ^ Nuke temp folder?   -> Sh PDFContent-renderBundleToPDF bundle = withTmpDir $ \unpackDir -> do+renderBundleToPDF bundle nuke = withTmpDir' nuke $ \unpackDir -> do   template <- storeTemplateInFiles bundle unpackDir-  renderTemplateToPDF template unpackDir+  renderTemplateToPDF template unpackDir nuke  -- | Render template and return content of resulted PDF file renderTemplateToPDF :: TemplateFile -- ^ Input template   -> FilePath -- ^ Base directory+  -> Bool -- ^ Nuke temp folder?   -> Sh PDFContent -- ^ Output PDF file-renderTemplateToPDF t@TemplateFile{..} baseDir = withTmpDir $ \outputFolder -> do+renderTemplateToPDF t@TemplateFile{..} baseDir nuke = withTmpDir' nuke $ \outputFolder -> do   -- Parse global input file and pass it as inherited input   minput <- case templateFileInput of     Nothing -> return Nothing@@ -185,7 +196,7 @@         , toTextArg $ baseDir </> bodyName ]         ++ templateFileHaskintexOpts   -- input file might be missing, if missing we can inject input from parent-  let outputFixedInputName = outputFolder </> (templateFileName <> "_input") <.> "json"+  let outputFixedInputName = outputFolder </> (("input" :: FilePath) <.> "json")   case templateFileInput of     Nothing -> whenJust minput $ \input -> do       writeBinary outputFixedInputName $ BZ.toStrict . A.encode $ input@@ -287,7 +298,7 @@   relInputName <- case templateInput of     Nothing -> return Nothing     Just input -> do-      let inputName = folder </> (templateName <> "_input") <.> "json"+      let inputName = folder </> ("input" :: FilePath) <.> "json"       writeBinary inputName $ BZ.toStrict $ A.encode input       fmap Just $ relativeTo folder inputName   let bodyName = folder </> templateName <.> "htex"@@ -324,3 +335,19 @@         mkdir_p $ directory bodyName         writeBinary bodyName body         return OtherDepFile++-- | Create a temporary directory and pass it as a parameter to a Sh+-- computation. The directory is nuked afterwards if first parameter is 'True'.+withTmpDir' :: Bool -> (FilePath -> Sh a) -> Sh a+withTmpDir' nuke act = do+  trace "withTmpDir"+  dir <- liftIO getTemporaryDirectory+  tid <- liftIO myThreadId+  (pS, fhandle) <- liftIO $ openTempFile dir ("tmp" ++ filter isAlphaNum (show tid))+  let p = decodeString pS+  liftIO $ hClose fhandle -- required on windows+  rm_f p+  mkdir p+  if nuke+    then act p `finally_sh` rm_rf p+    else act p