mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 01:42:17 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			3429 lines
		
	
	
		
			121 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			3429 lines
		
	
	
		
			121 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {
 | ||
| Reads EPS files
 | ||
| 
 | ||
| License: The same modified LGPL as the Free Pascal RTL
 | ||
|          See the file COPYING.modifiedLGPL for more details
 | ||
| 
 | ||
| AUTHORS: Felipe Monteiro de Carvalho
 | ||
| 
 | ||
| Documentation: http://www.tailrecursive.org/postscript/postscript.html
 | ||
| 
 | ||
| Good reference: http://atrey.karlin.mff.cuni.cz/~milanek/PostScript/Reference/PSL2e.html
 | ||
| }
 | ||
| unit epsvectorialreader;
 | ||
| 
 | ||
| {$mode objfpc}{$H+}
 | ||
| 
 | ||
| {.$define FPVECTORIALDEBUG_PATHS}
 | ||
| {.$define FPVECTORIALDEBUG_COLORS}
 | ||
| {.$define FPVECTORIALDEBUG_ROLL}
 | ||
| {.$define FPVECTORIALDEBUG_CODEFLOW}
 | ||
| {.$define FPVECTORIALDEBUG_INDEX}
 | ||
| {.$define FPVECTORIALDEBUG_DICTIONARY}
 | ||
| {.$define FPVECTORIALDEBUG_CONTROL}
 | ||
| {.$define FPVECTORIALDEBUG_ARITHMETIC}
 | ||
| {.$define FPVECTORIALDEBUG_CLIP_REGION}
 | ||
| {$define FPVECTORIAL_IMAGE_DICTIONARY_DEBUG}
 | ||
| 
 | ||
| interface
 | ||
| 
 | ||
| uses
 | ||
|   Classes, SysUtils, Math, contnrs,
 | ||
|   fpimage, fpcanvas,
 | ||
|   fpvectorial, fpvutils;
 | ||
| 
 | ||
| type
 | ||
|   TPSTokenType = (ttComment, ttFloat);
 | ||
| 
 | ||
|   TPSTokens = TFPList;// TPSToken;
 | ||
| 
 | ||
|   TPSToken = class
 | ||
|   public
 | ||
|     StrValue: string;
 | ||
|     FloatValue: double;
 | ||
|     IntValue: Integer;
 | ||
|     BoolValue: Boolean;
 | ||
|     Line: Integer; // To help debugging
 | ||
|     constructor Create; virtual;
 | ||
|     procedure CopyDataFrom(ASrc: TPSToken; AKeepTokenType: Boolean); virtual;
 | ||
|     function Duplicate: TPSToken; virtual;
 | ||
|     procedure PrepareIntValue;
 | ||
|   end;
 | ||
| 
 | ||
|   TCommentToken = class(TPSToken)
 | ||
|   public
 | ||
|   end;
 | ||
| 
 | ||
|   { TArrayToken }
 | ||
| 
 | ||
|   TArrayToken = class(TPSToken)
 | ||
|   public
 | ||
|     CurElementStr: string;
 | ||
|     ArrayData: TPSTokens;
 | ||
|     Parent: TArrayToken; // nil indicates a top-level array
 | ||
|     constructor Create; override;
 | ||
|     destructor Destroy; override;
 | ||
|     function Duplicate: TPSToken; override;
 | ||
|     procedure FreeToken(AToken, AData: Pointer);
 | ||
|     procedure AddNumber(ANumber: Double);
 | ||
|     procedure AddIdentityMatrix;
 | ||
|     function GetNumber(AIndex: Integer): Double;
 | ||
|     procedure ResolveOperators;
 | ||
|   end;
 | ||
| 
 | ||
|   { TProcedureToken }
 | ||
| 
 | ||
|   TProcedureToken = class(TPSToken)
 | ||
|   public
 | ||
|     Levels: Integer; // Used to count groups inside groups and find the end of a top-level group
 | ||
|     Childs: TPSTokens;
 | ||
|     Parsed: Boolean;
 | ||
|     constructor Create; override;
 | ||
|     destructor Destroy; override;
 | ||
|   end;
 | ||
| 
 | ||
|   TETType = (ettNamedElement, ettOperand, ettOperator, ettDictionary,
 | ||
|     ettVirtualMemorySnapshot, ettLiteralString, ettRawData, ettInvalid);
 | ||
| 
 | ||
|   { TExpressionToken }
 | ||
| 
 | ||
|   TExpressionToken = class(TPSToken)
 | ||
|   public
 | ||
|     ETType: TETType;
 | ||
|     SubstituteETType: TETType; // utilized when the token is substituted
 | ||
|     constructor Create; override;
 | ||
|     function IsExpressionOperand: Boolean;
 | ||
|     procedure PrepareFloatValue;
 | ||
|     procedure CopyDataFrom(ASrc: TPSToken; AKeepTokenType: Boolean); override;
 | ||
|     function Duplicate: TPSToken; override;
 | ||
|   end;
 | ||
| 
 | ||
|   { TDictionaryToken }
 | ||
| 
 | ||
|   // TDictionaryToken is utilized for <..> dictionary definitions
 | ||
|   // Do not confuse it with a directionary reference
 | ||
|   // which is a TExpressionToken with ETType=ettDictionary!
 | ||
|   TDictionaryToken = class(TPSToken)
 | ||
|   public
 | ||
|     Childs: TPSTokens;
 | ||
|     //
 | ||
|     Names: TStringList;
 | ||
|     Values: TPSTokens; // does not contain own references, don't free contents!
 | ||
|     constructor Create; override;
 | ||
|     destructor Destroy; override;
 | ||
|     procedure TransformToListOfNamedValues();
 | ||
|   end;
 | ||
| 
 | ||
|   TPostScriptScannerState = (ssSearchingToken, ssInComment, ssInDefinition,
 | ||
|     ssInGroup, ssInExpressionElement, ssInArray, ssInDictionary,
 | ||
|     ssReadingRawDataStart, ssReadingRawData);
 | ||
| 
 | ||
|   { TGraphicState }
 | ||
| 
 | ||
|   TGraphicState = class
 | ||
|   public
 | ||
|     Color: TFPColor;
 | ||
|     TranslateX, TranslateY: Double;
 | ||
|     ScaleX, ScaleY: Double;
 | ||
|     ClipPath: TPath;
 | ||
|     ClipMode: TvClipMode;
 | ||
|     OverPrint: Boolean; // not used currently
 | ||
|     ColorSpaceName: string;
 | ||
|     // Current Transformation Matrix
 | ||
|     //
 | ||
|     // See http://www.useragentman.com/blog/2011/01/07/css3-matrix-transform-for-the-mathematically-challenged/
 | ||
|     // This has 6 numbers, which means this:
 | ||
|     //                      (a  c  e)
 | ||
|     // [a, b, c, d, e, f] = (b  d  f)
 | ||
|     //                      (0  0  1)
 | ||
|     // scale(Num)  => a,d=Num  rest=0
 | ||
|     // scaleX(Num) => a=Num  d=1 rest=0
 | ||
|     // scaleY(Num) => a=1  d=Num rest=0
 | ||
|     // TranslateX(Num) => a,d=1 e=Num rest=0
 | ||
|     // TranslateY(Num) => a,d=1 f=Num rest=0
 | ||
|     // Translate(NumX,NumY)  => a,d=1 e=NumX f=NumY rest=0
 | ||
|     // skewX(TX) => a=1 b=0 c=tan(TX) d=1 rest=0
 | ||
|     // skewY(TY) => a=1 b=tan(TY) c=0 d=1 rest=0
 | ||
|     // skew(TX,TY) => a=1 b=tan(TY) c=tan(TX) d=1 rest=0
 | ||
|     // rotate(T) => a=cos(T) b=sin(T) c=-sin(T) d=cos(T) rest=0
 | ||
|     CTM: TArrayToken;
 | ||
|     //
 | ||
|     PenWidth: Integer;
 | ||
|     //
 | ||
|     constructor Create;
 | ||
|     function Duplicate: TGraphicState;
 | ||
|     procedure CTMNeeded;
 | ||
|     procedure SetCTM(ANewCTM: TArrayToken);
 | ||
|   end;
 | ||
| 
 | ||
|   { TPSTokenizer }
 | ||
| 
 | ||
|   TPSTokenizer = class
 | ||
|   public
 | ||
|     Tokens: TPSTokens;
 | ||
|     FCurLine: Integer;
 | ||
|     constructor Create(ACurLine: Integer = -1);
 | ||
|     destructor Destroy; override;
 | ||
|     procedure ReadFromStream(AStream: TStream);
 | ||
|     procedure DebugOut();
 | ||
|     function IsValidPostScriptChar(AChar: Byte): Boolean;
 | ||
|     function IsPostScriptSpace(AChar: Byte): Boolean;
 | ||
|     function IsEndOfLine(ACurChar: Byte; AStream: TStream): Boolean;
 | ||
|   end;
 | ||
| 
 | ||
|   { TvEPSVectorialReader }
 | ||
| 
 | ||
|   TvEPSVectorialReader = class(TvCustomVectorialReader)
 | ||
|   private
 | ||
|     Stack: TObjectStack;
 | ||
|     GraphicStateStack: TObjectStack; // TGraphicState
 | ||
|     Dictionary: TStringList;
 | ||
|     ExitCalled: Boolean;
 | ||
|     CurrentGraphicState: TGraphicState;
 | ||
|     //
 | ||
|     procedure DebugStack();
 | ||
|     //
 | ||
|     procedure RunPostScript(ATokens: TPsTokens; AData: TvVectorialPage; ADoc: TvVectorialDocument);
 | ||
|     //
 | ||
|     procedure ExecuteProcedureToken(AToken: TProcedureToken; AData: TvVectorialPage; ADoc: TvVectorialDocument);
 | ||
|     procedure ExecuteOperatorToken(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken);
 | ||
|     function  ExecuteArithmeticAndMathOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecutePathConstructionOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteGraphicStateOperatorsDI(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteGraphicStateOperatorsDD(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteDictionaryOperators(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteMiscellaneousOperators(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteStackManipulationOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteControlOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecutePaintingOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken): Boolean;
 | ||
|     function  ExecuteImageOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken): Boolean;
 | ||
|     function  ExecuteDeviceSetupAndOutputOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteArrayOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteStringOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteFileOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteResourceOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteVirtualMemoryOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     function  ExecuteErrorOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
|     //
 | ||
|     procedure PostScriptCoordsToFPVectorialCoords(AParam1, AParam2: TPSToken; var APosX, APosY: Double);
 | ||
|     procedure PostScriptCoordsToFPVectorialCoordsWithCGS(AParam1, AParam2: TPSToken; var APosX, APosY: Double);
 | ||
|     function DictionarySubstituteOperator(ADictionary: TStringList; var ACurToken: TPSToken): Boolean;
 | ||
|   public
 | ||
|     { General reading methods }
 | ||
|     Tokenizer: TPSTokenizer;
 | ||
|     constructor Create; override;
 | ||
|     Destructor Destroy; override;
 | ||
|     procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
 | ||
|   end;
 | ||
| 
 | ||
| implementation
 | ||
| 
 | ||
| type
 | ||
|   TStackAccess = class(TObjectStack)
 | ||
|   end;
 | ||
| 
 | ||
| var
 | ||
|   FPointSeparator: TFormatSettings;
 | ||
| 
 | ||
| { TDictionaryToken }
 | ||
| 
 | ||
| constructor TDictionaryToken.Create;
 | ||
| begin
 | ||
|   inherited Create;
 | ||
| 
 | ||
|   Childs := TPSTokens.Create;
 | ||
|   Names := TStringList.Create;
 | ||
|   Values := TPSTokens.Create;
 | ||
| end;
 | ||
| 
 | ||
| destructor TDictionaryToken.Destroy;
 | ||
| begin
 | ||
|   Names.Free;
 | ||
|   Values.Free;
 | ||
|   //
 | ||
|   Childs.Free;
 | ||
| 
 | ||
|   inherited Destroy;
 | ||
| end;
 | ||
| 
 | ||
| procedure TDictionaryToken.TransformToListOfNamedValues;
 | ||
| var
 | ||
|   i: Integer;
 | ||
|   CurToken: TPSToken;
 | ||
| begin
 | ||
|   for i := 0 to Childs.Count-1 do
 | ||
|   begin
 | ||
|     CurToken := TPSToken(Childs.Items[i]);
 | ||
|     if i mod 2 = 0 then
 | ||
|     begin
 | ||
|       Names.Add(CurToken.StrValue);
 | ||
|     end
 | ||
|     else
 | ||
|     begin
 | ||
|       Values.Add(Pointer(CurToken));
 | ||
|     end;
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| { TArrayToken }
 | ||
| 
 | ||
| constructor TArrayToken.Create;
 | ||
| begin
 | ||
|   inherited Create;
 | ||
|   ArrayData := TPSTokens.Create;
 | ||
| end;
 | ||
| 
 | ||
| destructor TArrayToken.Destroy;
 | ||
| begin
 | ||
|   //ArrayData.ForEachCall(@FreeToken, nil);
 | ||
|   ArrayData.Free;
 | ||
|   inherited Destroy;
 | ||
| end;
 | ||
| 
 | ||
| function TArrayToken.Duplicate: TPSToken;
 | ||
| begin
 | ||
|   Result := inherited Duplicate;
 | ||
| end;
 | ||
| 
 | ||
| procedure TArrayToken.FreeToken(AToken, AData: Pointer);
 | ||
| begin
 | ||
|   if AToken = nil then Exit;
 | ||
|   TPSToken(AToken).Free;
 | ||
| end;
 | ||
| 
 | ||
| procedure TArrayToken.AddNumber(ANumber: Double);
 | ||
| var
 | ||
|   lToken: TPSToken;
 | ||
| begin
 | ||
|   lToken := TPSToken.Create;
 | ||
|   lToken.FloatValue := ANumber;
 | ||
|   ArrayData.Add(lToken);
 | ||
| end;
 | ||
| 
 | ||
| procedure TArrayToken.AddIdentityMatrix;
 | ||
| begin
 | ||
|   AddNumber(1);
 | ||
|   AddNumber(0);
 | ||
|   AddNumber(0);
 | ||
|   AddNumber(1);
 | ||
|   AddNumber(0);
 | ||
|   AddNumber(0);
 | ||
| end;
 | ||
| 
 | ||
| function TArrayToken.GetNumber(AIndex: Integer): Double;
 | ||
| begin
 | ||
|   Result := TPSToken(ArrayData.Items[AIndex]).FloatValue;
 | ||
| end;
 | ||
| 
 | ||
| procedure TArrayToken.ResolveOperators;
 | ||
| begin
 | ||
| 
 | ||
| end;
 | ||
| 
 | ||
| { TGraphicState }
 | ||
| 
 | ||
| constructor TGraphicState.Create;
 | ||
| begin
 | ||
|   inherited Create;
 | ||
| 
 | ||
|   ScaleX := 1;
 | ||
|   ScaleY := 1;
 | ||
| end;
 | ||
| 
 | ||
| function TGraphicState.Duplicate: TGraphicState;
 | ||
| begin
 | ||
|   Result := TGraphicState(Self.ClassType.Create);
 | ||
|   Result.Color := Color;
 | ||
|   Result.TranslateX := TranslateX;
 | ||
|   Result.TranslateY := TranslateY;
 | ||
|   Result.ScaleX := ScaleX;
 | ||
|   Result.ScaleY := ScaleY;
 | ||
|   Result.ClipPath := ClipPath;
 | ||
|   Result.ClipMode := ClipMode;
 | ||
|   Result.OverPrint := OverPrint;
 | ||
|   Result.ColorSpaceName := ColorSpaceName;
 | ||
|   if CTM <> nil then
 | ||
|     Result.CTM := TArrayToken(CTM.Duplicate());
 | ||
|   Result.PenWidth := PenWidth;
 | ||
| end;
 | ||
| 
 | ||
| procedure TGraphicState.CTMNeeded;
 | ||
| begin
 | ||
|   if CTM <> nil then Exit;
 | ||
| 
 | ||
|   CTM := TArrayToken.Create;
 | ||
|   CTM.AddIdentityMatrix();
 | ||
| end;
 | ||
| 
 | ||
| procedure TGraphicState.SetCTM(ANewCTM: TArrayToken);
 | ||
| begin
 | ||
|   if CTM <> nil then CTM.Free;
 | ||
|   CTM := ANewCTM;
 | ||
| end;
 | ||
| 
 | ||
| { TPSToken }
 | ||
| 
 | ||
| constructor TPSToken.Create;
 | ||
| begin
 | ||
|   inherited Create;
 | ||
| end;
 | ||
| 
 | ||
| procedure TPSToken.CopyDataFrom(ASrc: TPSToken; AKeepTokenType: Boolean);
 | ||
| begin
 | ||
|   StrValue := ASrc.StrValue;
 | ||
|   FloatValue := ASrc.FloatValue;
 | ||
|   IntValue := ASrc.IntValue;
 | ||
|   BoolValue := ASrc.BoolValue;
 | ||
| end;
 | ||
| 
 | ||
| function TPSToken.Duplicate: TPSToken;
 | ||
| begin
 | ||
|   Result := TPSToken(Self.ClassType.Create);
 | ||
|   Result.StrValue := StrValue;
 | ||
|   Result.FloatValue := FloatValue;
 | ||
|   Result.IntValue := IntValue;
 | ||
|   Result.Line := Line;
 | ||
| end;
 | ||
| 
 | ||
| procedure TPSToken.PrepareIntValue;
 | ||
| begin
 | ||
|   if IntValue = 0 then IntValue := Round(FloatValue);
 | ||
| end;
 | ||
| 
 | ||
| { TProcedureToken }
 | ||
| 
 | ||
| constructor TProcedureToken.Create;
 | ||
| begin
 | ||
|   inherited Create;
 | ||
| 
 | ||
|   Childs := TPSTokens.Create;
 | ||
| end;
 | ||
| 
 | ||
| destructor TProcedureToken.Destroy;
 | ||
| begin
 | ||
|   Childs.Free;
 | ||
| 
 | ||
|   inherited Destroy;
 | ||
| end;
 | ||
| 
 | ||
| { TExpressionToken }
 | ||
| 
 | ||
| constructor TExpressionToken.Create;
 | ||
| begin
 | ||
|   inherited Create;
 | ||
|   SubstituteETType := ettInvalid;
 | ||
| end;
 | ||
| 
 | ||
| function TExpressionToken.IsExpressionOperand: Boolean;
 | ||
| begin
 | ||
|   if StrValue = '' then Exit(False);
 | ||
|   Result := StrValue[1] in ['0'..'9','-'];
 | ||
| end;
 | ||
| 
 | ||
| procedure TExpressionToken.PrepareFloatValue;
 | ||
| var
 | ||
|   lRadixPos: SizeInt;
 | ||
|   i: Integer;
 | ||
|   Len: Integer;
 | ||
|   lRadixStr: string;
 | ||
|   lRadixNum: Integer;
 | ||
| begin
 | ||
|   //if not IsExpressionOperand() then Exit;
 | ||
|   if ETType <> ettOperand then Exit; // faster, because this field should already be filled
 | ||
| 
 | ||
|   // If this is a radix number, we will have more work
 | ||
|   // Example of radix in Postscript: 2#1000 = 8
 | ||
|   // http://en.wikipedia.org/wiki/Radix
 | ||
|   // The first number is the base, 2 = binary, 10=decimal, 16=hex, etc
 | ||
|   lRadixPos := Pos('#', StrValue);
 | ||
|   if lRadixPos <> 0 then
 | ||
|   begin
 | ||
|     FloatValue := 0;
 | ||
|     Len := Length(StrValue);
 | ||
|     lRadixStr := Copy(StrValue, 1, lRadixPos-1);
 | ||
|     lRadixNum := StrToInt(lRadixStr); // for now assume only 1
 | ||
|     for i := Length(StrValue) downto lRadixPos+1 do
 | ||
|     begin
 | ||
|       FloatValue := FloatValue + StrToInt(StrValue[i]) * Math.Power(lRadixNum, Len - i);
 | ||
|     end;
 | ||
|   end
 | ||
|   else
 | ||
|   // Code for normal numbers, decimals
 | ||
|   begin
 | ||
|     FloatValue := StrToFloat(StrValue, FPointSeparator);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| procedure TExpressionToken.CopyDataFrom(ASrc: TPSToken; AKeepTokenType: Boolean);
 | ||
| begin
 | ||
|   inherited CopyDataFrom(ASrc, AKeepTokenType);
 | ||
|   if (ASrc is TExpressionToken) and (not AKeepTokenType) then
 | ||
|     ETType := TExpressionToken(ASrc).ETType;
 | ||
|   SubstituteETType := TExpressionToken(ASrc).ETType;
 | ||
| end;
 | ||
| 
 | ||
| function TExpressionToken.Duplicate: TPSToken;
 | ||
| begin
 | ||
|   Result:=inherited Duplicate;
 | ||
|   TExpressionToken(Result).ETType := ETType;
 | ||
| end;
 | ||
| 
 | ||
| {$DEFINE FPVECTORIALDEBUG}
 | ||
| 
 | ||
| { TPSTokenizer }
 | ||
| 
 | ||
| // ACurLine < 0 indicates that we should use the line of this list of strings
 | ||
| // else we use ACurLine
 | ||
| constructor TPSTokenizer.Create(ACurLine: Integer);
 | ||
| begin
 | ||
|   inherited Create;
 | ||
|   Tokens := TPSTokens.Create;
 | ||
|   FCurLine := ACurLine;
 | ||
| end;
 | ||
| 
 | ||
| destructor TPSTokenizer.Destroy;
 | ||
| begin
 | ||
|   Tokens.Free;
 | ||
|   inherited Destroy;
 | ||
| end;
 | ||
| 
 | ||
| {@@ Rules for parsing PostScript files:
 | ||
| 
 | ||
| * Coments go from the first occurence of % outside a line to the next new line
 | ||
| * The only accepted characters are printable ASCII ones, plus spacing ASCII chars
 | ||
|   See IsValidPostScriptChar about that
 | ||
| }
 | ||
| procedure TPSTokenizer.ReadFromStream(AStream: TStream);
 | ||
| var
 | ||
|   CurChar: Char;
 | ||
|   CurLine: Integer = 1;
 | ||
|   State: TPostScriptScannerState = ssSearchingToken;
 | ||
|   CommentToken: TCommentToken;
 | ||
|   ProcedureToken: TProcedureToken;
 | ||
|   ExpressionToken: TExpressionToken;
 | ||
|   ArrayToken, NewArrayToken: TArrayToken;
 | ||
|   DictionaryToken: TDictionaryToken;
 | ||
|   lReturnState: TStack; // of TPostScriptScannerState
 | ||
|   lExpressionStateReturn: TPostScriptScannerState;
 | ||
|   lIsEndOfLine: Boolean;
 | ||
|   lIsExpressionFinished: Boolean;
 | ||
|   lTmpStr: string;
 | ||
| begin
 | ||
|   lReturnState := TStack.Create;
 | ||
|   try
 | ||
| 
 | ||
|   // Check if the EPS file starts with a TIFF preview
 | ||
|   // See http://www.graphicsgroups.com/12-corel/f851f798a0e1ca7a.htm
 | ||
|   // 00000000: c5d0 d3c6 930b 0000 55f2 0000 0000 0000  ........U.......
 | ||
|   // 00000010: 0000 0000 1e00 0000 750b 0000 ffff 4949  ........u.....II
 | ||
|   CurChar := Char(AStream.ReadByte());
 | ||
|   if Byte(CurChar) = $C5 then
 | ||
|     AStream.Position := $20
 | ||
|   else
 | ||
|     AStream.Position := AStream.Position - 1;
 | ||
| 
 | ||
|   //
 | ||
|   // Now actualy read EPS data
 | ||
|   //
 | ||
|   while AStream.Position < AStream.Size do
 | ||
|   begin
 | ||
|     CurChar := Char(AStream.ReadByte());
 | ||
| //    {$ifdef FPVECTORIALDEBUG}
 | ||
| //    WriteLn(Format('Obtained token %s', [CurChar]));
 | ||
| //    {$endif}
 | ||
|     if not IsValidPostScriptChar(Byte(CurChar)) then
 | ||
|       raise Exception.Create(Format('[TPSTokenizer.ReadFromStream] Invalid char: %s at line %d',
 | ||
|         [IntToHex(Byte(CurChar), 2), CurLine]));
 | ||
| 
 | ||
|     lIsEndOfLine := IsEndOfLine(Byte(CurChar), AStream);
 | ||
|     if lIsEndOfLine then Inc(CurLine);
 | ||
|     if FCurLine >= 0 then CurLine := FCurLine;
 | ||
| 
 | ||
|     case State of
 | ||
|       { Searching for a token }
 | ||
|       ssSearchingToken:
 | ||
|       begin
 | ||
|         if CurChar = '%' then
 | ||
|         begin
 | ||
|           CommentToken := TCommentToken.Create;
 | ||
|           CommentToken.Line := CurLine;
 | ||
|           CommentToken.StrValue := '%';
 | ||
|           State := ssInComment;
 | ||
|           lReturnState.Push(Pointer(PtrInt(ssSearchingToken)));
 | ||
| //          {$ifdef FPVECTORIALDEBUG}
 | ||
| //          WriteLn(Format('Starting Comment at Line %d', [CurLine]));
 | ||
| //          {$endif}
 | ||
|         end
 | ||
|         else if CurChar = '{' then
 | ||
|         begin
 | ||
|           ProcedureToken := TProcedureToken.Create;
 | ||
|           ProcedureToken.Levels := 1;
 | ||
|           ProcedureToken.Line := CurLine;
 | ||
|           State := ssInGroup;
 | ||
|         end
 | ||
|         else if CurChar = '[' then
 | ||
|         begin
 | ||
|           ArrayToken := TArrayToken.Create;
 | ||
|           lReturnState.Push(Pointer(PtrInt(ssSearchingToken)));
 | ||
|           State := ssInArray;
 | ||
|         end
 | ||
|         else if CurChar = '<' then
 | ||
|         begin
 | ||
|           CurChar := Char(AStream.ReadByte());
 | ||
|           if CurChar = '<' then
 | ||
|           begin
 | ||
|             DictionaryToken := TDictionaryToken.Create;
 | ||
|             State := ssInDictionary;
 | ||
|             lReturnState.Push(Pointer(PtrInt(ssSearchingToken)));
 | ||
|           end
 | ||
|           else
 | ||
|             raise Exception.Create(Format('[TPSTokenizer.ReadFromStream] Unexpected char while searching for "<<" token: $%s in Line %d',
 | ||
|               [IntToHex(Byte(CurChar), 2), CurLine]));
 | ||
|         end
 | ||
|         else if CurChar in ['a'..'z','A'..'Z','0'..'9','-','/','('] then
 | ||
|         begin
 | ||
|           ExpressionToken := TExpressionToken.Create;
 | ||
|           ExpressionToken.Line := CurLine;
 | ||
|           ExpressionToken.StrValue := '';
 | ||
|           if CurChar = '/' then
 | ||
|             ExpressionToken.ETType := ettNamedElement
 | ||
|           else if CurChar = '(' then
 | ||
|             ExpressionToken.ETType := ettLiteralString
 | ||
|           else
 | ||
|           begin
 | ||
|             ExpressionToken.StrValue := CurChar;
 | ||
|             if ExpressionToken.IsExpressionOperand() then
 | ||
|               ExpressionToken.ETType := ettOperand
 | ||
|             else
 | ||
|               ExpressionToken.ETType := ettOperator;
 | ||
|           end;
 | ||
|           lReturnState.Push(Pointer(PtrInt(ssSearchingToken)));
 | ||
|           State := ssInExpressionElement;
 | ||
|         end
 | ||
|         else if lIsEndOfLine then Continue
 | ||
|         else if IsPostScriptSpace(Byte(CurChar)) then Continue
 | ||
|         else
 | ||
|           raise Exception.Create(Format('[TPSTokenizer.ReadFromStream] Unexpected char while searching for token: $%s in Line %d',
 | ||
|            [IntToHex(Byte(CurChar), 2), CurLine]));
 | ||
|       end;
 | ||
| 
 | ||
|       { Passing by comments }
 | ||
|       ssInComment:
 | ||
|       begin
 | ||
|         CommentToken.StrValue := CommentToken.StrValue + CurChar;
 | ||
|         if lIsEndOfLine then
 | ||
|         begin
 | ||
|           Tokens.Add(CommentToken);
 | ||
|           State := TPostScriptScannerState(PtrUint(lReturnState.Pop()));
 | ||
| //          {$ifdef FPVECTORIALDEBUG}
 | ||
| //          WriteLn(Format('Adding Comment "%s" at Line %d', [CommentToken.StrValue, CurLine]));
 | ||
| //          {$endif}
 | ||
|         end;
 | ||
|       end; // ssInComment
 | ||
| 
 | ||
|       // Starts at [ and ends in ]
 | ||
|       ssInArray:
 | ||
|       begin
 | ||
|         if CurChar = '%' then
 | ||
|         begin
 | ||
|           CommentToken := TCommentToken.Create;
 | ||
|           CommentToken.Line := CurLine;
 | ||
|           CommentToken.StrValue := '%';
 | ||
|           State := ssInComment;
 | ||
|           lReturnState.Push(Pointer(PtrInt(ssInArray)));
 | ||
|         end
 | ||
|         // Another array inside the array
 | ||
|         else if (CurChar = '[') then
 | ||
|         begin
 | ||
|           // We are starting another array, so save the parent and go to the new one
 | ||
|           NewArrayToken := TArrayToken.Create;
 | ||
|           NewArrayToken.Parent := ArrayToken;
 | ||
|           ArrayToken.ArrayData.Add(NewArrayToken);
 | ||
|           ArrayToken := NewArrayToken;
 | ||
|           lReturnState.Push(Pointer(PtrInt(ssInArray)));
 | ||
|         end
 | ||
|         else if (CurChar = ']') then
 | ||
|         begin
 | ||
|           ArrayToken.ResolveOperators();
 | ||
|           if ArrayToken.Parent = nil then
 | ||
|           begin
 | ||
|             State := TPostScriptScannerState(PtrUint(lReturnState.Pop()));
 | ||
|             if State = ssInDictionary then
 | ||
|             begin
 | ||
|               DictionaryToken.Childs.Add(ArrayToken);
 | ||
|             end
 | ||
|             else
 | ||
|             begin
 | ||
|               Tokens.Add(ArrayToken);
 | ||
|             end;
 | ||
|           end
 | ||
|           else
 | ||
|           begin
 | ||
|             ArrayToken := ArrayToken.Parent;
 | ||
|           end;
 | ||
|         end
 | ||
|         else if CurChar in ['a'..'z','A'..'Z','0'..'9','-','/','('] then
 | ||
|         begin
 | ||
|           ExpressionToken := TExpressionToken.Create;
 | ||
|           ExpressionToken.Line := CurLine;
 | ||
|           ExpressionToken.StrValue := '';
 | ||
|           if CurChar = '/' then
 | ||
|             ExpressionToken.ETType := ettNamedElement
 | ||
|           else
 | ||
|           begin
 | ||
|             ExpressionToken.StrValue := CurChar;
 | ||
|             if ExpressionToken.IsExpressionOperand() then
 | ||
|               ExpressionToken.ETType := ettOperand
 | ||
|             else
 | ||
|               ExpressionToken.ETType := ettOperator;
 | ||
|           end;
 | ||
|           lReturnState.Push(Pointer(PtrInt(ssInArray)));
 | ||
|           State := ssInExpressionElement;
 | ||
|         end
 | ||
|         else if lIsEndOfLine then Continue
 | ||
|         else if IsPostScriptSpace(Byte(CurChar)) then Continue;
 | ||
|       end;
 | ||
| 
 | ||
|       // Starts at { and ends in }, passing over nested groups
 | ||
|       ssInGroup:
 | ||
|       begin
 | ||
|         if (CurChar = '{') then ProcedureToken.Levels := ProcedureToken.Levels + 1;
 | ||
|         if (CurChar = '}') then ProcedureToken.Levels := ProcedureToken.Levels - 1;
 | ||
| 
 | ||
|         if ProcedureToken.Levels = 0 then
 | ||
|         begin
 | ||
|           Tokens.Add(ProcedureToken);
 | ||
|           State := ssSearchingToken;
 | ||
|         end
 | ||
|         else
 | ||
|         begin
 | ||
|           // Don't add line ends, because they cause problems when outputing the debug info
 | ||
|           // but in this case we need to add spaces to compensate, or else items separates only
 | ||
|           // by line end might get glued together
 | ||
|           if CurChar in [#10, #13] then
 | ||
|             ProcedureToken.StrValue := ProcedureToken.StrValue + ' '
 | ||
|           else
 | ||
|             ProcedureToken.StrValue := ProcedureToken.StrValue + CurChar;
 | ||
|         end;
 | ||
|       end;
 | ||
| 
 | ||
|       // Starts at << and ends in >>
 | ||
|       ssInDictionary:
 | ||
|       begin
 | ||
|         if (CurChar = '>') then
 | ||
|         begin
 | ||
|           CurChar := Char(AStream.ReadByte());
 | ||
|           if CurChar = '>' then
 | ||
|           begin
 | ||
|             Tokens.Add(DictionaryToken);
 | ||
|             State := TPostScriptScannerState(PtrUint(lReturnState.Pop()));
 | ||
|           end
 | ||
|           else
 | ||
|             raise Exception.Create(Format('[TPSTokenizer.ReadFromStream] ssInDictionary: Unexpected char while searching for ">>" token: $%s in Line %d',
 | ||
|               [IntToHex(Byte(CurChar), 2), CurLine]));
 | ||
|         end
 | ||
|         else if CurChar in ['a'..'z','A'..'Z','0'..'9','-','/'] then
 | ||
|         begin
 | ||
|           ExpressionToken := TExpressionToken.Create;
 | ||
|           ExpressionToken.Line := CurLine;
 | ||
|           ExpressionToken.StrValue := '';
 | ||
|           if CurChar = '/' then
 | ||
|             ExpressionToken.ETType := ettNamedElement
 | ||
|           else
 | ||
|           begin
 | ||
|             ExpressionToken.StrValue := CurChar;
 | ||
|             if ExpressionToken.IsExpressionOperand() then
 | ||
|               ExpressionToken.ETType := ettOperand
 | ||
|             else
 | ||
|               ExpressionToken.ETType := ettOperator;
 | ||
|           end;
 | ||
|           lReturnState.Push(Pointer(PtrInt(ssInDictionary)));
 | ||
|           State := ssInExpressionElement;
 | ||
|         end
 | ||
|         else if CurChar = '[' then
 | ||
|         begin
 | ||
|           ArrayToken := TArrayToken.Create;
 | ||
|           lReturnState.Push(Pointer(PtrInt(ssInDictionary)));
 | ||
|           State := ssInArray;
 | ||
|         end
 | ||
|         else if lIsEndOfLine then Continue
 | ||
|         else if IsPostScriptSpace(Byte(CurChar)) then Continue;
 | ||
|       end;
 | ||
| 
 | ||
|       // Goes until a space comes, or { or [ ...
 | ||
|       ssInExpressionElement:
 | ||
|       begin
 | ||
|         // Literal strings end only in a ")", while other expressions end in a space or delimiter
 | ||
|         if ExpressionToken.ETType = ettLiteralString then lIsExpressionFinished := CurChar = ')'
 | ||
|         else lIsExpressionFinished := IsPostScriptSpace(Byte(CurChar)) or (CurChar in ['{', '[', '}', ']', '/', '<', '>', '(', ')']);
 | ||
| 
 | ||
|         if lIsExpressionFinished then
 | ||
|         begin
 | ||
|           ExpressionToken.PrepareFloatValue();
 | ||
|           if lReturnState.Count = 0 then lExpressionStateReturn := ssSearchingToken
 | ||
|           else lExpressionStateReturn := TPostScriptScannerState(PtrUint(lReturnState.Pop()));
 | ||
|           if lExpressionStateReturn = ssInArray then
 | ||
|           begin
 | ||
|             ArrayToken.ArrayData.Add(ExpressionToken);
 | ||
|             State := ssInArray;
 | ||
|           end
 | ||
|           else if lExpressionStateReturn = ssInDictionary then
 | ||
|           begin
 | ||
|             DictionaryToken.Childs.Add(ExpressionToken);
 | ||
|             State := ssInDictionary;
 | ||
|           end
 | ||
|           else
 | ||
|           begin
 | ||
|             Tokens.Add(ExpressionToken);
 | ||
|             if ExpressionToken.StrValue = 'image' then
 | ||
|               State := ssReadingRawDataStart
 | ||
|             else
 | ||
|               State := ssSearchingToken;
 | ||
|           end;
 | ||
|           if (CurChar in ['{', '[', '}', ']', '<', '>', '%']) then
 | ||
|           begin
 | ||
|             AStream.Seek(-1, soFromCurrent);
 | ||
|           end;
 | ||
|         end
 | ||
|         else
 | ||
|           ExpressionToken.StrValue := ExpressionToken.StrValue + CurChar;
 | ||
|       end;
 | ||
|       // Raw data reading
 | ||
|       ssReadingRawDataStart:
 | ||
|       begin
 | ||
|         if IsPostScriptSpace(Byte(CurChar)) then Continue;
 | ||
| 
 | ||
|         ExpressionToken := TExpressionToken.Create;
 | ||
|         ExpressionToken.Line := CurLine;
 | ||
|         ExpressionToken.StrValue := CurChar;
 | ||
|         ExpressionToken.ETType := ettRawData;
 | ||
|         State := ssReadingRawData;
 | ||
|       end;
 | ||
|       // ASCII85 and Flate (compressed) go on until this appears: ~>
 | ||
|       // ToDo: Check if this is valid for all raw data
 | ||
|       ssReadingRawData:
 | ||
|       begin
 | ||
|         if IsPostScriptSpace(Byte(CurChar)) then Continue;
 | ||
| 
 | ||
|         ExpressionToken.StrValue := ExpressionToken.StrValue + CurChar;
 | ||
| 
 | ||
|         // Check if we are in the end of the raw data
 | ||
|         lTmpStr := Copy(ExpressionToken.StrValue, Length(ExpressionToken.StrValue)-1, 2);
 | ||
|         if lTmpStr = '~>' then
 | ||
|         begin
 | ||
|           Tokens.Add(ExpressionToken);
 | ||
|           State := ssSearchingToken;
 | ||
|         end;
 | ||
|       end;
 | ||
|     end; // case
 | ||
|   end; // while
 | ||
| 
 | ||
|   // If the stream finished, there might be a token still being built
 | ||
|   // so lets finish it
 | ||
|   if State = ssInExpressionElement then
 | ||
|   begin
 | ||
|     Tokens.Add(ExpressionToken);
 | ||
|   end;
 | ||
| 
 | ||
|   finally
 | ||
|     lReturnState.Free;
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| procedure TPSTokenizer.DebugOut();
 | ||
| var
 | ||
|   i: Integer;
 | ||
|   Token: TPSToken;
 | ||
| begin
 | ||
|   for i := 0 to Tokens.Count - 1 do
 | ||
|   begin
 | ||
|     Token := TPSToken(Tokens.Items[i]);
 | ||
| 
 | ||
|     if Token is TCommentToken then
 | ||
|     begin
 | ||
|       WriteLn(Format('TCommentToken StrValue=%s', [Token.StrValue]));
 | ||
|     end
 | ||
|     else if Token is TProcedureToken then
 | ||
|     begin
 | ||
|       WriteLn(Format('TProcedureToken StrValue=%s', [Token.StrValue]));
 | ||
|     end
 | ||
|     else if Token is TExpressionToken then
 | ||
|     begin
 | ||
|       WriteLn(Format('TExpressionToken StrValue=%s', [Token.StrValue]));
 | ||
|     end;
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| {@@ Valid PostScript Chars:
 | ||
| 
 | ||
| All printable ASCII: a..zA..Z0..9 plus punctuation
 | ||
| 
 | ||
| Plus the following white spaces
 | ||
| 000 00 0 Null (nul)
 | ||
| 011 09 9 Tab (tab)
 | ||
| 012 0A 10 Line feed (LF)
 | ||
| 014 0C 12 Form feed (FF)
 | ||
| 015 0D 13 Carriage return (CR)
 | ||
| 040 20 32 Space (SP)
 | ||
| }
 | ||
| function TPSTokenizer.IsValidPostScriptChar(AChar: Byte): Boolean;
 | ||
| begin
 | ||
|   Result := ((AChar > 32) and (AChar < 127)) or (AChar in [0, 9, 10, 12, 13, 32]);
 | ||
| end;
 | ||
| 
 | ||
| function TPSTokenizer.IsPostScriptSpace(AChar: Byte): Boolean;
 | ||
| begin
 | ||
|   Result := AChar in [0, 9, 10, 12, 13, 32];
 | ||
| end;
 | ||
| 
 | ||
| function TPSTokenizer.IsEndOfLine(ACurChar: Byte; AStream: TStream): Boolean;
 | ||
| var
 | ||
|   HasNextChar: Boolean = False;
 | ||
|   NextChar: Byte;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   if ACurChar = 13 then
 | ||
|   begin
 | ||
|     if AStream.Position < AStream.Size then
 | ||
|     begin
 | ||
|       HasNextChar := True;
 | ||
|       NextChar := AStream.ReadByte();
 | ||
|       if NextChar <> 10 then AStream.Seek(-1, soFromCurrent); // Go back if it wasnt a #13#10
 | ||
|       Exit(True);
 | ||
|     end;
 | ||
|   end;
 | ||
| 
 | ||
|   if ACurChar = 10 then Result := True;
 | ||
| end;
 | ||
| 
 | ||
| {$ifndef Windows}
 | ||
| {$define FPVECTORIALDEBUG}
 | ||
| {$endif}
 | ||
| 
 | ||
| { TvEPSVectorialReader }
 | ||
| 
 | ||
| procedure TvEPSVectorialReader.DebugStack();
 | ||
| var
 | ||
|   i: Integer;
 | ||
|   lToken: TPSToken;
 | ||
| begin
 | ||
|   WriteLn('====================');
 | ||
|   WriteLn('Stack dump');
 | ||
|   WriteLn('====================');
 | ||
|   for i := 0 to TStackAccess(Stack).List.Count - 1 do
 | ||
|   begin
 | ||
|     lToken := TPSToken(TStackAccess(Stack).List.Items[i]);
 | ||
|     WriteLn(Format('Stack #%d : %s', [i, lToken.StrValue]));
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| procedure TvEPSVectorialReader.RunPostScript(ATokens: TPsTokens;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument);
 | ||
| var
 | ||
|   i: Integer;
 | ||
|   lSubstituted: Boolean;
 | ||
|   CurToken, NextToken: TPSToken;
 | ||
| begin
 | ||
|   {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|   WriteLn('[TvEPSVectorialReader.RunPostScript] START');
 | ||
|   {$endif}
 | ||
|   if ExitCalled then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|     WriteLn('[TvEPSVectorialReader.RunPostScript] ExitCalled');
 | ||
|     {$endif}
 | ||
|     Exit;
 | ||
|   end;
 | ||
|   for i := 0 to ATokens.Count - 1 do
 | ||
|   begin
 | ||
|     CurToken := TPSToken(ATokens.Items[i]);
 | ||
|     // a preview of the next token is sometimes utilized
 | ||
|     if i < ATokens.Count-1 then NextToken := TPSToken(ATokens.Items[i+1])
 | ||
|     else NextToken := nil;
 | ||
| 
 | ||
| {    if CurToken.StrValue = 'setrgbcolor' then
 | ||
|     begin
 | ||
|       WriteLn('===================');
 | ||
|       WriteLn('CMYK__');
 | ||
|       WriteLn('===================');
 | ||
|       DebugStack();
 | ||
|     end;}
 | ||
| 
 | ||
|     if CurToken is TCommentToken then
 | ||
|     begin
 | ||
|       {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|       WriteLn(Format('[TvEPSVectorialReader.RunPostScript] Type: TCommentToken Token: %s', [CurToken.StrValue]));
 | ||
|       {$endif}
 | ||
| //      ProcessCommentToken(CurToken as TCommentToken, AData);
 | ||
| 
 | ||
|       // Give up in the trailer to avoid errors in the very end of files
 | ||
|       if (CurToken.StrValue = '%%Trailer') or (CurToken.StrValue = '%%Trailer'#10) then Exit;
 | ||
| 
 | ||
|       Continue;
 | ||
|     end;
 | ||
| 
 | ||
|     if CurToken is TProcedureToken then
 | ||
|     begin
 | ||
|       {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|       WriteLn(Format('[TvEPSVectorialReader.RunPostScript] Type: TProcedureToken Token: %s', [CurToken.StrValue]));
 | ||
|       {$endif}
 | ||
|       Stack.Push(CurToken);
 | ||
|       Continue;
 | ||
|     end;
 | ||
| 
 | ||
|     if CurToken is TExpressionToken then
 | ||
|     begin
 | ||
|       {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|       WriteLn(Format('[TvEPSVectorialReader.RunPostScript] Type: TExpressionToken Token: %s', [CurToken.StrValue]));
 | ||
|       {$endif}
 | ||
| 
 | ||
|       if (TExpressionToken(CurToken).ETType = ettOperand) or
 | ||
|         (TExpressionToken(CurToken).ETType = ettDictionary) or
 | ||
|         (TExpressionToken(CurToken).ETType = ettRawData) then
 | ||
|       begin
 | ||
|         Stack.Push(CurToken);
 | ||
|         Continue;
 | ||
|       end;
 | ||
| 
 | ||
|       // Now we need to verify if the operator should be substituted in the dictionary
 | ||
|       lSubstituted := DictionarySubstituteOperator(Dictionary, CurToken);
 | ||
| 
 | ||
|       // Check if this is the first time that a named element appears, if yes, don't try to execute it
 | ||
|       // just put it into the stack
 | ||
|       if (not lSubstituted) and (TExpressionToken(CurToken).ETType = ettNamedElement) then
 | ||
|       begin
 | ||
|         Stack.Push(CurToken);
 | ||
|         Continue;
 | ||
|       end;
 | ||
| 
 | ||
|       // If we got an array after the substitution, don't run it, just put it in the stack
 | ||
|       if CurToken is TArrayToken then
 | ||
|       begin
 | ||
|         Stack.Push(CurToken);
 | ||
|         Continue;
 | ||
|       end;
 | ||
| 
 | ||
|       // sometimes the substitution results in a direct reference to a dictionary
 | ||
|       // maybe sometimes to an operand too? In this cases don't try to run the code!
 | ||
|       if (TExpressionToken(CurToken).ETType = ettOperand) or
 | ||
|         (TExpressionToken(CurToken).ETType = ettDictionary) or
 | ||
|         (TExpressionToken(CurToken).ETType = ettRawData) then
 | ||
|       begin
 | ||
|         Stack.Push(CurToken);
 | ||
|         Continue;
 | ||
|       end;
 | ||
| 
 | ||
|       // If we got a procedure from the substitution, run it!
 | ||
|       if CurToken is TProcedureToken then ExecuteProcedureToken(TProcedureToken(CurToken), AData, ADoc)
 | ||
|       else ExecuteOperatorToken(TExpressionToken(CurToken), AData, ADoc, NextToken);
 | ||
| 
 | ||
|       if ExitCalled then Break;
 | ||
|     end;
 | ||
| 
 | ||
|     if CurToken is TDictionaryToken then
 | ||
|     begin
 | ||
|       {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|       //WriteLn(Format('[TvEPSVectorialReader.RunPostScript] Type: TProcedureToken Token: %s', [CurToken.StrValue]));
 | ||
|       {$endif}
 | ||
|       Stack.Push(CurToken);
 | ||
|       Continue;
 | ||
|     end;
 | ||
|   end;
 | ||
|   {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|   WriteLn('[TvEPSVectorialReader.RunPostScript] END');
 | ||
|   {$endif}
 | ||
| end;
 | ||
| 
 | ||
| procedure TvEPSVectorialReader.ExecuteProcedureToken(AToken: TProcedureToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument);
 | ||
| var
 | ||
|   ProcTokenizer: TPSTokenizer;
 | ||
|   lStream: TMemoryStream;
 | ||
|   lOldTokens: TPSTokens;
 | ||
|   i: Integer;
 | ||
| begin
 | ||
|   {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|   WriteLn('[TvEPSVectorialReader.ExecuteProcedureToken] START');
 | ||
|   {$endif}
 | ||
|   if ExitCalled then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecuteProcedureToken] ExitCalled');
 | ||
|     {$endif}
 | ||
|     Exit;
 | ||
|   end;
 | ||
| 
 | ||
|   if not AToken.Parsed then
 | ||
|   begin
 | ||
|     ProcTokenizer := TPSTokenizer.Create(AToken.Line);
 | ||
|     lStream := TMemoryStream.Create;
 | ||
|     try
 | ||
|       // Copy the string to a Stream
 | ||
|       for i := 1 to Length(AToken.StrValue) do
 | ||
|         lStream.WriteByte(Byte(AToken.StrValue[i]));
 | ||
| 
 | ||
|       // Change the Tokens so that it writes directly to AToken.Childs
 | ||
|       lOldTokens := ProcTokenizer.Tokens;
 | ||
|       ProcTokenizer.Tokens := AToken.Childs;
 | ||
| 
 | ||
|       // Now parse the procedure code
 | ||
|       lStream.Position := 0;
 | ||
|       ProcTokenizer.ReadFromStream(lStream);
 | ||
| 
 | ||
|       // Recover the old tokens for usage in .Free
 | ||
|       ProcTokenizer.Tokens := lOldTokens;
 | ||
|     finally
 | ||
|       lStream.Free;
 | ||
|       ProcTokenizer.Free;
 | ||
|     end;
 | ||
| 
 | ||
|     AToken.Parsed := True;
 | ||
|   end;
 | ||
| 
 | ||
|   // Now run the procedure
 | ||
|   RunPostScript(AToken.Childs, AData, ADoc);
 | ||
|   {$ifdef FPVECTORIALDEBUG_CODEFLOW}
 | ||
|   WriteLn('[TvEPSVectorialReader.ExecuteProcedureToken] END');
 | ||
|   {$endif}
 | ||
| end;
 | ||
| 
 | ||
| procedure TvEPSVectorialReader.ExecuteOperatorToken(AToken: TExpressionToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken);
 | ||
| begin
 | ||
|   if AToken.StrValue = '' then
 | ||
|   begin
 | ||
|     // A clean exit if the token was substituted by something else which cannot be executed
 | ||
|     if AToken.SubstituteETType <> ettInvalid then Exit;
 | ||
|     raise Exception.Create(Format('[TvEPSVectorialReader.ProcessExpressionToken] Empty operator line=%d', [AToken.Line]));
 | ||
|   end;
 | ||
| 
 | ||
|   if ExecuteDictionaryOperators(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteArithmeticAndMathOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecutePathConstructionOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteGraphicStateOperatorsDI(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteGraphicStateOperatorsDD(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteControlOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteStackManipulationOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteMiscellaneousOperators(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecutePaintingOperator(AToken, AData, ADoc, ANextToken) then Exit;
 | ||
| 
 | ||
|   if ExecuteDeviceSetupAndOutputOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteArrayOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteStringOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteFileOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteResourceOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteVirtualMemoryOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   if ExecuteErrorOperator(AToken, AData, ADoc) then Exit;
 | ||
| 
 | ||
|   // If we got here, there the command not yet implemented
 | ||
|   raise Exception.Create(Format('[TvEPSVectorialReader.ProcessExpressionToken] Unknown PostScript Command "%s" in Line %d',
 | ||
|     [AToken.StrValue, AToken.Line]));
 | ||
| end;
 | ||
| 
 | ||
| { Operand Stack Manipulation Operators
 | ||
| 
 | ||
|   any pop –                    Discard top element
 | ||
|   any1 any2 exch ==> any2 any1 Exchange top two elements
 | ||
|   any dup ==> any any          Duplicate top element
 | ||
|   any1 … anyn n copy any1 … anyn any1 … anyn
 | ||
|                                Duplicate top n elements
 | ||
|   anyn … any0 n index anyn … any0 anyn
 | ||
|                                Duplicate arbitrary element
 | ||
|   anyn-1 … any0 n j roll any(j-1) mod n … any0 anyn-1 … anyj mod n
 | ||
|                                Roll n elements up j times
 | ||
|   any1 … anyn clear            Discard all elements
 | ||
|   any1 … anyn count any1 … anyn n
 | ||
|                                Count elements on stack
 | ||
|   – mark mark                  Push mark on stack
 | ||
|   mark obj1 … objn cleartomark –
 | ||
|                                Discard elements down through mark
 | ||
|   mark obj1 … objn counttomark mark obj1 … objn n
 | ||
|                                Count elements down to mark
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteStackManipulationOperator(
 | ||
|   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| var
 | ||
|   Param1, Param2, NewToken: TPSToken;
 | ||
|   NewExprToken: TExpressionToken;
 | ||
|   lIndexN, lIndexJ: Integer;
 | ||
|   lTokens: array of TPSToken;
 | ||
|   i: Integer;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   // Discard top element
 | ||
|   if AToken.StrValue = 'pop' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Exchange top two elements
 | ||
|   if AToken.StrValue = 'exch' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     Stack.Push(Param1);
 | ||
|     Stack.Push(Param2);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Duplicate top element
 | ||
|   if AToken.StrValue = 'dup' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     NewToken := Param1.Duplicate();
 | ||
|     Stack.Push(Param1);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // any1 … anyn count any1 … anyn n
 | ||
|   // Count elements on stack
 | ||
|   if AToken.StrValue = 'count' then
 | ||
|   begin
 | ||
|     NewExprToken := TExpressionToken.Create;
 | ||
|     NewExprToken.ETType := ettOperand;
 | ||
|     NewExprToken.FloatValue := Stack.Count;
 | ||
|     NewExprToken.StrValue := IntToStr(Stack.Count);
 | ||
|     Stack.Push(NewExprToken);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // anyn … any0 n index anyn … any0 anyn
 | ||
|   // Duplicate arbitrary element
 | ||
|   if AToken.StrValue = 'index' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_INDEX}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecuteStackManipulationOperator] index');
 | ||
| //    DebugStack();
 | ||
|     {$endif}
 | ||
| 
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     lIndexN := Round(Param1.FloatValue);
 | ||
|     SetLength(lTokens, lIndexN+1);
 | ||
| 
 | ||
|     if lIndexN < 0 then raise Exception.Create('[TvEPSVectorialReader.ExecuteStackManipulationOperator] index operator: n must be positive or zero');
 | ||
| 
 | ||
|     // Unroll all elements necessary
 | ||
| 
 | ||
|     for i := 0 to lIndexN do
 | ||
|     begin
 | ||
|       lTokens[i] := TPSToken(Stack.Pop);
 | ||
|       Param2 := lTokens[i];
 | ||
|       if Param2 = nil then
 | ||
|       begin
 | ||
|         raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteStackManipulationOperator] Stack underflow in operation "index". Error at line %d', [AToken.Line]));
 | ||
|       end;
 | ||
|     end;
 | ||
| 
 | ||
|     // Duplicate the disired token
 | ||
| 
 | ||
|     NewToken := lTokens[lIndexN].Duplicate();
 | ||
| 
 | ||
|     // Roll them back
 | ||
| 
 | ||
|     for i := lIndexN downto 0 do
 | ||
|     begin
 | ||
|       Stack.Push(lTokens[i]);
 | ||
|     end;
 | ||
| 
 | ||
|     // Roll the duplicated element too
 | ||
| 
 | ||
|     Stack.Push(NewToken);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // anyn-1 … any0 n j roll any(j-1) mod n … any0 anyn-1 … anyj mod n
 | ||
|   //
 | ||
|   // performs a circular shift of the objects anyn-1 through any0 on the operand stack
 | ||
|   // by the amount j. Positive j indicates upward motion on the stack, whereas negative
 | ||
|   // j indicates downward motion.
 | ||
|   // n must be a nonnegative integer and j must be an integer. roll first removes these
 | ||
|   // operands from the stack; there must be at least n additional elements. It then performs
 | ||
|   // a circular shift of these n elements by j positions.
 | ||
|   // If j is positive, each shift consists of removing an element from the top of the stack
 | ||
|   // and inserting it between element n - 1 and element n of the stack, moving all in8.2
 | ||
|   // tervening elements one level higher on the stack. If j is negative, each shift consists
 | ||
|   // of removing element n - 1 of the stack and pushing it on the top of the stack,
 | ||
|   // moving all intervening elements one level lower on the stack.
 | ||
|   //
 | ||
|   // Examples    N J
 | ||
|   // (a) (b) (c) 3 -1 roll => (b) (c) (a)
 | ||
|   // (a) (b) (c) 3 1 roll  => (c) (a) (b)
 | ||
|   // (a) (b) (c) 3 0 roll  => (a) (b) (c)
 | ||
|   if AToken.StrValue = 'roll' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     lIndexJ := Round(Param1.FloatValue);
 | ||
|     lIndexN := Round(Param2.FloatValue);
 | ||
| 
 | ||
|     {$ifdef FPVECTORIALDEBUG_ROLL}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader] roll: N=%d J=%d', [lIndexN, lIndexJ]));
 | ||
|     {$endif}
 | ||
| 
 | ||
|     if lIndexN < 0 then raise Exception.Create('[TvEPSVectorialReader.ExecuteStackManipulationOperator] rool operator: n must be positive or zero');
 | ||
| 
 | ||
|     if lIndexJ = 0 then Exit(True);
 | ||
| 
 | ||
|     SetLength(lTokens, lIndexN);
 | ||
| 
 | ||
|     // Unroll all elements necessary
 | ||
| 
 | ||
|     for i := 0 to lIndexN-1 do
 | ||
|     begin
 | ||
|       lTokens[i] := TPSToken(Stack.Pop());
 | ||
|       Param2 := lTokens[i];
 | ||
|       if Param2 = nil then
 | ||
|       begin
 | ||
|         raise Exception.Create('[TvEPSVectorialReader.ExecuteStackManipulationOperator] nil element poped in operator index');
 | ||
|         //Exit(True);
 | ||
|       end;
 | ||
|     end;
 | ||
| 
 | ||
|     // Roll them back
 | ||
| 
 | ||
|     if lIndexJ > 0 then
 | ||
|     begin
 | ||
|       for i := lIndexJ-1 downto 0 do
 | ||
|       begin
 | ||
|         Stack.Push(lTokens[i]);
 | ||
|       end;
 | ||
|       for i := lIndexN-1 downto lIndexJ do
 | ||
|       begin
 | ||
|         Stack.Push(lTokens[i]);
 | ||
|       end;
 | ||
|     end
 | ||
|     else
 | ||
|     begin
 | ||
|       lIndexJ := -lIndexJ;
 | ||
| 
 | ||
|       for i := lIndexN-lIndexJ-1 downto 0 do
 | ||
|       begin
 | ||
|         Stack.Push(lTokens[i]);
 | ||
|       end;
 | ||
|       for i := lIndexN-1 downto lIndexN-lIndexJ do
 | ||
|       begin
 | ||
|         Stack.Push(lTokens[i]);
 | ||
|       end;
 | ||
|     end;
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| {  Control Operators
 | ||
| 
 | ||
|   any exec –          Execute arbitrary object
 | ||
|   bool proc if –      Execute proc if bool is true
 | ||
|   bool proc1 proc2 ifelse –
 | ||
|                       Execute proc1 if bool is true, proc2 if false
 | ||
|   initial increment limit proc for –
 | ||
|                       Execute proc with values from initial by steps
 | ||
|                       of increment to limit
 | ||
|   int proc repeat –   Execute proc int times
 | ||
|   proc loop –         Execute proc an indefinite number of times
 | ||
|   – exit –            Exit innermost active loop
 | ||
|   – stop –            Terminate stopped context
 | ||
|   any stopped bool    Establish context for catching stop
 | ||
|   – countexecstack int Count elements on execution stack
 | ||
|   array execstack subarray Copy execution stack into array
 | ||
|   – quit – Terminate interpreter
 | ||
|   – start – Executed at interpreter startup
 | ||
|   Type, Attribute, and Conversion Operators
 | ||
|   any type name Return type of any
 | ||
|   any cvlit any Make object literal
 | ||
|   any cvx any Make object executable
 | ||
|   any xcheck bool     Test executable attribute
 | ||
|   array|packedarray|file|string executeonly array|packedarray|file|string
 | ||
|   Reduce access to execute-only
 | ||
|   array|packedarray|dict|file|string noaccess array|packedarray|dict|file|string
 | ||
|   Disallow any access
 | ||
|   array|packedarray|dict|file|string readonly array|packedarray|dict|file|string
 | ||
|   Reduce access to read-only
 | ||
|   array|packedarray|dict|file|string rcheck bool Test read access
 | ||
|   array|packedarray|dict|file|string wcheck bool Test write access
 | ||
|   num|string cvi int Convert to integer
 | ||
|   string cvn name Convert to name
 | ||
|   num|string cvr real Convert to real
 | ||
|   num radix string cvrs substring Convert with radix to string
 | ||
|   any string cvs substring Convert to string
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteControlOperator(AToken: TExpressionToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| var
 | ||
|   Param1, Param2, Param3, Param4, CounterToken: TPSToken;
 | ||
|   NewToken: TExpressionToken;
 | ||
|   FloatCounter: Double;
 | ||
|   i, lRepeatCount: Integer;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   // any exec –          Execute arbitrary object
 | ||
|   if AToken.StrValue = 'exec' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop); // proc
 | ||
| 
 | ||
|     if (Param1 is TProcedureToken) then
 | ||
|       ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
 | ||
| 
 | ||
|     if (Param1 is TExpressionToken) then
 | ||
|       ExecuteOperatorToken(TExpressionToken(Param1), AData, ADoc, nil); // ToDo: Add next token for image
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Execute proc if bool is true
 | ||
|   if AToken.StrValue = 'if' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop); // proc
 | ||
|     Param2 := TPSToken(Stack.Pop); // bool
 | ||
| 
 | ||
|     if not (Param1 is TProcedureToken) then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator if requires a procedure. Error at line %d', [AToken.Line]));
 | ||
| 
 | ||
|     if Param2.BoolValue then ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Execute proc1 if bool is true, proc2 if false
 | ||
|   if AToken.StrValue = 'ifelse' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop); // proc2
 | ||
|     Param2 := TPSToken(Stack.Pop); // proc1
 | ||
|     Param3 := TPSToken(Stack.Pop); // bool
 | ||
| 
 | ||
|     if not (Param1 is TProcedureToken) then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator ifelse requires a procedure. Error at line %d', [AToken.Line]));
 | ||
|     if not (Param2 is TProcedureToken) then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator ifelse requires a procedure. Error at line %d', [AToken.Line]));
 | ||
| 
 | ||
|     if Param3.BoolValue then ExecuteProcedureToken(TProcedureToken(Param2), AData, ADoc)
 | ||
|     else ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // int proc repeat –   Execute proc int times
 | ||
|   if AToken.StrValue = 'repeat' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop); // proc
 | ||
|     Param2 := TPSToken(Stack.Pop); // num
 | ||
| 
 | ||
|     if not (Param1 is TProcedureToken) then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator repeat requires a procedure. Error at line %d', [AToken.Line]));
 | ||
| 
 | ||
|     lRepeatCount := Round(Param2.FloatValue);
 | ||
|     for i := 0 to lRepeatCount - 1 do
 | ||
|     begin
 | ||
|       ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
 | ||
|       if ExitCalled then
 | ||
|       begin
 | ||
|         ExitCalled := False;
 | ||
|         Break;
 | ||
|       end;
 | ||
|     end;
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Exit innermost active loop
 | ||
|   if AToken.StrValue = 'exit' then
 | ||
|   begin
 | ||
|     ExitCalled := True;
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   {
 | ||
|     Establish context for catching stop
 | ||
| 
 | ||
|      executes any, which is typically, but not necessarily, a procedure, executable file,
 | ||
|      or executable string object. If any runs to completion normally, stopped returns false on the operand stack.
 | ||
| 
 | ||
|      If any terminates prematurely as a result of executing stop, stopped returns
 | ||
|      true on the operand stack. Regardless of the outcome, the interpreter resumes execution at the next object in normal sequence after stopped.
 | ||
|      This mechanism provides an effective way for a PostScript language program
 | ||
|      to "catch" errors or other premature terminations, retain control, and perhaps perform its own error recovery.
 | ||
| 
 | ||
|      EXAMPLE:
 | ||
|      { ... } stopped {handleerror} if
 | ||
| 
 | ||
|      If execution of the procedure {...} causes an error,
 | ||
|      the default error-reporting procedure is invoked (by handleerror).
 | ||
|      In any event, normal execution continues at the token following the if.
 | ||
| 
 | ||
|      ERRORS: stackunderflow
 | ||
|   }
 | ||
|   if AToken.StrValue = 'stopped' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_CONTROL}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecuteControlOperator] stopped');
 | ||
| //    DebugStack();
 | ||
|     {$endif}
 | ||
| 
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     if not (Param1 is TProcedureToken) then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator stopped requires a procedure. Error at line %d', [AToken.Line]));
 | ||
| 
 | ||
|     ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
 | ||
| 
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     NewToken.BoolValue := False;
 | ||
|     NewToken.StrValue := 'false';
 | ||
|     Stack.Push(NewToken);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Execute proc an indefinite number of times
 | ||
|   if AToken.StrValue = 'loop' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     if not (Param1 is TProcedureToken) then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator loop requires a procedure. Error at line %d', [AToken.Line]));
 | ||
| 
 | ||
|     while True do
 | ||
|     begin
 | ||
|       ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
 | ||
| 
 | ||
|       if ExitCalled then
 | ||
|       begin
 | ||
|         ExitCalled := False;
 | ||
|         Break;
 | ||
|       end;
 | ||
|     end;
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   { initial increment limit proc for -
 | ||
| 
 | ||
|    executes proc repeatedly, passing it a sequence of values from initial
 | ||
|    by steps of increment to limit. The for operator expects initial, increment,
 | ||
|    and limit to be numbers. It maintains a temporary internal variable, known as
 | ||
|    the control variable, which it first sets to initial. Then, before each
 | ||
|    repetition, it compares the control variable with the termination value limit.
 | ||
|    If limit has not been exceeded, it pushes the control variable on the operand
 | ||
|    stack, executes proc, and adds increment to the control variable.
 | ||
| 
 | ||
|    The termination condition depends on whether increment is positive or negative.
 | ||
|    If increment is positive, for terminates when the control variable becomes
 | ||
|    greater than limit. If increment is negative, for terminates when the control
 | ||
|    variable becomes less than limit. If initial meets the termination condition,
 | ||
|    for does not execute proc at all. If proc executes the exit operator,
 | ||
|    for terminates prematurely.
 | ||
| 
 | ||
|    Usually, proc will use the value on the operand stack for some purpose.
 | ||
|    However, if proc does not remove the value, it will remain there.
 | ||
|    Successive executions of proc will cause successive values of the control
 | ||
|    variable to accumulate on the operand stack.
 | ||
| 
 | ||
|    EXAMPLE:
 | ||
|    0 1 1 4 {add} for -> 10
 | ||
|    1 2 6 { } for -> 1 3 5
 | ||
|    3 -.5 1 {-> } for -> 3.0 2.5 2.0 1.5 1.0
 | ||
| 
 | ||
|    In the first example, the value of the control variable is added to whatever
 | ||
|    is on the stack, so 1, 2, 3, and 4 are added in turn to a running sum whose
 | ||
|    initial value is 0. The second example has an empty procedure, so the
 | ||
|    successive values of the control variable are left on the stack. The
 | ||
|    last example counts backward from 3 to 1 by halves, leaving the successive
 | ||
|    values on the stack.
 | ||
| 
 | ||
|    Beware of using reals instead of integers for any of the first three operands.
 | ||
|    Most real numbers are not represented exactly. This can cause an error to
 | ||
|    accumulate in the value of the control variable, with possibly surprising results.
 | ||
|    In particular, if the difference between initial and limit is a multiple of
 | ||
|    increment, as in the third line of the example, the control variable may not
 | ||
|    achieve the limit value.
 | ||
| 
 | ||
|    ERRORS: stackoverflow stackunderflow, typecheck
 | ||
| 
 | ||
|    SEE ALSO: repeat, loop, forall, exit
 | ||
|   }
 | ||
|   if AToken.StrValue = 'for' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     Param3 := TPSToken(Stack.Pop);
 | ||
|     Param4 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     if not (Param1 is TProcedureToken) then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator for requires a procedure. Error at line %d', [AToken.Line]));
 | ||
| 
 | ||
|     FloatCounter := Param4.FloatValue;
 | ||
|     while FloatCounter < Param2.FloatValue do
 | ||
|     begin
 | ||
|       CounterToken := Param4.Duplicate();
 | ||
|       CounterToken.FloatValue := FloatCounter;
 | ||
|       Stack.Push(CounterToken);
 | ||
| 
 | ||
|       ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
 | ||
| 
 | ||
|       FloatCounter := FloatCounter + Param3.FloatValue;
 | ||
| 
 | ||
|       if ExitCalled then
 | ||
|       begin
 | ||
|         ExitCalled := False;
 | ||
|         Break;
 | ||
|       end;
 | ||
|     end;
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // any cvx any Make object executable
 | ||
|   if AToken.StrValue = 'cvx' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     if Param1 is TExpressionToken then
 | ||
|       TExpressionToken(Param1).ETType := ettOperator;
 | ||
| 
 | ||
|     Stack.Push(Param1);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // tests whether the operand has the executable or the literal attribute, returning true
 | ||
|   // if it is executable or false if it is literal
 | ||
|   if AToken.StrValue = 'xcheck' then
 | ||
|   begin
 | ||
| //    {$ifdef FPVECTORIALDEBUG_CONTROL}
 | ||
| //    WriteLn('[TvEPSVectorialReader.ExecuteControlOperator] xcheck');
 | ||
| //    DebugStack();
 | ||
| //    {$endif}
 | ||
| 
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     NewToken.BoolValue := (Param1 is TProcedureToken) or
 | ||
|       ((Param1 is TExpressionToken) and (TExpressionToken(Param1).ETType = ettOperator));
 | ||
|     if NewToken.BoolValue then NewToken.StrValue := 'true'
 | ||
|     else NewToken.StrValue := 'false';
 | ||
|     Stack.Push(NewToken);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| {  Painting Operators
 | ||
| 
 | ||
|   – erasepage –   Paint current page white
 | ||
|   – stroke –      Draw line along current path
 | ||
|   – fill –        Fill current path with current color
 | ||
|   – eofill –      Fill using even-odd rule
 | ||
|   x y width height rectstroke – Define rectangular path and stroke
 | ||
|   x y width height matrix rectstroke – Define rectangular path, concatenate matrix,
 | ||
|                                        and stroke
 | ||
|   numarray|numstring rectstroke – Define rectangular paths and stroke
 | ||
|   numarray|numstring matrix rectstroke – Define rectangular paths, concatenate
 | ||
|                                          matrix, and stroke
 | ||
|   x y width height rectfill – Fill rectangular path
 | ||
|   numarray|numstring rectfill – Fill rectangular paths
 | ||
|   userpath ustroke – Interpret and stroke userpath
 | ||
|   userpath matrix ustroke – Interpret userpath, concatenate matrix, and
 | ||
|                             stroke
 | ||
|   userpath ufill – Interpret and fill userpath
 | ||
|   userpath ueofill – Fill userpath using even-odd rule
 | ||
|   dict shfill – Fill area defined by shading pattern
 | ||
|   dict image – Paint any sampled image
 | ||
|   width height bits/sample matrix datasrc image – Paint monochrome sampled image
 | ||
|   width height bits/comp matrix
 | ||
|   datasrc0 … datasrcncomp-1 multi ncomp colorimage – Paint color sampled image
 | ||
|   dict imagemask – Paint current color through mask
 | ||
|   width height polarity matrix datasrc imagemask – Paint current color through mask
 | ||
|   Insideness-Testing Operators
 | ||
|   x y infill bool Test whether (x, y) would be painted by fill
 | ||
|   userpath infill bool Test whether pixels in userpath would be
 | ||
|   painted by fill
 | ||
|   x y ineofill bool Test whether (x, y) would be painted by eofill
 | ||
|   userpath ineofill bool Test whether pixels in userpath would be
 | ||
|   painted by eofill
 | ||
|   x y userpath inufill bool Test whether (x, y) would be painted by ufill
 | ||
|   of userpath
 | ||
|   userpath1 userpath2 inufill bool Test whether pixels in userpath1 would be
 | ||
|   painted by ufill of userpath2
 | ||
|   x y userpath inueofill bool Test whether (x, y) would be painted by
 | ||
|   ueofill of userpath
 | ||
|   userpath1 userpath2 inueofill bool Test whether pixels in userpath1 would be
 | ||
|   painted by ueofill of userpath2
 | ||
|   x y instroke bool Test whether (x, y) would be painted by
 | ||
|   stroke
 | ||
|   x y userpath inustroke bool Test whether (x, y) would be painted by
 | ||
|   ustroke of userpath
 | ||
|   x y userpath matrix inustroke bool Test whether (x, y) would be painted by
 | ||
|   ustroke of userpath
 | ||
|   userpath1 userpath2 inustroke bool Test whether pixels in userpath1 would be
 | ||
|   painted by ustroke of userpath2
 | ||
|   userpath1 userpath2 matrix inustroke bool Test whether pixels in userpath1 would be
 | ||
|   painted by ustroke of userpath2
 | ||
|   Form and Pattern Operators
 | ||
|   pattern matrix makepattern pattern’ Create pattern instance from prototype
 | ||
|   pattern setpattern – Install pattern as current color
 | ||
|   comp1 … compn pattern setpattern – Install pattern as current color
 | ||
|   form execform – Paint form
 | ||
| 
 | ||
|   Other painting operators:
 | ||
| 
 | ||
|   x y width height rectclip –
 | ||
|     numarray rectclip –
 | ||
|     numstring rectclip –
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecutePaintingOperator(AToken: TExpressionToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken): Boolean;
 | ||
| var
 | ||
|   Param1, Param2: TPSToken;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   if AToken.StrValue = 'stroke' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecutePaintingOperator] stroke');
 | ||
|     {$endif}
 | ||
|     AData.SetPenStyle(psSolid);
 | ||
|     AData.SetBrushStyle(bsClear);
 | ||
|     AData.SetPenColor(CurrentGraphicState.Color);
 | ||
|     AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
 | ||
|     AData.SetPenWidth(CurrentGraphicState.PenWidth);
 | ||
|     AData.EndPath();
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // – fill –        Fill current path with current color
 | ||
|   if AToken.StrValue = 'fill' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecutePaintingOperator] fill');
 | ||
|     {$endif}
 | ||
|     AData.SetBrushStyle(bsSolid);
 | ||
|     AData.SetPenStyle(psSolid);
 | ||
|     AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
 | ||
|     AData.SetPenWidth(CurrentGraphicState.PenWidth);
 | ||
|     AData.EndPath();
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // – eofill –      Fill using even-odd rule
 | ||
|   if AToken.StrValue = 'eofill' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecutePaintingOperator] eofill');
 | ||
|     {$endif}
 | ||
|     AData.SetBrushStyle(bsSolid);
 | ||
|     AData.SetPenStyle(psSolid);
 | ||
|     AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
 | ||
|     AData.SetPenWidth(CurrentGraphicState.PenWidth);
 | ||
|     AData.EndPath();
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // dict image – Paint any sampled image
 | ||
|   if AToken.StrValue = 'image' then
 | ||
|   begin
 | ||
|     Result := ExecuteImageOperator(AToken, AData, ADoc, ANextToken);
 | ||
|   end;
 | ||
|   //x y width height rectclip –
 | ||
|   //  numarray rectclip –
 | ||
|   //  numstring rectclip –
 | ||
|   if AToken.StrValue = 'rectclip' then
 | ||
|   begin
 | ||
|     // ToDo: Check for numarray and numstring
 | ||
|     // Todo: Implement properly
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| // The "image" operator is very complex, so we have a separate routine only for it =)
 | ||
| function TvEPSVectorialReader.ExecuteImageOperator(AToken: TExpressionToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken
 | ||
|   ): Boolean;
 | ||
| var
 | ||
|   Param1, Param2: TPSToken;
 | ||
|   // image operator data
 | ||
|   lRasterImage: TvRasterImage;
 | ||
|   lColor: TFPColor;
 | ||
|   i, x, y, lFindIndex: Integer;
 | ||
|   lDataSource, lImageDataStr: String;
 | ||
|   lImageType, lImageWidth, lImageHeight, lImageBitsPerComponent: Integer;
 | ||
|   lImageData, lImageDataCompressed: array of Byte;
 | ||
|   lCurDictToken: TPSToken;
 | ||
|   lColorC, lColorM, lColorY, lColorK: Double;
 | ||
|   lImageMatrix: TArrayToken;
 | ||
|   lMatrixTranslateX, lMatrixTranslateY, lMatrixScaleX, lMatrixScaleY,
 | ||
|     lMatrixSkewX, lMatrixSkewY, lMatrixRotate: Double;
 | ||
| begin
 | ||
|   Result := False;
 | ||
|   Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|   // Decode the dictionary into a list of names
 | ||
|   if not (Param1 is TDictionaryToken) then
 | ||
|     raise Exception.Create(Format('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Param1 is not a dictionary but should be. Param1.ClassName=%s', [Param1.ClassName]));
 | ||
|   TDictionaryToken(Param1).TransformToListOfNamedValues();
 | ||
| 
 | ||
|   // Read the source of the data
 | ||
|   TDictionaryToken(Param1).Names.Sorted := True;
 | ||
|   if TDictionaryToken(Param1).Names.Find('DataSource', lFindIndex) then
 | ||
|   begin
 | ||
|     lDataSource := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]).StrValue;
 | ||
|     if not (lDataSource = 'currentfile') then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Unimplemented data source: %s', [lDataSource]));
 | ||
|   end
 | ||
|   else
 | ||
|   begin
 | ||
|     // suppose that the source is the current file
 | ||
|   end;
 | ||
| 
 | ||
|   // Decode the image
 | ||
|   if ANextToken = nil then raise Exception.Create('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Image contents expected but nothing found.');
 | ||
|   if not (ANextToken is TExpressionToken) then raise Exception.Create('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Image contents is not a TExpressionToken.');
 | ||
|   if TExpressionToken(ANextToken).ETType <> ettRawData then raise Exception.Create('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Image contents is not a raw data.');
 | ||
|   lImageDataStr := TExpressionToken(ANextToken).StrValue;
 | ||
|   SetLength(lImageDataStr, Length(lImageDataStr)-2); // Remove the final ~>
 | ||
|   {$ifdef FPVECTORIAL_DEFLATE_DEBUG}
 | ||
|   FPVUDebugLn('[image] ImageDataStr='+lImageDataStr);
 | ||
|   {$endif}
 | ||
| 
 | ||
|   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('ASCII85Decode');
 | ||
|   if lFindIndex > 0 then
 | ||
|   begin
 | ||
|     DecodeASCII85(lImageDataStr, lImageData);
 | ||
|   end;
 | ||
| 
 | ||
|   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('FlateDecode');
 | ||
|   if lFindIndex > 0 then
 | ||
|   begin
 | ||
|     if Length(lImageData) = 0 then raise Exception.Create('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: no byte array prepared for FlateDecode. ASCII85Decode is missing.');
 | ||
|     lImageDataCompressed := lImageData;
 | ||
|     SetLength(lImageData, 0);
 | ||
|     DeflateBytes(lImageDataCompressed, lImageData);
 | ||
|   end;
 | ||
| 
 | ||
|   // Dictionary information
 | ||
|   lImageType := 1;
 | ||
|   lImageWidth := 0;
 | ||
|   lImageHeight := 0;
 | ||
|   lImageBitsPerComponent := 0;
 | ||
|   lImageMatrix := nil;
 | ||
|   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('ImageType');
 | ||
|   // debug dump all dictionary names
 | ||
|   {$ifdef FPVECTORIAL_IMAGE_DICTIONARY_DEBUG}
 | ||
|   FPVUDebug('TDictionaryToken(Param1).Names=');
 | ||
|   for i := 0 to TDictionaryToken(Param1).Names.Count-1 do
 | ||
|   begin
 | ||
|     FPVUDebug(TDictionaryToken(Param1).Names.Strings[i]+' ');
 | ||
|   end;
 | ||
|   FPVUDebugLn('');
 | ||
|   {$endif}
 | ||
|   if lFindIndex > 0 then
 | ||
|   begin
 | ||
|     lCurDictToken := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]);
 | ||
|     lCurDictToken.PrepareIntValue();
 | ||
|     lImageType := lCurDictToken.IntValue;
 | ||
|   end;
 | ||
|   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('Width');
 | ||
|   if lFindIndex > 0 then
 | ||
|   begin
 | ||
|     lCurDictToken := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]);
 | ||
