packages feed

hpdft-0.4.6.0: src/PDF/Error.hs

{-# LANGUAGE OverloadedStrings #-}

{-|
Module      : PDF.Error
Description : Error and warning types for hpdft
License     : MIT
Maintainer  : k16.shikano@gmail.com

Total-function error model for the 0.3 API. Parsing and extraction
functions return 'PdfResult' instead of calling 'error', so that a
broken PDF yields a diagnosis (and possibly partial output) rather
than a crash.

Migration plan (tracked per module):

* Phase A1: xref\/trailer\/object loading ('PDF.DocumentStructure', 'PDF.Object')
* Phase A2: content streams and fonts ('PDF.ContentStream', 'PDF.Cmap', 'PDF.CFF', ...)
* Phase A3: IO layer and CLI ('PDF.PDFIO', @hpdft.hs@)

@example
import PDF.Document (openDocument)
import PDF.Error (PdfError(..), PdfResult)

handle :: IO (PdfResult Text) -> IO ()
handle io = do
  result <- io
  case result of
    Left (MissingObject n) -> putStrLn $ "object " ++ show n ++ " missing"
    Left err               -> print err
    Right txt              -> putStr txt
-}

module PDF.Error
  ( PdfError(..)
  , PdfResult
  , PdfWarning(..)
  , renderPdfWarning
  , note
  ) where

import qualified Data.ByteString.Char8 as BS

-- | Structured errors. Constructors carry enough context to locate
-- the problem in the file without a debugger.
data PdfError
  = ParseError String BS.ByteString
    -- ^ Parser failure: message and a short excerpt of the input.
  | BrokenXref String
    -- ^ Cross-reference table\/stream could not be interpreted.
  | MissingObject Int
    -- ^ An object referenced as @n 0 R@ is not present.
  | MissingKey String String
    -- ^ Required dictionary key is absent: key name and context.
  | UnsupportedFeature String
    -- ^ Valid PDF that hpdft cannot handle yet (filter, encoding, ...).
  | DecryptionError String
    -- ^ Bad password, unsupported handler, or malformed \/Encrypt.
  | FontError Int String
    -- ^ Font-related failure: object number and description.
  deriving (Show, Eq)

-- | Result type for hpdft parsing and extraction. @Left@ carries a 'PdfError';
-- @Right@ carries success. Compose with do-notation or '>>='.
type PdfResult a = Either PdfError a

-- | Non-fatal diagnoses. Extraction continues; the caller may log these.
data PdfWarning
  = UnknownOperator String
    -- ^ Content-stream operator hpdft does not interpret.
  | MissingToUnicode Int
    -- ^ Font without usable \/ToUnicode; output may be garbled.
  | SubstitutedEncoding Int String
    -- ^ Fallback encoding used for a font.
  | UnmappedCid Int
    -- ^ CID not found in Adobe-Japan1-6; bracket placeholder emitted.
  deriving (Show, Eq)

renderPdfWarning :: PdfWarning -> String
renderPdfWarning (UnknownOperator op) = "unknown content-stream operator: " ++ op
renderPdfWarning (MissingToUnicode n) =
  "font object " ++ show n ++ " has no usable /ToUnicode map"
renderPdfWarning (SubstitutedEncoding n enc) =
  "font object " ++ show n ++ ": using fallback encoding " ++ enc
renderPdfWarning (UnmappedCid cid) =
  "unmapped CID " ++ show cid ++ " (Adobe-Japan1-6)"

-- | Annotate a 'Maybe' with an error.
note :: PdfError -> Maybe a -> PdfResult a
note e Nothing  = Left e
note _ (Just a) = Right a