]> gitweb.factorcode.org Git - factor.git/blob - extra/codebook/codebook.factor
unicode: make this the API for all unicode things.
[factor.git] / extra / codebook / codebook.factor
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.temp io.files.types io.files.unique io.launcher
7 io.pathnames kernel locals math math.parser namespaces sequences
8 sorting strings system unicode xml.syntax xml.writer
9 xmode.catalog xmode.marker xmode.tokens ;
10 IN: codebook
11
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
16
17 CONSTANT: codebook-style
18     {
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] ] }
36         [ drop ]
37     }
38
39 : first-line ( filename encoding -- line )
40     [ readln ] with-file-reader ;
41
42 TUPLE: code-file
43     name encoding mode ;
44
45 : include-file-name? ( name -- ? )
46     {
47         [ path-components [ "." head? ] any? not ]
48         [ link-info regular-file? ]
49     } 1&& ;
50
51 : code-files ( dir -- files )
52     '[
53         [ include-file-name? ] filter [
54             dup detect-file dup binary?
55             [ f ] [ 2dup dupd first-line find-mode ] if
56             code-file boa
57         ] map [ mode>> ] filter [ name>> ] sort-with
58     ] with-directory-tree-files ;
59
60 : html-name-char ( char -- str )
61     {
62         { [ dup alpha? ] [ 1string ] }
63         { [ dup digit? ] [ 1string ] }
64         [ >hex 6 CHAR: 0 pad-head "_" "_" surround ]
65     } cond ;
66
67 : file-html-name ( name -- name )
68     [ html-name-char ] { } map-as concat ".html" append ;
69
70 : toc-list ( files -- list )
71     [ name>> ] map natural-sort [
72         [ file-html-name ] keep
73         [XML <li><a href=<->><-></a></li> XML]
74     ] map ;
75
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 ;
79
80 ! We wrap every line in <tt> because Kindle tends to forget the font when
81 ! moving back pages
82 : htmlize-tokens ( tokens line# -- html-tokens )
83     swap [
84         [ str>> zwnj ] [ id>> ] bi codebook-style case
85     ] map [XML <tt><font size="-2" color="#666666"><-></font> <-></tt> XML]
86     "\n" 2array ;
87
88 : line#>string ( i line#len -- i-string )
89     [ number>string ] [ CHAR: \s pad-head ] bi* ;
90
91 :: code>html ( dir file -- page )
92     file name>> :> name
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
99     <XML <html>
100         <head>
101             <title><-name-></title>
102             <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
103         </head>
104         <body>
105             <h2><-name-></h2>
106             <pre><-html-lines-></pre>
107             <mbp:pagebreak xmlns:mbp="http://www.mobipocket.com/mbp" />
108         </body>
109     </html> XML> ;
110
111 :: code>toc-html ( dir name files -- html )
112     "Generating HTML table of contents" print flush
113
114     now timestamp>rfc822 :> timestamp
115     dir absolute-path :> source
116     dir [
117         files toc-list :> toc
118
119         <XML <html>
120             <head>
121                 <title><-name-></title>
122                 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
123             </head>
124             <body>
125                 <h1><-name-></h1>
126                 <font size="-2">Generated from<br/>
127                 <b><tt><-source-></tt></b><br/>
128                 at <-timestamp-></font><br/>
129                 <br/>
130                 <ul><-toc-></ul>
131                 <mbp:pagebreak xmlns:mbp="http://www.mobipocket.com/mbp" />
132             </body>
133         </html> XML>
134     ] with-directory ;
135
136 :: code>ncx ( dir name files -- xml )
137     "Generating NCX table of contents" print flush
138
139     files [| file i |
140         file name>> :> name
141         name file-html-name :> filename
142         i 2 + number>string :> istr
143
144         [XML <navPoint class="book" id=<-filename-> playOrder=<-istr->>
145             <navLabel><text><-name-></text></navLabel>
146             <content src=<-filename-> />
147         </navPoint> XML]
148     ] map-index :> file-nav-points
149
150     <XML <?xml version="1.0" encoding="UTF-8" ?>
151     <ncx version="2005-1" xmlns="http://www.daisy.org/z3986/2005/ncx/">
152         <navMap>
153             <navPoint class="book" id="toc" playOrder="1">
154                 <navLabel><text>Table of Contents</text></navLabel>
155                 <content src="_toc.html" />
156             </navPoint>
157             <-file-nav-points->
158         </navMap>
159     </ncx> XML> ;
160
161 :: code>opf ( dir name files -- xml )
162     "Generating OPF manifest" print flush
163     name ".ncx"  append :> ncx-name
164
165     files [
166         name>> file-html-name dup
167         [XML <item id=<-> href=<-> media-type="text/html" /> XML]
168     ] map :> html-manifest
169
170     files [ name>> file-html-name [XML <itemref idref=<-> /> XML] ] map :> html-spine
171
172     <XML <?xml version="1.0" encoding="UTF-8" ?>
173     <package
174         version="2.0"
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" />
181         </metadata>
182         <manifest>
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" />
185             <-html-manifest->
186             <item id="toc" href=<-ncx-name-> media-type="application/x-dtbncx+xml" />
187         </manifest>
188         <spine toc="toc">
189             <itemref idref="html-toc" />
190             <-html-spine->
191         </spine>
192         <guide>
193             <reference type="toc" title="Table of Contents" href="_toc.html" />
194         </guide>
195     </package> XML> ;
196
197 : write-dest-file ( xml name ext -- )
198     append utf8 [ write-xml ] with-file-writer ;
199
200 SYMBOL: kindlegen-path
201 kindlegen-path [ "kindlegen" ] initialize
202
203 SYMBOL: codebook-output-path
204 codebook-output-path [ "resource:codebooks" ] initialize
205
206 : kindlegen ( path -- )
207     [ kindlegen-path get "-unicode" ] dip 3array try-process ;
208
209 : kindle-path ( directory name extension -- path )
210     [ append-path ] dip append ;
211
212 :: codebook ( src-dir -- )
213     codebook-output-path get normalize-path :> dest-dir
214
215     "Generating ebook for " write src-dir write " in " write dest-dir print flush
216
217     dest-dir make-directories
218     [
219         [
220             src-dir file-name :> name
221             src-dir code-files :> files
222
223             src-dir name files code>opf
224             name ".opf" write-dest-file
225
226             "vocab:codebook/cover.jpg" "." copy-file-into
227
228             src-dir name files code>ncx
229             name ".ncx" write-dest-file
230
231             src-dir name files code>toc-html
232             "_toc.html" "" write-dest-file
233
234             files [| file |
235                 src-dir file code>html
236                 file name>> file-html-name "" write-dest-file
237             ] each
238
239             "." name ".opf" kindle-path kindlegen
240             "." name ".mobi" kindle-path dest-dir copy-file-into
241
242             dest-dir name ".mobi" kindle-path :> mobi-path
243
244             "Job's finished: " write mobi-path print flush
245         ] cleanup-unique-directory
246     ] with-temp-directory ;