lib/asciidoctor-epub3/converter.rb in asciidoctor-epub3-1.5.0.alpha.17 vs lib/asciidoctor-epub3/converter.rb in asciidoctor-epub3-1.5.0.alpha.18
- old
+ new
@@ -218,26 +218,52 @@
series_meta.refine 'dcterms:identifier', series_id unless series_id.nil?
# Calibre only understands 'series'
series_meta.refine 'collection-type', 'series'
end
- add_cover_image node
- add_front_matter_page node
+ # For list of supported landmark types see
+ # https://idpf.github.io/epub-vocabs/structure/
+ landmarks = []
+ cover_page = add_cover_page node
+ landmarks << { type: 'cover', href: cover_page.href, title: 'Cover' } unless cover_page.nil?
+
+ front_matter_page = add_front_matter_page node
+ landmarks << { type: 'frontmatter', href: front_matter_page.href, title: 'Front Matter' } unless front_matter_page.nil?
+
+ nav_item = @book.add_item('nav.xhtml', id: 'nav').nav
+
+ toclevels = [(node.attr 'toclevels', 1).to_i, 0].max
+ outlinelevels = [(node.attr 'outlinelevels', toclevels).to_i, 0].max
+
+ if node.attr? 'toc'
+ toc_item = @book.add_ordered_item 'toc.xhtml', id: 'toc'
+ landmarks << { type: 'toc', href: toc_item.href, title: node.attr('toc-title') }
+ else
+ toc_item = nil
+ end
+
if node.doctype == 'book'
toc_items = node.sections
node.content
else
toc_items = [node]
add_chapter node
end
- nav_xhtml = @book.add_item 'nav.xhtml', content: postprocess_xhtml(nav_doc(node, toc_items)), id: 'nav'
- nav_xhtml.nav
+ landmarks << { type: 'bodymatter', href: %(#{get_chapter_name toc_items[0]}.xhtml), title: 'Start of Content' } unless toc_items.empty?
+ toc_items.each do |item|
+ landmarks << { type: item.style, href: %(#{get_chapter_name item}.xhtml), title: item.title } if %w(appendix bibliography glossary index preface).include? item.style
+ end
+
+ nav_item.add_content postprocess_xhtml(nav_doc(node, toc_items, landmarks, outlinelevels))
+ # User is not supposed to see landmarks, so pass empty array here
+ toc_item&.add_content postprocess_xhtml(nav_doc(node, toc_items, [], toclevels))
+
# NOTE gepub doesn't support building a ncx TOC with depth > 1, so do it ourselves
- toc_ncx = ncx_doc node, toc_items
+ toc_ncx = ncx_doc node, toc_items, outlinelevels
@book.add_item 'toc.ncx', content: toc_ncx.to_ios, id: 'ncx'
docimagesdir = (node.attr 'imagesdir', '.').chomp '/'
docimagesdir = (docimagesdir == '.' ? nil : %(#{docimagesdir}/))
@@ -299,29 +325,26 @@
docid = get_chapter_name node
return nil if docid.nil?
chapter_item = @book.add_ordered_item %(#{docid}.xhtml)
- if node.context == :document && (doctitle = node.doctitle partition: true, use_fallback: true).subtitle?
+ doctitle = node.document.doctitle partition: true, use_fallback: true
+ doctitle_sanitized = sanitize_xml doctitle.combined, :cdata
+
+ if node.context == :document && doctitle.subtitle?
title = %(#{doctitle.main} )
subtitle = doctitle.subtitle
elsif node.title
# HACK: until we get proper handling of title-only in CSS
title = ''
subtitle = get_numbered_title node
+ doctitle_sanitized = sanitize_xml subtitle, :cdata
else
title = nil
subtitle = nil
end
- doctitle_sanitized = (node.document.doctitle sanitize: true, use_fallback: true).to_s
-
- # By default, Kindle does not allow the line height to be adjusted.
- # But if you float the elements, then the line height disappears and can be restored manually using margins.
- # See https://github.com/asciidoctor/asciidoctor-epub3/issues/123
- subtitle_formatted = subtitle ? subtitle.split.map {|w| %(<b>#{w}</b>) } * ' ' : nil
-
if node.document.doctype == 'book'
byline = ''
else
author = node.attr 'author'
username = node.attr 'username', 'default'
@@ -349,20 +372,21 @@
)
end
header = (title || subtitle) ? %(<header>
<div class="chapter-header">
-#{byline}<h1 class="chapter-title">#{title}#{subtitle ? %(<small class="subtitle">#{subtitle_formatted}</small>) : ''}</h1>
+#{byline}<h1 class="chapter-title">#{title}#{subtitle ? %(<small class="subtitle">#{subtitle}</small>) : ''}</h1>
</div>
</header>) : ''
- # TODO : support writing code highlighter CSS to a separate file
- linkcss = false
+ # We want highlighter CSS to be stored in a separate file
+ # in order to avoid style duplication across chapter files
+ linkcss = true
# NOTE kindlegen seems to mangle the <header> element, so we wrap its content in a div
lines = [%(<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="#{lang = node.document.attr 'lang', 'en'}" lang="#{lang}">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xmlns:mml="http://www.w3.org/1998/Math/MathML" xml:lang="#{lang = node.document.attr 'lang', 'en'}" lang="#{lang}">
<head>
<meta charset="UTF-8"/>
<title>#{doctitle_sanitized}</title>
<link rel="stylesheet" type="text/css" href="styles/epub3.css"/>
<link rel="stylesheet" type="text/css" href="styles/epub3-css3-only.css" media="(min-device-width: 0px)"/>
@@ -374,17 +398,16 @@
}
document.body.setAttribute('class', reader.name.toLowerCase().replace(/ /g, '-'));
});
]]></script>)]
- if self.class.supports_highlighter_docinfo? && (syntax_hl = node.document.syntax_highlighter) && (syntax_hl.docinfo? :head)
- lines << (syntax_hl.docinfo :head, node, linkcss: linkcss, self_closing_tag_slash: '/')
- end
+ syntax_hl = node.document.syntax_highlighter
+ lines << (syntax_hl.docinfo :head, node, linkcss: linkcss, self_closing_tag_slash: '/') if syntax_hl&.docinfo? :head
lines << %(</head>
<body>
-<section class="chapter" title="#{doctitle_sanitized.gsub '"', '"'}" epub:type="chapter" id="#{docid}">
+<section class="chapter" title="#{doctitle_sanitized}" epub:type="chapter" id="#{docid}">
#{header}
#{content})
unless (fns = node.document.footnotes - @footnotes).empty?
@footnotes += fns
@@ -403,11 +426,11 @@
</footer>'
end
lines << '</section>'
- lines << (syntax_hl.docinfo :footer, node.document, linkcss: linkcss, self_closing_tag_slash: '/') if syntax_hl && (syntax_hl.docinfo? :footer)
+ lines << (syntax_hl.docinfo :footer, node.document, linkcss: linkcss, self_closing_tag_slash: '/') if syntax_hl&.docinfo? :footer
lines << '</body>
</html>'
chapter_item.add_content postprocess_xhtml lines * LF
@@ -545,11 +568,12 @@
def convert_listing node
nowrap = (node.option? 'nowrap') || !(node.document.attr? 'prewrap')
if node.style == 'source'
lang = node.attr 'language'
- if self.class.supports_highlighter_docinfo? && (syntax_hl = node.document.syntax_highlighter)
+ syntax_hl = node.document.syntax_highlighter
+ if syntax_hl
opts = syntax_hl.highlight? ? {
css_mode: ((doc_attrs = node.document.attributes)[%(#{syntax_hl.name}-css)] || :class).to_sym,
style: doc_attrs[%(#{syntax_hl.name}-style)],
} : {}
opts[:nowrap] = nowrap
@@ -568,13 +592,33 @@
%(<figure class="#{figure_classes * ' '}">#{title_div}
#{syntax_hl ? (syntax_hl.format node, lang, opts) : pre_open + (node.content || '') + pre_close}
</figure>)
end
- # TODO: implement proper stem support. See https://github.com/asciidoctor/asciidoctor-epub3/issues/10
- alias convert_stem convert_listing
+ def convert_stem node
+ return convert_listing node if node.style != 'asciimath' || !asciimath_available?
+ id_attr = node.id ? %( id="#{node.id}") : ''
+ title_element = node.title? ? %(<figcaption>#{node.captioned_title}</figcaption>) : ''
+ equation_data = AsciiMath.parse(node.content).to_mathml 'mml:'
+
+ %(<figure#{id_attr} class="#{prepend_space node.role}">
+#{title_element}
+<div class="content">
+#{equation_data}
+</div>
+</figure>)
+ end
+
+ def asciimath_available?
+ (@asciimath_status ||= load_asciimath) == :loaded
+ end
+
+ def load_asciimath
+ Helpers.require_library('asciimath', true, :warn).nil? ? :unavailable : :loaded
+ end
+
# QUESTION should we wrap the <pre> in either <div> or <figure>?
def convert_literal node
%(<pre class="screen">#{node.content}</pre>)
end
@@ -655,45 +699,40 @@
def convert_table node
lines = [%(<div class="table">)]
lines << %(<div class="content">)
table_id_attr = node.id ? %( id="#{node.id}") : ''
- frame_class = {
- 'all' => 'table-framed',
- 'topbot' => 'table-framed-topbot',
- 'sides' => 'table-framed-sides',
- 'none' => '',
- }
- grid_class = {
- 'all' => 'table-grid',
- 'rows' => 'table-grid-rows',
- 'cols' => 'table-grid-cols',
- 'none' => '',
- }
- table_classes = %W[table #{frame_class[node.attr 'frame'] || frame_class['topbot']} #{grid_class[node.attr 'grid'] || grid_class['rows']}]
+ table_classes = [
+ 'table',
+ %(table-framed-#{node.attr 'frame', 'rows', 'table-frame'}),
+ %(table-grid-#{node.attr 'grid', 'rows', 'table-grid'}),
+ ]
if (role = node.role)
table_classes << role
end
- table_class_attr = %( class="#{table_classes * ' '}")
table_styles = []
- table_styles << %(width: #{node.attr 'tablepcwidth'}%) unless (node.option? 'autowidth') && !(node.attr? 'width', nil, false)
+ if (autowidth = node.option? 'autowidth') && !(node.attr? 'width')
+ table_classes << 'fit-content'
+ elsif (tablewidth = node.attr 'tablepcwidth') == 100
+ table_classes << 'stretch'
+ else
+ table_styles << %(width: #{tablewidth}%;)
+ end
+ table_class_attr = %( class="#{table_classes * ' '}")
table_style_attr = !table_styles.empty? ? %( style="#{table_styles * '; '}") : ''
lines << %(<table#{table_id_attr}#{table_class_attr}#{table_style_attr}>)
lines << %(<caption>#{node.captioned_title}</caption>) if node.title?
if (node.attr 'rowcount') > 0
lines << '<colgroup>'
- #if node.option? 'autowidth'
- tag = %(<col/>)
- node.columns.size.times do
- lines << tag
+ if autowidth
+ lines += (Array.new node.columns.size, %(<col/>))
+ else
+ node.columns.each do |col|
+ lines << ((col.option? 'autowidth') ? %(<col/>) : %(<col style="width: #{col.attr 'colpcwidth'}%;" />))
+ end
end
- #else
- # node.columns.each do |col|
- # lines << %(<col style="width: #{col.attr 'colpcwidth'}%"/>)
- # end
- #end
lines << '</colgroup>'
[:head, :body, :foot].reject {|tsec| node.rows[tsec].empty? }.each do |tsec|
lines << %(<t#{tsec}>)
node.rows[tsec].each do |row|
lines << '<tr>'
@@ -709,23 +748,20 @@
when :literal
cell_content = %(<div class="literal"><pre>#{cell.text}</pre></div>)
else
cell_content = ''
cell.content.each do |text|
- cell_content = %(#{cell_content}<p>#{text}</p>)
+ cell_content = %(#{cell_content}<p class="tableblock">#{text}</p>)
end
end
end
cell_tag_name = tsec == :head || cell.style == :header ? 'th' : 'td'
- cell_classes = []
- if (halign = cell.attr 'halign') && halign != 'left'
- cell_classes << 'halign-left'
- end
- if (halign = cell.attr 'valign') && halign != 'top'
- cell_classes << 'valign-top'
- end
+ cell_classes = [
+ "halign-#{cell.attr 'halign'}",
+ "valign-#{cell.attr 'valign'}",
+ ]
cell_class_attr = !cell_classes.empty? ? %( class="#{cell_classes * ' '}") : ''
cell_colspan_attr = cell.colspan ? %( colspan="#{cell.colspan}") : ''
cell_rowspan_attr = cell.rowspan ? %( rowspan="#{cell.rowspan}") : ''
cell_style_attr = (node.document.attr? 'cellbgcolor') ? %( style="background-color: #{node.document.attr 'cellbgcolor'}") : ''
lines << %(<#{cell_tag_name}#{cell_class_attr}#{cell_colspan_attr}#{cell_rowspan_attr}#{cell_style_attr}>#{cell_content}</#{cell_tag_name}>)
@@ -929,11 +965,11 @@
epub_properties = chapter.attr 'epub-properties'
epub_properties << 'svg' unless epub_properties.include? 'svg'
end
if Asciidoctor::Helpers.uriish? target
- # We need to add both local and remote media files to manifect
+ # We need to add both local and remote media files to manifest
fs_path = nil
else
out_dir = node.attr('outdir', nil, true) || doc_option(node.document, :to_dir)
fs_path = (::File.join out_dir, target)
unless ::File.exist? fs_path
@@ -1174,29 +1210,34 @@
end
def convert_inline_quoted node
open, close, tag = QUOTE_TAGS[node.type]
- # TODO: implement proper stem support. See https://github.com/asciidoctor/asciidoctor-epub3/issues/10
+ if node.type == :asciimath && asciimath_available?
+ content = AsciiMath.parse(node.text).to_mathml 'mml:'
+ else
+ content = node.text
+ end
+
node.add_role 'literal' if [:monospaced, :asciimath, :latexmath].include? node.type
if node.id
class_attr = class_string node
if tag
- %(#{open.chop} id="#{node.id}"#{class_attr}>#{node.text}#{close})
+ %(#{open.chop} id="#{node.id}"#{class_attr}>#{content}#{close})
else
- %(<span id="#{node.id}"#{class_attr}>#{open}#{node.text}#{close}</span>)
+ %(<span id="#{node.id}"#{class_attr}>#{open}#{content}#{close}</span>)
end
elsif role_valid_class? node.role
class_attr = class_string node
if tag
- %(#{open.chop}#{class_attr}>#{node.text}#{close})
+ %(#{open.chop}#{class_attr}>#{content}#{close})
else
- %(<span#{class_attr}>#{open}#{node.text}#{close}</span>)
+ %(<span#{class_attr}>#{open}#{content}#{close}</span>)
end
else
- %(#{open}#{node.text}#{close})
+ %(#{open}#{content}#{close})
end
end
def output_content node
node.content_model == :simple ? %(<p>#{node.content}</p>) : node.content
@@ -1256,10 +1297,23 @@
else
@book.add_item 'styles/epub3.css', content: (postprocess_css_file ::File.join(workdir, 'epub3.css'), format)
@book.add_item 'styles/epub3-css3-only.css', content: (postprocess_css_file ::File.join(workdir, 'epub3-css3-only.css'), format)
end
+ syntax_hl = doc.syntax_highlighter
+ if syntax_hl&.write_stylesheet? doc
+ Dir.mktmpdir do |dir|
+ syntax_hl.write_stylesheet doc, dir
+ Pathname.glob(dir + '/**/*').map do |filename|
+ # Workaround for https://github.com/skoji/gepub/pull/117
+ filename.open do |f|
+ @book.add_item filename.basename.to_s, content: f
+ end if filename.file?
+ end
+ end
+ end
+
font_files, font_css = select_fonts ::File.join(DATA_DIR, 'styles/epub3-fonts.css'), (doc.attr 'scripts', 'latin')
@book.add_item 'styles/epub3-fonts.css', content: font_css
unless font_files.empty?
# NOTE metadata property in oepbs package manifest doesn't work; must use proprietary iBooks file instead
#(@book.metadata.add_metadata 'meta', 'true')['property'] = 'ibooks:specified-fonts' unless format == :kf8
@@ -1275,12 +1329,12 @@
end
end
nil
end
- def add_cover_image doc
- return if (image_path = doc.attr 'front-cover-image').nil?
+ def add_cover_page doc
+ return nil if (image_path = doc.attr 'front-cover-image').nil?
imagesdir = (doc.attr 'imagesdir', '.').chomp '/'
imagesdir = (imagesdir == '.' ? '' : %(#{imagesdir}/))
image_attrs = {}
@@ -1293,24 +1347,25 @@
image_href = %(#{imagesdir}jacket/cover#{::File.extname image_path})
workdir = doc.attr 'docdir'
workdir = '.' if workdir.nil_or_empty?
- unless ::File.readable? ::File.join(workdir, image_path)
- logger.error %(#{::File.basename doc.attr('docfile')}: front cover image not found or readable: #{::File.expand_path image_path, workdir})
- return
+ begin
+ @book.add_item(image_href, content: File.join(workdir, image_path)).cover_image
+ rescue => e
+ logger.error %(#{::File.basename doc.attr('docfile')}: error adding front cover image. Make sure that :front-cover-image: attribute points to a valid image file. #{e})
+ return nil
end
+ return nil if @format == :kf8
+
unless !image_attrs.empty? && (width = image_attrs['width']) && (height = image_attrs['height'])
width, height = 1050, 1600
end
- @book.add_item(image_href, content: File.join(workdir, image_path)).cover_image
-
- unless @format == :kf8
- # NOTE SVG wrapper maintains aspect ratio and confines image to view box
- content = %(<!DOCTYPE html>
+ # NOTE SVG wrapper maintains aspect ratio and confines image to view box
+ content = %(<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en" lang="en">
<head>
<meta charset="UTF-8"/>
<title>#{sanitize_doctitle_xml doc, :cdata}</title>
<style type="text/css">
@@ -1336,14 +1391,11 @@
width="100%" height="100%" viewBox="0 0 #{width} #{height}" preserveAspectRatio="xMidYMid meet">
<image width="#{width}" height="#{height}" xlink:href="#{image_href}"/>
</svg></body>
</html>).to_ios
- # Gitden expects a cover.xhtml, so add it to the spine
- @book.add_ordered_item 'cover.xhtml', content: content, id: 'cover'
- end
- nil
+ @book.add_ordered_item 'cover.xhtml', content: content, id: 'cover'
end
def get_frontmatter_files doc, workdir
if doc.attr? 'epub3-frontmatterdir'
fmdir = doc.attr 'epub3-frontmatterdir'
@@ -1369,23 +1421,26 @@
def add_front_matter_page doc
workdir = doc.attr 'docdir'
workdir = '.' if workdir.nil_or_empty?
+ result = nil
get_frontmatter_files(doc, workdir).each do |front_matter|
front_matter_content = ::File.read front_matter
front_matter_file = File.basename front_matter, '.html'
item = @book.add_ordered_item "#{front_matter_file}.xhtml", content: (postprocess_xhtml front_matter_content)
item.add_property 'svg' if SvgImgSniffRx =~ front_matter_content
+ # Store link to first frontmatter page
+ result = item if result.nil?
front_matter_content.scan ImgSrcScanRx do
@book.add_item $1, content: File.join(File.dirname(front_matter), $1)
end
end
- nil
+ result
end
def add_profile_images doc, usernames
imagesdir = (doc.attr 'imagesdir', '.').chomp '/'
imagesdir = (imagesdir == '.' ? nil : %(#{imagesdir}/))
@@ -1413,28 +1468,43 @@
end
end
nil
end
- # TODO: aggregate authors of chapters into authors attribute(s) on main document
- def nav_doc doc, items
+ def nav_doc doc, items, landmarks, depth
lines = [%(<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="#{lang = doc.attr 'lang', 'en'}" lang="#{lang}">
<head>
<meta charset="UTF-8"/>
<title>#{sanitize_doctitle_xml doc, :cdata}</title>
<link rel="stylesheet" type="text/css" href="styles/epub3.css"/>
<link rel="stylesheet" type="text/css" href="styles/epub3-css3-only.css" media="(min-device-width: 0px)"/>
</head>
<body>
-<h1>#{sanitize_doctitle_xml doc, :pcdata}</h1>
-<nav epub:type="toc" id="toc">
-<h2>#{doc.attr 'toc-title'}</h2>)]
- lines << (nav_level items, [(doc.attr 'toclevels', 1).to_i, 0].max)
- lines << %(</nav>
+<section class="chapter">
+<header>
+<div class="chapter-header"><h1 class="chapter-title"><small class="subtitle">#{doc.attr 'toc-title'}</small></h1></div>
+</header>
+<nav epub:type="toc" id="toc">)]
+ lines << (nav_level items, [depth, 0].max)
+ lines << '</nav>'
+
+ unless landmarks.empty?
+ lines << '
+<nav epub:type="landmarks" id="landmarks" hidden="hidden">
+<ol>'
+ landmarks.each do |landmark|
+ lines << %(<li><a epub:type="#{landmark[:type]}" href="#{landmark[:href]}">#{landmark[:title]}</a></li>)
+ end
+ lines << '
+</ol>
+</nav>'
+ end
+ lines << '
+</section>
</body>
-</html>)
+</html>'
lines * LF
end
def nav_level items, depth, state = {}
lines = []
@@ -1464,11 +1534,11 @@
end
lines << '</ol>'
lines * LF
end
- def ncx_doc doc, items
+ def ncx_doc doc, items, depth
# TODO: populate docAuthor element based on unique authors in work
lines = [%(<?xml version="1.0" encoding="utf-8"?>
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="#{doc.attr 'lang', 'en'}">
<head>
<meta name="dtb:uid" content="#{@book.identifier}"/>
@@ -1476,11 +1546,11 @@
<meta name="dtb:totalPageCount" content="0"/>
<meta name="dtb:maxPageNumber" content="0"/>
</head>
<docTitle><text>#{sanitize_doctitle_xml doc, :cdata}</text></docTitle>
<navMap>)]
- lines << (ncx_level items, [(doc.attr 'toclevels', 1).to_i, 0].max, state = {})
+ lines << (ncx_level items, depth, state = {})
lines[0] = lines[0].sub '%{depth}', %(<meta name="dtb:depth" content="#{state[:max_depth]}"/>)
lines << %(</navMap>
</ncx>)
lines * LF
end
@@ -1672,18 +1742,10 @@
# Handles asciidoctor 1.5.6 quirk when role can be parent
def role_valid_class? role
role.is_a? String
end
-
- class << self
- def supports_highlighter_docinfo?
- # Asciidoctor only got pluggable syntax highlighters since 2.0:
- # https://github.com/asciidoctor/asciidoctor/commit/23ddbaed6818025cbe74365fec7e8101f34eadca
- Asciidoctor::Document.method_defined? :syntax_highlighter
- end
- end
end
class DocumentIdGenerator
ReservedIds = %w(cover nav ncx)
CharRefRx = /&(?:([a-zA-Z][a-zA-Z]+\d{0,2})|#(\d\d\d{0,4})|#x([\da-fA-F][\da-fA-F][\da-fA-F]{0,3}));/
@@ -1692,10 +1754,11 @@
LeadingDigitRx = /^\p{Nd}/
else
InvalidIdCharsRx = /[^[:word:]]+/
LeadingDigitRx = /^[[:digit:]]/
end
+
class << self
def generate_id doc, pre = nil, sep = nil
synthetic = false
unless (id = doc.id)
# NOTE we assume pre is a valid ID prefix and that pre and sep only contain valid ID chars
@@ -1747,10 +1810,13 @@
document.set_attribute 'listing-caption', 'Listing'
# TODO: bw theme for CodeRay
document.set_attribute 'pygments-style', 'bw' unless document.attr? 'pygments-style'
document.set_attribute 'rouge-style', 'bw' unless document.attr? 'rouge-style'
- unless Converter.supports_highlighter_docinfo?
+
+ # Old asciidoctor versions do not have public API for writing highlighter CSS file
+ # So just use inline CSS there.
+ unless Document.supports_syntax_highlighter?
document.set_attribute 'coderay-css', 'style'
document.set_attribute 'pygments-css', 'style'
document.set_attribute 'rouge-css', 'style'
end