# -*- mode: Perl -*-
# /=====================================================================\ #
# |  pgfsys latexml driver                                              | #
# | Implementation for LaTeXML                                          | #
# |=====================================================================| #
# | Part of LaTeXML:                                                    | #
# |  Public domain software, produced as part of work done by the       | #
# |  United States Government & not subject to copyright in the US.     | #
# |---------------------------------------------------------------------| #
# | Thanks to Silviu Vlad Oprea <s.oprea@jacobs-university.de>          | #
# | of the arXMLiv group for initial implementation                     | #
# |    http://arxmliv.kwarc.info/                                       | #
# | Released under the Gnu Public License                               | #
# | Released to the Public Domain                                       | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov>                        #_#     | #
# | http://dlmf.nist.gov/LaTeXML/                              (o o)    | #
# \=========================================================ooo==U==ooo=/ #
package LaTeXML::Package::Pool;
use strict;
use warnings;
use LaTeXML::Package;
use LaTeXML::Util::Image;
use List::Util qw(min max);

DebuggableFeature('pgf', "Debug pgf processing");

DefMacroI('\pgfsys@imagesuffixlist', undef,
  join('', map { '.' . $_ . ':' }
      qw(svg png gif jpg jpeg eps ps postscript ai pdf)));

#=====================================================================
# TODO:
# * ALL \pgfsys@<drivercmd> level commands should be Constructors
#   or expand into Constructors, which must yeild the svg
#   AND revert into the original \pgfsys@<drivercmd>
#   so that image generation can work.
# * Note \pgfsys@literal CAN defer the evaluation of its argument,
#   (or even discard?), so be VERY aware of expansion/digestion/ingestion timing
#=====================================================================
# Utilities
sub SVGNextObject {
  my $n = (LookupValue('svg_objcount') || 0) + 1;
  AssignValue(svg_objcount => $n, 'global');
  return $n; }

#====================================================================#
#= 0. Environment specific stuff ====================================#
#====================================================================#

sub installPGFCommands {
  # Local definitions.
  Let(T_CS('\halign'), T_CS('\lxSVG@halign'));
  Let('\ignorespaces', '\lx@inpgf@ignorespaces');    # Use local defn
  AssignValue(TEXT_MODE_BINDINGS => []);
  return; }

DefPrimitive('\lxSVG@installcommands', sub { installPGFCommands(); });

# Redefine to disappear from UnTeX, since it appears a zillion times...
# (but is this needed ???)
DefPrimitive('\lx@inpgf@ignorespaces SkipSpaces', sub { (); });
# Order might matter here
DefMacro('\lxSVG@picture', sub {
    (T_CS('\lxSVG@clearpath'),
      T_CS('\begingroup'), T_CS('\lxSVG@installcommands'),
      #        T_CS('\pgfsysprotocol@setcurrentprotocol'), T_CS('\pgfutil@empty')
    ); });

DefMacro('\endlxSVG@picture', '\endgroup');

# use overflow visible since we haven't got a good estimate of the TRUE size of the thing!
# And, if we're already in a picture?
DefConstructor('\lxSVG@insertpicture{}', sub {
    my ($document, $content, %props) = @_;
    my $current = $document->getNode();
    if ($document->getNodeQName($current) =~ /^svg:/) {    # Already in svg:svg ?
          # Seemingly this positioning is correct; otherwise inherit from context
      my ($x, $y) = (-$props{minx}, -$props{miny});
      my $gnode = $document->openElement('svg:g',
        transform   => "matrix(1 0 0 1 $x $y)",
        _scopebegin => 1, class => 'ltx_nestedsvg');
      $document->absorb($content);
      $document->closeElement('svg:g'); }
    else {
      $document->openElement('ltx:picture');
      $document->openElement('svg:svg',
        version => "1.1",
        width   => $props{pxwidth}, height => $props{pxheight},
####        viewBox  => "$props{minx} $props{miny} $props{pxwidth} $props{pxheight}",
        overflow => "visible");
      my $x0 = -(0 + $props{minx});
      my $y0 = $props{pxheight} + $props{miny};
      $document->openElement('svg:g',
        transform   => "translate($x0,$y0) matrix(1 0 0 -1 0 0)",
        _scopebegin => 1);
      $document->insertElement('svg:rect', undef,
        x     => $props{minx} - 6,            y      => $props{miny} - 6,
        width => $props{width}->pxValue + 12, height => $props{height}->pxValue + 12,
        fill  => 'none',                      stroke => '#FF00FF') if $LaTeXML::DEBUG{pgf};
      $document->absorb($content);
### Handled by \lxSVG@closegroups ???
      while ($document->maybeCloseElement('svg:g')) { }
      #                 $document->closeElement('svg:g');
      $document->closeElement('svg:svg');
      $document->closeElement('ltx:picture'); }
    return; },
  afterDigest => sub {
    my ($stomach, $whatsit) = @_;
    my $margin  = Dimension('2pt');
    my $margin2 = Dimension('4pt');
    # \pgf@picmaxx,\pgf@pixmaxy are now the SIZE of the picture!!!!!!
    my $minx    = LookupRegister('\pgf@picminx');
    my $miny    = LookupRegister('\pgf@picminy');
    my $width   = LookupRegister('\pgf@picmaxx');
    my $height  = LookupRegister('\pgf@picmaxy');
    my $w       = max($width->pxValue,  1);
    my $h       = max($height->pxValue, 1);
    my $content = $whatsit->getArg(1);
    my ($cwidth, $cheight, $cdepth) = $content->getSize;
    Debug("TIKZ size: " . ToString($width) . " x " . ToString($height)) if $LaTeXML::DEBUG{pgf};
    Debug("TIKZ pos: " . ToString($minx) . " x " . ToString($miny))     if $LaTeXML::DEBUG{pgf};
    Debug("TIKZ BODY: " . ToString($cwidth) . " x " . ToString($cheight)
        . " + " . ToString($cdepth)) if $LaTeXML::DEBUG{pgf};
    Debug("BODY is " . ToString($content)) if $LaTeXML::DEBUG{pgf};
    # Note viewbox is minx, miny, width, height (NOT maxx,maxy!)
##    $whatsit->setProperty(minx     => $minx->pxValue);
##    $whatsit->setProperty(miny     => $miny->pxValue);
    $whatsit->setProperty(minx     => 0);
    $whatsit->setProperty(miny     => 0);
    $whatsit->setProperty(width    => $width);
    $whatsit->setProperty(height   => $height);
    $whatsit->setProperty(depth    => Dimension(0));
    $whatsit->setProperty(pxwidth  => $w);
    $whatsit->setProperty(pxheight => $h);
    # or tikz macro (see corescopes)
    return; },
  # \pgfpicture seems to make a 0 sized box, which throws off our postprocessor
  reversion => sub {
    my ($whatsit) = @_;
    my $w         = $whatsit->getProperty('width')->ptValue;
    my $h         = $whatsit->getProperty('height')->ptValue;
    (T_CS('\hbox'), T_OTHER('to'), T_OTHER($w), T_OTHER('pt'), T_BEGIN,
      T_CS('\vbox'),       T_OTHER('to'),         T_OTHER($h), T_OTHER('pt'), T_BEGIN,
      T_CS('\pgfpicture'), T_CS('\makeatletter'), $whatsit->getArg(1)->revert, T_CS('\endpgfpicture'),
      T_END,               T_END) });    # ?

