
Рассмотрим следующий код для пакета:
% Preamble
\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{mypackage}[2024/01/01 MyPackage]
\makeatletter
% Dependencies
\RequirePackage{pgfopts}
\RequirePackage{xparse}
\RequirePackage{xcolor}
% Package options
\pgfkeys{
/mypackage/.cd,
firstcolor/.store in = \mypackage@firstcolor,
firstcolor = blue,
secondcolor/.store in = \mypackage@secondcolor,
secondcolor = red,
}
\ProcessPgfPackageOptions{/mypackage}
% Definition of `\setcolors` here
% Definition of `\resetcolors` here
% Definition of `\printcolors` here
% Definition of `\newcolortheme` here
% End
\makeatother
Я хотел бы определить три следующие функции со следующим поведением:
\setcolors
: изменениеfirstcolor
иsecondcolor
в момент вызова для остальной части документа\resetcolors
: забыть все изменения, сделанные setcolors, и вернуться к исходным параметрам пакета\printcolors
: по умолчанию используются текущие глобальныеfirstcolor
иsecondcolor
, но есть необязательный параметр для переопределения глобальных значений локальными значениями\newcolortheme
: предложить возможность создать цветовую тему, чтобы дать специальное имя пареfirstcolor
иsecondcolor
Со стороны пользователя это будет выглядеть так:
\documentclass[12pt]{article}
\usepackage[firstcolor = green, secondcolor = yellow]{mypackage}
\newcolortheme{mytheme}{firstcolor = brown, secondcolor = cyan}
\begin{document}
\printcolors % Will print green and yellow
\printcolors[secondcolor = blue] % Will print green and blue
\printcolors % Will print green and yellow
\setcolors{secondcolor = black}
\printcolors % Will print green and black
\printcolors[firstcolor = purple] % Will print purple and black
\printcolors % Will print green and black
\setcolors{firstcolor = red}
\printcolors % Will print red and black
\setcolors{firstcolor = orange, secondcolor = pink}
\printcolors % Will print orange and pink
\resetcolors
\printcolors % Will print green and yellow
\printcolors[mytheme] % Will print brown and cyan
\printcolors % Will print green and yellow
\setcolors{mytheme}
\printcolors % Will print brown and cyan
\end{document}
Я понятия не имею, как это осуществить.
ВОПРОС :Как определить \setcolors
, \resetcolors
, \printcolors
и \newcolortheme
как это сделать?
решение1
Вот возможный метод:
\begin{filecontents*}[overwrite]{mypackage.sty}
% Preamble
\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{mypackage}[2024/01/01 MyPackage]
% Dependencies
\RequirePackage{pgfopts}
\RequirePackage{xparse}
\RequirePackage{xcolor}
\pgfkeys{/mypackage/.is family}
\def\mypackage@set#1{\pgfkeys{/mypackage,#1}}
\mypackage@set{
% storage
first-color/.store in=\mypackage@firstcolor,
second-color/.store in=\mypackage@secondcolor,
% default value
first-color=blue,
second-color=red,
mypackage@init/.style/.expanded={
first-color=\mypackage@firstcolor,
second-color=\mypackage@secondcolor
}
}
\ProcessPgfPackageOptions{/mypackage}
\mypackage@set{
mypackage@init/.style/.expanded={
first-color=\mypackage@firstcolor,
second-color=\mypackage@secondcolor
}
}
\NewDocumentCommand\newcolortheme{mm}{\mypackage@set{#1/.style={#2}}}
\NewDocumentCommand\printcolors{O{}}{%
\bgroup%
\mypackage@set{#1}%
\mypackage@firstcolor{} and \mypackage@secondcolor{}\par%
\egroup
}
\NewDocumentCommand\setcolors{m}{\mypackage@set{#1}}
\NewDocumentCommand\resetcolors{}{\mypackage@set{mypackage@init}}
\end{filecontents*}
\documentclass[preview=true,varwidth=true]{standalone}
\usepackage[first-color=green, second-color=yellow]{mypackage}
\newcolortheme{my theme}{first-color=brown, second-color=cyan}
% \setcolors{my theme/.style={first-color=brown, second-color=cyan}}
\begin{document}
\printcolors % Will print green and yellow
\printcolors[second-color=blue] % Will print green and blue
\printcolors % Will print green and yellow
\setcolors{second-color=black}
\printcolors % Will print green and black
\printcolors[first-color=purple] % Will print purple and black
\printcolors % Will print green and black
\setcolors{first-color=red}
\printcolors % Will print red and black
\setcolors{first-color=orange, second-color=pink}
\printcolors % Will print orange and pink
\resetcolors
\printcolors % Will print green and yellow
\printcolors[my theme] % Will print brown and cyan
\printcolors % Will print green and yellow
\setcolors{my theme}
\printcolors % Will print brown and cyan
\end{document}
(Примечание: \setcolor
макрос похож на псевдоним \mypackage@set
)
Результат
решение2
Ниже показаны два метода, позволяющие достичь этого без \printcolors
необходимости какой-либо группировки (что может быть предпочтительным, а может и нет).
Оба эти кода используют пакеты семейства expkv
(Отказ от ответственности:автором которого я являюсь). Первый использует expkv-cs
в своей основе , что делает его довольно простым в написании кода, которому не нужны группы (так как обработчику key=value не нужны никакие назначения, поэтому макрос \printcolors
работает исключительно путем расширения и без назначений — если исключить разбор необязательного аргумента из этого утверждения). Второй просто сбрасывает значения по умолчанию при каждом вызове (довольно производительным образом) и \setcolors
просто изменяет макрос, используемый для установки обратно на значения по умолчанию.
\begin{filecontents}{\jobname.sty}
\ProvidesPackage{\@currname}[2024-01-24 adhoc test package]
\RequirePackage{expkv-cs,expkv-opt,expkv-def}
% define keys as used for the package (we don't need the keys defined here
% otherwise)
\ekvdefinekeys{\@currname}
{
store firstcolor = \my@firstcolor
,initial firstcolor = blue
,store secondcolor = \my@secondcolor
,initial secondcolor = red
}
\ekvoProcessLocalOptions{\@currname}
% parsing for an optional argument
\newcommand\printcolors[1][]{\@printcolors{#1}}
% assign default values ('o: ' means expand value once)
\ekvcSplit\@printcolors
{
o: firstcolor = \my@firstcolor
,o: secondcolor = \my@secondcolor
}
{#1 and #2\par}
% just change the default values of the underlying ekvc-macro
\protected\def\setcolors{\ekvcChange\@printcolors}
% store the current values (package defaults), using \edef for expansion
\protected\edef\resetcolors
{%
\setcolors
{%
% control the expansion to only do a single step (most likely
% unnecessary but we never know with arbitrary user input)
firstcolor = {\unexpanded\expandafter{\my@firstcolor}}
,secondcolor = {\unexpanded\expandafter{\my@secondcolor}}
}%
}
% just define '#1' to be considered additional key=value input '#2' using
% ekvcSecondaryKeys.
\protected\def\newcolortheme#1#2%
{\ekvcSecondaryKeys\@printcolors{nmeta #1 = {#2}}}
\end{filecontents}
\begin{filecontents}{\jobname2.sty}
\ProvidesPackage{\@currname}[2024-01-24 adhoc test package]
\RequirePackage{expkv-opt,expkv-def}
\ekvdefinekeys{my}
{
store firstcolor = \my@firstcolor
,initial firstcolor = blue
,store secondcolor = \my@secondcolor
,initial secondcolor = red
}
\ekvsetdef\@setcolors@keys{my}
\ekvoProcessLocalOptions{my}
% common code used to save the current values of all keys that should be
% persistent.
% 'o: ' means expand the value once before passing it to the key's code.
\newcommand*\my@storecolours@keys
{%
o: firstcolor = \my@firstcolor
,o: secondcolor = \my@secondcolor
}
% \@resetcolors is used to store the package default values
% 'R: ' means expand the following macro once and use it as key=value input
\protected\ekvcompile\@resetcolors{my}{R: \my@storecolours@keys}
% \@setcolors is used to restore the currently valid colours on each call
\protected\def\resetcolors{\let\@setcolors\@resetcolors}
\resetcolors
\protected\def\setcolors#1%
{%
% bit of juggling to get the normal key=value parser, but also build a fast
% list of finalised colours
\begingroup
\ekvset{my}{#1}%
\ekvcompile\@setcolors{my}{R: \my@storecolours@keys}%
% expansion trick to store the meaning of \@setcolors as valid inside this
% group outside the group, without needing any global assignments
\expanded{\endgroup
\protected\edef\noexpand\@setcolors
{\noexpand\unexpanded{\unexpanded\expandafter{\@setcolors}}}%
}%
}
% just treat the key '#1' as additional key=value input containing '#2' (rather
% straight forward)
\protected\def\newcolortheme#1#2{\ekvdefNoVal{my}{#1}{\ekvmorekv{#2}}}
\newcommand\printcolors[1][]
{%
% restore current default values
\@setcolors
% maybe set different values
\@setcolors@keys{#1}%
% use values
\my@firstcolor\space and \my@secondcolor
\par
}
\end{filecontents}
\documentclass[12pt]{article}
\usepackage[firstcolor = green, secondcolor = yellow]{\jobname} % both work
% \usepackage[firstcolor = green, secondcolor = yellow]{\jobname2} % both work
\newcolortheme{mytheme}{firstcolor = brown, secondcolor = cyan}
\begin{document}
\printcolors % Will print green and yellow
\printcolors[secondcolor = blue] % Will print green and blue
\printcolors % Will print green and yellow
\bigskip
\setcolors{secondcolor = black}
\printcolors % Will print green and black
\printcolors[firstcolor = purple] % Will print purple and black
\printcolors % Will print green and black
\bigskip
\setcolors{firstcolor = red}
\printcolors % Will print red and black
\bigskip
\setcolors{firstcolor = orange, secondcolor = pink}
\printcolors % Will print orange and pink
\bigskip
\resetcolors
\printcolors % Will print green and yellow
\printcolors[mytheme] % Will print brown and cyan
\printcolors % Will print green and yellow
\bigskip
\setcolors{mytheme}
\printcolors % Will print brown and cyan
\end{document}
Независимо от того, вы \usepackage{\jobname}
или нет, \usepackage{\jobname2}
результат будет выглядеть так: