2011-06-06 16:23:00 +00:00
|
|
|
{
|
|
|
|
Reads EPS files
|
|
|
|
|
|
|
|
License: The same modified LGPL as the Free Pascal RTL
|
|
|
|
See the file COPYING.modifiedLGPL for more details
|
|
|
|
|
|
|
|
AUTHORS: Felipe Monteiro de Carvalho
|
|
|
|
|
|
|
|
}
|
|
|
|
unit epsvectorialreader;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
Classes, SysUtils, Math,
|
2011-06-08 13:53:25 +00:00
|
|
|
fpvectorial, fpimage, fpvutils;
|
2011-06-06 16:23:00 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
TPSTokenType = (ttComment, ttFloat);
|
|
|
|
|
|
|
|
TPSTokens = TFPList;// TPSToken;
|
|
|
|
|
|
|
|
TPSToken = class
|
|
|
|
StrValue: string;
|
|
|
|
FloatValue: double;
|
|
|
|
IntValue: Integer;
|
|
|
|
Childs: TPSTokens;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TCommentToken = class(TPSToken)
|
|
|
|
end;
|
|
|
|
|
2011-06-08 13:53:25 +00:00
|
|
|
TDefinitionToken = class(TPSToken)
|
|
|
|
end;
|
|
|
|
|
|
|
|
TExpressionToken = class(TPSToken)
|
|
|
|
end;
|
|
|
|
|
|
|
|
TPostScriptScannerState = (ssSearchingToken, ssInComment, ssInDefinition, ssInExpressionElement);
|
2011-06-06 16:23:00 +00:00
|
|
|
|
|
|
|
{ TPSTokenizer }
|
|
|
|
|
|
|
|
TPSTokenizer = class
|
|
|
|
public
|
|
|
|
Tokens: TPSTokens;
|
|
|
|
constructor Create;
|
|
|
|
destructor Destroy; override;
|
|
|
|
procedure ReadFromStream(AStream: TStream);
|
|
|
|
procedure DebugOut();
|
|
|
|
function IsValidPostScriptChar(AChar: Byte): Boolean;
|
2011-06-08 13:53:25 +00:00
|
|
|
function IsPostScriptSpace(AChar: Byte): Boolean;
|
|
|
|
function IsEndOfLine(ACurChar: Byte; AStream: TStream): Boolean;
|
2011-06-06 16:23:00 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{ TvEPSVectorialReader }
|
|
|
|
|
|
|
|
TvEPSVectorialReader = class(TvCustomVectorialReader)
|
|
|
|
private
|
|
|
|
FPointSeparator: TFormatSettings;
|
|
|
|
public
|
|
|
|
{ General reading methods }
|
|
|
|
Tokenizer: TPSTokenizer;
|
|
|
|
constructor Create; override;
|
|
|
|
Destructor Destroy; override;
|
|
|
|
procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
|
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
{ TPSTokenizer }
|
|
|
|
|
|
|
|
constructor TPSTokenizer.Create;
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
Tokens := TPSTokens.Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TPSTokenizer.Destroy;
|
|
|
|
begin
|
|
|
|
Tokens.Free;
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{@@ Rules for parsing PostScript files:
|
|
|
|
|
|
|
|
* Coments go from the first occurence of % outside a line to the next new line
|
|
|
|
* The only accepted characters are printable ASCII ones, plus spacing ASCII chars
|
|
|
|
See IsValidPostScriptChar about that
|
|
|
|
}
|
|
|
|
procedure TPSTokenizer.ReadFromStream(AStream: TStream);
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
CurChar: Char;
|
2011-06-08 13:53:25 +00:00
|
|
|
CurLine: Integer = 1;
|
2011-06-06 16:23:00 +00:00
|
|
|
State: TPostScriptScannerState = ssSearchingToken;
|
|
|
|
CommentToken: TCommentToken;
|
2011-06-08 13:53:25 +00:00
|
|
|
DefinitionToken: TDefinitionToken;
|
|
|
|
ExpressionToken: TExpressionToken;
|
|
|
|
Len: Integer;
|
|
|
|
lIsEndOfLine: Boolean;
|
2011-06-06 16:23:00 +00:00
|
|
|
begin
|
|
|
|
while AStream.Position < AStream.Size do
|
|
|
|
begin
|
|
|
|
CurChar := Char(AStream.ReadByte());
|
|
|
|
if not IsValidPostScriptChar(Byte(CurChar)) then
|
|
|
|
raise Exception.Create('[TPSTokenizer.ReadFromStream] Invalid char: ' + IntToHex(Byte(CurChar), 2));
|
|
|
|
|
2011-06-08 13:53:25 +00:00
|
|
|
lIsEndOfLine := IsEndOfLine(Byte(CurChar), AStream);
|
|
|
|
if lIsEndOfLine then Inc(CurLine);
|
|
|
|
|
2011-06-06 16:23:00 +00:00
|
|
|
case State of
|
|
|
|
{ Searching for a token }
|
|
|
|
ssSearchingToken:
|
|
|
|
begin
|
2011-06-08 13:53:25 +00:00
|
|
|
if CurChar = '%' then
|
|
|
|
begin
|
|
|
|
CommentToken := TCommentToken.Create;
|
|
|
|
State := ssInComment;
|
|
|
|
end
|
|
|
|
else if CurChar = '/' then
|
|
|
|
begin
|
|
|
|
DefinitionToken := TDefinitionToken.Create;
|
|
|
|
State := ssInDefinition;
|
|
|
|
end
|
|
|
|
else if CurChar in ['a'..'z'] + ['A'..'Z'] + ['0'..'9'] then
|
|
|
|
begin
|
|
|
|
ExpressionToken := TExpressionToken.Create;
|
|
|
|
State := ssInExpressionElement;
|
|
|
|
end
|
|
|
|
else if lIsEndOfLine then Continue
|
|
|
|
else
|
|
|
|
raise Exception.Create(Format('[TPSTokenizer.ReadFromStream] Unexpected char while searching for token: $%s in Line %d',
|
|
|
|
[IntToHex(Byte(CurChar), 2), CurLine]));
|
2011-06-06 16:23:00 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{ Passing by comments }
|
|
|
|
ssInComment:
|
|
|
|
begin
|
|
|
|
CommentToken.StrValue := CommentToken.StrValue + CurChar;
|
2011-06-08 13:53:25 +00:00
|
|
|
if lIsEndOfLine then
|
|
|
|
begin
|
|
|
|
Tokens.Add(CommentToken);
|
|
|
|
State := ssSearchingToken;
|
|
|
|
end;
|
|
|
|
end; // ssInComment
|
2011-06-06 16:23:00 +00:00
|
|
|
|
2011-06-08 13:53:25 +00:00
|
|
|
// Dictionary definitions end in "def"
|
|
|
|
ssInDefinition:
|
|
|
|
begin
|
|
|
|
DefinitionToken.StrValue := DefinitionToken.StrValue + CurChar;
|
|
|
|
Len := Length(DefinitionToken.StrValue);
|
|
|
|
if Len >= 3 then
|
|
|
|
begin
|
|
|
|
if (DefinitionToken.StrValue[Len-2] = 'd') and (DefinitionToken.StrValue[Len-1] = 'e') and (DefinitionToken.StrValue[Len] = 'f') then
|
2011-06-06 16:23:00 +00:00
|
|
|
begin
|
2011-06-08 13:53:25 +00:00
|
|
|
Tokens.Add(DefinitionToken);
|
2011-06-06 16:23:00 +00:00
|
|
|
State := ssSearchingToken;
|
|
|
|
end;
|
2011-06-08 13:53:25 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Goes until a space comes
|
|
|
|
ssInExpressionElement:
|
|
|
|
begin
|
|
|
|
if IsPostScriptSpace(Byte(CurChar)) then
|
|
|
|
begin
|
|
|
|
Tokens.Add(ExpressionToken);
|
|
|
|
State := ssSearchingToken;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
ExpressionToken.StrValue := ExpressionToken.StrValue + CurChar;
|
|
|
|
end;
|
2011-06-06 16:23:00 +00:00
|
|
|
|
|
|
|
end; // case
|
|
|
|
end; // while
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TPSTokenizer.DebugOut();
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
Token: TPSToken;
|
|
|
|
begin
|
|
|
|
for i := 0 to Tokens.Count - 1 do
|
|
|
|
begin
|
|
|
|
Token := TPSToken(Tokens.Items[i]);
|
|
|
|
|
|
|
|
if Token is TCommentToken then
|
|
|
|
begin
|
|
|
|
WriteLn(Format('TCommentToken StrValue=%s', [Token.StrValue]));
|
2011-06-08 13:53:25 +00:00
|
|
|
end
|
|
|
|
else if Token is TDefinitionToken then
|
|
|
|
begin
|
|
|
|
WriteLn(Format('TDefinitionToken StrValue=%s', [Token.StrValue]));
|
|
|
|
end
|
|
|
|
else if Token is TExpressionToken then
|
|
|
|
begin
|
|
|
|
WriteLn(Format('TExpressionToken StrValue=%s', [Token.StrValue]));
|
2011-06-06 16:23:00 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{@@ Valid PostScript Chars:
|
|
|
|
|
2011-06-08 13:09:16 +00:00
|
|
|
All printable ASCII: a..zA..Z0..9 plus punctuation
|
2011-06-06 16:23:00 +00:00
|
|
|
|
|
|
|
Plus the following white spaces
|
|
|
|
000 00 0 Null (nul)
|
|
|
|
011 09 9 Tab (tab)
|
|
|
|
012 0A 10 Line feed (LF)
|
|
|
|
014 0C 12 Form feed (FF)
|
|
|
|
015 0D 13 Carriage return (CR)
|
|
|
|
040 20 32 Space (SP)
|
|
|
|
}
|
|
|
|
function TPSTokenizer.IsValidPostScriptChar(AChar: Byte): Boolean;
|
|
|
|
begin
|
|
|
|
Result := ((AChar > 32) and (AChar < 127)) or (AChar in [0, 9, 10, 12, 13, 32]);
|
|
|
|
end;
|
|
|
|
|
2011-06-08 13:53:25 +00:00
|
|
|
function TPSTokenizer.IsPostScriptSpace(AChar: Byte): Boolean;
|
|
|
|
begin
|
|
|
|
Result := AChar in [0, 9, 10, 12, 13, 32];
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TPSTokenizer.IsEndOfLine(ACurChar: Byte; AStream: TStream): Boolean;
|
|
|
|
var
|
|
|
|
HasNextChar: Boolean = False;
|
|
|
|
NextChar: Byte;
|
|
|
|
begin
|
|
|
|
Result := False;
|
|
|
|
|
|
|
|
if ACurChar = 13 then
|
|
|
|
begin
|
|
|
|
if AStream.Position < AStream.Size then
|
|
|
|
begin
|
|
|
|
HasNextChar := True;
|
|
|
|
NextChar := AStream.ReadByte();
|
|
|
|
if NextChar <> 10 then AStream.Seek(-1, soFromCurrent); // Go back if it wasnt a #13#10
|
|
|
|
Exit(True);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if ACurChar = 10 then Result := True;
|
|
|
|
end;
|
|
|
|
|
2011-06-06 16:23:00 +00:00
|
|
|
{$ifndef Windows}
|
|
|
|
{$define FPVECTORIALDEBUG}
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
{ TvEPSVectorialReader }
|
|
|
|
|
|
|
|
constructor TvEPSVectorialReader.Create;
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
|
|
|
|
Tokenizer := TPSTokenizer.Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TvEPSVectorialReader.Destroy;
|
|
|
|
begin
|
|
|
|
Tokenizer.Free;
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TvEPSVectorialReader.ReadFromStream(AStream: TStream;
|
|
|
|
AData: TvVectorialDocument);
|
|
|
|
begin
|
|
|
|
Tokenizer.ReadFromStream(AStream);
|
|
|
|
Tokenizer.DebugOut();
|
|
|
|
end;
|
|
|
|
|
|
|
|
initialization
|
|
|
|
|
|
|
|
RegisterVectorialReader(TvEPSVectorialReader, vfEncapsulatedPostScript);
|
|
|
|
|
|
|
|
end.
|
|
|
|
|