1 ! (c)2010 Joe Groff bsd license
2 USING: accessors arrays assocs calendar calendar.format
3 combinators combinators.short-circuit fry io io.backend
4 io.directories io.directories.hierarchy io.encodings.binary
5 io.encodings.detect io.encodings.utf8 io.files io.files.info
6 io.files.types io.files.unique io.launcher io.pathnames kernel
7 locals math math.parser namespaces sequences sorting strings
8 system unicode.categories xml.syntax xml.writer xmode.catalog
9 xmode.marker xmode.tokens ;
12 ! Usage: "my/source/tree" codebook
13 ! Writes tree.opf, tree.ncx, and tree.html to a temporary directory
14 ! Writes tree.mobi to resource:codebooks
15 ! Requires kindlegen to compile tree.mobi for Kindle
17 CONSTANT: codebook-style
19 { COMMENT1 [ [XML <i><font color="#555555"><-></font></i> XML] ] }
20 { COMMENT2 [ [XML <i><font color="#555555"><-></font></i> XML] ] }
21 { COMMENT3 [ [XML <i><font color="#555555"><-></font></i> XML] ] }
22 { COMMENT4 [ [XML <i><font color="#555555"><-></font></i> XML] ] }
23 { DIGIT [ [XML <font color="#333333"><-></font> XML] ] }
24 { FUNCTION [ [XML <b><font color="#111111"><-></font></b> XML] ] }
25 { KEYWORD1 [ [XML <b><font color="#111111"><-></font></b> XML] ] }
26 { KEYWORD2 [ [XML <b><font color="#111111"><-></font></b> XML] ] }
27 { KEYWORD3 [ [XML <b><font color="#111111"><-></font></b> XML] ] }
28 { KEYWORD4 [ [XML <b><font color="#111111"><-></font></b> XML] ] }
29 { LABEL [ [XML <b><font color="#333333"><-></font></b> XML] ] }
30 { LITERAL1 [ [XML <font color="#333333"><-></font> XML] ] }
31 { LITERAL2 [ [XML <font color="#333333"><-></font> XML] ] }
32 { LITERAL3 [ [XML <font color="#333333"><-></font> XML] ] }
33 { LITERAL4 [ [XML <font color="#333333"><-></font> XML] ] }
34 { MARKUP [ [XML <b><font color="#333333"><-></font></b> XML] ] }
35 { OPERATOR [ [XML <b><font color="#111111"><-></font></b> XML] ] }
39 : first-line ( filename encoding -- line )
40 [ readln ] with-file-reader ;
45 : include-file-name? ( name -- ? )
47 [ path-components [ "." head? ] any? not ]
48 [ link-info type>> +regular-file+ = ]
51 : code-files ( dir -- files )
53 [ include-file-name? ] filter [
54 dup detect-file dup binary?
55 [ f ] [ 2dup dupd first-line find-mode ] if
57 ] map [ mode>> ] filter [ name>> ] sort-with
58 ] with-directory-tree-files ;
60 : html-name-char ( char -- str )
62 { [ dup alpha? ] [ 1string ] }
63 { [ dup digit? ] [ 1string ] }
64 [ >hex 6 CHAR: 0 pad-head "_" "_" surround ]
67 : file-html-name ( name -- name )
68 [ html-name-char ] { } map-as concat ".html" append ;
70 : toc-list ( files -- list )
71 [ name>> ] map natural-sort [
72 [ file-html-name ] keep
73 [XML <li><a href=<->><-></a></li> XML]
76 ! insert zero-width non-joiner between all characters so words can wrap anywhere
77 : zwnj ( string -- s|t|r|i|n|g )
78 [ CHAR: \u00200c "" 2sequence ] { } map-as concat ;
80 ! We wrap every line in <tt> because Kindle tends to forget the font when
82 : htmlize-tokens ( tokens line# -- html-tokens )
84 [ str>> zwnj ] [ id>> ] bi codebook-style case
85 ] map [XML <tt><font size="-2" color="#666666"><-></font> <-></tt> XML]
88 : line#>string ( i line#len -- i-string )
89 [ number>string ] [ CHAR: \s pad-head ] bi* ;
91 :: code>html ( dir file -- page )
93 "Generating HTML for " write name write "..." print flush
94 dir [ file [ name>> ] [ encoding>> ] bi file-lines ] with-directory :> lines
95 lines length 1 + number>string length :> line#len
96 file mode>> load-mode :> rules
97 f lines [| l i | l rules tokenize-line i 1 + line#len line#>string htmlize-tokens ]
98 map-index concat nip :> html-lines
101 <title><-name-></title>
102 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
106 <pre><-html-lines-></pre>
107 <mbp:pagebreak xmlns:mbp="http://www.mobipocket.com/mbp" />
111 :: code>toc-html ( dir name files -- html )
112 "Generating HTML table of contents" print flush
114 now timestamp>rfc822 :> timestamp
115 dir absolute-path :> source
117 files toc-list :> toc
121 <title><-name-></title>
122 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
126 <font size="-2">Generated from<br/>
127 <b><tt><-source-></tt></b><br/>
128 at <-timestamp-></font><br/>
131 <mbp:pagebreak xmlns:mbp="http://www.mobipocket.com/mbp" />
136 :: code>ncx ( dir name files -- xml )
137 "Generating NCX table of contents" print flush
141 name file-html-name :> filename
142 i 2 + number>string :> istr
144 [XML <navPoint class="book" id=<-filename-> playOrder=<-istr->>
145 <navLabel><text><-name-></text></navLabel>
146 <content src=<-filename-> />
148 ] map-index :> file-nav-points
150 <XML <?xml version="1.0" encoding="UTF-8" ?>
151 <ncx version="2005-1" xmlns="http://www.daisy.org/z3986/2005/ncx/">
153 <navPoint class="book" id="toc" playOrder="1">
154 <navLabel><text>Table of Contents</text></navLabel>
155 <content src="_toc.html" />
161 :: code>opf ( dir name files -- xml )
162 "Generating OPF manifest" print flush
163 name ".ncx" append :> ncx-name
166 name>> file-html-name dup
167 [XML <item id=<-> href=<-> media-type="text/html" /> XML]
168 ] map :> html-manifest
170 files [ name>> file-html-name [XML <itemref idref=<-> /> XML] ] map :> html-spine
172 <XML <?xml version="1.0" encoding="UTF-8" ?>
175 xmlns="http://www.idpf.org/2007/opf"
176 unique-identifier=<-name->>
177 <metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
178 <dc:title><-name-></dc:title>
179 <dc:language>en</dc:language>
180 <meta name="cover" content="my-cover-image" />
183 <item href="cover.jpg" id="my-cover-image" media-type="image/jpeg" />
184 <item id="html-toc" href="_toc.html" media-type="text/html" />
186 <item id="toc" href=<-ncx-name-> media-type="application/x-dtbncx+xml" />
189 <itemref idref="html-toc" />
193 <reference type="toc" title="Table of Contents" href="_toc.html" />
197 : write-dest-file ( xml dest-dir name ext -- )
198 append append-path utf8 [ write-xml ] with-file-writer ;
200 SYMBOL: kindlegen-path
201 kindlegen-path [ "kindlegen" ] initialize
203 SYMBOL: codebook-output-path
204 codebook-output-path [ "resource:codebooks" ] initialize
206 : kindlegen ( path -- )
207 [ kindlegen-path get "-unicode" ] dip 3array try-process ;
209 : kindle-path ( directory name extension -- path )
210 [ append-path ] dip append ;
212 :: codebook ( src-dir -- )
213 codebook-output-path get normalize-path :> dest-dir
215 "Generating ebook for " write src-dir write " in " write dest-dir print flush
217 dest-dir make-directories
219 current-temporary-directory get :> temp-dir
220 src-dir file-name :> name
221 src-dir code-files :> files
223 src-dir name files code>opf
224 temp-dir name ".opf" write-dest-file
226 "vocab:codebook/cover.jpg" temp-dir copy-file-into
228 src-dir name files code>ncx
229 temp-dir name ".ncx" write-dest-file
231 src-dir name files code>toc-html
232 temp-dir "_toc.html" "" write-dest-file
235 src-dir file code>html
236 temp-dir file name>> file-html-name "" write-dest-file
239 temp-dir name ".opf" kindle-path kindlegen
240 temp-dir name ".mobi" kindle-path dest-dir copy-file-into
242 dest-dir name ".mobi" kindle-path :> mobi-path
244 "Job's finished: " write mobi-path print flush
245 ] cleanup-unique-working-directory ;