# -*- mode: Perl -*-
# /=====================================================================\ #
# | authblk.sty.ltxml | #
# |=====================================================================| #
# | Part of LaTeXML: | #
# | Public domain software, produced as part of work done by the | #
# | United States Government & not subject to copyright in the US. | #
# |---------------------------------------------------------------------| #
# | 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;
# NOTE: preliminary implementation focusing on the logic;
# We currently don't layout the author/afflition block quite the way authblk does:
# * don't combine authors with same affiliation,
# * don't use marks to associate the author/affiliation
DefMacro('\Affilfont', '\normalfont');
DefMacro('\Authfont', '\normalfont');
DefMacro('\Authsep', ',');
DefMacro('\Authand', ' and ');
DefMacro('\Authands', ', and ');
DefMacro('\authorcr', '\\\\\Authfont');
# package bookkeeping
DefConditional('\ifnewaffil');
DefRegister('\affilsep' => Dimension('1em'));
DefRegister('\@affilsep' => Dimension('1em'));
NewCounter('Maxaffil');
SetCounter('Maxaffil', Number(2));
NewCounter('authors');
NewCounter('affil');
NewCounter('@affil');
DefMacro('\the@affil', 'affil\arabic{@affil}');
DefMacro('\author[]{}', sub {
return map { Invocation(T_CS('\lx@ab@author'), $_[1], Tokens(@$_)) }
SplitTokens($_[2], T_CS('\and'), T_OTHER(',')); });
DefMacro('\lx@ab@author[]{}',
'\@add@frontmatter{ltx:creator}[role=author]'
. '{\@personname{#2}\lx@split@authormark{#1}}');
# Preserve the order, where we add an "affiliationmark" element
# for each numeric mark in the \author[] optional argument
DefMacro('\lx@split@authormark{}', sub {
if (my @marks = split(/\s*,\s*/, ToString($_[1]))) {
return (map { T_CS('\lx@authormark'), T_OTHER($_) } @marks); }
else {
return (T_CS('\lx@authormark'), T_CS('\the@affil')); } });
DefConstructor('\lx@authormark{}',
"^?#mark(<ltx:contact role='affiliationmark' _mark='#mark'></ltx:contact>)()",
properties => sub { (mark => ToString($_[1])); });
DefConstructor('\affil[]{}',
"^?#2(<ltx:note mark='#mark' role='affiliationtext'>#2</ltx:note>)()",
afterDigest => sub {
my ($stomach, $whatsit) = @_;
my $mark = $whatsit->getArg(1);
if (!defined $mark) {
$mark = Digest(T_CS('\the@affil')); # get mark
StepCounter('@affil'); } # and THEN step counter!
$whatsit->setProperty(mark => ToString($mark));
return; });
Tag('ltx:document', afterClose => \&authblkRelocateAffil);
sub authblkRelocateAffil {
my ($document) = @_;
my @author_nodes = $document->findnodes(".//ltx:contact[\@role='affiliationmark' and \@_mark]");
my @affil_nodes = $document->findnodes(".//ltx:note[\@role='affiliationtext']");
# first, unbind all affiliation text nodes - they get reparented or discarded.
my %mark_to_affil = ();
for my $affil_node (@affil_nodes) {
$affil_node->parentNode->removeChild($affil_node);
# if we get conflicting marks -- which we shouldn't
# the last node wins
if (my $mark = $affil_node->getAttribute('mark')) {
$mark_to_affil{$mark} = $affil_node; } }
# Now process all affiliationmarks
for my $author_node (@author_nodes) {
$author_node->setAttribute(role => 'affiliation');
my $mark = $author_node->getAttribute('_mark') || '';
if (my $affil_node = $mark_to_affil{$mark}) {
$document->appendClone($author_node, $affil_node->childNodes); } }
return; }
# we shouldn't need these, but if someone is using them low-level
# might as well provide them
DefMacro('\AB@authnote{}', '\textsuperscript{\normalfont#1}');
DefMacro('\AB@affilnote{}', '\textsuperscript{\normalfont#1}');
#======================================================================
1;