|     lCurDictToken.PrepareIntValue();
 | ||
|     lImageWidth := lCurDictToken.IntValue;
 | ||
|   end;
 | ||
|   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('Height');
 | ||
|   if lFindIndex > 0 then
 | ||
|   begin
 | ||
|     lCurDictToken := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]);
 | ||
|     lCurDictToken.PrepareIntValue();
 | ||
|     lImageHeight := lCurDictToken.IntValue;
 | ||
|   end;
 | ||
|   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('BitsPerComponent');
 | ||
|   if lFindIndex > 0 then
 | ||
|   begin
 | ||
|     lCurDictToken := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]);
 | ||
|     lCurDictToken.PrepareIntValue();
 | ||
|     lImageBitsPerComponent := lCurDictToken.IntValue;
 | ||
|   end;
 | ||
|   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('ImageMatrix');
 | ||
|   if lFindIndex > 0 then
 | ||
|   begin
 | ||
|     lImageMatrix := TArrayToken(TDictionaryToken(Param1).Values[lFindIndex]);
 | ||
|   end;
 | ||
| 
 | ||
|   // Read the image
 | ||
|   lRasterImage := TvRasterImage.Create(nil);
 | ||
|   lRasterImage.CreateRGB888Image(lImageWidth, lImageHeight);
 | ||
