Class: Prophecy::Chapter

Inherits:
Object
  • Object
show all
Defined in:
lib/prophecy/chapter.rb

Constant Summary collapse

@@section_name =

Array index corresponds to level, levels index from 0 too. In the .yml you give the level: N attrib as if they were indexed from 1, makes more sense there, but we compensate for it in the class.

[ "Chapter", ]
@@section_number =
[ 0, ]
@@toc_from_headers =
false
@@playOrder =
1
@@the_matter =
'frontmatter'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(book, config) ⇒ Chapter

Returns a new instance of Chapter.



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
# File 'lib/prophecy/chapter.rb', line 22

def initialize(book, config)
  @book = book

  if @book.output_format == 'mobi'
    unless config['level'].nil?
      lvl = config['level'].to_i - 1
      if lvl > 1
        @level = 1
      else
        @level = lvl
      end
    else
      @level = 0
    end
  else
    if config['level'].nil?
      @level = 0
    else
      @level = config['level'].to_i - 1
    end
  end

  if config['the_matter']
    @the_matter = config['the_matter']
    @@the_matter = config['the_matter']
  else
    @the_matter = @@the_matter
  end

  @linear = config['linear'] || nil

  @src = nil
  if config.is_a?(String)
    @src = config
  elsif config.is_a?(Hash) && config.has_key?('src')
    @src = config['src']
  end

  ext_for_output = {
    'epub' => '.xhtml',
    'mobi' => '.xhtml',
    'latex' => '.tex',
    'web' => '.html'
  }

  chapters_dir_for_output = {
    'epub' => File.join('OEBPS', 'Text'),
    'mobi' => File.join('OEBPS', 'Text'),
    'latex' => 'chapters',
    'web' => 'chapters',
  }

  @path = nil
  @href = nil

  unless @src.nil?
    @render_ext = ext_for_output[book.output_format]

    path = nil
    try_folders = [ book.format_dir(@src), 'manuscript' ].uniq
    try_folders.each do |dir|
      path = File.join(dir, @src)
      if File.exists?(path)
        @path = path
        break
      end
    end

    unless @path
      puts "Error. Cannot find #{@src} in folders #{try_folders.join(', ')}"
      exit 2
    end

    @render_name = File.basename(@path).sub(/\.erb$/, '').sub(/\.[^\.]+$/, @render_ext)
    @render_path = File.expand_path(
      File.join(
        self.book.build_dir,
        chapters_dir_for_output[book.output_format],
        @render_name
      )
    )

    a = Pathname.new(self.render_path)
    b = Pathname.new(File.expand_path(
      File.join(
        self.book.build_dir,
        chapters_dir_for_output[book.output_format],
        '..'
      )
    ))
    @href = a.relative_path_from(b)
  end

  @type = config['type'] || nil
  @title = config['title'] || self.first_header_text || ""
  @title = CGI.escapeHTML(@title)
  @class = config['class'] || ""
  @class += " #{@type}" if @type
  @id = config['id'] || 'chapter_' + @title.downcase.gsub(/[^a-z0-9-]/, '-').gsub(/--+/, '-')
  @lang = config['lang'] || book.lang
  @prefix = config['prefix'] || nil
  @postfix = config['postfix'] || nil
  @insert = config['insert'] || nil
  @show_chapter_name = config['show_chapter_name'] || self.book.show_chapter_name || nil
  @chapter_number_format = config['chapter_number_format'] || self.book.chapter_number_format || nil

  if config['section_name']
    @@section_name[@level] = config['section_name']
  else
  end
  if config['section_number']
    @@section_number[@level] = config['section_number'].to_i - 1
  elsif @@section_number[@level].nil?
    @@section_number[@level] = 1
  elsif !@path.nil?
    @@section_number[@level] += 1
  end
  @section_name = @@section_name[@level]
  @section_number = @@section_number[@level]

  @@toc_from_headers = config['toc_from_headers'] unless config['toc_from_headers'].nil?

  @navpoints = []
  if @path
    if @@toc_from_headers
      doc = Nokogiri::HTML(self.to_html)
      headers = doc.xpath("//*[name()='h1' or name()='h2' or name()='h3' or name()='h4']")
      headers.each do |h|
        # skip links (<a href=""> tag) in header (possibly end- or footnote references)
        t = ""
        h.children.select{|i| i.name != 'a'}.each{|s| t += s}
        @navpoints << {
          'text' => CGI.escapeHTML(t),
          'src' => "Text/#{@render_name}##{h.attributes['id']}",
          'playOrder' => @@playOrder,
          'level' => @level,
        }
        @@playOrder += 1
      end
    else
      @navpoints << {
        'text' => @title,
        'src' => "Text/#{@render_name}",
        'playOrder' => @@playOrder,
        'level' => @level,
      }
      @@playOrder += 1
    end
  end

  if config['layout'].nil?
    @layout_path = File.join(book.layouts_dir, book.chapter_layout)
  elsif config['layout'] == 'nil' || config['layout'] == 'none'
    @layout_path = nil
  else
    @layout_path = File.join(book.layouts_dir, config['layout'])
  end
