-
Notifications
You must be signed in to change notification settings - Fork 0
/
pollen.rkt
334 lines (265 loc) · 12.3 KB
/
pollen.rkt
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#lang racket
(require pollen/core
pollen/decode
pollen/tag
txexpr
sugar)
(require "helpers-template.rkt"
"helpers-misc.rkt")
(provide (all-from-out "helpers-template.rkt"
"helpers-misc.rkt"))
(provide (all-defined-out))
(define github-url "https://github.com/saadatm/ghalib")
(define base-url "https://saadatm.github.io/ghalib")
; Define some of the custom tags as block
(module setup racket/base
(provide (all-defined-out))
(require pollen/setup)
(define block-tags (append default-block-tags '(dummy svg ق سرخی دستخط شاعری-میں-سرخی))))
; Helper functions
(define (is-br? e)
(equal? '(br) e))
(define (has-qitah-mark? tx)
(equal? 'ق (get-tag tx)))
(define (western-to-urdu-digit d)
(cond
[(char=? d #\0) #\۰]
[(char=? d #\1) #\۱]
[(char=? d #\2) #\۲]
[(char=? d #\3) #\۳]
[(char=? d #\4) #\۴]
[(char=? d #\5) #\۵]
[(char=? d #\6) #\۶]
[(char=? d #\7) #\۷]
[(char=? d #\8) #\۸]
[(char=? d #\9) #\۹]))
(define (number->urdu-fn-string n)
(define s (number->string n))
(define lst (string->list s))
(define urdu-lst (map western-to-urdu-digit lst))
(define urdustring (list->string urdu-lst))
(format "~a" urdustring))
#|
Footnotes. Taken and adapted from:
https://groups.google.com/forum/#!topic/pollenpub/laWL4SWx0Zc , and
https://thelocalyarn.com/code/artifact/88b1006b
|#
(define (fn-id x) (string-append "حاشیہ-" x))
(define (fnref-id x) (string-append "ح-" x))
(define all-fn-names (make-hash))
(define all-fn-defs (make-hash))
#|
ح tag for referring to a footnote. This tag has the following variants:
1. ◊ح{نام-حاشیہ}
This is the simple variant where we only mention the footnote name.
2. ◊ح['شروع]{نام-حاشیہ}
This variant takes a symbol ('شروع), which is then used to append an extra
CSS class "start" (which, in turn, is used to position the footnote reference before
its respective line/misra or couplet/stanza in the web layout).
3. ◊ح['آخر]{نام-حاشیہ}
This variant takes a symbol ('آخر), which is then used to append an extra
CSS class "end" (which, in turn, is used to position the footnote reference after
its respective line/misra or couplet/stanza in the web layout).
4. ◊ح['ساکت]{نام-حاشیہ}
This variant takes a symbol ('ساکت), which is then used to append an extra
CSS class (which, in turn, is used to position the footnote reference without
absolute positioning in the web layout). The purpose of this variant is to refer
to those footnotes that are about a whole ghazal. In the content, this tag variant
conventionally appears inside a درمیان tag that comes before a شاعری tag.
|#
(define (ح . elems)
; Check if we have a symbol in ح (adapted from https://github.com/mbutterick/pollen-users/issues/65#issuecomment-653621118 )
(define-values (pos name-in)
(cond [(symbol? (car elems)) (values (car elems) (cdr elems))]
[else (values 'none elems)]))
(define name (apply string-append name-in))
(define page-path (hash-ref (current-metas) 'here-path))
(define page-fn-names (cons name (hash-ref! all-fn-names page-path '())))
(hash-set! all-fn-names page-path page-fn-names)
`(span ((class ,(cond [(equal? pos 'شروع) "fn-ref start"]
[(equal? pos 'آخر) "fn-ref end"]
[(equal? pos 'ساکت) "fn-ref static"]
[else "fn-ref"])))
(a ((href ,(string-append "#" (fn-id name)))
(id ,(fnref-id name)))
,(number->urdu-fn-string (length (member name page-fn-names))))))
; The حاشیہ tag for containing the actual footnote
(define (حاشیہ name . xs)
(define page-path (hash-ref (current-metas) 'here-path))
(define page-fn-defs (hash-ref! all-fn-defs page-path make-hash))
(hash-set! page-fn-defs (format "~a" name) xs))
(define (footnote-block)
(define page-path (hash-ref (current-metas) 'here-path))
(define page-fn-names (hash-ref! all-fn-names page-path '()))
(define page-fn-defs (hash-ref! all-fn-defs page-path (make-hash)))
(define note-items
(for/list ([fn-name (in-list (reverse page-fn-names))])
(let* ([fn-def-text (or (hash-ref page-fn-defs fn-name #f)
'("حاشیے کا متن دستیاب نہیں۔"))])
`(li ((id ,(fn-id fn-name)))
,@(append
(list `(span ((class "fn-count")) (a ((href ,(string-append "#" (fnref-id fn-name)))) ,(number->urdu-fn-string (length (member fn-name page-fn-names))))))
fn-def-text)))))
(if (empty? note-items)
empty ; return an empty list if there are no note-items
`(section ((class "footnotes")) (ol ,@note-items))))
#|
Lists. Taken from: https://docs.racket-lang.org/pollen-tfl/_pollen_rkt_.html#%28def._%28%28lib._pollen-tfl%2Fpollen..rkt%29._detect-list-items%29%29
|#
(define (detect-list-items elems)
(define elems-merged (merge-newlines elems))
(define (list-item-break? elem)
(define list-item-separator-pattern (regexp "\n\n\n+"))
(and (string? elem) (regexp-match list-item-separator-pattern elem)))
(define list-of-li-elems (filter-split elems-merged list-item-break?))
(define list-of-li-paragraphs
(map (lambda(li) (decode-paragraphs li #:force? #t)) list-of-li-elems))
(define li-tag (default-tag-function 'li))
(map (lambda(lip) (apply li-tag lip)) list-of-li-paragraphs))
(define (make-list-function tag [attrs empty])
(define (listifier . args)
(list* tag attrs (detect-list-items args)))
listifier)
(define سادہ-فہرست (make-list-function 'ul))
(define عددی-فہرست (make-list-function 'ol))
(define کتابیات-فہرست (make-list-function 'ul '((class "bibliography"))))
; Custom ◊حم tag (حم = abbreviation of حاشیہ منجانب)
; To be used to denote the author of a footnote
(define (حم name)
`(span ((class "fn-author")) ,(string-append "—" name)))
(define (سرخی . heading)
`(h1 ,@heading))
(define (ذیلی-سرخی . heading)
`(h2 ,@heading))
(define (شاعری-میں-سرخی . content)
`(h6 ,@content))
(define (ربط url . text)
`(a ((href ,(format "~a" (link-prefix url)))) ,@text))
(define (دستخط . content)
`(p ((class "signature")) ,@content))
(define (درمیان . content)
`(p ((class "center")) ,@content))
(define (علامت sign)
`(span ((class "poetic-sign")) ,sign))
(define (عربی . content)
`(span ((lang "ar") (class "ar")) ,@content))
(define (انگریزی . content)
`(span ((lang "en") (dir "ltr") (class "en")) ,@content))
; خک = abbreviation of خط کشیدہ
(define (خک . content)
`(i ((class "overline")) ,@content))
#|
Tags for various subtending marks
|#
(define (سال year)
; Not doing anything in here till a bug in Noto Nastaliq Urdu is fixed:
; https://github.com/googlefonts/noto-fonts/issues/1620
;(subtending-mark "" year))
year)
(define (حاشیہ-نمبر num)
(subtending-mark "" num))
(define (صفحہ page)
(subtending-mark "" page))
(define (نمبر num)
(subtending-mark "" num))
(define (subtending-mark mark num)
`(span ((class "subtending-mark")) ,mark ,num))
#|
Custom tags for handling poetry.
The generic شاعری tag does all the heavy lifting. Other convenience
functions call شاعری with a specific CSS class name for styling variations.
|#
(define (غزل . content)
(apply شاعری #:class "ghazal" content))
(define (رباعی . content)
(apply شاعری #:class "rubai" content))
(define (قطعہ . content)
(apply شاعری #:class "nazm" content))
(define (قصیدہ . content)
(apply شاعری #:class "nazm" content))
(define (مثنوی . content)
(apply شاعری #:class "nazm" content))
(define (سلام . content)
(apply شاعری #:class "nazm" content))
(define (سہرا . content)
(apply شاعری #:class "nazm" content))
(define (مخمس . content)
(apply شاعری #:class "mukhammas" content))
(define (مرثیہ . content)
(apply شاعری #:class "musaddas" content))
(define (شعر . content)
(apply شاعری #:class "couplet" content))
#|
A description of what is happening in the شاعری tag:
We first process `content` with decode-elements, and use `decode-paragraphs` for all its
txexprs. When calling `decode-paragraphs`, we look at the `class-name` argument to see
whether the poetry content is a rubai or a couplet, and if yes, use the #:force? option to
always have a 'p tag around `content`. This is necessary because a rubai/couplet is just
four/two lines separated by line breaks with no implicit paragraph break. We also exclude:
(i) the 'span tag because it will have come from the processing of footnote references, and
(ii) the 'i tag because it will have come from the processing of خک tag.
All this will give us `content-with-paras`, which will have something like this:
'((p "Line 1 of stanza 1" (br) "Line 2 of stanza 1")
(p "Line 1 of stanza 2" (br) "Line 2 of stanza 2")
(ق "Line 1 of stanza 3" (br) "Line 2 of stanza 3")
(p "Line 1 of" (span [footnote reference here]) "stanza 4") (br) "Line 2 of stanza 4")
(h6 "Some heading")
(p "Line 1 of stanza 5" (br) "Line 2 of stanza 5")
- Each p tag represents a stanza. (Here, I use the word "stanza" to merely denote a group
of "lines" (مصرعے). Depending on the genre, the "stanza" may be a شعر, رباعی, بند, مخمس, etc. and
may contain more than two lines.)
- A ق tag is also a stanza, but one that says that there should be a qitah mark shown against
this stanza.
- There may be a footnote reference in a 'span in a line of a stanza. This will have come from
the processing of the ◊ح tag.
- An h6 tag is for things like "مطلعِ ٹانی" or "غزل" among a qaseedah/qitah/nazm/etc. This will have
come from the processing of the ◊شاعری-میں-سرخی tag.
Next, we run `decode-elements` again on `content-with-paras`. We specify that its block tags
('p and 'ق) should be processed with `process-single-stanza`. We exclude the h6 tag because it
doesn't need any processing. (Description of `process-single-stanza` is stated with its definition.)
This will give us `poetry-tx-elems`, which are then wrapped in a div, and given a CSS class name for
styling.
|#
(define (شاعری #:class [class-name #f] . content)
(define (force-paras? x)
(or
(equal? x "rubai")
(equal? x "couplet")))
(define content-with-paras
(decode-elements content
#:txexpr-elements-proc (lambda (x) (decode-paragraphs x #:force? (force-paras? class-name)))
#:exclude-tags '(span i)))
(define poetry-tx-elems
(decode-elements content-with-paras
#:block-txexpr-proc process-single-stanza
#:exclude-tags '(h6)))
(define poetry-tx (txexpr 'div empty poetry-tx-elems))
(if class-name
(attr-set poetry-tx 'class (string-append "poetry " class-name))
(attr-set poetry-tx 'class "poetry")))
; This function takes a single stanza (either a 'p or a 'ق)
(define (process-single-stanza stanza)
; Take out the elements of a `stanza`
(define stanza-elems (get-elements stanza))
; Split its lines by using '(br) as a separator
(define lines (filter-split stanza-elems is-br?))
; Tag the lines with a 'span tag and give them a CSS class for styling
(define tagged-lines (map (lambda(x) (txexpr 'span '((class "line")) x)) lines))
; Insert '(br) again between the tagged lines, and combine them in a 'p tag with
; a specific CSS class name for styling. If the `stanza` was a 'ق, give it an
; additional CSS class.
(if (has-qitah-mark? stanza)
(txexpr 'p '((class "stanza has-qitah-mark")) (add-between tagged-lines '(br)))
(txexpr 'p '((class "stanza")) (add-between tagged-lines '(br)))))
#|
The root function
|#
(define (root . elements)
; If footnote-block (which is a function) returns a txexpr, include it at the end of elements
(define content (if (txexpr? (footnote-block))
`(,@elements ,separator-ornament ,(footnote-block))
elements))
; Now run decode-elements on content
(txexpr 'root empty (decode-elements content
#:txexpr-elements-proc decode-paragraphs
#:string-proc urdu-smart-quotes)))