|   if CurrentGraphicState.ColorSpaceName = 'DeviceCMYK' then
 | ||
|   begin
 | ||
|     if (lImageWidth*lImageHeight)*4 > Length(lImageData) then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: image data too small. Expected=%d Found=%d', [Length(lImageData), (lImageWidth*lImageHeight)*4]));
 | ||
|     for x := 0 to lImageWidth - 1 do
 | ||
|       for y := 0 to lImageHeight - 1 do
 | ||
|       begin
 | ||
|         lColorC := lImageData[(x+y*lImageWidth)*4] / $FF;
 | ||
|         lColorM := lImageData[(x+y*lImageWidth)*4+1] / $FF;
 | ||
|         lColorY := lImageData[(x+y*lImageWidth)*4+2] / $FF;
 | ||
|         lColorK := lImageData[(x+y*lImageWidth)*4+3] / $FF;
 | ||
|         lColor.Alpha := alphaOpaque;
 | ||
|         lColor.Red := Round($FF * (1-lColorC) * (1-lColorK) * $101);
 | ||
|         lColor.Green := Round($FF * (1-lColorM) * (1-lColorK) * $101);
 | ||
|         lColor.Blue := Round($FF * (1-lColorY) * (1-lColorK) * $101);
 | ||
|         lRasterImage.RasterImage.Colors[x, y] := lColor;
 | ||
