Adds a copy of the fpvectorial source code for convenience

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1448 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
sekelsenmat
2011-01-14 14:49:45 +00:00
parent 889d7ad6a0
commit 897d9483ac
14 changed files with 3387 additions and 190 deletions

View File

@ -0,0 +1,234 @@
{
Reads AvisoCNC G-Code
License: The same modified LGPL as the Free Pascal RTL
See the file COPYING.modifiedLGPL for more details
AUTHORS: Felipe Monteiro de Carvalho
Pedro Sol Pegorini L de Lima
}
unit avisocncgcodereader;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
fpvectorial;
type
{ Used by tcutils.SeparateString }
T10Strings = array[0..9] of shortstring;
{ TvAvisoCNCGCodeReader }
TvAvisoCNCGCodeReader = class(TvCustomVectorialReader)
private
LastX, LastY, LastZ: Double;
function SeparateString(AString: string; ASeparator: Char): T10Strings;
procedure ReadString(AStr: string; AData: TvVectorialDocument);
function GetCoordinate(AStr: shortstring): Integer;
function GetCoordinateValue(AStr: shortstring): Double;
public
{ General reading methods }
procedure ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); override;
end;
implementation
const
{ Coordinate constants }
INT_COORDINATE_NONE = 0;
INT_COORDINATE_X = 1;
INT_COORDINATE_Y = 2;
INT_COORDINATE_Z = 3;
{ GCode constants }
STR_GCODE_LINEAR_MOVE = 'G01';
STR_GCODE_STEPPER_MOVE = 'S01';
STR_GCODE_2DBEZIER_MOVE = 'B02';
STR_GCODE_3DBEZIER_MOVE = 'B03';
STR_GCODE_DRILL_UP = 'P01';
STR_GCODE_DRILL_DOWN = 'P02';
{ TvAvisoCNCGCodeReader }
{@@
Reads a string and separates it in substring
using ASeparator to delimite them.
Limits:
Number of substrings: 10 (indexed 0 to 9)
Length of each substring: 255 (they are shortstrings)
}
function TvAvisoCNCGCodeReader.SeparateString(AString: string; ASeparator: Char): T10Strings;
var
i, CurrentPart: Integer;
begin
CurrentPart := 0;
{ Clears the result }
for i := 0 to 9 do Result[i] := '';
{ Iterates througth the string, filling strings }
for i := 1 to Length(AString) do
begin
if Copy(AString, i, 1) = ASeparator then
begin
Inc(CurrentPart);
{ Verifies if the string capacity wasn't exceeded }
if CurrentPart > 9 then Exit;
end
else
Result[CurrentPart] := Result[CurrentPart] + Copy(AString, i, 1);
end;
end;
procedure TvAvisoCNCGCodeReader.ReadString(AStr: string;
AData: TvVectorialDocument);
var
AParams: T10Strings;
DestX, DestY, DestZ: Double;
i: Integer;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn('TvAvisoCNCGCodeReader.ReadString ', AStr);
{$endif}
AParams := SeparateString(AStr, ' ');
{
Format may be:
G01 X3
G01 X3 Y4
G01 X3 Y4 Z2
}
if AParams[0] = STR_GCODE_DRILL_UP then
begin
AData.AddLineToPath(LastX, LastY, 0);
LastZ := 0;
end
else if AParams[0] = STR_GCODE_DRILL_DOWN then
begin
AData.AddLineToPath(LastX, LastY, 50);
LastZ := 50;
end
else if AParams[0] = STR_GCODE_LINEAR_MOVE then
begin
DestX := LastX;
DestY := LastY;
DestZ := LastZ;
for i := 1 to 3 do
begin
case GetCoordinate(AParams[i]) of
INT_COORDINATE_X: DestX := GetCoordinateValue(AParams[i]);
INT_COORDINATE_Y: DestY := GetCoordinateValue(AParams[i]);
INT_COORDINATE_Z: DestZ := GetCoordinateValue(AParams[i]);
else
// error
end;
end;
AData.AddLineToPath(DestX, DestY, DestZ);
LastX := DestX;
LastY := DestY;
LastZ := DestZ;
end
else if AParams[0] = STR_GCODE_2DBEZIER_MOVE then
begin
AData.AddBezierToPath(
GetCoordinateValue(AParams[1]),
GetCoordinateValue(AParams[2]),
GetCoordinateValue(AParams[3]),
GetCoordinateValue(AParams[4]),
GetCoordinateValue(AParams[5]),
GetCoordinateValue(AParams[6])
);
LastX := GetCoordinateValue(AParams[5]);
LastY := GetCoordinateValue(AParams[6]);
end
else if AParams[0] = STR_GCODE_3DBEZIER_MOVE then
begin
AData.AddBezierToPath(
GetCoordinateValue(AParams[1]),
GetCoordinateValue(AParams[2]),
GetCoordinateValue(AParams[3]),
GetCoordinateValue(AParams[4]),
GetCoordinateValue(AParams[5]),
GetCoordinateValue(AParams[6]),
GetCoordinateValue(AParams[7]),
GetCoordinateValue(AParams[8]),
GetCoordinateValue(AParams[9])
);
LastX := GetCoordinateValue(AParams[7]);
LastY := GetCoordinateValue(AParams[8]);
LastZ := GetCoordinateValue(AParams[9]);
end;
{else
begin
Ignore any of these codes:
STR_GCODE_STEPPER_MOVE
and anything else
end;}
end;
function TvAvisoCNCGCodeReader.GetCoordinate(AStr: shortstring): Integer;
begin
Result := INT_COORDINATE_NONE;
if AStr = '' then Exit
else if AStr[1] = 'X' then Result := INT_COORDINATE_X
else if AStr[1] = 'Y' then Result := INT_COORDINATE_Y
else if AStr[1] = 'Z' then Result := INT_COORDINATE_Z;
end;
function TvAvisoCNCGCodeReader.GetCoordinateValue(AStr: shortstring): Double;
begin
Result := 0.0;
if Length(AStr) <= 1 then Exit;
Result := StrToFloat(Copy(AStr, 2, Length(AStr) - 1));
end;
{@@
The information of each separate path is lost in G-Code files
Only one path uniting all of them is created when reading G-Code
}
procedure TvAvisoCNCGCodeReader.ReadFromStrings(AStrings: TStrings;
AData: TvVectorialDocument);
var
i: Integer;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn('TvAvisoCNCGCodeReader.ReadFromStrings AStrings = ', PtrInt(AStrings), ' AData = ', PtrInt(AData));
{$endif}
AData.StartPath(0, 0);
for i := 0 to AStrings.Count - 1 do
ReadString(AStrings.Strings[i], AData);
{$ifdef FPVECTORIALDEBUG}
WriteLn('AData.EndPath');
{$endif}
AData.EndPath();
end;
initialization
RegisterVectorialReader(TvAvisoCNCGCodeReader, vfGCodeAvisoCNCPrototipoV5);
end.

View File

@ -0,0 +1,87 @@
{
Writes AvisoCNC G-Code
License: The same modified LGPL as the Free Pascal RTL
See the file COPYING.modifiedLGPL for more details
AUTHORS: Felipe Monteiro de Carvalho
Pedro Sol Pegorini L de Lima
}
unit avisocncgcodewriter;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
fpvectorial;
type
{ TvAvisoCNCGCodeWriter }
TvAvisoCNCGCodeWriter = class(TvCustomVectorialWriter)
public
{ General reading methods }
procedure WriteToStrings(AStrings: TStrings; AData: TvVectorialDocument); override;
end;
implementation
{ TvGCodeVectorialWriter }
procedure TvAvisoCNCGCodeWriter.WriteToStrings(AStrings: TStrings;
AData: TvVectorialDocument);
var
i, j: Integer;
Str: string;
APath: TPath;
begin
AStrings.Clear;
AStrings.Add('M216 // Ligar monitor de carga');
AStrings.Add('G28 // Ir rapidamente para posição inicial');
AStrings.Add('G00');
// itera por todos os itens
for i := 0 to AData.GetPathCount - 1 do
begin
APath := AData.GetPath(i);
// levanta a broca
AStrings.Add('P01 // Sobe a cabeça de gravação');
// vai para o ponto inicial
AStrings.Add(Format('G01 X%f Y%f',
[APath.Points[0].X, APath.Points[0].Y]));
AStrings.Add('P02 // Abaixa a cabeça de gravação');
for j := 1 to APath.Len - 1 do
begin
case APath.Points[j].SegmentType of
st2DLine: AStrings.Add(Format('G01 X%f Y%f',
[APath.Points[j].X, APath.Points[j].Y]));
st3DLine: AStrings.Add(Format('G01 X%f Y%f Z%f',
[APath.Points[j].X, APath.Points[j].Y, APath.Points[j].Z]));
st2DBezier: AStrings.Add(Format('B02 X%f Y%f X%f Y%f X%f Y%f',
[APath.Points[j].X2, APath.Points[j].Y2,
APath.Points[j].X3, APath.Points[j].Y3,
APath.Points[j].X, APath.Points[j].Y]));
st3DBezier: AStrings.Add(Format('B03 X%f Y%f Z%f X%f Y%f Z%f X%f Y%f Z%f',
[APath.Points[j].X2, APath.Points[j].Y2, APath.Points[j].Z2,
APath.Points[j].X3, APath.Points[j].Y3, APath.Points[j].Z3,
APath.Points[j].X, APath.Points[j].Y, APath.Points[j].Z]));
end;
end;
end;
AStrings.Add('P01 // Sobe a cabeça de gravação');
AStrings.Add('M30 // Parar o programa e retornar para posição inicial');
AStrings.Add('M215 // Desligar monitor de carga');
end;
initialization
RegisterVectorialWriter(TvAvisoCNCGCodeWriter, vfGCodeAvisoCNCPrototipoV5);
end.

View File

@ -0,0 +1,74 @@
unit avisozlib;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, paszlib;
type
Decode = class
public
procedure CHECK_ERR(err: Integer; msg: String);
procedure EXIT_ERR(const msg: String);
function test_inflate(compr: Pointer; comprLen : LongInt;
uncompr: Pointer; uncomprLen : LongInt): PChar;
constructor Create();
end;
implementation
procedure Decode.CHECK_ERR(err: Integer; msg: String);
begin
if err <> Z_OK then
begin
raise Exception.Create('ERROR: ' + msg);
Halt(1);
end;
end;
procedure Decode.EXIT_ERR(const msg: String);
begin
raise Exception.Create('ERROR: ' + msg);
Halt(1);
end;
function Decode.test_inflate(compr: Pointer; comprLen : LongInt;
uncompr: Pointer; uncomprLen : LongInt): PChar;
var err: Integer;
d_stream: TZStream; // decompression stream
begin
StrCopy(PChar(uncompr), 'garbage');
d_stream.next_in := compr;
d_stream.avail_in := 0;
d_stream.next_out := uncompr;
err := inflateInit(d_stream);
CHECK_ERR(err, 'inflateInit');
while (d_stream.total_out < uncomprLen) and
(d_stream.total_in < comprLen) do
begin
d_stream.avail_out := 1; // force small buffers
d_stream.avail_in := 1;
err := inflate(d_stream, Z_NO_FLUSH);
if err = Z_STREAM_END then
break;
CHECK_ERR(err, 'inflate');
end;
err := inflateEnd(d_stream);
CHECK_ERR(err, 'inflateEnd');
Result:=PChar(uncompr);
end;
constructor Decode.Create();
begin
inherited Create;
end;
end.

View File

@ -0,0 +1,180 @@
{
cdrvectorialreader.pas
Reads a Corel Draw vectorial file
CDR file format specification obtained from:
ADOBE SYSTEMS INCORPORATED. PDF Reference: Adobe®
Portable Document Format. San Jose, 2006. (Sixth edition).
AUTHORS: Felipe Monteiro de Carvalho
License: The same modified LGPL as the Free Pascal RTL
See the file COPYING.modifiedLGPL for more details
}
unit cdrvectorialreader;
{$ifdef fpc}
{$mode delphi}
{$endif}
interface
uses
Classes, SysUtils,
pdfvrlexico, pdfvrsintatico, pdfvrsemantico, avisozlib,
fpvectorial;
type
TCDRChunk = class
Name: array[0..3] of Char;
Size: Cardinal;
ChildChunks: TFPList;
end;
TCDRChunkClass = class of TCDRChunk;
TvCDRInternalData = TCDRChunk;
TCDRChunkVRSN = class(TCDRChunk)
VersionStr: string;
VersionNum: Integer;
end;
{ TvCDRVectorialReader }
TvCDRVectorialReader = class(TvCustomVectorialReader)
private
procedure ReadVersionChunk(AStream: TStream; var AData: TCDRChunk);
function AddNewChunk(var AData: TCDRChunk; AClass: TCDRChunkClass): TCDRChunk;
public
{ General reading methods }
procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
{ File format exploring methods }
procedure ExploreFromFile(AFilename: string; out AData: TvCDRInternalData);
procedure ExploreFromStream(AStream: TStream; out AData: TvCDRInternalData);
end;
implementation
{ TvPDFVectorialReader }
procedure TvCDRVectorialReader.ReadVersionChunk(AStream: TStream;
var AData: TCDRChunk);
var
lDWord: DWord;
lChunk: TCDRChunkVRSN absolute AData;
lVerBytes: array[0..1] of Byte;
begin
// Read the Chunk name
lDWord := AStream.ReadDWord();
// Read the Chunk size
lDWord := AStream.ReadDWord();
// Read the version
AStream.Read(lVerBytes, 2);
if (lVerBytes[0] = $BC) and (lVerBytes[1] = $02) then
begin
lChunk.VersionNum := 7;
lChunk.VersionStr := 'CorelDraw 7';
end
else if (lVerBytes[0] = $20) and (lVerBytes[1] = $03) then
begin
lChunk.VersionNum := 8;
lChunk.VersionStr := 'CorelDraw 8';
end
else if (lVerBytes[0] = $21) and (lVerBytes[1] = $03) then
begin
lChunk.VersionNum := 8;
lChunk.VersionStr := 'CorelDraw 8bidi';
end
else if (lVerBytes[0] = $84) and (lVerBytes[1] = $03) then
begin
lChunk.VersionNum := 9;
lChunk.VersionStr := 'CorelDraw 9';
end
else if (lVerBytes[0] = $E8) and (lVerBytes[1] = $03) then
begin
lChunk.VersionNum := 10;
lChunk.VersionStr := 'CorelDraw 10';
end
else if (lVerBytes[0] = $4C) and (lVerBytes[1] = $04) then
begin
lChunk.VersionNum := 11;
lChunk.VersionStr := 'CorelDraw 11';
end
else if (lVerBytes[0] = $B0) and (lVerBytes[1] = $04) then
begin
lChunk.VersionNum := 12;
lChunk.VersionStr := 'CorelDraw 12';
end
else if (lVerBytes[0] = $14) and (lVerBytes[1] = $05) then
begin
lChunk.VersionNum := 13;
lChunk.VersionStr := 'CorelDraw X3';
end;
end;
function TvCDRVectorialReader.AddNewChunk(var AData: TCDRChunk; AClass: TCDRChunkClass): TCDRChunk;
begin
if AData.ChildChunks = nil then AData.ChildChunks := TFPList.Create;
Result := AClass.Create;
AData.ChildChunks.Add(Result);
end;
procedure TvCDRVectorialReader.ReadFromStream(AStream: TStream;
AData: TvVectorialDocument);
begin
end;
procedure TvCDRVectorialReader.ExploreFromFile(AFilename: string;
out AData: TvCDRInternalData);
var
FileStream: TFileStream;
begin
FileStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
try
ExploreFromStream(FileStream, AData);
finally
FileStream.Free;
end;
end;
procedure TvCDRVectorialReader.ExploreFromStream(AStream: TStream;
out AData: TvCDRInternalData);
var
lRIFF: array[0..3] of Char;
lDocSize, lDWord: Cardinal;
lChild: TCDRChunk;
begin
// Create the data object
AData := TCDRChunk.Create;
// All CorelDraw files starts with "RIFF"
AStream.Read(lRIFF, 4);
if lRIFF <> 'RIFF' then
raise Exception.Create('[TvCDRVectorialReader.ExploreFromStream] The Corel Draw RIFF file marker wasn''t found.');
// And then 4 bytes for the document size
lDocSize := AStream.ReadDWord();
// And mroe 4 bytes of other stuff
lDWord := AStream.ReadDWord();
// Now comes the version
lChild := AddNewChunk(AData, TCDRChunkVRSN);
ReadVersionChunk(AStream, lChild);
end;
initialization
RegisterVectorialReader(TvCDRVectorialReader, vfCorelDrawCDR);
end.

View File

@ -0,0 +1,300 @@
{
Reads DXF files
License: The same modified LGPL as the Free Pascal RTL
See the file COPYING.modifiedLGPL for more details
AUTHORS: Felipe Monteiro de Carvalho
DXF is composed by records written in ASCII with the following structure:
0
SECTION
section_number
SECTION_NAME
<data>
0
ENDSEC
0
after all section end there is:
EOF
}
unit dxfvectorialreader;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
fpvectorial;
type
{ Used by tcutils.SeparateString }
T10Strings = array[0..9] of shortstring;
{ TvDXFVectorialReader }
TvDXFVectorialReader = class(TvCustomVectorialReader)
private
LineStartX, LineStartY, LineStartZ: Double;
LineEndX, LineEndY, LineEndZ: Double;
function SeparateString(AString: string; ASeparator: Char): T10Strings;
function ReadSection(AStrings: TStrings; var AIndex: Integer; AData: TvVectorialDocument): Boolean;
function ReadENTITIES(AStrings: TStrings; var AIndex: Integer; AData: TvVectorialDocument): Boolean;
function ReadENTITIES_LINE(AStrings: TStrings; var AIndex: Integer; AData: TvVectorialDocument): Boolean;
function GetCoordinate(AStr: shortstring): Integer;
function GetCoordinateValue(AStr: shortstring): Double;
public
{ General reading methods }
procedure ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); override;
end;
implementation
{$define FPVECTORIALDEBUG}
const
{ Coordinate constants }
INT_COORDINATE_NONE = 0;
INT_COORDINATE_X = 1;
INT_COORDINATE_Y = 2;
INT_COORDINATE_Z = 3;
{ GCode constants }
STR_GCODE_LINEAR_MOVE = 'G01';
STR_GCODE_STEPPER_MOVE = 'S01';
STR_GCODE_2DBEZIER_MOVE = 'B02';
STR_GCODE_3DBEZIER_MOVE = 'B03';
STR_GCODE_DRILL_UP = 'P01';
STR_GCODE_DRILL_DOWN = 'P02';
{ TvAvisoCNCGCodeReader }
{@@
Reads a string and separates it in substring
using ASeparator to delimite them.
Limits:
Number of substrings: 10 (indexed 0 to 9)
Length of each substring: 255 (they are shortstrings)
}
function TvDXFVectorialReader.SeparateString(AString: string; ASeparator: Char): T10Strings;
var
i, CurrentPart: Integer;
begin
CurrentPart := 0;
{ Clears the result }
for i := 0 to 9 do Result[i] := '';
{ Iterates througth the string, filling strings }
for i := 1 to Length(AString) do
begin
if Copy(AString, i, 1) = ASeparator then
begin
Inc(CurrentPart);
{ Verifies if the string capacity wasn't exceeded }
if CurrentPart > 9 then Exit;
end
else
Result[CurrentPart] := Result[CurrentPart] + Copy(AString, i, 1);
end;
end;
{@@
returns If an end of file marker was found
}
function TvDXFVectorialReader.ReadSection(
AStrings: TStrings; var AIndex: Integer; AData: TvVectorialDocument): Boolean;
var
DestX, DestY, DestZ: Double;
StrSectionNum, StrSectionName: string;
IntSectionNum, i: Integer;
begin
Result := False;
// Check if there is minimal space for a section
if AIndex+5 > AStrings.Count then
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn('Not enough space for a section');
{$endif}
Exit(True);
end;
// Check of the EOF marker
StrSectionName := Trim(AStrings.Strings[AIndex+1]);
if StrSectionName = 'EOF' then
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn('EOF found');
{$endif}
Exit(True);
end;
// Now read and process the section name
StrSectionNum := AStrings.Strings[AIndex+2];
IntSectionNum := StrToInt(Trim(StrSectionNum));
StrSectionName := AStrings.Strings[AIndex+3];
{$ifdef FPVECTORIALDEBUG}
WriteLn('TvDXFVectorialReader.ReadSection ' + StrSectionName);
{$endif}
if (StrSectionName = 'HEADER') or
(StrSectionName = 'CLASSES') or
(StrSectionName = 'TABLES') or
(StrSectionName = 'BLOCKS') or
(StrSectionName = 'OBJECTS') or
(StrSectionName = 'THUMBNAILIMAGE') then
begin
// We don't care about contents here, so let's just find the last section and get out of here.
for i := AIndex + 4 to AStrings.Count - 1 do
begin
if AStrings.Strings[i] = 'ENDSEC' then
begin
AIndex := i + 1;
Exit;
end;
end;
// If we reached here, the section in incomplete
raise Exception.Create('TvDXFVectorialReader.ReadSection: ENDSEC was not found in the SECTION');
end
else if StrSectionName = 'ENTITIES' then
begin
AIndex := AIndex + 4;
while not ReadENTITIES(AStrings, AIndex, AData) do ;
end;
{else
begin
end;}
end;
function TvDXFVectorialReader.ReadENTITIES(AStrings: TStrings;
var AIndex: Integer; AData: TvVectorialDocument): Boolean;
var
StrSectionNum, StrSectionName: string;
IntSectionNum, i: Integer;
begin
Result := False;
// Now read and process the item name
StrSectionName := AStrings.Strings[AIndex+1];
{$ifdef FPVECTORIALDEBUG}
WriteLn('TvDXFVectorialReader.ReadENTITIES ', StrSectionName);
{$endif}
if StrSectionName = 'ENDSEC' then
begin
Inc(AIndex, 2);
Exit(True);
end
else if StrSectionName = 'LINE' then
begin
// Initial values
LineStartX := 0;
LineStartY := 0;
LineStartZ := 0;
LineEndX := 0;
LineEndY := 0;
LineEndZ := 0;
// Read the data of the line
Inc(AIndex, 2);
while not ReadENTITIES_LINE(AStrings, AIndex, AData) do ;
// And now write it
{$ifdef FPVECTORIALDEBUG}
WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY]));
{$endif}
AData.StartPath(LineStartX, LineStartY);
AData.AddLineToPath(LineEndX, LineEndY);
AData.EndPath();
end;
end;
function TvDXFVectorialReader.ReadENTITIES_LINE(AStrings: TStrings;
var AIndex: Integer; AData: TvVectorialDocument): Boolean;
var
StrSectionNum, StrSectionValue: string;
IntSectionNum: Integer;
FloatSectionValue: double;
begin
Result := False;
// Now read and process the item name
StrSectionNum := AStrings.Strings[AIndex];
StrSectionValue := AStrings.Strings[AIndex+1];
if (StrSectionValue = 'LINE') or
(StrSectionValue = 'ENDSEC') then
begin
Exit(True);
end
else
begin
Inc(AIndex, 2);
IntSectionNum := StrToInt(Trim(StrSectionNum));
FloatSectionValue := StrToFloat(Trim(StrSectionValue));
case IntSectionNum of
10: LineStartX := FloatSectionValue;
20: LineStartY := FloatSectionValue;
30: LineStartZ := FloatSectionValue;
11: LineEndX := FloatSectionValue;
21: LineEndY := FloatSectionValue;
31: LineEndZ := FloatSectionValue;
end;
end;
end;
function TvDXFVectorialReader.GetCoordinate(AStr: shortstring): Integer;
begin
Result := INT_COORDINATE_NONE;
if AStr = '' then Exit
else if AStr[1] = 'X' then Result := INT_COORDINATE_X
else if AStr[1] = 'Y' then Result := INT_COORDINATE_Y
else if AStr[1] = 'Z' then Result := INT_COORDINATE_Z;
end;
function TvDXFVectorialReader.GetCoordinateValue(AStr: shortstring): Double;
begin
Result := 0.0;
if Length(AStr) <= 1 then Exit;
Result := StrToFloat(Copy(AStr, 2, Length(AStr) - 1));
end;
{@@
The information of each separate path is lost in G-Code files
Only one path uniting all of them is created when reading G-Code
}
procedure TvDXFVectorialReader.ReadFromStrings(AStrings: TStrings;
AData: TvVectorialDocument);
var
i: Integer;
begin
i := 0;
while i < AStrings.Count - 1 do
if ReadSection(AStrings, i, AData) then Break;
end;
initialization
RegisterVectorialReader(TvDXFVectorialReader, vfDXF);
end.

