%D \module %D [ file=anch-pos, % was core-pos %D version=1999.08.01, %D title=\CONTEXT\ Anchoring Macros, %D subtitle=Positioning Support, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. % needs a cleanup, things may change; we also need to move the mp % related code to meta-pos % shorter tags, ..:achtergrond:.. etc in pos actions % dubbele text- * pos's eruit % class pos -> als gelijk aan vorige, dan niet niet definieren % en erven, maw: % % 1 -> opslaan % 2 -> undef, dus == prev % 3 -> undef, dus == prev % 4 -> opslaan \writestatus{loading}{ConTeXt Anchoring Macros / Positioning} % saveposition : tag page x y % savepositionwhd : tag page x y w h d % savepositionplus : tag page x y w h d list % % at some point (when we no longer share code) we will move to numbers % do that we have less garbage collection and hashing % % the global table _ptbs_ is equivalent to job.positions.tobesaved % the global table _pcol_ is equivalent to job.positions.collected % % btw, using a function is more efficient than passing longer code % snippets to ctxlua \registerctxluafile{anch-pos}{1.001} % todo: topskip als optie voor eerste regel achtergrond % todo: build pos layers on top of layers % todo: positionlayer pos van text-1 etc delen %D Although \TEX\ has a rather powerful channel to the outside %D world, called \type {\special}, real communication with %D other programs is complicated by the fact that no positional %D information is available. Mid 1999, I discussed this with %D \THANH, the author of \PDFTEX, and after some experiments, %D \PDFTEX\ was extended with a simple but effective mechanism, %D that provided positional information. The interesting %D thought is that, although \TEX\ is frozen, similar %D functionality could have been achieved with \type %D {\specials} and an additional \DVI\ postprocessor. %D %D Since we want to be as compatible as can be, \CONTEXT\ will %D support both methods, although the development is primarily %D driven by the \PDFTEX\ way of doing things. Since the %D mechanism is basically not limited to one application, for %D the moment we stick to building the functionality around one %D \CONTEXT\ special command, but at the same time we keep our %D eyes open for extensions in other directions. %D %D A question that may arise when one reads this module, is to %D what extend these macros are generic, in the sense that they %D could be collected in a support module instead of a core %D module. Since the mechanism described here will closely %D cooperate with the \METAPOST\ support built in \CONTEXT, %D which in turn will be tightly integrated with the \CONTEXT\ %D overlay mechanisms, I decided to write a core module instead %D of a support one. This makes even more sense, when one takes %D into account that this kind of support depends on special %D drivers. \unprotect %D The first application of positional information was embedded %D graphics. Since we are interacting with text, it made sense %D to take the current line height and depth into account too. %D This is why we have two basic position macros: one for %D simple positions, and one for boxes. %D %D We could have sticked to one special, and actually did so in %D earlier experiments, but for convenience, as well for %D clearness, we now have two alternatives. This approach will %D save us quite some bytes when storing large quantities of %D positional information. We save as less information as %D needed, that is, we save no dimensions, in a \METAPOST\ %D friendly way. %D %D The three specials involved are: %D %D \starttyping %D \dosetposition {identifier} %D \dosetpositionwhd {identifier} {width} {height} {depth} %D \dosetpositionplus {identifier} {width} {height} {depth} {list} %D \dosetpositionpapersize {width} {height} %D \stoptyping %D %D Positions are either generated at a delayed write time %D (in \PDFTEX), or derived from the dvi file. The actual %D method is implemented in a special driver. If needed, the %D driver can fall back on the following macros. % \def\dolazysaveposition #1#2#3#4{\normalexpanded{\ctxlatelua{_ptbs_['#1']={#2,"#3","#4"}}}} % \def\dolazysavepositionwhd #1#2#3#4#5#6#7{\normalexpanded{\ctxlatelua{_ptbs_['#1']={#2,"#3","#4","#5","#6","#7"}}}} % \def\dolazysavepositionplus#1#2#3#4#5#6#7#8{\normalexpanded{\ctxlatelua{_ptbs_['#1']={#2,"#3","#4","#5","#6","#7","#8"}}}} % \def\dosaveposition #1#2#3#4{\normalexpanded{\ctxlua {_ptbs_['#1']={#2,"#3","#4"}}}} % \def\dosavepositionwhd #1#2#3#4#5#6#7{\normalexpanded{\ctxlua {_ptbs_['#1']={#2,"#3","#4","#5","#6","#7"}}}} % \def\dosavepositionplus #1#2#3#4#5#6#7#8{\normalexpanded{\ctxlua {_ptbs_['#1']={#2,"#3","#4","#5","#6","#7","#8"}}}} % % \def\lastsavedpositionx {\the\dimexpr\pdflastxpos\scaledpoint\relax} % \def\lastsavedpositiony {\the\dimexpr\pdflastypos\scaledpoint\relax} % \let\savecurrentposition\pdfsavepos % % \def\dosetposition#1% will become commands.setposition % {\savecurrentposition % \normalexpanded{\ctxlatelua{_ptbs_['#1']={% % \noexpand\realfolio,"\noexpand\lastsavedpositionx","\noexpand\lastsavedpositiony"}}}} % % \def\dosetpositionwhd#1#2#3#4% % {\savecurrentposition % \normalexpanded{\ctxlatelua{_ptbs_['#1']={% % \noexpand\realfolio,"\noexpand\lastsavedpositionx","\noexpand\lastsavedpositiony","#2","#3","#4"}}}} % % \def\dosetpositionplus#1#2#3#4#5% % {\savecurrentposition % \normalexpanded{\ctxlatelua{_ptbs_['#1']={% % \noexpand\realfolio,"\noexpand\lastsavedpositionx","\noexpand\lastsavedpositiony","#2","#3","#4","#5"}}}} \def\dosaveposition #1#2#3#4{\normalexpanded{\ctxlua {_plib_.setall("#1",\number#2,\number\dimexpr#3,\number\dimexpr#4)}}} \def\dosavepositionwhd #1#2#3#4#5#6#7{\normalexpanded{\ctxlua {_plib_.setall("#1",\number#2,\number\dimexpr#3,\number\dimexpr#4,\number\dimexpr#5,\number\dimexpr#6,\number\dimexpr#7)}}} \def\dosavepositionplus#1#2#3#4#5#6#7#8{\normalexpanded{\ctxlua {_plib_.setall("#1",\number#2,\number\dimexpr#3,\number\dimexpr#4,\number\dimexpr#5,\number\dimexpr#6,\number\dimexpr#7,"#8")}}} \def\dosetposition #1{\normalexpanded{\ctxlatelua{_plib_.setdim("#1")}}} \def\dosetpositionwhd #1#2#3#4{\normalexpanded{\ctxlatelua{_plib_.setdim("#1",\number\dimexpr#2,\number\dimexpr#3,\number\dimexpr#4)}}} \def\dosetpositionplus #1#2#3#4#5{\normalexpanded{\ctxlatelua{_plib_.setdim("#1",\number\dimexpr#2,\number\dimexpr#3,\number\dimexpr#4,"#5")}}} % % % \let\dosetpositionpapersize\gobbletwoarguments \newbox\positionbox \newif \ifpositioning \def\POSprefix{POS::} %D This is real tricky! The page anchor is applied to the %D page box and therefore flushed first. So, when present, it %D is applied to all positions except itself. % 0 = don't relocate page origin 1 = relocate page origin once \setnewconstant\positionanchormode\plusone %D Sometimes we want to trick the position handler a bit: % \def\replacepospxywhd#1#2#3#4#5#6#7% % {\ctxcommand{replacepospxywhd('#1',\number#2,"\the\dimexpr#3\relax","\the\dimexpr#4\relax","\the\dimexpr#5\relax","\the\dimexpr#6\relax","\the\dimexpr#7\relax")}} \def\replacepospxywhd#1#2#3#4#5#6#7% {\ctxcommand{replacepospxywhd('#1',\number#2,\number\dimexpr#3,\number\dimexpr#4,\number\dimexpr#5,\number\dimexpr#6,\number\dimexpr#7)}} %D The next switch can be used to communicate a special %D situation. Positioning and associated actions can be %D executed any time. However, in for instance backgrounds %D they can be collected in a layer, for instance the text %D layer (especially the hidden text layer). In the case of %D floats, we run into problems, since the page information is %D not applicable when the content floats indeed. In such %D situations one can treat positions and graphics local. \newif\iflocalpositioning % todo: conditional %D Watch out: sometimes a pagebreak occurs inside a float %D placement, so there we need to disable local mode. \appendtoks \localpositioningtrue \to \everyinsidefloat \appendtoks \localpositioningfalse \to \everypagebody %D \macros %D {MPp, MPx, MPy, MPw, MPh, MPd, %D MPxy, MPll, MPlr, MPur, MPul, MPpos} %D %D Access to the positional information is provided by macros %D with short names that are clearly meant for \METAPOST\ but %D nowadays also used for other purposes. \def\MPp #1{\ctxcommand{MPp("#1")}} \def\MPx #1{\ctxcommand{MPx("#1")}} \def\MPy #1{\ctxcommand{MPy("#1")}} \def\MPw #1{\ctxcommand{MPw("#1")}} \def\MPh #1{\ctxcommand{MPh("#1")}} \def\MPd #1{\ctxcommand{MPd("#1")}} \def\MPxy #1{\ctxcommand{MPxy("#1")}} \def\MPll #1{\ctxcommand{MPll("#1")}} \def\MPlr #1{\ctxcommand{MPlr("#1")}} \def\MPur #1{\ctxcommand{MPur("#1")}} \def\MPul #1{\ctxcommand{MPul("#1")}} \def\MPpos#1{\ctxcommand{MPpos("#1")}} %D \macros %D {MPplus, MPrest, MPv, MPvv} %D %D Since we will probably keep on extending, we provide a %D general extension macro. The plus alternative takes an %D extra argument, denoting what additional parameter to pick %D up. So, the third extra is fetched with, %D %D \starttyping %D \MPplus{identifier}{3}{default} %D \stoptyping %D %D All extras (comma separated) are fetched with: %D %D \starttyping %D \MPrest{identifier} %D \stoptyping %D %D The extra parameters are not treated. \def\MPplus#1#2#3{\ctxcommand{MPplus("#1",#2,"#3")}} \let\MPv \MPplus \def\MPrest #1#2{\ctxcommand{MPrest("#1","#2")}} \let\MPvv\MPrest %D \macros %D {MPanchor} %D %D For readability we define a few synonyms: \def\MPanchor{\MPpos} %D There are two low level positioning macros. Both store the %D position as well as execute an action associated with that %D position. \def\initializenextposition {\ifpositioning \else \global\positioningtrue \dosetpositionpapersize\printpaperwidth\printpaperheight \fi} \def\setpositiononly#1% {\iftrialtypesetting % nothing \else \initializenextposition \def\currentposition{#1}% \dosetposition\currentposition \fi} \def\setposition#1% {\iftrialtypesetting % nothing \else \initializenextposition \def\currentposition{#1}% \dosetposition\currentposition \traceposstring\llap\green{\currentposition>}% \dopositionaction\currentposition \fi} \def\setpositiondata#1#2#3#4% {\iftrialtypesetting \else \initializenextposition \hbox {\def\currentposition{#1}% \dosetpositionwhd\currentposition {\the\dimexpr#2\relax}% {\the\dimexpr#3\relax}% {\the\dimexpr#4\relax}% \traceposstring\llap\green{\currentposition>}% \dopositionaction\currentposition \hss}% \fi} \def\setpositionbox#1% {\dowithnextbox {\iftrialtypesetting \flushnextbox \else \initializenextposition \hbox to \nextboxwd {\edef\currentposition{#1}% \dosetpositionwhd\currentposition {\the\nextboxwd}% {\the\nextboxht}% {\the\nextboxdp}% \traceposstring\llap\green{\currentposition>}% \setbox\positionbox\flushnextbox \dopositionaction\currentposition \box\positionbox \hss}% \fi}} \def\setpositiondataplus#1#2#3#4#5% {\iftrialtypesetting \else \initializenextposition \hbox % bug: to \nextboxwd {\edef\currentposition{#1}% \dosetpositionplus\currentposition {\the\dimexpr#2\relax}% {\the\dimexpr#3\relax}% {\the\dimexpr#4\relax}% {#5}% \traceposstring\rlap\magenta{<\currentposition}% \dopositionaction\currentposition \hss}% \fi} \def\setpositionplus#1#2% {\dowithnextbox {\iftrialtypesetting \flushnextbox \else \initializenextposition \hbox to \nextboxwd {\edef\currentposition{#1}% \dosetpositionplus\currentposition {\the\nextboxwd}% {\the\nextboxht}% {\the\nextboxdp}% {#2}% \traceposstring\rlap\magenta{<\currentposition}% \setbox\positionbox\flushnextbox \dopositionaction\currentposition \box\positionbox \hss}% \fi}} \let\currentposition\s!unknown %D A few more low level macros take care of defining and %D recalling actions. We could save this information in the %D position containers themselves, this would save hash %D entries, but at the cost of much more time consuming %D expansion. Actions are saved globally! \newtoks\everypositionaction \let\POSactionprefix\POSprefix \def\dosetpositionaction#1% {\setgvalue{\POSactionprefix#1::}} %D The lists can become quite long (also because there can %D be lots of parameters passed on) so we provide a hook %D to clean up the list afterwards. \let\cleanuppositionaction\gobbleoneargument \def\doifpositionaction#1% {\ifcsname\POSactionprefix#1::\endcsname \@EA\firstofoneargument \else \@EA\gobbleoneargument \fi} \def\doifpositionactionelse#1% {\ifcsname\POSactionprefix#1::\endcsname \@EA\firstoftwoarguments \else \@EA\secondoftwoarguments \fi} %D We can copy a position with: %D %D \starttyping %D \copyposition {to} {from} %D \stoptyping %D %D Again, this is a global action. \def\copyposition#1#2{\ctxcommand{copyposition('#1','#2')}} %D The fact that handling positions is a two pass operation, is %D one of the reasons why we need to be able to test for %D existence, using: %D %D \starttyping %D \doifpositionelse {identifier} {found action} {not found action} %D \stoptyping \def\doifpositionelse#1{\ctxcommand{doifpositionelse('#1')}} %D We have now arrived at a few macros that would make sense as %D support macros, but ended up in the core. %D \macros %D {xypos} %D %D We have several macros available to save positions. Later %D we will see applications. %D %D \starttabulate[|l|l||] %D \NC \type {\xypos} \NC \NC simple position with no dimensions \NC \NR %D \NC \type {\hpos} \NC \NC position and characteristics of a \type {\hbox} \NC \NR %D \NC \type {\vpos} \NC \NC position and characteristics of a \type {\vbox} \NC \NR %D \NC \type {\bpos} \NC b: \NC begin point in a line \NC \NR %D \NC \type {\epos} \NC e: \NC end point in a line \NC \NR %D \NC \type {\fpos} \NC f: \NC begin point in a paragraph \NC \NR %D \NC \type {\tpos} \NC t: \NC end point in a paragraph \NC \NR %D \stoptabulate %D %D Each macro takes an identifier as argument, and the \type %D {\hpos} and \type {\vpos} also expect box content. % \def\xypos{\initializenextposition\dosetposition} \let\xypos\setpositiononly \def\hpos#1{\dontleavehmode\setpositionbox{#1}\hbox} \def\vpos#1{\setpositionbox{#1}\vbox} \def\bpos#1{\hpos{b:#1}{\strut}\ignorespaces} \def\epos#1{\removelastspace\hpos{e:#1}{\strut}} \def\fpos#1% {\setpositionplus{b:#1}{\number\parposcounter}\horizontalstrut \ignorespaces} \def\tpos#1% {\removelastspace \setpositionplus{e:#1}{\number\parposcounter}\horizontalstrut} \def\ffpos#1% {\setpositionplus{b:#1}{\number\parposcounter}\horizontalstrut\wpos{#1}% \ignorespaces} \def\ttpos#1% {\removelastspace \setpositionplus{e:#1}{\number\parposcounter}\horizontalstrut} \def\wpos#1% {\dontleavehmode\vadjust % may disappear if buried {\setbox0\hbox{\raise\strutdp\hbox{\rawwpos{#1}}}% \rlap{\smashedbox0}}} \def\wwpos#1% \hsmashed{\llap{\rawwpos{#1}}} {\rlap {\setbox0\hbox{\rawwpos{#1}}% \smashedbox0}} \def\rawwpos#1% {\hpos{w:#1} {\strut \hskip-\leftskip \hskip\hsize \hskip-\rightskip}} % the next macro disables par positions (in inner boxes) and % only registers the width \def\setinnerparpositions {\let\fpos\ffpos \let\tpos\ttpos \let\wpos\wwpos} % example of usage: (see for application "techniek") % % \appendtoks % \setinnerparpositions % \to \everytabulate %D When we want to calculate more complex backgrounds, we %D need to know what the current indentation scheme is. At %D the cost of many positions and memory, we can keep track %D of them. This mechanism is activated automatically %D based on information collected in the previous pass. \newcount\parposcounter \newif \ifpositioningpar \newif \iftracepositions % we can check for used entries, and if not, then not add one \def\enableparpositions % global {\global\let\registerparoptions\doregisterparoptions \global\positioningtrue \global\positioningpartrue} \def\disableparpositions % local {\positioningparfalse} \let\registerparoptions\relax \def\doregisterparoptions {\iftrialtypesetting \else \ifinpagebody \else \ifmmode \else \ifinformula \else \dodoregisterparoptions \fi \fi \fi \fi} \def\dodoregisterparoptions {\global\advance\parposcounter\plusone \setpositiondataplus {p:\number\parposcounter}% identifier {\the\zeropoint}% {\the\strutht}% {\the\strutdp}% {\the\hsize,\the\dimexpr\leftskip\relax,\the\dimexpr\rightskip\relax,\the\hangindent,\the\hangafter,\the\parindent}% %\normalhbox{\registerparsymbol}% \iftracepositions\registerparsymbol\fi} \def\traceposstring#1#2#3% {\iftracepositions \smashedhbox {#1{\infofont#2#3}% \scratchdimen.5\points \kern-2\scratchdimen \vrule\!!width4\scratchdimen\!!height\scratchdimen\!!depth\scratchdimen}% \fi} \def\registerparsymbol {\iftracepositions \smashedhbox to \zeropoint {\hss \startcolor[blue]% \llap{\infofont\number\parposcounter}% \scratchdimen\onepoint \vrule \!!width 4\scratchdimen \!!height2\scratchdimen \!!depth 2\scratchdimen \stopcolor \hss}% \fi} % \appendtoks \registerparoptions \to \everypar %D Eperimental code, don't use this yet: (must be sped up anyway) \def\@@noden{node:n:} \def\@@nodeo{node:o:} \def\@@nodep{node:p:} \def\doifelsenodelocation#1% {\ifcsname\@@noden#1\endcsname \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \def\nextnodelocation#1% {\ifcsname\@@noden#1\endcsname\pluscounter{\@@noden#1}\fi} \def\newnodelocation#1% {\ifcsname\@@noden#1\endcsname \setcounter{\@@noden#1}\zerocount \letgvalue {\@@nodeo#1}\!!zerocount \fi} \def\tagnodelocation#1% {\ifcsname\@@noden#1\endcsname\xypos{\@@nodep#1:\countervalue{\@@noden#1}}\fi} \def\getnodelocationp#1{\MPp{\@@nodep#1:\countervalue{\@@noden#1}}} \def\getnodelocationx#1{\MPx{\@@nodep#1:\countervalue{\@@noden#1}}} \def\getnodelocationy#1{\MPy{\@@nodep#1:\countervalue{\@@noden#1}}} \def\numnodelocationp#1#2{\MPp{\@@nodep#1:\number#2}} \def\numnodelocationx#1#2{\MPx{\@@nodep#1:\number#2}} \def\numnodelocationy#1#2{\MPy{\@@nodep#1:\number#2}} \def\getnodelocationn#1{\countervalue{\@@noden#1}} \def\getnodelocationo#1{\getvalue {\@@nodeo#1}} \setnewconstant\nodelocationmode\plusone \def\analyzenodelocation#1% {\ifcsname\@@noden#1\endcsname \doanalyzenodelocation{#1}{\getnodelocationn{#1}}\zerocount \fi} \def\doanalyzenodelocation#1#2#3% class n default {\begingroup \donefalse \ifcase\nodelocationmode % do nothing \else \edef\nodelocationselfn{#2}% \edef\nodelocationselfp{\numnodelocationp{#1}\nodelocationselfn}% \edef\nodelocationselfx{\numnodelocationx{#1}\nodelocationselfn}% \edef\nodelocationselfy{\numnodelocationy{#1}\nodelocationselfn}% \scratchcounter\plusone \doloop {\ifnum\recurselevel=\nodelocationselfn\relax \donetrue \else \edef\nodelocationotherp{\numnodelocationp{#1}\recurselevel}% \edef\nodelocationotherx{\numnodelocationx{#1}\recurselevel}% \edef\nodelocationothery{\numnodelocationy{#1}\recurselevel}% \ifcase\nodelocationmode \or % ok for single column \ifcase\nodelocationotherp\relax \exitloop \else\ifnum\nodelocationotherp<\nodelocationselfp\relax \donetrue \advance\scratchcounter\plusone \else\ifnum\nodelocationotherp>\nodelocationselfp\relax % skip \else\ifdim\nodelocationothery>\nodelocationselfy\relax \donetrue \advance\scratchcounter\plusone \else\ifdim\nodelocationothery<\nodelocationselfy\relax % skip \else\ifdim\nodelocationotherx<\nodelocationselfx\relax \donetrue \advance\scratchcounter\plusone \fi\fi\fi\fi\fi\fi \or % acceptable for double column \ifcase\nodelocationotherp\relax \exitloop \else\ifnum\nodelocationotherp<\nodelocationselfp\relax \donetrue \advance\scratchcounter\plusone \else\ifnum\nodelocationotherp>\nodelocationselfp\relax % skip \else\ifnum\recurselevel>\nodelocationselfn\relax \donetrue \exitloop \else \donetrue \advance\scratchcounter\plusone \fi\fi\fi\fi \else \exitloop \fi \fi}% \fi \ifdone \else \scratchcounter#3\relax \fi \setxvalue{\@@nodeo#1}{\the\scratchcounter}% \endgroup} \unexpanded\def\shownodelocation#1% {\ifcsname\@@noden#1\endcsname \analyzenodelocation{#1}% (#1,% n:\getnodelocationn{#1},% p:\getnodelocationp{#1},% x:\getnodelocationx{#1},% y:\getnodelocationy{#1},% o:\getnodelocationo{#1})% \fi} %D \macros %D {doifoverlappingelse} %D %D A first application of positional information, is to %D determine if two boxes do overlap: %D %D \starttyping %D \doifoverlappingelse{point a}{point b} %D {action when overlapping} %D {action when not overlapping} %D \stoptyping \def\doifoverlappingelse#1#2{\ctxcommand{doifoverlappingelse("#1","#2")}} %D \macros %D {doifpositionsonsamepageelse, %D doifpositionsonthispageelse} %D %D Instead of letting the user handle fuzzy expansion, we %D provide a simple test on positions being on the same page. %D %D \starttyping %D \doifpositionsonsamepageelse{point a}{point b} %D {action when on same page} %D {action when not on same page} %D \doifpositionsonthispageelse{point a}{point b} %D {action when on this page} %D {action when not on this page} %D \stoptyping \def\doifpositionsonsamepageelse#1{\ctxcommand{doifpositionsonsamepageelse("#1")}} \def\doifpositionsonthispageelse#1{\ctxcommand{doifpositionsonthispageelse("#1")}} %D Plugins: \let\MPv \MPplus \let\MPvv \MPrest \let\MPanchor\MPpos %D \macros %D {POSp, POSx, POSy, POSh, POSd, POSw} %D %D This is obsolete in \MKIV: \let\POSp\MPp \let\POSx\MPx \let\POSy\MPy \let\POSh\MPh \let\POSd\MPd \let\POSw\MPw \protect \endinput