|       end;
 | ||
|   end
 | ||
|   else
 | ||
|   begin
 | ||
|     if (lImageWidth*lImageHeight)*3 > Length(lImageData) then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: image data too small. Expected=%d Found=%d', [Length(lImageData), (lImageWidth*lImageHeight)*3]));
 | ||
|     for x := 0 to lImageWidth - 1 do
 | ||
|       for y := 0 to lImageHeight - 1 do
 | ||
|       begin
 | ||
|         lColor.Alpha := alphaOpaque;
 | ||
|         lColor.Red := lImageData[(x+y*lImageWidth)*3] * $101;
 | ||
|         lColor.Green := lImageData[(x+y*lImageWidth)*3+1] * $101;
 | ||
|         lColor.Blue := lImageData[(x+y*lImageWidth)*3+2] * $101;
 | ||
|         lRasterImage.RasterImage.Colors[x, y] := lColor;
 | ||
|       end;
 | ||
|   end;
 | ||
| 
 | ||
|   // Get information from the ImageMatrix
 | ||
|   // for example:   1  b c   d     f
 | ||
|   // /ImageMatrix [163 0 0 -134 0 134]
 | ||
|   //                       (163   0    0  )
 | ||
|   // means that we have:   ( 0  -134  134 )
 | ||
|   //                       ( 0    0    1  )
 | ||
|   // which means:
 | ||
|   // TranslateY(134)
 | ||
|   // scaleX(163)
 | ||
|   // scaleY(-134)
 | ||
|   // all inverted, since the matrix is user->image
 | ||
|   // and we want image->user
 | ||
|   if lImageMatrix <> nil then
 | ||
|   begin
 | ||
|     ConvertTransformationMatrixToOperations(
 | ||
|       lImageMatrix.GetNumber(0), lImageMatrix.GetNumber(1),
 | ||
|       lImageMatrix.GetNumber(2), lImageMatrix.GetNumber(3),
 | ||
|       lImageMatrix.GetNumber(4), lImageMatrix.GetNumber(5),
 | ||
|       lMatrixTranslateX, lMatrixTranslateY, lMatrixScaleX, lMatrixScaleY,
 | ||
|       lMatrixSkewX, lMatrixSkewY, lMatrixRotate);
 | ||
|     InvertMatrixOperations(
 | ||
|       lMatrixTranslateX, lMatrixTranslateY, lMatrixScaleX, lMatrixScaleY,
 | ||
|       lMatrixSkewX, lMatrixSkewY, lMatrixRotate);
 | ||
|   end
 | ||
|   else
 | ||
|   begin
 | ||
|     lMatrixTranslateX := 0;
 | ||
|     lMatrixTranslateY := 0;
 | ||
|     lMatrixScaleX := 1;
 | ||
|     lMatrixScaleY := 1;
 | ||
|     lMatrixSkewX := 0;
 | ||
|     lMatrixSkewY := 0;
 | ||
|     lMatrixRotate := 0;
 | ||
|   end;
 | ||
| 
 | ||
|   // Image data read from the CurrentGraphicState
 | ||
|   lRasterImage.X := CurrentGraphicState.TranslateX;// + lMatrixTranslateX) * CurrentGraphicState.ScaleX * lMatrixScaleX;
 | ||
|   lRasterImage.Y := CurrentGraphicState.TranslateY;//- lMatrixTranslateY;// * CurrentGraphicState.ScaleY * lMatrixScaleY;
 | ||
|   lRasterImage.Width := lImageWidth * CurrentGraphicState.ScaleX * lMatrixScaleX;
 | ||
|   lRasterImage.Height := lImageHeight * CurrentGraphicState.ScaleY * lMatrixScaleY;
 | ||
|   // EPS X,Y position of the image is the lower-left corner, but FPVectorial uses top-left
 | ||
|   lRasterImage.Y := lRasterImage.Y + Abs(lRasterImage.Height);
 | ||
|   // Height again if the image was stretched with inversion
 | ||
|   //if lRasterImage.Height < 0 then
 | ||
|   //  lRasterImage.Y := lRasterImage.Y - lRasterImage.Height;
 | ||
| 
 | ||
|   AData.AddEntity(lRasterImage);
 | ||
| 
 | ||
|   Exit(True);
 | ||
| end;
 | ||
| 
 | ||