View File

@ -0,0 +1,10 @@
unit fpvectbuildunit;
interface
Uses
avisocncgcodereader,avisocncgcodewriter,avisozlib,fpvectorial,
fpvtocanvas,pdfvectorialreader,pdfvrlexico,pdfvrsemantico,pdfvrsintatico,
svgvectorialwriter,cdrvectorialreader;
implementation
end.

View File

@ -0,0 +1,888 @@
{
fpvectorial.pas
Vector graphics document
License: The same modified LGPL as the Free Pascal RTL
See the file COPYING.modifiedLGPL for more details
AUTHORS: Felipe Monteiro de Carvalho
Pedro Sol Pegorini L de Lima
}
unit fpvectorial;
{$ifdef fpc}
{$mode delphi}
{$endif}
interface
uses
Classes, SysUtils;
type
TvVectorialFormat = (
{ Multi-purpose document formats }
vfPDF, vfPostScript, vfSVG, vfCorelDrawCDR, vfWindowsMetafileWMF,
{ CAD formats }
vfDXF,
{ GCode formats }
vfGCodeAvisoCNCPrototipoV5, vfGCodeAvisoCNCPrototipoV6);
const
{ Default extensions }
{ Multi-purpose document formats }
STR_PDF_EXTENSION = '.pdf';
STR_POSTSCRIPT_EXTENSION = '.ps';
STR_SVG_EXTENSION = '.svg';
STR_CORELDRAW_EXTENSION = '.cdr';
STR_WINMETAFILE_EXTENSION = '.wmf';
type
TSegmentType = (
st2DLine, st2DBezier,
st3DLine, st3DBezier, stMoveTo);
{@@
The coordinates in fpvectorial are given in millimiters and
the starting point is in the bottom-left corner of the document.
The X grows to the right and the Y grows to the top.
}
{ TPathSegment }
TPathSegment = class
public
SegmentType: TSegmentType;
// Fields for linking the list
Previous: TPathSegment;
Next: TPathSegment;
end;
{@@
In a 2D segment, the X and Y coordinates represent usually the
final point of the segment, being that it starts where the previous
segment ends. The exception is for the first segment of all, which simply
holds the starting point for the drawing and should always be of the type
stMoveTo.
}
T2DSegment = class(TPathSegment)
public
X, Y: Double;
end;
{@@
In Bezier segments, we remain using the X and Y coordinates for the ending point.
The starting point is where the previous segment ended, so that the intermediary
bezier control points are [X2, Y2] and [X3, Y3].
}
T2DBezierSegment = class(T2DSegment)
public
X2, Y2: Double;
X3, Y3: Double;
end;
T3DSegment = class(TPathSegment)
public
{@@
Coordinates of the end of the segment.
For the first segment, this is the starting point.
}
X, Y, Z: Double;
end;
T3DBezierSegment = class(T3DSegment)
public
X2, Y2, Z2: Double;
X3, Y3, Z3: Double;
end;
TPath = class
Len: Integer;
Points: TPathSegment; // Beginning of the double-linked list
PointsEnd: TPathSegment; // End of the double-linked list
CurPoint: TPathSegment; // Used in PrepareForSequentialReading and Next
procedure Assign(APath: TPath);
function Count(): TPathSegment;
procedure PrepareForSequentialReading;
function Next(): TPathSegment;
end;
{@@
TvText represents a text in memory.
At the moment fonts are unsupported, only simple texts
up to 255 chars are supported.
}
TvText = class
public
X, Y, Z: Double; // Z is ignored in 2D formats
FontSize: integer;
FontName: utf8string;
Value: utf8string;
end;
type
TvCustomVectorialWriter = class;
TvCustomVectorialReader = class;
{ TvVectorialDocument }
TvVectorialDocument = class
private
FPaths: TFPList;
FTexts: TFPList;
FTmpPath: TPath;
FTmpText: TvText;
procedure RemoveCallback(data, arg: pointer);
function CreateVectorialWriter(AFormat: TvVectorialFormat): TvCustomVectorialWriter;
function CreateVectorialReader(AFormat: TvVectorialFormat): TvCustomVectorialReader;
procedure ClearTmpPath();
procedure AppendSegmentToTmpPath(ASegment: TPathSegment);
public
Name: string;
Width, Height: Double; // in millimeters
{ Base methods }
constructor Create;
destructor Destroy; override;
procedure WriteToFile(AFileName: string; AFormat: TvVectorialFormat);
procedure WriteToStream(AStream: TStream; AFormat: TvVectorialFormat);
procedure WriteToStrings(AStrings: TStrings; AFormat: TvVectorialFormat);
procedure ReadFromFile(AFileName: string; AFormat: TvVectorialFormat);
procedure ReadFromStream(AStream: TStream; AFormat: TvVectorialFormat);
procedure ReadFromStrings(AStrings: TStrings; AFormat: TvVectorialFormat);
class function GetFormatFromExtension(AFileName: string): TvVectorialFormat;
function GetDetailedFileFormat(): string;
{ Data reading methods }
function GetPath(ANum: Cardinal): TPath;
function GetPathCount: Integer;
function GetText(ANum: Cardinal): TvText;
function GetTextCount: Integer;
{ Data removing methods }
procedure Clear;
procedure RemoveAllPaths;
procedure RemoveAllTexts;
{ Data writing methods }
procedure AddPath(APath: TPath);
procedure StartPath(AX, AY: Double);
procedure AddLineToPath(AX, AY: Double); overload;
procedure AddLineToPath(AX, AY, AZ: Double); overload;
procedure AddBezierToPath(AX1, AY1, AX2, AY2, AX3, AY3: Double); overload;
procedure AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: Double); overload;
procedure EndPath();
procedure AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string); overload;
procedure AddText(AX, AY, AZ: Double; AStr: utf8string); overload;
{ properties }
property PathCount: Integer read GetPathCount;
property Paths[Index: Cardinal]: TPath read GetPath;
end;
{@@ TvVectorialReader class reference type }
TvVectorialReaderClass = class of TvCustomVectorialReader;
{ TvCustomVectorialReader }
TvCustomVectorialReader = class
public
{ General reading methods }
procedure ReadFromFile(AFileName: string; AData: TvVectorialDocument); virtual;
procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); virtual;
procedure ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); virtual;
end;
{@@ TvVectorialWriter class reference type }
TvVectorialWriterClass = class of TvCustomVectorialWriter;
{@@ TvCustomVectorialWriter }
{ TvCustomVectorialWriter }
TvCustomVectorialWriter = class
public
{ General writing methods }
procedure WriteToFile(AFileName: string; AData: TvVectorialDocument); virtual;
procedure WriteToStream(AStream: TStream; AData: TvVectorialDocument); virtual;
procedure WriteToStrings(AStrings: TStrings; AData: TvVectorialDocument); virtual;
end;
{@@ List of registered formats }
TvVectorialFormatData = record
ReaderClass: TvVectorialReaderClass;
WriterClass: TvVectorialWriterClass;
ReaderRegistered: Boolean;
WriterRegistered: Boolean;
Format: TvVectorialFormat;
end;
var
GvVectorialFormats: array of TvVectorialFormatData;
procedure RegisterVectorialReader(
AReaderClass: TvVectorialReaderClass;
AFormat: TvVectorialFormat);
procedure RegisterVectorialWriter(
AWriterClass: TvVectorialWriterClass;
AFormat: TvVectorialFormat);
implementation
const
Str_Error_Nil_Path = ' The program attempted to add a segment before creating a path';
{@@
Registers a new reader for a format
}
procedure RegisterVectorialReader(
AReaderClass: TvVectorialReaderClass;
AFormat: TvVectorialFormat);
var
i, len: Integer;
FormatInTheList: Boolean;
begin
len := Length(GvVectorialFormats);
FormatInTheList := False;
{ First search for the format in the list }
for i := 0 to len - 1 do
begin
if GvVectorialFormats[i].Format = AFormat then
begin
if GvVectorialFormats[i].ReaderRegistered then
raise Exception.Create('RegisterVectorialReader: Reader class for format ' {+ AFormat} + ' already registered.');
GvVectorialFormats[i].ReaderRegistered := True;
GvVectorialFormats[i].ReaderClass := AReaderClass;
FormatInTheList := True;
Break;
end;
end;
{ If not already in the list, then add it }
if not FormatInTheList then
begin
SetLength(GvVectorialFormats, len + 1);
GvVectorialFormats[len].ReaderClass := AReaderClass;
GvVectorialFormats[len].WriterClass := nil;
GvVectorialFormats[len].ReaderRegistered := True;
GvVectorialFormats[len].WriterRegistered := False;
GvVectorialFormats[len].Format := AFormat;
end;
end;
{@@
Registers a new writer for a format
}
procedure RegisterVectorialWriter(
AWriterClass: TvVectorialWriterClass;
AFormat: TvVectorialFormat);
var
i, len: Integer;
FormatInTheList: Boolean;
begin
len := Length(GvVectorialFormats);
FormatInTheList := False;
{ First search for the format in the list }
for i := 0 to len - 1 do
begin
if GvVectorialFormats[i].Format = AFormat then
begin
if GvVectorialFormats[i].WriterRegistered then
raise Exception.Create('RegisterVectorialWriter: Writer class for format ' + {AFormat +} ' already registered.');
GvVectorialFormats[i].WriterRegistered := True;
GvVectorialFormats[i].WriterClass := AWriterClass;
FormatInTheList := True;
Break;
end;
end;
{ If not already in the list, then add it }
if not FormatInTheList then
begin
SetLength(GvVectorialFormats, len + 1);
GvVectorialFormats[len].ReaderClass := nil;
GvVectorialFormats[len].WriterClass := AWriterClass;
GvVectorialFormats[len].ReaderRegistered := False;
GvVectorialFormats[len].WriterRegistered := True;
GvVectorialFormats[len].Format := AFormat;
end;
end;
{ TsWorksheet }
{@@
Helper method for clearing the records in a spreadsheet.
}
procedure TvVectorialDocument.RemoveCallback(data, arg: pointer);
begin
{ if data <> nil then
begin
ldata := PObject(data);
ldata^.Free;
end;}
end;
{@@
Constructor.
}
constructor TvVectorialDocument.Create;
begin
inherited Create;
FPaths := TFPList.Create;
FTexts := TFPList.Create;
FTmpPath := TPath.Create;
end;
{@@
Destructor.
}
destructor TvVectorialDocument.Destroy;
begin
Clear;
FPaths.Free;
FTexts.Free;
inherited Destroy;
end;
{@@
Clears the list of Vectors and releases their memory.
}
procedure TvVectorialDocument.RemoveAllPaths;
begin
// FPaths.ForEachCall(RemoveCallback, nil);
FPaths.Clear;
end;
procedure TvVectorialDocument.RemoveAllTexts;
begin
// FTexts.ForEachCall(RemoveCallback, nil);
FTexts.Clear;
end;
procedure TvVectorialDocument.AddPath(APath: TPath);
var
lPath: TPath;
Len: Integer;
begin
lPath := TPath.Create;
lPath.Assign(APath);
FPaths.Add(Pointer(lPath));
//WriteLn(':>TvVectorialDocument.AddPath 1 Len = ', Len);
//WriteLn(':>TvVectorialDocument.AddPath 2');
//WriteLn(':>TvVectorialDocument.AddPath 3');
//WriteLn(':>TvVectorialDocument.AddPath 4');
end;
{@@
Starts writing a Path in multiple steps.
Should be followed by zero or more calls to AddPointToPath
and by a call to EndPath to effectively add the data.
@see StartPath, AddPointToPath
}
procedure TvVectorialDocument.StartPath(AX, AY: Double);
var
segment: T2DSegment;
begin
ClearTmpPath();
FTmpPath.Len := 1;
segment := T2DSegment.Create;
segment.SegmentType := stMoveTo;
segment.X := AX;
segment.Y := AY;
FTmpPath.Points := segment;
FTmpPath.PointsEnd := segment;
end;
{@@
Adds one more point to the end of a Path being
writing in multiple steps.
Does nothing if not called between StartPath and EndPath.
Can be called multiple times to add multiple points.
@see StartPath, EndPath
}
procedure TvVectorialDocument.AddLineToPath(AX, AY: Double);
var
segment: T2DSegment;
begin
segment := T2DSegment.Create;
segment.SegmentType := st2DLine;
segment.X := AX;
segment.Y := AY;
AppendSegmentToTmpPath(segment);
end;
procedure TvVectorialDocument.AddLineToPath(AX, AY, AZ: Double);
var
segment: T3DSegment;
begin
segment := T3DSegment.Create;
segment.SegmentType := st3DLine;
segment.X := AX;
segment.Y := AY;
segment.Z := AZ;
AppendSegmentToTmpPath(segment);
end;
{@@
Adds a bezier element to the path. It starts where the previous element ended
and it goes throw the control points [AX1, AY1] and [AX2, AY2] and ends
in [AX3, AY3].
}
procedure TvVectorialDocument.AddBezierToPath(AX1, AY1, AX2, AY2, AX3,
AY3: Double);
var
segment: T2DBezierSegment;
begin
segment := T2DBezierSegment.Create;
segment.SegmentType := st2DBezier;
segment.X := AX3;
segment.Y := AY3;
segment.X2 := AX1;
segment.Y2 := AY1;
segment.X3 := AX2;
segment.Y3 := AY2;
AppendSegmentToTmpPath(segment);
end;
procedure TvVectorialDocument.AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2,
AX3, AY3, AZ3: Double);
var
segment: T3DBezierSegment;
begin
segment := T3DBezierSegment.Create;
segment.SegmentType := st3DBezier;
segment.X := AX3;
segment.Y := AY3;
segment.Z := AZ3;
segment.X2 := AX1;
segment.Y2 := AY1;
segment.Z2 := AZ1;
segment.X3 := AX2;
segment.Y3 := AY2;
segment.Z3 := AZ2;
AppendSegmentToTmpPath(segment);
end;
{@@
Finishes writing a Path, which was created in multiple
steps using StartPath and AddPointToPath,
to the document.
Does nothing if there wasn't a previous correspondent call to
StartPath.
@see StartPath, AddPointToPath
}
procedure TvVectorialDocument.EndPath();
begin
if FTmPPath.Len = 0 then Exit;
AddPath(FTmPPath);
ClearTmpPath();
end;
procedure TvVectorialDocument.AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string);
var
lText: TvText;
begin
lText := TvText.Create;
lText.Value := AText;
lText.X := AX;
lText.Y := AY;
lText.Z := AZ;
lText.FontName := FontName;
lText.FontSize := FontSize;
FTexts.Add(lText);
end;
procedure TvVectorialDocument.AddText(AX, AY, AZ: Double; AStr: utf8string);
begin
AddText(AX, AY, AZ, '', 10, AStr);
end;
{@@
Convenience method which creates the correct
writer object for a given vector graphics document format.
}
function TvVectorialDocument.CreateVectorialWriter(AFormat: TvVectorialFormat): TvCustomVectorialWriter;
var
i: Integer;
begin
Result := nil;
for i := 0 to Length(GvVectorialFormats) - 1 do
if GvVectorialFormats[i].Format = AFormat then
begin
Result := GvVectorialFormats[i].WriterClass.Create;
Break;
end;
if Result = nil then raise Exception.Create('Unsuported vector graphics format.');
end;
{@@
Convenience method which creates the correct
reader object for a given vector graphics document format.
}
function TvVectorialDocument.CreateVectorialReader(AFormat: TvVectorialFormat): TvCustomVectorialReader;
var
i: Integer;
begin
Result := nil;
for i := 0 to Length(GvVectorialFormats) - 1 do
if GvVectorialFormats[i].Format = AFormat then
begin
Result := GvVectorialFormats[i].ReaderClass.Create;
Break;
end;
if Result = nil then raise Exception.Create('Unsuported vector graphics format.');
end;
procedure TvVectorialDocument.ClearTmpPath();
var
segment, oldsegment: TPathSegment;
begin
// segment := FTmpPath.Points;
// Don't free segments, because they are used when the path is added
// while segment <> nil do
// begin
// oldsegment := segment;
// segment := segment^.Next;
// oldsegment^.Free;
// end;
FTmpPath.Points := nil;
FTmpPath.PointsEnd := nil;
FTmpPath.Len := 0;
end;
procedure TvVectorialDocument.AppendSegmentToTmpPath(ASegment: TPathSegment);
var
L: Integer;
begin
if FTmpPath.PointsEnd = nil then
Exception.Create('[TvVectorialDocument.AppendSegmentToTmpPath]' + Str_Error_Nil_Path);
L := FTmpPath.Len;
Inc(FTmpPath.Len);
// Adds the element to the end of the list
FTmpPath.PointsEnd.Next := ASegment;
ASegment.Previous := FTmpPath.PointsEnd;
FTmpPath.PointsEnd := ASegment;
end;
{@@
Writes the document to a file.
If the file doesn't exist, it will be created.
}
procedure TvVectorialDocument.WriteToFile(AFileName: string; AFormat: TvVectorialFormat);
var
AWriter: TvCustomVectorialWriter;
begin
AWriter := CreateVectorialWriter(AFormat);
try
AWriter.WriteToFile(AFileName, Self);
finally
AWriter.Free;
end;
end;
{@@
Writes the document to a stream
}
procedure TvVectorialDocument.WriteToStream(AStream: TStream; AFormat: TvVectorialFormat);
var
AWriter: TvCustomVectorialWriter;
begin
AWriter := CreateVectorialWriter(AFormat);
try
AWriter.WriteToStream(AStream, Self);
finally
AWriter.Free;
end;
end;
procedure TvVectorialDocument.WriteToStrings(AStrings: TStrings;
AFormat: TvVectorialFormat);
var
AWriter: TvCustomVectorialWriter;
begin
AWriter := CreateVectorialWriter(AFormat);
try
AWriter.WriteToStrings(AStrings, Self);
finally
AWriter.Free;
end;
end;
{@@
Reads the document from a file.
Any current contents will be removed.
}
procedure TvVectorialDocument.ReadFromFile(AFileName: string;
AFormat: TvVectorialFormat);
var
AReader: TvCustomVectorialReader;
begin
Self.Clear;
AReader := CreateVectorialReader(AFormat);
try
AReader.ReadFromFile(AFileName, Self);
finally
AReader.Free;
end;
end;
{@@
Reads the document from a stream.
Any current contents will be removed.
}
procedure TvVectorialDocument.ReadFromStream(AStream: TStream;
AFormat: TvVectorialFormat);
var
AReader: TvCustomVectorialReader;
begin
Self.Clear;
AReader := CreateVectorialReader(AFormat);
try
AReader.ReadFromStream(AStream, Self);
finally
AReader.Free;
end;
end;
procedure TvVectorialDocument.ReadFromStrings(AStrings: TStrings;
AFormat: TvVectorialFormat);
var
AReader: TvCustomVectorialReader;
begin
Self.Clear;
AReader := CreateVectorialReader(AFormat);
try
AReader.ReadFromStrings(AStrings, Self);
finally
AReader.Free;
end;
end;
class function TvVectorialDocument.GetFormatFromExtension(AFileName: string
): TvVectorialFormat;
var
lExt: string;
begin
lExt := ExtractFileExt(AFileName);
if AnsiCompareText(lExt, STR_PDF_EXTENSION) = 0 then Result := vfPDF
else if AnsiCompareText(lExt, STR_POSTSCRIPT_EXTENSION) = 0 then Result := vfPostScript
else if AnsiCompareText(lExt, STR_SVG_EXTENSION) = 0 then Result := vfSVG
else if AnsiCompareText(lExt, STR_CORELDRAW_EXTENSION) = 0 then Result := vfCorelDrawCDR
else if AnsiCompareText(lExt, STR_WINMETAFILE_EXTENSION) = 0 then Result := vfWindowsMetafileWMF
else
raise Exception.Create('TvVectorialDocument.GetFormatFromExtension: The extension (' + lExt + ') doesn''t match any supported formats.');
end;
function TvVectorialDocument.GetDetailedFileFormat(): string;
begin
end;
function TvVectorialDocument.GetPath(ANum: Cardinal): TPath;
begin
if ANum >= FPaths.Count then raise Exception.Create('TvVectorialDocument.GetPath: Path number out of bounds');
if FPaths.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetPath: Invalid Path number');
Result := TPath(FPaths.Items[ANum]);
end;
function TvVectorialDocument.GetPathCount: Integer;
begin
Result := FPaths.Count;
end;
function TvVectorialDocument.GetText(ANum: Cardinal): TvText;
begin
if ANum >= FTexts.Count then raise Exception.Create('TvVectorialDocument.GetText: Text number out of bounds');
if FTexts.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetText: Invalid Text number');
Result := TvText(FTexts.Items[ANum]);
end;
function TvVectorialDocument.GetTextCount: Integer;
begin
Result := FTexts.Count;
end;
{@@
Clears all data in the document
}
procedure TvVectorialDocument.Clear;
begin
RemoveAllPaths();
RemoveAllTexts();
end;
{ TvCustomVectorialReader }
procedure TvCustomVectorialReader.ReadFromFile(AFileName: string; AData: TvVectorialDocument);
var
FileStream: TFileStream;
begin
FileStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
try
ReadFromStream(FileStream, AData);
finally
FileStream.Free;
end;
end;
procedure TvCustomVectorialReader.ReadFromStream(AStream: TStream;
AData: TvVectorialDocument);
var
AStringStream: TStringStream;
AStrings: TStringList;
begin
AStringStream := TStringStream.Create('');
AStrings := TStringList.Create;
try
AStringStream.CopyFrom(AStream, AStream.Size);
AStringStream.Seek(0, soFromBeginning);
AStrings.Text := AStringStream.DataString;
ReadFromStrings(AStrings, AData);
finally
AStringStream.Free;
AStrings.Free;
end;
end;
procedure TvCustomVectorialReader.ReadFromStrings(AStrings: TStrings;
AData: TvVectorialDocument);
var
AStringStream: TStringStream;
begin
AStringStream := TStringStream.Create('');
try
AStringStream.WriteString(AStrings.Text);
AStringStream.Seek(0, soFromBeginning);
ReadFromStream(AStringStream, AData);
finally
AStringStream.Free;
end;
end;
{ TsCustomSpreadWriter }
{@@
Default file writting method.
Opens the file and calls WriteToStream
@param AFileName The output file name.
If the file already exists it will be replaced.
@param AData The Workbook to be saved.
@see TsWorkbook
}
procedure TvCustomVectorialWriter.WriteToFile(AFileName: string; AData: TvVectorialDocument);
var
OutputFile: TFileStream;
begin
OutputFile := TFileStream.Create(AFileName, fmCreate or fmOpenWrite);
try
WriteToStream(OutputFile, AData);
finally
OutputFile.Free;
end;
end;
{@@
The default stream writer just uses WriteToStrings
}
procedure TvCustomVectorialWriter.WriteToStream(AStream: TStream;
AData: TvVectorialDocument);
var
lStringList: TStringList;
begin
lStringList := TStringList.Create;
try
WriteToStrings(lStringList, AData);
lStringList.SaveToStream(AStream);
finally
lStringList.Free;
end;
end;
procedure TvCustomVectorialWriter.WriteToStrings(AStrings: TStrings;
AData: TvVectorialDocument);
begin
end;
{ TPath }
procedure TPath.Assign(APath: TPath);
begin
Len := APath.Len;
Points := APath.Points;
PointsEnd := APath.PointsEnd;
CurPoint := APath.CurPoint;
end;
function TPath.Count(): TPathSegment;
begin
end;
procedure TPath.PrepareForSequentialReading;
begin
CurPoint := nil;
end;
function TPath.Next(): TPathSegment;
begin
if CurPoint = nil then Result := Points
else Result := CurPoint.Next;
CurPoint := Result;
end;
finalization
SetLength(GvVectorialFormats, 0);
end.

