lazarus-ccr/applications/fpvviewer/fpvectorialsrc/fpvectorial.pas
sekelsenmat a263b71828 Implements circular arcs in fpvectorial
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1463 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2011-01-26 20:40:56 +00:00

1035 lines
26 KiB
ObjectPascal

{
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, Math;
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;
{@@
}
TvEntity = class
public
end;
{@@
}
TvCircle = class(TvEntity)
public
CenterX, CenterY, CenterZ, Radius: Double;
end;
{@@
}
TvCircularArc = class(TvEntity)
public
CenterX, CenterY, CenterZ, Radius: Double;
{@@ The Angle is measured in degrees in relation to the positive X axis }
StartAngle, EndAngle: Double;
end;
{@@
}
{ TvEllipse }
TvEllipse = class(TvEntity)
public
// Mandatory fields
CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis: Double;
{@@ The Angle is measured in degrees in relation to the positive X axis }
Angle: Double;
// Calculated fields
BoundingRect: TRect;
procedure CalculateBoundingRectangle;
end;
type
TvCustomVectorialWriter = class;
TvCustomVectorialReader = class;
{ TvVectorialDocument }
TvVectorialDocument = class
private
FPaths: TFPList;
FTexts: TFPList;
FEntities: 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;
function GetEntity(ANum: Cardinal): TvEntity;
function GetEntityCount: 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;
procedure AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double);
procedure AddCircularArc(ACenterX, ACenterY, ACenterZ, ARadius, AStartAngle, AEndAngle: Double);
procedure AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle: Double);
{ 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 }
constructor Create; virtual;
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 }
constructor Create; virtual;
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;
{ TvEllipse }
procedure TvEllipse.CalculateBoundingRectangle;
var
t, tmp: Double;
begin
{
To calculate the bounding rectangle we can do this:
Ellipse equations:You could try using the parametrized equations for an ellipse rotated at an arbitrary angle:
x = CenterX + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle)
y = CenterY + MinorHalfAxis*sin(t)*cos(Angle) + MajorHalfAxis*cos(t)*sin(Angle)
You can then differentiate and solve for gradient = 0:
0 = dx/dt = -MajorHalfAxis*sin(t)*cos(Angle) - MinorHalfAxis*cos(t)*sin(Angle)
=>
tan(t) = -MinorHalfAxis*tan(Angle)/MajorHalfAxis
=>
t = cotang(-MinorHalfAxis*tan(Angle)/MajorHalfAxis)
On the other axis:
0 = dy/dt = b*cos(t)*cos(phi) - a*sin(t)*sin(phi)
=>
tan(t) = b*cot(phi)/a
}
t := cotan(-MinorHalfAxis*tan(Angle)/MajorHalfAxis);
tmp := CenterX + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle);
BoundingRect.Right := Round(tmp);
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;
FEntities := TFPList.Create;
FTmpPath := TPath.Create;
end;
{@@
Destructor.
}
destructor TvVectorialDocument.Destroy;
begin
Clear;
FPaths.Free;
FTexts.Free;
FEntities.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;
procedure TvVectorialDocument.AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double);
var
lCircle: TvCircle;
begin
lCircle := TvCircle.Create;
lCircle.CenterX := ACenterX;
lCircle.CenterY := ACenterY;
lCircle.CenterZ := ACenterZ;
lCircle.Radius := ARadius;
FEntities.Add(lCircle);
end;
procedure TvVectorialDocument.AddCircularArc(ACenterX, ACenterY, ACenterZ,
ARadius, AStartAngle, AEndAngle: Double);
var
lCircularArc: TvCircularArc;
begin
lCircularArc := TvCircularArc.Create;
lCircularArc.CenterX := ACenterX;
lCircularArc.CenterY := ACenterY;
lCircularArc.CenterZ := ACenterZ;
lCircularArc.Radius := ARadius;
lCircularArc.StartAngle := AStartAngle;
lCircularArc.EndAngle := AEndAngle;
FEntities.Add(lCircularArc);
end;
procedure TvVectorialDocument.AddEllipse(CenterX, CenterY, CenterZ,
MajorHalfAxis, MinorHalfAxis, Angle: Double);
var
lEllipse: TvEllipse;
begin
lEllipse := TvEllipse.Create;
lEllipse.CenterX := CenterX;
lEllipse.CenterY := CenterY;
lEllipse.CenterZ := CenterZ;
lEllipse.MajorHalfAxis := MajorHalfAxis;
lEllipse.MinorHalfAxis := MinorHalfAxis;
lEllipse.Angle := Angle;
FEntities.Add(lEllipse);
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;
function TvVectorialDocument.GetEntity(ANum: Cardinal): TvEntity;
begin
if ANum >= FEntities.Count then raise Exception.Create('TvVectorialDocument.GetEntity: Entity number out of bounds');
if FEntities.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetEntity: Invalid Entity number');
Result := TvEntity(FEntities.Items[ANum]);
end;
function TvVectorialDocument.GetEntityCount: Integer;
begin
Result := FEntities.Count;
end;
{@@
Clears all data in the document
}
procedure TvVectorialDocument.Clear;
begin
RemoveAllPaths();
RemoveAllTexts();
end;
{ TvCustomVectorialReader }
constructor TvCustomVectorialReader.Create;
begin
inherited Create;
end;
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 }
constructor TvCustomVectorialWriter.Create;
begin
inherited Create;
end;
{@@
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.