Plan 9 from Bell Labs’s /usr/web/sources/plan9/sys/src/cmd/gs/lib/pdf_draw.ps

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


%    Copyright (C) 1994, 2000 Aladdin Enterprises.  All rights reserved.
% 
% This software is provided AS-IS with no warranty, either express or
% implied.
% 
% This software is distributed under license and may not be copied,
% modified or distributed except as expressly authorized under the terms
% of the license contained in the file LICENSE in this distribution.
% 
% For more information about licensing, please refer to
% http://www.ghostscript.com/licensing/. For information on
% commercial licensing, go to http://www.artifex.com/licensing/ or
% contact Artifex Software, Inc., 101 Lucas Valley Road #110,
% San Rafael, CA  94903, U.S.A., +1(415)492-9861.

% $Id: pdf_draw.ps,v 1.98 2005/10/05 14:37:59 ray Exp $
% pdf_draw.ps
% PDF drawing operations (graphics, text, and images).

/.setlanguagelevel where { pop 2 .setlanguagelevel } if
.currentglobal true .setglobal
/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
GS_PDF_ProcSet begin
pdfdict begin

% For simplicity, we use a single interpretation dictionary for all
% PDF graphics operations, even though this is too liberal.
/drawopdict 100 dict def

% ================================ Graphics ================================ %

% ---------------- Functions ---------------- %

% Note that resolvefunction converts a PDF Function to a PostScript Function;
% resolve*fnproc converts a PDF function to a PostScript procedure.
% We need to process all required and optional parameters to resolve any
% use of indirect references.

/fnrdict mark
  0 { .resolvefn0 }
  2 { .resolvefn2 }
  3 { .resolvefn3 }
  4 { .resolvefn4 }
.dicttomark readonly def

/.resolvefn0 {
  dup length 1 add dict .copydict	% make room for DataSource
  % now resolve any indirect references
  dup /Size 2 copy knownoget { put } { pop pop } ifelse
  dup /BitsPerSample 2 copy knownoget { put } { pop pop } ifelse
  dup /Order 2 copy knownoget { put } { pop pop } ifelse
  dup /Encode 2 copy knownoget { put } { pop pop } ifelse
  dup /Decode 2 copy knownoget { put } { pop pop } ifelse
  
		% Don't lose our place in PDFfile.
  PDFfile fileposition exch
  dup true resolvestream
		% The stream isn't positionable, so read all the data now.
		% Stack: filepos fndict stream
  1 index /Range get length 2 idiv 2 index /BitsPerSample get mul
  2 index /Size get { mul } forall
  7 add 8 idiv string
  1 index exch readstring pop exch closefile
		% Stack: filepos fndict data
  exch dup /DataSource 4 -1 roll put
  exch PDFfile exch setfileposition
} bdef

/.resolvefn2 {
  dup length dict .copydict
  dup /C0 2 copy knownoget { put } { pop pop } ifelse
  dup /C1 2 copy knownoget { put } { pop pop } ifelse
  dup /N 2 copy knownoget { put } { pop pop } ifelse
} bdef

/.resolvefn3 {
  dup length dict .copydict
  dup /Bounds 2 copy knownoget { put } { pop pop } ifelse
  dup /Encode 2 copy knownoget { put } { pop pop } ifelse
  dup /Functions 2 copy oget mark exch dup {
    oforce .resolvefn
  } forall
  counttomark -1 roll astore exch pop put
} bdef

/.resolvefn4 {
  PDFfile fileposition exch             % filepos fndict
  dup true resolvestream                % filepos fndict stream
  exch dup length dict copy             % filepos stream fndict2
  dup /Function undef                   % filepos stream fndict2
  exch dup token not {
    () /rangecheck cvx signalerror
  } if
  exch token {
    /rangecheck cvx signalerror
  } if
		% Use .bind to avoid idiom recognition.
  .bind
  1 index /Function 3 -1 roll put
  exch PDFfile exch setfileposition
} bdef

/.resolvefn {		% <fndict> .resolvefn <fndict'>
  dup length dict .copydict
  dup /Domain 2 copy knownoget { put } { pop pop } ifelse
  dup /Range 2 copy knownoget { put } { pop pop } ifelse
  dup /FunctionType oget //fnrdict exch get exec
} bdef

/resolvefunction {	% <fndict> resolvefunction <function>
  .resolvefn
  PDFDEBUG { pdfdict /PDFSTEPcount .knownget { 1 le } { true } ifelse { (%Function: ) print dup === flush } if } if
} bdef

/resolvefnproc {	% <fndict> resolvefnproc <proc>
  resolvefunction .buildfunction
} bdef

/resolveidfnproc {	% <fndict> resolveidfnproc <proc>
  dup /Identity eq { pop { } } { resolvefnproc } ifelse
} bdef

/resolvedefaultfnproc {	% <fndict> <default> resolved'fnproc <proc>
  1 index /Default eq { exch pop } { pop resolveidfnproc } ifelse
} bdef

% ---------------- Shadings ---------------- %

/shrdict mark
  /ColorSpace {
    resolvecolorspace
  }
  /Function {
    dup type /dicttype eq {
      resolvefunction
    } {
      [ exch { oforce resolvefunction } forall ]
    } ifelse
  }
.dicttomark readonly def

/resolveshading {	% <shadingstream> resolveshading <shading>
  PDFfile fileposition exch
  mark exch {
    oforce //shrdict 2 index .knownget { exec } if
  } forall .dicttomark
  dup /ShadingType get 4 ge {
    dup dup true resolvestream
		% Make a reusable stream so that the shading doesn't
		% reposition PDFfile at unexpected times.
    /ReusableStreamDecode filter /DataSource exch put
  } if exch PDFfile exch setfileposition
} bdef
/resolvesh {		% <shname> resolveshading <shading>
  Page /Shading rget {
    resolveshading
  } {
    null
  }ifelse
} bdef

% ---------------- Halftones ---------------- %

