asciimath2unitsml 0.2.0 → 0.2.5
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/README.adoc +20 -1
- data/lib/asciimath2unitsml/conv.rb +28 -8
- data/lib/asciimath2unitsml/parse.rb +31 -20
- data/lib/asciimath2unitsml/render.rb +47 -16
- data/lib/asciimath2unitsml/unit.rb +5 -2
- data/lib/asciimath2unitsml/version.rb +1 -1
- data/lib/unitsdb/dimensions.yaml +31 -31
- data/lib/unitsdb/quantities.yaml +47 -19
- data/lib/unitsdb/units.yaml +96 -216
- data/lib/unitsdb_ruby/unitsdb.rb +1 -0
- data/spec/conv_spec.rb +510 -4
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 9c5ec436a8b058ce5c93dc246a8493d88a724c7d5f1048b8e42f7a1429791d3c
         | 
| 4 | 
            +
              data.tar.gz: a7106b478a1efa4f48c0133c9b644d77197db0d71031d48152274c9c43dde1f6
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e9fd0281fea82d844d833ae155875ab1b52d22cf384185a32330ed18709759aee6df93fb281e0b7844fe14bd79200211646d37b0152a0190a32b81d8d0176153
         | 
| 7 | 
            +
              data.tar.gz: 8cee75e492900a7ac7e622134feb3c1f0e10503af231af87c7f55d01c88aaf9d817ed66f753249ac5fe5c355a1de9963f3e6b3ed21379996335c89efccc9d022
         | 
    
        data/README.adoc
    CHANGED
    
    | @@ -1,3 +1,9 @@ | |
| 1 | 
            +
            image:https://img.shields.io/gem/v/asciimath2unitsml.svg["Gem Version", link="https://rubygems.org/gems/asciimath2unitsml"]
         | 
| 2 | 
            +
            image:https://github.com/plurimath/asciimath2unitsml/workflows/rake/badge.svg["Build Status", link="https://github.com/plurimath/asciimath2unitsml/actions?workflow=rake"]
         | 
| 3 | 
            +
            // image:https://codeclimate.com/github/plurimath/asciimath2unitsml/badges/gpa.svg["Code Climate", link="https://codeclimate.com/github/plurimath/asciimath2unitsml"]
         | 
| 4 | 
            +
            image:https://img.shields.io/github/issues-pr-raw/plurimath/asciimath2unitsml.svg["Pull Requests", link="https://github.com/plurimath/asciimath2unitsml/pulls"]
         | 
| 5 | 
            +
            image:https://img.shields.io/github/commits-since/plurimath/asciimath2unitsml/latest.svg["Commits since latest",link="https://github.com/plurimath/asciimath2unitsml/releases"]
         | 
| 6 | 
            +
             | 
| 1 7 | 
             
            = asciimath2unitsml
         | 
| 2 8 | 
             
            Convert Units expressions via MathML to UnitsML
         | 
| 3 9 |  | 
| @@ -43,6 +49,15 @@ https://github.com/unitsml/unitsdb/blob/master/units.yaml[]), that quantity is a | |
| 43 49 | 
             
            otherwise, no quantity is added unless explicitly nominated in this way.
         | 
| 44 50 | 
             
            * `unitsml(unit-string, name: NAME)` provides a name for the unit, if one is not already available
         | 
| 45 51 | 
             
            from UnitsDB. For example, `unitsml(cal_th/cm^2, name: langley)`.
         | 
| 52 | 
            +
            * `unitsml(unit-string, symbol: SYMBOL)` provides an alternate symbol for the unit, in AsciiMath.
         | 
| 53 | 
            +
            The unit-string gives the canonical representation of the unit, but SYMBOL is what will be rendered.
         | 
| 54 | 
            +
            For example, `unitsml(cal_th/cm^2, name: langley, symbol: La)`, or `unitsml(mm*s^-2, symbol: mm cdot s^-2)`.
         | 
| 55 | 
            +
            (All variables in SYMBOL are rendered upright, as is the default for units.)
         | 
| 56 | 
            +
            * `unitsml(unit-string, multiplier: SYMBOL)` provides an alternate symbol for the multiliper of
         | 
| 57 | 
            +
            units. The options are an XML entity, or the values `space` or `nospace` (for which see discussion under _Usage_).
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            Standalone prefixes can be recognised by replacing the unit with hyphen; so `unitsml(p-)` corresponds
         | 
| 60 | 
            +
            to the standalone prefix "pico" (and is rendered as "p").
         | 
| 46 61 |  | 
| 47 62 | 
             
            == Rendering
         | 
