%% eurion.sty -- EURion constellation overlay for LaTeX documents %% v0.1.0, 2026-04-26 %% Copyright (C) 2026 originalsouth <5300799+originalsouth@users.noreply.github.com> %% %% License: WTFPL (see LICENSE). The author does not endorse this %% license; it was picked because CTAN distribution requires *some* %% license to be selected. %% %% Draws the five-circle "EURion" anti-photocopy pattern across document %% pages. Visual reproduction only -- not a real anti-counterfeiting feature. %% %% Usage: %% \usepackage{eurion} % default scatter, gold %% \usepackage[scatter, color=yellow]{eurion} %% \usepackage[corner=top-right, color=green]{eurion} %% \usepackage[manual]{eurion} % then call \eurion[..]{x,y} %% %% References used to derive the geometry: %% - https://gist.github.com/petrkutalek/93b7eebf22f4dbcfab8cd80a91fd27a8 %% - https://en.wikipedia.org/wiki/EURion_constellation %% - https://everything2.com/user/spiregrain/writeups/EURion+Constellation \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{eurion}[2026/04/26 v0.1.0 EURion constellation overlay] \RequirePackage{xkeyval} \RequirePackage{tikz} \RequirePackage{eso-pic} %% --- color presets ------------------------------------------------------- \definecolor{eurion@gold}{cmyk}{0,0.1933,0.5714,0.0667}% kutalek default \definecolor{eurion@yellow}{rgb}{1,0.85,0} \definecolor{eurion@green}{rgb}{0.45,0.75,0.3} \definecolor{eurion@orange}{rgb}{1,0.6,0.1} %% --- option storage with defaults --------------------------------------- \def\eurion@mode{scatter} \def\eurion@color{eurion@gold} \def\eurion@size{1} \def\eurion@density{20} \def\eurion@rotate{random} \def\eurion@corner{north east} \def\eurion@seed{1} \def\eurion@rot@random{random} \def\eurion@rot@none{none} %% --- corner-name parser (top-right -> north east, etc.) ---------------- \def\eurion@parsecorner#1{% \def\eurion@tmp{#1}% \def\eurion@TL{top-left}\def\eurion@TR{top-right}% \def\eurion@BL{bottom-left}\def\eurion@BR{bottom-right}% \ifx\eurion@tmp\eurion@TL \def\eurion@corner{north west}\else \ifx\eurion@tmp\eurion@TR \def\eurion@corner{north east}\else \ifx\eurion@tmp\eurion@BL \def\eurion@corner{south west}\else \ifx\eurion@tmp\eurion@BR \def\eurion@corner{south east}\else \def\eurion@corner{north east}% \fi\fi\fi\fi} %% --- color-name resolver (preset vs user xcolor expr) ------------------ \def\eurion@parsecolor#1{% \@ifundefined{\string\color @eurion@#1}% {\def\eurion@color{#1}}% user color or xcolor expression {\def\eurion@color{eurion@#1}}}% built-in preset %% --- runtime keyval keys (used by \eurion[...] at point of call) ------- \define@key{eurion}{color}{\eurion@parsecolor{#1}} \define@key{eurion}{size}{\def\eurion@size{#1}} \define@key{eurion}{density}{\def\eurion@density{#1}} \define@key{eurion}{rotate}{\def\eurion@rotate{#1}} \define@key{eurion}{seed}{\def\eurion@seed{#1}} \define@key{eurion}{corner}{\eurion@parsecorner{#1}} \define@key{eurion}{mode}{\def\eurion@mode{#1}} %% --- package options (load-time) --------------------------------------- \DeclareOptionX{scatter}[]{\def\eurion@mode{scatter}} \DeclareOptionX{manual}[]{\def\eurion@mode{manual}} \DeclareOptionX{corner}[__flag__]{% \def\eurion@tmpa{#1}\def\eurion@tmpb{__flag__}% \ifx\eurion@tmpa\eurion@tmpb \def\eurion@mode{corner}% \else \eurion@parsecorner{#1}% \def\eurion@mode{corner}% \fi} \DeclareOptionX{mode}{\def\eurion@mode{#1}} \DeclareOptionX{color}{\eurion@parsecolor{#1}} \DeclareOptionX{size}{\def\eurion@size{#1}} \DeclareOptionX{density}{\def\eurion@density{#1}} \DeclareOptionX{rotate}{\def\eurion@rotate{#1}} \DeclareOptionX{seed}{\def\eurion@seed{#1}} \DeclareOptionX*{\PackageWarning{eurion}{Unknown option `\CurrentOption' ignored}} \ProcessOptionsX %% --- the constellation itself ------------------------------------------ %% Polar centers (from petrkutalek): (0,0), (2.5,0deg), (2.6,78deg), %% (4.0,188deg), (3.8,258deg), all in mm. Stroked 0.4mm rings. %% Public lore: 1mm circle diameter, ~4mm max spread, squared distances %% reportedly form integer ratios (Andrew Steer); the exact spec is secret. \newcommand{\eurion@constellation}{% \draw (0mm,0mm) circle (0.4mm); \draw (2.5mm,0mm) circle (0.4mm); \begin{scope}[rotate=78] \draw (2.6mm,0mm) circle (0.4mm); \begin{scope}[rotate=110] \draw (4.0mm,0mm) circle (0.4mm); \begin{scope}[rotate=70] \draw (3.8mm,0mm) circle (0.4mm); \end{scope} \end{scope} \end{scope}% } %% --- rotation resolver ------------------------------------------------- %% Sets \eurion@theta from \eurion@rotate ("random" / "none" / number). \newcommand{\eurion@computetheta}{% \ifx\eurion@rotate\eurion@rot@random \pgfmathsetmacro{\eurion@theta}{rnd*360}% \else \ifx\eurion@rotate\eurion@rot@none \def\eurion@theta{0}% \else \edef\eurion@theta{\eurion@rotate}% \fi \fi } %% --- scatter background ----------------------------------------------- \newcommand{\eurion@scatterpage}{% \begin{tikzpicture}[remember picture, overlay] \pgfmathsetseed{\numexpr\eurion@seed + \value{page}\relax}% \pgfmathsetmacro{\eurion@pw}{\paperwidth}% \pgfmathsetmacro{\eurion@ph}{\paperheight}% \foreach \eurion@i in {1,...,\eurion@density}{% \pgfmathsetmacro{\eurion@x}{rnd*\eurion@pw}% \pgfmathsetmacro{\eurion@y}{rnd*\eurion@ph}% \eurion@computetheta \begin{scope}[shift={(\eurion@x pt, \eurion@y pt)}, rotate=\eurion@theta, scale=\eurion@size, draw=\eurion@color, line width=0.3mm] \eurion@constellation \end{scope}% }% \end{tikzpicture}% } %% --- corner background ------------------------------------------------ \newcommand{\eurion@cornerpage}{% \begin{tikzpicture}[remember picture, overlay] \begin{scope}[shift={([shift={(\eurion@dx,\eurion@dy)}]current page.\eurion@corner)}, scale=\eurion@size, draw=\eurion@color, line width=0.3mm] \eurion@constellation \end{scope} \end{tikzpicture}% } %% --- install background hook based on selected mode -------------------- \AtBeginDocument{% \def\eurion@modes{scatter}% \def\eurion@modec{corner}% \def\eurion@modem{manual}% \ifx\eurion@mode\eurion@modes \AddToShipoutPictureBG{\eurion@scatterpage}% \else \ifx\eurion@mode\eurion@modec \def\eurion@nw{north west}\def\eurion@ne{north east}% \def\eurion@sw{south west}\def\eurion@se{south east}% \ifx\eurion@corner\eurion@nw \def\eurion@dx{ 15mm}\def\eurion@dy{-15mm}\else \ifx\eurion@corner\eurion@ne \def\eurion@dx{-15mm}\def\eurion@dy{-15mm}\else \ifx\eurion@corner\eurion@sw \def\eurion@dx{ 15mm}\def\eurion@dy{ 15mm}\else \ifx\eurion@corner\eurion@se \def\eurion@dx{-15mm}\def\eurion@dy{ 15mm}\else \def\eurion@dx{-15mm}\def\eurion@dy{-15mm}% \fi\fi\fi\fi \AddToShipoutPictureBG{\eurion@cornerpage}% \fi \fi } %% --- public macro: \eurion[opts]{coord-or-x,y} ------------------------- %% The user passes a TikZ coordinate (e.g. "current page.center" or %% "5cm,3cm"); the macro draws one constellation there in an overlay. \newcommand{\eurion}[2][]{% \begingroup \setkeys{eurion}{#1}% \eurion@computetheta \begin{tikzpicture}[remember picture, overlay] \begin{scope}[shift={(#2)}, rotate=\eurion@theta, scale=\eurion@size, draw=\eurion@color, line width=0.3mm] \eurion@constellation \end{scope} \end{tikzpicture}% \endgroup } \endinput