/spotfunctions mark
  /Round {
    abs exch abs 2 copy add 1 le {
      dup mul exch dup mul add 1 exch sub 
    } {
      1 sub dup mul exch 1 sub dup mul add 1 sub
    } ifelse
  }
  /Diamond {
    abs exch abs 2 copy add .75 le {
      dup mul exch dup mul add 1 exch sub
    } {
      2 copy add 1.23 le {
	.85 mul add 1 exch sub
      } {
	1 sub dup mul exch 1 sub dup mul add 1 sub
      } ifelse
    } ifelse
  }
  /Ellipse {
    abs exch abs 2 copy 3 mul exch 4 mul add 3 sub dup 0 lt {
      pop dup mul exch .75 div dup mul add 4 div 1 exch sub
    } {
      dup 1 gt {
	pop 1 exch sub dup mul exch 1 exch sub
	.75 div dup mul add 4 div 1 sub
      } {
	.5 exch sub exch pop exch pop
      } ifelse
    } ifelse
  }
  /EllipseA { dup mul .9 mul exch dup mul add 1 exch sub }
  /InvertedEllipseA { dup mul .9 mul exch dup mul add 1 sub }
  /EllipseB { dup 5 mul 8 div mul exch dup mul exch add sqrt 1 exch sub }
  /EllipseC { dup mul .9 mul exch dup mul add 1 exch sub }
  /InvertedEllipseC { dup mul .9 mul exch dup mul add 1 sub }
  /Line { exch pop abs neg }
  /LineX { pop }
  /LineY { exch pop }
  /Square { abs exch abs 2 copy lt { exch } if pop neg }
  /Cross { abs exch abs 2 copy gt { exch } if pop neg }
  /Rhomboid { abs exch abs 0.9 mul add 2 div }
  /DoubleDot { 2 {360 mul sin 2 div exch } repeat add }
  /InvertedDoubleDot { 2 {360 mul sin 2 div exch } repeat add neg }
  /SimpleDot { dup mul exch dup mul add 1 exch sub }
  /InvertedSimpleDot { dup mul exch dup mul add 1 sub }
  /CosineDot { 180 mul cos exch 180 mul cos add 2 div }
  /Double { exch 2 div exch 2 { 360 mul sin 2 div exch } repeat add }
  /InvertedDouble {
    exch 2 div exch 2 { 360 mul sin 2 div exch } repeat add neg
  }
.dicttomark readonly def

/htrdict mark
  1 { .resolveht1 }
  5 { .resolveht5 }
	% We don't support types 6, 10, or 16 yet.
.dicttomark readonly def

/.resolveht1 {
  mark exch {
    oforce
    1 index /SpotFunction eq {
      dup type /nametype eq
	{ //spotfunctions exch get } { resolvefnproc }
      ifelse
    } {
      1 index /TransferFunction eq {
	resolveidfnproc
      } if
    } ifelse
  } forall .dicttomark
} bdef

/.resolveht5 {
  mark exch {
    oforce dup type /dicttype eq { resolvehalftone } if
  } forall .dicttomark
} bdef

/resolvehalftone {	% <dict> resolvehalftone <halftone>
  dup /HalftoneType get
  dup //htrdict exch .knownget {
    exch pop exec
  } {
    (\n\n   **** Unsupported HalftoneType ) pdfformaterror
    =string cvs pdfformaterror (. ***\n\n) pdfformaterror
    /resolvehalftone cvx /unregistered signalerror
  } ifelse
} bdef

% ---------------- Graphics state management ---------------- %

/cmmatrix matrix def
drawopdict begin
			% Graphics state stack
  /q { q } def
  /Q { Q } def
			% Graphics state setting
  /cm { { //false upath } stopped {
	  pop	% discard 'false' (upath failed, probably no currentpoint).
	  //cmmatrix astore concat
	} {
	  % update the CTM, then uappend.
	  7 1 roll //cmmatrix astore concat
	  newpath { mark exch uappend } stopped 
	  cleartomark
	} ifelse
      } def
  /i { 1 .min setflat } def
  /J /setlinecap load def
  /d /setdash load def
  /j /setlinejoin load def
  /w /setlinewidth load def
  /M { 1 .max setmiterlimit } bdef
  /gs { gs } def
end

% Each entry in this dictionary is
%	<gsres> <value> -proc- <gsres>
/gsbg {
  /BGDefault load resolvedefaultfnproc setblackgeneration
} bdef
/gsucr {
  /UCRDefault load resolvedefaultfnproc setundercolorremoval
} bdef
/gstr {
  dup type /arraytype eq {
    { oforce /TRDefault load resolvedefaultfnproc } forall
    setcolortransfer
  } {
    /TRDefault load resolvedefaultfnproc settransfer
  } ifelse
} bdef
/gsparamdict mark
  /SA { setstrokeadjust }
  /OP { 1 index /op known not { dup op } if OP }
	% The PDF 1.3 specification says that the name /Default is only
	% recognized for {BG,UCR,TR}2.  However, PDF 1.3 files produced
	% by Adobe Acrobat Distiller 4.0 for Windows use the name /Default
	% with the older keys, so we have to implement this.
  /BG { 1 index /BG2 known { pop } { gsbg } ifelse }
  /UCR { 1 index /UCR2 known { pop } { gsucr } ifelse }
  /TR { 1 index /TR2 known { pop } { gstr } ifelse }
  /HT {
    dup /Default eq {
      pop .setdefaulthalftone
    } {
	%****** DOESN'T IMPLEMENT THE STREAM CASE YET ******
      resolvehalftone sethalftone
    } ifelse
    % the transfer function may dependent on the halftone, so make sure
    % it is set if included in the graphic state (otherwise this is
    % subject to order of a dictionary forall, which is unpredictable)
    dup /TR2 .knownget {
      dup /Default eq { oforce gsparamdict /TR2 get exec } { pop } ifelse
    } {
      dup /TR .knownget {
        /dup /Default eq { oforce gsparamdict /TR get exec } { pop } ifelse
      } if
    } ifelse
  }
  /HTP {
	% HTP may be present even if this isn't a DPS interpreter.
    /sethalftonephase where { pop aload pop sethalftonephase } { pop } ifelse
  }
	% PDF 1.3
  /Font { aload pop Tf }
  /LW { setlinewidth }
  /LC { setlinecap }
  /LJ { setlinejoin }
  /ML { 1 .max setmiterlimit }
  /D { aload pop setdash }
  /RI { ri }
  /op { op }
  /OPM { OPM }
  /BG2 { gsbg }
  /UCR2 { gsucr }
  /TR2 { gstr }
  /FL { 1 .min setflat }
  /SM {
	% SM may be present even if this is only a Level 2 interpreter.
    /setsmoothness where { pop setsmoothness } { pop } ifelse
  }
	% PDF 1.4
	% All of these require the "transparency" feature in the interpreter.
  /ca { ca }
  /CA { CA }
  /SMask { gssmask }
  /AIS { AIS }
  /BM { BM }
  /TK { TK }
.dicttomark readonly def
/gs {			% <gsres> gs -
  Page /ExtGState rget {
	% We keep the dictionary on the stack during the forall so that
	% keys that interact with each other have access to it.
    dup {
      oforce exch gsparamdict exch .knownget { exec } { pop } ifelse
    } forall pop
  } if
} bdef

% ------ Transparency support ------ %

/gssmask {
  dup /None eq PDFusingtransparency not or {
    pop null
  } {
	% Preprocess the SMask value into a parameter dictionary for
	% .begintransparencymaskgroup, with added /BBox and /Draw keys.
    mark exch		% Stack: mark smaskdict
    dup /S oget /Subtype exch 3 2 roll
			% Stack: mark ... smaskdict
    dup /BC knownoget {
      dup /Background exch 4 2 roll
      gsave
        1 index /G oget /Group oget /CS knownoget {
	  csresolve dup setgcolorspace csput
	} if
        aload pop setcolor [ currentgray ]
      grestore
      /GrayBackground exch 3 2 roll
    } if
    dup /TR knownoget {
      resolveidfnproc /TransferFunction exch 3 2 roll
    } if    
    dup /G oget dup /BBox oget /BBox exch 4 2 roll
    /.execmaskgroup cvx 2 packedarray cvx /Draw exch 3 2 roll
    pop .dicttomark
  } ifelse SMask
} bdef

% This procedure is called to actually render the soft mask.
/.execmaskgroup {	% <masknum> <paramdict> <formdict> .execmaskgroup -
	% Save our place in PDFfile, and do a gsave to avoid resetting
	% the color space.
  currentcolorspace 4 1 roll
  PDFfile fileposition 4 1 roll
	% We have to select the group's color space so that the
	% background color will be interpreted correctly.
  dup /Group oget /CS knownoget { csresolve dup setgcolorspace csput } if
  exch dup /BBox get aload pop .begintransparencymaskgroup {
    dup /Resources knownoget { oforce } { 0 dict } ifelse
    exch false resolvestream
    .execgroup .endtransparencymask
  } stopped {
    .discardtransparencymask stop
  } if
  PDFfile exch setfileposition 
  setcolorspace
} bdef
% Paint a Form+Group XObject, either for a transparency mask or for a Do.
/.execgroup {		% <resdict> <stream> .execgroup -
  gsave //nodict begin
  null SMask
  1 .setopacityalpha 1 .setshapealpha
  0 .inittransparencymask 1 .inittransparencymask
  /Compatible .setblendmode
	% Execute the body of the Form, similar to DoForm.
  pdfopdict .pdfruncontext
  end grestore
} bdef

/.beginformgroup {	% groupdict bbox .beginformgroup -
  exch mark exch			% bbox mark groupdict
  dup /CS knownoget { csresolve setgcolorspace } if
  dup /I knownoget { /Isolated exch 3 2 roll } if
  dup /K knownoget { /Knockout exch 3 2 roll } if
  pop .dicttomark
		% Stack: bbox paramdict
  exch aload pop
  .begintransparencygroup
} bdef

% .paintgroupform implements the Form PaintProc in the case where the
% Form XObject dictionary includes a Group key.  See .paintform below.
/.paintgroupform {	% <resdict> <stream> <formdict> .paintgroupform -
  dup /Group oget exch /BBox oget
		% Stack: resdict stream groupdict bbox
  .beginformgroup {
    .execgroup
  } stopped {
    .discardtransparencygroup stop
  } if .endtransparencygroup
} bdef

% Make an ImageType 103 (soft-masked) image.
/makesoftmaskimage {	% <datasource> <imagemask> <SMask> makesoftmaskimage
			%   <datasource> <imagemask>, updates currentdict =
			%   imagedict
		% See the ImageType 3 case of makemaskimage below.
		% SMask is a stream, another Image XObject.
		% Stack: datasource imagemask(false) smaskstreamdict
  PDFfile fileposition exch
  dup /Matte knownoget { /Matte exch def } if
  dup length dict makeimagedict pop
		% In order to prevent the two data sources from being
		% aliased, we need to make at least one a reusable stream.
		% We pick the mask, since it's smaller (in case we need to
		% read all its data now).
		% Stack: datasource imagemask(false) savedpos
		% maskdict is currentdict
  /DataSource DataSource mark
    /Intent 1
    /AsyncRead true
  .dicttomark .reusablestreamdecode def
  PDFfile exch setfileposition
  currentdict end currentdict end
  5 dict begin
  /ImageType 103 def
  /DataDict exch def
  dup /InterleaveType 3 put
  DataDict /Matte knownoget {
    /Matte exch def
  } if
  AlphaIsShape { /ShapeMaskDict } { /OpacityMaskDict } ifelse exch def
  /ColorSpace DataDict /ColorSpace get def
} bdef

% ---------------- Color setting ---------------- %

/01_1 [0 1] readonly def
/01_3 [0 1 0 1 0 1] readonly def
/01_4 [0 1 0 1 0 1 0 1] readonly def