| 48 63 |  | 
| @@ -54,6 +69,9 @@ The gem follows the MathML Units convention of inserting a spacing invisible tim | |
| 54 69 | 
             
            (`<mo rspace='thickmathspace'>⁢</mo>`) between any numbers (`<mn>`) and unit expressions
         | 
| 55 70 | 
             
            in MathML, and representing units in MathML as non-italic variables (`<mi mathvariant='normal'>`).
         | 
| 56 71 |  | 
| 72 | 
            +
            Space is not inserted between a number and a unit expression, when that unit expression wholly consists
         | 
| 73 | 
            +
            of punctuation: _1 m_, _1 °C_, but _9° 7′ 22″_.
         | 
| 74 | 
            +
             | 
| 57 75 | 
             
            == Example
         | 
| 58 76 |  | 
| 59 77 | 
             
            [source]
         | 
| @@ -214,7 +232,7 @@ This table can be generated (in Asciidoc format) through `Asciimath2UnitsML::Con | |
| 214 232 | 
             
            | ton | ton of TNT (energy equivalent): `ton_TNT` | ton of refrigeration (12 000 Btu_IT/h): `ton_refrigeration` | | | | 
         | 
| 215 233 | 
             
            | tsp | teaspoon: `tsp` | teaspoon (FDA): `tsp_label` | | | | 
         | 
| 216 234 | 
             
            | yd | yard: `yd` | yard (based on US survey foot): `yd_US_survey` | | | | 
         | 
| 217 | 
            -
            | &# | 
| 235 | 
            +
            | ° | degree (degree of arc): `deg` | | | | | 
         | 
| 218 236 | 
             
            | γ | gamma: `gamma` | | | | | 
         | 
| 219 237 | 
             
            | μ | micron: `micron` | | | | | 
         | 
| 220 238 | 
             
            | Ω | ohm: `Ohm` | | | | | 
         | 
| @@ -242,3 +260,4 @@ This table can be generated (in Asciidoc format) through `Asciimath2UnitsML::Con | |
| 242 260 | 
             
            | °R | degree Rankine: `degR` | | | | | 
         | 
| 243 261 | 
             
            | ƛ~C~ | natural unit of length: `lambda-bar_C` | | | | | 
         | 
| 244 262 | 
             
            |===
         | 
| 263 | 
            +
             | 
| @@ -35,6 +35,10 @@ module Asciimath2UnitsML | |
| 35 35 | 
             
                  @multiplier = multiplier(options[:multiplier] || "\u00b7")
         | 
| 36 36 | 
             
                end
         | 
| 37 37 |  | 
| 38 | 
            +
                def float_to_display(f)
         | 
| 39 | 
            +
                  ret = f.to_f.round(1).to_s.sub(/\.0$/, "")
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 38 42 | 
             
                def prefix(units)
         | 
| 39 43 | 
             
                  units.map { |u| u[:prefix] }.reject { |u| u.nil? }.uniq.map do |p|
         | 
| 40 44 | 
             
                    <<~END
         | 
| @@ -44,7 +48,7 @@ module Asciimath2UnitsML | |
| 44 48 | 
             
                      <PrefixSymbol type="ASCII">#{@prefixes[p].ascii}</PrefixSymbol>
         | 
| 45 49 | 
             
                      <PrefixSymbol type="unicode">#{@prefixes[p].unicode}</PrefixSymbol>
         | 
| 46 50 | 
             
                      <PrefixSymbol type="LaTeX">#{@prefixes[p].latex}</PrefixSymbol>
         | 
| 47 | 
            -
                      <PrefixSymbol type="HTML">#{ | 
| 51 | 
            +
                      <PrefixSymbol type="HTML">#{htmlent @prefixes[p].html}</PrefixSymbol>
         | 
| 48 52 | 
             
                    </Prefix>
         | 
| 49 53 | 
             
                    END
         | 
| 50 54 | 
             
                  end.join("\n")
         | 
| @@ -59,9 +63,22 @@ module Asciimath2UnitsML | |
| 59 63 | 
             
                  END
         | 
| 60 64 | 
             
                end
         | 
| 61 65 |  | 
| 66 | 
            +
                U2D = {
         | 
| 67 | 
            +
                  "m" => { dimension: "Length", order: 1, symbol: "L" },
         | 
| 68 | 
            +
                  "g" => { dimension: "Mass", order: 2, symbol: "M" },
         | 
| 69 | 
            +
                  "kg" => { dimension: "Mass", order: 2, symbol: "M" },
         | 
| 70 | 
            +
                  "s" => { dimension: "Time", order: 3, symbol: "T" },
         | 
| 71 | 
            +
                  "A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" },
         | 
| 72 | 
            +
                  "K" => { dimension: "ThermodynamicTemperature", order: 5, symbol: "Theta" },
         | 
