%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Module: ZzTeX Float Facilities % % Synopsis: This module contains the float and caption blocks, as well as % all the code required to support the scheduling of floats. % % Notes: The broadside capability was implemented by Richard A. Wells and % integrated into ZzTeX on 9/7/94. % % Author: Richard A. Wells & Paul C. Anagnostopoulos % Created: 7 March 1990 % % Copyright 1989--2020 by Paul C. Anagnostopoulos % under The MIT License (opensource.org/licenses/MIT) % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Data Structures % ---- ---------- \definecount{\zfltcount}{0} \def \zfloatfreeq {} \def \zfloatpendq {} \def \zdeffloat #1#2{% {\float}{\float-box} \definebox{#2}% \zfreefloat{#1}{#2}% \global\increment \zfltcount} \def \zgetfloat #1{% {\float} \if \emptydefp{\zfloatfreeq}% \error{toomanyflt} {More than `\the\zfltcount' floats remain for page makeup}% \else \pop{#1}{\zfloatfreeq}% \fi} \def \zfreefloat #1#2{% {\float}{\float-box} \gdef #1{\def \zcurfloatbox {#2}}% \append{#1}{\zfloatfreeq}} \zdeffloat{\zflta}{\zfltboxa} \zdeffloat{\zfltb}{\zfltboxb} \zdeffloat{\zfltc}{\zfltboxc} \zdeffloat{\zfltd}{\zfltboxd} \zdeffloat{\zflte}{\zfltboxe} \zdeffloat{\zfltf}{\zfltboxf} \zdeffloat{\zfltg}{\zfltboxg} \zdeffloat{\zflth}{\zfltboxh} \zdeffloat{\zflti}{\zfltboxi} \zdeffloat{\zfltj}{\zfltboxj} \zdeffloat{\zfltk}{\zfltboxk} \zdeffloat{\zfltl}{\zfltboxl} \zdeffloat{\zfltm}{\zfltboxm} \zdeffloat{\zfltn}{\zfltboxn} \zdeffloat{\zflto}{\zfltboxo} \zdeffloat{\zfltp}{\zfltboxp} \zdeffloat{\zfltq}{\zfltboxq} \zdeffloat{\zfltr}{\zfltboxr} \zdeffloat{\zflts}{\zfltboxs} \zdeffloat{\zfltt}{\zfltboxt} \zdeffloat{\zfltu}{\zfltboxu} \zdeffloat{\zfltv}{\zfltboxv} \zdeffloat{\zfltw}{\zfltboxw} \zdeffloat{\zfltx}{\zfltboxx} \zdeffloat{\zflty}{\zfltboxy} \zdeffloat{\zfltz}{\zfltboxz} % There is an insert class for each of the two places where floats % can go. \defineinsert{\ztopcolinsert} % Floats at top of column. \count\ztopcolinsert = 1000 \dimen\ztopcolinsert = \maxdimen \skip\ztopcolinsert = 0pt \defineinsert{\zbotcolinsert} % Floats at bottom of column. \count\zbotcolinsert = 1000 \dimen\zbotcolinsert = \maxdimen \skip\zbotcolinsert = 0pt % We need a dimension register to capture box depths. \definedimen{\zfloatdp}{0pt} % Float Types % ----- ----- \setlist \zfloattypelist = {} \def \definefloattype #1#2#3{% {type}{toc-level}{association} \if \emptyargp{#3}% \withname\definecount{\float#1number}{0}% \withname\declaresnapitem{\float#1number}% \else \edef \znext {% \global\withname\let{\float#1number}=\name{\float#3number}}% \znext \fi \withname\gdef{\zflt#1lvl}{#2}% \append{#1}{\zfloattypelist}} \def \resetfloatnumbers {% \maplist{\global\name{\float##1number}=0\relax}{\zfloattypelist}} % Predefined float types (don't forget an index locator for each one). \definefloattype{algorithm}{101}{} \definefloattype{figure}{102}{} \definefloattype{table}{103}{} \definefloattype{listing}{104}{} \definefloattype{sidebar}{105}{} % Float Block % ----- ----- \defineblock{\float}{\endfloat}{\false}{} %~block float Type % \aboveskip = glue % Space b/b above float. % \def \beginformat {...} % Beginning of float formatter. % \belowskip = glue % Space b/b below float. % \def \comptextformat {...} % Composite number text formatter. % \continue = integer % Continued or continuation. % \def \endformat {...} % End of float formatter. % \leftindent = dimen % Horizontal fudge for all floats. % \pageplacement = {...} % Overriding page placement. % \position = {...} % Position (see below). % \runners = {style} % Runners for a page of floats. % \topfloatfudge = dimen % Vertical fudge for topmost float. % \width = dimen % Explicit width for float. %~end \definetoks{\pageplacement} \definedimen{\topfloatfudge}{0pt} % These derived parameters are needed here and by \caption. They may % also be useful to the user. \definecount{\floatpage}{0} \definedimen{\floatshift}{0pt} \definedimen{\floatwidth}{0pt} \definecount{\zdivfltcounter}{0} % Count floats in each division. \definebox{\zfltbroadbox} % Floats are packaged up in a vbox using \vtop, so that the float box % has the same height as the first thing inside. This improves the % chances that a float at the top of a page will be positioned correctly. \def \float #1{% {type} \if \undefinedp{\float#1number}% \error{undefflt}{Float type `#1' has not been defined}% \fi \endgraf \blockcantbein{\float}{\float}% \blockcantbein{\float}{\multicolumn}% \beginblockscope{float}% \global\increment \floatdepth \global\increment \zdivfltcounter \whichpage{\floatpage}{zF:\the\divisionname-\the\zdivfltcounter}% \def \beginformat {}% %~default soft \continue = 0 %~default with \def \comptextformat {}% %~default soft \setflag \continuation = \false % Deprecated \def \endformat {}% %~default soft \leftindent = 0pt %~default soft \pageplacement = {}% %~default with \runners = {normal}% %~default soft \zoldrunners{\normalheader\normalfooter}% \width = \naturalwidth %~default soft \processdesign{\float}{#1}% \zsetcontinue % Handle old \continuation. \if \notp{\continuationp}\global\withname\increment{\float#1number}\fi \global\withname\floatnumber{\float#1number}% \setcomptext{\floatcomptext}% \def \zcurflttype {#1}% \edef \zcurfltlvl {\name{\zflt\zcurflttype lvl}}% \parindent = 0pt \parrag = 0pt \parskip = 0pt \zparsefltpos{\the\position}% \if \notp{\emptytoksp{\pageplacement}}% \zparsefltpagepos{\the\pageplacement}% \fi \if \zfltposhere \penalty \breakallowed % Synchronize main list. \zparsefltpos{\the\position}% % Recalculate global stuff. \if \notp{\emptytoksp{\pageplacement}} \zparsefltpagepos{\the\pageplacement}% \fi \fi \zcalcfltparams \zgetfloat{\zcurfloat}% \zcurfloat \global\setbox \zcurfloatbox = \vtop \bgroup \zpushvcontext \setindentation{0pt}{0pt}% \settextwidth{\floatwidth}% \settaginfo{\the\floatcomptext}{}% % Set tag info without title. \setsubtagnumber{0}% \beginformat} \def \endfloat {% \endgraf \endformat \global\zfloatdp = \prevdepth \tagpageonly{\uniquetag{zF}{\the\zdivfltcounter}}% \zpopvcontext \egroup % \vtop \expandafter\xdef \zcurfloat {% \def \noexpand\zcurfloatid {\zcurflttype\space \the\floatnumber}% \def \noexpand\zcurfloatbox {\zcurfloatbox}% \zfloatdp = \the\zfloatdp \aboveskip = \the\aboveskip \belowskip = \the\belowskip \leftindent = \the\leftindent \position = {\the\position}% \pageplacement = {\the\pageplacement}% \runners = {\the\runners}% \floatshift = \the\floatshift \topfloatfudge = \the\topfloatfudge}% \expandafter\append \zcurfloat{\zfloatpendq}% \zschedulefloats{\znext}{\false}{\false}% \if \gtrp{\znext}{99}% \zfltpage{\zpenfloatpage}% \fi \global\decrement \floatdepth \endblockscope{float}} %~ This |\float| modifier forces an alone float. \def \alonefloat {% %^modifier \with{\pageplacement={\alone}}} %~ This |\float| modifier forces a bottom float. \def \bottomfloat {% %^modifier \with{\pageplacement={\bottom}}} %~ This |\float| modifier forces a broadside float. \def \broadsidefloat {% %^modifier \with{\pageplacement={\broadside}}} %~ This |\float| modifier forces a here float. \def \herefloat {% %^modifier \with{\pageplacement={\here}}} %~ This |\float| modifier forces a separate float. \def \separatefloat {% %^modifier \with{\pageplacement={\separate}}} %~ This |\float| modifier allows the float to occupy the %~ running head area. Any running head is deleted. \def \userharea {% %^modifier \with{\runners={blind}% \advance \topfloatfudge by -\headerheight}} \def \adjusttopfloatfudge #1{% \global\advance \topfloatfudge by #1\relax} \def \piggybackfloat {% \edef \znext {\noexpand\global\noexpand\increment \name{\float\zcurflttype number}}% \znext \edef \znext {\floatnumber = \name{\float\zcurflttype number}}% \znext} % Controlling Floats % ----------- ------ % This macro is invoked at the beginning of each division. \def \zfltdivinit {% \global\zdivfltcounter = 0\relax} \setflag \zschedflts = \true \def \deferfloats #1{% {boolean} \global\setflag \zschedflts = {\notp{#1}}} % Scheduling Tools % ---------- ----- % These counters keep track of the number of floats scheduled for the % current page. \definecount{\ztopcolfloats}{0} % Number of top column floats. \definecount{\zbotcolfloats}{0} % Number of bottom column floats. \definecount{\zfloatsinserted}{0} % Total number of floats. \definetoks{\zfltrunners} % Runners for first float on page. % This macro initializes for handling floats on a new page. \def \zfloatinit {% \global\setflag \zschedflts = \true \global\ztopcolfloats = 0 \global\zbotcolfloats = 0 \zfltrunners = {}} % These macros simply hide some of the details to make the \zsched* % macros easier to read. \def \ztallytopfloat {% \if \orp{\notp{\zfltposmargonly}}{\posp{\ztopcolfloats}}% \global\increment \ztopcolfloats \fi} \def \zforcenewpage{% \global\zfloatsinserted = 101\relax} \let \zquitscheding = \exitmaplist \def \zsetfloattall{% \zmakefloatalone \setflag \zfloattall = \true \tracepagemakeup{Float is too tall for column; positioning `alone'.}} \def \zmakefloatalone{% \setflag \zfltposalone = \true \setflag \zfltposbottom = \false \setflag \zfltposhereonly = \false \setflag \zfltposhere = \false \setflag \zfltposseparate = \false \setflag \zfltpostop = \false} \def \zmakefloattop{% \setflag \zfltposalone = \false \setflag \zfltposbottom = \false \setflag \zfltposhereonly = \false \setflag \zfltposhere = \false \setflag \zfltposseparate = \false \setflag \zfltpostop = \true} % Float Scheduler % ----- --------- % return-count is a counter passed in by the caller that we should % set at the end of \zschedulefloats to indicate how % many floats were emitted during this call. A value % greater than 99 indicates that a new page should be % FORCED (e.g. for alone floats) by the output routine. % during-output? is a flag indicating that the caller is % part of the output routine. % flushing? A flag indicating whether we are flushing floats. \def \zschedulefloats #1#2#3{% {return-count}{during-output?}{flushing?} \tracepagemakeup{[Scheduling \if #2floats during output% \else float when encountered\fi.}% \tracepagemakeup{Page total: \the\pagetotal; goal: \the\pagegoal.}% % Reset our counter \global\zfloatsinserted = 0 \if \zschedflts \if \emptydefp{\zfloatpendq}% \tracepagemakeup{There are no floats to be scheduled.}% \else \endgraf \zinitfloatfit{#2}% {\setflag \zschedfirstseparate = \true \setflag \zschedseparateloop = \false \maplist{\zschedone{#2}{#3}{##1}}{\zfloatpendq}}% \fi \else \tracepagemakeup{Floats are deferred now.}% \fi \tracepagemakeup{Float scheduling complete; floats inserted: \the\zfloatsinserted.]}% \xdef #1{\the\zfloatsinserted}} \def \zschedone #1#2#3{% {during-output?}{flushing?}{\float} #3% \zparsefltpos{\the\position}% \if \notp{\emptytoksp{\pageplacement}} \zparsefltpagepos{\the\pageplacement}% \fi \if #2% % If flushing floats, force float \setflag \zfltposbottom = \false % to a `separate'. \setflag \zfltpostop = \false \setflag \zfltposseparate = \true \fi \tracepagemakeup{Trying to schedule \zcurfloatid; size: \the\ht\zcurfloatbox+\the\dp\zcurfloatbox.}% \if \orp{\andp{\zfltposeven}{\oddpagep}}% {\andp{\zfltposodd}{\evenpagep}}% \tracepagemakeup{Float is deferred until appropriate even/odd page.}% \zquitscheding \else \setflag \zfloatunsched = \true \setflag \zfloattall = \false % The following macros attempt, in order of priority, to schedule % the current float. Each macro may set up state that causes % the subsequent macro(s) to do nothing. \zschedhereonly{#1}% \zschedhere{#1}% \zschedalone{#1}% \zschedbroadside{#1}% \zschedseparate{#1}% \zschedtop{#1}% \zschedbottom{#1}% \if \zfloattall % Schedule as `alone'. \zschedalone{#1}% \fi \if \zfloatunsched \tracepagemakeup{Float could not be scheduled.}% % This exits a \maplist call to \zschedone. \zquitscheding \else % Remove the current float from the queue... \pop{\zcurfloat}{\zfloatpendq}% % ...and free up the associated data structures. \expandaftertwice\zfreefloat \expandafter\zcurfloat\zcurfloatbox \fi \fi} % Fitting a Float % ------- - ----- % These registers are used in float fitting. They contain, % respectively, the amount of vertical space used up by floats for % the current page and the maximum space available for floats. \definedimen{\zfloatpageused}{0pt} \definedimen{\zfloatmaxavail}{0pt} % Macro to initialize the above parameters (called in \zschedulefloats). \def\zinitfloatfit #1{% {during-output?} \if #1 \zfloatpageused = 0pt % Effectively: zfloatmaxavail = (100-mintextpercent) * vsize \calculate \tcounta = {100,-,\mintextpercent}% \calculate \zfloatmaxavail = {\vsize,P,\tcounta}% \else % Effectively: zfloatpageused = max(pagetotal, vsize * mintextpercent) \calculate \zfloatpageused = {\vsize,P,\mintextpercent,X,\pagetotal}% \zfloatmaxavail = \pagegoal \fi} % The spacing above and below a float is fairly complicated. % In this table, h is the height of the float, d is the depth. % % Position Sequence Above Below % % \alone only \topskip - h + none % \topfloatfudge % \bottom first \aboveskip - h none % rest min(\aboveskip, none % \belowskip) - h % % \separate first \topskip - h + none % \topfloatfudge % rest min(\aboveskip, none % \belowskip) - h % \top first \topskip - h + \belowskip - \topskip - d % \topfloatfudge % rest min(\aboveskip, \belowskip - \topskip - d % \belowskip) - h % Here and broadside floats are handled differently, below in \zfitfloathere % and \zfitfloatbroadside \def \zfitfloat #1#2#3#4#5{% {result-flag}{top?}{aboveskip-first} % {aboveskip-rest}{belowskip} {\if \zfltposmargonly \tcounta = 0 % Margin float should act as first one. \else \tcounta = \if #2\ztopcolfloats \else \zbotcolfloats \fi \fi \zskipa = \if \zerop{\tcounta}#3\else #4\fi \advance \zskipa by -\ht\zcurfloatbox \if \andp{#2}{\zerop{\tcounta}}\advance \zskipa by \topfloatfudge \fi \tdimena = \zskipa \advance \tdimena by \ht\zcurfloatbox \advance \tdimena by \dp\zcurfloatbox \tdimenb = #5\advance \tdimena by \tdimenb \tdimenc = \zfloatpageused \if \notp{\zfltposmargonly}\advance \tdimenc by \tdimena \fi \tracepagemakeup{Requires: \the\tdimena; available: \the\zfloatmaxavail; used: \the\zfloatpageused.}% \if \dimlssp{\tdimenc}{\zfloatmaxavail}% \if \andp{\notp{#2}}{\zfltposmargonly}% \insert \zbotcolinsert {% \vbox to 0pt{% \vss \moveright \floatshift \box\zcurfloatbox \vskip -\dp\zcurfloatbox}}% \else \insert \if #2\ztopcolinsert \else \zbotcolinsert \fi {% \vskip \zskipa \moveright \floatshift \box\zcurfloatbox \nobreak \if \zfltposmargonly \vskip \tdimenb \nobreak \vskip -\tdimena \else \vskip #5\relax \fi}% \fi \if \emptytoksp{\zfltrunners}% \global\zfltrunners = \expandafter{\the\runners}% \fi \global\increment \zfloatsinserted \global\zfloatpageused = \tdimenc \global\setflag #1= \true \else \global\setflag #1= \false \fi}} \def \zfitfloathere #1#2#3{% {result-flag}{aboveskip}{belowskip} {\tdimena = #2\relax \advance \tdimena by \dp\zcurfloatbox \advance \tdimena by #3\relax \tracepagemakeup{Requires: \the\tdimena; page total: \the\pagetotal; page goal: \the\pagegoal.}% \advance \tdimena by \pagetotal \if \dimlssp{\tdimena}{\pagegoal}% \bbskipabove{\breaknever}{#2}% \fakepar \moveright \floatshift \box\zcurfloatbox \prevdepth = \zfloatdp \bbskipbelow{\breakallowed}{#3}% \global\increment \zfloatsinserted \global\setflag #1= \true \else \global\setflag #1= \false \fi}} \def \zfitfloatbroadside {% {% We know there are no other floats on the the topcolumn when % this is called (by \zschedbroadside only) and we don't have to % worry about fixing up the vertical position at the end of the % page. % % Compute the space required % Start with the topskip \tdimena = \topfloatfudge % Add the *width* (since the box will be rotated) \advance \tdimena by \wd\zcurfloatbox \tracepagemakeup{Requires: \the\tdimena; broadside floats always fit.}% \insert \ztopcolinsert {% % vskip the top float fudge \vskip \topfloatfudge % save the width for unskipping later \tdimenb = \wd\zcurfloatbox % vskip the *width* of the box to do the position translation % that we can't do with PostScript \vskip \tdimenb % Put our hbox in a vbox, so we can "lower" it rather than move % it right (since it will be rotated) \global\setbox\zfltbroadbox = \vbox{% \vskip\floatshift % replaces \moveright in other floats \box\zcurfloatbox}% \rotatebox{\zfltbroadbox}{90}% % Undo vertical skip so that insert doesn't cause additional page \vskip -\tdimenb}% \if \emptytoksp{\zfltrunners}% \global\zfltrunners = \expandafter{\the\runners}% \fi \global\increment \zfloatsinserted \global\zfloatpageused = \tdimena}} % Individual Schedulers % ---------- ---------- % This is a temporary register used in calls to \zfitfloat where the % fit needs to take into account more than one skip value (such as % both the \aboveskip and \belowskip). In that case this dimen is % used as a temp to hold the sum of the skip values. \defineskip{\zfloatabove}{0pt} \defineskip{\zfloatbelow}{0pt} \definedimen{\zfloatextraskip}{0pt} % This scheduler is called for floats with position `here' and no others. \def\zschedhereonly #1{% {during-output?} \if \andp{\zfloatunsched}{\zfltposhereonly}% \tracepagemakeup{Float is to be positioned `here' only.}% \if \zschedseparateloop \tracepagemakeup{Making up a page of separates; quit.}% \zforcenewpage \zquitscheding \else \if #1% during output: \tracepagemakeup{Float moved to top of this page.}% \zmakefloattop \else % when encountered: \if \posp{\zbotcolfloats}% \error{hereonlybot}{Cannot position float `here'; already floats at bottom.}% \else\if \dimzerop{\pagetotal}% \tracepagemakeup{Float at top of column: schedule as `top' float.}% \zmakefloattop \else \zfitfloathere{\zfloatfits}{\aboveskip}{\belowskip}% \if \zfloatfits \tracepagemakeup{Float inserted here.}% \setflag \zfloatunsched = \false \else \tracepagemakeup{Float does not fit here.}% \zforcenewpage \zquitscheding \fi \fi\fi \fi \fi \fi} % This scheduler is called for floats with position `here' along with at % least one other page position. \def\zschedhere #1{% {during-output?} \if \andp{\zfloatunsched}{\zfltposhere}% \tracepagemakeup{Float can be positioned `here'.}% \if \zschedseparateloop \tracepagemakeup{Making up a page of separates; quit.}% \zforcenewpage \zquitscheding \else \if #1% during output: \relax \else % when encountered: \if \posp{\zbotcolfloats}% \tracepagemakeup{There are already floats positioned `bottom'.}% \else\if \dimzerop{\pagetotal}% \tracepagemakeup{Float at top of column: schedule as `top' float.}% \zmakefloattop \else \zfitfloathere{\zfloatfits}{\aboveskip}{\belowskip}% \if \zfloatfits \tracepagemakeup{Float inserted here.}% \setflag \zfloatunsched = \false \else \tracepagemakeup{Float does not fit here.}% \fi \fi\fi \fi \fi \fi} \def \zschedalone #1{% {during-output?} \if \andp{\zfloatunsched}{\zfltposalone}% \tracepagemakeup{Float can be positioned `alone'.}% \if \zschedseparateloop \tracepagemakeup{Making up a page of separates; quit.}% \zforcenewpage \zquitscheding \else \if \orp{#1}{\dimzerop{\pagetotal}}% \if \firstcolumnp % building new page \if \zerop{\zfloatsinserted}% no floats on this page, yet \dp\zcurfloatbox = 0pt % Make it fit. \zfitfloat{\zfloatfits}{\true}{\topskip}{999pt}{0pt}% \if \zfloatfits \ztallytopfloat \tracepagemakeup{Inserted on a page by itself.}% \zforcenewpage \setflag \zfloatunsched = \false \zquitscheding \fi \else % wait for next time \tracepagemakeup{Already floats in this column; quit.}% \zquitscheding \fi \else% still in the middle of a page \tracepagemakeup{Not in first column of page; quit.}% \zquitscheding \fi \else % As encountered, and page not empty. \tracepagemakeup{Must wait for new page; quit.}% \zquitscheding \fi \fi \fi} \def \zschedbroadside #1{% {during-output?} \if \andp{\zfloatunsched}{\zfltposbroadside}% \tracepagemakeup{Float can be positioned `broadside'.}% \if \zschedseparateloop \tracepagemakeup{Making up a page of separates; quit.}% \zforcenewpage \zquitscheding \else \if \orp{#1}{\dimzerop{\pagetotal}}% \if \firstcolumnp % building new page \if \zerop{\zfloatsinserted}% no floats on this page, yet % Broadside floats "always" fit. The output will have to be % inspected. \zfitfloatbroadside \ztallytopfloat \tracepagemakeup{Inserted on a page by itself.}% \zforcenewpage \setflag \zfloatunsched = \false \zquitscheding \else % wait for next time \tracepagemakeup{Already floats on this page; quit.}% \zquitscheding \fi \else% still in the middle of a page \tracepagemakeup{Not in first column of page; quit.}% \zquitscheding \fi \else % As encountered, and page not empty. \tracepagemakeup{Must wait for new page; quit.}% \zquitscheding \fi \fi \fi} \def\zschedseparate #1{% {during-output?} \if \andp{\zfloatunsched}{\zfltposseparate}% \tracepagemakeup{Float can be positioned `separate'.}% % If a separate float is called when encountered, we don't do anything. \if #1% during output \if \firstcolumnp % building a new page \if \zschedfirstseparate \if \zerop{\zfloatsinserted}% no floats on this page, yet \zfloatmaxavail = \vsize % Entire page is available now. \zfitfloat{\zfloatfits}{\true}{\topskip}{999pt}{0pt}% \if \zfloatfits \ztallytopfloat \setflag \zfloatunsched = \false \zforcenewpage % This is a page of separates now. \tracepagemakeup{Inserted at the top of a new page.}% \setflag \zschedfirstseparate = \false \setflag \zschedseparateloop = \true \else \zsetfloattall \fi \else % wait for next time \tracepagemakeup{Page already has other floats on it.}% \zquitscheding \fi \else % Subsequent separates. \zfloatabove = \if \dimlssp{\aboveskip}{\belowskip}\aboveskip \else \belowskip \fi \zfitfloat{\zfloatfits}{\true}{999pt}{\zfloatabove}{0pt}% \if \zfloatfits \ztallytopfloat \setflag \zfloatunsched = \false \tracepagemakeup{Inserted on a page with other separates.}% \else \zquitscheding \tracepagemakeup{Does not fit on this page; start new page.}% \fi \fi \else% still in the middle of a page \tracepagemakeup{Must wait for new page; quit;}% \zquitscheding \fi \else% when encountered \tracepagemakeup{Must wait for new page; quit.}% \zquitscheding \fi \fi} \def\zschedtop#1{% {during-output?} \if \andp{\andp{\zfloatunsched}{\zfltpostop}} {\orp{\notp{\zfltposfollow}}{#1}}% \tracepagemakeup{Float can be positioned `top'.}% \if \zschedseparateloop \tracepagemakeup{Making up a page of separates; quit.}% \zforcenewpage \zquitscheding \else \if \lssp{\ztopcolfloats}{\maxtopcolumnfloats}% \zfloatabove = \if \dimlssp{\aboveskip}{\belowskip}\aboveskip \else \belowskip \fi \advance \zfloatabove by -\belowskip \advance \zfloatabove by \topskip \zfloatbelow = \belowskip \tdimena = \topskip \advance \zfloatbelow by -\tdimena \advance \zfloatbelow by -\zfloatdp \zfitfloat{\zfloatfits}{\true}{\topskip}{\zfloatabove}{\zfloatbelow}% \if \zfloatfits \ztallytopfloat \tracepagemakeup{Inserted at top of column.}% \setflag \zfloatunsched = \false \else \if \andp{#1}{\zerop{\zfloatsinserted}}\zsetfloattall \fi \fi \fi \fi \fi} \def \zschedbottom#1{% {during-output?} \if \andp{\zfloatunsched}{\zfltposbottom}% \tracepagemakeup{Float can be positioned `bottom'.}% \if \zschedseparateloop \tracepagemakeup{Making up a page of separates; quit.}% \zforcenewpage \zquitscheding \else \if \lssp{\zbotcolfloats}{\maxbottomcolumnfloats}% \zfloatbelow = \if \dimlssp{\aboveskip}{\belowskip}\aboveskip \else \belowskip \fi \zfitfloat{\zfloatfits}{\false}{\aboveskip}{\zfloatbelow}{0pt}% \if \zfloatfits \if \notp{\zfltpostopafterbot}% \global\ztopcolfloats = \maxtopcolumnfloats \fi \global\increment \zbotcolfloats \tracepagemakeup{Inserted at bottom of column.}% \setflag \zfloatunsched = \false \else \if \andp{#1}{\zerop{\zfloatsinserted}}\zsetfloattall \fi \fi \fi \fi \fi} % Captions % -------- \defineblock{\caption}{\endcaption}{\false}{} %~block caption Type % \aboveskip = glue % Space b/b above caption. % \setflag \allowintoc = flag % Allow caption in ToC. % \belowskip = glue % Space b/b below caption. % \captionfudge = dimen % Fudge for \aboveskip. % \def \comptextformat {...} % (No longer used with captions.) % \difficulty = integer % Level of difficulty. % \leftindent = glue % Left indentation. % \legendfont = {...} % Font for optional legend. % \def \legendformat ##1{...} % Legend formatter. % \numberfont = {...} % Font for float number. % \parindent = dimen % Paragraph indent. % \parrag = dimen % Paragraph raggedness. % \parskip = glue % Paragraph skip. % \position = {...} % Position (see below). % \rightindent = glue % Right indentation. % \style = {...} % Heading style. % \titlefont = {...} % Font for caption title. % \def \titleformat ##1{...} % Title formatter. % \width = dimen % Width of caption text. %~end \definedimen{\captionfudge}{0pt} \definetoks{\legendfont} \definebox{\zcapbox} \definedimen{\zcapshift}{0pt} %~ This marker indicates that the following text is the legend or %~ credit for the caption. \definemarker{\legend} \def \caption #1{% {title with optional \legend} \blockmustbein{\caption}{\float}% \blockcantbein{\caption}{\caption}% \beginblockscope{caption}% \global\increment \captiondepth \captionfudge = 0pt %~default with \def \comptextformat {}% %~default hard \style = {Initial cap/lowercase}% %~default soft \difficulty = 0 %~default with \processdesign{\caption}{\zcurflttype}% \global\increment \captionnumber \if \notp{\emptydefp{\comptextformat}}% \error{capcomptext}{The \string\comptextformat\space parameter is no longer used in captions} \fi \captionformat #1\legend\zmark \if \andp{\allowintoc}{\notp{\continuationp}}\zcaptoc #1\legend\zmark \fi \endcaption \zcaptag #1\legend\zmark}% % Reset tag info with title. \def \endcaption {% \global\decrement \captiondepth \endblockscope{caption}} \def \captionformat #1\legend#2\zmark{% \the\numberfont \setbox\zcapbox = \vtop{% \zpushvcontext \setindentation{\leftindent}{\rightindent}% \settextwidth{\width}% \setparrag{\parrag}% \if \emptyargp{#2}% \titleformat{#1}% \else \let \legend = \relax % So it won't interfere with formatting. \titleformat{#1\the\legendfont \legendformat{#2}}% \fi \endgraf \global\zfloatdp = \prevdepth \zpopvcontext}% \zparsecappos{\the\position}% \zcalccapitems \bbskipabove{\breaknever}{\aboveskip}% \noindent \kern \zcapshift \box\zcapbox \par \prevdepth = \zfloatdp \bbskipbelow{\breaknever}{\belowskip}} \def \zcaptoc #1\legend#2\zmark{% \tocentry{\the\floatcomptext}{#1}{\zcurfltlvl}} \def \zcaptag #1\legend#2\zmark{% \settaginfo{\the\floatcomptext}{#1}} % Position Handling % -------- -------- % This macro sets up variables that specify the position of a float, % according to the \position design parameter: % % \alone Place float on a page by itself. % \anypage Place float on any page. % \any (synonym for \anypage) % \bottom Place float at the bottom of a page. % \broadside Place float broadside (alone, rotated 90 degrees). % \evenpage Place float on an even page. % \follow Float must follow its position in the text. % \here Place float exactly where it appears. % \ifevenpage Use next position only on even page. % \ifoddpage Use next position only on odd page. % \leftmargin Shift float to left margin. % \margin Same as \leftmargin. % \marginonly Float fits in the margin and does not occupy any % vertical space on the page. % \nofollow Float need not follow its position in the text. % \oddpage Place float on an odd page. % \outsidemargin Shift float to outside margin. % \rightmargin Shift float to right margin. % \separate Place float on a page with floats only. % \textarea Leave float at left edge of text area. % \text (synonym for \textarea) % \top Place float at the top of a page. % \topafterbottom Okay to schedule \top float after \bottom. \definecount{\zfltposshift}{0} \def \zparsefltpos #1{% {options...} {\zclearfltpagepos \global\setflag \zfltposmargonly = \false \global\zfltposshift = 0\relax \zsetupfltpageopts \def \ifevenpage ##1{\if \evenp{\floatpage}##1\fi}% \def \ifoddpage ##1{\if \oddp{\floatpage}##1\fi}% \def \leftmargin {\global\zfltposshift=1\relax}% \let \margin = \leftmargin \def \marginonly {\global\setflag\zfltposmargonly=\true}% \def \outsidemargin{\ifevenpage{\leftmargin}\ifoddpage{\rightmargin}}% \def \rightmargin {\global\zfltposshift=2\relax}% \def \textarea {\global\zfltposshift=0\relax}% \let \text = \textarea \global\floatpage = \floatpage #1% \global\setflag \zfltposhereonly = \zfltposhere \global\setflag \zfltposok = \zfltposhere \if \orp{\zfltposalone} {\orp{\zfltposbottom} {\orp{\zfltposbroadside} {\orp{\zfltposseparate} {\zfltpostop}}}}% \global\setflag \zfltposhereonly = \false \global\setflag \zfltposok = \true \fi \if \zfltposhereonly \setflag \zfltposhere = \false \fi \if \notp{\zfltposok}% \warning{nofloatpos}{No page position has been specified for a float}% \global\setflag \zfltposalone = \true \fi}} \def \zparsefltpagepos #1{% {options...} {\zclearfltpagepos \zsetupfltpageopts #1% \global\setflag \zfltposhereonly = \zfltposhere \if \orp{\zfltposalone} {\orp{\zfltposbottom}{\orp{\zfltposseparate}{\zfltpostop}}}% \global\setflag \zfltposhereonly = \false \fi \if \zfltposhereonly \global\setflag \zfltposhere = \false \fi}} \def \zclearfltpagepos {% \global\setflag \zfltposalone = \false \global\setflag \zfltposbottom = \false \global\setflag \zfltposbroadside = \false \global\setflag \zfltposeven = \false \global\setflag \zfltposfollow = \false \global\setflag \zfltposhere = \false \global\setflag \zfltposodd = \false \global\setflag \zfltposseparate = \false \global\setflag \zfltpostop = \false \global\setflag \zfltpostopafterbot = \false} \def \zsetupfltpageopts {% \def \alone {\global\setflag\zfltposalone=\true}% \def \anypage {\global\setflag\zfltposeven=\false \global\setflag\zfltposodd=\false}% \let \any = \anypage \def \bottom {\global\setflag\zfltposbottom=\true}% \def \broadside {\global\setflag\zfltposbroadside=\true}% \def \evenpage {\global\setflag\zfltposeven=\true}% \def \follow {\global\setflag\zfltposfollow=\true}% \def \here {\global\setflag\zfltposhere=\true}% \def \nofollow {\global\setflag\zfltposfollow=\false}% \def \oddpage {\global\setflag\zfltposodd=\true}% \def \separate {\global\setflag\zfltposseparate=\true}% \def \top {\global\setflag\zfltpostop=\true}% \def \topafterbottom {\global\setflag\zfltpostopafterbot=\true}} \def \zcalcfltparams {% \floatshift = 0pt\relax % Set \floatshift \ifcase \zfltposshift \relax \or \floatshift = \if \evenp{\floatpage}-\evenlefttextmargin \else -\oddlefttextmargin \fi \relax \or \floatshift = \textmeasure \else \zzerror{Invalid value for float `\string\zfltposshift'}% \fi \advance \floatshift by \leftindent % Set \floatwidth. Default of \naturalwidth is handled differently % for broadsides than for non-broadsides. If \width not default, use % it without modification. \if \dimeqlp{\width}{\naturalwidth}% \if \zfltposbroadside \floatwidth = \textareaheight \relax \else \floatwidth = \textmeasure \relax \ifcase \zfltposshift \relax \or \advance \floatwidth by -\floatshift \or \floatwidth = \if \evenp{\floatpage}\evenrighttextmargin \else \oddrighttextmargin \fi \relax \else \zzerror{Invalid value for float `\string\zfltposshift'}% \fi \fi \else \floatwidth = \width \relax \fi} % This macro sets up variables that specify the position of a caption, % according to the \position design parameter: % % \above Caption is placed above the figure. % \below Caption is placed below the figure. % \follow Caption follows left edge of float. % \ifevenpage Use next position only on even page. % \ifoddpage Use next position only on odd page. % \leftmargin Force caption in left margin. % \margin Same as \leftmargin % \outsidemargin Shift caption to outside margin. % \rightmargin Force caption in right margin. % \rise Caption rises up from bottom of figure. % \separate Caption is separate from figure. % \sink Caption sinks down from top of figure. % \textarea Force caption at left edge of text area. % \text (synonym for \textarea) % \textarearight Force caption at right edge of text area. % \textright (synonym for \textarearight) \definecount{\zcapposshift}{0} \definecount{\zcapposvflow}{0} \def \zparsecappos #1{% {options...} {\global\setflag \zcapposabove = \true \global\zcapposvflow = 0\relax \global\zcapposshift = 0\relax \def \above {\global\setflag\zcapposabove=\true}% \def \below {\global\setflag\zcapposabove=\false}% \def \follow {\global\zcapposshift=4\relax}% \def \ifevenpage ##1{\if \evenp{\floatpage}##1\fi}% \def \ifoddpage ##1{\if \oddp{\floatpage}##1\fi}% \def \leftmargin {\global\zcapposshift=1\relax}% \let \margin = \leftmargin \def \outsidemargin {\ifevenpage{\leftmargin}\ifoddpage{\rightmargin}}% \def \rightmargin {\global\zcapposshift=2\relax}% \def \rise {\global\zcapposvflow=1\relax}% \def \separate {\global\zcapposvflow=0\relax}% \def \sink {\global\zcapposvflow=2\relax}% \def \textarea {\global\zcapposshift=0\relax}% \let \text = \textarea \def \textarearight {\global\zcapposshift=3\relax}% \let \textright = \textarearight #1}} \def \zcalccapitems {% \ifcase \zcapposshift \zcapshift = 0pt \or \zcapshift = \if \evenp{\floatpage}-\evenlefttextmargin \else -\oddlefttextmargin \fi \relax \or \zcapshift = \textmeasure \or \zcapshift = \textmeasure \advance \zcapshift by -\wd\zcapbox \or \zcapshift = \floatshift \else \zzerror{Invalid value for caption `\string\zcapposshift'}% \fi \advance \zcapshift by -\floatshift % Captions in broadsides *always* follow, i.e. \zcapshift is 0pt \if \zfltposbroadside \zcapshift = 0pt\relax \fi \ifcase \zcapposvflow \relax \or \forcenextbbskip \aboveskip = -\dp\zcapbox \advance \aboveskip by \zfloatdp \advance \aboveskip by \captionfudge \or \advance \belowskip by -\dp\zcapbox \advance \belowskip by -\captionfudge \else \zzerror{Invalid value for caption `\string\zcapposvflow'}% \fi}