end

Instance Attribute Details

#bookObject

Returns the value of attribute book.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def book
  @book
end

#classObject

Returns the value of attribute class.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def class
  @class
end

#hrefObject

Returns the value of attribute href.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def href
  @href
end

#idObject

Returns the value of attribute id.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def id
  @id
end

#idrefObject

Returns the value of attribute idref.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def idref
  @idref
end

#insertObject

Returns the value of attribute insert.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def insert
  @insert
end

#langObject

Returns the value of attribute lang.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def lang
  @lang
end

#layoutObject

Returns the value of attribute layout.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def layout
  @layout
end

#levelObject

Returns the value of attribute level.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def level
  @level
end

#linearObject

Returns the value of attribute linear.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def linear
  @linear
end

Returns the value of attribute navpoints.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def navpoints
  @navpoints
end

#pathObject

Returns the value of attribute path.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def path
  @path
end

#postfixObject

Returns the value of attribute postfix.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def postfix
  @postfix
end

#prefixObject

Returns the value of attribute prefix.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def prefix
  @prefix
end

#render_nameObject

Returns the value of attribute render_name.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def render_name
  @render_name
end

#render_pathObject

Returns the value of attribute render_path.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def render_path
  @render_path
end

#section_nameObject

Returns the value of attribute section_name.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def section_name
  @section_name
end

#section_numberObject

Returns the value of attribute section_number.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def section_number
  @section_number
end

#srcObject

Returns the value of attribute src.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def src
  @src
end

#titleObject

Returns the value of attribute title.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def title
  @title
end

#typeObject

Returns the value of attribute type.



6
7
8
# File 'lib/prophecy/chapter.rb', line 6

def type
  @type
end

Class Method Details

.section_nameObject



378
379
380
# File 'lib/prophecy/chapter.rb', line 378

def self.section_name
  @@section_name
end

.section_name=(a) ⇒ Object



382
383
384
# File 'lib/prophecy/chapter.rb', line 382

def self.section_name=(a)
  @@section_name = a
end

.section_numberObject



386
387
388
# File 'lib/prophecy/chapter.rb', line 386

def self.section_number
  @@section_number
end

.section_number=(a) ⇒ Object



390
391
392
# File 'lib/prophecy/chapter.rb', line 390

def self.section_number=(a)
  @@section_number = a
end

.the_matterObject



402
403
404
# File 'lib/prophecy/chapter.rb', line 402

def self.the_matter
  @@the_matter
end

.the_matter=(s) ⇒ Object



406
407
408
# File 'lib/prophecy/chapter.rb', line 406

def self.the_matter=(s)
  @@the_matter = s
end

.toc_from_headersObject



394
395
396
# File 'lib/prophecy/chapter.rb', line 394

def self.toc_from_headers
  @@toc_from_headers
end

.toc_from_headers=(b) ⇒ Object



398
399
400
# File 'lib/prophecy/chapter.rb', line 398

def self.toc_from_headers=(b)
  @@toc_from_headers = b
end

Instance Method Details

#backmatter?Boolean

Returns:

  • (Boolean)


374
375
376
# File 'lib/prophecy/chapter.rb', line 374

def backmatter?
  @the_matter == 'backmatter'
end

#chapter_nameObject



351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/prophecy/chapter.rb', line 351

def chapter_name
  n = ''
  case @chapter_number_format
  when 'word'
    n = itow(@section_number)
  when 'arabic'
    n = @section_number
  when 'roman'
    n = RomanNumerals.to_roman(@section_number)
  else
    n = @section_number
  end
  "Chapter #{n}"
end

#first_header_textObject



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/prophecy/chapter.rb', line 187

