From 940409b05da8f7fad1b8dc70afa90116b03cd624 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Mon, 22 Jun 2026 23:59:17 -0700 Subject: [PATCH] tex, string, and separator options for units --- lib/Units.pm | 82 +++++++++++++++++++++++++++++---- macros/contexts/contextUnits.pl | 34 +++++++++----- 2 files changed, 95 insertions(+), 21 deletions(-) diff --git a/lib/Units.pm b/lib/Units.pm index 4436af73b1..4a23c9b814 100644 --- a/lib/Units.pm +++ b/lib/Units.pm @@ -38,6 +38,9 @@ our $PI = 4 * atan2(1, 1); # 9.80665 m/s^2 -- standard acceleration of gravity +# If adding a string property to a unit below, check that the default font +# in use for hardcopy has the glyphs needed for that string. + our %known_units = ( m => { factor => 1, @@ -61,16 +64,22 @@ our %known_units = ( degC => { factor => 1, degC => 1, + string => "\x{2103}", + tex => "\x{2103}", aliases => [ "\x{00B0}C", "\x{2103}" ] }, degF => { factor => 1, degF => 1, + string => "\x{00B0}F", + tex => "\x{00B0}F", aliases => [ "\x{00B0}F", "\x{2109}" ] }, K => { factor => 1, K => 1, + string => "\x{212A}", + tex => "\x{212A}", aliases => [ "\x{212A}", 'degK', "\x{00B0}K" ] # Should the degree forms be deleted? Probably. }, mol => { @@ -92,9 +101,13 @@ our %known_units = ( # ANGLES: fundamental unit rad (radian) deg => { # degree - factor => $PI / 180, - rad => 1, - aliases => [ "\x{00B0}", 'degree', 'degrees' ] + factor => $PI / 180, + rad => 1, + string => "\x{00B0}", + tex => "\x{00B0}", + string_separator => '', + tex_separator => '', + aliases => [ "\x{00B0}", 'degree', 'degrees' ] }, sr => { # steradian, a mesure of solid angle factor => 1, @@ -109,6 +122,8 @@ our %known_units = ( us => { # microsecond factor => 1E-6, s => 1, + string => "\x{00B5}s", + tex => "\x{00B5}s", aliases => ["\x{00B5}s"] }, ns => { # nanosecond @@ -163,22 +178,30 @@ our %known_units = ( um => { # micrometer factor => 1E-6, m => 1, - aliases => [ 'micron', "\x{00B5}m" ] + string => "\x{00B5}m", + tex => "\x{00B5}m", + aliases => ["\x{00B5}m"] + }, + micron => { # micrometer + factor => 1E-6, + m => 1, }, - nm => { # nanometer + nm => { # nanometer factor => 1E-9, m => 1 }, angstrom => { factor => 1E-10, m => 1, + string => "\x{00C5}", + tex => "\x{00C5}", aliases => [ 'angstroms', 'Angstrom', 'Angstroms', "\x{00C5}" ] }, - pm => { # picometer + pm => { # picometer factor => 1E-12, m => 1 }, - fm => { # femtometer + fm => { # femtometer factor => 1E-15, m => 1 }, @@ -218,8 +241,9 @@ our %known_units = ( # VOLUME: fundamental unit m^3 (cubic meter) L => { # liter - factor => 0.001, - m => 3 + factor => 0.001, + m => 3, + aliases => [ 'litre', 'litres', 'liter', 'liters' ] }, ml => { # milliliter (cubic centimeter) factor => 1E-6, @@ -331,6 +355,8 @@ our %known_units = ( m => 1, kg => 1, s => -2, + string => "\x{00B5}N", + tex => "\x{00B5}N", aliases => [ 'microN', "\x{00B5}N" ] }, kN => { # kilonewton @@ -561,6 +587,8 @@ our %known_units = ( factor => 1e-6, amp => 1, s => 1, + string => "\x{00B5}C", + tex => "\x{00B5}C", aliases => ["\x{00B5}C"] }, nC => { # nanocoulomb @@ -616,6 +644,8 @@ our %known_units = ( s => 4, kg => -1, m => -2, + string => "\x{00B5}F", + tex => "\x{00B5}F", aliases => [ "\x{00B5}F", "\x{338C}" ] }, ohm => { # V/amp @@ -624,6 +654,8 @@ our %known_units = ( m => 2, amp => -2, s => -3, + string => "\x{2126}", + tex => "\x{2126}", aliases => ["\x{2126}"] }, kohm => { # kiloohm @@ -632,6 +664,8 @@ our %known_units = ( m => 2, amp => -2, s => -3, + string => "k\x{2126}", + tex => "k\x{2126}", aliases => [ "k\x{2126}", "\x{33C0}" ] }, Mohm => { # megaohm @@ -640,6 +674,8 @@ our %known_units = ( m => 2, amp => -2, s => -3, + string => "M\x{2126}", + tex => "M\x{2126}", aliases => [ "M\x{2126}", "\x{33C1}" ] }, S => { # siemens (1/ohm) @@ -735,6 +771,8 @@ our %known_units = ( factor => 0.000001, m => 2, s => -2, + string => "\x{00B5}Sv", + tex => "\x{00B5}Sv", aliases => ["\x{00B5}Sv"] }, Bq => { # becquerel, radioactivity (https://en.wikipedia.org/wiki/Becquerel) @@ -802,6 +840,32 @@ for my $unit (keys %known_units) { } } +# A map from unit name to display options +our %unit_display; + +# Process display options +for my $unit (keys %known_units) { + # note that aliases were not deep copied, so cannot delete + # these until a separate loop after all are processed + for ('string', 'tex', 'string_separator', 'tex_separator') { + $unit_display{$unit}{$_} = $known_units{$unit}{$_} if defined $known_units{$unit}{$_}; + } +} + +# If a unit has a string option set, make sure that there is a unit with that name too. +for my $unit (keys %known_units) { + $known_units{ $known_units{$unit}{string} } = $known_units{$unit} + if $known_units{$unit}{string} && !$known_units{ $known_units{$unit}{string} }; +} + +for my $unit (keys %known_units) { + # note that aliases were not deep copied, so cannot delete + # these until a separate loop after all are processed + for ('string', 'tex', 'string_separator', 'tex_separator') { + delete $known_units{$unit}{$_}; + } +} + sub process_unit { my $string = shift; diff --git a/macros/contexts/contextUnits.pl b/macros/contexts/contextUnits.pl index c85bf6edb4..0137a9fbaf 100644 --- a/macros/contexts/contextUnits.pl +++ b/macros/contexts/contextUnits.pl @@ -684,14 +684,10 @@ package context::Units::Context; # # The units from the original Units package # -our %UNITS = (%Units::known_units); -our %ALIAS = (%Units::unit_aliased_to); -for ('litre', 'litres', 'liter', 'liters') { - $UNITS{$_} = $UNITS{L}; - $ALIAS{$_} = 'L'; -} +our %UNITS = (%Units::known_units); +our %ALIAS = (%Units::unit_aliased_to); +our %DISPLAY = (%Units::unit_display); -# # The categories of units that can be selected. # # These give the fundamental units of the unit names to be added to @@ -780,8 +776,9 @@ sub addUnit { if ($name) { $constants->add( $name => { - value => context::Units::Unit->new($name => $unit), - TeX => "\\text{$name}", + value => context::Units::Unit->new($name => $unit), + TeX => $DISPLAY{$name}{tex} ? "\\text{$DISPLAY{$name}{tex}}" : "\\text{$name}", + $DISPLAY{$name}{string} ? (string => $DISPLAY{$name}{string}) : (), isUnit => 1, isConstant => 1 } @@ -958,6 +955,8 @@ sub assignUnits { package context::Units::Unit; our @ISA = ('Value'); +our %DISPLAY = (%context::Units::Context::DISPLAY); + # # Create a new Unit object, either by parsing a string version of # the units, or by giving the name of a known unit, or as name => unit_def, @@ -1401,7 +1400,7 @@ sub pushUnitTeX { my ($self, $units, $u, $n, $invert) = @_; return unless $n; my $def = $self->context->constants->get($u); - my $unit = ($def->{TeX} || "\\text{$u}"); + my $unit = ($def->{TeX} || ($DISPLAY{$u}{tex} ? "\\text{$DISPLAY{$u}{tex}}" : "\\text{$u}")); if ($self->{negativePowers}{$u} || $self->getFlag('useNegativePowers')) { push(@$invert, $unit . "^{-$n}"); } else { @@ -1440,6 +1439,8 @@ sub perl { package context::Units::NumberWithUnit; our @ISA = ('Value'); +our %DISPLAY = (%context::Units::Context::DISPLAY); + # # Create a new Number-with-Unit object, either by giving the number # and units separately. The number can be any MathObject that is of @@ -1504,7 +1505,12 @@ sub string { my $unit = $self->unit; my $u = $unit->stringFor('nunits', 'dunits', $unit->{order}, 0, 1); my $string = $self->number->string; - $string .= substr($u, 0, 1) eq '/' ? $u : " $u"; + my $separator = + ((%{ $unit->{dunits} } > 0 && %{ $unit->{nunits} } == 1 || %{ $unit->{dunits} } == 0) + && defined $DISPLAY{ $unit->{order}[0] }{string_separator}) + ? $DISPLAY{ $unit->{order}[0] }{string_separator} + : ' '; + $string .= substr($u, 0, 1) eq '/' ? $u : "$separator$u"; $string = '(' . $string . ')' if defined($precedence) && $precedence > 1; return $string; } @@ -1520,7 +1526,11 @@ sub TeX { if (substr($u, 0, 1) eq '/') { $tex = "\\frac{$tex}{" . $unit->raiseUnit(-1, 1)->with(negativePowers => {})->TeX . '}'; } else { - $tex .= '\\,' . $unit->TeX; + $tex .= + ((%{ $unit->{dunits} } == 0 && defined $DISPLAY{ $unit->{order}[0] }{tex_separator}) + ? $DISPLAY{ $unit->{order}[0] }{tex_separator} + : '\\,') + . $unit->TeX; } $tex = '(' . $tex . ')' if defined($precedence) && $precedence > 1; return $tex;