%% This is part of the OpTeX project, see http://petr.olsak.net/optex \_codedecl \colordef {Colors <2022-03-07>} % preloaded in format \_doc ----------------------------- The basic colors in CMYK \`\Blue` \`\Red` \`\Brown` \`\Green` \`\Yellow` \`\Cyan` \`\Magenta` \`\Grey` \`\LightGrey` \`\White` and \`\Black` are declared here. \_cod ----------------------------- \_def\Blue {\_setcmykcolor{1 1 0 0}} \_def\Red {\_setcmykcolor{0 1 1 0}} \_def\Brown {\_setcmykcolor{0 .67 .67 .5}} \_def\Green {\_setcmykcolor{1 0 1 0}} \_def\Yellow {\_setcmykcolor{0 0 1 0}} \_def\Cyan {\_setcmykcolor{1 0 0 0}} \_def\Magenta {\_setcmykcolor{0 1 0 0}} \_def\Grey {\_setcmykcolor{0 0 0 .5}} \_def\LightGrey {\_setcmykcolor{0 0 0 .2}} \_def\White {\_setgreycolor{1}} \_def\Black {\_setgreycolor{0}} \_doc ----------------------------- By default, the \`\setcmykcolor` \`\setrgbcolor` and \`\setgreycolor` macros with `{}` parameter expand to \^`\_setcolor``{}{}{}` where is or or and is color operator for filling, is color operator for stroking. \_cod ----------------------------- \_def\_setcmykcolor#1{\_setcolor{#1}kK} \_def\_setrgbcolor#1{\_setcolor{#1}{rg}{RG}} \_def\_setgreycolor#1{\_setcolor{#1}gG} \_public \setcmykcolor \setrgbcolor \setgreycolor ; \_doc ----------------------------- The \`\onlyrgb` declaration redefines \^`\setcmykcolor` to do conversion to RGB just before \^`\_setcolor` is used. The \`\onlycmyk` declaration redefines \^`\setrgbcolor` to do conversion to CMYK just before \^`\_setcolor` is used. Moreover, \^`\onlyrgb` re-defines three basic RGB colors for RGB color space and re-declares \^`\colordef` as \^`\rgbcolordef`. \_cod ----------------------------- \_def\_onlyrgb{\_def\Red{\_setrgbcolor{1 0 0}}% \_def\Green{\_setrgbcolor{0 1 0}}\_def\Blue{\_setrgbcolor{0 0 1}}% \_let\_colordef=\_rgbcolordef \_def\_setrgbcolor##1{\_setcolor{##1}{rg}{RG}}% \_def\_setcmykcolor##1{\_ea\_setcolor\_ea{\_expanded{\_cmyktorgb ##1 ;}}{rg}{RG}}% \_public \colordef \setrgbcolor \setcmykcolor ;} \_def\_onlycmyk{% \_let\_colordef=\_cmykcolordef \_def\_setrgbcolor##1{\_ea\_setcolor\_ea{\_expanded{\_rgbtocmyk ##1 ;}}kK}% \_def\_setcmykcolor##1{\_setcolor{##1}kK}% \_public \colordef \setrgbcolor \setcmykcolor ;} \_public \onlyrgb \onlycmyk ; \_doc ----------------------------- The \`\_colorattr` for coloring is allocated and \`\_setcolor``{}{}{}` is defined here. This macro does `\_colorattr=`\`\_colorcnt` if the was not used before and prepare mapping from this integer value to the and increments \^`\_colorcnt`. If the were used already, then \^`\_setcolor` does `\_colorattr=`. This work is done by the \`\_translatecolor` macro. The following mapping macros are created: \begtt \catcode`<=13 \_color:: ... expands to used \_color: ... expands to \_color-s: ... expands to \endtt \_cod ----------------------------- \_newattribute \_colorattr \_newcount \_colorcnt \_colorcnt=1 % allocations start at 1 \_protected\_def\_setcolor{\_colorprefix\_colorattr=\_translatecolor} \_def\_translatecolor#1#2#3{\_ifcsname _color::#1 #2\_endcsname\_lastnamedcs\_relax \_else \_colorcnt \_sxdef{_color::#1 #2}{\_the\_colorcnt}% \_sxdef{_color:\_the\_colorcnt}{#1 #2}% \_sxdef{_color-s:\_the\_colorcnt}{#1 #3}% \_incr \_colorcnt \_fi } % Black is the default color. \_sdef{_color::0 g}{0} \_sdef{_color:0}{0 g} \_sdef{_color-s:0}{0 G} \_doc ----------------------------- We support concept of non-local color, i.e. all changes of the color attribute are global by setting \`\_colorprefix` to `\global`. \`\localcolor` is the default, i.e.\ \^`\_colorprefix` is `\relax`.\nl You can write `\global\Red` if you want to have global setting of the color. \_cod ----------------------------- \_protected\_def \_localcolor {\_let\_colorprefix=\_relax} \_protected\_def \_nolocalcolor {\_let\_colorprefix=\_global} \_public \localcolor \nolocalcolor ; \_localcolor \_doc ----------------------------- The attribute \`\_transpattr` is allocated and set by the \`\transparency``` macro. If such level of the tranparency was never used in the document then \^`\addextgstate{tr}`\code{\{<>\}} is applied (where `X` is `(255-)/255`). This information is used when shipout is processed (similarly as colors). It means `/tr gs` is inserted when the attribute is changed.\nl \`\_resetattrs` resets the \^`\_colorattr` and \^`\_transpattr` to their initial value `-"7FFFFFFF`. \_cod ----------------------------- \_newattribute\_transpattr \_def\_transparency {\_afterassignment\_transparencyA \_transpattr} \_def\_transparencyA{% \_ifnum\_transpattr<1 \_transpattr=\_noattr \_fi \_ifnum\_transpattr>255 \_opwarning{\_noexpand\transparency > 255 not allowed}% \_transpattr=\_noattr \_else \_ifcsname _transp:\_the\_transpattr\_endcsname \_else \_edef\_transpv{\_expr{(255-\_the\_transpattr)/255}}% \_addextgstate{tr\_the\_transpattr}{<>}% \_sxdef{_transp:\_the\_transpattr}{}% \_ifcsname _transp:0\_endcsname \_else \_addextgstate{tr0}{<>}% \_sxdef{_transp:0}{}% \_fi \_fi \_fi } \_def\_thetransparency{\_ifnum \_transpattr=-"7FFFFFFF 0\_else \_the\_transpattr \_fi} \_def\_resetattrs{\_colorattr=\_noattr \_transpattr=\_noattr} \_public \transparency \thetransparency ; \_doc ----------------------------- We use Lua codes for RGB to CMYK or CMYK to RGB conversions and for addition color components in the \^`\colordef` macro. The \`\_rgbtocmyk` ` ;` expands to ` ` and the \`\_cmyktorgb` ` ;` expands to ` `. The \`\_colorcrop`, \`\_colordefFin` and \`\_douseK` are auxiliary macros used in the \^`\colordef`. The `\_colorcrop` rescales color components in order to they are in $[0,1]$ interval. The `\colordefFin` expands to the values accumulated in Lua code `color_C`, `color_M`, `color_Y` and `color_K`. The `\_douseK` applies \^`\useK` to CMYK components. \nl The `\_tocmyk:` or `\_torgb:` control sequences (given by \^`\rgbcmykmap`) have precedence. \_cod ----------------------------- \_def\_rgbtocmyk #1 #2 #3 ;{\_trycs{_tocmyk:#1 #2 #3}{% \_ea \_stripzeros \_detokenize \_ea{\_directlua{ local kr = math.max(#1,#2,#3) if (kr==0) then tex.print('0. 0. 0. 1 ;') else tex.print(string.format('\_pcent.3f \_pcent.3f \_pcent.3f \_pcent.3f ;', (kr-#1)/kr, (kr-#2)/kr, (kr-#3)/kr, 1-kr)) end }}}} \_def\_cmyktorgb #1 #2 #3 #4 ;{\_trycs{_torgb:#1 #2 #3 #4}{% \_ea \_stripzeros \_detokenize \_ea{\_directlua{ local kr = 1-#4 tex.print(string.format('\_pcent.3f \_pcent.3f \_pcent.3f ;', (1-#1)*kr, (1-#2)*kr, (1-#3)*kr)) }}}} \_def\_colorcrop{\_directlua{ local m=math.max(color_C, color_M, color_Y, color_K) if (m>1) then color_C=color_C/m color_M=color_M/m color_Y=color_Y/m color_K=color_K/m end }} \_def\_colordefFin{\_colorcrop \_ea \_stripzeros \_detokenize \_ea{\_directlua{ tex.print(string.format('\_pcent.3f \_pcent.3f \_pcent.3f \_pcent.3f ;', color_C, color_M, color_Y, color_K)) }}} \_def\_douseK{\_colorcrop \_directlua{ kr=math.min(color_C, color_M, color_Y) if (kr>=1) then color_C=0 color_M=0 color_Y=0 color_K=1 else color_C=(color_C-kr)/(1-kr) color_M=(color_M-kr)/(1-kr) color_Y=(color_Y-kr)/(1-kr) color_K=math.min(color_K+kr,1) end }} \_doc ----------------------------- We have a problem with the `%.3f` directive in Lua code. It prints trailed zeros: (0.300 instead desired 0.3) but we want to save PDF file space. The macro \`\_stripzeros` removes these trailing zeros at the expand processor level. So `\_stripzeros 0.300 0.400 0.560 ;` expands to `.3 .4 .56`. \_cod ----------------------------- \_def\_stripzeros #1.#2 #3{\_ifx0#1\_else#1\_fi.\_stripzeroA #2 0 :% \_ifx;#3\_else \_space \_ea\_stripzeros\_ea#3\_fi} \_def\_stripzeroA #10 #2:{\_ifx^#2^\_stripzeroC#1:\_else \_stripzeroB#1 0 :\_fi} \_def\_stripzeroB #10 #2:{\_ifx^#2^\_stripzeroC#1:\_else #1\_fi} \_def\_stripzeroC #1 #2:{#1} \_doc ----------------------------- \`\rgbcmykmap` `{ }`\,`{ }` declares mapping from RGB to CMYK and from CMYK to RGB for given color. It has precedence before general formulae used in the \^`\_rgbtocmyk` and \^`\_cmyktorgb` macros. Note, that the values must be given exacly in the same format as in \^`\setcmykcolor` and \^`\setrgbcolor` parameters. For example, `0.5` or `.5` or `.50` are different values from point of view of this mapping. \_cod ----------------------------- \_def\_rgbcmykmap#1#2{\_sxdef{_torgb:#2}{#1}\_sxdef{_tocmyk:#1}{#2}} \_public \rgbcmykmap ; \_doc ----------------------------- The \`\rgbcolordef` and \`\cmykcolordef` use common macro \`\_commoncolordef` with different first four parameters. The `\_commoncolordef {}` does the real work. It initializes the Lua variables for summation. It expands in the group where color selectors have special meaning, then it adjusts the resulting string by \^`\replstring` and runs it. Example shows how the are processed: \begtt \catcode`\<=13 input : ".3\Blue + .6^\KhakiC \useK -\Black" expanded to: ".3 !=K 1 1 0 0 +.6^!=R .804 .776 .45 \_useK -!=G 0" adjusted to: "\_addcolor .3!=K 1 1 0 0 \_addcolor .6!^R .804 .776 .45 \_useK \_addcolor -1!=G 0" and this is processed. \endtt \`\_addcolor` `!` expands to `\_addcolor: ` for example it expands to `\_addcolor:=K ` followed by one or three or four numbers (depending on ). is `=` (use as is) or `^` (use complementary color). is `K` for CMYK, `R` for RGB and `G` for GREY color space. Uppercase informs that `\cmykcolordef` is processed and lowercase informs that `\rgbcolordef` is processed. All variants of commands `\_addcolor:` are defined. All of them expand to `\_addcolorA ` which adds the values of Lua variables. The `\rgbcolordef` uses `\_addcolorA 0` and `\cmkykcolordef` uses `\_addcolorA `. So the Lua variable names are a little confusing when `\rgbcolordef` is processed. Next, `\_commoncolordef` saves resulting values from Lua to `\_tmpb` using `\_colordefFin`. If `\rgbcolordef` is processed, then we must to remove the last component which is in the format `.0` in such case. The `\_stripK` macro does it. Finally, the `` is defined as `{}`, for example `\_setcmykclor{1 0 .5 .3}`. \_cod ----------------------------- \_def\_rgbcolordef {\_commoncolordef \_setrgbcolor krg} \_def\_cmykcolordef {\_commoncolordef \_setcmykcolor KRG} \_def\_commoncolordef#1#2#3#4#5#6{% \_begingroup \_directlua{color_C=0 color_M=0 color_Y=0 color_K=0}% \_def\_setcmykcolor##1{!=#2 ##1 }% \_def\_setrgbcolor ##1{!=#3 ##1 }% \_def\_setgreycolor##1{!=#4 ##1 }% \_let\_useK=\_relax \_edef\_tmpb{+#6}% \_replstring\_tmpb{+ }{+}\_replstring\_tmpb{- }{-}% \_replstring\_tmpb{+}{\_addcolor}\_replstring\_tmpb{-}{\_addcolor-}% \_replstring\_tmpb{^!=}{!^}\_replstring\_tmpb{-!}{-1!}% \_ifx K#2\_let\_useK=\_douseK \_fi \_tmpb \_edef\_tmpb{\_colordefFin}% \_ifx k#2\_edef\_tmpb{\_ea\_stripK \_tmpb;}\_fi \_ea\_endgroup \_ea\_def\_ea#5\_ea{\_ea#1\_ea{\_tmpb}}% } \_def\_addcolor#1!#2#3{\_cs{addcolor:#2#3}#1} \_def\_addcolorA #1 #2 #3 #4 #5 {% \_def\_tmpa{#1}\_ifx\_tmpa\_empty \_else \_edef\_tmpa{\_tmpa*}\_fi \_directlua{color_C=math.max(color_C+\_tmpa#2,0) color_M=math.max(color_M+\_tmpa#3,0) color_Y=math.max(color_Y+\_tmpa#4,0) color_K=math.max(color_K+\_tmpa#5,0) }} \_sdef{addcolor:=K}#1 #2 #3 #4 #5 {\_addcolorA #1 #2 #3 #4 #5 } \_sdef{addcolor:^K}#1 #2 #3 #4 #5 {\_addcolorA #1 (1-#2) (1-#3) (1-#4) #5 } \_sdef{addcolor:^G}#1 #2 {\_addcolorA #1 0 0 0 #2 } \_sdef{addcolor:=G}#1 #2 {\_addcolorA #1 0 0 0 (1-#2) } \_sdef{addcolor:=R}#1 #2 #3 #4 {% \_edef\_tmpa{\_noexpand\_addcolorA #1 \_rgbtocmyk #2 #3 #4 ; }\_tmpa } \_sdef{addcolor:^R}#1 #2 #3 #4 {\_cs{addcolor:=R}#1 (1-#2) (1-#3) (1-#4) } \_sdef{addcolor:=k}#1 #2 #3 #4 #5 {% \_edef\_tmpa{\_noexpand\_addcolorA #1 \_cmyktorgb #2 #3 #4 #5 ; 0 }\_tmpa } \_sdef{addcolor:^k}#1 #2 #3 #4 #5 {\_cs{addcolor:=k}#1 (1-#2) (1-#3) (1-#4) #5 } \_sdef{addcolor:^g}#1 #2 {\_addcolorA #1 (1-#2) (1-#2) (1-#2) 0 } \_sdef{addcolor:=g}#1 #2 {\_addcolorA #1 #2 #2 #2 0 } \_sdef{addcolor:=r}#1 #2 #3 #4 {\_addcolorA #1 #2 #3 #4 0 } \_sdef{addcolor:^r}#1 #2 #3 #4 {\_addcolorA #1 (1-#2) (1-#3) (1-#4) 0 } \_def\_stripK#1 .0;{#1} \_let\_colordef=\_cmykcolordef % default \_colordef is \_cmykcolordef \_doc ----------------------------- Public versions of \`\colordef` and \`\useK` macros are declared using `\_def`, because the internal versions `\_colordef` and `\_useK` are changed during processing. \_cod ----------------------------- \_def \useK{\_useK} \_def \colordef {\_colordef} \_public \cmykcolordef \rgbcolordef ; \_doc ----------------------------- The \LaTeX/ file `x11nam.def` is read by \`\morecolors`. The numbers 0,1,2,3,4 are transformed to letters O, , B, C, D in the name of the color. Colors defined already are not re-defined. The empty \`\_showcolor` macro should be re-defined for color catalog printing. For example: \begtt \def\vr{\vrule height10pt depth2pt width20pt} \def\_showcolor{\hbox{\tt\_bslash\_tmpb: \csname\_tmpb\endcsname \vr}\space\space} \begmulti 4 \typosize[10/14] \morecolors \endmulti \endtt \_cod ----------------------------- \_def\_morecolors{% \_long\_def\_tmp##1\preparecolorset##2##3##4##5{\_tmpa ##5;,,,;} \_def\_tmpa##1,##2,##3,##4;{\_ifx,##1,\_else \_def\_tmpb{##1}\_replstring\_tmpb{1}{}\_replstring\_tmpb{2}{B}% \_replstring\_tmpb{3}{C}\_replstring\_tmpb{4}{D}\_replstring\_tmpb{0}{O}% \_ifcsname \_tmpb\_endcsname \_else \_sdef{\_tmpb}{\_setrgbcolor{##2 ##3 ##4}}\_showcolor\_fi \_ea\_tmpa\_fi } \_ea\_tmp\_input x11nam.def } \_let\_showcolor=\_relax % re-define it if you want to print a color catalog \_public \morecolors ; \_endcode % ------------------------------------- \secc Basic concept Setting of color in PDF is handled by graphics operators which change the graphics context. Colors for fills/strokes are distinguished, but apart from that, only one color is active at time and is used for all material drawn by following graphics operators, until next color is set. Each PDF content (e.g. page or form XObject) has its own graphics context, that is initialized from zero. Hence we have different concept of selecting fonts in \TeX/ (it depends on \TeX/ groups but does not depends on pages) and color handling in PDF. \TeX/ itself has no concept of colors. Colors have always been handled by inserting whatsits (either using `\special` for DVI or using `\pdfliteral`/`\pdfcolorstack` for PDF). It is very efficient and \TeX/ doesn't even have to know anything about colors, but it is also problematic in many ways. That is the reason why we decided to change color handling from `\pdfcolorstack` to \LuaTeX/ attributes in version 1.04 of \OpTeX. Using attributes, the color setting behaves exactly like font selection from \TeX/ point of view: it respects \TeX/ groups, colors can span more pages, independent colors can be set for `\insert`s, etc. Moreover, once a material is created (using `\setbox` for example) then it has its fonts and its colors frozen and you can rely on it when you are using e.g. `\unhbox`. There are no internal whatsits for colors which can interfere with other typesetting material. In the end something like setting text to red (`{\Red text}`) should have the same nice behavior like setting text to bold (`{\bf text}`). \LuaTeX/ attributes can be set like count register -- one attribute holds one number at a time. But the value of attribute is propagated to each created typesetting element until the attribute is unset or set to another value. Very much like the font property. We use one attribute \^`\_colorattr` for storing the currently selected color (in number form). Macros \^`\setcmykcolor``{ }` or \^`\setrgbcolor``{ }` or\nl \^`\setgreycolor``{}` are used in color selectors. These macros expand to internal \^`\_setcolor` macro which sets the \^`\_colorattr` attribute to an integer value and prepares mapping between this value and the real color data. This mapping is used just before each `\shipout` in output routine. The \^`\_preshipout` pseudo-primitive is used here, it converts attribute values to internal PDF commands for selecting colors. The concept with color attributes has one limitation: the colors cannot be changed inside a ligature unless the ligature is broken manually. It means that `{\Red f}i` doesn't lead to the expected result but `{\Red f\null}i` does. \secc Color mixing The color mixing processed by the \^`\colordef` is done in the subtractive color model CMYK. If the result has a component greater than 1 then all components are multiplied by a coefficient in order to the maximal component is equal to 1. You can move a shared amount of CMY components (i.e. their minimum) to the $K$ component. This saves the color tonners and the result is more true. This should be done by \^`\useK` command at the end of a linear combination used in `\colordef`. For example \begtt \colordef \myColor {.3\Green + .4\Blue \useK} \endtt The `\useK` command exactly does: $$ \displaylines{ k' =\min(C,M,Y), \cr \ C=(C-k')/(1-k'), \ M=(M-k')/(1-k'), \ Y=(Y-k')/(1-k'), \cr K = \min(1,K+k'). } $$ You can use minus instead of plus in the linear combination in `\colordef`. The given color is substracted in such case and the negative components are rounded to zero immediately. For example \begtt \colordef \Color {\Brown-\Black} \endtt can be used for removing the black component from the color. You can use the `-\Black` trick after `\useK` command to remove grey components occurred during color mixing. Finally, you can use `^` immediately preceded before the macro name of the color. Then the complementary color is used here. \begtt \colordef\mycolor{\Grey+.6^\Blue} % the same as \colordef\mycolor{\Grey+.6\Yellow} \endtt The \^`\rgbcolordef` can be used to mix colors in additive color model RGB. If \^`\onlyrgb` is declared, then \^`\colordef` works as \^`\rgbcolordef`. If a CMYK to RGB or RGB to CMYK conversion is needed then direct conversion of given color is used (if declared using \^`\rgbcmykmap``{}{}`) or the following simple formulae are used (ICC profiles are not supported): $$ \displaylines{ \hbox{CMYK to RGB:}\cr R = (1-C)(1-K), \ G = (1-M)(1-K), \ B = (1-Y)(1-K). \cr \hbox{RGB to CMYK:}\cr K'=\max(R,G,B), \ C=(K'-R)/K', \ M=(K'-G)/K', \ Y=(K'-B)/K', \ K=1-K'. } $$ The RGB to CMYK conversion is invoked when a color is declared using `\setrgbcolor` and it is used in \^`\colordef` or if it is printed when \^`\onlycmyk` is declared. The CMYK to RGB conversion is invoked when a color is declared using \^`\setcmykcolor` and it is used in \^`\rgbcolordef` or if it is printed when \^`\onlyrgb` is declared. \secc Implementation \_endinput 2022-03-07 \_resetattrs instead \_resetcolor 2022-03-05 \transparency added 2021-07-16 colors reimplemented, now they are based on attributes 2021-05-28 \rgbcmykmap introduced 2020-04-22 \replstring\tmpb{+ }{+}, {- }{-} added in \colordef, bug fixed 2020-03-18 introduced