# How to clip a TeX box using low-level PS commands?

I like to clip an arbitrary TeX box (\hbox) but without using larger graphic related packages like PGF/TikZ or PSTricks. This is intended to be used in packages which should not force the user to also load one of that packages.

Here I mean the same as the clip option (together with viewport or trim) of \includegraphics does: some outer material is not displayed any longer. I just want to apply this to a TeX box. I realize that this is output driver dependent. I found a pdftex solution already which uses PDF commands. I'm now looking for a DVI/PS solution, i.e. some \special instructions which clip a Tex box. Any hints about other drivers are very welcome.

I need this for two of my packages gincltex and adjustbox, which both extend the features of \includegraphics to arbitrary TeX boxes. What I do at the moment I simply use PGF for this: I place the content in a node and clip the picture accordantly. Because the content is boxed I can calculate the required size beforehand.

So basically I'm looking for the low level definition of a macro like:

\@clipbox{<llx>}{<lly>}{<urx>}{<ury>}{<boxnumber>}


which accepts a previously saved box and clips the four amounts from that box (ll = lower left, ur = upper right).

For pdftex I found the related code in \$TEXMF/tex/latex/pdftex-def/pdftex.def (driver file for graphics) which tells me trim the box first using normal TeX commands (i.e. to \lower and \hskip the lower left margin and to limit the box height (\ht) and width (\wd) dependent on the upper right corner. The depth \dp is there set to zero for images, but I have to handle that differently.). After that it uses the following low level macros to actually clip the box:

\pdfxform<boxnumber>
\pdfrefxform\pdflastxform


I imagine that for DVI-mode (dvps driver) I need a PostScript \special command to do the same. How would such a command look like? Any ideas about a dvipdf solution?