| 73 | 
            +
                  "degK" => { dimension: "ThermodynamicTemperature", order: 5, symbol: "Theta" },
         | 
| 74 | 
            +
                  "mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" },
         | 
| 75 | 
            +
                  "cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" },
         | 
| 76 | 
            +
                  "deg" => { dimension: "PlaneAngle", order: 8, symbol: "Phi" },
         | 
| 77 | 
            +
                }.freeze
         | 
| 78 | 
            +
             | 
| 62 79 | 
             
                def units2dimensions(units)
         | 
| 63 80 | 
             
                  norm = decompose_units(units)
         | 
| 64 | 
            -
                  return if norm.any? { |u| u[:unit] == "unknown" || u[:prefix] == "unknown" }
         | 
| 81 | 
            +
                  return if norm.any? { |u| u[:unit] == "unknown" || u[:prefix] == "unknown" || u[:unit].nil? }
         | 
| 65 82 | 
             
                  norm.map do |u|
         | 
| 66 83 | 
             
                    { dimension: U2D[u[:unit]][:dimension],
         | 
| 67 84 | 
             
                      unit: u[:unit],
         | 
| @@ -71,7 +88,7 @@ module Asciimath2UnitsML | |
| 71 88 | 
             
                end
         | 
| 72 89 |  | 
| 73 90 | 
             
                def dimension1(u)
         | 
| 74 | 
            -
                  %(<#{u[:dimension]} symbol="#{u[:symbol]}" powerNumerator="#{u[:exponent]}"/>)
         | 
| 91 | 
            +
                  %(<#{u[:dimension]} symbol="#{u[:symbol]}" powerNumerator="#{float_to_display(u[:exponent])}"/>)
         | 
| 75 92 | 
             
                end
         | 
| 76 93 |  | 
| 77 94 | 
             
                def dim_id(dims)
         | 
| @@ -81,7 +98,9 @@ module Asciimath2UnitsML | |
| 81 98 | 
             
                                  AmountOfSubstance LuminousIntensity PlaneAngle)
         | 
| 82 99 | 
             
                    .map { |h| dimhash.dig(h, :exponent) }.join(":")
         | 
| 83 100 | 
             
                  id = @dimensions_id&.values&.select { |d| d.vector == dimsvector }&.first&.id and return id.to_s
         | 
| 84 | 
            -
                  "D_" + dims.map  | 
| 101 | 
            +
                  "D_" + dims.map do |d|
         | 
| 102 | 
            +
                    U2D[d[:unit]][:symbol] + (d[:exponent] == 1 ? "" : float_to_display(d[:exponent]))
         | 
| 103 | 
            +
                  end.join("")
         | 
| 85 104 | 
             
                end
         | 
| 86 105 |  | 
| 87 106 | 
             
                def decompose_units(units)
         | 
| @@ -94,7 +113,7 @@ module Asciimath2UnitsML | |
| 94 113 | 
             
                    else
         | 
| 95 114 | 
             
                      m[-1] = { prefix: combine_prefixes(@prefixes[m[-1][:prefix]], @prefixes[k[:prefix]]),
         | 
| 96 115 | 
             
                                unit: m[-1][:unit],
         | 
| 97 | 
            -
                                exponent: (k[:exponent]&. | 
| 116 | 
            +
                                exponent: (k[:exponent]&.to_f || 1) + (m[-1][:exponent]&.to_f || 1) }
         | 
| 98 117 | 
             
                    end
         | 
| 99 118 | 
             
                  end
         | 
| 100 119 | 
             
                end
         | 
| @@ -102,7 +121,8 @@ module Asciimath2UnitsML | |
| 102 121 | 
             
                # treat g not kg as base unit: we have stripped the prefix k in parsing
         | 
| 103 122 | 
             
                # reduce units down to basic units
         | 
| 104 123 | 
             
                def decompose_unit(u)
         | 
| 105 | 
            -
                  if u[:unit]  | 
| 124 | 
            +
                  if u[:unit].nil? then u
         | 
| 125 | 
            +
                  elsif u[:unit] == "g" then u
         | 
| 106 126 | 
             
                  elsif @units[u[:unit]].system_type == "SI_base" then u
         | 
| 107 127 | 
             
                  elsif !@units[u[:unit]].si_derived_bases
         | 
| 108 128 | 
             
                    { prefix: u[:prefix], unit: "unknown", exponent: u[:exponent] }
         | 
| @@ -111,7 +131,7 @@ module Asciimath2UnitsML | |
| 111 131 | 
             
                      m << { prefix: !k[:prefix].nil? && !k[:prefix].empty? ? 
         | 
| 112 132 | 
             
                             combine_prefixes(@prefixes_id[k[:prefix]], @prefixes[u[:prefix]]) : u[:prefix],
         | 
| 113 133 | 
             
                             unit: @units_id[k[:id]].symbolid,
         | 
| 114 | 
            -
                             exponent: (k[:power]&.to_i || 1) * (u[:exponent]&. | 
| 134 | 
            +
                             exponent: (k[:power]&.to_i || 1) * (u[:exponent]&.to_f || 1) }
         | 
| 115 135 | 
             
                    end
         | 
| 116 136 | 
             
                  end
         | 
| 117 137 | 
             
                end
         | 
| @@ -121,7 +141,7 @@ module Asciimath2UnitsML | |
| 121 141 | 
             
                  return p1.symbolid if p2.nil?
         | 
| 122 142 | 
             
                  return p2.symbolid if p1.nil?
         | 
| 123 143 | 
             
                  return "unknown" if p1.base != p2.base
         | 
| 124 | 
            -
                  @prefixes.each do |p|
         | 
| 144 | 
            +
                  @prefixes.each do |_, p|
         | 
| 125 145 | 
             
                    return p.symbolid if p.base == p1.base && p.power == p1.power + p2.power
         | 
| 126 146 | 
             
                  end
         | 
| 127 147 | 
             
                  "unknown"
         | 
| @@ -87,19 +87,24 @@ module Asciimath2UnitsML | |
| 87 87 | 
             
                  prefix2 = /#{@prefixes.keys.select { |x| x.size == 2 }.join("|")}/.r
         | 
| 88 88 | 
             
                  prefix1 = /#{@prefixes.keys.select { |x| x.size == 1 }.join("|")}/.r
         | 
| 89 89 | 
             
                  unit_keys = @units.keys.reject do |k|
         | 
| 90 | 
            -
                     | 
| 90 | 
            +
                    /\*|\^|\/|^1$/.match(k) || @units[k].prefixed
         | 
| 91 91 | 
             
                  end.map { |k| Regexp.escape(k) }
         | 
| 92 92 | 
             
                  unit1 = /#{unit_keys.sort_by(&:length).reverse.join("|")}/.r
         | 
| 93 93 | 
             
                  exponent = /\^\(-?\d+\)/.r.map { |m| m.sub(/\^/, "").gsub(/[()]/, "") } |
         | 
| 94 94 | 
             
                    /\^-?\d+/.r.map { |m| m.sub(/\^/, "") }
         | 
| 95 95 | 
             
                  multiplier = %r{\*|//|/}.r.map { |x| { multiplier: x[0] } }
         | 
| 96 96 | 
             
                  unit = 
         | 
| 97 | 
            +
                    seq("sqrt(", unit1, ")") { |x| { prefix: nil, unit: x[1], display_exponent: "0.5" } } |
         | 
| 98 | 
            +
                    seq("sqrt(", prefix1, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
         | 
| 99 | 
            +
                    seq("sqrt(", prefix2, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
         | 
| 97 100 | 
             
                    seq(unit1, exponent._? & multiplier) { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0] )} } |
         | 
| 98 101 | 
             
                    seq(unit1, exponent._?).eof { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0] )} } |
         | 
| 99 102 | 
             
                    seq(prefix1, unit1, exponent._? ) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0] ) } } |
         | 