| { Device Setup and Output Operators
 | ||
| 
 | ||
|   – showpage – Transmit and reset current page
 | ||
|   – copypage – Transmit current page
 | ||
|   dict setpagedevice – Install page-oriented output device
 | ||
|   – currentpagedevice dict Return current page device parameters
 | ||
|   – nulldevice – Install no-output device
 | ||
|   Glyph and Font Operators
 | ||
|   key font|cidfont definefont font|cidfont Register font|cidfont in Font resource
 | ||
|   category
 | ||
|   key name|string|dict array composefont font Register composite font dictionary created
 | ||
|   from CMap and array of CIDFonts or fonts
 | ||
|   key undefinefont – Remove Font resource registration
 | ||
|   key findfont font|cidfont Return Font resource instance identified by
 | ||
|   key
 | ||
|   font|cidfont scale scalefont font¢|cidfont¢ Scale font|cidfont by scale to produce
 | ||
|   font¢|cidfont¢
 | ||
|   font|cidfont matrix makefont font¢|cidfont¢ Transform font|cidfont by matrix to produce
 | ||
|   font¢|cidfont¢
 | ||
|   font|cidfont setfont – Set font or CIDFont in graphics state
 | ||
|   – rootfont font|cidfont Return last set font or CIDFont
 | ||
|   – currentfont font|cidfont Return current font or CIDFont, possibly a
 | ||
|   descendant of rootfont
 | ||
|   key scale|matrix selectfont – Set font or CIDFont given name and
 | ||
|   transform
 | ||
|   string show – Paint glyphs for string in current font
 | ||
|   ax ay string ashow – Add (ax , ay) to width of each glyph while
 | ||
|   showing string
 | ||
|   cx cy char string widthshow – Add (cx , cy) to width of glyph for char while
 | ||
|   showing string
 | ||
|   cx cy char ax ay string awidthshow – Combine effects of ashow and widthshow
 | ||
|   string numarray|numstring xshow – Paint glyphs for string using x widths in
 | ||
|   numarray|numstring
 | ||
|   string numarray|numstring xyshow – Paint glyphs for string using x and y widths
 | ||
|   in numarray|numstring
 | ||
|   string numarray|numstring yshow – Paint glyphs for string using y widths in
 | ||
|   numarray|numstring
 | ||
|   name|cid glyphshow – Paint glyph for character identified by
 | ||
|   name|cid
 | ||
|   string stringwidth wx wy Return width of glyphs for string in current
 | ||
|   font
 | ||
|   proc string cshow – Invoke character mapping algorithm and
 | ||
|   call proc
 | ||
|   proc string kshow – Execute proc between characters shown from
 | ||
|   string
 | ||
|   – FontDirectory dict Return dictionary of Font resource instances
 | ||
|   – GlobalFontDirectory dict Return dictionary of Font resource instances
 | ||
|   in global VM
 | ||
|   – StandardEncoding array Return Adobe standard font encoding vector
 | ||
|   – ISOLatin1Encoding array Return ISO Latin-1 font encoding vector
 | ||
|   key findencoding array Find encoding vector
 | ||
|   wx wy llx lly urx ury setcachedevice – Declare cached glyph metrics
 | ||
|   w0x w0y llx lly urx ury
 | ||
|   w1x w1y vx vy setcachedevice2 – Declare cached glyph metrics
 | ||
|   wx wy setcharwidth – Declare uncached glyph metrics
 | ||
|   Interpreter Parameter Operators
 | ||
|   dict setsystemparams – Set systemwide interpreter parameters
 | ||
|   – currentsystemparams dict Return systemwide interpreter parameters
 | ||
|   dict setuserparams – Set per-context interpreter parameters
 | ||
|   – currentuserparams dict Return per-context interpreter parameters
 | ||
|   string dict setdevparams – Set parameters for input/output device
 | ||
|   string currentdevparams dict Return device parameters
 | ||
|   int vmreclaim – Control garbage collector
 | ||
|   int setvmthreshold – Control garbage collector
 | ||
|   – vmstatus level used maximum
 | ||
|   Report VM status
 | ||
|   – cachestatus bsize bmax msize mmax csize cmax blimit
 | ||
|   Return font cache status and parameters
 | ||
|   int setcachelimit – Set maximum bytes in cached glyph
 | ||
|   mark size lower upper setcacheparams – Set font cache parameters
 | ||
|   – currentcacheparams mark size lower upper
 | ||
|   Return current font cache parameters
 | ||
|   mark blimit setucacheparams – Set user path cache parameters
 | ||
|   – ucachestatus mark bsize bmax rsize rmax blimit
 | ||
|   Return user path cache status and
 | ||
|   parameters
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteDeviceSetupAndOutputOperator(
 | ||
|   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| var
 | ||
|   Param1, Param2: TPSToken;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   if AToken.StrValue = 'showpage' then
 | ||
|   begin
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| { Array Operators
 | ||
| 
 | ||
|   int array array Create array of length int
 | ||
|   – [ mark Start array construction
 | ||
|   mark obj0 … objn-1 ] array End array construction
 | ||
|   array length int Return number of elements in array
 | ||
|   array index get any Return array element indexed by index
 | ||
|   array index any put – Put any into array at index
 | ||
|   array index count getinterval subarray Return subarray of array starting at index for
 | ||
|   count elements
 | ||
|   array1 index array2|packedarray2 putinterval – Replace subarray of array1 starting at index
 | ||
|   by array2|packedarray2
 | ||
|   any0 … anyn-1 array astore array Pop elements from stack into array
 | ||
|   array aload any0 … anyn-1 array Push all elements of array on stack
 | ||
|   array1 array2 copy subarray2 Copy elements of array1 to initial subarray of
 | ||
|   array2
 | ||
|   array proc forall – Execute proc for each element of array
 | ||
|   Packed Array Operators
 | ||
|   any0 … anyn-1 n packedarray packedarray Create packed array consisting of n elements
 | ||
|   from stack
 | ||
|   bool setpacking – Set array packing mode for { … } syntax
 | ||
|   (true = packed array)
 | ||
|   – currentpacking bool Return array packing mode
 | ||
|   packedarray length int Return number of elements in packedarray
 | ||
|   packedarray index get any Return packedarray element indexed by index
 | ||
|   packedarray index count getinterval subarray Return subarray of packedarray starting at
 | ||
|   index for count elements
 | ||
|   packedarray aload any0 … anyn-1 packedarray
 | ||
|   Push all elements of packedarray on stack
 | ||
|   packedarray1 array2 copy subarray2 Copy elements of packedarray1 to initial
 | ||
|   subarray of array2
 | ||
|   packedarray proc forall – Execute proc for each element of packedarray
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteArrayOperator(AToken: TExpressionToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
| end;
 | ||
| 
 | ||
| { String Operators
 | ||
| 
 | ||
|   int string string Create string of length int
 | ||
|   string length int Return number of elements in string
 | ||
|   string index get int Return string element indexed by index
 | ||
|   string index int put – Put int into string at index
 | ||
|   string index count getinterval substring Return substring of string starting at index
 | ||
|   for count elements
 | ||
|   string1 index string2 putinterval – Replace substring of string1 starting at index
 | ||
|   by string2
 | ||
|   string1 string2 copy substring2 Copy elements of string1 to initial substring
 | ||
|   of string2
 | ||
|   string proc forall – Execute proc for each element of string
 | ||
|   string seek anchorsearch post match true Search for seek at start of string
 | ||
|   or string false
 | ||
|   string seek search post match pre true Search for seek in string
 | ||
|   or string false
 | ||
|   string token post any true Read token from start of string
 | ||
|   or false
 | ||
|   Relational, Boolean, and Bitwise Operators
 | ||
|   any1 any2 eq bool Test equal
 | ||
|   any1 any2 ne bool Test not equal
 | ||
|   num1|str1 num2|str2 ge bool Test greater than or equal
 | ||
|   num1|str1 num2|str2 gt bool Test greater than
 | ||
|   num1|str1 num2|str2 le bool Test less than or equal
 | ||
|   num1|str1 num2|str2 lt bool Test less than
 | ||
|   bool1|int1 bool2|int2 and bool3|int3 Perform logical|bitwise and
 | ||
|   bool1|int1 not bool2|int2 Perform logical|bitwise not
 | ||
|   bool1|int1 bool2|int2 or bool3|int3 Perform logical|bitwise inclusive or
 | ||
|   bool1|int1 bool2|int2 xor bool3|int3 Perform logical|bitwise exclusive or
 | ||
|   – true true Return boolean value true
 | ||
|   – false false Return boolean value false
 | ||
|   int1 shift bitshift int2 Perform bitwise shift of int1 (positive is left)
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteStringOperator(AToken: TExpressionToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| var
 | ||
|   Param1, Param2: TPSToken;
 | ||
|   NewToken: TExpressionToken;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   // any1 any2 ne bool Test not equal
 | ||
|   if AToken.StrValue = 'ne' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     NewToken.BoolValue := Param1.StrValue = Param2.StrValue;
 | ||
|     if NewToken.BoolValue then NewToken.StrValue := 'true'
 | ||
|     else NewToken.StrValue := 'false';
 | ||
|     Stack.Push(NewToken);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // num1 num2 lt bool
 | ||
|   // string1 string2 lt bool
 | ||
|   // pops two objects from the operand stack and pushes true if the first operand is less
 | ||
|   // than the second, or false otherwise. If both operands are numbers, lt compares
 | ||
|   // their mathematical values. If both operands are strings, lt compares them element
 | ||
|   // by element, treating the elements as integers in the range 0 to 255, to determine
 | ||
|   // whether the first string is lexically less than the second. If the operands are of
 | ||
|   // other types or one is a string and the other is a number, a typecheck error occurs.
 | ||
|   if AToken.StrValue = 'lt' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     NewToken.BoolValue := Param1.FloatValue > Param2.FloatValue;
 | ||
|     if NewToken.BoolValue then NewToken.StrValue := 'true'
 | ||
|     else NewToken.StrValue := 'false';
 | ||
|     Stack.Push(NewToken);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| {  File Operators
 | ||
| 
 | ||
|   filename access file file Open named file with specified access
 | ||
|   datasrc|datatgt dict
 | ||
|   param1 … paramn filtername filter file Establish filtered file
 | ||
|   file closefile – Close file
 | ||
|   file read int true Read one character from file
 | ||
|   or false
 | ||
|   file int write – Write one character to file
 | ||
|   file string readhexstring substring bool Read hexadecimal numbers from file into
 | ||
|   string
 | ||
|   file string writehexstring – Write string to file as hexadecimal
 | ||
|   file string readstring substring bool Read string from file
 | ||
|   file string writestring – Write string to file
 | ||
|   file string readline substring bool Read line from file into string
 | ||
|   file token any true Read token from file
 | ||
|   or false
 | ||
|   file bytesavailable int Return number of bytes available to read
 | ||
|   – flush – Send buffered data to standard output file
 | ||
|   file flushfile – Send buffered data or read to EOF
 | ||
|   file resetfile – Discard buffered characters
 | ||
|   file status bool Return status of file (true = valid)
 | ||
|   filename status pages bytes referenced created true
 | ||
|   or false Return information about named file
 | ||
|   filename run – Execute contents of named file
 | ||
|   – currentfile file Return file currently being executed
 | ||
|   filename deletefile – Delete named file
 | ||
|   filename1 filename2 renamefile – Rename file filename1 to filename2
 | ||
|   template proc scratch filenameforall – Execute proc for each file name matching
 | ||
|   template
 | ||
|   file position setfileposition – Set file to specified position
 | ||
|   file fileposition position Return current position in file
 | ||
|   string print – Write string to standard output file
 | ||
|   any = – Write text representation of any to standard
 | ||
|   output file
 | ||
|   any == – Write syntactic representation of any to
 | ||
|   standard output file
 | ||
|   any1 … anyn stack any1 … anyn Print stack nondestructively using =
 | ||
|   any1 … anyn pstack any1 … anyn Print stack nondestructively using ==
 | ||
|   obj tag printobject – Write binary object to standard output file,
 | ||
|   using tag
 | ||
|   file obj tag writeobject – Write binary object to file, using tag
 | ||
|   int setobjectformat – Set binary object format (0 = disable,
 | ||
|   1 = IEEE high, 2 = IEEE low, 3 = native
 | ||
|   high, 4 = native low)
 | ||
|   – currentobjectformat int Return binary object format
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteFileOperator(AToken: TExpressionToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
| end;
 | ||
| 
 | ||
| { Resource Operators
 | ||
| 
 | ||
|   key instance category defineresource instance Register named resource instance in category
 | ||
|   key category undefineresource – Remove resource registration
 | ||
|   key category findresource instance Return resource instance identified by key in
 | ||
|   category
 | ||
|   renderingintent findcolorrendering name bool Select CIE-based color rendering dictionary
 | ||
|   by rendering intent
 | ||
|   key category resourcestatus status size true Return status of resource instance
 | ||
|   or false
 | ||
|   template proc scratch category resourceforall – Enumerate resource instances in category
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteResourceOperator(AToken: TExpressionToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
| end;
 | ||
| 
 | ||
| { Virtual Memory Operators
 | ||
| 
 | ||
|   – save save Create VM snapshot
 | ||
|   save restore – Restore VM snapshot
 | ||
|   bool setglobal – Set VM allocation mode (false = local,
 | ||
|   true = global)
 | ||
|   – currentglobal bool Return current VM allocation mode
 | ||
|   any gcheck bool Return true if any is simple or in global VM,
 | ||
|   false if in local VM
 | ||
|   bool1 password startjob bool2 Start new job that will alter initial VM if
 | ||
|   bool1 is true
 | ||
|   index any defineuserobject – Define user object associated with index
 | ||
|   index execuserobject – Execute user object associated with index
 | ||
|   index undefineuserobject – Remove user object associated with index
 | ||
|   – UserObjects array Return current UserObjects array defined in
 | ||
|   userdict
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteVirtualMemoryOperator(
 | ||
|   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument
 | ||
|   ): Boolean;
 | ||
| var
 | ||
|   Param1, Param2: TPSToken;
 | ||
|   NewToken: TExpressionToken;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   //– save save Create save snapshot
 | ||
|   if AToken.StrValue = 'save' then
 | ||
|   begin
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettVirtualMemorySnapshot;
 | ||
|     Stack.Push(NewToken);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   //save restore – Restore VM snapshot
 | ||
|   if AToken.StrValue = 'restore' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param1.Free;
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| { Errors
 | ||
| 
 | ||
|   configurationerror setpagedevice or setdevparams request
 | ||
|   cannot be satisfied
 | ||
|   dictfull No more room in dictionary
 | ||
|   dictstackoverflow Too many begin operators
 | ||
|   dictstackunderflow Too many end operators
 | ||
|   execstackoverflow Executive stack nesting too deep
 | ||
|   handleerror Called to report error information
 | ||
|   interrupt External interrupt request (for example,
 | ||
|   Control-C)
 | ||
|   invalidaccess Attempt to violate access attribute
 | ||
|   invalidexit exit not in loop
 | ||
|   invalidfileaccess Unacceptable access string
 | ||
|   invalidfont Invalid Font resource name or font or
 | ||
|   CIDFont dictionary
 | ||
|   invalidrestore Improper restore
 | ||
|   ioerror Input/output error
 | ||
|   limitcheck Implementation limit exceeded
 | ||
|   nocurrentpoint Current point undefined
 | ||
|   rangecheck Operand out of bounds
 | ||
|   stackoverflow Operand stack overflow
 | ||
|   stackunderflow Operand stack underflow
 | ||
|   syntaxerror PostScript language syntax error
 | ||
|   timeout Time limit exceeded
 | ||
|   typecheck Operand of wrong type
 | ||
|   undefined Name not known
 | ||
|   undefinedfilename File not found
 | ||
|   undefinedresource Resource instance not found
 | ||
|   undefinedresult Overflow, underflow, or meaningless result
 | ||
|   unmatchedmark Expected mark not on stack
 | ||
|   unregistered Internal error
 | ||
|   VMerror Virtual memory exhausted
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteErrorOperator(AToken: TExpressionToken;
 | ||
|   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
| end;
 | ||
| 
 | ||
| {  Arithmetic and Math Operators
 | ||
| 
 | ||
|   num1 num2 add sum        Return num1 plus num2
 | ||
|   num1 num2 div quotient   Return num1 divided by num2
 | ||
|   int1 int2 idiv quotient  Return int1 divided by int2
 | ||
|   int1 int2 mod remainder  Return remainder after dividing int1 by int2
 | ||
|   num1 num2 mul product    Return num1 times num2
 | ||
|   num1 num2 sub difference Return num1 minus num2
 | ||
|   num1 abs num2            Return absolute value of num1
 | ||
|   num1 neg num2            Return negative of num1
 | ||
|   num1 ceiling num2        Return ceiling of num1
 | ||
|   num1 floor num2          Return floor of num1
 | ||
|   num1 round num2          Round num1 to nearest integer
 | ||
|   num1 truncate num2       Remove fractional part of num1
 | ||
|   num sqrt real            Return square root of num
 | ||
|   num den atan angle       Return arctangent of num/den in degrees
 | ||
|   angle cos real           Return cosine of angle degrees
 | ||
|   angle sin real           Return sine of angle degrees
 | ||
|   base exponent exp real   Raise base to exponent power
 | ||
|   num ln real              Return natural logarithm (base e)
 | ||
|   num log real             Return common logarithm (base 10)
 | ||
|   – rand int               Generate pseudo-random integer
 | ||
|   int srand –              Set random number seed
 | ||
|   – rrand int              Return random number seed
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteArithmeticAndMathOperator(
 | ||
|   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| var
 | ||
|   Param1, Param2: TPSToken;
 | ||
|   NewToken: TExpressionToken;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   // Division
 | ||
|   // Param2 Param1 div ==> (Param2 div Param1)
 | ||
|   if AToken.StrValue = 'div' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     NewToken.FloatValue := Param2.FloatValue / Param1.FloatValue;
 | ||
|     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
 | ||
|     Stack.Push(NewToken);
 | ||
|     {$ifdef FPVECTORIALDEBUG_ARITHMETIC}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecuteArithmeticAndMathOperator] %f %f div %f', [Param2.FloatValue, Param1.FloatValue, NewToken.FloatValue]));
 | ||
|     {$endif}
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| 
 | ||
|   // Param2 Param1 mul ==> (Param2 mul Param1)
 | ||
|   if AToken.StrValue = 'mul' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     NewToken.FloatValue := Param2.FloatValue * Param1.FloatValue;
 | ||
|     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // num1 num2 sub difference Return num1 minus num2
 | ||
|   if AToken.StrValue = 'sub' then
 | ||
|   begin
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     Param1 := TPSToken(Stack.Pop); // num2
 | ||
|     Param2 := TPSToken(Stack.Pop); // num1
 | ||
|     NewToken.FloatValue := Param2.FloatValue - Param1.FloatValue;
 | ||
|     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   //num1 abs num2            Return absolute value of num1
 | ||
|   if AToken.StrValue = 'abs' then
 | ||
|   begin
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     Param1 := TPSToken(Stack.Pop); // num1
 | ||
|     NewToken.FloatValue := Abs(Param1.FloatValue);
 | ||
|     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Param1.Free;
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   //num1 neg num2            Return negative of num1
 | ||
|   if AToken.StrValue = 'neg' then
 | ||
|   begin
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     Param1 := TPSToken(Stack.Pop); // num1
 | ||
|     NewToken.FloatValue := -1 * Param1.FloatValue;
 | ||
|     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Param1.Free;
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   //num1 ceiling num2        Return ceiling of num1
 | ||
|   if AToken.StrValue = 'ceiling' then
 | ||
|   begin
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     Param1 := TPSToken(Stack.Pop); // num1
 | ||
|     NewToken.FloatValue := Ceil(Param1.FloatValue);
 | ||
|     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Param1.Free;
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   //num1 floor num2          Return floor of num1
 | ||
|   if AToken.StrValue = 'floor' then
 | ||
|   begin
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     Param1 := TPSToken(Stack.Pop); // num1
 | ||
|     NewToken.FloatValue := Trunc(Param1.FloatValue);
 | ||
|     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Param1.Free;
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   //num1 round num2          Round num1 to nearest integer
 | ||
|   if AToken.StrValue = 'round' then
 | ||
|   begin
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     Param1 := TPSToken(Stack.Pop); // num1
 | ||
|     NewToken.FloatValue := Round(Param1.FloatValue);
 | ||
|     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Param1.Free;
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   //num1 truncate num2       Remove fractional part of num1
 | ||
|   if AToken.StrValue = 'truncate' then
 | ||
|   begin
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     Param1 := TPSToken(Stack.Pop); // num1
 | ||
|     NewToken.FloatValue := Trunc(Param1.FloatValue);
 | ||
|     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Param1.Free;
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| { Path Construction Operators
 | ||
| 
 | ||
|   – newpath –              Initialize current path to be empty
 | ||
|   – currentpoint x y       Return current point coordinates
 | ||
|   x y moveto –             Set current point to (x, y)
 | ||
|   dx dy rmoveto –          Perform relative moveto
 | ||
|   x y lineto –             Append straight line to (x, y)
 | ||
|   dx dy rlineto –          Perform relative lineto
 | ||
|   x y r angle1 angle2 arc – Append counterclockwise arc
 | ||
|   x y r angle1 angle2 arcn – Append clockwise arc
 | ||
|   x1 y1 x2 y2 r arct –     Append tangent arc
 | ||
|   x1 y1 x2 y2 r arcto xt1 yt1 xt2 yt2 Append tangent arc
 | ||
|   x1 y1 x2 y2 x3 y3 curveto – Append Bézier cubic section
 | ||
|   dx1 dy1 dx2 dy2 dx3 dy3 rcurveto – Perform relative curveto
 | ||
|   – closepath –            Connect subpath back to its starting point
 | ||
|   – flattenpath –          Convert curves to sequences of straight lines
 | ||
|   – reversepath –          Reverse direction of current path
 | ||
|   – strokepath –           Compute outline of stroked path
 | ||
|   userpath ustrokepath – Compute outline of stroked userpath
 | ||
|   userpath matrix ustrokepath – Compute outline of stroked userpath
 | ||
|   string bool charpath – Append glyph outline to current path
 | ||
|   userpath uappend – Interpret userpath and append to current
 | ||
|   path
 | ||
|   – clippath – Set current path to clipping path
 | ||
|   llx lly urx ury setbbox – Set bounding box for current path
 | ||
|   – pathbbox llx lly urx ury Return bounding box of current path
 | ||
|   move line curve close pathforall – Enumerate current path
 | ||
|   bool upath userpath Create userpath for current path; include
 | ||
|   ucache if bool is true
 | ||
|   – initclip – Set clipping path to device default
 | ||
|   – clip – Clip using nonzero winding number rule
 | ||
|   – eoclip – Clip using even-odd rule
 | ||
|   x y width height rectclip – Clip with rectangular path
 | ||
|   numarray|numstring rectclip – Clip with rectangular paths
 | ||
|   – ucache – Declare that user path is to be cached
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecutePathConstructionOperator(
 | ||
|   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| var
 | ||
|   Param1, Param2, Param3, Param4, Param5, Param6: TPSToken;
 | ||
|   PosX, PosY, PosX2, PosY2, PosX3, PosY3, BaseX, BaseY: Double;
 | ||
|   // For Arc
 | ||
|   P1, P2, P3, P4: T3DPoint;
 | ||
|   startAngle, endAngle: Double;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   // – newpath –              Initialize current path to be empty
 | ||
|   if AToken.StrValue = 'newpath' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecutePathConstructionOperator] newpath');
 | ||
|     {$endif}
 | ||
| //    AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
 | ||
| //    AData.SetPenWidth(CurrentGraphicState.PenWidth);
 | ||
| //    AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
 | ||
|     AData.SetBrushStyle(bsClear);
 | ||
|     AData.SetPenStyle(psClear);
 | ||
|     AData.EndPath();
 | ||
|     AData.StartPath();
 | ||
| 
 | ||
|     AData.SetPenColor(CurrentGraphicState.Color);
 | ||
|     AData.SetBrushColor(CurrentGraphicState.Color);
 | ||
|     AData.SetPenStyle(psClear);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Param2 Param1 moveto - ===> moveto(X=Param2, Y=Param1);
 | ||
|   if AToken.StrValue = 'moveto' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX, PosY);
 | ||
|     PostScriptCoordsToFPVectorialCoordsWithCGS(Param1, Param2, PosX2, PosY2);
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] moveto %f, %f CurrentGraphicState.Translate %f, %f Resulting Value %f, %f',
 | ||
|       [PosX, PosY, CurrentGraphicState.TranslateX, CurrentGraphicState.TranslateY, PosX2, PosY2]));
 | ||
|     {$endif}
 | ||
|     AData.AddMoveToPath(PosX2, PosY2);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Absolute LineTo
 | ||
|   // x y lineto –             Append straight line to (x, y)
 | ||
|   if AToken.StrValue = 'lineto' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     PostScriptCoordsToFPVectorialCoordsWithCGS(Param1, Param2, PosX2, PosY2);
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX, PosY);
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] lineto %f, %f Resulting value %f, %f', [PosX, PosY, PosX2, PosY2]));
 | ||
|     {$endif}
 | ||
|     AData.AddLineToPath(PosX2, PosY2);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Relative LineTo
 | ||
|   // dx dy rlineto –          Perform relative lineto
 | ||
|   if AToken.StrValue = 'rlineto' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX, PosY);
 | ||
|     AData.GetCurrentPathPenPos(BaseX, BaseY);
 | ||
|     PosX2 := PosX + BaseX;
 | ||
|     PosY2 := PosY + BaseY;
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] rlineto %f, %f Base %f, %f Resulting %f, %f',
 | ||
|       [PosX, PosY, BaseX, BaseY, PosX2, PosY2]));
 | ||
|     {$endif}
 | ||
|     AData.AddLineToPath(PosX2, PosY2);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // x1 y1 x2 y2 x3 y3 curveto – Append Bézier cubic section
 | ||
|   if AToken.StrValue = 'curveto' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop); // y3
 | ||
|     Param2 := TPSToken(Stack.Pop); // x3
 | ||
|     Param3 := TPSToken(Stack.Pop); // y2
 | ||
|     Param4 := TPSToken(Stack.Pop); // x2
 | ||
|     Param5 := TPSToken(Stack.Pop); // y1
 | ||
|     Param6 := TPSToken(Stack.Pop); // x1
 | ||
|     PostScriptCoordsToFPVectorialCoordsWithCGS(Param5, Param6, PosX, PosY);
 | ||
|     PostScriptCoordsToFPVectorialCoordsWithCGS(Param3, Param4, PosX2, PosY2);
 | ||
|     PostScriptCoordsToFPVectorialCoordsWithCGS(Param1, Param2, PosX3, PosY3);
 | ||
|     AData.AddBezierToPath(PosX, PosY, PosX2, PosY2, PosX3, PosY3);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // dx1 dy1 dx2 dy2 dx3 dy3 rcurveto –
 | ||
|   // (relative curveto) appends a section of a cubic Bézier curve to the current path in
 | ||
|   // the same manner as curveto. However, the operands are interpreted as relative
 | ||
|   // displacements from the current point rather than as absolute coordinates. That is,
 | ||
|   // rcurveto constructs a curve between the current point (x0, y0) and the endpoint
 | ||
|   // (x0 + dx3, y0 + dy3), using (x0 + dx1, y0 + dy1) and (x0 + dx2, y0 + dy2) as the Bézier
 | ||
|   // control points. In all other respects, the behavior of rcurveto is identical to that of
 | ||
|   // curveto.
 | ||
|   if AToken.StrValue = 'rcurveto' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop); // dy3
 | ||
|     Param2 := TPSToken(Stack.Pop); // dx3
 | ||
|     Param3 := TPSToken(Stack.Pop); // dy2
 | ||
|     Param4 := TPSToken(Stack.Pop); // dx2
 | ||
|     Param5 := TPSToken(Stack.Pop); // dy1
 | ||
|     Param6 := TPSToken(Stack.Pop); // dx1
 | ||
|     PostScriptCoordsToFPVectorialCoords(Param5, Param6, PosX, PosY);
 | ||
|     PostScriptCoordsToFPVectorialCoords(Param3, Param4, PosX2, PosY2);
 | ||
|     PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX3, PosY3);
 | ||
|     AData.GetCurrentPathPenPos(BaseX, BaseY);
 | ||
|     // First move to the start of the arc
 | ||
| //    BaseX := BaseX + CurrentGraphicState.TranslateX;
 | ||
| //    BaseY := BaseY + CurrentGraphicState.TranslateY;
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] rcurveto translate %f, %f',
 | ||
|       [CurrentGraphicState.TranslateX, CurrentGraphicState.TranslateY]));
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] rcurveto from %f, %f via %f, %f %f, %f to %f, %f',
 | ||
