packages feed

pdf-toolbox-content-0.1.1: test/Test/UnicodeCMap.hs

{-# LANGUAGE OverloadedStrings #-}

module Test.UnicodeCMap
(
  spec,
)
where

import Pdf.Content.UnicodeCMap

import Data.Either
import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.ByteString as ByteString
import Test.Hspec

spec :: Spec
spec = describe "UnicodeCMap" $ do
  parseUnicodeCMapSpec
  unicodeCMapDecodeGlyphSpec
  unicodeCMapNextGlyphSpec

parseUnicodeCMapSpec :: Spec
parseUnicodeCMapSpec = describe "parseUnicodeCMap" $ do
  it "should parse trivial cmap" $ do
    let input = ByteString.concat
          [ "0 begincodespacerange\n"
          , "0 beginbfchar\n"
          , "0 beginbfrange\n"
          ]
        res = parseUnicodeCMap input
    res `shouldSatisfy` isRight

  it "should parse codespace ranges" $ do
    let input = ByteString.concat
          [ "1 begincodespacerange\n"
          , "<0000> <FFFF>\n"
          , "0 beginbfchar\n"
          , "0 beginbfrange\n"
          ]
        res = parseUnicodeCMap input
    fmap unicodeCMapCodeRanges res `shouldBe`
      Right [ (ByteString.pack [0, 0], ByteString.pack [255, 255])]

  it "should parse chars" $ do
    let input = ByteString.concat
          [ "0 begincodespacerange\n"
          , "1 beginbfchar\n"
          , "<3A51> <D840DC3E>\n"
          , "0 beginbfrange\n"
          ]
        res = parseUnicodeCMap input
    fmap unicodeCMapChars res `shouldBe`
      Right (Map.fromList [(14929,"\131134")])

  it "should parse multiple chars" $ do
    let input = ByteString.concat
          [ "0 begincodespacerange\n"
          , "1 beginbfchar\n"
          , "<25> <00660066>\n"
          , "endbfchar\n"
          , "1 beginbfchar\n"
          , "<26> <0066006c>\n"
          , "endbfchar\n"
          , "0 beginbfrange\n"
          ]
        res = parseUnicodeCMap input
    fmap unicodeCMapChars res `shouldBe`
      Right (Map.fromList [(37,"ff"), (38, "fl")])

  it "should parse ranges" $ do
    let input = ByteString.concat
          [ "0 begincodespacerange\n"
          , "0 beginbfchar\n"
          , "1 beginbfrange\n"
          , "<0000> <005E> <0020>\n"
          ]
        res = parseUnicodeCMap input
    fmap unicodeCMapRanges res `shouldBe`
      Right [(0,94,' ')]

  it "should parse multiple ranges" $ do
    let input = ByteString.concat
          [ "0 begincodespacerange\n"
          , "0 beginbfchar\n"
          , "1 beginbfrange\n"
          , "<21> <21> <0045>\n"
          , "endbfrange\n"
          , "1 beginbfrange\n"
          , "<22> <22> <0073>\n"
          , "endbfrange\n"
          ]
        res = parseUnicodeCMap input
    fmap (List.sort . unicodeCMapRanges) res `shouldBe`
      Right (List.sort [(33,33,'E'), (34, 34, 's')])

  it "should parse array ranges into char map" $ do
    let input = ByteString.concat
          [ "0 begincodespacerange\n"
          , "0 beginbfchar\n"
          , "1 beginbfrange\n"
          , "<005F> <0061> [<00660066> <00660069> <00660066006C>]\n"
          ]
        res = parseUnicodeCMap input
    fmap unicodeCMapChars res `shouldBe`
      Right (Map.fromList [(95,"ff"),(96,"fi"),(97,"ffl")])

  it "should allow spaces in hex numbers" $ do
    let input = ByteString.concat
          [ "1 begincodespacerange\n"
          , "<0000> <FF FF>\n"
          , "0 beginbfchar\n"
          , "0 beginbfrange\n"
          ]
        res = parseUnicodeCMap input
    fmap unicodeCMapCodeRanges res `shouldBe`
      Right [ (ByteString.pack [0, 0], ByteString.pack [255, 255])]

unicodeCMapDecodeGlyphSpec :: Spec
unicodeCMapDecodeGlyphSpec = describe "unicodeCMapDecodeGlyph" $ do
  it "should take glyph from char map if possible" $ do
    let cmap = UnicodeCMap [] charMap []
        charMap = Map.fromList [(42, "hello")]

    let res = unicodeCMapDecodeGlyph cmap 42
    res `shouldBe` Just "hello"

  it "should be able to decode the first glyph from a range" $ do
    let cmap = UnicodeCMap [] mempty [(10, 15, '1')]

    let res = unicodeCMapDecodeGlyph cmap 10
    res `shouldBe` Just "1"

  it "should be able to decode the last glyph from a range" $ do
    let cmap = UnicodeCMap [] mempty [(10, 15, '1')]

    let res = unicodeCMapDecodeGlyph cmap 15
    res `shouldBe` Just "6"

  it "should be able to decode a glyph from a middle of a range" $ do
    let cmap = UnicodeCMap [] mempty [(10, 15, '1')]

    let res = unicodeCMapDecodeGlyph cmap 11
    res `shouldBe` Just "2"

  it "should not decode a glyph before a range" $ do
    let cmap = UnicodeCMap [] mempty [(10, 15, '1')]

    let res = unicodeCMapDecodeGlyph cmap 9
    res `shouldBe` Nothing

  it "should not decode a glyph after a range" $ do
    let cmap = UnicodeCMap [] mempty [(10, 15, '1')]

    let res = unicodeCMapDecodeGlyph cmap 16
    res `shouldBe` Nothing

unicodeCMapNextGlyphSpec :: Spec
unicodeCMapNextGlyphSpec = describe "unicodeCMapNextGlyph" $ do
  it "correctly handles multibyte ranges" $ do
    let cmap = UnicodeCMap [("\0\0", "\1\1")] mempty []
    let Just (code, rest) = unicodeCMapNextGlyph cmap "\1\0rest"
    rest `shouldBe` rest
    code `shouldBe` 256