| 100 103 | 
             
                    seq(prefix2, unit1, exponent._? ) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0] ) } } |
         | 
| 101 | 
            -
                    "1".r.map { |_| { prefix: nil, unit: "1", display_exponent: nil } }
         | 
| 102 | 
            -
                  units =  | 
| 104 | 
            +
                    "1".r.map { |_| { prefix: nil, unit: "1", display_exponent: nil } } 
         | 
| 105 | 
            +
                  units = seq(prefix2, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } |
         | 
| 106 | 
            +
                    seq(prefix1, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } |
         | 
| 107 | 
            +
                    unit.join(multiplier)
         | 
| 103 108 | 
             
                  parser = units.eof
         | 
| 104 109 | 
             
                end
         | 
| 105 110 |  | 
| @@ -117,11 +122,13 @@ module Asciimath2UnitsML | |
| 117 122 | 
             
                  units = postprocess1(units)
         | 
| 118 123 | 
             
                  quantity = text[1..-1]&.select { |x| /^quantity:/.match(x) }&.first&.sub(/^quantity:\s*/, "")
         | 
| 119 124 | 
             
                  name = text[1..-1]&.select { |x| /^name:/.match(x) }&.first&.sub(/^name:\s*/, "")
         | 
| 125 | 
            +
                  symbol = text[1..-1]&.select { |x| /^symbol:/.match(x) }&.first&.sub(/^symbol:\s*/, "")
         | 
| 126 | 
            +
                  multiplier = text[1..-1]&.select { |x| /^multiplier:/.match(x) }&.first&.sub(/^multiplier:\s*/, "")
         | 
