From 19f8dd63445ef04ae492bd6e4a293f8f04090b27 Mon Sep 17 00:00:00 2001 From: Tissevert Date: Sat, 18 May 2019 11:04:02 +0200 Subject: [PATCH] First working version, no help and can only set the text rendering mode to 'Fill' --- .gitignore | 2 ++ CHANGELOG.md | 5 ++++ LICENSE | 30 +++++++++++++++++++++++ reveal.cabal | 30 +++++++++++++++++++++++ src/Main.hs | 51 ++++++++++++++++++++++++++++++++++++++++ src/PDF/TextRendering.hs | 29 +++++++++++++++++++++++ 6 files changed, 147 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 reveal.cabal create mode 100644 src/Main.hs create mode 100644 src/PDF/TextRendering.hs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ebadcab --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +dist*/ +.ghc.environment.* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3159518 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Revision history for reveal + +## 0.1.0.0 -- 2019-05-18 + +* First version. Released on an unsuspecting world. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..42f34d0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2019, Tissevert + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Tissevert nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/reveal.cabal b/reveal.cabal new file mode 100644 index 0000000..2753954 --- /dev/null +++ b/reveal.cabal @@ -0,0 +1,30 @@ +cabal-version: >=1.10 +-- Initial package description 'reveal.cabal' generated by 'cabal init'. +-- For further documentation, see http://haskell.org/cabal/users-guide/ + +name: reveal +version: 0.1.0.0 +synopsis: A simple program to change the text rendering mode of a PDF +-- description: +homepage: https://git.marvid.fr/Tissevert/reveal +-- bug-reports: +license: BSD3 +license-file: LICENSE +author: Tissevert +maintainer: tissevert+devel@marvid.fr +-- copyright: +category: Data +build-type: Simple +extra-source-files: CHANGELOG.md + +executable reveal + main-is: Main.hs + other-modules: PDF.TextRendering + -- other-extensions: + build-depends: base >=4.12 && <4.13 + , bytestring + , containers + , Hufflepdf + , zlib + hs-source-dirs: src + default-language: Haskell2010 diff --git a/src/Main.hs b/src/Main.hs new file mode 100644 index 0000000..2a23ba6 --- /dev/null +++ b/src/Main.hs @@ -0,0 +1,51 @@ +{-# LANGUAGE NamedFieldPuns #-} +module Main where + +import Codec.Compression.Zlib (compress, decompress) +import Data.ByteString.Lazy.Char8 (ByteString) +import qualified Data.ByteString.Lazy.Char8 as BS ( + length, lines, readFile, unlines, writeFile + ) +import qualified Data.Map as Map (insert, lookup) +import PDF (Document(..), parseDocument, render) +import PDF.Object (Content(..), DirectObject(..), Object(..), Name(..), Number(..)) +import PDF.TextRendering (TextRendering(..), update) +import System.Environment (getArgs) +import System.IO (hPutStrLn, stderr) + +revealFlateEncodedText :: ByteString -> ByteString +revealFlateEncodedText = compress . revealText . decompress + where + revealText = BS.unlines . fmap fill . BS.lines + fill = update $ const Fill + +revealObject :: Object -> Object +revealObject obj@(Stream {header, streamContent}) = + case Map.lookup (Name "Filter") header of + Just (NameObject (Name "FlateDecode")) -> + let newStreamContent = revealFlateEncodedText streamContent in + let newLength = Number . fromIntegral $ BS.length newStreamContent in + Stream { + header = Map.insert (Name "Length") (NumberObject newLength) header + , streamContent = newStreamContent + } + _ -> obj +revealObject obj = obj + +revealContent :: Content -> Content +revealContent content = content { + objects = revealObject <$> (objects content) + } + +reveal :: Document -> Document +reveal document = document { + contents = revealContent <$> (contents document) + } + +main :: IO () +main = do + [inputPath, outputPath] <- getArgs + result <- parseDocument <$> BS.readFile inputPath + case result of + Left parseError -> hPutStrLn stderr $ show parseError + Right doc -> BS.writeFile outputPath . render $ reveal doc diff --git a/src/PDF/TextRendering.hs b/src/PDF/TextRendering.hs new file mode 100644 index 0000000..e61fbc0 --- /dev/null +++ b/src/PDF/TextRendering.hs @@ -0,0 +1,29 @@ +{-# LANGUAGE OverloadedStrings #-} +module PDF.TextRendering ( + TextRendering(..) + , update + ) where + +import Data.ByteString.Lazy.Char8 (ByteString) +import qualified Data.ByteString.Lazy.Char8 as BS (pack, readInt, unwords, words) + +data TextRendering = + Fill + | Stroke + | FillStroke + | Invisible + | FillAddPath + | StrokeAddPath + | FillStrokeAddPath + | AddPath + deriving (Enum, Show) + +update :: (TextRendering -> TextRendering) -> ByteString -> ByteString +update f = BS.unwords . changeTextRendering . BS.words + where + changeTextRendering :: [ByteString] -> [ByteString] + changeTextRendering l@[value, "Tr"] = + case BS.readInt value of + Just (n, _) -> [BS.pack . show . fromEnum . f $ toEnum n, "Tr"] + _ -> l + changeTextRendering l = l