DefParameterType('SVGMoveableBox', sub {
    my ($gullet) = @_;
    $gullet->skipSpaces;
    my ($box, @stuff) = $STATE->getStomach->invokeToken($gullet->readXToken(1));
    Error(":expected:<box>  A <svghbox> was supposed to be here, got "
        . Stringify($box))
      unless $box && $box->isa('LaTeXML::Core::Whatsit')
      && ($box->getDefinition->getCSName =~ /^(\\hbox||)$/);
    $box; });

# Check whether a svg:foreignObject is open,
# but don't check beyond an svg:svg node, in case we're nested.
sub foreignObjectCheck {
  my ($doc) = @_;
  my $node = $doc->getNode;
  while ($node) {
    my $n = $doc->getNodeQName($node);
    return       if $n eq 'svg:svg';
    return $node if $n eq 'svg:foreignObject';
    $node = $node->parentNode; }
  return; }

DefMacroI('\pgf@shift@baseline',         undef, '0pt');
DefMacroI('\pgf@shift@baseline@smuggle', undef, '0pt');

#=====================================================================#
# 1. Beginning and ending a stream ===================================#
#=====================================================================#
DefMacro('\pgfsys@typesetpicturebox{}', <<'EoTeX');
% \pgf@picmaxx,\pgf@pixmaxy are now the SIZE of the picture!!!!!!
\advance\pgf@picmaxy by-\pgf@picminy\relax%
   \advance\pgf@picmaxx by-\pgf@picminx\relax%
   \setbox#1=\hbox{\hskip-\pgf@picminx\lower\pgf@picminy\box#1}
   \ht#1=\pgf@picmaxy%
   \wd#1=\pgf@picmaxx%
   \dp#1=0pt%
   \leavevmode%\message{width: \the\pgf@picmaxx, height:\the\pgf@picmaxy}%%
