On 15/10/2023, 15:17, I wrote :
At the moment, one can include a PDF file using the following syntax :

    \XeTeXpdffile ⟨filename⟩ [ page ⟨int⟩ ] [ crop | media | bleed |
    trim | art ]
    [ scaled ⟨int⟩ | xscaled ⟨int⟩ | yscaled ⟨int⟩ | width ⟨dimen⟩ |
    height ⟨dimen⟩ | rotated ⟨decimal⟩ ]
    Insert (pages of) a PDF. See below for explanation

where [ crop | media | bleed | trim | art ] specify to which of the four boxes known to PDF the inserted file is to be cropped.  I frequently have the need to crop a PDF file during inclusion to arbitrary dimensions, but at the moment that would appear to be impossible (short of pre-massaging the PDF to be inserted).  Would it be possible to enhance XeTeX to recognise and honour the following (or similar) syntax :

    \XeTeXpdffile ⟨filename⟩ [ page ⟨int⟩ ] [ crop | media | bleed |
    trim | art | custom [xmin ymin xmax ymax]]
    [ scaled ⟨int⟩ | xscaled ⟨int⟩ | yscaled ⟨int⟩ | width ⟨dimen⟩ |
    height ⟨dimen⟩ | rotated ⟨decimal⟩ ]
    Insert (pages of) a PDF. See below for explanation

to which David Carlisle kindly replied :

You can clip an arbitrary box so no need to have this as an image inclusion feature.

\input{graphicx}
\includegraphics{example-image.pdf}
\bigskip
\includegraphics[trim=20pt 50pt 50pt 100pt, clip]{example-image.pdf}
\bye
The need for this feature disappeared, so I did not pursue David’s suggestion at the time, but the need returned yesterday so I gave David's suggestion a try, and although it took me several attempts to work out the correct dimensions to pass as parameter to \includegraphics (in part because the PostScript/PDF bounding box is bottom-up, while TeX assembles boxes top down (and the TeXworks ruler also increases as one goes downwards)) I eventually managed to make it work.  However, while graphicx.tex is only 40 or so lines long, graphicx.sty (which graphicx.tex slaves) is over 250 lines, miniltx.tex is almost 600 lines, and then there are the driver files (xetex.def is over 500 lines in itself).  In total, maybe 1500 lines of code or so.  This seemed somewhat excessive to me, so I set about finding out how graphicx.tex achieved my goal.  It turned out that the key part was less than 20 lines in length, and "hidden" in xetex.def :

\def\GPT@clipbox#1{%
  \setbox#1=\hbox{%
    \Gin@defaultbp\WIDTH{\wd#1}%
    \Gin@defaultbp\DEPTH{\dp#1}%
    \@tempdima\ht#1%
    \advance\@tempdima\dp#1%
    \Gin@defaultbp\TOTALHEIGHT{\@tempdima}%
    \special{x:gsave}%
    \special{%
      pdf:literal
      0 -\DEPTH\GPT@space \WIDTH\GPT@space \TOTALHEIGHT\GPT@space re  W n
    }%
    \rlap{\box#1}%
    \special{x:grestore}%
    \special{pdfcolorstack \@pdfcolorstack current}%
    \hskip\wd#1%
  }%
}

or in a somewhat more readable form (and omitting the commercial-ats and most eol percent comments)

\def \GPTclipbox #1%
    {%
      \setbox #1 = \hbox
          {%
            \Gindefaultbp \WIDTH = \wd#1
            \Gindefaultbp \DEPTH = \dp#1
            \tempdima = \ht #1
            \advance \tempdima by \dp #1
            \Gindefaultbp \TOTALHEIGHT = \tempdima
            \special {x:gsave}
            \special {pdf:literal 0 -\DEPTH \space \WIDTH \space \TOTALHEIGHT \space re W n}
            \rlap {\box#1}
            \special {x:grestore}
            \special {pdfcolorstack \pdfcolorstack current}
            \hskip \wd #1
          }%
    }

As the depth of most boxes is normally small, we can approximate the key line as follows, omitting the \spaces for readability :

   \special {pdf:literal 0 0 \WIDTH \TOTALHEIGHT re W n}

where :
/
/
/The //|re|//operator constructs a rectangle. It takes four parameters (the values preceding the operator) that define a rectangle: the x co-ordinate of the lower-left corner, the y co-ordinate of the lower-left corner, the width and the height. /

/The //|W|//operator sets the clipping path, which in this case is the rectangle just drawn, for the content that follows./

//

/The //|n|//operator starts a new path. It discards the paths constructed so far. It thus prevents the rectangle just drawn (used as clipping path) from being drawn/.

Now as both co-ordinates of the lower left corner are (or almost are) zero, the effect of the \special {pdf:literal  ... re W n} will be to clip the contents of the box passed as parameter to a rectangle with lower-left corner  approximately (0,0).  But what if we want a rectangle of the same size, but /not /with origin (0, 0) ?  Well, clearly, before this code is called, we mus massage the contents of the box to be passed as parameter such that our /intended/ origin has been mapped to the (0, 0) point. Once we realise this, we are in a position to write the code :

   % !TeX Program=XeTeX

   \pdfpageheight = 210 mm
   \pdfpagewidth = 297 mm

   \hsize = \pdfpagewidth
   \vsize = \pdfpageheight

   \parindent = 1 cm

   \output = {\advance \hoffset by -1 in \advance \voffset by -1 in
   \shipout \box 255 }

   \newcount \xmin
   \newcount \ymin
   \newcount \xmax
   \newcount \ymax
   \newcount \height
   \newcount \width

   \xmin = 1900
   \ymin = 300

   \width = 700
   \height = 70

   \xmax = \numexpr \xmin + \width \relax
   \ymax = \numexpr \ymin + \height \relax

   \setbox 0 = \vbox {\XeTeXpdffile Example-image-3.pdf }
   \setbox 0 = \hbox to \width bp {\kern - \xmin bp \box 0 \hss}
   \setbox 0 = \vbox to \height bp {\kern \dimexpr \ymax bp - (\ht 0 +
   \dp 0) \box 0 \vss}

   \setbox 0 = \hbox
        \bgroup
            \special {x:gsave}
            \special {pdf:literal 0 0 \number \width \space \number
   \height \space re W n }
            \box 0
            \special {x:grestore}
        \egroup

   \topglue 1 cm
   \leavevmode \box 0

   \end

Just over 40 lines in total.  The file that I was seeking to clip, renamed as Example-image-3.pdf, is attached.  It is 100 cm wide by 20 cm tall (plus bleed and trim marks), or about 2800 x 600 bp, so I would normally want to express \xmin, \ymin, ..., \width in cm rather than in bp, as is implied in the code above, but that would just be syntactic sugar so I leave the code as I initially wrote it.  Note that my code does not correctly handle non-zero box depths as it stands.

My sincere thanks to David Carlisle for his suggestion which led to my being able to develop this code.
--
/Philip Taylor/

Attachment: Example-image-3.pdf
Description: Adobe PDF document

Reply via email to