# -*- 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}{flag}
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;