1# -*- mode: Perl -*-
2# /=====================================================================\ #
3# |  numprint                                                           | #
4# | Implementation for LaTeXML                                          | #
5# |=====================================================================| #
6# | Part of LaTeXML:                                                    | #
7# |  Public domain software, produced as part of work done by the       | #
8# |  United States Government & not subject to copyright in the US.     | #
9# |---------------------------------------------------------------------| #
10# | Bruce Miller <bruce.miller@nist.gov>                        #_#     | #
11# | http://dlmf.nist.gov/LaTeXML/                              (o o)    | #
12# \=========================================================ooo==U==ooo=/ #
13package LaTeXML::Package::Pool;
14use strict;
15use warnings;
16use LaTeXML::Package;
17
18#======================================================================
19# What an interesting package! A bit complex to rewrite in Perl
20# So, we'd prefer to just process the TeX source for it.
21# Basically, we read in the TeX distribution's numprint
22InputDefinitions('numprint', type => 'sty', noltxml => 1);
23# That done, however, there are a few issues.
24#  * numprint uses a mix of text & math modes to get the desired appearance.
25#    Here, we'd like to try to be slightly semantic, so rewrap things.
26#  * numprint uses a bit too much knowledge of latex's tabular
27#    and latex's tabular doesn't quite match the internals of latexml's!
28#======================================================================
29
30#======================================================================
31# (Pseudo) Semantics
32#======================================================================
33# When printing in text mode, wrap the number in something that _says_ that it's a number!
34# And, avoid putting the various symbolic bits in Math mode (if we can)
35# In Math mode, wrap it in an XMDual, and try to avoid parsing too much 'nonsense'.
36Let('\ltx@orig@numprint', '\numprint');
37DefMacro('\numprint[]{}',
38  '\ifx.#1.\ltx@numprint@{#2}\else\ltx@numprint@@{#1}{#2}\fi');
39DefMacro('\ltx@numprint@{}',
40  '\ifmmode\ltx@math@numprint@{#1}\else\ltx@text@numprint@{#1}\fi');
41DefMacro('\ltx@numprint@@{}{}',
42  '\ifmmode\ltx@math@numprint@@{#1}{#2}\else\ltx@text@numprint@@{#1}{#2}\fi');
43
44# Text Mode
45DefMacro('\ltx@text@numprint@{}',    '\ltx@text@number{\ltx@orig@numprint{#1}}');
46DefMacro('\ltx@text@numprint@@{}{}', '\ltx@text@number{\ltx@orig@numprint[#1]{#2}}');
47
48DefConstructor('\ltx@text@number{}',
49  "<ltx:text class='ltx_number' _noautoclose='1'>#1</ltx:text>");
50
51# Math mode
52DefMacro('\ltx@math@numprint@{}',
53  '\ltx@math@@numprint@{#1}{\ltx@orig@numprint{#1}}');
54DefMacro('\ltx@math@numprint@@{}{}',
55  '\ltx@math@@numprint@@{#1}{#2}{\ltx@mark@units{#1}}{\ltx@orig@numprint[#1]{#2}}');
56
57# Note that this could be even more "semantic" if we'd peel off any sign...
58DefConstructor('\ltx@math@@numprint@{}{}',
59  "<ltx:XMDual>"
60    . "<ltx:XMTok meaning='#value' role='NUMBER'>#value</ltx:XMTok>"
61    . "<ltx:XMWrap>#2</ltx:XMWrap>"
62    . "</ltx:XMDual>",
63  reversion  => '\numprint{#1}',
64  properties => sub { (value => ToString($_[1])); });
65DefConstructor('\ltx@math@@numprint@@{}{}{}{}',
66  "<ltx:XMDual>"
67    . "<ltx:XMApp>"
68    . "<ltx:XMTok meaning='times' role='MULOP'>\x{2062}</ltx:XMTok>"
69    . "<ltx:XMTok meaning='#value' role='NUMBER'>#value</ltx:XMTok>"
70    . "<ltx:XMWrap>#3</ltx:XMWrap>"
71    . "</ltx:XMApp>"
72    . "<ltx:XMWrap>#4</ltx:XMWrap>"
73    . "</ltx:XMDual>",
74  reversion  => '\numprint[#1]{#2}',
75  properties => sub { (value => ToString($_[2])); });
76
77# When printing the numbers in text, use (unicode) text symbols where possible
78DefMacroI('\nprt@sign@+',  undef, '\ifmmode+\else\ltx@text@plus\fi');
79DefMacroI('\nprt@sign@-',  undef, '\ifmmode-\else\ltx@text@minus\fi');
80DefMacroI('\nprt@sign@+-', undef, '\ifmmode\pm\else\ltx@text@plusminus\fi');
81DefPrimitiveI('\ltx@text@plus',      undef, '+');
82DefPrimitiveI('\ltx@text@minus',     undef, '-');
83DefPrimitiveI('\ltx@text@plusminus', undef, UTF(0xB1));
84
85# When defining the product sign, use the text form, if possible...
86DefMacro('\npproductsign{}',
87  '\ifmmode #1'
88    . '\else\@ifundefined{ltx@text@prod\string #1}'
89    . '{\def\nprt@prod{\ensuremath{{}#1{}}}}'
90    . '{\def\nprt@prod{\csname ltx@text@prod\string #1\endcsname}}'
91    . '\fi');
92DefPrimitiveI('\ltx@text@prod\times', undef, UTF(0xD7));
93DefPrimitiveI('\ltx@text@prod\cdot',  undef, "\x{22C5}");
94
95# Mark units, as well
96# But note that this effect is easily lost,
97# since \npunitcommand is an official "customization" point (and thus user defined)!
98DefMacro('\npunitcommand{}', '\ensuremath{\mathrm{\ltx@mark@units #1}}');
99DefConstructor('\ltx@mark@units{}', sub {
100    my ($document, $units) = @_;
101    my @nodes = $document->filterChildren($document->filterDeletions($document->absorb($units)));
102    foreach my $node (@nodes) {
103      # Only add this class to "identifiers" ?
104      my $role;
105      if (($node->nodeType == XML_ELEMENT_NODE)
106        && (!($role = $node->getAttribute('role'))
107          || ($role eq 'ID') || ($role eq 'UNKNOWN')
108          || ($role eq 'FLOATSUPERSCRIPT'))) {         # This covers things like primes(?)
109        $document->addClass($node, 'ltx_unit'); } } },
110  reversion => '#1');
111
112#======================================================================
113# Tabular issues
114#======================================================================
115# numprint is scanning for args late, the lazy TeX way,
116# but that doesn't fit the way LaTeXML want's to recognize the argument structure (too) early.
117# So, we need BOTH to define the templates n & N in a way that works with LaTeXML,
118# AND we need to tweak the scanner that looks for the end of the column
119# (since ours end differently than normal LaTeX's)
120#
121# NOTE also that current browsers do not (yet) support the char:. alignment
122# and that LaTeXML does not (yet) compute actual box dimensions.
123# Thus (at the moment) these tabulars will not be aligned on the . Sigh!
124# But maybe soon...?
125
126DefColumnType('N Optional:-1 Optional:-1 {}{}', sub {
127    my ($gullet, $nd_exp_before, $nd_exp_after, $nd_man_before, $nd_man_after) = @_;
128    $LaTeXML::BUILD_TEMPLATE->addColumn(before => Tokens(T_CS('\nprt@begin'), T_CS('\ignorespaces')),
129      after => Invocation(T_CS('\nprt@end'),
130        $nd_man_before, $nd_man_after,
131        $nd_exp_before, $nd_exp_after,
132        Tokens(), Tokens()),
133      align => 'char:' . ToString(Digest(T_CS('\nprt@decimal'))));
134    return; });
135
136DefColumnType('n Optional:-1 Optional:-1 {}{}', sub {
137    my ($gullet, $nd_exp_before, $nd_exp_after, $nd_man_before, $nd_man_after) = @_;
138    $LaTeXML::BUILD_TEMPLATE->addColumn(before => Tokens(T_CS('\nprt@begin'), T_CS('\ignorespaces')),
139      after => Invocation(T_CS('\nprt@end'),
140        $nd_man_before, $nd_man_after,
141        $nd_exp_before, $nd_exp_after,
142        T_MATH, T_MATH),
143      align => 'char:' . ToString(Digest(T_CS('\nprt@decimal'))));
144    return; });
145
1461;
147