summaryrefslogtreecommitdiffhomepage
path: root/dlang/README.md
blob: e3a41b6da23986a760a4ec340ac4d9993760a234 (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
# ocda-write, in D

Sample writers over the abstraction:

    ocda-write --text|--md|--html|--epub|--typst <file> [lang]
    ocda-write --md <file> [lang] [--image-prefix <path>]

writes a document out, from any serialisation of its abstraction.

## Modules

| | |
|---|---|
| `write.text` | the model -> spine's plain text, byte for byte |
| `write.html` | the model -> one self-contained, valid html5 file |
| `write.epub` | the model -> an epub3, one file per segment |
| `write.markdown` | the model -> CommonMark, object numbers and all |
| `write.typst` | the model -> typst markup, for a pdf |
| `write.zip` | writing a zip, stored only, with no timestamps |
| `write.rgx` | the internal markup, as regular expressions |
| `main` | the program: which format, which file, which language |

`write.text` takes an `OcdaDocument` and returns a string. It is given no
file, no path and no option: a writer is a function from the model to bytes,
and everything it needs is in the model.

## Build and run

```sh
nix build --override-input ocda-lib path:../../sisudoc-ocda-lib
./result/bin/ocda-write --text <doc>.ssp
./result/bin/ocda-write --html <doc>.ocda.db de
./result/bin/ocda-write --html <doc>.sisupod
./result/bin/ocda-write --md <doc>.ssp
./result/bin/ocda-write --epub <doc>.ocda.db en --out <doc>.epub
./result/bin/ocda-write --typst <doc>.ocda.db en --out out/<doc>.typ
typst compile --root out out/<doc>.typ out/<doc>.pdf
```

`--epub` writes a zip, so it takes `--out` rather than stdout. `--typst` goes
to stdout too, but with `--out` it also writes the `image/` directory the
`.typ` names.

`nix build .#ocda-write-dmd` builds with dmd instead of ldc.

The library's modules are compiled alongside these rather than linked as a
separate product; for five modules a second build step buys nothing.

## The checks

`--text` and `--md` are checked against the html writer's objects, word for
word, and against each other language's writer, byte for byte: **36 of 36 on
every count.** Byte identity with `spine --text` was the first oracle and is
no longer the target; see the repository README.

`--html` is checked harder still: it parses, every `href="#x"`
resolves, no id appears twice, no heading falls outside `h1`..`h6`, and the
objects' visible text equals spine's scroll html exactly. **36 of 36 on each,
and byte identical to the Gleam writer.** See the repository README for what
it does differently from spine and why.

`--epub` is judged by `epubcheck`, and `--typst` by typst: a link to a label
that does not exist is a compile error, so a silent compile means every
internal link in the document resolves. **36 of 36 on each, and byte
identical to the Gleam writer.**

## Where it is faithful rather than sensible

The *text* writer reproduces spine's output exactly, which means reproducing
what would not be chosen from scratch. The comments say so at each point.
The three worth knowing before reading the code:

- **the table layout strips trailing whitespace from the whole accumulated
  table after every cell**, not once per row, so a row ending in an empty
  cell loses the newline before it;
- **a column's width is the widest cell in bytes and the padding is counted
  in codepoints**, so a cell holding `©` is padded one place further than one
  holding `c`;
- **the section sequence is spine's `scroll` sequence**, not its `text` one:
  head, toc, body, then each trailing section *that has more than one
  object*, and no `tail` unless an html or epub output was also asked for.

The *html* writer takes none of these as given. What it takes from the model
and what it declines to take from spine is set out in its own comments, at
the point where each choice is made.