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 any0 … anyn-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 forall – Execute 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