isodoc 1.6.3 → 1.6.4
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.
- checksums.yaml +4 -4
- data/.github/workflows/rake.yml +2 -12
- data/.hound.yml +3 -1
- data/.rubocop.yml +3 -5
- data/isodoc.gemspec +1 -1
- data/lib/isodoc.rb +0 -2
- data/lib/isodoc/convert.rb +4 -0
- data/lib/isodoc/function/cleanup.rb +52 -43
- data/lib/isodoc/function/inline.rb +7 -7
- data/lib/isodoc/function/section.rb +29 -16
- data/lib/isodoc/function/to_word_html.rb +6 -3
- data/lib/isodoc/function/utils.rb +181 -163
- data/lib/isodoc/headlesshtml_convert.rb +8 -7
- data/lib/isodoc/html_function/comments.rb +2 -0
- data/lib/isodoc/html_function/footnotes.rb +14 -7
- data/lib/isodoc/html_function/html.rb +30 -26
- data/lib/isodoc/html_function/postprocess.rb +16 -13
- data/lib/isodoc/pdf_convert.rb +11 -13
- data/lib/isodoc/presentation_function/bibdata.rb +43 -22
- data/lib/isodoc/presentation_function/inline.rb +20 -15
- data/lib/isodoc/version.rb +1 -1
- data/lib/isodoc/word_function/postprocess.rb +50 -36
- data/lib/isodoc/xref/xref_gen_seq.rb +60 -35
- data/lib/isodoc/xref/xref_sect_gen.rb +4 -4
- data/spec/assets/scripts_override.html +3 -0
- data/spec/isodoc/blocks_spec.rb +196 -245
- data/spec/isodoc/inline_spec.rb +442 -289
- data/spec/isodoc/postproc_spec.rb +111 -50
- data/spec/isodoc/presentation_xml_spec.rb +354 -277
- data/spec/isodoc/terms_spec.rb +83 -83
- metadata +5 -4
| @@ -3,6 +3,7 @@ module IsoDoc::HtmlFunction | |
| 3 3 |  | 
| 4 4 | 
             
                def footnotes(div)
         | 
| 5 5 | 
             
                  return if @footnotes.empty?
         | 
| 6 | 
            +
             | 
| 6 7 | 
             
                  @footnotes.each { |fn| div.parent << fn }
         | 
| 7 8 | 
             
                end
         | 
| 8 9 |  | 
| @@ -44,15 +45,19 @@ module IsoDoc::HtmlFunction | |
| 44 45 | 
             
                def get_table_ancestor_id(node)
         | 
| 45 46 | 
             
                  table = node.ancestors("table") || node.ancestors("figure")
         | 
| 46 47 | 
             
                  return UUIDTools::UUID.random_create.to_s if table.empty?
         | 
| 48 | 
            +
             | 
| 47 49 | 
             
                  table.last["id"]
         | 
| 48 50 | 
             
                end
         | 
| 49 51 |  | 
| 52 | 
            +
                # @seen_footnote:
         | 
| 53 | 
            +
                # do not output footnote text if we have already seen it for this table
         | 
| 54 | 
            +
             | 
| 50 55 | 
             
                def table_footnote_parse(node, out)
         | 
| 51 56 | 
             
                  fn = node["reference"] || UUIDTools::UUID.random_create.to_s
         | 
| 52 57 | 
             
                  tid = get_table_ancestor_id(node)
         | 
| 53 58 | 
             
                  make_table_footnote_link(out, tid + fn, fn)
         | 
| 54 | 
            -
                  # do not output footnote text if we have already seen it for this table
         | 
| 55 59 | 
             
                  return if @seen_footnote.include?(tid + fn)
         | 
| 60 | 
            +
             | 
| 56 61 | 
             
                  @in_footnote = true
         | 
| 57 62 | 
             
                  out.aside **{ class: "footnote" } do |a|
         | 
| 58 63 | 
             
                    a << make_table_footnote_text(node, tid + fn, fn)
         | 
| @@ -62,8 +67,9 @@ module IsoDoc::HtmlFunction | |
| 62 67 | 
             
                end
         | 
| 63 68 |  | 
| 64 69 | 
             
                def footnote_parse(node, out)
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                    !node.ancestors.map | 
| 70 | 
            +
                  return table_footnote_parse(node, out) if (@in_table || @in_figure) &&
         | 
| 71 | 
            +
                    !node.ancestors.map(&:name).include?("name")
         | 
| 72 | 
            +
             | 