| 120 127 | 
             
                  normtext = units_only(units).each.map do |u|
         | 
| 121 128 | 
             
                    exp = u[:exponent] && u[:exponent] != "1" ? "^#{u[:exponent]}" : ""
         | 
| 122 129 | 
             
                    "#{u[:prefix]}#{u[:unit]}#{exp}"
         | 
| 123 130 | 
             
                  end.join("*")
         | 
| 124 | 
            -
                  [units, text[0], normtext, quantity, name]
         | 
| 131 | 
            +
                  [units, text[0], normtext, quantity, name, symbol, multiplier]
         | 
| 125 132 | 
             
                end
         | 
| 126 133 |  | 
| 127 134 | 
             
                def postprocess1(units)
         | 
| @@ -137,19 +144,6 @@ module Asciimath2UnitsML | |
| 137 144 | 
             
                  end
         | 
| 138 145 | 
             
                end
         | 
| 139 146 |  | 
| 140 | 
            -
                U2D = {
         | 
| 141 | 
            -
                  "m" => { dimension: "Length", order: 1, symbol: "L" },
         | 
| 142 | 
            -
                  "g" => { dimension: "Mass", order: 2, symbol: "M" },
         | 
| 143 | 
            -
                  "kg" => { dimension: "Mass", order: 2, symbol: "M" },
         | 
| 144 | 
            -
                  "s" => { dimension: "Time", order: 3, symbol: "T" },
         | 
| 145 | 
            -
                  "A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" },
         | 
| 146 | 
            -
                  "K" => { dimension: "ThermodynamicTemperature", order: 5, symbol: "Theta" },
         | 
| 147 | 
            -
                  "degK" => { dimension: "ThermodynamicTemperature", order: 5, symbol: "Theta" },
         | 
| 148 | 
            -
                  "mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" },
         | 
| 149 | 
            -
                  "cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" },
         | 
| 150 | 
            -
                  "deg" => { dimension: "PlaneAngle", order: 8, symbol: "Phi" },
         | 
| 151 | 
            -
                }
         | 
| 152 | 
            -
             | 
| 153 147 | 
             
                def Asciimath2UnitsML(expression)
         | 
| 154 148 | 
             
                  xml = Nokogiri::XML(asciimath2mathml(expression))
         | 
| 155 149 | 
             
                  MathML2UnitsML(xml).to_xml
         | 
| @@ -161,14 +155,25 @@ module Asciimath2UnitsML | |
| 161 155 | 
             
                  xml.xpath(".//m:mtext", "m" => MATHML_NS).each do |x|
         | 
| 162 156 | 
             
                    next unless %r{^unitsml\(.+\)$}.match(x.text)
         | 
| 163 157 | 
             
                    text = x.text.sub(%r{^unitsml\((.+)\)$}m, "\\1")
         | 
| 164 | 
            -
                    units, origtext, normtext, quantity, name = parse(text)
         | 
| 165 | 
            -
                     | 
| 166 | 
            -
             | 
| 158 | 
            +
                    units, origtext, normtext, quantity, name, symbol, multiplier = parse(text)
         | 
| 159 | 
            +
                    rendering = symbol ? embeddedmathml(asciimath2mathml(symbol)) :
         | 
| 160 | 
            +
                      mathmlsymbol(units, false, multiplier)
         | 
| 161 | 
            +
                    x.replace("#{delimspace(rendering, x)}<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\
         | 
| 167 162 | 
             
                              "#{unitsml(units, origtext, normtext, quantity, name)}")
         | 
| 168 163 | 
             
                  end
         | 
| 169 164 | 
             
                  dedup_ids(xml)
         | 
| 170 165 | 
             
                end
         | 
| 171 166 |  | 
| 167 | 
            +
                # if previous sibling's last descendent non-whitespace is MathML and mn or mi, no space
         | 
| 168 | 
            +
                def delimspace(rendering, elem)
         | 
| 169 | 
            +
                  prec_text_elem = elem.xpath("./preceding-sibling::*[namespace-uri() = '#{MATHML_NS}']/"\
         | 
| 170 | 
            +
                                              "descendant::text()[normalize-space()!=''][last()]/parent::*").last
         | 
| 171 | 
            +
                  return "" if prec_text_elem.nil? || !%w(mn mi).include?(prec_text_elem&.name)
         | 
| 172 | 
            +
                  text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{rendering}</mrow>").text.strip)
         | 
| 173 | 
            +
                  /\p{L}|\p{N}/.match(text) ?
         | 
| 174 | 
            +
                    "<mo rspace='thickmathspace'>⁢</mo>" : "<mo>⁢</mo>"
         | 