%%%   \lxSVG@insertpicture{\box#1\lxSVG@closescope}%
  \pgf@ya=\pgf@shift@baseline\relax%
  \advance\pgf@ya by-\pgf@picminy\relax%
%%%   \lxSVG@insertpicture{\raise-\pgf@ya\box#1\lxSVG@closescope}%
   \lxSVG@insertpicture{\box#1\lxSVG@closescope}%
EoTeX

DefMacro('\pgfsys@beginpicture', '');
DefMacro('\pgfsys@endpicture',   '');

DefConstructor('\pgfsys@hbox{Number}', sub {
    my ($doc, $boxnumber, %prop) = @_;
    $doc->absorb($prop{thebox}) if $prop{thebox}; },
  sizer => sub {
    my $box = $_[0]->getProperty('thebox');
    return $box && $box->getSize; },
  reversion => sub {
    my $box = $_[0]->getProperty('thebox');
    return ($box ? $box->revert : ()); },
  afterDigest => sub {
    my ($stomach, $whatsit) = @_;
    $whatsit->setProperty(thebox => LookupValue('box' . $whatsit->getArg(1)->valueOf)); }
);

#====================================================================#
#= 2. Path construction =============================================#
#====================================================================#

#=====================================================================
# Helpers

# Adding an element to the current SVG path
sub addToSVGPath {
  my ($operation, @points) = @_;
  my $newPath = join(' ', $operation, map { $_->pxValue } @points);
  if (my $currentPath = LookupValue('pgf_SVGpath')) {
    AssignValue(pgf_SVGpath => $currentPath . ' ' . $newPath, 'global'); }
  else {
    AssignValue(pgf_SVGpath => $newPath, 'global'); }
  return; }

DefPrimitive('\lxSVG@clearpath', sub {
    AssignValue(pgf_SVGpath => '', 'global'); });
DefPrimitive('\lxSVG@clearclip', sub {
    AssignValue(pgf_clipnext => 0); });

#=====================================================================
# Implementation
# These are listed as "protocolled", but actually only record the path
# the path user (eg \pgfsys@stroke) is protocolled.

# Start a path at a specific point (x,y) or to move the current point of the
# current path to (x,yp) without drawing anything upon stroking (the current
# path is 'interrupted'
DefConstructor('\pgfsys@moveto{Dimension}{Dimension}', '',
  afterDigest => sub { addToSVGPath('M', $_[1]->getArgs); },
  sizer       => 0);

# Continue the current path to (#1,#2) with a line.
DefConstructor('\pgfsys@lineto{Dimension}{Dimension}', '',
  afterDigest => sub { addToSVGPath('L', $_[1]->getArgs); },
  sizer       => 0);

# Continue the current path with a bezier curver to (#5,#6). The
# control points of the curve are at (#1,#2) and (#3,#4).
DefConstructor('\pgfsys@curveto{Dimension}{Dimension}{Dimension}'
    . '{Dimension}{Dimension}{Dimension}', '',
  afterDigest => sub { addToSVGPath('C', $_[1]->getArgs); },
  sizer       => 0);

# Append a rectangle to the current path whose lower left corner is at
# (#1,#2) and whose width/height is given by (#3,#4).
DefConstructor('\pgfsys@rect{Dimension}{Dimension}{Dimension}{Dimension}', '',
  afterDigest => sub {
    my ($x, $y, $w, $h) = $_[1]->getArgs;
    addToSVGPath('M', $x, $y);
    addToSVGPath('h', $w);
    addToSVGPath('v', $h);
    addToSVGPath('h', $w->negate);
    addToSVGPath('Z'); },
  sizer => sub {
    my ($x, $y, $w, $h) = $_[0]->getArgs;
    return ($w, $h, Dimension(0)); });
# Close the current path. This results in joining the current point of
# the path with the point specified by the last moveto
# operation.
DefConstructor('\pgfsys@closepath', '',
  afterDigest => sub { addToSVGPath('Z'); },
  sizer       => 0);

#=====================================================================#
#= 3. Canvas transformation ==========================================#
#=====================================================================#

# Perform a concatenation of the low-level current transformation
# matrix with the matrix given by the values #1 to #6. The
# transformation matrix is a transformation on a homogeneous
# 2D-coordinate system.

DefMacro('\pgfsys@transformcm {Float}{Float}{Float}{Float}{Dimension}{Dimension}',
  '\lxSVG@transformcm{#1}{#2}{#3}{#4}{#5}{#6}'
    . '\lxSVG@@transformcm{#1}{#2}{#3}{#4}{#5}{#6}');

DefConstructor('\lxSVG@transformcm {Float}{Float}{Float}{Float}{Dimension}{Dimension}', '',
  alias => '\pgfsys@transformcm',
  sizer => 0);

DefMacro('\lxSVG@@transformcm {Float}{Float}{Float}{Float}{Dimension}{Dimension}', sub {
    my ($gullet, @args) = @_;
    my $transform = 'transform=matrix('
      . join(' ',
      (map { floatformat($_->valueOf, 5) } @args[0 .. 3]),
      (map { $_->pxValue } @args[4 .. 5])) . ')';
    Invocation('\lxSVG@begingroup', TokenizeInternal($transform)); });

#=====================================================================#
#= 4. Stroking, filling, and clipping ================================#
#=====================================================================#

DefConstructor('\pgfsys@clipnext', '',
  afterDigest => sub { AssignValue(pgf_clipnext => 1); },
  sizer       => 0);

DefMacro('\pgfsys@stroke',     '\lxSVG@stroke\lxSVG@drawpath{fill:none}');
DefMacro('\pgfsys@fill',       '\lxSVG@fill\lxSVG@drawpath{stroke:none}');
DefMacro('\pgfsys@fillstroke', '\lxSVG@fillstroke\lxSVG@drawpath{}');

DefConstructor('\lxSVG@stroke',     '', alias => '\pgfsys@stroke',     sizer => 0);
DefConstructor('\lxSVG@fill',       '', alias => '\pgfsys@fill',       sizer => 0);
DefConstructor('\lxSVG@fillstroke', '', alias => '\pgfsys@fillstroke', sizer => 0);

#======================================================================

DefMacro('\lxSVG@drawpath{}', sub {
    my ($gullet, $arg) = @_;
    my $path = LookupValue('pgf_SVGpath') || '';
    if (LookupValue('pgf_clipnext')) {
      Tokens(
        T_CS('\lxSVG@clearpath'),
        T_CS('\lxSVG@clearclip'),
        Invocation('\pgfsysprotocol@literal',
          Invocation('\lxSVG@drawpath@clipped', $path, $arg))); }
    else {
      Tokens(
        T_CS('\lxSVG@clearpath'),
        Invocation('\pgfsysprotocol@literal',
          Invocation('\lxSVG@drawpath@unclipped', $path, $arg))); } });

# Seemingly can get called with no path, which is invalid
DefConstructor('\lxSVG@drawpath@unclipped{}{}',
  '?#1(<svg:path d="#1" style="#2" />)()',
  reversion => '', sizer => 0);

DefConstructor('\lxSVG@drawpath@clipped{}{}',
  '<svg:clipPath id="pgfcp#obj">'
    . '<svg:path id="pgfpath#obj" d="#1" />'
    . '</svg:clipPath>'
    . '<svg:use xlink:href="##pgfpath#obj" style="#2" />'
    . '<svg:g clip-path="url(##pgfcp#obj)">',
  reversion  => '', sizer => 0,
  properties => { obj => sub { SVGNextObject(); } });

# #======================================================================
DefMacro('\pgfsys@discardpath', '\lxSVG@discardpath\lxSVG@@discardpath');
DefConstructor('\lxSVG@discardpath', '',
  alias => '\pgfsys@discardpath', sizer => 0);
DefMacro('\lxSVG@@discardpath', sub {
    my $path = LookupValue('pgf_SVGpath') || '';
    if (LookupValue('pgf_clipnext')) {
      Tokens(
        T_CS('\lxSVG@clearpath'),
        T_CS('\lxSVG@clearclip'),
        Invocation('\pgfsysprotocol@literal',
          Invocation(T_CS('\lxSVG@discardpath@clipped'), $path))); }
    else {
      (); } });

DefConstructor('\lxSVG@discardpath@clipped{}',
  '<svg:clipPath id="pgfcp#obj">'
    . '<svg:path d="#1" />'
    . '</svg:clipPath>'
    . '<svg:g clip-path="url(##pgfcp#obj)">',
  reversion  => '', sizer => 0,
  properties => { obj => sub { SVGNextObject(); } });

#=====================================================================#
#= 5. Graphic state option ===========================================#
#=====================================================================#

#=====================================================================
# Open an <svg:g>, with some sort of options
# Note this is basically \pgf@sys@svg@gs
# but we mark the svg:g that initiates a "scope" using attribute _scopebegin
# Since svg:g's always occur within a scope, the scope will handle closing them.
# Note silliness of \ifpgfpicture if invoked outside picture(?)
DefPrimitive('\lxSVG@stopexpansion', undef);
DefMacro('\lxSVG@begingroup{}',
  '\ifpgfpicture\pgfsysprotocol@literal{\lxSVG@begingroup@{#1}}\fi');

DefConstructor('\lxSVG@begingroup@ RequiredKeyVals', sub {
    my ($doc, $kv) = @_;
    my $this = $doc->getElement;
    # Special case check: if we are in a latexml context, move up to parent svg:g
    if ($doc->getNodeQName($this) =~ /^ltx:/
      && (my $g = $doc->findnode("ancestor::svg:g[position()=1]", $this))) {
      $doc->openElement('svg:svg', _autoopened => 1); }
    $doc->openElement('svg:g', $kv->getHash); },
  reversion => '', sizer => 0);

#=====================================================================
# Implementation

DefMacro('\pgfsys@setlinewidth{}',
  '\lxSVG@setlinewidth{#1}\lxSVG@begingroup{stroke-width={#1}}');
DefMacro('\pgfsys@buttcap',
  '\lxSVG@buttcap\lxSVG@begingroup{stroke-linecap=butt}');
DefMacro('\pgfsys@roundcap',
  '\lxSVG@roundcap\lxSVG@begingroup{stroke-linecap=round}');
DefMacro('\pgfsys@rectcap',
  '\lxSVG@rectcap\lxSVG@begingroup{stroke-linecap=rect}');
DefMacro('\pgfsys@miterjoin',
  '\lxSVG@miterjoin\lxSVG@begingroup{stroke-linejoin=miter}');
DefMacro('\pgfsys@setmiterlimit{}',
  '\lxSVG@setmiterlimit{#1}\lxSVG@begingroup{stroke-miterlimit={#1}}');
DefMacro('\pgfsys@roundjoin',
  '\lxSVG@roundjoin\lxSVG@begingroup{stroke-linejoin=round}');
DefMacro('\pgfsys@beveljoin',
  '\lxSVG@beveljoin\lxSVG@begingroup{stroke-linejoin=bevel}');
DefMacro('\pgfsys@setdash{}{}',
  '\lxSVG@setdash{#1}{#2}'
    . '\edef\pgf@test@dashpattern{#1}'
    . '\lxSVG@begingroup{stroke-dasharray={\ifx\pgf@test@dashpattern\pgfutil@empty none\else#1\fi},'
    . ' stroke-dashoffset={#2}}');
DefMacro('\pgfsys@eoruletrue',
  '\lxSVG@eoruletrue\lxSVG@begingroup{fill-rule=evenodd}');
DefMacro('\pgfsys@eorulefalse',
  '\lxSVG@eorulefalse\lxSVG@begingroup{fill-rule=nonzero}');

# Constructors just for reversion
DefConstructor('\lxSVG@setlinewidth{}',  '', alias => '\pgfsys@setlinewidth',  sizer => 0);
DefConstructor('\lxSVG@buttcap',         '', alias => '\pgfsys@buttcap',       sizer => 0);
DefConstructor('\lxSVG@roundcap',        '', alias => '\pgfsys@roundcap',      sizer => 0);
DefConstructor('\lxSVG@rectcap',         '', alias => '\pgfsys@rectcap',       sizer => 0);
DefConstructor('\lxSVG@miterjoin',       '', alias => '\pgfsys@miterjoin',     sizer => 0);
DefConstructor('\lxSVG@setmiterlimit{}', '', alias => '\pgfsys@setmiterlimit', sizer => 0);
DefConstructor('\lxSVG@roundjoin',       '', alias => '\pgfsys@roundjoin',     sizer => 0);
DefConstructor('\lxSVG@beveljoin',       '', alias => '\pgfsys@beveljoin',     sizer => 0);
DefConstructor('\lxSVG@setdash{}{}',     '', alias => '\pgfsys@setdash',       sizer => 0);
DefConstructor('\lxSVG@eoruletrue',      '', alias => '\pgfsys@eoruletrue',    sizer => 0);
DefConstructor('\lxSVG@eorulefalse',     '', alias => '\pgfsys@eorulefalse',   sizer => 0);

#=====================================================================#
#= 6. Color ==========================================================#
#=====================================================================#

DefMacro('\lxSVG@setcolor{}{}', '\ifpgfpicture\lxSVG@begingroup{#1={#2}}\fi');

# Need to defer until after color/xcolor are loaded!
AtBeginDocument('\def\XC@mcolor{\pgfsetcolor{.}}');

#=====================================================================
# Implementation

DefMacro('\pgfsys@color@rgb{}{}{}',
  '\ifpgfpicture\pgfsys@color@rgb@stroke{#1}{#2}{#3}\pgfsys@color@rgb@fill{#1}{#2}{#3}\fi');

DefMacro('\lxSVG@RGB{}{}{}',    sub { Explode(Color('rgb',  $_[1], $_[2], $_[3])->toHex); });
DefMacro('\lxSVG@CMYK{}{}{}{}', sub { Explode(Color('cmyk', $_[1], $_[2], $_[3], $_[4])->toHex); });
DefMacro('\lxSVG@CMY{}{}{}',    sub { Explode(Color('cmy',  $_[1], $_[2], $_[3])->toHex); });
DefMacro('\lxSVG@GRAY{}',       sub { Explode(Color('gray', $_[1])->toHex); });

DefMacro('\pgfsys@color@rgb@stroke{}{}{}',
  '\lxSVG@color@rgb@stroke{#1}{#2}{#3}\lxSVG@begingroup{stroke=\lxSVG@RGB{#1}{#2}{#3}}');
DefMacro('\pgfsys@color@rgb@fill{}{}{}',
  '\lxSVG@color@rgb@fill{#1}{#2}{#3}\lxSVG@begingroup{fill=\lxSVG@RGB{#1}{#2}{#3}}');
DefMacro('\pgfsys@color@cmyk@stroke{}{}{}{}',
  '\lxSVG@color@cmyk@stroke{#1}{#2}{#3}{#4}\lxSVG@begingroup{stroke=\lxSVG@CMYK{#1}{#2}{#3}{#4}}');
DefMacro('\pgfsys@color@cmyk@fill{}{}{}{}',
  '\lxSVG@color@cmyk@fill{#1}{#2}{#3}{#4}\lxSVG@begingroup{fill=\lxSVG@CMYK{#1}{#2}{#3}{#4}}');
DefMacro('\pgfsys@color@cmy@stroke{}{}{}',
  '\lxSVG@color@cmy@stroke{#1}{#2}{#3}\lxSVG@begingroup{stroke=\lxSVG@CMY{#1}{#2}{#3}}');
DefMacro('\pgfsys@color@cmy@fill{}{}{}',
  '\lxSVG@color@cmy@fill{#1}{#2}{#3}\lxSVG@begingroup{fill=\lxSVG@CMY{#1}{#2}{#3}}');
DefMacro('\pgfsys@color@gray@stroke{}',
  '\lxSVG@color@gray@stroke{#1}\lxSVG@begingroup{stroke=\lxSVG@GRAY{#1}}');
DefMacro('\pgfsys@color@gray@fill{}',
  '\lxSVG@color@gray@fill{#1}\lxSVG@begingroup{fill=\lxSVG@GRAY{#1}}');

DefConstructor('\lxSVG@color@rgb@stroke{}{}{}', '', alias => '\pgfsys@color@rgb@stroke', sizer => 0);
DefConstructor('\lxSVG@color@rgb@fill{}{}{}',   '', alias => '\pgfsys@color@rgb@fill', sizer => 0);
DefConstructor('\lxSVG@color@cmyk@stroke{}{}{}{}', '', alias => '\pgfsys@color@cmyk@stroke', sizer => 0);
DefConstructor('\lxSVG@color@cmyk@fill{}{}{}{}', '', alias => '\pgfsys@color@cmyk@fill', sizer => 0);
DefConstructor('\lxSVG@color@cmy@stroke{}{}{}', '', alias => '\pgfsys@color@cmy@stroke', sizer => 0);
DefConstructor('\lxSVG@color@cmy@fill{}{}{}', '', alias => '\pgfsys@color@cmy@fill',    sizer => 0);
DefConstructor('\lxSVG@color@gray@stroke{}',  '', alias => '\pgfsys@color@gray@stroke', sizer => 0);
DefConstructor('\lxSVG@color@gray@fill{}',    '', alias => '\pgfsys@color@gray@fill',   sizer => 0);

#====================================================================#
#= 7. Pattern =======================================================#
#====================================================================#

# \pgfsys@declarepattern{name}{x1}{y1}{x2}{y2}{x step}{y step}{code}{ﬂag}
DefMacro('\pgfsys@declarepattern{} {}{}{}{}{}{} {}{Number}', sub {
    my ($gullet, $name, $x1, $y1, $x2, $y2, $x_step, $y_step, $code, $flag) = @_;
    my $op = ($flag->valueOf == 1 ? '\lxSVG@coloredpattern' : '\lxSVG@uncoloredpattern');
    return Invocation('\pgfsysprotocol@literal',
      Invocation('\lxSVG@setpattern', $x1, $y1, $x2, $y2, $x_step, $y_step),
      Invocation($op, $name, $x_step, $y_step, $code)); });

DefPrimitive('\lxSVG@setpattern {Dimension}{Dimension}{Dimension}{Dimension}{Dimension}{Dimension}', sub {
    #    my ($gullet, $x1, $y1, $x2, $y2, $x_step, $y_step) = @_;
    my ($stomach, $x1, $y1, $x2, $y2, $x_step, $y_step) = @_;
    AssignRegister('\pgf@xa' => $x1);     AssignRegister('\pgf@ya' => $y1);
    AssignRegister('\pgf@xb' => $x2);     AssignRegister('\pgf@yb' => $y2);
    AssignRegister('\pgf@xc' => $x_step); AssignRegister('\pgf@yc' => $y_step);
    return; });

DefMacro('\pgfsys@setpatternuncolored{}{}{}{}', sub {
    Invocation('\pgfsysprotocol@literal',
      Invocation('\lxSVG@setpatternuncolored@', @_[1 .. $#_])); });

DefMacro('\pgfsys@setpatterncolored{}',
  '\lxSVG@setcolor{fill}{url(\#pgfpat#1)}');

DefConstructor('\lxSVG@coloredpattern{}{Dimension}{Dimension}{}',
  '<svg:defs><svg:pattern ' .
    'id ="pgfpat#1" ' .
    'patternUnits="userSpaceOnUse" ' .
    'width="#x_step" height="#y_step">' .
    '#4' .
    '</svg:pattern></svg:defs>',
  properties => {
    x_step => sub { $_[2]->pxValue; },
    y_step => sub { $_[3]->pxValue; } },
  sizer => 0);

DefConstructor('\lxSVG@uncoloredpattern{}{Dimension}{Dimension}{}',
  '<svg:defs>' .
    '<svg:pattern '
    . 'id ="pgfpat#1" '
    . 'patternUnits="userSpaceOnUse" '
    . 'width="#x_step" height="#y_step">'
    . '<svg:symbol id="pgfsym#1">#4</svg:symbol>'
    . '</svg:pattern></svg:defs>',
  properties => {
    x_step => sub { $_[2]->pxValue; },
    y_step => sub { $_[3]->pxValue; } },
  sizer => 0);

DefConstructor('\lxSVG@setpatternuncolored@{}{}{}{}',
  '<svg:defs><svg:pattern id="pgfupat#obj" xlink:href="##pgfpat#1">'
    . '<svg:g stroke="#color" fill="#color">'
    . '<svg:use xlink:href="##pgfsym#1"/>'
    . '</svg:g>'
    . '</svg:pattern></svg:defs>'
    . '<svg:g fill="url(##pgfupat#obj)">',
  properties => {
    obj   => sub { SVGNextObject(); },
    color => sub { Color('rgb', $_[2], $_[3], $_[4]); } },
  sizer => 0);

#====================================================================#
#= 8. Scoping =======================================================#
#====================================================================#

# Saves the current graphic state on a graphic state stack. All
# changes to the graphic state parameters mentioned for \pgfsys@stroke
# and \pgfsys@fill will be local to the current graphic state and will
# the old values will be restored after endscope is used.

# Do we really need an <svg:g> w/no attributes???
DefMacro('\pgfsys@beginscope',
  '\lxSVG@beginscope\lxSVG@begingroup{_scopebegin=1}');
DefMacro('\pgfsys@endscope',
  '\pgfsysprotocol@literal{\lxSVG@closescope}\lxSVG@endscope');

DefConstructor('\lxSVG@closescope', sub {
    my ($doc, %props) = @_;
    my $n = 0;
    while (my $node = $doc->maybeCloseElement('svg:g')) {
      $n++;
      last if $node->getAttribute('_scopebegin'); }
    return; },
  sizer => 0);

DefConstructor('\lxSVG@beginscope', '',
  afterDigest => sub {
    my ($stomach) = @_;
    $stomach->begingroup; },
  alias => '\pgfsys@beginscope', sizer => 0);

DefConstructor('\lxSVG@endscope', '',
  afterDigest => sub {
    my ($stomach) = @_;
    $stomach->endgroup; },
  alias => '\pgfsys@endscope', sizer => 0);

#=====================================================================#
#= 9. Image ==========================================================#
#=====================================================================#

#=====================================================================
# Helpers

RawTeX('\newbox\lxSVG@imgbox');
#=====================================================================
# Implementation

DefMacro('\pgfsys@defineimage',
  '\edef\pgf@image{\lxSVG@includegraphics{\pgf@imagewidth}{\pgf@imageheight}{\pgf@filename}}');

DefConstructor('\lxSVG@includegraphics{}{} Semiverbatim',
  "<ltx:graphics graphic='#graphic' candidates='#candidates' options='#options'/>",
  beforeConstruct => sub {
    my ($document, $whatsit) = @_;
    if (!foreignObjectCheck($document)) {
      $document->openElement('svg:foreignObject',
        width => $whatsit->getWidth, height => $whatsit->getHeight);
      $whatsit->setProperty(OpenedForeignObject => 1); } },
  afterConstruct => sub {
    my ($document, $whatsit) = @_;
    if ($whatsit->getProperty('OpenedForeignObject')) {
      $document->closeElement('svg:foreignObject'); } },
  sizer      => \&image_graphicx_sizer,
  properties => sub {
    my ($stomach, $w, $h, $graphic) = @_;
    $w = ToString($w);
    $h = ToString($h);
    my $options = join(',', ($w ? ('width=' . $w) : ()), ($h ? ('height=' . $h) : ()));
    $graphic = ToString($graphic); $graphic =~ s/^\s+//; $graphic =~ s/\s+$//;
    my @candidates = pathname_findall($graphic, types => ['*'],
      paths => LookupValue('GRAPHICSPATHS'));
    if (my $base = LookupValue('SOURCEDIRECTORY')) {
      @candidates = map { pathname_relative($_, $base) } @candidates; }
    (graphic => $graphic,
      candidates => join(',', @candidates),
      options    => $options); },
  alias => '\includegraphics');

#=====================================================================#
#= 10. Shading =======================================================#
#=====================================================================#

#=====================================================================
# Helpers

DefMacro('\lxSVG@sh@create', sub {
    (T_CS('\lxSVG@sh@intervals'), Expand(T_CS('\pgf@sys@shading@ranges')),
      T_BEGIN,
      T_BEGIN, T_CS('\pgf@sys@shading@end@pos'), T_END,
      T_BEGIN, T_CS('\pgf@sys@shading@end@pos'), T_END,
      T_BEGIN, T_CS('\pgf@sys@shading@end@rgb'), T_END,
      T_BEGIN, T_CS('\pgf@sys@shading@end@rgb'), T_END,
      T_END,   T_BEGIN,                          T_END); });

DefMacro('\lxSVG@sh@interval@{}{}',
  '\lxSVG@sh@stashstop{\lxSVG@sh@stop{#1}{\pgf@sys@shading@end@pos}#2}');

DefPrimitive('\lxSVG@sh@stashstop{}', sub {
    PushValue('pgf_sh_stops' => Digest($_[1]));
    return; });

DefConstructor('\lxSVG@sh@stop{Dimension}{Dimension}{Float}{Float}{Float}',
  "<svg:stop offset='#offset' stop-color='#stopcolor'/>",
  properties => sub {
    my ($stomach, $start, $end, $r, $g, $b) = @_;
    (offset => floatformat($start->pxValue / $end->pxValue, 5),
      stopcolor => Color('rgb', map { $_->valueOf } $r, $g, $b)); },
  sizer => 0);

DefMacro('\lxSVG@sh@interval{}{}{}{}', sub {
    Invocation(T_CS('\lxSVG@sh@interval@'), $_[1], $_[3]); });

DefMacro('\lxSVG@sh@intervals{}', sub {
    my ($gullet, $point) = @_;
    $point = Expand($point);
    return if !ToString($point);
    (T_CS('\lxSVG@sh@interval'), $point,
      T_CS('\lxSVG@sh@intervals')); });

# These should avoid creating so many constructors at run time
DefPrimitive('\lxSVG@sh@defstripes{}{Number}', sub {
    my ($stomach, $name, $flag) = @_;
    my $stops = List(@{ LookupValue('pgf_sh_stops') });
    AssignValue(pgf_sh_stops => [], 'global');
    my $x = LookupRegister('\pgf@x')->pxValue;
    my $y = LookupRegister('\pgf@y')->pxValue;
    DefPrimitiveI('\@pgfshading' . ToString($name) . '!', undef, sub {
        my $objcount = SVGNextObject();
        DefConstructor('\lxSVG@sh@defs',
          '<svg:defs><svg:linearGradient id="pgfsh' . $objcount . '" '
            . ($flag->valueOf == 1 ? 'gradientTransform="rotate(90)"' : '') . '>'
            . '#stops'
            . '</svg:linearGradient></svg:defs>',
          properties => { stops => $stops },
          sizer      => 0
        );
        DefConstructor('\lxSVG@sh',
          '<svg:rect width="' . $x . '" height="' . $y . '" '
            . 'style="fill:url(##pgfsh' . $objcount . ');stroke:none" />',
          sizer => 0);
        DefMacro('\lxSVG@pos', sub { Invocation(T_CS('\pgfpoint'), $x, $y); });
        return; }, scope => 'global');
    return; });

DefPrimitive('\lxSVG@sh@defcircles{}', sub {
    my ($stomach, $name) = @_;
    my $stops = List(@{ LookupValue('pgf_sh_stops') });
    AssignValue(pgf_sh_stops => [], 'global');
    my $endpos = Dimension(ToString(Expand(T_CS('\pgf@sys@shading@end@pos'))))->pxValue;
    my $x      = floatformat(LookupRegister('\pgf@x')->pxValue * 8 / ($endpos * 16) + 0.5, 5);
    my $y      = floatformat(LookupRegister('\pgf@y')->pxValue * 8 / ($endpos * 16) + 0.5, 5);
    DefPrimitiveI('\@pgfshading' . ToString($name) . '!', undef, sub {
        my $objcount = SVGNextObject();
        DefConstructor('\lxSVG@sh@defs',
          "<svg:defs><svg:radialGradient id='pgfsh$objcount' fx='$x' fy='$y'>"
            . '#stops'
            . '</svg:radialGradient></svg:defs>',
          properties => { stops => $stops },
          sizer      => 0
        );
        DefConstructor('\lxSVG@sh',
          "<svg:circle cx='$endpos' cy='$endpos' r='$endpos'"
            . " style='fill:url(##pgfsh$objcount);stroke:none'/>",
          sizer => 0);
        DefMacro('\lxSVG@pos', sub {
            Invocation(T_CS('\pgfpoint'), 2 * $endpos, 2 * $endpos) });
        return; }, scope => 'global');
    return; });

DefConstructor('\lxSVG@sh@insert{Dimension}{Dimension}{}',
  '<svg:g transform="translate(#x #y)">#3</svg:g>',
  properties => {
## Something odd with scales here; these SHOULD be pixels!
##    x => sub { $_[1]->pxValue; },
##    y => sub { $_[2]->pxValue; } },
    x => sub { $_[1]->ptValue; },
    y => sub { $_[2]->ptValue; } },
  sizer => 0);

# \lxSVG@process{Dimension}{Dimension}
# NOTE: This just can't be right; see callers!?!?!?
DefMacro('\lxSVG@process{}{}',
  '\ifdim\pgf@picmaxx<#1\global\pgf@picmaxx=#1\fi'
    . '\ifdim\pgf@picmaxy<#2\global\pgf@picmaxy=#2\fi'
    . '\ifdim\pgf@picminx>#1\global\pgf@picminx=#1\fi'
    . '\ifdim\pgf@picminy>#2\global\pgf@picminy=#2\fi');

#=====================================================================
# Implementation

DefMacro('\pgfsys@shadinginsidepgfpicture{}', <<'EoTeX');
%\message{Using shading \string#1}
 #1\lxSVG@sh@defs%
  \pgf@process{\lxSVG@pos}%
  \pgf@x=-.5\pgf@x\relax\pgf@y=-.5\pgf@y\relax%
%  \lxSVG@process{\pgf@x}{\pgf@y}%
%  \pgf@x=-1\pgf@x\relax\pgf@y=-1\pgf@y\relax%
%  \lxSVG@process{\pgf@x}{\pgf@y}%
  \lxSVG@sh@insert{\pgf@x}{\pgf@y}{\lxSVG@sh}
EoTeX

DefMacro('\pgfsys@shadingoutsidepgfpicture{}', <<'EoTeX');
\begingroup\lxSVG@installcommands%
  #1%
  \setbox\pgfpic=\hbox to0pt{%
      \lxSVG@sh@defs%
      \lxSVG@sh%
    }%
    \pgf@process{\lxSVG@pos}%
    \pgf@picminx=0pt%
    \pgf@picminy=0pt%
    \pgf@picmaxx=\pgf@x%
    \pgf@picmaxy=\pgf@y%
    \pgfsys@typesetpicturebox{\pgfpic}%
  \endgroup
EoTeX

# \pgfsys@horishading{}{Dimension}{}
DefMacro('\pgfsys@horishading{}{}{}', sub {
    my ($gullet, $name, $height, $specs) = @_;
    (Invocation(T_CS('\pgf@parsefunc'), $specs),
      T_CS('\lxSVG@sh@create'),
      Invocation(T_CS('\pgf@process'), Invocation(T_CS('\pgfpoint'),
          T_CS('\pgf@sys@shading@end@pos'), $height)),
      Invocation(T_CS('\lxSVG@sh@defstripes'),
        $name, Number(0))); });

## \pgfsys@vertshading{}{Dimension}{}
DefMacro('\pgfsys@vertshading{}{}{}', sub {
    my ($gullet, $name, $height, $specs) = @_;
    (Invocation(T_CS('\pgf@parsefunc'), $specs),
      T_CS('\lxSVG@sh@create'),
      Invocation(T_CS('\pgf@process'), Invocation(T_CS('\pgfpoint'),
          T_CS('\pgf@sys@shading@end@pos'), $height)),
      Invocation(T_CS('\lxSVG@sh@defstripes'), $name, Number(1))); });

DefMacro('\pgfsys@radialshading{}{}{}', sub {
    my ($gullet, $name, $point, $specs) = @_;
    (Invocation(T_CS('\pgf@parsefunc'), $specs),
      T_CS('\lxSVG@sh@create'),
      Invocation(T_CS('\pgf@process'),         $point),
      Invocation(T_CS('\lxSVG@sh@defcircles'), $name)); });

# Wow... postscript function...
DefMacro('\pgfsys@functionalshading{}{}{}', sub {
    my ($gullet, $name, $ll, $ur, $psfct) = @_;
    Let(T_CS('\lxSVG@sh@defs'), T_CS('\relax'));
    Let(T_CS('\lxSVG@sh'),      T_CS('\relax'));
    Let(T_CS('\lxSVG@pos'),     T_CS('\relax')); });

#=====================================================================#
#= 11. Transparency ==================================================#
#=====================================================================#

DefMacro('\pgfsys@stroke@opacity{}',
  '\lxSVG@stroke@opacity{#1}\lxSVG@begingroup{stroke-opacity={#1}}');
DefMacro('\pgfsys@fill@opacity{}',
  '\lxSVG@fill@opacity{#1}\lxSVG@begingroup{fill-opacity={#1}}');
DefMacro('\pgfsys@fadingfrombox{}{}',
  '\lxSVG@fadingfrombox{#1}{#2}');    # NOT IMPLEMENTED
DefMacro('\pgfsys@usefading{}{}{}{}{}{}{}',
  '\lxSVG@usefading{#1}{#2}{#3}{#4}{#5}{#6}{#7}');    # NOT IMPLEMENTED
DefMacro('\pgfsys@transparencygroupfrombox{}',
  '\lxSVG@transparencygroupfrombox{#1}');             # NOT IMPLEMENTED
DefMacro('\pgfsys@definemask',
  '\lxSVG@definemask');                               # NOT IMPLEMENTED

# For reversion
DefConstructor('\lxSVG@stroke@opacity{}',        '', alias => '\pgfsys@stroke@opacity', sizer => 0);
DefConstructor('\lxSVG@fill@opacity{}',          '', alias => '\pgfsys@fill@opacity',   sizer => 0);
DefConstructor('\lxSVG@fadingfrombox{}{Number}', '', alias => '\pgfsys@fadingfrombox',  sizer => 0);
DefConstructor('\lxSVG@usefading{}{}{}{}{}{}{}', '', alias => '\pgfsys@usefading',      sizer => 0);
DefConstructor('\lxSVG@transparencygroupfrombox{Number}', '', alias => '\pgfsys@transparencygroupfrombox{}', sizer => 0);
DefConstructor('\lxSVG@definemask', '', alias => '\pgfsys@definemask', sizer => 0);

#=====================================================================#
#= 12. Reusable objects ==============================================#
#=====================================================================#

DefConstructor('\pgfsys@invoke{}', '#1', sizer => 0);
DefMacro('\pgfsys@markposition{}', '');

#=====================================================================#
#= 13. Invisibility ==================================================#
#=====================================================================#

RawTeX('\def\pgfsys@begininvisible#1\pgfsys@endinvisible{}');    # well...

#=====================================================================#
#= 14. The protocol subsystem ========================================#
#=====================================================================#

# # Adds the literal text to the current protocol, after it has been
# # \edef-ed. This command will always protocol.
# DefPrimitive('\pgfsysprotocol@literalbuffered{}', sub {});

# # First calls \pgfsysprotocol@literalbuffered on literal text . Then,
# # if protocolling is currently switched off, the literal text is passed
# # on to pgfsys@invoke, which just inserts it into the document.
# DefMacro('\pgfsysprotocol@literal{}', '');

# # Stores the current protocol in macro name for later use.
# DefPrimitive('\pgfsysprotocol@getcurrentprotocol{}', sub {});

# # Sets the current protocol to macro name.
# DefMacro('\pgfsysprotocol@setcurrentprotocol{}', sub{});

# # Inserts the text stored in the current protocol into the file.
# DefMacro('\pgfsysprotocol@invokecurrentprotocol', sub{});

# # First inserts the current protocol, then sets the current protocol to
# # the empty string.
# DefMacro('\pgfsysprotocol@flushcurrentprotocol', sub{});

#=====================================================================#
#= 15. Overflowing ===================================================#
#=====================================================================#

#=====================================================================#
#= XX. Matrix ========================================================#
#=====================================================================#
# Here we're basically groping to figure out how to adapt
# LaTeXML's Alignment structures to the tikz code.
# Note that svg doesn't have a table/array structure.
#
# I guess we should just be putting <svg:g> in, but with appropriate class?
# For this to work, we'll have to come in after the fact, sum up things & specify cell sizes.
# Also, it seems that we've already ended up with a foreign object box?
#
# But there are still some major problems:
# On top of that, we're somehow getting out-of-sync with the
# bgroup/egroup & begingroup/endgroup pairs that tikz/pgf is adding
# and the ones embedded in our alignment machinery.
# I _THINK_ we're running afoul of when the last item of \matrix ends with \\ ???
#
# It also appears that in order to align the cells,
# tikz makes their width 0; unfortunately this makes them
# disappear in the svg (overflow=visible doesn't seem to help)

# row sep, column sep are not yet accounted for...
DefConstructor('\lxSVG@halign BoxSpecification',
  "#alignment",
  reversion => sub {
    my ($whatsit, $spec) = @_;
    my $template  = $whatsit->getProperty('template');
    my $alignment = $whatsit->getProperty('alignment');
    Tokens(T_CS('\halign'), Revert($spec), T_BEGIN, Revert($template), T_CS('\cr'),
      Revert($alignment), T_END); },
  bounded => 1,
  #  sizer       => '#1',
  sizer       => sub { $_[0]->getProperty('alignment')->getSize; },
  afterDigest => sub {
    my ($stomach, $whatsit) = @_;
    $stomach->bgroup;    # This will be closed by the \halign's closing }  (or will it?)
    my $template = parseHAlignTemplate($stomach->getGullet, $whatsit);
    my $spec     = $whatsit->getArg(1);
    # Parse the template and initialize the Alignment object.
    tikzAlignmentBindings($template, undef,
      #    alignmentBindings($template, undef,
      attributes => {
        width => orNull(GetKeyVal($spec, 'to')) });
    digestAlignmentBody($stomach, $whatsit);
    $stomach->egroup;
    $LaTeXML::ALIGN_STATE--;    # Balance the opening { OUTSIDE of the masking of ALIGN_STATE
    return; });

sub tikzAlignmentBindings {
  my ($template, $mode, %properties) = @_;
  $mode = LookupValue('MODE') unless $mode;
  my $ismath    = $mode =~ /math$/;
  my $container = 'svg:g';
  my $rowtype   = 'svg:g';
  my $coltype   = 'svg:g';
  my $alignment = LaTeXML::Core::Alignment->new(
    template       => $template,
    openContainer  => sub { openTikzAlignment($container, @_); },
    closeContainer => sub { closeTikzAlignmentElement($container, @_); },
    openRow        => sub { openTikzAlignmentRow($rowtype, @_); },
    closeRow       => sub { closeTikzAlignmentElement($container, @_); },
    openColumn     => sub { openTikzAlignmentCol($coltype, @_); },
    closeColumn    => sub { closeTikzAlignmentElement($container, @_); },
    isMath         => $ismath,
    properties     => { preserve_structure => 1, %properties });
  AssignValue(Alignment => $alignment);
  Let(T_MATH, ($ismath ? '\@dollar@in@mathmode' : '\@dollar@in@textmode'));
  return; }

# Note that pgf *seems* to have set the coordinate system to the center(?) (w/ \pgfsys@transformcm)
# Additionally, there's a svg:g outside the alignment (w/ ltx_svg_fog)
# The following tweaks (heuristically) the transform to position the alignment object
#
# Coordinates are currently pgf: y moves UP
# Coordinates of alignment are : y moves DOWN
sub openTikzAlignment {
  my ($tag, $doc, %props) = @_;
  my $w          = ($props{cwidth}  && $props{cwidth}->pxValue)  || 0;
  my $h          = ($props{cheight} && $props{cheight}->pxValue) || 0;
  my $d          = ($props{cdepth}  && $props{cdepth}->pxValue)  || 0;
  my @rowheights = ($props{rowheights}   ? @{ $props{rowheights} }   : (Dimension(0)));
  my @colwidths  = ($props{columnwidths} ? @{ $props{columnwidths} } : (Dimension(0)));
  my $x          = 0;
  # This SHOULD be just $h+$d, but the shift is necessary to align
  # w/additional material outside the \halign ( arrows & such)
  my $y = ($h + $d) - $rowheights[-1]->pxValue / 2;    # HEURISTIC adjustment! half of LAST row!?
  Debug("TIKZ Alignment size $w x $h + $d"
      . "  rows: " . join(',', map { $_->pxValue; } @rowheights)
      . "  cols: " . join(',', map { $_->pxValue; } @colwidths)
      . " => alignment adjustment = $x, $y") if $LaTeXML::DEBUG{pgf};
  my $array = $doc->openElement($tag, class => 'ltx_tikzmatrix',
    _scopebegin => 1,
    transform   => "matrix(1 0 0 -1 $x $y)",
    %props);
  $doc->insertElement('svg:rect', undef, class => 'ltx_debug',
    x => -4, y => -4, width => $w + 8, height => $h + $d + 8, stroke => '#FF0000', fill => 'none')
    if $LaTeXML::DEBUG{pgf};
  return $array; }

sub closeTikzAlignmentElement {
  my ($tag, $document) = @_;
  my $node = $document->closeElement($tag);
  if ($LaTeXML::DEBUG{pgf} && $node && (($node->getAttribute('class') || '') eq 'ltx_debug')) {
    my $rect = $node->firstChild;    # Move the rectangle to the end, so it overlays
    $node->removeChild($rect);
    $node->appendChild($rect); }
  return $node; }

sub openTikzAlignmentRow {
  my ($tag, $doc, %props) = @_;
  my $class = 'ltx_tikzmatrix_row' . ($props{class} ? ' ' . $props{class} : '');
  my $y     = ($props{y}       && $props{y}->pxValue)       || 0;
  my $w     = ($props{cwidth}  && $props{cwidth}->pxValue)  || 0;
  my $h     = ($props{cheight} && $props{cheight}->pxValue) || 0;
  my $d     = ($props{cdepth}  && $props{cdepth}->pxValue)  || 0;
  my $yy    = $y + $h;
  Debug("TIKZ alignment Row ($w x $h + $d); y=$yy; ") if $LaTeXML::DEBUG{pgf};
  my $row = $doc->openElement($tag, class => $class,
    _scopebegin => 1,
    transform   => "matrix(1 0 0 1 0 $yy)",
  );
  $doc->insertElement('svg:rect', undef,
    x => -2, y => -$h - 2, width => $w + 4, height => $h + $d + 4, stroke => '#00FF00', fill => 'none')
    if $LaTeXML::DEBUG{pgf};
  return $row; }

sub openTikzAlignmentCol {
  my ($tag, $doc, %props) = @_;
  my $class = 'ltx_tikzmatrix_col' . ($props{class} ? ' ' . $props{class} : '');
  # BEWARE: pgf places each column at 0 width, preceded by an \hskip width/2
  # and followed by an extra column of width/2.
  # Be careful accounting for rowwidths, etc while pruning & positioning in Alignment.pm
  my $w = ($props{cwidth}  && $props{cwidth}->pxValue)  || 0;
  my $h = ($props{cheight} && $props{cheight}->pxValue) || 0;
  my $d = ($props{cdepth}  && $props{cdepth}->pxValue)  || 0;
  my $x = ($props{x}       && $props{x}->pxValue)       || 0;
  my $y = 0;
  Debug("TIKZ alignment Cell ($w x $h + $d); x=$x; "
      . join(',', map { $_ . "=" . ToString($props{$_}); } sort keys %props)) if $LaTeXML::DEBUG{pgf};
  my $col = $doc->openElement($tag, class => $class,
    _scopebegin => 1,
    transform   => "matrix(1 0 0 -1 $x $y)",    # NOTE: Flip!!!
  );
  $doc->insertElement('svg:rect', undef,
    x => -1, y => -$h - 1, width => $w + 2, height => $h + $d + 2, stroke => '#0000FF', fill => 'none')
    if $LaTeXML::DEBUG{pgf};
  return $col; }

#=====================================================================
# Dealing with quick commands

# Coordinates
# Let(T_CS('\pgfqpoint'),T_CS('\pgfpoint'));
# Let(T_CS('\pgfqpointxy'),T_CS('\pgfpointxy'));
# Let(T_CS('\pgfqpointxyz'),T_CS('\pgfpointxyz'));
# Let(T_CS('\pgfqpointscale'),T_CS('\pgfpointscale'));

# Path construction

1;
