pslm 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # "joins" several input streams, so that #gets behaves as if they were
4
+ # a single stream
5
+ class JoinedInput
6
+
7
+ # accepts any number of open IOs
8
+ def initialize(*ins)
9
+ @streams = ins
10
+ end
11
+
12
+ def gets
13
+ if @streams.empty? then
14
+ return nil
15
+ end
16
+
17
+ l = @streams.first.gets
18
+ if l == nil
19
+ @streams.shift
20
+ return gets
21
+ end
22
+
23
+ unless l.end_with? "\n"
24
+ l += "\n"
25
+ end
26
+
27
+ return l
28
+ end
29
+
30
+ def read
31
+ @streams.collect {|s| s.read }.join "\n"
32
+ end
33
+
34
+ def close
35
+ @streams.each {|s| s.close }
36
+ @streams = []
37
+ end
38
+
39
+ def closed?
40
+ @streams.empty?
41
+ end
42
+ end
@@ -0,0 +1,449 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Pslm
4
+
5
+ # formats a psalm for LaTeX
6
+ class LatexOutputter < Outputter
7
+
8
+ # each Formatter must have it's symbol in the order, otherwise it won't be used
9
+ FORMATTER_ORDER = [
10
+ :pointing,
11
+ :break_hints,
12
+ :parts,
13
+ :strophes,
14
+ :verses,
15
+ :skip_verses,
16
+ :title,
17
+ :lettrine,
18
+ :final_add_content,
19
+ :wrapper,
20
+ :prepend_text,
21
+ :output_append_text,
22
+ :line_break_last_line,
23
+ :quote,
24
+ :mark_short_verses,
25
+ ]
26
+
27
+ # takes a Psalm, returns a String with the psalm formatted
28
+ def process_psalm(psalm, opts={})
29
+ formatters = get_formatters(opts)
30
+
31
+ # build the output; on each step apply the appropriate method
32
+ # of each formatter in the given order
33
+ psalm_assembled = psalm.strophes.collect do |strophe|
34
+ process_strophe(strophe, opts, psalm, formatters)
35
+ end.join "\n"
36
+
37
+ return Formatter.format(formatters, :psalm,
38
+ psalm_assembled,
39
+ psalm)
40
+ end
41
+
42
+ alias :process :process_psalm
43
+
44
+ def process_strophe(strophe, opts, psalm, formatters=nil)
45
+ formatters = formatters || get_formatters(opts)
46
+
47
+ strophe_assembled = strophe.verses.collect do |verse|
48
+ process_verse(verse, opts, strophe, psalm, formatters)
49
+ end.delete_if {|v| v == '' }.join "\n"
50
+
51
+ return Formatter.format(formatters, :strophe,
52
+ strophe_assembled,
53
+ strophe,
54
+ psalm)
55
+ end
56
+
57
+ def process_verse(verse, opts, strophe=nil, psalm=nil, formatters=nil)
58
+ formatters = formatters || get_formatters(opts)
59
+
60
+ verse_assembled = verse.parts.collect do |part|
61
+
62
+ part_assembled = part.words.reverse.collect do |word|
63
+
64
+ word_assembled = word.syllables.reverse.collect do |syll|
65
+ Formatter.format(formatters, :syllable,
66
+ syll, syll, word, part, verse, strophe, psalm)
67
+ end.reverse.join ''
68
+
69
+ Formatter.format(formatters, :word,
70
+ word_assembled, word, part, verse, strophe, psalm)
71
+ end.reverse.join ' '
72
+
73
+ Formatter.format(formatters, :part,
74
+ part_assembled, part, verse, strophe, psalm)
75
+ end.join "\n"
76
+
77
+ return Formatter.format(formatters, :verse,
78
+ verse_assembled, verse, strophe, psalm)
79
+ end
80
+
81
+ # takes a Hash of options,
82
+ # returns a list of accordingly initialized Formatter instances
83
+ def get_formatters(options)
84
+ return FORMATTER_ORDER.collect do |f|
85
+ next unless options.include? f
86
+ next unless options[f]
87
+
88
+ get_formatter(f, options[f])
89
+ end.compact!
90
+ end
91
+
92
+ # takes a Symbol - name of a configuration option, and option/s value;
93
+ # returns an instance of a corresponding Formatter class or nil
94
+ def get_formatter(sym, options)
95
+ cls_name = sym.to_s.gsub(/_(\w)/) {|m| m[1].upcase }
96
+ cls_name[0] = cls_name[0].upcase
97
+ cls_name += 'Formatter'
98
+
99
+ if self.class.const_defined? cls_name then
100
+ return self.class.const_get(cls_name).new(options)
101
+ else
102
+ #STDERR.puts "formatter #{cls_name} not found"
103
+ return nil
104
+ end
105
+ end
106
+
107
+ # abstract superclass of the formatters providing dumb implementation
108
+ # of all formatting methods
109
+ class Formatter
110
+
111
+ class << self
112
+ # lets all the :formatters: subsequently format :text: assembled from :obj: on the assembly :level:
113
+ def format(formatters, level, text, *args)
114
+ formatters.each do |f|
115
+ text = f.send("#{level}_format", text, *args)
116
+ end
117
+ return text
118
+ end
119
+ end
120
+
121
+ def initialize(options)
122
+ @options = options
123
+ @syll_counter = 0
124
+ @word_counter = 0
125
+ @part_counter = 0
126
+ @verse_counter = 0
127
+ end
128
+
129
+ def psalm_format(text, psalm)
130
+ @syll_counter = 0
131
+ @word_counter = 0
132
+ @part_counter = 0
133
+ @verse_counter = 0
134
+ text
135
+ end
136
+
137
+ def strophe_format(text, strophe, psalm)
138
+ text
139
+ end
140
+
141
+ def verse_format(text, verse, strophe, psalm)
142
+ @verse_counter += 1
143
+ @syll_counter = 0
144
+ @word_counter = 0
145
+ @part_counter = 0
146
+ text
147
+ end
148
+
149
+ def part_format(text, part, verse, strophe, psalm)
150
+ @part_counter += 1
151
+ @syll_counter = 0
152
+ @word_counter = 0
153
+ text
154
+ end
155
+
156
+ def word_format(text, word, part, verse, strophe, psalm)
157
+ @word_counter += 1
158
+ text
159
+ end
160
+
161
+ def syllable_format(text, syll, word, part, verse, strophe, psalm)
162
+ @syll_counter += 1
163
+ text
164
+ end
165
+ end
166
+
167
+ # marks accentuated and preparatory syllables
168
+ class PointingFormatter < Formatter
169
+ def initialize(options)
170
+ super(options)
171
+ @accent_counter = 0
172
+ @preparatories_counter = 0
173
+
174
+ # validate options
175
+ if @options.has_key? :tone and
176
+ (@options.has_key? :accents or @options.has_key? :preparatory) then
177
+ raise RuntimeError.new('Overconfigured: both accents/preparatories number and psalm tone specified.')
178
+ elsif @options.has_key? :accents then
179
+ # ok, nothing to do
180
+ elsif @options.has_key? :tone
181
+ # convert psalm tone identifier to numbers
182
+ tone = PsalmPatterns.default.tone_data_str(@options[:tone])
183
+ @options[:accents] = tone.collect {|part| part[0] }
184
+ @options[:preparatory] = tone.collect {|part| part[1] }
185
+ end
186
+ end
187
+
188
+ def part_format(text, part, verse, strophe, psalm)
189
+ super(text, part, verse, strophe, psalm)
190
+ @accent_counter = 0
191
+ @preparatories_counter = 0
192
+ text
193
+ end
194
+
195
+ MARKS = {
196
+ :underline => 'underline',
197
+ :bold => 'textbf',
198
+ :semantic => 'accent'
199
+ }
200
+
201
+ def syllable_format(text, syll, word, part, verse, strophe, psalm)
202
+ super(text, syll, word, part, verse, strophe, psalm)
203
+ r = text
204
+ if syll.accent? then
205
+ @accent_counter += 1
206
+ if @accent_counter <= num_accents_for(part) then
207
+ r = "\\#{MARKS[@options[:accent_style]]}{#{r}}"
208
+ end
209
+ end
210
+
211
+ if num_preparatory_syllables_for(part) > 0 and
212
+ @accent_counter >= num_accents_for(part) then
213
+
214
+ if @accent_counter == num_accents_for(part) and
215
+ @preparatories_counter == 1 then
216
+ r = r + "}"
217
+ end
218
+ if @preparatories_counter == num_preparatory_syllables_for(part) then
219
+ r = '\textit{' + r
220
+ end
221
+
222
+ @preparatories_counter += 1
223
+ end
224
+
225
+ return r
226
+ end
227
+
228
+ private
229
+
230
+ # how many accents to mark in this verse-part?
231
+ def num_accents_for(part)
232
+ case part.pos
233
+ when :flex
234
+ 1
235
+ when :first
236
+ @options[:accents][0]
237
+ when :second
238
+ @options[:accents][1]
239
+ end
240
+ end
241
+
242
+ # how many preparatory syllables to mark in this verse-part?
243
+ def num_preparatory_syllables_for(part)
244
+ case part.pos
245
+ when :flex
246
+ 0
247
+ when :first
248
+ @options[:preparatory][0]
249
+ when :second
250
+ @options[:preparatory][1]
251
+ end
252
+ end
253
+ end
254
+
255
+ # inserts break hints between syllables
256
+ class BreakHintsFormatter < Formatter
257
+ def syllable_format(text, syll, word, part, verse, strophe, psalm)
258
+ unless syll == word.syllables.last or
259
+ (word.syllables.size >= 2 and syll == word.syllables[-2] and word.syllables[-1] =~ /^[\.,!?]+$/)
260
+ return text + '\-'
261
+ else
262
+ return text
263
+ end
264
+ end
265
+ end
266
+
267
+ # formatting of verse parts -
268
+ # adds part dividing marks (flex, asterisk),
269
+ # eventually inserts newlines
270
+ class PartsFormatter < Formatter
271
+
272
+ MARKS = {
273
+ :simple => { :flex => '~\dag\mbox{}', :first => '~*', :second => '' },
274
+ :semantic => { :flex => '\flex', :first => '\asterisk', :second => '' },
275
+ :no => { :flex => '', :first => '', :second => '' }
276
+ }
277
+
278
+ def part_format(text, part, verse, strophe, psalm)
279
+ text +
280
+ MARKS[@options[:marks_type]][part.pos] +
281
+ ((@options[:novydvur_newlines] && part.pos != :second) ? "\\\\" : '') # insert two backslashes
282
+ end
283
+ end
284
+
285
+ # wraps the whole psalm in a LaTeX environment
286
+ class WrapperFormatter < Formatter
287
+ def psalm_format(text, psalm)
288
+ "\\begin{#{@options[:environment_name]}}\n" + text + "\n\\end{#{@options[:environment_name]}}\n"
289
+ end
290
+ end
291
+
292
+ class StrophesFormatter < Formatter
293
+
294
+ END_MARKS = {
295
+ :semantic => '\psalmStrophe',
296
+ :simple => '\hspace*{0pt}\hfill--'
297
+ }
298
+
299
+ def initialize(options)
300
+ super
301
+ @options[:paragraph_space] ||= false
302
+ @options[:end_marks] ||= false
303
+ @options[:mark_last_strophe] ||= false
304
+ end
305
+
306
+ def verse_format(text, verse, strophe, psalm)
307
+ if strophe == psalm.strophes.last and not @options[:mark_last_strophe] then
308
+ return text
309
+ end
310
+ if verse != strophe.verses.last then
311
+ return text
312
+ end
313
+
314
+ if @options[:end_marks] then
315
+ text += END_MARKS[@options[:end_marks]]
316
+ end
317
+ if @options[:paragraph_space] then
318
+ text += '\\'
319
+ end
320
+ return text
321
+ end
322
+ end
323
+
324
+ # inserts a newline between verses
325
+ class VersesFormatter < Formatter
326
+ def verse_format(text, verse, strophe, psalm)
327
+ if verse != psalm.verses.last and text != '' then
328
+ return text + "\n"
329
+ else
330
+ return text
331
+ end
332
+ end
333
+ end
334
+
335
+ # skips verses at the beginning
336
+ class SkipVersesFormatter < Formatter
337
+ def initialize(options)
338
+ super(options)
339
+ @skip_verses = @options # takes just one number as a parameter
340
+ end
341
+
342
+ def verse_format(text, verse, strophe, psalm)
343
+ #super(text, psalm, verse)
344
+ @verse_counter += 1
345
+ if @verse_counter <= @skip_verses then
346
+ return ""
347
+ end
348
+
349
+ return text
350
+ end
351
+ end
352
+
353
+ # formats the first word of the first verse as a lettrine
354
+ class LettrineFormatter < Formatter
355
+ def initialize(options)
356
+ super(options)
357
+ @digraphs = []
358
+ if @options and @options[:digraphs] then
359
+ @digraphs = @options[:digraphs]
360
+ end
361
+ @done = false
362
+ end
363
+
364
+ def verse_format(text, verse, strophe, psalm)
365
+ return text if @done
366
+
367
+ @done = true
368
+ return text.sub(/^([^\s]+)/) {
369
+ initial_size = 1
370
+ digraph = @digraphs.find {|d| text.downcase.start_with? d }
371
+ if digraph then
372
+ initial_size = digraph.size
373
+ end
374
+ '\lettrine{'+$1[0...initial_size].upcase+'}{'+$1[initial_size..-1]+'}'
375
+ }
376
+ end
377
+ end
378
+
379
+ # formats title
380
+ class TitleFormatter < Formatter
381
+ TEMPLATE = {
382
+ :no => "", # "" % anything => ""
383
+ :plain => "%s\n\n",
384
+ :semantic => "\\titulusPsalmi{%s}\n\n"
385
+ }
386
+
387
+ def psalm_format(text, psalm)
388
+ if @options[:template].is_a? Symbol then
389
+ return TEMPLATE[@options[:template]] % psalm.header.title +
390
+ text
391
+ elsif @options[:template].is_a? String then
392
+ return @options[:template] % psalm.header.title +
393
+ text
394
+ else
395
+ raise ArgumentError, "Don't know what to do with template of type #{@options[:template].class}."
396
+ end
397
+ end
398
+ end
399
+
400
+ # replaces dumb quotation marks "" by smarter ones
401
+ class QuoteFormatter < Formatter
402
+
403
+ STYLES = {
404
+ :double => ["``", "''"],
405
+ :single => ["'", "'"],
406
+ :guillemets => ['\guillemotright ', '\guillemotleft '],
407
+ :delete => ['', '']
408
+ }
409
+
410
+ def initialize(options)
411
+ super(options)
412
+ @style = @options
413
+ unless STYLES.has_key? @style
414
+ raise "Quotation marks style '#{@style}' unknown."
415
+ end
416
+ @quote_counter = 0
417
+ end
418
+
419
+ def psalm_format(text, psalm)
420
+ super(text, psalm)
421
+ @quote_counter = 0
422
+ text
423
+ end
424
+
425
+ def verse_format(text, verse, strophe, psalm)
426
+ return text.gsub('"') do
427
+ @quote_counter += 1
428
+ if @quote_counter % 2 == 1 then
429
+ STYLES[@style].first
430
+ else
431
+ STYLES[@style].last
432
+ end
433
+ end
434
+ end
435
+ end
436
+
437
+ # appends arbitrary text to the final output
438
+ class FinalAddContentFormatter < Formatter
439
+ def psalm_format(text, psalm)
440
+ super(text, psalm)
441
+ if @options[:append] then
442
+ return text + "\n" + @options[:append]
443
+ else
444
+ return text
445
+ end
446
+ end
447
+ end
448
+ end
449
+ end