View File

@ -0,0 +1,109 @@
unit fpvtocanvas;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
fpcanvas,
fpvectorial;
procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument; ADest: TFPCustomCanvas;
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0);
implementation
{@@
This function draws a FPVectorial vectorial image to a TFPCustomCanvas
descendent, such as TCanvas from the LCL.
Be careful that by default this routine does not execute coordinate transformations,
and that FPVectorial works with a start point in the bottom-left corner, with
the X growing to the right and the Y growing to the top. This will result in
an image in TFPCustomCanvas mirrored in the Y axis in relation with the document
as seen in a PDF viewer, for example. This can be easily changed with the
provided parameters. To have the standard view of an image viewer one could
use this function like this:
DrawFPVectorialToCanvas(ASource, ADest, 0, ASource.Height, 1.0, -1.0);
}
procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument; ADest: TFPCustomCanvas;
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0);
var
i, j, k: Integer;
PosX, PosY: Integer; // Not modified by ADestX, etc
CurSegment: TPathSegment;
Cur2DSegment: T2DSegment absolute CurSegment;
Cur2DBSegment: T2DBezierSegment absolute CurSegment;
// For bezier
CurX, CurY: Integer; // Not modified by ADestX, etc
CurveLength: Integer;
t: Double;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':>DrawFPVectorialToCanvas');
{$endif}
PosX := 0;
PosY := 0;
ADest.MoveTo(ADestX, ADestY);
for i := 0 to ASource.PathCount - 1 do
begin
//WriteLn('i = ', i);
ASource.Paths[i].PrepareForSequentialReading;
for j := 0 to ASource.Paths[i].Len - 1 do
begin
//WriteLn('j = ', j);
CurSegment := TPathSegment(ASource.Paths[i].Next());
case CurSegment.SegmentType of
stMoveTo:
begin
ADest.MoveTo(
Round(ADestX + AMulX * Cur2DSegment.X),
Round(ADestY + AMulY * Cur2DSegment.Y)
);
end;
st2DLine, st3DLine:
begin
ADest.LineTo(
Round(ADestX + AMulX * Cur2DSegment.X),
Round(ADestY + AMulY * Cur2DSegment.Y)
);
end;
{ To draw a bezier we need to divide the interval in parts and make
lines between this parts }
st2DBezier, st3DBezier:
begin
CurveLength :=
Round(sqrt(sqr(Cur2DBSegment.X3 - PosX) + sqr(Cur2DBSegment.Y3 - PosY))) +
Round(sqrt(sqr(Cur2DBSegment.X2 - Cur2DBSegment.X3) + sqr(Cur2DBSegment.Y2 - Cur2DBSegment.Y3))) +
Round(sqrt(sqr(Cur2DBSegment.X - Cur2DBSegment.X3) + sqr(Cur2DBSegment.Y - Cur2DBSegment.Y3)));
for k := 1 to CurveLength do
begin
t := k / CurveLength;
CurX := Round(sqr(1 - t) * (1 - t) * PosX + 3 * t * sqr(1 - t) * Cur2DBSegment.X2 + 3 * t * t * (1 - t) * Cur2DBSegment.X3 + t * t * t * Cur2DBSegment.X);
CurY := Round(sqr(1 - t) * (1 - t) * PosY + 3 * t * sqr(1 - t) * Cur2DBSegment.Y2 + 3 * t * t * (1 - t) * Cur2DBSegment.Y3 + t * t * t * Cur2DBSegment.Y);
ADest.LineTo(
Round(ADestX + AMulX * CurX),
Round(ADestY + AMulY * CurY));
end;
PosX := Round(Cur2DBSegment.X);
PosY := Round(Cur2DBSegment.Y);
end;
end;
end;
end;
{$ifdef FPVECTORIALDEBUG}
WriteLn(':<DrawFPVectorialToCanvas');
{$endif}
end;
end.

