packages feed

hpdft 0.1.0.6 → 0.1.1.1

raw patch · 15 files changed

+1323/−391 lines, 15 filesdep +regex-basedep +regex-compat-tdfaPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: regex-base, regex-compat-tdfa

API changes (from Hackage documentation)

- PDF.Definition: FontMap :: [(Char, String)] -> FontMap
- PDF.Definition: data FontMap
- PDF.Definition: instance GHC.Show.Show PDF.Definition.FontMap
- PDF.DocumentStructure: findObjThroughDict :: Dict -> String -> Maybe Obj
- PDF.DocumentStructure: findObjThroughDictByRef :: Int -> String -> [PDFObj] -> Maybe Obj
+ PDF.CFF: encoding :: ByteString -> Encoding
+ PDF.CFF: instance GHC.Show.Show PDF.CFF.DictOp
+ PDF.Character: extendedAscii :: Map Int Char
+ PDF.Definition: Encoding :: [(Char, String)] -> Encoding
+ PDF.Definition: data Encoding
+ PDF.Definition: instance GHC.Show.Show PDF.Definition.Encoding
+ PDF.DocumentStructure: findObjFromDict :: Dict -> String -> Maybe Obj
+ PDF.DocumentStructure: findObjFromDictWithRef :: Int -> String -> [PDFObj] -> Maybe Obj
+ PDF.DocumentStructure: rawStream :: [Obj] -> ByteString
+ PDF.DocumentStructure: rawStreamByRef :: [PDFObj] -> Int -> ByteString
+ PDF.OpenType: cmap :: ByteString -> CMap
+ PDF.OpenType: instance GHC.Show.Show PDF.OpenType.EncRecord
+ PDF.OpenType: instance GHC.Show.Show PDF.OpenType.Table
+ PDF.PDFIO: getStream :: Bool -> [Obj] -> IO ByteString
+ PDF.Type1: encoding :: ByteString -> Encoding
- PDF.Definition: CIDmap :: String -> FontMap
+ PDF.Definition: CIDmap :: String -> Encoding
- PDF.Definition: NullMap :: FontMap
+ PDF.Definition: NullMap :: Encoding
- PDF.Definition: PSR :: Double -> Double -> Double -> Double -> (Double, Double, Double, Double, Double, Double) -> (Double, Double, Double, Double, Double, Double) -> Bool -> Double -> Double -> Double -> Double -> String -> [(String, CMap)] -> [(String, FontMap)] -> String -> [String] -> PSR
+ PDF.Definition: PSR :: Double -> Double -> Double -> Double -> (Double, Double, Double, Double, Double, Double) -> (Double, Double, Double, Double, Double, Double) -> Bool -> Double -> Double -> Double -> Double -> String -> [(String, CMap)] -> [(String, Encoding)] -> String -> [String] -> PSR
- PDF.Definition: WithCharSet :: String -> FontMap
+ PDF.Definition: WithCharSet :: String -> Encoding
- PDF.Definition: [fontmaps] :: PSR -> [(String, FontMap)]
+ PDF.Definition: [fontmaps] :: PSR -> [(String, Encoding)]

Files

