summaryrefslogtreecommitdiff
path: root/Master/tlpkg/tlgs/Resource/Init/pdf_ops.ps
diff options
context:
space:
mode:
Diffstat (limited to 'Master/tlpkg/tlgs/Resource/Init/pdf_ops.ps')
-rw-r--r--Master/tlpkg/tlgs/Resource/Init/pdf_ops.ps1815
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