-
I already have a rough idea about pdftex (which seems to work with \hboxes anyway), so I would be happy to hear about Postscript solutions. The \special{PSfile="#1" .. clip} command I found in the dvips.def file doesn't help me much in this case. –  Martin Scharrer Jul 10 '11 at 23:48
Remember that XeTeX is unable to clip. :( –  egreg Jul 11 '11 at 11:28
@egreg: Ok, so then clipping in PGF pictures doesn't work as well with XeTeX? It uses dvipdfm, doesn't it? (I'm totally XeTeX ignorant :-( ) –  Martin Scharrer Jul 11 '11 at 11:31
@egreg: No, \clip with PGF works fine with XeLaTeX. I try to use the same lower level as PGF for as much output driver as possible. The rest will fall back to PGF. –  Martin Scharrer Jul 11 '11 at 11:37
I stand corrected; it can't clip included images, but TikZ pictures are correctly clipped. Maybe @Altermundus can help. –  egreg Jul 11 '11 at 11:41

Both Herbert and Alexander have offered solutions for dvips. Here, I'm taking inspiration from those answers plus the more convenient approach available in pdfTeX, plus a modified version of the pgf method for XeTeX, and combining into a single approach. First, note that I'm assuming e-TeX and also using a somewhat 'LaTeX3-like' programming approach. I've also shared as much code as possible.

\documentclass{article}
\usepackage{ifpdf,ifxetex}
\makeatletter


The main internal macro takes five arguments: the box to modify, then four dimension expressions to be clipped off the left, bottom, right and top respectively. You could also set up a similar approach to take a final size. The idea here is that the baseline is respected if possible, so there is a bit of care needed with the vertical placement so that the content only moves down while there is some depth available.

\protected\long\def\box@clip#1#2#3#4#5%
{%
\ht#1\dimexpr\ht#1 - \dimexpr#5\relax\relax
\ifdim\dp#1>\dimexpr#3\relax
\dp#1\dimexpr\dp#1 - \dimexpr#3\relax\relax
\else
\setbox#1=\hbox
{\lower\dimexpr\dimexpr#3\relax - \dp#1\relax\box#1}%
\dp#1\z@
\fi
\wd#1\dimexpr\wd#1-\dimexpr#4\relax\relax
\setbox#1=\hbox
{%
\hskip-\dimexpr#2\relax
\box#1%
}%
\ifxetex
\expandafter\box@clip@xdvipdfmx
\else
\ifpdf
\expandafter\expandafter\expandafter\box@clip@pdfmode
\else
\expandafter\expandafter\expandafter\box@clip@dvips
\fi
\fi
#1%
}


For each driver supported, there is an auxiliary. First, for dvips this is Herbert's method slightly altered (pgf makes things very complex):

\protected\long\def\box@clip@dvips#1%
{%
\setbox#1=\hbox
{%
\special
{%
ps:
/mtrxc matrix currentmatrix def
currentpoint gsave
translate
Resolution 72 div VResolution 72 div scale
0 -\to@bp{\dp#1} neg  \to@bp{\wd#1} \to@bp{\ht#1 + \dp#1} neg
rectclip
mtrxc setmatrix
}%
\box#1%
\special{ps: grestore }%
}%
}


As I said, pdfTeX makes life very easy :-)

\protected\long\def\box@clip@pdfmode#1%
{%
\pdfxform#1%
\setbox#1=\hbox{\pdfrefxform\pdflastxform}%
}


XeTeX is possibly the most complex one to tackle. The pgf approach is used, but here I've removed a lot of unnecessary transformations. After reading the dvipdfmx manual, it becomes clear that the best approach is as follows

\protected\long\def\box@clip@xdvipdfmx#1%
{%
\setbox#1=\hbox
{%


The first special saves the current point and starts a new 'graphics level'. Using the bcontent operation saves the current location automatically.

        \special{pdf:bcontent }%


Draw a rectangle the size of the modified box: in pgf this is done using the lower-level m, l and h operations, but there is no gain in working that way. This will be located at 'current point' TeX-wise.

          \special
{%
pdf:literal direct
0 -\to@bp{\dp#1} \to@bp{\wd#1} \to@bp{\ht#1 + \dp#1} re
}%


The W operation specifies a clip, and n finalises the path without any output (it's a 'no-op').

          \special{pdf:literal direct W }%
\special{pdf:literal direct n }%


Insert the box and tidy up.

                \box#1%
\special{pdf:econtent }%
}%
}


A simple conversion taken from Is there a command to convert cm to bp?

\long\def\to@bp#1{\strip@pt\dimexpr0.99626\dimexpr#1\relax\relax}


Wrap everything up in a user macro and finish the code block

\protected\long\def\boxclip#1#2#3#4#5{\box@clip#1{#2}{#3}{#4}{#5}}
\makeatother

\newbox\testbox


Now for some testing.

\begin{document}
\setbox\testbox=\hbox{Some test text with (g)}
\boxclip{\testbox}{10 pt}{2pt}{5pt}{2 pt}

\noindent\box\testbox{}

\end{document}


(I'll be adding this to LaTeX3 now I know how it works!) If you want to see the effect of various parts of the XeTeX code, comment out the W line to turn off the clipping. You can also replace the n operations by s so that you get a box where the clipping path is.

In earlier versions of the answer, for XeTeX I used the content q operation to save the current location, but this requires a series of manipulations to get the clip path and the box insert to line up. Using the bcontent ... econtent pair is much clearer.

While using the XForm implementation for pdfTeX is convenient, in a case where code is to be shared between branches an alternative approach is possible. (The above could be viewed as an abuse of the XForm object system in any case).

\protected\long\def\box@clip@pdfmode#1%
{%
\setbox#1=\hbox
{%
\pdfsave
\pdfliteral direct
{%
0 -\to@bp{\dp#1} \to@bp{\wd#1} \to@bp{\ht#1 + \dp#1} re W n
}%
\hbox to 0pt{\copy#1\hss}%
\pdfrestore
\hskip \wd#1
}%
}


The zero width box here is used to keep placement correct in the \pdfsave/\pdfrestore pair (which perform the same task as the bcontent/econtent pair for XeTeX but which should be places in the same output position.)

-
Thanks, that looks fantastic! I was working on XeTeX for the last two hours I was going to open a new question because I had problems with the PDF literals, I got the q, m, l, W, n, Q part but something seemed to be missing. Your answer comes also just at the right time! –  Martin Scharrer Nov 10 '11 at 23:39
@MartinScharrer What's crucial for XeTeX is the fact that the content literal inserts q 0 0 0 <x> <y> before the <stuff> and Q after, while literal direct does not. This is used to track the current location when needed. –  Joseph Wright Nov 10 '11 at 23:44
@MartinScharrer It turned out that you only need two graphic layers: I've simplified the answer. –  Joseph Wright Nov 10 '11 at 23:57
I worked this into the XeTeX driver for adjustbox: bitbucket.org/martin_scharrer/adjustbox/src/2a041fc594b7/…. Thanks again, I have to mention you in the source file. We should also think about creating XeTeX clipping support for \includegraphics, which is still missing. –  Martin Scharrer Nov 11 '11 at 0:10
Note that I changed your code a little. I got wrong results with the depth, so I changed the rectangle to a manual drawn one. I'm also using some existing internal macros from adjustbox. –  Martin Scharrer Nov 11 '11 at 0:12

%%%% only lines for some demo, delete the two later

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{kpfonts}
\makeatletter
\def\clipbox#1#2#3#4#5{%
\leavevmode%
\special{ps:
/mtrxc matrix currentmatrix def
currentpoint
gsave translate
0 0 10 0 360 arc 0 0 1 gsave fill grestore setrgbcolor 0.5 setlinewidth  %%%%%%
#1 #2 neg moveto #3 0 rlineto 0 #4 neg rlineto #3 neg 0 rlineto closepath  stroke %%%%
newpath
#1 #2 neg #3 #4 neg
rectclip newpath
mtrxc setmatrix
}%
#5%
\special{ps: grestore }%
}

\begin{document}

\fontsize{60}{62} \selectfont Foo
\clipbox{30}{30}{400}{200}{Foo}
Foo

\end{document}


and here is a solution with dimensions and the correct scaling:

\documentclass{article}
\makeatletter
\newlength\@templlx
\newlength\@templly
\newlength\@tempurx
\newlength\@tempury
\def\clipbox#1#2#3#4#5{%
\@templlx=#1%
\@templly=#2%
\@tempurx=#3%
\@tempury=#4%
\leavevmode%
\special{ps:
/mtrxc matrix currentmatrix def
currentpoint
gsave translate Resolution 72 div VResolution 72 div scale
newpath
\strip@pt\@templlx\space \strip@pt\@templly\space neg
\strip@pt\@tempurx\space \strip@pt\@tempury\space neg
rectclip newpath
mtrxc setmatrix
}%
#5%
\special{ps: grestore }%
}
\makeatother
\begin{document}
\rule{1cm}{1cm}
\clipbox{0cm}{0cm}{1cm}{1cm}{\rule{4cm}{4cm}}%
\rule{1cm}{1cm}

\end{document}


The second square is unclipped 4cm x 4cm

-
Thanks Herbert, this looks very promising. I changed it a little for my application. However, I can't find out the proper scaling. The values should be in bp, don't they? I got an almost 10x to small size if I use my requested sizes when I convert them to bp. I actually already trimmed the content, so its official size is already set. I now like to use \dp, \ht and \dp to draw a clipping rectangle. –  Martin Scharrer Nov 10 '11 at 15:49
I'll have a look at weekend, currently I'm busy ... –  Herbert Nov 10 '11 at 16:22
ok, couldn't stop thinking about it ... :-) Delete the two lines with %%%%% tehy are only for demonstartion here –  Herbert Nov 10 '11 at 17:49
Thanks a lot! In the meantime I worked with your original code. I got it working for my application, but I still have a funny factor in it (~111.1111 = 1000/9). See bitbucket.org/martin_scharrer/adjustbox/src/0feadff016c3/… for the MWE. There seems to be a scale factor already active. I tried initmatrix which gave me a fresh matrix where I could scale directly, but most other text on the same page was then gone :-( I'm ok with how it is now, but would like to know if this will keep working or be influenced by some setting. –  Martin Scharrer Nov 10 '11 at 17:57
@Martin: use TeXs values for Resultion and VResolution. See edited answer –  Herbert Nov 10 '11 at 18:52

Place

\special{ps: currentpoint /lly exch def /llx exch def}


and

\special{ps: currentpoint /ury exch def /urx exch def}


at the desired lower left and upper right corners of your box. This stores the Postscript coordinates into the llx, lly, urx, ury Postscript variables. These can later be used to define a clipping rectangle.

EDIT:

Below you find an example of a \trimbox command, which is similar to your requested \@clipbox in that it wants 4 TeX lengths and a box number. However, the four TeX lengths specify the amount measured from the box edges to be clipped.

Note that Glyphs are somewhat bigger than their box. See the first use of \trimbox.

The q has been put into the box to show that boxes with non-zero depth are correctly treated.

\def\trimbox#1#2#3#4#5{%
\leavevmode%
\special{ps: gsave}%
\hbox to 0pt{\hskip#1\special{ps: currentpoint pop /llx exch def}\hss}%
\hbox to 0pt{\hskip\wd#5\hskip-#3\special{ps: currentpoint pop /urx exch def}\hss}%
\raise-\dp#5\hbox{\raise#2\hbox{\special{ps: currentpoint /lly exch def pop}}}%
\raise\ht#5\hbox{\raise-#4\hbox{\special{ps: currentpoint /ury exch def pop}}}%
\special{ps: llx lly urx llx sub ury lly sub rectclip}%
\copy#5%
\special{ps: grestore}%
}

\setbox0=\hbox{Foo q Bar}

x\trimbox{0pt}{0pt}{0pt}{0pt}{0}x\par
x\trimbox{2pt}{2pt}{2pt}{2pt}{0}x

\bye

-
I see. Good start, but how do I add the clipping rectangle? –  Martin Scharrer Nov 10 '11 at 14:14
You mentioned \pdfxform in your question. Do you want to implement a similar command to store the content of a TeX box into a PDF XObject for the dvips/ps2pdf driver? –  AlexG Nov 10 '11 at 14:30
I don't really care about the XObject. I only use it because it clips as a side-effect (I took this from the pdftex.def graphics driver). Here I like to clip in Postscript. I don't this dvips does PDF XObjects, does it? –  Martin Scharrer Nov 10 '11 at 15:02
Thanks, this looks now very good. I changed it a little for my application: bitbucket.org/martin_scharrer/adjustbox/src/211783d437dd/…. Note that \@trimbox already trims the box, i.e. reduces the official size, so that \@clipbox just has to remove the overlapping material. I like the fact that I don't have to do any conversation to PS lengths! –  Martin Scharrer Nov 10 '11 at 19:14