aboutsummaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorGregor Kleen <pngwjpgh@users.noreply.github.com>2017-01-23 16:09:05 +0100
committerGregor Kleen <pngwjpgh@users.noreply.github.com>2017-01-23 16:09:05 +0100
commit64b6ead0d1e157701f8569743eda496bc71b8351 (patch)
treef7ae09e9ec950e91cacb1923aa54b96d59c9a8f3 /server
parente95dac748371afcad3ffddf5c98e5fcb0a8302b7 (diff)
downloadthermoprint-64b6ead0d1e157701f8569743eda496bc71b8351.tar
thermoprint-64b6ead0d1e157701f8569743eda496bc71b8351.tar.gz
thermoprint-64b6ead0d1e157701f8569743eda496bc71b8351.tar.bz2
thermoprint-64b6ead0d1e157701f8569743eda496bc71b8351.tar.xz
thermoprint-64b6ead0d1e157701f8569743eda496bc71b8351.zip
Add support for ESC/POS text decoration
Diffstat (limited to 'server')
-rw-r--r--server/src/Thermoprint/Server/Printer/Generic.hs30
-rw-r--r--server/test/Thermoprint/Server/Printer/GenericSpec.hs69
-rw-r--r--server/thermoprint-server.cabal3
-rw-r--r--server/thermoprint-server.nix8
4 files changed, 103 insertions, 7 deletions
diff --git a/server/src/Thermoprint/Server/Printer/Generic.hs b/server/src/Thermoprint/Server/Printer/Generic.hs
index f431e4f..ce818ee 100644
--- a/server/src/Thermoprint/Server/Printer/Generic.hs
+++ b/server/src/Thermoprint/Server/Printer/Generic.hs
@@ -6,6 +6,7 @@
6 6
7module Thermoprint.Server.Printer.Generic 7module Thermoprint.Server.Printer.Generic
8 ( genericPrint 8 ( genericPrint
9 , mkPrintout
9 ) where 10 ) where
10 11
11import Thermoprint.Printout 12import Thermoprint.Printout
@@ -31,6 +32,7 @@ import Data.Encoding
31import Data.Encoding.CP437 32import Data.Encoding.CP437
32import Data.Binary.Put 33import Data.Binary.Put
33import Data.Word 34import Data.Word
35import Data.Bits
34 36
35import Control.Monad 37import Control.Monad
36import Control.Monad.Reader 38import Control.Monad.Reader
@@ -47,6 +49,8 @@ import Control.Monad.Catch
47import Data.Foldable 49import Data.Foldable
48 50
49import Data.List (genericReplicate, genericLength, intercalate) 51import Data.List (genericReplicate, genericLength, intercalate)
52import Data.Set (Set)
53import qualified Data.Set as Set
50 54
51import Data.Monoid 55import Data.Monoid
52 56
@@ -69,7 +73,10 @@ genericPrint' path = flip catches handlers . withFile path . print
69 ] 73 ]
70 print printout handle = $(logDebug) (T.pack $ show printout') >> liftIO (slowPut handle printout' >> return Nothing) 74 print printout handle = $(logDebug) (T.pack $ show printout') >> liftIO (slowPut handle printout' >> return Nothing)
71 where 75 where
72 printout' = runPut $ initialize >> render printout >> finalize 76 printout' = mkPrintout printout
77
78mkPrintout :: Printout -> Lazy.ByteString
79mkPrintout printout = runPut $ initialize >> render printout >> finalize
73 80
74slowPut :: Handle -> Lazy.ByteString -> IO () 81slowPut :: Handle -> Lazy.ByteString -> IO ()
75slowPut h = slowPut' . LBS.split (LBS.last newl) 82slowPut h = slowPut' . LBS.split (LBS.last newl)
@@ -128,6 +135,7 @@ data Doc = Doc
128 , space :: Integer 135 , space :: Integer
129 , remainingSpace :: Integer 136 , remainingSpace :: Integer
130 , overflows :: Integer 137 , overflows :: Integer
138 , currentMarkup :: Set MarkupMode
131 } 139 }
132 140
133initDoc :: Integer -> Doc 141initDoc :: Integer -> Doc
@@ -136,6 +144,7 @@ initDoc space = Doc { lines = Seq.empty
136 , space = space 144 , space = space
137 , remainingSpace = space 145 , remainingSpace = space
138 , overflows = 0 146 , overflows = 0
147 , currentMarkup = Set.empty
139 } 148 }
140 149
141breakLine :: Doc -> Doc 150breakLine :: Doc -> Doc
@@ -151,6 +160,19 @@ renderDoc Doc{..} = intersperse' (newls' 1) id lines'
151 | remainingSpace == space = lines 160 | remainingSpace == space = lines
152 | otherwise = lines |> currentLine 161 | otherwise = lines |> currentLine
153 162
163escSequence' :: [Word8] -> State Doc ()
164escSequence' s = modify (\(doc@(Doc{..})) -> doc { currentLine = currentLine >> escSequence s })
165
166setMarkup :: Set MarkupMode -> State Doc ()
167setMarkup m = escSequence' [33, byte m] >> modify (\doc -> doc { currentMarkup = m })
168 where
169 byte = foldr (.|.) zeroBits . Set.map bitMask
170 bitMask Bold = 8
171 bitMask DoubleHeight = 16
172 bitMask DoubleWidth = 32
173 bitMask Underline = 128
174 bitMask _ = zeroBits
175
154renderBlock :: Block -> State Doc () 176renderBlock :: Block -> State Doc ()
155renderBlock (VSpace n) = sequence_ . genericReplicate n $ modify' breakLine 177renderBlock (VSpace n) = sequence_ . genericReplicate n $ modify' breakLine
156renderBlock (NewlSep xs) = intersperse' (modify' breakLine) renderBlock xs 178renderBlock (NewlSep xs) = intersperse' (modify' breakLine) renderBlock xs
@@ -165,6 +187,11 @@ renderLine (HSpace n) = modify' insertSpace
165 } 187 }
166 | remainingSpace == n = breakLine doc 188 | remainingSpace == n = breakLine doc
167 | otherwise = breakLine $ doc { overflows = overflows + 1 } 189 | otherwise = breakLine $ doc { overflows = overflows + 1 }
190renderLine (Markup ms l)
191 | Set.null ms = renderLine l
192 | otherwise = do
193 prevMarkup <- gets currentMarkup
194 setMarkup (prevMarkup <> ms) >> renderLine l >> setMarkup prevMarkup
168renderLine (JuxtaPos xs) = mapM_ renderLine xs 195renderLine (JuxtaPos xs) = mapM_ renderLine xs
169renderLine (cotext . Line -> word) = modify' $ insertWord 196renderLine (cotext . Line -> word) = modify' $ insertWord
170 where 197 where
@@ -192,4 +219,3 @@ renderLine (cotext . Line -> word) = modify' $ insertWord
192 } 219 }
193 | otherwise = doc 220 | otherwise = doc
194 encode'' = encode' . TL.unpack 221 encode'' = encode' . TL.unpack
195
diff --git a/server/test/Thermoprint/Server/Printer/GenericSpec.hs b/server/test/Thermoprint/Server/Printer/GenericSpec.hs
new file mode 100644
index 0000000..80096b0
--- /dev/null
+++ b/server/test/Thermoprint/Server/Printer/GenericSpec.hs
@@ -0,0 +1,69 @@
1{-# LANGUAGE OverloadedStrings, OverloadedLists #-}
2{-# LANGUAGE StandaloneDeriving #-}
3{-# LANGUAGE ViewPatterns #-}
4
5module Thermoprint.Server.Printer.GenericSpec (spec) where
6
7import Test.Hspec
8import Test.Hspec.QuickCheck (prop)
9import Test.QuickCheck (Property, Discard(..), property)
10import Test.QuickCheck.Instances
11
12import Thermoprint.Server.Printer.Generic
13import Thermoprint.Printout
14
15import Data.Text (Text)
16import qualified Data.Text.Lazy as TL (pack)
17
18import qualified Data.ByteString.Lazy as L (ByteString)
19import qualified Data.ByteString.Lazy as L.BS
20
21import Data.String (IsString(..))
22
23import Control.Monad (zipWithM_, join)
24import Data.Monoid ((<>))
25import Data.Function (on)
26
27import Data.Word
28
29import Data.Sequence (Seq)
30import qualified Data.Sequence as Seq
31
32import Debug.Trace
33
34instance IsString Line where
35 fromString = (\(Right l) -> l) . text . TL.pack
36
37spec :: Spec
38spec = do
39 describe "example printouts" $
40 zipWithM_ example [1..] examples
41 where
42 example n (s, ts) = let str = show s
43 in specify str $ traceDump (mkPrintout . Printout . fmap (Paragraph . Seq.singleton) . fmap Cooked $ s) == ts
44
45final, initial :: L.ByteString
46initial = esc [64] <> esc [64]
47final = "\n\n\n \n"
48
49esc :: [Word8] -> L.ByteString
50esc = L.BS.pack . (27:)
51
52p :: L.ByteString -> L.ByteString
53p = (<> final) . (initial <>)
54
55traceDump :: L.ByteString -> L.ByteString
56traceDump bs = traceShow (map (\b -> (b, (toEnum $ fromEnum b) :: Char)) $ L.BS.unpack bs) bs
57
58examples :: [(Seq Block, L.ByteString)]
59examples = [ ( [Line (JuxtaPos ["Hello", HSpace 1, "World!"])]
60 , p "Hello World!")
61 , ( [Line (JuxtaPos ["Hello", HSpace 4, "World!"])]
62 , p "Hello World!")
63 , ( [Line ("Par1"), Line ("Par2"), Line (JuxtaPos ["Par3", HSpace 1, "Word2"])]
64 , p "Par1\n\nPar2\n\nPar3 Word2")
65 , ( [Line (JuxtaPos ["Par1", HSpace 4]), NewlSep [VSpace 2, Line ("Par2")], Line (JuxtaPos ["Par3", HSpace 1, "Word2"])]
66 , p "Par1 \n\n\n\n\nPar2\n\nPar3 Word2")
67 , ( [Line (JuxtaPos [Markup [Bold] (JuxtaPos ["B",HSpace 1,Markup [Underline] "BM"]),HSpace 1,Markup [Bold,Underline] "BM"])]
68 , p $ esc [33, 8] <> "B " <> esc [33, 128 + 8] <> "BM" <> esc [33, 8] <> esc [33, 0] <> " " <> esc [33, 128 + 8] <> "BM" <> esc [33, 0])
69 ]
diff --git a/server/thermoprint-server.cabal b/server/thermoprint-server.cabal
index 259abf3..c80351b 100644
--- a/server/thermoprint-server.cabal
+++ b/server/thermoprint-server.cabal
@@ -50,7 +50,7 @@ library
50 , servant-server >=0.4.4 && <1 50 , servant-server >=0.4.4 && <1
51 , stm >=2.4.4 && <3 51 , stm >=2.4.4 && <3
52 , text >=1.2.1 && <2 52 , text >=1.2.1 && <2
53 , thermoprint-spec ==4.0.* 53 , thermoprint-spec ==5.0.*
54 , time >=1.5.0 && <2 54 , time >=1.5.0 && <2
55 , wai >=3.0.4 && <4 55 , wai >=3.0.4 && <4
56 , warp >=3.1.9 && <4 56 , warp >=3.1.9 && <4
@@ -94,6 +94,7 @@ Test-Suite tests
94 , containers >=0.5.6 && <1 94 , containers >=0.5.6 && <1
95 , async >=2.1.0 && <3 95 , async >=2.1.0 && <3
96 , http-types >=0.9.1 && <1 96 , http-types >=0.9.1 && <1
97 , bytestring >=0.10.6 && <1
97 98
98executable thermoprint-server 99executable thermoprint-server
99 main-is: Main.hs 100 main-is: Main.hs
diff --git a/server/thermoprint-server.nix b/server/thermoprint-server.nix
index a40dddb..71a4211 100644
--- a/server/thermoprint-server.nix
+++ b/server/thermoprint-server.nix
@@ -25,10 +25,10 @@ mkDerivation {
25 base monad-logger mtl persistent-sqlite resourcet 25 base monad-logger mtl persistent-sqlite resourcet
26 ]; 26 ];
27 testHaskellDepends = [ 27 testHaskellDepends = [
28 async base containers exceptions hspec http-types monad-logger mtl 28 async base bytestring containers exceptions hspec http-types
29 persistent-sqlite QuickCheck quickcheck-instances resourcet stm 29 monad-logger mtl persistent-sqlite QuickCheck quickcheck-instances
30 temporary text thermoprint-client thermoprint-spec transformers 30 resourcet stm temporary text thermoprint-client thermoprint-spec
31 warp 31 transformers warp
32 ]; 32 ];
33 homepage = "http://dirty-haskell.org/tags/thermoprint.html"; 33 homepage = "http://dirty-haskell.org/tags/thermoprint.html";
34 description = "Server for thermoprint-spec"; 34 description = "Server for thermoprint-spec";