| 175 | 
            +
                end
         | 
| 176 | 
            +
             | 
| 172 177 | 
             
                def dedup_ids(xml)
         | 
| 173 178 | 
             
                  %w(Unit Dimension Prefix Quantity).each do |t|
         | 
| 174 179 | 
             
                    xml.xpath(".//m:#{t}/@xml:id", "m" => UNITSML_NS).map { |a| a.text }.uniq.each do |v|
         | 
| @@ -187,6 +192,12 @@ module Asciimath2UnitsML | |
| 187 192 | 
             
                  gsub(/<math>/, "<math xmlns='#{MATHML_NS}'>")
         | 
| 188 193 | 
             
                end
         | 
| 189 194 |  | 
| 195 | 
            +
                def embeddedmathml(mathml)
         | 
| 196 | 
            +
                  x = Nokogiri::XML(mathml)
         | 
| 197 | 
            +
                  x.xpath(".//m:mi", "m" => MATHML_NS).each { |mi| mi["mathvariant"] = "normal" }
         | 
| 198 | 
            +
                  x.children.to_xml
         | 
| 199 | 
            +
                end
         | 
| 200 | 
            +
             | 
| 190 201 | 
             
                def ambig_units
         | 
| 191 202 | 
             
                  u = @units_id.each_with_object({}) do |(k, v), m|
         | 
| 192 203 | 
             
                    v.symbolids.each do |x|
         | 
| @@ -10,40 +10,71 @@ module Asciimath2UnitsML | |
| 10 10 | 
             
                    { html: HTMLEntities.new.encode(x), mathml: "<mo>#{HTMLEntities.new.encode(x)}</mo>" }
         | 
| 11 11 | 
             
                  end
         | 
| 12 12 | 
             
                end
         | 
| 13 | 
            +
             | 
| 13 14 | 
             
                def render(unit, style)
         | 
| 14 15 | 
             
                  @symbols[unit][style] || unit
         | 
| 15 16 | 
             
                end
         | 
| 16 17 |  | 
| 18 | 
            +
                def htmlent(x)
         | 
| 19 | 
            +
                  HTMLEntities.new.decode(x).split(/([<>&])/)
         | 