View File

@ -0,0 +1,265 @@
{
pdfvectorialreader.pas
Reads the vectorial information form a PDF file
PDF file format specification obtained from:
ADOBE SYSTEMS INCORPORATED. PDF Reference: Adobe®
Portable Document Format. San Jose, 2006. (Sixth edition).
AUTHORS: Felipe Monteiro de Carvalho
Pedro Sol Pegorini L de Lima
}
unit pdfvectorialreader;
{$ifdef fpc}
{$mode delphi}
{$endif}
interface
uses
Classes, SysUtils,
pdfvrlexico, pdfvrsintatico, pdfvrsemantico, avisozlib,
fpvectorial;
type
{ TvPDFVectorialReader }
TvPDFVectorialReader = class(TvCustomVectorialReader)
private
procedure WriteStringToStream(AStream: TStream; AString: string);
public
{ public to allow uncompressing PDFs independently }
function getFirstPage(AInput: TStream; AOutput: TStream):PageHeader;
procedure unzipPage(AInput: TStream; AOutput: TStream);
procedure translatePage(AInput: TStream; AData: TvVectorialDocument;
APageHeader: PageHeader);
{ General reading methods }
procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
end;
implementation
{ TvPDFVectorialReader }
procedure TvPDFVectorialReader.WriteStringToStream(AStream: TStream;
AString: string);
begin
AStream.WriteBuffer(AString[1], Length(AString));
end;
function TvPDFVectorialReader.getFirstPage(AInput: TStream; AOutput: TStream): PageHeader;
var
mytoken: Token;
myAnLexicoPage: AnLexico;
myAnLexicoContents: AnLexico;
myAnSintaticoPage: AnSintaticoPage;
myAnSintaticoContents: AnSintaticoPageContents;
AInput2: TStream;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> TvPDFVectorialReader.getFirstPage');
{$endif}
AInput2 := TMemoryStream.Create;
AInput2.Size := AInput.Size;
AInput2.CopyFrom(AInput, AInput.Size);
AInput.Seek(0, soFromBeginning);
AInput2.Seek(0, soFromBeginning);
myAnLexicoPage := AnLexico.Create;
myAnLexicoPage.Doc := AInput;
myAnLexicoPage.bytesRemaining:= myAnLexicoPage.Doc.Size;
myAnSintaticoPage := AnSintaticoPage.Create;
// find first page
while ((myAnSintaticoPage.pageFound <> true) and
(myAnLexicoPage.bytesRemaining > 0)) do
begin
mytoken := myAnLexicoPage.getToken();
myAnSintaticoPage.automata(mytoken);
end;
if (myAnSintaticoPage.pageFound = false) then
begin
raise Exception.Create('ERROR: Arquivo corrompido.');
Halt(1);
end;
AInput.Seek(0, soFromBeginning);
myAnLexicoContents := AnLexico.Create;
myAnLexicoContents.Doc := AInput;
myAnLexicoContents.bytesRemaining:= myAnLexicoContents.Doc.Size;
myAnSintaticoContents := AnSintaticoPageContents.Create;
// gathering information of the first page
myAnSintaticoContents.obj1:=myAnSintaticoPage.obj1;
myAnSintaticoContents.obj2:=myAnSintaticoPage.obj2;
//find first page contents
while ((myAnSintaticoContents.contentsFound <> true) and
(myAnLexicoContents.bytesRemaining > 0)) do
begin
mytoken := myAnLexicoContents.getToken();
myAnSintaticoContents.automata(mytoken, AInput2);
end;
if (myAnSintaticoContents.contentsFound = false) then
begin
raise Exception.Create('ERROR: Arquivo corrompido.');
Halt(1);
end;
// gathering information of the first page
myAnLexicoContents.bytesRemaining:=myAnSintaticoContents.h.page_length;
// write file with content just from the first page
while (myAnLexicoContents.bytesRemaining > 0) do
begin
mytoken := myAnLexicoContents.getPageToken();
WriteStringToStream(AOutput, mytoken.token_string);
end;
Result:=myAnSintaticoContents.h;
{$ifdef FPVECTORIALDEBUG}
WriteLn(':< TvPDFVectorialReader.getFirstPage');
{$endif}
// AInput2.Free;
end;
procedure TvPDFVectorialReader.unzipPage(AInput: TStream; AOutput: TStream);
var
compr, uncompr: Pbyte;
comprLen, uncomprLen: LongInt;
myDecode: decode;
BufStr: string;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> TvPDFVectorialReader.unzipPage');
{$endif}
myDecode := Decode.Create;
comprLen := 10000 * SizeOf(Integer); // don't overflow
uncomprLen := comprLen;
GetMem(compr, comprLen);
GetMem(uncompr, uncomprLen);
if (compr = NIL) or (uncompr = NIL) then
myDecode.EXIT_ERR('Out of memory');
(* compr and uncompr are cleared to avoid reading uninitialized
* data and to ensure that uncompr compresses well.
*)
FillChar(compr^, comprLen, 0);
FillChar(uncompr^, uncomprLen, 0);
AInput.Read(compr^, comprLen);
BufStr := string(myDecode.test_inflate(compr, comprLen, uncompr, uncomprLen));
WriteStringToStream(AOutput, BufStr);
FreeMem(compr, comprLen);
FreeMem(uncompr, uncomprLen);
{$ifdef FPVECTORIALDEBUG}
WriteLn(':< TvPDFVectorialReader.unzipPage');
{$endif}
end;
procedure TvPDFVectorialReader.translatePage(AInput: TStream;
AData: TvVectorialDocument; APageHeader: PageHeader);
var
myAnLexico: AnLexico;
myAnSintaticoCommand: AnSintaticoCommand;
myAnSemantico: AnSemantico;
mytoken: Token;
c: Command;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> TvPDFVectorialReader.translatePage');
{$endif}
// initialize data main
myAnLexico := AnLexico.Create;
myAnLexico.Doc := AInput;
myAnLexico.bytesRemaining:= myAnLexico.Doc.Size;
myAnSintaticoCommand := AnSintaticoCommand.Create;
myAnSemantico := AnSemantico.Create;
// initialize machine
myAnSemantico.startMachine();
while (myAnLexico.bytesRemaining > 0) do
begin
mytoken := myAnLexico.getToken();
c:=myAnSintaticoCommand.automata(mytoken);
if (myAnSintaticoCommand.Codigo = true) then
myAnSemantico.generate(c, AData);
end;
// end machine
myAnSemantico.endMachine();
end;
procedure TvPDFVectorialReader.ReadFromStream(AStream: TStream;
AData: TvVectorialDocument);
var
APageHeader: PageHeader;
APageStream, AUnzipStream: TStream;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> TvPDFVectorialReader.ReadFromStream');
{$endif}
APageStream := TMemoryStream.Create;
AUnzipStream := TMemoryStream.Create;
// get first page
APageHeader := getFirstPage(AStream, APageStream);
// unzip page
if (APageHeader.flate_decode = true) then
begin
APageStream.Seek(0, soFromBeginning);
unzipPage(APageStream, AUnzipStream);
// translate page to doc data
AUnzipStream.Seek(0, soFromBeginning);
translatePage(AUnzipStream, AData, APageHeader);
end
else
begin
// translate page to doc data
APageStream.Seek(0, soFromBeginning);
translatePage(APageStream, AData, APageHeader);
end;
APageStream.Free;
AUnzipStream.Free;
//ShowMessage('Sucesso!');
{$ifdef FPVECTORIALDEBUG}
WriteLn(':< TvPDFVectorialReader.ReadFromStream');
WriteLn('Sucesso!');
{$endif}
end;
{*******************************************************************
* Initialization section
*
* Registers this reader / writer on fpVectorial
*
*******************************************************************}
initialization
RegisterVectorialReader(TvPDFVectorialReader, vfPDF);
end.