|       [BaseX, BaseY, BaseX + PosX, BaseY + PosY, BaseX + PosX2, BaseY + PosY2, BaseX + PosX3, BaseY + PosY3]));
 | ||
|     {$endif}
 | ||
|     AData.AddBezierToPath(BaseX + PosX, BaseY + PosY, BaseX + PosX2, BaseY + PosY2, BaseX + PosX3, BaseY + PosY3);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // – closepath –
 | ||
|   //
 | ||
|   // Don't do anything, because a stroke or fill might come after closepath
 | ||
|   // and newpath will be called after stroke and fill anyway
 | ||
|   //
 | ||
|   if AToken.StrValue = 'closepath' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecutePathConstructionOperator] closepath');
 | ||
|     {$endif}
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   {
 | ||
|     x y r angle1 angle2 arc – Append counterclockwise arc
 | ||
| 
 | ||
|     Arcs in PostScript are described by a center (x, y), a radius r and
 | ||
|     two angles, angle1 for the start and angle2 for the end. These two
 | ||
|     angles are relative to the X axis growing to the right (positive direction).
 | ||
| 
 | ||
|   }
 | ||
|   if AToken.StrValue = 'arc' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop); // angle2
 | ||
|     Param2 := TPSToken(Stack.Pop); // angle1
 | ||
|     Param3 := TPSToken(Stack.Pop); // r
 | ||
|     Param4 := TPSToken(Stack.Pop); // y
 | ||
|     Param5 := TPSToken(Stack.Pop); // x
 | ||
|     PostScriptCoordsToFPVectorialCoords(Param4, Param5, PosX, PosY);
 | ||
|     PosX := PosX + CurrentGraphicState.TranslateX;
 | ||
|     PosY := PosY + CurrentGraphicState.TranslateY;
 | ||
|     startAngle := Param2.FloatValue * Pi / 180;
 | ||
|     endAngle := Param1.FloatValue * Pi / 180;
 | ||
| 
 | ||
|     // If the angle is too big we need to use two beziers
 | ||
|     if endAngle - startAngle > Pi then
 | ||
|     begin
 | ||
|       CircularArcToBezier(PosX, PosY, Param3.FloatValue, startAngle, endAngle - Pi, P1, P2, P3, P4);
 | ||
|       AData.AddMoveToPath(P1.X, P1.Y);
 | ||
|       AData.AddBezierToPath(P2.X, P2.Y, P3.X, P3.Y, P4.X, P4.Y);
 | ||
| 
 | ||
|       CircularArcToBezier(PosX, PosY, Param3.FloatValue, startAngle + Pi, endAngle, P1, P2, P3, P4);
 | ||
|       AData.AddMoveToPath(P1.X, P1.Y);
 | ||
|       AData.AddBezierToPath(P2.X, P2.Y, P3.X, P3.Y, P4.X, P4.Y);
 | ||
|     end
 | ||
|     else
 | ||
|     begin
 | ||
|       CircularArcToBezier(PosX, PosY, Param3.FloatValue, startAngle, endAngle, P1, P2, P3, P4);
 | ||
|       AData.AddMoveToPath(P1.X, P1.Y);
 | ||
|       AData.AddBezierToPath(P2.X, P2.Y, P3.X, P3.Y, P4.X, P4.Y);
 | ||
|     end;
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] arc X,Y=%f, %f Resulting X,Y=%f, %f R=%f Angles Start,End=%f,%f',
 | ||
|       [Param5.FloatValue, Param4.FloatValue, PosX, PosY, Param3.FloatValue, Param2.FloatValue, Param1.FloatValue]));
 | ||
|     {$endif}
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // – clip – Clip using nonzero winding number rule
 | ||
|   //
 | ||
|   // See the description on eoclip
 | ||
|   //
 | ||
|   if AToken.StrValue = 'clip' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecutePathConstructionOperator] clip');
 | ||
|     {$endif}
 | ||
|     {$ifndef FPVECTORIALDEBUG_CLIP_REGION}
 | ||
|     AData.SetPenStyle(psClear);
 | ||
|     {$endif}
 | ||
|     AData.SetBrushStyle(bsClear);
 | ||
|     AData.EndPath();
 | ||
|     CurrentGraphicState.ClipPath := AData.GetEntity(AData.GetEntitiesCount()-1) as TPath;
 | ||
|     CurrentGraphicState.ClipMode := vcmNonzeroWindingRule;
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // – eoclip – Clip using even-odd rule
 | ||
|   //
 | ||
|   // intersects the inside of the current clipping path with the inside
 | ||
|   // of the current path to produce a new, smaller current clipping path.
 | ||
|   // The inside of the current path is determined by the even-odd rule,
 | ||
|   // while the inside of the current clipping path is determined by whatever
 | ||
|   // rule was used at the time that path was created.
 | ||
|   //
 | ||
|   // Except for the choice of insideness rule, the behavior of eoclip is identical to that of clip.
 | ||
|   //
 | ||
|   // ERRORS: limitcheck
 | ||
|   //
 | ||
|   if AToken.StrValue = 'eoclip' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecutePathConstructionOperator] eoclip');
 | ||
|     {$endif}
 | ||
|     {$ifndef FPVECTORIALDEBUG_CLIP_REGION}
 | ||
|     AData.SetPenStyle(psClear);
 | ||
|     {$endif}
 | ||
|     AData.SetBrushStyle(bsClear);
 | ||
|     AData.EndPath();
 | ||
|     CurrentGraphicState.ClipPath := AData.GetEntity(AData.GetEntitiesCount()-1) as TPath;
 | ||
|     CurrentGraphicState.ClipMode := vcmEvenOddRule;
 | ||
|     Exit(True);
 | ||
|   end
 | ||
| end;
 | ||
| 
 | ||
| {  Graphics State Operators (Device-Independent)
 | ||
| 
 | ||
|   – gsave –                    Push graphics state
 | ||
|   – grestore –                 Pop graphics state
 | ||
|   – clipsave –                 Push clipping path
 | ||
|   – cliprestore –              Pop clipping path
 | ||
|   – grestoreall –              Pop to bottommost graphics state
 | ||
|   – initgraphics –             Reset graphics state parameters
 | ||
|   – gstate gstate              Create graphics state object
 | ||
|   gstate setgstate –           Set graphics state from gstate
 | ||
|   gstate currentgstate gstate  Copy current graphics state into gstate
 | ||
|   num setlinewidth –           Set line width
 | ||
|   – currentlinewidth num       Return current line width
 | ||
|   int setlinecap –             Set shape of line ends for stroke (0 = butt,
 | ||
|                                1 = round, 2 = square)
 | ||
|   – currentlinecap int         Return current line cap
 | ||
|   int setlinejoin –            Set shape of corners for stroke (0 = miter,
 | ||
|                                1 = round, 2 = bevel)
 | ||
|   – currentlinejoin int Return current line join
 | ||
|   num setmiterlimit – Set miter length limit
 | ||
|   – currentmiterlimit num Return current miter limit
 | ||
|   bool setstrokeadjust – Set stroke adjustment (false = disable,
 | ||
|   true = enable)
 | ||
|   – currentstrokeadjust bool Return current stroke adjustment
 | ||
|   array offset setdash – Set dash pattern for stroking
 | ||
|   – currentdash array offset Return current dash pattern
 | ||
|   array|name setcolorspace – Set color space
 | ||
|   – currentcolorspace array Return current color space
 | ||
|   comp1 … compn setcolor – Set color components
 | ||
|   pattern setcolor – Set colored tiling pattern as current color
 | ||
|   comp1 … compn pattern setcolor – Set uncolored tiling pattern as current color
 | ||
|   – currentcolor comp1 … compn Return current color components
 | ||
|   num setgray – Set color space to DeviceGray and color to
 | ||
|   specified gray value (0 = black, 1 = white)
 | ||
|   – currentgray num Return current color as gray value
 | ||
|   hue saturation brightness sethsbcolor – Set color space to DeviceRGB and color to
 | ||
|   specified hue, saturation, brightness
 | ||
|   – currenthsbcolor hue saturation brightness
 | ||
|   Return current color as hue, saturation,
 | ||
|   brightness
 | ||
|   red green blue setrgbcolor – Set color space to DeviceRGB and color to
 | ||
|                                specified red, green, blue
 | ||
|   – currentrgbcolor red green blue Return current color as red, green, blue
 | ||
|   cyan magenta yellow black setcmykcolor – Set color space to DeviceCMYK and color to
 | ||
|                                            specified cyan, magenta, yellow, black
 | ||
|   – currentcmykcolor cyan magenta yellow black
 | ||
|   Return current color as cyan, magenta,
 | ||
|   yellow, black
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI(
 | ||
|   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| var
 | ||
|   Param1, Param2, Param3, Param4: TPSToken;
 | ||
|   lRed, lGreen, lBlue, lColorC, lColorM, lColorY, lColorK: Double;
 | ||
|   lGraphicState: TGraphicState;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   // – gsave – Push graphics state
 | ||
|   if AToken.StrValue = 'gsave' then
 | ||
|   begin
 | ||
|     GraphicStateStack.Push(CurrentGraphicState.Duplicate());
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] gsave');
 | ||
|     {$endif}
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // – grestore -                 Pop graphics state
 | ||
|   if AToken.StrValue = 'grestore' then
 | ||
|   begin
 | ||
|     lGraphicState := TGraphicState(GraphicStateStack.Pop());
 | ||
|     if lGraphicState = nil then raise Exception.Create('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] grestore: call to grestore without corresponding gsave');
 | ||
|     CurrentGraphicState.Free;
 | ||
|     CurrentGraphicState := lGraphicState;
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] grestore');
 | ||
|     {$endif}
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // num setlinewidth –           Set line width
 | ||
|   if AToken.StrValue = 'setlinewidth' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     CurrentGraphicState.PenWidth := Round(Param1.FloatValue);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // int setlinecap –             Set shape of line ends for stroke (0 = butt,
 | ||
|   //                             1 = round, 2 = square)
 | ||
|   if AToken.StrValue = 'setlinecap' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // int setlinejoin –            Set shape of corners for stroke (0 = miter,
 | ||
|   //                             1 = round, 2 = bevel)
 | ||
|   if AToken.StrValue = 'setlinejoin' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // num setmiterlimit – Set miter length limit
 | ||
|   if AToken.StrValue = 'setmiterlimit' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // array offset setdash – Set dash pattern for stroking
 | ||
|   if AToken.StrValue = 'setdash' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // num setgray – Set color space to DeviceGray and color to
 | ||
|   // specified gray value (0 = black, 1 = white)
 | ||
|   if AToken.StrValue = 'setgray' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     lRed := EnsureRange(Param1.FloatValue, 0, 1);
 | ||
| 
 | ||
|     CurrentGraphicState.Color.Red := Round(lRed * $FFFF);
 | ||
|     CurrentGraphicState.Color.Green := Round(lRed * $FFFF);
 | ||
|     CurrentGraphicState.Color.Blue := Round(lRed * $FFFF);
 | ||
|     CurrentGraphicState.Color.alpha := alphaOpaque;
 | ||
| 
 | ||
|     AData.SetPenColor(CurrentGraphicState.Color);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // array|name setcolorspace – Set color space
 | ||
|   if AToken.StrValue = 'setcolorspace' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     CurrentGraphicState.ColorSpaceName := Param1.StrValue;
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // red green blue setrgbcolor –
 | ||
|   // sets the current color space in the graphics state to DeviceRGB and the current color
 | ||
|   // to the component values specified by red, green, and blue. Each component
 | ||
|   // must be a number in the range 0.0 to 1.0. If any of the operands is outside this
 | ||
|   // range, the nearest valid value is substituted without error indication.
 | ||
|   if AToken.StrValue = 'setrgbcolor' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     Param3 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     lRed := EnsureRange(Param3.FloatValue, 0, 1);
 | ||
|     lGreen := EnsureRange(Param2.FloatValue, 0, 1);
 | ||
|     lBlue := EnsureRange(Param1.FloatValue, 0, 1);
 | ||
| 
 | ||
|     CurrentGraphicState.Color.Red := Round(lRed * $FFFF);
 | ||
|     CurrentGraphicState.Color.Green := Round(lGreen * $FFFF);
 | ||
|     CurrentGraphicState.Color.Blue := Round(lBlue * $FFFF);
 | ||
|     CurrentGraphicState.Color.alpha := alphaOpaque;
 | ||
| 
 | ||
|     AData.SetPenColor(CurrentGraphicState.Color);
 | ||
| 
 | ||
|     {$ifdef FPVECTORIALDEBUG_COLORS}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] setrgbcolor r=%f g=%f b=%f',
 | ||
|       [Param3.FloatValue, Param2.FloatValue, Param1.FloatValue]));
 | ||
|     {$endif}
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // cyan magenta yellow black setcmykcolor – Set color space to DeviceCMYK and color to
 | ||
|   //                                          specified cyan, magenta, yellow, black
 | ||
|   if AToken.StrValue = 'setcmykcolor' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     Param3 := TPSToken(Stack.Pop);
 | ||
|     Param4 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     lColorC := EnsureRange(Param4.FloatValue, 0, 1);
 | ||
|     lColorM := EnsureRange(Param3.FloatValue, 0, 1);
 | ||
|     lColorY := EnsureRange(Param2.FloatValue, 0, 1);
 | ||
|     lColorK := EnsureRange(Param1.FloatValue, 0, 1);
 | ||
| 
 | ||
|     CurrentGraphicState.Color.Red := Round($FF * (1-lColorC) * (1-lColorK) * $101);
 | ||
|     CurrentGraphicState.Color.Green := Round($FF * (1-lColorM) * (1-lColorK) * $101);
 | ||
|     CurrentGraphicState.Color.Blue := Round($FF * (1-lColorY) * (1-lColorK) * $101);
 | ||
|     CurrentGraphicState.Color.alpha := alphaOpaque;
 | ||
| 
 | ||
|     AData.SetPenColor(CurrentGraphicState.Color);
 | ||
| 
 | ||
|     {$ifdef FPVECTORIALDEBUG_COLORS}
 | ||
|     {WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] setrgbcolor r=%f g=%f b=%f',
 | ||
|       [Param3.FloatValue, Param2.FloatValue, Param1.FloatValue]));}
 | ||
