%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Module: ZzTeX Art Facilities % % Synopsis: This module provides facilities to deal with external art. % % Author: Paul C. Anagnostopoulos % Created: 7 June 1991 % % Copyright 1989--2020 by Paul C. Anagnostopoulos % under The MIT License (opensource.org/licenses/MIT) % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Art File Root & Placing Control % --- ---- ---- - ------- ------- \def \zartroot {} \setflag \zplaceart = \true \def \setartroot #1{% {root/} \gdef \zartroot {#1}} \def \zprependartroot #1#2{% {\name}{filespec} \edef #1{\zartroot#2}} \def \placeart #1{% \setflag \zplaceart = #1} % Imported Art % -------- --- \defineblock{\art}{\endart}{\false}{} %~block art Type % \abovepenalty = integer % Penalty above block. % \aboveskip = glue % Space b/b above block. % \belowpenalty = integer % Penalty below block. % \belowskip = glue % Space b/b below block. % \setflag \cliptobb = flag % Clip art to its bounding box? % \hfuzz = dimen % Horizontal fuzz. % \hscale = dimen % Horizontal scale (in points). % \leftindent = glue % Left indentation. % \rightindent = glue % Right indentation. % \vscale = dimen % Vertical scale (in points). % \vshift = dimen % Vertical shift. %~end \definedimen{\artwidth}{0pt} \definedimen{\artheight}{0pt} \definedimen{\hscale}{0pt} \definedimen{\vscale}{0pt} %~ The format of the *file-info* argument is: %~ *file*,*width*,*height*,*scale*[,*gutter*,*file*,...]&/ %~ The *file* is a file name preceded by |!| or |=|. \def \art #1#2{% {type}{file-info} \beginblockscope{art}% \global\increment \artdepth \abovepenalty = \breakgood %~default hard \belowpenalty = \breakgood %~default hard \setflag \cliptobb = \false %~default soft \hfuzz = 1pt %~default soft \hscale = 1.0pt %~default hard \vscale = 1.0pt %~default hard \vshift = \mindimen %~default soft \processdesign{\art}{#1}% \global\increment \artnumber \global\artwidth = 0pt \global\artheight = 0pt \artformat{#2}% \endart} \def \endart {% \global\decrement \artdepth \endblockscope{art}} \def \artformat #1{% {info} \endgraf \bbskipabove{\abovepenalty}{\aboveskip}% \alterindentation{\leftindent}{\rightindent}% \noindent \zbldartline 0pt,#1,\zmark \endgraf \prevdepth = 0pt % Nothing better to do. \bbskipbelow{\belowpenalty}{\belowskip}} \def \zbldartline #1,#2,#3,#4,#5,#6\zmark{% gutter,file,width,height,scale,...,\zmark \zbuildartlineb{#1}{#2}{#3}{#4}{#5}% \zwriteartinfo{#2}{#3}{#4}{#5}% \if \notp{\emptyargp{#6}}\zbldartline #6\zmark \fi} % Place Art File % ----- --- ---- \definedimen{\zleftslop}{0pt} \definedimen{\zrightslop}{0pt} \definedimen{\ztopslop}{0pt} \definedimen{\zbottomslop}{0pt} \definedimen{\zartwidth}{0pt} \definedimen{\zartheight}{0pt} \definedimen{\zartscale}{0pt} \def \zbuildartlineb #1#2#3#4#5{% {gutter}{file}{width}{height}{scale} \kern #1\relax \zparseartfile #2,0pt,0pt,0pt,0pt,\zmark \zchkartfile{\zgotart}% \zcalcartdimens{#3}{#4}{#5}% \zbuildartlinec{\zartfile}% \gcalculate \artwidth = {\artwidth,+,#1,+,\zartwidth}% \if \dimgtrp{\zartheight}{\artheight}\global\artheight = \zartheight \fi} \def \zparseartfile #1,#2,#3,#4,#5,#6\zmark{% spec,left,right,top,bottom\zmark \zparseartspec #1.eps.\zmark \zleftslop = #2% \zrightslop = #3% \ztopslop = #4% \zbottomslop = #5\relax} \def \zcalcartdimens #1#2#3{% {width}{height}{scale} \if \orp{\andp{\dimzerop{#1}}{\dimzerop{#2}}} {\forceepsbb}% \if \zgotart \zreadepsbb{\zartwidth}{\zartheight}{\zartfile}% \else \zartwidth = 20pc \zartheight = 10pc \fi \else \calculate \zartwidth = {#1,-,\zleftslop,-,\zrightslop}% \calculate \zartheight = {#2,-,\ztopslop,-,\zbottomslop}% \fi \zcalcartscale #3\zmark \calculate \zartwidth = {\zartwidth,S,\zartscale,S,\hscale}% \calculate \zartheight = {\zartheight,S,\zartscale,S,\vscale}% \calculate \zleftslop = {\zleftslop,S,\zartscale,S,\hscale}% \calculate \zbottomslop = {\zbottomslop,S,\zartscale,S,\vscale}% \if \forceepsbb \advance \zleftslop by \zbbxorigin \advance \zbottomslop by \zbbyorigin \fi} \def \zcalcartscale #1#2\zmark{% scale\zmark \if \codeeqlp{#1}{=}% \if \emptyargp{#2}% \calculate \zartscale = {\hsize,R,\zartwidth}% \else \calculate \zartscale = {#2,R,\zartwidth}% \fi \else \calculate \zartscale = {#1#2pt,/,100}% \fi} \def \zbuildartlinec #1{% {title} \if \dimeqlp{\vshift}{\mindimen}% \measureascenderheight{\vshift}% \fi \vtop {% % Place art in a vbox. \offinterlineskip \rule{height \vshift width 0pt}% % Force the height of the \kern -\vshift % box to match ascenders. \rule{height 0pt width \zartwidth}% % Force the width of the box. \if \andp{\zplaceart}{\zgotart}% \rule{height 0pt depth \zartheight width 0pt}% % Force its depth. \zplaceartfile \vss \else \vbox to \zartheight{% % Force its depth. \topcornermarks{\zartwidth}{.5pc}{.25pt}% \vss \hbox to \zartwidth {\hss \zannofont #1\hss}% \hbox to \zartwidth {\hss \zannofont \if \notp{\zplaceart}[not placing]\fi \if \notp{\zgotart}[to come]\fi \hss}% \vss \bottomcornermarks{\zartwidth}{.5pc}{.25pt}}% \fi}} \def \zplaceartfile {% \kern \zbottomslop \moveleft \zleftslop \vbox{% \special{CTM: push}% \special{CTM: scale \thefactor\hscale \space \thefactor\vscale}% \specialplaceepsfile{\zartfile}{\zartscale}{\cliptobb}% \special{CTM: pop}}% \kern -\zbottomslop} % Duplicates and Missing Art % ---------- --- ------- --- % Write an art entry into the cross-reference file. \def \zwriteartinfo #1#2#3#4{% {file}{width}{height}{scale} \zbeginhidewrite \edef \znext {\noexpand\xref{art}{\noexpand\folio}% {#1}% {#2,#3} {#4}}% \znext \zendhidewrite} % This macro is invoked by \xref. In copy mode, it checks to make sure % that no art file is used twice. \def \xrefart #1#2#3#4{% {page}{number}{title}{tag} \ifnum \xrefmode=\xrefcopymode \zxrefarta #2,\zmark \fi} \def \zxrefarta #1#2,#3\zmark{% \if \notp{\orp{\codeeqlp{#1}{!}}{\codeeqlp{#1}{=}}}% \if \definedp{\zart:#1#2}% \warning{dupart}{Art file `#1#2' is used more than once in the book}% \fi \withname\gdef{\zart:#1#2}{}% \fi} % This macro is invoked at the end of the run. \def \zmissingartmsg {% \if \posp{\zmissingart}% \remark{Number of missing art files in this run: \number\zmissingart.}% \fi} % Built-In Art Blocks % -------- --- ------ \def \fullpagegraphic #1{% {art-spec} \vsinkfromtrim{-\standardbleed} \smashbox{% \with{\hfuzz=99pc% \leftindent=\if \evenpagep \evenbleedshift \else \oddbleedshift \fi} \art{graphic}{#1}}} \def \artgraphicdesign {% \aboveskip = \noskip \belowskip = \noskip \setflag \cliptobb = \false \hfuzz = 1pt \leftindent = 0pt \rightindent = 0pt \vshift = 0pt} % Glyph Definitions % ----- ----------- % This macro allows the user to define a "glyph," which is an EPS file % that is treated like a fancy character. The bounding box must be % perfect. \def \defineglyph #1#2#3{% {\name}{spec,width,height,scale}{raise} \gdef #1{\zglyph #2,#3\zmark}} \def \zglyph #1,#2,#3,#4,#5\zmark{% spec,width,height,scale,raise\zmark {\zparseartspec #1.eps.\zmark \zchkartfile{\zgotart}% \zartwidth = #2% \zartheight = #3% \tdimena = #4\typesize % \tdimena = final scale in points. \divide \tdimena by 10 \calculate \zartwidth = {\zartwidth,P,\thefactor\tdimena}% \calculate \zartheight = {\zartheight,P,\thefactor\tdimena}% \raise #5 \hbox to \zartwidth{% \rule{height \zartheight depth 0pt width 0pt}% \if \zgotart \specialplaceglyph{\zartfile}{\tdimena}{\false}% \fi \hfil}}} \defineglyph{\acidfree}{!acidfree,2pc,2pc,35}{-1.5pt} % Panel Label Block % ----- ----- ----- \defineblock{\panellabel}{\endpanellabel}{\false}{} %~block panellabel Type % \aboveskip = glue % Space b/b above block. % \belowskip = glue % Space b/b below block. % \bodyfont = {...} % Font for text. % \style = {...} % Label style. % \vshift = dimen % Vertical shift. %~end \def \panellabel #1#2{% {type}{label-list} \beginblockscope{panellabel}% \global\increment \panellabeldepth \style = {letter in parens}% %~default soft \vshift = 0pt %~default soft \processdesign{\panellabel}{#1}% \global\increment \panellabelnumber \panellabelformat{#2}% \endpanellabel} \def \endpanellabel {% \global\decrement \panellabeldepth \endblockscope{panellabel}} \def \panellabelformat #1{% {panel-list} \endgraf \bbskipabove{\breaknever}{\aboveskip}% \the\bodyfont \noindent \zdolabel #1,\zmark \endgraf \bbskipbelow{\breakallowed}{\belowskip}} \def \zdolabel #1,#2,#3\zmark{% shift,label,... \rlap{% \hspace{#1}% \smash{\lower \vshift \hbox{#2}}}% \if \notp{\emptyargp{#3}}\zdolabel #3\zmark \fi} % Frames % ------ \defineblock{\frame}{\endframe}{\false}{} %~block frame Type % \abovepenalty = integer % Penalty above block. % \aboveskip = glue % Space b/b above block. % \def \backgroundformat ##1{...} % Background formatter. % \belowpenalty = integer % Penalty below block. % \belowskip = glue % Space b/b below block. % \deferredparams = {...} % Deferred parameter calculations. % \framecolor = {color} % Color of frame. % \framewidths = {left,right,top,bot} % Widths of frame segments. % \framegaps = {left,right,top,bot} % Gaps between frame and material. % \leftindent = glue % Left indentation of enclosed material. % \screencolor = {color} % Background screen color. % \width = dimen % Width of enclosed material. %~end % Together, \leftindent and \width specify the position of the enclosed % text within the text box, not of the frame itself. \definetoks{\deferredparams} \definetoks{\framecolor} \definetoks{\framewidths} \definetoks{\framegaps} \definetoks{\screencolor} \definecount{\framepage}{0} \definecount{\zdivframecounter}{0} \def \frame #1{% {type} \beginblockscope{frame}% \global\increment \framedepth \global\increment \zdivframecounter \whichpage{\framepage}{zE:\the\divisionname-\the\zdivframecounter}% \abovepenalty = \breakgood %~default hard \def \backgroundformat ##1{}% %~default soft \belowpenalty = \breakgood %~default hard \deferredparams = {}% %~default soft \framecolor = {black}% %~default soft \leftindent = 0pt %~default soft \screencolor = {}% %~default soft \width = \naturalwidth %~default soft \processdesign{\frame}{#1}% \global\increment \framenumber \setbox\zboxa = \vtop \bgroup \zpushvcontext} \def \endframe {% \endgraf \unskip \zpopvcontext \egroup % \vtop \the\deferredparams \setbox\zboxb = \hbox to \width{% \hspace{-\leftindent}\box\zboxa \hss}% \edef \zcolorrule {\noexpand\colorrule{\the\framecolor}}% \def \zarga ##1,##2,##3,##4\zmark{##1\relax}% \def \zargb ##1,##2,##3,##4\zmark{##2\relax}% \def \zargc ##1,##2,##3,##4\zmark{##3\relax}% \def \zargd ##1,##2,##3,##4\zmark{##4\relax}% \tdimenc = \expandafter\zargc\the\framegaps\zmark \advance \tdimenc by \ht\zboxb \advance \tdimenc by \dp\zboxb \advance \tdimenc by \expandafter\zargd\the\framegaps\zmark \setbox\zboxa = \vtop {% \offinterlineskip \measureascenderheight{\tdimena}% % Force the height of the \rule{width 0pt height \tdimena}% % box to match ascenders. \kern -\tdimena \smashbox{\backgroundformat{\tdimenc}}% \if \notp{\emptytoksp{\screencolor}}% \tdimend = \expandafter\zarga\the\framegaps\zmark \advance \tdimend by \wd\zboxb \advance \tdimend by \expandafter\zargb\the\framegaps\zmark \smashbox{% %%% \moveright \expandafter\zarga\the\framewidths\zmark \smashbox{% \colorrule{\the\screencolor}{width \tdimend height \tdimenc}}% \fi \zcolorrule{height \expandafter\zargc\the\framewidths\zmark}% \kern -\expandafter\zargc\the\framewidths\zmark \hbox {% \rlap{\zcolorrule{height \tdimenc width \expandafter\zarga\the\framewidths\zmark}}% \hskip \expandafter\zarga\the\framegaps\zmark \vbox {% \vskip \expandafter\zargc\the\framegaps\zmark \box\zboxb \vskip \expandafter\zargd\the\framegaps\zmark}% \hskip \expandafter\zargb\the\framegaps\zmark \llap{\zcolorrule{height \tdimenc width \expandafter\zargb\the\framewidths\zmark}}}% \kern -\expandafter\zargd\the\framewidths\zmark \zcolorrule{height \expandafter\zargd\the\framewidths\zmark}% \tagpageonly{\uniquetag{zE}{\the\zdivframecounter}}}% \advance \leftindent by -\expandafter\zarga\the\framewidths\zmark \advance \leftindent by -\expandafter\zarga\the\framegaps\zmark \frameformat \global\decrement \framedepth \endblockscope{frame}} \def \frameformat {% \bbskipabove{\abovepenalty}{\aboveskip}% \hindent{\leftindent} \rlap{\box\zboxa}\par \prevdepth = 0pt % Last thing in box is rule. \bbskipbelow{\belowpenalty}{\belowskip}} % This macro is invoked at the beginning of each division. \def \zframedivinit {% \global\zdivframecounter = 0\relax} % Margin Rules % ------ ----- \defineblock{\marginrule}{\endmarginrule}{\false}{} %~block marginrule Type % \artspec = {...} % Art spec for graphic above rule. % \leftshift = dimen % Horizontal shift for rule in left margin. % \position = {...} % Position options. % \rightshift = dimen % Horizonal shift for rule in right margin. % \rulecolor = {...} % Color of rule. % \rulewidth = dimen % Width of rule. % \vshift = dimen % Vertical shift for note. %~end \definetoks{\artspec} \definedimen{\leftshift}{0pt} \definedimen{\rightshift}{0pt} \definecount{\marginrulepage}{0} \definecount{\zdivmrcounter}{0} \definedimen{\zmrshift}{0pt} \definedimen{\zmrprevdepth}{0pt} \def \marginrule #1#2{% {type}{height,depth} \blockcantbein{\marginrule}{\marginrule}% \beginblockscope{marginrule}% \global\increment \marginruledepth \global\increment \zdivmrcounter \whichpage{\marginrulepage}{zR:\the\divisionname-\the\zdivmrcounter}% \rulecolor = {black} %~default soft \vshift = 0pt %~default soft \processdesign{\marginrule}{#1}% \zparsemrpos{\the\position}% \marginruleformat{#2}% \endmarginrule} \def \endmarginrule {% \global\decrement \marginruledepth \endblockscope{marginrule}} \def \marginruleformat #1{% {height,depth} \if \vmodep \zmrprevdepth = \prevdepth \nointerlineskip \else \vadjust \fi \bgroup \topskip = 0pt \moveright \zmrshift \vtop to 0pt{% \vskip \vshift \zmrgraphic \zmrrule #1\zmark \vss \tagpageonly{\uniquetag{zR}{\the\zdivmrcounter}}}% \egroup \if \vmodep \prevdepth = \zmrprevdepth \fi} % This macro sets up variables that specify the position of a margin rule % according to the \position design parameter: % % \ifevenpage Use next position only on even page. % \ifoddpage Use next position only on odd page. % \leftmargin Shift note to left margin. % \outsidemargin Shift note to outside margin. % \rightmargin Shift note to right margin. \definecount{\zmrhpos}{0} \def \zparsemrpos #1{% {options...} \global\zmrhpos = 0 {\def \ifevenpage ##1{\if \evenp{\marginrulepage}##1\fi}% \def \ifoddpage ##1{\if \oddp{\marginrulepage}##1\fi}% \def \leftmargin {\global\zmrhpos=1\relax}% \def \outsidemargin{\ifevenpage{\leftmargin}\ifoddpage{\rightmargin}}% \def \rightmargin {\global\zmrhpos=2\relax}% #1\relax}% \ifcase \zmrhpos \error{nomrpos} {No horizontal position has been specified for a margin rule}% \or \if \negp{\leftshift}% \zmrshift = \leftshift \else \if \evenp{\marginrulepage}% \calculate \zmrshift = {-\evenlefttextmargin,+,\leftshift}% \else \calculate \zmrshift = {-\oddlefttextmargin,+,\leftshift}% \fi \fi \or \calculate \zmrshift = {\textmeasure,+,\rightshift}% \fi} \def \zmrgraphic {% \if \emptytoksp{\artspec}% \artheight = 0pt \else \xdef \znext {\noexpand\art{zmarginrule}{\the\artspec}}% \znext \kern -1pt \fi} \definedimen{\zmrdepth}{0pt} \def \zmrrule #1,#2\zmark{% height,depth\zmark \calculate \zmrdepth = {#2,-,\artheight}% \setbox \zboxa = \hbox{\vrule width 0pt height #1 depth \zmrdepth}% \vskip -\ht\zboxa \colorrule{\the\rulecolor}{width \rulewidth height #1 depth \zmrdepth}} \def \artzmarginruledesign {% \aboveskip = \noskip \belowskip = \noskip \setflag \cliptobb = \false \hfuzz = 999pc \leftindent = 0pt \rightindent = 0pt \vshift = \mindimen} % This macro is invoked at the beginning of each division. \def \zmrdivinit {% \global\zdivmrcounter = 0\relax} % Corner Marks % ------ ----- \def \topcornermarks #1#2#3{% {box-width}{length}{thickness} \hbox to #1{\vrule height #3 depth 0pt width #2 \hss \vrule height #3 depth 0pt width #2}% \nointerlineskip \hbox to #1{\vrule height #2 depth 0pt width #3 \hss \vrule height #2 depth 0pt width #3}} \def \bottomcornermarks #1#2#3{% {box-width}{length}{thickness} \hbox to #1{\vrule height #2 depth 0pt width #3 \hss \vrule height #2 depth 0pt width #3}% \nointerlineskip \hbox to #1{\vrule height #3 depth 0pt width #2 \hss \vrule height #3 depth 0pt width #2}} % Utilities % --------- \definecount{\zmissingart}{0} % Count of missing art files. % Check that the art file exists and issue remark if not. \def \zchkartfile #1{% {\flag} \setflag #1= \true \checkfile{\znext}{\zartfile}% \if \notp{\znext}% \remark{Art file `\zartfile' is missing; ignored for now}% \global\increment \zmissingart \setflag #1= \false \fi} \def \zparseartspec #1#2.#3.#4\zmark{% name.ext.\zmark \def \zartfile {???}% % Why do this? \if \codeeqlp{#1}{!}% \def \zartfile{#2.#3}% \else\if \codeeqlp{#1}{=}% \zprependartroot{\zartfile}{#2.#3}% \else \zprependartroot{\zartfile}{#1#2.#3}% \fi\fi}