View File

@ -0,0 +1,113 @@
unit pdfvrlexico;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
type
Token = record
tipo: Int64;
token_string: String;
end;
TPDFCommandCode = (cc_NONE, cc_m_START_PATH, cc_l_ADD_LINE_TO_PATH,
cc_H_CLOSE_PATH, cc_S_END_PATH, cc_hS_CLOSE_AND_END_PATH,
cc_c_BEZIER_TO_X_Y_USING_X2_Y2_AND_X3_Y3,
cc_v_BEZIER_TO_X_Y_USING_CURRENT_POS_AND_X2_Y2,
cc_y_BEZIER_TO_X_Y_USING_X_Y_AND_X2_Y2,
cc_CONCATENATE_MATRIX,cc_RESTORE_MATRIX);
Command = record
cord_x3: String;
cord_y3: String;
cord_x2: String;
cord_y2: String;
cord_x: String;
cord_y: String;
my_operator: String;
code: TPDFCommandCode;
end;
PageHeader = record
page_length: Int64;
flate_decode: Boolean;
end;
AnLexico = class
public
Doc: TStream;
bytesRemaining: Int64;
constructor Create();
function getToken(): Token;
function getPageToken(): Token;
end;
implementation
function AnLexico.getToken(): Token;
var
t: Byte;
mytoken: Token;
begin
mytoken.tipo := 0;
while( bytesRemaining > 0 ) do
begin
t := Doc.ReadByte();
bytesRemaining := bytesRemaining - 1;
// numbers or points or minus
if((((t >= 48) and (t <= 57)) or (t = 46 ) or (t = 45)) and
((mytoken.tipo = 1) or (mytoken.tipo = 0))) then
begin
mytoken.token_string := mytoken.token_string + char(t);
mytoken.tipo:=1;
end
else if(((t >= 65) and (t <= 90)) or ((t >= 97) and (t <= 122)) // letters
or (t = 42) // *
and ((mytoken.tipo = 2) or (mytoken.tipo = 0))) then
begin
mytoken.token_string := mytoken.token_string + char(t);
mytoken.tipo:=2;
end
else // everything else
begin
if (mytoken.tipo <> 0) then
begin
// solve CorelDraw problem after "stream"
if ((t=13) and (bytesRemaining>0)) then
begin
t := Doc.ReadByte();
bytesRemaining:=bytesRemaining-1;
end;
Result := mytoken;
Exit;
end;
end;
end;
Result := mytoken;
end;
function AnLexico.getPageToken(): Token;
var
t: Byte;
mytoken: Token;
begin
mytoken.tipo := 0;
if (bytesRemaining > 0) then
begin
t := Doc.ReadByte;
mytoken.token_string:=char(t);
bytesRemaining := bytesRemaining - 1;
end;
Result := mytoken;
end;
constructor AnLexico.Create();
begin
inherited Create;
end;
end.

