summaryrefslogtreecommitdiff
path: root/provider/posts/thermoprint-1.md
blob: 032e2f6098bb9573b7465930fb144747b4170b22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
---
title: On the Architecture of a tool-set for interacting with character-oriented printers
published: 2015-12-25
tags: Thermoprint
---

# Motivation

Some time ago I bought a cheap Chinese
[thermoprinter](https://en.wikipedia.org/wiki/Thermal_printing) off eBay.
As expected the printers firmware is really awkward to use (including binary
control codes used to switch between char sets such as bold, italic, underlined,
etc.).
The obvious solution was to write a library to parse a more sensible
representation and send it to be printed.

Since there might, at some point, be other users wanting to print to my
acquisition the architecture is intended to be present a somewhat usable
interface to the uninitiated.

# Implementation

## Location

Recently I created a new branch in
[thermoprint](https://git.yggdrasil.li/thermoprint) called
[rewrite](https://git.yggdrasil.li/thermoprint?h=rewrite).

## Architecture Overview

The new macroscopic architecture I´m currently aiming for is quite similar to
the old one:

 * A server intended to run on the machine connected to my cheap printer talking
   directly to the printer on one end and serving a
   [json api](https://hackage.haskell.org/package/servant) on the other.
 * A (hopefully) tiny cli tool for debugging and personal use.
 * A website (it will probably end up being based on
   [yesod](https://hackage.haskell.org/package/yesod)) presenting a web interface
   similar to the cli tool.

## Features

Features I intend to implement include:

  * A parser for a bbcode-dialect which should be used in both the cli tool and the
    website (it will probably end up using
    [attoparsec](https://hackage.haskell.org/package/attoparsec)) -- bbcode as
    presented on [Wikipedia](https://en.wikipedia.org/wiki/BBCode) is a proper
    superset of the feature-set of my cheap Chinese printer.
  * Reasonable test coverage using
    [QuickCheck](https://hackage.haskell.org/package/QuickCheck),
    [HUnit](http://hackage.haskell.org/package/HUnit).
   
    Automatic testing with [cabal](https://www.haskell.org/cabal/) facilitated by
    [hspec](https://hackage.haskell.org/package/hspec).
  * Support and server-side storage for drafts.
  * The Website should provide some richer formats than bbcode which will
    probably find inclusion in the payload datastructure such as lists,
    checklists, tables, etc.

    The cli-tool should be able to use these too (the input will probably end up
    being json-formatted).

## Work so far

### Prototype

I already have a prototype.
It's quite bug-ridden and has recently developed serious problems actually
printing after working satisfactorily for a few weeks.

It also does not include a web-interface and I am quite unsatisfied with the
overall code quality.

The [685 lines of code](http://cloc.sourceforge.net/) can be found in the
[repo](https://git.yggdrasil.li/thermoprint?h=master) as well.

### Rewrite

Currently the [rewrite](https://git.yggdrasil.li/thermoprint?h=rewrite) contains a
single file of moment -- spec/src/Thermoprint/Printout.hs -- wherein we define
the payload for the api -- our take on a structured document format (somewhat
inspired by
[pandoc](http://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Definition.html)):

~~~ {.haskell}
-- | A 'Printout' is a sequence of visually seperated 'Paragraph's
type Printout = Seq Paragraph

-- | A 'Paragraph' is a non-seperated sequence of 'Chunk's
type Paragraph = Seq Chunk

-- | We introduce both 'Chunk' and 'Paragraph' mainly to allow 'Raw'.
-- 
-- Were we to disallow 'Raw', 'Block' would be identical to 'Paragraph'
data Chunk = Cooked Block -- ^ text semantically structured to be rendered in accordance with the display format of printer
           | Raw ByteString -- ^ direct instructions to the printer
           deriving (Generic, NFData, Show, CoArbitrary)

-- | 'Block' is the entry point for our structured document format
data Block = Line Line -- ^ a single 'Line' of text
           | VSpace Integer -- ^ vertical space of height equivalent to 'Integer' lines
           | NewlSep (Seq Block) -- ^ A sequence of 'Block's seperated by newlines
           deriving (Generic, NFData, Show, CoArbitrary)

{- | A 'Line' is one of:

  * a single word
  * horizontal space equivalent to the width of 'Integer' `em`.
  * a sequence of words seperated by spaces

We don't export all constructors and instead encourage the use of 'text'.
-} 
data Line = Word Text
          | HSpace Integer
          | SpaceSep (Seq Line)
          deriving (Generic, NFData, Show, CoArbitrary)
~~~

(The code is verbatim as of 8307d7e).

<!--  LocalWords:  Thermoprint thermoprint json api cli yesod bbcode attoparsec
 -->
<!--  LocalWords:  superset QuickCheck HUnit hspec datastructure repo pandoc
 -->
<!--  LocalWords:  haskell ByteString NFData CoArbitrary VSpace
 -->
<!--  LocalWords:  NewlSep HSpace SpaceSep
 -->