% \iffalse meta-comment % %% File: l3backend-header.dtx % % Copyright (C) 2019-2023 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % This file is part of the "l3backend bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/latex3/latex3 % % for those people who are interested. % %<*driver> \documentclass[full,kernel]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \textsf{l3backend-header} package\\ Backend graphics support^^A % } % % \author{^^A % The \LaTeX{} Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2023-04-19} % % \maketitle % % \begin{documentation} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3backend-header} implementation} % % \begin{macrocode} %<*dvips&header> % \end{macrocode} % % \begin{macro}{color.sc} % Empty definition for color at the top level. % \begin{macrocode} /color.sc { } def % \end{macrocode} % \end{macro} % % \begin{macro}{TeXcolorseparation, separation} % Support for separation/spot colors: this strange naming is so % things work with the color stack. % \begin{macrocode} TeXDict begin /TeXcolorseparation { setcolor } def end % \end{macrocode} % \end{macro} % % \begin{macro}{pdf.globaldict} % A small global dictionary for backend use. % \begin{macrocode} true setglobal /pdf.globaldict 4 dict def false setglobal % \end{macrocode} % \end{macro} % % \begin{macro} % { % pdf.cvs , % pdf.dvi.pt , % pdf.pt.dvi , % pdf.rect.ht % } % Small utilities for PostScript manipulations. Conversion to DVI dimensions % is done here to allow for |Resolution|. The total height of a rectangle % (an array) needs a little maths, in contrast to simply extracting a value. % \begin{macrocode} /pdf.cvs { 65534 string cvs } def /pdf.dvi.pt { 72.27 mul Resolution div } def /pdf.pt.dvi { 72.27 div Resolution mul } def /pdf.rect.ht { dup 1 get neg exch 3 get add } def % \end{macrocode} % \end{macro} % % \begin{macro}{pdf.linkmargin, pdf.linkdp.pad, pdf.linkht.pad} % Settings which are defined up-front in |SDict|. % \begin{macrocode} /pdf.linkmargin { 1 pdf.pt.dvi } def /pdf.linkdp.pad { 0 } def /pdf.linkht.pad { 0 } def % \end{macrocode} % \end{macro} % % \begin{macro} % { % pdf.rect , % pdf.save.ll , % pdf.save.ur , % pdf.save.linkll , % pdf.save.linkur , % pdf.llx , % pdf.lly , % pdf.urx , % pdf.ury % } % Functions for marking the limits of an annotation/link, plus drawing the % border. We separate links for generic annotations to support adding a % margin and setting a minimal size. % \begin{macrocode} /pdf.rect { /Rect [ pdf.llx pdf.lly pdf.urx pdf.ury ] } def /pdf.save.ll { currentpoint /pdf.lly exch def /pdf.llx exch def } def /pdf.save.ur { currentpoint /pdf.ury exch def /pdf.urx exch def } def /pdf.save.linkll { currentpoint pdf.linkmargin add pdf.linkdp.pad add /pdf.lly exch def pdf.linkmargin sub /pdf.llx exch def } def /pdf.save.linkur { currentpoint pdf.linkmargin sub pdf.linkht.pad sub /pdf.ury exch def pdf.linkmargin add /pdf.urx exch def } def % \end{macrocode} % \end{macro} % % \begin{macro} % { % pdf.dest.anchor , % pdf.dest.x , % pdf.dest.y , % pdf.dest.point , % pdf.dest2device , % pdf.dev.x , % pdf.dev.y , % pdf.tmpa , % pdf.tmpb , % pdf.tmpc , % pdf.tmpd % } % For finding the anchor point of a destination link. We make the use case % a separate function as it comes up a lot, and as this makes it easier to % adjust if we need additional effects. We also need a more complex approach % to convert a co-ordinate pair correctly when defining a rectangle: this % can otherwise be out when using a landscape page. (Thanks to Alexander % Grahn for the approach here.) % \begin{macrocode} /pdf.dest.anchor { currentpoint exch pdf.dvi.pt 72 add /pdf.dest.x exch def pdf.dvi.pt vsize 72 sub exch sub /pdf.dest.y exch def } def /pdf.dest.point { pdf.dest.x pdf.dest.y } def /pdf.dest2device { /pdf.dest.y exch def /pdf.dest.x exch def matrix currentmatrix matrix defaultmatrix matrix invertmatrix matrix concatmatrix cvx exec /pdf.dev.y exch def /pdf.dev.x exch def /pdf.tmpd exch def /pdf.tmpc exch def /pdf.tmpb exch def /pdf.tmpa exch def pdf.dest.x pdf.tmpa mul pdf.dest.y pdf.tmpc mul add pdf.dev.x add pdf.dest.x pdf.tmpb mul pdf.dest.y pdf.tmpd mul add pdf.dev.y add } def % \end{macrocode} % \end{macro} % % \begin{macro} % { % pdf.bordertracking , % pdf.bordertracking.begin , % pdf.bordertracking.end , % pdf.leftboundary , % pdf.rightboundary , % pdf.brokenlink.rect , % pdf.brokenlink.skip , % pdf.brokenlink.dict , % pdf.bordertracking.endpage , % pdf.bordertracking.continue , % pdf.originx , % pdf.originy % } % To know where a breakable link can go, we need to track the boundary % rectangle. That can be done by hooking into |a| and |x| operations: % those names have to be retained. The boundary is stored at the end of % the operation. Special effort is needed at the start and end of pages % (or rather galleys), such that everything works properly. % \begin{macrocode} /pdf.bordertracking false def /pdf.bordertracking.begin { SDict /pdf.bordertracking true put SDict /pdf.leftboundary undef SDict /pdf.rightboundary undef /a where { /a { currentpoint pop SDict /pdf.rightboundary known dup { SDict /pdf.rightboundary get 2 index lt { not } if } if { pop } { SDict exch /pdf.rightboundary exch put } ifelse moveto currentpoint pop SDict /pdf.leftboundary known dup { SDict /pdf.leftboundary get 2 index gt { not } if } if { pop } { SDict exch /pdf.leftboundary exch put } ifelse } put } if } def /pdf.bordertracking.end { /a where { /a { moveto } put } if /x where { /x { 0 exch rmoveto } put } if SDict /pdf.leftboundary known { pdf.outerbox 0 pdf.leftboundary put } if SDict /pdf.rightboundary known { pdf.outerbox 2 pdf.rightboundary put } if SDict /pdf.bordertracking false put } def /pdf.bordertracking.endpage { pdf.bordertracking { pdf.bordertracking.end true setglobal pdf.globaldict /pdf.brokenlink.rect [ pdf.outerbox aload pop ] put pdf.globaldict /pdf.brokenlink.skip pdf.baselineskip put pdf.globaldict /pdf.brokenlink.dict pdf.link.dict pdf.cvs put false setglobal mark pdf.link.dict cvx exec /Rect [ pdf.llx pdf.lly pdf.outerbox 2 get pdf.linkmargin add currentpoint exch pop pdf.outerbox pdf.rect.ht sub pdf.linkmargin sub ] /ANN pdf.pdfmark } if } def /pdf.bordertracking.continue { /pdf.link.dict pdf.globaldict /pdf.brokenlink.dict get def /pdf.outerbox pdf.globaldict /pdf.brokenlink.rect get def /pdf.baselineskip pdf.globaldict /pdf.brokenlink.skip get def pdf.globaldict dup dup /pdf.brokenlink.dict undef /pdf.brokenlink.skip undef /pdf.brokenlink.rect undef currentpoint /pdf.originy exch def /pdf.originx exch def /a where { /a { moveto SDict begin currentpoint pdf.originy ne exch pdf.originx ne or { pdf.save.linkll /pdf.lly pdf.lly pdf.outerbox 1 get sub def pdf.bordertracking.begin } if end } put } if /x where { /x { 0 exch rmoveto SDict begin currentpoint pdf.originy ne exch pdf.originx ne or { pdf.save.linkll /pdf.lly pdf.lly pdf.outerbox 1 get sub def pdf.bordertracking.begin } if end } put } if } def % \end{macrocode} % \end{macro} % % \begin{macro} % { % pdf.breaklink , % pdf.breaklink.write , % pdf.count , % pdf.currentrect % } % Dealing with link breaking itself has multiple stage. The first step is to % find the |Rect| entry in the dictionary, looping over key--value pairs. % The first line is handled first, adjusting the rectangle to stay inside the % text area. The second phase is a loop over the height of the bulk of the % link area, done on the basis of a number of baselines. Finally, the end of % the link area is tidied up, again from the boundary of the text area. % \begin{macrocode} /pdf.breaklink { pop counttomark 2 mod 0 eq { counttomark /pdf.count exch def { pdf.count 0 eq { exit } if counttomark 2 roll 1 index /Rect eq { dup 4 array copy dup dup 1 get pdf.outerbox pdf.rect.ht pdf.linkmargin 2 mul add sub 3 exch put dup pdf.outerbox 2 get pdf.linkmargin add 2 exch put dup dup 3 get pdf.outerbox pdf.rect.ht pdf.linkmargin 2 mul add add 1 exch put /pdf.currentrect exch def pdf.breaklink.write { pdf.currentrect dup pdf.outerbox 0 get pdf.linkmargin sub 0 exch put dup pdf.outerbox 2 get pdf.linkmargin add 2 exch put dup dup 1 get pdf.baselineskip add 1 exch put dup dup 3 get pdf.baselineskip add 3 exch put /pdf.currentrect exch def pdf.breaklink.write } 1 index 3 get pdf.linkmargin 2 mul add pdf.outerbox pdf.rect.ht add 2 index 1 get sub pdf.baselineskip div round cvi 1 sub exch repeat pdf.currentrect dup pdf.outerbox 0 get pdf.linkmargin sub 0 exch put dup dup 1 get pdf.baselineskip add 1 exch put dup dup 3 get pdf.baselineskip add 3 exch put dup 2 index 2 get 2 exch put /pdf.currentrect exch def pdf.breaklink.write SDict /pdf.pdfmark.good false put exit } { pdf.count 2 sub /pdf.count exch def } ifelse } loop } if /ANN } def /pdf.breaklink.write { counttomark 1 sub index /_objdef eq { counttomark -2 roll dup wcheck { readonly counttomark 2 roll } { pop pop } ifelse } if counttomark 1 add copy pop pdf.currentrect /ANN pdfmark } def % \end{macrocode} % \end{macro} % % \begin{macro} % { % pdf.pdfmark , % pdf.pdfmark.good , % pdf.outerbox , % pdf.baselineskip , % pdf.pdfmark.dict % } % The business end of breaking links starts by hooking into |pdfmarks|. % Unlike \pkg{hypdvips}, we avoid altering any links we have not created % by using a copy of the core |pdfmarks| function. Only mark types which % are known are altered. At present, this is purely |ANN| marks, which are % measured relative to the size of the baseline skip. If they are % more than one apparent line high, breaking is applied. % \begin{macrocode} /pdf.pdfmark { SDict /pdf.pdfmark.good true put dup /ANN eq { pdf.pdfmark.store pdf.pdfmark.dict begin Subtype /Link eq currentdict /Rect known and SDict /pdf.outerbox known and SDict /pdf.baselineskip known and { Rect 3 get pdf.linkmargin 2 mul add pdf.outerbox pdf.rect.ht add Rect 1 get sub pdf.baselineskip div round cvi 0 gt { pdf.breaklink } if } if end SDict /pdf.outerbox undef SDict /pdf.baselineskip undef currentdict /pdf.pdfmark.dict undef } if pdf.pdfmark.good { pdfmark } { cleartomark } ifelse } def /pdf.pdfmark.store { /pdf.pdfmark.dict 65534 dict def counttomark 1 add copy pop { dup mark eq { pop exit } { pdf.pdfmark.dict begin def end } ifelse } loop } def % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex