% Copyright (C) 1991, 1995, 1996, 1998, 1999 Aladdin Enterprises. All rights reserved.% % This software is provided AS-IS with no warranty, either express or% implied.% % This software is distributed under license and may not be copied,% modified or distributed except as expressly authorized under the terms% of the license contained in the file LICENSE in this distribution.% % For more information about licensing, please refer to% http://www.ghostscript.com/licensing/. For information on% commercial licensing, go to http://www.artifex.com/licensing/ or% contact Artifex Software, Inc., 101 Lucas Valley Road #110,% San Rafael, CA 94903, U.S.A., +1(415)492-9861. % $Id: ps2ascii.ps,v 1.10 2004/06/23 09:04:17 igor Exp $% Extract the ASCII text from a PostScript file. Nothing is displayed.% Instead, ASCII information is written to stdout. The idea is similar to% Glenn Reid's `distillery', only a lot more simple-minded, and less robust. % If SIMPLE is defined, just the text is written, with a guess at line% breaks and word spacing. If SIMPLE is not defined, lines are written% to stdout as follows:%% F <height> <width> (<fontname>)% Indicate the font height and the width of a space.%% P% Indicate the end of the page.% % S <x> <y> (<string>) <width>% Display a string.%% <width> and <height> are integer dimensions in units of 1/720".% <x> and <y> are integer coordinates, in units of 1/720", with the origin% at the lower left.% <string> and <fontname> are strings represented with the standard% PostScript escape conventions. % If COMPLEX is defined, the following additional types of lines are% written to stdout.%% C <r> <g> <b>% Indicate the current color.%% I <x> <y> <width> <height>% Note the presence of an image.%% R <x> <y> <width> <height>% Fill a rectangle.%% <r>, <g>, and <b> are RGB values expressed as integers between 0 and 1000.%% Note that future versions of this program (in COMPLEX mode) may add% other output elements, so programs parsing the output should be% prepared to ignore elements that they do not recognize. % Note that this code will only work in all cases if systemdict is writable% and if `binding' the definitions of operators defined as procedures% is deferred. For this reason, it is normally invoked with% gs -q -dNODISPLAY -dDELAYBIND -dWRITESYSTEMDICT ps2ascii.ps % Thanks to:% J Greely <jgreely@cis.ohio-state.edu> for improvements to this code;% Jerry Whelan <jerryw@abode.ccd.bnl.gov> for motivating other improvements;% David M. Jones <dmjones@theory.lcs.mit.edu> for improvements noted below. %% Additional modifications by David M. Jones%% (dmjones@theory.lcs.mit.edu), December 23, 1997%% %% (a) Rewrote forall loop at the end of .show.write. This fixes a%% stack leakage problem, but the changes are more significant%% than that. %% %% .char.map includes the names of all characters in the%% StandardEncoding, ISOLatin1Encoding, OT1Encoding and%% T1Encoding vectors. Thus, if the Encoding vector for the%% current font contains a name that is not in .char.map, it's%% redundant to check if the Encoding vector is equal to one of%% the known vectors. Previous versions of ps2ascii would give%% up at this point, and substitute an asterisk (*) for the%% character. I've taken the liberty of instead using the%% OT1Encoding vector to translate the character, on the grounds%% that in the cases I'm most interested in, a font without a%% useful Encoding vector was most likely created by a DVI to PS%% converter such as dvips or DVILASER (and OT1Encoding is%% largely compatible with StandardEncoding anyway). [Note that%% this does not make my earlier changes to support dvips (see%% fix (a) under my 1996 changes) completely obsolete, since%% there's additional useful information I can extract in that%% case.]%% %% Overall, this should provide better support for some documents%% (e.g, DVILASER documents will no longer be translated into a%% series of *'s) without breaking any other documents any worse%% than they already were broken.%% %% (b) Fixed two bugs in dvips.df-tail: (1) changed "dup 127" to "dup%% 128" to fix fencepost error, and (2) gave each font it's own%% FontName rather than having all fonts share the same name.%% %% (c) Added one further refinement to the heuristic for detecting%% paragraph breaks: do not ever start a new paragraph after a%% line ending in a hyphen.%% %% (d) Added a bunch of missing letters from the T1Encoding,%% OT1Encoding and ISOLatin1Encoding vectors to .letter.chars to%% improve hyphen-elimination algorithm. This still won't help%% if there's no useful Encoding vector.%% %% NOTE: A better solution to the problem of missing Encoding vectors%% might be to redefine definefont to check whether the Encoding%% vector is sensible and, if not, replace it by a default. This%% would alleviate the need for constant tests in the .show.write%% loop, as well as automatically solving the problem noted in fix%% (d) above, and the similar problem with .break.chars. This should%% be investigated. Also, the hyphen-elimination algorithm really%% needs to be looked at carefully and rethought. %%* Modifications to ps2ascii.ps by David M. Jones%%* (dmjones@theory.lcs.mit.edu), June 25-July 8, 1996 %%* Modifications:%%* %%* (a) added code to give better support for dvips files by providing%%* FontBBox's, FontName's and Encoding vectors for downloaded%%* bitmap fonts. This is done by using dvips's start-hook to%%* overwrite the df-tail and D procedures that dvips uses to%%* define its Type 3 bitmap fonts. Thus, this change should%%* provide better support for dvips-generated PS files without%%* affecting the handling of other documents.%%* %%* (b) Fixed two bugs that could potentially affect any PS file, not%%* just those created by dvips: (1) added missing "get" operator%%* in .show.write and (2) fixed bug that caused a hyphen at the%%* end of a line to be replaced by a space rather than begin%%* deleted. Note that the first bug was a source of stack%%* leakage, causing ps2ascii to run out of operand stack space%%* occasionally.%%* %%* Search for "%%* BF" to find these modifications.%%* %%* (c) Improved the heuristic for determining whether a line break%%* has occurred and whether a line break represents a paragraph%%* break. Previously, any change in the vertical position caused%%* a line break; now a line break is only registered if the%%* change is larger than the height of the current font. This%%* means that superscripts, subscripts, and such things as%%* shifted accents generated by TeX won't cause line breaks.%%* Paragraph-recognition is now done by comparing the indentation%%* of the new line to the indentation of the previous line and by%%* comparing the vertical distance between the new line and the%%* previous line to the vertical distance between the previous%%* line and its predecessor.%%* %%* (d) Added a hook for renaming the files where stdout and stderr%%* go.%%* %%* In general, my additions or changes to the code are described in%%* comments beginning with "%%*". However, there are numerous other%%* places where I have either re-formatted code or added comments to%%* the code while I was trying to understand it. These are usually%%* not specially marked.%%* /QUIET true defsystemdict wcheck { systemdict } { userdict } ifelse begin/.max where { pop } { /.max { 2 copy lt { exch } if pop } bind def } ifelse/COMPLEX dup where { pop true } { false } ifelse def/SIMPLE dup where { pop true } { false } ifelse def/setglobal where { pop currentglobal /setglobal load true setglobal } { { } }ifelse % Define a way to store and retrieve integers that survives save/restore./.i.string0 (0 ) def/.i.string .i.string0 length string def/.iget { cvi } bind def/.iput { exch //.i.string exch copy cvs pop } bind def/.inew { //.i.string0 dup length string copy } bind def % We only want to redefine operators if they are defined already. /codef { 1 index where { pop def } { pop pop } ifelse } def % Redefine the end-of-page operators. /erasepage { } codef/copypage { SIMPLE { (\014) } { (P\n) } ifelse //print } codef/showpage { copypage erasepage initgraphics } codef % Redefine the fill operators to detect rectangles. /.orderrect % <llx> <lly> <urx> <ury> .orderrect <llx> <lly> <w> <h> { % Ensure llx <= urx, lly <= ury. 1 index 4 index lt { 4 2 roll } if dup 3 index lt { 3 1 roll exch } if exch 3 index sub exch 2 index sub } odef/.fillcomplex { % Do a first pass to see if the path is all rectangles in % the output coordinate system. We don't worry about overlapping % rectangles that might be partially not filled. % Stack: mark llx0 lly0 urx0 ury0 ... true mark x0 y0 ... mark true mark % Add a final moveto so we pick up any trailing unclosed subpath. 0 0 itransform moveto { .coord counttomark 2 gt { counttomark 4 gt { .fillcheckrect } { 4 2 roll pop pop } ifelse } if } { .coord } { cleartomark not mark exit } { counttomark -2 roll 2 copy counttomark 2 roll .fillcheckrect } pathforall cleartomark { .showcolor counttomark 4 idiv { counttomark -4 roll .orderrect (R ) //print .show==4 } repeat pop } { cleartomark } ifelse } odef/.fillcheckrect { % Check whether the current subpath is a rectangle. % If it is, add it to the list of rectangles being accumulated; % if not exit the .fillcomplex loop. % The subpath has not been closed. % Stack: as in .fillcomplex, + newx newy counttomark 10 eq { 9 index 9 index 4 2 roll } if counttomark 12 ne { cleartomark not mark exit } if 12 2 roll % Check for the two possible forms of rectangles: % x0 y0 x0 y1 x1 y1 x1 y0 x0 y0 % x0 y0 x1 y0 x1 y1 x0 y1 x0 y0 9 index 2 index eq 9 index 2 index eq and 10 index 9 index eq { % Check for first form. 7 index 6 index eq and 6 index 5 index eq and 3 index 2 index eq and } { % Check for second form. 9 index 8 index eq and 8 index 7 index eq and 5 index 4 index eq and 4 index 3 index eq and } ifelse not { cleartomark not mark exit } if % We have a rectangle. pop pop pop pop 4 2 roll pop pop 8 4 roll } odef/eofill { COMPLEX { .fillcomplex } if newpath } codef/fill { COMPLEX { .fillcomplex } if newpath } codef/rectfill { gsave newpath .rectappend fill grestore } codef/ueofill { gsave newpath uappend eofill grestore } codef/ufill { gsave newpath uappend fill grestore } codef % Redefine the stroke operators to detect rectangles. /rectstroke { gsave newpath dup type dup /arraytype eq exch /packedarraytype eq or { dup length 6 eq { exch .rectappend concat } { .rectappend } ifelse } { .rectappend } ifelse stroke grestore } codef/.strokeline % <fromx> <fromy> <tox> <toy> .strokeline <tox> <toy> % Note: fromx and fromy are in output coordinates; % tox and toy are in user coordinates. { .coord 2 copy 6 2 roll .orderrect % Add in the line width. Assume square or round caps. currentlinewidth 2 div dup .dcoord add abs 1 .max 5 1 roll 4 index add 4 1 roll 4 index add 4 1 roll 4 index sub 4 1 roll 5 -1 roll sub 4 1 roll (R ) //print .show==4 } odef/.strokecomplex { % Do a first pass to see if the path is all horizontal and vertical % lines in the output coordinate system. % Stack: true mark origx origy curx cury true mark null null null null { .coord 6 2 roll pop pop pop pop 2 copy } { .coord 1 index 4 index eq 1 index 4 index eq or { 4 2 roll pop pop } { cleartomark not mark exit } ifelse } { cleartomark not mark exit } { counttomark -2 roll 2 copy counttomark 2 roll 1 index 4 index eq 1 index 4 index eq or { pop pop 2 copy } { cleartomark not mark exit } ifelse } pathforall cleartomark 0 currentlinewidth .dcoord 0 eq exch 0 eq or and % Do the second pass to write out the rectangles. % Stack: origx origy curx cury { .showcolor null null null null { 6 2 roll pop pop pop pop 2 copy .coord } { .strokeline } { } { 3 index 3 index .strokeline } pathforall pop pop pop pop } if } odef/stroke { COMPLEX { .strokecomplex } if newpath } codef/ustroke { gsave newpath dup length 6 eq { exch uappend concat } { uappend } ifelse stroke grestore } codef % The image operators must read the input and note the dimensions.% Eventually we should redefine these to detect 1-bit-high all-black images,% since this is how dvips does underlining (!). /.noteimagerect % <width> <height> <matrix> .noteimagerect - { COMPLEX { gsave setmatrix itransform 0 0 itransform grestore .coord 4 2 roll .coord .orderrect (I ) //print .show==4 } { pop pop pop } ifelse } odef/colorimage where { pop /colorimage { 1 index { dup 6 add index 1 index 6 add index 2 index 5 add index } { 6 index 6 index 5 index } ifelse .noteimagerect gsave nulldevice //colorimage grestore } codef } if/.noteimage % Arguments as for image[mask] { dup type /dicttype eq { dup /Width get 1 index /Height get 2 index /ImageMatrix get } { 4 index 4 index 3 index } ifelse .noteimagerect } odef/image { .noteimage gsave nulldevice //image grestore } codef/imagemask { .noteimage gsave nulldevice //imagemask grestore } codef % Output the current color if necessary./.color.r .inew def .color.r -1 .iput % make sure we write the color at the beginning/.color.g .inew def/.color.b .inew def/.showcolor { COMPLEX { currentrgbcolor 1000 mul round cvi 3 1 roll 1000 mul round cvi exch 1000 mul round cvi % Stack: b g r dup //.color.r .iget eq 2 index //.color.g .iget eq and 3 index //.color.b .iget eq and { pop pop pop } { (C ) //print dup //.color.r exch .iput .show==only ( ) //print dup //.color.g exch .iput .show==only ( ) //print dup //.color.b exch .iput .show==only (\n) //print } ifelse } if } bind def % Redefine `show'. % Set things up so our output will be in tenths of a point, with origin at% lower left. This isolates us from the peculiarities of individual devices. /.show.ident.matrix matrix def/.show.ident { % - .show.ident <scale> <matrix>% //.show.ident.matrix defaultmatrix% % Assume the original transformation is well-behaved.% 0.1 0 2 index dtransform abs exch abs .max /.show.scale exch def% 0.1 dup 3 -1 roll scale gsave initmatrix % Assume the original transformation is well-behaved... 0.1 0 dtransform abs exch abs .max 0.1 dup scale .show.ident.matrix currentmatrix % ... but undo any rotation into landscape orientation. dup 0 get 0 eq { 1 get dup abs div 90 mul rotate .show.ident.matrix currentmatrix } if grestore} bind def /.coord { % <x> <y> .coord <x'> <y'> transform .show.ident exch pop itransform exch round cvi exch round cvi} odef /.dcoord { % <dx> <dy> .coord <dx'> <dy'> % Transforming distances is trickier, because % the coordinate system might be rotated. .show.ident pop 3 1 roll exch 0 dtransform dup mul exch dup mul add sqrt 2 index div round cvi exch 0 exch dtransform dup mul exch dup mul add sqrt 3 -1 roll div round cvi} odef % Remember the current X, Y, and height./.show.x .inew def/.show.y .inew def/.show.height .inew def % Remember the last character of the previous string; if it was a% hyphen preceded by a letter, we didn't output the hyphen. /.show.last (\000) def % Remember the current font./.font.name 130 string def/.font.name.length .inew def/.font.height .inew def/.font.width .inew def %%* Also remember indentation of current line and previous vertical%%* skip /.show.indent .inew def/.show.dy .inew def % We have to redirect stdout somehow.... /.show.stdout { (%stdout) (w) file } bind def % Make sure writing will work even if a program uses =string./.show.string =string length string def/.show.=string =string length string def/.show==only { //=string //.show.=string copy pop dup type /stringtype eq { dup length //.show.string length le { dup rcheck { //.show.string copy } if } if } if .show.stdout exch write==only //.show.=string //=string copy pop } odef/.show==4 { 4 -1 roll .show==only ( ) //print 3 -1 roll .show==only ( ) //print exch .show==only ( ) //print .show==only (\n) //print } odef /.showwidth % Same as stringwidth, but disable COMPLEX so that % we don't try to detect rectangles during BuildChar. { COMPLEX { /COMPLEX false def stringwidth /COMPLEX true def } { stringwidth } ifelse } odef /.showfont % <string> .showfont <string> { gsave % Try getting the height and width of the font from the FontBBox. currentfont /FontBBox .knownget not { {0 0 0 0} } if aload pop % llx lly urx ury exch 4 -1 roll % lly ury urx llx sub % lly ury dx 3 1 roll exch % dx ury lly sub % dx dy 2 copy .max 0 ne { currentfont /FontMatrix get dtransform } { pop pop % Fonts produced by dvips, among other applications, have % BuildChar procedures that bomb out when given unexpected % characters, and there is no way to determine whether a given % character will do this. So for Type 1 fonts, we measure a % typical character ('X'); for others, we punt. currentfont /FontType get 1 eq { (X) .showwidth pop dup 1.3 mul } { % No safe way to get the character size. Punt. 0 0 } ifelse } ifelse .dcoord exch currentfont /FontName .knownget not { () } if dup type /stringtype ne { //.show.string cvs } if grestore % Stack: height width fontname SIMPLE { pop pop //.show.height exch .iput }Lines 1–500 of 1524. Load more
% Copyright (C) 1991, 1995, 1996, 1998, 1999 Aladdin Enterprises. All rights reserved. % % This software is provided AS-IS with no warranty, either express or % implied. % % This software is distributed under license and may not be copied, % modified or distributed except as expressly authorized under the terms % of the license contained in the file LICENSE in this distribution. % % For more information about licensing, please refer to % http://www.ghostscript.com/licensing/. For information on % commercial licensing, go to http://www.artifex.com/licensing/ or % contact Artifex Software, Inc., 101 Lucas Valley Road #110, % San Rafael, CA 94903, U.S.A., +1(415)492-9861. % $Id: ps2ascii.ps,v 1.10 2004/06/23 09:04:17 igor Exp $ % Extract the ASCII text from a PostScript file. Nothing is displayed. % Instead, ASCII information is written to stdout. The idea is similar to % Glenn Reid's `distillery', only a lot more simple-minded, and less robust. % If SIMPLE is defined, just the text is written, with a guess at line % breaks and word spacing. If SIMPLE is not defined, lines are written % to stdout as follows: % % F <height> <width> (<fontname>) % Indicate the font height and the width of a space. % % P % Indicate the end of the page. % % S <x> <y> (<string>) <width> % Display a string. % % <width> and <height> are integer dimensions in units of 1/720". % <x> and <y> are integer coordinates, in units of 1/720", with the origin % at the lower left. % <string> and <fontname> are strings represented with the standard % PostScript escape conventions. % If COMPLEX is defined, the following additional types of lines are % written to stdout. % % C <r> <g> <b> % Indicate the current color. % % I <x> <y> <width> <height> % Note the presence of an image. % % R <x> <y> <width> <height> % Fill a rectangle. % % <r>, <g>, and <b> are RGB values expressed as integers between 0 and 1000. % % Note that future versions of this program (in COMPLEX mode) may add % other output elements, so programs parsing the output should be % prepared to ignore elements that they do not recognize. % Note that this code will only work in all cases if systemdict is writable % and if `binding' the definitions of operators defined as procedures % is deferred. For this reason, it is normally invoked with % gs -q -dNODISPLAY -dDELAYBIND -dWRITESYSTEMDICT ps2ascii.ps % Thanks to: % J Greely <jgreely@cis.ohio-state.edu> for improvements to this code; % Jerry Whelan <jerryw@abode.ccd.bnl.gov> for motivating other improvements; % David M. Jones <dmjones@theory.lcs.mit.edu> for improvements noted below. %% Additional modifications by David M. Jones %% (dmjones@theory.lcs.mit.edu), December 23, 1997 %% %% (a) Rewrote forall loop at the end of .show.write. This fixes a %% stack leakage problem, but the changes are more significant %% than that. %% %% .char.map includes the names of all characters in the %% StandardEncoding, ISOLatin1Encoding, OT1Encoding and %% T1Encoding vectors. Thus, if the Encoding vector for the %% current font contains a name that is not in .char.map, it's %% redundant to check if the Encoding vector is equal to one of %% the known vectors. Previous versions of ps2ascii would give %% up at this point, and substitute an asterisk (*) for the %% character. I've taken the liberty of instead using the %% OT1Encoding vector to translate the character, on the grounds %% that in the cases I'm most interested in, a font without a %% useful Encoding vector was most likely created by a DVI to PS %% converter such as dvips or DVILASER (and OT1Encoding is %% largely compatible with StandardEncoding anyway). [Note that %% this does not make my earlier changes to support dvips (see %% fix (a) under my 1996 changes) completely obsolete, since %% there's additional useful information I can extract in that %% case.] %% %% Overall, this should provide better support for some documents %% (e.g, DVILASER documents will no longer be translated into a %% series of *'s) without breaking any other documents any worse %% than they already were broken. %% %% (b) Fixed two bugs in dvips.df-tail: (1) changed "dup 127" to "dup %% 128" to fix fencepost error, and (2) gave each font it's own %% FontName rather than having all fonts share the same name. %% %% (c) Added one further refinement to the heuristic for detecting %% paragraph breaks: do not ever start a new paragraph after a %% line ending in a hyphen. %% %% (d) Added a bunch of missing letters from the T1Encoding, %% OT1Encoding and ISOLatin1Encoding vectors to .letter.chars to %% improve hyphen-elimination algorithm. This still won't help %% if there's no useful Encoding vector. %% %% NOTE: A better solution to the problem of missing Encoding vectors %% might be to redefine definefont to check whether the Encoding %% vector is sensible and, if not, replace it by a default. This %% would alleviate the need for constant tests in the .show.write %% loop, as well as automatically solving the problem noted in fix %% (d) above, and the similar problem with .break.chars. This should %% be investigated. Also, the hyphen-elimination algorithm really %% needs to be looked at carefully and rethought. %%* Modifications to ps2ascii.ps by David M. Jones %%* (dmjones@theory.lcs.mit.edu), June 25-July 8, 1996 %%* Modifications: %%* %%* (a) added code to give better support for dvips files by providing %%* FontBBox's, FontName's and Encoding vectors for downloaded %%* bitmap fonts. This is done by using dvips's start-hook to %%* overwrite the df-tail and D procedures that dvips uses to %%* define its Type 3 bitmap fonts. Thus, this change should %%* provide better support for dvips-generated PS files without %%* affecting the handling of other documents. %%* %%* (b) Fixed two bugs that could potentially affect any PS file, not %%* just those created by dvips: (1) added missing "get" operator %%* in .show.write and (2) fixed bug that caused a hyphen at the %%* end of a line to be replaced by a space rather than begin %%* deleted. Note that the first bug was a source of stack %%* leakage, causing ps2ascii to run out of operand stack space %%* occasionally. %%* %%* Search for "%%* BF" to find these modifications. %%* %%* (c) Improved the heuristic for determining whether a line break %%* has occurred and whether a line break represents a paragraph %%* break. Previously, any change in the vertical position caused %%* a line break; now a line break is only registered if the %%* change is larger than the height of the current font. This %%* means that superscripts, subscripts, and such things as %%* shifted accents generated by TeX won't cause line breaks. %%* Paragraph-recognition is now done by comparing the indentation %%* of the new line to the indentation of the previous line and by %%* comparing the vertical distance between the new line and the %%* previous line to the vertical distance between the previous %%* line and its predecessor. %%* %%* (d) Added a hook for renaming the files where stdout and stderr %%* go. %%* %%* In general, my additions or changes to the code are described in %%* comments beginning with "%%*". However, there are numerous other %%* places where I have either re-formatted code or added comments to %%* the code while I was trying to understand it. These are usually %%* not specially marked. %%* /QUIET true def systemdict wcheck { systemdict } { userdict } ifelse begin /.max where { pop } { /.max { 2 copy lt { exch } if pop } bind def } ifelse /COMPLEX dup where { pop true } { false } ifelse def /SIMPLE dup where { pop true } { false } ifelse def /setglobal where { pop currentglobal /setglobal load true setglobal } { { } } ifelse % Define a way to store and retrieve integers that survives save/restore. /.i.string0 (0 ) def /.i.string .i.string0 length string def /.iget { cvi } bind def /.iput { exch //.i.string exch copy cvs pop } bind def /.inew { //.i.string0 dup length string copy } bind def % We only want to redefine operators if they are defined already. /codef { 1 index where { pop def } { pop pop } ifelse } def % Redefine the end-of-page operators. /erasepage { } codef /copypage { SIMPLE { (\014) } { (P\n) } ifelse //print } codef /showpage { copypage erasepage initgraphics } codef % Redefine the fill operators to detect rectangles. /.orderrect % <llx> <lly> <urx> <ury> .orderrect <llx> <lly> <w> <h> { % Ensure llx <= urx, lly <= ury. 1 index 4 index lt { 4 2 roll } if dup 3 index lt { 3 1 roll exch } if exch 3 index sub exch 2 index sub } odef /.fillcomplex { % Do a first pass to see if the path is all rectangles in % the output coordinate system. We don't worry about overlapping % rectangles that might be partially not filled. % Stack: mark llx0 lly0 urx0 ury0 ... true mark x0 y0 ... mark true mark % Add a final moveto so we pick up any trailing unclosed subpath. 0 0 itransform moveto { .coord counttomark 2 gt { counttomark 4 gt { .fillcheckrect } { 4 2 roll pop pop } ifelse } if } { .coord } { cleartomark not mark exit } { counttomark -2 roll 2 copy counttomark 2 roll .fillcheckrect } pathforall cleartomark { .showcolor counttomark 4 idiv { counttomark -4 roll .orderrect (R ) //print .show==4 } repeat pop } { cleartomark } ifelse } odef /.fillcheckrect { % Check whether the current subpath is a rectangle. % If it is, add it to the list of rectangles being accumulated; % if not exit the .fillcomplex loop. % The subpath has not been closed. % Stack: as in .fillcomplex, + newx newy counttomark 10 eq { 9 index 9 index 4 2 roll } if counttomark 12 ne { cleartomark not mark exit } if 12 2 roll % Check for the two possible forms of rectangles: % x0 y0 x0 y1 x1 y1 x1 y0 x0 y0 % x0 y0 x1 y0 x1 y1 x0 y1 x0 y0 9 index 2 index eq 9 index 2 index eq and 10 index 9 index eq { % Check for first form. 7 index 6 index eq and 6 index 5 index eq and 3 index 2 index eq and } { % Check for second form. 9 index 8 index eq and 8 index 7 index eq and 5 index 4 index eq and 4 index 3 index eq and } ifelse not { cleartomark not mark exit } if % We have a rectangle. pop pop pop pop 4 2 roll pop pop 8 4 roll } odef /eofill { COMPLEX { .fillcomplex } if newpath } codef /fill { COMPLEX { .fillcomplex } if newpath } codef /rectfill { gsave newpath .rectappend fill grestore } codef /ueofill { gsave newpath uappend eofill grestore } codef /ufill { gsave newpath uappend fill grestore } codef % Redefine the stroke operators to detect rectangles. /rectstroke { gsave newpath dup type dup /arraytype eq exch /packedarraytype eq or { dup length 6 eq { exch .rectappend concat } { .rectappend } ifelse } { .rectappend } ifelse stroke grestore } codef /.strokeline % <fromx> <fromy> <tox> <toy> .strokeline <tox> <toy> % Note: fromx and fromy are in output coordinates; % tox and toy are in user coordinates. { .coord 2 copy 6 2 roll .orderrect % Add in the line width. Assume square or round caps. currentlinewidth 2 div dup .dcoord add abs 1 .max 5 1 roll 4 index add 4 1 roll 4 index add 4 1 roll 4 index sub 4 1 roll 5 -1 roll sub 4 1 roll (R ) //print .show==4 } odef /.strokecomplex { % Do a first pass to see if the path is all horizontal and vertical % lines in the output coordinate system. % Stack: true mark origx origy curx cury true mark null null null null { .coord 6 2 roll pop pop pop pop 2 copy } { .coord 1 index 4 index eq 1 index 4 index eq or { 4 2 roll pop pop } { cleartomark not mark exit } ifelse } { cleartomark not mark exit } { counttomark -2 roll 2 copy counttomark 2 roll 1 index 4 index eq 1 index 4 index eq or { pop pop 2 copy } { cleartomark not mark exit } ifelse } pathforall cleartomark 0 currentlinewidth .dcoord 0 eq exch 0 eq or and % Do the second pass to write out the rectangles. % Stack: origx origy curx cury { .showcolor null null null null { 6 2 roll pop pop pop pop 2 copy .coord } { .strokeline } { } { 3 index 3 index .strokeline } pathforall pop pop pop pop } if } odef /stroke { COMPLEX { .strokecomplex } if newpath } codef /ustroke { gsave newpath dup length 6 eq { exch uappend concat } { uappend } ifelse stroke grestore } codef % The image operators must read the input and note the dimensions. % Eventually we should redefine these to detect 1-bit-high all-black images, % since this is how dvips does underlining (!). /.noteimagerect % <width> <height> <matrix> .noteimagerect - { COMPLEX { gsave setmatrix itransform 0 0 itransform grestore .coord 4 2 roll .coord .orderrect (I ) //print .show==4 } { pop pop pop } ifelse } odef /colorimage where { pop /colorimage { 1 index { dup 6 add index 1 index 6 add index 2 index 5 add index } { 6 index 6 index 5 index } ifelse .noteimagerect gsave nulldevice //colorimage grestore } codef } if /.noteimage % Arguments as for image[mask] { dup type /dicttype eq { dup /Width get 1 index /Height get 2 index /ImageMatrix get } { 4 index 4 index 3 index } ifelse .noteimagerect } odef /image { .noteimage gsave nulldevice //image grestore } codef /imagemask { .noteimage gsave nulldevice //imagemask grestore } codef % Output the current color if necessary. /.color.r .inew def .color.r -1 .iput % make sure we write the color at the beginning /.color.g .inew def /.color.b .inew def /.showcolor { COMPLEX { currentrgbcolor 1000 mul round cvi 3 1 roll 1000 mul round cvi exch 1000 mul round cvi % Stack: b g r dup //.color.r .iget eq 2 index //.color.g .iget eq and 3 index //.color.b .iget eq and { pop pop pop } { (C ) //print dup //.color.r exch .iput .show==only ( ) //print dup //.color.g exch .iput .show==only ( ) //print dup //.color.b exch .iput .show==only (\n) //print } ifelse } if } bind def % Redefine `show'. % Set things up so our output will be in tenths of a point, with origin at % lower left. This isolates us from the peculiarities of individual devices. /.show.ident.matrix matrix def /.show.ident { % - .show.ident <scale> <matrix> % //.show.ident.matrix defaultmatrix % % Assume the original transformation is well-behaved. % 0.1 0 2 index dtransform abs exch abs .max /.show.scale exch def % 0.1 dup 3 -1 roll scale gsave initmatrix % Assume the original transformation is well-behaved... 0.1 0 dtransform abs exch abs .max 0.1 dup scale .show.ident.matrix currentmatrix % ... but undo any rotation into landscape orientation. dup 0 get 0 eq { 1 get dup abs div 90 mul rotate .show.ident.matrix currentmatrix } if grestore } bind def /.coord { % <x> <y> .coord <x'> <y'> transform .show.ident exch pop itransform exch round cvi exch round cvi } odef /.dcoord { % <dx> <dy> .coord <dx'> <dy'> % Transforming distances is trickier, because % the coordinate system might be rotated. .show.ident pop 3 1 roll exch 0 dtransform dup mul exch dup mul add sqrt 2 index div round cvi exch 0 exch dtransform dup mul exch dup mul add sqrt 3 -1 roll div round cvi } odef % Remember the current X, Y, and height. /.show.x .inew def /.show.y .inew def /.show.height .inew def % Remember the last character of the previous string; if it was a % hyphen preceded by a letter, we didn't output the hyphen. /.show.last (\000) def % Remember the current font. /.font.name 130 string def /.font.name.length .inew def /.font.height .inew def /.font.width .inew def %%* Also remember indentation of current line and previous vertical %%* skip /.show.indent .inew def /.show.dy .inew def % We have to redirect stdout somehow.... /.show.stdout { (%stdout) (w) file } bind def % Make sure writing will work even if a program uses =string. /.show.string =string length string def /.show.=string =string length string def /.show==only { //=string //.show.=string copy pop dup type /stringtype eq { dup length //.show.string length le { dup rcheck { //.show.string copy } if } if } if .show.stdout exch write==only //.show.=string //=string copy pop } odef /.show==4 { 4 -1 roll .show==only ( ) //print 3 -1 roll .show==only ( ) //print exch .show==only ( ) //print .show==only (\n) //print } odef /.showwidth % Same as stringwidth, but disable COMPLEX so that % we don't try to detect rectangles during BuildChar. { COMPLEX { /COMPLEX false def stringwidth /COMPLEX true def } { stringwidth } ifelse } odef /.showfont % <string> .showfont <string> { gsave % Try getting the height and width of the font from the FontBBox. currentfont /FontBBox .knownget not { {0 0 0 0} } if aload pop % llx lly urx ury exch 4 -1 roll % lly ury urx llx sub % lly ury dx 3 1 roll exch % dx ury lly sub % dx dy 2 copy .max 0 ne { currentfont /FontMatrix get dtransform } { pop pop % Fonts produced by dvips, among other applications, have % BuildChar procedures that bomb out when given unexpected % characters, and there is no way to determine whether a given % character will do this. So for Type 1 fonts, we measure a % typical character ('X'); for others, we punt. currentfont /FontType get 1 eq { (X) .showwidth pop dup 1.3 mul } { % No safe way to get the character size. Punt. 0 0 } ifelse } ifelse .dcoord exch currentfont /FontName .knownget not { () } if dup type /stringtype ne { //.show.string cvs } if grestore % Stack: height width fontname SIMPLE { pop pop //.show.height exch .iput }