1 {
2 Reads EPS files
3 
4 License: The same modified LGPL as the Free Pascal RTL
5          See the file COPYING.modifiedLGPL for more details
6 
7 AUTHORS: Felipe Monteiro de Carvalho
8 
9 Documentation: http://www.tailrecursive.org/postscript/postscript.html
10 
11 Good reference: http://atrey.karlin.mff.cuni.cz/~milanek/PostScript/Reference/PSL2e.html
12 }
13 unit epsvectorialreader;
14 
15 {$mode objfpc}{$H+}
16 
17 {.$define FPVECTORIALDEBUG_PATHS}
18 {.$define FPVECTORIALDEBUG_COLORS}
19 {.$define FPVECTORIALDEBUG_ROLL}
20 {.$define FPVECTORIALDEBUG_CODEFLOW}
21 {.$define FPVECTORIALDEBUG_INDEX}
22 {.$define FPVECTORIALDEBUG_DICTIONARY}
23 {.$define FPVECTORIALDEBUG_CONTROL}
24 {.$define FPVECTORIALDEBUG_ARITHMETIC}
25 {.$define FPVECTORIALDEBUG_CLIP_REGION}
26 {$define FPVECTORIAL_IMAGE_DICTIONARY_DEBUG}
27 
28 interface
29 
30 uses
31   Classes, SysUtils, Math, contnrs,
32   fpimage, fpcanvas,
33   fpvectorial, fpvutils;
34 
35 type
36   TPSTokenType = (ttComment, ttFloat);
37 
38   TPSTokens = TFPList;// TPSToken;
39 
40   TPSToken = class
41   public
42     StrValue: string;
43     FloatValue: double;
44     IntValue: Integer;
45     BoolValue: Boolean;
46     Line: Integer; // To help debugging
47     constructor Create; virtual;
48     procedure CopyDataFrom(ASrc: TPSToken; AKeepTokenType: Boolean); virtual;
Duplicatenull49     function Duplicate: TPSToken; virtual;
50     procedure PrepareIntValue;
51   end;
52 
53   TCommentToken = class(TPSToken)
54   public
55   end;
56 
57   { TArrayToken }
58 
59   TArrayToken = class(TPSToken)
60   public
61     CurElementStr: string;
62     ArrayData: TPSTokens;
63     Parent: TArrayToken; // nil indicates a top-level array
64     constructor Create; override;
65     destructor Destroy; override;
Duplicatenull66     function Duplicate: TPSToken; override;
67     procedure FreeToken(AToken, AData: Pointer);
68     procedure AddNumber(ANumber: Double);
69     procedure AddIdentityMatrix;
GetNumbernull70     function GetNumber(AIndex: Integer): Double;
71     procedure ResolveOperators;
72   end;
73 
74   { TProcedureToken }
75 
76   TProcedureToken = class(TPSToken)
77   public
78     Levels: Integer; // Used to count groups inside groups and find the end of a top-level group
79     Childs: TPSTokens;
80     Parsed: Boolean;
81     constructor Create; override;
82     destructor Destroy; override;
83   end;
84 
85   TETType = (ettNamedElement, ettOperand, ettOperator, ettDictionary,
86     ettVirtualMemorySnapshot, ettLiteralString, ettRawData, ettInvalid);
87 
88   { TExpressionToken }
89 
90   TExpressionToken = class(TPSToken)
91   public
92     ETType: TETType;
93     SubstituteETType: TETType; // utilized when the token is substituted
94     constructor Create; override;
IsExpressionOperandnull95     function IsExpressionOperand: Boolean;
96     procedure PrepareFloatValue;
97     procedure CopyDataFrom(ASrc: TPSToken; AKeepTokenType: Boolean); override;
Duplicatenull98     function Duplicate: TPSToken; override;
99   end;
100 
101   { TDictionaryToken }
102 
103   // TDictionaryToken is utilized for <..> dictionary definitions
104   // Do not confuse it with a directionary reference
105   // which is a TExpressionToken with ETType=ettDictionary!
106   TDictionaryToken = class(TPSToken)
107   public
108     Childs: TPSTokens;
109     //
110     Names: TStringList;
111     Values: TPSTokens; // does not contain own references, don't free contents!
112     constructor Create; override;
113     destructor Destroy; override;
114     procedure TransformToListOfNamedValues();
115   end;
116 
117   TPostScriptScannerState = (ssSearchingToken, ssInComment, ssInDefinition,
118     ssInGroup, ssInExpressionElement, ssInArray, ssInDictionary,
119     ssReadingRawDataStart, ssReadingRawData);
120 
121   { TGraphicState }
122 
123   TGraphicState = class
124   public
125     Color: TFPColor;
126     TranslateX, TranslateY: Double;
127     ScaleX, ScaleY: Double;
128     ClipPath: TPath;
129     ClipMode: TvClipMode;
130     OverPrint: Boolean; // not used currently
131     ColorSpaceName: string;
132     // Current Transformation Matrix
133     //
134     // See http://www.useragentman.com/blog/2011/01/07/css3-matrix-transform-for-the-mathematically-challenged/
135     // This has 6 numbers, which means this:
136     //                      (a  c  e)
137     // [a, b, c, d, e, f] = (b  d  f)
138     //                      (0  0  1)
139     // scale(Num)  => a,d=Num  rest=0
140     // scaleX(Num) => a=Num  d=1 rest=0
141     // scaleY(Num) => a=1  d=Num rest=0
142     // TranslateX(Num) => a,d=1 e=Num rest=0
143     // TranslateY(Num) => a,d=1 f=Num rest=0
144     // Translate(NumX,NumY)  => a,d=1 e=NumX f=NumY rest=0
145     // skewX(TX) => a=1 b=0 c=tan(TX) d=1 rest=0
146     // skewY(TY) => a=1 b=tan(TY) c=0 d=1 rest=0
147     // skew(TX,TY) => a=1 b=tan(TY) c=tan(TX) d=1 rest=0
148     // rotate(T) => a=cos(T) b=sin(T) c=-sin(T) d=cos(T) rest=0
149     CTM: TArrayToken;
150     //
151     PenWidth: Integer;
152     //
153     constructor Create;
154     function Duplicate: TGraphicState;
155     procedure CTMNeeded;
156     procedure SetCTM(ANewCTM: TArrayToken);
157   end;
158 
159   { TPSTokenizer }
160 
161   TPSTokenizer = class
162   public
163     Tokens: TPSTokens;
164     FCurLine: Integer;
165     constructor Create(ACurLine: Integer = -1);
166     destructor Destroy; override;
167     procedure ReadFromStream(AStream: TStream);
168     procedure DebugOut();
169     function IsValidPostScriptChar(AChar: Byte): Boolean;
170     function IsPostScriptSpace(AChar: Byte): Boolean;
171     function IsEndOfLine(ACurChar: Byte; AStream: TStream): Boolean;
172   end;
173 
174   { TvEPSVectorialReader }
175 
176   TvEPSVectorialReader = class(TvCustomVectorialReader)
177   private
178     Stack: TObjectStack;
179     GraphicStateStack: TObjectStack; // TGraphicState
180     Dictionary: TStringList;
181     ExitCalled: Boolean;
182     CurrentGraphicState: TGraphicState;
183     //
184     procedure DebugStack();
185     //
186     procedure RunPostScript(ATokens: TPsTokens; AData: TvVectorialPage; ADoc: TvVectorialDocument);
187     //
188     procedure ExecuteProcedureToken(AToken: TProcedureToken; AData: TvVectorialPage; ADoc: TvVectorialDocument);
189     procedure ExecuteOperatorToken(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken);
190     function  ExecuteArithmeticAndMathOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
191     function  ExecutePathConstructionOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
192     function  ExecuteGraphicStateOperatorsDI(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
193     function  ExecuteGraphicStateOperatorsDD(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
194     function  ExecuteDictionaryOperators(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
195     function  ExecuteMiscellaneousOperators(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
196     function  ExecuteStackManipulationOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
197     function  ExecuteControlOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
198     function  ExecutePaintingOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken): Boolean;
199     function  ExecuteImageOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken): Boolean;
200     function  ExecuteDeviceSetupAndOutputOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
201     function  ExecuteArrayOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
202     function  ExecuteStringOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
203     function  ExecuteFileOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
204     function  ExecuteResourceOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
205     function  ExecuteVirtualMemoryOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
206     function  ExecuteErrorOperator(AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
207     //
208     procedure PostScriptCoordsToFPVectorialCoords(AParam1, AParam2: TPSToken; var APosX, APosY: Double);
209     procedure PostScriptCoordsToFPVectorialCoordsWithCGS(AParam1, AParam2: TPSToken; var APosX, APosY: Double);
210     function DictionarySubstituteOperator(ADictionary: TStringList; var ACurToken: TPSToken): Boolean;
211   public
212     { General reading methods }
213     Tokenizer: TPSTokenizer;
214     constructor Create; override;
215     Destructor Destroy; override;
216     procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
217   end;
218 
219 implementation
220 
221 type
222   TStackAccess = class(TObjectStack)
223   end;
224 
225 var
226   FPointSeparator: TFormatSettings;
227 
228 { TDictionaryToken }
229 
230 constructor TDictionaryToken.Create;
231 begin
232   inherited Create;
233 
234   Childs := TPSTokens.Create;
235   Names := TStringList.Create;
236   Values := TPSTokens.Create;
237 end;
238 
239 destructor TDictionaryToken.Destroy;
240 begin
241   Names.Free;
242   Values.Free;
243   //
244   Childs.Free;
245 
246   inherited Destroy;
247 end;
248 
249 procedure TDictionaryToken.TransformToListOfNamedValues;
250 var
251   i: Integer;
252   CurToken: TPSToken;
253 begin
254   for i := 0 to Childs.Count-1 do
255   begin
256     CurToken := TPSToken(Childs.Items[i]);
257     if i mod 2 = 0 then
258     begin
259       Names.Add(CurToken.StrValue);
260     end
261     else
262     begin
263       Values.Add(Pointer(CurToken));
264     end;
265   end;
266 end;
267 
268 { TArrayToken }
269 
270 constructor TArrayToken.Create;
271 begin
272   inherited Create;
273   ArrayData := TPSTokens.Create;
274 end;
275 
276 destructor TArrayToken.Destroy;
277 begin
278   //ArrayData.ForEachCall(@FreeToken, nil);
279   ArrayData.Free;
280   inherited Destroy;
281 end;
282 
Duplicatenull283 function TArrayToken.Duplicate: TPSToken;
284 begin
285   Result := inherited Duplicate;
286 end;
287 
288 procedure TArrayToken.FreeToken(AToken, AData: Pointer);
289 begin
290   if AToken = nil then Exit;
291   TPSToken(AToken).Free;
292 end;
293 
294 procedure TArrayToken.AddNumber(ANumber: Double);
295 var
296   lToken: TPSToken;
297 begin
298   lToken := TPSToken.Create;
299   lToken.FloatValue := ANumber;
300   ArrayData.Add(lToken);
301 end;
302 
303 procedure TArrayToken.AddIdentityMatrix;
304 begin
305   AddNumber(1);
306   AddNumber(0);
307   AddNumber(0);
308   AddNumber(1);
309   AddNumber(0);
310   AddNumber(0);
311 end;
312 
GetNumbernull313 function TArrayToken.GetNumber(AIndex: Integer): Double;
314 begin
315   Result := TPSToken(ArrayData.Items[AIndex]).FloatValue;
316 end;
317 
318 procedure TArrayToken.ResolveOperators;
319 begin
320 
321 end;
322 
323 { TGraphicState }
324 
325 constructor TGraphicState.Create;
326 begin
327   inherited Create;
328 
329   ScaleX := 1;
330   ScaleY := 1;
331 end;
332 
Duplicatenull333 function TGraphicState.Duplicate: TGraphicState;
334 begin
335   Result := TGraphicState(Self.ClassType.Create);
336   Result.Color := Color;
337   Result.TranslateX := TranslateX;
338   Result.TranslateY := TranslateY;
339   Result.ScaleX := ScaleX;
340   Result.ScaleY := ScaleY;
341   Result.ClipPath := ClipPath;
342   Result.ClipMode := ClipMode;
343   Result.OverPrint := OverPrint;
344   Result.ColorSpaceName := ColorSpaceName;
345   if CTM <> nil then
346     Result.CTM := TArrayToken(CTM.Duplicate());
347   Result.PenWidth := PenWidth;
348 end;
349 
350 procedure TGraphicState.CTMNeeded;
351 begin
352   if CTM <> nil then Exit;
353 
354   CTM := TArrayToken.Create;
355   CTM.AddIdentityMatrix();
356 end;
357 
358 procedure TGraphicState.SetCTM(ANewCTM: TArrayToken);
359 begin
360   if CTM <> nil then CTM.Free;
361   CTM := ANewCTM;
362 end;
363 
364 { TPSToken }
365 
366 constructor TPSToken.Create;
367 begin
368   inherited Create;
369 end;
370 
371 procedure TPSToken.CopyDataFrom(ASrc: TPSToken; AKeepTokenType: Boolean);
372 begin
373   StrValue := ASrc.StrValue;
374   FloatValue := ASrc.FloatValue;
375   IntValue := ASrc.IntValue;
376   BoolValue := ASrc.BoolValue;
377 end;
378 
Duplicatenull379 function TPSToken.Duplicate: TPSToken;
380 begin
381   Result := TPSToken(Self.ClassType.Create);
382   Result.StrValue := StrValue;
383   Result.FloatValue := FloatValue;
384   Result.IntValue := IntValue;
385   Result.Line := Line;
386 end;
387 
388 procedure TPSToken.PrepareIntValue;
389 begin
390   if IntValue = 0 then IntValue := Round(FloatValue);
391 end;
392 
393 { TProcedureToken }
394 
395 constructor TProcedureToken.Create;
396 begin
397   inherited Create;
398 
399   Childs := TPSTokens.Create;
400 end;
401 
402 destructor TProcedureToken.Destroy;
403 begin
404   Childs.Free;
405 
406   inherited Destroy;
407 end;
408 
409 { TExpressionToken }
410 
411 constructor TExpressionToken.Create;
412 begin
413   inherited Create;
414   SubstituteETType := ettInvalid;
415 end;
416 
IsExpressionOperandnull417 function TExpressionToken.IsExpressionOperand: Boolean;
418 begin
419   if StrValue = '' then Exit(False);
420   Result := StrValue[1] in ['0'..'9','-'];
421 end;
422 
423 procedure TExpressionToken.PrepareFloatValue;
424 var
425   lRadixPos: SizeInt;
426   i: Integer;
427   Len: Integer;
428   lRadixStr: string;
429   lRadixNum: Integer;
430 begin
431   //if not IsExpressionOperand() then Exit;
432   if ETType <> ettOperand then Exit; // faster, because this field should already be filled
433 
434   // If this is a radix number, we will have more work
435   // Example of radix in Postscript: 2#1000 = 8
436   // http://en.wikipedia.org/wiki/Radix
437   // The first number is the base, 2 = binary, 10=decimal, 16=hex, etc
438   lRadixPos := Pos('#', StrValue);
439   if lRadixPos <> 0 then
440   begin
441     FloatValue := 0;
442     Len := Length(StrValue);
443     lRadixStr := Copy(StrValue, 1, lRadixPos-1);
444     lRadixNum := StrToInt(lRadixStr); // for now assume only 1
445     for i := Length(StrValue) downto lRadixPos+1 do
446     begin
447       FloatValue := FloatValue + StrToInt(StrValue[i]) * Math.Power(lRadixNum, Len - i);
448     end;
449   end
450   else
451   // Code for normal numbers, decimals
452   begin
453     FloatValue := StrToFloat(StrValue, FPointSeparator);
454   end;
455 end;
456 
457 procedure TExpressionToken.CopyDataFrom(ASrc: TPSToken; AKeepTokenType: Boolean);
458 begin
459   inherited CopyDataFrom(ASrc, AKeepTokenType);
460   if (ASrc is TExpressionToken) and (not AKeepTokenType) then
461     ETType := TExpressionToken(ASrc).ETType;
462   SubstituteETType := TExpressionToken(ASrc).ETType;
463 end;
464 
Duplicatenull465 function TExpressionToken.Duplicate: TPSToken;
466 begin
467   Result:=inherited Duplicate;
468   TExpressionToken(Result).ETType := ETType;
469 end;
470 
471 {$DEFINE FPVECTORIALDEBUG}
472 
473 { TPSTokenizer }
474 
475 // ACurLine < 0 indicates that we should use the line of this list of strings
476 // else we use ACurLine
477 constructor TPSTokenizer.Create(ACurLine: Integer);
478 begin
479   inherited Create;
480   Tokens := TPSTokens.Create;
481   FCurLine := ACurLine;
482 end;
483 
484 destructor TPSTokenizer.Destroy;
485 begin
486   Tokens.Free;
487   inherited Destroy;
488 end;
489 
490 {@@ Rules for parsing PostScript files:
491 
492 * Coments go from the first occurence of % outside a line to the next new line
493 * The only accepted characters are printable ASCII ones, plus spacing ASCII chars
494   See IsValidPostScriptChar about that
495 }
496 procedure TPSTokenizer.ReadFromStream(AStream: TStream);
497 var
498   CurChar: Char;
499   CurLine: Integer = 1;
500   State: TPostScriptScannerState = ssSearchingToken;
501   CommentToken: TCommentToken;
502   ProcedureToken: TProcedureToken;
503   ExpressionToken: TExpressionToken;
504   ArrayToken, NewArrayToken: TArrayToken;
505   DictionaryToken: TDictionaryToken;
506   lReturnState: TStack; // of TPostScriptScannerState
507   lExpressionStateReturn: TPostScriptScannerState;
508   lIsEndOfLine: Boolean;
509   lIsExpressionFinished: Boolean;
510   lTmpStr: string;
511 begin
512   lReturnState := TStack.Create;
513   try
514 
515   // Check if the EPS file starts with a TIFF preview
516   // See http://www.graphicsgroups.com/12-corel/f851f798a0e1ca7a.htm
517   // 00000000: c5d0 d3c6 930b 0000 55f2 0000 0000 0000  ........U.......
518   // 00000010: 0000 0000 1e00 0000 750b 0000 ffff 4949  ........u.....II
519   CurChar := Char(AStream.ReadByte());
520   if Byte(CurChar) = $C5 then
521     AStream.Position := $20
522   else
523     AStream.Position := AStream.Position - 1;
524 
525   //
526   // Now actualy read EPS data
527   //
528   while AStream.Position < AStream.Size do
529   begin
530     CurChar := Char(AStream.ReadByte());
531 //    {$ifdef FPVECTORIALDEBUG}
532 //    WriteLn(Format('Obtained token %s', [CurChar]));
533 //    {$endif}
534     if not IsValidPostScriptChar(Byte(CurChar)) then
535       raise Exception.Create(Format('[TPSTokenizer.ReadFromStream] Invalid char: %s at line %d',
536         [IntToHex(Byte(CurChar), 2), CurLine]));
537 
538     lIsEndOfLine := IsEndOfLine(Byte(CurChar), AStream);
539     if lIsEndOfLine then Inc(CurLine);
540     if FCurLine >= 0 then CurLine := FCurLine;
541 
542     case State of
543       { Searching for a token }
544       ssSearchingToken:
545       begin
546         if CurChar = '%' then
547         begin
548           CommentToken := TCommentToken.Create;
549           CommentToken.Line := CurLine;
550           CommentToken.StrValue := '%';
551           State := ssInComment;
552           lReturnState.Push(Pointer(PtrInt(ssSearchingToken)));
553 //          {$ifdef FPVECTORIALDEBUG}
554 //          WriteLn(Format('Starting Comment at Line %d', [CurLine]));
555 //          {$endif}
556         end
557         else if CurChar = '{' then
558         begin
559           ProcedureToken := TProcedureToken.Create;
560           ProcedureToken.Levels := 1;
561           ProcedureToken.Line := CurLine;
562           State := ssInGroup;
563         end
564         else if CurChar = '[' then
565         begin
566           ArrayToken := TArrayToken.Create;
567           lReturnState.Push(Pointer(PtrInt(ssSearchingToken)));
568           State := ssInArray;
569         end
570         else if CurChar = '<' then
571         begin
572           CurChar := Char(AStream.ReadByte());
573           if CurChar = '<' then
574           begin
575             DictionaryToken := TDictionaryToken.Create;
576             State := ssInDictionary;
577             lReturnState.Push(Pointer(PtrInt(ssSearchingToken)));
578           end
579           else
580             raise Exception.Create(Format('[TPSTokenizer.ReadFromStream] Unexpected char while searching for "<<" token: $%s in Line %d',
581               [IntToHex(Byte(CurChar), 2), CurLine]));
582         end
583         else if CurChar in ['a'..'z','A'..'Z','0'..'9','-','/','('] then
584         begin
585           ExpressionToken := TExpressionToken.Create;
586           ExpressionToken.Line := CurLine;
587           ExpressionToken.StrValue := '';
588           if CurChar = '/' then
589             ExpressionToken.ETType := ettNamedElement
590           else if CurChar = '(' then
591             ExpressionToken.ETType := ettLiteralString
592           else
593           begin
594             ExpressionToken.StrValue := CurChar;
595             if ExpressionToken.IsExpressionOperand() then
596               ExpressionToken.ETType := ettOperand
597             else
598               ExpressionToken.ETType := ettOperator;
599           end;
600           lReturnState.Push(Pointer(PtrInt(ssSearchingToken)));
601           State := ssInExpressionElement;
602         end
603         else if lIsEndOfLine then Continue
604         else if IsPostScriptSpace(Byte(CurChar)) then Continue
605         else
606           raise Exception.Create(Format('[TPSTokenizer.ReadFromStream] Unexpected char while searching for token: $%s in Line %d',
607            [IntToHex(Byte(CurChar), 2), CurLine]));
608       end;
609 
610       { Passing by comments }
611       ssInComment:
612       begin
613         CommentToken.StrValue := CommentToken.StrValue + CurChar;
614         if lIsEndOfLine then
615         begin
616           Tokens.Add(CommentToken);
617           State := TPostScriptScannerState(PtrUint(lReturnState.Pop()));
618 //          {$ifdef FPVECTORIALDEBUG}
619 //          WriteLn(Format('Adding Comment "%s" at Line %d', [CommentToken.StrValue, CurLine]));
620 //          {$endif}
621         end;
622       end; // ssInComment
623 
624       // Starts at [ and ends in ]
625       ssInArray:
626       begin
627         if CurChar = '%' then
628         begin
629           CommentToken := TCommentToken.Create;
630           CommentToken.Line := CurLine;
631           CommentToken.StrValue := '%';
632           State := ssInComment;
633           lReturnState.Push(Pointer(PtrInt(ssInArray)));
634         end
635         // Another array inside the array
636         else if (CurChar = '[') then
637         begin
638           // We are starting another array, so save the parent and go to the new one
639           NewArrayToken := TArrayToken.Create;
640           NewArrayToken.Parent := ArrayToken;
641           ArrayToken.ArrayData.Add(NewArrayToken);
642           ArrayToken := NewArrayToken;
643           lReturnState.Push(Pointer(PtrInt(ssInArray)));
644         end
645         else if (CurChar = ']') then
646         begin
647           ArrayToken.ResolveOperators();
648           if ArrayToken.Parent = nil then
649           begin
650             State := TPostScriptScannerState(PtrUint(lReturnState.Pop()));
651             if State = ssInDictionary then
652             begin
653               DictionaryToken.Childs.Add(ArrayToken);
654             end
655             else
656             begin
657               Tokens.Add(ArrayToken);
658             end;
659           end
660           else
661           begin
662             ArrayToken := ArrayToken.Parent;
663           end;
664         end
665         else if CurChar in ['a'..'z','A'..'Z','0'..'9','-','/','('] then
666         begin
667           ExpressionToken := TExpressionToken.Create;
668           ExpressionToken.Line := CurLine;
669           ExpressionToken.StrValue := '';
670           if CurChar = '/' then
671             ExpressionToken.ETType := ettNamedElement
672           else
673           begin
674             ExpressionToken.StrValue := CurChar;
675             if ExpressionToken.IsExpressionOperand() then
676               ExpressionToken.ETType := ettOperand
677             else
678               ExpressionToken.ETType := ettOperator;
679           end;
680           lReturnState.Push(Pointer(PtrInt(ssInArray)));
681           State := ssInExpressionElement;
682         end
683         else if lIsEndOfLine then Continue
684         else if IsPostScriptSpace(Byte(CurChar)) then Continue;
685       end;
686 
687       // Starts at { and ends in }, passing over nested groups
688       ssInGroup:
689       begin
690         if (CurChar = '{') then ProcedureToken.Levels := ProcedureToken.Levels + 1;
691         if (CurChar = '}') then ProcedureToken.Levels := ProcedureToken.Levels - 1;
692 
693         if ProcedureToken.Levels = 0 then
694         begin
695           Tokens.Add(ProcedureToken);
696           State := ssSearchingToken;
697         end
698         else
699         begin
700           // Don't add line ends, because they cause problems when outputing the debug info
701           // but in this case we need to add spaces to compensate, or else items separates only
702           // by line end might get glued together
703           if CurChar in [#10, #13] then
704             ProcedureToken.StrValue := ProcedureToken.StrValue + ' '
705           else
706             ProcedureToken.StrValue := ProcedureToken.StrValue + CurChar;
707         end;
708       end;
709 
710       // Starts at << and ends in >>
711       ssInDictionary:
712       begin
713         if (CurChar = '>') then
714         begin
715           CurChar := Char(AStream.ReadByte());
716           if CurChar = '>' then
717           begin
718             Tokens.Add(DictionaryToken);
719             State := TPostScriptScannerState(PtrUint(lReturnState.Pop()));
720           end
721           else
722             raise Exception.Create(Format('[TPSTokenizer.ReadFromStream] ssInDictionary: Unexpected char while searching for ">>" token: $%s in Line %d',
723               [IntToHex(Byte(CurChar), 2), CurLine]));
724         end
725         else if CurChar in ['a'..'z','A'..'Z','0'..'9','-','/'] then
726         begin
727           ExpressionToken := TExpressionToken.Create;
728           ExpressionToken.Line := CurLine;
729           ExpressionToken.StrValue := '';
730           if CurChar = '/' then
731             ExpressionToken.ETType := ettNamedElement
732           else
733           begin
734             ExpressionToken.StrValue := CurChar;
735             if ExpressionToken.IsExpressionOperand() then
736               ExpressionToken.ETType := ettOperand
737             else
738               ExpressionToken.ETType := ettOperator;
739           end;
740           lReturnState.Push(Pointer(PtrInt(ssInDictionary)));
741           State := ssInExpressionElement;
742         end
743         else if CurChar = '[' then
744         begin
745           ArrayToken := TArrayToken.Create;
746           lReturnState.Push(Pointer(PtrInt(ssInDictionary)));
747           State := ssInArray;
748         end
749         else if lIsEndOfLine then Continue
750         else if IsPostScriptSpace(Byte(CurChar)) then Continue;
751       end;
752 
753       // Goes until a space comes, or { or [ ...
754       ssInExpressionElement:
755       begin
756         // Literal strings end only in a ")", while other expressions end in a space or delimiter
757         if ExpressionToken.ETType = ettLiteralString then lIsExpressionFinished := CurChar = ')'
758         else lIsExpressionFinished := IsPostScriptSpace(Byte(CurChar)) or (CurChar in ['{', '[', '}', ']', '/', '<', '>', '(', ')']);
759 
760         if lIsExpressionFinished then
761         begin
762           ExpressionToken.PrepareFloatValue();
763           if lReturnState.Count = 0 then lExpressionStateReturn := ssSearchingToken
764           else lExpressionStateReturn := TPostScriptScannerState(PtrUint(lReturnState.Pop()));
765           if lExpressionStateReturn = ssInArray then
766           begin
767             ArrayToken.ArrayData.Add(ExpressionToken);
768             State := ssInArray;
769           end
770           else if lExpressionStateReturn = ssInDictionary then
771           begin
772             DictionaryToken.Childs.Add(ExpressionToken);
773             State := ssInDictionary;
774           end
775           else
776           begin
777             Tokens.Add(ExpressionToken);
778             if ExpressionToken.StrValue = 'image' then
779               State := ssReadingRawDataStart
780             else
781               State := ssSearchingToken;
782           end;
783           if (CurChar in ['{', '[', '}', ']', '<', '>', '%']) then
784           begin
785             AStream.Seek(-1, soFromCurrent);
786           end;
787         end
788         else
789           ExpressionToken.StrValue := ExpressionToken.StrValue + CurChar;
790       end;
791       // Raw data reading
792       ssReadingRawDataStart:
793       begin
794         if IsPostScriptSpace(Byte(CurChar)) then Continue;
795 
796         ExpressionToken := TExpressionToken.Create;
797         ExpressionToken.Line := CurLine;
798         ExpressionToken.StrValue := CurChar;
799         ExpressionToken.ETType := ettRawData;
800         State := ssReadingRawData;
801       end;
802       // ASCII85 and Flate (compressed) go on until this appears: ~>
803       // ToDo: Check if this is valid for all raw data
804       ssReadingRawData:
805       begin
806         if IsPostScriptSpace(Byte(CurChar)) then Continue;
807 
808         ExpressionToken.StrValue := ExpressionToken.StrValue + CurChar;
809 
810         // Check if we are in the end of the raw data
811         lTmpStr := Copy(ExpressionToken.StrValue, Length(ExpressionToken.StrValue)-1, 2);
812         if lTmpStr = '~>' then
813         begin
814           Tokens.Add(ExpressionToken);
815           State := ssSearchingToken;
816         end;
817       end;
818     end; // case
819   end; // while
820 
821   // If the stream finished, there might be a token still being built
822   // so lets finish it
823   if State = ssInExpressionElement then
824   begin
825     Tokens.Add(ExpressionToken);
826   end;
827 
828   finally
829     lReturnState.Free;
830   end;
831 end;
832 
833 procedure TPSTokenizer.DebugOut();
834 var
835   i: Integer;
836   Token: TPSToken;
837 begin
838   for i := 0 to Tokens.Count - 1 do
839   begin
840     Token := TPSToken(Tokens.Items[i]);
841 
842     if Token is TCommentToken then
843     begin
844       WriteLn(Format('TCommentToken StrValue=%s', [Token.StrValue]));
845     end
846     else if Token is TProcedureToken then
847     begin
848       WriteLn(Format('TProcedureToken StrValue=%s', [Token.StrValue]));
849     end
850     else if Token is TExpressionToken then
851     begin
852       WriteLn(Format('TExpressionToken StrValue=%s', [Token.StrValue]));
853     end;
854   end;
855 end;
856 
857 {@@ Valid PostScript Chars:
858 
859 All printable ASCII: a..zA..Z0..9 plus punctuation
860 
861 Plus the following white spaces
862 000 00 0 Null (nul)
863 011 09 9 Tab (tab)
864 012 0A 10 Line feed (LF)
865 014 0C 12 Form feed (FF)
866 015 0D 13 Carriage return (CR)
867 040 20 32 Space (SP)
868 }
IsValidPostScriptCharnull869 function TPSTokenizer.IsValidPostScriptChar(AChar: Byte): Boolean;
870 begin
871   Result := ((AChar > 32) and (AChar < 127)) or (AChar in [0, 9, 10, 12, 13, 32]);
872 end;
873 
IsPostScriptSpacenull874 function TPSTokenizer.IsPostScriptSpace(AChar: Byte): Boolean;
875 begin
876   Result := AChar in [0, 9, 10, 12, 13, 32];
877 end;
878 
IsEndOfLinenull879 function TPSTokenizer.IsEndOfLine(ACurChar: Byte; AStream: TStream): Boolean;
880 var
881   HasNextChar: Boolean = False;
882   NextChar: Byte;
883 begin
884   Result := False;
885 
886   if ACurChar = 13 then
887   begin
888     if AStream.Position < AStream.Size then
889     begin
890       HasNextChar := True;
891       NextChar := AStream.ReadByte();
892       if NextChar <> 10 then AStream.Seek(-1, soFromCurrent); // Go back if it wasnt a #13#10
893       Exit(True);
894     end;
895   end;
896 
897   if ACurChar = 10 then Result := True;
898 end;
899 
900 {$ifndef Windows}
901 {$define FPVECTORIALDEBUG}
902 {$endif}
903 
904 { TvEPSVectorialReader }
905 
906 procedure TvEPSVectorialReader.DebugStack();
907 var
908   i: Integer;
909   lToken: TPSToken;
910 begin
911   WriteLn('====================');
912   WriteLn('Stack dump');
913   WriteLn('====================');
914   for i := 0 to TStackAccess(Stack).List.Count - 1 do
915   begin
916     lToken := TPSToken(TStackAccess(Stack).List.Items[i]);
917     WriteLn(Format('Stack #%d : %s', [i, lToken.StrValue]));
918   end;
919 end;
920 
921 procedure TvEPSVectorialReader.RunPostScript(ATokens: TPsTokens;
922   AData: TvVectorialPage; ADoc: TvVectorialDocument);
923 var
924   i: Integer;
925   lSubstituted: Boolean;
926   CurToken, NextToken: TPSToken;
927 begin
928   {$ifdef FPVECTORIALDEBUG_CODEFLOW}
929   WriteLn('[TvEPSVectorialReader.RunPostScript] START');
930   {$endif}
931   if ExitCalled then
932   begin
933     {$ifdef FPVECTORIALDEBUG_CODEFLOW}
934     WriteLn('[TvEPSVectorialReader.RunPostScript] ExitCalled');
935     {$endif}
936     Exit;
937   end;
938   for i := 0 to ATokens.Count - 1 do
939   begin
940     CurToken := TPSToken(ATokens.Items[i]);
941     // a preview of the next token is sometimes utilized
942     if i < ATokens.Count-1 then NextToken := TPSToken(ATokens.Items[i+1])
943     else NextToken := nil;
944 
945 {    if CurToken.StrValue = 'setrgbcolor' then
946     begin
947       WriteLn('===================');
948       WriteLn('CMYK__');
949       WriteLn('===================');
950       DebugStack();
951     end;}
952 
953     if CurToken is TCommentToken then
954     begin
955       {$ifdef FPVECTORIALDEBUG_CODEFLOW}
956       WriteLn(Format('[TvEPSVectorialReader.RunPostScript] Type: TCommentToken Token: %s', [CurToken.StrValue]));
957       {$endif}
958 //      ProcessCommentToken(CurToken as TCommentToken, AData);
959 
960       // Give up in the trailer to avoid errors in the very end of files
961       if (CurToken.StrValue = '%%Trailer') or (CurToken.StrValue = '%%Trailer'#10) then Exit;
962 
963       Continue;
964     end;
965 
966     if CurToken is TProcedureToken then
967     begin
968       {$ifdef FPVECTORIALDEBUG_CODEFLOW}
969       WriteLn(Format('[TvEPSVectorialReader.RunPostScript] Type: TProcedureToken Token: %s', [CurToken.StrValue]));
970       {$endif}
971       Stack.Push(CurToken);
972       Continue;
973     end;
974 
975     if CurToken is TExpressionToken then
976     begin
977       {$ifdef FPVECTORIALDEBUG_CODEFLOW}
978       WriteLn(Format('[TvEPSVectorialReader.RunPostScript] Type: TExpressionToken Token: %s', [CurToken.StrValue]));
979       {$endif}
980 
981       if (TExpressionToken(CurToken).ETType = ettOperand) or
982         (TExpressionToken(CurToken).ETType = ettDictionary) or
983         (TExpressionToken(CurToken).ETType = ettRawData) then
984       begin
985         Stack.Push(CurToken);
986         Continue;
987       end;
988 
989       // Now we need to verify if the operator should be substituted in the dictionary
990       lSubstituted := DictionarySubstituteOperator(Dictionary, CurToken);
991 
992       // Check if this is the first time that a named element appears, if yes, don't try to execute it
993       // just put it into the stack
994       if (not lSubstituted) and (TExpressionToken(CurToken).ETType = ettNamedElement) then
995       begin
996         Stack.Push(CurToken);
997         Continue;
998       end;
999 
1000       // If we got an array after the substitution, don't run it, just put it in the stack
1001       if CurToken is TArrayToken then
1002       begin
1003         Stack.Push(CurToken);
1004         Continue;
1005       end;
1006 
1007       // sometimes the substitution results in a direct reference to a dictionary
1008       // maybe sometimes to an operand too? In this cases don't try to run the code!
1009       if (TExpressionToken(CurToken).ETType = ettOperand) or
1010         (TExpressionToken(CurToken).ETType = ettDictionary) or
1011         (TExpressionToken(CurToken).ETType = ettRawData) then
1012       begin
1013         Stack.Push(CurToken);
1014         Continue;
1015       end;
1016 
1017       // If we got a procedure from the substitution, run it!
1018       if CurToken is TProcedureToken then ExecuteProcedureToken(TProcedureToken(CurToken), AData, ADoc)
1019       else ExecuteOperatorToken(TExpressionToken(CurToken), AData, ADoc, NextToken);
1020 
1021       if ExitCalled then Break;
1022     end;
1023 
1024     if CurToken is TDictionaryToken then
1025     begin
1026       {$ifdef FPVECTORIALDEBUG_CODEFLOW}
1027       //WriteLn(Format('[TvEPSVectorialReader.RunPostScript] Type: TProcedureToken Token: %s', [CurToken.StrValue]));
1028       {$endif}
1029       Stack.Push(CurToken);
1030       Continue;
1031     end;
1032   end;
1033   {$ifdef FPVECTORIALDEBUG_CODEFLOW}
1034   WriteLn('[TvEPSVectorialReader.RunPostScript] END');
1035   {$endif}
1036 end;
1037 
1038 procedure TvEPSVectorialReader.ExecuteProcedureToken(AToken: TProcedureToken;
1039   AData: TvVectorialPage; ADoc: TvVectorialDocument);
1040 var
1041   ProcTokenizer: TPSTokenizer;
1042   lStream: TMemoryStream;
1043   lOldTokens: TPSTokens;
1044   i: Integer;
1045 begin
1046   {$ifdef FPVECTORIALDEBUG_CODEFLOW}
1047   WriteLn('[TvEPSVectorialReader.ExecuteProcedureToken] START');
1048   {$endif}
1049   if ExitCalled then
1050   begin
1051     {$ifdef FPVECTORIALDEBUG_CODEFLOW}
1052     WriteLn('[TvEPSVectorialReader.ExecuteProcedureToken] ExitCalled');
1053     {$endif}
1054     Exit;
1055   end;
1056 
1057   if not AToken.Parsed then
1058   begin
1059     ProcTokenizer := TPSTokenizer.Create(AToken.Line);
1060     lStream := TMemoryStream.Create;
1061     try
1062       // Copy the string to a Stream
1063       for i := 1 to Length(AToken.StrValue) do
1064         lStream.WriteByte(Byte(AToken.StrValue[i]));
1065 
1066       // Change the Tokens so that it writes directly to AToken.Childs
1067       lOldTokens := ProcTokenizer.Tokens;
1068       ProcTokenizer.Tokens := AToken.Childs;
1069 
1070       // Now parse the procedure code
1071       lStream.Position := 0;
1072       ProcTokenizer.ReadFromStream(lStream);
1073 
1074       // Recover the old tokens for usage in .Free
1075       ProcTokenizer.Tokens := lOldTokens;
1076     finally
1077       lStream.Free;
1078       ProcTokenizer.Free;
1079     end;
1080 
1081     AToken.Parsed := True;
1082   end;
1083 
1084   // Now run the procedure
1085   RunPostScript(AToken.Childs, AData, ADoc);
1086   {$ifdef FPVECTORIALDEBUG_CODEFLOW}
1087   WriteLn('[TvEPSVectorialReader.ExecuteProcedureToken] END');
1088   {$endif}
1089 end;
1090 
1091 procedure TvEPSVectorialReader.ExecuteOperatorToken(AToken: TExpressionToken;
1092   AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken);
1093 begin
1094   if AToken.StrValue = '' then
1095   begin
1096     // A clean exit if the token was substituted by something else which cannot be executed
1097     if AToken.SubstituteETType <> ettInvalid then Exit;
1098     raise Exception.Create(Format('[TvEPSVectorialReader.ProcessExpressionToken] Empty operator line=%d', [AToken.Line]));
1099   end;
1100 
1101   if ExecuteDictionaryOperators(AToken, AData, ADoc) then Exit;
1102 
1103   if ExecuteArithmeticAndMathOperator(AToken, AData, ADoc) then Exit;
1104 
1105   if ExecutePathConstructionOperator(AToken, AData, ADoc) then Exit;
1106 
1107   if ExecuteGraphicStateOperatorsDI(AToken, AData, ADoc) then Exit;
1108 
1109   if ExecuteGraphicStateOperatorsDD(AToken, AData, ADoc) then Exit;
1110 
1111   if ExecuteControlOperator(AToken, AData, ADoc) then Exit;
1112 
1113   if ExecuteStackManipulationOperator(AToken, AData, ADoc) then Exit;
1114 
1115   if ExecuteMiscellaneousOperators(AToken, AData, ADoc) then Exit;
1116 
1117   if ExecutePaintingOperator(AToken, AData, ADoc, ANextToken) then Exit;
1118 
1119   if ExecuteDeviceSetupAndOutputOperator(AToken, AData, ADoc) then Exit;
1120 
1121   if ExecuteArrayOperator(AToken, AData, ADoc) then Exit;
1122 
1123   if ExecuteStringOperator(AToken, AData, ADoc) then Exit;
1124 
1125   if ExecuteFileOperator(AToken, AData, ADoc) then Exit;
1126 
1127   if ExecuteResourceOperator(AToken, AData, ADoc) then Exit;
1128 
1129   if ExecuteVirtualMemoryOperator(AToken, AData, ADoc) then Exit;
1130 
1131   if ExecuteErrorOperator(AToken, AData, ADoc) then Exit;
1132 
1133   // If we got here, there the command not yet implemented
1134   raise Exception.Create(Format('[TvEPSVectorialReader.ProcessExpressionToken] Unknown PostScript Command "%s" in Line %d',
1135     [AToken.StrValue, AToken.Line]));
1136 end;
1137 
1138 { Operand Stack Manipulation Operators
1139 
1140   any pop –                    Discard top element
1141   any1 any2 exch ==> any2 any1 Exchange top two elements
1142   any dup ==> any any          Duplicate top element
1143   any1 … anyn n copy any1 … anyn any1 … anyn
1144                                Duplicate top n elements
1145   anyn … any0 n index anyn … any0 anyn
1146                                Duplicate arbitrary element
1147   anyn-1 … any0 n j roll any(j-1) mod n … any0 anyn-1 … anyj mod n
1148                                Roll n elements up j times
1149   any1 … anyn clear            Discard all elements
1150   any1 … anyn count any1 … anyn n
1151                                Count elements on stack
1152   – mark mark                  Push mark on stack
1153   mark obj1 … objn cleartomark –
1154                                Discard elements down through mark
1155   mark obj1 … objn counttomark mark obj1 … objn n
1156                                Count elements down to mark
1157 }
TvEPSVectorialReader.ExecuteStackManipulationOperatornull1158 function TvEPSVectorialReader.ExecuteStackManipulationOperator(
1159   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
1160 var
1161   Param1, Param2, NewToken: TPSToken;
1162   NewExprToken: TExpressionToken;
1163   lIndexN, lIndexJ: Integer;
1164   lTokens: array of TPSToken;
1165   i: Integer;
1166 begin
1167   Result := False;
1168 
1169   // Discard top element
1170   if AToken.StrValue = 'pop' then
1171   begin
1172     Param1 := TPSToken(Stack.Pop);
1173     Exit(True);
1174   end;
1175   // Exchange top two elements
1176   if AToken.StrValue = 'exch' then
1177   begin
1178     Param1 := TPSToken(Stack.Pop);
1179     Param2 := TPSToken(Stack.Pop);
1180     Stack.Push(Param1);
1181     Stack.Push(Param2);
1182     Exit(True);
1183   end;
1184   // Duplicate top element
1185   if AToken.StrValue = 'dup' then
1186   begin
1187     Param1 := TPSToken(Stack.Pop);
1188     NewToken := Param1.Duplicate();
1189     Stack.Push(Param1);
1190     Stack.Push(NewToken);
1191     Exit(True);
1192   end;
1193   // any1 … anyn count any1 … anyn n
1194   // Count elements on stack
1195   if AToken.StrValue = 'count' then
1196   begin
1197     NewExprToken := TExpressionToken.Create;
1198     NewExprToken.ETType := ettOperand;
1199     NewExprToken.FloatValue := Stack.Count;
1200     NewExprToken.StrValue := IntToStr(Stack.Count);
1201     Stack.Push(NewExprToken);
1202     Exit(True);
1203   end;
1204   // anyn … any0 n index anyn … any0 anyn
1205   // Duplicate arbitrary element
1206   if AToken.StrValue = 'index' then
1207   begin
1208     {$ifdef FPVECTORIALDEBUG_INDEX}
1209     WriteLn('[TvEPSVectorialReader.ExecuteStackManipulationOperator] index');
1210 //    DebugStack();
1211     {$endif}
1212 
1213     Param1 := TPSToken(Stack.Pop);
1214     lIndexN := Round(Param1.FloatValue);
1215     SetLength(lTokens, lIndexN+1);
1216 
1217     if lIndexN < 0 then raise Exception.Create('[TvEPSVectorialReader.ExecuteStackManipulationOperator] index operator: n must be positive or zero');
1218 
1219     // Unroll all elements necessary
1220 
1221     for i := 0 to lIndexN do
1222     begin
1223       lTokens[i] := TPSToken(Stack.Pop);
1224       Param2 := lTokens[i];
1225       if Param2 = nil then
1226       begin
1227         raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteStackManipulationOperator] Stack underflow in operation "index". Error at line %d', [AToken.Line]));
1228       end;
1229     end;
1230 
1231     // Duplicate the disired token
1232 
1233     NewToken := lTokens[lIndexN].Duplicate();
1234 
1235     // Roll them back
1236 
1237     for i := lIndexN downto 0 do
1238     begin
1239       Stack.Push(lTokens[i]);
1240     end;
1241 
1242     // Roll the duplicated element too
1243 
1244     Stack.Push(NewToken);
1245 
1246     Exit(True);
1247   end;
1248   // anyn-1 … any0 n j roll any(j-1) mod n … any0 anyn-1 … anyj mod n
1249   //
1250   // performs a circular shift of the objects anyn-1 through any0 on the operand stack
1251   // by the amount j. Positive j indicates upward motion on the stack, whereas negative
1252   // j indicates downward motion.
1253   // n must be a nonnegative integer and j must be an integer. roll first removes these
1254   // operands from the stack; there must be at least n additional elements. It then performs
1255   // a circular shift of these n elements by j positions.
1256   // If j is positive, each shift consists of removing an element from the top of the stack
1257   // and inserting it between element n - 1 and element n of the stack, moving all in8.2
1258   // tervening elements one level higher on the stack. If j is negative, each shift consists
1259   // of removing element n - 1 of the stack and pushing it on the top of the stack,
1260   // moving all intervening elements one level lower on the stack.
1261   //
1262   // Examples    N J
1263   // (a) (b) (c) 3 -1 roll => (b) (c) (a)
1264   // (a) (b) (c) 3 1 roll  => (c) (a) (b)
1265   // (a) (b) (c) 3 0 roll  => (a) (b) (c)
1266   if AToken.StrValue = 'roll' then
1267   begin
1268     Param1 := TPSToken(Stack.Pop);
1269     Param2 := TPSToken(Stack.Pop);
1270     lIndexJ := Round(Param1.FloatValue);
1271     lIndexN := Round(Param2.FloatValue);
1272 
1273     {$ifdef FPVECTORIALDEBUG_ROLL}
1274     WriteLn(Format('[TvEPSVectorialReader] roll: N=%d J=%d', [lIndexN, lIndexJ]));
1275     {$endif}
1276 
1277     if lIndexN < 0 then raise Exception.Create('[TvEPSVectorialReader.ExecuteStackManipulationOperator] rool operator: n must be positive or zero');
1278 
1279     if lIndexJ = 0 then Exit(True);
1280 
1281     SetLength(lTokens, lIndexN);
1282 
1283     // Unroll all elements necessary
1284 
1285     for i := 0 to lIndexN-1 do
1286     begin
1287       lTokens[i] := TPSToken(Stack.Pop());
1288       Param2 := lTokens[i];
1289       if Param2 = nil then
1290       begin
1291         raise Exception.Create('[TvEPSVectorialReader.ExecuteStackManipulationOperator] nil element poped in operator index');
1292         //Exit(True);
1293       end;
1294     end;
1295 
1296     // Roll them back
1297 
1298     if lIndexJ > 0 then
1299     begin
1300       for i := lIndexJ-1 downto 0 do
1301       begin
1302         Stack.Push(lTokens[i]);
1303       end;
1304       for i := lIndexN-1 downto lIndexJ do
1305       begin
1306         Stack.Push(lTokens[i]);
1307       end;
1308     end
1309     else
1310     begin
1311       lIndexJ := -lIndexJ;
1312 
1313       for i := lIndexN-lIndexJ-1 downto 0 do
1314       begin
1315         Stack.Push(lTokens[i]);
1316       end;
1317       for i := lIndexN-1 downto lIndexN-lIndexJ do
1318       begin
1319         Stack.Push(lTokens[i]);
1320       end;
1321     end;
1322 
1323     Exit(True);
1324   end;
1325 end;
1326 
1327 {  Control Operators
1328 
1329   any exec –          Execute arbitrary object
1330   bool proc if –      Execute proc if bool is true
1331   bool proc1 proc2 ifelse –
1332                       Execute proc1 if bool is true, proc2 if false
1333   initial increment limit proc for –
1334                       Execute proc with values from initial by steps
1335                       of increment to limit
1336   int proc repeat –   Execute proc int times
1337   proc loop –         Execute proc an indefinite number of times
1338   – exit –            Exit innermost active loop
1339   – stop –            Terminate stopped context
1340   any stopped bool    Establish context for catching stop
1341   – countexecstack int Count elements on execution stack
1342   array execstack subarray Copy execution stack into array
1343   – quit – Terminate interpreter
1344   – start – Executed at interpreter startup
1345   Type, Attribute, and Conversion Operators
1346   any type name Return type of any
1347   any cvlit any Make object literal
1348   any cvx any Make object executable
1349   any xcheck bool     Test executable attribute
1350   array|packedarray|file|string executeonly array|packedarray|file|string
1351   Reduce access to execute-only
1352   array|packedarray|dict|file|string noaccess array|packedarray|dict|file|string
1353   Disallow any access
1354   array|packedarray|dict|file|string readonly array|packedarray|dict|file|string
1355   Reduce access to read-only
1356   array|packedarray|dict|file|string rcheck bool Test read access
1357   array|packedarray|dict|file|string wcheck bool Test write access
1358   num|string cvi int Convert to integer
1359   string cvn name Convert to name
1360   num|string cvr real Convert to real
1361   num radix string cvrs substring Convert with radix to string
1362   any string cvs substring Convert to string
1363 }
TvEPSVectorialReader.ExecuteControlOperatornull1364 function TvEPSVectorialReader.ExecuteControlOperator(AToken: TExpressionToken;
1365   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
1366 var
1367   Param1, Param2, Param3, Param4, CounterToken: TPSToken;
1368   NewToken: TExpressionToken;
1369   FloatCounter: Double;
1370   i, lRepeatCount: Integer;
1371 begin
1372   Result := False;
1373 
1374   // any exec –          Execute arbitrary object
1375   if AToken.StrValue = 'exec' then
1376   begin
1377     Param1 := TPSToken(Stack.Pop); // proc
1378 
1379     if (Param1 is TProcedureToken) then
1380       ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
1381 
1382     if (Param1 is TExpressionToken) then
1383       ExecuteOperatorToken(TExpressionToken(Param1), AData, ADoc, nil); // ToDo: Add next token for image
1384 
1385     Exit(True);
1386   end;
1387   // Execute proc if bool is true
1388   if AToken.StrValue = 'if' then
1389   begin
1390     Param1 := TPSToken(Stack.Pop); // proc
1391     Param2 := TPSToken(Stack.Pop); // bool
1392 
1393     if not (Param1 is TProcedureToken) then
1394       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator if requires a procedure. Error at line %d', [AToken.Line]));
1395 
1396     if Param2.BoolValue then ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
1397 
1398     Exit(True);
1399   end;
1400   // Execute proc1 if bool is true, proc2 if false
1401   if AToken.StrValue = 'ifelse' then
1402   begin
1403     Param1 := TPSToken(Stack.Pop); // proc2
1404     Param2 := TPSToken(Stack.Pop); // proc1
1405     Param3 := TPSToken(Stack.Pop); // bool
1406 
1407     if not (Param1 is TProcedureToken) then
1408       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator ifelse requires a procedure. Error at line %d', [AToken.Line]));
1409     if not (Param2 is TProcedureToken) then
1410       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator ifelse requires a procedure. Error at line %d', [AToken.Line]));
1411 
1412     if Param3.BoolValue then ExecuteProcedureToken(TProcedureToken(Param2), AData, ADoc)
1413     else ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
1414 
1415     Exit(True);
1416   end;
1417   // int proc repeat –   Execute proc int times
1418   if AToken.StrValue = 'repeat' then
1419   begin
1420     Param1 := TPSToken(Stack.Pop); // proc
1421     Param2 := TPSToken(Stack.Pop); // num
1422 
1423     if not (Param1 is TProcedureToken) then
1424       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator repeat requires a procedure. Error at line %d', [AToken.Line]));
1425 
1426     lRepeatCount := Round(Param2.FloatValue);
1427     for i := 0 to lRepeatCount - 1 do
1428     begin
1429       ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
1430       if ExitCalled then
1431       begin
1432         ExitCalled := False;
1433         Break;
1434       end;
1435     end;
1436 
1437     Exit(True);
1438   end;
1439   // Exit innermost active loop
1440   if AToken.StrValue = 'exit' then
1441   begin
1442     ExitCalled := True;
1443 
1444     Exit(True);
1445   end;
1446   {
1447     Establish context for catching stop
1448 
1449      executes any, which is typically, but not necessarily, a procedure, executable file,
1450      or executable string object. If any runs to completion normally, stopped returns false on the operand stack.
1451 
1452      If any terminates prematurely as a result of executing stop, stopped returns
1453      true on the operand stack. Regardless of the outcome, the interpreter resumes execution at the next object in normal sequence after stopped.
1454      This mechanism provides an effective way for a PostScript language program
1455      to "catch" errors or other premature terminations, retain control, and perhaps perform its own error recovery.
1456 
1457      EXAMPLE:
1458      { ... } stopped {handleerror} if
1459 
1460      If execution of the procedure {...} causes an error,
1461      the default error-reporting procedure is invoked (by handleerror).
1462      In any event, normal execution continues at the token following the if.
1463 
1464      ERRORS: stackunderflow
1465   }
1466   if AToken.StrValue = 'stopped' then
1467   begin
1468     {$ifdef FPVECTORIALDEBUG_CONTROL}
1469     WriteLn('[TvEPSVectorialReader.ExecuteControlOperator] stopped');
1470 //    DebugStack();
1471     {$endif}
1472 
1473     Param1 := TPSToken(Stack.Pop);
1474 
1475     if not (Param1 is TProcedureToken) then
1476       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator stopped requires a procedure. Error at line %d', [AToken.Line]));
1477 
1478     ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
1479 
1480     NewToken := TExpressionToken.Create;
1481     NewToken.ETType := ettOperand;
1482     NewToken.BoolValue := False;
1483     NewToken.StrValue := 'false';
1484     Stack.Push(NewToken);
1485 
1486     Exit(True);
1487   end;
1488   // Execute proc an indefinite number of times
1489   if AToken.StrValue = 'loop' then
1490   begin
1491     Param1 := TPSToken(Stack.Pop);
1492 
1493     if not (Param1 is TProcedureToken) then
1494       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator loop requires a procedure. Error at line %d', [AToken.Line]));
1495 
1496     while True do
1497     begin
1498       ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
1499 
1500       if ExitCalled then
1501       begin
1502         ExitCalled := False;
1503         Break;
1504       end;
1505     end;
1506 
1507     Exit(True);
1508   end;
1509   { initial increment limit proc for -
1510 
1511    executes proc repeatedly, passing it a sequence of values from initial
1512    by steps of increment to limit. The for operator expects initial, increment,
1513    and limit to be numbers. It maintains a temporary internal variable, known as
1514    the control variable, which it first sets to initial. Then, before each
1515    repetition, it compares the control variable with the termination value limit.
1516    If limit has not been exceeded, it pushes the control variable on the operand
1517    stack, executes proc, and adds increment to the control variable.
1518 
1519    The termination condition depends on whether increment is positive or negative.
1520    If increment is positive, for terminates when the control variable becomes
1521    greater than limit. If increment is negative, for terminates when the control
1522    variable becomes less than limit. If initial meets the termination condition,
1523    for does not execute proc at all. If proc executes the exit operator,
1524    for terminates prematurely.
1525 
1526    Usually, proc will use the value on the operand stack for some purpose.
1527    However, if proc does not remove the value, it will remain there.
1528    Successive executions of proc will cause successive values of the control
1529    variable to accumulate on the operand stack.
1530 
1531    EXAMPLE:
1532    0 1 1 4 {add} for -> 10
1533    1 2 6 { } for -> 1 3 5
1534    3 -.5 1 {-> } for -> 3.0 2.5 2.0 1.5 1.0
1535 
1536    In the first example, the value of the control variable is added to whatever
1537    is on the stack, so 1, 2, 3, and 4 are added in turn to a running sum whose
1538    initial value is 0. The second example has an empty procedure, so the
1539    successive values of the control variable are left on the stack. The
1540    last example counts backward from 3 to 1 by halves, leaving the successive
1541    values on the stack.
1542 
1543    Beware of using reals instead of integers for any of the first three operands.
1544    Most real numbers are not represented exactly. This can cause an error to
1545    accumulate in the value of the control variable, with possibly surprising results.
1546    In particular, if the difference between initial and limit is a multiple of
1547    increment, as in the third line of the example, the control variable may not
1548    achieve the limit value.
1549 
1550    ERRORS: stackoverflow stackunderflow, typecheck
1551 
1552    SEE ALSO: repeat, loop, forall, exit
1553   }
1554   if AToken.StrValue = 'for' then
1555   begin
1556     Param1 := TPSToken(Stack.Pop);
1557     Param2 := TPSToken(Stack.Pop);
1558     Param3 := TPSToken(Stack.Pop);
1559     Param4 := TPSToken(Stack.Pop);
1560 
1561     if not (Param1 is TProcedureToken) then
1562       raise Exception.Create(Format('[TvEPSVectorialReader.ExecuteControlOperator] The operator for requires a procedure. Error at line %d', [AToken.Line]));
1563 
1564     FloatCounter := Param4.FloatValue;
1565     while FloatCounter < Param2.FloatValue do
1566     begin
1567       CounterToken := Param4.Duplicate();
1568       CounterToken.FloatValue := FloatCounter;
1569       Stack.Push(CounterToken);
1570 
1571       ExecuteProcedureToken(TProcedureToken(Param1), AData, ADoc);
1572 
1573       FloatCounter := FloatCounter + Param3.FloatValue;
1574 
1575       if ExitCalled then
1576       begin
1577         ExitCalled := False;
1578         Break;
1579       end;
1580     end;
1581 
1582     Exit(True);
1583   end;
1584   // any cvx any Make object executable
1585   if AToken.StrValue = 'cvx' then
1586   begin
1587     Param1 := TPSToken(Stack.Pop);
1588 
1589     if Param1 is TExpressionToken then
1590       TExpressionToken(Param1).ETType := ettOperator;
1591 
1592     Stack.Push(Param1);
1593 
1594     Exit(True);
1595   end;
1596   // tests whether the operand has the executable or the literal attribute, returning true
1597   // if it is executable or false if it is literal
1598   if AToken.StrValue = 'xcheck' then
1599   begin
1600 //    {$ifdef FPVECTORIALDEBUG_CONTROL}
1601 //    WriteLn('[TvEPSVectorialReader.ExecuteControlOperator] xcheck');
1602 //    DebugStack();
1603 //    {$endif}
1604 
1605     Param1 := TPSToken(Stack.Pop);
1606 
1607     NewToken := TExpressionToken.Create;
1608     NewToken.ETType := ettOperand;
1609     NewToken.BoolValue := (Param1 is TProcedureToken) or
1610       ((Param1 is TExpressionToken) and (TExpressionToken(Param1).ETType = ettOperator));
1611     if NewToken.BoolValue then NewToken.StrValue := 'true'
1612     else NewToken.StrValue := 'false';
1613     Stack.Push(NewToken);
1614 
1615     Exit(True);
1616   end;
1617 end;
1618 
1619 {  Painting Operators
1620 
1621   – erasepage –   Paint current page white
1622   – stroke –      Draw line along current path
1623   – fill –        Fill current path with current color
1624   – eofill –      Fill using even-odd rule
1625   x y width height rectstroke – Define rectangular path and stroke
1626   x y width height matrix rectstroke – Define rectangular path, concatenate matrix,
1627                                        and stroke
1628   numarray|numstring rectstroke – Define rectangular paths and stroke
1629   numarray|numstring matrix rectstroke – Define rectangular paths, concatenate
1630                                          matrix, and stroke
1631   x y width height rectfill – Fill rectangular path
1632   numarray|numstring rectfill – Fill rectangular paths
1633   userpath ustroke – Interpret and stroke userpath
1634   userpath matrix ustroke – Interpret userpath, concatenate matrix, and
1635                             stroke
1636   userpath ufill – Interpret and fill userpath
1637   userpath ueofill – Fill userpath using even-odd rule
1638   dict shfill – Fill area defined by shading pattern
1639   dict image – Paint any sampled image
1640   width height bits/sample matrix datasrc image – Paint monochrome sampled image
1641   width height bits/comp matrix
1642   datasrc0 … datasrcncomp-1 multi ncomp colorimage – Paint color sampled image
1643   dict imagemask – Paint current color through mask
1644   width height polarity matrix datasrc imagemask – Paint current color through mask
1645   Insideness-Testing Operators
1646   x y infill bool Test whether (x, y) would be painted by fill
1647   userpath infill bool Test whether pixels in userpath would be
1648   painted by fill
1649   x y ineofill bool Test whether (x, y) would be painted by eofill
1650   userpath ineofill bool Test whether pixels in userpath would be
1651   painted by eofill
1652   x y userpath inufill bool Test whether (x, y) would be painted by ufill
1653   of userpath
1654   userpath1 userpath2 inufill bool Test whether pixels in userpath1 would be
1655   painted by ufill of userpath2
1656   x y userpath inueofill bool Test whether (x, y) would be painted by
1657   ueofill of userpath
1658   userpath1 userpath2 inueofill bool Test whether pixels in userpath1 would be
1659   painted by ueofill of userpath2
1660   x y instroke bool Test whether (x, y) would be painted by
1661   stroke
1662   x y userpath inustroke bool Test whether (x, y) would be painted by
1663   ustroke of userpath
1664   x y userpath matrix inustroke bool Test whether (x, y) would be painted by
1665   ustroke of userpath
1666   userpath1 userpath2 inustroke bool Test whether pixels in userpath1 would be
1667   painted by ustroke of userpath2
1668   userpath1 userpath2 matrix inustroke bool Test whether pixels in userpath1 would be
1669   painted by ustroke of userpath2
1670   Form and Pattern Operators
1671   pattern matrix makepattern pattern’ Create pattern instance from prototype
1672   pattern setpattern – Install pattern as current color
1673   comp1 … compn pattern setpattern – Install pattern as current color
1674   form execform – Paint form
1675 
1676   Other painting operators:
1677 
1678   x y width height rectclip –
1679     numarray rectclip –
1680     numstring rectclip –
1681 }
ExecutePaintingOperatornull1682 function TvEPSVectorialReader.ExecutePaintingOperator(AToken: TExpressionToken;
1683   AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken): Boolean;
1684 var
1685   Param1, Param2: TPSToken;
1686 begin
1687   Result := False;
1688 
1689   if AToken.StrValue = 'stroke' then
1690   begin
1691     {$ifdef FPVECTORIALDEBUG_PATHS}
1692     WriteLn('[TvEPSVectorialReader.ExecutePaintingOperator] stroke');
1693     {$endif}
1694     AData.SetPenStyle(psSolid);
1695     AData.SetBrushStyle(bsClear);
1696     AData.SetPenColor(CurrentGraphicState.Color);
1697     AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
1698     AData.SetPenWidth(CurrentGraphicState.PenWidth);
1699     AData.EndPath();
1700     Exit(True);
1701   end;
1702   // – fill –        Fill current path with current color
1703   if AToken.StrValue = 'fill' then
1704   begin
1705     {$ifdef FPVECTORIALDEBUG_PATHS}
1706     WriteLn('[TvEPSVectorialReader.ExecutePaintingOperator] fill');
1707     {$endif}
1708     AData.SetBrushStyle(bsSolid);
1709     AData.SetPenStyle(psSolid);
1710     AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
1711     AData.SetPenWidth(CurrentGraphicState.PenWidth);
1712     AData.EndPath();
1713 
1714     Exit(True);
1715   end;
1716   // – eofill –      Fill using even-odd rule
1717   if AToken.StrValue = 'eofill' then
1718   begin
1719     {$ifdef FPVECTORIALDEBUG_PATHS}
1720     WriteLn('[TvEPSVectorialReader.ExecutePaintingOperator] eofill');
1721     {$endif}
1722     AData.SetBrushStyle(bsSolid);
1723     AData.SetPenStyle(psSolid);
1724     AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
1725     AData.SetPenWidth(CurrentGraphicState.PenWidth);
1726     AData.EndPath();
1727 
1728     Exit(True);
1729   end;
1730   // dict image – Paint any sampled image
1731   if AToken.StrValue = 'image' then
1732   begin
1733     Result := ExecuteImageOperator(AToken, AData, ADoc, ANextToken);
1734   end;
1735   //x y width height rectclip –
1736   //  numarray rectclip –
1737   //  numstring rectclip –
1738   if AToken.StrValue = 'rectclip' then
1739   begin
1740     // ToDo: Check for numarray and numstring
1741     // Todo: Implement properly
1742     Param1 := TPSToken(Stack.Pop);
1743     Param1 := TPSToken(Stack.Pop);
1744     Param1 := TPSToken(Stack.Pop);
1745     Param1 := TPSToken(Stack.Pop);
1746     Exit(True);
1747   end;
1748 end;
1749 
1750 // The "image" operator is very complex, so we have a separate routine only for it =)
ExecuteImageOperatornull1751 function TvEPSVectorialReader.ExecuteImageOperator(AToken: TExpressionToken;
1752   AData: TvVectorialPage; ADoc: TvVectorialDocument; ANextToken: TPSToken
1753   ): Boolean;
1754 var
1755   Param1, Param2: TPSToken;
1756   // image operator data
1757   lRasterImage: TvRasterImage;
1758   lColor: TFPColor;
1759   i, x, y, lFindIndex: Integer;
1760   lDataSource, lImageDataStr: String;
1761   lImageType, lImageWidth, lImageHeight, lImageBitsPerComponent: Integer;
1762   lImageData, lImageDataCompressed: array of Byte;
1763   lCurDictToken: TPSToken;
1764   lColorC, lColorM, lColorY, lColorK: Double;
1765   lImageMatrix: TArrayToken;
1766   lMatrixTranslateX, lMatrixTranslateY, lMatrixScaleX, lMatrixScaleY,
1767     lMatrixSkewX, lMatrixSkewY, lMatrixRotate: Double;
1768 begin
1769   Result := False;
1770   Param1 := TPSToken(Stack.Pop);
1771 
1772   // Decode the dictionary into a list of names
1773   if not (Param1 is TDictionaryToken) then
1774     raise Exception.Create(Format('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Param1 is not a dictionary but should be. Param1.ClassName=%s', [Param1.ClassName]));
1775   TDictionaryToken(Param1).TransformToListOfNamedValues();
1776 
1777   // Read the source of the data
1778   TDictionaryToken(Param1).Names.Sorted := True;
1779   if TDictionaryToken(Param1).Names.Find('DataSource', lFindIndex) then
1780   begin
1781     lDataSource := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]).StrValue;
1782     if not (lDataSource = 'currentfile') then
1783       raise Exception.Create(Format('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Unimplemented data source: %s', [lDataSource]));
1784   end
1785   else
1786   begin
1787     // suppose that the source is the current file
1788   end;
1789 
1790   // Decode the image
1791   if ANextToken = nil then raise Exception.Create('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Image contents expected but nothing found.');
1792   if not (ANextToken is TExpressionToken) then raise Exception.Create('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Image contents is not a TExpressionToken.');
1793   if TExpressionToken(ANextToken).ETType <> ettRawData then raise Exception.Create('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: Image contents is not a raw data.');
1794   lImageDataStr := TExpressionToken(ANextToken).StrValue;
1795   SetLength(lImageDataStr, Length(lImageDataStr)-2); // Remove the final ~>
1796   {$ifdef FPVECTORIAL_DEFLATE_DEBUG}
1797   FPVUDebugLn('[image] ImageDataStr='+lImageDataStr);
1798   {$endif}
1799 
1800   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('ASCII85Decode');
1801   if lFindIndex > 0 then
1802   begin
1803     DecodeASCII85(lImageDataStr, lImageData);
1804   end;
1805 
1806   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('FlateDecode');
1807   if lFindIndex > 0 then
1808   begin
1809     if Length(lImageData) = 0 then raise Exception.Create('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: no byte array prepared for FlateDecode. ASCII85Decode is missing.');
1810     lImageDataCompressed := lImageData;
1811     SetLength(lImageData, 0);
1812     DeflateBytes(lImageDataCompressed, lImageData);
1813   end;
1814 
1815   // Dictionary information
1816   lImageType := 1;
1817   lImageWidth := 0;
1818   lImageHeight := 0;
1819   lImageBitsPerComponent := 0;
1820   lImageMatrix := nil;
1821   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('ImageType');
1822   // debug dump all dictionary names
1823   {$ifdef FPVECTORIAL_IMAGE_DICTIONARY_DEBUG}
1824   FPVUDebug('TDictionaryToken(Param1).Names=');
1825   for i := 0 to TDictionaryToken(Param1).Names.Count-1 do
1826   begin
1827     FPVUDebug(TDictionaryToken(Param1).Names.Strings[i]+' ');
1828   end;
1829   FPVUDebugLn('');
1830   {$endif}
1831   if lFindIndex > 0 then
1832   begin
1833     lCurDictToken := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]);
1834     lCurDictToken.PrepareIntValue();
1835     lImageType := lCurDictToken.IntValue;
1836   end;
1837   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('Width');
1838   if lFindIndex > 0 then
1839   begin
1840     lCurDictToken := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]);
1841     lCurDictToken.PrepareIntValue();
1842     lImageWidth := lCurDictToken.IntValue;
1843   end;
1844   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('Height');
1845   if lFindIndex > 0 then
1846   begin
1847     lCurDictToken := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]);
1848     lCurDictToken.PrepareIntValue();
1849     lImageHeight := lCurDictToken.IntValue;
1850   end;
1851   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('BitsPerComponent');
1852   if lFindIndex > 0 then
1853   begin
1854     lCurDictToken := TPSToken(TDictionaryToken(Param1).Values[lFindIndex]);
1855     lCurDictToken.PrepareIntValue();
1856     lImageBitsPerComponent := lCurDictToken.IntValue;
1857   end;
1858   lFindIndex := TDictionaryToken(Param1).Names.IndexOf('ImageMatrix');
1859   if lFindIndex > 0 then
1860   begin
1861     lImageMatrix := TArrayToken(TDictionaryToken(Param1).Values[lFindIndex]);
1862   end;
1863 
1864   // Read the image
1865   lRasterImage := TvRasterImage.Create(nil);
1866   lRasterImage.CreateRGB888Image(lImageWidth, lImageHeight);
1867   if CurrentGraphicState.ColorSpaceName = 'DeviceCMYK' then
1868   begin
1869     if (lImageWidth*lImageHeight)*4 > Length(lImageData) then
1870       raise Exception.Create(Format('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: image data too small. Expected=%d Found=%d', [Length(lImageData), (lImageWidth*lImageHeight)*4]));
1871     for x := 0 to lImageWidth - 1 do
1872       for y := 0 to lImageHeight - 1 do
1873       begin
1874         lColorC := lImageData[(x+y*lImageWidth)*4] / $FF;
1875         lColorM := lImageData[(x+y*lImageWidth)*4+1] / $FF;
1876         lColorY := lImageData[(x+y*lImageWidth)*4+2] / $FF;
1877         lColorK := lImageData[(x+y*lImageWidth)*4+3] / $FF;
1878         lColor.Alpha := alphaOpaque;
1879         lColor.Red := Round($FF * (1-lColorC) * (1-lColorK) * $101);
1880         lColor.Green := Round($FF * (1-lColorM) * (1-lColorK) * $101);
1881         lColor.Blue := Round($FF * (1-lColorY) * (1-lColorK) * $101);
1882         lRasterImage.RasterImage.Colors[x, y] := lColor;
1883       end;
1884   end
1885   else
1886   begin
1887     if (lImageWidth*lImageHeight)*3 > Length(lImageData) then
1888       raise Exception.Create(Format('[TvEPSVectorialReader.ExecutePaintingOperator] operator image: image data too small. Expected=%d Found=%d', [Length(lImageData), (lImageWidth*lImageHeight)*3]));
1889     for x := 0 to lImageWidth - 1 do
1890       for y := 0 to lImageHeight - 1 do
1891       begin
1892         lColor.Alpha := alphaOpaque;
1893         lColor.Red := lImageData[(x+y*lImageWidth)*3] * $101;
1894         lColor.Green := lImageData[(x+y*lImageWidth)*3+1] * $101;
1895         lColor.Blue := lImageData[(x+y*lImageWidth)*3+2] * $101;
1896         lRasterImage.RasterImage.Colors[x, y] := lColor;
1897       end;
1898   end;
1899 
1900   // Get information from the ImageMatrix
1901   // for example:   1  b c   d     f
1902   // /ImageMatrix [163 0 0 -134 0 134]
1903   //                       (163   0    0  )
1904   // means that we have:   ( 0  -134  134 )
1905   //                       ( 0    0    1  )
1906   // which means:
1907   // TranslateY(134)
1908   // scaleX(163)
1909   // scaleY(-134)
1910   // all inverted, since the matrix is user->image
1911   // and we want image->user
1912   if lImageMatrix <> nil then
1913   begin
1914     ConvertTransformationMatrixToOperations(
1915       lImageMatrix.GetNumber(0), lImageMatrix.GetNumber(1),
1916       lImageMatrix.GetNumber(2), lImageMatrix.GetNumber(3),
1917       lImageMatrix.GetNumber(4), lImageMatrix.GetNumber(5),
1918       lMatrixTranslateX, lMatrixTranslateY, lMatrixScaleX, lMatrixScaleY,
1919       lMatrixSkewX, lMatrixSkewY, lMatrixRotate);
1920     InvertMatrixOperations(
1921       lMatrixTranslateX, lMatrixTranslateY, lMatrixScaleX, lMatrixScaleY,
1922       lMatrixSkewX, lMatrixSkewY, lMatrixRotate);
1923   end
1924   else
1925   begin
1926     lMatrixTranslateX := 0;
1927     lMatrixTranslateY := 0;
1928     lMatrixScaleX := 1;
1929     lMatrixScaleY := 1;
1930     lMatrixSkewX := 0;
1931     lMatrixSkewY := 0;
1932     lMatrixRotate := 0;
1933   end;
1934 
1935   // Image data read from the CurrentGraphicState
1936   lRasterImage.X := CurrentGraphicState.TranslateX;// + lMatrixTranslateX) * CurrentGraphicState.ScaleX * lMatrixScaleX;
1937   lRasterImage.Y := CurrentGraphicState.TranslateY;//- lMatrixTranslateY;// * CurrentGraphicState.ScaleY * lMatrixScaleY;
1938   lRasterImage.Width := lImageWidth * CurrentGraphicState.ScaleX * lMatrixScaleX;
1939   lRasterImage.Height := lImageHeight * CurrentGraphicState.ScaleY * lMatrixScaleY;
1940   // EPS X,Y position of the image is the lower-left corner, but FPVectorial uses top-left
1941   lRasterImage.Y := lRasterImage.Y + Abs(lRasterImage.Height);
1942   // Height again if the image was stretched with inversion
1943   //if lRasterImage.Height < 0 then
1944   //  lRasterImage.Y := lRasterImage.Y - lRasterImage.Height;
1945 
1946   AData.AddEntity(lRasterImage);
1947 
1948   Exit(True);
1949 end;
1950 
1951 { Device Setup and Output Operators
1952 
1953   – showpage – Transmit and reset current page
1954   – copypage – Transmit current page
1955   dict setpagedevice – Install page-oriented output device
1956   – currentpagedevice dict Return current page device parameters
1957   – nulldevice – Install no-output device
1958   Glyph and Font Operators
1959   key font|cidfont definefont font|cidfont Register font|cidfont in Font resource
1960   category
1961   key name|string|dict array composefont font Register composite font dictionary created
1962   from CMap and array of CIDFonts or fonts
1963   key undefinefont – Remove Font resource registration
1964   key findfont font|cidfont Return Font resource instance identified by
1965   key
1966   font|cidfont scale scalefont font¢|cidfont¢ Scale font|cidfont by scale to produce
1967   font¢|cidfont¢
1968   font|cidfont matrix makefont font¢|cidfont¢ Transform font|cidfont by matrix to produce
1969   font¢|cidfont¢
1970   font|cidfont setfont – Set font or CIDFont in graphics state
1971   – rootfont font|cidfont Return last set font or CIDFont
1972   – currentfont font|cidfont Return current font or CIDFont, possibly a
1973   descendant of rootfont
1974   key scale|matrix selectfont – Set font or CIDFont given name and
1975   transform
1976   string show – Paint glyphs for string in current font
1977   ax ay string ashow – Add (ax , ay) to width of each glyph while
1978   showing string
1979   cx cy char string widthshow – Add (cx , cy) to width of glyph for char while
1980   showing string
1981   cx cy char ax ay string awidthshow – Combine effects of ashow and widthshow
1982   string numarray|numstring xshow – Paint glyphs for string using x widths in
1983   numarray|numstring
1984   string numarray|numstring xyshow – Paint glyphs for string using x and y widths
1985   in numarray|numstring
1986   string numarray|numstring yshow – Paint glyphs for string using y widths in
1987   numarray|numstring
1988   name|cid glyphshow – Paint glyph for character identified by
1989   name|cid
1990   string stringwidth wx wy Return width of glyphs for string in current
1991   font
1992   proc string cshow – Invoke character mapping algorithm and
1993   call proc
1994   proc string kshow – Execute proc between characters shown from
1995   string
1996   – FontDirectory dict Return dictionary of Font resource instances
1997   – GlobalFontDirectory dict Return dictionary of Font resource instances
1998   in global VM
1999   – StandardEncoding array Return Adobe standard font encoding vector
2000   – ISOLatin1Encoding array Return ISO Latin-1 font encoding vector
2001   key findencoding array Find encoding vector
2002   wx wy llx lly urx ury setcachedevice – Declare cached glyph metrics
2003   w0x w0y llx lly urx ury
2004   w1x w1y vx vy setcachedevice2 – Declare cached glyph metrics
2005   wx wy setcharwidth – Declare uncached glyph metrics
2006   Interpreter Parameter Operators
2007   dict setsystemparams – Set systemwide interpreter parameters
2008   – currentsystemparams dict Return systemwide interpreter parameters
2009   dict setuserparams – Set per-context interpreter parameters
2010   – currentuserparams dict Return per-context interpreter parameters
2011   string dict setdevparams – Set parameters for input/output device
2012   string currentdevparams dict Return device parameters
2013   int vmreclaim – Control garbage collector
2014   int setvmthreshold – Control garbage collector
2015   – vmstatus level used maximum
2016   Report VM status
2017   – cachestatus bsize bmax msize mmax csize cmax blimit
2018   Return font cache status and parameters
2019   int setcachelimit – Set maximum bytes in cached glyph
2020   mark size lower upper setcacheparams – Set font cache parameters
2021   – currentcacheparams mark size lower upper
2022   Return current font cache parameters
2023   mark blimit setucacheparams – Set user path cache parameters
2024   – ucachestatus mark bsize bmax rsize rmax blimit
2025   Return user path cache status and
2026   parameters
2027 }
ExecuteDeviceSetupAndOutputOperatornull2028 function TvEPSVectorialReader.ExecuteDeviceSetupAndOutputOperator(
2029   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2030 var
2031   Param1, Param2: TPSToken;
2032 begin
2033   Result := False;
2034 
2035   if AToken.StrValue = 'showpage' then
2036   begin
2037     Exit(True);
2038   end;
2039 end;
2040 
2041 { Array Operators
2042 
2043   int array array Create array of length int
2044   – [ mark Start array construction
2045   mark obj0 … objn-1 ] array End array construction
2046   array length int Return number of elements in array
2047   array index get any Return array element indexed by index
2048   array index any put – Put any into array at index
2049   array index count getinterval subarray Return subarray of array starting at index for
2050   count elements
2051   array1 index array2|packedarray2 putinterval – Replace subarray of array1 starting at index
2052   by array2|packedarray2
2053   any0 … anyn-1 array astore array Pop elements from stack into array
2054   array aload any0 … anyn-1 array Push all elements of array on stack
2055   array1 array2 copy subarray2 Copy elements of array1 to initial subarray of
2056   array2
2057   array proc forall – Execute proc for each element of array
2058   Packed Array Operators
2059   any0 … anyn-1 n packedarray packedarray Create packed array consisting of n elements
2060   from stack
2061   bool setpacking – Set array packing mode for { … } syntax
2062   (true = packed array)
2063 currentpacking bool Return array packing mode
2064   packedarray length int Return number of elements in packedarray
2065   packedarray index get any Return packedarray element indexed by index
2066   packedarray index count getinterval subarray Return subarray of packedarray starting at
2067   index for count elements
2068   packedarray aload any0anyn-1 packedarray
2069   Push all elements of packedarray on stack
2070   packedarray1 array2 copy subarray2 Copy elements of packedarray1 to initial
2071   subarray of array2
2072   packedarray proc forallExecute proc for each element of packedarray
2073 }
ExecuteArrayOperatornull2074 function TvEPSVectorialReader.ExecuteArrayOperator(AToken: TExpressionToken;
2075   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2076 begin
2077   Result := False;
2078 
2079 end;
2080 
2081 { String Operators
2082 
2083   int string string Create string of length int
2084   string length int Return number of elements in string
2085   string index get int Return string element indexed by index
2086   string index int put – Put int into string at index
2087   string index count getinterval substring Return substring of string starting at index
2088   for count elements
2089   string1 index string2 putinterval – Replace substring of string1 starting at index
2090   by string2
2091   string1 string2 copy substring2 Copy elements of string1 to initial substring
2092   of string2
2093   string proc forall – Execute proc for each element of string
2094   string seek anchorsearch post match true Search for seek at start of string
2095   or string false
2096   string seek search post match pre true Search for seek in string
2097   or string false
2098   string token post any true Read token from start of string
2099   or false
2100   Relational, Boolean, and Bitwise Operators
2101   any1 any2 eq bool Test equal
2102   any1 any2 ne bool Test not equal
2103   num1|str1 num2|str2 ge bool Test greater than or equal
2104   num1|str1 num2|str2 gt bool Test greater than
2105   num1|str1 num2|str2 le bool Test less than or equal
2106   num1|str1 num2|str2 lt bool Test less than
2107   bool1|int1 bool2|int2 and bool3|int3 Perform logical|bitwise and
2108   bool1|int1 not bool2|int2 Perform logical|bitwise not
2109   bool1|int1 bool2|int2 or bool3|int3 Perform logical|bitwise inclusive or
2110   bool1|int1 bool2|int2 xor bool3|int3 Perform logical|bitwise exclusive or
2111   – true true Return boolean value true
2112   – false false Return boolean value false
2113   int1 shift bitshift int2 Perform bitwise shift of int1 (positive is left)
2114 }
TvEPSVectorialReader.ExecuteStringOperatornull2115 function TvEPSVectorialReader.ExecuteStringOperator(AToken: TExpressionToken;
2116   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2117 var
2118   Param1, Param2: TPSToken;
2119   NewToken: TExpressionToken;
2120 begin
2121   Result := False;
2122 
2123   // any1 any2 ne bool Test not equal
2124   if AToken.StrValue = 'ne' then
2125   begin
2126     Param1 := TPSToken(Stack.Pop);
2127     Param2 := TPSToken(Stack.Pop);
2128 
2129     NewToken := TExpressionToken.Create;
2130     NewToken.ETType := ettOperand;
2131     NewToken.BoolValue := Param1.StrValue = Param2.StrValue;
2132     if NewToken.BoolValue then NewToken.StrValue := 'true'
2133     else NewToken.StrValue := 'false';
2134     Stack.Push(NewToken);
2135 
2136     Exit(True);
2137   end;
2138   // num1 num2 lt bool
2139   // string1 string2 lt bool
2140   // pops two objects from the operand stack and pushes true if the first operand is less
2141   // than the second, or false otherwise. If both operands are numbers, lt compares
2142   // their mathematical values. If both operands are strings, lt compares them element
2143   // by element, treating the elements as integers in the range 0 to 255, to determine
2144   // whether the first string is lexically less than the second. If the operands are of
2145   // other types or one is a string and the other is a number, a typecheck error occurs.
2146   if AToken.StrValue = 'lt' then
2147   begin
2148     Param1 := TPSToken(Stack.Pop);
2149     Param2 := TPSToken(Stack.Pop);
2150 
2151     NewToken := TExpressionToken.Create;
2152     NewToken.ETType := ettOperand;
2153     NewToken.BoolValue := Param1.FloatValue > Param2.FloatValue;
2154     if NewToken.BoolValue then NewToken.StrValue := 'true'
2155     else NewToken.StrValue := 'false';
2156     Stack.Push(NewToken);
2157 
2158     Exit(True);
2159   end;
2160 end;
2161 
2162 {  File Operators
2163 
2164   filename access file file Open named file with specified access
2165   datasrc|datatgt dict
2166   param1 … paramn filtername filter file Establish filtered file
2167   file closefile – Close file
2168   file read int true Read one character from file
2169   or false
2170   file int write – Write one character to file
2171   file string readhexstring substring bool Read hexadecimal numbers from file into
2172   string
2173   file string writehexstring – Write string to file as hexadecimal
2174   file string readstring substring bool Read string from file
2175   file string writestring – Write string to file
2176   file string readline substring bool Read line from file into string
2177   file token any true Read token from file
2178   or false
2179   file bytesavailable int Return number of bytes available to read
2180   – flush – Send buffered data to standard output file
2181   file flushfile – Send buffered data or read to EOF
2182   file resetfile – Discard buffered characters
2183   file status bool Return status of file (true = valid)
2184   filename status pages bytes referenced created true
2185   or false Return information about named file
2186   filename run – Execute contents of named file
2187   – currentfile file Return file currently being executed
2188   filename deletefile – Delete named file
2189   filename1 filename2 renamefile – Rename file filename1 to filename2
2190   template proc scratch filenameforall – Execute proc for each file name matching
2191   template
2192   file position setfileposition – Set file to specified position
2193   file fileposition position Return current position in file
2194   string print – Write string to standard output file
2195   any = – Write text representation of any to standard
2196   output file
2197   any == – Write syntactic representation of any to
2198   standard output file
2199   any1 … anyn stack any1 … anyn Print stack nondestructively using =
2200   any1 … anyn pstack any1 … anyn Print stack nondestructively using ==
2201   obj tag printobject – Write binary object to standard output file,
2202   using tag
2203   file obj tag writeobject – Write binary object to file, using tag
2204   int setobjectformat – Set binary object format (0 = disable,
2205   1 = IEEE high, 2 = IEEE low, 3 = native
2206   high, 4 = native low)
2207   – currentobjectformat int Return binary object format
2208 }
ExecuteFileOperatornull2209 function TvEPSVectorialReader.ExecuteFileOperator(AToken: TExpressionToken;
2210   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2211 begin
2212   Result := False;
2213 
2214 end;
2215 
2216 { Resource Operators
2217 
2218   key instance category defineresource instance Register named resource instance in category
2219   key category undefineresource – Remove resource registration
2220   key category findresource instance Return resource instance identified by key in
2221   category
2222   renderingintent findcolorrendering name bool Select CIE-based color rendering dictionary
2223   by rendering intent
2224   key category resourcestatus status size true Return status of resource instance
2225   or false
2226   template proc scratch category resourceforall – Enumerate resource instances in category
2227 }
TvEPSVectorialReader.ExecuteResourceOperatornull2228 function TvEPSVectorialReader.ExecuteResourceOperator(AToken: TExpressionToken;
2229   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2230 begin
2231   Result := False;
2232 
2233 end;
2234 
2235 { Virtual Memory Operators
2236 
2237   – save save Create VM snapshot
2238   save restore – Restore VM snapshot
2239   bool setglobal – Set VM allocation mode (false = local,
2240   true = global)
2241   – currentglobal bool Return current VM allocation mode
2242   any gcheck bool Return true if any is simple or in global VM,
2243   false if in local VM
2244   bool1 password startjob bool2 Start new job that will alter initial VM if
2245   bool1 is true
2246   index any defineuserobject – Define user object associated with index
2247   index execuserobject – Execute user object associated with index
2248   index undefineuserobject – Remove user object associated with index
2249   – UserObjects array Return current UserObjects array defined in
2250   userdict
2251 }
TvEPSVectorialReader.ExecuteVirtualMemoryOperatornull2252 function TvEPSVectorialReader.ExecuteVirtualMemoryOperator(
2253   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument
2254   ): Boolean;
2255 var
2256   Param1, Param2: TPSToken;
2257   NewToken: TExpressionToken;
2258 begin
2259   Result := False;
2260 
2261   //– save save Create save snapshot
2262   if AToken.StrValue = 'save' then
2263   begin
2264     NewToken := TExpressionToken.Create;
2265     NewToken.ETType := ettVirtualMemorySnapshot;
2266     Stack.Push(NewToken);
2267     Exit(True);
2268   end;
2269   //save restore – Restore VM snapshot
2270   if AToken.StrValue = 'restore' then
2271   begin
2272     Param1 := TPSToken(Stack.Pop);
2273     Param1.Free;
2274     Exit(True);
2275   end;
2276 end;
2277 
2278 { Errors
2279 
2280   configurationerror setpagedevice or setdevparams request
2281   cannot be satisfied
2282   dictfull No more room in dictionary
2283   dictstackoverflow Too many begin operators
2284   dictstackunderflow Too many end operators
2285   execstackoverflow Executive stack nesting too deep
2286   handleerror Called to report error information
2287   interrupt External interrupt request (for example,
2288   Control-C)
2289   invalidaccess Attempt to violate access attribute
2290   invalidexit exit not in loop
2291   invalidfileaccess Unacceptable access string
2292   invalidfont Invalid Font resource name or font or
2293   CIDFont dictionary
2294   invalidrestore Improper restore
2295   ioerror Input/output error
2296   limitcheck Implementation limit exceeded
2297   nocurrentpoint Current point undefined
2298   rangecheck Operand out of bounds
2299   stackoverflow Operand stack overflow
2300   stackunderflow Operand stack underflow
2301   syntaxerror PostScript language syntax error
2302   timeout Time limit exceeded
2303   typecheck Operand of wrong type
2304   undefined Name not known
2305   undefinedfilename File not found
2306   undefinedresource Resource instance not found
2307   undefinedresult Overflow, underflow, or meaningless result
2308   unmatchedmark Expected mark not on stack
2309   unregistered Internal error
2310   VMerror Virtual memory exhausted
2311 }
TvEPSVectorialReader.ExecuteErrorOperatornull2312 function TvEPSVectorialReader.ExecuteErrorOperator(AToken: TExpressionToken;
2313   AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2314 begin
2315   Result := False;
2316 
2317 end;
2318 
2319 {  Arithmetic and Math Operators
2320 
2321   num1 num2 add sum        Return num1 plus num2
2322   num1 num2 div quotient   Return num1 divided by num2
2323   int1 int2 idiv quotient  Return int1 divided by int2
2324   int1 int2 mod remainder  Return remainder after dividing int1 by int2
2325   num1 num2 mul product    Return num1 times num2
2326   num1 num2 sub difference Return num1 minus num2
2327   num1 abs num2            Return absolute value of num1
2328   num1 neg num2            Return negative of num1
2329   num1 ceiling num2        Return ceiling of num1
2330   num1 floor num2          Return floor of num1
2331   num1 round num2          Round num1 to nearest integer
2332   num1 truncate num2       Remove fractional part of num1
2333   num sqrt real            Return square root of num
2334   num den atan angle       Return arctangent of num/den in degrees
2335   angle cos real           Return cosine of angle degrees
2336   angle sin real           Return sine of angle degrees
2337   base exponent exp real   Raise base to exponent power
2338   num ln real              Return natural logarithm (base e)
2339   num log real             Return common logarithm (base 10)
2340   – rand int               Generate pseudo-random integer
2341   int srand –              Set random number seed
2342   – rrand int              Return random number seed
2343 }
TvEPSVectorialReader.ExecuteArithmeticAndMathOperatornull2344 function TvEPSVectorialReader.ExecuteArithmeticAndMathOperator(
2345   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2346 var
2347   Param1, Param2: TPSToken;
2348   NewToken: TExpressionToken;
2349 begin
2350   Result := False;
2351 
2352   // Division
2353   // Param2 Param1 div ==> (Param2 div Param1)
2354   if AToken.StrValue = 'div' then
2355   begin
2356     Param1 := TPSToken(Stack.Pop);
2357     Param2 := TPSToken(Stack.Pop);
2358     NewToken := TExpressionToken.Create;
2359     NewToken.ETType := ettOperand;
2360     NewToken.FloatValue := Param2.FloatValue / Param1.FloatValue;
2361     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
2362     Stack.Push(NewToken);
2363     {$ifdef FPVECTORIALDEBUG_ARITHMETIC}
2364     WriteLn(Format('[TvEPSVectorialReader.ExecuteArithmeticAndMathOperator] %f %f div %f', [Param2.FloatValue, Param1.FloatValue, NewToken.FloatValue]));
2365     {$endif}
2366     Exit(True);
2367   end;
2368 
2369   // Param2 Param1 mul ==> (Param2 mul Param1)
2370   if AToken.StrValue = 'mul' then
2371   begin
2372     Param1 := TPSToken(Stack.Pop);
2373     Param2 := TPSToken(Stack.Pop);
2374     NewToken := TExpressionToken.Create;
2375     NewToken.ETType := ettOperand;
2376     NewToken.FloatValue := Param2.FloatValue * Param1.FloatValue;
2377     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
2378     Stack.Push(NewToken);
2379     Exit(True);
2380   end;
2381   // num1 num2 sub difference Return num1 minus num2
2382   if AToken.StrValue = 'sub' then
2383   begin
2384     NewToken := TExpressionToken.Create;
2385     NewToken.ETType := ettOperand;
2386     Param1 := TPSToken(Stack.Pop); // num2
2387     Param2 := TPSToken(Stack.Pop); // num1
2388     NewToken.FloatValue := Param2.FloatValue - Param1.FloatValue;
2389     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
2390     Stack.Push(NewToken);
2391     Exit(True);
2392   end;
2393   //num1 abs num2            Return absolute value of num1
2394   if AToken.StrValue = 'abs' then
2395   begin
2396     NewToken := TExpressionToken.Create;
2397     NewToken.ETType := ettOperand;
2398     Param1 := TPSToken(Stack.Pop); // num1
2399     NewToken.FloatValue := Abs(Param1.FloatValue);
2400     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
2401     Stack.Push(NewToken);
2402     Param1.Free;
2403     Exit(True);
2404   end;
2405   //num1 neg num2            Return negative of num1
2406   if AToken.StrValue = 'neg' then
2407   begin
2408     NewToken := TExpressionToken.Create;
2409     NewToken.ETType := ettOperand;
2410     Param1 := TPSToken(Stack.Pop); // num1
2411     NewToken.FloatValue := -1 * Param1.FloatValue;
2412     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
2413     Stack.Push(NewToken);
2414     Param1.Free;
2415     Exit(True);
2416   end;
2417   //num1 ceiling num2        Return ceiling of num1
2418   if AToken.StrValue = 'ceiling' then
2419   begin
2420     NewToken := TExpressionToken.Create;
2421     NewToken.ETType := ettOperand;
2422     Param1 := TPSToken(Stack.Pop); // num1
2423     NewToken.FloatValue := Ceil(Param1.FloatValue);
2424     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
2425     Stack.Push(NewToken);
2426     Param1.Free;
2427     Exit(True);
2428   end;
2429   //num1 floor num2          Return floor of num1
2430   if AToken.StrValue = 'floor' then
2431   begin
2432     NewToken := TExpressionToken.Create;
2433     NewToken.ETType := ettOperand;
2434     Param1 := TPSToken(Stack.Pop); // num1
2435     NewToken.FloatValue := Trunc(Param1.FloatValue);
2436     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
2437     Stack.Push(NewToken);
2438     Param1.Free;
2439     Exit(True);
2440   end;
2441   //num1 round num2          Round num1 to nearest integer
2442   if AToken.StrValue = 'round' then
2443   begin
2444     NewToken := TExpressionToken.Create;
2445     NewToken.ETType := ettOperand;
2446     Param1 := TPSToken(Stack.Pop); // num1
2447     NewToken.FloatValue := Round(Param1.FloatValue);
2448     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
2449     Stack.Push(NewToken);
2450     Param1.Free;
2451     Exit(True);
2452   end;
2453   //num1 truncate num2       Remove fractional part of num1
2454   if AToken.StrValue = 'truncate' then
2455   begin
2456     NewToken := TExpressionToken.Create;
2457     NewToken.ETType := ettOperand;
2458     Param1 := TPSToken(Stack.Pop); // num1
2459     NewToken.FloatValue := Trunc(Param1.FloatValue);
2460     NewToken.StrValue := FloatToStr(NewToken.FloatValue);
2461     Stack.Push(NewToken);
2462     Param1.Free;
2463     Exit(True);
2464   end;
2465 end;
2466 
2467 { Path Construction Operators
2468 
2469   – newpath –              Initialize current path to be empty
2470   – currentpoint x y       Return current point coordinates
2471   x y moveto –             Set current point to (x, y)
2472   dx dy rmoveto –          Perform relative moveto
2473   x y lineto –             Append straight line to (x, y)
2474   dx dy rlineto –          Perform relative lineto
2475   x y r angle1 angle2 arc – Append counterclockwise arc
2476   x y r angle1 angle2 arcn – Append clockwise arc
2477   x1 y1 x2 y2 r arct –     Append tangent arc
2478   x1 y1 x2 y2 r arcto xt1 yt1 xt2 yt2 Append tangent arc
2479   x1 y1 x2 y2 x3 y3 curveto – Append Bézier cubic section
2480   dx1 dy1 dx2 dy2 dx3 dy3 rcurveto – Perform relative curveto
2481   – closepath –            Connect subpath back to its starting point
2482   – flattenpath –          Convert curves to sequences of straight lines
2483   – reversepath –          Reverse direction of current path
2484   – strokepath –           Compute outline of stroked path
2485   userpath ustrokepath – Compute outline of stroked userpath
2486   userpath matrix ustrokepath – Compute outline of stroked userpath
2487   string bool charpath – Append glyph outline to current path
2488   userpath uappend – Interpret userpath and append to current
2489   path
2490   – clippath – Set current path to clipping path
2491   llx lly urx ury setbbox – Set bounding box for current path
2492   – pathbbox llx lly urx ury Return bounding box of current path
2493   move line curve close pathforall – Enumerate current path
2494   bool upath userpath Create userpath for current path; include
2495   ucache if bool is true
2496   – initclip – Set clipping path to device default
2497   – clip – Clip using nonzero winding number rule
2498   – eoclip – Clip using even-odd rule
2499   x y width height rectclip – Clip with rectangular path
2500   numarray|numstring rectclip – Clip with rectangular paths
2501   – ucache – Declare that user path is to be cached
2502 }
TvEPSVectorialReader.ExecutePathConstructionOperatornull2503 function TvEPSVectorialReader.ExecutePathConstructionOperator(
2504   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2505 var
2506   Param1, Param2, Param3, Param4, Param5, Param6: TPSToken;
2507   PosX, PosY, PosX2, PosY2, PosX3, PosY3, BaseX, BaseY: Double;
2508   // For Arc
2509   P1, P2, P3, P4: T3DPoint;
2510   startAngle, endAngle: Double;
2511 begin
2512   Result := False;
2513 
2514   // – newpath –              Initialize current path to be empty
2515   if AToken.StrValue = 'newpath' then
2516   begin
2517     {$ifdef FPVECTORIALDEBUG_PATHS}
2518     WriteLn('[TvEPSVectorialReader.ExecutePathConstructionOperator] newpath');
2519     {$endif}
2520 //    AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
2521 //    AData.SetPenWidth(CurrentGraphicState.PenWidth);
2522 //    AData.SetClipPath(CurrentGraphicState.ClipPath, CurrentGraphicState.ClipMode);
2523     AData.SetBrushStyle(bsClear);
2524     AData.SetPenStyle(psClear);
2525     AData.EndPath();
2526     AData.StartPath();
2527 
2528     AData.SetPenColor(CurrentGraphicState.Color);
2529     AData.SetBrushColor(CurrentGraphicState.Color);
2530     AData.SetPenStyle(psClear);
2531 
2532     Exit(True);
2533   end;
2534   // Param2 Param1 moveto - ===> moveto(X=Param2, Y=Param1);
2535   if AToken.StrValue = 'moveto' then
2536   begin
2537     Param1 := TPSToken(Stack.Pop);
2538     Param2 := TPSToken(Stack.Pop);
2539     PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX, PosY);
2540     PostScriptCoordsToFPVectorialCoordsWithCGS(Param1, Param2, PosX2, PosY2);
2541     {$ifdef FPVECTORIALDEBUG_PATHS}
2542     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] moveto %f, %f CurrentGraphicState.Translate %f, %f Resulting Value %f, %f',
2543       [PosX, PosY, CurrentGraphicState.TranslateX, CurrentGraphicState.TranslateY, PosX2, PosY2]));
2544     {$endif}
2545     AData.AddMoveToPath(PosX2, PosY2);
2546     Exit(True);
2547   end;
2548   // Absolute LineTo
2549   // x y lineto –             Append straight line to (x, y)
2550   if AToken.StrValue = 'lineto' then
2551   begin
2552     Param1 := TPSToken(Stack.Pop);
2553     Param2 := TPSToken(Stack.Pop);
2554     PostScriptCoordsToFPVectorialCoordsWithCGS(Param1, Param2, PosX2, PosY2);
2555     {$ifdef FPVECTORIALDEBUG_PATHS}
2556     PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX, PosY);
2557     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] lineto %f, %f Resulting value %f, %f', [PosX, PosY, PosX2, PosY2]));
2558     {$endif}
2559     AData.AddLineToPath(PosX2, PosY2);
2560     Exit(True);
2561   end;
2562   // Relative LineTo
2563   // dx dy rlineto –          Perform relative lineto
2564   if AToken.StrValue = 'rlineto' then
2565   begin
2566     Param1 := TPSToken(Stack.Pop);
2567     Param2 := TPSToken(Stack.Pop);
2568     PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX, PosY);
2569     AData.GetCurrentPathPenPos(BaseX, BaseY);
2570     PosX2 := PosX + BaseX;
2571     PosY2 := PosY + BaseY;
2572     {$ifdef FPVECTORIALDEBUG_PATHS}
2573     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] rlineto %f, %f Base %f, %f Resulting %f, %f',
2574       [PosX, PosY, BaseX, BaseY, PosX2, PosY2]));
2575     {$endif}
2576     AData.AddLineToPath(PosX2, PosY2);
2577     Exit(True);
2578   end;
2579   // x1 y1 x2 y2 x3 y3 curveto – Append Bézier cubic section
2580   if AToken.StrValue = 'curveto' then
2581   begin
2582     Param1 := TPSToken(Stack.Pop); // y3
2583     Param2 := TPSToken(Stack.Pop); // x3
2584     Param3 := TPSToken(Stack.Pop); // y2
2585     Param4 := TPSToken(Stack.Pop); // x2
2586     Param5 := TPSToken(Stack.Pop); // y1
2587     Param6 := TPSToken(Stack.Pop); // x1
2588     PostScriptCoordsToFPVectorialCoordsWithCGS(Param5, Param6, PosX, PosY);
2589     PostScriptCoordsToFPVectorialCoordsWithCGS(Param3, Param4, PosX2, PosY2);
2590     PostScriptCoordsToFPVectorialCoordsWithCGS(Param1, Param2, PosX3, PosY3);
2591     AData.AddBezierToPath(PosX, PosY, PosX2, PosY2, PosX3, PosY3);
2592     Exit(True);
2593   end;
2594   // dx1 dy1 dx2 dy2 dx3 dy3 rcurveto –
2595   // (relative curveto) appends a section of a cubic Bézier curve to the current path in
2596   // the same manner as curveto. However, the operands are interpreted as relative
2597   // displacements from the current point rather than as absolute coordinates. That is,
2598   // rcurveto constructs a curve between the current point (x0, y0) and the endpoint
2599   // (x0 + dx3, y0 + dy3), using (x0 + dx1, y0 + dy1) and (x0 + dx2, y0 + dy2) as the Bézier
2600   // control points. In all other respects, the behavior of rcurveto is identical to that of
2601   // curveto.
2602   if AToken.StrValue = 'rcurveto' then
2603   begin
2604     Param1 := TPSToken(Stack.Pop); // dy3
2605     Param2 := TPSToken(Stack.Pop); // dx3
2606     Param3 := TPSToken(Stack.Pop); // dy2
2607     Param4 := TPSToken(Stack.Pop); // dx2
2608     Param5 := TPSToken(Stack.Pop); // dy1
2609     Param6 := TPSToken(Stack.Pop); // dx1
2610     PostScriptCoordsToFPVectorialCoords(Param5, Param6, PosX, PosY);
2611     PostScriptCoordsToFPVectorialCoords(Param3, Param4, PosX2, PosY2);
2612     PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX3, PosY3);
2613     AData.GetCurrentPathPenPos(BaseX, BaseY);
2614     // First move to the start of the arc
2615 //    BaseX := BaseX + CurrentGraphicState.TranslateX;
2616 //    BaseY := BaseY + CurrentGraphicState.TranslateY;
2617     {$ifdef FPVECTORIALDEBUG_PATHS}
2618     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] rcurveto translate %f, %f',
2619       [CurrentGraphicState.TranslateX, CurrentGraphicState.TranslateY]));
2620     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] rcurveto from %f, %f via %f, %f %f, %f to %f, %f',
2621       [BaseX, BaseY, BaseX + PosX, BaseY + PosY, BaseX + PosX2, BaseY + PosY2, BaseX + PosX3, BaseY + PosY3]));
2622     {$endif}
2623     AData.AddBezierToPath(BaseX + PosX, BaseY + PosY, BaseX + PosX2, BaseY + PosY2, BaseX + PosX3, BaseY + PosY3);
2624     Exit(True);
2625   end;
2626   // – closepath –
2627   //
2628   // Don't do anything, because a stroke or fill might come after closepath
2629   // and newpath will be called after stroke and fill anyway
2630   //
2631   if AToken.StrValue = 'closepath' then
2632   begin
2633     {$ifdef FPVECTORIALDEBUG_PATHS}
2634     WriteLn('[TvEPSVectorialReader.ExecutePathConstructionOperator] closepath');
2635     {$endif}
2636 
2637     Exit(True);
2638   end;
2639   {
2640     x y r angle1 angle2 arc – Append counterclockwise arc
2641 
2642     Arcs in PostScript are described by a center (x, y), a radius r and
2643     two angles, angle1 for the start and angle2 for the end. These two
2644     angles are relative to the X axis growing to the right (positive direction).
2645 
2646   }
2647   if AToken.StrValue = 'arc' then
2648   begin
2649     Param1 := TPSToken(Stack.Pop); // angle2
2650     Param2 := TPSToken(Stack.Pop); // angle1
2651     Param3 := TPSToken(Stack.Pop); // r
2652     Param4 := TPSToken(Stack.Pop); // y
2653     Param5 := TPSToken(Stack.Pop); // x
2654     PostScriptCoordsToFPVectorialCoords(Param4, Param5, PosX, PosY);
2655     PosX := PosX + CurrentGraphicState.TranslateX;
2656     PosY := PosY + CurrentGraphicState.TranslateY;
2657     startAngle := Param2.FloatValue * Pi / 180;
2658     endAngle := Param1.FloatValue * Pi / 180;
2659 
2660     // If the angle is too big we need to use two beziers
2661     if endAngle - startAngle > Pi then
2662     begin
2663       CircularArcToBezier(PosX, PosY, Param3.FloatValue, startAngle, endAngle - Pi, P1, P2, P3, P4);
2664       AData.AddMoveToPath(P1.X, P1.Y);
2665       AData.AddBezierToPath(P2.X, P2.Y, P3.X, P3.Y, P4.X, P4.Y);
2666 
2667       CircularArcToBezier(PosX, PosY, Param3.FloatValue, startAngle + Pi, endAngle, P1, P2, P3, P4);
2668       AData.AddMoveToPath(P1.X, P1.Y);
2669       AData.AddBezierToPath(P2.X, P2.Y, P3.X, P3.Y, P4.X, P4.Y);
2670     end
2671     else
2672     begin
2673       CircularArcToBezier(PosX, PosY, Param3.FloatValue, startAngle, endAngle, P1, P2, P3, P4);
2674       AData.AddMoveToPath(P1.X, P1.Y);
2675       AData.AddBezierToPath(P2.X, P2.Y, P3.X, P3.Y, P4.X, P4.Y);
2676     end;
2677     {$ifdef FPVECTORIALDEBUG_PATHS}
2678     WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] arc X,Y=%f, %f Resulting X,Y=%f, %f R=%f Angles Start,End=%f,%f',
2679       [Param5.FloatValue, Param4.FloatValue, PosX, PosY, Param3.FloatValue, Param2.FloatValue, Param1.FloatValue]));
2680     {$endif}
2681     Exit(True);
2682   end;
2683   // – clip – Clip using nonzero winding number rule
2684   //
2685   // See the description on eoclip
2686   //
2687   if AToken.StrValue = 'clip' then
2688   begin
2689     {$ifdef FPVECTORIALDEBUG_PATHS}
2690     WriteLn('[TvEPSVectorialReader.ExecutePathConstructionOperator] clip');
2691     {$endif}
2692     {$ifndef FPVECTORIALDEBUG_CLIP_REGION}
2693     AData.SetPenStyle(psClear);
2694     {$endif}
2695     AData.SetBrushStyle(bsClear);
2696     AData.EndPath();
2697     CurrentGraphicState.ClipPath := AData.GetEntity(AData.GetEntitiesCount()-1) as TPath;
2698     CurrentGraphicState.ClipMode := vcmNonzeroWindingRule;
2699     Exit(True);
2700   end;
2701   // – eoclip – Clip using even-odd rule
2702   //
2703   // intersects the inside of the current clipping path with the inside
2704   // of the current path to produce a new, smaller current clipping path.
2705   // The inside of the current path is determined by the even-odd rule,
2706   // while the inside of the current clipping path is determined by whatever
2707   // rule was used at the time that path was created.
2708   //
2709   // Except for the choice of insideness rule, the behavior of eoclip is identical to that of clip.
2710   //
2711   // ERRORS: limitcheck
2712   //
2713   if AToken.StrValue = 'eoclip' then
2714   begin
2715     {$ifdef FPVECTORIALDEBUG_PATHS}
2716     WriteLn('[TvEPSVectorialReader.ExecutePathConstructionOperator] eoclip');
2717     {$endif}
2718     {$ifndef FPVECTORIALDEBUG_CLIP_REGION}
2719     AData.SetPenStyle(psClear);
2720     {$endif}
2721     AData.SetBrushStyle(bsClear);
2722     AData.EndPath();
2723     CurrentGraphicState.ClipPath := AData.GetEntity(AData.GetEntitiesCount()-1) as TPath;
2724     CurrentGraphicState.ClipMode := vcmEvenOddRule;
2725     Exit(True);
2726   end
2727 end;
2728 
2729 {  Graphics State Operators (Device-Independent)
2730 
2731   – gsave –                    Push graphics state
2732   – grestore –                 Pop graphics state
2733   – clipsave –                 Push clipping path
2734   – cliprestore –              Pop clipping path
2735   – grestoreall –              Pop to bottommost graphics state
2736   – initgraphics –             Reset graphics state parameters
2737   – gstate gstate              Create graphics state object
2738   gstate setgstate –           Set graphics state from gstate
2739   gstate currentgstate gstate  Copy current graphics state into gstate
2740   num setlinewidth –           Set line width
2741   – currentlinewidth num       Return current line width
2742   int setlinecap –             Set shape of line ends for stroke (0 = butt,
2743                                1 = round, 2 = square)
2744   – currentlinecap int         Return current line cap
2745   int setlinejoin –            Set shape of corners for stroke (0 = miter,
2746                                1 = round, 2 = bevel)
2747   – currentlinejoin int Return current line join
2748   num setmiterlimit – Set miter length limit
2749   – currentmiterlimit num Return current miter limit
2750   bool setstrokeadjust – Set stroke adjustment (false = disable,
2751   true = enable)
2752   – currentstrokeadjust bool Return current stroke adjustment
2753   array offset setdash – Set dash pattern for stroking
2754   – currentdash array offset Return current dash pattern
2755   array|name setcolorspace – Set color space
2756   – currentcolorspace array Return current color space
2757   comp1 … compn setcolor – Set color components
2758   pattern setcolor – Set colored tiling pattern as current color
2759   comp1 … compn pattern setcolor – Set uncolored tiling pattern as current color
2760   – currentcolor comp1 … compn Return current color components
2761   num setgray – Set color space to DeviceGray and color to
2762   specified gray value (0 = black, 1 = white)
2763   – currentgray num Return current color as gray value
2764   hue saturation brightness sethsbcolor – Set color space to DeviceRGB and color to
2765   specified hue, saturation, brightness
2766   – currenthsbcolor hue saturation brightness
2767   Return current color as hue, saturation,
2768   brightness
2769   red green blue setrgbcolor – Set color space to DeviceRGB and color to
2770                                specified red, green, blue
2771   – currentrgbcolor red green blue Return current color as red, green, blue
2772   cyan magenta yellow black setcmykcolor – Set color space to DeviceCMYK and color to
2773                                            specified cyan, magenta, yellow, black
2774   – currentcmykcolor cyan magenta yellow black
2775   Return current color as cyan, magenta,
2776   yellow, black
2777 }
ExecuteGraphicStateOperatorsDInull2778 function TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI(
2779   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2780 var
2781   Param1, Param2, Param3, Param4: TPSToken;
2782   lRed, lGreen, lBlue, lColorC, lColorM, lColorY, lColorK: Double;
2783   lGraphicState: TGraphicState;
2784 begin
2785   Result := False;
2786 
2787   // – gsave – Push graphics state
2788   if AToken.StrValue = 'gsave' then
2789   begin
2790     GraphicStateStack.Push(CurrentGraphicState.Duplicate());
2791     {$ifdef FPVECTORIALDEBUG_PATHS}
2792     WriteLn('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] gsave');
2793     {$endif}
2794     Exit(True);
2795   end;
2796   // – grestore -                 Pop graphics state
2797   if AToken.StrValue = 'grestore' then
2798   begin
2799     lGraphicState := TGraphicState(GraphicStateStack.Pop());
2800     if lGraphicState = nil then raise Exception.Create('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] grestore: call to grestore without corresponding gsave');
2801     CurrentGraphicState.Free;
2802     CurrentGraphicState := lGraphicState;
2803     {$ifdef FPVECTORIALDEBUG_PATHS}
2804     WriteLn('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] grestore');
2805     {$endif}
2806     Exit(True);
2807   end;
2808   // num setlinewidth –           Set line width
2809   if AToken.StrValue = 'setlinewidth' then
2810   begin
2811     Param1 := TPSToken(Stack.Pop);
2812     CurrentGraphicState.PenWidth := Round(Param1.FloatValue);
2813     Exit(True);
2814   end;
2815   // int setlinecap –             Set shape of line ends for stroke (0 = butt,
2816   //                             1 = round, 2 = square)
2817   if AToken.StrValue = 'setlinecap' then
2818   begin
2819     Param1 := TPSToken(Stack.Pop);
2820     Exit(True);
2821   end;
2822   // int setlinejoin –            Set shape of corners for stroke (0 = miter,
2823   //                             1 = round, 2 = bevel)
2824   if AToken.StrValue = 'setlinejoin' then
2825   begin
2826     Param1 := TPSToken(Stack.Pop);
2827     Exit(True);
2828   end;
2829   // num setmiterlimit – Set miter length limit
2830   if AToken.StrValue = 'setmiterlimit' then
2831   begin
2832     Param1 := TPSToken(Stack.Pop);
2833     Exit(True);
2834   end;
2835   // array offset setdash – Set dash pattern for stroking
2836   if AToken.StrValue = 'setdash' then
2837   begin
2838     Param1 := TPSToken(Stack.Pop);
2839     Param2 := TPSToken(Stack.Pop);
2840     Exit(True);
2841   end;
2842   // num setgray – Set color space to DeviceGray and color to
2843   // specified gray value (0 = black, 1 = white)
2844   if AToken.StrValue = 'setgray' then
2845   begin
2846     Param1 := TPSToken(Stack.Pop);
2847 
2848     lRed := EnsureRange(Param1.FloatValue, 0, 1);
2849 
2850     CurrentGraphicState.Color.Red := Round(lRed * $FFFF);
2851     CurrentGraphicState.Color.Green := Round(lRed * $FFFF);
2852     CurrentGraphicState.Color.Blue := Round(lRed * $FFFF);
2853     CurrentGraphicState.Color.alpha := alphaOpaque;
2854 
2855     AData.SetPenColor(CurrentGraphicState.Color);
2856 
2857     Exit(True);
2858   end;
2859   // array|name setcolorspace – Set color space
2860   if AToken.StrValue = 'setcolorspace' then
2861   begin
2862     Param1 := TPSToken(Stack.Pop);
2863     CurrentGraphicState.ColorSpaceName := Param1.StrValue;
2864     Exit(True);
2865   end;
2866   // red green blue setrgbcolor –
2867   // sets the current color space in the graphics state to DeviceRGB and the current color
2868   // to the component values specified by red, green, and blue. Each component
2869   // must be a number in the range 0.0 to 1.0. If any of the operands is outside this
2870   // range, the nearest valid value is substituted without error indication.
2871   if AToken.StrValue = 'setrgbcolor' then
2872   begin
2873     Param1 := TPSToken(Stack.Pop);
2874     Param2 := TPSToken(Stack.Pop);
2875     Param3 := TPSToken(Stack.Pop);
2876 
2877     lRed := EnsureRange(Param3.FloatValue, 0, 1);
2878     lGreen := EnsureRange(Param2.FloatValue, 0, 1);
2879     lBlue := EnsureRange(Param1.FloatValue, 0, 1);
2880 
2881     CurrentGraphicState.Color.Red := Round(lRed * $FFFF);
2882     CurrentGraphicState.Color.Green := Round(lGreen * $FFFF);
2883     CurrentGraphicState.Color.Blue := Round(lBlue * $FFFF);
2884     CurrentGraphicState.Color.alpha := alphaOpaque;
2885 
2886     AData.SetPenColor(CurrentGraphicState.Color);
2887 
2888     {$ifdef FPVECTORIALDEBUG_COLORS}
2889     WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] setrgbcolor r=%f g=%f b=%f',
2890       [Param3.FloatValue, Param2.FloatValue, Param1.FloatValue]));
2891     {$endif}
2892 
2893     Exit(True);
2894   end;
2895   // cyan magenta yellow black setcmykcolor – Set color space to DeviceCMYK and color to
2896   //                                          specified cyan, magenta, yellow, black
2897   if AToken.StrValue = 'setcmykcolor' then
2898   begin
2899     Param1 := TPSToken(Stack.Pop);
2900     Param2 := TPSToken(Stack.Pop);
2901     Param3 := TPSToken(Stack.Pop);
2902     Param4 := TPSToken(Stack.Pop);
2903 
2904     lColorC := EnsureRange(Param4.FloatValue, 0, 1);
2905     lColorM := EnsureRange(Param3.FloatValue, 0, 1);
2906     lColorY := EnsureRange(Param2.FloatValue, 0, 1);
2907     lColorK := EnsureRange(Param1.FloatValue, 0, 1);
2908 
2909     CurrentGraphicState.Color.Red := Round($FF * (1-lColorC) * (1-lColorK) * $101);
2910     CurrentGraphicState.Color.Green := Round($FF * (1-lColorM) * (1-lColorK) * $101);
2911     CurrentGraphicState.Color.Blue := Round($FF * (1-lColorY) * (1-lColorK) * $101);
2912     CurrentGraphicState.Color.alpha := alphaOpaque;
2913 
2914     AData.SetPenColor(CurrentGraphicState.Color);
2915 
2916     {$ifdef FPVECTORIALDEBUG_COLORS}
2917     {WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] setrgbcolor r=%f g=%f b=%f',
2918       [Param3.FloatValue, Param2.FloatValue, Param1.FloatValue]));}
2919     {$endif}
2920 
2921     Exit(True);
2922   end;
2923 end;
2924 
2925 {  Graphics State Operators (Device-Dependent)
2926 
2927   halftone sethalftone – Set halftone dictionary
2928   – currenthalftone halftone
2929   Return current halftone dictionary
2930   frequency angle proc setscreen – Set gray halftone screen by frequency, angle,
2931   and spot function
2932   frequency angle halftone setscreen – Set gray halftone screen from halftone
2933   dictionary
2934   – currentscreen frequency angle proc|halftone
2935   Return current gray halftone screen
2936   redfreq redang redproc|redhalftone
2937   greenfreq greenang greenproc|greenhalftone
2938   bluefreq blueang blueproc|bluehalftone
2939   grayfreq grayang grayproc|grayhalftone setcolorscreen – Set all four halftone screens
2940   – currentcolorscreen redfreq redang redproc|redhalftone
2941   greenfreq greenang greenproc|greenhalftone
2942   bluefreq blueang blueproc|bluehalftone
2943   grayfreq grayang grayproc|grayhalftone
2944   Return all four halftone screens
2945   proc settransfer – Set gray transfer function
2946   – currenttransfer proc
2947   Return current gray transfer function
2948   redproc greenproc blueproc grayproc setcolortransfer – Set all four transfer functions
2949   – currentcolortransfer redproc greenproc blueproc grayproc
2950   Return current transfer functions
2951   proc setblackgeneration – Set black-generation function
2952   – currentblackgeneration proc
2953   Return current black-generation function
2954   proc setundercolorremoval – Set undercolor-removal function
2955   – currentundercolorremoval proc
2956   Return current undercolor-removal
2957   function
2958   dict setcolorrendering – Set CIE-based color rendering dictionary
2959   – currentcolorrendering dict
2960   Return current CIE-based color rendering
2961   dictionary
2962   num setflat – Set flatness tolerance
2963   – currentflat num Return current flatness
2964   bool setoverprint – Set overprint parameter
2965   – currentoverprint bool Return current overprint parameter
2966   num setsmoothness – Set smoothness parameter
2967   – currentsmoothness num Return current smoothness parameter
2968   Coordinate System and Matrix Operators
2969   – matrix matrix Create identity matrix
2970   – initmatrix – Set CTM to device default
2971   matrix identmatrix matrix Fill matrix with identity transform
2972   matrix defaultmatrix matrix Fill matrix with device default matrix
2973   matrix currentmatrix matrix Fill matrix with CTM
2974   matrix setmatrix –       Replace CTM by matrix
2975   tx ty translate –        Translate user space by (tx , ty)
2976   tx ty matrix translate matrix Define translation by (tx , ty)
2977   sx sy scale – Scale user space by sx and sy
2978   sx sy matrix scale matrix Define scaling by sx and sy
2979   angle rotate – Rotate user space by angle degrees
2980   angle matrix rotate matrix Define rotation by angle degrees
2981   matrix concat – Replace CTM by matrix ´ CTM
2982   matrix1 matrix2 matrix3 concatmatrix matrix3 Fill matrix3 with matrix1 ´ matrix2
2983   x y transform x¢ y¢ Transform (x, y) by CTM
2984   x y matrix transform x¢ y¢ Transform (x, y) by matrix
2985   dx dy dtransform dx¢ dy¢ Transform distance (dx, dy) by CTM
2986   dx dy matrix dtransform dx¢ dy¢ Transform distance (dx, dy) by matrix
2987   x¢ y¢ itransform x y Perform inverse transform of (x¢, y¢) by
2988   CTM
2989   x¢ y¢ matrix itransform x y Perform inverse transform of (x¢, y¢) by
2990   matrix
2991   dx¢ dy¢ idtransform dx dy Perform inverse transform of distance
2992   (dx¢, dy¢) by CTM
2993   dx¢ dy¢ matrix idtransform dx dy Perform inverse transform of distance
2994   (dx¢, dy¢) by matrix
2995   matrix1 matrix2 invertmatrix matrix2 Fill matrix2 with inverse of matrix1
2996 }
ExecuteGraphicStateOperatorsDDnull2997 function TvEPSVectorialReader.ExecuteGraphicStateOperatorsDD(
2998   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
2999 var
3000   Param1, Param2: TPSToken;
3001   ArrayToken: TArrayToken;
3002 begin
3003   Result := False;
3004 
3005   // bool setoverprint – Set overprint parameter
3006   if AToken.StrValue = 'setoverprint' then
3007   begin
3008     Param1 := TPSToken(Stack.Pop);
3009 
3010     CurrentGraphicState.OverPrint := Param1.BoolValue;
3011 
3012     Exit(True);
3013   end;
3014   //– matrix matrix Create identity matrix
3015   if AToken.StrValue = 'matrix' then
3016   begin
3017     ArrayToken := TArrayToken.Create;
3018     ArrayToken.AddIdentityMatrix();
3019 
3020     Stack.Push(ArrayToken);
3021 
3022     Exit(True);
3023   end;
3024   //– initmatrix – Set CTM to device default
3025 
3026   //matrix identmatrix matrix Fill matrix with identity transform
3027 
3028   //matrix defaultmatrix matrix Fill matrix with device default matrix
3029 
3030   //matrix currentmatrix matrix Fill matrix with CTM
3031   if AToken.StrValue = 'currentmatrix' then
3032   begin
3033     Param1 := TPSToken(Stack.Pop);
3034     Param1.Free;
3035 
3036     CurrentGraphicState.CTMNeeded();
3037     ArrayToken := TArrayToken(CurrentGraphicState.CTM.Duplicate());
3038 
3039     Stack.Push(ArrayToken);
3040 
3041     Exit(True);
3042   end;
3043   // matrix setmatrix –       Replace CTM by matrix
3044   if AToken.StrValue = 'setmatrix' then
3045   begin
3046     Param1 := TPSToken(Stack.Pop);
3047 
3048     CurrentGraphicState.SetCTM(TArrayToken(Param1));
3049 
3050     Exit(True);
3051   end;
3052   // sx sy scale – Scale user space by sx and sy
3053   if AToken.StrValue = 'scale' then
3054   begin
3055     Param1 := TPSToken(Stack.Pop);
3056     Param2 := TPSToken(Stack.Pop);
3057 
3058     if Param2 = nil then
3059     begin
3060       Exit(True);
3061     end;
3062 
3063     CurrentGraphicState.ScaleX := CurrentGraphicState.ScaleX * Param2.FloatValue;
3064     CurrentGraphicState.ScaleY := CurrentGraphicState.ScaleY * Param1.FloatValue;
3065     {$ifdef FPVECTORIALDEBUG_PATHS}
3066     WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] scale %f %f',
3067      [CurrentGraphicState.ScaleX, CurrentGraphicState.ScaleY]));
3068     {$endif}
3069 
3070     Exit(True);
3071   end;
3072   {
3073     translate tx ty translate
3074     - tx ty matrix translate matrix
3075 
3076     With no matrix operand, translate builds a temporary matrix and concatenates
3077     this matrix with the current transformation matrix (CTM). Precisely, translate
3078     replaces the CTM by T x CTM. The effect of this is to move the origin of the
3079     user coordinate system by tx units in the x direction and ty units in the y
3080     direction relative to the former user coordinate system. The sizes of the x
3081     and y units and the orientation of the axes are unchanged.
3082 
3083     If the matrix operand is supplied, translate replaces the value of matrix by
3084     T and pushes the modified matrix back on the operand stack.
3085     In this case, translate does not affect the CTM.
3086   }
3087   if AToken.StrValue = 'translate' then
3088   begin
3089     Param1 := TPSToken(Stack.Pop); // ty
3090     Param2 := TPSToken(Stack.Pop); // tx
3091 
3092     if Param2 = nil then
3093     begin
3094       raise Exception.Create('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] Stack underflow in operator "translate"');
3095     end;
3096 
3097     {$ifdef FPVECTORIALDEBUG_PATHS}
3098     WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] translate %f, %f CurrentGraphicState.Translate %f %f',
3099       [Param2.FloatValue, Param1.FloatValue, CurrentGraphicState.TranslateX, CurrentGraphicState.TranslateY]));
3100     {$endif}
3101 
3102     CurrentGraphicState.TranslateX := CurrentGraphicState.TranslateX + Param2.FloatValue;
3103     CurrentGraphicState.TranslateY := CurrentGraphicState.TranslateY + Param1.FloatValue;
3104 
3105     Exit(True);
3106   end;
3107   // angle rotate – Rotate user space by angle degrees
3108   if AToken.StrValue = 'rotate' then
3109   begin
3110     Param1 := TPSToken(Stack.Pop);
3111 
3112     {$ifdef FPVECTORIALDEBUG_PATHS}
3113     WriteLn(Format('[TvEPSVectorialReader.ExecuteGraphicStateOperatorsDI] rotate angle=%f', [Param1.FloatValue]));
3114     DebugStack();
3115     {$endif}
3116 
3117     Exit(True);
3118   end;
3119 end;
3120 
3121 {  Dictionary Operators
3122 
3123   int dict dict Create dictionary with capacity for int
3124   elements
3125   – << mark             Start dictionary construction
3126   mark key1 value1 … keyn valuen >> dict
3127                         End dictionary construction
3128   dict length int       Return number of entries in dict
3129   dict maxlength int    Return current capacity of dict
3130   dict begin –          Push dict on dictionary stack
3131   – end –               Pop current dictionary off dictionary stack
3132   key value def –       Associate key and value in current dictionary
3133   key load value        Search dictionary stack for key and return
3134                         associated value
3135   key value store –     Replace topmost definition of key
3136   dict key get any      Return value associated with key in dict
3137   dict key value put –  Associate key with value in dict
3138   dict key undef –      Remove key and its value from dict
3139   dict key known bool Test whether key is in dict
3140   key where dict true   Find dictionary in which key is defined
3141              or false
3142   dict1 dict2 copy dict2 Copy contents of dict1 to dict2
3143   dict proc forall – Execute proc for each entry in dict
3144   – currentdict dict Return current dictionary
3145   – errordict dict Return error handler dictionary
3146   – $error dict Return error control and status dictionary
3147   – systemdict dict Return system dictionary
3148   – userdict dict Return writeable dictionary in local VM
3149   – globaldict dict Return writeable dictionary in global VM
3150   – statusdict dict Return product-dependent dictionary
3151   – countdictstack int Count elements on dictionary stack
3152   array dictstack subarray Copy dictionary stack into array
3153   – cleardictstack – Pop all nonpermanent dictionaries off
3154   dictionary stack
3155 }
ExecuteDictionaryOperatorsnull3156 function TvEPSVectorialReader.ExecuteDictionaryOperators(
3157   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
3158 var
3159   Param1, Param2: TPSToken;
3160   NewToken: TExpressionToken;
3161 begin
3162   Result := False;
3163 
3164   // int dict dict Create dictionary with capacity for int
3165   // elements
3166   if AToken.StrValue = 'dict' then
3167   begin
3168     Param1 := TPSToken(Stack.Pop);
3169     NewToken := TExpressionToken.Create;
3170     NewToken.ETType := ettDictionary;
3171     Stack.Push(NewToken);
3172     Exit(True);
3173   end;
3174   // dict begin –          Push dict on dictionary stack
3175   if AToken.StrValue = 'begin' then
3176   begin
3177     Param1 := TPSToken(Stack.Pop);
3178     Exit(True);
3179   end;
3180   // – end –               Pop current dictionary off dictionary stack
3181   if AToken.StrValue = 'end' then
3182   begin
3183     Exit(True);
3184   end;
3185   // Adds a dictionary definition
3186   // key value def –       Associate key and value in current dictionary
3187   if AToken.StrValue = 'def' then
3188   begin
3189     Param1 := TPSToken(Stack.Pop);
3190     Param2 := TPSToken(Stack.Pop);
3191     Dictionary.AddObject(Param2.StrValue, Param1);
3192     Exit(True);
3193   end;
3194 
3195   // Can be ignored, because in the files found it only loads
3196   // standard routines, like /moveto ...
3197   //
3198   // key load value        Search dictionary stack for key and return
3199   //                      associated value
3200   if AToken.StrValue = 'load' then
3201   begin
3202 //    {$ifdef FPVECTORIALDEBUG_DICTIONARY}
3203 //    WriteLn('[TvEPSVectorialReader.ExecuteDictionaryOperators] load');
3204 //    DebugStack();
3205 //    {$endif}
3206 
3207     Exit(True);
3208   end;
3209 
3210   // Find dictionary in which key is defined
3211   //key where dict true   Find dictionary in which key is defined
3212   //           or false
3213   if AToken.StrValue = 'where' then
3214   begin
3215     {$ifdef FPVECTORIALDEBUG_DICTIONARY}
3216     WriteLn('[TvEPSVectorialReader.ExecuteDictionaryOperators] where');
3217     DebugStack();
3218     {$endif}
3219 
3220     Param1 := TPSToken(Stack.Pop);
3221 
3222     if Dictionary.IndexOf(Param1.StrValue) >= 0 then
3223     begin
3224       // We use only 1 dictionary, so this is just a representation of our single dictionary
3225       NewToken := TExpressionToken.Create;
3226       NewToken.ETType := ettDictionary;
3227       Stack.Push(NewToken);
3228 
3229       NewToken := TExpressionToken.Create;
3230       NewToken.ETType := ettOperand;
3231       NewToken.BoolValue := True;
3232       Stack.Push(NewToken);
3233 
3234       {$ifdef FPVECTORIALDEBUG_DICTIONARY}
3235       WriteLn('[TvEPSVectorialReader.ExecuteDictionaryOperators] where True');
3236       {$endif}
3237     end
3238     else
3239     begin
3240       NewToken := TExpressionToken.Create;
3241       NewToken.ETType := ettOperand;
3242       NewToken.BoolValue := False;
3243       Stack.Push(NewToken);
3244 
3245       {$ifdef FPVECTORIALDEBUG_DICTIONARY}
3246       WriteLn('[TvEPSVectorialReader.ExecuteDictionaryOperators] where False');
3247       {$endif}
3248     end;
3249 
3250     Exit(True);
3251   end;
3252   // - userdict dict
3253   // pushes the dictionary object userdict on the operand stack
3254   // (see Section 3.7.5, “Standard and User-Defined Dictionaries”).
3255   // userdict is not an operator; it is a name in systemdict associated with the dictionary object.
3256   if AToken.StrValue = 'userdict' then
3257   begin
3258     Param1 := TPSToken(Stack.Pop);
3259     NewToken := TExpressionToken.Create;
3260     NewToken.ETType := ettDictionary;
3261     Stack.Push(NewToken);
3262     Exit(True);
3263   end;
3264   // – globaldict dict Return writeable dictionary in global VM
3265   if AToken.StrValue = 'globaldict' then
3266   begin
3267     Param1 := TPSToken(Stack.Pop);
3268     NewToken := TExpressionToken.Create;
3269     NewToken.ETType := ettDictionary;
3270     Stack.Push(NewToken);
3271     Exit(True);
3272   end;
3273   // – countdictstack int Count elements on dictionary stack
3274   // countdictstack ==> int
3275   if AToken.StrValue = 'countdictstack' then
3276   begin
3277     NewToken := TExpressionToken.Create;
3278     NewToken.ETType := ettOperand;
3279     NewToken.FloatValue := Dictionary.Count;
3280     NewToken.StrValue := IntToStr(Dictionary.Count);
3281     Stack.Push(NewToken);
3282     Exit(True);
3283   end;
3284 end;
3285 
3286 {  Miscellaneous Operators
3287 
3288   proc bind proc Replace operator names in proc with
3289   operators; perform idiom recognition
3290   – null null Push null on stack
3291   – version string Return interpreter version
3292   – realtime int Return real time in milliseconds
3293   – usertime int Return execution time in milliseconds
3294   – languagelevel int Return LanguageLevel
3295   – product string Return product name
3296   – revision int Return product revision level
3297   – serialnumber int Return machine serial number
3298   – executive – Invoke interactive executive
3299   bool echo – Turn echoing on or off
3300   – prompt – Executed when ready for interactive input
3301 }
ExecuteMiscellaneousOperatorsnull3302 function TvEPSVectorialReader.ExecuteMiscellaneousOperators(
3303   AToken: TExpressionToken; AData: TvVectorialPage; ADoc: TvVectorialDocument): Boolean;
3304 begin
3305   Result := False;
3306 
3307   // Just a hint for more efficient parsing, we can ignore
3308   //
3309   // proc bind proc Replace operator names in proc with
3310   // operators; perform idiom recognition
3311   if AToken.StrValue = 'bind' then
3312   begin
3313     {$ifdef FPVECTORIALDEBUG_CONTROL}
3314     WriteLn('[TvEPSVectorialReader.ExecuteControlOperator] bind');
3315     DebugStack();
3316     {$endif}
3317 
3318     Exit(True);
3319   end;
3320 end;
3321 
3322 procedure TvEPSVectorialReader.PostScriptCoordsToFPVectorialCoords(AParam1,
3323   AParam2: TPSToken; var APosX, APosY: Double);
3324 begin
3325   APosX := AParam2.FloatValue;
3326   APosY := AParam1.FloatValue;
3327 end;
3328 
3329 procedure TvEPSVectorialReader.PostScriptCoordsToFPVectorialCoordsWithCGS(
3330   AParam1, AParam2: TPSToken; var APosX, APosY: Double);
3331 begin
3332   PostScriptCoordsToFPVectorialCoords(AParam1, AParam2, APosX, APosY);
3333   // Using CurrentGraphicState.ScaleX here breaks radat.eps
3334   APosX := APosX {* CurrentGraphicState.ScaleX} + CurrentGraphicState.TranslateX;
3335   APosY := APosY {* CurrentGraphicState.ScaleY} + CurrentGraphicState.TranslateY;
3336 end;
3337 
3338 // Returns true if a dictionary substitution was executed
DictionarySubstituteOperatornull3339 function TvEPSVectorialReader.DictionarySubstituteOperator(
3340   ADictionary: TStringList; var ACurToken: TPSToken): Boolean;
3341 var
3342   lIndex: Integer;
3343   SubstituteToken, NewToken: TPSToken;
3344   lOldStrValue: string; // for debugging purposes
3345 begin
3346   Result := False;
3347   lOldStrValue := ACurToken.StrValue;
3348   lIndex := ADictionary.IndexOf(ACurToken.StrValue);
3349   if lIndex >= 0 then
3350   begin
3351     Result := True;
3352 
3353     SubstituteToken := TPSToken(ADictionary.Objects[lIndex]);
3354 
3355     if SubstituteToken is TExpressionToken then
3356     begin
3357       ACurToken.CopyDataFrom(SubstituteToken, True);
3358     end
3359     else if (SubstituteToken is TProcedureToken) or
3360       (SubstituteToken is TArrayToken) then
3361     begin
3362       ACurToken := SubstituteToken;
3363     end;
3364 
3365     if (not (SubstituteToken is TArrayToken)) and
3366        (not ((SubstituteToken is TExpressionToken) and (TExpressionToken(SubstituteToken).ETType = ettDictionary))) and
3367        (not (SubstituteToken is TDictionaryToken)) and (ACurToken.StrValue = '') then
3368       raise Exception.Create(Format('[TvEPSVectorialReader.DictionarySubstituteOperator] '
3369        + 'The Dictionary substitution resulted in an empty value. SubstituteClass=%s Original StrValue=%s Line=%d',
3370        [SubstituteToken.ClassName, lOldStrValue, ACurToken.Line]));
3371   end;
3372 end;
3373 
3374 constructor TvEPSVectorialReader.Create;
3375 begin
3376   inherited Create;
3377 
3378   FPointSeparator := SysUtils.DefaultFormatSettings;
3379   FPointSeparator.DecimalSeparator := '.';
3380   FPointSeparator.ThousandSeparator := ',';
3381 
3382   Tokenizer := TPSTokenizer.Create(-1);
3383   Stack := TObjectStack.Create;
3384   GraphicStateStack := TObjectStack.Create;
3385   Dictionary := TStringList.Create;
3386   Dictionary.CaseSensitive := True;
3387   CurrentGraphicState := TGraphicState.Create;
3388 end;
3389 
3390 destructor TvEPSVectorialReader.Destroy;
3391 begin
3392   Tokenizer.Free;
3393   Stack.Free;
3394   GraphicStateStack.Free;
3395   Dictionary.Free;
3396   CurrentGraphicState.Free;
3397 
3398   inherited Destroy;
3399 end;
3400 
3401 procedure TvEPSVectorialReader.ReadFromStream(AStream: TStream;
3402   AData: TvVectorialDocument);
3403 var
3404   lPage: TvVectorialPage;
3405 begin
3406   Tokenizer.ReadFromStream(AStream);
3407 //  Tokenizer.DebugOut();
3408 
3409   // Make sure we have at least one path
3410   lPage := AData.AddPage();
3411   lPage.StartPath();
3412 
3413   RunPostScript(Tokenizer.Tokens, lPage, AData);
3414 
3415   // Make sure we have at least one path
3416   lPage.EndPath();
3417 
3418   // PostScript has no document size information, so lets calculate it ourselves
3419   AData.GuessDocumentSize();
3420   AData.GuessGoodZoomLevel()
3421 end;
3422 
3423 initialization
3424 
3425   RegisterVectorialReader(TvEPSVectorialReader, vfEncapsulatedPostScript);
3426 
3427 end.
3428 
3429