View File

@ -0,0 +1,244 @@
unit pdfvrsemantico;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, pdfvrlexico, fpvectorial;
type
{ AnSemantico }
AnSemantico = class
public
FPointSeparator, FCommaSeparator: TFormatSettings;
close_path_x: String;
close_path_y: String;
cm_a, cm_b, cm_c, cm_d, cm_e, cm_f: Real; // coordinate spaces constants
function StringToFloat(AStr: string): Double;
function generate(c: Command; AData: TvVectorialDocument): String;
function convert(x: String; y: String; Axis: Char): String;
function startMachine(): String;
function endMachine(): String;
constructor Create;
end;
implementation
{ PDF doesn't seam very consistent when it comes to using commas or
points as decimal separator, so we just try both }
function AnSemantico.StringToFloat(AStr: string): Double;
begin
if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)
else Result := StrToFloat(AStr, FCommaSeparator);
end;
function AnSemantico.generate(c: Command; AData: TvVectorialDocument): String;
var
enter_line : String;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.generate');
{$endif}
enter_line:= LineEnding; //chr(13) + chr(10); // CR and LF
if ((c.code = cc_H_CLOSE_PATH) or (c.code = cc_hS_CLOSE_AND_END_PATH)) then // command h or s
begin
c.cord_x:=close_path_x;
c.cord_y:=close_path_y;
end;
if ((c.code <> cc_H_CLOSE_PATH) and (c.code <> cc_hS_CLOSE_AND_END_PATH)) then // close path already converted
begin
if ((c.code = cc_m_START_PATH) or (c.code = cc_l_ADD_LINE_TO_PATH)) then
begin
//WriteLn(':: anSemantico.generate convert code ', Integer(c.code));
c.cord_x := convert(c.cord_x,c.cord_y,'x');
c.cord_y := convert(c.cord_x,c.cord_y,'y');
end;
if ((c.code = cc_c_BEZIER_TO_X_Y_USING_X2_Y2_AND_X3_Y3)) then
begin
//WriteLn(':: anSemantico.generate convert code ', Integer(c.code));
c.cord_x := convert(c.cord_x,c.cord_y,'x');
c.cord_y := convert(c.cord_x,c.cord_y,'y');
c.cord_x2 := convert(c.cord_x2,c.cord_y2,'x');
c.cord_y2 := convert(c.cord_x2,c.cord_y2,'y');
c.cord_x3 := convert(c.cord_x3,c.cord_y3,'x');
c.cord_y3 := convert(c.cord_x3,c.cord_y3,'y');
end;
end;
case c.code of
cc_m_START_PATH: // command m
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.generate Estado 1 EndPath StartPath');
{$endif}
// Result:='G01' + ' ' + 'X' + c.cord_x + ' ' + 'Y' + c.cord_y + enter_line +
// 'G01 Z50 // Abaixa a cabeça de gravação';
// Correcao para programas de desenho que geram um novo inicio no
// fim do desenho, terminamos qualquer desenho inacabado
AData.EndPath();
AData.StartPath(StringToFloat(c.cord_x), StringToFloat(c.cord_y));
close_path_x:=c.cord_x;
close_path_y:=c.cord_y;
end;
cc_l_ADD_LINE_TO_PATH: // command l
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.generate Estado 2 AddPointToPath');
{$endif}
// Result:='G01' + ' ' + 'X' + c.cord_x + ' ' + 'Y' + c.cord_y;
AData.AddLineToPath(StringToFloat(c.cord_x), StringToFloat(c.cord_y));
end;
cc_h_CLOSE_PATH: // command h
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.generate Estado 3 AddPointToPath');
{$endif}
//Result:='G01' + ' ' + 'X' + c.cord_x + ' ' + 'Y' + c.cord_y;
AData.AddLineToPath(StringToFloat(c.cord_x), StringToFloat(c.cord_y));
end;
cc_S_END_PATH: // command S
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.generate Estado 4 EndPath');
{$endif}
// Result:='G01 Z0 // Sobe a cabeça de gravação' + enter_line;
AData.EndPath();
end;
cc_hS_CLOSE_AND_END_PATH: // command s
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.generate Estado 5 AddPoint EndPath');
{$endif}
//Result:='G01' + ' ' + 'X' + c.cord_x + ' ' + 'Y' + c.cord_y + enter_line
// +'G01 Z0 // Sobe a cabeça de gravação' + enter_line;
AData.AddLineToPath(StringToFloat(c.cord_x), StringToFloat(c.cord_y));
AData.EndPath();
end;
cc_c_BEZIER_TO_X_Y_USING_X2_Y2_AND_X3_Y3: // command c
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.generate Estado 6 Bezier');
{$endif}
//Result:='G01' + ' ' + 'X' + c.cord_x + ' ' + 'Y' + c.cord_y + enter_line
// +'G01 Z0 // Sobe a cabeça de gravação' + enter_line;
AData.AddBezierToPath(
StringToFloat(c.cord_x3), StringToFloat(c.cord_y3),
StringToFloat(c.cord_x2), StringToFloat(c.cord_y2),
StringToFloat(c.cord_x), StringToFloat(c.cord_y)
);
end;
cc_CONCATENATE_MATRIX: // command cm
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.cc_CONCATENATE_MATRIX');
{$endif}
cm_a := StringToFloat(c.cord_x3);
cm_b := StringToFloat(c.cord_y3);
cm_c := StringToFloat(c.cord_x2);
cm_d := StringToFloat(c.cord_y2);
cm_e := StringToFloat(c.cord_x);
cm_f := StringToFloat(c.cord_y);
end;
cc_RESTORE_MATRIX: // command Q
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.cc_RESTORE_MATRIX');
{$endif}
cm_a:=1;
cm_b:=0;
cm_c:=0;
cm_d:=1;
cm_e:=0;
cm_f:=0;
end;
else
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.generate Estado ELSE');
{$endif}
Result:=c.my_operator;
end;
end;
function AnSemantico.convert(x: String; y: String; Axis: Char): String;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.convert');
{$endif}
// convert from 1/72 inch to milimeters and change axis if necessary
if (Axis = 'y') then
begin
// y' = b * x + d * y + f
Result:=FloatToStr((cm_b*StringToFloat(x)+cm_d*StringToFloat(y)+cm_f)*(25.40/72));
end
else
// Axis = 'x'
begin
// x' = a * x + c * y + e
Result:=FloatToStr((cm_a*StringToFloat(x)+cm_c*StringToFloat(y)+cm_e)*(25.40/72));
end;
end;
function AnSemantico.startMachine(): String;
var
enter_line : String;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.startMachine');
{$endif}
enter_line:=chr(13) + chr(10); // CR and LF
Result:='M216 // Ligar monitor de carga' + enter_line +
'G28 // Ir rapidamente para posição inicial' + enter_line +
'G00' + enter_line;
end;
function AnSemantico.endMachine(): String;
var
enter_line : String;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSemantico.endMachine');
{$endif}
enter_line:=chr(13) + chr(10); // CR and LF
Result:='M30 // Parar o programa e retornar para posição inicial' + enter_line +
'M215 // Desligar monitor de carga' + enter_line;
end;
constructor AnSemantico.Create;
begin
inherited Create;
cm_a:=1;
cm_b:=0;
cm_c:=0;
cm_d:=1;
cm_e:=0;
cm_f:=0;
// Format seetings to convert a string to a float
FPointSeparator := DefaultFormatSettings;
FPointSeparator.DecimalSeparator := '.';
FPointSeparator.ThousandSeparator := '#';// disable the thousand separator
FCommaSeparator := DefaultFormatSettings;
FCommaSeparator.DecimalSeparator := ',';
FCommaSeparator.ThousandSeparator := '#';// disable the thousand separator
end;
end.

View File