def first_header_text
  return nil unless @path
  format ||= File.extname(@path)
  case format
  when '.tex'
    str = IO.read(@path)
    m = str.match(/\\chapter\*{,1}[{]([^}]+)[}]/)
    m[1] if m
  else
    doc = Nokogiri::HTML(self.to_html)
    h = doc.xpath("//h1").first
    #h = doc.xpath("//*[name()='h1' or name()='h2' or name()='h3' or name()='h4']").first
    h.text.strip if h
  end
end

#frontmatter?Boolean

Returns:

  • (Boolean)


366
367
368
# File 'lib/prophecy/chapter.rb', line 366

def frontmatter?
  @the_matter == 'frontmatter'
end

#itow(i) ⇒ Object

Opinionated conversion: over 100, chapter titles would be too long. It’s also a good excuse for using a simple array.



416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/prophecy/chapter.rb', line 416

def itow(i)
  return i.to_s if i > 100

  a = [ "Zero", "One", "Two", "Three", "Four", "Five", "Six",
        "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve",
        "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen",
        "Eighteen", "Nineteen", "Twenty", "Twenty-one",
        "Twenty-two", "Twenty-three", "Twenty-four", "Twenty-five",
        "Twenty-six", "Twenty-seven", "Twenty-eight", "Twenty-nine",
        "Thirty", "Thirty-one", "Thirty-two", "Thirty-three",
        "Thirty-four", "Thirty-five", "Thirty-six", "Thirty-seven",
        "Thirty-eight", "Thirty-nine", "Fourty", "Fourty-one",
        "Fourty-two", "Fourty-three", "Fourty-four", "Fourty-five",
        "Fourty-six", "Fourty-seven", "Fourty-eight", "Fourty-nine",
        "Fifty", "Fifty-one", "Fifty-two", "Fifty-three",
        "Fifty-four", "Fifty-five", "Fifty-six", "Fifty-seven",
        "Fifty-eight", "Fifty-nine", "Sixty", "Sixty-one",
        "Sixty-two", "Sixty-three", "Sixty-four", "Sixty-five",
        "Sixty-six", "Sixty-seven", "Sixty-eight", "Sixty-nine",
        "Seventy", "Seventy-one", "Seventy-two", "Seventy-three",
        "Seventy-four", "Seventy-five", "Seventy-six",
        "Seventy-seven", "Seventy-eight", "Seventy-nine", "Eighty",
        "Eighty-one", "Eighty-two", "Eighty-three", "Eighty-four",
        "Eighty-five", "Eighty-six", "Eighty-seven", "Eighty-eight",
        "Eighty-nine", "Ninety", "Ninety-one", "Ninety-two",
        "Ninety-three", "Ninety-four", "Ninety-five", "Ninety-six",
        "Ninety-seven", "Ninety-eight", "Ninety-nine",
        "One-hundred", ]

  a[i.to_i]
end

#mainmatter?Boolean

Returns:

  • (Boolean)


370
371
372
# File 'lib/prophecy/chapter.rb', line 370

def mainmatter?
  @the_matter == 'mainmatter'
end

#to_chapterlistObject



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/prophecy/chapter.rb', line 305

def to_chapterlist
  return "#{@insert}" if @insert
  ret = ""
  ret += @prefix + "\n" if @prefix

  if @src
    ret += "% #{@title}\n" unless @title.empty?
    if File.extname(@src) == '.tex'
      ret += "\\input{" + File.join('..', '..', @book.tex_dir, @src) + "}"
    else
      ret += "\\input{#{@href}}"
    end
  end
  ret += "\n" + @postfix if @postfix
  ret
end

#to_guide_referenceObject



203
204
205
206
207
208
# File 'lib/prophecy/chapter.rb', line 203

def to_guide_reference
  return "" if @type.nil?
  ret = "<reference href=\"#{@href}\" title=\"#{@title.gsub('"', "'")}\""
  ret += " type=\"#{@type}\""
  ret += " />"
end

#to_html(format = nil, text = nil, layout_path = @layout_path) ⇒ Object



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
# File 'lib/prophecy/chapter.rb', line 210