% The keys here are resolved (PostScript, not PDF) color space names.
/csncompdict mark
  /DeviceGray { pop 1 }
  /DeviceRGB { pop 3 }
  /DeviceCMYK { pop 4 }
  /CIEBasedA { pop 1 }
  /CIEBasedABC { pop 3 }
  /ICCBased { 1 oget /N oget }
  /Separation { pop 1 }
  /DeviceN { 1 oget length }
.dicttomark readonly def

/csrdict mark
  /DeviceGray { }
  /DeviceRGB { }
  /DeviceCMYK { }
  /CalGray {
    1 oget 6 dict begin
    dup /Gamma knownoget {
      /exp load 2 packedarray cvx /DecodeA exch def
    } if
    dup /BlackPoint knownoget { /BlackPoint exch def } if
    dup /WhitePoint knownoget {
      dup /WhitePoint exch def
      dup /MatrixA exch def
      /RangeLMN [ 3 2 roll { 0 exch } forall ] def
    } if
    /PDFColorSpace exch def [ /CIEBasedA currentdict end ]
  }
  /CalRGB {
    1 oget 6 dict begin
    dup /Gamma knownoget {
      [ exch { /exp load 2 packedarray cvx } forall
      ] /DecodeABC exch def
    } if
    dup /Matrix knownoget { /MatrixABC exch def } if
    dup /BlackPoint knownoget { /BlackPoint exch def } if
    dup /WhitePoint knownoget { /WhitePoint exch def } if
    /PDFColorSpace exch def [ /CIEBasedABC currentdict end ]
  }
  /CalCMYK {
    pop /DeviceCMYK		% not defined by Adobe
  }
  /Lab {
    1 oget 6 dict begin
    dup /Range knownoget not { [-100 100 -100 100] } if
    [0 100 null null null null] dup 2 4 -1 roll putinterval
    /RangeABC exch def
    /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind] def
    /MatrixABC [1 1 1 1 0 0 0 0 -1] def
    dup /BlackPoint knownoget { /BlackPoint exch def } if
    dup /WhitePoint knownoget { /WhitePoint exch def } {
      (   **** Warning: Lab colorspace is missing WhitePoint.\n)
      pdfformaterror
      /WhitePoint [0.9505 1 1.089] def
    } ifelse
    % scaling function g() for DecodeLMN construction
    { dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse }
    /DecodeLMN [
      % Store white point implicitly inside procedures.
      [ 3 index aload pop WhitePoint 0 get /mul .systemvar ] cvx bind
      [ 4 index aload pop WhitePoint 1 get /mul .systemvar ] cvx bind
      [ 5 index aload pop WhitePoint 2 get /mul .systemvar ] cvx bind
    ] def pop
    /PDFColorSpace exch def [ /CIEBasedABC currentdict end ]
  }
  /ICCBased {
    dup 1 get type /dicttype ne {	% don't resolve more than once
    PDFfile fileposition exch
    dup dup 1 oget
    mark exch { oforce } forall .dicttomark
    dup dup true resolvestream
    /ReusableStreamDecode filter /DataSource exch put
    1 exch put
    exch PDFfile exch setfileposition
    % Resolve alternate color space
    dup 1 get			% Get colorspace dictionary
    dup /Alternate .knownget	% Check for alternate color space
    { oforce resolvecolorspace /Alternate exch put }	% resolve and replace
    { pop }			% remove colorspace dictionary
    ifelse
    } if
  } bind
  /Separation {
    aload pop exch oforce resolvecolorspace
                % Contrary to PDF manuals up to v.1.5, Acrobat Distiller 3.01
                % can use /Identity name here instead of a function.
    exch oforce resolveidfnproc
    4 array astore
  }
  /DeviceN {
    0 4 getinterval                    % ignore attributes
    aload pop 3 -1 roll oforce	       % resolve names array
    [ exch { oforce } forall ]	       % resolve each of the names
    3 -1 roll oforce resolvecolorspace
    3 -1 roll oforce resolvefnproc
    4 array astore
  }
  /Indexed {
    aload pop 3 -1 roll oforce resolvecolorspace
		% Stack: /Indexed hival lookup basespace
		% If the underlying space is a Lab space, we must scale
		% the output of the lookup table as part of DecodeABC.
    dup dup type /arraytype eq { 0 get } if /CIEBasedABC eq {
      dup 1 get /DecodeLMN known {
	1 get dup length dict copy
	begin /DecodeABC [ 0 2 4 {
	  RangeABC 1 index 1 add get RangeABC 2 index get sub /mul load
	  RangeABC 3 index get /add load
	  DecodeABC 6 -1 roll 2 idiv get [ 6 1 roll aload pop ] cvx
	} for ] def
	/RangeABC //01_3 def
	currentdict end /CIEBasedABC exch 2 array astore
      } if
    } if
    3 1 roll
    oforce dup type /stringtype ne {
		% The color lookup table is a stream.
		% Get its contents.  Don't lose our place in PDFfile.
		% Stack: /Indexed basespace hival lookup
	PDFfile fileposition 5 1 roll true resolvestream
		% Stack: filepos /Indexed basespace hival lookupstream
	1 index 1 add
		% Stack: filepos /Indexed basespace hival lookupstream len
	3 index
	  dup dup type /arraytype eq { 0 get } if
	  //csncompdict exch get exec mul
	string readstring pop
		% Stack: filepos /Indexed basespace hival table
	5 -1 roll PDFfile exch setfileposition
    }
    if 4 array astore
		% Replace the PDFColorSpace with the Indexed space if needed.
    dup 1 get
    dup type /arraytype eq {
      dup length 2 ge {
	dup 1 get type /dicttype eq {
	  dup 1 get /PDFColorSpace known {
	    dup 1 get /PDFColorSpace 3 index put
	  } if
	} if
      } if
    } if pop
  }
  /Pattern {
    dup type /nametype ne {
      dup length 1 gt {
	1 oget resolvecolorspace
	/Pattern exch 2 array astore
      } if
    } if
  }
.dicttomark readonly def