@ -0,0 +1,628 @@
unit pdfvrsintatico;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, pdfvrlexico;
type
AnSintaticoPage = class
public
Estado: Int64;
obj1,obj2 : String;
pageFound: Boolean;
constructor Create;
procedure automata(t: Token);
end;
AnSintaticoPageContents = class
public
Estado: Int64;
obj1,obj2 : String;
len_obj1,len_obj2: String;
contentsFound: Boolean;
h: PageHeader;
constructor Create;
procedure automata(t: Token; Input: TStream);
end;
AnSintaticoCommand = class
public
Estado: Int64;
Codigo: Boolean;
c: Command;
constructor Create;
function automata(t: Token):Command;
end;
AnSintaticoLength = class
public
Estado: Int64;
len_obj1,len_obj2: String;
page_length : Int64;
lenghtFound: Boolean;
constructor Create;
procedure automata(t: Token);
end;
implementation
procedure AnSintaticoPage.automata(t: Token);
begin
case Estado of
1:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPage.automata Estado 1');
{$endif}
if(t.token_string = 'Type') then
begin
Estado := 2;
end
else
begin
Estado := 1;
end;
end;
2:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPage.automata Estado 2');
{$endif}
if(t.token_string = 'Page') then
begin
Estado := 3;
end
else
begin
Estado := 1;
end;
end;
3:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPage.automata Estado 3');
{$endif}
if(t.token_string = 'Contents') then
begin
Estado := 4;
end
else
begin
Estado := 3;
end;
end;
4:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPage.automata Estado 4');
{$endif}
if(t.tipo = 1) then // numbers 1
begin
obj1:=t.token_string;
Estado := 5;
end
else
begin
raise Exception.Create('ERROR: Arquivo corrompido.');
Halt(1);
end;
end;
5:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPage.automata Estado 5');
{$endif}
if(t.tipo = 1) then // numbers 2
begin
obj2:=t.token_string;
Estado := 6; // symbolic state
pageFound := true;
end
else
begin
raise Exception.Create('ERROR: Arquivo corrompido.');
Halt(1);
end;
end;
else
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPage.automata Estado ELSE');
{$endif}
Estado := 1;
end;
end;
procedure AnSintaticoPageContents.automata(t: Token; Input: TStream);
var
myAnLexicoLength: AnLexico;
myAnSintaticoLength: AnSintaticoLength;
mytokenLength: Token;
begin
case Estado of
1:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPageContents.automata Estado 1');
{$endif}
if(t.token_string = obj1) then
begin
Estado := 2;
end
else
begin
Estado := 1;
end;
end;
2:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPageContents.automata Estado 2');
{$endif}
if(t.token_string = obj2) then
begin
Estado := 3;
end
else
begin
Estado := 1;
end;
end;
3:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPageContents.automata Estado 3');
{$endif}
if(t.token_string = 'obj') then
begin
Estado := 4;
end
else
begin
Estado := 1;
end;
end;
4:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPageContents.automata Estado 4');
{$endif}
if(t.token_string = 'Length') then
begin
Estado := 5;
end
else if (t.token_string = 'Filter') then
begin
Estado := 7;
end
else
begin
Estado := 4;
end;
end;
5:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPageContents.automata Estado 5');
{$endif}
if(t.tipo = 1) then
begin
h.page_length := StrToInt(t.token_string);
len_obj1:=t.token_string;
Estado := 6;
end
else
begin
raise Exception.Create('ERROR: Arquivo corrompido.');
Halt(1);
end;
end;
6:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPageContents.automata Estado 6');
{$endif}
if(t.token_string = 'Filter') then
begin
Estado := 7;
end
else if (t.token_string = 'stream') then
begin
contentsFound := true;
Estado := 9; // symbolic state
end
else if (t.tipo = 1) then
begin
len_obj2:=t.token_string;
myAnLexicoLength := AnLexico.Create;
myAnLexicoLength.Doc := Input;
myAnLexicoLength.bytesRemaining:= myAnLexicoLength.Doc.Size;
myAnSintaticoLength := AnSintaticoLength.Create;
myAnSintaticoLength.len_obj1:=len_obj1;
myAnSintaticoLength.len_obj2:=len_obj2;
while ((myAnSintaticoLength.lenghtFound <> true) and
(myAnLexicoLength.bytesRemaining > 0)) do
begin
mytokenLength := myAnLexicoLength.getToken();
myAnSintaticoLength.automata(mytokenLength);
end;
if (myAnSintaticoLength.lenghtFound = false) then
begin
raise Exception.Create('ERROR: Arquivo corrompido.');
Halt(1);
end;
h.page_length:=myAnSintaticoLength.page_length;
myAnLexicoLength.Doc.Destroy;
Estado := 6;
end
else
begin
Estado := 6;
end;
end;
7:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPageContents.automata Estado 7');
{$endif}
if(t.token_string = 'FlateDecode') then
begin
h.flate_decode := true;
Estado := 8;
end
else
begin
raise Exception.Create('ERROR: Encodificacao nao suportada.');
Halt(1);
end;
end;
8:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPageContents.automata Estado 8');
{$endif}
if(t.token_string = 'stream') then
begin
contentsFound := true;
Estado := 9; // symbolic state
end
else if (t.token_string = 'Length') then
begin
Estado := 5;
end
else
begin
Estado := 8;
end;
end;
else
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoPageContents.automata Estado ELSE');
{$endif}
Estado := 1;
end;
end;
procedure AnSintaticoLength.automata(t: Token);
begin
case Estado of
1:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoLength.automata Estado 1');
{$endif}
if(t.token_string = len_obj1) then
begin
Estado := 2;
end
else
begin
Estado := 1;
end;
end;
2:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoLength.automata Estado 2');
{$endif}
if(t.token_string = len_obj2) then
begin
Estado := 3;
end
else
begin
Estado := 1;
end;
end;
3:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoLength.automata Estado 3');
{$endif}
if(t.token_string = 'obj') then
begin
Estado := 4;
end
else
begin
Estado := 1;
end;
end;
4:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoLength.automata Estado 4 Length: ', StrToInt(t.token_string));
{$endif}
if(t.tipo = 1) then
begin
page_length:=StrToInt(t.token_string);
lenghtFound:=true;
Estado := 5; // symbolic state
end
else
begin
raise Exception.Create('ERROR: Arquivo corrompido.');
Halt(1);
end;
end;
else
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoLength.automata Estado ELSE');
{$endif}
Estado := 1;
end;
end;
function AnSintaticoCommand.automata(t: Token):Command;
begin
c.cord_x3 := c.cord_y3;
c.cord_y3 := c.cord_x2;
c.cord_x2 := c.cord_y2;
c.cord_y2 := c.cord_x;
c.cord_x := c.cord_y;
c.cord_y := c.my_operator;
c.my_operator := t.token_string;
c.code := cc_NONE;
Codigo := false;
case Estado of
1:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoCommand.automata Estado 1');
{$endif}
if(t.tipo = 1) then // numbers 1
begin
Estado := 2;
end
else if( t.token_string = 'h' ) then // command h
begin
Estado := 9; // symbolic state
Estado := 1;
Codigo := true;
c.code:=cc_H_CLOSE_PATH;
Result:=c;
end
else if( t.token_string = 's' ) then // command s
begin
Estado := 10; // symbolic state
Estado := 1;
Codigo := true;
c.code:=cc_hS_CLOSE_AND_END_PATH;
Result:=c;
end
else if( t.token_string = 'S' ) then // command S
begin
Estado := 11; // symbolic state
Estado := 1;
Codigo := true;
c.code:=cc_S_END_PATH;
Result:=c;
end
else if( t.token_string = 'Q' ) then // command Q
begin
Estado := 21; // symbolic state
Estado := 1;
Codigo := true;
c.code:=cc_RESTORE_MATRIX;
Result:=c;
end
else if ((t.token_string = 'f') or (t.token_string = 'F')
or (t.token_string = 'f*') or (t.token_string = 'B')
or (t.token_string = 'B*') or (t.token_string = 'b')
or (t.token_string = 'b*') or (t.token_string = 'n')) then
begin
Estado := 12; // symbolic state
Estado := 1;
Codigo := true;
c.code:=cc_hS_CLOSE_AND_END_PATH; // ignore painting..
Result:=c;
//raise Exception.Create('ERROR: Prenchimento nao eh suportado.');
//Halt(1);
end
else if ((t.token_string = 'W') or (t.token_string = 'W*')) then
begin
Estado := 13; // symbolic state
raise Exception.Create('ERROR: Clipping nao eh suportado.');
Halt(1);
end
else
begin
Estado := 1;
end;
end;
2:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoCommand.automata Estado 2');
{$endif}
if(t.tipo = 1) then // numbers 2
begin
Estado := 3;
end
else
begin
Estado := 1;
end;
end;
3:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoCommand.automata Estado 3');
{$endif}
if(t.tipo = 1) then // numbers 3
begin
Estado := 5;
end
else if(t.token_string = 'l') then // command l
begin
Estado := 14; // symbolic state
Estado := 1;
c.code:=cc_l_ADD_LINE_TO_PATH;
Codigo := true;
Result:=c;
end
else if(t.token_string = 'm') then // command m
begin
Estado := 15; // symbolic state
Estado := 1;
c.code:=cc_m_START_PATH;
Codigo := true;
Result:=c;
end
else
begin
Estado := 1;
end;
end;
5:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoCommand.automata Estado 5');
{$endif}
if(t.tipo = 1) then // numbers 4
begin
Estado := 6;
end
else
begin
Estado := 1;
end;
end;
6:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoCommand.automata Estado 6');
{$endif}
if(t.tipo = 1) then // numbers 5
begin
Estado := 7;
end
else if( t.token_string = 'v' ) then // command v
begin
Estado := 16; // symbolic state
raise Exception.Create('ERROR: Curva de bezier nao eh suportada.');
Halt(1);
end
else if( t.token_string = 'y' ) then // command y
begin
Estado := 17; // symbolic state
raise Exception.Create('ERROR: Curva de bezier nao eh suportada.');
Halt(1);
end
else if( t.token_string = 're' ) then // command re
begin
Estado := 18; // symbolic state
raise Exception.Create('ERROR: Comando nao suportado.');
Halt(1);
end
else
begin
Estado := 1;
end;
end;
7:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoCommand.automata Estado 7');
{$endif}
if(t.tipo = 1) then // numbers 6
begin
Estado := 8;
end
else
begin
Estado := 1;
end;
end;
8:
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoCommand.automata Estado 8');
{$endif}
if(t.token_string = 'c') then // commmand c
begin
Estado := 19; // symbolic state
Estado := 1;
c.code:=cc_c_BEZIER_TO_X_Y_USING_X2_Y2_AND_X3_Y3;
Codigo := true;
Result:=c;
end
else if( t.token_string = 'cm' ) then // command cm
begin
Estado := 20; // symbolic state
Estado := 1;
c.code:=cc_CONCATENATE_MATRIX;
Codigo := true;
Result:=c;
end
else
begin
Estado := 1;
end;
end;
else
{$ifdef FPVECTORIALDEBUG}
WriteLn(':> AnSintaticoCommand.automata Estado ELSE');
{$endif}
Estado := 1;
end;
end;
constructor AnSintaticoCommand.Create;
begin
inherited Create;
Estado := 1;
end;
constructor AnSintaticoPage.Create;
begin
inherited Create;
Estado := 1;
pageFound := false;
end;
constructor AnSintaticoPageContents.Create;
begin
inherited Create;
Estado := 1;
contentsFound := false;
h.flate_decode := false;
end;
constructor AnSintaticoLength.Create;
begin
inherited Create;
Estado := 1;
lenghtFound := false;
end;
end.

View File

