%D \module %D [ file=supp-pdf, %D version=1997.05.21, %D title=\CONTEXT\ Support Macros, %D subtitle=\METAPOST\ to \PDF\ conversion, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA / Hans Hagen \& Ton Otten}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. Non||commercial use is %C granted. %D These macros are written as generic as possible. Some %D general support macro's are loaded from a small module %D especially made for non \CONTEXT\ use. In this module I %D use a matrix transformation macro written by Tanmoy %D Bhattacharya. Thanks to extensive testing of Sebastian %D Ratz I was able to complete this module within reasonable %D time. First we take care of non||\CONTEXT\ use: \ifx \undefined \writestatus \input supp-mis.tex \relax \fi %D This module handles some \PDF\ conversion and insertions %D topics. The macros use the \PDFTEX\ primitive %D \type{\pdfliteral}. \writestatus{loading}{Context Support Macros / PDF} \unprotect \ifx\pdfliteral\undefined \def\pdfliteral#1{\message{[ignored pdfliteral: #1]}} \fi %D \macros %D {convertPDFtoPDF} %D {} %D %D \PDFTEX\ supports verbatim inclusion of \PDF\ code. The %D following macro takes care of inserting externally defined %D illustrations in \PDF\ format. According to a suggestion %D Tanmoy Bhattacharya posted to the \PDFTEX\ mailing list, we %D first skip lines until \type{stream} is reached and then %D copy lines until \type{endstream} is encountered. This %D scheme only works with vectorized graphics in which no %D indirect references to objects are used. Bitmaps also don't %D work. Interpreting their specifications is beyond the %D current implementation. %D %D \starttypen %D \convertPDFtoPDF %D {filename} %D {x scale} {y scale} %D {x offset } {y offset} %D {width} {height} %D \stoptypen %D %D When the scales are set to~1, the last last four values %D are the same as the bounding box, e.g. %D %D \starttypen %D \convertPDFtoPDF{mp-pra-1.pdf} {1} {1}{-1bp}{-1bp}{398bp}{398bp} %D \convertPDFtoPDF{mp-pra-1.pdf}{.5}{.5} {0bp} {0bp}{199bp}{199bp} %D \stoptypen %D %D Keep in mind, that this kind of copying only works for %D pure and valid pdf code (without fonts). %D The scanning and copying is straightforward and quite fast. %D To speed up things we use two constants. \def\@@PDFstream@@ {stream} \def\@@PDFendstream@@ {endstream} %D \macros %D {PDFmediaboxprefered} %D {} %D %D If needed, the macros can scan for the mediabox that %D specifies the dimensions and offsets of the graphic. When %D we say: %D %D \starttypen %D \PDFmediaboxpreferedtrue %D \stoptypen %D %D the mediabox present in the file superseded the user %D specified, already scaled and calculated offset and %D dimensions. Beware: the user supplied values are not the %D bounding box ones! \newif\ifPDFmediaboxprefered \def\setPDFboundingbox#1#2#3#4#5#6% {\dimen0=#1\dimen0=#5\dimen0 \ScaledPointsToBigPoints{\number\dimen0}\PDFxoffset \dimen0=#3\dimen0=#5\dimen0 \xdef\PDFwidth{\the\dimen0}% \dimen0=#2\dimen0=#6\dimen0 \ScaledPointsToBigPoints{\number\dimen0}\PDFyoffset \dimen0=#4\dimen0=#6\dimen0 \xdef\PDFheight{\the\dimen0}% \global\let\PDFxoffset=\PDFxoffset \global\let\PDFyoffset=\PDFyoffset} \def\setPDFmediabox#1[#2 #3 #4 #5]#6\done% {\dimen2=#2bp\dimen2=-\dimen2 \dimen4=#3bp\dimen4=-\dimen4 \dimen6=#4bp\advance\dimen6 by \dimen2 \dimen8=#5bp\advance\dimen8 by \dimen4 \setPDFboundingbox{\dimen2}{\dimen4}{\dimen6}{\dimen8}\PDFxscale\PDFyscale} \def\checkPDFmediabox#1/MediaBox#2#3\done% {\ifx#2\relax \else \message{mediabox}% \setPDFmediabox#2#3\done \fi} %D We use the general macro \type{\doprocessfile} and feed this %D with a line handling macro that changed it's behavior when %D the stream operators are encountered. \def\handlePDFline% {\ifx\@@PDFstream@@\fileline \let\doprocessPDFline=\copyPDFobject \startPDFtoPDF \else\ifPDFmediaboxprefered \expandafter\checkPDFmediabox\fileline/MediaBox\relax\done \fi\fi} \def\copyPDFobject% {\ifx\@@PDFendstream@@\fileline \ifPDFmediaboxprefered \let\doprocessPDFline=\findPDFmediabox \else \let\doprocessPDFline=\relax \fi \else \advance\scratchcounter by 1 \pdfliteral{\fileline}% \fi} \def\findPDFmediabox% {\expandafter\checkPDFmediabox\fileline/MediaBox\relax\done} %D The main conversion macro wraps the \PDF\ codes in a box %D that is output as an object. The graphics are embedded %D in~\type{q} and~\type{Q} and are scaled and positioned using %D one transform call (\type{cm}). This saves some additional %D scaling. \def\startPDFtoPDF% {\setbox0=\vbox\bgroup \message{[PDF to PDF \PDFfilename}% \forgetall \scratchcounter=0 \let\stopPDFtoPDF=\dostopPDFtoPDF} \def\dostopPDFtoPDF% {\ifnum\scratchcounter<0 \scratchcounter=1 \fi \message{(\the\scratchcounter\space lines)]}% \egroup \wd0=\PDFwidth \vbox to \PDFheight {\forgetall \vfill \pdfliteral{q}% \pdfliteral{1 0 0 1 \PDFxoffset\space \PDFyoffset\space cm}% \pdfliteral{\PDFxscale\space 0 0 \PDFyscale\space 0 0 cm}% \box0 \pdfliteral{Q}}} \def\stopPDFtoPDF% {\message{[PDF to PDF \PDFfilename\space not found]}} \def\convertPDFtoPDF#1#2#3#4#5#6#7% {\bgroup \def\PDFfilename{#1}% \def\PDFxscale {#2}% \def\PDFyscale {#3}% \setPDFboundingbox{#4}{#5}{#6}{#7}{1}{1}% \uncatcodespecials \endlinechar=-1 \let\doprocessPDFline=\handlePDFline \doprocessfile\scratchread\PDFfilename\doprocessPDFline \stopPDFtoPDF \egroup} %D \macros %D {convertMPtoPDF} %D {} %D %D The next set of macros implements \METAPOST\ to \PDF\ %D conversion. Because we want to test as fast as possible, we %D first define the \POSTSCRIPT\ operators that \METAPOST\ %D uses. We don't define irrelevant ones, because these are %D skipped anyway. \def \PScurveto {curveto} \def \PSlineto {lineto} \def \PSmoveto {moveto} \def \PSshowpage {showpage} \def \PSnewpath {newpath} \def \PSfshow {fshow} \def \PSclosepath {closepath} \def \PSfill {fill} \def \PSstroke {stroke} \def \PSclip {clip} \def \PSrlineto {rlineto} \def \PSsetlinejoin {setlinejoin} \def \PSsetlinecap {setlinecap} \def \PSsetmiterlimit {setmiterlimit} \def \PSsetgray {setgray} \def \PSsetrgbcolor {setrgbcolor} \def \PSsetcmykcolor {setcmykcolor} \def \PSsetdash {setdash} \def \PSgsave {gsave} \def \PSgrestore {grestore} \def \PStranslate {translate} \def \PSscale {scale} \def \PSconcat {concat} \def \PSdtransform {dtransform} \def \PSBoundingBox {BoundingBox:} \def \PSHiResBoundingBox {HiResBoundingBox:} \def \PSExactBoundingBox {ExactBoundingBox:} \def \PSPage {Page:} %D By the way, the \type {setcmykcolor} operator is not %D output by \METAPOST\ but can result from converting the %D \kap{RGB} color specifications, as implemented in %D \type{supp-mps}. %D In \POSTSCRIPT\ arguments precede the operators. Due to the %D fact that in some translations we need access to those %D arguments, as well as that sometimes we have to skip them, %D we stack them up. The stack is one||dimensional for non path %D operators and two||dimensional for operators inside a path. %D This is because we have to save the whole path for %D (optional) postprocessing. Values are pushed onto the stack %D by: %D %D \starttypen %D \setMPargument {value} %D \stoptypen %D %D They can be retrieved by the short named macros: %D %D \starttypen %D \gMPa {number} %D \sMPa {number} %D \stoptypen %D %D When scanning a path specification, we also save the %D operator, using %D %D \starttypen %D \setMPkeyword {n} %D \stoptypen %D %D The path drawing operators are coded for speed: \type{clip}, %D \type{stroke}, \type{fill} and \type{fillstroke} become %D 1, 2, 3 and~4. %D %D When processing the path this code can be retrieved %D using %D %D \starttypen %D \getMPkeyword{n} %D \stoptypen %D %D When setting an argument, the exact position on the stack %D depend on the current value of the \COUNTERS\ %D \type{\nofMPsegments} and \type{\nofMParguments}. \newcount\nofMPsegments \newcount\nofMParguments %D These variables hold the coordinates. The argument part of %D the stack is reset by: %D %D \starttypen %D \resetMPstack %D \stoptypen %D %D We use the prefix \type{@@MP} to keep the stack from %D conflicting with existing macros. To speed up things bit %D more, we use the constant \type{\@@MP}. \def\@@MP{@@MP} \def\setMPargument#1% {\advance\nofMParguments by 1 \expandafter\def \csname\@@MP\the\nofMPsegments\the\nofMParguments\endcsname% {\do#1}} \def\gMPa#1% {\csname\@@MP0#1\endcsname} \def\gMPs#1% {\csname\@@MP\the\nofMPsegments#1\endcsname} \def\setMPkeyword#1 {\expandafter\def\csname\@@MP\the\nofMPsegments0\endcsname{#1}% \advance\nofMPsegments by 1 \nofMParguments=0\relax} \def\getMPkeyword#1% {\csname\@@MP#10\endcsname} %D When we reset the stack, we can assume that all further %D comment is to be ignored as well as handled in strings. %D By redefining the reset macro after the first call, we %D save some run time. \def\resetMPstack% {\catcode`\%=\@@active \let\handleMPgraphic=\handleMPendgraphic \def\resetMPstack{\nofMParguments=0\relax}% \resetMPstack} %D The arguments are saved with the preceding command %D \type{\do}. By default this command expands to nothing, but %D when we deal with strings it's used to strip off the %D \type{(} and \type{)}. %D %D Strings are kind of tricky, because characters can be %D passed verbatim \type{(hello)}, by octal number %D \type{(\005)} or as command \type{(\()}. We therefore %D cannot simply ignore \type{(} and \type{)}, the way we do %D with \type{[} and \type{]}. Another complication is that %D strings may contain characters that normally have a %D special meaning in \TEX, like \type{$} and \type{{}}. %D %D A previous solution made \type{\} an active character and %D let it look ahead for a number or character. W ehad to %D abandon this scheme because of the need for verbatim %D support. The next solution involved some \CATCODE\ %D trickery but works well. \def\octalMPcharacter#1#2#3% {\char'#1#2#3\relax} \bgroup \catcode`\|=\@@comment \catcode`\%=\@@active \catcode`\[=\@@active \catcode`\]=\@@active \catcode`\{=\@@active \catcode`\}=\@@active \catcode`B=\@@begingroup \catcode`E=\@@endgroup \gdef\ignoreMPspecials| B\def%BE| \def[BE| \def]BE| \def{BE| \def}BEE \gdef\obeyMPspecials| B\def%B\char 37\relax E| \def[B\char 91\relax E| \def]B\char 93\relax E| \def{B\char123\relax E| \def}B\char125\relax EE \gdef\setMPspecials| B\catcode`\%=\@@active \catcode`\[=\@@active \catcode`\]=\@@active \catcode`\{=\@@active \catcode`\}=\@@active \catcode`\$=\@@letter \catcode`\_=\@@letter \catcode`\#=\@@letter \catcode`\^=\@@letter \catcode`\&=\@@letter \catcode`\|=\@@letter \catcode`\~=\@@letter \def\(B\char40\relax E| \def\)B\char41\relax E| \def\\B\char92\relax E| \def\0B\octalMPcharacter0E| \def\1B\octalMPcharacter1E| \def\2B\octalMPcharacter2E| \def\3B\octalMPcharacter3E| \def\4B\octalMPcharacter4E| \def\5B\octalMPcharacter5E| \def\6B\octalMPcharacter6E| \def\7B\octalMPcharacter7E| \def\8B\octalMPcharacter8E| \def\9B\octalMPcharacter9EE \egroup %D We use the comment symbol as a sort of trigger: \bgroup \catcode`\%=\@@active \gdef\startMPscanning{\let%=\startMPconversion} \egroup %D In earlier versions we used the sequence %D %D \starttypen %D \expandafter\handleMPsequence\input filename\relax %D \stoptypen %D %D Persistent problems in \LATEX\ however forced us to use a %D different scheme. Every \POSTSCRIPT\ file starts with a %D \type{%}, so we temporary make this an active character %D that starts the scanning and redefines itself. (The problem %D originates in the redefinition by \LATEX\ of the %D \type{\input} primitive.) \def\startMPconversion% {\catcode`\%=\@@ignore \ignoreMPspecials \handleMPsequence} %D Here comes the main loop. Most arguments are numbers. This %D means that they can be recognized by their \type{\lccode}. %D This method saves a lot of processing time. We could %D speed up the conversion by handling the \type{path} %D seperately. \def\dohandleMPsequence#1#2 % {\ifnum\lccode`#1=0 \setMPargument{#1#2}% \else \edef\somestring{#1#2}% \ifx\somestring\PSmoveto \edef\lastMPmoveX{\gMPa1}% \edef\lastMPmoveY{\gMPa2}% \pdfliteral{\gMPa1 \gMPa2 m}% \resetMPstack \else\ifx\somestring\PSnewpath \let\handleMPsequence=\handleMPpath \else\ifx\somestring\PSgsave \pdfliteral{q}% \resetMPstack \else\ifx\somestring\PSgrestore \pdfliteral{Q}% \resetMPstack \else\ifx\somestring\PSdtransform % == setlinewidth \let\handleMPsequence=\handleMPdtransform \else\ifx\somestring\PSconcat \pdfliteral{\gMPa1 \gMPa2 \gMPa3 \gMPa4 \gMPa5 \gMPa6 cm}% \resetMPstack \else\ifx\somestring\PSsetrgbcolor \pdfliteral{\gMPa1 \gMPa2 \gMPa3 rg \gMPa1 \gMPa2 \gMPa3 RG}% \resetMPstack \else\ifx\somestring\PSsetcmykcolor \pdfliteral{\gMPa1 \gMPa2 \gMPa3 \gMPa4 k \gMPa1 \gMPa2 \gMPa3 \gMPa4 K}% \resetMPstack \else\ifx\somestring\PSsetgray \pdfliteral{\gMPa1 g \gMPa1 G}% \resetMPstack \else\ifx\somestring\PStranslate \pdfliteral{1 0 0 1 \gMPa1 \gMPa2 cm}% \resetMPstack \else\ifx\somestring\PSsetdash \handleMPsetdash \resetMPstack \else\ifx\somestring\PSsetlinejoin \pdfliteral{\gMPa1 j}% \resetMPstack \else\ifx\somestring\PSsetmiterlimit \pdfliteral{\gMPa1 M}% \resetMPstack \else\ifx\somestring\PSfshow \handleMPfshow \resetMPstack \else\ifx\somestring\PSsetlinecap \pdfliteral{\gMPa1 J}% \resetMPstack \else\ifx\somestring\PSrlineto \pdfliteral{\lastMPmoveX\space \lastMPmoveY\space l S}% \resetMPstack \else\ifx\somestring\PSscale \pdfliteral{\gMPa1 0 0 \gMPa2 0 0 cm}% \resetMPstack \else \handleMPgraphic{#1#2}% \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \fi \handleMPsequence} %D Beginning and ending the graphics is taken care of by the %D macro \type{\handleMPgraphic}, which is redefined when %D the first graphics operator is met. \def\handleMPendgraphic#1% {\ifx\somestring\PSshowpage \let\handleMPsequence=\finishMPgraphic \else \setMPargument{#1}% \fi} \def\handleMPbegingraphic#1% {\ifx\somestring\PSBoundingBox \let\handleMPsequence=\handleMPboundingbox \else\ifx\somestring\PSHiResBoundingBox \let\handleMPsequence=\handleMPboundingbox \else\ifx\somestring\PSExactBoundingBox \let\handleMPsequence=\handleMPboundingbox \else\ifx\somestring\PSPage \let\handleMPsequence=\handleMPpage \else \setMPargument{#1}% \fi\fi\fi\fi} \let\handleMPgraphic=\handleMPbegingraphic %D We check for three kind of bounding boxes: the normal one %D and two high precission ones: %D %D \starttypen %D BoundingBox: llx lly ucx ucy %D HiResBoundingBox: llx lly ucx ucy %D ExactBoundingBox: llx lly ucx ucy %D \stoptypen %D %D The dimensions are saved for later use. \def\handleMPboundingbox #1 #2 #3 #4 {\dimen0=#1pt\dimen0=-\MPxscale\dimen0 \dimen2=#2pt\dimen2=-\MPyscale\dimen2 \xdef\MPxoffset{\withoutpt{\the\dimen0}}% \xdef\MPyoffset{\withoutpt{\the\dimen2}}% \dimen0=#1bp\dimen0=-\dimen0 \dimen2=#2bp\dimen2=-\dimen2 \advance\dimen0 by #3bp \dimen0=\MPxscale\dimen0 \xdef\MPwidth{\the\dimen0}% \advance\dimen2 by #4bp \dimen2=\MPyscale\dimen2 \xdef\MPheight{\the\dimen2}% \nofMParguments=0 \let\handleMPsequence=\dohandleMPsequence \handleMPsequence} %D We use the \type{page} comment as a signal that %D stackbuilding can be started. \def\handleMPpage #1 #2 {\nofMParguments=0 \let\handleMPsequence=\dohandleMPsequence \handleMPsequence} %D \METAPOST\ draws it dots by moving to a location and %D invoking \type{0 0 rlineto}. This operator is not %D available in \PDF. Our solution is straightforward: we draw %D a line from $(current\_x, current\_y)$ to itself. This %D means that the arguments of the preceding \type{moveto} have %D to be saved. \def\lastMPmoveX{0} \def\lastMPmoveY{0} %D These saved coordinates are also used when we handle the %D texts. Text handling proved to be a bit of a nuisance, but %D finaly I saw the light. It proved that we also had to %D take care of \type{(split arguments)}. \def\handleMPfshow% {\setbox0=\hbox {\obeyMPspecials \edef\size{\gMPa{\the\nofMParguments} }% \advance\nofMParguments by -1 \font\temp=\gMPa{\the\nofMParguments} at \size bp \advance\nofMParguments by -1 \temp \ifnum\nofMParguments=1 \def\do(##1){##1}% \gMPa1% \else \scratchcounter=1 \def\do(##1{##1}% \gMPa{\the\scratchcounter}\space \def\do{}% \loop \advance\scratchcounter by 1 \ifnum\scratchcounter<\nofMParguments \gMPa{\the\scratchcounter}\space \repeat \def\do##1){##1}% \gMPa{\the\scratchcounter}% \fi \unskip}% \dimen0=\lastMPmoveY bp \advance\dimen0 by \ht0 \ScaledPointsToBigPoints{\number\dimen0}\lastMPmoveY \pdfliteral{n q 1 0 0 1 \lastMPmoveX\space\lastMPmoveY\space cm}% \dimen0=\ht0 \advance\dimen0 by \dp0 \box0 \vskip-\dimen0 \pdfliteral{Q}} %D Most operators are just converted and keep their %D arguments. Dashes however need a bit different treatment, %D otherwise \PDF\ viewers complain loudly. Another %D complication is that one argument comes after the \type{]}. %D When reading the data, we simple ignore the array boundary %D characters. We save ourselves some redundant newlines and %D at the same time keep the output readable by packing the %D literals. \def\handleMPsetdash% {\bgroup \def\somestring{[}% \scratchcounter=1 \loop \ifnum\scratchcounter<\nofMParguments \edef\somestring{\somestring\space\gMPa{\the\scratchcounter}}% \advance\scratchcounter by 1 \repeat \edef\somestring{\somestring]\gMPa{\the\scratchcounter} d}% \pdfliteral{\somestring}% \egroup} %D The \type{setlinewidth} commands look a bit complicated. There are %D two alternatives, that alsways look the same. As John Hobby %D says: %D %D \startsmaller %D \starttypen %D x 0 dtransform exch truncate exch idtransform pop setlinewidth %D 0 y dtransform truncate idtransform setlinewidth pop %D \stoptypen %D %D These are just fancy versions of \type{x setlinewidth} and %D \type{y setlinewidth}. The \type{x 0 ...} form is used if %D the path is {\em primarily vertical}. It rounds the width %D so that vertical lines come out an integer number of pixels %D wide in device space. The \type{0 y ...} form does the same %D for paths that are {\em primarily horizontal}. The reason %D why I did this is Knuth insists on getting exactly the %D widths \TEX\ intends for the horizontal and vertical rules %D in \type{btex...etex} output. (Note that PostScript scan %D conversion rules cause a horizontal or vertical line of %D integer width $n$ in device space to come out $n+1$ pixels %D wide, regardless of the phase relative to the pixel grid.) %D \stopsmaller %D %D The common operator in these sequences is \type{dtransform}, %D so we can use this one to trigger setting the linewidth. \def\handleMPdtransform% {\ifdim\gMPa1pt>\!!zeropoint \pdfliteral{\gMPa1 w}% \def\next##1 ##2 ##3 ##4 ##5 ##6 {\handleMPsequence}% \else \pdfliteral{\gMPa2 w}% \def\next##1 ##2 ##3 ##4 {\handleMPsequence}% \fi \let\handleMPsequence=\dohandleMPsequence \resetMPstack \next} %D The most complicated command is \type{concat}. \METAPOST\ %D applies this operator to \type{stoke}. At that moment the %D points set by \type{curveto} and \type{moveto}, are already %D fixed. In \PDF\ however the \type{cm} operator affects the %D points as well as the pen (stroke). Like more \PDF\ %D operators, \type{cm} is a defined in a bit ambiguous way. %D The only save route for non||circular penshapes, is saving %D teh path, recalculating the points and applying the %D transformation matrix in such a way that we can be sure %D that its behavior is well defined. This comes down to %D inverting the path and applying \type{cm} to that path as %D well as the pen. This all means that we have to save the %D path. %D In \METAPOST\ there are three ways to handle a path $p$: %D %D \starttypen %D draw p; fill p; filldraw p; %D \stoptypen %D %D The last case outputs a \type{gsave fill grestore} before %D \type{stroke}. Handling the path outside the main loops %D saves about 40\% run time.\voetnoot{We can save some more by %D following the \METAPOST\ output routine, but for the moment %D we keep things simple.} Switching between the main loop and %D the path loop is done by means of the recursely called %D macro \type{\handleMPsequence}. \def\handleMPpath% {\chardef\finiMPpath=0 \let\closeMPpath=\relax \let\flushMPpath=\flushnormalMPpath \resetMPstack \nofMPsegments=1 \let\handleMPsequence=\dohandleMPpath \dohandleMPpath} %D Most paths are drawn with simple round pens. Therefore we've %D split up the routinein two. \def\flushnormalMPpath% {\scratchcounter=\nofMPsegments \nofMPsegments=1 \loop \expandafter\ifcase\getMPkeyword{\the\nofMPsegments}\relax \pdfliteral{\gMPs1 \gMPs2 l}% \or \pdfliteral{\gMPs1 \gMPs2 \gMPs3 \gMPs4 \gMPs5 \gMPs6 c}% \or \pdfliteral{\lastMPmoveX\space \lastMPmoveY\space l S}% \or \edef\lastMPmoveX{\gMPs1}% \edef\lastMPmoveY{\gMPs2}% \pdfliteral{\lastMPmoveX\space \lastMPmoveY\space m}% \fi \advance\nofMPsegments by 1\relax \ifnum\nofMPsegments<\scratchcounter \repeat} \def\flushconcatMPpath% {\scratchcounter=\nofMPsegments \nofMPsegments=1 \loop \expandafter\ifcase\getMPkeyword{\the\nofMPsegments}\relax \doMPconcat{\gMPs1}\a{\gMPs2}\b \pdfliteral{\a\space \b\space l}% \or \doMPconcat{\gMPs1}\a{\gMPs2}\b \doMPconcat{\gMPs3}\c{\gMPs4}\d \doMPconcat{\gMPs5}\e{\gMPs6}\f \pdfliteral{\a\space \b\space \c\space \d\space \e\space \f\space c}% \or \bgroup \noMPtranslate \doMPconcat\lastMPmoveX\a\lastMPmoveY\b \pdfliteral{\a\space \b\space l S}% \egroup \or \edef\lastMPmoveX{\gMPs1}% \edef\lastMPmoveY{\gMPs2}% \doMPconcat\lastMPmoveX\a\lastMPmoveY\b \pdfliteral{\a\space \b\space m}% \fi \advance\nofMPsegments by 1\relax \ifnum\nofMPsegments<\scratchcounter \repeat} %D The transformation of the coordinates is handled by one of %D the macros Tanmoy posted to the \PDFTEX\ mailing list. %D I rewrote and optimized the original macro to suit the other %D macros in this module. %D %D \starttypen %D \doMPconcat {x position} \xresult {y position} \yresult %D \stoptypen %D %D %D By setting the auxiliary \DIMENSIONS\ \type{\dimen0} upto %D \type{\dimen10} only once per path, we save over 20\% run %D time. Some more speed was gained by removing some parameter %D passing. These macros can be optimized a bit more by using %D more constants. There is however not much need for further %D optimization because penshapes usually are round and %D therefore need no transformation. Nevertheless we move the %D factor to the outer level and use bit different \type{pt} %D removal macro. Although the values represent base points, %D we converted them to pure points, simply because those can %D be converted back. \def\MPconcatfactor{256} \def\doMPreducedimen#1 {\count0=\MPconcatfactor \advance\dimen#1 \ifdim\dimen#1>\!!zeropoint .5\else -.5\fi\count0 \divide\dimen#1 \count0\relax} \def\doMPexpanddimen#1 {\multiply\dimen#1 \MPconcatfactor\relax} \def\presetMPconcat% {\dimen 0=\gMPs1 pt \doMPreducedimen 0 % r_x \dimen 2=\gMPs2 pt \doMPreducedimen 2 % s_x \dimen 4=\gMPs3 pt \doMPreducedimen 4 % s_y \dimen 6=\gMPs4 pt \doMPreducedimen 6 % r_y \dimen 8=\gMPs5 pt \doMPreducedimen 8 % t_x \dimen10=\gMPs6 pt \doMPreducedimen10 } % t_y \def\noMPtranslate% use this one grouped {\dimen 8=\!!zeropoint % t_x \dimen10=\!!zeropoint} % t_y \def\doMPconcat#1#2#3#4% {\dimen12=#1 pt \doMPreducedimen12 % p_x \dimen14=#3 pt \doMPreducedimen14 % p_y % \dimen16 \dimen 0 \multiply \dimen16 \dimen 6 \dimen20 \dimen 2 \multiply \dimen20 \dimen 4 \advance \dimen16 -\dimen20 % \dimen18 \dimen12 \multiply \dimen18 \dimen 6 \dimen20 \dimen14 \multiply \dimen20 \dimen 4 \advance \dimen18 -\dimen20 \dimen20 \dimen 4 \multiply \dimen20 \dimen10 \advance \dimen18 \dimen20 \dimen20 \dimen 6 \multiply \dimen20 \dimen 8 \advance \dimen18 -\dimen20 % \multiply \dimen12 -\dimen 2 \multiply \dimen14 \dimen 0 \advance \dimen12 \dimen14 \dimen20 \dimen 2 \multiply \dimen20 \dimen 8 \advance \dimen12 \dimen20 \dimen20 \dimen 0 \multiply \dimen20 \dimen10 \advance \dimen12 -\dimen20 % \doMPreducedimen16 \divide \dimen18 \dimen16 \doMPexpanddimen18 \divide \dimen12 \dimen16 \doMPexpanddimen12 % \edef#2{\withoutpt{\the\dimen18}}% % p_x^\prime \edef#4{\withoutpt{\the\dimen12}}} % p_y^\prime %D The following explanation of the conversion process was %D posted to the \PDFTEX\ mailing list by Tanmoy. The original %D macro was part of a set of macro's that included sinus and %D cosinus calculation as well as scaling and translating. The %D \METAPOST\ to \PDF\ conversion however only needs %D transformation. %D \start \switchnaarkorps [ss] %D %D Given a point $(U_x, U_y)$ in user coordinates, the business %D of \POSTSCRIPT\ is to convert it to device space. Let us say %D that the device space coordinates are $(D_x, D_y)$. Then, in %D \POSTSCRIPT\ $(D_x, D_y)$ can be written in terms of %D $(U_x, U_y)$ in matrix notation, either as %D %D \plaatsformule %D \startformule %D \pmatrix{D_x&D_y&1\cr} = \pmatrix{U_x&U_y&1\cr} %D \pmatrix{s_x&r_x&0\cr %D r_y&s_y&0\cr %D t_x&t_y&1\cr} %D \stopformule %D %D or %D %D \plaatsformule %D \startformule %D \pmatrix{D_x\cr D_y\cr 1} = \pmatrix{s_x&r_y&t_x\cr %D r_x&s_y&t_y\cr %D 0 &0 &1 \cr} %D \pmatrix{U_x\cr %D U_y\cr %D 1 \cr} %D \stopformule %D %D both of which is a shorthand for the same set of equations: %D %D \plaatsformule %D \startformule %D D_x = s_x U_x + r_y U_y + t_x %D \stopformule %D %D \plaatsformule %D \startformule %D D_y = r_x U_x + s_y U_y + t_y %D \stopformule %D %D which define what is called an `affine transformation'. %D %D \POSTSCRIPT\ represents the `transformation matrix' as a %D six element matrix instead of a $3\times 3$ array because %D three of the elements are always~0, 0 and~1. Thus the above %D transformation is written in postscript as $[s_x\, r_x\, %D r_y\, s_y\, t_x\, t_y]$. However, when doing any %D calculations, it is useful to go back to the original %D matrix notation (whichever: I will use the second) and %D continue from there. %D %D As an example, if the current transformation matrix is %D $[s_x\, r_x\, r_y\, s_y\, t_x\, t_y]$ and you say \typ{[a b %D c d e f] concat}, this means: %D %D \startsmaller %D Take the user space coordinates and transform them to an %D intermediate set of coordinates using array $[a\, b\, c\, d\, %D e\, f]$ as the transformation matrix. %D %D Take the intermediate set of coordinates and change them to %D device coordinates using array $[s_x\, r_x\, r_y\, s_y\, t_x\, t_y]$ %D as the transformation matrix. %D \stopsmaller %D %D Well, what is the net effect? In matrix notation, it is %D %D \plaatsformule %D \startformule %D \pmatrix{I_x\cr I_y\cr 1\cr} = \pmatrix{a&c&e\cr %D b&d&f\cr %D 0&0&1\cr} %D \pmatrix{U_x\cr %D U_y\cr %D 1 \cr} %D \stopformule %D %D \plaatsformule %D \startformule %D \pmatrix{D_y\cr D_y\cr 1\cr} = \pmatrix{s_x&r_y&t_x\cr %D r_x&s_y&t_y\cr %D 0 &0 &1 \cr} %D \pmatrix{I_x\cr %D I_y\cr %D 1 \cr} %D \stopformule %D %D where $(I_x, I_y)$ is the intermediate coordinate. %D %D Now, the beauty of the matrix notation is that when there is %D a chain of such matrix equations, one can always compose %D them into one matrix equation using the standard matrix %D composition law. The composite matrix from two matrices can %D be derived very easily: the element in the $i$\hoog{th} %D horizontal row and $j$\hoog{th} vertical column is %D calculated by`multiplying' the $i$\hoog{th} row of the first %D matrix and the $j$\hoog{th} column of the second matrix (and %D summing over the elements). Thus, in the above: %D %D \plaatsformule %D \startformule %D \pmatrix{D_x\cr D_y\cr 1} = \pmatrix{s_x^\prime&r_y^\prime&t_x^\prime\cr %D r_x^\prime&s_y^\prime&t_y^\prime\cr %D 0 &0 &0 \cr} %D \pmatrix{U_x\cr %D U_y\cr %D 1 \cr} %D \stopformule %D %D with %D %D \plaatsformule %D \startformule %D \eqalign %D {s_x^\prime & = s_x a + r_y b \cr %D r_x^\prime & = r_x a + s_y b \cr %D r_y^\prime & = s_x c + r_y d \cr %D s_y^\prime & = r_x c + s_y d \cr %D t_x^\prime & = s_x e + r_y f + t_x \cr %D t_y^\prime & = r_x e + s_y f + t_y \cr} %D \stopformule %D In fact, the same rule is true not only when one is going %D from user coordinates to device coordinates, but whenever %D one is composing two `transformations' together %D (transformations are `associative'). Note that the formula %D is not symmetric: you have to keep track of which %D transformation existed before (i.e.\ the equivalent of %D $[s_x\, r_x\, r_y\, s_y\, t_x\, t_y]$) and which was %D specified later (i.e.\ the equivalent of $[a\, b\, c\, d\, %D e\, f]$). Note also that the language can be rather %D confusing: the one specified later `acts earlier', %D converting the user space coordinates to intermediate %D coordinates, which are then acted upon by the pre||existing %D transformation. The important point is that order of %D transformation matrices cannot be flipped (transformations %D are not `commutative'). %D %D Now what does it mean to move a transformation matrix %D before a drawing? What it means is that given a point %D $(P_x, P_y)$ we need a different set of coordinates %D $(P_x^\prime, P_y^\prime)$ such that if the transformation %D acts on $(P_x^\prime, P_y^\prime)$, they produce $(P_x, %D P_y)$. That is we need to solve the set of equations: %D %D \plaatsformule %D \startformule %D \pmatrix{P_x\cr P_y\cr 1\cr} = \pmatrix{s_x&r_y&t_x\cr %D r_x&s_y&t_y\cr %D 0 &0 &1 \cr} %D \pmatrix{P_x^\prime\cr %D P_y^\prime\cr %D 1 \cr} %D \stopformule %D %D Again matrix notation comes in handy (i.e. someone has %D already solved the problem for us): we need the inverse %D transformation matrix. The inverse transformation matrix can %D be calculated very easily: it is %D %D \plaatsformule %D \startformule %D \pmatrix{P_x^\prime\cr P_y^\prime\cr 1\cr} = %D \pmatrix{s_x^\prime&r_y^\prime&t_x^\prime\cr %D r_x^\prime&s_y^\prime&t_y^\prime\cr %D 0 &0 &1 \cr} %D \pmatrix{P_x\cr %D P_y\cr %D 1 \cr} %D \stopformule %D %D where, the inverse transformation matrix is given by %D %D \plaatsformule %D \startformule %D \eqalign %D {D & = s_x s_y - r_x r_y \cr %D s_x^\prime & = s_y / D \cr %D s_y^\prime & = s_x / D \cr %D r_x^\prime & = - r_x / D \cr %D r_y^\prime & = - r_y / D \cr %D t_x^\prime & = ( - s_y t_x + r_y t_y ) / D \cr %D t_y^\prime & = ( r_x t_x - s_x t_y ) / D \cr} %D \stopformule %D %D And you can see that when expanded out, this does %D give the formulas: %D %D \plaatsformule %D \startformule %D P_x^\prime = { { s_y(p_x-t_x) + r_y(t_y-p_y) } \over %D { s_x*s_y-r_x*r_y } } %D \stopformule %D %D \plaatsformule %D \startformule %D P_y^\prime = { { s_x(p_y-t_y) + r_x(t_x-p_x) } \over %D { s_x*s_y-r_x*r_y } } %D \stopformule %D %D The code works by representing a real number by converting %D it to a dimension to be put into a \DIMENSION\ register: 2.3 would %D be represented as 2.3pt for example. In this scheme, %D multiplying two numbers involves multiplying the \DIMENSION\ %D registers and dividing by 65536. Accuracy demands that the %D division be done as late as possible, but overflow %D considerations need early division. %D %D Division involves dividing the two \DIMENSION\ registers and %D multiplying the result by 65536. Again, accuracy would %D demand that the numerator be multiplied (and|/|or the %D denominator divided) early: but that can lead to overflow %D which needs to be avoided. %D %D If nothing is known about the numbers to start with (in %D concat), I have chosen to divide the 65536 as a 256 in each %D operand. However, in the series calculating the sine and %D cosine, I know that the terms are small (because I never %D have an angle greater than 45 degrees), so I chose to %D apportion the factor in a different way. %D %D \stop %D %D The path is output using the values saved on the stack. If %D needed, all coordinates are recalculated. \def\processMPpath% {\flushMPpath \closeMPpath \pdfliteral{\ifcase\finiMPpath W n\or S\or f\or B\fi}% \let\handleMPsequence=\dohandleMPsequence \resetMPstack \nofMPsegments=0 \handleMPsequence} %D In \PDF\ the \type{cm} operator must precede the path %D specification. We therefore can output the \type{cm} at %D the moment we encounter it. \def\handleMPpathconcat% {\presetMPconcat \pdfliteral{\gMPs1 \gMPs2 \gMPs3 \gMPs4 \gMPs5 \gMPs6 cm}% \resetMPstack} %D This macro interprets the path and saves it as compact as %D possible. \def\dohandleMPpath#1#2 % {\ifnum\lccode`#1=0 \setMPargument{#1#2}% \else \def\somestring{#1#2}% \ifx\somestring\PSlineto \setMPkeyword0 \else\ifx\somestring\PScurveto \setMPkeyword1 \else\ifx\somestring\PSrlineto \setMPkeyword2 \else\ifx\somestring\PSmoveto \setMPkeyword3 \else\ifx\somestring\PSclip \let\handleMPsequence=\processMPpath \else\ifx\somestring\PSgsave \chardef\finiMPpath=3 \else\ifx\somestring\PSgrestore \else\ifx\somestring\PSfill \ifnum\finiMPpath=0 \chardef\finiMPpath=2 \let\handleMPsequence=\processMPpath \fi \else\ifx\somestring\PSstroke \ifnum\finiMPpath=0 \chardef\finiMPpath=1 \fi \let\handleMPsequence=\processMPpath \else\ifx\somestring\PSclosepath \def\closeMPpath{\pdfliteral{h}}% \else\ifx\somestring\PSconcat \let\flushMPpath=\flushconcatMPpath \handleMPpathconcat \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \fi \handleMPsequence} %D The main conversion command is %D %D \starttypen %D \convertMPtoPDF {filename} {x scale} {y scale} %D \stoptypen %D %D The dimensions are derived from the bounding box. So we %D only have to say: %D %D \starttypen %D \convertMPtoPDF{mp-pra-1.eps}{1}{1} %D \convertMPtoPDF{mp-pra-1.eps}{.5}{.5} %D \stoptypen \def\convertMPtoPDF#1#2#3% {\bgroup \message{[MP to PDF #1]}% \setMPspecials \startMPscanning \def\do{}% \edef\MPxscale{#2}% \edef\MPyscale{#3}% \setbox0=\vbox\bgroup \forgetall \offinterlineskip \pdfliteral{q}% \let\handleMPsequence=\dohandleMPsequence \catcode`\^^M=\@@endofline \input #1\relax} \def\finishMPgraphic% {\pdfliteral{Q}% \egroup \wd0=\MPwidth \vbox to \MPheight {\forgetall \vfill \pdfliteral{q \MPxscale\space 0 0 \MPyscale\space \MPxoffset\space \MPyoffset\space cm}% \box0 \pdfliteral{Q}}% \egroup} %D This kind of conversion is possible because \METAPOST\ %D does all the calculations. Converting other \POSTSCRIPT\ %D files would drive both me and \TEX\ crazy. \protect \endinput