/cssubst {		% <csname> cssubst <cspace'> true
			% <csname> cssubst false
  dup resolvecolorspace
  dup 1 index ne { exch pop true } { pop pop false } ifelse
} bdef

/csnames mark
  /DeviceGray dup  /DeviceRGB dup  /DeviceCMYK dup  /Pattern dup
.dicttomark readonly def
/csresolve {		% <csresourcename> csresolve <cspace>
  dup type /nametype ne {
    (\n   **** Warning: CS/cs (setcolorspace) operand not a name: ) pdfformaterror
    dup stderrfile dup 3 -1 roll write==only flushfile
    ( ****\n) pdfformaterror
    dup type /arraytype eq {	% Adobe InDesign + PDF Library has array
      resolvecolorspace
    } if
  } {
    dup Page /ColorSpace rget {
      exch pop resolvecolorspace
    } {
      //csnames 1 index known not { /undefined cvx signalerror } if
    } ifelse
  } ifelse
} bdef
/resolvecolorspace {	% <cspace> resolvecolorspace <cspace'>
  dup dup type /arraytype eq { 0 get } if
  //csrdict exch .knownget
  {
    exec dup type /nametype ne { dup length 1 eq { 0 get } if } if
  } {
    dup type /nametype eq { csresolve } { csset exch pop } ifelse
  } ifelse
} bdef

/scresolve {	% <c0> ... scresolve <multi>
		% We can't really make sc[n] and SC[N] work, because
		% the color space information isn't available at
		% conversion time; so we hack it by assuming that
		% all the operands on the stack are used, and that
		% if the top operand is a name, it's a Pattern resource.
  dup type /nametype eq
    { Page /Pattern rget { resolvepattern } { null } ifelse }
  if
  dup type /dicttype eq {
		% Check the PaintType, if any (shading patterns don't
		% have one).
    dup /PaintType knownoget { 2 eq } { false } ifelse
  } {
    .pdfcount 1 gt
  } ifelse
} bdef

/.pdfpaintproc {         % <patdict> <resdict> .pdfpaintproc -
  PDFDEBUG { pdfdict /PDFSTEPcount .knownget { 1 le } { true } ifelse { (%Begin PaintProc) print dup === flush } if } if
	% For uncolored patterns, we have to unbind the current
	% color and color space before running the PaintProc.
	% There's no harm in doing this for colored patterns,
	% so for simplicity, we always do it.
  PDFfile fileposition 3 1 roll
  q
  null sc1 null SC1

  % save old value of pdfemptycount on opstack, set to new value
  pdfemptycount /pdfemptycount count 3 sub def 3 1 roll
  exch false resolvestream pdfopdict .pdfruncontext
  % restore pdfemptycount
  /pdfemptycount exch def

  Q
  PDFDEBUG { pdfdict /PDFSTEPcount .knownget { 1 le } { true } ifelse { (%End PaintProc) print dup === flush } if } if
  PDFfile exch setfileposition
} bdef

/resolvepattern {	% <patternstreamdict> resolvepattern <patterndict>
		% Don't do the resolvestream now: just capture the data
		% from the file if necessary.
  dup length dict copy
  dup /FilePosition .knownget {
    1 index /File get dup fileposition 3 1 roll
		% Stack: dict savepos pos file
    dup 3 -1 roll setfileposition
    dup 3 index /Length oget

    dup 65535 le {
      string readstring pop
    } {
      () /SubFileDecode filter /ReusableStreamDecode filter
    } ifelse
		% Stack: dict savepos file string
    3 1 roll exch setfileposition
    1 index /File 3 -1 roll put
    dup /FilePosition undef
  } if
  dup /Shading knownoget {
    resolveshading 1 index /Shading 3 -1 roll put
  } if
  dup /PaintProc [
		% Bind the resource dictionary into the PaintProc.
    2 index /Resources knownoget { oforce } { 0 dict } ifelse
    /.pdfpaintproc cvx
  ] cvx put
  PDFDEBUG { pdfdict /PDFSTEPcount .knownget { 1 le } { true } ifelse { (%Pattern: ) print dup === flush } if } if
} bdef

drawopdict begin
  /g { /DeviceGray cssubst { cs sc1 } { g } ifelse } bdef
  /rg { /DeviceRGB cssubst { cs sc* } { rg } ifelse } bdef
  /k { k } bdef
  /cs { csresolve cs } bdef
  /sc { scresolve { sc* } { sc1 } ifelse } bdef
  /scn /sc load def
  /G { /DeviceGray cssubst { CS SC1 } { G } ifelse } bdef
  /RG { /DeviceRGB cssubst { CS SC* } { RG } ifelse } bdef
  /K { K } bdef
  /CS { csresolve CS } bdef
  /ri { ri } bdef
  /SC { scresolve { SC* } { SC1 } ifelse } bdef
  /SCN /SC load def
end

% ---------------- Paths ---------------- %

drawopdict begin
			% Path construction
  /m /moveto load def
  /l /lineto load def
  /c /curveto load def
  /v { currentpoint 6 2 roll curveto } def
  /y { 2 copy curveto } def
  /re {
   4 2 roll moveto  exch dup 0 rlineto  0 3 -1 roll rlineto  neg 0 rlineto
   closepath
  } def
  /h /closepath load def
			% Path painting and clipping
  /n { n } def
  /S { S } def
  /s { s } def
  /f { f } def
  /f* { f* } def
  /B { B } def
  /b { b } def
  /B* { B* } def
  /b* { b* } def
  /W { W } def
  /W* { W* } def
  /sh { setfillstate resolvesh shfill } def
end

% ---------------- XObjects ---------------- %

/xobjectprocs mark		% <dict> -proc- -
  /Image { DoImage }
  /Form { DoForm }
  /PS { DoPS }
.dicttomark readonly def

