Skip to content

Instantly share code, notes, and snippets.

@zr-tex8r
Created July 19, 2015 07:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zr-tex8r/d0e59ad020c85a99f75a to your computer and use it in GitHub Desktop.
Save zr-tex8r/d0e59ad020c85a99f75a to your computer and use it in GitHub Desktop.
LaTeX: to make 'pagebox' option key of \includegraphics available with dvipdfmx/xetex drivers
% tcpagebox.sty
%
%% Prologue
\ifx\tcizIntr\undefined \let\tcizISkip\relax
\else \long\def\tcizISkip#1\tcizISkip{}
\fi \tcizISkip
% When loaded with \usepackage, start from here.
%% package declaration
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{tcpagebox}[2015/07/19 v0.2]
%% preparations
\def\tciz@pkgname{tcpagebox}
\def\tciz@error{\PackageError\tciz@pkgname}
\def\tciz@warn{\PackageWarning\tciz@pkgname}
\providecommand\bxDebug[1]{}
%% prerequisite packages
\RequirePackage{etoolbox}
\RequirePackage{pdftexcmds}
%--------------------------------------- settings
%% \tciz@temp@base
% The base-name of the temporary MetaPost source file.
\edef\tciz@temp@base{\jobname-bb}
%% \tciz@cmd@mpost
% The name of MetaPost command.
\def\tciz@cmd@mpost{mpost}
%% \tciz@cmd@pdftex
\def\tciz@cmd@pdftex{pdftex}
%% \iftciz@clean
% Is clean-up needed?
\newbool{tciz@clean}
%% options
\DeclareOption{clean}{%
\tciz@cleantrue
}
\DeclareOption{noclean}{%
\tciz@cleanfalse
}
\ExecuteOptions{clean}
\ProcessOptions*\relax
%--------------------------------------- environment check
% e-TeX cehck
\ifx\patchcmd\@undefined
\expandafter\endinput\fi\relax
%% \tciz@driver
\let\tciz@driver\relax
%% detect graphics driver
\@ifpackageloaded{graphicx}{%
\ifnum\pdf@strcmp{\Gin@driver}{dvipdfmx.def}=\z@
\def\tciz@driver{dvipdfmx}%
\else\ifnum\pdf@strcmp{\Gin@driver}{xetex.def}=\z@
\def\tciz@driver{xetex}%
\else
\tciz@warn{Unsupported graphics driver '\Gin@driver' is used,\MessageBreak
package loading aborted}%
\fi\fi
}{%else
\tciz@error{Package 'graphicx' is not yet loaded}{%
The '\tciz@pkgname' package must be loaded after 'graphicx'
package.\MessageBreak\@ehc}%
}
%% check if pagebox is seemingly already supported
\ifnum 0\ifdefined\Gin@pagebox 1\fi
\ifdefined\Gin@XeTeX@pagebox 1\fi
\ifdefined\GTP@pagebox 1\fi >\z@
\let\Gin@driver\relax
\fi
%% aobrt if wrong
\ifx\tciz@driver\relax
\expandafter\endinput\fi\relax
%--------------------------------------- helpers
%% variables
\newbool{tciz@ok}
\csuse{newwrite}\tciz@out
\newbool{tciz@cmd@ok}
%% unique tokens
\def\tciz@end{\tciz@end@}
\def\tciz@mt{\tciz@mt@}
\let\tciz@stop\relax
%% \tciz@PC: the string '%'
%% \tciz@BS: the string '\'
\begingroup \escapechar\m@ne
\xdef\tciz@PC{\string\%}
\xdef\tciz@BS{\string\\}
\endgroup
%% \if\tciz@eq{<str1>}{<str2>}...\fi
\def\tciz@eq#1#2{%
\ifnum\pdf@strcmp{#1}{#2}=\z@ T\else F\fi T%
}
%% tciz@avail@shell
% shell escape availablity
\let\tciz@avail@shell\m@ne
\ifdefined\pdf@shellescape
\chardef\tciz@avail@shell=\pdf@shellescape\relax
\fi
\begingroup
\ifnum\tciz@avail@shell=\z@
\if\tciz@eq{\tciz@driver}{dvipdfmx}%
\tciz@error{Shell escape is entirely disabled}%
{Package loading is aborted.\MessageBreak\@ehc}%
\aftergroup\endinput
\fi
\fi
\endgroup
%% \tciz@drop@dot
% (fully-expandable)
\def\tciz@drop@dot#1{%
\expandafter\tciz@drop@dot@a#1\tciz@end#1%
}
\def\tciz@drop@dot@a#1#2\tciz@end#3{\def#3{#2}}
%% \tciz@write@to@file{<filename>}{<content>}
\def\tciz@write@to@file#1#2{%
\begingroup
\catcode10=12 \newlinechar=10
\let\%\tciz@PC \let\\\tciz@BS
\def\n{^^J}\let\ \@space
\immediate\openout\tciz@out=#1\relax
\immediate\write\tciz@out{#2}%
\immediate\closeout\tciz@out
\endgroup
}
%% \tciz@empty@mark : empty-file marker
\edef\tciz@empty@mark{\string\empty}
%% \tciz@if@file@empty{<filename>}{<true>}{<false>}
\def\tciz@if@file@empty#1{%
\openin\@inputcheck#1\relax
\ifeof\@inputcheck
\global\let\@gtempa\@firstoftwo
\else
\begingroup \endlinechar\m@ne
\readline\@inputcheck to\tciz@tmpa
\if\tciz@eq{\tciz@tmpa}{\tciz@empty@mark}%
\global\let\@gtempa\@firstoftwo
\else
\global\let\@gtempa\@secondoftwo
\fi
\endgroup
\fi
\@gtempa
}
%% \tciz@clobber@file{<filename>}
\def\tciz@clobber@file#1{%
\tciz@write@to@file{#1}{\tciz@empty@mark}%
}
%--------------------------------------- graphics parameters
%% \tciz@base
\let\tciz@base\relax
%% \tciz@ext
\let\tciz@ext\relax
%% \tciz@page
\let\tciz@page\@empty
%% \tciz@pagebox
\let\tciz@pagebox\@empty
%% \tciz@set@imgfile{<file>}
% Sets \tciz@base and \tciz@ext.
\def\tciz@set@imgfile#1{%
\edef\tciz@tmpa{#1}%
\IfFileExists\tciz@tmpa{%
\filename@parse\tciz@tmpa
\edef\tciz@base{\filename@area\filename@base}%
\edef\tciz@ext{\filename@ext}%
}{%else
\tciz@error{File '\tciz@tmpa' not found}\@ehc
\tciz@okfalse
}%
}
%% \tciz@set@imgfileext{<base>}{.<ext>}
% Sets \tciz@base and \tciz@exr.
\def\tciz@set@imgfileext#1#2{%
\IfFileExists{#1#2}{%
\edef\tciz@base{#1}%
\edef\tciz@ext{#2}\tciz@drop@dot\tciz@ext%
}{%else
\tciz@error{File '#1#2' not found}\@ehc
\tciz@okfalse
}%
}
%% \tciz@set@page{<page>}
% Sets \tciz@page.
\def\tciz@set@page#1{%
\unless\if\tciz@eq{#1}{}%
\afterassignment\tciz@set@page@a\count@=#1\tciz@stop{#1}%
\fi
}
\def\tciz@set@page@a#1\tciz@stop#2{%
\ifx\tciz@mt#1\tciz@mt
\edef\tciz@page{\the\count@}%
\else
\tciz@error{Invalid number format (#2)}\@ehc
\fi
}
%% \tciz@set@pagebox{<box-spec>}
% Sets to \tciz@pagebox.
\def\tciz@set@pagebox#1{%
\unless\if\tciz@eq{#1}{}%
\ifcsundef{tciz@box/#1}{%
\tciz@error{Invalid pagebox value (#1)}\@ehc
}{%else
\edef\tciz@pagebox{#1}%
}%
\fi
}
\@for\tciz@x:={media,crop,bleed,trim,art}\do{%
\csedef{tciz@box/\tciz@x box}{\tciz@x}%
}
%--------------------------------------- oatch to graphics driver
\if\tciz@eq{\tciz@driver}{dvipdfmx}%
%% \tciz@bbox : the result
\let\tciz@bbox\relax
%% tciz@g@clean
\chardef\tciz@g@clean=0
%% add Gin parameters
\AtBeginDocument{%
\tciz@oktrue
% 'page' may or may not exist
\unless\ifdefined\KV@Gin@page
\define@key{Gin}{page}{}%
\fi
%NB. \patchcmd fails to do with '#1' in replacement...
\let\tciz@KV@page\KV@Gin@page
\def\KV@Gin@page#1{%
\tciz@set@page{#1}% added
\tciz@KV@page{#1}}%
% 'pagebox' must not exist
\ifdefined\KV@Gin@pagebox
\Gin@ok@false
\else
\define@key{Gin}{pagebox}{%
\tciz@set@pagebox{#1}}%
\fi
\unless\iftciz@ok
\PackageError{Patch to graphics driver has failed}\@ehc
\fi
}
%% rewrite \Gread@pdf
\let\tciz@Gread@pdf\Gread@pdf
\def\Gread@pdf#1{%
\if\tciz@eq{\tciz@pagebox}{}%
\tciz@Gread@pdf{#1}% do original
\else
\begingroup
\edef\tciz@base{\Gin@base}%
\edef\tciz@ext{\Gin@ext}%
\let\tciz@bbox\relax
\tciz@measure@pagebox
\ifx\tciz@bbox\relax
\tciz@error{Cannot determine size of image (no BoundingBox)}%
\@ehc
\gdef\@gtempa{0 0 72 72 }%
\else
\xdef\@gtempa{\tciz@bbox\space}%
\fi
\endgroup
\expandafter\Gread@parse@bb\@gtempa\\%
\fi
}
%% \tcizIntrResult{<text>}
% The result handler.
\def\tcizIntrResult#1{%
\def\tciz@bbox{#1}%
}
%% \tciz@measure@pagebox@mpost
\def\tciz@measure@pagebox@mpost{%
\tciz@write@to@file{\tciz@temp@base.tex}{%
\% \tciz@temp@base.mp\n
verbatimtex\n
\%&pdftex -translate-file=natural\n
\\let\\tcizIntr=t \\input \tciz@pkgname.sty\\relax\n
\\tcizIntrMPProcess{\tciz@base\tciz@ext}%
{\tciz@page}{\tciz@pagebox}{\tciz@temp@base.aux}\n
etex\n
label(btex \\relax etex, (0, 0));\n
bye}%
\tciz@exec@sub{\tciz@cmd@mpost}{2}%
}
%% \tciz@measure@pagebox@pdftex
\def\tciz@measure@pagebox@pdftex{%
\tciz@write@to@file{\tciz@temp@base.tex}{%
\% \tciz@temp@base.tex\n
\\let\\tcizIntr=t \\input \tciz@pkgname.sty\\relax\n
\\tcizIntrProcess{\tciz@base\tciz@ext}%
{\tciz@page}{\tciz@pagebox}{\tciz@temp@base.aux}\n
\\bye}%
\tciz@exec@sub{\tciz@cmd@pdftex}{1}%
}
%% \tciz@exec@sub{<command>}{<clear>}
\def\tciz@exec@sub#1#2{%
\tciz@clobber@file{\tciz@temp@base.log}%
\tciz@clobber@file{\tciz@temp@base.aux}%
\pdf@system{#1 \tciz@temp@base.tex}%
\tciz@if@file@empty{\tciz@temp@base.log}{% not executed
\tciz@cmd@okfalse
}{%else
\tciz@cmd@oktrue
\global\chardef\tciz@g@clean=#2\relax
\input{\tciz@temp@base.aux}%
}%
}
%% \tciz@measure@pagebox@disabled
\def\tciz@measure@pagebox@disabled{%
\tciz@warn{Process feiled, perhaps because shell is\MessageBreak
disabled}%
}
%% \tciz@measure@pagebox@trial
\def\tciz@measure@pagebox@trial{%
\tciz@measure@pagebox@pdftex
\iftciz@cmd@ok
\tciz@resolve@proc\tciz@measure@pagebox@pdftex
\else
\tciz@measure@pagebox@mpost
\iftciz@cmd@ok
\tciz@resolve@proc\tciz@measure@pagebox@mpost
\else
\tciz@measure@pagebox@disabled
\tciz@resolve@proc\tciz@measure@pagebox@disabled
\fi
\fi
}
%% \tciz@resolve@proc
\def\tciz@resolve@proc#1{%
\global\let\tciz@measure@pagebox=#1\relax
\global\undef\tciz@measure@pagebox@disabled
\global\undef\tciz@measure@pagebox@pdftex
\global\undef\tciz@measure@pagebox@mpost
\global\undef\tciz@measure@pagebox@trial
\global\undef\tciz@resolve@proc
}
\ifcase\tciz@avail@shell
\tciz@resolve@proc\tciz@measure@pagebox@disabled
\or \tciz@resolve@proc\tciz@measure@pagebox@pdftex
\or \tciz@resolve@proc\tciz@measure@pagebox@mpost
\else \let\tciz@measure@pagebox\tciz@measure@pagebox@trial
\fi
%% clean-up
\iftciz@clean
\AtEndDocument{%
\ifcase\tciz@g@clean
\relax % nothing to do
\or % 'pdftex' is done
\tciz@write@to@file{\tciz@temp@base.lua}{%
os.remove([=[\tciz@temp@base.tex]=])\n
os.remove([=[\tciz@temp@base.aux]=])\n
os.remove([=[\tciz@temp@base.log]=])\n
os.remove([=[\tciz@temp@base.pdf]=])\n
os.remove([=[\tciz@temp@base.lua]=])\n
}%
\pdf@system{texlua \tciz@temp@base.lua}%
\or % 'mpost' is done
% shell is restricted, so files cannot be removed
\tciz@clobber@file{\tciz@temp@base.tex}%
\tciz@clobber@file{\tciz@temp@base.aux}%
\tciz@clobber@file{\tciz@temp@base.log}%
\tciz@clobber@file{\tciz@temp@base.mpx}%
\fi
}
\fi
\fi
%--------------------------------------- oatch to graphics driver (Xetex)
\if\tciz@eq{\tciz@driver}{xetex}%
%% add Gin parameters
\AtBeginDocument{%
\tciz@oktrue
% 'page' must exist
\ifdefined\KV@Gin@page
\let\tciz@KV@page\KV@Gin@page
\def\KV@Gin@page#1{%
\tciz@set@page{#1}% added
\tciz@KV@page{#1}}%
\else
\tciz@okfalse
\fi
% 'pagebox' must not exist
\ifdefined\KV@Gin@pagebox
\Gin@ok@false
\else
\define@key{Gin}{pagebox}{%
\tciz@set@pagebox{#1}}%
\fi
% \Ginclude@ for pdf
\@gobble\if \@gobble\if
\patchcmd{\Ginclude@QTm}{%
{ page\Gin@XeTeX@page}\fi
}{%
{ page\Gin@XeTeX@page}\fi
\tciz@pagebox@info
}{}{\tciz@okfalse}%
% \Gread@ for pdf
\@gobble\if \@gobble\if
\patchcmd{\G@measure@QTm}{%
{ page\Gin@XeTeX@page}\fi
}{%
{ page\Gin@XeTeX@page}\fi
\tciz@pagebox@info
}{}{\tciz@okfalse}%
% check
\unless\iftciz@ok
\PackageError{Patch to graphics driver has failed}\@ehc
\let\tciz@pagebox@info\relax
\fi
}
%% \tciz@pagebox@info
\def\tciz@pagebox@info{%
\unless\ifx\tciz@pagebox\@empty
\edef\picpage{\picpage\space
\@nameuse{tciz@box/\tciz@pagebox}}%
\fi
}
\fi
%--------------------------------------- done
\endinput
\tcizISkip
%=======================================
% The code below is employed on plain pdfTeX spawned
% inside the run of MetaPost.
%--------------------------------------- intra-MetaPost procedures
%% code guards
\edef\tcizIRestore{%
\catcode64=\the\catcode64%<@>
\relax}
\catcode64=11 %\makeatletter
%% variables
\newwrite\tciz@out
%%<+> \tcizIntrMPProcess{<file>}{<page>}{<page_box>}{<out_file>}
\def\tcizIntrMPProcess#1#2#3#4{%
\begingroup
\edef\tciz@imgfile{#1}%
\edef\tciz@outfile{#4}%
\edef\tciz@page{#2}%
\edef\tciz@pagebox{#3}%
\tciz@get@bbox
\tciz@write@out
\tciz@write@dvi
\endgroup
}
%%<+> \tcizIntrProcess{<file>}{<page>}{<page_box>}{<out_file>}
\def\tcizIntrProcess#1#2#3#4{%
\begingroup
\edef\tciz@imgfile{#1}%
\edef\tciz@outfile{#4}%
\edef\tciz@page{#2}%
\edef\tciz@pagebox{#3}%
\tciz@get@bbox
\tciz@write@out
\endgroup
}
%% \tciz@get@bbox
% Obtains the bbox of the target image.
\def\tciz@get@bbox{%
\edef\tciz@tmpa{%
\ifx\tciz@page\empty\else page \tciz@page\space\fi
\tciz@pagebox}%
\pdfximage\tciz@tmpa{\tciz@imgfile}%
\edef\tciz@bbox{%
\pdfximagebbox\pdflastximage 1 \space
\pdfximagebbox\pdflastximage 2 \space
\pdfximagebbox\pdflastximage 3 \space
\pdfximagebbox\pdflastximage 4}%
}
%% \tciz@write@out
% Writes the result file.
\def\tciz@write@out{%
\begingroup
\immediate\openout\tciz@out=\tciz@outfile\relax
\immediate\write\tciz@out{%
\string\tcizIntrResult{\tciz@bbox}}%
\immediate\closeout\tciz@out
\endgroup
}
%% \tciz@dummy@dvi
% The desired procedure requires features that are available
% only in PDF-mode. MetaPost that spawned pdfTeX, however,
% expects that there will be a DVI file output as result.
% Thus I prepare a dummy DVI file to fake "output".
% Here is the hex-dump of the file.
\edef\tciz@dummy@dvi{\pdfunescapehex{%
F702018392C01C3B0000000003E8008B%
00000001000000000000000000000000%
00000000000000000000000000000000%
0000000000000000FFFFFFFF8D840000%
0001000000018E8CF80000000F018392%
C01C3B0000000003E800000000000000%
0100010001F90000004802DFDFDFDFDF%
}}
%% \tciz@write@dvi
% Writes the dummy DVI file.
\def\tciz@write@dvi{%
\immediate\openout\tciz@out=\jobname.dvi\relax
\immediate\write\tciz@out{\tciz@dummy@dvi}%
\immediate\closeout\tciz@out
}
%--------------------------------------- done
\tcizIRestore
\endinput
%% EOF
% LaTeX document
\documentclass[a4paper,dvipdfmx]{article}% dvipdfmx!
\usepackage{graphicx}
% Load it after graphicx.
\usepackage{tcpagebox}
%\includegraphics[width=2cm]{testimg.pdf}
\begin{document}
% 'pagebox' is respected. (.xbb file is not used.)
\includegraphics[width=2cm,pagebox=mediabox]{image.pdf}
\includegraphics[width=2cm,page=2,pagebox=artbox]{image.pdf}
% When 'pagebox' is absent, the command will work exactly
% the same when tcpagebox is not loaded.
% Note that 'page' key is not (fully) supported in some old
% version of the dvipdfmx driver.
\includegraphics[width=2cm]{image.pdf}
\includegraphics[width=2cm,page=2]{image.pdf}
\end{document}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment