packages feed

data-pdf-fieldreader 0.1.0.0 → 0.1.1.0

raw patch · 4 files changed

+66/−22 lines, 4 filesdep +megaparsecPVP ok

version bump matches the API change (PVP)

Dependencies added: megaparsec

API changes (from Hackage documentation)

+ Data.Pdf.FieldReader: instance GHC.Classes.Eq Data.Pdf.FieldReader.FieldPart
+ Data.Pdf.FieldReader: instance GHC.Show.Show Data.Pdf.FieldReader.FieldPart

Files

CHANGELOG.md view
@@ -6,4 +6,8 @@  * Initially created. +## 0.1.1.0++* Updated to read hex-encoded and multiline field values. This required changing the parser to megaparsec.+ [1]: https://pvp.haskell.org
README.md view
@@ -1,8 +1,10 @@ # Data.Pdf.FieldReader -[![Hackage](https://img.shields.io/hackage/v/pdfreader.svg?logo=haskell)](https://hackage.haskell.org/package/pdfreader)+[![Hackage](https://img.shields.io/hackage/v/data-pdf-fieldreader.svg?logo=haskell)](https://hackage.haskell.org/package/data-pdf-fieldreader-0.1.1.0) [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)  This is a minimal library for reading the data in form fields out of a PDF document.++Updated to be able to read hex-encoded field values, and multiline field values.  A trivial example of how to use this library can be found in `app/Main.hs` in the source repository.
data-pdf-fieldreader.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.4 name:                data-pdf-fieldreader-version:             0.1.0.0+version:             0.1.1.0 description:         Simple function to extract PDF form field values from a PDF file. synopsis:            Read PDF form fields category:            Data@@ -39,6 +39,7 @@   default-language:    Haskell2010   default-extensions:  ConstraintKinds                        DeriveGeneric+                       DerivingStrategies                        GeneralizedNewtypeDeriving                        InstanceSigs                        KindSignatures@@ -55,6 +56,7 @@   import:              common-options   hs-source-dirs:      src   exposed-modules:     Data.Pdf.FieldReader+  build-depends:       megaparsec >= 9.1.0 && < 9.2  executable pdfreader   import:              common-options
src/Data/Pdf/FieldReader.hs view
@@ -15,12 +15,16 @@     readPdfFields   ) where -import Prelude hiding (drop, init, lines) import Data.ByteString (ByteString)-import Data.ByteString.Char8 (unpack)+import Data.Char (chr, digitToInt)+import Data.Functor ((<&>)) import Data.List (foldl')-import Data.Map (Map, fromList)-import Data.Text (Text, drop, init, isPrefixOf, lines, pack, replace, strip)+import qualified Data.Map as M+import qualified Data.Text as T+import Data.Text (Text)+import Data.Text.Encoding (decodeLatin1)+import Data.Void (Void)+import Text.Megaparsec (Parsec, (<|>), anySingle, chunk, many, parseMaybe, takeWhileP)  -- $fileDataParser -- @@ -36,20 +40,52 @@ -- >  let ys = readPdfFields xs -- >  print ys --- | Read fields from file data-readPdfFields :: ByteString -> Map Text Text-readPdfFields = fromList . snd . foldl' f (Nothing, []) . lines . pack . unpack+-- | Read fields from PDF file data++readPdfFields :: ByteString -> M.Map Text Text+readPdfFields = maybe M.empty collate . parseMaybe pdfFieldParser . decodeLatin1++type Parser = Parsec Void Text+data FieldPart +  = FieldName Text+  | FieldValue Text+  | FieldNothing+  deriving stock (Show, Eq)++collate :: [FieldPart] -> M.Map Text Text+collate = snd . foldl' f (Nothing, M.empty)   where-    f (Nothing,  b) x | isFldName x  = (Just (fmtFldName x), b) -                      | otherwise    = (Nothing, b)-    f ((Just n), b) x | isFldName x  = (Just (fmtFldName x), b)-                      | isFldValue x = (Nothing, (n, (fmtFldValue x)) : b)-                      | otherwise    = ((Just n), b)-    isFldName     = isPrefixOf "/T("-    fmtFldName    = stripBrackets-    isFldValue    = isPrefixOf "/V("-    fmtFldValue   = unescape . stripBrackets-    stripBrackets = init . drop 3 . strip-    unescape xs   = foldr (\(a, b) c -> replace a b c) xs escPairs-    escPairs      = [("\\n", "\n"), ("\\(", "("), ("\\)", ")")]-       +    f (Nothing, m) (FieldName n) = (Just n, m)+    f (Nothing, m) _             = (Nothing, m)+    f (Just n, m) (FieldValue v) = (Nothing, M.insert n v m) +    f (Just n, m) _              = (Just n, m) ++pdfFieldParser :: Parser [FieldPart]+pdfFieldParser = many (pFieldName <|> pFieldValue <|> pFieldHexValue <|> anythingElse)++anythingElse :: Parser FieldPart+anythingElse = anySingle >> pure FieldNothing++pFieldName :: Parser FieldPart+pFieldName = getBetween "T(" ")" <&> FieldName++pFieldValue :: Parser FieldPart+pFieldValue = getBetween "V(" ")" <&> FieldValue++pFieldHexValue :: Parser FieldPart+pFieldHexValue = getBetween "V<" ">" <&> FieldValue . decodeHexField++getBetween :: Text -> Text -> Parser Text+getBetween x y = chunk x >> takeWhileP Nothing (/= T.head y) >>= \z -> chunk y >> pure z++decodeHexField :: Text -> Text+decodeHexField xs = if head ys /= "FEFF" then "" else T.pack (map (chr . decodeHex) (tail ys))+  where+    ys = splitBy 4 $ T.concat $ T.lines xs++splitBy :: Int -> Text -> [Text]+splitBy _ "" = []+splitBy x s  = T.take x s : splitBy x (T.drop x s)++decodeHex :: Text -> Int+decodeHex = foldl' (\b a -> b * 16 + digitToInt a) 0 . T.unpack