| 20 | 
            +
                    .map { |c| /[<>'"]/.match(c) ? c : HTMLEntities.new.encode(c, :hexadecimal) }.join
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 17 23 | 
             
                def htmlsymbol(units, normalise)
         | 
| 18 24 | 
             
                  units.map do |u|
         | 
| 19 25 | 
             
                    if u[:multiplier] then u[:multiplier] == "*" ? @multiplier[:html] : u[:multiplier]
         | 
| 26 | 
            +
                    elsif u[:unit].nil? && u[:prefix]
         | 
| 27 | 
            +
                      @prefixes[u[:prefix]].html
         | 
| 20 28 | 
             
                    else
         | 
| 21 | 
            -
                      u[: | 
| 22 | 
            -
                       | 
| 23 | 
            -
                      "#{u[:prefix]}#{base}#{exp}"
         | 
| 29 | 
            +
                      base = (u[:prefix] || "") + render(normalise ? @units[u[:unit]].symbolid : u[:unit], :html)
         | 
| 30 | 
            +
                      htmlsymbol_exponent(u, base)
         | 
| 24 31 | 
             
                    end
         | 
| 25 32 | 
             
                  end.join("")
         | 
| 26 33 | 
             
                end
         | 
| 27 34 |  | 
| 28 | 
            -
                def  | 
| 35 | 
            +
                def htmlsymbol_exponent(u, base)
         | 
| 36 | 
            +
                  if u[:display_exponent] == "0.5"
         | 
| 37 | 
            +
                    base = "√#{base}"
         | 
| 38 | 
            +
                  elsif u[:display_exponent]
         | 
| 39 | 
            +
                    exp = "<sup>#{u[:display_exponent].sub(/-/, "−")}</sup>"
         | 
| 40 | 
            +
                    base += exp
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                  base
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def mathmlsymbol(units, normalise, multiplier = nil)
         | 
| 46 | 
            +
                  multiplier = multiplier ? "<mo>#{multiplier}</mo>" : @multiplier[:mathml]
         | 
| 29 47 | 
             
                  exp = units.map do |u|
         | 
| 30 | 
            -
                    if u[:multiplier] then u[:multiplier] == "*" ?  | 
| 48 | 
            +
                    if u[:multiplier] then u[:multiplier] == "*" ? multiplier : "<mo>#{u[:multiplier]}</mo>"
         | 
| 49 | 
            +
                    elsif u[:unit].nil? && u[:prefix]
         | 
| 50 | 
            +
                      %(<mi mathvariant='normal'>#{htmlent(@prefixes[u[:prefix]].html)}</mi>)
         | 
| 31 51 | 
             
                    else
         | 
| 32 | 
            -
                       | 
| 33 | 
            -
                      if u[:prefix]
         | 
| 34 | 
            -
                        base = base.match(/<mi mathvariant='normal'>/) ?
         | 
| 35 | 
            -
                          base.sub(/<mi mathvariant='normal'>/, "<mi mathvariant='normal'>#{u[:prefix]}") :
         | 
| 36 | 
            -
                          "<mrow><mi mathvariant='normal'>#{u[:prefix]}#{base}</mrow>"
         | 
| 37 | 
            -
                      end
         | 
| 38 | 
            -
                      if u[:display_exponent]
         | 
| 39 | 
            -
                        exp = "<mn>#{u[:display_exponent]}</mn>".sub(/<mn>-/, "<mo>−</mo><mn>")
         | 
| 40 | 
            -
                        base = "<msup><mrow>#{base}</mrow><mrow>#{exp}</mrow></msup>"
         | 
| 41 | 
            -
                      end
         | 
| 42 | 
            -
                      base
         | 
| 52 | 
            +
                      mathmlsymbol1(u, normalise)
         | 
| 43 53 | 
             
                    end
         | 
| 44 54 | 
             
                  end.join("")
         | 
| 45 55 | 
             
                end
         | 
| 46 56 |  | 
| 57 | 
            +
                def mathmlsymbol1(u, normalise)
         | 
| 58 | 
            +
                  base = render(normalise ? @units[u[:unit]].symbolid : u[:unit], :mathml)
         | 
| 59 | 
            +
                  if u[:prefix]
         | 
| 60 | 
            +
                    prefix = htmlent(@prefixes[u[:prefix]].html)
         | 
| 61 | 
            +
                    base = base.match(/<mi mathvariant='normal'>/) ?
         | 
| 62 | 
            +
                      base.sub(/<mi mathvariant='normal'>/, "<mi mathvariant='normal'>#{prefix}") :
         | 
| 63 | 
            +
                      "<mrow><mi mathvariant='normal'>#{prefix}#{base}</mrow>"
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                  mathmlsymbol_exponent(u, base)
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def mathmlsymbol_exponent(u, base)
         | 
| 69 | 
            +
                  if u[:display_exponent] == "0.5"
         | 
| 70 | 
            +
                    base = "<msqrt>#{base}</msqrt>"
         | 
| 71 | 
            +
                  elsif u[:display_exponent]
         | 
| 72 | 
            +
                    exp = "<mn>#{u[:display_exponent]}</mn>".sub(/<mn>-/, "<mo>−</mo><mn>")
         | 
| 73 | 
            +
                    base = "<msup><mrow>#{base}</mrow><mrow>#{exp}</mrow></msup>"
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
                  base
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 47 78 | 
             
                def mathmlsymbolwrap(units, normalise)
         | 
| 48 79 | 
             
                  <<~END
         | 
| 49 80 | 
             
                  <math xmlns='#{MATHML_NS}'><mrow>#{mathmlsymbol(units, normalise)}</mrow></math>
         | 
| @@ -6,11 +6,12 @@ module Asciimath2UnitsML | |
| 6 6 |  | 
| 7 7 | 
             
                def unit_id(text)
         | 
| 8 8 | 
             
                  text = text.gsub(/[()]/, "")
         | 
| 9 | 
            -
                  " | 
| 10 | 
            -
             | 
| 9 | 
            +
                  /-$/.match(text) and return @prefixes[text.sub(/-$/, "")].id
         | 
| 10 | 
            +
                  "U_" + (@units[text] ? @units[text].id.gsub(/'/, "_") : text.gsub(/\*/, ".").gsub(/\^/, ""))
         | 
| 11 11 | 
             
                end
         | 
| 12 12 |  | 
| 13 13 | 
             
                def unit(units, origtext, normtext, dims, name)
         | 
| 14 | 
            +
                  return if units_only(units).any? { |x| x[:unit].nil? }
         | 
| 14 15 | 
             
                  dimid = dim_id(dims)
         | 
| 15 16 | 
             
                  norm_units = normalise_units(units)
         | 
| 16 17 | 
             
                  <<~END
         | 
| @@ -34,6 +35,7 @@ module Asciimath2UnitsML | |
| 34 35 |  | 
| 35 36 | 
             
                # kg exception
         | 
| 36 37 | 
             
                def unitsystem(units)
         | 
| 38 | 
            +
                  return if units_only(units).any? { |x| x[:unit].nil? }
         | 
| 37 39 | 
             
                  ret = []
         | 
| 38 40 | 
             
                  units = units_only(units)
         | 
| 39 41 | 
             
                  units.any? { |x| @units[x[:unit]].system_name != "SI" } and
         | 
| @@ -64,6 +66,7 @@ module Asciimath2UnitsML | |
| 64 66 | 
             
                end
         | 
| 65 67 |  | 
| 66 68 | 
             
                def rootunits(units)
         | 
| 69 | 
            +
                  return if units_only(units).any? { |x| x[:unit].nil? }
         | 
| 67 70 | 
             
                  return if units.size == 1 && !units[0][:prefix]
         | 
| 68 71 | 
             
                  exp = units_only(units).map do |u|
         | 
| 69 72 | 
             
                    prefix = " prefix='#{u[:prefix]}'" if u[:prefix]
         | 
    
        data/lib/unitsdb/dimensions.yaml
    CHANGED
    
    | @@ -753,50 +753,50 @@ NISTd75: | |
| 753 753 | 
             
                powerNumerator: 1
         | 
| 754 754 | 
             
                symbol: I
         | 
| 755 755 |  | 
| 756 | 
            -
            NISTd85:
         | 
| 757 | 
            -
             | 
| 756 | 
            +
            #NISTd85:
         | 
| 757 | 
            +
            #dimensionless: true
         | 
| 758 758 |  | 
| 759 | 
            -
            NISTd84:
         | 
| 760 | 
            -
             | 
| 759 | 
            +
            #NISTd84:
         | 
| 760 | 
            +
            #dimensionless: true
         | 
| 761 761 |  | 
| 762 | 
            -
            NISTd87:
         | 
| 763 | 
            -
             | 
| 762 | 
            +
            #NISTd87:
         | 
| 763 | 
            +
            #dimensionless: true
         | 
| 764 764 |  | 
| 765 | 
            -
            NISTd86:
         | 
| 766 | 
            -
             | 
| 765 | 
            +
            #NISTd86:
         | 
| 766 | 
            +
            #dimensionless: true
         | 
| 767 767 |  | 
| 768 | 
            -
            NISTd81:
         | 
| 769 | 
            -
             | 
| 768 | 
            +
            #NISTd81:
         | 
| 769 | 
            +
            #dimensionless: true
         | 
| 770 770 |  | 
| 771 771 | 
             
            NISTd80:
         | 
| 772 | 
            -
             | 
| 772 | 
            +
                dimensionless: true
         | 
| 773 773 |  | 
| 774 | 
            -
            NISTd83:
         | 
| 775 | 
            -
             | 
| 774 | 
            +
            #NISTd83:
         | 
| 775 | 
            +
            #dimensionless: true
         | 
| 776 776 |  | 
| 777 | 
            -
            NISTd82:
         | 
| 778 | 
            -
             | 
| 777 | 
            +
            #NISTd82:
         | 
| 778 | 
            +
            #dimensionless: true
         | 
| 779 779 |  | 
| 780 | 
            -
            NISTd95:
         | 
| 781 | 
            -
             | 
| 780 | 
            +
            #NISTd95:
         | 
| 781 | 
            +
            #dimensionless: true
         | 
| 782 782 |  | 
| 783 | 
            -
            NISTd94:
         | 
| 784 | 
            -
             | 
| 783 | 
            +
            #NISTd94:
         | 
| 784 | 
            +
            #dimensionless: true
         | 
| 785 785 |  | 
| 786 | 
            -
            NISTd89:
         | 
| 787 | 
            -
             | 
| 786 | 
            +
            #NISTd89:
         | 
| 787 | 
            +
            #dimensionless: true
         | 
| 788 788 |  | 
| 789 | 
            -
            NISTd88:
         | 
| 790 | 
            -
             | 
| 789 | 
            +
            #NISTd88:
         | 
| 790 | 
            +
            #dimensionless: true
         | 
| 791 791 |  | 
| 792 | 
            -
            NISTd91:
         | 
| 793 | 
            -
             | 
| 792 | 
            +
            #NISTd91:
         | 
| 793 | 
            +
            #dimensionless: true
         | 
| 794 794 |  | 
| 795 | 
            -
            NISTd90:
         | 
| 796 | 
            -
             | 
| 795 | 
            +
            #NISTd90:
         | 
| 796 | 
            +
            #dimensionless: true
         | 
| 797 797 |  | 
| 798 | 
            -
            NISTd92:
         | 
| 799 | 
            -
             | 
| 798 | 
            +
            #NISTd92:
         | 
| 799 | 
            +
            #dimensionless: true
         | 
| 800 800 |  | 
| 801 | 
            -
            NISTd93:
         | 
| 802 | 
            -
             | 
| 801 | 
            +
            #NISTd93:
         | 
| 802 | 
            +
            #dimensionless: true
         |