%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} %D In \MKIV\ there was already a different housekeeping model %D for positions quite early, but starting in 2012 more dramatic %D changes started to happen, especially in relation to background %D graphics. It will probably take some time to settle. % 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 \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")}}} \def\dosetposition #1{\ctxcommand{posxy("#1")}} \def\dosetpositionwhd #1#2#3#4{\ctxcommand{poswhd("#1",\number\dimexpr#2,\number\dimexpr#3,\number\dimexpr#4)}} \def\dosetpositionplus#1#2#3#4#5{\ctxcommand{posplus("#1",\number\dimexpr#2,\number\dimexpr#3,\number\dimexpr#4,"#5")}} \def\dosetpositionbox #1#2{\ctxcommand{poswhd("#1",\number\wd#2,\number\ht#2,\number\dp#2)}} \def\dosetpositionstrut #1{\ctxcommand{posstrut("#1")}} \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 % not used %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,\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 \newif\ifregionpositioning % 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 \regionpositioningfalse \localpositioningfalse \to \everybeforepagebody %D \macros %D {MPp, MPx, MPy, MPw, MPh, MPd, %D MPxy, MPll, MPlr, MPur, MPul, MPpos,MPanchor} %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")}} \let\MPpage \MPp \def\MPr #1{\ctxcommand{MPr("#1")}} \let\MPregion \MPr \def\MPc #1{\ctxcommand{MPc("#1")}} \let\MPcolumn \MPc \def\MPn #1{\ctxcommand{MPn("#1")}} \let\MPparagraph\MPn \def\MPx #1{\ctxcommand{MPx("#1")}} \def\MPy #1{\ctxcommand{MPy("#1")}} \def\MPw #1{\ctxcommand{MPw("#1")}} % first we need to replace \MPwidth etc \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")}} \let\MPanchor\MPpos % overloaded locally when needed \def\MPe #1{\ctxcommand{MPe("#1")}} \def\MPls #1{\ctxcommand{MPls("#1")}} \let\MPleftskip\MPls % compatible feature \def\MPrs #1{\ctxcommand{MPrs("#1")}} \let\MPrightkip\MPrs % compatible feature \def\MPpardata#1{\ctxcommand{MPpardata("#1")}} \def\MPxywhd #1{\ctxcommand{MPxywhd("#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 There are two low level positioning macros. Both store the %D position as well as execute an action associated with that %D position. \let\dopositionaction\gobbleoneargument % implemented later \def\anch_positions_initialize {\ifpositioning \else \global\positioningtrue \dosetpositionpapersize\printpaperwidth\printpaperheight \fi} \unexpanded\def\setpositiononly {\iftrialtypesetting \expandafter\gobbleoneargument \else \expandafter\anch_positions_set_only_indeed \fi} \def\anch_positions_set_only_indeed#1% {\anch_positions_initialize \edef\currentposition{#1}% \dosetposition\currentposition} \unexpanded\def\setposition {\iftrialtypesetting \expandafter\gobbleoneargument \else \expandafter\anch_positions_set_indeed \fi} \def\anch_positions_set_indeed#1% {\anch_positions_initialize \edef\currentposition{#1}% \dosetposition\currentposition \traceposstring\llap\green{\currentposition>}% \dopositionaction\currentposition} \unexpanded\def\setpositiondata {\iftrialtypesetting \expandafter\gobblefourarguments \else \expandafter\anch_positions_set_data_indeed \fi} \def\anch_positions_set_data_indeed#1#2#3#4% {\anch_positions_initialize \hbox {\edef\currentposition{#1}% \dosetpositionwhd\currentposition{#2}{#3}{#4}% already \the\dimexpr \traceposstring\llap\green{\currentposition>}% \dopositionaction\currentposition \hss}} \unexpanded\def\setpositionbox {\iftrialtypesetting \expandafter\anch_positions_set_box_nop \else \expandafter\anch_positions_set_box_yes \fi} \def\anch_positions_set_box_nop#1% {\dowithnextboxcs\flushnextbox} \def\anch_positions_set_box_yes#1% {\dowithnextbox{\anch_positions_set_box_finish{#1}}} \def\anch_positions_set_box_finish#1% {\anch_positions_initialize \hbox to \wd\nextbox {\edef\currentposition{#1}% \dosetpositionbox\currentposition\nextbox \traceposstring\llap\green{\currentposition>}% \setbox\positionbox\box\nextbox \dopositionaction\currentposition \box\positionbox \hss}} \unexpanded\def\setpositionstrut {\iftrialtypesetting \expandafter\anch_positions_set_strut_nop \else \expandafter\anch_positions_set_strut_yes \fi} \def\anch_positions_set_strut_nop#1% {\strut} \def\anch_positions_set_strut_yes#1% {\anch_positions_initialize \hbox to \zeropoint {\edef\currentposition{#1}% \dosetpositionstrut\currentposition \traceposstring\llap\green{\currentposition>}% \dopositionaction\currentposition \strut \hss}} \def\setpositiondataplus {\iftrialtypesetting \expandafter\gobblefivearguments \else \expandafter\anch_positions_set_plus_indeed \fi} \def\anch_positions_set_plus_indeed#1#2#3#4#5% {\anch_positions_initialize \hbox % just package {\edef\currentposition{#1}% \dosetpositionplus\currentposition{#2}{#3}{#4}{#5}% already \the\dimexpr \traceposstring\rlap\magenta{<\currentposition}% \dopositionaction\currentposition \hss}} \def\setpositionplus {\iftrialtypesetting \expandafter\anch_positions_set_plus_nop \else \expandafter\anch_positions_set_plus_yes \fi} \def\anch_positions_set_plus_nop#1#2% {\dowithnextboxcs\flushnextbox} \def\anch_positions_set_plus_yes#1#2% {\dowithnextbox{\anch_positions_set_plus_yes_finish{#1}{#2}}} \def\anch_positions_set_plus_yes_finish#1#2% {\anch_positions_initialize \hbox to \nextboxwd {\edef\currentposition{#1}% \dosetpositionplus\currentposition{\wd\nextbox}{\ht\nextbox}{\dp\nextbox}{#2}% \traceposstring\rlap\magenta{<\currentposition}% \setbox\positionbox\flushnextbox \dopositionaction\currentposition \box\positionbox \hss}} \let\currentposition\s!unknown %D A few special ones .. will be cleaned up \def\pageanchor {page:\the\realpageno} % for the moment only one pagesize \def\textanchor {text:\the\realpageno} \def\regionanchor{region:0} \newcount\c_anch_column % will be delegated to lua \newcount\c_anch_text % will be delegated to lua \unexpanded\def\anch_mark_column_box#1% {\global\advance\c_anch_column\plusone \ctxcommand{markregionbox(\number#1,"columnarea:\the\c_anch_column")}} % extra height \unexpanded\def\anch_mark_region_box {\iftrialtypesetting \singleexpandafter\gobbleoneargument \else\ifpositioning \doubleexpandafter\gobbleoneargument \else \doubleexpandafter\anch_mark_region_box_indeed \fi\fi} \unexpanded\def\anch_mark_region_box#1% {\ctxcommand{markregionbox(\number#1)}} \unexpanded\def\anch_mark_flow_box#1% will be extended / renamed {\hbox\bgroup \global\advance\c_anch_text\plusone \ctxcommand{markregionbox(\number#1,"textarea:\the\c_anch_text")}% \box#1% \egroup} \unexpanded\def\anch_make_page_box#1% maybe like text {\ctxcommand{markregionbox(\number#1,"\pageanchor")}} % needs an hbox \unexpanded\def\anch_mark_text_box#1% {\ctxcommand{markregionbox(\number#1,"\textanchor")}} % needs an hbox %D We can copy a position with: %D %D \starttyping %D \copyposition {to} {from} %D \stoptyping %D %D Again, this is a global operation. \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')}} \def\doifposition #1{\ctxcommand{doifposition('#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{\anch_positions_initialize\dosetposition} \let\xypos\setpositiononly \unexpanded\def\hpos #1{\dontleavehmode\setpositionbox{#1}\hbox} \unexpanded\def\vpos #1{\setpositionbox{#1}\vbox} \unexpanded\def\bpos #1{\dontleavehmode \setpositionstrut{b:#1}\ignorespaces} \unexpanded\def\epos #1{\removeunwantedspaces\setpositionstrut{e:#1}} %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 \unexpanded\def\enableparpositions % global {\global\let\registerparoptions\doregisterparoptions \global\positioningtrue \global\positioningpartrue} \unexpanded\def\disableparpositions % local {\positioningparfalse} \let\registerparoptions\relax \unexpanded\def\doregisterparoptions {\iftrialtypesetting \else \ifinpagebody \else \ifmmode \else \ifinformula \else \anch_positions_register_par_options \fi \fi \fi \fi} % \def\anch_positions_register_par_options % {\global\advance\parposcounter\plusone % \setpositiondataplus % {p:\number\parposcounter}\zeropoint\strutht\strutdp % {\the\hsize,\the\dimexpr\leftskip\relax,\the\dimexpr\rightskip\relax,\the\hangindent,\the\hangafter,\the\parindent}% % %\hbox{\registerparsymbol}% % \iftracepositions\registerparsymbol\fi} \def\anch_positions_register_par_options {\dontleavehmode\ctxcommand{parpos()}% \iftracepositions\registerparsymbol\fi} \unexpanded\def\traceposstring#1#2#3% {\iftracepositions \smashedhbox {#1{\infofont#2#3}% \kern-\onepoint \vrule\!!width2\onepoint\!!height\halfapoint\!!depth\halfapoint}% \fi} \unexpanded\def\registerparsymbol {\iftracepositions \smashedhbox to \zeropoint {\hss \startcolor[blue]% \llap{\infofont\number\parposcounter}% \vrule \!!width 4\onepoint \!!height2\onepoint \!!depth 2\onepoint \stopcolor \hss}% \fi} % \appendtoks \registerparoptions \to \everypar %D Eperimental code, don't use this yet: (must be sped up anyway) .. obsolete \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 \anch_positions_node_location_analyze{#1}{\getnodelocationn{#1}}\zerocount \fi} \def\anch_positions_node_location_analyze#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 % \exitloop \or % ok for single column \anch_positions_node_location_analyze_one \or % acceptable for double column \anch_positions_node_location_analyze_two \else \exitloop \fi \fi}% \fi \ifdone \else \scratchcounter#3\relax \fi \setxvalue{\@@nodeo#1}{\the\scratchcounter}% \endgroup} \def\anch_positions_node_location_analyze_one {\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} \def\anch_positions_node_location_analyze_two {\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} \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 \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