arti_mark 0.0.1.beta0
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.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +75 -0
- data/Rakefile +2 -0
- data/arti_mark.gemspec +20 -0
- data/lib/arti_mark/article_parser.rb +12 -0
- data/lib/arti_mark/base_parser.rb +25 -0
- data/lib/arti_mark/block_image_parser.rb +24 -0
- data/lib/arti_mark/command_lexer.rb +58 -0
- data/lib/arti_mark/common_block_parser.rb +34 -0
- data/lib/arti_mark/context.rb +78 -0
- data/lib/arti_mark/definition_list_parser.rb +23 -0
- data/lib/arti_mark/div_parser.rb +12 -0
- data/lib/arti_mark/head_parser.rb +18 -0
- data/lib/arti_mark/list_parser.rb +27 -0
- data/lib/arti_mark/ordered_list_parser.rb +14 -0
- data/lib/arti_mark/paragraph_parser.rb +28 -0
- data/lib/arti_mark/result_holder.rb +74 -0
- data/lib/arti_mark/section_parser.rb +12 -0
- data/lib/arti_mark/syntax.rb +123 -0
- data/lib/arti_mark/universal_block_parser.rb +10 -0
- data/lib/arti_mark/unordered_list_parser.rb +14 -0
- data/lib/arti_mark/version.rb +3 -0
- data/lib/arti_mark.rb +45 -0
- data/memo.txt +22 -0
- data/spec/arti_mark_spec.rb +457 -0
- data/spec/fixture/test_src_ja.arti +48 -0
- data/spec/nokogiri_test_helper.rb +41 -0
- data/spec/spec_helper.rb +29 -0
- metadata +111 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 KOJIMA Satoshi
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# ArtiMark
|
2
|
+
|
3
|
+
ArtiMark is a simple text markup language. It focuses on creating XHTML files for EPUB books. It is optimized for Japanese text for the present.
|
4
|
+
|
5
|
+
**CAUTION This is very early alpha version, so it's not stable at all. Even the markup syntax will change. **
|
6
|
+
|
7
|
+
I hope it will be partly stable by the end of Feburary, 2013
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Note: This gem is not yet released to rubygems.org.
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'arti_mark'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install arti_mark
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
require 'arti_mark'
|
28
|
+
|
29
|
+
document = ArtiMark::Document.new(:lang => 'ja')
|
30
|
+
document.read(string_or_io)
|
31
|
+
put document.result[0] # outputs 1st page of converted XHTML file
|
32
|
+
|
33
|
+
Source text looks like this.
|
34
|
+
|
35
|
+
art {
|
36
|
+
h1: header 1
|
37
|
+
article comes here.
|
38
|
+
linebreak will produce paragraph.
|
39
|
+
|
40
|
+
blank line will procude div.pgroup.
|
41
|
+
|
42
|
+
d.column {
|
43
|
+
This block will produce div.column.
|
44
|
+
Inline commands like [l(http://github.com/skoji/arti_mark/){this}] and [s.strong{this}] is available.
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
It is converted to XHTML like this, sorrounded with appropriate html,head and body tags.
|
49
|
+
|
50
|
+
<article>
|
51
|
+
<h1>header 1</h1>
|
52
|
+
<div class='pgroup'>
|
53
|
+
<p>article comes here.</p>
|
54
|
+
<p>linebreak will produce paragraph.</p>
|
55
|
+
</div>
|
56
|
+
<div class='pgroup'>
|
57
|
+
<p>blank line will produce</p>
|
58
|
+
</div>
|
59
|
+
<div class='column'>
|
60
|
+
<div class='pgroup'>
|
61
|
+
<p>This block will produce div.column.</p>
|
62
|
+
<p>Inline commands like <a href='http://github.com/skoji/arti_mark/'>this</a> and <span class='strong'>this</span> is available.</p>
|
63
|
+
</div>
|
64
|
+
</div>
|
65
|
+
</article>
|
66
|
+
|
67
|
+
In a near future version, you will be able to add custom commands.
|
68
|
+
|
69
|
+
## Contributing
|
70
|
+
|
71
|
+
1. Fork it
|
72
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
73
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
74
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
75
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/arti_mark.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/arti_mark/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["KOJIMA Satoshi"]
|
6
|
+
gem.email = ["skoji@mac.com"]
|
7
|
+
gem.description = %q{simple and customizable text markup language for EPUB}
|
8
|
+
gem.summary = %q{simple and customizable text markup language for EPUB}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "arti_mark"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = ArtiMark::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency "rspec", "~> 2.11"
|
19
|
+
gem.add_development_dependency "nokogiri", "~> 1.5.6"
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module ArtiMark
|
3
|
+
module BaseParser
|
4
|
+
include CommandLexer
|
5
|
+
|
6
|
+
|
7
|
+
def paragraph(line, syntax, cls_array = [])
|
8
|
+
if line =~/^(「|『|()/ # TODO: should be plaggable
|
9
|
+
cls_array << 'noindent'
|
10
|
+
end
|
11
|
+
"<p#{class_string(cls_array)}>#{line}</p>\n"
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_line(line, syntax, context)
|
15
|
+
line = escape_html line
|
16
|
+
line = replace_inline_commands(line, syntax, context)
|
17
|
+
lexed = lex_line_command(line)
|
18
|
+
if !lexed[:cmd].nil? && syntax.linecommand_handler.respond_to?(lexed[:cmd].to_sym)
|
19
|
+
syntax.linecommand_handler.send(lexed[:cmd], lexed, context)
|
20
|
+
else
|
21
|
+
paragraph(line, syntax)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#require 'singleton'
|
2
|
+
|
3
|
+
module ArtiMark
|
4
|
+
class BlockImageParser
|
5
|
+
include BaseParser, Singleton
|
6
|
+
def accept?(lines)
|
7
|
+
lex_line_command(lines[0])[:cmd] =~ /image/
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(lines, r, syntax)
|
11
|
+
lexed = lex_line_command(lines[0])
|
12
|
+
raise 'HeadParser called for #{lines[0]}' unless lexed[:cmd] =~ /image/
|
13
|
+
lines.shift
|
14
|
+
lexed[:cls] << 'img-wrap' if lexed[:cls].size == 0
|
15
|
+
src = lexed[:params][0].strip
|
16
|
+
alt = lexed[:params][1].strip
|
17
|
+
caption = lexed[:text].strip
|
18
|
+
|
19
|
+
r << "<div#{class_string(lexed[:cls])}><img src='#{src}' alt='#{alt}' />"
|
20
|
+
r << "<p>#{caption}</p>" if !caption.nil? && caption.size > 0
|
21
|
+
r << "</div>\n"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module ArtiMark
|
3
|
+
module CommandLexer
|
4
|
+
def escape_html(string)
|
5
|
+
string.to_s.gsub("&", "&").
|
6
|
+
gsub("<", "<").
|
7
|
+
gsub(">", ">").
|
8
|
+
gsub('"', """)
|
9
|
+
end
|
10
|
+
|
11
|
+
def class_string(cls_array)
|
12
|
+
if cls_array.size == 0
|
13
|
+
''
|
14
|
+
else
|
15
|
+
" class='#{cls_array.join(' ')}'"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def class_array(cls_part)
|
20
|
+
cls_array = []
|
21
|
+
if !cls_part.nil? && cls_part.size > 0
|
22
|
+
cls_array = cls_part[1..-1].split('.')
|
23
|
+
end
|
24
|
+
cls_array
|
25
|
+
end
|
26
|
+
|
27
|
+
def param_array(param_part)
|
28
|
+
r = []
|
29
|
+
if !param_part.nil? && param_part.size > 0
|
30
|
+
r = param_part.split(',')
|
31
|
+
end
|
32
|
+
r
|
33
|
+
end
|
34
|
+
|
35
|
+
def lex_line_command(line)
|
36
|
+
line =~ /^([\w\*;]+?)((?:\.[A-Za-z0-9_\-]+?)*)(?:\((.+?)\))?\s*:(.*?)$/
|
37
|
+
return { :cmd => $1, :cls => class_array($2), :params => param_array($3), :text => $4 }
|
38
|
+
end
|
39
|
+
|
40
|
+
def lex_block_command(line)
|
41
|
+
line =~ /^(\w+?)((?:\.[A-Za-z0-9_\-]+?)*)(?:\((.+?)\))?\s*{\s*$/
|
42
|
+
return { :cmd => $1, :cls => class_array($2), :params => param_array($3)}
|
43
|
+
end
|
44
|
+
|
45
|
+
def replace_inline_commands(line, syntax, context)
|
46
|
+
line.gsub(/\[(\w+?)((?:\.[A-Za-z0-9_\-]+?)*)(?:\((.+?)\))?\s*{(.*?)}\]/) {
|
47
|
+
|matched|
|
48
|
+
lexed = {:cmd => $1, :cls => class_array($2), :params => param_array($3), :text => $4 }
|
49
|
+
if !lexed[:cmd].nil?
|
50
|
+
syntax.inline_handler.send(lexed[:cmd], lexed, context)
|
51
|
+
else
|
52
|
+
matched
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module ArtiMark
|
5
|
+
module CommonBlockParser
|
6
|
+
include BaseParser
|
7
|
+
def accept?(lines)
|
8
|
+
lex_block_command(lines[0])[:cmd] =~ @command
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(lines, r, syntax)
|
12
|
+
lexed = lex_block_command(lines.shift)
|
13
|
+
throw 'something wrong here #{lines}' unless lexed[:cmd] =~ @command
|
14
|
+
@markup = lexed[:cmd] if @markup.nil?
|
15
|
+
process_block(lines, r, syntax, lexed[:cls], lexed[:params])
|
16
|
+
end
|
17
|
+
|
18
|
+
def process_block(lines, r, syntax, cls_array, params)
|
19
|
+
previous_pgroup , r.enable_pgroup = r.enable_pgroup , false if params.member? 'wo-pgroup'
|
20
|
+
r << "<#{@markup}#{class_string(cls_array)}>\n"
|
21
|
+
while lines.size > 0
|
22
|
+
if lines[0] == '}'
|
23
|
+
lines.shift
|
24
|
+
break
|
25
|
+
else
|
26
|
+
syntax.determine_parser(lines, :get_default => true).call(lines, r, syntax)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
r << "</#{@markup}>\n"
|
30
|
+
r.enable_pgroup = previous_pgroup if !previous_pgroup.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module ArtiMark
|
2
|
+
class Context
|
3
|
+
attr_accessor :title, :head_inserters, :toc, :stylesheets, :enable_pgroup
|
4
|
+
def initialize(param = {})
|
5
|
+
@head_inserters = []
|
6
|
+
@toc = []
|
7
|
+
@lang = param[:lang] || 'en'
|
8
|
+
@title = param[:title] || 'ArtiMark generated document'
|
9
|
+
@stylesheets = param[:stylesheets] || []
|
10
|
+
@stylesheets_alt = param[:stylesheets_alt] || []
|
11
|
+
@enable_pgroup = param[:enable_pgroup] || true
|
12
|
+
@pages = []
|
13
|
+
head_inserter do
|
14
|
+
ret = ""
|
15
|
+
@stylesheets.each { |s|
|
16
|
+
if s.is_a? String
|
17
|
+
ret << "<link rel=\"stylesheet\" type=\"text/css\" href=\"#{s}\" />\n"
|
18
|
+
elsif s.is_a? Array
|
19
|
+
ret << "<link rel=\"stylesheet\" type=\"text/css\" media=\"#{s[1]}\" href=\"#{s[0]}\" />\n"
|
20
|
+
else
|
21
|
+
raise "Can't use #{s} as a stylesheet"
|
22
|
+
end
|
23
|
+
}
|
24
|
+
ret
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def head_inserter(&block)
|
29
|
+
head_inserters << block
|
30
|
+
end
|
31
|
+
|
32
|
+
def start_html(title = nil)
|
33
|
+
@title = title if !title.nil?
|
34
|
+
if @pages.size >0 && !@pages.last.frozen?
|
35
|
+
end_html
|
36
|
+
end
|
37
|
+
page = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
38
|
+
page << "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"#{@lang}\" xml:lang=\"#{@lang}\">\n"
|
39
|
+
page << "<head>\n"
|
40
|
+
page << "<title>#{@title}</title>\n"
|
41
|
+
@head_inserters.each {
|
42
|
+
|f|
|
43
|
+
page << f.call
|
44
|
+
}
|
45
|
+
page << "</head>\n"
|
46
|
+
page << "<body>\n"
|
47
|
+
@pages << page
|
48
|
+
@toc << title
|
49
|
+
end
|
50
|
+
|
51
|
+
def end_html
|
52
|
+
page = @pages.last
|
53
|
+
if !page.frozen?
|
54
|
+
page << "</body>\n"
|
55
|
+
page << "</html>\n"
|
56
|
+
page.freeze
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def toc=(label)
|
61
|
+
@toc[-1] = label if @toc.size > 0
|
62
|
+
end
|
63
|
+
|
64
|
+
def <<(text)
|
65
|
+
if @pages.size == 0 || @pages.last.frozen?
|
66
|
+
start_html
|
67
|
+
end
|
68
|
+
@pages.last << text
|
69
|
+
end
|
70
|
+
|
71
|
+
def result
|
72
|
+
if !@pages.last.frozen?
|
73
|
+
end_html
|
74
|
+
end
|
75
|
+
@pages
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module ArtiMark
|
5
|
+
class DefinitionListParser
|
6
|
+
include ListParser, Singleton
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@cmd = /;/
|
10
|
+
@blockname = 'dl'
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_block(lines, r, syntax)
|
14
|
+
while lines.size > 0
|
15
|
+
lexed = lex_line_command(lines[0])
|
16
|
+
return unless lexed[:cmd] =~ @cmd
|
17
|
+
dt, dd = lexed[:text].split(':', 2).map(&:strip)
|
18
|
+
r << "<dt>#{escape_html dt}</dt><dd>#{escape_html dd}</dd>\n"
|
19
|
+
lines.shift
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#require 'singleton'
|
2
|
+
|
3
|
+
module ArtiMark
|
4
|
+
class HeadParser
|
5
|
+
include BaseParser, Singleton
|
6
|
+
def accept?(lines)
|
7
|
+
lex_line_command(lines[0])[:cmd] =~ /h[1-6]/
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(lines, r, syntax)
|
11
|
+
lexed = lex_line_command(lines[0])
|
12
|
+
raise 'HeadParser called for #{lines[0]}' unless lexed[:cmd] =~ /h([1-6])/
|
13
|
+
lines.shift
|
14
|
+
r << "<#{lexed[:cmd]}#{class_string(lexed[:cls])}>#{escape_html lexed[:text].strip}</#{lexed[:cmd]}>\n"
|
15
|
+
r.toc = lexed[:text].strip if lexed[:params].member? 'in-toc'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module ArtiMark
|
4
|
+
module ListParser
|
5
|
+
include BaseParser
|
6
|
+
|
7
|
+
def accept?(lines)
|
8
|
+
lex_line_command(lines[0])[:cmd] =~ @cmd
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(lines, r, syntax)
|
12
|
+
lexed = lex_line_command(lines[0])
|
13
|
+
r << "<#{@blockname}#{class_string(lexed[:cls])}>\n"
|
14
|
+
process_block(lines, r, syntax)
|
15
|
+
r << "</#{@blockname}>\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
def process_block(lines, r, syntax)
|
19
|
+
while lines.size > 0
|
20
|
+
lexed = lex_line_command(lines[0])
|
21
|
+
return unless lexed[:cmd] =~ @cmd
|
22
|
+
r << "<li>#{escape_html lexed[:text].strip}</li>\n"
|
23
|
+
lines.shift
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module ArtiMark
|
5
|
+
class ParagraphParser
|
6
|
+
include BaseParser, Singleton
|
7
|
+
|
8
|
+
def accept?(lines)
|
9
|
+
lines[0].size == 0
|
10
|
+
end
|
11
|
+
def parse(lines, context, syntax)
|
12
|
+
lines.shift while lines[0].size == 0
|
13
|
+
return unless syntax.determine_parser(lines).nil?
|
14
|
+
context << process_paragraph_group(lines, '', syntax, context)
|
15
|
+
end
|
16
|
+
|
17
|
+
def process_paragraph_group(lines, paragraph, syntax, context)
|
18
|
+
paragraph << "<div class='pgroup'>\n" if context.enable_pgroup
|
19
|
+
while (lines.size > 0 &&
|
20
|
+
lines[0] != '}' && # TODO: is this correct...?
|
21
|
+
syntax.determine_parser(lines).nil?)
|
22
|
+
paragraph << process_line(lines.shift, syntax, context)
|
23
|
+
end
|
24
|
+
paragraph << "</div>\n" if context.enable_pgroup
|
25
|
+
paragraph
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ArtiMark
|
2
|
+
class Context
|
3
|
+
attr_accessor :title, :head_inserters, :toc
|
4
|
+
def initialize(param = {})
|
5
|
+
@head_inserters = []
|
6
|
+
@toc = []
|
7
|
+
@lang = param[:lang] || 'en'
|
8
|
+
@title = param[:title] || 'ArtiMark generated document'
|
9
|
+
@stylesheets = param[:stylesheets] || []
|
10
|
+
@stylesheets_alt = param[:stylesheets_alt] || []
|
11
|
+
@pages = []
|
12
|
+
head_inserter do
|
13
|
+
ret = ""
|
14
|
+
@stylesheets.each { |s|
|
15
|
+
ret << "<link rel=\"stylesheet\" type=\"text/css\" href=\"#{s}\" />\n"
|
16
|
+
}
|
17
|
+
@stylesheets_alt.each { |s,m|
|
18
|
+
ret << "<link rel=\"stylesheet\" type=\"text/css\" media = \"#{m}\" href=\"#{s}\" />\n"
|
19
|
+
}
|
20
|
+
ret
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def head_inserter(&block)
|
25
|
+
head_inserters << block
|
26
|
+
end
|
27
|
+
|
28
|
+
def start_html(title = nil)
|
29
|
+
@title = title if !title.nil?
|
30
|
+
if @pages.size >0 && !@pages.last.frozen?
|
31
|
+
end_html
|
32
|
+
end
|
33
|
+
page = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
34
|
+
page << "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"#{@lang}\" xml:lang=\"#{@lang}\">\n"
|
35
|
+
page << "<head>\n"
|
36
|
+
page << "<title>#{@title}</title>\n"
|
37
|
+
@head_inserters.each {
|
38
|
+
|f|
|
39
|
+
page << f.call
|
40
|
+
}
|
41
|
+
page << "</head>\n"
|
42
|
+
page << "<body>\n"
|
43
|
+
@pages << page
|
44
|
+
@toc << title
|
45
|
+
end
|
46
|
+
|
47
|
+
def end_html
|
48
|
+
page = @pages.last
|
49
|
+
if !page.frozen?
|
50
|
+
page << "</body>\n"
|
51
|
+
page << "</html>\n"
|
52
|
+
page.freeze
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def toc=(label)
|
57
|
+
@toc[-1] = label if @toc.size > 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def <<(text)
|
61
|
+
if @pages.size == 0 || @pages.last.frozen?
|
62
|
+
start_html
|
63
|
+
end
|
64
|
+
@pages.last << text
|
65
|
+
end
|
66
|
+
|
67
|
+
def result
|
68
|
+
if !@pages.last.frozen?
|
69
|
+
end_html
|
70
|
+
end
|
71
|
+
@pages
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|