diff options
Diffstat (limited to 'Master/tlpkg/tlgs/Resource/Init/pdf_ops.ps')
-rw-r--r-- | Master/tlpkg/tlgs/Resource/Init/pdf_ops.ps | 1815 |
1 files changed, 1815 insertions, 0 deletions
diff --git a/Master/tlpkg/tlgs/Resource/Init/pdf_ops.ps b/Master/tlpkg/tlgs/Resource/Init/pdf_ops.ps new file mode 100644 index 00000000000..ba53b54cd7e --- /dev/null +++ b/Master/tlpkg/tlgs/Resource/Init/pdf_ops.ps @@ -0,0 +1,1815 @@ +% Copyright (C) 2001-2012 Artifex Software, Inc. +% 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. +% +% Refer to licensing information at http://www.artifex.com or contact +% Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael, +% CA 94903, U.S.A., +1(415)492-9861, for further information. +% + +% Definitions for most of the PDF operators. + +.currentglobal //true .setglobal + +% Define pdfmark. Don't allow it to be bound in. +% Also don't define it in systemdict, because this leads some Adobe code +% to think this interpreter is a distiller. +% (If this interpreter really is a distiller, don't do this.) +systemdict /pdfmark known not + { userdict /pdfmark { cleartomark } bind put } if + +userdict /GS_PDF_ProcSet 256 dict dup begin + +% ---------------- Abbreviations ---------------- % + +/bdef { bind def } bind def + +% ---------------- Graphics state stack ---------------- % + +% PDF adds a number of parameters to the graphics state. +% We implement this by pushing and popping a dictionary +% each time we do a PDF gsave or grestore. +% The keys in this dictionary are as follows: +% self % identifies the dictionary as one of ours +% ClipRect % (optional) +% Show +% TextSaveMatrix % matrix at time of BT (iff within BT/ET) +% (The following correspond directly to PDF state parameters.) +% AlphaIsShape +% FillConstantAlpha +% FillColor +% FillColorSpace +% FillOverprint +% SoftMask +% StrokeConstantAlpha +% StrokeColor +% StrokeColorSpace +% StrokeOverprint +% TextSpacing +% TextHScaling +% Leading +% TextFont +% TextLineMatrix +% TextMatrix +% TextRise +% TextRenderingMode +% WordSpacing +% (The following is cached information derived from other graphics state params) +% FontMatrixNonHV % TextFont.FontMatrix alters horz/vert glyph advance vector direction + +/nodict 1 dict def +nodict /self { //nodict } executeonly put % to avoid recursion in dumps +nodict readonly pop + +/dictbeginpage { % <initialdict> dictbeginpage - + 20 dict copy dup begin + 1 packedarray cvx executeonly /self exch def + graphicsbeginpage textbeginpage +} bdef +/endpage { % - endpage - + showpage end +} bdef + +/graphicsbeginpage { + initgraphics + //true .setaccuratecurves + currentdict /ClipRect knownoget { aload pop rectclip } if + 0 g 0 G //false op //false OP 0 OPM + 1 ca 1 CA //null SMask //false AIS /Compatible BM //true TK +} bdef + +% We must allow /Show to be set, otherwise a text operation can +% end up in infinite recursion with showfirst calling Show, and +% Show calling showfirst. +/gput_always_allow +1 dict dup begin + /Show 0 def +end def + +/gput % <value> <key> gput - + { + dup //gput_always_allow exch known not currentdict /n known and { + pop pop + ( **** Error: Ignoring changes to the graphic state after operator 'W'.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + } { + exch currentdict //nodict eq { /self dup load end 5 dict begin def } if + def + } ifelse + } bdef +currentdict /gput_always_allow .undef + +/q { + %% Special case, if we get a 'q' while accumulating clip/eoclip then + %% we need to make sure we close the current dictionary, and reopen it after + %% performing the gsave, so that the redefinition of 'n' in particular is correct + currentdict /n known { + currentdict %% copy the dicitonary for the accumulation + end %% and close it (NB *before* the gsave!) + gsave //nodict begin %% execute gsave and start 'nodict' just like normal + begin %% reopen the accumulation dictionary. + %% when we execute 'n' this will close and we'll go back + %% to the regular 'nodict' opened above. This could cause some + %% odd interactions with 'Q' for unbalanced files..... + } { + %% ugly hackery to work around badly broken PDF file in Bug #695897. The file has nested BT/ET + %% sections, and delimits them with q/Q. The problem is that our code to deal with nested + %% text sections looks in the current dictionary for TextSaveMatrix, and a 'q' makes a brand + %% new empty dictionary, so it defeats it. + %% I tried copying the TextSaveMatrix from the parent state into this one, but that then causes + %% other problems because we end up apllying the TextSaveMatrix when we shouold not. Attempting + %% to clear the TextSaveMatrix from all saved states on ET then caused it not to be defined at + %% times when we needed it..... + %% This is all terribly flaky, but defining a new matrix inside gsaves that tells us that we had + %% were already in a text block allows us to detect and cope with the nested BT's and leaving the + %% original TextSaveMatrix behind allows the matching ET not to throw an error, as well as coping + %% with the various other problems listed above. Its not pretty though. + currentdict /TextSaveMatrix known { + currentdict /TextSaveMatrix get matrix copy + gsave //nodict begin + /qTextSaveMatrix gput + }{ + gsave //nodict begin + }ifelse + } ifelse + PDFusingtransparency { .pushextendedgstate } if +} bdef + +% Some PDF files have excess Q operators! +/Q { + //false + { currentdict /n known { end pop //true } { exit } ifelse + } loop { + ( **** Error: Encountered a 'Q' before finishing 'W' mode.\n) + pdfformaterror + ( Output may be incorrect..\n) pdfformaterror + } if + currentdict /self .knownget { + exec //nodict eq { + end + PDFusingtransparency { .popextendedgstate } if + % Restore graphics state, but do not modify path. Paths are not part + % of the PDF graphics state; see 4.4.1 of PDF reference 3rd ed. + % Collecting the path with one ctm and re-playing it with another ctm + % transforms the path exactly as PDF needs. + {.getpath} stopped { + ( **** Error: unable to preserve current path, probable degenerate CTM, output may be incorrect.\n) pdfformaterror + grestore newpath + }{ + grestore newpath { exec } forall + } ifelse + //false + } { + //true + } ifelse + } { + //true % formaterror -- not a gsave dict + } ifelse + { + (\n **** Error: File has unbalanced q/Q operators \(too many Q's\)\n Output may be incorrect.\n) + + pdfdict /.Qqwarning_issued .knownget + { + { + pop + } + { + currentglobal pdfdict gcheck .setglobal + pdfdict /.Qqwarning_issued //true .forceput + .setglobal + pdfformaterror + } ifelse + } + { + currentglobal pdfdict gcheck .setglobal + pdfdict /.Qqwarning_issued //true .forceput + .setglobal + pdfformaterror + } ifelse + } if +} bdef + +% Save PDF gstate +/qstate { % - qstate <qstate> + gstate +} bdef + +% Set PDF gstate +/setqstate { % <qstate> setqstate - + { matrix setmatrix //false upath } stopped { + pop setgstate newpath + } { + % Save the CTM, set identity during the uappend, then set the CTM + exch setgstate matrix currentmatrix matrix setmatrix + exch newpath uappend setmatrix + } ifelse +} bdef + +% Save most of graphic state attributes. +% - get-gs-attrs ... +/get-gs-attrs { + currentsmoothness + currentflat + currentoverprint + currentstrokeadjust + currentdash + currentmiterlimit + currentlinejoin + currentlinecap + currentlinewidth + currentfont + currentcolor + currentcolorspace + { currentpoint } stopped + matrix currentmatrix +} bdef + +% Restore most of graphic state attributes. +% ... get-gs-attrs - +/set-gs-attrs { + setmatrix + { newpath } { moveto } ifelse + setcolorspace + setcolor + setfont + setlinewidth + setlinecap + setlinejoin + setmiterlimit + setdash + setstrokeadjust + setoverprint + setflat + setsmoothness +} bdef + +% ---------------- Color setting ---------------- % + +/fcput % <color> <colorspace> fcput - + { /FillColorSpace gput /FillColor gput + } bdef +/scput % <color> <colorspace> scput - + { /StrokeColorSpace gput /StrokeColor gput + } bdef +/csput % <colorspace> csput - + { csset 2 copy fcput scput + } bdef + +/csdevgray [/DeviceGray] readonly def +/csdevrgb [/DeviceRGB] readonly def +/csdevcmyk [/DeviceCMYK] readonly def +/cspattern [/Pattern] readonly def +/nullpatternproc { pop } bdef +/nullpattern mark + /PatternType 1 /PaintType 1 /TilingType 3 /BBox [0 0 1 1] + /XStep 1 /YStep 1 /PaintProc //nullpatternproc +.dicttomark readonly def + +% Each entry in the color space dictionary is a procedure of the form +% <cspace> -proc- <cspace> <initial-color> +/CSdict mark + /DeviceGray { pop //csdevgray 0 } bind + /DeviceRGB { pop //csdevrgb [0 0 0] cvx } bind + /DeviceCMYK { pop //csdevcmyk [0 0 0 1] cvx } bind + /CIEBasedA { 0 } bind + /CIEBasedABC { [0 0 0] cvx } bind + /CalGray { pop //csdevgray 0 } bind + /CalRGB { pop //csdevrgb [0 0 0] cvx } bind + /Lab {[0 0 0] cvx } bind + /ICCBased { [ 1 index 1 oget /N get { 0 } repeat ] cvx } bind + /Separation { 1 } bind + /DeviceN { % What is the correct value?? + [ 1 index 1 get length { 1 } repeat ] cvx + } bind + /Indexed { 0 } bind + /Pattern { + dup type /nametype eq 1 index length 1 eq or { + pop //cspattern //nullpattern matrix makepattern + } { + //nullpattern matrix makepattern 1 index 1 get csset + % Stack: patternspace nullpattern basecolor basespace + pop [ 3 1 roll dup type /arraytype eq { aload pop } if + counttomark -1 roll ] cvx + } ifelse + } bind +.dicttomark readonly def +/csset % <cspace> csset <color> <cspace> + { dup dup type /nametype ne { 0 get } if //CSdict exch get exec exch + } bdef + +/g { //csdevgray fcput } bdef +/G { //csdevgray scput } bdef +/rg { 3 array astore cvx //csdevrgb fcput } bdef +/RG { 3 array astore cvx //csdevrgb scput } bdef +/k { 4 array astore cvx //csdevcmyk fcput } bdef +/K { 4 array astore cvx //csdevcmyk scput } bdef +%% +%% Bug #694412, we set the colour space immediately now. Previously we +%% only set the colour space and colour when a stroke or fill took place. +%% if that happened inside a gsave/grestore we could end up setting the +%% same colour space many times, which is a problem if its an expensive +%% space, like ICCBased. +%% +/cs { csset dup setgcolorspace fcput } bdef +/CS { csset dup .swapcolors setgcolorspace .swapcolors scput } bdef +/ri { //.renderingintentdict exch .knownget { .setrenderingintent } if } bdef +% We have to break up sc according to the number of operands. +%% +%% Also Bug #694412, we need to set the actual colour in response to the +%% PDF operators as well. However, the transparenmcy code also uses the +%% original calls (sc, SC, sc1, SC1, sc* and SC*) and so we create +%% a duplicate set which do the same as the originals, but se the current +%% colour as well. +%% +/sc1_and_set { /FillColor gput FillColor FillColorSpace setgcolor} bdef +/sc1 { /FillColor gput } bdef +/SC1_and_set {/StrokeColor gput .swapcolors StrokeColor StrokeColorSpace setgcolor .swapcolors } bdef +/SC1 { /StrokeColor gput } bdef +% We have to avoid storing into a color array associated with an outer +% gsave level, so we do a kind of "copy on write". +/sc* { + currentdict /FillColor .knownget { + astore pop + } { + /FillColor load + % FillColor may contain either a single value or an array. + dup type /arraytype eq { length }{ pop 1 } ifelse + array astore cvx /FillColor gput + } ifelse +} bdef +/SC* { + currentdict /StrokeColor .knownget { + astore pop + } { + /StrokeColor load + % StrokeColor may contain either a single value or an array. + dup type /arraytype eq { length }{ pop 1 } ifelse + array astore cvx /StrokeColor gput + } ifelse +} bdef +/sc*_and_set { + sc* + FillColor FillColorSpace setgcolor +} bdef +/SC*_and_set { + SC* + .swapcolors StrokeColor StrokeColorSpace setgcolor .swapcolors +} bdef + +% ---------------- Overprint/transparency setting ---------------- % + +/op { /FillOverprint gput } bdef % NB pdf_draw:gsparamdict handled /OP with no /op +/OP { /StrokeOverprint gput } bdef +/OPM { + /.setoverprintmode where { pop dup .setoverprintmode .swapcolors .setoverprintmode .swapcolors } { pop } ifelse +} bdef +/ca { /FillConstantAlpha gput } bdef +/CA { /StrokeConstantAlpha gput } bdef +/SMask { /SoftMask gput } bdef +/AIS { /AlphaIsShape gput } bdef +/BM { + /.setblendmode where { + pop [ exch dup type /nametype ne { aload pop } if /Normal ] { + { .setblendmode } .internalstopped not { exit } if pop + } forall + } { + pop + } ifelse +} bdef +/TK { + /.settextknockout where { pop .settextknockout } { pop } ifelse +} bdef + +% ---------------- Color installation ---------------- % + +% Establish a given color (and color space) as current. +/.settransparencyparams { % <alpha> <smask> .settransparencyparams - + PDFusingtransparency { + /.begintransparencygroup where { + pop AlphaIsShape { + 1 .setopacityalpha exch .setshapealpha 1 + } { + 1 .setshapealpha exch .setopacityalpha 0 + } ifelse + % Set the soft mask by rendering the XObject. Doing this every time + % is obviously very inefficient; we'll improve it later. + .settransparencymask + } { + pop pop + } ifelse + } { + pop pop + } ifelse +} bdef +/.settransparencymask { % <paramdict> <masknum> .settransparencymask - + exch dup type /dicttype ne { + PDFusingtransparency { + pop pop + } { + dup /Draw get exec + } ifelse + } { + dup /Draw get exec + } ifelse +} bdef +% (Non-mask) images must execute setfillblend. +/setfillblend { + FillOverprint setoverprint + FillConstantAlpha SoftMask .settransparencyparams +} def +/setfillstate { + FillColor FillColorSpace setgcolor setfillblend +} def + +/setstrokestate { + .swapcolors + StrokeColor StrokeColorSpace setgcolor StrokeOverprint setoverprint + StrokeConstantAlpha SoftMask .settransparencyparams + .swapcolors +} def +/Cdict 15 dict dup begin % <color...> <colorspace> -proc- - + /DeviceGray { pop setgray } bdef + /DeviceRGB { pop setrgbcolor } bdef + /DeviceCMYK { pop setcmykcolor } bdef + /CIEBasedA { setgcolorspace setcolor } bdef + /CIEBasedABC /CIEBasedA load def + /CIEBasedDEF /CIEBasedA load def + /CIEBasedDEFG /CIEBasedA load def + /CalRGB /CIEBasedA load def + /CalGray /CIEBasedA load def + /Lab /CIEBasedA load def + %% This section is to deal with the horrible pair of files in Bug #696690 and Bug #696120 + %% These files have ICCBased spaces where the value of /N and the number of components + %% in the profile differ. In addition the profile in Bug #696690 is invalid. In the + %% case of Bug #696690 the /N value is correct, and the profile is wrong, in the case + %% of Bug #696120 the /N value is incorrect and the profile is correct. + %% We 'suspect' that Acrobat uses the fact that Bug #696120 is a pure image to detect + %% that the /N is incorrect, we can't be sure whether it uses the profile or just uses + %% the /N to decide on a device space. What we now do is; If the /N and device profile + %% number of components don't match, we assume the device profile is correct and patch + %% /N to be the same as the profile (see /ICCBased-resolve), but we save the original + %% value of /N in /OrigN. In setcolor, if the space is a genuine ICCBased space + %% (not a replacement for a device profile) we call set_dev_color which will actually + %% exercise the profile. If that fails we return an error. Here we run setcolor in a + %% stopped context, and if it fails we check to see if there is a /OrigN (ths occurs + %% only if the /N was different to the number of components in the profile). If there + %% is a /OrigN then prefer that to the profile, otherwise they agreed, so just use + %% /N and select a device space. If we can't select a device space with the correct + %% number of components, give up and throw an error. See also /last-ditch-bpc-csp + %% in pdf_draw.ps. + /ICCBased { + dup 1 get + dup /OrigN .knownget {exch /N get}{/N get dup} ifelse + 3 add mark exch 2 roll + setgcolorspace {setcolor} stopped + { + cleartomark + [//null /DeviceGray //null /DeviceRGB /DeviceCMYK] exch + {get} stopped + { + ( **** Error: Unable to set an ICCBased colour space, and cannot set an alternate from the number of components.\n) print + /setcolorspace cvx /undefined signalerror + } + { + dup //null eq { + ( **** Error: Unable to set an ICCBased colour space, and cannot set an alternate from the number of components.\n) print + /setcolorspace cvx /undefined signalerror + }{ + ICCProfileNError flush not { + ( **** Warning : Error setting an ICCBased colour space, using /N to set an alternate device space.\n) print + ( Output may be incorrect.\n) print + } if + setcolorspace + /ICCProfileNError true def + } ifelse + }ifelse + }{ + cleartomark pop + } + ifelse + } bdef + /Separation /CIEBasedA load def + /DeviceN /CIEBasedA load def + /Indexed /CIEBasedA load def + /Pattern + { setgcolorspace + + % Since multiple patterns may share + % same data stream, we need to ensure + % that the stream is at 0 position. + % Making this consistently with resolveshading, + % which applies ReusableStreamDecode filter + % to the PS stream, which represents the + % PDF stream in dynamics. + + dup /Shading knownoget { + dup /ShadingType oget 4 ge { + /DataSource knownoget { + dup type /filetype eq { + 0 setfileposition + } { + pop + } ifelse + } if + } { + pop + } ifelse + } if + + % Associate pattern instance with the default qstate for the context. + % A single pattren object can be reused in several contexts. + dup DefaultQstate .knownget { + exch pop + } { + % But don't update read-only initial null pattern. + dup /PaintProc .knownget { //nullpatternproc ne } { //true } ifelse { + dup dup /Matrix knownoget not { { 1 0 0 1 0 0 } } if + gsave DefaultQstate setqstate makepattern grestore + dup 3 1 roll + DefaultQstate exch put + } if + } ifelse + setcolor + } bdef +end def +/setgcolor % (null | <color...>) <colorspace> setgcolor - + { 1 index //null eq + { pop pop } + { dup 0 get //Cdict exch get exec } + ifelse + } bdef +% Compare the old and new color spaces in an attempt to avoid expensive +% reloads of CIEBased color spaces. +/PCSdict 15 dict dup begin % <colorspace> -proc- <colorspace|pdfcspace> + /CIEBasedA { dup 1 get /PDFColorSpace .knownget { exch pop } if } bdef + /CIEBasedABC /CIEBasedA load def + /CIEBasedDEF /CIEBasedA load def + /CIEBasedDEFG /CIEBasedA load def + /Indexed { + dup 1 get dup pdfcolorspace 2 copy ne { 3 1 roll } if pop pop + } bdef +end def +/pdfcolorspace { % <colorspace> pdfcolorspace <colorspace|pdfcspace> + dup type /arraytype eq { + //PCSdict 1 index 0 get .knownget { exec } if + } if +} bdef +/setgcolorspace { % <colorspace> setgcolorspace - + dup pdfcolorspace currentcolorspace pdfcolorspace eq { + pop + } { + setcolorspace + } ifelse +} bdef + +/OPsavedict 2 dict def % for saveOP, saveBM + +% colorspaces that don't require special overprint transparency handling +/okOPcs mark /DeviceGray 0 /DeviceCMYK 1 /DeviceN 2 /Separation 3 .dicttomark def + +/checkOPtrans % <fillop|strokeop> checkOPtrans <bool> +% return true if OP needs special transparency treatment +% If the colorspace is not one where overprint makes sense, no special handling (return false) +% NB: This will get replaced with a quick "no-op" if the device doesn't support Overprint +{ % Check OP and BM in case we need to push a group + okOPcs currentcolorspace 0 get dup /Indexed eq { + pop currentcolorspace 1 get % use the base space + } if + known { + /stroke ne { FillOverprint } { StrokeOverprint } ifelse + .currentblendmode dup /Normal eq exch /Compatible eq or not and + } { + pop //false + } ifelse +} bdef + +/fsexec % <fillop|strokeop> fsexec - +{ + PDFusingtransparency { + SoftMask //null ne { + mark /Subtype /Group /Isolated //true .dicttomark pathbbox .begintransparencygroup + } if + dup checkOPtrans exch 1 index { + % We need to push a non-isolated, non-knockout transparency group and + % perform the operation in CompatibleOverprint mode, then end the + % transparency group. Do the begintransparencygroup step here. + mark /Subtype /Group /Isolated //false .dicttomark pathbbox .begintransparencygroup + exch .currentblendmode exch .currentopacityalpha exch 4 -1 roll % save current values + /CompatibleOverprint .setblendmode 1 .setopacityalpha + } if + cvx exec + { + .endtransparencygroup .setopacityalpha .setblendmode % end the CompatibleOverprint group + } if + SoftMask //null ne { + .endtransparencygroup + } if + } { + cvx exec + } ifelse +} bdef + +% ---------------- Path painting and clipping ---------------- % + +%% Bug #696017 In order to get the correct position of paths and other objects which +%% are illegally present inside a text block, we need to use the saved TextSaveMatrix +%% to construct/place them. But we don't want to do lots of lookups on every segment of +%% a path (we feel its OK to do lookups for images). So what we do is redefine the +%% path contruction operators at the beginning of a text block, and put them back afterwards. +%% Here we define the 'normal' and 'inside text' versions of those operators, and routines +%% to switch the definitions back and forth. + +/check_and_set_saved_matrix { + currentdict /TextSaveMatrix known { + currentdict /TextSaveMatrix get setmatrix + } + { + currentdict /qTextSaveMatrix known { + currentdict /qTextSaveMatrix get setmatrix + } if + } ifelse +} bind def + +/normal_m { { moveto } stopped { count pdfemptycount sub 2 .min { pop } repeat 0 0 moveto } if } bdef +/inside_text_m { + { + matrix currentmatrix 3 1 roll + check_and_set_saved_matrix + moveto + setmatrix + } + stopped { count pdfemptycount sub 2 .min { pop } repeat 0 0 moveto } if +} bdef + +/normal_l { { lineto } stopped { count pdfemptycount sub 2 .min { pop } repeat } if } bdef +/inside_text_l { + { + matrix currentmatrix 3 1 roll + check_and_set_saved_matrix + lineto + setmatrix + } + stopped { count pdfemptycount sub 2 .min { pop } repeat } if +} bdef + +/normal_c { { curveto } stopped { count pdfemptycount sub 6 .min { pop } repeat } if } bdef +/inside_text_c { + { + matrix currentmatrix 7 1 roll + check_and_set_saved_matrix + curveto + setmatrix + } + stopped { count pdfemptycount sub 6 .min { pop } repeat } if +} bdef + +/normal_v { count pdfemptycount sub 4 ge { + { currentpoint 6 2 roll curveto } stopped { count pdfemptycount sub 6 .min { pop } repeat } if + } { + count pdfemptycount sub { pop } repeat + } ifelse + } bdef +/inside_text_v { count pdfemptycount sub 4 ge { + { + matrix currentmatrix 5 1 roll + check_and_set_saved_matrix + currentpoint 6 2 roll curveto + setmatrix + } stopped { count pdfemptycount sub 6 .min { pop } repeat } if + } { + count pdfemptycount sub { pop } repeat + } ifelse + } bdef + +/normal_y { { 2 copy curveto } stopped { count pdfemptycount sub 6 .min { pop } repeat } if } bdef +/inside_text_y { + { + matrix currentmatrix 5 1 roll + check_and_set_saved_matrix + 2 copy curveto + setmatrix + } + stopped { count pdfemptycount sub 6 .min { pop } repeat } if +} bdef + +/normal_re { + 4 2 roll moveto exch dup 0 rlineto 0 3 -1 roll rlineto neg 0 rlineto + closepath + } bdef +/inside_text_re { + matrix currentmatrix 5 1 roll + check_and_set_saved_matrix + 4 2 roll moveto exch dup 0 rlineto 0 3 -1 roll rlineto neg 0 rlineto + closepath + setmatrix + } bdef + +/S { + OFFlevels length 0 eq { + setstrokestate .swapcolors /stroke fsexec .swapcolors + } { + newpath + } ifelse +} bdef + +/f { + OFFlevels length 0 eq { + setfillstate /fill fsexec + } { + newpath + } ifelse +} bdef + +/f* { + OFFlevels length 0 eq { + setfillstate /eofill fsexec + } { + newpath + } ifelse +} bdef + +/n { newpath } bdef % don't allow n to get bound in + +/s { closepath S } bdef + +/B { + OFFlevels length 0 eq { + PDFusingtransparency { + % knockout trans group around the filled and stroked object + mark + /Isolated //true /Knockout //true + .dicttomark + % strokepath will return empty path (no currentpoint) if nothing is stroked + gsave { strokepath pathbbox } stopped grestore not { + 1 .setopacityalpha + .begintransparencygroup + gsave setfillstate fill grestore + setstrokestate .swapcolors /stroke fsexec .swapcolors + .endtransparencygroup + } { newpath pop } ifelse + } { + gsave setfillstate fill grestore + setstrokestate .swapcolors /stroke fsexec .swapcolors + } ifelse + } { + newpath + } ifelse +} bdef + +/b { closepath B } bdef + +/B* { + OFFlevels length 0 eq { + PDFusingtransparency { + % knockout trans group around the filled and stroked object + mark + /Isolated //true /Knockout //true + .dicttomark + % strokepath will return empty path (no currentpoint) if nothing is stroked + gsave { strokepath pathbbox } stopped grestore not { + 1 .setopacityalpha + .begintransparencygroup + gsave setfillstate eofill grestore + setstrokestate .swapcolors /stroke fsexec .swapcolors + .endtransparencygroup + } { newpath pop } ifelse + } { + gsave setfillstate eofill grestore + setstrokestate .swapcolors /stroke fsexec .swapcolors + } ifelse + } { + newpath + } ifelse +} bdef + +/b* { closepath B* } bdef + +% Clipping: + +/Wdict 4 dict dup begin +/S { OFFlevels length 0 eq { gsave setstrokestate .swapcolors stroke .swapcolors grestore } if n } bdef +/f { OFFlevels length 0 eq { gsave setfillstate fill grestore } if n } bdef +/f* { OFFlevels length 0 eq { gsave setfillstate eofill grestore } if n } bdef +/n { end { currentpoint } stopped not { pop pop clip } if newpath } bdef +end readonly def +/W { //Wdict begin } bdef +/W*dict 4 dict dup begin +Wdict { def } forall +/n { end { currentpoint } stopped not { pop pop eoclip } if newpath } bdef +end readonly def +/W* { //W*dict begin } bdef + +% ---------------- Text control ---------------- % + +/textbeginpage + { /TextSpacing 0 def % 0 Tc + /TextLeading 0 def % 0 TL + /TextRenderingMode 0 def % 0 Tr + /TextRise 0 def % 0 Ts + /WordSpacing 0 def % 0 Tw + /TextHScaling 1.0 def % 100 Tz + /TextFont //null def + /FontMatrixNonHV //false def + /Show { showfirst } def + /TextFillStateNeeded //true def + } bdef + +/TestDegenerateCTM { + matrix currentmatrix + dup 0 get + exch dup 3 get + exch dup 1 get + exch 2 get + mul + 3 1 roll mul + exch sub + 0 eq { + true + }{ + false + } ifelse +} bdef + +% Contrary to the statement in the PDF manual, BT and ET *can* be nested, +% if the CharProc for a Type 3 font does a BT/ET itself. +% Since we always call the CharProc inside a q/Q, we simply ensure that +% the text state is saved and restored like the rest of the extended +% graphics state. + +/settextmatrix { + matrix currentmatrix + TextMatrix concat + TestDegenerateCTM { + (\n **** Error: Degenerate text matrix detected, ignoring Tm operation\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + setmatrix + } { + pop + } ifelse + TextHScaling 1 ne { TextHScaling 1 scale } if + TextRise 0 ne { 0 TextRise translate } if + TextFont dup //null eq { pop } { setfont } ifelse +} bdef + +/settextstate { + % The text state can be set even outside BT/ET. + currentdict /TextSaveMatrix known { + TextSaveMatrix + aload pop + 5 index 0 ne 3 index 0 ne and + 5 index 0 ne 5 index 0 ne and or not { + (\n **** Error: Invalid (0 scaling) text matrix for Tm ****\n) + pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + % handle invalid scale by using a really small value + 2 -6 roll pop pop pop pop + 0.00000001 0 0 0.00000001 + 6 -2 roll + }if + 6 array astore + setmatrix settextmatrix + } if +} bdef +/settextposition { + % Update the TextMatrix translation. + gsave TextSaveMatrix setmatrix + {currentpoint} stopped not { TextRise sub TextMatrix 4 2 getinterval astore pop + % We would like to do "grestore currentpoint translate" + % here, but some PDF files set a singular text matrix + % (0 0 0 0 <x> <y> Tm), so we can't do this. + TextTempMatrix identmatrix setmatrix currentpoint + grestore + TextTempMatrix currentmatrix 4 2 getinterval astore pop + TextTempMatrix setmatrix + } { + ( **** Error: Invalid currentpoint, probable degenerate CTM.\n) pdfformaterror + ( Ignroing error, output may be incorrect.\n) pdfformaterror + grestore + } ifelse +} bdef + +/switch_to_text_marking_ops { + pdfopdict /m {inside_text_m} bind .forceput + pdfopdict /l {inside_text_l} bind .forceput + pdfopdict /c {inside_text_c} bind .forceput + pdfopdict /v {inside_text_v} bind .forceput + pdfopdict /y {inside_text_y} bind .forceput + pdfopdict /re {inside_text_re} bind .forceput +} bdef + +/switch_to_normal_marking_ops { + pdfopdict /m {normal_m} bind .forceput + pdfopdict /l {normal_l} bind .forceput + pdfopdict /c {normal_c} bind .forceput + pdfopdict /v {normal_v} bind .forceput + pdfopdict /y {normal_y} bind .forceput + pdfopdict /re {normal_re} bind .forceput +} bdef + +/BT { + currentdict /TextSaveMatrix known { + ( **** Error: illegal nested BT operator detected.\n) + pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + ET_NO_TXT_KO %% Does not push any compositor actions + } if + + %% Bug #695897 see the explanation in /q defined above. + currentdict /qTextSaveMatrix known { + currentdict /qTextSaveMatrix get /TextSaveMatrix gput + ( **** Error: Illegal nested BT operator (inside a gsave) detected.\n) + pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + ET_NO_TXT_KO %% Does not push any compositor actions + } if + currentdict /n known { + currentdict end + /.W exch def + } if + currentdict /TextLineMatrix .knownget + { identmatrix pop TextMatrix identmatrix pop } + { matrix /TextLineMatrix gput matrix /TextMatrix gput } + ifelse + { showfirst } /Show gput + currentdict /TextSaveMatrix .knownget not { + matrix dup /TextSaveMatrix gput + } if currentmatrix pop settextmatrix + matrix /TextTempMatrix gput % see settextposition + + %% Set the special version of the path constructoin operators, so that + %% they will work as expected if they are illgeally present in a text block. + switch_to_text_marking_ops + + %% If needed, let the pdf14 device know we are in a BT condition. This + %% distinguishes BT from an Annotation /FreeText show command which also + %% can come into pdf_text_begin with an opacity not equal to 1. + PDFusingtransparency .currenttextknockout and { + .begintransparencytextgroup + } if + +} bdef + +/ET_NO_TXT_KO { + currentdict /TextSaveMatrix known { + TextRenderingMode 4 ge { .currentfilladjust 0 .setfilladjust clip .setfilladjust} if + newpath TextSaveMatrix setmatrix + currentdict /TextSaveMatrix undef + + % if we were in a W/W* context, grab the dict, undefine temp entry for it, + % and push it back on the dict stack + currentdict /.W known { + .W + currentdict /.W undef + begin + } if + } { + ( **** Error: Ignoring spurious ET operator.\n) + pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + } ifelse + %% Only switch back to the regular marking operations if we were not + %% in a nested text block (inside a gsave). + currentdict /qTextSaveMatrix known not { + switch_to_normal_marking_ops + } if +} bdef + +/ET { + //ET_NO_TXT_KO exec + % Group push occurred in pdf14_text_begin. + PDFusingtransparency .currenttextknockout and { + .endtransparencytextgroup + } if +} bdef + +/Tc { /TextSpacing gput { showfirst } /Show gput } bdef +/TL { /TextLeading gput } bdef +/Tr { dup .settextrenderingmode /TextRenderingMode gput { showfirst } /Show gput } bdef +/Ts { /TextRise gput settextstate } bdef +/Tw { /WordSpacing gput { showfirst } /Show gput } bdef +/Tz { + dup 0 eq { + (\n **** Error: Invalid 0.0 horizontal text scaling given for Tz\n) + ( Output may be incorrect.\n) pdfformaterror + pdfformaterror + pop 0.00000001 % handle invalid scale by using a really small value + }{ + 100 div + }ifelse /TextHScaling gput settextstate} bdef + +% ---------------- Font control ---------------- % + +% Test if the FontMatrix could transform a horizontal/vertical (depending +% on writing mode) advance vector in glyph space into one with a different +% direction in text space. +% - if FontMatrix = [a b c d tx ty], this condition translates to: +% b != 0 for horizontal writing mode +% c != 0 for vertical writing mode +% - when false, we automatically have wy/x == 0 in text space whenever wy/x == 0 +% in glyph space, and can avoid the slow method that is otherwise necessary +% to implement zeroing wy/x cf PDF Ref 5.3.3 "Text space details" + % Worker procedure for testing a single font matrix +/?FontMatrixNonHV { % ?horz <<fontdict>> -- ?horz ?nonhv + /FontMatrix .knownget { + 1 index { 1 } { 2 } ifelse get 0 ne + } { + //false + } ifelse +} bdef + % Worker procedure for recursive checking of font matrices +/?FontMatrixNonHV { % {self} ?horz <<font>> -- {self} ?horz ?nonhv + 2 copy //?FontMatrixNonHV exec { % check the font's own FontMatrix + pop pop //true + } { + % look for descendents/ components + pop % {self} ?horz <<font>> + dup /FontType get + dup 0 eq { % Type 0: look into FDepVector + pop /FDepVector get 2 index % {self} ?horz [fonts..] {testproc} + } { + 9 eq { % Type 9 (CIDFontType 0): check FDArray + /FDArray get //?FontMatrixNonHV + } { % all others: nothing more to check + pop {} {} + } ifelse + } ifelse + %stack: {self} ?horz [fonts..] {testproc} + //false 5 2 roll { % {testproc} false {self} ?horz <<subfont>> + 4 index exec { + 4 -1 roll pop //true 4 1 roll + exit + } if + } forall + %stack: {testproc} ?nonhv {self} ?horz + 4 2 roll exch pop + } ifelse +} bdef + % Main procedure +/?FontMatrixNonHV { % <<rootfont>> -- ?nonhv + //?FontMatrixNonHV exch + % determine WMode + dup /WMode .knownget { 0 eq } { //true } ifelse % {proc} <<rootfont>> ?horz + exch + % call the worker procedure + //?FontMatrixNonHV exec + exch pop exch pop +} bdef + +/Tf { % <font> <scale> Tf - + dup 0 eq { + (\n **** Error: Invalid 0.0 font scale given for Tf\n) + pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + pop 0.00000001 % handle invalid scale by using a really small value + } if + 1 index type /dicttype ne { + selectfont currentfont + } { + dup 1 eq { pop } { scalefont } ifelse + } + ifelse + dup ?FontMatrixNonHV dup FontMatrixNonHV ne { + /FontMatrixNonHV gput + { showfirst } /Show gput + } { + pop + } ifelse + /TextFont gput settextstate +} bdef + +% Copy a font, removing its FID. If changed is true, also remove +% the UniqueID and XUID, if any. If the original dictionary doesn't have +% the keys being removed, don't copy it. +/.copyfontdict % <font> <changed> .copyfontdict <dict> + { + 1 index /.OrigUniqueIDXUID .knownget + {//false} + { + 1 index /XUID .knownget + { + //true 1 index {0 gt and} forall + {//false}{pop //true} ifelse + } + { + //true + }ifelse + } ifelse + + { + 1 index /UniqueID .knownget + {[exch]}{//null} ifelse + } if + 3 1 roll + + 1 index /FID known + 1 index { 2 index /UniqueID known or 2 index /XUID known or } if + { % We add 1 to the length just in case the original + % didn't have a FID. + exch dup length 1 add dict exch + { % Stack: changed newfont key value + 1 index /FID eq + not { 3 copy put } if pop pop + } + forall exch + } + if pop + + 1 index //null eq + {exch pop} + {dup 3 -1 roll /.OrigUniqueIDXUID exch put} + ifelse + +} bdef + +% Insert a new Encoding or Metrics into a font if necessary. +% Return a possibly updated font, and a flag to indicate whether +% the font was actually copied. +/.updatefontmetrics { % <font> <Metrics|null> .updatefontmetrics + % <font'> <copied> + dup //null ne { + exch //true .copyfontdict dup /Metrics 4 -1 roll put //true + } { + pop //false + } ifelse +} bdef + +/.updatefontencoding { % <font> <Encoding|null> .updatefontencoding + % <font'> <copied> + dup //null ne { dup 2 index /Encoding get ne } { //false } ifelse { + exch //false .copyfontdict dup /Encoding 4 -1 roll put //true + } { + pop //false + } ifelse +} bdef + +% Duplicate keys in CharString dictionary according to GlyphMap: <</new_glyph /old_glyph>> +% We have to do this because PDF fonts can associate multiple widths with the same glyph +% but Metrics dictionary works by the glyph name. +/.update_charstring { % <font> <GlyphMap> .update_charstring <font'> <copied> + dup //null ne { + exch //true .copyfontdict % map font + dup dup /CharStrings get % map font font cstr + dup length % map font font cstr len + 4 index length add % map font font cstr len+map_len + dict copy dup begin % map font font cstr' + /CharStrings exch put % map font + exch { % font /new /old + currentdict exch .knownget { + def + } { + currentdict /.notdef .knownget { + def + } { + pop + % The font has no .notdef. + % Could not resolve the conflict, + % but either the font is invalid or the glyph name is never used. + } ifelse + } ifelse + } forall + end //true + } { + pop //false + } ifelse +} bdef + +/.updatefont { % <font> <Encoding|null> <Metrics|null> <GlyphMap|null> + % .updatefont <font'> <copied> + 4 2 roll % <Metrics|null> <GlyphMap> <font> <Encoding|null> + .updatefontencoding % <Metrics|null> <GlyphMap> <font> bool + 4 1 roll exch % bool <Metrics|null> <font> <GlyphMap> + .update_charstring % bool <Metrics|null> <font> bool + 3 1 roll exch % bool bool <font> <Metrics|null> + .updatefontmetrics % bool bool <font> bool + 4 2 roll or or % <font> is_copied +} bdef + +% ---------------- Text positioning ---------------- % + +/Td { + %% Bug #695950 - invalid parameter to 'Td' operator + %% Technically the parameters to Td are reals and can vary +/- 3.403x10^38 + %% However that would be kind of hard to validate, and in any event these + %% are somewhat unlikely numbers. We validate against +/- 2^32 instead. If this + %% should ever be a problem we'll have to modify this check. + %% Testing reveals that Acrobat clamps these out of range values to 0, it doesn't + %% simply ignore the error. + exch dup dup 4294967295 ge exch 4294967295 neg le or { + ( **** Error: invalid argument to Td, Treating argument as 0.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + pop 0 + } if + exch dup dup 4294967295 ge exch 4294967295 neg le or { + ( **** Error: invalid argument to Td, Treating argument as 0.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + pop 0 + } if + TextLineMatrix transform TextLineMatrix 4 2 getinterval astore pop + TextLineMatrix TextMatrix copy pop settextstate +} bdef +/TD { dup neg /TextLeading gput Td } bdef +/T* { 0 TextLeading neg Td } bdef +/Tm { + 5 index 0 ne 3 index 0 ne and + 5 index 0 ne 5 index 0 ne and or not { + ( **** Error: Invalid (0 scaling) text matrix for Tm ****\n) + pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + % handle invalid scale by using a really small value + 2 -6 roll pop pop pop pop + 0.00000001 0 0 0.00000001 + 6 -2 roll + }if + TextLineMatrix astore TextMatrix copy pop settextstate +} bdef + +% ---------------- Text painting ---------------- % + +/Vexch { + rootfont /WMode knownoget { 1 eq { exch } if } if +} bind def + +/textrenderingprocs [ % (0 is handled specially) + % Painting-only modes + { tf } { tS } { tB } { tn } + % Clipping modes + { gsave tf grestore tW } + { gsave tS grestore tW } + { gsave tB grestore tW } + { tW } +] readonly def + +/pdfwrite_textrenderingprocs [ + % Tr 0 - Fill + { setfillstate show } bind + % Tr 1 - Stroke + { currentlinewidth exch setstrokestate + % Need to set the stroke width to a value which gives the correct + % width under pdfwrite. Pdfwrite uses (in text mode) an identity + % CTM, so we need to calculate the stroke width which would result + % if the CTM had been unity. + currentlinewidth dup + matrix defaultmatrix idtransform TextSaveMatrix dtransform + abs 2 copy exch abs eq { + pop + }{ + % non-square scaling reduces to Text matrix in pdfwrite , so + % we can ignore it. (wrong answer, but consistent) + pop pop currentlinewidth + }ifelse setlinewidth + show setlinewidth} bind + % Tr 2 - Fill then Stroke + { currentlinewidth exch setstrokestate + % Need to set the stroke width to a value which gives the correct + % width under pdfwrite. Pdfwrite uses (in text mode) an identity + % CTM, so we need to calculate the stroke width which would result + % if the CTM had been unity. + currentlinewidth dup + matrix defaultmatrix idtransform TextSaveMatrix dtransform + abs 2 copy exch abs eq { + pop + }{ + % non-square scaling reduces to Text matrix in pdfwrite , so + % we can ignore it. (wrong answer, but consistent) + pop pop currentlinewidth + }ifelse setlinewidth + setfillstate show setlinewidth} bind + % Tr 3 - Neither fill nor stroke + { setfillstate show } bind + % + % pdfwrite emits all text inside a gsave/grestore pair. As + % a result we can't preserve any of the 'clip' modes, as the + % clip gets undone by the restore. We need to handle the clip + % separately. + % + % Tr 4 - Fill, add to clip + { gsave 0 .settextrenderingmode + setfillstate dup show grestore //true charpath } bind + % Tr 5 - Stroke, add to clip + { gsave 1 .settextrenderingmode + currentlinewidth dup + matrix defaultmatrix idtransform TextSaveMatrix dtransform + abs 2 copy exch abs eq { + pop + }{ + % non-square scaling reduces to Text matrix in pdfwrite , so + % we can ignore it. (wrong answer, but consistent) + pop pop currentlinewidth + }ifelse setlinewidth + setstrokestate dup show grestore + //true charpath} bind + % Tr 6 - Fill, stroke, add to clip + { gsave 2 .settextrenderingmode + currentlinewidth dup + matrix defaultmatrix idtransform TextSaveMatrix dtransform + abs 2 copy exch abs eq { + pop + }{ + % non-square scaling reduces to Text matrix in pdfwrite , so + % we can ignore it. (wrong answer, but consistent) + pop pop currentlinewidth + }ifelse setlinewidth + setstrokestate setfillstate dup show grestore + //true charpath} bind + % Tr 7 - Add to clip + { //true charpath} bind +] readonly def + +/setstrokeforTrpreservation { + % Check to see if the current device supports Tr + /PreserveTrMode /GetDeviceParam .special_op { + exch pop + }{ + //false + }ifelse + { + TextRenderingMode 1 eq TextRenderingMode 2 eq or + TextRenderingMode 5 eq TextRenderingMode 6 eq or or { + setstrokestate + % Need to set the stroke width to a value which gives the correct + % width under pdfwrite. Pdfwrite uses (in text mode) an identity + % CTM, so we need to calculate the stroke width which would result + % if the CTM had been unity. NOTE! Only interested in magnitudes, + % not signs. + currentlinewidth dup + matrix defaultmatrix idtransform TextSaveMatrix dtransform + abs 2 copy exch abs eq { + pop + }{ + % non-square scaling reduces to Text matrix in pdfwrite , so + % we can ignore it. (wrong answer, but consistent) + pop pop currentlinewidth + }ifelse + setlinewidth + } if + } if +} bind def + +% conditionally set fillstate to avoid multiple operations during text operators +/settextfillstate { + TextFillStateNeeded { setfillstate //false /TextFillStateNeeded gput } if +} bind def + +% If current path is not known to be valid, use the clip path +/TextTransSetup { % showarg path_valid TextTransSetup false showarg + % showarg path_valid false TextTransSetup prev_BM prev_opacity true showarg + % NB: if 'show' is used, then we use the clippath, but a smaller bbox is preferred + SoftMask //null ne { + dup mark /Subtype /Group /Isolated //true .dicttomark exch + { pathbbox } { gsave clippath pathbbox grestore } ifelse + .begintransparencygroup + } if + //null checkOPtrans { + % We need to push a non-isolated, non-knockout transparency group and + % perform the operation in CompatibleOverprint mode, then end the + % transparency group. Do the begintransparencygroup step here. + mark /Subtype /Group /Isolated //false .dicttomark exch + { pathbbox } { gsave clippath pathbbox grestore } ifelse + .begintransparencygroup + OPsavedict dup /saveBM .currentblendmode put /saveOA .currentopacityalpha put % save current values + /CompatibleOverprint .setblendmode 1 .setopacityalpha + } { + pop % discard path_valid boolean + } ifelse +} bind def + +/TextTransTeardown { % stack: path_valid + .currentblendmode /CompatibleOverprint eq { + % end the CompatibleOverprint group and restore the opacity and BM + .endtransparencygroup + OPsavedict dup /saveOA get .setopacityalpha /saveBM get .setblendmode + } if + SoftMask //null ne { + .endtransparencygroup + } if +} bind def + +/setshowstate + { WordSpacing 0 eq TextSpacing 0 eq and FontMatrixNonHV not and + { + % Check to see if the current device supports Tr + /PreserveTrMode /GetDeviceParam .special_op { + exch pop + }{ + //false + }ifelse + { + pdfwrite_textrenderingprocs TextRenderingMode get + } + { + TextRenderingMode 0 eq + currentfont /FontType get 3 eq TextRenderingMode 3 eq not + and or + { + { settextfillstate //false TextTransSetup show TextTransTeardown } + } { + TextRenderingMode 3 eq { + % Some PDF files execute 'tm' with a singular matrix, + % and then use the text rendering mode 3. + % The graphics library currently cannot handle text + % operations when the CTM is singular. + % Work around this here. + { + matrix currentmatrix dup dup + dup 0 get 0 eq 1 index 1 get 0 eq and { + dup dup 2 get 0 eq { 0 }{ 1 } ifelse 1 put + } if + dup 2 get 0 eq 1 index 3 get 0 eq and { + dup dup 1 get 0 eq { 3 }{ 2 } ifelse 1 put + } if + setmatrix + currentpoint + % don't worry about transparency for invisible text + 4 index settextfillstate show % Tr was set to graphic state. + moveto + setmatrix + % now set the currentpoint using the original matrix + gsave + setmatrix + //false charpath currentpoint newpath + grestore + moveto + } + } { + { //false charpath textrenderingprocs TextRenderingMode get exec } + } ifelse + } ifelse + } ifelse + } + { + % If we are doing a plain old fill, or no text at all, *or* we are going to + % a device supporting text rendering modes, then go through this route. + % + TextRenderingMode 0 eq TextRenderingMode 3 eq or + currentfont /FontType get 3 eq or + + /PreserveTrMode /GetDeviceParam .special_op { + exch pop + }{ + //false + }ifelse + % pdfwrite can't handle rendering modes which involve clipping + TextRenderingMode 4 lt and or + % Tr was set to graphic state. + { + FontMatrixNonHV { + { + % preserve current line width around possible stroke setup + currentlinewidth exch + setstrokeforTrpreservation + settextfillstate //false TextTransSetup + [ TextSpacing WordSpacing 3 index + { % str [... weach wword c undef|ythis xthis|undef + exch % will be removed, unless FontMatrix.xx/yy == 0 (FontMatrixNonHV already true) + Vexch pop % str [... weach wword c wthis + 3 index add exch 32 eq {1 index add} if % str [... weach wword w + 3 1 roll % str [... w weach wword + } + % for the "exch" removed or not below, see comment in pdf_font.ps::getfontmetrics + currentfont /FontMatrix get 0 3 Vexch pop get 0 ne { + 1 1 index length 1 sub getinterval cvx + } if + cshow pop pop ] + { xshow } { yshow } Vexch pop exec + setlinewidth TextTransTeardown + } + } { + WordSpacing 0 eq { + { + { setstrokeforTrpreservation + settextfillstate //false TextTransSetup TextSpacing 0 Vexch 3 -1 roll ashow TextTransTeardown + } + currentlinewidth + { setlinewidth } + 3 .execn + } + } { + TextSpacing 0 eq { + { % preserve current line width around possible stroke setup + { setstrokeforTrpreservation + settextfillstate //false TextTransSetup WordSpacing 0 Vexch 32 4 -1 roll .pdfwidthshow + TextTransTeardown + } + currentlinewidth + { setlinewidth } + 3 .execn + } + } { + { % preserve current line width around possible stroke setup + { setstrokeforTrpreservation + settextfillstate //false TextTransSetup WordSpacing 0 Vexch 32 + TextSpacing 0 Vexch 6 -1 roll .pdfawidthshow TextTransTeardown + } + currentlinewidth + { setlinewidth } + 3 .execn + } + } ifelse + } ifelse + } ifelse + } + { + { currentlinewidth exch + WordSpacing TextSpacing + % Implement the combination of t3 and false charpath. + % Note that we must use cshow for this, because we + % can't parse multi-byte strings any other way. + % Stack: string wword wchar + { + exch % will be removed, unless FontMatrixNonHV && FontMatrix.xx/yy == 0 + % Stack: str wword wchar ccode xthis ythis + Vexch pop currentpoint 6 3 roll + % Stack: str wthis xorig yorig wword wchar ccode + (x) dup 0 3 index put + % + % for devices which can handle the text rendering mode we don't want + % to decompose into paths, we want to do a 'show'. However pdfwrite + % can't handle clipping text, so we *do* want to do the clip path + % as a 'true charpath'. + % + /PreserveTrMode /GetDeviceParam .special_op { + exch pop + }{ + //false + }ifelse { + % NB we must have Tr at least 4 or the test above would have + % taken a different path, so we *know* the Tr involves clipping + % or we wouldn't be here. Subtract 4 from the Tr and preserve + % that mode, then do a charpath so the clip path gets set up + % correctly. + gsave TextRenderingMode 4 sub .settextrenderingmode + setstrokeforTrpreservation + setstrokestate settextfillstate //false TextTransSetup dup show grestore TextTransTeardown } if + //false charpath + 3 copy 32 eq { add } { exch pop } ifelse + % Stack: str wthis xorig yorig wword wchar ccode wextra + 7 -3 roll moveto add + 0 Vexch rmoveto pop + } + % for the "exch" removed or not below, see comment in pdf_font.ps::getfontmetrics + FontMatrixNonHV dup not exch { + currentfont /FontMatrix get 0 3 Vexch pop get 0 ne + } if { + 1 1 index length 1 sub getinterval cvx + } if + 4 -1 roll cshow pop pop + % + % If the device doesn't handle text rendering modes then we now have a suitable + % path, so call the correct routine to stroke/fill it (clip is handled when we + % get ET). + % + /PreserveTrMode /GetDeviceParam .special_op { + exch pop not + }{ + //true + }ifelse { + textrenderingprocs TextRenderingMode get exec + } if + setlinewidth + } + } + ifelse + } + ifelse /Show gput + } bdef +/showfirst { setshowstate Show } def + +/Tj { + { + //true /TextFillStateNeeded gput + 0 0 moveto Show settextposition + } + OFFlevels length 0 eq { + exec + } { + gsave get-gs-attrs nulldevice set-gs-attrs + exec + get-gs-attrs grestore set-gs-attrs + } ifelse + //true /TextFillStateNeeded gput +} bdef +/' { T* Tj } bdef +/" { exch Tc exch Tw T* Tj } bdef +/TJ { + { 0 0 moveto { + dup type /stringtype eq { + Show + } { -1000 div + currentfont /ScaleMatrix .knownget { 0 get mul } if + 0 Vexch rmoveto + } ifelse + } forall settextposition + //true /TextFillStateNeeded gput + } + OFFlevels length 0 eq { + exec + } { + gsave get-gs-attrs nulldevice set-gs-attrs + exec + get-gs-attrs grestore set-gs-attrs + } ifelse + //true /TextFillStateNeeded gput +} bdef + +% NB: We don't need clippath when filling or stroking +/tf { + //true /TextFillStateNeeded gput + settextfillstate //true TextTransSetup currentpoint fill TextTransTeardown moveto +} bdef +/tn { currentpoint newpath moveto } bdef % Obsolete, never used. +% For stroking characters, temporarily restore the graphics CTM so that +% the line width will be transformed properly. +/Tmatrix matrix def +/tS + { + currentfont /FontType get 3 eq + { + settextfillstate //true TextTransSetup currentpoint fill TextTransTeardown moveto + } + { + setstrokestate //true TextTransSetup + currentpoint //Tmatrix currentmatrix TextSaveMatrix setmatrix .swapcolors stroke TextTransTeardown .swapcolors + setmatrix moveto + } ifelse + //true /TextFillStateNeeded gput + } bdef + +% Handle transparency the same as /B operator +/tB { + PDFusingtransparency { + % knockout trans group around the filled and stroked object + mark + /Isolated //true /Knockout //true + .dicttomark + 1 .setopacityalpha + % While text will always have a currentpoint, strokepath seems to mess with it. + % we get the currentpoint, then use moveto to restore it for pathbbox + gsave currentpoint strokepath moveto pathbbox grestore + .begintransparencygroup + gsave tf grestore tS + .endtransparencygroup + } { + gsave tf grestore tS + } ifelse + //true /TextFillStateNeeded gput +} bdef + +% This does the wrong thing if there have been multiple text operations +% within a single BT/ET pair, but it's a start. +/tW { } bdef + +%% split a string containing \r or \n into multiple strings) +%% \r\n is also handled. +%% (string) .splitstring (string1) (string2) ..... +/.splitstring { + 0 exch %% start_index string + dup length 1 sub %% start_index string (string length - 1) + 0 1 3 -1 roll %% start_index string 0 1 (string_length -1) + { %% start_index string loop_index + dup %% start_index string loop_index loop_index + 2 index exch get %% start_index string loop_index character + dup 10 eq exch 13 eq or { %% start_index string loop_index + dup 3 index %% start_index string loop_index loop_index start_index + sub %% start_index string loop_index (loop_index - start_index) + dup 0 gt { %% \r\n will give us a 0 length string so ignore it + 3 index exch %% start_index string loop_index start_index (loop_index - start_index) + 3 index 3 1 roll %% start_index string loop_index string start_index (loop_index - start_index) + getinterval %% start_index string loop_index sub_string + 4 1 roll %% sub_string start_index string loop_index + 1 add + 3 -1 roll %% sub_string string loop_index start_index + pop exch %% sub_string loop_index string %% loop_index becomes new start_index + }{ + pop %% start_index string loop_index + 1 add 3 1 roll exch %% loop_index string start_index + pop %% loop_index string %% loop_index becomes new start index + } ifelse + }{ + pop %% start_index string + } ifelse + } for + %% start_index string + dup length 2 index sub dup %% start_index string (length - start_index) (length - start_index) + 0 gt { %% we have characters left %% start_index string (length - start_index) + 3 -1 roll exch %% string start_index (length - start_index) + getinterval %% sub_string + } { + pop pop pop + } ifelse +} bind def + +% Text formatting and painting for the AcroForm field without appearance streams. +/Tform { % <MaxLen> (V) <Ff> <Q> Tform - + clippath pathbbox 4 2 roll pop pop % MaxLen (V) Ff Q dx dy + currentfont /ScaleMatrix .knownget { 3 get } { 1 } ifelse % MaxLen (V) Ff Q dx dy yy + + currentfont /FontBBox .knownget + not + { + currentfont dup /FontType get + 0 eq + { + /FDepVector get 0 get /FontBBox get} + { + pop null % force an error + } ifelse + }if + dup 1 get neg exch 3 get % MaxLen (V) Ff Q dx dy yy desc asc + + dup 0 ne { div } { pop pop 0 } ifelse % MaxLen (V) Ff Q dx dy yy desc/asc + 1 index mul % MaxLen (V) Ff Q dx dy yy desc/asc*yy + + %% Check if this is declared as a multiine text widget, and if it is + %% check that the string has at least 2 characters, so we can split it + %% into at least two strings.... + %% Bug #687498.pdf has a multiline text widget with an empty string (amongst + %% many other problems with this file). + 5 index 16#1000 and 0 ne 7 index length 1 gt and{ % multiline + %% First lets see how many lines we have, make a copy of the string + 6 index mark exch .splitstring % MaxLen (V) Ff Q dx dy yy desc/asc*yy [ () () ... + counttomark 1 add 4 add index % MaxLen (V) Ff Q dx dy yy desc/asc*yy [ () () ... count Q + dup 0 eq { + pop % remove extra copy of Q + counttomark dup 2 add 2 add index exch div % MaxLen (V) Ff Q dx dy yy desc/asc*yy [ () () ... line_height + counttomark 1 add 1 roll % MaxLen (V) Ff Q dx dy yy desc/asc*yy line_height [ () () ... + 2 0 moveto % start at bottom left of rect + counttomark 1 1 3 -1 roll { % for every string + pop gsave + <FEFF> anchorsearch { + pop + } if + Show grestore % pop loop counter, show string (saving current point) + counttomark 1 add index 0 exch rmoveto % get line height, move that amount vertically. + } for + pop pop + }{ + 1 eq { + counttomark dup 2 add 2 add index exch div % MaxLen (V) Ff Q dx dy yy desc/asc*yy [ () () ... line_height + counttomark 1 add 1 roll % MaxLen (V) Ff Q dx dy yy desc/asc*yy line_height [ () () ... + 2 0 moveto % start at bottom left of rect + counttomark 1 1 3 -1 roll { % for every string + pop gsave % pop loop counter + <FEFF> anchorsearch { + pop + } if + dup stringwidth pop counttomark 5 add index % get rectangle width + exch sub 2 div 0 rmoveto % subtract width of string, divide by 2 move that amount + Show grestore % show string + counttomark 1 add index 0 exch rmoveto % get line height, move that amount vertically. + } for + pop pop + }{ + counttomark dup 2 add 2 add index exch div % MaxLen (V) Ff Q dx dy yy desc/asc*yy [ () () ... line_height + counttomark 1 add 1 roll % MaxLen (V) Ff Q dx dy yy desc/asc*yy line_height [ () () ... + 2 0 moveto % start at bottom left of rect + counttomark 1 1 3 -1 roll { % for every string + pop gsave % pop loop counter + <FEFF> anchorsearch { + pop + } if + dup stringwidth pop counttomark 5 add index % get rectangle width + exch sub 2 sub 0 rmoveto % subtract width of string,move that amount + Show grestore % show string + counttomark 1 add index 0 exch rmoveto % get line height, move that amount vertically. + } for + pop pop + }ifelse + }ifelse + 8 { pop } repeat + } { + 5 index 16#1000000 and 0 ne { % comb + 8 { pop } repeat (Combed form fields are not yet implemented.) = + } { % plain text + 3 1 roll sub add 2 div % MaxLen (V) Ff Q dx (dy-yy+desc)/2 + 0 exch moveto % MaxLen (V) Ff Q dx + 1 index 0 ne { + 3 index + <FEFF> anchorsearch { + pop + } if + stringwidth pop % MaxLen (V) Ff Q dx w + sub exch 1 eq { 2 div } { 2 sub } ifelse % MaxLen (V) Ff (dx-w)/2 + 0 rmoveto % MaxLen (V) Ff + pop % MaxLen (V) + } { + pop pop pop % MaxLen (V) + 2 0 rmoveto + } ifelse + exch pop + <FEFF> anchorsearch { + pop + } if + Show % - + } ifelse + } ifelse +} bdef + +end readonly put % GS_PDF_ProcSet + +.setglobal |