| 67 73 | 
             
                  fn = node["reference"] || UUIDTools::UUID.random_create.to_s
         | 
| 68 74 | 
             
                  attrs = { class: "FootnoteRef", href: "#fn:#{fn}" }
         | 
| 69 75 | 
             
                  out.a **attrs do |a|
         | 
| @@ -72,12 +78,13 @@ module IsoDoc::HtmlFunction | |
| 72 78 | 
             
                  make_footnote(node, fn)
         | 
| 73 79 | 
             
                end
         | 
| 74 80 |  | 
| 75 | 
            -
                def make_footnote(node,  | 
| 76 | 
            -
                  return if @seen_footnote.include?( | 
| 81 | 
            +
                def make_footnote(node, fnote)
         | 
| 82 | 
            +
                  return if @seen_footnote.include?(fnote)
         | 
| 83 | 
            +
             | 
| 77 84 | 
             
                  @in_footnote = true
         | 
| 78 | 
            -
                  @footnotes << make_generic_footnote_text(node,  | 
| 85 | 
            +
                  @footnotes << make_generic_footnote_text(node, fnote)
         | 
| 79 86 | 
             
                  @in_footnote = false
         | 
| 80 | 
            -
                  @seen_footnote <<  | 
| 87 | 
            +
                  @seen_footnote << fnote
         | 
| 81 88 | 
             
                end
         | 
| 82 89 | 
             
              end
         | 
| 83 90 | 
             
            end
         | 
| @@ -5,9 +5,9 @@ module IsoDoc::HtmlFunction | |
| 5 5 | 
             
              module Html
         | 
| 6 6 | 
             
                def convert1(docxml, filename, dir)
         | 
| 7 7 | 
             
                  noko do |xml|
         | 
| 8 | 
            -
                    xml.html **{ lang:  | 
| 8 | 
            +
                    xml.html **{ lang: @lang.to_s } do |html|
         | 
| 9 9 | 
             
                      info docxml, nil
         | 
| 10 | 
            -
                      populate_css | 
| 10 | 
            +
                      populate_css
         | 
| 11 11 | 
             
                      html.head { |head| define_head head, filename, dir }
         | 
| 12 12 | 
             
                      make_body(html, docxml)
         | 
| 13 13 | 
             
                    end
         | 
| @@ -15,13 +15,17 @@ module IsoDoc::HtmlFunction | |
| 15 15 | 
             
                end
         | 
| 16 16 |  | 
| 17 17 | 
             
                def make_body1(body, _docxml)
         | 
| 18 | 
            +
                  return if @bare
         | 
| 19 | 
            +
             | 
| 18 20 | 
             
                  body.div **{ class: "title-section" } do |div1|
         | 
| 19 21 | 
             
                    div1.p { |p| p << " " } # placeholder
         | 
| 20 22 | 
             
                  end
         | 
| 21 23 | 
             
                  section_break(body)
         | 
| 22 24 | 
             
                end
         | 
| 23 25 |  | 
| 24 | 
            -
                def make_body2(body,  | 
| 26 | 
            +
                def make_body2(body, _docxml)
         | 
| 27 | 
            +
                  return if @bare
         | 
| 28 | 
            +
             | 
| 25 29 | 
             
                  body.div **{ class: "prefatory-section" } do |div2|
         | 
| 26 30 | 
             
                    div2.p { |p| p << " " } # placeholder
         | 
| 27 31 | 
             
                  end
         | 
| @@ -43,46 +47,47 @@ module IsoDoc::HtmlFunction | |
| 43 47 | 
             
                  end
         | 
| 44 48 | 
             
                end
         | 
| 45 49 |  | 
| 46 | 
            -
                def googlefonts | 
| 50 | 
            +
                def googlefonts
         | 
| 47 51 | 
             
                  <<~HEAD.freeze
         | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 52 | 
            +
                    <link href="https://fonts.googleapis.com/css?family=Overpass:300,300i,600,900" rel="stylesheet">
         | 
| 53 | 
            +
                    <link href="https://fonts.googleapis.com/css?family=Lato:400,400i,700,900" rel="stylesheet">
         | 
| 50 54 | 
             
                  HEAD
         | 
| 51 55 | 
             
                end
         | 
| 52 56 |  | 
| 53 | 
            -
                def html_head | 
| 57 | 
            +
                def html_head
         | 
| 54 58 | 
             
                  <<~HEAD.freeze
         | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 59 | 
            +
                    <title>#{@meta&.get&.dig(:doctitle)}</title>
         | 
| 60 | 
            +
                    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    <!--TOC script import-->
         | 
| 63 | 
            +
                    <script type="text/javascript"  src="https://cdn.rawgit.com/jgallen23/toc/0.3.2/dist/toc.min.js"></script>
         | 
| 64 | 
            +
                    <script type="text/javascript">#{toclevel}</script>
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    <!--Google fonts-->
         | 
| 67 | 
            +
                      <link rel="preconnect" href="https://fonts.gstatic.com">#{' '}
         | 
| 68 | 
            +
                      #{googlefonts}
         | 
| 69 | 
            +
                    <!--Font awesome import for the link icon-->
         | 
| 70 | 
            +
                    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/solid.css" integrity="sha384-v2Tw72dyUXeU3y4aM2Y0tBJQkGfplr39mxZqlTBDUZAb9BGoC40+rdFCG0m10lXk" crossorigin="anonymous">
         | 
| 71 | 
            +
                    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/fontawesome.css" integrity="sha384-q3jl8XQu1OpdLgGFvNRnPdj5VIlCvgsDQTQB6owSOHWlAurxul7f+JpUOVdAiJ5P" crossorigin="anonymous">
         | 
| 72 | 
            +
                    <style class="anchorjs"></style>
         | 
| 69 73 | 
             
                  HEAD
         | 
| 70 74 | 
             
                end
         | 
| 71 75 |  | 
| 72 | 
            -
                def html_button | 
| 76 | 
            +
                def html_button
         | 
| 73 77 | 
             
                  '<button onclick="topFunction()" id="myBtn" '\
         | 
| 74 78 | 
             
                    'title="Go to top">Top</button>'.freeze
         | 
| 75 79 | 
             
                end
         | 
| 76 80 |  | 
| 77 81 | 
             
                def html_main(docxml)
         | 
| 78 | 
            -
                  docxml.at("//head").add_child(html_head | 
| 82 | 
            +
                  docxml.at("//head").add_child(html_head)
         | 
| 79 83 | 
             
                  d = docxml.at('//div[@class="main-section"]')
         | 
| 80 84 | 
             
                  d.name = "main"
         | 
| 81 | 
            -
                  d.children.empty? or d.children.first.previous = html_button | 
| 85 | 
            +
                  d.children.empty? or d.children.first.previous = html_button
         | 
| 82 86 | 
             
                end
         | 
| 83 87 |  | 
| 84 88 | 
             
                def sourcecodelang(lang)
         | 
| 85 89 | 
             
                  return unless lang
         | 
| 90 | 
            +
             | 
| 86 91 | 
             
                  case lang.downcase
         | 
| 87 92 | 
             
                  when "javascript" then "lang-js"
         | 
| 88 93 | 
             
                  when "c" then "lang-c"
         | 
| @@ -117,7 +122,6 @@ module IsoDoc::HtmlFunction | |
| 117 122 | 
             
                  end
         | 
| 118 123 | 
             
                end
         | 
| 119 124 |  | 
| 120 | 
            -
                def table_long_strings_cleanup(docxml)
         | 
| 121 | 
            -
                end
         | 
| 125 | 
            +
                def table_long_strings_cleanup(docxml); end
         | 
| 122 126 | 
             
              end
         | 
| 123 127 | 
             
            end
         | 
| @@ -29,15 +29,15 @@ module IsoDoc::HtmlFunction | |
| 29 29 | 
             
                    .sub(%r{<\?xml[^>]+>}, "")
         | 
| 30 30 | 
             
                end
         | 
| 31 31 |  | 
| 32 | 
            -
                def html_cleanup( | 
| 32 | 
            +
                def html_cleanup(html)
         | 
| 33 33 | 
             
                  mathml(
         | 
| 34 34 | 
             
                    footnote_format(
         | 
| 35 35 | 
             
                      footnote_backlinks(
         | 
| 36 36 | 
             
                        html_toc(
         | 
| 37 | 
            -
                          term_header(html_footnote_filter(html_preface(htmlstyle( | 
| 38 | 
            -
                        )
         | 
| 39 | 
            -
                      )
         | 
| 40 | 
            -
                    )
         | 
| 37 | 
            +
                          term_header(html_footnote_filter(html_preface(htmlstyle(html)))),
         | 
| 38 | 
            +
                        ),
         | 
| 39 | 
            +
                      ),
         | 
| 40 | 
            +
                    ),
         | 
| 41 41 | 
             
                  )
         | 
| 42 42 | 
             
                end
         | 
| 43 43 |  | 
| @@ -60,7 +60,6 @@ module IsoDoc::HtmlFunction | |
| 60 60 | 
             
                def htmlstyle(docxml)
         | 
| 61 61 | 
             
                  return docxml unless @htmlstylesheet
         | 
| 62 62 |  | 
| 63 | 
            -
                  title = docxml.at("//*[local-name() = 'head']/*[local-name() = 'title']")
         | 
| 64 63 | 
             
                  head = docxml.at("//*[local-name() = 'head']")
         | 
| 65 64 | 
             
                  head << htmlstylesheet(@htmlstylesheet)
         | 
| 66 65 | 
             
                  s = htmlstylesheet(@htmlstylesheet_override) and head << s
         | 
| @@ -68,8 +67,8 @@ module IsoDoc::HtmlFunction | |
| 68 67 | 
             
                end
         | 
| 69 68 |  | 
| 70 69 | 
             
                def html_preface(docxml)
         | 
| 71 | 
            -
                  html_cover(docxml) if @htmlcoverpage
         | 
| 72 | 
            -
                  html_intro(docxml) if @htmlintropage
         | 
| 70 | 
            +
                  html_cover(docxml) if @htmlcoverpage && !@bare
         | 
| 71 | 
            +
                  html_intro(docxml) if @htmlintropage && !@bare
         | 
| 73 72 | 
             
                  docxml.at("//body") << mathjax(@openmathdelim, @closemathdelim)
         | 
| 74 73 | 
             
                  docxml.at("//body") << sourcecode_highlighter
         | 
| 75 74 | 
             
                  html_main(docxml)
         | 
| @@ -79,7 +78,8 @@ module IsoDoc::HtmlFunction | |
| 79 78 |  | 
| 80 79 | 
             
                def authority_cleanup1(docxml, klass)
         | 
| 81 80 | 
             
                  dest = docxml.at("//div[@id = 'boilerplate-#{klass}-destination']")
         | 
| 82 | 
            -
                  auth = docxml.at("//div[@id = 'boilerplate-#{klass}' or  | 
| 81 | 
            +
                  auth = docxml.at("//div[@id = 'boilerplate-#{klass}' or "\
         | 
| 82 | 
            +
                                   "@class = 'boilerplate-#{klass}']")
         | 
| 83 83 | 
             
                  auth&.xpath(".//h1[not(text())] | .//h2[not(text())]")&.each(&:remove)
         | 
| 84 84 | 
             
                  auth&.xpath(".//h1 | .//h2")&.each { |h| h["class"] = "IntroTitle" }
         | 
| 85 85 | 
             
                  dest and auth and dest.replace(auth.remove)
         | 
| @@ -96,7 +96,7 @@ module IsoDoc::HtmlFunction | |
| 96 96 | 
             
                  d = docxml.at('//div[@class="title-section"]')
         | 
| 97 97 | 
             
                  # d.children.first.add_previous_sibling doc.to_xml(encoding: "US-ASCII")
         | 
| 98 98 | 
             
                  d.children.first.add_previous_sibling(
         | 
| 99 | 
            -
                    populate_template(doc.to_xml(encoding: "US-ASCII"), :html)
         | 
| 99 | 
            +
                    populate_template(doc.to_xml(encoding: "US-ASCII"), :html),
         | 
| 100 100 | 
             
                  )
         | 
| 101 101 | 
             
                end
         | 
| 102 102 |  | 
| @@ -105,7 +105,7 @@ module IsoDoc::HtmlFunction | |
| 105 105 | 
             
                  d = docxml.at('//div[@class="prefatory-section"]')
         | 
| 106 106 | 
             
                  # d.children.first.add_previous_sibling doc.to_xml(encoding: "US-ASCII")
         | 
| 107 107 | 
             
                  d.children.first.add_previous_sibling(
         | 
| 108 | 
            -
                    populate_template(doc.to_xml(encoding: "US-ASCII"), :html)
         | 
| 108 | 
            +
                    populate_template(doc.to_xml(encoding: "US-ASCII"), :html),
         | 
| 109 109 | 
             
                  )
         | 
| 110 110 | 
             
                end
         | 
| 111 111 |  | 
| @@ -149,7 +149,7 @@ module IsoDoc::HtmlFunction | |
| 149 149 | 
             
                  docxml.xpath("//*[local-name() = 'img']").each do |i|
         | 
| 150 150 | 
             
                    i["width"], i["height"] = Html2Doc.image_resize(i, image_localfile(i),
         | 
| 151 151 | 
             
                                                                    @maxheight, @maxwidth)
         | 
| 152 | 
            -
                    next if /^data:/.match i["src"]
         | 
| 152 | 
            +
                    next if /^data:/.match? i["src"]
         | 
| 153 153 |  | 
| 154 154 | 
             
                    @datauriimage ? datauri(i) : move_image1(i)
         | 
| 155 155 | 
             
                  end
         | 
| @@ -187,8 +187,11 @@ module IsoDoc::HtmlFunction | |
| 187 187 | 
             
                  return doc unless @scripts
         | 
| 188 188 |  | 
| 189 189 | 
             
                  scripts = File.read(@scripts, encoding: "UTF-8")
         | 
| 190 | 
            +
                  scripts_override = ""
         | 
| 191 | 
            +
                  @scripts_override and
         | 
| 192 | 
            +
                    scripts_override = File.read(@scripts_override, encoding: "UTF-8")
         | 
| 190 193 | 
             
                  a = doc.split(%r{</body>})
         | 
| 191 | 
            -
                  a[0] | 
| 194 | 
            +
                  "#{a[0]}#{scripts}#{scripts_override}</body>#{a[1]}"
         | 
| 192 195 | 
             
                end
         | 
| 193 196 |  | 
| 194 197 | 
             
                def sourcecode_highlighter
         | 
    
        data/lib/isodoc/pdf_convert.rb
    CHANGED
    
    | @@ -1,11 +1,10 @@ | |
| 1 | 
            -
            require_relative "html_function/comments | 
| 2 | 
            -
            require_relative "html_function/footnotes | 
| 3 | 
            -
            require_relative "html_function/html | 
| 1 | 
            +
            require_relative "html_function/comments"
         | 
| 2 | 
            +
            require_relative "html_function/footnotes"
         | 
| 3 | 
            +
            require_relative "html_function/html"
         | 
| 4 4 | 
             
            require "metanorma"
         | 
| 5 5 |  | 
| 6 6 | 
             
            module IsoDoc
         | 
| 7 7 | 
             
              class PdfConvert < ::IsoDoc::Convert
         | 
| 8 | 
            -
             | 
| 9 8 | 
             
                include HtmlFunction::Comments
         | 
| 10 9 | 
             
                include HtmlFunction::Footnotes
         | 
| 11 10 | 
             
                include HtmlFunction::Html
         | 
| @@ -13,6 +12,8 @@ module IsoDoc | |
| 13 12 | 
             
                def initialize(options)
         | 
| 14 13 | 
             
                  @standardstylesheet = nil
         | 
| 15 14 | 
             
                  super
         | 
| 15 | 
            +
                  @format = :pdf
         | 
| 16 | 
            +
                  @suffix = "pdf"
         | 
| 16 17 | 
             
                  @scripts = @scripts_pdf if @scripts_pdf
         | 
| 17 18 | 
             
                  @maxwidth = 500
         | 
| 18 19 | 
             
                  @maxheight = 800
         | 
| @@ -22,22 +23,19 @@ module IsoDoc | |
| 22 23 | 
             
                  "_pdfimages"
         | 
| 23 24 | 
             
                end
         | 
| 24 25 |  | 
| 25 | 
            -
                def initialize(options)
         | 
| 26 | 
            -
                  @format = :pdf
         | 
| 27 | 
            -
                  @suffix = "pdf"
         | 
| 28 | 
            -
                  super
         | 
| 29 | 
            -
                end
         | 
| 30 | 
            -
             | 
| 31 26 | 
             
                def convert(input_filename, file = nil, debug = false, output_filename = nil)
         | 
| 32 27 | 
             
                  file = File.read(input_filename, encoding: "utf-8") if file.nil?
         | 
| 33 28 | 
             
                  @openmathdelim, @closemathdelim = extract_delims(file)
         | 
| 34 29 | 
             
                  docxml, filename, dir = convert_init(file, input_filename, debug)
         | 
| 35 30 | 
             
                  result = convert1(docxml, filename, dir)
         | 
| 36 31 | 
             
                  return result if debug
         | 
| 37 | 
            -
             | 
| 32 | 
            +
             | 
| 33 | 
            +
                  postprocess(result, "#{filename}.tmp.html", dir)
         | 
| 38 34 | 
             
                  FileUtils.rm_rf dir
         | 
| 39 | 
            -
                  ::Metanorma::Output::Pdf.new.convert( | 
| 40 | 
            -
             | 
| 35 | 
            +
                  ::Metanorma::Output::Pdf.new.convert(
         | 
| 36 | 
            +
                    "#{filename}.tmp.html",
         | 
| 37 | 
            +
                    output_filename || "#{filename}.#{@suffix}",
         | 
| 38 | 
            +
                  )
         | 
| 41 39 | 
             
                  FileUtils.rm_rf ["#{filename}.tmp.html", tmpimagedir]
         | 
| 42 40 | 
             
                end
         | 
| 43 41 |  | 
| @@ -2,12 +2,32 @@ module IsoDoc | |
| 2 2 | 
             
              class PresentationXMLConvert < ::IsoDoc::Convert
         | 
| 3 3 | 
             
                def bibdata(docxml)
         | 
| 4 4 | 
             
                  a = bibdata_current(docxml) or return
         | 
| 5 | 
            +
                  address_precompose(a)
         | 
| 5 6 | 
             
                  bibdata_i18n(a)
         | 
| 6 7 | 
             
                  a.next =
         | 
| 7 8 | 
             
                    "<localized-strings>#{i8n_name(trim_hash(@i18n.get), '').join('')}"\
         | 
| 8 9 | 
             
                    "</localized-strings>"
         | 
| 9 10 | 
             
                end
         | 
| 10 11 |  | 
| 12 | 
            +
                def address_precompose(bib)
         | 
| 13 | 
            +
                  bib.xpath(ns("//bibdata//address")).each do |b|
         | 
| 14 | 
            +
                    next if b.at(ns("./formattedAddress"))
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    x = address_precompose1(b)
         | 
| 17 | 
            +
                    b.children = "<formattedAddress>#{x}</formattedAddress>"
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def address_precompose1(addr)
         | 
| 22 | 
            +
                  ret = []
         | 
| 23 | 
            +
                  addr.xpath(ns("./street")).each { |s| ret << s.children.to_xml }
         | 
| 24 | 
            +
                  a = addr.at(ns("./city")) and ret << a.children.to_xml
         | 
| 25 | 
            +
                  addr.xpath(ns("./state")).each { |s| ret << s.children.to_xml }
         | 
| 26 | 
            +
                  a = addr.at(ns("./country")) and ret << a.children.to_xml
         | 
| 27 | 
            +
                  a = addr.at(ns("./postcode")) and ret[-1] += " #{a.children.to_xml}"
         | 
| 28 | 
            +
                  ret.join("<br/>")
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 11 31 | 
             
                def bibdata_current(docxml)
         | 
| 12 32 | 
             
                  a = docxml.at(ns("//bibdata")) or return
         | 
| 13 33 | 
             
                  a.xpath(ns("./language")).each do |l|
         | 
| @@ -35,27 +55,28 @@ module IsoDoc | |
| 35 55 | 
             
                  x.next.children = hash[x.text]
         | 
| 36 56 | 
             
                end
         | 
| 37 57 |  | 
| 38 | 
            -
                def i18n_tag( | 
| 39 | 
            -
                  "<localized-string key='#{ | 
| 58 | 
            +
                def i18n_tag(key, value)
         | 
| 59 | 
            +
                  "<localized-string key='#{key}' language='#{@lang}'>#{value}"\
         | 
| 60 | 
            +
                    "</localized-string>"
         | 
| 40 61 | 
             
                end
         | 
| 41 62 |  | 
| 42 | 
            -
                def i18n_safe( | 
| 43 | 
            -
                   | 
| 63 | 
            +
                def i18n_safe(key)
         | 
| 64 | 
            +
                  key.to_s.gsub(/\s|\./, "_")
         | 
| 44 65 | 
             
                end
         | 
| 45 66 |  | 
| 46 | 
            -
                def i8n_name( | 
| 47 | 
            -
                  if  | 
| 48 | 
            -
                  elsif  | 
| 49 | 
            -
                     | 
| 67 | 
            +
                def i8n_name(hash, pref)
         | 
| 68 | 
            +
                  if hash.is_a? Hash then i8n_name1(hash, pref)
         | 
| 69 | 
            +
                  elsif hash.is_a? Array
         | 
| 70 | 
            +
                    hash.reject { |a| blank?(a) }.each_with_object([])
         | 
| 50 71 | 
             
                      .with_index do |(v1, g), i|
         | 
| 51 72 | 
             
                      i8n_name(v1, "#{i18n_safe(k)}.#{i}").each { |x| g << x }
         | 
| 52 73 | 
             
                    end
         | 
| 53 | 
            -
                  else [i18n_tag(pref,  | 
| 74 | 
            +
                  else [i18n_tag(pref, hash)]
         | 
| 54 75 | 
             
                  end
         | 
| 55 76 | 
             
                end
         | 
| 56 77 |  | 
| 57 | 
            -
                def i8n_name1( | 
| 58 | 
            -
                   | 
| 78 | 
            +
                def i8n_name1(hash, pref)
         | 
| 79 | 
            +
                  hash.reject { |_k, v| blank?(v) }.each_with_object([]) do |(k, v), g|
         | 
| 59 80 | 
             
                    if v.is_a? Hash then i8n_name(v, i18n_safe(k)).each { |x| g << x }
         | 
| 60 81 | 
             
                    elsif v.is_a? Array
         | 
| 61 82 | 
             
                      v.reject { |a| blank?(a) }.each_with_index do |v1, i|
         | 
| @@ -68,28 +89,28 @@ module IsoDoc | |
| 68 89 | 
             
                end
         | 
| 69 90 |  | 
| 70 91 | 
             
                # https://stackoverflow.com/a/31822406
         | 
| 71 | 
            -
                def blank?( | 
| 72 | 
            -
                   | 
| 92 | 
            +
                def blank?(elem)
         | 
| 93 | 
            +
                  elem.nil? || elem.respond_to?(:empty?) && elem.empty?
         | 
| 73 94 | 
             
                end
         | 
| 74 95 |  | 
| 75 | 
            -
                def trim_hash( | 
| 96 | 
            +
                def trim_hash(hash)
         | 
| 76 97 | 
             
                  loop do
         | 
| 77 | 
            -
                    h_new = trim_hash1( | 
| 78 | 
            -
                    break  | 
| 98 | 
            +
                    h_new = trim_hash1(hash)
         | 
| 99 | 
            +
                    break hash if hash == h_new
         | 
| 79 100 |  | 
| 80 | 
            -
                     | 
| 101 | 
            +
                    hash = h_new
         | 
| 81 102 | 
             
                  end
         | 
| 82 103 | 
             
                end
         | 
| 83 104 |  | 
| 84 | 
            -
                def trim_hash1( | 
| 85 | 
            -
                  return  | 
| 105 | 
            +
                def trim_hash1(hash)
         | 
| 106 | 
            +
                  return hash unless hash.is_a? Hash
         | 
| 86 107 |  | 
| 87 | 
            -
                   | 
| 108 | 
            +
                  hash.each_with_object({}) do |(k, v), g|
         | 
| 88 109 | 
             
                    next if blank?(v)
         | 
| 89 110 |  | 
| 90 | 
            -
                    g[k] = if v.is_a? Hash then trim_hash1( | 
| 111 | 
            +
                    g[k] = if v.is_a? Hash then trim_hash1(hash[k])
         | 
| 91 112 | 
             
                           elsif v.is_a? Array
         | 
| 92 | 
            -
                              | 
| 113 | 
            +
                             hash[k].map { |a| trim_hash1(a) }.reject { |a| blank?(a) }
         | 
| 93 114 | 
             
                           else
         | 
| 94 115 | 
             
                             v
         | 
| 95 116 | 
             
                           end
         | 
| @@ -9,8 +9,8 @@ module IsoDoc | |
| 9 9 | 
             
                    return @xrefs.anchor(node["bibitemid"], :xref) || "???"
         | 
| 10 10 | 
             
                  elsif node["target"] && node["droploc"]
         | 
| 11 11 | 
             
                    return @xrefs.anchor(node["target"], :value) ||
         | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 12 | 
            +
                        @xrefs.anchor(node["target"], :label) ||
         | 
| 13 | 
            +
                        @xrefs.anchor(node["target"], :xref) || "???"
         | 
| 14 14 | 
             
                  elsif node["target"] && !/.#./.match(node["target"])
         | 
| 15 15 | 
             
                    linkend = anchor_linkend1(node)
         | 
| 16 16 | 
             
                  end
         | 
| @@ -33,6 +33,10 @@ module IsoDoc | |
| 33 33 | 
             
                  return linkend&.downcase if node["case"] == "lowercase"
         | 
| 34 34 | 
             
                  return linkend if linkend[0, 1].match?(/\p{Upper}/)
         | 
| 35 35 |  | 
| 36 | 
            +
                  capitalise_xref1(node, linkend)
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def capitalise_xref1(node, linkend)
         | 
| 36 40 | 
             
                  prec = nearest_block_parent(node).xpath("./descendant-or-self::text()") &
         | 
| 37 41 | 
             
                    node.xpath("./preceding::text()")
         | 
| 38 42 | 
             
                  if prec.empty? || /(?!<[^.].)\.\s+$/.match(prec.map(&:text).join)
         | 
| @@ -43,8 +47,8 @@ module IsoDoc | |
| 43 47 |  | 
| 44 48 | 
             
                def nearest_block_parent(node)
         | 
| 45 49 | 
             
                  until %w(p title td th name formula li dt dd sourcecode pre)
         | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 50 | 
            +
                      .include?(node.name)
         | 
| 51 | 
            +
                    node = node.parent
         | 
| 48 52 | 
             
                  end
         | 
| 49 53 | 
             
                  node
         | 
| 50 54 | 
             
                end
         | 
| @@ -90,7 +94,7 @@ module IsoDoc | |
| 90 94 | 
             
                  ret
         | 
| 91 95 | 
             
                end
         | 
| 92 96 |  | 
| 93 | 
            -
                def eref_localities0(ref,  | 
| 97 | 
            +
                def eref_localities0(ref, _idx, target, delim, node)
         | 
| 94 98 | 
             
                  if ref["type"] == "whole" then l10n("#{delim} #{@i18n.wholeoftext}")
         | 
| 95 99 | 
             
                  else
         | 
| 96 100 | 
             
                    eref_localities1(target, ref["type"], ref.at(ns("./referenceFrom")),
         | 
| @@ -99,24 +103,24 @@ module IsoDoc | |
| 99 103 | 
             
                end
         | 
| 100 104 |  | 
| 101 105 | 
             
                # TODO: move to localization file
         | 
| 102 | 
            -
                def eref_localities1_zh(_target, type, from,  | 
| 106 | 
            +
                def eref_localities1_zh(_target, type, from, upto, node, delim)
         | 
| 103 107 | 
             
                  ret = "#{delim} 第#{from.text}" if from
         | 
| 104 | 
            -
                  ret += "–#{ | 
| 108 | 
            +
                  ret += "–#{upto.text}" if upto
         | 
| 105 109 | 
             
                  loc = (@i18n.locality[type] || type.sub(/^locality:/, "").capitalize)
         | 
| 106 110 | 
             
                  ret += " #{loc}" unless node["droploc"] == "true"
         | 
| 107 111 | 
             
                  ret
         | 
| 108 112 | 
             
                end
         | 
| 109 113 |  | 
| 110 114 | 
             
                # TODO: move to localization file
         | 
| 111 | 
            -
                def eref_localities1(target, type, from,  | 
| 115 | 
            +
                def eref_localities1(target, type, from, upto, delim, node, lang = "en")
         | 
| 112 116 | 
             
                  return "" if type == "anchor"
         | 
| 113 117 |  | 
| 114 118 | 
             
                  lang == "zh" and
         | 
| 115 | 
            -
                    return l10n(eref_localities1_zh(target, type, from,  | 
| 119 | 
            +
                    return l10n(eref_localities1_zh(target, type, from, upto, node, delim))
         | 
| 116 120 | 
             
                  ret = delim
         | 
| 117 121 | 
             
                  ret += eref_locality_populate(type, node)
         | 
| 118 122 | 
             
                  ret += " #{from.text}" if from
         | 
| 119 | 
            -
                  ret += "–#{ | 
| 123 | 
            +
                  ret += "–#{upto.text}" if upto
         | 
| 120 124 | 
             
                  l10n(ret)
         | 
| 121 125 | 
             
                end
         | 
| 122 126 |  | 
| @@ -161,11 +165,12 @@ module IsoDoc | |
| 161 165 | 
             
                  content = node.first_element_child.children.reject do |c|
         | 
| 162 166 | 
             
                    %w{locality localityStack}.include? c.name
         | 
| 163 167 | 
             
                  end.select { |c| !c.text? || /\S/.match(c) }
         | 
| 164 | 
            -
                  if  | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 167 | 
            -
             | 
| 168 | 
            -
             | 
| 168 | 
            +
                  n = if content.empty?
         | 
| 169 | 
            +
                        @i18n.term_defined_in.sub(/%/, node.first_element_child.to_xml)
         | 
| 170 | 
            +
                      else
         | 
| 171 | 
            +
                        "<em>#{node.children.to_xml}</em>"
         | 
| 172 | 
            +
                      end
         | 
| 173 | 
            +
                  node.replace(n)
         | 
| 169 174 | 
             
                end
         | 
| 170 175 |  | 
| 171 176 | 
             
                def variant(docxml)
         |