% Note that the keys in defaultdecodedict are resolved (PostScript, not PDF)
% color space names.
/defaultdecodedict mark
  /DeviceGray { pop //01_1 } bind
  /DeviceRGB { pop //01_3 } bind
  /DeviceCMYK { pop //01_4 } bind
  /CIEBasedA { 1 get /RangeA knownoget not { //01_1 } if } bind
  /CIEBasedABC { 1 get /RangeABC knownoget not { //01_3 } if } bind
  /ICCBased {
     1 oget dup /Range knownoget {
       exch pop
     }{
       /N get [ exch {0 1} repeat ] readonly
     } ifelse
  } bind
  /Separation { pop //01_1 } bind
  /DeviceN {
    1 oget length [ exch {0 1} repeat ] readonly
  } bind
  /Indexed {
    pop [ 0 1 BitsPerComponent bitshift 1 sub ]
  } bind
.dicttomark readonly def

/checkaltimage {	% <resdict> checkaltimage <resdict[']>
  Printed {
    dup /Alternates knownoget {
      {
	dup /DefaultForPrinting knownoget {
	  {
	    /Image oget exch pop exit
	  } {
	    pop
	  } ifelse
	} {
	  pop
	} ifelse
      } forall
    } if
  } if
} bdef

/makeimagedict {	% <resdict> <newdict> makeimagedict <imagemask>
			% On return, newdict' is currentdict
  begin
  /Width 2 copy oget def
  /Height 2 copy oget def
		% Handle missing BitsPerComponent later.
  /BitsPerComponent 2 copy knownoget { def } { pop } ifelse
  /Interpolate 2 copy knownoget { def } { pop } ifelse
  makeimagekeys
} bdef
/makeimagekeys {	% <resdict> makeimagekeys <imagemask>
		% newdict is currentdict
		% Assumes Width, Height, BPC, Interpolate already copied.
  /ImageType 1 def
  /ImageMatrix Width 0 0
		% Handle 0-height images specially.
    Height dup 0 eq { pop 1 } if neg 0 1 index neg
    6 array astore def
  dup /ImageMask knownoget dup { and } if {
		% Image mask
		% Decode is required for the PostScript image operators.
                % AI8 writes bogus decode array [0 1 0 0 0 0 0 0]
    /Decode 2 copy knownoget { 0 2 getinterval } { //01_1 } ifelse def
		% BitsPerComponent is optional for masks.
    /BitsPerComponent 2 copy known { pop } { 1 def } ifelse
    true
  } {
		% Opaque image
    dup /ColorSpace oget resolvecolorspace /ColorSpace exch def
		% Decode is required for the PostScript image operators.
    /Decode 2 copy knownoget not {
      ColorSpace //defaultdecodedict
      ColorSpace dup type /arraytype eq { 0 get } if get exec
    } if def
    false
  } ifelse
		% Even though we're going to read data,
		% pass false to resolvestream so that
		% it doesn't try to use Length (which may not be present).
  exch false resolvestream /DataSource exch def
} bdef

/DoImage {
  checkaltimage dup length 6 add dict
  1 index /SMask knownoget { 1 index exch /SMask exch put } if
  1 index /Mask knownoget { 1 index exch /Mask exch put } if
  makeimagedict doimagesmask
} bdef
/makemaskimage {	% <datasource> <imagemask> <Mask> makemaskimage
			%   <datasource> <imagemask>, updates currentdict =
			%   imagedict
  dup type /arraytype eq {
    /ImageType 4 def
                % Check that every element of the Mask is an integer.
    //false 1 index {
      type /integertype ne or
    } forall {
      (\n   **** Warning: Some elements of Mask array are not integers.\n)
      pdfformaterror
      [ exch { 0.5 add cvi } forall ]  % following AR4, 5, 6 implementation
    } if
	% Check elements of array are within 0::(2**BitsPerComponent)-1
	% This is a PostScript error, but AR ignores Mask in that case
    2 BitsPerComponent exp cvi 1 sub //false 2 index {
      % stack: max_value result_bool value
      dup 0 lt exch 3 index gt or or
    } forall exch pop {
      (\n   **** Warning: Some elements of Mask array are out of range.\n)
      pdfformaterror
      pop /ImageType 1 def		% revert to non-masked image
    } {
      /MaskColor exch def
    } ifelse
  } {
		% Mask is a stream, another Image XObject.
		% Stack: datasource imagemask(false) maskstreamdict
    PDFfile fileposition exch
    dup length dict makeimagedict pop
		% In order to prevent the two data sources from being
		% aliased, we need to make at least one a reusable stream.
		% We pick the mask, since it's smaller (in case we need to
		% read all its data now).
		% Stack: datasource imagemask(false) savedpos
		% maskdict is currentdict
    /DataSource DataSource mark
      /Intent 1
      /AsyncRead true
    .dicttomark .reusablestreamdecode def
    PDFfile exch setfileposition
    currentdict end currentdict end
    5 dict begin
    /ImageType 3 def
    /InterleaveType 3 def
    /DataDict exch def
    /MaskDict exch def
    /ColorSpace DataDict /ColorSpace get def
  } ifelse
} bdef
/doimagesmask { % <imagemask> doimagesmask -
  PDFusingtransparency { currentdict /SMask knownoget } { false } ifelse {
    .begintransparencymaskimage
    PDFfile fileposition exch
    gsave //nodict begin
    null /SoftMask gput
    1 .setopacityalpha 1 .setshapealpha
    0 .inittransparencymask 1 .inittransparencymask
    /Compatible .setblendmode
    DoImage
    end grestore
    PDFfile exch setfileposition
    0 .endtransparencymask
    << /Subtype /Group /Isolated true >> 0 0 1 1 .begintransparencygroup
    doimage
    .endtransparencygroup
  } {
    doimage
  } ifelse
} bdef
/doimage {	% <imagemask> doimage -
		% imagedict is currentdict, gets popped from dstack
  DataSource exch
  PDFusingtransparency {
    currentdict /SMask knownoget
  } {
    false
  } ifelse {
    makesoftmaskimage
  } {
    currentdict /Mask knownoget {
      makemaskimage
    } if
  } ifelse
		% Stack: datasource imagemask
   % image and imagemask can be redefined in gs_init.ps to tweak interpolation
   % after device-specific files are run. Don't bind them here.
   { currentdict end setfillstate /imagemask }
   { ColorSpace setgcolorspace currentdict end setfillblend /image }
  ifelse
  .systemvar stopped {
    pop
    $error /errorname get dup /ioerror eq {
      pop (\n   **** Warning: File has insufficient data for an image.\n)
      pdfformaterror
    } {
      (\n   **** Warning: File encountered ')
      exch 40 string cvs concatstrings
      (' error while processing an image.\n) concatstrings
      pdfformaterror
    } ifelse
  } if
		% Close the input stream, unless it is PDFfile or
		% PDFsource.
  dup dup PDFfile eq exch PDFsource eq or { pop } { closefile } ifelse
} bdef

/.paintform {	% <formdict> <resdict> <stream> .paintform -
  3 -1 roll dup /Group known PDFusingtransparency and {
    .paintgroupform
  } {
    pop pdfopdict .pdfruncontext
  } ifelse
} bdef

/DoForm {
    % Adobe 2nd edition of the PDF 1.3 spec makes /FormType
    % and /Matrix keys optional. Cope with the missing keys.
  begin <<
  currentdict /FormType known not { /FormType 1 } if
  currentdict /Matrix   known not { /Matrix { 1 0 0 1 0 0 } cvlit } if
  currentdict end { oforce } forall
  >>
  dup [ 2 index /Resources knownoget { oforce } { 0 dict } ifelse
  3 index false /resolvestream cvx
  /.paintform cvx
  ] cvx /PaintProc exch put
    % Adjust pdfemptycount since we have an extra dictionary on the stack
  pdfemptycount exch
  /pdfemptycount where pop count 2 sub /pdfemptycount exch put
  q execform Q			% gsave / grestore around the Form
    % Restore pdfemptycount
  /pdfemptycount where pop exch /pdfemptycount exch put
} bdef

/_dops_save 1 array def

/DoPS {
  DOPS
   {
     //_dops_save 0 save put
     true resolvestream cvx exec
     //_dops_save 0 get restore
   }
   { pop }
  ifelse
} bdef

currentdict /_dops_save undef

drawopdict begin
  /Do {
    setfillblend
    PDFfile fileposition exch
    dup Page /XObject rget { 
      exch pop dup /Subtype get xobjectprocs exch get
		% Don't leave extra objects on the stack while executing
		% the definition of the form.
      3 -1 roll 2 .execn
    } {
		% This should cause an error, but Acrobat Reader can
		% continue, so we do too.
      (   **** Undefined XObject resource: ) 
      exch =string cvs concatstrings (\n) concatstrings
      pdfformaterror
    } ifelse
    PDFfile exch setfileposition
  } bdef
end

% ---------------- In-line images ---------------- %

% Undo the abbreviations in an in-line image dictionary.
% Note that we must look inside array values.
% /I is context-dependent.
/unabbrevkeydict mark
  /BPC /BitsPerComponent  /CS /ColorSpace  /D /Decode  /DP /DecodeParms
  /F /Filter  /H /Height  /I /Interpolate  /IM /ImageMask  /W /Width
.dicttomark readonly def
/unabbrevvaluedict mark
  /AHx /ASCIIHexDecode  /A85 /ASCII85Decode  /CC /CalCMYK
  /CCF /CCITTFaxDecode  /CG /CalGray  /CR /CalRGB
  /DCT /DCTDecode  /CMYK /DeviceCMYK  /Fl /FlateDecode
  /G /DeviceGray  /RGB /DeviceRGB
  /I /Indexed  /LZW /LZWDecode  /RL /RunLengthDecode
.dicttomark readonly def
/unabbrevtypedict mark
  /nametype {
    //unabbrevvaluedict 1 index .knownget { exch pop } if
  }
  /arraytype {
    dup 0 1 2 index length 1 sub {
      2 copy get unabbrevvalue put dup
    } for pop
  }
.dicttomark readonly def
/unabbrevvalue {	% <obj> unabbrevvalue <obj'>
  oforce //unabbrevtypedict 1 index type .knownget { exec } if
} bdef

drawopdict begin
  /BI { mark } bdef
  /ID {
    counttomark 2 idiv dup 7 add dict begin {
      exch //unabbrevkeydict 1 index .knownget { exch pop } if
      exch unabbrevvalue def
    } repeat pop
    /IDFlag true def  % flag for stream processing.
    /File PDFsource def
    currentdict makeimagekeys doimage	
	% The Adobe documentation says that the data following ID
	% consists of "lines", and some PDF files (specifically, some files
	% produced by PCL2PDF from Visual Software) contain garbage bytes
	% between the last byte of valid data and an EOL.
        % Some files (PDFOUT v3.8d by GenText) have EI immediately following
        % the stream. Some have no EOL and garbage bytes.
        % Therefore, we skip all bytes before EI or EOL 
    0
      { PDFsource read not { //true exit } if
        dup 10 eq 1 index 13 eq or
          { pop PDFsource token pop /EI ne exit
          }
        if
        exch 69 eq 1 index 73 eq and { //false exit } if  % 'EI'
      }
    loop
    exch pop
      { /ID cvx /syntaxerror signalerror
      }
    if
  } bdef
end

% ================================ Text ================================ %

drawopdict begin
			% Text control
  /BT { BT } def
  /ET { ET } def
  /Tc { Tc } def
  /TL { TL } def
  /Tr { Tr } def
  /Ts { Ts } def
  /Tw { Tw } def
  /Tz { Tz } def
			% Text positioning
  /Td { Td } def
  /TD { TD } def
  /Tm { Tm } def
  /T* { T* } def
			% Text painting
  /Tj { Tj } def
  /' { ' } def
  /" { " } def
  /TJ { TJ } def
end

% ============================== Annotations ============================== %



% Get and normalize an annotation's rectangle.
/annotrect {		% <annot> annotrect <x> <y> <w> <h>
  /Rect get aload pop
  exch 3 index sub dup 0 lt { dup 5 -1 roll add 4 1 roll neg } if
  exch 2 index sub dup 0 lt { dup 4 -1 roll add 3 1 roll neg } if
} bdef

% Set an annotation color.
/annotsetcolor {	% <annot> annotsetcolor -
  /C knownoget { aload pop setrgbcolor } { 0 setgray } ifelse
} bdef

% Draw the border.  Currently, we ignore requests for beveling, and we
% don't round the corners of rectangles.
/strokeborder {		% <annot> <width> <dash> strokeborder -
  1 index 0 ne {	% do not draw if border width is 0
    gsave
    2 index annotsetcolor
    0 setdash dup setlinewidth
    exch annotrect
    2 { 4 index sub 4 1 roll } repeat
    2 { 4 index 0.5 mul add 4 1 roll } repeat
    rectstroke pop
    grestore
  } {
    pop pop pop
  } ifelse
} bdef

% Draw an annotation border.
/drawborder {		% <annot> drawborder -
  gsave
  dup /BS knownoget {
    dup /W knownoget not { 1 } if
    [] 2 index /S knownoget {
      /D eq { 2 index /D knownoget not { [3] } if exch pop } if
    } if 3 -1 roll pop strokeborder
  } {
    dup /Border knownoget {
      dup 2 get
      exch dup length 3 gt { 3 get } { pop [] } ifelse
      strokeborder
    } {
      1 [] strokeborder
    } ifelse
  } ifelse
  grestore
} bdef

%
%   The PDF annotation F (flags) integer is bit encoded.
%   Bit 1 (LSB) Invisible:  1 --> Do not display if no handler.
%         Note:  We have no handlers but we ignore this bit.
%   Bit 2 Hidden:  1 --> Do not display.  We will not display if this bit is set.
%   Bit 3 Print:  1 --> Display if printing.  We will display if this bit set
%         (and not hidden) and Printed is true
%   Bit 4 NoZoom:  1 --> Do not zoom annotation even if image is zoomed.
%   Bit 5 NoRotate:  1 --> Do not rotate annotation even if image is rotated.
%   Bit 6 NoView:  0 --> Display if this is a 'viewer'.  We will display
%         if this bit is not set (and not hidden) and Printed is false
%   Bit 7 Read Only - 1 --> No interaction.  We ignore this bit
%
/annotvisible {			% <annot> annotvisible <visible>
  /F knownoget not { 0 } if 		% Get flag value
  dup 2 and 0 eq  			% Check hidden flag
  exch dup 4 and 0 ne Printed and	% Check print flag
  exch 32 and 0 eq Printed not and	% Check noview flag
  or					% Combine print and view
  and 					% Combine with 'hidden' flag test
} bdef

/drawwidget {			% <scalefactor> <annot> drawwidget -
  dup /AP knownoget {
    false
    [/N /R /D] {
	% stack: scale annot appearance false key
      dup 3 index exch known {
	exch pop true exit 
      } if
      pop
    } forall
	% stack: scale annot appearance key true
	% stack: scale annot appearance false
    dup {
      pop
      oget
		% Acrobat Distiller produces files in which this Form
		% XObject lacks Type and Subtype keys.  This is illegal,
		% but Acrobat Reader accepts it.  The only way we can
		% tell whether this is a Form or a set of sub-appearances
		% is by testing for the stream Length key.
      dup /Length known {
      		% If this is a form then simply use it
        true
      } {
        1 index /AS knownoget not {
      		% If we do not have AS then use any appearance
  	  { exch pop oforce exit } forall true
        } { 
		% Stack: annot Ndict AS
		% Get the specified appearance.  If no appearance, then
		% display nothing - set stack = false.
	  knownoget
        } ifelse
      } ifelse
    } {
      exch pop	% discard useless AP dictionary
    } ifelse

		% Stack: scale annot appearance true
		% Stack: scale annot false
    {
      		% Draw appearance
                % Initialize graphic following "7.4.4 Appearance Streams"
      q graphicsbeginpage textbeginpage
      1 index annotrect pop pop translate
      2 index dup scale		% Apply scale factor
      DoForm Q
    } if
  } if pop pop
} bdef

%  For stamp object we have to determine the size of the output rectangle
%  and the size of the BBox for the stamp image.  From these we calculate
%  a scale factor for drawing the stamp.
/calcstampscale {		% <annot> calcstampscale scale
  dup /Rect known {
    dup annotrect 4 -2 roll pop pop	 % get width height size in user space
    3 -1 roll /AP knownoget {
      /N knownoget {
	dup /Matrix knownoget {
	  % transform /Annot /Rect xwidth to Form space
	  4 -2 roll 3 -1 roll dtransform 3 -1 roll
	} if
	/BBox knownoget {
	  exch pop	% discard y height
	  aload pop pop exch pop sub	% BBox width
	  dup 0 eq {
            (   **** Warning: /BBox has zero width which is not allowed.\n)
            pdfformaterror
            pop pop 1 1		% 0 width -- revert to unity scaling
          } if
	  div		% scale x widths
	  dup 0 lt { neg } if		% get magnitude
	} {
	  pop pop 1		% default to unity scaling
	} ifelse			% if we have /BBox
      } {
	pop pop 1
      } ifelse			% if we have /N
    } {
      pop pop 1
    } ifelse			% if we have /AP
  } {
    (   **** Warning: /Annot dict is missing required /Rect entry.\n)
    pdfformaterror
    pop 1
  } ifelse
} bdef

/drawlink {			% <annot> drawlink -
  dup drawborder dup calcstampscale exch drawwidget
} bdef

% Draw an annotation.
/drawannottypes mark
  /Link { drawlink } bind
.dicttomark readonly def
/drawannot {		% <annot> drawannot -
  dup annotvisible {
    gsave
    dup dup /Subtype get //drawannottypes exch .knownget {
      exec
    } {
      dup calcstampscale exch drawwidget	% Use drawwidget for everything else
    } ifelse			% type known
    grestore
  } if pop			% annotvisible
} bdef
currentdict /drawannottypes undef

end			% pdfdict
end			% GS_PDF_ProcSet
.setglobal

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.