mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 16:34:24 +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.
 | 
						||
 |