@ -0,0 +1,245 @@
{
Writes an SVG Document
License: The same modified LGPL as the Free Pascal RTL
See the file COPYING.modifiedLGPL for more details
AUTHORS: Felipe Monteiro de Carvalho
}
unit svgvectorialwriter;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, math, fpvectorial;
type
{ TvSVGVectorialWriter }
TvSVGVectorialWriter = class(TvCustomVectorialWriter)
private
FPointSeparator, FCommaSeparator: TFormatSettings;
procedure WriteDocumentSize(AStrings: TStrings; AData: TvVectorialDocument);
procedure WriteDocumentName(AStrings: TStrings; AData: TvVectorialDocument);
procedure WritePaths(AStrings: TStrings; AData: TvVectorialDocument);
procedure WriteTexts(AStrings: TStrings; AData: TvVectorialDocument);
procedure ConvertFPVCoordinatesToSVGCoordinates(
const AData: TvVectorialDocument;
const ASrcX, ASrcY: Double; var ADestX, ADestY: double);
public
{ General reading methods }
procedure WriteToStrings(AStrings: TStrings; AData: TvVectorialDocument); override;
end;
implementation
const
// SVG requires hardcoding a DPI value
// The Opera Browser and Inkscape use 90 DPI, so we follow that
// 1 Inch = 25.4 milimiters
// 90 inches per pixel = (1 / 90) * 25.4 = 0.2822
// FLOAT_MILIMETERS_PER_PIXEL = 0.3528; // DPI 72 = 1 / 72 inches per pixel
FLOAT_MILIMETERS_PER_PIXEL = 0.2822; // DPI 90 = 1 / 90 inches per pixel
FLOAT_PIXELS_PER_MILIMETER = 3.5433; // DPI 90 = 1 / 90 inches per pixel
{ TvSVGVectorialWriter }
procedure TvSVGVectorialWriter.WriteDocumentSize(AStrings: TStrings; AData: TvVectorialDocument);
begin
AStrings.Add(' width="' + FloatToStr(AData.Width, FPointSeparator) + 'mm"');
AStrings.Add(' height="' + FloatToStr(AData.Height, FPointSeparator) + 'mm"');
end;
procedure TvSVGVectorialWriter.WriteDocumentName(AStrings: TStrings; AData: TvVectorialDocument);
begin
AStrings.Add(' sodipodi:docname="New document 1">');
end;
{@@
SVG Coordinate system measures things only in pixels, so that we have to
hardcode a DPI value for the screen, which is usually 72.
FPVectorial uses only milimeters (mm).
The initial point in FPVectorial is in the bottom-left corner of the document
and it grows to the top and to the right. In SVG, on the other hand, the
initial point is in the top-left corner, growing to the bottom and right.
Besides that, coordinates in SVG are also lengths in comparison to the
previous point and not absolute coordinates.
SVG uses commas "," to separate the X,Y coordinates, so it always uses points
"." as decimal separators and uses no thousand separators
}
procedure TvSVGVectorialWriter.WritePaths(AStrings: TStrings; AData: TvVectorialDocument);
var
i, j: Integer;
PathStr: string;
lPath: TPath;
PtX, PtY, OldPtX, OldPtY: double;
BezierCP1X, BezierCP1Y, BezierCP2X, BezierCP2Y: double;
segment: TPathSegment;
l2DSegment: T2DSegment absolute segment;
l2DBSegment: T2DBezierSegment absolute segment;
begin
for i := 0 to AData.GetPathCount() - 1 do
begin
OldPtX := 0;
OldPtY := 0;
PathStr := '';
lPath := AData.GetPath(i);
lPath.PrepareForSequentialReading;
for j := 0 to lPath.Len - 1 do
begin
segment := TPathSegment(lPath.Next());
if (segment.SegmentType <> st2DLine)
and (segment.SegmentType <> stMoveTo)
and (segment.SegmentType <> st2DBezier)
then Break; // unsupported line type
// Coordinate conversion from fpvectorial to SVG
ConvertFPVCoordinatesToSVGCoordinates(
AData, l2DSegment.X, l2DSegment.Y, PtX, PtY);
PtX := PtX - OldPtX;
PtY := PtY - OldPtY;
if (segment.SegmentType = stMoveTo) then
begin
PathStr := PathStr + 'm '
+ FloatToStr(PtX, FPointSeparator) + ','
+ FloatToStr(PtY, FPointSeparator) + ' ';
end
else if (segment.SegmentType = st2DLine) then
begin
PathStr := PathStr + 'l '
+ FloatToStr(PtX, FPointSeparator) + ','
+ FloatToStr(PtY, FPointSeparator) + ' ';
end
else if (segment.SegmentType = st2DBezier) then
begin
// Converts all coordinates to absolute values
ConvertFPVCoordinatesToSVGCoordinates(
AData, l2DBSegment.X2, l2DBSegment.Y2, BezierCP1X, BezierCP1Y);
ConvertFPVCoordinatesToSVGCoordinates(
AData, l2DBSegment.X3, l2DBSegment.Y3, BezierCP2X, BezierCP2Y);
// Transforms them into values relative to the initial point
BezierCP1X := BezierCP1X - OldPtX;
BezierCP1Y := BezierCP1Y - OldPtY;
BezierCP2X := BezierCP2X - OldPtX;
BezierCP2Y := BezierCP2Y - OldPtY;
// PtX and PtY already contains the destination point
// Now render our 2D cubic bezier
PathStr := PathStr + 'c '
+ FloatToStr(BezierCP1X, FPointSeparator) + ','
+ FloatToStr(BezierCP1Y, FPointSeparator) + ' '
+ FloatToStr(BezierCP2X, FPointSeparator) + ','
+ FloatToStr(BezierCP2Y, FPointSeparator) + ' '
+ FloatToStr(PtX, FPointSeparator) + ','
+ FloatToStr(PtY, FPointSeparator) + ' '
;
end;
// Store the current position for future points
OldPtX := OldPtX + PtX;
OldPtY := OldPtY + PtY;
end;
AStrings.Add(' <path');
AStrings.Add(' style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"');
AStrings.Add(' d="' + PathStr + '"');
AStrings.Add(' id="path' + IntToStr(i) + '" />');
end;
end;
procedure TvSVGVectorialWriter.ConvertFPVCoordinatesToSVGCoordinates(
const AData: TvVectorialDocument; const ASrcX, ASrcY: Double; var ADestX,
ADestY: double);
begin
ADestX := ASrcX / FLOAT_MILIMETERS_PER_PIXEL;
ADestY := (AData.Height - ASrcY) / FLOAT_MILIMETERS_PER_PIXEL;
end;
procedure TvSVGVectorialWriter.WriteToStrings(AStrings: TStrings;
AData: TvVectorialDocument);
begin
// Format seetings to convert a string to a float
FPointSeparator := DefaultFormatSettings;
FPointSeparator.DecimalSeparator := '.';
FPointSeparator.ThousandSeparator := '#';// disable the thousand separator
FCommaSeparator := DefaultFormatSettings;
FCommaSeparator.DecimalSeparator := ',';
FCommaSeparator.ThousandSeparator := '#';// disable the thousand separator
// Headers
AStrings.Add('<?xml version="1.0" encoding="UTF-8" standalone="no"?>');
AStrings.Add('<!-- Created with fpVectorial (http://wiki.lazarus.freepascal.org/fpvectorial) -->');
AStrings.Add('');
AStrings.Add('<svg');
AStrings.Add(' xmlns:dc="http://purl.org/dc/elements/1.1/"');
AStrings.Add(' xmlns:cc="http://creativecommons.org/ns#"');
AStrings.Add(' xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"');
AStrings.Add(' xmlns:svg="http://www.w3.org/2000/svg"');
AStrings.Add(' xmlns="http://www.w3.org/2000/svg"');
AStrings.Add(' xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"');
WriteDocumentSize(AStrings, AData);
AStrings.Add(' id="svg2"');
AStrings.Add(' version="1.1"');
WriteDocumentName(AStrings, AData);
// Now data
AStrings.Add(' <g id="layer1">');
WritePaths(AStrings, AData);
WriteTexts(AStrings, AData);
AStrings.Add(' </g>');
// finalization
AStrings.Add('</svg>');
end;
procedure TvSVGVectorialWriter.WriteTexts(AStrings: TStrings; AData: TvVectorialDocument);
var
i, j, FontSize: Integer;
TextStr, FontName, SVGFontFamily: string;
lText: TvText;
PtX, PtY: double;
begin
for i := 0 to AData.GetTextCount() - 1 do
begin
TextStr := '';
lText := AData.GetText(i);
ConvertFPVCoordinatesToSVGCoordinates(
AData, lText.X, lText.Y, PtX, PtY);
TextStr := lText.Value;
FontSize:= ceil(lText.FontSize / FLOAT_MILIMETERS_PER_PIXEL);
SVGFontFamily := 'Arial, sans-serif';//lText.FontName;
AStrings.Add(' <text ');
AStrings.Add(' x="' + FloatToStr(PtX, FPointSeparator) + '"');
AStrings.Add(' y="' + FloatToStr(PtY, FPointSeparator) + '"');
// AStrings.Add(' font-size="' + IntToStr(FontSize) + '"'); Doesn't seam to work, we need to use the tspan
AStrings.Add(' font-family="' + SVGFontFamily + '">');
AStrings.Add(' <tspan ');
AStrings.Add(' style="font-size:' + IntToStr(FontSize) + '" ');
// AStrings.Add(' id="tspan2828" ');
AStrings.Add(' >');
AStrings.Add(TextStr + '</tspan></text>');
end;
end;
initialization
RegisterVectorialWriter(TvSVGVectorialWriter, vfSVG);
end.

View File

@ -3,11 +3,14 @@
<ProjectOptions>
<Version Value="9"/>
<General>
<Flags>
<AlwaysBuild Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<ResourceType Value="res"/>
<UseXPManifest Value="True"/>
<Icon Value="0"/>
<ActiveWindowIndexAtStart Value="0"/>
</General>
<i18n>
<EnableI18N LFM="False"/>
@ -15,7 +18,7 @@
<VersionInfo>
<StringTable ProductVersion=""/>
</VersionInfo>
<BuildModes Count="1" Active="Default">
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
@ -34,12 +37,11 @@
<PackageName Value="LCL"/>
</Item1>
</RequiredPackages>
<Units Count="8">
<Units Count="2">
<Unit0>
<Filename Value="fpvviewer.lpr"/>
<IsPartOfProject Value="True"/>
<UnitName Value="fpvviewer"/>
<UsageCount Value="21"/>
</Unit0>
<Unit1>
<Filename Value="fpvv_mainform.pas"/>
@ -47,194 +49,8 @@
<ComponentName Value="frmFPVViewer"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="fpvv_mainform"/>
<IsVisibleTab Value="True"/>
<EditorIndex Value="0"/>
<WindowIndex Value="0"/>
<TopLine Value="26"/>
<CursorPos X="80" Y="58"/>
<UsageCount Value="21"/>
<Loaded Value="True"/>
<LoadedDesigner Value="True"/>
</Unit1>
<Unit2>
<Filename Value="fpvectorial.pas"/>
<UnitName Value="fpvectorial"/>
<EditorIndex Value="4"/>
<WindowIndex Value="0"/>
<TopLine Value="23"/>
<CursorPos X="35" Y="44"/>
<UsageCount Value="11"/>
<Loaded Value="True"/>
</Unit2>
<Unit3>
<Filename Value="fpvtocanvas.pas"/>
<UnitName Value="fpvtocanvas"/>
<EditorIndex Value="3"/>
<WindowIndex Value="0"/>
<TopLine Value="54"/>
<CursorPos X="1" Y="73"/>
<UsageCount Value="11"/>
<Loaded Value="True"/>
</Unit3>
<Unit4>
<Filename Value="dxfvectorialreader.pas"/>
<UnitName Value="dxfvectorialreader"/>
<EditorIndex Value="2"/>
<WindowIndex Value="0"/>
<TopLine Value="184"/>
<CursorPos X="16" Y="221"/>
<UsageCount Value="11"/>
<Loaded Value="True"/>
</Unit4>
<Unit5>
<Filename Value="../../../lazarus/lcl/include/spinedit.inc"/>
<EditorIndex Value="1"/>
<WindowIndex Value="0"/>
<TopLine Value="118"/>
<CursorPos X="1" Y="145"/>
<UsageCount Value="10"/>
<Loaded Value="True"/>
<DefaultSyntaxHighlighter Value="Delphi"/>
</Unit5>
<Unit6>
<Filename Value="../../../lazarus/lcl/include/wincontrol.inc"/>
<WindowIndex Value="0"/>
<TopLine Value="7144"/>
<CursorPos X="1" Y="7165"/>
<UsageCount Value="10"/>
<DefaultSyntaxHighlighter Value="Delphi"/>
</Unit6>
<Unit7>
<Filename Value="../../../lazarus/lcl/include/customimage.inc"/>
<WindowIndex Value="0"/>
<TopLine Value="34"/>
<CursorPos X="1" Y="66"/>
<UsageCount Value="10"/>
<DefaultSyntaxHighlighter Value="Delphi"/>
</Unit7>
</Units>
<JumpHistory Count="30" HistoryIndex="29">
<Position1>
<Filename Value="../../../lazarus/lcl/include/spinedit.inc"/>
<Caret Line="145" Column="1" TopLine="118"/>
</Position1>
<Position2>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="48" Column="1" TopLine="28"/>
</Position2>
<Position3>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="49" Column="1" TopLine="28"/>
</Position3>
<Position4>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="51" Column="1" TopLine="28"/>
</Position4>
<Position5>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="53" Column="1" TopLine="28"/>
</Position5>
<Position6>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="56" Column="1" TopLine="28"/>
</Position6>
<Position7>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="58" Column="1" TopLine="28"/>
</Position7>
<Position8>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="61" Column="1" TopLine="29"/>
</Position8>
<Position9>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="63" Column="1" TopLine="31"/>
</Position9>
<Position10>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="61" Column="1" TopLine="31"/>
</Position10>
<Position11>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="63" Column="1" TopLine="31"/>
</Position11>
<Position12>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="66" Column="1" TopLine="34"/>
</Position12>
<Position13>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="67" Column="1" TopLine="35"/>
</Position13>
<Position14>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="70" Column="1" TopLine="38"/>
</Position14>
<Position15>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="71" Column="1" TopLine="39"/>
</Position15>
<Position16>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="69" Column="1" TopLine="39"/>
</Position16>
<Position17>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="56" Column="1" TopLine="39"/>
</Position17>
<Position18>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="58" Column="1" TopLine="39"/>
</Position18>
<Position19>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="61" Column="1" TopLine="39"/>
</Position19>
<Position20>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="63" Column="1" TopLine="39"/>
</Position20>
<Position21>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="61" Column="1" TopLine="39"/>
</Position21>
<Position22>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="63" Column="1" TopLine="39"/>
</Position22>
<Position23>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="66" Column="1" TopLine="39"/>
</Position23>
<Position24>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="67" Column="1" TopLine="39"/>
</Position24>
<Position25>
<Filename Value="dxfvectorialreader.pas"/>
<Caret Line="221" Column="16" TopLine="184"/>
</Position25>
<Position26>
<Filename Value="fpvtocanvas.pas"/>
<Caret Line="64" Column="11" TopLine="43"/>
</Position26>
<Position27>
<Filename Value="fpvv_mainform.pas"/>
<Caret Line="54" Column="52" TopLine="22"/>
</Position27>
<Position28>
<Filename Value="fpvv_mainform.pas"/>
<Caret Line="55" Column="52" TopLine="23"/>
</Position28>
<Position29>
<Filename Value="fpvv_mainform.pas"/>
<Caret Line="56" Column="52" TopLine="24"/>
</Position29>
<Position30>
<Filename Value="fpvv_mainform.pas"/>
<Caret Line="57" Column="52" TopLine="25"/>
</Position30>
</JumpHistory>
</ProjectOptions>
<CompilerOptions>
<Version Value="9"/>
@ -243,6 +59,7 @@
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<OtherUnitFiles Value="fpvectorialsrc"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Linking>
@ -253,6 +70,9 @@
</Options>
</Linking>
<Other>
<CompilerMessages>
<UseMsgFile Value="True"/>
</CompilerMessages>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>