metanorma/isodoc

View on GitHub
lib/isodoc/function/table.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module IsoDoc
  module Function
    module Table
      def table_title_parse(node, out)
        name = node.at(ns("./name")) or return
        out.p class: "TableTitle", style: "text-align:center;" do |p|
          name&.children&.each { |n| parse(n, p) }
        end
      end

      def thead_parse(node, table)
        thead = node.at(ns("./thead"))
        if thead
          table.thead do |h|
            thead.element_children.each_with_index do |n, i|
              tr_parse(n, h, i, thead.xpath(ns("./tr")).size, true)
            end
          end
        end
      end

      def tbody_parse(node, table)
        tbody = node.at(ns("./tbody")) or return
        rowcount = tbody.xpath(ns("./tr")).size
        table.tbody do |h|
          tbody.element_children.each_with_index do |n, i|
            tr_parse(n, h, i, rowcount, false)
          end
        end
      end

      def tfoot_parse(node, table)
        tfoot = node.at(ns("./tfoot"))
        if tfoot
          table.tfoot do |h|
            tfoot.element_children.each_with_index do |n, i|
              tr_parse(n, h, i, tfoot.xpath(ns("./tr")).size, false)
            end
          end
        end
      end

      def table_attrs(node)
        width = node["width"] ? "width:#{node['width']};" : nil
        c = node["class"]
        bordered = "border-width:1px;border-spacing:0;"
        (%w(modspec).include?(c) || !c) or bordered = ""
        attr_code(
          id: node["id"],
          class: c || "MsoISOTable",
          style: "#{bordered}#{width}#{keep_style(node)}",
          title: node["alt"],
        )
      end

      def tcaption(node, table)
        return unless node["summary"]

        table.caption do |c|
          c.span style: "display:none" do |s|
            s << node["summary"]
          end
        end
      end

      def colgroup(node, table)
        colgroup = node.at(ns("./colgroup")) or return
        table.colgroup do |cg|
          colgroup.xpath(ns("./col")).each do |c|
            cg.col style: "width: #{c['width']};"
          end
        end
      end

      def table_parse(node, out)
        @in_table = true
        table_title_parse(node, out)
        out.table **table_attrs(node) do |t|
          table_parse_core(node, t)
          table_parse_tail(node, t)
        end
        @in_table = false
      end

      def table_parse_tail(node, out)
        (dl = node.at(ns("./dl"))) && parse(dl, out)
        node.xpath(ns("./source")).each { |n| parse(n, out) }
        node.xpath(ns("./note")).each { |n| parse(n, out) }
      end

      def table_parse_core(node, out)
        tcaption(node, out)
        colgroup(node, out)
        thead_parse(node, out)
        tbody_parse(node, out)
        tfoot_parse(node, out)
      end

      SW = "solid windowtext".freeze

      # def make_tr_attr(td, row, totalrows, cols, totalcols, header)
      # border-left:#{col.zero? ? "#{SW} 1.5pt;" : "none;"}
      # border-right:#{SW} #{col == totalcols && !header ? "1.5" : "1.0"}pt;

      def make_tr_attr(cell, row, totalrows, header, bordered)
        style = cell.name == "th" ? "font-weight:bold;" : ""
        cell["align"] and style += "text-align:#{cell['align']};"
        cell["valign"] and style += "vertical-align:#{cell['valign']};"
        rowmax = cell["rowspan"] ? row + cell["rowspan"].to_i - 1 : row
        style += make_tr_attr_style(row, rowmax, totalrows, header, bordered)
        header and scope = (cell["colspan"] ? "colgroup" : "col")
        !header && cell.name == "th" and
          scope = (cell["rowspan"] ? "rowgroup" : "row")
        { rowspan: cell["rowspan"], colspan: cell["colspan"],
          style: style.gsub(/\n/, ""), scope: scope, class: cell["class"] }
      end

      def make_tr_attr_style(row, rowmax, totalrows, _header, bordered)
        bordered or return ""
        <<~STYLE.gsub(/\n/, "")
          border-top:#{row.zero? ? "#{SW} 1.5pt;" : 'none;'}
          border-bottom:#{SW} #{rowmax >= totalrows ? '1.5' : '1.0'}pt;
        STYLE
      end

      def tr_parse(node, out, ord, totalrows, header)
        c = node.parent.parent["class"]
        bordered = %w(modspec).include?(c) || !c
        out.tr do |r|
          node.elements.each do |td|
            attrs = make_tr_attr(td, ord, totalrows - 1, header, bordered)
            r.send td.name, **attr_code(attrs) do |entry|
              td.children.each { |n| parse(n, entry) }
            end
          end
        end
      end
    end
  end
end