我正在开发一个用于设置和更改包的距离参数的界面。这些距离以字符串形式存储,以便用户可以根据需要以 em 和 ex 单位指定它们,并且在使用时它们将扩展为适当的值。还有一个缩放算法,允许用户快速缩放所有距离。现在,虽然每个函数都经过了测试并且可以单独运行,但当我尝试将它们放在一起时,我得到了对我来说毫无意义的错误(LaTeX 抱怨 \ifstretch 没有定义,而它显然只是前面几行)。下面是一个提取包相关部分的 MWE。按原样,它不会引发任何错误,但不会存储重新缩放的距离。如果我取消注释应该这样做的行(testing.sty 的第 100 行),则会出现错误(在最终实现的第 98 行中,测试行也被注释了)。有人能告诉我为什么这些函数不喜欢互相合作以及我该怎么做才能解决这个问题?
我对这个 MWE 的大小感到抱歉,但由于问题与这些功能的交互有关,因此修改它们以使其更简单可能会掩盖该问题。
主要文件:
\documentclass[12pt]{article}
\usepackage{testing}
\def\test{1 cm
}
\newskip\testc
\testc = \test
\multiply\testc by 17
\divide\testc by 18
\begin{document}
\gresetdim{additionallineswidth}{\test}{1}
Original length:
\test
Stored length: \makeatletter
\gre@additionallineswidth
Manually Rescaled length:
\gre@convertto{cm}{\the\testc}
Internally Rescaled length:
Testing mode: \newfunction{additionallineswidth}{18}{17} %should be blank when properly implemented.
Implementation mode: \gre@additionallineswidth %should be the same as the manually rescaled length when properly implemented
\end{document}
测试.sty
% ------IDENTIFICATION------
\NeedsTeXFormat{LaTeX2e}[2005/12/01]
\ProvidesPackage{testing}
% For testing package code.
% ------INITIAL CODE------
% ------DECLARATION OF OPTIONS------
% ------EXECUTION OF OPTIONS------
\ProcessOptions
% ------PACKAGE LOADING------
\RequirePackage{xstring}
% ------MAIN CODE------
\AtBeginDocument{}
\AtEndDocument{}
\def\greerror#1{\PackageError{GregorioTeX}{#1}{}}
%%%%%%%%%%%%%
%% Rescaling dimensions (for when \grefactor changes)
%%%%%%%%%%%%%
%% This macro sets one dim (#1) to the value #2 and sets whether it should scale when the \grefactor changes (#3, 1 if yes, 0 if no). While it does check that #1 can accept the kind of distance given in #2, it does not propagate the changes through the calculated distances.
\def\gresetdim#1#2#3{%
\newif\ifchecklength\checklengthfalse
\newif\ifbadlength\badlengthfalse
%check if #2 is a rubber length (contains plus and/or minus)
\IfSubStr{#2}{plus}{\checklengthtrue}{\relax}
\IfSubStr{#2}{minus}{\checklengthtrue}{\relax}
%if #1 is one of the distances which cannot be rubber.
\ifchecklength
\IfStrEq{#1}{additionallineswidth}{\badlengthtrue}{\relax}
\IfStrEq{#1}{maximumspacewithoutdash}{\badlengthtrue}{\relax}
\IfStrEq{#1}{additionalcustoslineswidth}{\badlengthtrue}{\relax}
\IfStrEq{#1}{minimalspaceatlinebeginning}{\badlengthtrue}{\relax}
\IfStrEq{#1}{manualinitialwidth}{\badlengthtrue}{\relax}
\IfStrEq{#1}{aboveinitialseparation}{\badlengthtrue}{\relax}
\IfStrEq{#1}{noclefspace}{\badlengthtrue}{\relax}
\IfStrEq{#1}{abovesignsspace}{\badlengthtrue}{\relax}
\IfStrEq{#1}{belowsignsspace}{\badlengthtrue}{\relax}
\IfStrEq{#1}{lowchoralsignsshift}{\badlengthtrue}{\relax}
\IfStrEq{#1}{highchoralsignsshift}{\badlengthtrue}{\relax}
\IfStrEq{#1}{translationheight}{\badlengthtrue}{\relax}
\IfStrEq{#1}{abovelinestextraise}{\badlengthtrue}{\relax}
\IfStrEq{#1}{abovelinestextheight}{\badlengthtrue}{\relax}
\IfStrEq{#1}{braceshift}{\badlengthtrue}{\relax}
\IfStrEq{#1}{curlybraceaccentusshift}{\badlengthtrue}{\relax}
\IfStrEq{#1}{clivisalignmentmin}{\badlengthtrue}{\relax}
\fi
% did both of the above conditions hold true?
\ifbadlength
\greerror{#1 cannot be a rubber length.}
\else
\expandafter\xdef\csname gre@scale@#1\endcsname{#3}%
\expandafter\xdef\csname gre@#1\endcsname{#2}%
\fi
\relax %
}
%% an aux function adapting the value #1 from the factor #2 to the factor #3
%% Note: This function is assumed to touch only dimensions which are meant to scale with the \grefactor (i.e. if it acts on distance x, \gre@scale@x is 1)
\def\gre@changeonedimenfactor#1#2#3{%
\newif\ifrubber\rubbertrue%
% is length one that cannot be rubber?
\IfStrEq{#1}{additionallineswidth}{\rubberfalse}{\relax}%
\IfStrEq{#1}{maximumspacewithoutdash}{\rubberfalse}{\relax}%
\IfStrEq{#1}{additionalcustoslineswidth}{\rubberfalse}{\relax}%
\IfStrEq{#1}{minimalspaceatlinebeginning}{\rubberfalse}{\relax}%
\IfStrEq{#1}{manualinitialwidth}{\rubberfalse}{\relax}%
\IfStrEq{#1}{aboveinitialseparation}{\rubberfalse}{\relax}%
\IfStrEq{#1}{noclefspace}{\rubberfalse}{\relax}%
\IfStrEq{#1}{abovesignsspace}{\rubberfalse}{\relax}%
\IfStrEq{#1}{belowsignsspace}{\rubberfalse}{\relax}%
\IfStrEq{#1}{lowchoralsignsshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{highchoralsignsshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{translationheight}{\rubberfalse}{\relax}%
\IfStrEq{#1}{abovelinestextraise}{\rubberfalse}{\relax}%
\IfStrEq{#1}{abovelinestextheight}{\rubberfalse}{\relax}%
\IfStrEq{#1}{braceshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{curlybraceaccentusshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{clivisalignmentmin}{\rubberfalse}{\relax}%
\ifrubber% if we have a rubber allowed length we create a temporary skip
\newskip\gre@temp%
\else% otherwise we create a temporary dimen
\newdimen\gre@temp%
\fi%
% Math
\edef\gre@convert{\csname gre@#1\endcsname}%
\gre@temp=\gre@convert%
\multiply \gre@temp by \number #3%
\divide \gre@temp by \number #2%
% This testing line works
\gre@consistentunits{\gre@convert}{\gre@temp}%
% This implementation line doesn’t
% \gresetdim{#1}{\gre@consistentunits{\gre@convert}{\gre@temp}}{1}
\relax %
}
% These functions are used for stripping out the units and decimal portion of a distance to make it more amenable to being used in the conversion function below
{\catcode`p=12 \catcode`t=12 \gdef\gre@makein#1.#2pt{#1}}
{\catcode`p=12 \catcode`t=12 \gdef\gre@makenum#1pt{#1}}
% This function converts a distance to the units indicated in #1 and returns it as a string.
\def\gre@convertto#1#2{%
\ifdim#2=0pt\relax%
0 #1
\else
\newdimen\gre@unit%
\expandafter\gre@unit = 1 #1%
\newdimen\gre@base%
\gre@base = #2%
% Code to increase precision
\newdimen\gre@maxlen%
\gre@maxlen = 16383.99999pt%
\newdimen\gre@factor%
\gre@factor = \gre@maxlen%
\divide\gre@factor by \gre@unit%
\newdimen\gre@basefactor%
\gre@basefactor = \gre@maxlen%
\divide\gre@basefactor by \gre@base%
\ifdim\gre@basefactor<\gre@factor%
\advance\gre@basefactor by -10sp%
\multiply\gre@unit by \gre@basefactor%
\multiply\gre@base by \gre@basefactor%
\else%
\advance\gre@factor by -10sp%
\multiply\gre@unit by \gre@factor%
\multiply\gre@base by \gre@factor%
\fi%
\newcount\temp%
\temp = \expandafter\gre@makein\the\gre@unit%
\divide\gre@base by \temp%
\expandafter\gre@makenum\the\gre@base\relax #1%
\fi
}
% This function takes a distance (#2) and formats it as a string so that its units conform to the pattern set by a string representation of a distance (#1)
\def\gre@consistentunits#1#2{%
\newif\ifstretch\stretchfalse%
\newif\ifshrink\shrinkfalse%
\IfSubStr{#1}{plus}{\stretchtrue}{\relax}%
\IfSubStr{#1}{minus}{\shrinktrue}{\relax}%
\ifstretch%
\ifshrink%
%rubber with both stretch and shrink
\StrBefore{#1}{plus}[\gre@baseunit]%
\StrBetween{#1}{plus}{minus}[\gre@stretchunit]%
\StrBehind{#1}{minus}[\gre@shrinkunit]%
\else%
%rubber with stretch only
\StrBefore{#1}{plus}[\gre@baseunit]%
\StrBehind{#1}{plus}[\gre@stretchunit]%
\def\gre@shrinkunit{}%
\fi%
\else%
\ifshrink%
%rubber with shrink only
\StrBefore{#1}{minus}[\gre@baseunit]%
\def\gre@stretchunit{}%
\StrBehind{#1}{minus}[\gre@shrinkunit]%
\else%
%non-rubber
\def\gre@baseunit{#1}%
\def\gre@stretchunit{}%
\def\gre@shrinkunit{}%
\fi%
\fi%
\StrDel{\gre@baseunit}{ }[\gre@baseunit]%
\StrRight{\gre@baseunit}{2}[\gre@baseunit]%
\StrDel{\gre@stretchunit}{ }[\gre@stretchunit]%
\StrRight{\gre@stretchunit}{2}[\gre@stretchunit]%
\StrDel{\gre@shrinkunit}{ }[\gre@shrinkunit]%
\StrRight{\gre@shrinkunit}{2}[\gre@shrinkunit]%
\gre@convertto{\gre@baseunit}{\dimexpr#2\relax} %
\IfStrEq{}{\gre@stretchunit}{}{%
plus %
\gre@convertto{\gre@stretchunit}{\gluestretch#2}
}%
\IfStrEq{}{\gre@shrinkunit}{}{%
minus %
\gre@convertto{\gre@shrinkunit}{\glueshrink#2}
}%
}
\def\newfunction#1#2#3{%
\gre@changeonedimenfactor{#1}{#2}{#3}
}
答案1
您的宏无法通过扩展工作,因此它们在 edef 中或作为您使用的字符串测试函数的参数是不安全的。我做了一些小的清理,将\new...
宏移出到顶层,并添加了几个\show
日志显示
> \z=macro:
->\test .
\gresetdim ...badlengthfalse \def \z {#2}\show \z
\IfSubStr {#2}{plus}{\chec...
l.15 \gresetdim{additionallineswidth}{\test}{1}
?
> \endz=undefined.
\gresetdim ...cklengthtrue }{\relax } \show \endz
\IfSubStr {#2}{minus}{\che...
l.15 \gresetdim{additionallineswidth}{\test}{1}
?
> \z=macro:
->\gre@consistentunits {\gre@convert }{\gre@temp }.
\gresetdim ...badlengthfalse \def \z {#2}\show \z
\IfSubStr {#2}{plus}{\chec...
l.28 ...\newfunction{additionallineswidth}{18}{17}
%should be blank when pro...
?
)
! Incomplete \iffalse; all text was ignored after line 28.
<inserted text>
\fi
<*> ff566
?
显示第一次调用没有问题,但第二次#2
调用
\gre@consistentunits {\gre@convert }{\gre@temp }
你不能使用它作为参数,\IfSubStr
所以你永远不会到达下一行
\show\endz
修改的testing.sty
% ------IDENTIFICATION------
\NeedsTeXFormat{LaTeX2e}[2005/12/01]
\ProvidesPackage{testing}
% For testing package code.
% ------INITIAL CODE------
% ------DECLARATION OF OPTIONS------
% ------EXECUTION OF OPTIONS------
\ProcessOptions
% ------PACKAGE LOADING------
\RequirePackage{xstring}
% ------MAIN CODE------
\AtBeginDocument{}
\AtEndDocument{}
\def\greerror#1{\PackageError{GregorioTeX}{#1}{}}
%%%%%%%%%%%%%
%% Rescaling dimensions (for when \grefactor changes)
%%%%%%%%%%%%%
%% This macro sets one dim (#1) to the value #2 and sets whether it should scale when the \grefactor changes (#3, 1 if yes, 0 if no). While it does check that #1 can accept the kind of distance given in #2, it does not propagate the changes through the calculated distances.
\newif\ifchecklength
\newif\ifbadlength
\def\gresetdim#1#2#3{%
\checklengthfalse
\badlengthfalse
%check if #2 is a rubber length (contains plus and/or minus)
\def\z{#2}\show\z
\IfSubStr{#2}{plus}{\checklengthtrue}{\relax}
\show\endz
\IfSubStr{#2}{minus}{\checklengthtrue}{\relax}
%if #1 is one of the distances which cannot be rubber.
\ifchecklength
\IfStrEq{#1}{additionallineswidth}{\badlengthtrue}{\relax}
\IfStrEq{#1}{maximumspacewithoutdash}{\badlengthtrue}{\relax}
\IfStrEq{#1}{additionalcustoslineswidth}{\badlengthtrue}{\relax}
\IfStrEq{#1}{minimalspaceatlinebeginning}{\badlengthtrue}{\relax}
\IfStrEq{#1}{manualinitialwidth}{\badlengthtrue}{\relax}
\IfStrEq{#1}{aboveinitialseparation}{\badlengthtrue}{\relax}
\IfStrEq{#1}{noclefspace}{\badlengthtrue}{\relax}
\IfStrEq{#1}{abovesignsspace}{\badlengthtrue}{\relax}
\IfStrEq{#1}{belowsignsspace}{\badlengthtrue}{\relax}
\IfStrEq{#1}{lowchoralsignsshift}{\badlengthtrue}{\relax}
\IfStrEq{#1}{highchoralsignsshift}{\badlengthtrue}{\relax}
\IfStrEq{#1}{translationheight}{\badlengthtrue}{\relax}
\IfStrEq{#1}{abovelinestextraise}{\badlengthtrue}{\relax}
\IfStrEq{#1}{abovelinestextheight}{\badlengthtrue}{\relax}
\IfStrEq{#1}{braceshift}{\badlengthtrue}{\relax}
\IfStrEq{#1}{curlybraceaccentusshift}{\badlengthtrue}{\relax}
\IfStrEq{#1}{clivisalignmentmin}{\badlengthtrue}{\relax}
\fi
% did both of the above conditions hold true?
\ifbadlength
\greerror{#1 cannot be a rubber length.}
\else
\expandafter\xdef\csname gre@scale@#1\endcsname{#3}%
\expandafter\xdef\csname gre@#1\endcsname{#2}%
\fi
\relax %
}
%% an aux function adapting the value #1 from the factor #2 to the factor #3
%% Note: This function is assumed to touch only dimensions which are meant to scale with the \grefactor (i.e. if it acts on distance x, \gre@scale@x is 1)
\newif\ifrubber
\def\gre@changeonedimenfactor#1#2#3{%
\rubbertrue%
% is length one that cannot be rubber?
\IfStrEq{#1}{additionallineswidth}{\rubberfalse}{\relax}%
\IfStrEq{#1}{maximumspacewithoutdash}{\rubberfalse}{\relax}%
\IfStrEq{#1}{additionalcustoslineswidth}{\rubberfalse}{\relax}%
\IfStrEq{#1}{minimalspaceatlinebeginning}{\rubberfalse}{\relax}%
\IfStrEq{#1}{manualinitialwidth}{\rubberfalse}{\relax}%
\IfStrEq{#1}{aboveinitialseparation}{\rubberfalse}{\relax}%
\IfStrEq{#1}{noclefspace}{\rubberfalse}{\relax}%
\IfStrEq{#1}{abovesignsspace}{\rubberfalse}{\relax}%
\IfStrEq{#1}{belowsignsspace}{\rubberfalse}{\relax}%
\IfStrEq{#1}{lowchoralsignsshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{highchoralsignsshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{translationheight}{\rubberfalse}{\relax}%
\IfStrEq{#1}{abovelinestextraise}{\rubberfalse}{\relax}%
\IfStrEq{#1}{abovelinestextheight}{\rubberfalse}{\relax}%
\IfStrEq{#1}{braceshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{curlybraceaccentusshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{clivisalignmentmin}{\rubberfalse}{\relax}%
\ifrubber% if we have a rubber allowed length we create a temporary skip
\let\gre@temp\gre@temp@skip
\else% otherwise we create a temporary dimen
\let\gre@temp\gre@temp@dimen
\fi%
% Math
\edef\gre@convert{\csname gre@#1\endcsname}%
\gre@temp=\gre@convert%
\multiply \gre@temp by \number #3%
\divide \gre@temp by \number #2%
% This testing line works
\gre@consistentunits{\gre@convert}{\gre@temp}%
% This implementation line doesn’t
\gresetdim{#1}{\gre@consistentunits{\gre@convert}{\gre@temp}}{1}
\relax %
}
\newskip\gre@temp@skip
\newdimen\gre@temp@dimen
% These functions are used for stripping out the units and decimal portion of a distance to make it more amenable to being used in the conversion function below
{\catcode`p=12 \catcode`t=12 \gdef\gre@makein#1.#2pt{#1}}
{\catcode`p=12 \catcode`t=12 \gdef\gre@makenum#1pt{#1}}
% This function converts a distance to the units indicated in #1 and returns it as a string.
\def\gre@convertto#1#2{%
\ifdim#2=0pt\relax%
0 #1
\else
\newdimen\gre@unit%
\expandafter\gre@unit = 1 #1%
\newdimen\gre@base%
\gre@base = #2%
% Code to increase precision
\newdimen\gre@maxlen%
\gre@maxlen = 16383.99999pt%
\newdimen\gre@factor%
\gre@factor = \gre@maxlen%
\divide\gre@factor by \gre@unit%
\newdimen\gre@basefactor%
\gre@basefactor = \gre@maxlen%
\divide\gre@basefactor by \gre@base%
\ifdim\gre@basefactor<\gre@factor%
\advance\gre@basefactor by -10sp%
\multiply\gre@unit by \gre@basefactor%
\multiply\gre@base by \gre@basefactor%
\else%
\advance\gre@factor by -10sp%
\multiply\gre@unit by \gre@factor%
\multiply\gre@base by \gre@factor%
\fi%
\newcount\temp%
\temp = \expandafter\gre@makein\the\gre@unit%
\divide\gre@base by \temp%
\expandafter\gre@makenum\the\gre@base\relax #1%
\fi
}
% This function takes a distance (#2) and formats it as a string so that its units conform to the pattern set by a string representation of a distance (#1)
\newif\ifstretch
\newif\ifshrink
\def\gre@consistentunits#1#2{%
\stretchfalse%
\shrinkfalse%
\IfSubStr{#1}{plus}{\stretchtrue}{\relax}%
\IfSubStr{#1}{minus}{\shrinktrue}{\relax}%
\ifstretch%
\ifshrink%
%rubber with both stretch and shrink
\StrBefore{#1}{plus}[\gre@baseunit]%
\StrBetween{#1}{plus}{minus}[\gre@stretchunit]%
\StrBehind{#1}{minus}[\gre@shrinkunit]%
\else%
%rubber with stretch only
\StrBefore{#1}{plus}[\gre@baseunit]%
\StrBehind{#1}{plus}[\gre@stretchunit]%
\def\gre@shrinkunit{}%
\fi%
\else%
\ifshrink%
%rubber with shrink only
\StrBefore{#1}{minus}[\gre@baseunit]%
\def\gre@stretchunit{}%
\StrBehind{#1}{minus}[\gre@shrinkunit]%
\else%
%non-rubber
\def\gre@baseunit{#1}%
\def\gre@stretchunit{}%
\def\gre@shrinkunit{}%
\fi%
\fi%
\StrDel{\gre@baseunit}{ }[\gre@baseunit]%
\StrRight{\gre@baseunit}{2}[\gre@baseunit]%
\StrDel{\gre@stretchunit}{ }[\gre@stretchunit]%
\StrRight{\gre@stretchunit}{2}[\gre@stretchunit]%
\StrDel{\gre@shrinkunit}{ }[\gre@shrinkunit]%
\StrRight{\gre@shrinkunit}{2}[\gre@shrinkunit]%
\gre@convertto{\gre@baseunit}{\dimexpr#2\relax} %
\IfStrEq{}{\gre@stretchunit}{}{%
plus %
\gre@convertto{\gre@stretchunit}{\gluestretch#2}%
}%
\IfStrEq{}{\gre@shrinkunit}{}{%
minus %
\gre@convertto{\gre@shrinkunit}{\glueshrink#2}%
}%
}
\def\newfunction#1#2#3{%
\gre@changeonedimenfactor{#1}{#2}{#3}%
}
答案2
虽然 David 的帖子诊断了我的问题,但并没有提供解决方案。下面是我根据 David 的回答能够运行的 testing.sty 版本。
编辑:在现场处理代码后,我发现我还没有完全解决所有的扩展问题。虽然第一个测试打印得很好,但额外的非打印内容被放置在存储的字符串中(可以用 看到\typeout
),导致在我的代码中稍后将其用作距离时出现问题。我需要的是一个类似的接口,其中\convertto
结果存储在使用 的宏中,\edef
而不是直接分发。此更改还需要进行一些更改,\consistentunits
因为它不能再一次定义其输出宏。下面的代码包显示了我所有的修复,现在可以按预期工作。
% ------IDENTIFICATION------
\NeedsTeXFormat{LaTeX2e}[2005/12/01]%
\ProvidesPackage{testing}%
% For testing package code.
% ------INITIAL CODE------
% ------DECLARATION OF OPTIONS------
% ------EXECUTION OF OPTIONS------
\ProcessOptions%
% ------PACKAGE LOADING------
\RequirePackage{xstring}%
% ------MAIN CODE------
\AtBeginDocument{}%
\AtEndDocument{}%
\def\greerror#1{\PackageError{GregorioTeX}{#1}{}}%
%%%%%%%%%%%%%
%% Rescaling dimensions (for when \grefactor changes)
%%%%%%%%%%%%%
%% This macro sets one dim (#1) to the value #2 and sets whether it should scale when the \grefactor changes (#3, 1 if yes, 0 if no). While it does check that #1 can accept the kind of distance given in #2, it does not propagate the changes through the calculated distances.
\newif\ifchecklength%
\newif\ifbadlength%
\def\gresetdim#1#2#3{%
\checklengthfalse%
\badlengthfalse%
%check if #2 is a rubber length (contains plus and/or minus)
\IfSubStr{#2}{plus}{\checklengthtrue}{\relax}%
\IfSubStr{#2}{minus}{\checklengthtrue}{\relax}%
%if #1 is one of the distances which cannot be rubber.
\ifchecklength%
\IfStrEq{#1}{additionallineswidth}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{maximumspacewithoutdash}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{additionalcustoslineswidth}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{minimalspaceatlinebeginning}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{manualinitialwidth}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{aboveinitialseparation}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{noclefspace}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{abovesignsspace}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{belowsignsspace}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{lowchoralsignsshift}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{highchoralsignsshift}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{translationheight}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{abovelinestextraise}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{abovelinestextheight}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{braceshift}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{curlybraceaccentusshift}{\badlengthtrue}{\relax}%
\IfStrEq{#1}{clivisalignmentmin}{\badlengthtrue}{\relax}%
\fi%
% did both of the above conditions hold true?
\ifbadlength%
\greerror{#1 cannot be a rubber length.}%
\else%
\expandafter\xdef\csname gre@scale@#1\endcsname{#3}%
\expandafter\xdef\csname gre@#1\endcsname{#2}%
\fi%
\relax %
}%
%% an aux function adapting the value #1 from the factor #2 to the factor #3
%% Note: This function is assumed to touch only dimensions which are meant to scale with the \grefactor (i.e. if it acts on distance x, \gre@scale@x is 1)
\newif\ifrubber%
\newskip\gre@skip@temp%
\newdimen\gre@dimen@temp%
\def\gre@changeonedimenfactor#1#2#3{%
\rubbertrue%
% is length one that cannot be rubber?
\IfStrEq{#1}{additionallineswidth}{\rubberfalse}{\relax}%
\IfStrEq{#1}{maximumspacewithoutdash}{\rubberfalse}{\relax}%
\IfStrEq{#1}{additionalcustoslineswidth}{\rubberfalse}{\relax}%
\IfStrEq{#1}{minimalspaceatlinebeginning}{\rubberfalse}{\relax}%
\IfStrEq{#1}{manualinitialwidth}{\rubberfalse}{\relax}%
\IfStrEq{#1}{aboveinitialseparation}{\rubberfalse}{\relax}%
\IfStrEq{#1}{noclefspace}{\rubberfalse}{\relax}%
\IfStrEq{#1}{abovesignsspace}{\rubberfalse}{\relax}%
\IfStrEq{#1}{belowsignsspace}{\rubberfalse}{\relax}%
\IfStrEq{#1}{lowchoralsignsshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{highchoralsignsshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{translationheight}{\rubberfalse}{\relax}%
\IfStrEq{#1}{abovelinestextraise}{\rubberfalse}{\relax}%
\IfStrEq{#1}{abovelinestextheight}{\rubberfalse}{\relax}%
\IfStrEq{#1}{braceshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{curlybraceaccentusshift}{\rubberfalse}{\relax}%
\IfStrEq{#1}{clivisalignmentmin}{\rubberfalse}{\relax}%
\ifrubber% if we have a rubber allowed length we create a temporary skip
\let\gre@temp\gre@skip@temp%
\else% otherwise we create a temporary dimen
\let\gre@temp\gre@dimen@temp%
\fi%
% Math
\edef\gre@convert{\csname gre@#1\endcsname}%
\gre@temp=\gre@convert%
\multiply \gre@temp by \number #3%
\divide \gre@temp by \number #2%
\gre@consistentunits{\gre@convert}{\gre@temp}%
\gresetdim{#1}{\gre@stringdist}{1}%
\relax %
}%
% These functions are used for stripping out the units and decimal portion of a distance to make it more amenable to being used in the conversion function below
{\catcode`p=12 \catcode`t=12 \gdef\gre@makein#1.#2pt{#1}}%
{\catcode`p=12 \catcode`t=12 \gdef\gre@makenum#1pt{#1}}%
% This function converts a distance to the units indicated in #1 and returns it as a string.
\newdimen\gre@unit%
\newdimen\gre@base%
\newdimen\gre@maxlen%
\newcount\gre@unitfactor%
\newcount\gre@basefactor%
\newcount\temp%
\def\gre@convertto#1#2{%
\ifdim#2=0pt\relax%
\edef\gre@converted{0 #1}%
\else%
\expandafter\gre@unit = 1 #1%
\gre@base = #2%
% Code to increase precision
\gre@maxlen = 16383.99999pt%
\gre@unitfactor = \number\gre@maxlen%
\divide\gre@unitfactor by \number\gre@unit%
\gre@basefactor = \gre@maxlen%
\divide\gre@basefactor by \gre@base%
\ifnum\gre@basefactor<\gre@unitfactor%
% \advance\gre@basefactor by -10sp%
\multiply\gre@unit by \gre@basefactor%
\multiply\gre@base by \gre@basefactor%
\else%
% \advance\gre@unitfactor by -10sp%
\multiply\gre@unit by \gre@unitfactor%
\multiply\gre@base by \gre@unitfactor%
\fi%
\temp = \expandafter\gre@makein\the\gre@unit%
\divide\gre@base by \temp%
\edef\gre@converted{%
\expandafter\gre@makenum\the\gre@base #1%
}%
\fi%
}%
% This function takes a distance (#2) and formats it as a string so that its units conform to the pattern set by a string representation of a distance (#1)
\newif\ifstretch%
\newif\ifshrink%
\def\gre@consistentunits#1#2{%
\stretchfalse%
\shrinkfalse%
\IfSubStr{#1}{plus}{\stretchtrue}{\relax}%
\IfSubStr{#1}{minus}{\shrinktrue}{\relax}%
\ifstretch%
\ifshrink%
%rubber with both stretch and shrink
\StrBefore{#1}{plus}[\gre@baseunit]%
\StrBetween{#1}{plus}{minus}[\gre@stretchunit]%
\StrBehind{#1}{minus}[\gre@shrinkunit]%
\else%
%rubber with stretch only
\StrBefore{#1}{plus}[\gre@baseunit]%
\StrBehind{#1}{plus}[\gre@stretchunit]%
\def\gre@shrinkunit{\relax}%
\fi%
\else%
\ifshrink%
%rubber with shrink only
\StrBefore{#1}{minus}[\gre@baseunit]%
\def\gre@stretchunit{\relax}%
\StrBehind{#1}{minus}[\gre@shrinkunit]%
\else%
%non-rubber
\def\gre@baseunit{#1}%
\def\gre@stretchunit{\relax}%
\def\gre@shrinkunit{\relax}%
\fi%
\fi%
\StrDel{\gre@baseunit}{ }[\gre@baseunit]%
\StrRight{\gre@baseunit}{2}[\gre@baseunit]%
\StrDel{\gre@stretchunit}{ }[\gre@stretchunit]%
\StrRight{\gre@stretchunit}{2}[\gre@stretchunit]%
\StrDel{\gre@shrinkunit}{ }[\gre@shrinkunit]%
\StrRight{\gre@shrinkunit}{2}[\gre@shrinkunit]%
\gre@convertto{\gre@baseunit}{\dimexpr#2\relax}%
\edef\gre@stringdist{\gre@converted}%
\if\relax\gre@stretchunit\else%
\gre@convertto{\gre@stretchunit}{\gluestretch#2}%
\edef\gre@stringdist{\gre@stringdist plus \gre@converted}%
\fi%
\if\relax\gre@shrinkunit\else%
\gre@convertto{\gre@shrinkunit}{\glueshrink#2}%
\edef\gre@stringdist{\gre@stringdist minus \gre@converted}%
\fi%
}%
\def\newfunction#1#2#3{%
\gre@changeonedimenfactor{#1}{#2}{#3}%
}%