summaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2023-08-10 03:01:21 +0000
committerNorbert Preining <norbert@preining.info>2023-08-10 03:01:21 +0000
commitf2278c4ebe48c174e43845ccfd838fffc3376514 (patch)
treefa8ae4a3eb80e79f3fcad8c0cb8d306ef28a823b /graphics
parent9c6ffed1253f7d203afb08f7fffc9c661c48567b (diff)
CTAN sync 202308100301
Diffstat (limited to 'graphics')
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/README.md57
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/doc/ctan.bib99
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/doc/doc-wc.pdfbin0 -> 6760 bytes
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/doc/fond.pdfbin0 -> 1309 bytes
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-doc-en.pdfbin0 -> 118155 bytes
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-doc-en.tex429
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-preamble.tex214
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/metapost/wordcloud.mp467
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/scripts/wordcloud.lua224
-rw-r--r--graphics/metapost/contrib/macros/wordcloud/tex/wordcloud.sty149
10 files changed, 1639 insertions, 0 deletions
diff --git a/graphics/metapost/contrib/macros/wordcloud/README.md b/graphics/metapost/contrib/macros/wordcloud/README.md
new file mode 100644
index 0000000000..a4db68ebf4
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/README.md
@@ -0,0 +1,57 @@
+# wordcloud
+
+This package (MetaPost, Lua, and LaTeX) allows you to draw wordclouds.
+
+_This package is in beta version, do not hesitate to report bugs, as well as
+requests for improvement. There is lots to do!_
+
+## Github
+
+To allow for easier feedback and bug reporting, this repository has a mirror
+repository on github:
+[https://github.com/chupinmaxime/wordcloud](https://github.com/chupinmaxime/wordcloud)
+
+## Installation
+
+MPchess is on the [ctan](ctan.org) and can be installed via the package manager of your
+distribution [https://www.ctan.org/pkg/wordcloud](https://www.ctan.org/pkg/wordcloud).
+
+### With TeX live under Linux or MacOS
+
+To install `wordcloud` with TeX live, you will have to create the directory `texmf`
+directory in your home.
+```bash
+user $> mkdir ~/texmf
+```
+
+Then, you will have to place the .mp files in the
+`~/texmf/tex/metapost/wordcloud/`.
+
+`wordcloud` consists of 3 files:
+* `wordcloud.mp` (MetaPost) that should be in `~/texmf/metapost/wordcloud/`;
+* `wordcloud.tex` (LaTeX) that should be in `~/texmf/tex/latex/wordcloud/`;
+* `wordcloud.lua` (Lua) that should be in `~/texmf/scripts/wordcloud/`.
+
+### With MikTEX and Windows
+
+These two systems are unknown to the author of MPchess, so we refer to their
+documentation to add local packages:
+[http://docs.miktex.org/manual/localadditions.html](http://docs.miktex.org/manual/localadditions.html)
+
+## Dependencies
+
+* metapost-colorbrewer
+* latexmp
+* luamplib
+* xcolor
+## Documentation
+
+* [English documentation](doc/wordcloud-doc-en.pdf)
+
+## Contact
+
+Maxime Chupin, `notezik(at)gmail.com`
+
+## Licenses
+
+This projet is under LATEX Project Public License 1.3c. \ No newline at end of file
diff --git a/graphics/metapost/contrib/macros/wordcloud/doc/ctan.bib b/graphics/metapost/contrib/macros/wordcloud/doc/ctan.bib
new file mode 100644
index 0000000000..fb435f0a3d
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/doc/ctan.bib
@@ -0,0 +1,99 @@
+
+@manual{ctan-metapost,
+ title = {The \texttt{metapost} package},
+ subtitle = {A development of Metafont for creating graphics},
+ author = {{The MetaPost Team} and John Hobby},
+ date = {2021-08-26},
+ license = {lgpl},
+ url = {https://ctan.org/pkg/metapost},
+ annotation = {MetaPost uses a language based on that of
+ to produce precise
+ technical illustrations. Its output is scalable PostScript or
+ SVG, rather than the bitmaps Metafont creates.},
+}
+
+
+
+@manual{ctan-metapost-colorbrewer,
+ title = {The \texttt{metapost-colorbrewer} package},
+ subtitle = {An implementation of the colorbrewer2.org colours for MetaPost},
+ author = {Toby Thurston},
+ date = {2018-09-25},
+ version = {},
+ license = {gpl3+},
+ mirror = {https://mirror.ctan.org/graphics/metapost/contrib/macros/metapost-colorbrewer},
+ url = {https://ctan.org/pkg/metapost-colorbrewer},
+}
+
+@manual{ctan-latexmp,
+ title = {The \texttt{latexMP} package},
+ subtitle = {Interface for \LaTeX{}-based typesetting in MetaPost},
+ author = {Jens-Uwe Morawski},
+ date = {2020-06-21},
+ version = {1.2.1},
+ license = {pd},
+ mirror = {https://mirror.ctan.org/graphics/metapost/contrib/macros/latexmp},
+ url = {https://ctan.org/pkg/latexmp},
+ annotation = {The MetaPost package latexMP implements a user-friendly
+ interface to access \LaTeX{}-based typesetting capabilities in
+ MetaPost. The text to be typeset is given as string. This
+ allows even dynamic text elements, for example counters, to be
+ used in labels. Compared to other implementations it is much
+ more flexible, since it can be used as direct replacement for
+ , and much faster, compared for example to the
+ solution provided by .},
+}
+
+@manual{ctan-lualatex-doc,
+ title = {The \texttt{lualatex-doc} package},
+ subtitle = {A guide to use of \LaTeX{} with LuaTeX},
+ author = {Manuel Pégourié-Gonnard},
+ date = {2016-06-24},
+ version = {},
+ license = {fdl},
+ mirror = {https://mirror.ctan.org/info/luatex/lualatex-doc},
+ url = {https://ctan.org/pkg/lualatex-doc},
+}
+
+
+@manual{ctan-luamplib,
+ title = {The \texttt{luamplib} package},
+ subtitle = {Use LuaTeX’s built-in MetaPost interpreter},
+ author = {Hans Hagen and Taco Hoekwater and Élie Roux and Manuel Pégourié-Gonnard and Philipp Gesang and Dohyun Kim},
+ date = {2022-01-12},
+ version = {2.23.0},
+ license = {gpl2},
+ mirror = {https://mirror.ctan.org/macros/luatex/generic/luamplib},
+ url = {https://ctan.org/pkg/luamplib},
+}
+
+@manual{ctan-xcolor,
+ title = {The \texttt{xcolor} package},
+ subtitle = {Driver-independent color extensions for \LaTeX{} and pdfLaTeX},
+ author = {Uwe Kern and {The \LaTeX{} Project Team}},
+ date = {2022-06-12},
+ version = {2.14},
+ license = {lppl1.3c},
+ mirror = {https://mirror.ctan.org/macros/latex/contrib/xcolor},
+ url = {https://ctan.org/pkg/xcolor},
+ annotation = {The package starts from the basic facilities of the
+ package, and
+ provides easy driver-independent access to several kinds of
+ color tints, shades, tones, and mixes of arbitrary colors. It
+ allows a user to select a document-wide target color model and
+ offers complete tools for conversion between eight color models.
+ Additionally, there is a command for alternating row colors plus
+ repeated non-aligned material (like horizontal lines) in tables.
+ Colors can be mixed like \textbackslash{}color{red!30!green!40!blue}.},
+}
+
+@manual{ctan-graphicx,
+ title = {The \texttt{graphicx} package},
+ subtitle = {Enhanced support for graphics},
+ author = {David Carlisle and {The \LaTeX{} Project Team}},
+ date = {2021-11-12},
+ version = {1.2d},
+ license = {lppl1.3c},
+ mirror = {https://mirror.ctan.org/macros/latex/required/graphics},
+ url = {https://ctan.org/pkg/graphicx},
+} \ No newline at end of file
diff --git a/graphics/metapost/contrib/macros/wordcloud/doc/doc-wc.pdf b/graphics/metapost/contrib/macros/wordcloud/doc/doc-wc.pdf
new file mode 100644
index 0000000000..682af3842a
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/doc/doc-wc.pdf
Binary files differ
diff --git a/graphics/metapost/contrib/macros/wordcloud/doc/fond.pdf b/graphics/metapost/contrib/macros/wordcloud/doc/fond.pdf
new file mode 100644
index 0000000000..22218152cc
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/doc/fond.pdf
Binary files differ
diff --git a/graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-doc-en.pdf b/graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-doc-en.pdf
new file mode 100644
index 0000000000..38656d0d9b
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-doc-en.pdf
Binary files differ
diff --git a/graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-doc-en.tex b/graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-doc-en.tex
new file mode 100644
index 0000000000..e3ef5b24ae
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-doc-en.tex
@@ -0,0 +1,429 @@
+
+% LTeX: language=en
+
+% wordcloud : draw word cloud with MetaPost and Lua
+%
+% Originally written by Maxime Chupin <notezik@gmail.com>,
+% 2023.
+%
+% Distributed under the terms of the GNU free documentation licence:
+% http://www.gnu.org/licenses/fdl.html
+% without any invariant section or cover text.
+
+\documentclass[english]{ltxdoc}
+
+\input{wordcloud-preamble}
+\usepackage{wordcloud}
+\usepackage[english]{babel}
+
+\makeindex[title=Command Index, columns=2]
+
+
+
+%\lstset{moredelim=*[s][\color{red}\rmfamily\itshape]{<}{>}}
+%\lstset{moredelim=*[s][\color{blue}\rmfamily\itshape]{<<}{>>}}
+
+\begin{document}
+
+\title{{Wordcloud} : drawing wordclouds with \hologo{METAPOST} and Lua}
+\author{Maxime Chupin, \url{notezik@gmail.com}}
+\date{\today}
+
+%% === Page de garde ===================================================
+\thispagestyle{empty}
+\begin{tikzpicture}[remember picture, overlay]
+ \node[below right, shift={(-4pt,4pt)}] at (current page.north west) {%
+ \includegraphics{fond.pdf}%
+ };
+\end{tikzpicture}%
+
+\noindent
+{\Huge \texttt{wordcloud}}\par\bigskip
+\noindent
+{\Large drawing wordclouds \\[0.2cm]with \hologo{METAPOST} and Lua}\\[3cm]
+\parbox{0.6\textwidth}{
+ \wordcloud[scale=2,rotate=0,margin=0.5pt,usecolor]{(Wordcloud,10);(\\hologo{METAPOST},6);(\\LaTeX,7);(Lua,4);(algorithm,3);(code,2);(mathematics,2);(CTAN,2);(mplib,4);(\\hologo{LuaTeX},4);(latexmp,3);(graphism,2)}
+}\hfill
+\parbox{0.5\textwidth}{\Large\raggedleft
+ \textbf{Contributor}\\
+ Maxime \textsc{Chupin}\\
+ \url{notezik@gmail.com}
+}
+\vfill
+\begin{center}
+ Version 0.1, 2023, August, 9th \\
+ \url{https://plmlab.math.cnrs.fr/mchupin/wordcloud}
+\end{center}
+%% == Page de garde ====================================================
+\newpage
+
+%\maketitle
+
+\begin{abstract}
+ These \hologo{METAPOST} and \hologo{LuaLaTeX} packages allows drawing
+ wordclouds from a list of words and weights. The algorithm is implemented with
+ \hologo{METAPOST} whereas Lua is used to parse \LaTeX{} commands, to build
+ the list of words and weights from a text file, and to generate
+ \hologo{METAPOST} code interpreted by \package{luamplib}.
+\end{abstract}
+
+
+\begin{center}
+ \url{https://plmlab.math.cnrs.fr/mchupin/wordcloud}
+ \url{https://github.com/chupinmaxime/wordcloud}
+\end{center}
+
+\tableofcontents
+
+\bigskip
+
+\begin{tcolorbox}[ arc=0pt,outer arc=0pt,
+ colback=darkred!3,
+ colframe=darkred,
+ breakable,
+ boxsep=0pt,left=5pt,right=5pt,top=5pt,bottom=5pt, bottomtitle =
+ 3pt, toptitle=3pt,
+ boxrule=0pt,bottomrule=0.5pt,toprule=0.5pt, toprule at break =
+ 0pt, bottomrule at break = 0pt,]
+ \itshape
+ This package is in beta version---do not hesitate to report bugs, as well as
+ requests for improvement, or better: to help me to improve it.
+\end{tcolorbox}
+
+\section{Installation}
+
+\wordcloudpkg is on \ctan{} and can also be installed via the package manager of your
+distribution.
+
+\begin{center}
+ \url{https://www.ctan.org/pkg/wordcloud}
+\end{center}
+
+
+\subsection{With \TeX live under Linux or macOS}
+
+To install \wordcloudpkg with \TeX Live, you will have to create the directory
+\lstinline+texmf+ in your \lstinline+home+.
+
+\begin{commandshell}
+mkdir ~/texmf
+\end{commandshell}
+
+Then, you will have to place the \lstinline+wordcloud.mp+ file in
+\begin{center}
+ \verb+~/texmf/metapost/wordcloud/+
+\end{center}
+
+
+You will also have to place the \lstinline+wordcloud.lua+ file in
+\begin{center}
+ \verb+~/texmf/scripts/wordcloud/+
+\end{center}
+
+And finally, you will have to place the \lstinline+wordcloud.sty+ file in
+\begin{center}
+ \verb+~/texmf/tex/latex/wordcloud/+
+\end{center}
+
+
+
+Once this is done, \wordcloudpkg will be loaded with the classic \MP{}
+input code
+\begin{mpcode}
+input wordcloud
+\end{mpcode}
+
+And for the \hologo{LuaLaTeX} side, \wordcloudpkg will be loaded with
+\begin{latexcode}
+\usepackage{wordcloud}
+\end{latexcode}
+
+\subsection{With Mik\TeX{} and Windows}
+
+These two systems are unknown to the author of \wordcloudpkg, so we
+refer you to the Mik\TeX documentation concerning the addition of local packages:
+\begin{center}
+ \url{http://docs.miktex.org/manual/localadditions.html}
+\end{center}
+
+
+
+\subsection{Dependencies}
+
+
+\wordcloudpkg depends, for the \MP side, of course on \MP~\cite{ctan-metapost},
+but also on \package{metapost-colorbrewer}~\cite{ctan-metapost-colorbrewer} and the
+\package{latexmp} package~\cite{ctan-latexmp}. For the \hologo{LuaLaTeX} side~\cite{ctan-lualatex-doc},
+\wordcloudpkg depends on the \package{luamplib} package~\cite{ctan-luamplib}
+and the \package{xcolor}~\cite{ctan-xcolor}.
+
+
+\section{\hologo{METAPOST} side}
+
+\subsection{Description of the algorithm}
+
+Given a set of words and weights, we first use a
+\emph{scale function} of the weights to scale the words. In this beta version of
+\package{wordcloud}, we only provide a log-based function\footnote{Other scale
+options could be provided in the next versions.}.
+
+Then, we compute a spiral line starting at the center\footnote{There is variants
+of the algorithm that use different line: squared spiral, etc.}.
+
+Then the algorithm is quite simple:
+\begin{algorithmic}[1]
+\Require set of words $(W_i)_{i\in\{1,\dots,N}$ and corresponding weight
+$(w_i)_{i\in\{1,\dots,N}$, and a spiral line $\mathcal{S}$
+\ForAll{$i\in\{1, \dots,N\}$}
+\State Place $W_i$ at the start of $\mathcal{S}$
+\Repeat
+ \State Set $b_\text{draw}:=\text{true}$
+ \ForAll{$j\in{1,\dots,i}$}
+ \If{$W_i\cap W_j\neq\emptyset$}
+ \State Set $b_\text{draw}:=\text{false}$
+ \EndIf
+ \EndFor
+ \If{$b_\text{draw}==\text{true}$}
+ \State Draw $W_i$
+ \Else
+ \State Move $W_i$ along $\mathcal{S}$
+ \EndIf
+\Until{$W_i$ is drawn}
+\EndFor
+\end{algorithmic}
+
+The hard part is making it perform efficiently! According to Jonathan Feinberg,
+Wordle\footnote{One of the first web application to build wordcloud.} uses a
+combination of hierarchical bounding boxes and quadtrees to achieve reasonable
+speeds. Here, with \hologo{METAPOST}, we compute intersections with the bounding
+box of the word.
+
+\begin{remark}
+ \begin{itemize}
+ \item
+ The words with \hologo{METAPOST} are built with the \lstinline+textext()+
+ function of \package{latexmp} or \package{luamplib}. We are trying to use the
+ bounding boxes of the letters when we get an intersection between ``global''
+ bounding boxes to allow placing words nearer of each other. Unfortunately,
+ this does not work for the moment. Any help is welcomed. \medskip
+
+ \item We first tried to compute intersections between words by decomposing the
+ letter using their contours and compute intersection of contours (with
+ \lstinline+intersectiontimes+). Unfortunately, this is much too slow.
+\end{itemize}
+
+\end{remark}
+
+Some explanations can be found here:
+\begin{center}
+ \url{https://www.jasondavies.com/wordcloud/about/}
+\end{center}
+
+\subsection{Main command}
+
+The main command is
+
+\commande|draw_wordcloud(«words», «weights»,«rotation»,«size»)|\smallskip\index{draw_wordcloud@\lstinline+draw_wordcloud+}
+
+\begin{description}
+\item[\meta{words}:] array of strings ;
+\item[\meta{weights}:] array of numerics ;
+\item[\meta{rotation}:] angle for wordcloud drawing;
+\item[\meta{size}:] number of elements in arrays.
+\end{description}
+
+\begin{ExempleMP}
+input wordcloud
+beginfig(0);
+string words[];
+numeric weights[];
+words[1]:="\LaTeX";
+words[2]:="\hologo{METAPOST}";
+words[3]:="Document";
+words[4]:="Lua";
+words[5]:="\TeX";
+weights[1]:=5;
+weights[2]:=4;
+weights[3]:=3.5;
+weights[4]:=3;
+weights[5]:=3;
+draw_wordcloud(words,weights,0,5);
+endfig;
+\end{ExempleMP}
+
+\begin{remark}
+The ``unity'' of weights is not important because internally,
+\package{wordcloud} compute new weights to work
+with the internal scaling function.
+\end{remark}
+
+\subsection{Parameters}
+
+There are few parameters.
+
+\subsubsection{Colors}\label{sec:colors}
+
+You can use set of colors to draw the wordcloud. For that, you have to use the
+following command:
+\commande|wordcloud_use_color(«bool»)|\smallskip\index{wordcloud_use_color@\lstinline+wordcloud_use_color+}
+
+\begin{description}
+ \item[\meta{bool}:] boolean \lstinline+true+ or \lstinline+false+ (default \lstinline+false+).
+\end{description}
+
+\package{wordcloud} provides a set of five colors using the \hologo{METAPOST}
+package \package{metapost-colorbrewer}~\cite{ctan-metapost-colorbrewer}.
+\package{wordcloud} defines an array of \lstinline+color+s and a
+\lstinline+numeric+ to set the colors to use.\index{wordcloud_colors@\lstinline+wordcloud_colors+}\index{wordcloud_colors_number@\lstinline+wordcloud_colors_number+}
+\begin{mpcode}
+% default colors
+wordcloud_colors[1]:=Reds[3][3];
+wordcloud_colors[2]:=Greens[3][3];
+wordcloud_colors[3]:=Blues[3][3];
+wordcloud_colors[4]:=Oranges[3][3];
+wordcloud_colors[5]:=black;
+wordcloud_colors_number:=5;
+\end{mpcode}
+Feel free to modify that variables to customize the colors.
+
+\subsubsection{Scaling}
+
+You can globally scale the picture using the following command:
+\commande|set_wordcloud_scale(«scale»)|\smallskip\index{set_wordcloud_scale@\lstinline+set_wordcloud_scale+}
+
+\begin{description}
+ \item[\meta{scale}:] \lstinline+numeric+.
+\end{description}
+
+\subsubsection{Margins}
+
+You can adjust the margins of the global bounding boxes of words using the
+following command:
+\commande|set_box_margin(«dim»)|\smallskip\index{set_box_margin@\lstinline+set_box_margin+}
+
+\begin{description}
+ \item[\meta{dim}:] a dimension with units (default 0.3pt).
+\end{description}
+
+
+
+\section{\hologo{LuaLaTeX} side}
+
+\package{wordcloud} provides a \hologo{LuaLaTeX} package. It uses the package
+\package{luamplib} to interpret the \hologo{METAPOST} code produced by Lua.
+
+
+\subsection{Main commands}
+
+The first \LaTeX{} command provided by \wordcloudpkg is:
+
+\commande|\wordcloud[«options»]{«list of words and weights»}|\smallskip\index{\wordcloud@\lstinline+\wordcloud+}
+
+where
+\begin{description}
+ \item[\meta{list of words and weights}:] is a list of couples of the form
+ \lstinline+(word1,weight1);(word2,weight2);(word3,weight3);...+
+\end{description}
+
+The second \LaTeX{} command allows to read a text file, to build the list of
+words and weights and draw the wordcloud up to a certain number of words.
+
+\commande|\wordcloudFile[«options»]{«text file»}{«number of words»}|\smallskip\index{\wordcloudFile@\lstinline+\wordcloudFile+}
+
+where:
+\begin{description}
+ \item[\meta{text file}:] is a text file to analyze and from which the
+ wordcloud is build ;
+ \item[\meta{number of words}:] is the number of words composing the wordcloud.
+\end{description}
+
+\subsubsection{Options}
+
+Both of these functions (\lstinline+\wordcloud+ and \lstinline+\wordcloudFile+)
+have the same options:
+
+\begin{description}
+ \item[\texttt{scale=}\meta{value}:] to scale the picture\footnote{Beware
+ that scaling increases the computation time and the values manipulated by
+ \hologo{METAPOST}.} ;
+ \item[\texttt{margin=}\meta{value with units}:] to adjust the margins
+ (default \lstinline+0.3pt+) ;
+ \item[\texttt{rotation=}\meta{angle}:] to rotate (degrees) the words with
+ $\pm$\meta{angle} alternatively (default 0) ;
+ \item[\texttt{usecolor}:] to use color for word drawing (boolean, default
+ \lstinline+false+) as described in section~\ref{sec:colors} ;
+ \item[\texttt{colors=}\meta{list of colors}:] to define a new
+ set of colors as described in section~\ref{sec:colors}\footnote{This needs
+ \package{xcolor} because the colors are converted to rgb coding and then
+ transferred to \hologo{METAPOST}.}.
+\end{description}
+
+
+Here an example:
+\begin{ExempleLaTeX}
+\wordcloud[scale=1,rotate=45,margin=0.5pt,usecolor,colors={red!40,blue!40,green!20!black}]{(Wordcloud,10);(\\hologo{METAPOST},6);(\\LaTeX,7);(Lua,4);(algorithm,3);(code,2);(mathematics,2);(CTAN,2);(mplib,4);(\\hologo{LuaTeX},4);(latexmp,3);(graphism,2)}
+\end{ExempleLaTeX}
+
+\begin{remark}
+Because the list of words and weights is given to Lua, we have to escape the
+\lstinline+\+ to use \LaTeX{} commands.
+\end{remark}
+
+\subsection{Add ignored words}
+
+The Lua function that builds words and weights from a text file ignores some
+words (and characters). For the moment, \package{wordcloud} only includes word
+lists to ignore for English and French.
+
+However, you can add a list of words to ignore with the following command:
+
+\commande|\wordcloudIgnoreWords{«word list»}|\smallskip\index{\wordcloudIgnoreWords@\lstinline+\wordcloudIgnoreWords+}
+
+\begin{description}
+ \item[\meta{word list}:] the list of words, separated with commas, to ignore
+ \lstinline+word1,word2,word3, etc.+
+\end{description}
+
+
+\section{With \texttt{pdftotext}}
+
+Thanks to \package{wordcloud} and the program \texttt{pdftotext}\footnote{It
+should be possible to parse a \pdf{} with \hologo{LuaTeX}, though. See \url{https://tex.stackexchange.com/questions/692930/recovering-the-textual-content-of-a-pdf-file-with-luatex}.} one can
+easily produce the wordcloud of the current \pdf.
+
+For that, you can produce the text file of the \pdf{}:
+\begin{commandshell}
+pdftotext wordcloud-doc-en.pdf
+\end{commandshell}
+and then, you can use the following code:
+\begin{latexcode}
+\wordcloudFile[usecolor]{wordcloud-doc-en.txt}{50}
+\end{latexcode}
+
+This produce the following wordcloud\footnote{Note that, because the wordcloud
+production is slow, we used a separate file to only produce the \pdf{} of the
+wordcloud, without any scaling, and we inserted the result scaling it with \package{graphicx}\cite{ctan-graphicx}.}
+\begin{center}
+ \includegraphics[width=\linewidth]{doc-wc.pdf}
+\end{center}
+
+\section{To do}
+
+Some things to do:
+\begin{itemize}
+ \item Improve intersection of words by using the letters bounding boxes.
+ \item Work on speed of the algorithm.
+ \item Add supported languages (ignored words).
+ \item Improve text file analysis with Lua to build the set of words and
+ weights.
+ \item Build wordcloud inside a shape.
+ \item Add options for rotation of words.
+\end{itemize}
+
+\printbibliography
+\printindex
+\newpage
+
+
+
+\end{document}
diff --git a/graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-preamble.tex b/graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-preamble.tex
new file mode 100644
index 0000000000..2e950254c4
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/doc/wordcloud-preamble.tex
@@ -0,0 +1,214 @@
+\usepackage{tcolorbox}
+\tcbuselibrary{listings,breakable}
+%\tcbuselibrary{documentation}
+\usepackage{enumitem}
+\usepackage[tikz]{bclogo}
+\usepackage{mflogo}
+\usepackage{hologo}
+\usepackage{luamplib}
+\mplibtextextlabel{enable}
+\usepackage{biblatex}
+\addbibresource{ctan.bib}
+\usepackage{wrapfig}
+\usepackage{accsupp}
+\usepackage{siunitx}
+\usepackage{imakeidx}
+%\usepackage{csquotes}
+\usepackage{fancyvrb,xparse,xargs}
+\usepackage[sfdefault]{FiraSans}
+\usepackage[mathrm=sym]{firamath-otf}
+\setmonofont{Fira Mono}
+%\setmonofont{FiraCode-Regular.ttf}[BoldFont= FiraCode-Bold.ttf,ItalicFont= FiraCode-RegularItalic.otf,BoldItalicFont= FiraCode-BoldItalic.otf,Ligatures={NoCommon, NoDiscretionary, NoHistoric, NoRequired, NoContextual}]
+\usepackage[noEnd=false,]{algpseudocodex}
+\usepackage{xspace}
+\usepackage{animate}
+\newcommand{\ctan}{\textsc{ctan}}
+\NewDocumentCommand{\package}{ m }{%
+ \href{https://ctan.org/pkg/#1}{#1}\xspace
+}
+
+\definecolor{darkred}{rgb}{0.6,0.1,0.1}
+\definecolor{vert}{rgb}{0.1,0.4,0.1}
+\definecolor{bleu}{rgb}{0.2,0.2,0.6}
+\definecolor{orange}{rgb}{0.6,0.4,0.}
+\colorlet{code}{blue!80!black}
+
+\usepackage[colorlinks=true,urlcolor=orange,linkcolor=orange,menucolor=black,citecolor=orange]{hyperref}
+
+\newcommand \file {\nolinkurl}
+\renewcommand \cmd {\texttt}
+\renewcommand \code [1] {\texorpdfstring {\texttt{\color{code}#1}} {#1}}
+\renewcommand*\cs [1] {\code{\textbackslash #1}}
+
+
+
+\newcommand*\commande{\par\bigskip\noindent\hspace{-30pt}%
+ \SaveVerb[aftersave={%
+ \UseVerb{Vitem}%
+ }%
+ ]{Vitem}%
+ }
+ \newcommand\vitem[1][]{\SaveVerb[%
+ aftersave={\item[\textnormal{\UseVerb[#1]{vsave}}]}]{vsave}}
+\newcommand*\textme[1]{\textcolor{black}{\rmfamily\textit{#1}}}
+%\renewcommand*\meta[1]{% % meta
+% \textme{\ensuremath{\langle}#1\ensuremath{\rangle}}}
+\newcommand*\optstar{% % optional star
+ \meta{\ensuremath{*}}\xspace}
+\DefineShortVerb{\|}
+\newcommand\R{\mathbf{R}}
+\setlength{\fboxsep}{2pt}
+\fvset{%
+ codes={\catcode`\«\active \catcode`\×\active },
+ defineactive={\makefancyog\makefancytimes},
+ formatcom=\color{darkred},
+ frame=single
+}
+% rendre «...» équivalent à \meta{...}
+{\catcode`\«\active
+ \newcommandx\makefancyog[0][addprefix=\global]{%
+ \def«##1»{\meta{##1}}}}
+% rendre × équivalent à \optstar
+{\catcode`\×\active
+ \newcommandx\makefancytimes[0][addprefix=\global]{%
+ \def×{\optstar{}}}}
+
+
+\newcommand\wordcloudpkg{\package{wordcloud}}
+
+\def\MO/{{\fontspec{fetamont}META\-OBJ}}
+
+%\addbibresource{biblio.bib}
+
+
+\lstset{
+ numberstyle=\footnotesize\color{vert},
+ keywordstyle=\ttfamily\bfseries\color{bleu},
+ basicstyle=\ttfamily,
+ commentstyle=\itshape\color{vert},
+ stringstyle=\ttfamily\color{orange},
+ showstringspaces=false,
+ language=MetaPost,
+ breaklines=true,
+ breakindent=30pt,
+ defaultdialect=MetaPost,
+ classoffset=1,% frame=tb
+ morekeywords={draw_wordcloud,set_box_margin,set_wordcloud_scale,wordcloud_colors,wordcloud_colors_number,wordcloud_use_color,\wordcloud,\wordcloudFile,\wordcloudIgnoreWords},
+ keywordstyle=\color{darkred},
+ classoffset=2,% frame=tb
+ morekeywords={Reds,Greens,Blues,Oranges},
+ keywordstyle=\color{vert},
+ classoffset=0,% frame=tb
+ morekeywords={draw},
+ keywordstyle=\color{bleu}
+}
+
+
+\extractcolorspecs{darkred!3}\modelcmd\colorcmd
+\makeatletter
+\tcbset{%
+ listing metapost/.code={%
+ \def\tcbuselistingtext@input{\begin{mplibcode} verbatimtex \leavevmode etex;
+ background:=(\colorcmd); input \jobname.listing; \end{mplibcode}}%
+ }
+}
+\makeatother
+\newtcblisting[auto counter,]{ExempleMP}[1][]{%
+ arc=0pt,outer arc=0pt,
+ colback=darkred!3,
+ colframe=darkred,
+ breakable,fontupper=\small,
+ boxsep=0pt,left=5pt,right=5pt,top=5pt,bottom=5pt, bottomtitle =
+ 3pt, toptitle=3pt,
+ boxrule=0pt,bottomrule=0.5pt,toprule=0.5pt, toprule at break =
+ 0pt, bottomrule at break = 0pt,
+ %listing side text,
+ listing metapost,
+ title=Exemple \hologo{METAPOST}~\thetcbcounter,
+ listing options={breaklines},#1
+}
+
+\newtcblisting[auto counter,]{ExempleLaTeX}{%
+ arc=0pt,outer arc=0pt,
+ colback=darkred!3,
+ colframe=darkred,
+ breakable,fontupper=\small,
+ boxsep=0pt,left=5pt,right=5pt,top=5pt,bottom=5pt, bottomtitle =
+ 3pt, toptitle=3pt,
+ boxrule=0pt,bottomrule=0.5pt,toprule=0.5pt, toprule at break =
+ 0pt, bottomrule at break = 0pt,
+ %listing side text,
+ title=Exemple \LaTeX~\thetcbcounter,
+ listing options={breaklines,language={[LaTeX]TeX},
+ classoffset=1,% frame=tb
+ morekeywords={\wordcloud,\wordcloudFile},
+ keywordstyle=\color{darkred},
+ classoffset=2,% frame=tb
+ morekeywords={scale,rotate,margin,usecolor,colors},
+ keywordstyle=\color{vert},
+ classoffset=0,% frame=tb
+ morekeywords={draw},
+ keywordstyle=\color{bleu}
+ }
+}
+
+\newtcblisting{commandshell}{colback=black,colupper=white,colframe=black,
+ arc=0pt,
+ listing only,boxsep=0pt,listing
+ options={style=tcblatex,language=sh},
+ every listing line={\BeginAccSupp{ActualText={}}\textcolor{red}{\small\ttfamily\bfseries user \$> }}\EndAccSupp{}}
+
+
+ \newtcblisting{mpcode}[1][]{
+ arc=0pt,outer arc=0pt,
+ colback=darkred!3,
+ colframe=darkred,
+ breakable,
+ boxsep=0pt,left=5pt,right=5pt,top=5pt,bottom=5pt, bottomtitle =
+ 3pt, toptitle=3pt,
+ boxrule=0pt,bottomrule=0.5pt,toprule=0.5pt, toprule at break =
+ 0pt, bottomrule at break = 0pt,
+ listing only,boxsep=0pt,listing
+ options={breaklines},#1
+}
+
+\newtcblisting{latexcode}{
+ arc=0pt,outer arc=0pt,
+ colback=darkred!3,
+ colframe=darkred,
+ breakable,
+ boxsep=0pt,left=5pt,right=5pt,top=5pt,bottom=5pt, bottomtitle =
+ 3pt, toptitle=3pt,
+ boxrule=0pt,bottomrule=0.5pt,toprule=0.5pt, toprule at break =
+ 0pt, bottomrule at break = 0pt,
+ listing only,boxsep=0pt,listing
+ options={breaklines,language={[LaTeX]TeX},
+ classoffset=1,% frame=tb
+ morekeywords={\wordcloud,\wordcloudFile},
+ keywordstyle=\color{darkred},
+ classoffset=2,% frame=tb
+ morekeywords={scale,rotate,margin,usecolor,colors},
+ keywordstyle=\color{vert},
+ classoffset=0,% frame=tb
+ morekeywords={draw},
+ keywordstyle=\color{bleu}}
+}
+
+
+\newcommand\pdf{\textsc{pdf}}
+
+\usepackage{array,booktabs}
+\usepackage{collcell}
+
+\newcolumntype{H}{>{\collectcell\lstinline}l<{\endcollectcell}}
+
+\NewDocumentEnvironment{remark}{o}{
+ \tcbset{%
+ arc=0pt,outer arc=0pt,colback=black!10,colframe=black,
+ boxsep=0pt,left=10pt,right=10pt,top=8pt,bottom=10pt, bottomtitle = 3pt, toptitle=3pt,
+ boxrule=0pt
+ }
+ \smallskip
+ \begin{tcolorbox}[fonttitle=\bfseries,title={Remark \IfNoValueF{#1}{(#1)}}]}%
+ {\end{tcolorbox}\medskip
+} \ No newline at end of file
diff --git a/graphics/metapost/contrib/macros/wordcloud/metapost/wordcloud.mp b/graphics/metapost/contrib/macros/wordcloud/metapost/wordcloud.mp
new file mode 100644
index 0000000000..6a69fb5191
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/metapost/wordcloud.mp
@@ -0,0 +1,467 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% wordcloud %%
+%% drawing wordclouds %%
+%% with METAPOST and Lua %%
+%% chupin@ceremade.dauphine.fr %%
+%% Version 0.1 (aout 2023) %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This work may be distributed and/or modified under the conditions of
+% the LaTeX Project Public License, either version 1.3c 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
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+if not known mplib: input latexmp ; show"wordcloud Infos: latexmp loaded";fi;
+input colorbrewer-rgb
+
+
+% function to get all the path of letters (too slow)
+vardef build_pathpicture(expr pict)=
+ save tex_pct,glp_pct,glp_idx,glp_num,pth_num,glp_pth,fnt_str, txt_str, sub_str,txt_wd;
+ % text material and glyphs contours
+ picture tex_pct, glp_pct;
+ numeric glp_num, pth_num[];
+ path glp_pth[][];
+ % tex_pct btex .. etex material
+ % glp_num number of glyphs within tex_pct
+ % pth_num[i] number of paths defining the rank i (0 .. glp_num-1) glyph
+ % glp_pth[i][j] rank j (0 .. pth_num[i]-1) path of the rank i glyph
+ % glp_pct glyphs picture
+
+ tex_pct:=pict;
+
+ glp_pct:=nullpicture;
+ string fnt_str, txt_str, sub_str;
+ numeric txt_wd;
+ glp_num:=0;
+ for tkn within pict:
+ %show tkn;
+ if textual tkn:
+ %show "textual";
+ fnt_str:=fontpart tkn;
+ txt_str:=textpart tkn;
+ txt_wd:=0;
+ for glp_idx=0 upto (length txt_str-1):
+ sub_str:=substring (glp_idx, glp_idx+1) of txt_str;
+ for sub_tkn within glyph ASCII sub_str of fnt_str
+ scaled (fontsize fnt_str/1000)
+ xscaled xxpart tkn
+ yscaled yypart tkn
+ shifted (txt_wd+xpart tkn, ypart tkn):
+ addto glp_pct doublepath (pathpart sub_tkn);
+ endfor
+ txt_wd:=txt_wd+
+ (xxpart tkn)*xpart urcorner (sub_str infont fnt_str);
+ endfor
+ fi
+ endfor
+ glp_pct
+enddef;
+
+
+% path intersection (trop gourmant en temps, too slow...)
+vardef word_intersection(expr tic, tac)=
+ save _p, _w, _v,_output;
+ boolean _output;
+ _output:=false;
+ path _p,_w,_v;
+ for _p within tic:
+ _w := pathpart _p;
+ for _q within tac:
+ _v := pathpart _q;
+ if((_w intersectiontimes _v)<>(-1,-1)): % intersection
+ _output:=true;
+ fi
+ endfor
+ endfor
+ _output
+enddef;
+
+% global variable
+_wordcloud_box_margin:=0.3pt;
+
+% setting the box margin
+def set_box_margin(expr s)=
+ _wordcloud_box_margin:=s;
+enddef;
+
+% extrenal box intersection (horizontal or vertical)
+vardef extbb_intersection(expr tic, tac)=
+ save xmax_, xmin_, ymax_, ymin_, xmax__, xmin__, ymax__, ymin__, _output;
+ boolean _output;
+ _output:=false;
+ %show _p;
+ xmin_:= (xpart llcorner tic) -_wordcloud_box_margin;
+ ymin_ := (ypart llcorner tic)-_wordcloud_box_margin;
+ xmax_:= (xpart urcorner tic)+_wordcloud_box_margin;
+ ymax_ := (ypart urcorner tic)+_wordcloud_box_margin;
+ xmin__:= (xpart llcorner tac)-_wordcloud_box_margin;
+ ymin__ := (ypart llcorner tac)-_wordcloud_box_margin;
+ xmax__:= (xpart urcorner tac)+_wordcloud_box_margin;
+ ymax__ := (ypart urcorner tac)+_wordcloud_box_margin;
+ if ((xmin_<= xmax__) and (xmax_ >= xmin__) and (ymin_<= ymax__) and (ymax_ >= ymin__)):
+ _output:=true;
+ fi
+ _output
+enddef;
+
+% subbb_entersection of words (from textext)
+%%%!!!!!! not working !!!!!!!!!
+vardef bb_intersection(expr tic, tac)=
+ save xmax_, xmin_, ymax_, ymin_, xmax__, xmin__, ymax__, ymin__,
+ _output;
+ save _p, _w, _v,_output,_q;
+ boolean _output;
+ _output:=false;
+ path _p,_w,_v;
+ i:=0;
+ for _v within tic:
+ for _p within _v:
+ i:=i+1;
+ %show "i"&decimal(i);
+ xmin_:= (xpart llcorner _p)-_wordcloud_box_margin;
+ ymin_ := (ypart llcorner _p)-_wordcloud_box_margin;
+ xmax_:= (xpart urcorner _p)+_wordcloud_box_margin;
+ ymax_ := (ypart urcorner _p)+_wordcloud_box_margin;
+ j:=0;
+ for _w within tac:
+ for _q within _w:
+ j:=j+1;
+ %show "j"&decimal(j);
+ xmin__:= (xpart llcorner _q)-_wordcloud_box_margin;
+ ymin__ := (ypart llcorner _q)-_wordcloud_box_margin;
+ xmax__:= (xpart urcorner _q)+_wordcloud_box_margin;
+ ymax__ := (ypart urcorner _q)+_wordcloud_box_margin;
+ if ((xmin_<= xmax__) and (xmax_ >= xmin__) and (ymin_<= ymax__) and (ymax_ >= ymin__)):
+ _output:=true;
+ fi
+ exitif(_output);
+ endfor
+ endfor
+ exitif(_output);
+ endfor
+ endfor
+ _output
+enddef;
+
+
+% build the spiral to put words
+path _wordcloud_spiral;
+
+_wordcloud_spiral:=(0,0);
+
+_wordcloud_N:= 10*360;
+_tours:=20;
+_scale:=0.3;
+for j:=1 upto _tours:
+ _rapj:=j/_tours;
+ for i:=1 step 10 until 360:
+ _rapi:=((i)+(j)*360)/360;
+ %show (_rapj*_rapi);
+ %show (6*_rapj*_rapi)*(cosd(i)*1cm,sind(i)*1cm);
+ _wordcloud_spiral:=_wordcloud_spiral--((_scale*_rapi)*(cosd(i)*1cm,sind(i)*1cm));
+ endfor;
+endfor
+
+picture _wordcloud_words[], _wordcloud_pathwords[];
+path _wordcloud_words_oriented_bb[];
+numeric _wordcloud_weights[];
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%% FONCTIONS USUELLES %%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% from repere.mp Olivier Péault
+
+pi:=3.141592654;
+e:=2.718281828;
+
+vardef sin(expr x) = sind(180*x/pi) enddef;
+
+vardef cos(expr x) = cosd(180*x/pi) enddef;
+
+vardef tan(expr x) = sin(x)/cos(x) enddef;
+
+vardef exp(expr x) = mexp(x*256) enddef;
+
+vardef ln(expr x) = mlog(x)/256 enddef;
+
+vardef ch(expr x)=(exp(x)+exp(-x))/2 enddef;
+
+vardef sh(expr x)=(exp(x)-exp(-x))/2 enddef;
+
+vardef Arctan(expr x)= pi*angle(1,x)/180 enddef;
+vardef Arctand(expr x)= angle(1,x) enddef;
+
+vardef Arcsin(expr x)= Arctan(x/sqrt(1-x**2)) enddef;
+vardef Arcsind(expr x)= Arctand(x/sqrt(1-x**2)) enddef;
+
+vardef Arccos(expr x)= pi/2-Arcsin(x) enddef;
+vardef Arccosd(expr x)= Arctand((sqrt(1-x**2))/x)enddef;
+
+% general scaling
+_wordcloud_gen_scale:=1;
+
+def set_wordcloud_scale(expr s)=
+ _wordcloud_gen_scale:=s;
+enddef;
+
+% fonction d'agrandissement en fonction du poids du mot
+vardef _wordcloud_scale_fct(expr e,_max,_min)=
+ save _t,_N,_a,_b;
+ _a*_min+_b=0.7;
+ _a*_max+_b=2.5;
+ _t:=_a*e+_b;
+ _t:=ln(_t)-ln(0.7)+0.295;
+ _wordcloud_gen_scale*(1.1*_t)
+enddef;
+
+% fabrication des mots avec la taille requise
+vardef _build_tabs(suffix _words, _weights)(expr _N)=
+ save _tmp_pict,_width,_height;
+ picture _tmp_pict;
+ for i=1 upto _N:
+ _tmp_pict:=textext(_words[i]) scaled (_wordcloud_scale_fct(_weights[i],_weights[1],_weights[_N]));
+ _width:=abs(ulcorner _tmp_pict - urcorner _tmp_pict);
+ _height:=abs(ulcorner _tmp_pict - llcorner _tmp_pict);
+ _wordcloud_words[i]:= _tmp_pict shifted (-0.5_width,-0.5_height);
+ _wordcloud_weights[i]:=_weights[i];
+ _bbox_old:=bboxmargin;
+ bboxmargin:=_wordcloud_box_margin;
+ _wordcloud_words_oriented_bb[i]:= bbox _wordcloud_words[i];
+ bboxmargin:=_bbox_old;
+ _wordcloud_pathwords[i]:=build_pathpicture(_wordcloud_words[i]) ;
+ endfor;
+enddef;
+% function to compute angle
+def _wordcloud_rotation(expr a,b)=
+% a: angle
+% b: 0 or 1
+ if b=0:
+ a
+ else:
+ if(a=0):
+ 90
+ else:
+ -a
+ fi
+ fi
+enddef;
+
+%%% colors
+boolean _wordcloud_use_color;
+_wordcloud_use_color:=false;
+
+numeric wordcloud_colors_number;
+color wordcloud_colors[];
+
+% default colors
+wordcloud_colors[1]:=Reds[3][3];
+wordcloud_colors[2]:=Greens[3][3];
+wordcloud_colors[3]:=Blues[3][3];
+wordcloud_colors[4]:=Oranges[3][3];
+wordcloud_colors[5]:=black;
+wordcloud_colors_number:=5;
+
+def wordcloud_use_color(expr b)=
+ _wordcloud_use_color:= b;
+enddef;
+
+% main function to draw the picture
+vardef draw_wordcloud(suffix _words, _weights, rotation)(expr _N)=
+ save _rotation_bool,_position,_ok_draw,_tmp_word,_random_pt,_position_pt,_pt;
+ pair _random_pt,_position_pt,_pt;
+ path _tmp_word;
+ boolean _ok_draw;
+ _position:=0;
+ _build_tabs(_words,_weights,_N);
+ for i=1 upto _N:
+ %show i;
+ _position:=0;
+ if(i mod 2) = 0:
+ _rotation_bool:=1;
+ else:
+ _rotation_bool:=0;
+ fi
+ if(i=1):
+ draw _wordcloud_words[i] rotated (_wordcloud_rotation(rotation,_rotation_bool)) shifted (point _position
+ of _wordcloud_spiral) if(_wordcloud_use_color): withcolor wordcloud_colors[i] fi ;
+ _position_pt:=(0,0);
+ _random_pt:=(0,0);
+ else:
+ _count:=0;
+
+ forever:
+ _position_pt:=point _position of _wordcloud_spiral;
+ _random_pt:=(uniformdeviate(1)*0.01cm,uniformdeviate(1)*0.01cm);
+ _pt:=_position_pt+_random_pt;
+ _count:=_count+1;
+ _ok_draw:= true;
+ _tmp_word:=_wordcloud_words_oriented_bb[i] rotated (_wordcloud_rotation(rotation,_rotation_bool)) shifted (_position_pt + _random_pt);
+ %_tmp_word:=_wordcloud_words[i] rotated (_rotation*90) shifted (point %_position
+ % of _wordcloud_spiral);
+ for j=1 upto i-1:
+ if(rectangles_intersection(_tmp_word,_wordcloud_words_oriented_bb[j])):
+ %if(bb_intersection(_tmp_word,_wordcloud_words[j])):
+ _ok_draw:= false;
+ %fi
+ fi
+ endfor;
+ if(_ok_draw): % if there is no intersections
+ draw _wordcloud_words[i] rotated
+ (_wordcloud_rotation(rotation,_rotation_bool)) shifted (_position_pt +
+ _random_pt) if(_wordcloud_use_color): withcolor wordcloud_colors[((i-1)
+ mod wordcloud_colors_number)+1] fi;
+ else: % else, we move along the spiral
+ _position:=_position+1;
+ fi
+ exitif(_ok_draw);
+ %exitif(_count>30);
+ endfor;
+ fi
+ _wordcloud_words[i]:=_wordcloud_words[i] rotated (_wordcloud_rotation(rotation,_rotation_bool)) shifted
+ (_position_pt + _random_pt);
+ _wordcloud_words_oriented_bb[i]:=_wordcloud_words_oriented_bb[i] rotated (_wordcloud_rotation(rotation,_rotation_bool)) shifted
+ (_position_pt + _random_pt);
+ _wordcloud_pathwords[i]:=_wordcloud_pathwords[i] rotated (_wordcloud_rotation(rotation,_rotation_bool)) shifted
+ (_position_pt + _random_pt);
+ endfor;
+enddef;
+
+
+vardef dotproduct(expr a,b)=
+ save _output;
+ _output := (xpart a)*(xpart b)+(ypart a)*(ypart b);
+ _output
+enddef;
+
+vardef normvec(expr p)=
+ p/(sqrt((xpart p)**2+(ypart p)**2))
+enddef;
+
+vardef bb_projection(expr p,a)=
+ % p : edge of a rectangle (pair)
+ % a : axis of proj
+ dotproduct(p,a)
+enddef;
+
+% intersection of oriented rectangel
+vardef rectangles_intersection(expr tic,tac)=
+ % tic, tac, two rectangular path (closed)
+ save x,y,a,b,normedge,edge,_min,_max,_dotprod,_output,_minEdge,_maxEdge;
+ pair normedge,edge;
+ boolean _output;
+ _output:=false;
+ numeric x[],y[],a[],b[];
+ (x1,y1) = point 1 of tic;
+ (x2,y2) = point 2 of tic;
+ (x3,y3) = point 3 of tic;
+ (x4,y4) = point 4 of tic;
+ (a1,b1) = point 1 of tac;
+ (a2,b2) = point 2 of tac;
+ (a3,b3) = point 3 of tac;
+ (a4,b4) = point 4 of tac;
+ for i=1 upto 4:
+ %show (x[i],y[i]);
+ endfor;
+ for i=1 upto 4:
+ %show (a[i],b[i]);
+ endfor;
+ %draw (a1,b1)--(a2,b2)--(a3,b3)--(a4,b4);
+ for i=1 upto 2: % on prend deux côtés perpendiculaire du rectangle 1
+ %show "premier";
+ %show i;
+ edge:=(x[i+1],y[i+1])-(x[i],y[i]);
+ %show edge;
+ normedge:=normvec(edge);
+ for j=1 upto 4: %pour chaque point de deuxième triangle on projette sur
+ % l’axe du côté du rectangle 1 qu’on considère
+ _dotprod:=dotproduct((x[j],y[j]),normedge);
+ if(j=1):
+ _minEdge:=_dotprod;
+ _maxEdge:=_dotprod;
+ fi
+ if(_dotprod<_minEdge):
+ _minEdge:=_dotprod;
+ else:
+ if(_dotprod>_maxEdge):
+ _maxEdge:=_dotprod;
+ fi
+ fi
+ %show _minEdge;
+ %show _maxEdge;
+ endfor;
+ %show _minEdge;
+ %show _maxEdge;
+
+ for j=1 upto 4: %pour chaque point de deuxième triangle on projette sur
+ % l’axe du côté du rectangle 1 qu’on considère
+ _dotprod:=dotproduct((a[j],b[j]),normedge);
+ if(j=1):
+ _min:=_dotprod;
+ _max:=_dotprod;
+ fi
+ if(_dotprod<_min):
+ _min:=_dotprod;
+ else:
+ if(_dotprod>_max):
+ _max:=_dotprod;
+ fi
+ fi
+ endfor;
+ %show _min;
+ %show _max;
+ if (_min>_maxEdge) or (_max<_minEdge):
+ %show "OK";
+ _output:=true;
+ fi
+ endfor; %the other way
+ for i=1 upto 2: % on prend deux côtés perpendiculaire du rectangle 1
+ %show "deuxième";
+ %show i;
+ edge:=(a[i+1],b[i+1])-(a[i],b[i]);
+ %show edge;
+ normedge:=normvec(edge);
+ for j=1 upto 4: %pour chaque point de deuxième triangle on projette sur
+ % l’axe du côté du rectangle 1 qu’on considère
+ _dotprod:=dotproduct((a[j],b[j]),normedge);
+ if(j=1):
+ _minEdge:=_dotprod;
+ _maxEdge:=_dotprod;
+ fi
+ if(_dotprod<_minEdge):
+ _minEdge:=_dotprod;
+ else:
+ if(_dotprod>_maxEdge):
+ _maxEdge:=_dotprod;
+ fi
+ fi
+ %show _minEdge;
+ %show _maxEdge;
+ endfor;
+ %show _minEdge;
+ %show _maxEdge;
+
+ for j=1 upto 4: %pour chaque point de deuxième triangle on projette sur
+ % l’axe du côté du rectangle 1 qu’on considère
+ _dotprod:=dotproduct((x[j],y[j]),normedge);
+ if(j=1):
+ _min:=_dotprod;
+ _max:=_dotprod;
+ fi
+ if(_dotprod<_min):
+ _min:=_dotprod;
+ else:
+ if(_dotprod>_max):
+ _max:=_dotprod;
+ fi
+ fi
+ endfor;
+ if (_min>_maxEdge) or (_max<_minEdge):
+ %show "OK";
+ _output:=true;
+ fi
+ endfor;
+ not _output
+enddef;
+
diff --git a/graphics/metapost/contrib/macros/wordcloud/scripts/wordcloud.lua b/graphics/metapost/contrib/macros/wordcloud/scripts/wordcloud.lua
new file mode 100644
index 0000000000..209ab0ca71
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/scripts/wordcloud.lua
@@ -0,0 +1,224 @@
+--[[
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% wordcloud %%
+%% drawing wordclouds %%
+%% with METAPOST and Lua %%
+%% chupin@ceremade.dauphine.fr %%
+%% Version 0.1 (aout 2023) %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This work may be distributed and/or modified under the conditions of
+% the LaTeX Project Public License, either version 1.3c 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
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+]]
+function wc_file2string(file)
+ local f = assert(io.open(file, "rb"))
+ local content = f:read("*all")
+ f:close()
+ return content
+end
+
+function wc_table_concat(t1,t2)
+ for i=1,#t2 do
+ t1[#t1+1] = t2[i]
+ end
+ return t1
+end
+
+function wc_ignor_fr()
+ local output;
+ output= {"et", "donc", "alors", "les", "ainsi", "des", "que", "qui", "sont", "par", "dans","est","pour","une","peut","avec","sans","Les","son","sur","ces","ses","tandis","quand"}
+ return output
+end
+
+function wc_ignor_en()
+ local output;
+ output= {"and", "so", "the", "the", "hence", "some", "that", "are", "is", "have", "has","in","on","could","should","with","be","this","these","where","when","not","with","can",}
+ return output
+end
+
+-- global variable for ignored words
+wordcloud_ignor_words = {};
+wc_table_concat(wordcloud_ignor_words,wc_ignor_fr())
+wc_table_concat(wordcloud_ignor_words,wc_ignor_en())
+
+-- function to add list of words to be ignored
+function wc_add_ignored_words(tab)
+ wc_table_concat(wordcloud_ignor_words,tab)
+end
+
+-- function to build a tab from a string of the form
+-- word1,word2,word3,etc.
+function wc_string_to_tab(strWord)
+ strWord=string.gsub(strWord, "%s+", "") -- space delete
+ local tab
+ tab = string.explode(strWord, ",")
+ return tab
+end
+
+function wc_build_word_table(mystr)
+ local loc_str = mystr;
+ local ignor_chars = {";","’","'","\"","{","}","[","]","(",")","…",".","?","!","$","\\","#","<",">","«","»","+","*","-","/","=","%%", "€",":","~"}
+ loc_str = loc_str:gsub('[%p%c]+',' ')
+ for i=1,#ignor_chars do
+ loc_str = loc_str:gsub('['..ignor_chars[i]..']+',' ')
+ end
+ for i=1,#wordcloud_ignor_words do
+ loc_str = loc_str:gsub(' '..wordcloud_ignor_words[i]..' ',' ')
+ end
+ loc_str = loc_str:gsub(' LaTeX ',' \\LaTeX ')
+ loc_str = loc_str:gsub(' TeX ',' \\TeX ')
+ local t = {}
+ for i in loc_str:gmatch("%S+") do
+ t[#t + 1] = i
+ end
+ -- delete 2 size words
+ local id_two={}
+ for i=1,#t do
+ if(string.len(t[i])<=2) then
+ table.insert(id_two,i)
+ end
+ end
+ for i=#id_two,1,-1 do
+ table.remove(t,id_two[i])
+ end
+ return t
+end
+
+function wc_build_table_weight(t)
+ local weight = {}
+ for _, v in ipairs(t) do
+ weight[v] = (weight[v] or 0) + 1
+ end
+ return weight
+end
+
+function wc_table_to_tabular(table_weight)
+ local total_occ = 0
+ local tabular_weight = {}
+ for k,v in pairs(table_weight) do
+ total_occ=total_occ+v
+ end
+ local i=0
+ for k,v in pairs(table_weight) do
+ i=i+1
+ tabular_weight[i] ={}
+ tabular_weight[i][1]=k
+ tabular_weight[i][2]=v/total_occ
+ end
+ table.sort(tabular_weight,function (k1, k2) return k1[2] > k2[2] end)
+ return tabular_weight
+end
+
+function wc_build_mp_code(table_weight,maximum,rotation)
+ -- optional arguments
+ maximum = maximum or 50
+ rotation = rotation or 0
+
+ local total_occ = 0
+ local tabular_weight = wc_table_to_tabular(table_weight)
+
+
+ local str_mp=[[
+ string words[];
+ numeric weights[];
+ ]]
+ local i=0
+ for i=1, #tabular_weight do
+
+ str_mp=str_mp.."words["..i.."]:=\""..tabular_weight[i][1].."\";"
+ str_mp=str_mp.."weights["..i.."]:="..tabular_weight[i][2]..";"
+ if (i>=maximum) then
+ break
+ end
+ end
+ str_mp=str_mp.."draw_wordcloud(words,weights,"..rotation..","..math.min(maximum,#tabular_weight)..");"
+ return str_mp
+end
+
+function wc_list_to_table(list)
+ -- list of words and weights (word1,weight1);(word2,weight2); etc.
+ local table_weight = {}
+ local pair = string.explode(list, ";")
+ local lgth=#pair
+ for i=1,lgth do
+ word,weight=string.match(pair[i],"%((.+),(.+)%)")
+ table_weight[word]=weight
+ end
+ return table_weight
+end
+
+function wc_size_of_table(table)
+ local lengthNum = 0
+
+ for k, v in pairs(table) do -- for every key in the table with a corresponding non-nil value
+ lengthNum = lengthNum + 1
+ end
+ return lengthNum
+end
+
+
+function wc_build_color_list(colors)
+ -- list of LaTeX colors separated with colons
+ local out = ""
+ local pair = string.explode(colors:sub(1,-2), ";") -- sub(1,-2) to delete last ;
+ local lgth=#pair
+ for i=1,lgth do
+ out=out.."wordcloud_colors["..i.."]:="..pair[i]..";";
+ end
+ out=out.."wordcloud_colors_number:="..lgth..";"
+ return out
+end
+
+-- build mp code for the wordcloud of a list given in LaTeX command
+function wc_build_wordcloud(str,rotation,scale,margin,usecolor,colors)
+
+ maximum = maximum or 50
+ local table = wc_list_to_table(str)
+ local lgth_table = wc_size_of_table(table)
+ local output
+ output= [[\begin{mplibcode}[wordcloud]
+ input wordcloud
+ beginfig(0);
+ ]]
+ if(usecolor=="true") then
+ output = output.."wordcloud_use_color(true);"
+ if(colors~="") then
+ output=output..wc_build_color_list(colors)
+ end
+ end
+ output = output.."set_wordcloud_scale("..scale..");"
+ output = output.."set_box_margin("..margin..");"
+ output = output..wc_build_mp_code(table,lgth_table,rotation)
+ output = output.."endfig;\\end{mplibcode}"
+ tex.sprint(output)
+end
+
+
+-- build mp code for the wordcloud of a file given in LaTeX command
+function wc_build_wordcloud_file(file,number,rotation,scale,margin,usecolor,colors)
+ local str = wc_file2string(file)
+
+ local words = wc_build_word_table(str)
+
+ local table_weight = wc_build_table_weight(words)
+ local output
+ output= [[\begin{mplibcode}[wordcloud]
+ input wordcloud
+ beginfig(0);
+ ]]
+ if(usecolor=="true") then
+ output = output.."wordcloud_use_color(true);"
+ if(colors~="") then
+ output=output..wc_build_color_list(colors)
+ end
+ end
+ output = output.."set_wordcloud_scale("..scale..");"
+ output = output.."set_box_margin("..margin..");"
+ output = output..wc_build_mp_code(table_weight,number,rotation)
+ output = output.."endfig;\\end{mplibcode}"
+ tex.sprint(output)
+end
diff --git a/graphics/metapost/contrib/macros/wordcloud/tex/wordcloud.sty b/graphics/metapost/contrib/macros/wordcloud/tex/wordcloud.sty
new file mode 100644
index 0000000000..6fce2b03d9
--- /dev/null
+++ b/graphics/metapost/contrib/macros/wordcloud/tex/wordcloud.sty
@@ -0,0 +1,149 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% wordcloud %%
+%% drawing wordclouds %%
+%% with METAPOST and Lua %%
+%% chupin@ceremade.dauphine.fr %%
+%% Version 0.1 (aout 2023) %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This work may be distributed and/or modified under the conditions of
+% the LaTeX Project Public License, either version 1.3c 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
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\NeedsTeXFormat{LaTeX2e}%
+\def\PackageName{wordcloud}%
+\def\fileversion{v0.1}%
+\def\filedate{2023/07/26}%
+\ProvidesPackage{wordcloud}[\filedate\space\fileversion]%
+%
+% package to load
+\RequirePackage{luamplib}%
+
+%
+% load the lua code
+\directlua{require("wordcloud.lua")}%
+
+\ExplSyntaxOn
+
+% variable globale
+\tl_new:N \g__wc_wordcloud_scale_tl
+\tl_new:N \g__wc_wordcloud_rotate_tl
+\tl_new:N \g__wc_wordcloud_margin_tl
+
+% definitions des clés/valeurs pour la commande \wordcloud
+\keys_define:nn{wc/wordcloud}{%
+ scale .tl_gset:N = \g__wc_wordcloud_scale_tl,
+ scale .value_required:n = true,
+ rotate .tl_gset:N = \g__wc_wordcloud_rotate_tl,
+ rotate .value_required:n = true,
+ margin .tl_gset:N = \g__wc_wordcloud_margin_tl,
+ margin .value_required:n = true,
+ usecolor .bool_set:N = \g__wc_wordcloud_usecolor_bool,
+ colors .clist_set:N = \g__wc_wordcloud_colors_clist, % list of LaTeX colors
+ colors .value_required:n = true,
+% usecolor .default:n = true,
+}
+
+% local variable to stock metapost colors list
+\tl_new:N \l_colors_list_tl%
+
+% function to build a wordcloud from a list of the form :
+% (word1,weight1);(word2,weight2);etc.
+\NewDocumentCommand\wordcloud{ o m }{%
+ % mandatory argument : list (word1,weight1);(word2,weight2);etc.
+ % optional arguments :
+ % scale=<value> (default 1)
+ % rotation=<value> (default 0)
+ % margin=<value> (default 0.3pt)
+ % usecolor=true/false (default false)
+ % colors={color1,color2,etc}
+ % pour les clés à valeur par défaut
+ \keys_set:nn { wc/wordcloud } { scale=1 } %
+ \keys_set:nn { wc/wordcloud } { rotate=0 } %
+ \keys_set:nn { wc/wordcloud } { margin=0.3pt } %
+ \keys_set:nn { wc/wordcloud } { colors={} } %
+ \keys_set:nn { wc/wordcloud } { usecolor=false } %
+
+ \tl_clear:N \l_colors_list_tl%
+ \IfNoValueF {#1}
+ {
+ \keys_set:nn { wc/wordcloud } { #1 }
+ }
+
+ % parse the list to build a list of rgb colors for metapost code
+
+ \clist_map_variable:NNn \g__wc_wordcloud_colors_clist \l_onecolor_tl {%
+ \extractcolorspecs{\tl_use:N
+ \l_onecolor_tl}{\wc_onecolor_type}{\wc_onecolor_values}
+ % get the type and values of color (see xcolor doc)
+ \convertcolorspec{\wc_onecolor_type}{\wc_onecolor_values}{rgb}\wc_rgb_values
+ % convert to rgb (see xcolor loc)
+ \tl_gput_right:Nx \l_colors_list_tl {(\wc_rgb_values);} % add value with
+ %parenthesis
+ }%
+
+
+ %\show {coucous}
+ \directlua{%
+ %tex.sprint("test")
+ wc_build_wordcloud("#2",\tl_use:N \g__wc_wordcloud_rotate_tl,\tl_use:N \g__wc_wordcloud_scale_tl,"\tl_use:N \g__wc_wordcloud_margin_tl",\bool_if:NTF \g__wc_wordcloud_usecolor_bool{"true"}{"false"},"\tl_use:N \l_colors_list_tl")
+ }%
+}
+
+
+% function to build a wordcloud from a text file :
+\NewDocumentCommand\wordcloudFile{ o m m}{%
+ % two mandatory arguments : name of the file and number of words for the
+ % wordcloud
+ % optional arguments :
+ % scale=<value> (default 1)
+ % rotation=<value> (default 0)
+ % margin=<value> (default 0.3pt)
+ % usecolor=true/false (default false)
+ % colors={color1,color2,etc}
+
+ % pour les clés à valeur par défaut
+ \keys_set:nn { wc/wordcloud } { scale=1 } %
+ \keys_set:nn { wc/wordcloud } { rotate=0 } %
+ \keys_set:nn { wc/wordcloud } { margin=0.3pt } %
+ \keys_set:nn { wc/wordcloud } { colors={} } %
+ \keys_set:nn { wc/wordcloud } { usecolor=false } %
+ \tl_clear:N \l_colors_list_tl%
+
+ \IfNoValueF {#1}
+ {
+ \keys_set:nn { wc/wordcloud } { #1 }
+ }
+
+ % parse the list to build a list of rgb colors for metapost code
+
+ \clist_map_variable:NNn \g__wc_wordcloud_colors_clist \l_onecolor_tl {%
+ \extractcolorspecs{\tl_use:N
+ \l_onecolor_tl}{\wc_onecolor_type}{\wc_onecolor_values}
+ % get the type and values of color (see xcolor doc)
+ \convertcolorspec{\wc_onecolor_type}{\wc_onecolor_values}{rgb}\wc_rgb_values
+ % convert to rgb (see xcolor loc)
+ \tl_gput_right:Nx \l_colors_list_tl {(\wc_rgb_values);} % add value with
+ %parenthesis
+ }%
+
+ %\show {coucous}
+ \directlua{%
+ wc_build_wordcloud_file("#2",#3,\tl_use:N \g__wc_wordcloud_rotate_tl,\tl_use:N \g__wc_wordcloud_scale_tl,"\tl_use:N \g__wc_wordcloud_margin_tl",\bool_if:NTF \g__wc_wordcloud_usecolor_bool{"true"}{"false"},"\tl_use:N \l_colors_list_tl")
+ }%
+}
+
+% function to add words to ignor when a wordcloud is build from a text file
+\NewDocumentCommand\wordcloudIgnoreWords{m}{%
+ % mandatory argument : list of words separated by commas
+ % word1, word2, word3, etc.
+ \directlua{%
+ tab=wc_string_to_tab("#1")
+ wc_add_ignored_words(tab)
+ }%
+}
+
+
+\ExplSyntaxOff \ No newline at end of file