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
|
/++
ocda-write: a sample writer over the abstraction.
Usage: ocda-write --text|--md|--html|--epub|--typst <file> [lang]
--epub writes a zip, so it takes a path to write to rather than
stdout: ocda-write --epub <file> [lang] --out <file>.epub
The input may be either serialisation, and the writer is not told which.
That is the claim being demonstrated: a writer is a function from the
model to bytes, so a document renders the same whichever of its two
abstraction files it is given.
Output goes to stdout, so that the comparison against spine's own output
is a shell pipeline and not a directory of files to clean up.
+/
import std.stdio;
import ocda.load;
import ocda.model;
import write.epub;
import write.html;
import write.markdown;
import write.text;
import write.typst;
int main(string[] args) {
string format_;
string out_path;
/+ ↓ where the images are, relative to the file being written. Only markdown
takes it: a .md names an image by path, and the path depends on where the
.md is put, which is the caller's business and not the model's. +/
string image_pth = "image";
string[] rest;
bool want_out = false;
bool want_image_pth = false;
foreach (a; args[1 .. $]) {
if (want_out) { out_path = a; want_out = false; continue; }
if (want_image_pth) { image_pth = a; want_image_pth = false; continue; }
if (a == "--text") { format_ = "text"; }
else if (a == "--html") { format_ = "html"; }
else if (a == "--epub") { format_ = "epub"; }
else if (a == "--typst" || a == "--typ") { format_ = "typst"; }
else if (a == "--md" || a == "--markdown") { format_ = "markdown"; }
else if (a == "--out") { want_out = true; }
else if (a == "--image-prefix") { want_image_pth = true; }
else if (a.length > 2 && a[0 .. 2] == "--") {
stderr.writeln("ocda-write: unknown option ", a);
return 2;
} else { rest ~= a; }
}
if (format_.length == 0 || rest.length < 1) {
stderr.writeln("usage: ocda-write --text|--md|--html|--epub|--typst"
~ " <file>.ssp|<file>.ocda.db|<file>.sisupod [lang]"
~ "\n --md also takes --image-prefix <path> (default \"image\")");
return 2;
}
string lang = (rest.length > 1) ? rest[1] : "";
OcdaDocument doc;
try {
doc = load(rest[0], lang);
} catch (Exception e) {
stderr.writeln("error: ", e.msg);
return 2;
}
if (format_ == "epub") {
if (out_path.length == 0) {
stderr.writeln("ocda-write --epub: --out <file>.epub is needed,"
~ " because an epub is a zip and stdout is not a file");
return 2;
}
import std.file : write;
write(out_path, writeEpub(doc, rest[0]));
return 0;
}
if (format_ == "typst") {
string typ = writeTypst(doc);
if (out_path.length == 0) { stdout.write(typ); return 0; }
/+ a .typ names its images by path, so they are written beside it +/
import std.file : mkdirRecurse, write;
import std.path : buildPath, dirName;
import ocda.images : canGiveImages, imageBytes;
write(out_path, typ);
if (canGiveImages(rest[0])) {
string img_dir = buildPath(out_path.dirName, "image");
bool made = false;
foreach (name; imagesNamed(doc)) {
auto bytes = imageBytes(rest[0], name);
if (bytes.length == 0) { continue; }
if (!made) { mkdirRecurse(img_dir); made = true; }
write(buildPath(img_dir, name), bytes);
}
}
return 0;
}
if (format_ == "markdown") { stdout.write(writeMarkdown(doc, image_pth)); return 0; }
stdout.write((format_ == "html") ? writeHtmlScroll(doc) : writeText(doc));
return 0;
}
|