README.md view
@@ -5,22 +5,25 @@ Command usage:   ```-hpdft [-p|--page PAGE] [-r|--ref REF] [-R|--refs] [-T|--title]-             [-I|--info] [-O|--toc] FILE+hpdft [-p|--page PAGE] [-r|--ref REF] [-g|--grep RegExp] [-R|--refs]+             [-T|--title] [-I|--info] [-O|--toc] [--trailer] FILE  Available options:-  -p,--page PAGE           Page number+  -p,--page PAGE           Page number (nomble)   -r,--ref REF             Object reference+  -g,--grep RegExp         grep PDF   -R,--refs                Show object references in page order   -T,--title               Show title (from metadata)   -I,--info                Show PDF metainfo   -O,--toc                 Show table of contents (from metadata)-  --trailer                Show trailer object+  --trailer                Show the trailer of PDF   FILE                     input pdf file   -h,--help                Show this help text ```  ## install++Clone this repository and do cabal-install.  ``` $ cabal install
− data/sample/Sample.hs
@@ -1,196 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Main where--import PDF.Definition--import PDF.Object-import PDF.PDFIO-import PDF.Outlines--import Data.ByteString.UTF8 (ByteString)-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as BSL-import Data.List (nub)--import Debug.Trace--initstate = PSR { linex=0-                , liney=0-                , absolutex=0-                , absolutey=0-                , leftmargin=0.0-                , top=0.0-                , bottom=0.0-                , fontfactor=1-                , curfont=""-                , fontmaps=[]-                , cmaps=[]-                , colorspace=""-                , xcolorspaces=[]-                , text_lm = (0,0,0,0,0,0)-                , text_m = (0,0,0,0,0,0)-                , text_break=True-                }----- | Show bare data in 'ref'ed-object.--objectByRef filename ref = getObjectByRef ref (getPDFObjFromFile filename)---- | Show contents of page 'page' in 'filename'.--showPage filename page = do -  pagetree <- refByPage filename-  contentByRef filename $ pagetree !! (page - 1)-  --- | Show /Content referenced from the 'ref'ed-object in 'filename'.--contentByRef filename ref = do-  objs <- getPDFObjFromFile filename-  obj <- objectByRef filename ref-  BSL.putStrLn $ contentOfObject obj objs-  where contentOfObject obj objs =-          case findDictOfType "/Page" obj of-            Just dict -> contentsStream dict initstate objs-            Nothing -> ""---- | Show raw bytestring stream (without deflated) of page 'page' in 'filename'.--showRawPage filename page = do-  pagetree <- refByPage filename-  rawContentByRef filename $ pagetree !! (page - 1)---- | Show raw bytestring stream (without deflated) of 'ref'ed-object.--streamByRef filename ref = do-  obj <- getObjectByRef ref (getPDFObjFromFile filename) -  return $ rawStream obj---- | Show raw bytestring stream (without deflated) in a /Content referenced from the 'ref'ed-object in 'filename'.--rawContentByRef filename ref = do-  objs <- getPDFObjFromFile filename-  obj <- objectByRef filename ref-  BSL.putStrLn $ rawContentOfObject obj objs-  where rawContentOfObject obj objs =-          case findDictOfType "/Page" obj of-            Just dict -> rawContentsStream dict objs-            Nothing -> ""---- | Show CMap information in object 'ref' in 'filename'.--cmapStreamByRef filename ref = do-  objs <- getPDFObjFromFile filename-  return $ toUnicode ref objs-----data  PageTree = Nop | Page Int | Pages [PageTree]-                 deriving Show---- | Sort object references in page order.--refByPage filename = do-  root <- getRootRef filename-  objs <- getPDFObjFromFile filename-  return $  pageTreeToList $ pageorder root objs--pageorder :: Int -> [PDFObj] -> PageTree-pageorder parent objs = -  case findObjsByRef parent objs of-    Just os -> case findDictOfType "/Catalog" os of-      Just dict -> case pages dict of -        Just pr -> pageorder pr objs-        Nothing -> Nop-      Nothing -> case findDictOfType "/Pages" os of-        Just dict -> case pagesKids dict of-          Just kidsrefs -> Pages $ map (\f -> f objs) (map pageorder kidsrefs)-          Nothing -> Nop-        Nothing -> case findDictOfType "/Page" os of-          Just dict -> Page parent-          Nothing -> Nop-    Nothing -> Nop--pageTreeToList :: PageTree -> [Int]-pageTreeToList (Pages ps) = concatMap pageTreeToList ps-pageTreeToList (Page n) = [n]-pageTreeToList Nop = []----- | Get a whole text from 'filename'. It works as:---    (1) grub objects---    (2) parse within each object, deflating its stream---    (3) linearize stream--pdfToText filename = do-  contents <- BS.readFile filename-  let objs = map parsePDFObj $ getObjs contents-  let rootref = case rootRef contents of-                  Just r  -> r-                  Nothing -> 0-  putStrLn $ show $ linearize rootref objs--linearize :: Int -> [PDFObj] -> PDFStream-linearize parent objs = -  case findObjsByRef parent objs of-    Just os -> case findDictOfType "/Catalog" os of-      Just dict -> case pages dict of -        Just pr -> linearize pr objs-        Nothing -> ""-      Nothing -> case findDictOfType "/Pages" os of-        Just dict -> case pagesKids dict of-          Just kidsrefs -> BSL.concat $ map (\f -> f objs) (map linearize kidsrefs)-          Nothing -> ""-        Nothing -> case findDictOfType "/Page" os of-          Just dict -> contentsStream dict initstate objs-          Nothing -> ""-    Nothing -> ""----- | Show /Title from meta information in 'filename'--showTitle filename = do-  d <- getInfo filename-  let title = -        case findObjThroughDict d "/Title" of-          Just (PdfText s) -> s-          Just x -> show x-          Nothing -> error "No title anyway"-  putStrLn title-  return ()---- | Show /Info from meta information in 'filename'--showInfo filename = do-  d <- getInfo filename-  putStrLn $ toString 0 (PdfDict d)-  return ()---- | Show /Outlines from meta information in 'filename'--showOutlines filename = do-  d <- getOutlines filename-  putStrLn $ show d-  return ()----- | Show device color spaces of each page.--contentColorSpaceByRef filename ref = do-  objs <- getPDFObjFromFile filename-  obj <- objectByRef filename ref-  putStrLn $ show $ filter (/="") $ nub $ csOfObject obj objs-  where csOfObject obj objs =-          case findDictOfType "/Page" obj of-            Just dict -> contentsColorSpace dict initstate objs-            Nothing -> error "Seems to be no color space in content stream"---- | Show device color spaces of all pages.--showColorSpaces filename = do-  pages <- refByPage filename-  pagescs <- mapM (contentColorSpaceByRef filename) pages-  mapM (putStrLn . show) $ zip [1..] pagescs-  return ()
hpdft.cabal view
@@ -1,5 +1,6 @@+cabal-version:       3.0 name:                hpdft-version:             0.1.0.6+version:             0.1.1.1 synopsis:            A tool for looking through PDF file using Haskell -- description:          homepage:            https://github.com/k16shikano/hpdft@@ -11,12 +12,10 @@ category:            PDF build-type:          Simple extra-source-files:  README.md-cabal-version:       >=1.10  data-dir:            data/  data-files:          map/Adobe-Japan1-6.map-data-files:          sample/Sample.hs                          library@@ -29,6 +28,9 @@                      , PDF.DocumentStructure                      , PDF.Outlines                      , PDF.Object+                     , PDF.OpenType+                     , PDF.CFF+                     , PDF.Type1   other-modules:       Paths_hpdft   other-extensions:    OverloadedStrings   build-depends:       attoparsec >=0.13.0@@ -41,6 +43,7 @@                      , memory >= 0.14.5                      , optparse-applicative                      , parsec >=3.0 && <3.2+                     , regex-compat-tdfa >= 0.95.1.4                      , semigroups                      , text >=0.11                      , utf8-string >=0.3@@ -59,7 +62,11 @@                      , hpdft                      , memory >= 0.14.5                      , optparse-applicative+                     , regex-base >= 0.94.0.2+                     , regex-compat-tdfa >= 0.95.1.4+                     , regex-compat-tdfa >= 0.95.1.4                      , semigroups+                     , text >=0.11                      , utf8-string >=0.3   default-language:    Haskell2010   buildable:         True
hpdft.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PackageImports #-} {-# LANGUAGE OverloadedStrings #-}  module Main where@@ -14,12 +16,19 @@ import Data.ByteString.UTF8 (ByteString) import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BSL-import Data.List (nub)+import Data.Text.Lazy as TL (unpack)+import Data.Text.Lazy.Encoding as TL+import Data.List (nub, find) import Data.Maybe (fromMaybe)  import Options.Applicative import Data.Semigroup ((<>)) +import "regex-compat-tdfa" Text.Regex as R+import Options.Applicative (strOption)+import Control.Monad (when)+import PDF.Definition (Obj(PdfStream))+ import Debug.Trace  initstate = PSR { linex=0@@ -54,6 +63,7 @@ data CmdOpt = CmdOpt {   page :: Int,   ref  :: Int,+  grep :: String,   refs :: Bool,   pdftitle :: Bool,   pdfinfo :: Bool,@@ -76,6 +86,12 @@             <> value 0             <> metavar "REF"             <> help "Object reference" )+          <*> strOption+          ( long "grep"+            <> short 'g'+            <> value ""+            <> metavar "RegExp"+            <> help "grep PDF" )           <*> switch           ( long "refs"             <> short 'R'@@ -101,14 +117,15 @@             <> action "file" )  hpdft :: CmdOpt -> IO ()-hpdft (CmdOpt 0 0 False False False False False fn) = pdfToText fn  -hpdft (CmdOpt 0 0 False True _ _ _ fn) = showTitle fn-hpdft (CmdOpt 0 0 False _ True _ _ fn) = showInfo fn-hpdft (CmdOpt 0 0 False _ _ True _ fn) = showOutlines fn-hpdft (CmdOpt 0 0 False _ _ _ True fn) = print =<< getTrailer fn-hpdft (CmdOpt 0 0 True _ _ _ _ fn) = print =<< refByPage fn-hpdft (CmdOpt n 0 False _ _ _ _ fn) = showPage fn n-hpdft (CmdOpt 0 r False _ _ _ _ fn) = print =<< getObjectByRef r =<< getPDFObjFromFile fn+hpdft (CmdOpt 0 0 "" False False False False False fn) = pdfToText fn  +hpdft (CmdOpt 0 0 "" False True _ _ _ fn) = showTitle fn+hpdft (CmdOpt 0 0 "" False _ True _ _ fn) = showInfo fn+hpdft (CmdOpt 0 0 "" False _ _ True _ fn) = showOutlines fn+hpdft (CmdOpt 0 0 "" False _ _ _ True fn) = print =<< getTrailer fn+hpdft (CmdOpt 0 0 "" True _ _ _ _ fn) = print =<< refByPage fn+hpdft (CmdOpt n 0 "" False _ _ _ _ fn) = showPage fn n+hpdft (CmdOpt 0 r "" False _ _ _ _ fn) = showContent fn r+hpdft (CmdOpt 0 0 r  False _ _ _ _ fn) = grepPDF fn r hpdft _ = return ()  -- | Get a whole text from 'filename'. It works as:@@ -172,7 +189,9 @@  showPage filename page = do    pagetree <- refByPage filename-  contentByRef filename $ pagetree !! (page - 1)+  case length pagetree >= page of+    True -> contentByRef filename $ pagetree !! (page - 1)+    False -> putStrLn $ "hpdft: No Page "++(show page)  -- | Show /Content referenced from the 'ref'ed-object in 'filename'. @@ -185,12 +204,39 @@             Just dict -> contentsStream dict initstate objs             Nothing -> "" +showContent filename ref = do+  objs <- getPDFObjFromFile filename+  obj <- getObjectByRef ref objs+  let d = fromMaybe [] $ findDict obj+  if hasStream obj+    then+      if hasSubtype d -- then it's not content stream+        then printStreamWithDict d obj+        else BSL.putStrLn =<< getStream False obj+    else print =<< getObjectByRef ref objs+  where++    hasStream obj = case find isStream obj of+      Just _ -> True+      Nothing -> False+    isStream (PdfStream _) = True+    isStream _             = False++    hasSubtype d = case find isSubtype d of+                       Just _ -> True+                       Nothing -> False+    isSubtype (PdfName "/Subtype", _) = True+    isSubtype x = False++    printStreamWithDict :: Dict -> [Obj] -> IO ()+    printStreamWithDict d obj = print (PdfDict d) >> (BSL.putStrLn =<< getStream True obj)+ -- | Show /Title from meta information in 'filename'  showTitle filename = do   d <- getInfo filename   let title = -        case findObjThroughDict d "/Title" of+        case findObjFromDict d "/Title" of           Just (PdfText s) -> s           Just x -> show x           Nothing -> "No title anyway"@@ -210,4 +256,40 @@   d <- getOutlines filename   putStrLn $ show d   return ()++-- | Find string in each page.++grepPDF filename re = do+  root <- getRootRef filename+  objs <- getPDFObjFromFile filename+  mapM+    (\(strm, pagenm) -> (grepByPage pagenm re . contentInObjs objs) strm)+    $ zip (pageTreeToList $ pageorder root objs) [1..]+  return ()   +  where+    contentInObjs objs ref =+      case findObjsByRef ref objs of+        Just obj -> case findDictOfType "/Page" obj of+                      Just dict -> contentsStream dict initstate objs+                      Nothing -> ""+        Nothing -> error $ "No Object with Ref " ++ show ref++    grepByPage :: Int -> String -> PDFStream -> IO ()+    grepByPage pagenm re txt = do+      let matched = filter (not . null) $ map (grepByLine re) $ BSL.lines txt+      when (not $ null matched) (showResult pagenm matched)+      return ()+      where+        showResult p m = do+          putStrLn $ "At page " <> show p <> "..."+          mapM (putStrLn . (" | " <>)) m+          return ()++    grepByLine :: String -> PDFStream -> String+    grepByLine re txt =+      case matchRegexAll (mkRegex re) $ TL.unpack $ TL.decodeUtf8 txt of+        Just (b, m, a, _) -> (b <> (highlight m) <> a)+        Nothing -> ""++    highlight m = "\ESC[31m" <> m <> "\ESC[0m"
+ src/PDF/CFF.hs view
@@ -0,0 +1,453 @@+{-# LANGUAGE OverloadedStrings #-}++module PDF.CFF (encoding) where++import Numeric (readInt)+import Data.Char (chr, intToDigit)+import Data.List (isPrefixOf)++import Data.Word+import Data.Bits++import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BSC+import qualified Data.ByteString.Lazy as BSL++import Data.Attoparsec.ByteString (Parser, parseOnly, word8, string)+import qualified Data.Attoparsec.ByteString as AP+import Data.Attoparsec.Combinator++import Control.Applicative++import Debug.Trace++import PDF.Definition++type SID = Integer++test f = do+  c <- BS.readFile f+  return $ encoding c++encoding :: ByteString -> Encoding+encoding c =+  Encoding $ map findEncodings $ zip charset encodings++  where+    ds = parseTopDictInd c+    encodings  = concatMap (parseEncoding c) ds+    charset = concatMap (parseCharset c) ds+    fontname = concat $ map (parseFontname c) ds+    strings = case parseOnly stringInd c of+                Right arr -> arr+                Left e -> error "Failed to parse STRING Index"++    findEncodings :: (SID, Char) -> (Char, String)+    findEncodings (char,enc) =+      case char of+        s | s > 390 -> (enc, stringToText $ strings !! fromInteger (char - 390 - 1))+          | s > 95 -> (enc, sidToText s)+          | otherwise -> (enc, [enc])++    -- defined in String INDEX of each font+    stringToText "a113" = "‡"+    stringToText "a114" = "・"+    stringToText "trianglesolid" = "▲"+    stringToText x = "[CFF:String " <> x <> "]"++    -- pre-defined in Appendix C of CFF specs+    sidToText n = [complementSID 0 predefinedChars !! fromInteger n]++parseTopDictInd :: ByteString -> [ByteString]+parseTopDictInd c = case parseOnly (header >> index *> index) c of+  Right ds -> ds+  Left e -> error "Can not find Top DICT INDEX"++parseEncoding :: ByteString -> ByteString -> [Char]+parseEncoding c d = case parseOnly (many1 dict) d of+  -- '16' is key for 'Encoding' in Top DICT+  Right dictData -> case lookup [16] dictData of +    Just (DictInt 0:[]) -> []  +    Just (DictInt 1:[]) -> [] -- Expert Encoding (not supported)+    Just (DictInt n:[]) -> -- n is offset+      case parseOnly encodingArray $ BS.drop n c of+        Right arr -> map (chr . fromInteger) arr+        Left e -> error "Failed to parse Encoding Array"+    Just a -> error (show a)+    Nothing -> error $ "No Encodind Array in " ++ show dictData+  Left _ -> error "Failed to parse Top DICT in CFF"++parseFontname c d = case parseOnly (many1 dict) d of+  Right dictData -> case lookup [2] dictData of+    Just (DictInt n:[]) ->+      case parseOnly stringInd c of+        Right arr -> arr !! (n - 390 - 1) -- 390 seems to be a magic number+        Left e -> error "Failed to parse Fontname"+    Just a -> error (show a)+    Nothing -> error $ "No Fontname in " <> show dictData+  Left _ -> error "Failed to parse Top DICT in CFF"++parseCharset c d = case parseOnly (many1 dict) d of+  Right dictData -> case lookup [15] dictData of+    Just (DictInt offset:[]) -> +      case parseOnly (charsetData $ charStringsInd c dictData) $+           BS.drop offset c of+        Right arr -> arr+        Left _ -> error ""+    Just a -> error (show a)+    Nothing -> error $ "No Charset in " <> show dictData+  Left _ -> error "Failed to parse Top DICT in CFF"++charStringsInd c dictData = +  case lookup [17] dictData of+    Just (DictInt offset:[]) -> +      case parseOnly index (BS.drop offset c) of+        Right [] -> error "failed to get CharStrings"+        Right ind -> ind+        Left "" -> error "failed to get CharStrings"+    Nothing -> error $ "No CharStrings in " <> show dictData++nameInd = map BSC.unpack <$> (header *> index)++dictInd = BSC.concat <$> (header >> index *> index)++stringInd = map BSC.unpack <$> (header >> index >> index *> index)++charsetData :: [ByteString] -> Parser [Integer]+charsetData ind = do+  format <- getCard 1+  charsetObj format+  +  where+    -- .notdef must be excluded, so minus one+    charsetObj 0 = count (length ind - 1) getSID +++encodingArray :: Parser [Integer]+encodingArray = do+  format <- getCard 1+  p <- fromInteger <$> getCard 1+  encodeObj format p++  where+    encodeObj :: Integer -> Int -> Parser [Integer]+    encodeObj 0 p = count (p - 1) (getCard 1)+    encodeObj 1 p = concat <$> count p getRange1+    encodeObj _ p = error "CFF Supplement Format is not supported."++    getRange1 :: Parser [Integer]+    getRange1 = do+      first <- getCard 1+      nleft <- getCard 1+      return $ [first .. first + nleft]++data DictOp = DictInt Int | DictReal Double+  deriving (Show)++dict = (flip (,)) <$> (manyTill dictOp (try $ lookAhead dictKey)) <*> dictKey++dictOp :: Parser DictOp+dictOp = do+  b0 <- fromInteger <$> getCard 1+  opEnc b0+  +  where+    opEnc :: Int -> Parser DictOp+    opEnc b0 | b0 >= 32 && b0 <= 246 = return $ DictInt $ b0 - 139+             | b0 >= 247 && b0 <= 250 = do+                 b1 <- fromInteger <$> getCard 1+                 return $ DictInt $ (b0 - 247) * 256 + b1 + 108+             | b0 >= 251 && b0 <= 254 = do+                 b1 <- fromInteger <$> getCard 1+                 return $ DictInt $ - (b0 - 251) * 256 - b1 - 108+             | b0 == 28 = do+                 b1 <- fromInteger <$> getCard 1+                 b2 <- fromInteger <$> getCard 1+                 return $ DictInt $ b1 `shiftL` 8 .|. b2+             | b0 == 29 = do+                 b1 <- fromInteger <$> getCard 1+                 b2 <- fromInteger <$> getCard 1+                 b3 <- fromInteger <$> getCard 1+                 b4 <- fromInteger <$> getCard 1+                 return $ DictInt $  b1 `shiftL` 24 .|. b2 `shiftL` 16 .|. b3 `shiftL` 8 .|. b4+             | b0 == 30 = do+                 r <- many1 $ AP.satisfy (\w -> (240 .|. w) `xor` 255 /= 0)+                 f <- getCard 1+                 return $ DictReal $ readNibble r f+             | otherwise = error (show b0)+    readNibble s1 s2 = 0++dictKey :: Parser [Word8]+dictKey = do+  key <- AP.choice [ try $ many1 $ AP.satisfy $ AP.inClass "\0-\5\13-\18"+                   , try $ (flip (flip (:) . (:[])))+                     <$> AP.satisfy (==12) <*> (AP.satisfy $ AP.inClass "\0-\8\20-\23\30-\38")+                   ]+  return key++index :: Parser [ByteString]+index = do+  indexCount <- fromInteger <$> getCard 2+  offSize <- fromInteger <$> getCard 1+  offsets <- map fromInteger <$> count (indexCount+1) (getCard offSize)+  indexData <- repeatFor offsets+  return indexData+  where+    repeatFor ls = sequence $ map AP.take $ differenciate ls+    differenciate ls = tail $ zipWith subtract (0:ls) ls++header :: Parser Integer+header = do+  major <- getCard 1+  minor <- getCard 1+  hrdSize <- getCard 1+  offSize <- getCard 1+  return $ major++getCard :: Int -> Parser Integer+getCard n = fromBytes <$> AP.take n++getSID :: Parser SID+getSID = fromBytes <$> AP.take 2++fromBytes :: ByteString -> Integer+fromBytes = BS.foldl' f 0+  where+    f a b = a `shiftL` 8 .|. fromIntegral b++complementSID _ [] = []+complementSID i arr@((n,c):rest) +  | i == n = c:(complementSID (i+1) rest)+  | otherwise = ' ':(complementSID (i+1) arr)++predefinedChars = +  [ (1, ' ')+  , (2, '!')+  , (3, '"')+  , (4, '#')+  , (5, '$')+  , (6, '%')+  , (7, '&')+  , (8, '’')+  , (9, '(')+  , (10, ')')+  , (11, '*')+  , (12, '+')+  , (13, ',')+  , (14, '-')+  , (15, '.')+  , (16, '/')+  , (17, '0')+  , (18, '1')+  , (19, '2')+  , (20, '3')+  , (21, '4')+  , (22, '5')+  , (23, '6')+  , (24, '7')+  , (25, '8')+  , (26, '9')+  , (27, ':')+  , (28, ';')+  , (29, '<')+  , (30, '=')+  , (31, '>')+  , (32, '?')+  , (33, '@')+  , (34, 'A')+  , (35, 'B')+  , (36, 'C')+  , (37, 'D')+  , (38, 'E')+  , (39, 'F')+  , (40, 'G')+  , (41, 'H')+  , (42, 'I')+  , (43, 'J')+  , (44, 'K')+  , (45, 'L')+  , (46, 'M')+  , (47, 'N')+  , (48, 'O')+  , (49, 'P')+  , (50, 'Q')+  , (51, 'R')+  , (52, 'S')+  , (53, 'T')+  , (54, 'U')+  , (55, 'V')+  , (56, 'W')+  , (57, 'X')+  , (58, 'Y')+  , (59, 'Z')+  , (60, '{')+  , (61, '/')+  , (62, '}')+  , (63, '^')+  , (64, '_')+  , (65, '‘')+  , (66, 'a')+  , (67, 'b')+  , (68, 'c')+  , (69, 'd')+  , (70, 'e')+  , (71, 'f')+  , (72, 'g')+  , (73, 'h')+  , (74, 'i')+  , (75, 'j')+  , (76, 'k')+  , (77, 'l')+  , (78, 'm')+  , (79, 'n')+  , (80, 'o')+  , (81, 'p')+  , (82, 'q')+  , (83, 'r')+  , (84, 's')+  , (85, 't')+  , (86, 'u')+  , (87, 'v')+  , (88, 'w')+  , (89, 'x')+  , (90, 'y')+  , (91, 'z')+  , (92, '[')+  , (93, 'ˉ')+  , (94, ']')+  , (95, '~')+  , (96, '¡')+  , (97, '¢')+  , (98, '£')+  , (99, '/')+  , (100, '¥')+  , (101, 'ƒ')+  , (102, '§')+  , (103, '$')+  , (104, '\'')+  , (105, '“')+  , (106, '«')+  , (107, '‹')+  , (108, '›')+  , (109, 'fi')+  , (110, 'fl')+  , (111, '–')+  , (112, '†')+  , (113, '‡')+  , (114, '·')+  , (115, '❡')+  , (116, '・')+  , (117, '‚')+  , (118, '„')+  , (119, '”')+  , (120, '»')+  , (121, '…')+  , (122, '‰')+  , (123, '¿')+  , (124, '`')+  , (125, '´')+  , (126, '^')+  , (127, '~')+  , (128, '¯')+  , (129, '˘')+  , (130, '˙')+  , (131, '¨')+  , (132, '°')+  , (133, '¸')+  , (134, '˝')+  , (135, '˛')+  , (136, 'ˇ')+  , (137, '—')+  , (138, 'Æ')+  , (139, 'ª')+  , (140, 'Ł')+  , (141, 'Ø')+  , (142, 'Œ')+  , (143, 'º')+  , (144, 'æ')+  , (145, 'ı')+  , (146, 'ł')+  , (147, 'ø')+  , (148, 'œ')+  , (149, 'ẞ')+  , (150, '¹')+  , (151, '¬')+  , (152, 'µ')+  , (153, '™')+  , (154, 'Ð')+  , (155, '½')+  , (156, '±')+  , (157, 'Þ')+  , (158, '¼')+  , (159, '÷')+  , (160, '¦')+  , (161, '°')+  , (162, 'þ')+  , (163, '¾')+  , (164, '²')+  , (165, '®')+  , (166, '-')+  , (167, 'ð')+  , (168, '×')+  , (169, '³')+  , (170, 'Ⓒ')+  , (171, 'Á')+  , (172, 'Â')+  , (173, 'Ä')+  , (174, 'À')+  , (175, 'Å')+  , (176, 'Ã')+  , (177, 'Ç')+  , (178, 'É')+  , (179, 'Ê')+  , (180, 'Ë')+  , (181, 'È')+  , (182, 'Í')+  , (183, 'Î')+  , (184, 'Ï')+  , (185, 'Ì')+  , (186, 'Ñ')+  , (187, 'Ó')+  , (188, 'Ô')+  , (189, 'Ö')+  , (190, 'Ò')+  , (191, 'Õ')+  , (192, 'Š')+  , (193, 'Ú')+  , (194, 'Û')+  , (195, 'Ü')+  , (196, 'Ù')+  , (197, 'Ý')+  , (198, 'Ÿ')+  , (199, 'Ž')+  , (200, 'á')+  , (201, 'â')+  , (202, 'ä')+  , (203, 'à')+  , (204, 'å')+  , (205, 'ã')+  , (206, 'ç')+  , (207, 'é')+  , (208, 'ê')+  , (209, 'ë')+  , (210, 'è')+  , (211, 'í')+  , (212, 'î')+  , (213, 'ï')+  , (214, 'ì')+  , (215, 'ñ')+  , (216, 'ó')+  , (217, 'ô')+  , (218, 'ö')+  , (219, 'ò')+  , (220, 'õ')+  , (221, 'š')+  , (222, 'ú')+  , (223, 'û')+  , (224, 'ü')+  , (225, 'ù')+  , (226, 'ý')+  , (227, 'ÿ')+  , (228, 'ž')+  ]
src/PDF/Character.hs view
@@ -3,6 +3,7 @@  module PDF.Character        ( pdfcharmap+       , extendedAscii        , adobeJapanOneSixMap) where  import qualified Data.Text as T@@ -15,8 +16,142 @@  pdfcharmap = Map.fromList pdfchardict  +extendedAscii = Map.fromList extendedasciidict+ adobeJapanOneSixMap :: Map.Map Int BSLU.ByteString adobeJapanOneSixMap = decode . decompress . BSL.fromChunks . (:[]) $ $(embedFile "data/map/Adobe-Japan1-6.map")++extendedasciidict :: [(Int, Char)]+extendedasciidict =+  [ (128, '€')+  , (129, ' ')+  , (130, '‚')+  , (131, 'ƒ')+  , (132, '„')+  , (133, '…')+  , (134, '†')+  , (135, '‡')+  , (136, 'ˆ')+  , (137, '‰')+  , (138, 'Š')+  , (139, '‹')+  , (140, 'Œ')+  , (141, ' ')+  , (142, 'Ž')+  , (143, ' ')+  , (144, ' ')+  , (145, '‘')+  , (146, '’')+  , (147, '“')+  , (148, '”')+  , (149, '•')+  , (150, '–')+  , (151, '—')+  , (152, '˜')+  , (153, '™')+  , (154, 'š')+  , (155, '›')+  , (156, 'œ')+  , (157, ' ')+  , (158, 'ž')+  , (159, 'Ÿ')+  , (160, ' ')+  , (161, '¡')+  , (162, '¢')+  , (163, '£')+  , (164, '¤')+  , (165, '¥')+  , (166, '¦')+  , (167, '§')+  , (168, '¨')+  , (169, '©')+  , (170, 'ª')+  , (171, '«')+  , (172, '¬')+  , (173, '-')+  , (174, '®')+  , (175, '¯')+  , (176, '°')+  , (177, '±')+  , (178, '²')+  , (179, '³')+  , (180, '´')+  , (181, 'µ')+  , (182, '¶')+  , (183, '·')+  , (184, '¸')+  , (185, '¹')+  , (186, 'º')+  , (187, '»')+  , (188, '¼')+  , (189, '½')+  , (190, '¾')+  , (191, '¿')+  , (192, 'À')+  , (193, 'Á')+  , (194, 'Â')+  , (195, 'Ã')+  , (196, 'Ä')+  , (197, 'Å')+  , (198, 'Æ')+  , (199, 'Ç')+  , (200, 'È')+  , (201, 'É')+  , (202, 'Ê')+  , (203, 'Ë')+  , (204, 'Ì')+  , (205, 'Í')+  , (206, 'Î')+  , (207, 'Ï')+  , (208, 'Ð')+  , (209, 'Ñ')+  , (210, 'Ò')+  , (211, 'Ó')+  , (212, 'Ô')+  , (213, 'Õ')+  , (214, 'Ö')+  , (215, '×')+  , (216, 'Ø')+  , (217, 'Ù')+  , (218, 'Ú')+  , (219, 'Û')+  , (220, 'Ü')+  , (221, 'Ý')+  , (222, 'Þ')+  , (223, 'ß')+  , (224, 'à')+  , (225, 'á')+  , (226, 'â')+  , (227, 'ã')+  , (228, 'ä')+  , (229, 'å')+  , (230, 'æ')+  , (231, 'ç')+  , (232, 'è')+  , (233, 'é')+  , (234, 'ê')+  , (235, 'ë')+  , (236, 'ì')+  , (237, 'í')+  , (238, 'î')+  , (239, 'ï')+  , (240, 'ð')+  , (241, 'ñ')+  , (242, 'ò')+  , (243, 'ó')+  , (244, 'ô')+  , (245, 'õ')+  , (246, 'ö')+  , (247, '÷')+  , (248, 'ø')+  , (249, 'ù')+  , (250, 'ú')+  , (251, 'û')+  , (252, 'ü')+  , (253, 'ý')+  , (254, 'þ')+  , (255, 'ÿ')+  ]  pdfchardict :: [(String, T.Text)] pdfchardict =
src/PDF/Cmap.hs view
@@ -34,8 +34,12 @@                                  (try $ string "endcmap"))                                () "" str of   Left err -> error $ "Can not parse CMap " ++ (show err)-  Right cmap -> cmap+  Right cmap -> mkUniq cmap +  where+    mkUniq = reverse++ skipHeader :: Parser () skipHeader = do   manyTill anyChar (try $ string "endcodespacerange")@@ -57,11 +61,13 @@  bfrange :: Parser [CMap] bfrange = do-  many1 digit+  d <- many1 digit   spaces    string "beginbfrange"   spaces-  ms <- many (toCmap <$> (getRange <$> hexletters <*> hexletters) <*> (hexletters <|> hexletterArray))+  ms <- many (toCmap+              <$> (getRange <$> hexletters <*> hexletters)+              <*> ((mkStrList d . lines) <$> (try hexletters <|> hexletterArray)))   spaces   string "endbfrange"   spaces@@ -69,7 +75,10 @@     where        gethex = fst.head.readHex       getRange cid cid' = [gethex cid .. gethex cid']-      toCmap range ucs = zip range (map ((:[]).chr) [gethex ucs ..])+      mkStrList d src = if (length src) == 1+                        then [gethex $ head src .. ]+                        else map gethex src+      toCmap range ucs = zip range (map ((:[]).chr) ucs)   hexletters :: Parser String
src/PDF/ContentStream.hs view
@@ -7,17 +7,16 @@  import Data.Char (chr, ord) import Data.String (fromString)-import Data.List (isPrefixOf)+import Data.List (isPrefixOf, dropWhileEnd) import Numeric (readOct, readHex) import Data.Maybe (fromMaybe)+import qualified Data.Map as Map -import Data.Binary (decode) import Data.ByteString (ByteString)-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as BSC-import qualified Data.ByteString.Lazy.UTF8 as BSL+import qualified Data.ByteString.Lazy.Char8 as BSLC (ByteString, pack)+import qualified Data.ByteString.Char8 as BSSC (unpack)+import qualified Data.ByteString.Lazy.UTF8 as BSLU (toString) import qualified Data.Text as T-import qualified Data.Map as Map import Data.Text.Encoding (encodeUtf8)  import Text.Parsec hiding (many, (<|>))@@ -28,7 +27,7 @@  import PDF.Definition import PDF.Object-import PDF.Character (pdfcharmap, adobeJapanOneSixMap)+import PDF.Character (pdfcharmap, extendedAscii, adobeJapanOneSixMap)  type PSParser a = GenParser Char PSR a @@ -36,11 +35,11 @@  parseStream :: PSR -> PDFStream -> PDFStream parseStream psr pdfstream = -  case parseContentStream (T.concat <$> many (elems <|> skipOther)) psr pdfstream of+  case parseContentStream (T.concat <$> (spaces >> many (try elems <|> skipOther))) psr pdfstream of     Left  err -> error $ "Nothing to be parsed: " ++ (show err) -    Right str -> BSC.pack $ BS.unpack $ encodeUtf8 str+    Right str -> BSLC.pack $ BSSC.unpack $ encodeUtf8 str -parseColorSpace :: PSR -> BSC.ByteString -> [T.Text]+parseColorSpace :: PSR -> BSLC.ByteString -> [T.Text] parseColorSpace psr pdfstream =    case parseContentStream (many (choice [ try colorSpace                                         , try $ T.concat <$> xObject@@ -59,6 +58,7 @@                , try pdfopTd                , try pdfopTm                , try pdfopTc+               , try pdfopTs                , try pdfopTw                , try pdfopTL                , try pdfopTz@@ -170,9 +170,7 @@  pdfopBDC :: PSParser T.Text pdfopBDC = do-  n1 <- (++) <$> string "/" <*> manyTill anyChar (try $ lookAhead propertyList)-  spaces-  n2 <- propertyList+  n1 <- name *> propertyList   spaces   string "BDC"   spaces@@ -186,11 +184,25 @@   return T.empty  propertyList :: PSParser T.Text-propertyList = do-  plist <- spaces >> string "<<" >> spaces *> manyTill anyChar (try $ spaces >> string ">>")-  return $ T.pack plist+propertyList = spaces >> choice [try dictionary, try name] +dictionary :: PSParser T.Text+dictionary = T.concat <$> (spaces >> string "<<" >> spaces+                            *> manyTill dictEntry (try (string ">>" >> (notFollowedBy $ string ">")))) +dictEntry :: PSParser T.Text+dictEntry = choice [ try name+                   , try letters+                   , T.pack <$> try hex+                   , T.pack <$> try (many1 digit)+                   ] <* spaces+  where+    hex = string "<" >> (manyTill (oneOf "0123456789abcdefABCDEF") (try $ string ">"))++name :: PSParser T.Text+name = T.pack <$> ((++) <$> string "/" <*> (manyTill anyChar (try $ lookAhead $ oneOf "><][)( \n\r/")) <* spaces)++ pdfopTj :: PSParser T.Text pdfopTj = do   spaces@@ -230,9 +242,15 @@ unknowns :: PSParser T.Text unknowns = do    ps <- manyTill anyChar (try $ oneOf "\r\n")-  return $ if ps=="" -           then "" -           else T.pack $ "[[[UNKNOWN STREAM:" ++ take 100 (show ps) ++ "]]]"+  st <- getState+  -- linebreak within (...) is parsed as Tj+  return $ case runParser elems st "" $ BSLC.pack ((Data.List.dropWhileEnd (=='\\') ps)++")Tj") of+             Right xs -> xs+             Left e -> case runParser elems st "" $ BSLC.pack ("("++ps) of+               Right xs -> xs+               Left e -> case ps of+                 "" -> ""+                 otherwise -> T.pack $ "[[[UNKNOWN STREAM:" ++ take 100 (show ps) ++ "]]]"  skipOther :: PSParser T.Text skipOther = do@@ -255,16 +273,48 @@ letters = do   char '('   st <- getState-  let letterParser = case lookup (curfont st) (fontmaps st) of-        Just (FontMap m) -> psletter m+  let cmap = fromMaybe [] (lookup (curfont st) (cmaps st))+      letterParser = case lookup (curfont st) (fontmaps st) of+        Just (Encoding m) -> psletter m         Just (CIDmap s) -> cidletter s-        Just (WithCharSet s) -> cidletters+        Just (WithCharSet s) -> try $ bytesletter cmap <|> cidletters         Just NullMap -> psletter []-        Nothing -> cidletter "Adobe-Japan1" -- as a defalt map-  lets <- manyTill letterParser (try $ char ')')+        Nothing -> (T.pack) <$> (many1 $ choice [ try $ ')' <$ (string "\\)")+                                                , try $ '(' <$ (string "\\(")+                                                , try $ noneOf ")"+                                                ])+  lets <- manyTill letterParser $ (try $ char ')')   spaces   return $ T.concat lets +bytesletter :: CMap -> PSParser T.Text+bytesletter cmap = do+  txt <- (many1 $ choice [ try $ ')' <$ (string "\\)")+                         , try $ '(' <$ (string "\\(")+                         , try $ (chr 10) <$ (string "\\n")+                         , try $ (chr 13) <$ (string "\\r")+                         , try $ (chr 8) <$ (string "\\b")+                         , try $ (chr 9) <$ (string "\\t")+                         , try $ (chr 12) <$ (string "\\f")+                         , try $ (chr 92) <$ (string "\\\\")+                         , try $ (chr 0) <$ (char '\NUL')+                         , try $ (chr 32) <$ (char ' ')+                         , try $ chr <$> ((string "\\") *> octnum)+                         , try $ noneOf ")"+                         ])+  return $ byteStringToText cmap txt+  where+    byteStringToText :: CMap -> String -> T.Text+    byteStringToText cmap str = T.concat $ map (toUcs cmap) $ asInt16 $ map ord str++    asInt16 :: [Int] -> [Int]+    asInt16 [] = []+    asInt16 (a:[]) = [a] --error $ "Can not read string "++(show a)+    asInt16 (a:b:rest) = (a * 256 + b):(asInt16 rest)++    -- for debug+    -- myToUcs cmap x = if x == 636 then trace (show cmap) $ toUcs cmap x else toUcs cmap x+ hexletters :: PSParser T.Text hexletters = do   char '<'@@ -281,21 +331,25 @@  adobeOneSix :: Int -> T.Text adobeOneSix a = case Map.lookup a adobeJapanOneSixMap of-  Just cs -> T.pack $ BSL.toString cs+  Just cs -> T.pack $ BSLU.toString cs   Nothing -> T.pack $ "[" ++ (show a) ++ "]"  toUcs :: CMap -> Int -> T.Text toUcs m h = case lookup h m of   Just ucs -> T.pack ucs-  Nothing -> adobeOneSix h+  Nothing -> if m == [] then adobeOneSix h else T.pack [chr h] -cidletters = choice [try hexletter, octletter]+cidletters = choice [try hexletter, try octletter]  hexletter :: PSParser T.Text hexletter = do   st <- getState-  let cmap = fromMaybe [] (lookup (curfont st) (cmaps st))-  (hexToString cmap . readHex) <$> (count 4 $ oneOf "0123456789ABCDEFabcdef")+  let font = curfont st+      cmap = fromMaybe [] (lookup font (cmaps st))+  (hexToString cmap . readHex) <$> choice [ try $ count 4 $ oneOf "0123456789ABCDEFabcdef"+                                          , try $ count 2 $ oneOf "0123456789ABCDEFabcdef"+                                          , try $ (:"0") <$> (oneOf "0123456789ABCDEFabcdef")+                                          ]   where hexToString m [(h,"")] = toUcs m h         hexToString _ _ = "????" @@ -326,7 +380,9 @@             [(i,"")] -> T.singleton $ chr i             [(i,x)] -> T.pack (chr i : " ")             _ -> T.pack s-          octToChar [(o,"")] = chr o+          octToChar [(o,"")] = case Map.lookup o extendedAscii of+            Just c -> c+            Nothing -> chr o           octToChar _ = '?'  cidletter :: String -> PSParser T.Text@@ -492,6 +548,14 @@   spaces   st <- getState   let ff = fontfactor st+  return $ ""++pdfopTs :: PSParser T.Text+pdfopTs = do+  tc <- digitParam+  spaces+  string "Ts"+  spaces   return $ ""  desideParagraphBreak :: Double -> Double -> Double -> Double -> Double -> Double 
src/PDF/Definition.hs view
@@ -48,11 +48,11 @@ toString depth (PdfNull) = ""  -data FontMap = CIDmap String | FontMap [(Char,String)] | WithCharSet String | NullMap+data Encoding = CIDmap String | Encoding [(Char,String)] | WithCharSet String | NullMap -instance Show FontMap where+instance Show Encoding where   show (CIDmap s) = "CIDmap"++s-  show (FontMap a) = "FontMap"++show a+  show (Encoding a) = "Encoding"++show a   show (WithCharSet s) = "WithCharSet"++s   show NullMap = [] @@ -71,7 +71,7 @@                , fontfactor :: Double                , curfont    :: String                , cmaps      :: [(String, CMap)]-               , fontmaps   :: [(String, FontMap)]+               , fontmaps   :: [(String, Encoding)]                , colorspace :: String                , xcolorspaces :: [String]                }
src/PDF/DocumentStructure.hs view
@@ -13,23 +13,28 @@        , expandObjStm        , rootRef        , contentsStream+       , rawStreamByRef        , findKids        , findPages        , findDict        , findDictByRef        , findDictOfType-       , findObjThroughDict-       , findObjThroughDictByRef+       , findObjFromDict+       , findObjFromDictWithRef        , findObjsByRef        , findObjs        , findTrailer+       , rawStream        ) where  import Data.Char (chr) import Data.List (find) import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BSL+import qualified Data.ByteString.Builder as B import qualified Data.Text as T+import Data.Maybe (fromMaybe)+import Numeric (readDec)  import Data.Attoparsec.ByteString.Char8 hiding (take) import Data.Attoparsec.Combinator@@ -42,6 +47,9 @@ import PDF.Object import PDF.ContentStream (parseStream, parseColorSpace) import PDF.Cmap (parseCMap)+import qualified PDF.OpenType as OpenType+import qualified PDF.CFF as CFF+import qualified PDF.Type1 as Type1  spaces = skipSpace oneOf = satisfy . inClass@@ -67,13 +75,13 @@     isRefObj (Just x) (y, objs) = if x==y then True else False     isRefObj _ _ = False -findObjThroughDictByRef :: Int -> String -> [PDFObj] -> Maybe Obj-findObjThroughDictByRef ref name objs = case findDictByRef ref objs of -  Just d -> findObjThroughDict d name+findObjFromDictWithRef :: Int -> String -> [PDFObj] -> Maybe Obj+findObjFromDictWithRef ref name objs = case findDictByRef ref objs of +  Just d -> findObjFromDict d name   Nothing -> Nothing   -findObjThroughDict :: Dict -> String -> Maybe Obj-findObjThroughDict d name = case find isName d of+findObjFromDict :: Dict -> String -> Maybe Obj+findObjFromDict d name = case find isName d of   Just (_, o) -> Just o   otherwise -> Nothing   where isName (PdfName n, _) = if name == n then True else False@@ -118,17 +126,25 @@  contentsStream :: Dict -> PSR -> [PDFObj] -> PDFStream contentsStream dict st objs = case find contents dict of-  Just (PdfName "/Contents", PdfArray arr) -> parseContentStream dict st objs $ BSL.concat $ map (rawStreamByRef objs) (parseRefsArray arr)-  Just (PdfName "/Contents", ObjRef r)     -> parseContentStream dict st objs $ rawStreamByRef objs r-  Nothing                                  -> error "No content to be shown"+  Just (PdfName "/Contents", PdfArray arr) -> getContentArray arr+  Just (PdfName "/Contents", ObjRef r) ->+    case findObjsByRef r objs of+      Just [PdfArray arr] -> getContentArray arr+      Just _ -> getContent r+      Nothing -> error "No content to be shown"+  Nothing -> error "No content to be shown"   where     contents (PdfName "/Contents", _) = True-    contents _                        = False+    contents _ = False +    getContentArray arr = parseContentStream dict st objs $+                          BSL.concat $ map (rawStreamByRef objs) (parseRefsArray arr)+    getContent r = parseContentStream dict st objs $ rawStreamByRef objs r+ parseContentStream :: Dict -> PSR -> [PDFObj] -> BSL.ByteString -> PDFStream parseContentStream dict st objs s =    parseStream (st {fontmaps=fontdict, cmaps=cmap}) s-  where fontdict = findFontMap dict objs+  where fontdict = findFontEncoding dict objs         cmap = findCMap dict objs  rawStreamByRef :: [PDFObj] -> Int -> BSL.ByteString@@ -138,19 +154,23 @@  rawStream :: [Obj] -> BSL.ByteString rawStream objs = case find isStream objs of-  Just (PdfStream strm) -> streamFilter strm-  Nothing               -> error $ (show objs) ++ "\n  No stream to be shown"+  Just (PdfStream strm) -> rawStream' (fromMaybe [] $ findDict objs) strm+  Nothing               -> BSL.pack $ show objs   where     isStream (PdfStream s) = True     isStream _             = False -    streamFilter = case findDict objs of-                     Just d -> case find withFilter d of-                                 Just (PdfName "/Filter", PdfName "/FlateDecode")-                                   -> decompress-                                 Just _ -> id -- need fix-                                 Nothing -> id-                     Nothing -> id+    rawStream' :: Dict -> BSL.ByteString -> BSL.ByteString+    rawStream' d s = streamFilter d s++    streamFilter d = case find withFilter d of+      Just (PdfName "/Filter", PdfName "/FlateDecode")+        -> decompress+      Just (PdfName "/Filter", PdfName f)+        -> error $ "Unknown Stream Compression: " ++ f -- need fix+      Just _ -> error $ "No Stream Compression Filter."+      Nothing -> id+     withFilter (PdfName "/Filter", _) = True     withFilter _                      = False @@ -175,13 +195,13 @@     pairwise x = ""  findXObject dict objs = case findResourcesDict dict objs of-  Just d -> case findObjThroughDict d "/XObject" of+  Just d -> case findObjFromDict d "/XObject" of     Just (PdfDict d) -> d     otherwise -> []   Nothing -> []  xobjColorSpace :: Int -> [PDFObj] -> String-xobjColorSpace x objs = case findObjThroughDictByRef x "/ColorSpace" objs of+xobjColorSpace x objs = case findObjFromDictWithRef x "/ColorSpace" objs of   Just (PdfName cs) -> cs   otherwise -> "" @@ -189,31 +209,32 @@ -- find root ref from Trailer or Cross-Reference Dictionary  parseTrailer :: BS.ByteString -> Maybe Dict-parseTrailer bs = case parseOnly (try trailer <|> xref) bs of-  Left  err -> (trace (show err) Nothing)-  Right rlt -> Just (parseCRDict rlt)-  where trailer :: Parser BS.ByteString-        trailer = do-          manyTill anyChar (try $ string "trailer")-          t <- manyTill anyChar (try $ string "startxref")-          return $ BS.pack t-        xref :: Parser BS.ByteString-        xref = do-          manyTill anyChar (try $ string "startxref" >> spaces >> lookAhead (oneOf "123456789"))-          offset <- many1 digit-          return $ BS.drop (read offset :: Int) bs+parseTrailer bs = case BS.breakEnd (== '\n') bs of+  (source, eofLine)+    | "%%EOF" `BS.isPrefixOf` eofLine+      -> Just (parseCRDict $ BS.drop (getOffset source) bs)+    | source == "" -> Nothing+    | otherwise -> parseTrailer (BS.init bs) +getOffset bs = case BS.breakEnd (== '\n') (BS.init bs) of+  (_, nstr) -> case readDec $ BS.unpack nstr of+                 [(n,_)] -> n+                 _ -> error "Could not find Offset"+ parseCRDict :: BS.ByteString -> Dict parseCRDict rlt = case parseOnly crdict rlt of   Left  err  -> error $ show (BS.take 100 rlt)   Right (PdfDict dict) -> dict-  Right other -> error "Could not find Cross-Reference dictionary"-  where crdict :: Parser Obj-        crdict = do -          spaces-          many (many1 digit >> spaces >> digit >> string " obj" >> spaces)-          d <- pdfdictionary <* spaces-          return d+  Right _ -> error "Could not find Cross-Reference dictionary"+  where+    crdict :: Parser Obj+    crdict = do +      spaces+      (try skipCRtable <|> skipCRstream)+      d <- pdfdictionary <* spaces+      return d+    skipCRtable = ((manyTill anyChar (try $ string "trailer")) >> spaces)+    skipCRstream = (many1 digit >> spaces >> digit >> string " obj" >> spaces)  rootRef :: BS.ByteString -> Maybe Int rootRef bs = case parseTrailer bs of@@ -285,19 +306,22 @@  -- make fontmap from page's /Resources (see 3.7.2 of PDF Ref.) -findFontMap d os = findEncoding (fontObjs d os) os+findFontEncoding d os = findEncoding (fontObjs d os) os -findEncoding :: Dict -> [PDFObj] -> [(String, FontMap)]+findEncoding :: Dict -> [PDFObj] -> [(String, Encoding)] findEncoding dict objs = map pairwise dict-  where -    pairwise (PdfName n, ObjRef r) = (n, fontMap r objs)+  where+    pairwise (PdfName n, ObjRef r) = (n, encoding r objs)     pairwise x = ("", NullMap)  fontObjs :: Dict -> [PDFObj] -> Dict fontObjs dict objs = case findResourcesDict dict objs of-  Just d -> case findObjThroughDict d "/Font" of-    Just (PdfDict d) -> d-    otherwise -> []+  Just d -> case findObjFromDict d "/Font" of+    Just (PdfDict d') -> d'+    Just (ObjRef x) -> case findDictByRef x objs of+                         Just d' -> d'+                         otherwise -> error "cannot find /Font dictionary"+    otherwise -> trace (show d) $ []   Nothing -> []  findResourcesDict :: Dict -> [PDFObj] -> Maybe Dict@@ -309,61 +333,126 @@     resources (PdfName "/Resources", _) = True     resources _                         = False --- Needs rewrite!-fontMap :: Int -> [PDFObj] -> FontMap-fontMap x objs = case findObjThroughDictByRef x "/Encoding" objs of-  Just (ObjRef ref) -> case findObjThroughDictByRef ref "/Differences" objs of-    Just (PdfArray arr) -> charMap arr-    otherwise -> trace "no /differences" NullMap-  Just (PdfName "/StandardEncoding") -> NullMap-  Just (PdfName "/MacRomanEncoding") -> NullMap-  Just (PdfName "/MacExpertEncoding") -> NullMap-  Just (PdfName "/WinAnsiEncoding") -> NullMap-  otherwise -> case findObjThroughDictByRef x "/ToUnicode" objs of-    Just (ObjRef ref) -> case findObjThroughDictByRef ref "/CharSet" objs of-      Just (PdfText str) -> WithCharSet str-      otherwise -> WithCharSet ""-    otherwise -> case findObjThroughDictByRef x "/DescendantFonts" objs of -- needs CID to Unicode map-      Just (ObjRef ref) -> case findObjsByRef ref objs of-        Just [(PdfArray ((ObjRef subref):_))] -> case findObjThroughDictByRef subref "/CIDSystemInfo" objs of-          Just (ObjRef inforef) -> case findObjThroughDictByRef inforef "/Registry" objs of-            Just (PdfText "Adobe") -> case findObjThroughDictByRef inforef "/Ordering" objs of-              Just (PdfText "Japan1") -> case findObjThroughDictByRef inforef "/Supplement" objs of-                Just (PdfNumber _) -> CIDmap "Adobe-Japan1"-                _ -> trace (show inforef) defaultCIDMap-              _ -> trace (show inforef) defaultCIDMap-            _ -> trace (show inforef) defaultCIDMap-          _ -> trace (show subref ++ " no /cidsysteminfoy. using Adobe-Japan1...") defaultCIDMap-        _ -> trace (show ref ++ " no array in /descendantfonts. using Adobe-Japan1...") defaultCIDMap-      _ -> trace (show x ++ " no /descendantfonts. using Adobe-Japan1...") defaultCIDMap +encoding :: Int -> [PDFObj] -> Encoding+encoding x objs = case subtype of+  Just (PdfName "/Type0") -> case encoding of+    Just (PdfName "/Identity-H") -> head $ cidSysInfo descendantFonts+    -- TODO" when /Encoding is stream of CMap+    Just (PdfName s) -> error $ "Unknown Encoding " ++ (show s) ++ " for a Type0 font. Check " ++ show x+    _ -> error $ "Something wrong with a Type0 font. Check " ++ (show x)+  Just (PdfName "/Type1") -> case encoding of+    Just (ObjRef r) -> case findObjFromDictWithRef r "/Differences" objs of+                     Just (PdfArray arr) -> charDiff arr+                     _ -> error "No /Differences"+    Just (PdfDict d) -> case findObjFromDict d "/Differences" of+                     Just (PdfArray arr) -> charDiff arr+                     _ -> error "No /Differences"+    Just (PdfName "/MacRomanEncoding") -> NullMap+    Just (PdfName "/MacExpertEncoding") -> NullMap+    Just (PdfName "/WinAnsiEncoding") -> NullMap+    -- TODO: FontFile (Type 1), FontFile2 (TrueType), FontFile3 (Other than Type1C)+    _ -> case findObjFromDict (fontDescriptor' x) "/FontFile3" of+           Just (ObjRef fontfile) ->+             CFF.encoding $ BSL.toStrict $ rawStreamByRef objs fontfile+           _ -> case findObjFromDict (fontDescriptor' x) "/FontFile" of+             Just (ObjRef fontfile) ->+               Type1.encoding $ BSL.toStrict $ rawStreamByRef objs fontfile+             _ -> NullMap+  -- TODO+  Just (PdfName "/Type2") -> NullMap+  Just (PdfName "/Type3") -> NullMap+  _ -> NullMap+   where-    defaultCIDMap = CIDmap "Adobe-Japan1"+    subtype = get "/Subtype"+    encoding = get "/Encoding"+    toUnicode = get "/ToUnicode"  -charMap :: [Obj] -> FontMap-charMap objs = FontMap $ fontmap objs 0-  where fontmap (PdfNumber x : PdfName n : xs) i = +    get s = findObjFromDictWithRef x s objs++    -- Should be an array (or ref to an array) containing refs+    descendantFonts :: [Obj]+    descendantFonts = case findObjFromDictWithRef x "/DescendantFonts" objs of+      Just (PdfArray dfrs) -> dfrs+      Just (ObjRef r) -> case findObjsByRef r objs of+        Just [(PdfArray dfrs)] -> dfrs+        _ -> error $ "Can not find /DescendantFonts entries in " ++ show r+      _ -> error $ "Can not find /DescendantFonts itself in " ++ show x ++    cidSysInfo :: [Obj] -> [Encoding]+    cidSysInfo [] = []+    cidSysInfo ((ObjRef r):rs) = (cidSysInfo' r):(cidSysInfo rs)+    cidSysInfo' dfr = case findObjFromDictWithRef dfr "/CIDSystemInfo" objs of+      Just (PdfDict dict) -> getCIDSystemInfo dict+      Just (ObjRef r) -> case findDictByRef r objs of+                           Just dict -> getCIDSystemInfo dict+                           _ -> error $ "Can not find /CIDSystemInfo entries in" ++ show r+      _ -> error $ "Can not find /CidSystemInfo itself " ++ show dfr++    fontDescriptor :: [Obj] -> [Dict]+    fontDescriptor [] = []+    fontDescriptor ((ObjRef r):rs) = (fontDescriptor' r):(fontDescriptor rs)+    fontDescriptor' :: Int -> Dict+    fontDescriptor' fdr = case findObjFromDictWithRef fdr "/FontDescriptor" objs of+      Just (ObjRef r) -> case findDictByRef r objs of+                           Just dict -> dict+                           _ -> error $ "No /FontDescriptor entries in " ++ show r+      _ -> error $ "Can not find /FontDescriptor itself in " ++ show fdr++    getCIDSystemInfo d =+      let registry = case findObjFromDict d "/Registry" of+                       Just (PdfText r) -> r+                       otherwise -> error "Can not find /Registry"+          ordering = case findObjFromDict d "/Ordering" of+                       Just (PdfText o) -> o+                       othserwise -> error "Can not find /Ordering"+          supplement = case findObjFromDict d "/Supplement" of+                         Just (PdfNumber s) -> s+                         otherwise -> error "Can not find /Supprement"+          cmap = registry ++ "-" ++ ordering -- ex. "Adobe-Japan1"+      in if cmap == "Adobe-Japan1"+         then CIDmap cmap+         else WithCharSet ""+++charDiff :: [Obj] -> Encoding+charDiff objs = Encoding $ charmap objs 0+  where charmap (PdfNumber x : PdfName n : xs) i =            if i < truncate x then -            (chr $ truncate x, n) : (fontmap xs $ incr x)+            (chr $ truncate x, n) : (charmap xs $ incr x)           else -            (chr $ i, n) : (fontmap xs $ i+1)-        fontmap (PdfName n : xs) i = (chr i, n) : (fontmap xs $ i+1)-        fontmap [] i               = []+            (chr $ i, n) : (charmap xs $ i+1)+        charmap (PdfName n : xs) i = (chr i, n) : (charmap xs $ i+1)+        charmap [] i               = []         incr x = (truncate x) + 1 -findCMap d os = cMap (fontObjs d os) os -cMap :: Dict -> [PDFObj] -> [(String, CMap)]-cMap dict objs = map pairwise dict+findCMap :: Dict -> [PDFObj] -> [(String, CMap)]+findCMap d objs = map pairwise (fontObjs d objs)   where     pairwise (PdfName n, ObjRef r) = (n, toUnicode r objs)     pairwise x = ("", [])  toUnicode :: Int -> [PDFObj] -> CMap-toUnicode x objs = case findObjThroughDictByRef x "/ToUnicode" objs of-  Just (ObjRef ref) -> parseCMap $ rawStreamByRef objs ref-  otherwise -> case findObjThroughDictByRef x "/Encoding" objs of-    Just (PdfName "/Identity-H") -> case findObjThroughDictByRef x "/ToUnicode" objs of-      Just (ObjRef ref) -> parseCMap $ rawStreamByRef objs ref-      otherwise -> []+toUnicode x objs =+  case findObjFromDictWithRef x "/ToUnicode" objs of+    Just (ObjRef ref) ->+      parseCMap $ rawStreamByRef objs ref+    otherwise -> noToUnicode x objs++noToUnicode x objs = +  case findObjFromDictWithRef x "/DescendantFonts" objs of+    Just (ObjRef ref) ->+      case findObjsByRef ref objs of+        Just [(PdfArray ((ObjRef subref):_))] ->+          case findObjFromDictWithRef subref "/FontDescriptor" objs of+            Just (ObjRef desc) ->+              case findObjFromDictWithRef desc "/FontFile2" objs of+                Just (ObjRef fontfile) ->+                  OpenType.cmap $ BSL.toStrict $ rawStreamByRef objs fontfile+                otherwise -> []+            otherwise -> []+        otherwise -> []     otherwise -> []+
src/PDF/Object.hs view
@@ -28,7 +28,7 @@ import qualified Data.ByteString.Lazy.Char8 as BSL import qualified Data.Text as T import Data.Text.Encoding (decodeUtf16BEWith)-import Data.Text.Encoding.Error (lenientDecode)+import Data.Text.Encoding.Error (strictDecode) import Numeric (readOct, readHex) import Data.ByteString.Builder (toLazyByteString, word16BE) @@ -40,7 +40,7 @@  import PDF.Definition -spaces = skipSpace+spaces = skipMany (comment <|> oneOf pdfspaces) --skipSpace oneOf = satisfy . inClass noneOf = satisfy . notInClass @@ -48,7 +48,7 @@  pdfObj :: Parser PDFBS pdfObj = do-  skipMany (comment <|> oneOf "\r\n")+  spaces -- skipMany (comment <|> oneOf pdfspaces)   objn <- many1 digit <* (spaces >> oneOf "0123456789" >> string " obj")   object <- manyTill anyChar (try $ string "endobj")   spaces@@ -56,6 +56,9 @@   skipMany startxref   return $ (read objn, BS.pack object) +pdfspaces :: [Char]+pdfspaces = map chr [0, 9, 10, 12, 13, 32]+ parsePDFObj :: PDFBS -> PDFObj parsePDFObj (n,pdfobject) = case parseOnly (spaces >> many1 (try pdfobj <|> try objother)) pdfobject of   Left  err -> (n,[PdfNull])@@ -103,43 +106,46 @@ pdfarray = PdfArray <$> (string "[" >> spaces *> manyTill pdfobj (try $ spaces >> string "]"))  pdfname :: Parser Obj-pdfname = PdfName . BS.unpack <$> (BS.append <$> string "/" <*> (BS.pack <$> (manyTill anyChar (try $ lookAhead $ oneOf "><][)( \n\r/")))) <* spaces+pdfname = PdfName . BS.unpack <$> (BS.append <$> string "/" <*> (BS.pack <$> manyTill anyChar (try $ lookAhead $ oneOf "><][)( \n\r/"))) <* spaces  pdfletters :: Parser Obj pdfletters = PdfText <$> parsePdfLetters  parsePdfLetters :: Parser String-parsePdfLetters = (concat <$> (char '(' *> manyTill (choice [try pdfutf, try pdfoctutf, pdfletter]) (try $ char ')')))-  where pdfletter = do-          str <- choice [ return <$> try (char '\\' >> oneOf "\\()")-                        , "\n" <$ try (string "\n")-                        , "\r" <$ try (string "\r")-                        , "\t" <$ try (string "\t")-                        , "\b" <$ try (string "\b")-                        , "\f" <$ try (string "\f")-                        , (++) <$> ("(" <$ char '(') <*> ((++")") . concat <$> manyTill pdfletter (try $ char ')'))-                        , return <$> (noneOf "\\")-                        ]-          return $ str+parsePdfLetters = concat <$> (char '(' *>+                               manyTill (choice [ try pdfutf+                                                , try pdfoctutf+                                                , pdfletter])+                               (try $ char ')'))+  where pdfletter =+          choice [ "(" <$ try (string "\\(")+                 , ")" <$ try (string "\\)")+                 , "\\" <$ try (string "\\\\")+                 , "\n" <$ try (string "\\n")+                 , "\r" <$ try (string "\\r")+                 , "\t" <$ try (string "\\t")+                 , "\b" <$ try (string "\\b")+                 , "\f" <$ try (string "\\f")+                 , octal <$> try (char '\\' *> count 3 (oneOf "01234567"))+                 , octal <$> try (char '\\' *> count 2 (oneOf "01234567"))+                 , octal . (:[]) <$> try (char '\\' *> oneOf "01234567")+                 , return <$> try (noneOf "\\")+                 , "" <$ string "\\"+                 ]++        octal = return . chr . fst . head . readOct+                 pdfutf :: Parser String         pdfutf = do -          str <- string "\254\255" *> manyTill anyChar (lookAhead $ string ")")-          return $ utf16be str+          str <- string "\254\255" *> manyTill pdfletter (lookAhead $ string ")")+          return $ utf16be $ concat str                  pdfoctutf :: Parser String         pdfoctutf = do-          string "\\376\\377" -          octstr <- manyTill (choice [ try (return . chr . fst . head . readOct <$> (char '\\' *> count 3 (oneOf "01234567")))-                                     , try ("\92" <$ string "\\\\")-                                     , return <$> noneOf "\\"-                                     ])-                    (lookAhead $ string ")")-          return $ utf16be $ concat octstr--        octToString [] = "????"-        octToString [(o,_)] = [chr o]--utf16be = T.unpack . decodeUtf16BEWith lenientDecode . BS.pack+          str <- string "\\376\\377" *> manyTill pdfletter (lookAhead $ string ")")+          return $ utf16be $ concat str+        +utf16be = T.unpack . decodeUtf16BEWith strictDecode . BS.pack  pdfstream :: Parser Obj pdfstream = PdfStream <$> stream@@ -185,13 +191,13 @@ pdfobj = choice [ try rrefs <* spaces                 , try pdfname <* spaces                 , try pdfnumber <* spaces-                , try pdfhex <* spaces+                , try pdfhex <* spaces -- Hexadecimal String                 , try pdfbool <* spaces                 , try pdfnull <* spaces                 , try pdfarray <* spaces                 , try pdfdictionary <* spaces                 , {-# SCC pdfstream #-} try pdfstream <* spaces-                , pdfletters <* spaces+                , pdfletters <* spaces -- Literal String                 ]  rrefs :: Parser Obj
+ src/PDF/OpenType.hs view
@@ -0,0 +1,191 @@+{-# LANGUAGE OverloadedStrings #-}++module PDF.OpenType (cmap) where++import Numeric (readInt)+import Data.Char (chr)++import Data.Word+import Data.Bits++import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BSC++import Data.Attoparsec.ByteString (Parser, parseOnly, word8, string)+import qualified Data.Attoparsec.ByteString as AP+import Data.Attoparsec.Combinator++import Control.Applicative++import Debug.Trace++import PDF.Definition++data Table = Table String Integer Integer+  deriving (Show)++data EncRecord = EncRecord Integer Integer Integer+  deriving (Show)++{-+test f = do+  c <- BS.readFile f+  let bs = cmap c+  return bs+-}++cmap :: ByteString -> CMap+cmap c = case parseOnly (offsetTable >>= tableRecords) c of+  Right b -> let b' = (takeCmap b)+             in case parseOnly cmapEncRecords b' of+                  Right records -> concatMap (subtable b') records+                  Left e -> error e+  Left e -> error e+  where+    offsetTable = do+      sfntVersion+      n <- numTables+      searchRange >> entrySelector >> rangeShift+      return $ fromIntegral n++    takeCmap ((Table "cmap" start end):_)+      = BS.take (fromInteger end) $ BS.drop (fromInteger start) c+    takeCmap (_:rest) = takeCmap rest+    takeCmap [] = error "no cmap"++    cmapEncRecords =+      cmapVersion >>+      numEncRecords >>=+      (encodeRecords . fromIntegral)+      +subtable c (EncRecord pid eid offset) =+  let body = BS.drop (fromInteger offset) c+      format = fromBytes $ BS.take 2 body+  in case parseOnly (parserByFormat format) body of+       Right b -> b+       Left e -> error e++parserByFormat :: Integer -> Parser CMap+parserByFormat 14 = do+  format <- getUint16+  length <- getUint32+  rest <- (AP.take . fromInteger) length+  return $ []++parserByFormat 12 = do+  format <- getUint16+  reserved <- getUint16+  length <- getUint32+  language <- getUint32+  numGroups <- fromInteger <$> getUint32+  seqMapGroups <- count (numGroups) seqMapGroup+  return $ concat seqMapGroups++  where+    seqMapGroup :: Parser CMap+    seqMapGroup = do+      startCharCode <- fromInteger <$> getUint32+      endCharCode <- fromInteger <$> getUint32+      startGlyphID  <- fromInteger <$> getUint32+      return $ toCmap startGlyphID [startCharCode .. endCharCode]+    toCmap gid range = zip [gid ..] $ map ((:[]).chr) range++parserByFormat 4 = do+  format <- getUint16+  length <- fromInteger <$> getUint16+  language <- getUint16+  segCount2 <- fromInteger <$> getUint16+  searchRange <- getUint16+  entrySlector <- getUint16+  rangeShift <-getUint16+  let segCount = segCount2 `div` 2+  endCodes <- count segCount $ fromInteger <$> getUint16+  reservedPad <- contiguous [0x00, 0x00]+  startCodes <- count segCount $ fromInteger <$> getUint16+  idDelta <- count segCount $ fromInteger <$> getUint16+  rest <- AP.take (length - 16 - segCount2 * 3)+  return $ concat $ getGlyphIDs startCodes endCodes idDelta rest++  where+    getGlyphIDs [] _ _ _ = []+    getGlyphIDs (s:ss) (e:ee) (d:dd) rest =+      -- take 2bytes from idRangeOffset[uint16]+      let rest' = BS.drop 2 rest+      in (getGlyphID s e d rest):(getGlyphIDs ss ee dd rest')++    getGlyphID :: Int -> Int -> Int -> ByteString+                -> CMap+    getGlyphID start end delta rest =+      let offset = fromInteger $ fromBytes $ BS.take 2 rest+      in +        if offset == 0+        then zip (map (+delta) [start .. end])+                 (map ((:[]).chr) [start .. end])+        else zip (map (getRangeOffsetGlyphID start offset rest) [start .. end])+                 (map ((:[]).chr) [start .. end])+             +    getRangeOffsetGlyphID s o bytestring c =+      fromInteger $ fromBytes $ BS.take 2 $ BS.drop (o + 2 * (c - s)) bytestring++parserByFormat _ = return []+++-- main tables++sfntVersion :: Parser ByteString+sfntVersion = contiguous [0x00, 0x01, 0x00, 0x00] <|> string "OTTO"++numTables =  getUint16+searchRange = getUint16+entrySelector = getUint16+rangeShift = getUint16++tableRecords n = count n tableRecord++tableRecord :: Parser Table+tableRecord = do+  tableTag <- BSC.unpack <$> AP.take 4+  checkSum <- getUint32+  offset <- getUint32+  length <- getUint32+  return $ Table tableTag offset length++getUint16 :: Parser Integer+getUint16 = fromBytes <$> AP.take 2++getUint32 :: Parser Integer+getUint32 = fromBytes <$> AP.take 4++tableTag :: Parser String+tableTag = BSC.unpack <$> AP.take 4++-- subtables++cmapVersion = getUint16+numEncRecords = getUint16++encodeRecords n = count n encodeRecord++encodeRecord :: Parser EncRecord+encodeRecord = do+  platformID <- getUint16+  encodingID <- getUint16+  offset <- getUint32+  return $ EncRecord platformID encodingID offset+++fromBytes :: ByteString -> Integer+fromBytes = BS.foldl' f 0+  where+    f a b = a `shiftL` 8 .|. fromIntegral b++contiguous :: [Word8] -> Parser ByteString+contiguous bs = BS.pack <$> contiguous' bs+  where+    contiguous' (b:[]) = (:[]) <$> word8 b+    contiguous' (b:bs) = do+      byte <- word8 b+      rest <- contiguous' bs+      return $ (byte:rest)+
src/PDF/Outlines.hs view
@@ -53,12 +53,14 @@     Nothing -> error "No top level outline entry."   firstdict <- case findObjsByRef firstref objs of     Just [PdfDict d] -> return d+    Just s -> error $ "Unknown Object: " ++ show s     Nothing -> error $ "No Object with Ref " ++ show firstref   return $ gatherOutlines firstdict objs  gatherChildren dict objs = case findFirst dict of   Just r -> case findObjsByRef r objs of     Just [PdfDict d] -> gatherOutlines d objs+    Just s -> error $ "Unknown Object at " ++ show r     Nothing -> error $ "No Object with Ref " ++ show r   Nothing -> PDFOutlinesNE @@ -70,14 +72,16 @@                                                             , text = findTitle dict objs ++ "\n"                                                             , subs = c}                                            : [gatherOutlines d objs])+      Just s -> error $ "Unknown Object at " ++ show r       Nothing -> error $ "No Object with Ref " ++ show r     Nothing -> PDFOutlinesEntry { dest = head $ findDest dict                                 , text = findTitle dict objs ++ "\n"-                                , subs = PDFOutlinesNE}+                                , subs = c}  outlines :: Dict -> Int outlines dict = case find isOutlinesRef dict of   Just (_, ObjRef x) -> x+  Just s -> error $ "Unknown Object: " ++ show s   Nothing            -> error "There seems no /Outlines in the root"   where     isOutlinesRef (PdfName "/Outlines", ObjRef x) = True@@ -95,30 +99,35 @@     Nothing   -> error "Something wrong..."   case findObjsByRef outlineref objs of     Just [PdfDict d] -> return d+    Just s -> error $ "Unknown Object: " ++ show s     Nothing -> error "Could not get outlines object"  findTitle dict objs = -  case findObjThroughDict dict "/Title" of+  case findObjFromDict dict "/Title" of     Just (PdfText s) -> case parseOnly parsePdfLetters (BS.pack s) of       Right t -> t       Left err -> s     Just (ObjRef r) -> case findObjsByRef r objs of       Just [PdfText s] -> s+      Just s -> error $ "Unknown Object at " ++ show r       Nothing -> error $ "No title object in " ++ show r     Just x -> show x     Nothing -> error "No title object."  findDest dict = -  case findObjThroughDict dict "/Dest" of+  case findObjFromDict dict "/Dest" of     Just (PdfArray a) -> parseRefsArray a+    Just s -> error $ "Unknown Object: " ++ show s     Nothing -> error "No destination object."  findNext dict = -  case findObjThroughDict dict "/Next" of+  case findObjFromDict dict "/Next" of     Just (ObjRef x) -> Just x+    Just s -> error $ "Unknown Object: " ++ show s     Nothing -> Nothing  findFirst dict =-  case findObjThroughDict dict "/First" of+  case findObjFromDict dict "/First" of     Just (ObjRef x) -> Just x+    Just s -> error $ "Unknown Object: " ++ show s     Nothing -> Nothing
src/PDF/PDFIO.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ {-| Module      : PDF.PDFIO Description : IO utilities for hpdft@@ -13,19 +15,25 @@                  , getPDFObjFromFile                  , getRootRef                  , getRootObj+                 , getStream                  , getTrailer                  , getInfo                  ) where  import PDF.Definition-import PDF.DocumentStructure (findObjs, findObjsByRef, findDictByRef, findObjThroughDict, rootRef, findTrailer, expandObjStm)+import PDF.DocumentStructure+  (rawStream, rawStreamByRef, findObjs, findObjsByRef,+   findDictByRef, findObjFromDict, rootRef,+   findTrailer, expandObjStm) import PDF.Object (parsePDFObj)  import Debug.Trace  import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Builder as B+import qualified Data.ByteString.Lazy.Char8 as BSL --- | Get PDF objects as a whole bytestring. Use 'getPDFObjFromFile' instead if there's no reason to see a raw bytestring. +-- | Get PDF objects as a whole bytestring. Use `getPDFObjFromFile` instead if there's no reason to see a raw bytestring.   getPDFBSFromFile :: FilePath -> IO [PDFBS] getPDFBSFromFile f = do@@ -41,7 +49,7 @@   let obj = expandObjStm $ map parsePDFObj $ findObjs c   return obj --- | Get a PDF object from a whole 'PDFObj' by specifying 'ref :: Int'+-- | Get a PDF object from a whole 'PDFObj' by specifying `ref :: Int`  getObjectByRef :: Int -> [PDFObj] -> IO [Obj] getObjectByRef ref pdfobjs = do@@ -49,8 +57,19 @@     Just os -> return os     Nothing -> error $ "No Object with Ref " ++ show ref --- | The reference number of /Root in 'filename'.+-- | Get a PDF stream from a whole 'PDFObj' by specifying `ref :: Int` +getStream :: Bool -> [Obj] -> IO BSL.ByteString+getStream hex obj = return $ showBSL hex $ rawStream obj++showBSL hex s =+  let strm' = (B.toLazyByteString . B.lazyByteStringHex) s+  in if hex+     then if BSL.length strm' > 256 then BSL.concat [BSL.take 256 strm', "...(omit)"] else strm'+     else s++-- | The reference number of /Root in `filename`.+ getRootRef :: FilePath -> IO Int getRootRef filename = do   c <- BS.readFile filename@@ -59,7 +78,7 @@     Just i -> return i     Nothing -> error "Could not find rood object"     --- | The /Root object in 'filename'.+-- | The /Root object in `filename`.  getRootObj :: FilePath -> IO [Obj] getRootObj filename = do@@ -69,20 +88,20 @@     Just os -> return os     Nothing -> error "Could not get root object" --- | The trailer of 'filename'.+-- | The trailer of `filename`.  getTrailer :: FilePath -> IO Dict getTrailer filename = do   c <- BS.readFile filename   return $ findTrailer c --- | /Info of 'filename'.+-- | /Info of `filename`.  getInfo :: FilePath -> IO Dict getInfo filename = do   d <- getTrailer filename   objs <- getPDFObjFromFile filename-  let inforef = case findObjThroughDict d "/Info" of+  let inforef = case findObjFromDict d "/Info" of                   Just (ObjRef ref) -> ref                   Just _ -> error "There seems to be no Info"                   Nothing -> error "There seems to be no Info"
+ src/PDF/Type1.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE OverloadedStrings #-}++module PDF.Type1 (encoding) where++import Numeric (readInt)+import Data.Char (chr)++import Data.Word+import Data.Bits++import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BSC+import qualified Data.ByteString.Lazy as BSL++import Data.Attoparsec.ByteString.Char8+import qualified Data.Attoparsec.ByteString as AP+import Data.Attoparsec.Combinator++import Control.Applicative++import Debug.Trace++import PDF.Definition++test f = do+  c <- BS.readFile f+  return $ encoding c++spaces = skipMany (oneOf " \n\r") --skipSpace+oneOf = satisfy . inClass+noneOf = satisfy . notInClass++encoding :: ByteString -> Encoding+encoding c = case parseOnly encodingArray c of+  Right ss -> Encoding ss+  Left e -> error "Can not find /Encoding in the Type1 Font"++encodingArray :: Parser [(Char,String)]+encodingArray = do+  manyTill anyChar (try $ lookAhead $ string "/Encoding")+  string "/Encoding"+  spaces+  choice [ [] <$ (string "StandardEncoding"+                   >> spaces >> string "def")+         , (skipFor >> spaces +             *> manyTill specialEncodings+             (try $ string "readonly" <|> string "def"))+         ]+    where+      skipFor = manyTill anyChar (try $ string "for")++specialEncodings :: Parser (Char, String)+specialEncodings = do+  spaces +  (,) <$> (spaces >> string "dup" >> spaces *> index)+    <*> (spaces >> charName <* spaces)+  where+    index = (chr . read) <$> many1 digit+    charName = manyTill anyChar (try $ (spaces >> string "put"))+