summaryrefslogtreecommitdiff
path: root/Master
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2014-08-11 23:01:04 +0000
committerKarl Berry <karl@freefriends.org>2014-08-11 23:01:04 +0000
commit64660cd86edb8ae68a28d7ef7550583d76ae5f5e (patch)
tree35b526e4b35ea14914e1830ce292a3c442e44a25 /Master
parent7f08054bf5aebef2dca4f32b2d21e95a50f7e2c5 (diff)
luatodonotes (8aug14)
git-svn-id: svn://tug.org/texlive/trunk@34908 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master')
-rw-r--r--Master/texmf-dist/doc/lualatex/luatodonotes/README25
-rw-r--r--Master/texmf-dist/doc/lualatex/luatodonotes/luatodonotes.pdfbin0 -> 186868 bytes
-rw-r--r--Master/texmf-dist/source/lualatex/luatodonotes/luatodonotes.dtx1823
-rw-r--r--Master/texmf-dist/source/lualatex/luatodonotes/luatodonotes.ins65
-rw-r--r--Master/texmf-dist/tex/lualatex/luatodonotes/inspect.lua297
-rw-r--r--Master/texmf-dist/tex/lualatex/luatodonotes/luatodonotes.lua2160
-rw-r--r--Master/texmf-dist/tex/lualatex/luatodonotes/luatodonotes.sty634
-rw-r--r--Master/texmf-dist/tex/lualatex/luatodonotes/path_line.lua120
-rw-r--r--Master/texmf-dist/tex/lualatex/luatodonotes/path_point.lua73
-rwxr-xr-xMaster/tlpkg/bin/tlpkg-ctan-check2
-rwxr-xr-xMaster/tlpkg/libexec/ctan2tds1
-rw-r--r--Master/tlpkg/tlpsrc/collection-luatex.tlpsrc1
-rw-r--r--Master/tlpkg/tlpsrc/luatodonotes.tlpsrc0
13 files changed, 5200 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/lualatex/luatodonotes/README b/Master/texmf-dist/doc/lualatex/luatodonotes/README
new file mode 100644
index 00000000000..cd40a3995b4
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/luatodonotes/README
@@ -0,0 +1,25 @@
+This package allows you to insert comments for further editing in your document.
+The comments are shown in the margins besides the text.
+Different styles for the comments can be used, which can be configured using
+package options.
+This package depends heavily on Lua, so it can only be used with LuaLaTeX.
+
+luatodonotes is based on the todonotes package by Henrik Skov Midtiby
+(http://www.ctan.org/pkg/todonotes).
+
+Installation
+------------
+Run 'latex luatodonotes.ins' to generate the package files and copy the listed
+files into your TEXMF tree.
+
+License
+-------
+The luatodonotes package is subject to the LATEX Project Public License.
+The following external lua libraries are used:
+
+- path_line.lua and path_point.lua
+ taken from luapower.com (Public domain)
+
+- inspect.lua
+ by Enrique GarcĂ­a Cota
+ MIT License
diff --git a/Master/texmf-dist/doc/lualatex/luatodonotes/luatodonotes.pdf b/Master/texmf-dist/doc/lualatex/luatodonotes/luatodonotes.pdf
new file mode 100644
index 00000000000..d6ebf3dae39
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/luatodonotes/luatodonotes.pdf
Binary files differ
diff --git a/Master/texmf-dist/source/lualatex/luatodonotes/luatodonotes.dtx b/Master/texmf-dist/source/lualatex/luatodonotes/luatodonotes.dtx
new file mode 100644
index 00000000000..3188307f4c5
--- /dev/null
+++ b/Master/texmf-dist/source/lualatex/luatodonotes/luatodonotes.dtx
@@ -0,0 +1,1823 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2014 by Fabian Lipp <fabian.lipp@gmx.de>
+% based on the todonotes package
+% by Henrik Skov Midtiby <henrikmidtiby@gmail.com>
+% -------------------------------------------------------
+%
+% This file may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.2
+% of this license or (at your option) any later version.
+% The latest version of this license is in:
+%
+% http://www.latex-project.org/lppl.txt
+%
+% and version 1.2 or later is part of all distributions of LaTeX
+% version 1999/12/01 or later.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\ProvidesFile{luatodonotes.dtx}
+%</driver>
+%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+%<package>\ProvidesPackage{luatodonotes}
+%<*package>
+ [2014/08/07 v0.1 luatodonotes source and documentation.]
+%</package>
+%
+%<*driver>
+\documentclass{ltxdoc}
+\usepackage{wrapfig}
+\usepackage[colorlinks, linkcolor=black, urlcolor=blue]{hyperref}
+\usepackage[colorinlistoftodos, shadow]{luatodonotes}[2014/06/08]
+\usepackage{fontspec}
+\usepackage{amsmath}
+\usepackage{setspace}
+\usepackage{soul}
+\setcounter{tocdepth}{2}
+\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+ \DocInput{luatodonotes.dtx}
+%\iffalse
+% \PrintChanges
+% \PrintIndex
+%\fi
+\end{document}
+%</driver>
+% \fi
+%
+% \CheckSum{841}
+%
+% \CharacterTable
+% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
+% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
+% Digits \0\1\2\3\4\5\6\7\8\9
+% Exclamation \! Double quote \" Hash (number) \#
+% Dollar \$ Percent \% Ampersand \&
+% Acute accent \' Left paren \( Right paren \)
+% Asterisk \* Plus \+ Comma \,
+% Minus \- Point \. Solidus \/
+% Colon \: Semicolon \; Less than \<
+% Equals \= Greater than \> Question mark \?
+% Commercial at \@ Left bracket \[ Backslash \\
+% Right bracket \] Circumflex \^ Underscore \_
+% Grave accent \` Left brace \{ Vertical bar \|
+% Right brace \} Tilde \~}
+%
+%
+% \changes{0.1}{2014/08/07}{The first version of the package}
+% \GetFileInfo{luatodonotes.dtx}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+%
+% \iffalse
+% A macro for marking things todo before the next relase
+% (typically update of documentation).
+% \fi
+% \newcommand{\donow}[1]{\todo[color=blue]{#1}}
+%
+%
+% \title{The \textsf{luatodonotes} package\thanks{This document
+% corresponds to \textsf{luatodonotes}~\fileversion, dated \filedate.}}
+% \author{Fabian Lipp\thanks{This documentation and the whole package is based
+% on version 1.0.2 of the todonotes package by Henrik Skov Midtiby.} \\
+% \texttt{fabian.lipp@gmx.de}}
+%
+% \maketitle
+%
+% \begin{abstract}
+% The |luatodonotes| package allows you to insert to--do items in your
+% document. At any point in the document a list of all the inserted
+% to--do items can be listed with the |\listoftodos| command.
+%
+% It is an extended version of the |todonotes| package and uses more advanced
+% algorithms to place the to--do notes notes on the page.
+% For this algorithms it depends on Lua\TeX.
+% \end{abstract}
+%
+% \tableofcontents
+%
+% \newpage
+% \section{Introduction}
+%
+% The |luatodonotes| package makes three commands available to the
+% user: |\todo[]{}|, |\missingfigure{}| and |\listoftodos|.
+% |\todo[]{}| and |\missingfigure{}| makes it possible to insert
+% notes in your document about things that has to be done later
+% (todonotes \ldots).
+% This package is based on version 1.0.2 of
+% |todonotes|\footnote{\url{http://www.ctan.org/pkg/todonotes}} by Henrik Skov
+% Midtiby.
+%
+% The positions of the notes on the page is determined using algorithms
+% implemented in Lua, so you have to process your documents using Lua\LaTeX.
+% The package can be used as a drop-in replacement for the original
+% |todonotes| package, you
+% only need to modify |\usepackage{todonotes}| to |\usepackage{luatodonotes}|.
+% Note that |todonotes| and |luatodonotes| must not be loaded inside the same
+% document.
+%
+% Some alternatives for the luatodonotes package are:
+% \begin{itemize}
+% \item \href{http://www.ctan.org/tex-archive/macros/latex/contrib/easy-todo}{easy-todo}\\
+% Depends on |color|, |tocloft| and |ifthen|, small feature set.
+% \item \href{http://www.ctan.org/pkg/fixmetodonotes}{fixmetodonotes}\\
+% Depends on |graphicx|, |color|, |transparent|, |watermark|, |fix-cm|, |ulem| and |tocloft|, small feature set.
+% \item \href{http://www.ctan.org/tex-archive/macros/latex/contrib/todo}{todo}\\
+% Depends on |amssymb|, medium feature set.
+% \item \href{http://www.ctan.org/tex-archive/macros/latex/contrib/fixme}{fixme}\\
+% Large package with a lot of features.
+% \item \href{http://www.ctan.org/pkg/todonotes}{todonotes}
+% \end{itemize}
+%
+% Compared to the classical todonotes this package has more advanced algorithms
+% and more configuration options to control the position of the notes on the
+% page.
+% Additionally, we are able to place notes at almost every position on the
+% page, e.\,g., in floating environments or in footnotes.
+% As a disadvantage luatodonotes requires Lua\LaTeX for document processing, so
+% a standard |pdflatex| won't work.
+% Depending on the chosen layout for the to--do notes the runtime can be much
+% higher than with todonotes.
+% Labels placed by luatodonotes can conflict with text placed with
+% |\marginpar|.
+%
+% The main reason for considering other packages is that the todonotes
+% package is quire large and relies heavily on tikz.
+% This can slow down compilation of large documents significantly.
+% The mentioned alternatives have a different feature set and does not
+% rely on tikz, which makes them require less ressoureces.
+%
+%
+% \subsection{Usage}
+% The package is loaded with |\usepackage|\oarg{options}|{luatodonotes}|.
+% Valid options are described in Section~\ref{subsecPackageOptions}.
+% Note that |todonotes| must \emph{not} be loaded.
+% You have to use |lualatex| to process your document, |pdflatex| will not
+% work.
+% The package depends on positions written to the aux-file, so you have to run
+% |lualatex| twice or even three times to get the labels and leaders for the
+% notes right.
+%
+% \DescribeMacro{\todo}
+% My \index{\todo}most common usage of the todonotes package, is to
+% insert an todonotes somewhere in a latex document.
+% An example of this usage is the command
+%
+% |\todo{Make a cake \ldots}|,
+%
+% \noindent
+% which renders like\todo{Make a cake \ldots}.
+% The |\todo| command has this structure:
+% |\todo|\oarg{options}\marg{todo text}.
+% The |todo text| is the text that will be shown in the todonote and
+% in the list of todos. The optional argument |options|, allows the
+% user to customize the appearance of the inserted todonotes.
+% For a description of all the options see section
+% \ref{subsecTodoOptions}.
+%
+% \DescribeMacro{\todoarea}
+% The |\todoarea| is similiar to |\todo|, but is able to highlight a specified
+% area in the text, to which the note is connected.
+% The command has this structure:
+% |\todoarea|\oarg{options}\marg{note text}\marg{highlighted text}.
+% This command was not tested extensively until now, so it should be used with
+% caution.
+%
+% \DescribeMacro{\missingfigure}
+% The |\missingfigure| command inserts an image containing an
+% attention sign and the given text.
+% The command takes only one argument
+% |\missingfigure|\marg{text}, a text string that could
+% describe what the figure should consist of.
+% An example of its usage could be
+%
+% |\missingfigure{Make a sketch of the structure of a trebuchet.}|
+%
+% \noindent
+% which renders like.
+%
+% \missingfigure{Make a sketch of the structure of a trebuchet.}
+%
+%
+% \DescribeMacro{\listoftodos}
+% The |\listoftodos| command inserts a list of all the todos in the
+% current document. |\listoftodos| takes no arguments.
+% For this document the list of to--do's looks like.
+% \listoftodos
+% \vspace{0.5cm}
+%
+% \DescribeMacro{\todototoc}
+% The |\todototoc| command adds an entry to the table of contents for
+% list of todos. The command should be placed right before the
+% |\listoftodos| command.
+%
+% \subsection{Package options}
+% \label{subsecPackageOptions}
+% \DescribeMacro{disable}
+% If the option |disable| is passed to the package, the macros
+% usually defined by the package (|\todo|, |\todoarea|, |\listoftodos| and
+% |\missingfigure|) are defined as macros with no effect, and thus
+% all inserted notes are removed.
+%
+% \DescribeMacro{obeyDraft, obeyFinal}
+% When the option |obeyDraft| is given, the package checks
+% if the one of the options |draft|, |draftcls| or |draftclsnofoot|
+% is given (this option is usually given to
+% the documentclass). If the |draft| option is given, the
+% functionality of the package is enabled and otherwise the effect
+% of the package is disabled.
+% The option |obeyFinal| does something similar, except that the
+% todonotes package is only disabled if the |final| option given.
+%
+% \DescribeMacro{danish, german, ngerman, french, swedish}
+% \DescribeMacro{spanish, catalan, italian}
+% \DescribeMacro{portuguese, dutch}
+% Use translations of the text strings
+% ''List of todos'' and ''Missing figure''.
+% The default is to use none of these options, which results in
+% english text strings.
+% Currently the following languages are supported:
+% catalan,
+% danish,
+% dutch,
+% french,
+% german,
+% ngerman,
+% italian,
+% portuguese,
+% spanish and
+% swedish.
+%
+% \DescribeMacro{colorinlistoftodos}
+% Adds a small colored square in front of all items in the Todo
+% list. The color of the square is the same as the fill color of the
+% inserted todonote.
+% This can be useful if there are different types of todos
+% (insert reference, explain in detail, \ldots) where the color of
+% the inserted todonote marks the type of todo.
+%
+% \DescribeMacro{color}
+% \DescribeMacro{backgroundcolor}
+% \DescribeMacro{linecolor}
+% \DescribeMacro{bordercolor}
+% These options sets the default colors for the todo command.
+% There is three colors that can be specified. The border color
+% (default |bordercolor=black|) around the inserted text, the color
+% behind the inserted text (default |backgroundcolor=orange|) and
+% the color of the line connecting the inserted textbox with the
+% current location in the text (default |linecolor=black!30|).
+% Setting the |color| option to |val| passes this value on to the
+% background and line color options.
+% The specified colors must be valid according to the
+% |xcolor| package.
+%
+% \DescribeMacro{textsize}
+% |textsize=value| sets the default text size of the inserted
+% todonotes to the given value.
+% Value is the ''name'' of the used font size, eg. if the desired
+% fontsize is |\tiny| use |textsize=tiny|. The default value is
+% |textsize=normalsize|.
+%
+% \DescribeMacro{prependcaption}
+% The |prependcaption| option triggers a special behaviour of the
+% |caption=val| option for the todo command, where the given value
+% |val| is inserted in the inserted todonote.
+%
+% \DescribeMacro{shadow}
+% If the |shadow| option is given, the inserted todonotes will be
+% displayed with a gray shadow.
+% I expect that the option will trigger problems with tikz versions
+% prior to 2.0.
+%
+% \DescribeMacro{figwidth}
+% \DescribeMacro{figheight}
+% The |figwidth=length| option and |figheight=length| option set the default
+% width and height of the figure
+% inserted by the |\missingfigure| command.
+% The default value is |\columnwidth| for the width and |4cm| for the height.
+%
+% \DescribeMacro{leaderwidth}
+% The |leaderwidth=length| option specifies the width of the leader lines.
+% The argument is passed to the |line width| option in TikZ.
+% The default value is |1.6pt|.
+%
+% \DescribeMacro{leadertype}
+% The |leadertype=type| option specifies the shape of the leaders, which are
+% drawn between the labels in the margin and the corresponding sites in text.
+% We use the characterization of the leader types known from boundary labeling:
+% $p$ denotes a segment parallel to the left/right side of the text area, while
+% $o$ denotes a orthogonal segment.
+% $s$ is a straight-line segment.
+% The following types are available (|opo| is the default value):
+% \begin{itemize}
+% \item |s|:
+% Straight-line connection between site and label.
+% \item |sBezier|:
+% Uses the straight-line leaders but transforms them into B\'ezier curves,
+% which are easier to follow for the reader.
+% The generated curves don't cross each other when the straight-line leaders
+% are crossing-free.
+% \item |opo|:
+% This is the style used in the original todonotes package.
+% The leaders start with a horizontal segment at the site in the text,
+% followed by a vertical segment in the margin beneath the text.
+% The last segment is a vertical segment, which connects to the label.
+% \item |os|:
+% This is the style used in common word processing applications like
+% LibreOffice.
+% The leader also starts with a horizontal segment that leads to the margin
+% and is connected to the label by a straight line.
+% \item |po|:
+% The leader starts with a vertical segment at the site in text and is then
+% connected to the label by a horizontal segment.
+% \end{itemize}
+%
+% \DescribeMacro{positioning}
+% The |positioning=algorithm| option specifies, which algorithm is used to
+% determine the positions of the notes on the page.
+% You should choose the algorithm depending on the leader type you want to use.
+% The default value for this option is |inputOrderStacks|.
+% The following algorithms are available:
+% \begin{itemize}
+% \item |inputOrder|:
+% Place the labels in the order given by the
+% y-coordinates of the corresponding sites in text.
+% Intended for use with $opo$- or $os$-leaders.
+% \item |inputOrderStacks|:
+% Like the algorithm before, but the labels are
+% clustered before they are placed.
+% Thus the labels are placed nearer to their sites.
+% Intended for use with $opo$- or $os$-leaders.
+% \item |sLeaderNorthEast|:
+% Places labels in a way that they can be connected to their sites by
+% straight-line leaders without crossings.
+% The leaders are attached to the upper right or upper left corner of the
+% label (depending on which site of the text the label is placed).
+% Intended for use with $s$-leaders or B\'ezier leaders.
+% \item |sLeaderNorthEastBelow|:
+% Like the algorithm before, but the leader is attached to a point that is a
+% constant offset below the corner of the label.
+% Intended for use with $s$-leaders or B\'ezier leaders.
+% \item |sLeaderNorthEastBelowStacks|:
+% Like the algorithm before, but the labels are cluster before they are
+% placed.
+% Thus the labels are placed nearer to their sites.
+% Intended for use with $s$-leaders or B\'ezier leaders.
+% \item |sLeaderEast|:
+% Like the algorithms before, but the leader is attached to the center of the
+% right or left boundary of the label.
+% Intended for use with $s$-leaders or B\'ezier leaders.
+% \item |poLeaders|:
+% Calculates label positions that lead to $po$-leaders with minimum total
+% length.
+% This algorithm depends heavily on the number of notes, so the runtime and
+% memory consumption can get very high.
+% \item |poLeadersAvoidLines|:
+% Like the algorithm before, but tries to avoid overlapping of horizontal
+% leader segments with text.
+% This algorithm depends heavily on advanced Lua\TeX\ features to manipulate
+% the data structures of the page, so it possibly could give conflicts with
+% other packages.
+% \end{itemize}
+%
+% \DescribeMacro{splitting}
+% The |splitting=algorithm| option can be used to place the labels on both sides
+% of the text.
+% The notes are only separated when there is enough space on both sides (see
+% |minNoteWidth|.
+% The default value for this option is |none|.
+% Available algorithms for this option are:
+% \begin{itemize}
+% \item |none|:
+% Labels are placed in the wider margin only.
+% \item |middle|:
+% The text area is split in the middle in a left and a right half.
+% Labels, whose sites are in the left half of the text, are placed in the
+% left margin, the others in the right margin.
+% \item |median|:
+% The notes are seperated at the median of the sites (sorted by
+% x-coordinate).
+% That is, the number of notes in the left and the right margin is equal
+% (except for one note).
+% \item |weightedMedian|:
+% Considers the height of the labels for the median.
+% So the total height of the labels in the left margin is approximately equal
+% to that in the right margin.
+% \end{itemize}
+%
+% \DescribeMacro{interNoteSpace}
+% The |interNoteSpace=length| option specifies the minimum vertical distance
+% between two notes.
+% The default value is |5pt|.
+%
+% \DescribeMacro{noteInnerSep}
+% The |noteInnerSep=length| option specifies the |inner sep| used for the TikZ
+% nodes, i.\,e., the distance between the border of the note and the text inside
+% it.
+% The default value is |5pt|.
+%
+% \DescribeMacro{routingAreaWidth}
+% The |routingAreaWidth=length| option specifies the width of the so called
+% routing area.
+% This is the area, in which the vertical segment of $opo$-leaders are placed.
+% The area is also used for $os$-leaders.
+% The default value is |0.4cm|.
+%
+% \DescribeMacro{minNoteWidth}
+% The |minNoteWidth=length| option specifies the minimum width of the labels.
+% When there is fewer space in one of the margins, this margin is not considered
+% for label placement.
+% If both margins are narrower, no labels are placed and an error message is
+% printed to the console output.
+% The default value of this option is |2.0cm|.
+%
+% \DescribeMacro{distanceNotesPageBorder}
+% The |distanceNotesPageBorder=length| option specifies the horizontal distance
+% from the labels to the borders of the paper.
+% You can adjust this setting to your printer margins.
+% The default value of this option is |0.5cm|.
+%
+% \DescribeMacro{distanceNotesText}
+% The |distanceNotesPageBorder=length| option specifies the horizontal distance
+% between the labels and the text area.
+% With $opo$- or $os$-leaders the routing area is inserted additionally so the
+% distance between labels and text area increases.
+% The default value of this option is |0.2cm|.
+%
+% \DescribeMacro{rasterHeight}
+% The |rasterHeight=length| option is used only for the $po$-leader algorithm.
+% For this algorithm the page is rasterized and the labels are placed only on
+% the positions given by this raster.
+% Decreasing this value can yield better results (i.\,e., smaller total leader
+% length), but strongly increases the runtime and memory consumption.
+% The default value of this option is |1cm|.
+%
+% \DescribeMacro{debug}
+% When the |debug| option is activated the package is more verbose on
+% the commandline.
+% Additionally, some markers, which can be used to understand the algorithms,
+% are drawn on the page (depending on the chosen algorithm).
+%
+%
+%
+%
+% \subsection{Options for the todo command}
+% \label{subsecTodoOptions}
+% There are several options that can be given to the |\todo|
+% command. All the options are described here and often I have
+% included examples of the change in visual appearance.
+% Default values for these options can be set using the presetkeys
+% command.
+% \begin{verbatim}
+% \presetkeys{todonotes}{fancyline, color=blue!30}{}
+% \end{verbatim}
+%
+% \DescribeMacro{disable}
+% The |disable| option can be given directly to the todo command.
+% If given the command has no effect.
+%
+% \DescribeMacro{color}
+% \DescribeMacro{backgroundcolor}
+% \DescribeMacro{linecolor}
+% \DescribeMacro{bordercolor}
+% These options set the color that is used in the current todo
+% command.
+% The color classes is the same as used in the color package
+% options, see section \ref{subsecPackageOptions}.
+% Default values can be set by the color
+% options when the todonotes package is loaded.
+% \todo[color=green!40]{And a green note}
+% The todo notes inserted in this paragraph is created with the
+% command
+% |\todo[color=green!40]{And a green note}|.
+% The color of the inserted note could be used to mark different
+% types of tasks (insert references, explain something in detail,
+% \ldots), this could be streamlined by defining new commands like
+% below.
+% \begin{verbatim}
+% \newcommand{\insertref}[1]{\todo[color=green!40]{#1}}
+% \newcommand{\explainindetail}[1]{\todo[color=red!40]{#1}}
+% \end{verbatim}
+% An example that uses all of the color options is given below
+% \todo[linecolor=green!70!white, backgroundcolor=blue!20!white,
+% bordercolor=red]{Anything but default colors}.
+% \begin{verbatim}
+% \todo[linecolor=green!70!white, backgroundcolor=blue!20!white,
+% bordercolor=red]{Anything but default colors}.
+% \end{verbatim}
+%
+%
+% \DescribeMacro{line / noline}
+% If you want to get rid of the line connecting the inserted note
+% with the place in the text where the note occurs in the latex
+% code, the option |noline| can be used.
+% \todo[noline]{A note with no line connecting it to the placement
+% in the original text.}
+% |\todo[noline]{A note with no line ...}|
+% \vspace{1.0cm}
+%
+%
+% \DescribeMacro{inline / noinline}
+% It is possible to place a todonote inside the text instead of
+% placing it in the margin, this could be desirable if the text in
+% the note has a considerable length.
+% |\todo[inline]{A todonote placed in the text}|
+% \todo[inline]{A todonote placed in the text}
+
+% \begin{wrapfigure}[1]{r}[20mm]{40mm}
+% \begin{tikzpicture}
+% \draw[red] (0, 0) circle(0.45);
+% \draw[green] (1, 0) circle(0.45);
+% \draw[blue] (2, 0) circle(0.45);
+% \end{tikzpicture}
+% \caption{A text explaining the image.
+% \todo[inline]{Fill those circles \ldots}}
+% \end{wrapfigure}
+% Another usage for the inline option is when you want to add a
+% todonote to a figure caption.
+%
+% \begin{verbatim}
+% \begin{wrapfigure}{r}[20mm]{40mm}
+% \begin{tikzpicture}
+% \draw[red] (0, 0) circle(0.45);
+% \draw[green] (1, 0) circle(0.45);
+% \draw[blue] (2, 0) circle(0.45);
+% \end{tikzpicture}
+% \caption{A text explaining the image.
+% \todo[inline]{Fill those circles \ldots}}
+% \end{wrapfigure}
+% \end{verbatim}
+%
+% \DescribeMacro{size}
+% |size=val| changes the size of the text inside the todonote.
+% The commands used to create the notes below are \\ \noindent
+% |\todo[size=\Large]{A note with a large font size.}|
+% and \\ \noindent
+% |\todo[inline, size=\tiny]{Note with very small font size.}|.
+% \todo[size=\Large]{A note with a large font size.}
+% \todo[inline, size=\footnotesize]{Note with very small font size.}
+%
+% \DescribeMacro{list / nolist}
+% When the option |nolist| is given, the todo item will not appear in
+% the list of todos.
+%
+% \DescribeMacro{caption}
+% The |caption| option enables the user to specify a short
+% description of the todonote that are inserted in the list of
+% todos instead of the full todonote text.
+% \todo[caption={Short note}]{A very long and tedious note that
+% cannot be on one line in the list of todos.}
+% \begin{verbatim}
+% \todo[caption={Short note}]{A very long and tedious note that
+% cannot be on one line in the list of todos.}.
+% \end{verbatim}
+% The effect of this option is altered with the package option
+% |prependcaption| or the |prepend| / |noprepend| option for the
+% todo command.
+%
+% \DescribeMacro{prepend / noprepend}
+% The options |prepend| and |noprepend| can be used for setting
+% whether a given caption should be prepended to the todonote or
+% not.
+% Globally this can be set using the |prependcaption| option for the
+% package.~\todo[prepend, caption={Short note with prepend}]{A very
+% long and tedious note that cannot be on one line in the list of
+% todos.} Below is the effect of the option shown using the code:
+% \todo[noprepend, caption={Short note with noprepend}]{A very long
+% and tedious note that cannot be on one line in the list of
+% todos.}
+% \begin{verbatim}
+% \todo[prepend, caption={Short note with prepend}]{A very long and tedious
+% note that cannot be on one line in the list of todos.}.
+% \todo[noprepend, caption={Short note with noprepend}]{A very long and
+% tedious note that cannot be on one line in the list of todos.}.
+% \end{verbatim}
+%
+% \DescribeMacro{author}
+% The |author| option takes a parameter, the name of the author.
+% The given name is inserted in the todonote.
+% \todo[author=Xavier]{Testing author option.}
+% \todo[author=Xavier, inline]{Testing author option.}
+% \begin{verbatim}
+% \todo[author=Xavier]{Testing author option.}
+% \todo[author=Xavier, inline]{Testing author option.}
+% \end{verbatim}
+%
+% \subsection{Options for the missingfigure command}
+%
+% \DescribeMacro{figwidth}
+% The |figwidth=length| option sets the width of the figure inserted by the
+% |\missingfigure| command.
+% Length values below $6cm$ might trigger some problems with the
+% visual appearance.
+% Try to compare the default of the missing figure command, when the
+% option is given or not.
+% \begin{verbatim}
+% \missingfigure[figwidth=6cm]{Testing a long text string}
+% \end{verbatim}
+% \missingfigure[figwidth=6cm]{Testing a long text string}
+% \begin{verbatim}
+% \missingfigure{Testing a long text string}
+% \end{verbatim}
+% \missingfigure{Testing a long text string}
+% \begin{wrapfigure}{r}[2cm]{6cm}
+% \missingfigure[figwidth=6cm]{Add a test image \ldots}
+% \end{wrapfigure}
+% Another usage of the option is when |\missingfigure| is used in
+% the wrapfigure environment.
+% \begin{verbatim}
+% \begin{wrapfigure}{r}[2cm]{6cm}
+% \missingfigure[figwidth=6cm]{Add a test image \ldots}
+% \end{wrapfigure}
+% \end{verbatim}
+%
+% \DescribeMacro{figheight}
+% The |figheight=length| option changes the height of the inserted
+% missing figure.
+% The default height is 4cm and using values lower than this might
+% cause the warning sign to pop out of the gray area.
+% \begin{verbatim}
+% \missingfigure[figheight=6cm]{Testing a long text string}
+% \end{verbatim}
+% \missingfigure[figheight=6cm]{Testing}
+%
+%
+% \subsection{Options for the listoftodos command}
+% The |\listoftodos| command takes one optional argument, that
+% defines the name of the inserted list of todos.
+% \begin{verbatim}
+% \listoftodos[I can be called anything]
+% \end{verbatim}
+%
+%
+%
+% \subsection{Known issues}
+% \subsubsection{Package loading order}
+% The luatodonotes package requires the following packages:
+% \begin{multicols}{2}
+% \begin{itemize}
+% \item ifthen
+% \item xkeyval
+% \item xcolor
+% \item tikz
+% \item graphicx (is loaded via the tikz package)
+% \item luacode
+% \item luatex
+% \item atbegshi
+% \item xstring
+% \item zref-abspage
+% \item ifoddpage
+% \item soul
+% \item soulpos
+% \end{itemize}
+% \end{multicols}
+% \noindent
+% When luatodonotes are loaded in the preamble, the package checks
+% if these packages all are loaded. If that is not the case it loads
+% the missing packages with no options given.
+% If you want to give some specific options to some of these
+% packages, you have to load them \emph{before} the luatodonotes
+% package, otherwise you will get an ''Option clash'' error when
+% latex works on the document.
+%
+% If both the menukeys and the xcolor (with the option \verb!table!)
+% package should be loaded, the following order must be used.
+% \begin{verbatim}
+%\usepackage[table]{xcolor}
+%\usepackage{todonotes}
+%\usepackage{menukeys}
+% \end{verbatim}
+%
+% \subsubsection{Spacing around inserted notes}
+% Inserted todo commands will eat the white space after the command.
+% \begin{verbatim}
+%Testing\todo{Does this eat the space?} testing
+% \end{verbatim}
+%
+% Testing\todo{Does this eat the space?}testing
+%
+%
+% \subsubsection{Conflicts with the amsart documentclass}
+% The |amsart| document class redefines some internal commands that
+% is used by the todonotes package, this will cause an malfunctioning
+% |\listoftodos| command.
+% The following code to circumvent the problem was given by Dan
+% Luecking on comp.text.tex
+% \begin{verbatim}
+% \makeatletter
+% \providecommand\@dotsep{5}
+% \makeatother
+% \listoftodos\relax
+% \end{verbatim}
+%
+% NOT TESTED
+% NOT TESTED
+% NOT TESTED
+%
+% Dominique suggests the following workaround.
+% \begin{verbatim}
+% \makeatletter
+% \providecommand\@dotsep{5}
+% \def\listtodoname{List of Todos}
+% \def\listoftodos{\@starttoc{tdo}\listtodoname}
+% \makeatother
+% \end{verbatim}
+%
+%
+%
+% \subsubsection{Unknown option ''remember picture''}
+% If latex throws the error
+% \begin{verbatim}
+% Package tikz Error: I do not know what to do with the option ``remember picture''.
+% \end{verbatim}
+% It probably means that your latex installation is outdated, as
+% only newer versions of latex driver for tikz supports the
+% |remember picture| option.
+% For additional info consult
+% ''Section 9.2.2 Producing PDF Output'' in the tikz manual.
+% \url{http://www.ctan.org/tex-archive/graphics/pgf/base/doc/generic/pgf/pgfmanual.pdf}
+%
+%
+%
+% \subsubsection{List of todo heading is not correctly formatted}
+% If using natbib, the todonotes list title gets screwed up unless
+% you do something like this:
+% \begin{verbatim}
+% \makeatletter\let\chapter\@undefined\makeatother
+% \end{verbatim}
+% Suggestion by Richard Stanton.
+%
+%
+%
+% \subsubsection{Some commands not working inside notes}
+% Some commands will not work like expected, when used inside of a note.
+% They will cause errors when processing the document or have simply no effect.
+% This is caused by the mechanism used to layout the notes:
+% The content is written into a hbox when a |\todo| is encountered.
+% The contents of this box are then stored until the note is typeset.
+% By that time the contents are taken out of the hbox (by |\unhbox|) and put
+% into a |\parbox| with the width required for the note.
+% I don't have a solution for this problem yet.
+%
+%
+%
+%
+%
+% \iffalse
+% \StopEventually{\PrintChanges\PrintIndex}
+% \fi
+% \StopEventually{\PrintChanges}
+%
+% \newpage
+% \section{Implementation}
+%
+% Identifies the package and loads the packages dependences.
+% \begin{macrocode}
+\ProvidesPackage{luatodonotes}[2014/06/08]
+\RequirePackage{ifthen}
+\RequirePackage{xkeyval}
+\RequirePackage{xcolor}
+\RequirePackage{tikz}
+\usetikzlibrary{positioning}
+\usetikzlibrary{intersections}
+\usetikzlibrary{decorations.pathmorphing}
+\RequirePackage{luacode}
+\RequirePackage{luatex}
+\RequirePackage{atbegshi}
+\RequirePackage{xstring}
+\RequirePackage{zref-abspage}
+\RequirePackage{ifoddpage}
+\RequirePackage{soul}
+\RequirePackage{soulpos}
+% \end{macrocode}
+% Some default values are set
+% \begin{macrocode}
+\newcommand{\@todonotes@text}{}%
+\newcommand{\@todonotes@backgroundcolor}{orange}
+\newcommand{\@todonotes@linecolor}{black!30}
+\newcommand{\@todonotes@bordercolor}{black}
+\newcommand{\@todonotes@leaderwidth}{1.6pt}
+\newcommand{\@todonotes@textsize}{\normalsize}
+\newcommand{\@todonotes@figwidth}{\columnwidth}
+\newcommand{\@todonotes@figheight}{4cm}
+% \end{macrocode}
+% Default values for variables added by luatodonotes
+% \begin{macrocode}
+\newcommand{\@todonotes@positioning}{inputOrderStacks}
+\newcommand{\@todonotes@splitting}{none}
+\newcommand{\@todonotes@leadertype}{opo}
+\newcommand{\@todonotes@interNoteSpace}{5pt}
+\newcommand{\@todonotes@noteInnerSep}{5pt}
+\newcommand{\@todonotes@routingAreaWidth}{0.4cm}
+\newcommand{\@todonotes@minNoteWidth}{2.0cm}
+\newcommand{\@todonotes@distanceNotesPageBorder}{0.5cm}
+\newcommand{\@todonotes@distanceNotesText}{0.2cm}
+\newcommand{\@todonotes@rasterHeight}{1cm}
+% \end{macrocode}
+% \begin{macrocode}
+\AtBeginDocument{
+\ifx\undefined\phantomsection
+\newcommand{\phantomsection}{}
+\fi
+}
+% \end{macrocode}
+%
+% \subsection{Declaration of options for the package}
+% In this part the various options for
+% the package are defined.
+%
+% Define the default text strings and set localization options for
+% the danish and german languages.
+% \begin{macrocode}
+\newcommand{\@todonotes@todolistname}{Todo list}
+\newcommand{\@todonotes@MissingFigureText}{Figure}
+\newcommand{\@todonotes@MissingFigureUp}{Missing}
+\newcommand{\@todonotes@MissingFigureDown}{figure}
+\newcommand{\@todonotes@SetTodoListName}[1]
+ {\renewcommand{\@todonotes@todolistname}{#1}}
+\newcommand{\@todonotes@SetMissingFigureText}[1]
+ {\renewcommand{\@todonotes@MissingFigureText}{#1}}
+\newcommand{\@todonotes@SetMissingFigureUp}[1]
+ {\renewcommand{\@todonotes@MissingFigureUp}{#1}}
+\newcommand{\@todonotes@SetMissingFigureDown}[1]
+ {\renewcommand{\@todonotes@MissingFigureDown}{#1}}
+\newif{\if@todonotes@reverseMissingFigureTriangle}
+\DeclareOptionX{catalan}{
+ \@todonotes@SetTodoListName{Llista de feines pendents}%
+ \@todonotes@SetMissingFigureText{Figura}%
+ \@todonotes@SetMissingFigureUp{Figura}%
+ \@todonotes@SetMissingFigureDown{pendent}%
+}
+\DeclareOptionX{danish}{%
+ \@todonotes@SetTodoListName{G\o{}rem\aa{}lsliste}%
+ \@todonotes@SetMissingFigureText{Figur}%
+ \@todonotes@SetMissingFigureUp{Manglende}%
+ \@todonotes@SetMissingFigureDown{figur}%
+}
+\DeclareOptionX{dutch}{%
+ \@todonotes@SetTodoListName{Lijst van onafgewerkte taken}%
+ \@todonotes@SetMissingFigureText{Figuur}%
+ \@todonotes@SetMissingFigureUp{Ontbrekende}%
+ \@todonotes@SetMissingFigureDown{figuur}%
+}
+\DeclareOptionX{english}{%
+ \@todonotes@SetTodoListName{Todo list}%
+ \@todonotes@SetMissingFigureText{Figure}%
+ \@todonotes@SetMissingFigureUp{Missing}%
+ \@todonotes@SetMissingFigureDown{figure}%
+}
+\DeclareOptionX{french}{%
+ \@todonotes@SetTodoListName{Liste des points \`a traiter}%
+ \@todonotes@SetMissingFigureText{Figure}%
+ \@todonotes@SetMissingFigureUp{Figure}%
+ \@todonotes@SetMissingFigureDown{manquante}%
+ \@todonotes@reverseMissingFigureTrianglefalse
+}
+\DeclareOptionX{german}{%
+ \@todonotes@SetTodoListName{Liste der noch zu erledigenden Punkte}%
+ \@todonotes@SetMissingFigureText{Abbildung}%
+ \@todonotes@SetMissingFigureUp{Fehlende}%
+ \@todonotes@SetMissingFigureDown{Abbildung}%
+}
+\DeclareOptionX{italian}{
+ \@todonotes@SetTodoListName{Elenco delle cose da fare}%
+ \@todonotes@SetMissingFigureText{Figura}%
+ \@todonotes@SetMissingFigureUp{Figura}%
+ \@todonotes@SetMissingFigureDown{mancante}%
+}
+\DeclareOptionX{ngerman}{%
+ \@todonotes@SetTodoListName{Liste der noch zu erledigenden Punkte}%
+ \@todonotes@SetMissingFigureText{Abbildung}%
+ \@todonotes@SetMissingFigureUp{Fehlende}%
+ \@todonotes@SetMissingFigureDown{Abbildung}%
+}
+\DeclareOptionX{portuguese}{
+ \@todonotes@SetTodoListName{Lista de tarefas pendentes}%
+ \@todonotes@SetMissingFigureText{Figura}%
+ \@todonotes@SetMissingFigureUp{Figura}%
+ \@todonotes@SetMissingFigureDown{pendente}%
+}
+\DeclareOptionX{spanish}{
+ \@todonotes@SetTodoListName{Lista de tareas pendientes}%
+ \@todonotes@SetMissingFigureText{Figura}%
+ \@todonotes@SetMissingFigureUp{Figura}%
+ \@todonotes@SetMissingFigureDown{pendiente}%
+}
+\DeclareOptionX{swedish}{%
+ \@todonotes@SetTodoListName{Att g\"{o}ra-lista}%
+ \@todonotes@SetMissingFigureText{Figur}%
+ \@todonotes@SetMissingFigureUp{Figur}%
+ \@todonotes@SetMissingFigureDown{saknas}%
+}
+% \end{macrocode}
+% Create a counter, for storing the number of inserted todos.
+% \begin{macrocode}
+\newcounter{@todonotes@numberoftodonotes}
+% \end{macrocode}
+% Create a counter, for storing the number of lines in the current todoarea.
+% \begin{macrocode}
+\newcounter{@todonotes@numberofLinesInArea}
+% \end{macrocode}
+% Toggle whether the package should obey the global draft option.
+% \begin{macrocode}
+\newif{\if@todonotes@obeyDraft}
+\DeclareOptionX{obeyDraft}{\@todonotes@obeyDrafttrue}
+\newif{\if@todonotes@isDraft}
+\DeclareOptionX{draft}{\@todonotes@isDrafttrue}
+\DeclareOptionX{draftcls}{\@todonotes@isDrafttrue}
+\DeclareOptionX{draftclsnofoot}{\@todonotes@isDrafttrue}
+\newif{\if@todonotes@obeyFinal}
+\DeclareOptionX{obeyFinal}{\@todonotes@obeyFinaltrue}
+\newif{\if@todonotes@isFinal}
+\DeclareOptionX{final}{\@todonotes@isFinaltrue}
+% \end{macrocode}
+% Make it possible to disable the functionality
+% of the package. If this option is given, the
+% commands |\todo{}| and |\listoftodos| are defined
+% as commands with no effect. (But you can still
+% compile you document with these commands).
+% \begin{macrocode}
+\newif{\if@todonotes@disabled}
+\DeclareOptionX{disable}{\@todonotes@disabledtrue}
+% \end{macrocode}
+% Show small boxes in the list of todos with the color of the
+% inserted todonotes.
+% \begin{macrocode}
+\newif{\if@todonotes@colorinlistoftodos}
+\DeclareOptionX{colorinlistoftodos}{\@todonotes@colorinlistoftodostrue}
+% \end{macrocode}
+% We only define dvistyle for compatibility with todonotes.
+% The option was intented for use with \texttt{tex}, there should be no problems
+% using \texttt{luatex}.
+% So we ignore this option and issue a warning.
+% \begin{macrocode}
+\DeclareOptionX{dvistyle}{\PackageWarningNoLine{luatodonotes}
+ {Parameter dvistyle is not supported by luatodonotes.
+ Ignoring this option}}
+% \end{macrocode}
+% Create a color option.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {color}{
+ \renewcommand{\@todonotes@backgroundcolor}{#1}
+ \renewcommand{\@todonotes@linecolor}{#1}}
+% \end{macrocode}
+% Make the background color of the notes as
+% an option.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {backgroundcolor}{\renewcommand{\@todonotes@backgroundcolor}{#1}}
+% \end{macrocode}
+% Make the line color of the notes as
+% an option.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {linecolor}{\renewcommand{\@todonotes@linecolor}{#1}}
+% \end{macrocode}
+% Make the color of the notes box color as
+% an option.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {bordercolor}{\renewcommand{\@todonotes@bordercolor}{#1}}
+% \end{macrocode}
+% Make the width of the leader line as
+% an option.
+% It is later set as \texttt{line width} in TikZ.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {leaderwidth}{\renewcommand{\@todonotes@leaderwidth}{#1}}
+% \end{macrocode}
+% Set whether short captions given as arguments to the todo command
+% should be included in the inserted todonote.
+% \begin{macrocode}
+\newif{\if@todonotes@prependcaptionglobal}
+\@todonotes@prependcaptionglobalfalse
+\DeclareOptionX{prependcaption}{\@todonotes@prependcaptionglobaltrue}
+% \end{macrocode}
+% This option is only there for compatibility with todonotes.
+% We ignore it and issue a warning because the width of our labels is
+% determined dynamically based on the page layout.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {textwidth}{\PackageWarningNoLine{luatodonotes}
+ {Parameter textwidth is not supported by luatodonotes}}
+% \end{macrocode}
+% Make the text size as an option. It requires some magic with the
+% |\csname| and |\endcsname| macros, as commands cannot be taken as
+% options for a package.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {textsize}{\renewcommand{\@todonotes@textsize}{\csname #1\endcsname}}
+% \end{macrocode}
+% Add option for shadows behind the inserted notes
+% \begin{macrocode}
+\newif{\if@todonotes@shadowenabled}
+\@todonotes@shadowenabledfalse
+\DeclareOptionX{shadow}{\@todonotes@shadowenabledtrue
+\usetikzlibrary{shadows}}
+% \end{macrocode}
+% Add option for the default width of the figure inserted with
+% |\missingfigure|.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {figwidth}{\renewcommand{\@todonotes@figwidth}{#1}}
+\define@key{luatodonotes.sty}%
+ {figheight}{\renewcommand{\@todonotes@figheight}{#1}}
+% \end{macrocode}
+% Specify the name of the algorithm used to specify the position of the labels.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {positioning}{\renewcommand{\@todonotes@positioning}{#1}}
+% \end{macrocode}
+% Specify the name of the algorithm used to split the notes for left and right side.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {splitting}{\renewcommand{\@todonotes@splitting}{#1}}
+% \end{macrocode}
+% Specify the type of leaders that are drawn.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {leadertype}{\renewcommand{\@todonotes@leadertype}{#1}}
+% \end{macrocode}
+% Specify the vertical distance between the notes.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {interNoteSpace}{\renewcommand{\@todonotes@interNoteSpace}{#1}}
+% \end{macrocode}
+% Specify the distance from the text inside the notes to the border.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {noteInnerSep}{\renewcommand{\@todonotes@noteInnerSep}{#1}}
+% \end{macrocode}
+% Specify the width of the routing area used for $opo$- and $os$-leaders.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {routingAreaWidth}{\renewcommand{\@todonotes@routingAreaWidth}{#1}}
+% \end{macrocode}
+% Minimum width of notes in one margin beside the text to be considered for
+% label placement.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {minNoteWidth}{\renewcommand{\@todonotes@minNoteWidth}{#1}}
+% \end{macrocode}
+% Specify horizontal distance from the notes to the borders of the page.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {distanceNotesPageBorder}%
+ {\renewcommand{\@todonotes@distanceNotesPageBorder}{#1}}
+% \end{macrocode}
+% Specify the horizontal distance between the notes and the text area.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {distanceNotesText}{\renewcommand{\@todonotes@distanceNotesText}{#1}}
+% \end{macrocode}
+% Specify the height of the raster used for the $po$-leader algorithm.
+% \begin{macrocode}
+\define@key{luatodonotes.sty}%
+ {rasterHeight}{\renewcommand{\@todonotes@rasterHeight}{#1}}
+% \end{macrocode}
+% This option is used to activate debug mode.
+% Luatex prints more verbose output to the commandline in this mode.
+% Furthermore, some of the algorithms also print debugging hints onto the
+% output page.
+% \begin{macrocode}
+\newif{\if@todonotes@debugenabled}
+\@todonotes@debugenabledfalse
+\DeclareOptionX{debug}{\@todonotes@debugenabledtrue}
+% \end{macrocode}
+% Finally process the given options.
+% \begin{macrocode}
+\ProcessOptionsX*
+% \end{macrocode}
+% If the |obeyDraft| is given, check whether one of the |draft|,
+% |draftcls| or |draftclsnofoot|
+% options are given and enable or disable the functionality of this
+% package.
+% If the |obeyFinal| option is given together with the |final| option
+% the todonotes are disabled.
+% The |disable| option will overrule the effect of |obeyDraft|.
+% \begin{macrocode}
+\if@todonotes@disabled
+\else
+ \if@todonotes@obeyDraft
+ \@todonotes@disabledtrue
+ \if@todonotes@isDraft
+ \@todonotes@disabledfalse
+ \fi
+ \fi
+ \if@todonotes@obeyFinal
+ \@todonotes@disabledfalse
+ \if@todonotes@isFinal
+ \@todonotes@disabledtrue
+ \fi
+ \fi
+\fi
+% \end{macrocode}
+% \subsection{Initialisation of our Lua code}
+% In this part we define some of the variables used by Lua depending on the
+% package options and do some other initialisation tasks.
+%
+% We first need some temporary dimensions, which are written by \TeX\ and read
+% from Lua.
+% We use dimensions here because it is easier to access \TeX\ dimensions from
+% Lua than \LaTeX\ lengths.
+% We use |tex.dimen| in Lua to access dimensions.
+% The first dimensions are used when extracting the absolute coordinates of a
+% position on the page.
+% \begin{macrocode}
+\newdimen\@todonotes@extractx
+\newdimen\@todonotes@extracty
+% \end{macrocode}
+% The following savebox and dimensions are used to calculate the height of a
+% certain label.
+% The box and dimensions are filled by \TeX\ and then read from Lua.
+% \begin{macrocode}
+\newsavebox\@todonotes@heightcalcbox
+\newdimen\@todonotes@heightcalcboxdepth
+\newdimen\@todonotes@heightcalcboxheight
+% \end{macrocode}
+% The following savebox is used to store the contents of a note and is then
+% read from Lua.
+% \begin{macrocode}
+\newsavebox\@todonotes@notetextbox
+% \end{macrocode}
+% The following dimensions are used to read |\baselineskip| and
+% |\f@size| from Lua.
+% Dimension |\@todonotes@currentsidemargin| is set to the left margin,
+% i.\,e., to the value of length |\oddsidemargin| or
+% |\evensidemargin| depending on the type page.
+% \begin{macrocode}
+\newdimen\@todonotes@baselineskip
+\newdimen\@todonotes@fontsize
+\newdimen\@todonotes@currentsidemargin
+% \end{macrocode}
+% Loading our main Lua file.
+% \begin{macrocode}
+\directlua{require("luatodonotes")}
+% \end{macrocode}
+% Setting variables to values given by package options.
+% \begin{macrocode}
+\directlua{noteInnerSep =
+ string.todimen("\luatexluaescapestring{\@todonotes@noteInnerSep}")}
+\directlua{noteInterSpace =
+ string.todimen("\luatexluaescapestring{\@todonotes@interNoteSpace}")}
+\directlua{routingAreaWidth =
+ string.todimen("\luatexluaescapestring{\@todonotes@routingAreaWidth}")}
+\directlua{minNoteWidth =
+ string.todimen("\luatexluaescapestring{\@todonotes@minNoteWidth}")}
+\directlua{distanceNotesPageBorder =
+ string.todimen("\luatexluaescapestring{\@todonotes@distanceNotesPageBorder}")}
+\directlua{distanceNotesText =
+ string.todimen("\luatexluaescapestring{\@todonotes@distanceNotesText}")}
+\directlua{rasterHeight =
+ string.todimen("\luatexluaescapestring{\@todonotes@rasterHeight}")}
+% \end{macrocode}
+% Set the variable for the positioning algorithm depending on the corresponding
+% package option.
+% \begin{macrocode}
+\IfStrEqCase{\@todonotes@positioning}{%
+ {inText}{\directlua{positioning = positioningAlgos["inText"]}}%
+ {inputOrder}{\directlua{positioning = positioningAlgos["inputOrder"]}}%
+ {inputOrderStacks}{\directlua{positioning =
+ positioningAlgos["inputOrderStacks"]}}%
+ {sLeaderNorthEast}{\directlua{positioning =
+ positioningAlgos["sLeaderNorthEast"]}}%
+ {sLeaderNorthEastBelow}{\directlua{positioning =
+ positioningAlgos["sLeaderNorthEastBelow"]}}%
+ {sLeaderEast}{\directlua{positioning =
+ positioningAlgos["sLeaderEast"]}}%
+ {poLeaders}{\directlua{positioning = positioningAlgos["poLeaders"]}}%
+ {poLeadersAvoidLines}{\directlua{positioning =
+ positioningAlgos["poLeadersAvoidLines"]}}%
+ {sLeaderNorthEastBelowStacks}{\directlua{positioning =
+ positioningAlgos["sLeaderNorthEastBelowStacks"]}}}%
+ [\directlua{positioning = positioningAlgos["inputOrderStacks"]}
+ \PackageWarningNoLine{luatodonotes}
+ {Invalid value for parameter positioning: \@todonotes@positioning}]
+% \end{macrocode}
+% Set the variable for the splitting algorithm depending on the corresponding
+% package option.
+% \begin{macrocode}
+\IfStrEqCase{\@todonotes@splitting}{%
+ {none}{\directlua{splitting = splittingAlgos["none"]}}%
+ {middle}{\directlua{splitting = splittingAlgos["middle"]}}%
+ {median}{\directlua{splitting = splittingAlgos["median"]}}%
+ {weightedMedian}{\directlua{splitting = splittingAlgos["weightedMedian"]}}}%
+ [\directlua{splitting = splittingAlgos["none"]}
+ \PackageWarningNoLine{luatodonotes}
+ {Invalid value for parameter split: \@todonotes@splitting}]
+% \end{macrocode}
+% Specify the requested leader type.
+% \begin{macrocode}
+\IfStrEqCase{\@todonotes@leadertype}{%
+ {s}{\directlua{leaderType = leaderTypes["s"]}}%
+ {opo}{\directlua{leaderType = leaderTypes["opo"]}}%
+ {po}{\directlua{leaderType = leaderTypes["po"]}}%
+ {sBezier}{\directlua{leaderType = leaderTypes["sBezier"]}}%
+ {os}{\directlua{leaderType = leaderTypes["os"]}}}%
+ [\directlua{leaderType = leaderTypes["opo"]}
+ \PackageWarningNoLine{luatodonotes}
+ {Invalid value for parameter leadertype: \@todonotes@leadertype}]
+% \end{macrocode}
+% The following commands are used to detect the absolute positions of lines on
+% the page.
+%
+% We first need to define a command to be able to insert the position from
+% |\pdflastypos| into a write-whatsit in Lua.
+% We need this workaround because we cannot insert |\pdflastypos| directly into
+% the tokenlist in the Lua callback \texttt{callbackOutputLinePositions()}.
+% \begin{macrocode}
+\def\@todonotes@pdflastypos{\the\pdflastypos}
+% \end{macrocode}
+% The following commands are written to the temporary \texttt{lpo}-file.
+% When reading this file we call a Lua function for each line in the file and
+% thus can collect the line positions in a Lua table.
+% \begin{macrocode}
+\newcommand{\@todonotes@lineposition}[3]{%
+ \directlua{linePositionsAddLine(#1,#2,#3)}%
+}
+\newcommand{\@todonotes@nextpage}{%
+ \directlua{linePositionsNextPage()}%
+}%
+% \end{macrocode}
+% The following macro is used in \texttt{AtBeginShipout} to signal in the
+% \texttt{lpo}-file that a new page is started.
+% \begin{macrocode}
+\newcommand{\@todonotes@writeNextpageToLpo}{%
+ \ifdefined\tf@lpo%
+ \immediate\write\tf@lpo{\@backslashchar @todonotes@nextpage}%
+ \fi
+}
+% \end{macrocode}
+% Depending on the debug-option of the package we set the corresponding Lua
+% variable here.
+% Additionally, we prepare to print our notes and leaders in foreground when in
+% debug mode.
+% \begin{macrocode}
+\if@todonotes@debugenabled
+ \directlua{todonotesDebug = true}
+ \newcommand{\@todonotes@AtBeginShipoutUpperLeft}
+ {\AtBeginShipoutUpperLeftForeground}
+\else
+ \directlua{todonotesDebug = false}
+ \newcommand{\@todonotes@AtBeginShipoutUpperLeft}
+ {\AtBeginShipoutUpperLeft}
+\fi
+% \end{macrocode}
+% Initialise the script when all Lua variables are set according to the package
+% options.
+% \begin{macrocode}
+\directlua{initTodonotes()}
+% \end{macrocode}
+% Some definitions to highlight areas in text.
+% The first command is needed to accept control spaces (|\ |) in arguments for
+% soul commands.
+% After that we define the highlighting command used for todoareas.
+% \begin{macrocode}
+\soulregister{\ }{0}
+\newlength{\todonotes@textmark@width}
+\newlength{\todonotes@textmark@fontsize}
+\newlength{\todonotes@textmark@linebelow}
+\newlength{\todonotes@textmark@lineabove}
+\ulposdef{\todonotes@textmark@highlight}{%
+ \setlength\todonotes@textmark@width\ulwidth%
+ \setlength\todonotes@textmark@fontsize{\f@size pt}%
+ \stepcounter{@todonotes@numberofLinesInArea}%
+ \ifulstarttype{0}%
+ {% begin of area
+ \def\todonotes@textmark@decoLeft{}%
+ \def\todonotes@textmark@shift{-2pt}%
+ \addtolength\todonotes@textmark@width{2pt}%
+ \setcounter{@todonotes@numberofLinesInArea}{1}}%
+ {\def\todonotes@textmark@decoLeft{@todonotes@todoarea}%
+ \def\todonotes@textmark@shift{-4pt}%
+ \addtolength\todonotes@textmark@width{4pt}}%
+ \ifulendtype{0}%
+ {% last line of area
+ \def\todonotes@textmark@decoRight{}%
+ \addtolength\todonotes@textmark@width{2pt}%
+ \directlua{processLastLineInTodoArea()}}%
+ {\def\todonotes@textmark@decoRight{@todonotes@todoarea}%
+ \addtolength\todonotes@textmark@width{4pt}}%
+ \newcommand{\@todonotes@nodeNamePrefix}%
+ {@todonotes@\arabic{@todonotes@numberoftodonotes}%
+ @\arabic{@todonotes@numberofLinesInArea} }%
+ \hspace*{\todonotes@textmark@shift}{\smash{%
+ \begin{tikzpicture}[overlay,remember picture,
+ deco/.style={}]%
+ \setlength\todonotes@textmark@linebelow%
+ {-0.95\dimexpr\baselineskip-\f@size pt\relax}%
+ \setlength\todonotes@textmark@lineabove%
+ {\dimexpr\f@size pt+\todonotes@textmark@linebelow\relax}%
+ \coordinate
+ (\@todonotes@nodeNamePrefix areaSW)
+ at (0,\todonotes@textmark@linebelow);
+ \coordinate
+ (\@todonotes@nodeNamePrefix areaSE)
+ at (\todonotes@textmark@width, \todonotes@textmark@linebelow);
+ \coordinate
+ (\@todonotes@nodeNamePrefix areaNE)
+ at (\todonotes@textmark@width,\todonotes@textmark@lineabove);
+ \coordinate
+ (\@todonotes@nodeNamePrefix areaNW)
+ at (0,\todonotes@textmark@lineabove);
+ \draw[draw=green!70,fill=green,fill opacity=.2]
+ (\@todonotes@nodeNamePrefix areaSW)
+ decorate[\todonotes@textmark@decoLeft] {
+ -- (\@todonotes@nodeNamePrefix areaNW)
+ }
+ -- (\@todonotes@nodeNamePrefix areaNE)
+ decorate[\todonotes@textmark@decoRight] {
+ -- (\@todonotes@nodeNamePrefix areaSE)
+ }
+ -- cycle;
+ \end{tikzpicture}%
+ }}%
+}%
+% \end{macrocode}
+% \subsection{Options for the todo command}
+%
+% In this part the various options for
+% commands in the package are defined.
+% Set an arbitrarily fill color
+% \begin{macrocode}
+\newcommand{\@todonotes@currentlinecolor}{}%
+\newcommand{\@todonotes@currentbackgroundcolor}{}%
+\newcommand{\@todonotes@currentbordercolor}{}%
+\define@key{todonotes}{color}{%
+ \renewcommand{\@todonotes@currentlinecolor}{#1}%
+ \renewcommand{\@todonotes@currentbackgroundcolor}{#1}}%
+\define@key{todonotes}{linecolor}{%
+ \renewcommand{\@todonotes@currentlinecolor}{#1}}%
+\define@key{todonotes}{backgroundcolor}{%
+ \renewcommand{\@todonotes@currentbackgroundcolor}{#1}}%
+\define@key{todonotes}{bordercolor}{%
+ \renewcommand{\@todonotes@currentbordercolor}{#1}}%
+\newcommand{\@todonotes@currentleaderwidth}{}%
+\define@key{todonotes}{leaderwidth}{%
+ \renewcommand{\@todonotes@currentleaderwidth}{#1}}%
+% \end{macrocode}
+% Set a relative font size
+% \begin{macrocode}
+\newcommand{\@todonotes@sizecommand}{}%
+\define@key{todonotes}{size}{\renewcommand{\@todonotes@sizecommand}{#1}}%
+% \end{macrocode}
+% Should the todo item be disabled?
+% \begin{macrocode}
+\newif\if@todonotes@localdisable%
+\define@key{todonotes}{disable}[]{\@todonotes@localdisabletrue}%
+\define@key{todonotes}{nodisable}[]{\@todonotes@localdisablefalse}%
+% \end{macrocode}
+% Should the todo item be included in the list of todos?
+% \begin{macrocode}
+\newif\if@todonotes@appendtolistoftodos%
+\define@key{todonotes}{list}[]{\@todonotes@appendtolistoftodostrue}%
+\define@key{todonotes}{nolist}[]{\@todonotes@appendtolistoftodosfalse}%
+% \end{macrocode}
+% Should the todo item be displayed inline?
+% \begin{macrocode}
+\newif\if@todonotes@inlinenote%
+\define@key{todonotes}{inline}[]{\@todonotes@inlinenotetrue}%
+\define@key{todonotes}{noinline}[]{\@todonotes@inlinenotefalse}%
+% \end{macrocode}
+% \begin{macrocode}
+\newif\if@todonotes@prependcaption%
+\define@key{todonotes}{prepend}[]{\@todonotes@prependcaptiontrue}%
+\define@key{todonotes}{noprepend}[]{\@todonotes@prependcaptionfalse}%
+% \end{macrocode}
+% Should the note in the margin be connected to the insertion point
+% in the text?
+% \begin{macrocode}
+\newif\if@todonotes@line%
+\define@key{todonotes}{line}[]{\@todonotes@linetrue}%
+\define@key{todonotes}{noline}[]{\@todonotes@linefalse}%
+% \end{macrocode}
+% Only here for compatibility with todonotes.
+% We don't need the fancy lines because we have more advanced drawing styles.
+% So we ignore this option and issue a warning.
+% \begin{macrocode}
+\define@key{todonotes}{fancyline}[]{\PackageWarningNoLine{luatodonotes}
+ {Parameter fancyline is not supported by luatodonotes}}%
+\define@key{todonotes}{nofancyline}[]{}%
+% \end{macrocode}
+% Author option.
+% \begin{macrocode}
+\newcommand{\@todonotes@author}{}%
+\newif\if@todonotes@authorgiven%
+\define@key{todonotes}{author}{%
+ \renewcommand{\@todonotes@author}{#1}%
+ \@todonotes@authorgiventrue}%
+\define@key{todonotes}{noauthor}[]{\@todonotes@authorgivenfalse}%
+% \end{macrocode}
+% Should the text in the list of todos be different from the text
+% in the todonote?
+% \begin{macrocode}
+\newcommand{\@todonotes@caption}{}%
+\newif\if@todonotes@captiongiven%
+\define@key{todonotes}{caption}%
+ {\renewcommand{\@todonotes@caption}{#1}%
+ \@todonotes@captiongiventrue}%
+\define@key{todonotes}{nocaption}[]{\@todonotes@captiongivenfalse}%
+% \end{macrocode}
+% Change the current figure width and height.
+% \begin{macrocode}
+\newcommand{\@todonotes@currentfigwidth}{\@todonotes@figwidth}
+\define@key{todonotes}%
+ {figwidth}{\renewcommand{\@todonotes@currentfigwidth}{#1}}
+\newcommand{\@todonotes@currentfigheight}{\@todonotes@figheight}
+\define@key{todonotes}%
+ {figheight}{\renewcommand{\@todonotes@currentfigheight}{#1}}
+% \end{macrocode}
+% Preset values of the options
+% \begin{macrocode}
+\presetkeys%
+ {todonotes}%
+ {linecolor=\@todonotes@linecolor,%
+ backgroundcolor=\@todonotes@backgroundcolor,%
+ bordercolor=\@todonotes@bordercolor,%
+ leaderwidth=\@todonotes@leaderwidth,%
+ nodisable,%
+ noinline,%
+ nocaption,%
+ noauthor,%
+ figwidth=\@todonotes@figwidth,%
+ figheight=\@todonotes@figheight,%
+ line, list, size=\@todonotes@textsize}{}%
+% \end{macrocode}
+% \subsection{The main code part}
+% Here are the actual macros defined.
+% The following boolean is used to remember if |\todo| or |\todoarea|
+% was called.
+% \begin{macrocode}
+\newif\if@todonotes@areaselected%
+% \end{macrocode}
+% The following token registers are used to access the data for a note (which
+% is stored in macros) from Lua.
+% \begin{macrocode}
+\newtoks\@todonotes@toks@currentlinecolor%
+\newtoks\@todonotes@toks@currentbackgroundcolor%
+\newtoks\@todonotes@toks@currentbordercolor%
+\newtoks\@todonotes@toks@currentleaderwidth%
+\newtoks\@todonotes@toks@sizecommand%
+% \end{macrocode}
+% If the option "disable" was passed to the package
+% define empty commands.
+% \begin{macrocode}
+\if@todonotes@disabled%
+ \newcommand{\listoftodos}[1][]{}
+ \newcommand{\@todo}[2][]{}
+ \newcommand{\@todoarea}[3][]{}
+ \newcommand{\missingfigure}[2][]{}
+\else % \if@todonotes@disabled
+% \end{macrocode}
+% Define the |\listoftodos| command and define the
+% appearance of the list of todos.
+% \begin{macrocode}
+\newcommand{\listoftodos}[1][\@todonotes@todolistname]
+ {\@ifundefined{chapter}{\section*{#1}}{\chapter*{#1}} \@starttoc{tdo}}
+\newcommand{\l@todo}
+ {\@dottedtocline{1}{0em}{2.3em}}
+% \end{macrocode}
+% Define styles used by the todo command.
+% Colors are set directly when placing the notes.
+% \begin{macrocode}
+\tikzset{@todonotes@todoarea/.style={
+ decoration={snake,amplitude=3.5pt,segment length=5pt}}}
+\tikzset{@todonotes@notestyleraw/.style={
+ line width=0.5pt,
+ inner sep = \@todonotes@noteInnerSep,
+ rounded corners=4pt}}
+% \end{macrocode}
+% Add shadows and rounded corners to the inserted todonotes.
+% \begin{macrocode}
+\if@todonotes@shadowenabled
+ \tikzset{@todonotes@notestyle/.style={@todonotes@notestyleraw,
+ general shadow={shadow xshift=.5ex, shadow yshift=-.5ex,
+ opacity=1,fill=black!50}}}
+\else
+ \tikzset{@todonotes@notestyle/.style={@todonotes@notestyleraw}}
+\fi
+\tikzset{@todonotes@leader/.style={}}
+\tikzset{@todonotes@textmark/.style={rounded corners}}
+\tikzset{@todonotes@inlinenote/.style={
+ @todonotes@notestyle,
+ draw=\@todonotes@currentbordercolor,
+ fill=\@todonotes@currentbackgroundcolor,
+ text width=\linewidth - 1.6 ex - 1 pt}}
+% \end{macrocode}
+% \begin{macro}{\@todocommon}
+% Common macro used from |\@todo| and |\@todoarea|.
+% Used to actually draw/save the note.
+% \begin{macrocode}
+\newcommand{\@todocommon}[2]{%
+% \end{macrocode}
+% Use the global value for determining the default prepend behavior.
+% \begin{macrocode}
+\if@todonotes@prependcaptionglobal%
+\@todonotes@prependcaptiontrue%
+\else%
+\@todonotes@prependcaptionfalse%
+\fi%
+% \end{macrocode}
+% Store the original text for later usage and parse the given options.
+% \begin{macrocode}
+\renewcommand{\@todonotes@text}{#2}%
+\renewcommand{\@todonotes@caption}{#2}%
+\setkeys{todonotes}{#1}%
+% \end{macrocode}
+% If the option |disable| is given to the command, no output is generated.
+% \begin{macrocode}
+\if@todonotes@localdisable%
+\else%
+% \end{macrocode}
+% Add the item to the list of todos. When the option
+% |colorinlistoftodos| is given to the package a small colored
+% square is added in front of the text.
+% \begin{macrocode}
+\addtocounter{@todonotes@numberoftodonotes}{1}%
+\if@todonotes@appendtolistoftodos%
+ \phantomsection%
+ \if@todonotes@captiongiven%
+ \else%
+ \renewcommand{\@todonotes@caption}{#2}%
+ \fi%
+ \@todonotes@addElementToListOfTodos%
+\fi%
+% \end{macrocode}
+% Prepend the short caption given if it is requested
+% \begin{macrocode}
+\if@todonotes@captiongiven%
+ \if@todonotes@prependcaption%
+ \renewcommand{\@todonotes@text}{\@todonotes@caption: #2}%
+ \fi%
+\fi%
+% \end{macrocode}
+% Place the todonote as indicated by the options (inline or in a
+% marginpar), below is the code for the inline placement.
+% \begin{macrocode}
+\if@todonotes@inlinenote%
+ \@todonotes@drawInlineNote%
+\else%
+ \@todonotes@drawMarginNoteWithLine%
+\fi%\if@todonotes@inlinenote
+\fi%\if@todonotes@localdisable
+}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\@todo}
+% Command that draws normal notes.
+% \begin{macrocode}
+\newcommand{\@todo}[2][]{%
+ \@todonotes@areaselectedfalse%
+ \@todocommon{#1}{#2}%
+}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\@todoarea}
+% Command that draws notes that highlight a certain area in text.
+% \begin{macrocode}
+\newcommand{\@todoarea}[3][]{%
+ \@todonotes@areaselectedtrue%
+ \@todocommon{#1}{#2}%
+ \todonotes@textmark@highlight{#3}%
+% \end{macrocode}
+% Mark the end of the highlighted area with a Tikz coordinate.
+% The begin is marked by |\@todocommon|.
+% \begin{macrocode}
+ \begin{tikzpicture}[remember picture, overlay]%
+ \node [coordinate] (@todonotes@\arabic{@todonotes@numberoftodonotes} %
+ inTextEnd) {};%
+ \end{tikzpicture}%
+ \zref@label{@todonotes@\arabic{@todonotes@numberoftodonotes}@end}%
+}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{drawMarginNoteWithLine}
+% Define helper function |drawMarginNoteWithLine|.
+% \begin{macrocode}
+\newcommand{\@todonotes@drawMarginNoteWithLine}{%
+% \end{macrocode}
+% When the todonote should be placed inside a marginpar, the code
+% below is applied.
+% First is the current location in the document stored, this enables
+% us later to connect this point with the inserted todonote.
+% \begin{macrocode}
+ \begin{tikzpicture}[remember picture, overlay]%
+ \node [coordinate] (@todonotes@\arabic{@todonotes@numberoftodonotes} %
+ inText) {};%
+ \end{tikzpicture}%
+% \end{macrocode}
+% Update the dimensions to be accessed by Lua.
+% \begin{macrocode}
+ \@todonotes@baselineskip=\baselineskip%
+ \@todonotes@fontsize=\f@size pt%
+% \end{macrocode}
+% Place a label at the site.
+% We use this to query the page number, on which the note was placed.
+% \begin{macrocode}
+ \zref@label{@todonotes@\arabic{@todonotes@numberoftodonotes}}%
+% \end{macrocode}
+% Append author before the note text if one is given.
+% \begin{macrocode}
+ \if@todonotes@authorgiven%
+ \let\@todonotes@text@old=\@todonotes@text
+ \renewcommand{\@todonotes@text}{\@todonotes@author: \@todonotes@text@old}%
+ \fi%
+% \end{macrocode}
+% We use edef here to get these macros fully expanded.
+% After that we write them to a toks register and read them from Lua.
+% \begin{macrocode}
+ \edef\@todonotes@tmp{\@todonotes@currentlinecolor}%
+ \@todonotes@toks@currentlinecolor=\expandafter{\@todonotes@tmp}%
+ \edef\@todonotes@tmp{\@todonotes@currentbackgroundcolor}%
+ \@todonotes@toks@currentbackgroundcolor=\expandafter{\@todonotes@tmp}%
+ \edef\@todonotes@tmp{\@todonotes@currentbordercolor}%
+ \@todonotes@toks@currentbordercolor=\expandafter{\@todonotes@tmp}%
+ \edef\@todonotes@tmp{\@todonotes@currentleaderwidth}%
+ \@todonotes@toks@currentleaderwidth=\expandafter{\@todonotes@tmp}%
+% \end{macrocode}
+% We cannot fully expand the size command (using |\edef| causes errors when
+% compiling).
+% \begin{macrocode}
+ \@todonotes@toks@sizecommand=\expandafter{\@todonotes@sizecommand}%
+% \end{macrocode}
+% We store the text that should be shown in this note into a box and copy this
+% box to a variable in Lua.
+% \begin{macrocode}
+ \savebox\@todonotes@notetextbox{\@todonotes@sizecommand\@todonotes@text}%
+% \end{macrocode}
+% Prepare parameters and add the note to the list in Lua.
+% \begin{macrocode}
+ \if@todonotes@line%
+ \def\@todonotes@param@drawLeader{true}%
+ \else%
+ \def\@todonotes@param@drawLeader{false}%
+ \fi%
+ \if@todonotes@areaselected%
+ \def\@todonotes@param@noteType{area}%
+ \else%
+ \def\@todonotes@param@noteType{}%
+ \fi%
+ \directlua{addNoteToList(\arabic{@todonotes@numberoftodonotes},%
+ \@todonotes@param@drawLeader,\luastringO{\@todonotes@param@noteType})}%
+}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{addElementToListOfTodos}
+% Define helper function |addElementToListOfTodos|.
+% \begin{macrocode}
+\newcommand{\@todonotes@addElementToListOfTodos}{%
+ \if@todonotes@colorinlistoftodos%
+ \addcontentsline{tdo}{todo}{%
+ \fcolorbox{\@todonotes@currentbordercolor}%
+ {\@todonotes@currentbackgroundcolor}%
+ {\textcolor{\@todonotes@currentbackgroundcolor}{o}}%
+ \ \@todonotes@caption}%
+ \else%
+ \addcontentsline{tdo}{todo}{\@todonotes@caption}%
+ \fi}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{drawInlineNote}
+% Define helper function |drawInlineNote|.
+% \begin{macrocode}
+\newcommand{\@todonotes@drawInlineNote}{%
+ {\par\noindent\begin{tikzpicture}[remember picture]%
+ \draw node[@todonotes@inlinenote,font=\@todonotes@sizecommand]{%
+ \if@todonotes@authorgiven%
+ {\noindent \@todonotes@sizecommand %
+ \@todonotes@author:\,\@todonotes@text}%
+ \else%
+ {\noindent \@todonotes@sizecommand \@todonotes@text}%
+ \fi};%
+ \end{tikzpicture}\par}%
+ }%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\missingfigure}
+% Defines the |\missingfigure| macro.
+% \begin{macrocode}
+\newcommand{\missingfigure}[2][]{%
+\setkeys{todonotes}{#1}%
+\addcontentsline{tdo}{todo}{\@todonotes@MissingFigureText: #2}%
+\par
+\noindent
+\begin{tikzpicture}
+\draw[fill=black!40, draw = white, line width=0pt]
+ (-2, -2.5) rectangle +(\@todonotes@currentfigwidth, \@todonotes@currentfigheight);
+\draw (2, -0.3) node[right, text
+ width=\@todonotes@currentfigwidth-4.5cm] {#2};
+\draw[red, fill=white, rounded corners = 5pt, line width=10pt]
+ (30:2cm) -- (150:2cm) -- (270:2cm) -- cycle;
+\draw (0, 0.3) node {\@todonotes@MissingFigureUp};
+\draw (0, -0.3) node {\@todonotes@MissingFigureDown};
+\end{tikzpicture}
+}% Ending \missingfigure command
+\fi % Ending \@todonotes@ifdisabled
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\todototoc}
+% Inserts a reference to the list of todos in the table of contents. If |chapter| is defined,
+% |chapter| is used as level otherwise will |section| be used.
+% The |\todototoc| command respects the disable option.
+% \begin{macrocode}
+\newcommand{\todototoc}
+{
+ \if@todonotes@disabled
+ \else
+ \addcontentsline{toc}{\@ifundefined{chapter}{section}{chapter}}{\@todonotes@todolistname}
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\todo}
+% Define the |\todo| command as a redirection to |\@todo|.
+% \begin{macrocode}
+\newcommand{\todo}[2][]{\@bsphack\@todo[#1]{#2}\@esphack\ignorespaces}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\todoarea}
+% Define the |\todoarea| command as a redirection to |\@todoarea|.
+% We don't want to ignore spaces after this command.
+% \begin{macrocode}
+\newcommand{\todoarea}[3][]{\@bsphack\@todoarea[#1]{#2}{#3}\@esphack}%
+% \end{macrocode}
+% \end{macro}
+% The following commands are executed when a page is complete and is written to
+% the output PDF (shipout in \TeX\ terms).
+% The |\AtBeginShipout| command is provided by package atbegshi.
+% \begin{macrocode}
+\if@todonotes@disabled
+\else
+\AtBeginShipout{%
+% \end{macrocode}
+% We draw to the foreground or background of the page (depending if debug option
+% is set for the package).
+% \begin{macrocode}
+ \@todonotes@AtBeginShipoutUpperLeft{
+ \@todonotes@writeNextpageToLpo
+% \end{macrocode}
+% Determine if we are on a left or on a right side (important for margins) and
+% set variables accordingly.
+% |\relax| seems to be needed at end to really write new value for
+% currentsidemargin.
+% \begin{macrocode}
+ \checkoddpage%
+ \ifoddpageoroneside%
+ \@todonotes@currentsidemargin=\the\oddsidemargin%
+ \directlua{currentPageOdd = true}%
+ \else%
+ \@todonotes@currentsidemargin=\the\evensidemargin%
+ \directlua{currentPageOdd = false}%
+ \fi\relax%
+% \end{macrocode}
+% We switch to the default catcodes of \LaTeX\ here.
+% This is important if catcodes are changed in the main text, e.\,g., by a
+% verbatim environment at the end of the page.
+% \begin{macrocode}
+ \BeginCatcodeRegime\CatcodeTableLaTeX
+% \end{macrocode}
+% Calculates the areas, in which the labels can be placed.
+% This calculation depends on currentsidemargin.
+% So this has to be done inside |\AtBeginShipoutUpperLeft| (otherwise odd/even
+% page detection won't work).
+% \begin{macrocode}
+ \directlua{calcLabelAreaDimensions()}%
+% \end{macrocode}
+% Calculates the needed height for every note.
+% This has to be outside of the tikzpicture because it uses a savebox to compute
+% the height.
+% This box does not work in the tikzpicture.
+% \begin{macrocode}
+ \directlua{calcHeightsForNotes()}% has to be outside of tikzpicture
+ \begin{tikzpicture}[remember picture,overlay]
+% \end{macrocode}
+% Reads the absolute coordinates of every note on the page and writes them to
+% the Lua objects.
+% \begin{macrocode}
+ \directlua{getInputCoordinatesForNotes()}
+% \end{macrocode}
+% Runs the positioning algorithm and actually draws the notes and leaders.
+% \begin{macrocode}
+ \directlua{printNotes()}
+ \end{tikzpicture}%
+% \end{macrocode}
+% Delete the drawn notes from the Lua lists and prepare for the next page.
+% \begin{macrocode}
+ \directlua{clearNotes()}%
+ \EndCatcodeRegime
+ }%
+}
+\fi % Ending \@todonotes@ifdisabled
+% \end{macrocode}
+%
+% \newpage
+% \Finale
+\endinput
+
diff --git a/Master/texmf-dist/source/lualatex/luatodonotes/luatodonotes.ins b/Master/texmf-dist/source/lualatex/luatodonotes/luatodonotes.ins
new file mode 100644
index 00000000000..779664abf56
--- /dev/null
+++ b/Master/texmf-dist/source/lualatex/luatodonotes/luatodonotes.ins
@@ -0,0 +1,65 @@
+%%
+%% Copyright (C) 2014 by Fabian Lipp <fabian.lipp@gmx.de>
+%% based on the todonotes package by
+%% Henrik Skov Midtiby <henrikmidtiby@gmail.com>
+%%
+%% This file may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License, either version 1.2 of this license
+%% or (at your option) any later version. The latest version of this
+%% license is in:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.2 or later is part of all distributions of LaTeX version
+%% 1999/12/01 or later.
+%%
+
+\input docstrip.tex
+\keepsilent
+
+\usedir{tex/latex/luatodonotes}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2014 by Fabian Lipp <fabian.lipp@gmx.de>
+based on the todonotes package by
+ Henrik Skov Midtiby <henrikmidtiby@gmail.com>
+
+This file may be distributed and/or modified under the conditions of
+the LaTeX Project Public License, either version 1.2 of this license
+or (at your option) any later version. The latest version of this
+license is in:
+
+ http://www.latex-project.org/lppl.txt
+
+and version 1.2 or later is part of all distributions of LaTeX version
+1999/12/01 or later.
+
+\endpreamble
+
+\generate{\file{luatodonotes.sty}{\from{luatodonotes.dtx}{package}}}
+
+\obeyspaces
+\Msg{*************************************************************}
+\Msg{* *}
+\Msg{* To finish the installation you have to move the following *}
+\Msg{* files into a directory searched by TeX: *}
+\Msg{* *}
+\Msg{* luatodonotes.sty *}
+\Msg{* luatodonotes.lua *}
+\Msg{* path_line.lua *}
+\Msg{* path_point.lua *}
+\Msg{* inspect.lua *}
+\Msg{* *}
+\Msg{* To produce the documentation run the file todonotes.dtx *}
+\Msg{* through LaTeX. *}
+\Msg{* *}
+\Msg{* Happy TeXing! *}
+\Msg{* *}
+\Msg{*************************************************************}
+
+
+\endbatchfile
+
diff --git a/Master/texmf-dist/tex/lualatex/luatodonotes/inspect.lua b/Master/texmf-dist/tex/lualatex/luatodonotes/inspect.lua
new file mode 100644
index 00000000000..638a673a708
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luatodonotes/inspect.lua
@@ -0,0 +1,297 @@
+local inspect ={
+ _VERSION = 'inspect.lua 2.0.0',
+ _URL = 'http://github.com/kikito/inspect.lua',
+ _DESCRIPTION = 'human-readable representations of tables',
+ _LICENSE = [[
+ MIT LICENSE
+
+ Copyright (c) 2013 Enrique GarcĂ­a Cota
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ]]
+}
+
+-- Apostrophizes the string if it has quotes, but not aphostrophes
+-- Otherwise, it returns a regular quoted string
+local function smartQuote(str)
+ if str:match('"') and not str:match("'") then
+ return "'" .. str .. "'"
+ end
+ return '"' .. str:gsub('"', '\\"') .. '"'
+end
+
+local controlCharsTranslation = {
+ ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
+ ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
+}
+
+local function escapeChar(c) return controlCharsTranslation[c] end
+
+local function escape(str)
+ local result = str:gsub("\\", "\\\\"):gsub("(%c)", escapeChar)
+ return result
+end
+
+local function isIdentifier(str)
+ return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
+end
+
+local function isArrayKey(k, length)
+ return type(k) == 'number' and 1 <= k and k <= length
+end
+
+local function isDictionaryKey(k, length)
+ return not isArrayKey(k, length)
+end
+
+local defaultTypeOrders = {
+ ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
+ ['function'] = 5, ['userdata'] = 6, ['thread'] = 7
+}
+
+local function sortKeys(a, b)
+ local ta, tb = type(a), type(b)
+
+ -- strings and numbers are sorted numerically/alphabetically
+ if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
+
+ local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
+ -- Two default types are compared according to the defaultTypeOrders table
+ if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
+ elseif dta then return true -- default types before custom ones
+ elseif dtb then return false -- custom types after default ones
+ end
+
+ -- custom types are sorted out alphabetically
+ return ta < tb
+end
+
+local function getDictionaryKeys(t)
+ local keys, length = {}, #t
+ for k,_ in pairs(t) do
+ if isDictionaryKey(k, length) then table.insert(keys, k) end
+ end
+ table.sort(keys, sortKeys)
+ return keys
+end
+
+local function getToStringResultSafely(t, mt)
+ local __tostring = type(mt) == 'table' and rawget(mt, '__tostring')
+ local str, ok
+ if type(__tostring) == 'function' then
+ ok, str = pcall(__tostring, t)
+ str = ok and str or 'error: ' .. tostring(str)
+ end
+ if type(str) == 'string' and #str > 0 then return str end
+end
+
+local maxIdsMetaTable = {
+ __index = function(self, typeName)
+ rawset(self, typeName, 0)
+ return 0
+ end
+}
+
+local idsMetaTable = {
+ __index = function (self, typeName)
+ local col = setmetatable({}, {__mode = "kv"})
+ rawset(self, typeName, col)
+ return col
+ end
+}
+
+local function countTableAppearances(t, tableAppearances)
+ tableAppearances = tableAppearances or setmetatable({}, {__mode = "k"})
+
+ if type(t) == 'table' then
+ if not tableAppearances[t] then
+ tableAppearances[t] = 1
+ for k,v in pairs(t) do
+ countTableAppearances(k, tableAppearances)
+ countTableAppearances(v, tableAppearances)
+ end
+ countTableAppearances(getmetatable(t), tableAppearances)
+ else
+ tableAppearances[t] = tableAppearances[t] + 1
+ end
+ end
+
+ return tableAppearances
+end
+
+local function parse_filter(filter)
+ if type(filter) == 'function' then return filter end
+ -- not a function, so it must be a table or table-like
+ filter = type(filter) == 'table' and filter or {filter}
+ local dictionary = {}
+ for _,v in pairs(filter) do dictionary[v] = true end
+ return function(x) return dictionary[x] end
+end
+
+local function makePath(path, key)
+ local newPath, len = {}, #path
+ for i=1, len do newPath[i] = path[i] end
+ newPath[len+1] = key
+ return newPath
+end
+
+-------------------------------------------------------------------
+function inspect.inspect(rootObject, options)
+ options = options or {}
+ local depth = options.depth or math.huge
+ local filter = parse_filter(options.filter or {})
+
+ local tableAppearances = countTableAppearances(rootObject)
+
+ local buffer = {}
+ local maxIds = setmetatable({}, maxIdsMetaTable)
+ local ids = setmetatable({}, idsMetaTable)
+ local level = 0
+ local blen = 0 -- buffer length
+
+ local function puts(...)
+ local args = {...}
+ for i=1, #args do
+ blen = blen + 1
+ buffer[blen] = tostring(args[i])
+ end
+ end
+
+ local function down(f)
+ level = level + 1
+ f()
+ level = level - 1
+ end
+
+ local function tabify()
+ puts("\n", string.rep(" ", level))
+ end
+
+ local function commaControl(needsComma)
+ if needsComma then puts(',') end
+ return true
+ end
+
+ local function alreadyVisited(v)
+ return ids[type(v)][v] ~= nil
+ end
+
+ local function getId(v)
+ local tv = type(v)
+ local id = ids[tv][v]
+ if not id then
+ id = maxIds[tv] + 1
+ maxIds[tv] = id
+ ids[tv][v] = id
+ end
+ return id
+ end
+
+ local putValue -- forward declaration that needs to go before putTable & putKey
+
+ local function putKey(k)
+ if isIdentifier(k) then return puts(k) end
+ puts( "[" )
+ putValue(k, {})
+ puts("]")
+ end
+
+ local function putTable(t, path)
+ if alreadyVisited(t) then
+ puts('<table ', getId(t), '>')
+ elseif level >= depth then
+ puts('{...}')
+ else
+ if tableAppearances[t] > 1 then puts('<', getId(t), '>') end
+
+ local dictKeys = getDictionaryKeys(t)
+ local length = #t
+ local mt = getmetatable(t)
+ local to_string_result = getToStringResultSafely(t, mt)
+
+ puts('{')
+ down(function()
+ if to_string_result then
+ puts(' -- ', escape(to_string_result))
+ if length >= 1 then tabify() end -- tabify the array values
+ end
+
+ local needsComma = false
+ for i=1, length do
+ needsComma = commaControl(needsComma)
+ puts(' ')
+ putValue(t[i], makePath(path, i))
+ end
+
+ for _,k in ipairs(dictKeys) do
+ needsComma = commaControl(needsComma)
+ tabify()
+ putKey(k)
+ puts(' = ')
+ putValue(t[k], makePath(path, k))
+ end
+
+ if mt then
+ needsComma = commaControl(needsComma)
+ tabify()
+ puts('<metatable> = ')
+ putValue(mt, makePath(path, '<metatable>'))
+ end
+ end)
+
+ if #dictKeys > 0 or mt then -- dictionary table. Justify closing }
+ tabify()
+ elseif length > 0 then -- array tables have one extra space before closing }
+ puts(' ')
+ end
+
+ puts('}')
+ end
+
+ end
+
+ -- putvalue is forward-declared before putTable & putKey
+ putValue = function(v, path)
+ if filter(v, path) then
+ puts('<filtered>')
+ else
+ local tv = type(v)
+
+ if tv == 'string' then
+ puts(smartQuote(escape(v)))
+ elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
+ puts(tostring(v))
+ elseif tv == 'table' then
+ putTable(v, path)
+ else
+ puts('<',tv,' ',getId(v),'>')
+ end
+ end
+ end
+
+ putValue(rootObject, {})
+
+ return table.concat(buffer)
+end
+
+setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
+
+return inspect
+
diff --git a/Master/texmf-dist/tex/lualatex/luatodonotes/luatodonotes.lua b/Master/texmf-dist/tex/lualatex/luatodonotes/luatodonotes.lua
new file mode 100644
index 00000000000..61175bbbf68
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luatodonotes/luatodonotes.lua
@@ -0,0 +1,2160 @@
+--
+-- Copyright (C) 2014 by Fabian Lipp <fabian.lipp@gmx.de>
+-- -------------------------------------------------------
+--
+-- This file may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.2
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in:
+--
+-- http://www.latex-project.org/lppl.txt
+--
+-- and version 1.2 or later is part of all distributions of LaTeX
+-- version 1999/12/01 or later.
+--
+
+require("lualibs")
+--require("debugger")()
+local inspect = require('inspect')
+
+local point = require'path_point'
+local pathLine = require'path_line'
+--local bezier3 = require'path_bezier3'
+
+-- TODO Funktionen/Variablen in Namespace o. Ă„. packen
+
+-- strings used to switch to standard catcodes for LaTeX packages
+local catcodeStart = "\\makeatletter"
+local catcodeEnd = "\\makeatother"
+
+local currentPage = 1
+
+local const1In = string.todimen("1in") -- needed for calculations of page borders
+ -- (used as a constant in TeX)
+-- constants set in sty-file
+-- + noteInnerSep (inner sep used for tikz nodes)
+-- + noteInterSpace (vertical space between notes)
+-- + routingAreaWidth (width of the track routing area for opo-leaders)
+-- + minNoteWidth (width that must be available for labels to consider the left or
+-- right border of the page for placing labels)
+-- + distanceNotesPageBorder (distance from the page borders to the outmost point
+-- of the labels)
+-- + distanceNotesText (horizontal distance between the labels and the text area)
+-- + todonotesDebug (activate debug outputs when true)
+
+
+-- stores information about available algorithms
+positioningAlgos = {}
+splittingAlgos = {}
+leaderTypes = {}
+
+-- stores the notes for the current page
+notesForPage = {}
+-- Fields for each note:
+-- index: numbers notes in whole document
+-- indexOnPage: index of the note in the notesForPage array
+-- textbox: links to a hbox that contains the text, which is displayed inside
+-- the note
+-- origInputX, origInputY: position in which the todo-command was issued
+-- inputX, inputY: position to which the leader should be attached (can have a
+-- certain offset to origInputX/Y)
+-- heightLeft, heightRight: height of the contained text when placed on
+-- left/right side
+-- pageNr: absolute number of page on which site for note is placed
+-- rightSide: true means the note should be placed on the right side;
+-- otherwise left side is meant
+-- fontsize: fontsize used for paragraph in that the note was defined
+-- baselineskip: \baselineskip in the paragraph in that the note was defined
+-- outputX, outputY: position on which the north west anchor of the note should
+-- be placed
+-- lineColor: color of line connecting note to text
+-- backgroundColor: color of background of note
+-- borderColor: color of border of note
+-- leaderWidth: width of leader (used as argument for tikz line width)
+--
+-- Additional fields for text area:
+-- noteType: constant string "area"
+-- origInputEndX, origInputEndY: position at which the todo area ends
+-- pageNrEnd: absolute number of page on which todo area ends
+-- lineCountInArea: highest line index in area
+-- linesInArea: positions of lines in area (to detect page/column break)
+
+-- stores the areas for the labels on the current page
+-- (calculated in function calcLabelAreaDimensions())
+local labelArea = {}
+
+-- stores the positions of the text lines on every page
+local linePositions = {}
+-- Fields for every position:
+-- 1: position of baseline
+-- 2: upper bound of line (baseline + height)
+-- 3: lower bound of line (baseline - depth)
+
+-- variables used for construction of linePositions list when reading the file
+local linePositionsCurPage = {}
+local linePositionsPageNr = 0
+
+
+-- *** metatable for note objects ***
+local noteMt = {}
+-- getHeight(): yield heightLeft or heightRight depending on rightSide (implemented by metatable)
+function noteMt:getHeight()
+ if self.rightSide then
+ return self.heightRight
+ else
+ return self.heightLeft
+ end
+end
+function noteMt:getLabelAnchorY()
+ local leaderAnchor = positioning.leaderAnchor
+ local y
+ if leaderAnchor == "north east" then
+ y = self.outputY
+ elseif leaderAnchor == "east" then
+ y = self.outputY - noteInnerSep - self:getHeight() / 2
+ else
+ error("Invalid anchor for algorithm")
+ end
+
+ if positioning.leaderShift then
+ y = y + self.leaderShiftY
+ end
+
+ return y
+end
+function noteMt:getInTextAnchorTikz()
+ return "(" .. self.inputX .. "sp," .. self.inputY .. "sp)"
+end
+function noteMt:getLabelAnchorTikz()
+ local leaderAnchor = positioning.leaderAnchor
+ if leaderAnchor == "north east" and self.rightSide then
+ leaderAnchor = "north west"
+ elseif leaderAnchor == "east" and self.rightSide then
+ leaderAnchor = "west"
+ end
+
+ local shiftStr = ""
+ if positioning.leaderShift then
+ shiftStr = "[shift={(" .. self.leaderShiftX .. "sp," .. self.leaderShiftY .. "sp)}]"
+ end
+
+ return "(" .. shiftStr .. " @todonotes@" .. self.index .. " note." .. leaderAnchor .. ")"
+end
+function noteMt:boxForNoteText(rightSide)
+ local area = labelArea:getArea(rightSide)
+ local noteWidth
+ if area == nil then
+ noteWidth = minNoteWidth
+ else
+ noteWidth = area.noteWidth - 2*noteInnerSep
+ end
+ local retval = "\\directlua{tex.box[\"@todonotes@notetextbox\"] = " ..
+ "node.copy_list(notesForPage[" .. self.indexOnPage .. "].textbox)}"
+ retval = retval .. "\\parbox{" .. noteWidth .. "sp}" ..
+ "{\\raggedright\\unhbox\\@todonotes@notetextbox}"
+ return retval
+end
+
+function noteMt:getClipPathForTodoArea()
+ -- detext which lines are in same column/page as start of area
+ local lineCount = self.lineCountInArea
+ local maxLine = 1
+ local lines = self.linesInArea
+ local lastY = lines[1]
+ while maxLine < lineCount do
+ if lines[maxLine + 1] < lastY then
+ maxLine = maxLine + 1
+ lastY = lines[maxLine]
+ end
+ end
+
+ local function nodename(i, corner)
+ return "(@todonotes@" .. self.index .. "@" .. i .. " area" .. corner .. ")"
+ end
+ local path = nodename(1, "NW")
+ local pathLeft = ""
+ if maxLine == 1 then
+ -- only one line
+ path = path .. " -- " .. nodename(1, "NE") ..
+ " -- " .. nodename(1, "SE")
+ else
+ path = path .. " -- " .. nodename(1, "NE") ..
+ " decorate[@todonotes@todoarea] { -- " .. nodename(1, "SE") .. "}"
+ end
+ for i = 2, maxLine do
+ if i == lineCount then
+ -- area does not use the whole line
+ path = path .. " -| " .. nodename(i, "NE") ..
+ " -- " .. nodename(i, "SE")
+ else
+ -- area uses whole line
+ path = path .. " -- " .. nodename(i, "NE") ..
+ " decorate[@todonotes@todoarea] { -- " .. nodename(i, "SE") .. "}"
+ end
+ pathLeft = " -- " .. nodename(i, "SW") ..
+ " decorate[@todonotes@todoarea] { -- " .. nodename(i, "NW") .. "}" ..
+ pathLeft
+ end
+ path = path .. pathLeft .. " -| " .. nodename(1, "SW") .. "-- cycle"
+ return path
+end
+
+
+
+-- *** label areas ***
+-- stores areas for placing labels on current page
+function labelArea:getArea(rightSide)
+ if rightSide then
+ return self.right
+ else
+ return self.left
+ end
+end
+-- yields the x-coordinate of the boundary of the label that is pointing
+-- towards the text
+function labelArea:getXTextSide(rightSide)
+ if rightSide then
+ return self.right.left
+ else
+ return self.left.right
+ end
+end
+function labelArea:isOneSided()
+ if self.right == nil or self.left == nil then
+ return true
+ else
+ return false
+ end
+end
+
+
+
+-- is called by the sty-file when all settings (algorithms etc.) are made
+function initTodonotes()
+ if positioning.needLinePositions then
+ luatexbase.add_to_callback("post_linebreak_filter", callbackOutputLinePositions, "outputLinePositions")
+ tex.print("\\@starttoc{lpo}")
+ tex.print("\\directlua{lpoFileStream = \\the\\tf@lpo}")
+ end
+end
+
+
+-- valid values for noteType: nil/"" (for point in text), "area"
+function addNoteToList(index, drawLeader, noteType)
+ local newNote = {}
+ newNote.index = index
+ newNote.textbox = node.copy_list(tex.box["@todonotes@notetextbox"])
+ newNote.baselineskip = tex.dimen["@todonotes@baselineskip"]
+ newNote.fontsize = tex.dimen["@todonotes@fontsize"]
+ newNote.lineColor = tex.toks["@todonotes@toks@currentlinecolor"]
+ newNote.backgroundColor = tex.toks["@todonotes@toks@currentbackgroundcolor"]
+ newNote.borderColor = tex.toks["@todonotes@toks@currentbordercolor"]
+ newNote.leaderWidth = tex.toks["@todonotes@toks@currentleaderwidth"]
+ newNote.drawLeader = drawLeader
+ if noteType == "area" then
+ newNote.noteType = "area"
+ newNote.lineCountInArea = 0
+ -- else: newNote.noteType = nil (default value)
+ end
+ setmetatable(newNote, {__index = noteMt})
+ newNote.indexOnPage = #notesForPage + 1
+ notesForPage[newNote.indexOnPage] = newNote
+end
+
+function clearNotes()
+ -- delete the texts for the notes on this page from memory
+ -- (garbage collection does not work for nodes)
+ for _, v in pairs(notesForPage) do
+ node.free(v.textbox)
+ end
+ notesForPage = notesForNextPage
+ -- update indexOnPage for the new notes
+ for k, v in pairs(notesForPage) do
+ v.indexOnPage = k
+ end
+
+ currentPage = currentPage + 1
+end
+
+function processLastLineInTodoArea()
+ -- LaTeX counter is accessed as TeX count by prefixing c@
+ ind = tex.count["c@@todonotes@numberoftodonotes"]
+ val = tex.count["c@@todonotes@numberofLinesInArea"]
+ notesForPage[#notesForPage].lineCountInArea = val
+end
+
+
+-- *** constructing the linePositions list ***
+function linePositionsNextPage()
+ linePositionsPageNr = linePositionsPageNr + 1
+ linePositionsCurPage = {}
+ linePositions[linePositionsPageNr] = linePositionsCurPage
+end
+
+function linePositionsAddLine(ycoord, lineheight, linedepth)
+ local baseline = ycoord - tex.pageheight
+ linePositionsCurPage[#linePositionsCurPage + 1] = {baseline, baseline + lineheight, baseline - linedepth}
+end
+
+-- DEBUG
+function markLines()
+ print("marking lines for page " .. currentPage)
+ if linePositions[currentPage] ~= nil then
+ for _, v in pairs(linePositions[currentPage]) do
+ --local yPos = - (tex.pageheight - v[1])
+ local yPos = v[3]
+ local yTop = v[2]
+ tex.print("\\draw[red] (10cm," .. yPos .."sp) circle(3pt);")
+ tex.print("\\fill[red] (10cm," .. yPos .."sp) circle(1pt);")
+ tex.print("\\draw[green] (2cm," .. yTop .."sp) -- ++(17cm,0);")
+ tex.print("\\draw[blue] (2cm," .. yPos .."sp) -- ++(17cm,0);")
+ end
+ end
+end
+
+
+function getInputCoordinatesForNotes()
+ tex.sprint(catcodeStart)
+ for k, v in ipairs(notesForPage) do
+ local nodename = "@todonotes@" .. v.index .. " inText"
+
+ tex.sprint("\\pgfextractx{\\@todonotes@extractx}{\\pgfpointanchor{" ..
+ nodename .. "}{center}}")
+ tex.sprint("\\pgfextracty{\\@todonotes@extracty}{\\pgfpointanchor{" ..
+ nodename .. "}{center}}")
+ tex.print("\\directlua{notesForPage[" .. k .. "].origInputX = " ..
+ "tex.dimen[\"@todonotes@extractx\"]}")
+ tex.print("\\directlua{notesForPage[" .. k .. "].origInputY = " ..
+ "tex.dimen[\"@todonotes@extracty\"]}")
+
+ if v.noteType == "area" then
+ nodename = nodename .. "End"
+ tex.sprint("\\pgfextractx{\\@todonotes@extractx}{\\pgfpointanchor{" ..
+ nodename .. "}{center}}")
+ tex.sprint("\\pgfextracty{\\@todonotes@extracty}{\\pgfpointanchor{" ..
+ nodename .. "}{center}}")
+ tex.print("\\directlua{notesForPage[" .. k .. "].origInputEndX = " ..
+ "tex.dimen[\"@todonotes@extractx\"]}")
+ tex.print("\\directlua{notesForPage[" .. k .. "].origInputEndY = " ..
+ "tex.dimen[\"@todonotes@extracty\"]}")
+
+ notesForPage[k].linesInArea = {}
+ for i = 1, v.lineCountInArea do
+ nodename = "@todonotes@" .. v.index .. "@" .. i .. " areaSW"
+ tex.sprint("\\pgfextracty{\\@todonotes@extracty}{\\pgfpointanchor{" ..
+ nodename .. "}{center}}")
+ tex.print("\\directlua{notesForPage[" .. k .. "].linesInArea[" ..
+ i .. "] = " .. "tex.dimen[\"@todonotes@extracty\"]}")
+ end
+ end
+ end
+ tex.sprint(catcodeEnd)
+end
+
+function calcLabelAreaDimensions()
+ local routingAreaSpace = 0
+ if leaderType.needRoutingArea then
+ routingAreaSpace = routingAreaWidth
+ end
+
+ local top = tex.voffset + tex.dimen.topmargin + const1In
+ local bottom = top + tex.dimen.headheight + tex.dimen.headsep + tex.dimen.textheight + tex.dimen.footskip
+ local currentsidemargin = tex.hoffset + tex.dimen["@todonotes@currentsidemargin"] + const1In
+
+ local left = {}
+ left.top = -top
+ left.bottom = -bottom
+ left.left = distanceNotesPageBorder
+ left.right = currentsidemargin - distanceNotesText - routingAreaSpace
+ if left.right - left.left < minNoteWidth then
+ -- not enough space left of text
+ left = nil
+ else
+ left.noteWidth = left.right - left.left
+ end
+
+ local right = {}
+ right.top = -top
+ right.bottom = -bottom
+ right.left = currentsidemargin + tex.dimen.textwidth + distanceNotesText + routingAreaSpace
+ right.right = tex.pagewidth - distanceNotesPageBorder
+ if right.right - right.left < minNoteWidth then
+ -- not enough space right of text
+ right = nil
+ else
+ right.noteWidth = right.right - right.left
+ end
+
+ local text = {}
+ text.left = currentsidemargin
+ text.right = currentsidemargin + tex.dimen.textwidth
+
+ labelArea.left = left
+ labelArea.right = right
+ labelArea.text = text
+end
+
+function calcHeightsForNotes()
+ -- function has to be called outside of a tikzpicture-environment
+ tex.sprint(catcodeStart)
+ for k, v in ipairs(notesForPage) do
+ -- store height for note
+ -- (is determined by creating a box with the text and reading its size)
+
+ -- left side
+ tex.sprint("\\savebox{\\@todonotes@heightcalcbox}" ..
+ "{" .. v:boxForNoteText(false) .. "}")
+ tex.sprint("\\@todonotes@heightcalcboxdepth=\\dp\\@todonotes@heightcalcbox")
+ tex.sprint("\\@todonotes@heightcalcboxheight=\\ht\\@todonotes@heightcalcbox")
+ tex.sprint("\\directlua{notesForPage[" .. k .. "].heightLeft = " ..
+ "tex.dimen[\"@todonotes@heightcalcboxheight\"]" ..
+ " + tex.dimen[\"@todonotes@heightcalcboxdepth\"]}")
+
+ -- right side
+ tex.sprint("\\savebox{\\@todonotes@heightcalcbox}" ..
+ "{" .. v:boxForNoteText(true) .. "}")
+ tex.sprint("\\@todonotes@heightcalcboxdepth=\\dp\\@todonotes@heightcalcbox")
+ tex.sprint("\\@todonotes@heightcalcboxheight=\\ht\\@todonotes@heightcalcbox")
+ tex.sprint("\\directlua{notesForPage[" .. k .. "].heightRight = " ..
+ "tex.dimen[\"@todonotes@heightcalcboxheight\"]" ..
+ " + tex.dimen[\"@todonotes@heightcalcboxdepth\"]}")
+
+ -- store pageNr for note
+ -- (is determined as reference to a label)
+ tex.sprint("\\directlua{notesForPage[" .. k .. "].pageNr = " ..
+ "\\zref@extract{@todonotes@" .. v.index .. "}{abspage}}")
+ if v.noteType == "area" then
+ tex.sprint("\\directlua{notesForPage[" .. k .. "].pageNrEnd = " ..
+ "\\zref@extract{@todonotes@" .. v.index .. "@end}{abspage}}")
+ end
+ end
+ tex.sprint(catcodeEnd)
+end
+
+local inputShiftX = string.todimen("-0.05cm") -- sensible value depends on shape of mark
+function printNotes()
+ print("drawing labels for page " .. currentPage)
+
+ -- seperate notes that should be placed on another page
+ -- This can occur when note is in a paragraph which doesn't fit on the
+ -- current page and is thus moved to the next one. But the \todo-command is
+ -- still read before the shipout of the current page is done
+ notesForNextPage = {}
+ local k=1
+ while k <= #notesForPage do
+ local v = notesForPage[k]
+ if v.pageNr ~= currentPage then
+ table.insert(notesForNextPage, v)
+ table.remove(notesForPage, k)
+ print("moving " .. k)
+ else
+ -- update index here (needed if a note was deleted before)
+ v.indexOnPage = k
+ k = k + 1
+ end
+ end
+
+ -- add offset to input coordinates
+ for _, v in pairs(notesForPage) do
+ if v.noteType ~= "area" then
+ v.inputX = v.origInputX + inputShiftX
+ v.inputY = v.origInputY - 1.3 * (v.baselineskip - v.fontsize)
+ else
+ v.inputX = v.origInputX
+ v.inputY = v.origInputY
+ end
+ end
+
+ splitting.algo()
+ if positioning.twoSided then
+ local notesLeft, notesRight = segmentNotes(notesForPage)
+ if #notesLeft > 0 then
+ positioning.algo(notesLeft, false)
+ end
+ if #notesRight > 0 then
+ positioning.algo(notesRight, true)
+ end
+ else
+ positioning.algo()
+ end
+ for k, v in ipairs(notesForPage) do
+ if todonotesDebug then
+ local function outputWithPoints(val)
+ if val ~= nil then
+ return val .. " (" .. number.topoints(val) .. ")"
+ else
+ return ""
+ end
+ end
+ print("-----------------")
+ print(k .. ": ")
+ print("index: " .. v.index)
+ print("origInputX: " .. v.origInputX)
+ print("origInputY: " .. v.origInputY)
+ if (v.noteType ~= nil) then
+ print("noteType: " .. v.noteType)
+ print("origInputEndX:" .. v.origInputEndX)
+ print("origInputEndY:" .. v.origInputEndY)
+ print("lineCountInArea:" .. v.lineCountInArea)
+ print("linesInArea :" .. inspect(v.linesInArea))
+ else
+ print("noteType: nil")
+ end
+ print("inputX: " .. v.inputX)
+ print("inputY: " .. v.inputY)
+ print("outputX: " .. v.outputX)
+ print("outputY: " .. v.outputY)
+ if (v.rasterSlots ~= nil) then
+ print("rasterSlots: " .. v.rasterSlots)
+ end
+ print("baselineskip: " .. outputWithPoints(v.baselineskip))
+ print("fontsize: " .. outputWithPoints(v.fontsize))
+ print("textbox: " .. inspect(v.textbox))
+ print("height: " .. outputWithPoints(v:getHeight()))
+ print("heightLeft: " .. outputWithPoints(v.heightLeft))
+ print("heightRight: " .. outputWithPoints(v.heightRight))
+ print("rightSide: " .. tostring(v.rightSide))
+ if v.pageNr ~= nil then
+ print("pageNr: " .. v.pageNr)
+ end
+ print("lineColor: " .. v.lineColor)
+ print("backgroundColor:" .. v.backgroundColor)
+ print("borderColor: " .. v.borderColor)
+ print("leaderWidth: " .. v.leaderWidth)
+ print("drawLeader: " .. tostring(v.drawLeader))
+ end
+
+ -- print note
+ tex.print(catcodeStart)
+ tex.print("\\node[@todonotes@notestyle,anchor=north west," ..
+ "fill=" .. v.backgroundColor .. ",draw=" .. v.borderColor .. "] " ..
+ "(@todonotes@" .. v.index ..
+ " note) at (" .. v.outputX .. "sp," .. v.outputY .. "sp) {" ..
+ v:boxForNoteText(v.rightSide) .. "};")
+ tex.print(catcodeEnd)
+
+
+ -- output debugging hints on page
+ if todonotesDebug then
+ tex.print("\\node[anchor=north west,text=blue,fill=white,rectangle] at (@todonotes@" .. v.index .. " inText) {" .. v.index .. "};")
+ tex.print("\\draw[green,fill] (@todonotes@" .. v.index .. " inText) circle(2pt);")
+ tex.print("\\draw[black,fill] (@todonotes@" .. v.index .. " inText) circle(0.2pt);")
+ if v.noteType == "area" then
+ tex.print("\\draw[red,fill] (@todonotes@" .. v.index .. " inTextEnd) circle(2pt);")
+ end
+
+ if (v.noteType ~= nil) then
+ print(v:getClipPathForTodoArea())
+ tex.print("\\draw[blue] " .. v:getClipPathForTodoArea() .. ";")
+ --for i=1, v.lineCountInArea do
+ --tex.print(" (@todonotes@" .. v.index .. "@" .. i .. " areaSW) -- ")
+ --end
+ --tex.print("cycle;")
+ end
+ end
+ end
+ -- draw leader
+ leaderType.algo()
+
+ -- draw mark in text
+ for _, v in pairs(notesForPage) do
+ if v.drawLeader ~= false and v.noteType ~= "area" then
+ local shiftStr = "(" .. v.inputX .. "sp," .. v.inputY .. "sp)"
+ tex.print("\\draw[@todonotes@textmark," ..
+ "draw=" .. v.lineColor .. ",fill=" .. v.lineColor .. "," ..
+ "shift={" .. shiftStr .. "}," ..
+ "scale around={0.5:(-0,-0)},shift={(-0.5,-0.1)}]" ..
+ "(1,0) .. controls (0.5,0.2) and (0.65,0.3) .." ..
+ "(0.5,0.7) .. controls (0.35,0.3) and (0.5,0.2) .." ..
+ "(0,0) -- cycle;")
+ end
+ end
+
+ --- draw label areas when requested
+ if todonotesDebug then
+ local area = labelArea.left
+ if area ~= nil then
+ tex.print("\\draw[blue] (" .. area.left .. "sp," .. area.top .. "sp) rectangle (" ..
+ area.right .. "sp," .. area.bottom .. "sp);")
+ end
+ area = labelArea.right
+ if area ~= nil then
+ tex.print("\\draw[blue] (" .. area.left .. "sp," .. area.top .. "sp) rectangle (" ..
+ area.right .. "sp," .. area.bottom .. "sp);")
+ end
+ end
+end
+
+
+
+
+-- ********** Helper Functions **********
+
+-- * comparators for table.sort() *
+-- (yields true if first parameter should be placed before second parameter in
+-- sorted table)
+function compareNoteInputXAsc(note1, note2)
+ if note1.inputX < note2.inputX then
+ return true
+ end
+end
+
+function compareNoteIndInputXAsc(key1, key2)
+ if notesForPage[key1].inputX < notesForPage[key2].inputX then
+ return true
+ end
+end
+
+function compareNoteIndInputXDesc(key1, key2)
+ if notesForPage[key1].inputX > notesForPage[key2].inputX then
+ return true
+ end
+end
+
+function compareNoteIndInputYDesc(key1, key2)
+ local v1 = notesForPage[key1]
+ local v2 = notesForPage[key2]
+ if v1.inputY > v2.inputY then
+ return true
+ elseif v1.inputY == v2.inputY then
+ if v1.inputX < v2.inputX then
+ return true
+ end
+ end
+end
+
+-- * callbacks for Luatex *
+function appendStrToTokenlist(tokenlist, str)
+ str:gsub(".", function(c)
+ tokenlist[#tokenlist + 1] = {12, c:byte(), 0}
+ end)
+end
+-- writes commands into the node tree that print the absolute position on the
+-- page to the output file (streamId is taken from lpoFileStream) at the
+-- beginning of every line
+-- should be called as post_linebreak_filter
+function callbackOutputLinePositions(head)
+ while head do
+ if head.id == 0 then -- id 0 is a hlist
+ -- check if we are in the main text area (hlists in, e.g.,
+ -- tikz nodes should have other widths)
+ if head.width == tex.dimen.textwidth then
+ -- check if there is a glyph in this hlist
+ -- -> then we consider it a text line
+ local foundGlyph = false
+ local glyphTest = head.head
+ while glyphTest do
+ if glyphTest.id == 37 then -- glyph
+ foundGlyph = true
+ break
+ end
+ glyphTest = glyphTest.next
+ end
+
+ if foundGlyph then
+ local w = node.new("whatsit", "write") -- 8/1
+ w.stream = lpoFileStream
+ local tokenlist = {
+ {12, 92, 0}, -- \
+ {12, 64, 0}, -- @
+ {12, 116, 0}, -- t
+ {12, 111, 0}, -- o
+ {12, 100, 0}, -- d
+ {12, 111, 0}, -- o
+ {12, 110, 0}, -- n
+ {12, 111, 0}, -- o
+ {12, 116, 0}, -- t
+ {12, 101, 0}, -- e
+ {12, 115, 0}, -- s
+ {12, 64, 0}, -- @
+ {12, 108, 0}, -- l
+ {12, 105, 0}, -- i
+ {12, 110, 0}, -- n
+ {12, 101, 0}, -- e
+ {12, 112, 0}, -- p
+ {12, 111, 0}, -- o
+ {12, 115, 0}, -- s
+ {12, 105, 0}, -- i
+ {12, 116, 0}, -- t
+ {12, 105, 0}, -- i
+ {12, 111, 0}, -- o
+ {12, 110, 0}, -- n
+ {12, 123, 0} -- {
+ }
+ t = token.create("@todonotes@pdflastypos")
+ tokenlist[#tokenlist + 1] = t
+ tokenlist[#tokenlist + 1] = {12, 125, 0}
+ tokenlist[#tokenlist + 1] = {12, 123, 0}
+ appendStrToTokenlist(tokenlist, tostring(head.height))
+ tokenlist[#tokenlist + 1] = {12, 125, 0}
+ tokenlist[#tokenlist + 1] = {12, 123, 0}
+ appendStrToTokenlist(tokenlist, tostring(head.depth))
+ tokenlist[#tokenlist + 1] = {12, 125, 0}
+ w.data = tokenlist
+ head.head = node.insert_before(head.head,head.head,w)
+
+ local w = node.new("whatsit", "pdf_save_pos") -- 8/23
+ head.head = node.insert_before(head.head,head.head,w)
+ end
+ end
+ end
+ head = head.next
+ end
+ return true
+end
+
+
+
+
+
+-- ********** Leader Drawing Algorithms **********
+
+function drawLeaderPath(note, path)
+ if note.drawLeader == false then
+ return
+ end
+ local clipPath
+ if note.noteType == "area" then
+ clipPath = note:getClipPathForTodoArea()
+ tex.print("\\begin{scope}")
+ tex.print("\\clip (current page.north west) rectangle (current page.south east) ")
+ tex.print(clipPath)
+ tex.print(";")
+ end
+ tex.print("\\draw[@todonotes@leader,draw=" .. note.lineColor ..
+ ",line width=" .. note.leaderWidth .. ",name path=leader] " .. path .. ";")
+ if note.noteType == "area" then
+ tex.print("\\path[name path=clipping] " .. clipPath .. ";")
+ tex.print("\\fill[@todonotes@leader,name intersections={of=leader and clipping, by=x,sort by=leader}] (x) circle(3pt);")
+ tex.print("\\end{scope}")
+ end
+end
+
+
+-- ** leader drawing: s-leaders
+function drawSLeaders()
+ for k, v in ipairs(notesForPage) do
+ drawLeaderPath(v, v:getLabelAnchorTikz() ..
+ " -- " .. v:getInTextAnchorTikz())
+ end
+end
+leaderTypes["s"] = {algo = drawSLeaders}
+
+
+
+-- ** leader drawing: opo-leaders
+function drawOpoLeader(v, opoShift, rightSide)
+ if rightSide then
+ opoShift = - opoShift
+ end
+ drawLeaderPath(v, v:getLabelAnchorTikz() .. " -- +(" .. opoShift .. "sp,0) " ..
+ "|- " .. v:getInTextAnchorTikz())
+end
+function drawOpoGroup(group, directionDown, rightSide)
+ if directionDown == nil then
+ for _, v2 in ipairs(group) do
+ drawOpoLeader(notesForPage[v2], 0, rightSide)
+ end
+ else
+ if #group == 1 then
+ -- place p-section of leader in center of routing area
+ local opoShift = distanceNotesText / 2 + routingAreaWidth / 2
+ drawOpoLeader(notesForPage[group[1]], opoShift, rightSide)
+ else
+ local leaderDistance = routingAreaWidth / (#group - 1)
+
+ -- initialise shift value
+ local nextOpoShift, move
+ if directionDown then
+ nextOpoShift = distanceNotesText / 2 + routingAreaWidth
+ move = -leaderDistance
+ else
+ nextOpoShift = distanceNotesText / 2
+ move = leaderDistance
+ end
+
+ -- cycle through group
+ for _, v2 in ipairs(group) do
+ drawOpoLeader(notesForPage[v2], nextOpoShift, rightSide)
+ nextOpoShift = nextOpoShift + move
+ end
+ end
+ end
+end
+function drawOpoLeadersSide(notes, rightSide)
+ table.sort(notes, compareNoteIndInputYDesc)
+
+ local lastDirectionDown = nil
+ local group = {}
+ local prevNote
+ for _, ind in ipairs(notes) do
+ local v = notesForPage[ind]
+
+ local leaderAnchorY = v:getLabelAnchorY()
+ if leaderAnchorY > v.inputY then
+ newDirectionDown = true
+ elseif leaderAnchorY < v.inputY then
+ newDirectionDown = false
+ else
+ newDirectionDown = nil
+ end
+
+ if lastDirectionDown == newDirectionDown and
+ prevNote ~= nil and
+ -- following conditions check that leaders would really intersect
+ -- otherwise we can start a new group
+ ((newDirectionDown and leaderAnchorY >= prevNote.inputY) or
+ (not newDirectionDown and v.inputY >= prevNote:getLabelAnchorY())) then
+ -- note belongs to group
+ table.insert(group, ind)
+ else
+ -- draw leaders for group
+ drawOpoGroup(group, lastDirectionDown, rightSide)
+
+ -- initialise new group with this note
+ lastDirectionDown = newDirectionDown
+ group = {ind}
+ end
+
+ prevNote = v
+ end
+ drawOpoGroup(group, lastDirectionDown, rightSide)
+end
+function drawOpoLeaders()
+ local notesLeft, notesRight = segmentNotes(notesForPage)
+ if #notesLeft > 0 then
+ drawOpoLeadersSide(notesLeft, false)
+ end
+ if #notesRight > 0 then
+ drawOpoLeadersSide(notesRight, true)
+ end
+end
+leaderTypes["opo"] = {algo = drawOpoLeaders,
+ needRoutingArea = true}
+
+
+
+-- ** leader drawing: po-leaders
+function drawPoLeaders()
+ for _, v in ipairs(notesForPage) do
+ drawLeaderPath(v, v:getLabelAnchorTikz() .. " -| " .. v:getInTextAnchorTikz())
+ end
+end
+leaderTypes["po"] = {algo = drawPoLeaders}
+
+
+
+-- ** leader drawing: os-leaders
+function drawOsLeaders()
+ for _, v in ipairs(notesForPage) do
+ local cornerX
+ if v.rightSide then
+ cornerX = labelArea.right.left - distanceNotesText / 2 - routingAreaWidth
+ else
+ cornerX = labelArea.left.right + distanceNotesText / 2 + routingAreaWidth
+ end
+ drawLeaderPath(v, v:getInTextAnchorTikz() ..
+ " -- (" .. cornerX .. "sp,0 |- 0," .. v.inputY .. "sp) -- " ..
+ v:getLabelAnchorTikz())
+ end
+end
+leaderTypes["os"] = {algo = drawOsLeaders,
+ needRoutingArea = true}
+
+
+
+-- ** leader drawing: s-Bezier-leaders
+-- additional fields for each note:
+-- leaderArmY
+-- movableControlPointX
+-- optimalPositionX
+-- currentForce
+-- forceLimitDec
+-- forceLimitInc
+
+-- settings for algorithm
+maxIterations = 1000
+factorRepulsiveControlPoint = 1
+factorAttractingControlPoint = 1
+stopCondition = 65536 -- corresponds to 1pt
+
+function constructCurve(l)
+ local curve = {}
+
+ -- site
+ curve[1] = {}
+ curve[1].x = l.inputX
+ curve[1].y = l.inputY
+
+ -- unmovable control point (middle point of site and movable control point)
+ curve[2] = {}
+ curve[2].x = (l.inputX + l.movableControlPointX) / 2
+ curve[2].y = (l.inputY + l.leaderArmY) / 2
+
+ -- movable control point
+ curve[3] = {}
+ curve[3].x = l.movableControlPointX
+ curve[3].y = l.leaderArmY
+
+ -- port
+ curve[4] = {}
+ curve[4].x = labelArea:getXTextSide(l.rightSide)
+ curve[4].y = l.leaderArmY
+
+ return curve
+end
+function computeRepulsiveControlPointForces()
+ for k1, l1 in pairs(notesForPage) do
+ for k2, l2 in pairs(notesForPage) do
+ if k1 ~= k2 then
+ -- curves of the leaders
+ local curve1 = constructCurve(l1)
+ local curve2 = constructCurve(l2)
+
+ local distance = checkCurveApproximation(curve1, curve2);
+
+ -- check if R1 has to be increased or decreased to increase the distance of the 2 curves
+ -- if curve1 is bent into the direction of curve2, R1 has to be decreased
+ local actualR = math.abs(labelArea:getXTextSide(l1.rightSide) - l1.movableControlPointX)
+ if ((l1.inputY < l1.leaderArmY and
+ l2.leaderArmY < l1.leaderArmY) or
+ (l1.inputY > l1.leaderArmY and
+ l2.leaderArmY > l1.leaderArmY)) then
+ -- R1 has to be increased
+ local desiredR = math.abs(labelArea:getXTextSide(l1.rightSide) - l1.optimalPositionX)
+ local diff = math.abs(desiredR - actualR)
+ if distance == 0 then
+ distance = 0.01
+ end
+ local force = diff / distance
+ local newForce = force * factorRepulsiveControlPoint
+ l1.currentForce = l1.currentForce + newForce
+ l1.forceLimitDec = math.min(l1.forceLimitDec, distance * 0.45)
+ else
+ -- R1 has to be decreased
+ if distance == 0 then
+ distance = 0.01
+ end
+ local force = actualR / distance
+ local newForce = -force * factorRepulsiveControlPoint
+ l1.currentForce = l1.currentForce + newForce
+ local oldLim = l1.forceLimitInc
+ l1.forceLimitInc = math.min(l1.forceLimitInc, distance * 0.45)
+ --if oldLim ~= l1.forceLimitInc then
+ --print(k1 .. ": Reduced forceLimitInc from " .. oldLim .. " to " .. l1.forceLimitInc .. " because of " .. k2 .. " (distance: " .. distance .. ")")
+ --end
+ end
+ end
+ end
+ end
+end
+function computeAttractingControlPointForces()
+ for _, l in pairs(notesForPage) do
+ local desiredR = math.abs(labelArea:getXTextSide(l.rightSide) - l.optimalPositionX)
+ local actualR = math.abs(labelArea:getXTextSide(l.rightSide) - l.movableControlPointX)
+ local newForce = (desiredR - actualR) * factorAttractingControlPoint
+ l.currentForce = l.currentForce + newForce
+ end
+end
+function getPointOnCurve(t, curve)
+ if #curve ~= 4 then
+ error("4 points needed for a Bezier-curve. Given size was: " .. #curve)
+ end
+
+ local x = (1 - t) * (1 - t) * (1 - t) * curve[1].x +
+ 3 * t * (1 - t) * (1 - t) * curve[2].x +
+ 3 * t * t * (1 - t) * curve[3].x +
+ t * t * t * curve[4].x
+
+ local y = (1 - t) * (1 - t) * (1 - t) * curve[1].y +
+ 3 * t * (1 - t) * (1 - t) * curve[2].y +
+ 3 * t * t * (1 - t) * curve[3].y +
+ t * t * t * curve[4].y;
+
+ return x, y
+end
+function checkCurveApproximation(curve1, curve2)
+ -- these lists will contain the sections of the approximation of the two curves
+ local sectionsCurve1 = {}
+ local sectionsCurve2 = {}
+
+ -- get line segments of the first curve
+ local numberOfSectionsCurve1 = numberOfCurvePartitions
+ local temp1X, temp1Y = getPointOnCurve(0, curve1)
+ local i = 1
+ while i <= numberOfSectionsCurve1 do
+ local t = i / numberOfSectionsCurve1
+ local temp2X, temp2Y = getPointOnCurve(t, curve1)
+ local line = {}
+ line.x1 = temp1X
+ line.y1 = temp1Y
+ line.x2 = temp2X
+ line.y2 = temp2Y
+ table.insert(sectionsCurve1, line)
+ temp1X, temp1Y = temp2X, temp2Y
+ i = i + 1
+ end
+
+ -- get line segments of the second curve
+ local numberOfSectionsCurve2 = numberOfCurvePartitions
+ temp1X, temp1Y = getPointOnCurve(0, curve2)
+ i = 1
+ while i <= numberOfSectionsCurve2 do
+ local t = i / numberOfSectionsCurve2
+ local temp2X, temp2Y = getPointOnCurve(t, curve2)
+ local line = {}
+ line.x1 = temp1X
+ line.y1 = temp1Y
+ line.x2 = temp2X
+ line.y2 = temp2Y
+ table.insert(sectionsCurve2, line)
+ temp1X, temp1Y = temp2X, temp2Y
+ i = i + 1
+ end
+
+ -- get the minimal distance of the 2 curve approximations
+ local minDistance = math.huge
+ for _, line1 in pairs(sectionsCurve1) do
+ for _, line2 in pairs(sectionsCurve2) do
+ local distance = getDistance(line1, line2)
+ if distance <= minDistance then
+ minDistance = distance
+ end
+ end
+ end
+
+ return minDistance
+end
+function getDistance(line1, line2)
+ local t1, t2 = pathLine.line_line_intersection(line1.x1, line1.y1, line1.x2, line1.y2,
+ line2.x1, line2.y1, line2.x2, line2.y2)
+ if 0 <= t1 and t1 <= 1 and 0 <= t2 and t2 <= 1 then
+ -- the lines do intersect
+ return 0
+ end
+
+ local d1 = pathLine.hit(line2.x1, line2.y1, line1.x1, line1.y1, line1.x2, line1.y2)
+ local d2 = pathLine.hit(line2.x2, line2.y2, line1.x1, line1.y1, line1.x2, line1.y2)
+ local d3 = pathLine.hit(line1.x1, line1.y1, line2.x1, line2.y1, line2.x2, line2.y2)
+ local d4 = pathLine.hit(line1.x2, line1.y2, line2.x1, line2.y1, line2.x2, line2.y2)
+ return math.sqrt(math.min(d1, d2, d3, d4))
+end
+function applyForces(v)
+ --print("force on note " .. v.index .. ": " .. v.currentForce .. " (limit: +" .. v.forceLimitInc .. ", -" .. v.forceLimitDec .. ")")
+
+ -- limit the force so the movable control point is between the port and the optimal position
+ local actualR = math.abs(labelArea:getXTextSide(v.rightSide) - v.movableControlPointX)
+ local differenceR = math.abs(labelArea:getXTextSide(v.rightSide) - v.optimalPositionX) - actualR
+ if (v.currentForce < 0 and math.abs(v.currentForce) > actualR) then
+ v.currentForce = (-1) * actualR
+ end
+ if (v.currentForce > 0 and v.currentForce > differenceR) then
+ v.currentForce = differenceR
+ end
+
+ -- limit the force so 2 curves do not get too close to each other and do not cross
+ if v.currentForce > v.forceLimitInc then
+ v.currentForce = v.forceLimitInc
+ end
+ if v.currentForce < (-1) * v.forceLimitDec then
+ v.currentForce = (-1) * v.forceLimitDec
+ end
+ v.forceLimitDec = math.huge
+ v.forceLimitInc = math.huge
+
+ if v.rightSide then
+ v.movableControlPointX = v.movableControlPointX - v.currentForce
+ else
+ v.movableControlPointX = v.movableControlPointX + v.currentForce
+ end
+
+ --print("force on note " .. v.index .. ": " .. v.currentForce)
+ local c = v.currentForce
+ v.currentForce = 0
+ return c
+end
+function getAngle(centerX, centerY, x, y)
+ local vectorX = x - centerX
+ local vectorY = y - centerY
+ local length = math.sqrt((vectorX ^ 2) + (vectorY ^ 2))
+
+ vectorX = vectorX / length
+ vectorY = vectorY / length
+
+ local radAngle = math.acos(vectorX)
+ local degAngle = (radAngle * 180) / math.pi
+
+ if vectorY < 0 then
+ degAngle = 360 - degAngle
+ end
+
+ return degAngle
+end
+function solveQuadraticEquation(a, b, c)
+ local discr = (b * b) - (4 * a * c)
+
+ if discr < 0 then
+ error("Fehler bei der Berechnung das optimalen Punktes")
+ end
+
+ local solution1 = ((-b) + math.sqrt(discr)) / (2 * a)
+ local solution2 = ((-b) - math.sqrt(discr)) / (2 * a)
+
+ if solution1 < 0 and solution2 < 0 then
+ error("no positive solution")
+ end
+
+ if solution1 < solution2 then
+ return solution2
+ else
+ return solution1
+ end
+end
+function computeOptimalPosition(v)
+ local distance = point.distance(v.inputX, v.inputY, labelArea:getXTextSide(v.rightSide), v.leaderArmY)
+
+ -- the angle at the port between the point and the movable control point
+ local tempAngle = getAngle(v.inputX, v.inputY, labelArea:getXTextSide(v.rightSide), v.leaderArmY)
+
+ local gamma
+ if v.rightSide then
+ if tempAngle < 180 then
+ gamma = tempAngle
+ else
+ gamma = 360 - tempAngle
+ end
+ else
+ if tempAngle < 180 then
+ gamma = 180 - tempAngle
+ else
+ gamma = tempAngle - 180
+ end
+ end
+
+ -- a quadratic formula has to be solved to get the optimal position
+ local a = 3
+ local b = 2 * distance * math.cos(math.rad(gamma))
+ local c = -(distance * distance)
+
+ local r = solveQuadraticEquation(a, b, c)
+
+ if v.rightSide then
+ v.optimalPositionX = labelArea:getXTextSide(v.rightSide) - r
+ else
+ v.optimalPositionX = labelArea:getXTextSide(v.rightSide) + r
+ end
+end
+function drawSBezierLeaders()
+ for _, v in pairs(notesForPage) do
+ -- initialise leader
+ v.leaderArmY = v:getLabelAnchorY()
+ v.movableControlPointX = labelArea:getXTextSide(v.rightSide)
+ v.currentForce = 0
+ v.forceLimitDec = math.huge
+ v.forceLimitInc = math.huge
+ end
+
+ numberOfCurvePartitions = #notesForPage * 3
+
+ for _, v in pairs(notesForPage) do
+ computeOptimalPosition(v)
+ end
+
+ -- main loop
+ local proceed = true
+ local loopCounter = 0
+ while (proceed and loopCounter < maxIterations) do
+ --print("")
+ --print("--------------------------------------------------")
+ print("iteration " .. loopCounter .. ":")
+
+ -- compute forces
+ computeRepulsiveControlPointForces()
+ computeAttractingControlPointForces()
+
+ -- apply forces
+ proceed = false
+ for _, l in pairs(notesForPage) do
+ local diff = applyForces(l)
+ if diff > stopCondition then
+ proceed = true
+ end
+ end
+
+ loopCounter = loopCounter + 1
+ end
+
+ print("")
+ print("End of Force-directed algo")
+ print("number of iterations: " .. loopCounter)
+ print("")
+
+ -- draw
+ for _, v in pairs(notesForPage) do
+ local curve = constructCurve(v)
+ local unmovableStr = "(" .. curve[2].x .. "sp," .. curve[2].y .. "sp)"
+ local movableStr = "(" .. curve[3].x .. "sp," .. curve[3].y .. "sp)"
+ drawLeaderPath(v, v:getInTextAnchorTikz() .. " .. controls " ..
+ unmovableStr .. " and " .. movableStr .. " .. " ..
+ v:getLabelAnchorTikz())
+
+ -- draw control points when requested
+ if todonotesDebug then
+ local optimalStr = "(" .. v.optimalPositionX .. "sp," .. v.leaderArmY .. "sp)"
+ tex.print("\\node[anchor=north west,text=pink,fill=white,rectangle] at " .. optimalStr .. " {" .. v.index .. "};")
+ tex.print("\\node[anchor=north west,text=red,fill=white,rectangle] at " .. movableStr .. " {" .. v.index .. "};")
+ tex.print("\\node[anchor=north west,text=orange,fill=white,rectangle] at " .. unmovableStr .. " {" .. v.index .. "};")
+ tex.print("\\draw[red,fill] " .. movableStr .. " circle(2pt);")
+ tex.print("\\draw[orange,fill] " .. unmovableStr .. " circle(2pt);")
+ tex.print("\\draw[pink,fill] " .. optimalStr .. " circle(1pt);")
+ end
+ end
+end
+leaderTypes["sBezier"] = {algo = drawSBezierLeaders}
+
+
+
+
+
+-- ********** Positioning Algorithms **********
+
+-- ** helper functions
+
+-- divides notes in two lists (for left and right side)
+-- side must be stored in note.rightSide for every note before using this function
+function segmentNotes(notes)
+ local availableNotesLeft = {}
+ local availableNotesRight = {}
+ for k, v in pairs(notes) do
+ if v.rightSide == true then
+ table.insert(availableNotesRight, k)
+ else
+ table.insert(availableNotesLeft, k)
+ end
+ end
+ return availableNotesLeft, availableNotesRight
+end
+
+-- finds the index in the list given as parameter with the minimum angle
+-- the function used for computation of the angle is given as second parameter
+-- (the alphaFormula gets the note, to which the angle should be computed, as
+-- the only parameter)
+function findIndexMinAlpha(availableNotesIndex, alphaFormula)
+ local minAlpha = math.huge -- infinity
+ local minIndex = -1
+
+ for k, v in pairs(availableNotesIndex) do
+ local alpha = alphaFormula(notesForPage[v])
+ if alpha < minAlpha then
+ minAlpha = alpha
+ minIndex = k
+ end
+ end
+
+ return minIndex
+end
+
+
+
+-- ** partition into stacks
+function getMeanYHeight(stack)
+ -- TODO: Alternative: nicht das arithmetische Mittel verwenden, sondern
+ -- Mittelpunkt zwischen dem obersten und untersten Punkt
+ local sumY = 0
+ local height = 0
+ for _, v in pairs(stack) do
+ sumY = sumY + notesForPage[v].inputY
+ height = height + notesForPage[v]:getHeight() + 2 * noteInnerSep + noteInterSpace
+ end
+
+ local area = labelArea:getArea(notesForPage[stack[1]].rightSide)
+
+ local meanY = sumY / #stack
+ local height = height - noteInterSpace
+ if meanY + (height/2) > area.top then
+ meanY = area.top - (height/2)
+ elseif meanY - (height/2) < area.bottom then
+ meanY = area.bottom + (height/2)
+ end
+ return meanY, height
+end
+function stacksIntersect(stackTop, stackBottom)
+ local topMeanY, topHeight = getMeanYHeight(stackTop)
+ local topLower = topMeanY - topHeight/2
+
+ local bottomMeanY, bottomHeight = getMeanYHeight(stackBottom)
+ local bottomUpper = bottomMeanY + bottomHeight/2
+
+ if topLower - bottomUpper < noteInterSpace then
+ return true
+ else
+ return false
+ end
+end
+function findStacks(notesOnSide)
+ local notes = table.copy(notesOnSide)
+ table.sort(notes, compareNoteIndInputYDesc)
+
+ -- list that contains stacks
+ -- is initialized by putting all notes as single stacks ordered by their inputY
+ local stacks = {}
+ for _, v in pairs(notes) do
+ table.insert(stacks, {v})
+ end
+
+ -- Collapse Stacks where needed
+ local i = 1
+ while i <= #stacks - 1 do
+ if stacksIntersect(stacks[i], stacks[i+1]) then
+ collapsedStacks = true
+ for _, v in pairs(stacks[i+1]) do
+ table.insert(stacks[i], v)
+ end
+ table.remove(stacks,i+1)
+ if i > 1 then
+ -- as stack i has increased in size we look at the previous
+ -- stack again in next iteration
+ i = i - 1
+ end
+ else
+ -- look at next stack in next iteration
+ i = i + 1
+ end
+ end
+
+ return stacks
+end
+
+
+-- ** positioning: inText
+function posInText()
+ -- trivial algorithm
+ -- places notes in text on position where todo-command was issued
+ for k, v in ipairs(notesForPage) do
+ v.outputX = v.inputX
+ v.outputY = v.inputY
+ end
+end
+positioningAlgos["inText"] = {algo = posInText,
+ leaderAnchor = "north west",
+ leaderShift = false,
+ twoSided = false}
+
+
+
+-- ** positioning: inputOrder
+-- start at top and place notes below each other on left/right side
+-- notes are placed in the order induced by their y-coordinates
+function posInputOrder(notes, rightSide)
+ table.sort(notes, compareNoteIndInputYDesc)
+ placeNotesInputOrder(notes, labelArea:getArea(rightSide).top, rightSide)
+end
+positioningAlgos["inputOrder"] = {algo = posInputOrder,
+ leaderAnchor = "east",
+ leaderShift = false,
+ twoSided = true}
+
+
+
+-- ** positioning: inputOrderStacks
+function placeNotesInputOrder(stack, yStart, rightSide)
+ local freeY = yStart
+
+ for _, k in ipairs(stack) do
+ local v = notesForPage[k]
+ v.outputX = labelArea:getArea(rightSide).left
+ v.outputY = freeY
+ freeY = freeY - v:getHeight() - 2 * noteInnerSep - noteInterSpace
+ end
+end
+function posInputOrderStacks(notesOnSide, rightSide)
+ table.sort(notesOnSide, compareNoteIndInputYDesc)
+
+ local stacks = findStacks(notesOnSide)
+
+ -- place stacks
+ for k, stack in pairs(stacks) do
+ local meanY, height = getMeanYHeight(stack)
+ local stackStart = meanY + height / 2
+ placeNotesInputOrder(stack, stackStart, rightSide)
+ end
+end
+positioningAlgos["inputOrderStacks"] = {algo = posInputOrderStacks,
+ leaderAnchor = "east",
+ leaderShift = false,
+ twoSided = true}
+
+
+
+-- ** positioning: sLeaderNorthEast
+function posSLeaderNorthEast(notes, rightSide)
+ local noteY = labelArea:getArea(rightSide).top
+
+ local alphaFormula
+ local noteX = labelArea:getXTextSide(rightSide)
+ local outputX = labelArea:getArea(rightSide).left
+ if rightSide then
+ alphaFormula = function (note)
+ return (noteY - note.inputY) / (noteX - note.inputX)
+ end
+ else
+ alphaFormula = function (note)
+ return (noteY - note.inputY) / (note.inputX - noteX)
+ end
+ end
+
+ while #notes > 0 do
+ local minIndex = findIndexMinAlpha(notes, alphaFormula)
+
+ -- place note identified by minIndex
+ local note = notesForPage[notes[minIndex]]
+ note.outputX = outputX
+ note.outputY = noteY
+ noteY = noteY - note:getHeight() - 2 * noteInnerSep - noteInterSpace
+
+ table.remove(notes, minIndex)
+ end
+end
+positioningAlgos["sLeaderNorthEast"] = {algo = posSLeaderNorthEast,
+ leaderAnchor = "north east",
+ leaderShift = false,
+ twoSided = true}
+
+
+
+-- ** positioning: sLeaderNorthEastBelow
+function placeNotesNorthEastBelow(stack, yStart, rightSide)
+ -- calculate minimum height of all notes
+ local minHeight = math.huge -- (infinity)
+ for _, v in pairs(stack) do
+ if notesForPage[v]:getHeight() < minHeight then
+ minHeight = notesForPage[v]:getHeight()
+ end
+ end
+ local leaderShiftY = (- minHeight - 2 * noteInnerSep) / 2
+
+ local noteY = yStart
+ local availableNotes = table.copy(stack)
+
+ local alphaFormula
+ local noteX = labelArea:getXTextSide(rightSide)
+ local outputX = labelArea:getArea(rightSide).left
+ if rightSide == true then
+ alphaFormula = function (note)
+ return ((noteY + leaderShiftY) - note.inputY) / (noteX - note.inputX)
+ end
+ else
+ alphaFormula = function (note)
+ return ((noteY + leaderShiftY) - note.inputY) / (note.inputX - noteX)
+ end
+ end
+ while #availableNotes > 0 do
+ local minIndex = findIndexMinAlpha(availableNotes, alphaFormula)
+
+ -- place note identified by minIndex
+ local note = notesForPage[availableNotes[minIndex]]
+ note.outputX = outputX
+ note.outputY = noteY
+ note.leaderShiftX = 0
+ note.leaderShiftY = leaderShiftY
+ noteY = noteY - note:getHeight() - 2 * noteInnerSep - noteInterSpace
+
+ table.remove(availableNotes, minIndex)
+ end
+end
+function posSLeaderNorthEastBelow(notes, rightSide)
+ placeNotesNorthEastBelow(notes, labelArea:getArea(rightSide).top, rightSide)
+end
+positioningAlgos["sLeaderNorthEastBelow"] = {algo = posSLeaderNorthEastBelow,
+ leaderAnchor = "north east",
+ leaderShift = true,
+ twoSided = true}
+
+
+
+-- ** positioning: sLeaderNorthEastBelowStacks
+function posSLeaderNorthEastBelowStacks(notesOnSide, rightSide)
+ local stacks = findStacks(notesOnSide)
+
+ -- place stacks
+ for k, stack in pairs(stacks) do
+ local meanY, height = getMeanYHeight(stack)
+ local stackStart = meanY + height / 2
+ placeNotesNorthEastBelow(stack, stackStart, rightSide)
+ end
+end
+positioningAlgos["sLeaderNorthEastBelowStacks"] = {algo = posSLeaderNorthEastBelowStacks,
+ leaderAnchor = "north east",
+ leaderShift = true,
+ twoSided = true}
+
+
+
+-- ** positioning: sLeaderEast
+function posSLeaderEast(notes, rightSide)
+ local leaderPosY
+ local noteY = labelArea:getArea(rightSide).top
+
+ local alphaFormula
+ local noteX = labelArea:getXTextSide(rightSide)
+ local outputX = labelArea:getArea(rightSide).left
+ if rightSide == true then
+ alphaFormula = function (note)
+ return (leaderPosY - note.inputY) / (noteX - note.inputX)
+ end
+ else
+ alphaFormula = function (note)
+ return (leaderPosY - note.inputY) / (note.inputX - noteX)
+ end
+ end
+
+ local placedNotes = {}
+ while #notes > 0 do
+ -- build a array with all distinct heights of the notes
+ -- first create a set and then convert to sorted array
+ local heights = {}
+ print(#heights)
+ for _, v in pairs(notes) do
+ heights[notesForPage[v]:getHeight()] = true
+ end
+ heights = table.keys(heights)
+ table.sort(heights)
+
+ local chosenIndex = -1
+ local chosenH = -1
+ for _, h in pairs(heights) do
+ print("testing height: " .. h) -- DEBUG
+ leaderPosY = noteY - noteInnerSep - h/2
+
+ -- find point with highest angle
+ local minIndex = findIndexMinAlpha(notes, alphaFormula)
+
+ -- found a valid note
+ if notesForPage[notes[minIndex]]:getHeight() <= h then
+ chosenIndex = minIndex
+ chosenH = h
+ break
+ end
+ end
+
+ -- place note identified by chosenIndex
+ local note = notesForPage[notes[chosenIndex]]
+ note.outputX = outputX
+ -- let free space above note if needed (if chosenH ~= note:getHeight())
+ note.outputY = noteY - (chosenH - note:getHeight()) / 2
+ -- no extraordinary free space below note (even if chosenH ~= note:getHeight())
+ noteY = note.outputY - note:getHeight() - 2 * noteInnerSep - noteInterSpace
+ -- DEBUG
+ if chosenH ~= note:getHeight() then
+ print("Creating free space above note " .. note.index)
+ end
+
+ table.insert(placedNotes, notes[chosenIndex])
+ table.remove(notes, chosenIndex)
+ end
+
+ -- postprocessing: reduce spaces between notes where possible
+ for ind, noteNr in pairs(placedNotes) do
+ local note = notesForPage[noteNr]
+
+ local aimedPos
+ if ind == 1 then
+ aimedPos = labelArea:getArea(rightSide).top
+ else
+ local prevNote = notesForPage[placedNotes[ind-1]]
+ aimedPos = prevNote.outputY - prevNote:getHeight() - 2 * noteInnerSep - noteInterSpace
+ end
+
+ -- DEBUG
+ if aimedPos ~= note.outputY then
+ print("note " .. note.index .. " got moved:")
+ print("aimed: " .. aimedPos)
+ print("real: " .. note.outputY)
+ end
+
+ local aimedLeaderAnchorY = aimedPos - noteInnerSep - note:getHeight() / 2
+ local realLeaderAnchorY = note.outputY - noteInnerSep - note:getHeight() / 2
+ -- it holds: realLeaderAnchorY < aimedLeaderAnchorY (realLeaderAnchor is lower on page)
+
+ -- check if there are points in triangle (aimedLeaderAnchor, note.input, realLeaderAnchor)
+ -- we perform this check by calculating the angle of the points referred to note.input
+ local pointsInTriangle = false
+ local denom
+ if rightSide then
+ denom = noteX - note.inputX
+ else
+ denom = note.inputX - noteX
+ end
+ local aimedLeaderAnchorAngle = (aimedLeaderAnchorY - note.inputY) / denom
+ local realLeaderAnchorAngle = (realLeaderAnchorY - note.inputY) / denom
+ local minAngle = math.huge
+ local minAngleIndex = -1 -- takes index of lowest point in triangle
+ for otherInd, otherNote in pairs(notesForPage) do
+ if otherInd ~= noteNr and
+ ((not rightSide and otherNote.inputX < note.inputX) or
+ (rightSide and otherNote.inputX > note.inputX)) then
+ local otherNoteAngle
+ if rightSide then
+ otherNoteAngle = (otherNote.inputY - note.inputY) / (otherNote.inputX - note.inputX)
+ else
+ otherNoteAngle = (otherNote.inputY - note.inputY) / (note.inputX - otherNote.inputX)
+ end
+
+ if (realLeaderAnchorAngle < otherNoteAngle)
+ and (otherNoteAngle < aimedLeaderAnchorAngle) then
+ pointsInTriangle = true
+ if otherNoteAngle < minAngle then
+ minAngle = otherNoteAngle
+ minAngleIndex = otherInd
+ end
+ print(otherNote.index .. " is in triangle for " .. note.index) -- DEBUG
+ end
+ end
+ end
+
+ if not pointsInTriangle then
+ -- no points in triangle
+ -- => we can move this note to aimedPos
+ note.outputY = aimedPos
+ else
+ -- move note upwards so that leader touches lowest point in triangle
+ -- new point for leader anchor is determined by the ray from note.input through the lowest point in triangle (otherNote.input)
+ -- TODO: force a certain distance between leader and other points (at the moment a leader can contain endpoints of other leaders)
+ local otherNote = notesForPage[minAngleIndex]
+ local aimedLeaderAnchorY = note.inputY - (note.inputY - otherNote.inputY) * (note.inputX - noteX) / (note.inputX - otherNote.inputX)
+ note.outputY = aimedLeaderAnchorY + noteInnerSep + note:getHeight() / 2
+ end
+ end
+end
+positioningAlgos["sLeaderEast"] = {algo = posSLeaderEast,
+ leaderAnchor = "east",
+ leaderShift = false,
+ twoSided = true}
+
+
+
+-- ** positioning: poLeaders
+function getRasterAbsolute(rasterHeight, top, rasterIndex)
+ return top - (rasterIndex - 1) * rasterHeight
+end
+ -- distance between line and leader that algorithm tries to reach when there is
+ -- no neighbouring line
+local poMinDistLine = string.todimen("4pt")
+function getPosAboveLine(linePositionsCurPage, lineInd)
+ local line = linePositionsCurPage[lineInd]
+ local posAbove
+ if linePositionsCurPage[lineInd - 1] ~= nil then
+ posAbove = (line[2] + linePositionsCurPage[lineInd - 1][3]) / 2
+ end
+ if posAbove == nil or posAbove - line[2] > poMinDistLine then
+ posAbove = line[2] + poMinDistLine
+ end
+ return posAbove
+end
+function getPosBelowLine(linePositionsCurPage, lineInd)
+ local line = linePositionsCurPage[lineInd]
+ local posBelow
+ if linePositionsCurPage[lineInd + 1] ~= nil then
+ posBelow = (line[3] + (linePositionsCurPage[lineInd + 1][2])) / 2
+ end
+ if posBelow == nil or line[3] - posBelow > poMinDistLine then
+ posBelow = line[3] - poMinDistLine
+ end
+ return posBelow
+end
+function posPoLeaders(notes, rightSide, avoidLines)
+ print("rasterHeight: " .. rasterHeight)
+
+ local linePositionsCurPage
+ if avoidLines then
+ linePositionsCurPage = linePositions[currentPage] or {}
+ end
+
+ -- number of slots on the whole page
+ local area = labelArea:getArea(rightSide)
+ local totalNumSlots = math.floor((area.top - area.bottom) / rasterHeight)
+
+ -- calculate number of raster slots for each note
+ for _, ind in pairs(notes) do
+ local v = notesForPage[ind]
+ local height = v:getHeight() + 2 * noteInnerSep + noteInterSpace
+ v.rasterSlots = math.ceil(height / rasterHeight)
+ end
+
+ -- sort notes by inputY
+ table.sort(notes, compareNoteIndInputYDesc)
+
+ -- draw slots
+ if todonotesDebug then
+ for i = 1,totalNumSlots+1 do
+ local pos = area.top - (i-1) * rasterHeight
+ tex.print("\\draw[blue,dashed] (0," .. pos .. "sp) -- +(21cm,0);")
+ end
+ end
+
+ -- initialise table opt for dynamic program
+ -- opt[topPoint, bottomPoint, topSlot, bottomSlot, numberLabeledSites]
+ -- opt[a][b][c][d][e] describes length-minimal placement of the labels for
+ -- sites from a to b in the raster slots c to d
+ -- the leftmost/rightmost e sites between a and b are selected until there
+ -- are no more free slots
+ local opt = {}
+ for a = 1, #notes do
+ opt[a] = {}
+ for b = a, #notes do
+ opt[a][b] = {}
+ -- TODO: needed label slots are restricted by points
+ -- only create tables for needed slots
+ for i = 1, totalNumSlots do
+ opt[a][b][i] = {}
+ for j = i, totalNumSlots do
+ if i == j then
+ opt[a][b][i][j] = {}
+ opt[a][b][i][j][0] = {}
+ opt[a][b][i][j][0].totalLength = 0
+ opt[a][b][i][j][0].positions = {}
+ opt[a][b][i][j][0].leaderShiftY = {}
+ end
+ end
+ end
+ end
+ end
+
+ -- constant use an partial solution without labeled points
+ local optEmpty = {}
+ optEmpty.totalLength = 0
+ optEmpty.positions = {}
+ optEmpty.leaderShiftY = {}
+
+ -- fill table opt for dynamic program
+ -- numberOfPoints is difference between topPoint and bottomPoint when computing opt
+ for numberOfPoints = 1, #notes do
+ for topPoint = 1, (#notes - numberOfPoints + 1) do
+ -- compute opt[topStrip][bottomStrip]
+ local bottomPoint = topPoint + numberOfPoints - 1
+
+ local pointsBetween = {}
+ for i = topPoint, bottomPoint do
+ table.insert(pointsBetween, notes[i])
+ end
+ if rightSide then
+ table.sort(pointsBetween, compareNoteIndInputXDesc)
+ else
+ table.sort(pointsBetween, compareNoteIndInputXAsc)
+ end
+
+ -- TODO: Einschränken, nicht alle Kombinationen von Slots notwendig
+ -- (siehe auch oben)
+ -- numberOfSlots is difference between topSlot and bottomSlot when computing opt
+ for numberOfSlots = 1, totalNumSlots do
+ for topSlot = 1, (totalNumSlots - numberOfSlots + 1) do
+ local bottomSlot = topSlot + numberOfSlots - 1
+
+ -- DEBUG
+ --print("computing opt[" .. topPoint .. "][" .. bottomPoint ..
+ --"][" .. topSlot .. "][" .. bottomSlot .. "]")
+
+ opt[topPoint][bottomPoint][topSlot][bottomSlot] = {}
+ opt[topPoint][bottomPoint][topSlot][bottomSlot][0] = {}
+ opt[topPoint][bottomPoint][topSlot][bottomSlot][0].totalLength = 0
+ opt[topPoint][bottomPoint][topSlot][bottomSlot][0].positions = {}
+ opt[topPoint][bottomPoint][topSlot][bottomSlot][0].leaderShiftY = {}
+
+ -- stelle fest, wie viele Punkte gelabelt werden (bestimme also r)
+ local labeledSites = {}
+ local usedSlots = 0
+ for _, v in pairs(pointsBetween) do
+ local note = notesForPage[v]
+ if usedSlots + note.rasterSlots <= numberOfSlots then
+ usedSlots = usedSlots + note.rasterSlots
+ table.insert(labeledSites, v)
+ else
+ break
+ end
+ end
+
+ -- TODO: Teste, ob bei kleinerer Anzahl an Slots gleiche Punkte gelabelt werden
+ -- -> dann kann Teillösung übernommen werden
+
+ -- Mögliche Aufteilungen (Positionierung des Labels für r) testen
+ -- und Optimum auswählen
+ if #labeledSites > 0 then
+ for numLabeledSites = 1, #labeledSites do
+ -- we place rightmost point: labeledSites[#labeledSites]
+ local rIndex = labeledSites[numLabeledSites]
+ local r = notesForPage[rIndex]
+
+ -- slotIndexR is the slot in that the label for r begins (topmost slot)
+ local bestVal = math.huge
+ local bestOpt = {}
+ -- try all label positions for r (leader should enter the label at east-anchor)
+ for slotIndexR = topSlot, (bottomSlot + 1 - r.rasterSlots) do
+ -- calculate position in which leader arm is placed
+ local labelTopR = getRasterAbsolute(rasterHeight, area.top, slotIndexR)
+ local leaderArmR, leaderShiftR
+ if avoidLines then
+ leaderArmR = labelTopR - noteInnerSep - r:getHeight() / 2 -- east anchor
+
+ -- find first line (from the top) which lower bound is below leaderArmR
+ local lineBelowInd
+ for ind, v in pairs(linePositionsCurPage) do
+ if v[3] <= leaderArmR then
+ lineBelowInd = ind
+ break
+ end
+ end
+
+ -- choose the desired position for the leader arm
+ -- (later we check if the label is high enough to shift the port to this position)
+ local desiredPos = leaderArmR
+ if lineBelowInd == nil then
+ -- there is no line below the leaderArmR
+ local lowestLine = linePositionsCurPage[#linePositionsCurPage]
+ if lowestLine ~= nil and lowestLine[3] - leaderArmR < poMinDistLine then
+ -- leader is too near to lowest line on page
+ -- -> use the valid position below this line
+ desiredPos = getPosBelowLine(linePositionsCurPage, #linePositionsCurPage)
+ end
+ else
+ local lineBelow = linePositionsCurPage[lineBelowInd]
+ local lineAbove = linePositionsCurPage[lineBelowInd - 1]
+ if lineBelow ~= nil and leaderArmR - lineBelow[2] < poMinDistLine then
+ -- leader is too near (or conflicting) to the line below
+ -- -> move below or above this line (using the position closer to the original one)
+
+ local posAbove = getPosAboveLine(linePositionsCurPage, lineBelowInd)
+ local posBelow = getPosBelowLine(linePositionsCurPage, lineBelowInd)
+
+ -- chose position which is closer to east anchor
+ if posAbove - leaderArmR <= leaderArmR - posBelow then
+ desiredPos = posAbove
+ else
+ desiredPos = posBelow
+ end
+ elseif lineAbove ~= nil and lineAbove[3] - leaderArmR < poMinDistLine then
+ -- leader is too near to the line below
+ -- -> use the valid position above this line
+ desiredPos = getPosAboveLine(linePositionsCurPage, lineBelowInd)
+ end
+ end
+
+ -- check if label is high enough to move leader to desired position
+ if math.abs(desiredPos - leaderArmR) <= r:getHeight() / 2 + noteInnerSep then
+ leaderShiftR = desiredPos - leaderArmR
+ leaderArmR = desiredPos
+ else
+ leaderShiftR = 0
+ end
+ else
+ leaderArmR = labelTopR - noteInnerSep - r:getHeight() / 2
+ end
+
+ -- determine index of last point above arm
+ local pointAboveArm = 0
+ for k, ind in pairs(notes) do
+ v = notesForPage[ind]
+ if v.inputY >= leaderArmR then
+ pointAboveArm = k
+ else
+ break
+ end
+ end
+ local numPointsAbove = 0
+ local numPointsBelow = 0
+ for _, v in pairs(pointsBetween) do
+ if v == rIndex then
+ break
+ end
+
+ local note = notesForPage[v]
+ if note.inputY >= leaderArmR then
+ numPointsAbove = numPointsAbove + 1
+ else
+ numPointsBelow = numPointsBelow + 1
+ end
+ end
+
+ local optAbove, optBelow
+ if pointAboveArm < topPoint then
+ optAbove = optEmpty
+ elseif slotIndexR - 1 < topSlot then
+ optAbove = optEmpty
+ elseif pointAboveArm > bottomPoint then
+ optAbove = opt[topPoint][bottomPoint][topSlot][slotIndexR - 1][numPointsAbove]
+ else
+ optAbove = opt[topPoint][pointAboveArm][topSlot][slotIndexR - 1][numPointsAbove]
+ end
+ if pointAboveArm + 1 > bottomPoint then
+ optBelow = optEmpty
+ elseif slotIndexR + r.rasterSlots > bottomSlot then
+ optBelow = optEmpty
+ elseif pointAboveArm + 1 < topPoint then
+ optBelow = opt[topPoint][bottomPoint][slotIndexR + r.rasterSlots][bottomSlot][numPointsBelow]
+ else
+ optBelow = opt[pointAboveArm + 1][bottomPoint][slotIndexR + r.rasterSlots][bottomSlot][numPointsBelow]
+ end
+
+ local partitionValid = true
+ if optAbove == nil or optBelow == nil then
+ partitionValid = false
+ else
+ local labeledAboveArm = table.keys(optAbove.positions)
+ local labeledBelowArm = table.keys(optBelow.positions)
+ if #labeledAboveArm + #labeledBelowArm + 1 ~= numLabeledSites then
+ partitionValid = false
+ else
+ -- test if all of labeledNotes are in one of the sets
+ -- last element (= r) must not be tested
+ for testIndex = 1, (numLabeledSites - 1) do
+ if not table.contains(labeledAboveArm, labeledSites[testIndex])
+ and not table.contains(labeledBelowArm, labeledSites[testIndex]) then
+ partitionValid = false
+ end
+ end
+ end
+ end
+
+ if partitionValid then
+ local newVal = math.abs(r.inputY - leaderArmR) + optAbove.totalLength + optBelow.totalLength
+ if newVal < bestVal then
+ bestVal = newVal
+ bestOpt = {}
+ bestOpt.totalLength = newVal
+ bestOpt.positions = {}
+ for k, v in pairs(optAbove.positions) do
+ bestOpt.positions[k] = v
+ end
+ for k, v in pairs(optBelow.positions) do
+ bestOpt.positions[k] = v
+ end
+ -- DEBUG
+ if bestOpt.positions[rIndex] ~= nil then
+ error("WARNING: Overwriting position of " .. rIndex .. " from " .. bestOpt.positions[rIndex] .. " to " .. slotIndexR)
+ end
+ bestOpt.positions[rIndex] = slotIndexR
+ bestOpt.leaderShiftY = {}
+ if avoidLines then
+ for k, v in pairs(optAbove.leaderShiftY) do
+ bestOpt.leaderShiftY[k] = v
+ end
+ for k, v in pairs(optBelow.leaderShiftY) do
+ bestOpt.leaderShiftY[k] = v
+ end
+ bestOpt.leaderShiftY[rIndex] = leaderShiftR
+ end
+ end
+ end
+ end
+ if next(bestOpt) ~= nil then -- bestOpt is not an empty table
+ --print("setting opt[" .. topPoint .. "][" .. bottomPoint ..
+ --"][" .. topSlot .. "][" .. bottomSlot .. "][" .. numLabeledSites .. "] = " .. inspect(bestOpt))
+ opt[topPoint][bottomPoint][topSlot][bottomSlot][numLabeledSites] = bestOpt
+ else
+ --print ("WARNING: Found no valid position for label in opt[" .. topPoint .. "][" .. bottomPoint ..
+ --"][" .. topSlot .. "][" .. bottomSlot .. "][" .. numLabeledSites .. "] = " .. inspect(bestOpt))
+ opt[topPoint][bottomPoint][topSlot][bottomSlot][numLabeledSites] = nil
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ if #notes > 0 then
+ --DEBUG
+ local maxPlaced = 0
+ for i = 1,#notes do
+ if opt[1][#notes][1][totalNumSlots][i] ~= nil then
+ maxPlaced = i
+ end
+ end
+
+ if maxPlaced < #notes then
+ print("WARNING: could not fit all labels on page")
+ end
+
+ print("Using result: opt[1][" .. #notes ..
+ "][1][" .. totalNumSlots .. "][" .. maxPlaced .. "]")
+ local res = opt[1][#notes][1][totalNumSlots][maxPlaced]
+ local length = res.totalLength
+ local positions = res.positions
+ local leaderShiftY = res.leaderShiftY
+
+ print("")
+ print("----------------")
+ print("resulting length: " .. length)
+ print("")
+ print("resulting positions:")
+ print(inspect(positions))
+ print("")
+ print("resulting leaderShifts:")
+ print(inspect(leaderShiftY))
+ print("")
+ print("")
+ print("")
+
+
+ -- DEBUG on page
+ if todonotesDebug then
+ tex.print("\\node[text=blue,fill=white,rectangle,align=center] at (10.5cm,-27cm) {" ..
+ "total length: " .. number.tocentimeters(length) .. "\\\\ " ..
+ "rasterHeight: " .. number.tocentimeters(rasterHeight) ..
+ "};")
+ end
+
+ for _, ind in pairs(notes) do
+ local v = notesForPage[ind]
+ v.outputX = area.left
+ v.leaderShiftX = 0
+ if positions[ind] == nil then
+ print("did not define a position for note " .. v.index)
+ v.outputY = 0
+ v.leaderShiftY = 0
+ else
+ v.outputY = getRasterAbsolute(rasterHeight, area.top, positions[ind])
+ if leaderShiftY[ind] ~= nil then
+ v.leaderShiftY = leaderShiftY[ind]
+ end
+ end
+ end
+ end
+end
+positioningAlgos["poLeaders"] = {algo = posPoLeaders,
+ leaderAnchor = "east",
+ leaderShift = false,
+ twoSided = true}
+
+function posPoLeadersAvoid(notes, rightSide)
+ posPoLeaders(notes, rightSide, true)
+end
+positioningAlgos["poLeadersAvoidLines"] = {algo = posPoLeadersAvoid,
+ leaderAnchor = "east",
+ leaderShift = true,
+ twoSided = true,
+ needLinePositions = true}
+
+
+
+
+-- ********** Splitting Algorithms **********
+
+-- ** splittingAlgorithm: none
+-- place all notes on the wider side
+function splitNone()
+ local rightSideSelected = false
+ if labelArea.left == nil and labelArea.right == nil then
+ error("Cannot place labels on any side of text (not enough space). " ..
+ "Consider using the minNoteWidth option of the package to " ..
+ "decrease the minimum width required")
+ elseif labelArea.left == nil then
+ rightSideSelected = true
+ elseif labelArea.right ~= nil and
+ labelArea.right.noteWidth > labelArea.left.noteWidth then
+ rightSideSelected = true
+ end
+
+ for _, v in pairs(notesForPage) do
+ v.rightSide = rightSideSelected
+ end
+end
+splittingAlgos["none"] = {algo = splitNone}
+
+
+
+-- ** splittingAlgorithm: middle
+-- split on middle of page
+function splitMiddle()
+ if labelArea:isOneSided() then
+ splitNone()
+ else
+ local splitLine = (labelArea.text.right + labelArea.text.left)/2
+ for _, v in pairs(notesForPage) do
+ if v.inputX <= splitLine then
+ v.rightSide = false
+ else
+ v.rightSide = true
+ end
+ end
+ end
+end
+splittingAlgos["middle"] = {algo = splitMiddle}
+
+
+
+-- ** splittingAlgorithm: median
+-- split at median (sites sorted by x-coordinate)
+function splitMedian()
+ if labelArea:isOneSided() then
+ splitNone()
+ else
+ if #notesForPage == 0 then
+ return
+ end
+
+ -- list that contains notes sorted by their inputX-coordinate
+ local notesSorted = {}
+ for _, v in pairs(notesForPage) do
+ table.insert(notesSorted, v)
+ end
+ table.sort(notesSorted, compareNoteInputXAsc)
+
+ local maxIndLeft
+ if #notesSorted % 2 == 1 then
+ maxIndLeft = math.ceil(#notesSorted / 2)
+ else
+ maxIndLeft = #notesSorted / 2
+ end
+
+ for k, v in pairs(notesSorted) do
+ if k <= maxIndLeft then
+ v.rightSide = false
+ else
+ v.rightSide = true
+ end
+ end
+ end
+end
+splittingAlgos["median"] = {algo = splitMedian}
+
+
+
+-- ** splittingAlgorithm: weightedMedian
+-- split at weighted median (sites sorted by x-coordinate)
+-- sum of heights of labels on both sides are similiar to each other
+function splitWeightedMedian()
+ if labelArea:isOneSided() then
+ splitNone()
+ else
+ if #notesForPage == 0 then
+ return
+ end
+
+ -- list that contains notes sorted by their inputX-coordinate
+ local notesSorted = {}
+ for _, v in pairs(notesForPage) do
+ table.insert(notesSorted, v)
+ end
+ table.sort(notesSorted, compareNoteInputXAsc)
+
+ local heightLeft = 0
+ local heightRight = 0
+ while #notesSorted > 0 do
+ if heightRight < heightLeft then
+ -- place next note on the right side
+ local note = notesSorted[#notesSorted]
+ note.rightSide = true
+ heightRight = heightRight + note:getHeight() + 2 * noteInnerSep + noteInterSpace
+ table.remove(notesSorted, #notesSorted)
+ else
+ -- place next note on the left side
+ local note = notesSorted[1]
+ note.rightSide = false
+ heightLeft = heightLeft + note:getHeight() + 2 * noteInnerSep + noteInterSpace
+ table.remove(notesSorted, 1)
+ end
+ end
+ end
+end
+splittingAlgos["weightedMedian"] = {algo = splitWeightedMedian}
+
diff --git a/Master/texmf-dist/tex/lualatex/luatodonotes/luatodonotes.sty b/Master/texmf-dist/tex/lualatex/luatodonotes/luatodonotes.sty
new file mode 100644
index 00000000000..6f7a193ccce
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luatodonotes/luatodonotes.sty
@@ -0,0 +1,634 @@
+%%
+%% This is file `luatodonotes.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% luatodonotes.dtx (with options: `package')
+%%
+%% This is a generated file.
+%%
+%% Copyright (C) 2014 by Fabian Lipp <fabian.lipp@gmx.de>
+%% based on the todonotes package by
+%% Henrik Skov Midtiby <henrikmidtiby@gmail.com>
+%%
+%% This file may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License, either version 1.2 of this license
+%% or (at your option) any later version. The latest version of this
+%% license is in:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.2 or later is part of all distributions of LaTeX version
+%% 1999/12/01 or later.
+%%
+\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+\ProvidesPackage{luatodonotes}
+ [2014/08/07 v0.1 luatodonotes source and documentation.]
+
+\ProvidesPackage{luatodonotes}[2014/06/08]
+\RequirePackage{ifthen}
+\RequirePackage{xkeyval}
+\RequirePackage{xcolor}
+\RequirePackage{tikz}
+\usetikzlibrary{positioning}
+\usetikzlibrary{intersections}
+\usetikzlibrary{decorations.pathmorphing}
+\RequirePackage{luacode}
+\RequirePackage{luatex}
+\RequirePackage{atbegshi}
+\RequirePackage{xstring}
+\RequirePackage{zref-abspage}
+\RequirePackage{ifoddpage}
+\RequirePackage{soul}
+\RequirePackage{soulpos}
+\newcommand{\@todonotes@text}{}%
+\newcommand{\@todonotes@backgroundcolor}{orange}
+\newcommand{\@todonotes@linecolor}{black!30}
+\newcommand{\@todonotes@bordercolor}{black}
+\newcommand{\@todonotes@leaderwidth}{1.6pt}
+\newcommand{\@todonotes@textsize}{\normalsize}
+\newcommand{\@todonotes@figwidth}{\columnwidth}
+\newcommand{\@todonotes@figheight}{4cm}
+\newcommand{\@todonotes@positioning}{inputOrderStacks}
+\newcommand{\@todonotes@splitting}{none}
+\newcommand{\@todonotes@leadertype}{opo}
+\newcommand{\@todonotes@interNoteSpace}{5pt}
+\newcommand{\@todonotes@noteInnerSep}{5pt}
+\newcommand{\@todonotes@routingAreaWidth}{0.4cm}
+\newcommand{\@todonotes@minNoteWidth}{2.0cm}
+\newcommand{\@todonotes@distanceNotesPageBorder}{0.5cm}
+\newcommand{\@todonotes@distanceNotesText}{0.2cm}
+\newcommand{\@todonotes@rasterHeight}{1cm}
+\AtBeginDocument{
+\ifx\undefined\phantomsection
+\newcommand{\phantomsection}{}
+\fi
+}
+\newcommand{\@todonotes@todolistname}{Todo list}
+\newcommand{\@todonotes@MissingFigureText}{Figure}
+\newcommand{\@todonotes@MissingFigureUp}{Missing}
+\newcommand{\@todonotes@MissingFigureDown}{figure}
+\newcommand{\@todonotes@SetTodoListName}[1]
+ {\renewcommand{\@todonotes@todolistname}{#1}}
+\newcommand{\@todonotes@SetMissingFigureText}[1]
+ {\renewcommand{\@todonotes@MissingFigureText}{#1}}
+\newcommand{\@todonotes@SetMissingFigureUp}[1]
+ {\renewcommand{\@todonotes@MissingFigureUp}{#1}}
+\newcommand{\@todonotes@SetMissingFigureDown}[1]
+ {\renewcommand{\@todonotes@MissingFigureDown}{#1}}
+\newif{\if@todonotes@reverseMissingFigureTriangle}
+\DeclareOptionX{catalan}{
+ \@todonotes@SetTodoListName{Llista de feines pendents}%
+ \@todonotes@SetMissingFigureText{Figura}%
+ \@todonotes@SetMissingFigureUp{Figura}%
+ \@todonotes@SetMissingFigureDown{pendent}%
+}
+\DeclareOptionX{danish}{%
+ \@todonotes@SetTodoListName{G\o{}rem\aa{}lsliste}%
+ \@todonotes@SetMissingFigureText{Figur}%
+ \@todonotes@SetMissingFigureUp{Manglende}%
+ \@todonotes@SetMissingFigureDown{figur}%
+}
+\DeclareOptionX{dutch}{%
+ \@todonotes@SetTodoListName{Lijst van onafgewerkte taken}%
+ \@todonotes@SetMissingFigureText{Figuur}%
+ \@todonotes@SetMissingFigureUp{Ontbrekende}%
+ \@todonotes@SetMissingFigureDown{figuur}%
+}
+\DeclareOptionX{english}{%
+ \@todonotes@SetTodoListName{Todo list}%
+ \@todonotes@SetMissingFigureText{Figure}%
+ \@todonotes@SetMissingFigureUp{Missing}%
+ \@todonotes@SetMissingFigureDown{figure}%
+}
+\DeclareOptionX{french}{%
+ \@todonotes@SetTodoListName{Liste des points \`a traiter}%
+ \@todonotes@SetMissingFigureText{Figure}%
+ \@todonotes@SetMissingFigureUp{Figure}%
+ \@todonotes@SetMissingFigureDown{manquante}%
+ \@todonotes@reverseMissingFigureTrianglefalse
+}
+\DeclareOptionX{german}{%
+ \@todonotes@SetTodoListName{Liste der noch zu erledigenden Punkte}%
+ \@todonotes@SetMissingFigureText{Abbildung}%
+ \@todonotes@SetMissingFigureUp{Fehlende}%
+ \@todonotes@SetMissingFigureDown{Abbildung}%
+}
+\DeclareOptionX{italian}{
+ \@todonotes@SetTodoListName{Elenco delle cose da fare}%
+ \@todonotes@SetMissingFigureText{Figura}%
+ \@todonotes@SetMissingFigureUp{Figura}%
+ \@todonotes@SetMissingFigureDown{mancante}%
+}
+\DeclareOptionX{ngerman}{%
+ \@todonotes@SetTodoListName{Liste der noch zu erledigenden Punkte}%
+ \@todonotes@SetMissingFigureText{Abbildung}%
+ \@todonotes@SetMissingFigureUp{Fehlende}%
+ \@todonotes@SetMissingFigureDown{Abbildung}%
+}
+\DeclareOptionX{portuguese}{
+ \@todonotes@SetTodoListName{Lista de tarefas pendentes}%
+ \@todonotes@SetMissingFigureText{Figura}%
+ \@todonotes@SetMissingFigureUp{Figura}%
+ \@todonotes@SetMissingFigureDown{pendente}%
+}
+\DeclareOptionX{spanish}{
+ \@todonotes@SetTodoListName{Lista de tareas pendientes}%
+ \@todonotes@SetMissingFigureText{Figura}%
+ \@todonotes@SetMissingFigureUp{Figura}%
+ \@todonotes@SetMissingFigureDown{pendiente}%
+}
+\DeclareOptionX{swedish}{%
+ \@todonotes@SetTodoListName{Att g\"{o}ra-lista}%
+ \@todonotes@SetMissingFigureText{Figur}%
+ \@todonotes@SetMissingFigureUp{Figur}%
+ \@todonotes@SetMissingFigureDown{saknas}%
+}
+\newcounter{@todonotes@numberoftodonotes}
+\newcounter{@todonotes@numberofLinesInArea}
+\newif{\if@todonotes@obeyDraft}
+\DeclareOptionX{obeyDraft}{\@todonotes@obeyDrafttrue}
+\newif{\if@todonotes@isDraft}
+\DeclareOptionX{draft}{\@todonotes@isDrafttrue}
+\DeclareOptionX{draftcls}{\@todonotes@isDrafttrue}
+\DeclareOptionX{draftclsnofoot}{\@todonotes@isDrafttrue}
+\newif{\if@todonotes@obeyFinal}
+\DeclareOptionX{obeyFinal}{\@todonotes@obeyFinaltrue}
+\newif{\if@todonotes@isFinal}
+\DeclareOptionX{final}{\@todonotes@isFinaltrue}
+\newif{\if@todonotes@disabled}
+\DeclareOptionX{disable}{\@todonotes@disabledtrue}
+\newif{\if@todonotes@colorinlistoftodos}
+\DeclareOptionX{colorinlistoftodos}{\@todonotes@colorinlistoftodostrue}
+\DeclareOptionX{dvistyle}{\PackageWarningNoLine{luatodonotes}
+ {Parameter dvistyle is not supported by luatodonotes.
+ Ignoring this option}}
+\define@key{luatodonotes.sty}%
+ {color}{
+ \renewcommand{\@todonotes@backgroundcolor}{#1}
+ \renewcommand{\@todonotes@linecolor}{#1}}
+\define@key{luatodonotes.sty}%
+ {backgroundcolor}{\renewcommand{\@todonotes@backgroundcolor}{#1}}
+\define@key{luatodonotes.sty}%
+ {linecolor}{\renewcommand{\@todonotes@linecolor}{#1}}
+\define@key{luatodonotes.sty}%
+ {bordercolor}{\renewcommand{\@todonotes@bordercolor}{#1}}
+\define@key{luatodonotes.sty}%
+ {leaderwidth}{\renewcommand{\@todonotes@leaderwidth}{#1}}
+\newif{\if@todonotes@prependcaptionglobal}
+\@todonotes@prependcaptionglobalfalse
+\DeclareOptionX{prependcaption}{\@todonotes@prependcaptionglobaltrue}
+\define@key{luatodonotes.sty}%
+ {textwidth}{\PackageWarningNoLine{luatodonotes}
+ {Parameter textwidth is not supported by luatodonotes}}
+\define@key{luatodonotes.sty}%
+ {textsize}{\renewcommand{\@todonotes@textsize}{\csname #1\endcsname}}
+\newif{\if@todonotes@shadowenabled}
+\@todonotes@shadowenabledfalse
+\DeclareOptionX{shadow}{\@todonotes@shadowenabledtrue
+\usetikzlibrary{shadows}}
+\define@key{luatodonotes.sty}%
+ {figwidth}{\renewcommand{\@todonotes@figwidth}{#1}}
+\define@key{luatodonotes.sty}%
+ {figheight}{\renewcommand{\@todonotes@figheight}{#1}}
+\define@key{luatodonotes.sty}%
+ {positioning}{\renewcommand{\@todonotes@positioning}{#1}}
+\define@key{luatodonotes.sty}%
+ {splitting}{\renewcommand{\@todonotes@splitting}{#1}}
+\define@key{luatodonotes.sty}%
+ {leadertype}{\renewcommand{\@todonotes@leadertype}{#1}}
+\define@key{luatodonotes.sty}%
+ {interNoteSpace}{\renewcommand{\@todonotes@interNoteSpace}{#1}}
+\define@key{luatodonotes.sty}%
+ {noteInnerSep}{\renewcommand{\@todonotes@noteInnerSep}{#1}}
+\define@key{luatodonotes.sty}%
+ {routingAreaWidth}{\renewcommand{\@todonotes@routingAreaWidth}{#1}}
+\define@key{luatodonotes.sty}%
+ {minNoteWidth}{\renewcommand{\@todonotes@minNoteWidth}{#1}}
+\define@key{luatodonotes.sty}%
+ {distanceNotesPageBorder}%
+ {\renewcommand{\@todonotes@distanceNotesPageBorder}{#1}}
+\define@key{luatodonotes.sty}%
+ {distanceNotesText}{\renewcommand{\@todonotes@distanceNotesText}{#1}}
+\define@key{luatodonotes.sty}%
+ {rasterHeight}{\renewcommand{\@todonotes@rasterHeight}{#1}}
+\newif{\if@todonotes@debugenabled}
+\@todonotes@debugenabledfalse
+\DeclareOptionX{debug}{\@todonotes@debugenabledtrue}
+\ProcessOptionsX*
+\if@todonotes@disabled
+\else
+ \if@todonotes@obeyDraft
+ \@todonotes@disabledtrue
+ \if@todonotes@isDraft
+ \@todonotes@disabledfalse
+ \fi
+ \fi
+ \if@todonotes@obeyFinal
+ \@todonotes@disabledfalse
+ \if@todonotes@isFinal
+ \@todonotes@disabledtrue
+ \fi
+ \fi
+\fi
+\newdimen\@todonotes@extractx
+\newdimen\@todonotes@extracty
+\newsavebox\@todonotes@heightcalcbox
+\newdimen\@todonotes@heightcalcboxdepth
+\newdimen\@todonotes@heightcalcboxheight
+\newsavebox\@todonotes@notetextbox
+\newdimen\@todonotes@baselineskip
+\newdimen\@todonotes@fontsize
+\newdimen\@todonotes@currentsidemargin
+\directlua{require("luatodonotes")}
+\directlua{noteInnerSep =
+ string.todimen("\luatexluaescapestring{\@todonotes@noteInnerSep}")}
+\directlua{noteInterSpace =
+ string.todimen("\luatexluaescapestring{\@todonotes@interNoteSpace}")}
+\directlua{routingAreaWidth =
+ string.todimen("\luatexluaescapestring{\@todonotes@routingAreaWidth}")}
+\directlua{minNoteWidth =
+ string.todimen("\luatexluaescapestring{\@todonotes@minNoteWidth}")}
+\directlua{distanceNotesPageBorder =
+ string.todimen("\luatexluaescapestring{\@todonotes@distanceNotesPageBorder}")}
+\directlua{distanceNotesText =
+ string.todimen("\luatexluaescapestring{\@todonotes@distanceNotesText}")}
+\directlua{rasterHeight =
+ string.todimen("\luatexluaescapestring{\@todonotes@rasterHeight}")}
+\IfStrEqCase{\@todonotes@positioning}{%
+ {inText}{\directlua{positioning = positioningAlgos["inText"]}}%
+ {inputOrder}{\directlua{positioning = positioningAlgos["inputOrder"]}}%
+ {inputOrderStacks}{\directlua{positioning =
+ positioningAlgos["inputOrderStacks"]}}%
+ {sLeaderNorthEast}{\directlua{positioning =
+ positioningAlgos["sLeaderNorthEast"]}}%
+ {sLeaderNorthEastBelow}{\directlua{positioning =
+ positioningAlgos["sLeaderNorthEastBelow"]}}%
+ {sLeaderEast}{\directlua{positioning =
+ positioningAlgos["sLeaderEast"]}}%
+ {poLeaders}{\directlua{positioning = positioningAlgos["poLeaders"]}}%
+ {poLeadersAvoidLines}{\directlua{positioning =
+ positioningAlgos["poLeadersAvoidLines"]}}%
+ {sLeaderNorthEastBelowStacks}{\directlua{positioning =
+ positioningAlgos["sLeaderNorthEastBelowStacks"]}}}%
+ [\directlua{positioning = positioningAlgos["inputOrderStacks"]}
+ \PackageWarningNoLine{luatodonotes}
+ {Invalid value for parameter positioning: \@todonotes@positioning}]
+\IfStrEqCase{\@todonotes@splitting}{%
+ {none}{\directlua{splitting = splittingAlgos["none"]}}%
+ {middle}{\directlua{splitting = splittingAlgos["middle"]}}%
+ {median}{\directlua{splitting = splittingAlgos["median"]}}%
+ {weightedMedian}{\directlua{splitting = splittingAlgos["weightedMedian"]}}}%
+ [\directlua{splitting = splittingAlgos["none"]}
+ \PackageWarningNoLine{luatodonotes}
+ {Invalid value for parameter split: \@todonotes@splitting}]
+\IfStrEqCase{\@todonotes@leadertype}{%
+ {s}{\directlua{leaderType = leaderTypes["s"]}}%
+ {opo}{\directlua{leaderType = leaderTypes["opo"]}}%
+ {po}{\directlua{leaderType = leaderTypes["po"]}}%
+ {sBezier}{\directlua{leaderType = leaderTypes["sBezier"]}}%
+ {os}{\directlua{leaderType = leaderTypes["os"]}}}%
+ [\directlua{leaderType = leaderTypes["opo"]}
+ \PackageWarningNoLine{luatodonotes}
+ {Invalid value for parameter leadertype: \@todonotes@leadertype}]
+\def\@todonotes@pdflastypos{\the\pdflastypos}
+\newcommand{\@todonotes@lineposition}[3]{%
+ \directlua{linePositionsAddLine(#1,#2,#3)}%
+}
+\newcommand{\@todonotes@nextpage}{%
+ \directlua{linePositionsNextPage()}%
+}%
+\newcommand{\@todonotes@writeNextpageToLpo}{%
+ \ifdefined\tf@lpo%
+ \immediate\write\tf@lpo{\@backslashchar @todonotes@nextpage}%
+ \fi
+}
+\if@todonotes@debugenabled
+ \directlua{todonotesDebug = true}
+ \newcommand{\@todonotes@AtBeginShipoutUpperLeft}
+ {\AtBeginShipoutUpperLeftForeground}
+\else
+ \directlua{todonotesDebug = false}
+ \newcommand{\@todonotes@AtBeginShipoutUpperLeft}
+ {\AtBeginShipoutUpperLeft}
+\fi
+\directlua{initTodonotes()}
+\soulregister{\ }{0}
+\newlength{\todonotes@textmark@width}
+\newlength{\todonotes@textmark@fontsize}
+\newlength{\todonotes@textmark@linebelow}
+\newlength{\todonotes@textmark@lineabove}
+\ulposdef{\todonotes@textmark@highlight}{%
+ \setlength\todonotes@textmark@width\ulwidth%
+ \setlength\todonotes@textmark@fontsize{\f@size pt}%
+ \stepcounter{@todonotes@numberofLinesInArea}%
+ \ifulstarttype{0}%
+ {% begin of area
+ \def\todonotes@textmark@decoLeft{}%
+ \def\todonotes@textmark@shift{-2pt}%
+ \addtolength\todonotes@textmark@width{2pt}%
+ \setcounter{@todonotes@numberofLinesInArea}{1}}%
+ {\def\todonotes@textmark@decoLeft{@todonotes@todoarea}%
+ \def\todonotes@textmark@shift{-4pt}%
+ \addtolength\todonotes@textmark@width{4pt}}%
+ \ifulendtype{0}%
+ {% last line of area
+ \def\todonotes@textmark@decoRight{}%
+ \addtolength\todonotes@textmark@width{2pt}%
+ \directlua{processLastLineInTodoArea()}}%
+ {\def\todonotes@textmark@decoRight{@todonotes@todoarea}%
+ \addtolength\todonotes@textmark@width{4pt}}%
+ \newcommand{\@todonotes@nodeNamePrefix}%
+ {@todonotes@\arabic{@todonotes@numberoftodonotes}%
+ @\arabic{@todonotes@numberofLinesInArea} }%
+ \hspace*{\todonotes@textmark@shift}{\smash{%
+ \begin{tikzpicture}[overlay,remember picture,
+ deco/.style={}]%
+ \setlength\todonotes@textmark@linebelow%
+ {-0.95\dimexpr\baselineskip-\f@size pt\relax}%
+ \setlength\todonotes@textmark@lineabove%
+ {\dimexpr\f@size pt+\todonotes@textmark@linebelow\relax}%
+ \coordinate
+ (\@todonotes@nodeNamePrefix areaSW)
+ at (0,\todonotes@textmark@linebelow);
+ \coordinate
+ (\@todonotes@nodeNamePrefix areaSE)
+ at (\todonotes@textmark@width, \todonotes@textmark@linebelow);
+ \coordinate
+ (\@todonotes@nodeNamePrefix areaNE)
+ at (\todonotes@textmark@width,\todonotes@textmark@lineabove);
+ \coordinate
+ (\@todonotes@nodeNamePrefix areaNW)
+ at (0,\todonotes@textmark@lineabove);
+ \draw[draw=green!70,fill=green,fill opacity=.2]
+ (\@todonotes@nodeNamePrefix areaSW)
+ decorate[\todonotes@textmark@decoLeft] {
+ -- (\@todonotes@nodeNamePrefix areaNW)
+ }
+ -- (\@todonotes@nodeNamePrefix areaNE)
+ decorate[\todonotes@textmark@decoRight] {
+ -- (\@todonotes@nodeNamePrefix areaSE)
+ }
+ -- cycle;
+ \end{tikzpicture}%
+ }}%
+}%
+\newcommand{\@todonotes@currentlinecolor}{}%
+\newcommand{\@todonotes@currentbackgroundcolor}{}%
+\newcommand{\@todonotes@currentbordercolor}{}%
+\define@key{todonotes}{color}{%
+ \renewcommand{\@todonotes@currentlinecolor}{#1}%
+ \renewcommand{\@todonotes@currentbackgroundcolor}{#1}}%
+\define@key{todonotes}{linecolor}{%
+ \renewcommand{\@todonotes@currentlinecolor}{#1}}%
+\define@key{todonotes}{backgroundcolor}{%
+ \renewcommand{\@todonotes@currentbackgroundcolor}{#1}}%
+\define@key{todonotes}{bordercolor}{%
+ \renewcommand{\@todonotes@currentbordercolor}{#1}}%
+\newcommand{\@todonotes@currentleaderwidth}{}%
+\define@key{todonotes}{leaderwidth}{%
+ \renewcommand{\@todonotes@currentleaderwidth}{#1}}%
+\newcommand{\@todonotes@sizecommand}{}%
+\define@key{todonotes}{size}{\renewcommand{\@todonotes@sizecommand}{#1}}%
+\newif\if@todonotes@localdisable%
+\define@key{todonotes}{disable}[]{\@todonotes@localdisabletrue}%
+\define@key{todonotes}{nodisable}[]{\@todonotes@localdisablefalse}%
+\newif\if@todonotes@appendtolistoftodos%
+\define@key{todonotes}{list}[]{\@todonotes@appendtolistoftodostrue}%
+\define@key{todonotes}{nolist}[]{\@todonotes@appendtolistoftodosfalse}%
+\newif\if@todonotes@inlinenote%
+\define@key{todonotes}{inline}[]{\@todonotes@inlinenotetrue}%
+\define@key{todonotes}{noinline}[]{\@todonotes@inlinenotefalse}%
+\newif\if@todonotes@prependcaption%
+\define@key{todonotes}{prepend}[]{\@todonotes@prependcaptiontrue}%
+\define@key{todonotes}{noprepend}[]{\@todonotes@prependcaptionfalse}%
+\newif\if@todonotes@line%
+\define@key{todonotes}{line}[]{\@todonotes@linetrue}%
+\define@key{todonotes}{noline}[]{\@todonotes@linefalse}%
+\define@key{todonotes}{fancyline}[]{\PackageWarningNoLine{luatodonotes}
+ {Parameter fancyline is not supported by luatodonotes}}%
+\define@key{todonotes}{nofancyline}[]{}%
+\newcommand{\@todonotes@author}{}%
+\newif\if@todonotes@authorgiven%
+\define@key{todonotes}{author}{%
+ \renewcommand{\@todonotes@author}{#1}%
+ \@todonotes@authorgiventrue}%
+\define@key{todonotes}{noauthor}[]{\@todonotes@authorgivenfalse}%
+\newcommand{\@todonotes@caption}{}%
+\newif\if@todonotes@captiongiven%
+\define@key{todonotes}{caption}%
+ {\renewcommand{\@todonotes@caption}{#1}%
+ \@todonotes@captiongiventrue}%
+\define@key{todonotes}{nocaption}[]{\@todonotes@captiongivenfalse}%
+\newcommand{\@todonotes@currentfigwidth}{\@todonotes@figwidth}
+\define@key{todonotes}%
+ {figwidth}{\renewcommand{\@todonotes@currentfigwidth}{#1}}
+\newcommand{\@todonotes@currentfigheight}{\@todonotes@figheight}
+\define@key{todonotes}%
+ {figheight}{\renewcommand{\@todonotes@currentfigheight}{#1}}
+\presetkeys%
+ {todonotes}%
+ {linecolor=\@todonotes@linecolor,%
+ backgroundcolor=\@todonotes@backgroundcolor,%
+ bordercolor=\@todonotes@bordercolor,%
+ leaderwidth=\@todonotes@leaderwidth,%
+ nodisable,%
+ noinline,%
+ nocaption,%
+ noauthor,%
+ figwidth=\@todonotes@figwidth,%
+ figheight=\@todonotes@figheight,%
+ line, list, size=\@todonotes@textsize}{}%
+\newif\if@todonotes@areaselected%
+\newtoks\@todonotes@toks@currentlinecolor%
+\newtoks\@todonotes@toks@currentbackgroundcolor%
+\newtoks\@todonotes@toks@currentbordercolor%
+\newtoks\@todonotes@toks@currentleaderwidth%
+\newtoks\@todonotes@toks@sizecommand%
+\if@todonotes@disabled%
+ \newcommand{\listoftodos}[1][]{}
+ \newcommand{\@todo}[2][]{}
+ \newcommand{\@todoarea}[3][]{}
+ \newcommand{\missingfigure}[2][]{}
+\else % \if@todonotes@disabled
+\newcommand{\listoftodos}[1][\@todonotes@todolistname]
+ {\@ifundefined{chapter}{\section*{#1}}{\chapter*{#1}} \@starttoc{tdo}}
+\newcommand{\l@todo}
+ {\@dottedtocline{1}{0em}{2.3em}}
+\tikzset{@todonotes@todoarea/.style={
+ decoration={snake,amplitude=3.5pt,segment length=5pt}}}
+\tikzset{@todonotes@notestyleraw/.style={
+ line width=0.5pt,
+ inner sep = \@todonotes@noteInnerSep,
+ rounded corners=4pt}}
+\if@todonotes@shadowenabled
+ \tikzset{@todonotes@notestyle/.style={@todonotes@notestyleraw,
+ general shadow={shadow xshift=.5ex, shadow yshift=-.5ex,
+ opacity=1,fill=black!50}}}
+\else
+ \tikzset{@todonotes@notestyle/.style={@todonotes@notestyleraw}}
+\fi
+\tikzset{@todonotes@leader/.style={}}
+\tikzset{@todonotes@textmark/.style={rounded corners}}
+\tikzset{@todonotes@inlinenote/.style={
+ @todonotes@notestyle,
+ draw=\@todonotes@currentbordercolor,
+ fill=\@todonotes@currentbackgroundcolor,
+ text width=\linewidth - 1.6 ex - 1 pt}}
+\newcommand{\@todocommon}[2]{%
+\if@todonotes@prependcaptionglobal%
+\@todonotes@prependcaptiontrue%
+\else%
+\@todonotes@prependcaptionfalse%
+\fi%
+\renewcommand{\@todonotes@text}{#2}%
+\renewcommand{\@todonotes@caption}{#2}%
+\setkeys{todonotes}{#1}%
+\if@todonotes@localdisable%
+\else%
+\addtocounter{@todonotes@numberoftodonotes}{1}%
+\if@todonotes@appendtolistoftodos%
+ \phantomsection%
+ \if@todonotes@captiongiven%
+ \else%
+ \renewcommand{\@todonotes@caption}{#2}%
+ \fi%
+ \@todonotes@addElementToListOfTodos%
+\fi%
+\if@todonotes@captiongiven%
+ \if@todonotes@prependcaption%
+ \renewcommand{\@todonotes@text}{\@todonotes@caption: #2}%
+ \fi%
+\fi%
+\if@todonotes@inlinenote%
+ \@todonotes@drawInlineNote%
+\else%
+ \@todonotes@drawMarginNoteWithLine%
+\fi%\if@todonotes@inlinenote
+\fi%\if@todonotes@localdisable
+}%
+\newcommand{\@todo}[2][]{%
+ \@todonotes@areaselectedfalse%
+ \@todocommon{#1}{#2}%
+}%
+\newcommand{\@todoarea}[3][]{%
+ \@todonotes@areaselectedtrue%
+ \@todocommon{#1}{#2}%
+ \todonotes@textmark@highlight{#3}%
+ \begin{tikzpicture}[remember picture, overlay]%
+ \node [coordinate] (@todonotes@\arabic{@todonotes@numberoftodonotes} %
+ inTextEnd) {};%
+ \end{tikzpicture}%
+ \zref@label{@todonotes@\arabic{@todonotes@numberoftodonotes}@end}%
+}%
+\newcommand{\@todonotes@drawMarginNoteWithLine}{%
+ \begin{tikzpicture}[remember picture, overlay]%
+ \node [coordinate] (@todonotes@\arabic{@todonotes@numberoftodonotes} %
+ inText) {};%
+ \end{tikzpicture}%
+ \@todonotes@baselineskip=\baselineskip%
+ \@todonotes@fontsize=\f@size pt%
+ \zref@label{@todonotes@\arabic{@todonotes@numberoftodonotes}}%
+ \if@todonotes@authorgiven%
+ \let\@todonotes@text@old=\@todonotes@text
+ \renewcommand{\@todonotes@text}{\@todonotes@author: \@todonotes@text@old}%
+ \fi%
+ \edef\@todonotes@tmp{\@todonotes@currentlinecolor}%
+ \@todonotes@toks@currentlinecolor=\expandafter{\@todonotes@tmp}%
+ \edef\@todonotes@tmp{\@todonotes@currentbackgroundcolor}%
+ \@todonotes@toks@currentbackgroundcolor=\expandafter{\@todonotes@tmp}%
+ \edef\@todonotes@tmp{\@todonotes@currentbordercolor}%
+ \@todonotes@toks@currentbordercolor=\expandafter{\@todonotes@tmp}%
+ \edef\@todonotes@tmp{\@todonotes@currentleaderwidth}%
+ \@todonotes@toks@currentleaderwidth=\expandafter{\@todonotes@tmp}%
+ \@todonotes@toks@sizecommand=\expandafter{\@todonotes@sizecommand}%
+ \savebox\@todonotes@notetextbox{\@todonotes@sizecommand\@todonotes@text}%
+ \if@todonotes@line%
+ \def\@todonotes@param@drawLeader{true}%
+ \else%
+ \def\@todonotes@param@drawLeader{false}%
+ \fi%
+ \if@todonotes@areaselected%
+ \def\@todonotes@param@noteType{area}%
+ \else%
+ \def\@todonotes@param@noteType{}%
+ \fi%
+ \directlua{addNoteToList(\arabic{@todonotes@numberoftodonotes},%
+ \@todonotes@param@drawLeader,\luastringO{\@todonotes@param@noteType})}%
+}%
+\newcommand{\@todonotes@addElementToListOfTodos}{%
+ \if@todonotes@colorinlistoftodos%
+ \addcontentsline{tdo}{todo}{%
+ \fcolorbox{\@todonotes@currentbordercolor}%
+ {\@todonotes@currentbackgroundcolor}%
+ {\textcolor{\@todonotes@currentbackgroundcolor}{o}}%
+ \ \@todonotes@caption}%
+ \else%
+ \addcontentsline{tdo}{todo}{\@todonotes@caption}%
+ \fi}%
+\newcommand{\@todonotes@drawInlineNote}{%
+ {\par\noindent\begin{tikzpicture}[remember picture]%
+ \draw node[@todonotes@inlinenote,font=\@todonotes@sizecommand]{%
+ \if@todonotes@authorgiven%
+ {\noindent \@todonotes@sizecommand %
+ \@todonotes@author:\,\@todonotes@text}%
+ \else%
+ {\noindent \@todonotes@sizecommand \@todonotes@text}%
+ \fi};%
+ \end{tikzpicture}\par}%
+ }%
+\newcommand{\missingfigure}[2][]{%
+\setkeys{todonotes}{#1}%
+\addcontentsline{tdo}{todo}{\@todonotes@MissingFigureText: #2}%
+\par
+\noindent
+\begin{tikzpicture}
+\draw[fill=black!40, draw = white, line width=0pt]
+ (-2, -2.5) rectangle +(\@todonotes@currentfigwidth, \@todonotes@currentfigheight);
+\draw (2, -0.3) node[right, text
+ width=\@todonotes@currentfigwidth-4.5cm] {#2};
+\draw[red, fill=white, rounded corners = 5pt, line width=10pt]
+ (30:2cm) -- (150:2cm) -- (270:2cm) -- cycle;
+\draw (0, 0.3) node {\@todonotes@MissingFigureUp};
+\draw (0, -0.3) node {\@todonotes@MissingFigureDown};
+\end{tikzpicture}
+}% Ending \missingfigure command
+\fi % Ending \@todonotes@ifdisabled
+\newcommand{\todototoc}
+{
+ \if@todonotes@disabled
+ \else
+ \addcontentsline{toc}{\@ifundefined{chapter}{section}{chapter}}{\@todonotes@todolistname}
+ \fi
+}
+\newcommand{\todo}[2][]{\@bsphack\@todo[#1]{#2}\@esphack\ignorespaces}%
+\newcommand{\todoarea}[3][]{\@bsphack\@todoarea[#1]{#2}{#3}\@esphack}%
+\if@todonotes@disabled
+\else
+\AtBeginShipout{%
+ \@todonotes@AtBeginShipoutUpperLeft{
+ \@todonotes@writeNextpageToLpo
+ \checkoddpage%
+ \ifoddpageoroneside%
+ \@todonotes@currentsidemargin=\the\oddsidemargin%
+ \directlua{currentPageOdd = true}%
+ \else%
+ \@todonotes@currentsidemargin=\the\evensidemargin%
+ \directlua{currentPageOdd = false}%
+ \fi\relax%
+ \BeginCatcodeRegime\CatcodeTableLaTeX
+ \directlua{calcLabelAreaDimensions()}%
+ \directlua{calcHeightsForNotes()}% has to be outside of tikzpicture
+ \begin{tikzpicture}[remember picture,overlay]
+ \directlua{getInputCoordinatesForNotes()}
+ \directlua{printNotes()}
+ \end{tikzpicture}%
+ \directlua{clearNotes()}%
+ \EndCatcodeRegime
+ }%
+}
+\fi % Ending \@todonotes@ifdisabled
+\endinput
+%%
+%% End of file `luatodonotes.sty'.
diff --git a/Master/texmf-dist/tex/lualatex/luatodonotes/path_line.lua b/Master/texmf-dist/tex/lualatex/luatodonotes/path_line.lua
new file mode 100644
index 00000000000..e365a36bf48
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luatodonotes/path_line.lua
@@ -0,0 +1,120 @@
+-- Taken from luapower.com (Public domain)
+--
+--math for 2D line segments defined as (x1, y1, x2, y2).
+
+local abs, min, max = math.abs, math.min, math.max
+
+local distance = require'path_point'.distance
+local distance2 = require'path_point'.distance2
+
+--evaluate a line at time t using linear interpolation.
+--the time between 0..1 covers the segment interval.
+local function point(t, x1, y1, x2, y2)
+ return x1 + t * (x2 - x1), y1 + t * (y2 - y1)
+end
+
+--length of line at time t.
+local function length(t, x1, y1, x2, y2)
+ return t * distance(x1, y1, x2, y2)
+end
+
+--bounding box of line in (x,y,w,h) form.
+local function bounding_box(x1, y1, x2, y2)
+ if x1 > x2 then x1, x2 = x2, x1 end
+ if y1 > y2 then y1, y2 = y2, y1 end
+ return x1, y1, x2-x1, y2-y1
+end
+
+--split line segment into two line segments at time t (t is capped between 0..1).
+local function split(t, x1, y1, x3, y3)
+ t = min(max(t,0),1)
+ local x2, y2 = point(t, x1, y1, x3, y3)
+ return
+ x1, y1, x2, y2, --first segment
+ x2, y2, x3, y3 --second segment
+end
+
+--intersect infinite line with its perpendicular from point (x, y); return the intersection point.
+local function point_line_intersection(x, y, x1, y1, x2, y2)
+ local dx = x2 - x1
+ local dy = y2 - y1
+ local k = dx^2 + dy^2
+ if k == 0 then return x1, y1 end --line has no length
+ local k = ((x - x1) * dy - (y - y1) * dx) / k
+ return x - k * dy, y + k * dx
+end
+
+--return shortest distance-squared from point (x0, y0) to line, plus the touch point, and the time in the line
+--where the touch point splits the line.
+local function hit(x0, y0, x1, y1, x2, y2)
+ local x, y = point_line_intersection(x0, y0, x1, y1, x2, y2)
+ local tx = x2 == x1 and 0 or (x - x1) / (x2 - x1)
+ local ty = y2 == y1 and 0 or (y - y1) / (y2 - y1)
+ if tx < 0 or ty < 0 then --intersection occurs outside the segment, closer to the first endpoint
+ return distance2(x0, y0, x1, y1), x1, y1, 0
+ elseif tx > 1 or ty > 1 then --intersection occurs outside the segment, closer to the second endpoint
+ return distance2(x0, y0, x2, y2), x2, y2, 1
+ end
+ return distance2(x0, y0, x, y), x, y, max(tx, ty)
+end
+
+--intersect line segment (x1, y1, x2, y2) with line segment (x3, y3, x4, y4).
+--returns the time on the first line and the time on the second line where intersection occurs.
+--if the intersection occurs outside the segments themselves, then t1 and t2 are outside the 0..1 range.
+--if the lines are parallel or coincidental then t1 and t2 are infinite.
+local function line_line_intersection(x1, y1, x2, y2, x3, y3, x4, y4)
+ local d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
+ if d == 0 then return 1/0, 1/0 end --lines are parallel or coincidental
+ return
+ ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / d,
+ ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / d
+end
+
+--transform to a quad bezier that advances linearly i.e. the point on the line at t
+--best matches the point on the curve at t.
+local function to_bezier2(x1, y1, x2, y2)
+ return
+ x1, y1,
+ (x1 + x2) / 2,
+ (y1 + y2) / 2,
+ x2, y2
+end
+
+--transform to a cubic bezier that advances linearly i.e. the point on the line at t
+--best matches the point on the curve at t.
+local function to_bezier3(x1, y1, x2, y2)
+ return
+ x1, y1,
+ (2 * x1 + x2) / 3,
+ (2 * y1 + y2) / 3,
+ (x1 + 2 * x2) / 3,
+ (y1 + 2 * y2) / 3,
+ x2, y2
+end
+
+--parallel line segment at a distance on the right side of a segment.
+--use a negative distance for the left side, or reflect the returned points against their respective initial points
+local function offset(d, x1, y1, x2, y2)
+ local dx, dy = -(y2-y1), x2-x1 --normal vector of the same length as original segment
+ local k = d / distance(x1, y1, x2, y2) --normal vector scale factor
+ return --normal vector scaled and translated to (x1,y1) and (x2,y2)
+ x1 + dx * k, y1 + dy * k,
+ x2 + dx * k, y2 + dy * k
+end
+
+if not ... then require'path_line_demo' end
+
+return {
+ point_line_intersection = point_line_intersection,
+ line_line_intersection = line_line_intersection,
+ to_bezier2 = to_bezier2,
+ to_bezier3 = to_bezier3,
+ offset = offset,
+ --path API
+ bounding_box = bounding_box,
+ point = point,
+ length = length,
+ split = split,
+ hit = hit,
+}
+
diff --git a/Master/texmf-dist/tex/lualatex/luatodonotes/path_point.lua b/Master/texmf-dist/tex/lualatex/luatodonotes/path_point.lua
new file mode 100644
index 00000000000..1144e6de093
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luatodonotes/path_point.lua
@@ -0,0 +1,73 @@
+-- Taken from luapower.com (Public domain)
+
+--basic math for the cartesian plane.
+--angles are expressed in degrees, not radians.
+
+local sqrt, abs, min, max, sin, cos, radians, degrees, atan2 =
+ math.sqrt, math.abs, math.min, math.max, math.sin, math.cos, math.rad, math.deg, math.atan2
+
+--hypotenuse function: computes sqrt(a^2 + b^2) without underflow / overflow problems.
+local function hypot(a, b)
+ if a == 0 and b == 0 then return 0 end
+ a, b = abs(a), abs(b)
+ a, b = max(a,b), min(a,b)
+ return a * sqrt(1 + (b / a)^2)
+end
+
+--distance between two points. avoids underflow and overflow.
+local function distance(x1, y1, x2, y2)
+ return hypot(x2-x1, y2-y1)
+end
+
+--distance between two points squared.
+local function distance2(x1, y1, x2, y2)
+ return (x2-x1)^2 + (y2-y1)^2
+end
+
+--point at a specified angle on a circle.
+local function point_around(cx, cy, r, angle)
+ angle = radians(angle)
+ return
+ cx + cos(angle) * r,
+ cy + sin(angle) * r
+end
+
+--rotate point (x,y) around origin (cx,cy) by angle.
+local function rotate_point(x, y, cx, cy, angle)
+ if angle == 0 then return x, y end
+ angle = radians(angle)
+ x, y = x-cx, y-cy
+ local c, s = cos(angle), sin(angle)
+ return cx + x*c - y*s, cy + y*c + x*s
+end
+
+--angle between two points in -360..360 degree range.
+local function point_angle(x, y, cx, cy)
+ return degrees(atan2(y - cy, x - cx))
+end
+
+--reflect point through origin (i.e. rotate point 180deg around another point).
+local function reflect_point(x, y, cx, cy)
+ return 2 * cx - x, 2 * cy - y
+end
+
+--reflect point through origin at a specified distance.
+local function reflect_point_distance(x, y, cx, cy, length)
+ local d = distance(x, y, cx, cy)
+ if d == 0 then return cx, cy end
+ local scale = length / d
+ return
+ cx + (cx - x) * scale,
+ cy + (cy - y) * scale
+end
+
+return {
+ hypot = hypot,
+ distance = distance,
+ distance2 = distance2,
+ point_around = point_around,
+ rotate_point = rotate_point,
+ point_angle = point_angle,
+ reflect_point = reflect_point,
+ reflect_point_distance = reflect_point_distance,
+}
diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check
index dccdf988b7e..0b588ce947d 100755
--- a/Master/tlpkg/bin/tlpkg-ctan-check
+++ b/Master/tlpkg/bin/tlpkg-ctan-check
@@ -308,7 +308,7 @@ my @TLP_working = qw(
luaindex luainputenc luaintro lualatex-doc lualatex-doc-de
lualatex-math lualibs
luamplib luaotfload
- luasseq luatexbase luatexja luatexko luatextra luaxml
+ luasseq luatexbase luatexja luatexko luatextra luatodonotes luaxml
lxfonts ly1
m-tx macros2e macroswap mafr magaz mailing mailmerge
makebarcode makebox makecell makecirc makecmds makedtx makeglos makeplot
diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds
index a6e9d016000..219f47f6238 100755
--- a/Master/tlpkg/libexec/ctan2tds
+++ b/Master/tlpkg/libexec/ctan2tds
@@ -1497,6 +1497,7 @@ $standardtex='\.(.bx|cfg|sty|clo|ldf|cls|def|fd|cmap|4ht)$';
'luaintro', 'NULL', # doc pkg
'lualatex-math', '\.sty', # not phst-doc.cls
'luatexko', '\.lua|' . $standardtex,
+ 'luatodonotes','\.lua|' . $standardtex,
'luaxml', '\.lua|' . $standardtex,
'magyar', 'NULL', # do not install in runtime, conflicts with babel
'manjutex', '\.sty|\.clo|\.ldf|\.cls|\.def|\.fd$|manju.tex',
diff --git a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
index 88a92f004a7..118a5807890 100644
--- a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
+++ b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
@@ -27,6 +27,7 @@ depend luasseq
depend luatexbase
depend luatexko
depend luatextra
+depend luatodonotes
depend luaxml
depend odsfile
depend placeat
diff --git a/Master/tlpkg/tlpsrc/luatodonotes.tlpsrc b/Master/tlpkg/tlpsrc/luatodonotes.tlpsrc
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/Master/tlpkg/tlpsrc/luatodonotes.tlpsrc