def to_html(format = nil, text = nil, layout_path = @layout_path)
  format ||= File.extname(@path)
  text ||= IO.read(@path)
  # for ERB binding
  book = self.book
  chapter = self

  ret = nil

  case format
  when '.html', '.xhtml'
    ret = text
  when '.md', '.mkd', '.markdown'
    ret = Kramdown::Document.new(text).to_html
    # Rewrite footnote IDs and refs, otherwise Epubcheck complains
    # about colon
    ret = ret.gsub('id="fn:', 'id="fn_').gsub('href="#fn:', 'href="#fn_').gsub('id="fnref:', 'id="fnref_').gsub('href="#fnref:', 'href="#fnref_')
  when '.tex'
    # Is Pandoc installed?
    unless system("pandoc --version > /dev/null 2>&1")
      puts "Error. Pandoc not found, I'll need that for TeX to HTML conversion."
      exit 2
    end
    File.open('./temp-chapter.tex', 'w'){|f| f << text }
    r = system 'pandoc --smart --normalize --from=latex --to=html -o ./temp-chapter.xhtml ./temp-chapter.tex'
    warn "WARNING: pandoc returned non-zero for #{self.to_s}" unless r
    ret = IO.read('./temp-chapter.xhtml')
    FileUtils.rm(['./temp-chapter.tex', './temp-chapter.xhtml'])
  when '.erb'
    template = ERB.new(text)
    fmt = File.extname(@path.sub(/#{format}$/, ''))
    return self.to_html(format = fmt, text = template.result(binding))
  else
    warn "Don't know how to render: #{@src}"
    raise "Error while rendering chapter"
  end

  if @show_chapter_name && self.mainmatter?
    ret = "<h2 class=\"chapter-name\">#{self.chapter_name}</h2>\n" + ret
  end

  unless layout_path.nil?
    template = ERB.new(IO.read(layout_path))
    content = ret
    ret = template.result(binding)
  end

  # remove empty attributes
  ret.gsub!(/ *class=""/, "")

  ret
end

#to_sObject



410
411
412
# File 'lib/prophecy/chapter.rb', line 410

def to_s
  [@title, @path].join(", ")
end

#to_spineitemObject



299
300
301
302
303
# File 'lib/prophecy/chapter.rb', line 299

def to_spineitem
  ret = "<itemref idref=\"#{self.idref}\" "
  ret += "linear=\"#{self.linear}\"" unless self.linear.nil?
  ret += " />"
end

#to_tex(format = nil, text = nil, layout_path = @layout_path) ⇒ Object



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
# File 'lib/prophecy/chapter.rb', line 263

def to_tex(format = nil, text = nil, layout_path = @layout_path)
  format ||= File.extname(@path)
  text ||= IO.read(@path)
  # for ERB binding
  book = self.book
  chapter = self

  ret = nil

  case format
  when '.html', '.xhtml'
    # Is Pandoc installed?
    unless system("pandoc --version > /dev/null 2>&1")
      puts "Error. Pandoc not found, I'll need that for HTML to TeX conversion."
      exit 2
    end
    File.open('./temp-chapter.html', 'w'){|f| f << text }
    system 'pandoc --smart --normalize --chapters --from=html --to=latex -o ./temp-chapter.tex ./temp-chapter.html'
    ret = IO.read('./temp-chapter.tex')
    FileUtils.rm(['./temp-chapter.tex', './temp-chapter.html'])
  when '.md', '.mkd', '.markdown'
    ret = Kramdown::Document.new(text, options = {:latex_headers => %w{chapter section subsection subsubsection paragraph subparagraph}}).to_latex
  when '.tex'
    ret = text
  when '.erb'
    template = ERB.new(text)
    fmt = File.extname(@path.sub(/#{format}$/, ''))
    ret = self.to_tex(format = fmt, text = template.result(binding))
  else
    warn "Don't know how to render: #{@src}"
    raise "Error while rendering chapter"
  end

  ret
end

#to_toc_liObject



322
323
324
# File 'lib/prophecy/chapter.rb', line 322

def to_toc_li
  "<li><a href='../Text/#{@render_name}'><span>#{@title}</span></a></li>"
end

#to_toc_trObject



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/prophecy/chapter.rb', line 326

def to_toc_tr
  ret = ""
  @navpoints.each do |nav|
    ret += "<tr>\n"
    # Section name and number
    ret += "<td class='section'>\n"
    if self.mainmatter? && !@section_name.nil? && !@section_name.empty?
      ret += "#{@section_name} #{@section_number}"
    end
    ret += "</td>\n"
    # Separator
    ret += "<td class='separator'>"
    if self.mainmatter? && !@section_name.nil? && !@section_name.empty?
      ret += " &middot; "
    end
    ret += "</td>\n"
    # Title
    ret += "<td class='title #{@the_matter}'>\n"
    ret += "<a href='#{File.join('..', 'Text', File.basename(nav['src']))}'><span>#{nav['text']}</span></a>\n"
    ret += "</td>\n"
    ret += "</tr>"
  end
  ret
end