|     {$endif}
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| {  Graphics State Operators (Device-Dependent)
 | ||
| 
 | ||
|   halftone sethalftone – Set halftone dictionary
 | ||
|   – currenthalftone halftone
 | ||
|   Return current halftone dictionary
 | ||
|   frequency angle proc setscreen – Set gray halftone screen by frequency, angle,
 | ||
|   and spot function
 | ||
|   frequency angle halftone setscreen – Set gray halftone screen from halftone
 | ||
|   dictionary
 | ||
|   – currentscreen frequency angle proc|halftone
 | ||
|   Return current gray halftone screen
 | ||
|   redfreq redang redproc|redhalftone
 | ||
|   greenfreq greenang greenproc|greenhalftone
 | ||
|   bluefreq blueang blueproc|bluehalftone
 | ||
|   grayfreq grayang grayproc|grayhalftone setcolorscreen – Set all four halftone screens
 | ||
|   – currentcolorscreen redfreq redang redproc|redhalftone
 | ||
|   greenfreq greenang greenproc|greenhalftone
 | ||
|   bluefreq blueang blueproc|bluehalftone
 | ||
|   grayfreq grayang grayproc|grayhalftone
 | ||
|   Return all four halftone screens
 | ||
|   proc settransfer – Set gray transfer function
 | ||
|   – currenttransfer proc
 | ||
|   Return current gray transfer function
 | ||
|   redproc greenproc blueproc grayproc setcolortransfer – Set all four transfer functions
 | ||
|   – currentcolortransfer redproc greenproc blueproc grayproc
 | ||
|   Return current transfer functions
 | ||
|   proc setblackgeneration – Set black-generation function
 | ||
|   – currentblackgeneration proc
 | ||
|   Return current black-generation function
 | ||
|   proc setundercolorremoval – Set undercolor-removal function
 | ||
|   – currentundercolorremoval proc
 | ||
|   Return current undercolor-removal
 | ||
|   function
 | ||
|   dict setcolorrendering – Set CIE-based color rendering dictionary
 | ||
|   – currentcolorrendering dict
 | ||
|   Return current CIE-based color rendering
 | ||
|   dictionary
 | ||
|   num setflat – Set flatness tolerance
 | ||
|   – currentflat num Return current flatness
 | ||
|   bool setoverprint – Set overprint parameter
 | ||
|   – currentoverprint bool Return current overprint parameter
 | ||
|   num setsmoothness – Set smoothness parameter
 | ||
|   – currentsmoothness num Return current smoothness parameter
 | ||
|   Coordinate System and Matrix Operators
 | ||
|   – matrix matrix Create identity matrix
 | ||
|   – initmatrix – Set CTM to device default
 | ||
|   matrix identmatrix matrix Fill matrix with identity transform
 | ||
|   matrix defaultmatrix matrix Fill matrix with device default matrix
 | ||
|   matrix currentmatrix matrix Fill matrix with CTM
 | ||
|   matrix setmatrix –       Replace CTM by matrix
 | ||
|   tx ty translate –        Translate user space by (tx , ty)
 | ||
|   tx ty matrix translate matrix Define translation by (tx , ty)
 | ||
|   sx sy scale – Scale user space by sx and sy
 | ||
|   sx sy matrix scale matrix Define scaling by sx and sy
 | ||
|   angle rotate – Rotate user space by angle degrees
 | ||
|   angle matrix rotate matrix Define rotation by angle degrees
 | ||
|   matrix concat – Replace CTM by matrix ´ CTM
 | ||
|   matrix1 matrix2 matrix3 concatmatrix matrix3 Fill matrix3 with matrix1 ´ matrix2
 | ||
|   x y transform x¢ y¢ Transform (x, y) by CTM
 | ||
|   x y matrix transform x¢ y¢ Transform (x, y) by matrix
 | ||
|   dx dy dtransform dx¢ dy¢ Transform distance (dx, dy) by CTM
 | ||
|   dx dy matrix dtransform dx¢ dy¢ Transform distance (dx, dy) by matrix
 | ||
|   x¢ y¢ itransform x y Perform inverse transform of (x¢, y¢) by
 | ||
|   CTM
 | ||
|   x¢ y¢ matrix itransform x y Perform inverse transform of (x¢, y¢) by
 | ||
|   matrix
 | ||
|   dx¢ dy¢ idtransform dx dy Perform inverse transform of distance
 | ||
|   (dx¢, dy¢) by CTM
 | ||
|   dx¢ dy¢ matrix idtransform dx dy Perform inverse transform of distance
 | ||
|   (dx¢, dy¢) by matrix
 | ||
|   matrix1 matrix2 invertmatrix matrix2 Fill matrix2 with inverse of matrix1
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteGraphicStateOperatorsDD(
 | ||
|   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| var
 | ||
|   Param1, Param2: TPSToken;
 | ||
|   ArrayToken: TArrayToken;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   // bool setoverprint – Set overprint parameter
 | ||
|   if AToken.StrValue = 'setoverprint' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     CurrentGraphicState.OverPrint := Param1.BoolValue;
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   //– matrix matrix Create identity matrix
 | ||
|   if AToken.StrValue = 'matrix' then
 | ||
|   begin
 | ||
|     ArrayToken := TArrayToken.Create;
 | ||
|     ArrayToken.AddIdentityMatrix();
 | ||
| 
 | ||
|     Stack.Push(ArrayToken);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   //– initmatrix – Set CTM to device default
 | ||
| 
 | ||
|   //matrix identmatrix matrix Fill matrix with identity transform
 | ||
| 
 | ||
|   //matrix defaultmatrix matrix Fill matrix with device default matrix
 | ||
| 
 | ||
|   //matrix currentmatrix matrix Fill matrix with CTM
 | ||
|   if AToken.StrValue = 'currentmatrix' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param1.Free;
 | ||
| 
 | ||
|     CurrentGraphicState.CTMNeeded();
 | ||
|     ArrayToken := TArrayToken(CurrentGraphicState.CTM.Duplicate());
 | ||
| 
 | ||
|     Stack.Push(ArrayToken);
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // matrix setmatrix –       Replace CTM by matrix
 | ||
|   if AToken.StrValue = 'setmatrix' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     CurrentGraphicState.SetCTM(TArrayToken(Param1));
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // sx sy scale – Scale user space by sx and sy
 | ||
|   if AToken.StrValue = 'scale' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     if Param2 = nil then
 | ||
|     begin
 | ||
|       Exit(True);
 | ||
|     end;
 | ||
| 
 | ||
|     CurrentGraphicState.ScaleX := CurrentGraphicState.ScaleX * Param2.FloatValue;
 | ||
|     CurrentGraphicState.ScaleY := CurrentGraphicState.ScaleY * Param1.FloatValue;
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] scale %f %f',
 | ||
|      [CurrentGraphicState.ScaleX, CurrentGraphicState.ScaleY]));
 | ||
|     {$endif}
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   {
 | ||
|     translate tx ty translate
 | ||
|     - tx ty matrix translate matrix
 | ||
| 
 | ||
|     With no matrix operand, translate builds a temporary matrix and concatenates
 | ||
|     this matrix with the current transformation matrix (CTM). Precisely, translate
 | ||
|     replaces the CTM by T x CTM. The effect of this is to move the origin of the
 | ||
|     user coordinate system by tx units in the x direction and ty units in the y
 | ||
|     direction relative to the former user coordinate system. The sizes of the x
 | ||
|     and y units and the orientation of the axes are unchanged.
 | ||
| 
 | ||
|     If the matrix operand is supplied, translate replaces the value of matrix by
 | ||
|     T and pushes the modified matrix back on the operand stack.
 | ||
|     In this case, translate does not affect the CTM.
 | ||
|   }
 | ||
|   if AToken.StrValue = 'translate' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop); // ty
 | ||
|     Param2 := TPSToken(Stack.Pop); // tx
 | ||
| 
 | ||
|     if Param2 = nil then
 | ||
|     begin
 | ||
|       raise Exception.Create('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] Stack underflow in operator "translate"');
 | ||
|     end;
 | ||
| 
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] translate %f, %f CurrentGraphicState.Translate %f %f',
 | ||
|       [Param2.FloatValue, Param1.FloatValue, CurrentGraphicState.TranslateX, CurrentGraphicState.TranslateY]));
 | ||
|     {$endif}
 | ||
| 
 | ||
|     CurrentGraphicState.TranslateX := CurrentGraphicState.TranslateX + Param2.FloatValue;
 | ||
|     CurrentGraphicState.TranslateY := CurrentGraphicState.TranslateY + Param1.FloatValue;
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // angle rotate – Rotate user space by angle degrees
 | ||
|   if AToken.StrValue = 'rotate' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     {$ifdef FPVECTORIALDEBUG_PATHS}
 | ||
|     WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] rotate angle=%f', [Param1.FloatValue]));
 | ||
|     DebugStack();
 | ||
|     {$endif}
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| {  Dictionary Operators
 | ||
| 
 | ||
|   int dict dict Create dictionary with capacity for int
 | ||
|   elements
 | ||
|   – << mark             Start dictionary construction
 | ||
|   mark key1 value1 … keyn valuen >> dict
 | ||
|                         End dictionary construction
 | ||
|   dict length int       Return number of entries in dict
 | ||
|   dict maxlength int    Return current capacity of dict
 | ||
|   dict begin –          Push dict on dictionary stack
 | ||
|   – end –               Pop current dictionary off dictionary stack
 | ||
|   key value def –       Associate key and value in current dictionary
 | ||
|   key load value        Search dictionary stack for key and return
 | ||
|                         associated value
 | ||
|   key value store –     Replace topmost definition of key
 | ||
|   dict key get any      Return value associated with key in dict
 | ||
|   dict key value put –  Associate key with value in dict
 | ||
|   dict key undef –      Remove key and its value from dict
 | ||
|   dict key known bool Test whether key is in dict
 | ||
|   key where dict true   Find dictionary in which key is defined
 | ||
|              or false
 | ||
|   dict1 dict2 copy dict2 Copy contents of dict1 to dict2
 | ||
|   dict proc forall – Execute proc for each entry in dict
 | ||
|   – currentdict dict Return current dictionary
 | ||
|   – errordict dict Return error handler dictionary
 | ||
|   – $error dict Return error control and status dictionary
 | ||
|   – systemdict dict Return system dictionary
 | ||
|   – userdict dict Return writeable dictionary in local VM
 | ||
|   – globaldict dict Return writeable dictionary in global VM
 | ||
|   – statusdict dict Return product-dependent dictionary
 | ||
|   – countdictstack int Count elements on dictionary stack
 | ||
|   array dictstack subarray Copy dictionary stack into array
 | ||
|   – cleardictstack – Pop all nonpermanent dictionaries off
 | ||
|   dictionary stack
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteDictionaryOperators(
 | ||
|   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| var
 | ||
|   Param1, Param2: TPSToken;
 | ||
|   NewToken: TExpressionToken;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   // int dict dict Create dictionary with capacity for int
 | ||
|   // elements
 | ||
|   if AToken.StrValue = 'dict' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettDictionary;
 | ||
|     Stack.Push(NewToken);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // dict begin –          Push dict on dictionary stack
 | ||
|   if AToken.StrValue = 'begin' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // – end –               Pop current dictionary off dictionary stack
 | ||
|   if AToken.StrValue = 'end' then
 | ||
|   begin
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // Adds a dictionary definition
 | ||
|   // key value def –       Associate key and value in current dictionary
 | ||
|   if AToken.StrValue = 'def' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     Param2 := TPSToken(Stack.Pop);
 | ||
|     Dictionary.AddObject(Param2.StrValue, Param1);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| 
 | ||
|   // Can be ignored, because in the files found it only loads
 | ||
|   // standard routines, like /moveto ...
 | ||
|   //
 | ||
|   // key load value        Search dictionary stack for key and return
 | ||
|   //                      associated value
 | ||
|   if AToken.StrValue = 'load' then
 | ||
|   begin
 | ||
| //    {$ifdef FPVECTORIALDEBUG_DICTIONARY}
 | ||
| //    WriteLn('[TvEPSVectorialReader.ExecuteDictionaryOperators] load');
 | ||
| //    DebugStack();
 | ||
| //    {$endif}
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| 
 | ||
|   // Find dictionary in which key is defined
 | ||
|   //key where dict true   Find dictionary in which key is defined
 | ||
|   //           or false
 | ||
|   if AToken.StrValue = 'where' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_DICTIONARY}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecuteDictionaryOperators] where');
 | ||
|     DebugStack();
 | ||
|     {$endif}
 | ||
| 
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
| 
 | ||
|     if Dictionary.IndexOf(Param1.StrValue) >= 0 then
 | ||
|     begin
 | ||
|       // We use only 1 dictionary, so this is just a representation of our single dictionary
 | ||
|       NewToken := TExpressionToken.Create;
 | ||
|       NewToken.ETType := ettDictionary;
 | ||
|       Stack.Push(NewToken);
 | ||
| 
 | ||
|       NewToken := TExpressionToken.Create;
 | ||
|       NewToken.ETType := ettOperand;
 | ||
|       NewToken.BoolValue := True;
 | ||
|       Stack.Push(NewToken);
 | ||
| 
 | ||
|       {$ifdef FPVECTORIALDEBUG_DICTIONARY}
 | ||
|       WriteLn('[TvEPSVectorialReader.ExecuteDictionaryOperators] where True');
 | ||
|       {$endif}
 | ||
|     end
 | ||
|     else
 | ||
|     begin
 | ||
|       NewToken := TExpressionToken.Create;
 | ||
|       NewToken.ETType := ettOperand;
 | ||
|       NewToken.BoolValue := False;
 | ||
|       Stack.Push(NewToken);
 | ||
| 
 | ||
|       {$ifdef FPVECTORIALDEBUG_DICTIONARY}
 | ||
|       WriteLn('[TvEPSVectorialReader.ExecuteDictionaryOperators] where False');
 | ||
|       {$endif}
 | ||
|     end;
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // - userdict dict
 | ||
|   // pushes the dictionary object userdict on the operand stack
 | ||
|   // (see Section 3.7.5, “Standard and User-Defined Dictionaries”).
 | ||
|   // userdict is not an operator; it is a name in systemdict associated with the dictionary object.
 | ||
|   if AToken.StrValue = 'userdict' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettDictionary;
 | ||
|     Stack.Push(NewToken);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // – globaldict dict Return writeable dictionary in global VM
 | ||
|   if AToken.StrValue = 'globaldict' then
 | ||
|   begin
 | ||
|     Param1 := TPSToken(Stack.Pop);
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettDictionary;
 | ||
|     Stack.Push(NewToken);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
|   // – countdictstack int Count elements on dictionary stack
 | ||
|   // countdictstack ==> int
 | ||
|   if AToken.StrValue = 'countdictstack' then
 | ||
|   begin
 | ||
|     NewToken := TExpressionToken.Create;
 | ||
|     NewToken.ETType := ettOperand;
 | ||
|     NewToken.FloatValue := Dictionary.Count;
 | ||
|     NewToken.StrValue := IntToStr(Dictionary.Count);
 | ||
|     Stack.Push(NewToken);
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| {  Miscellaneous Operators
 | ||
| 
 | ||
|   proc bind proc Replace operator names in proc with
 | ||
|   operators; perform idiom recognition
 | ||
|   – null null Push null on stack
 | ||
|   – version string Return interpreter version
 | ||
|   – realtime int Return real time in milliseconds
 | ||
|   – usertime int Return execution time in milliseconds
 | ||
|   – languagelevel int Return LanguageLevel
 | ||
|   – product string Return product name
 | ||
|   – revision int Return product revision level
 | ||
|   – serialnumber int Return machine serial number
 | ||
|   – executive – Invoke interactive executive
 | ||
|   bool echo – Turn echoing on or off
 | ||
|   – prompt – Executed when ready for interactive input
 | ||
| }
 | ||
| function TvEPSVectorialReader.ExecuteMiscellaneousOperators(
 | ||
|   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
 | ||
| begin
 | ||
|   Result := False;
 | ||
| 
 | ||
|   // Just a hint for more efficient parsing, we can ignore
 | ||
|   //
 | ||
|   // proc bind proc Replace operator names in proc with
 | ||
|   // operators; perform idiom recognition
 | ||
|   if AToken.StrValue = 'bind' then
 | ||
|   begin
 | ||
|     {$ifdef FPVECTORIALDEBUG_CONTROL}
 | ||
|     WriteLn('[TvEPSVectorialReader.ExecuteControlOperator] bind');
 | ||
|     DebugStack();
 | ||
|     {$endif}
 | ||
| 
 | ||
|     Exit(True);
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| procedure TvEPSVectorialReader.PostScriptCoordsToFPVectorialCoords(AParam1,
 | ||
|   AParam2: TPSToken; var APosX, APosY: Double);
 | ||
| begin
 | ||
|   APosX := AParam2.FloatValue;
 | ||
|   APosY := AParam1.FloatValue;
 | ||
| end;
 | ||
| 
 | ||
| procedure TvEPSVectorialReader.PostScriptCoordsToFPVectorialCoordsWithCGS(
 | ||
|   AParam1, AParam2: TPSToken; var APosX, APosY: Double);
 | ||
| begin
 | ||
|   PostScriptCoordsToFPVectorialCoords(AParam1, AParam2, APosX, APosY);
 | ||
|   // Using CurrentGraphicState.ScaleX here breaks radat.eps
 | ||
|   APosX := APosX {* CurrentGraphicState.ScaleX} + CurrentGraphicState.TranslateX;
 | ||
|   APosY := APosY {* CurrentGraphicState.ScaleY} + CurrentGraphicState.TranslateY;
 | ||
| end;
 | ||
| 
 | ||
| // Returns true if a dictionary substitution was executed
 | ||
| function TvEPSVectorialReader.DictionarySubstituteOperator(
 | ||
|   ADictionary: TStringList; var ACurToken: TPSToken): Boolean;
 | ||
| var
 | ||
|   lIndex: Integer;
 | ||
|   SubstituteToken, NewToken: TPSToken;
 | ||
|   lOldStrValue: string; // for debugging purposes
 | ||
| begin
 | ||
|   Result := False;
 | ||
|   lOldStrValue := ACurToken.StrValue;
 | ||
|   lIndex := ADictionary.IndexOf(ACurToken.StrValue);
 | ||
|   if lIndex >= 0 then
 | ||
|   begin
 | ||
|     Result := True;
 | ||
| 
 | ||
|     SubstituteToken := TPSToken(ADictionary.Objects[lIndex]);
 | ||
| 
 | ||
|     if SubstituteToken is TExpressionToken then
 | ||
|     begin
 | ||
|       ACurToken.CopyDataFrom(SubstituteToken, True);
 | ||
|     end
 | ||
|     else if (SubstituteToken is TProcedureToken) or
 | ||
|       (SubstituteToken is TArrayToken) then
 | ||
|     begin
 | ||
|       ACurToken := SubstituteToken;
 | ||
|     end;
 | ||
| 
 | ||
|     if (not (SubstituteToken is TArrayToken)) and
 | ||
|        (not ((SubstituteToken is TExpressionToken) and (TExpressionToken(SubstituteToken).ETType = ettDictionary))) and
 | ||
|        (not (SubstituteToken is TDictionaryToken)) and (ACurToken.StrValue = '') then
 | ||
|       raise Exception.Create(Format('[TvEPSVectorialReader.DictionarySubstituteOperator] '
 | ||
|        + 'The Dictionary substitution resulted in an empty value. SubstituteClass=%s Original StrValue=%s Line=%d',
 | ||
|        [SubstituteToken.ClassName, lOldStrValue, ACurToken.Line]));
 | ||
|   end;
 | ||
| end;
 | ||
| 
 | ||
| constructor TvEPSVectorialReader.Create;
 | ||
| begin
 | ||
|   inherited Create;
 | ||
| 
 | ||
|   FPointSeparator := SysUtils.DefaultFormatSettings;
 | ||
|   FPointSeparator.DecimalSeparator := '.';
 | ||
|   FPointSeparator.ThousandSeparator := ',';
 | ||
| 
 | ||
|   Tokenizer := TPSTokenizer.Create(-1);
 | ||
|   Stack := TObjectStack.Create;
 | ||
|   GraphicStateStack := TObjectStack.Create;
 | ||
|   Dictionary := TStringList.Create;
 | ||
|   Dictionary.CaseSensitive := True;
 | ||
|   CurrentGraphicState := TGraphicState.Create;
 | ||
| end;
 | ||
| 
 | ||
| destructor TvEPSVectorialReader.Destroy;
 | ||
| begin
 | ||
|   Tokenizer.Free;
 | ||
|   Stack.Free;
 | ||
|   GraphicStateStack.Free;
 | ||
|   Dictionary.Free;
 | ||
|   CurrentGraphicState.Free;
 | ||
| 
 | ||
|   inherited Destroy;
 | ||
| end;
 | ||
| 
 | ||
| procedure TvEPSVectorialReader.ReadFromStream(AStream: TStream;
 | ||
|   AData: TvVectorialDocument);
 | ||
| var
 | ||
|   lPage: TvVectorialPage;
 | ||
| begin
 | ||
|   Tokenizer.ReadFromStream(AStream);
 | ||
| //  Tokenizer.DebugOut();
 | ||
| 
 | ||
|   // Make sure we have at least one path
 | ||
|   lPage := AData.AddPage();
 | ||
|   lPage.StartPath();
 | ||
| 
 | ||
|   RunPostScript(Tokenizer.Tokens, lPage, AData);
 | ||
| 
 | ||
|   // Make sure we have at least one path
 | ||
|   lPage.EndPath();
 | ||
| 
 | ||
|   // PostScript has no document size information, so lets calculate it ourselves
 | ||
|   AData.GuessDocumentSize();
 | ||
|   AData.GuessGoodZoomLevel()
 | ||
| end;
 | ||
| 
 | ||
| initialization
 | ||
| 
 | ||
|   RegisterVectorialReader(TvEPSVectorialReader, vfEncapsulatedPostScript);
 | ||
| 
 | ||
| end.
 | ||
| 
 | 
