فهرست منبع

Large improvements to the DXF reader and adds a new circle entity to fpvectorial

git-svn-id: trunk@16816 -
sekelsenmat 14 سال پیش
والد
کامیت
6bad2fa169

+ 7 - 7
packages/fpvectorial/examples/fpvc_mainform.lfm

@@ -7,12 +7,12 @@ object formVectorialConverter: TformVectorialConverter
   Caption = 'FP Vectorial Converter'
   ClientHeight = 439
   ClientWidth = 240
-  LCLVersion = '0.9.29'
+  LCLVersion = '0.9.31'
   object Label1: TLabel
     Left = 8
-    Height = 17
+    Height = 18
     Top = 112
-    Width = 160
+    Width = 172
     Caption = 'Location of the Input file:'
     ParentColor = False
   end
@@ -30,7 +30,7 @@ object formVectorialConverter: TformVectorialConverter
   end
   object editInput: TFileNameEdit
     Left = 8
-    Height = 22
+    Height = 25
     Top = 128
     Width = 192
     DialogOptions = []
@@ -43,15 +43,15 @@ object formVectorialConverter: TformVectorialConverter
   end
   object Label3: TLabel
     Left = 8
-    Height = 17
+    Height = 18
     Top = 152
-    Width = 173
+    Width = 184
     Caption = 'Full path of the Output file:'
     ParentColor = False
   end
   object editOutput: TFileNameEdit
     Left = 8
-    Height = 22
+    Height = 25
     Top = 168
     Width = 192
     DialogOptions = []

+ 1 - 5
packages/fpvectorial/examples/fpvectorialconverter.lpr

@@ -3,12 +3,8 @@ program fpvectorialconverter;
 {$mode objfpc}{$H+}
 
 uses
-  {$IFDEF UNIX}{$IFDEF UseCThreads}
-  cthreads,
-  {$ENDIF}{$ENDIF}
   Interfaces, // this includes the LCL widgetset
-  Forms, fpvc_mainform
-  { you can add units after this };
+  Forms, fpvc_mainform;
 
 {$R *.res}
 

+ 406 - 151
packages/fpvectorial/src/dxfvectorialreader.pas

@@ -17,7 +17,7 @@ SECTION_NAME
 ENDSEC
 0
 
-after all section end there is:
+after all sections there is:
 
 EOF
 
@@ -37,20 +37,53 @@ type
   { Used by tcutils.SeparateString }
   T10Strings = array[0..9] of shortstring;
 
+  TDXFToken = class;
+
+  TDXFTokens = TFPList;// TDXFToken;
+
+  TDXFToken = class
+    GroupCode: Integer;
+    StrValue: string;
+    FloatValue: double;
+    IntValue: Integer;
+    Childs: TDXFTokens;
+    constructor Create;
+    Destructor Destroy; override;
+  end;
+
+  { TDXFTokenizer }
+
+  TDXFTokenizer = class
+  public
+    Tokens: TDXFTokens;
+    constructor Create;
+    Destructor Destroy; override;
+    procedure ReadFromStrings(AStrings: TStrings);
+    function  IsENTITIES_Subsection(AStr: string): Boolean;
+  end;
+
   { TvDXFVectorialReader }
 
   TvDXFVectorialReader = class(TvCustomVectorialReader)
   private
+    // CIRCLE
+    CircleCenterX, CircleCenterY, CircleCenterZ, CircleRadius: Double;
+    // LINE
     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;
+    procedure ReadENTITIES(ATokens: TDXFTokens; AData: TvVectorialDocument);
+    procedure ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvVectorialDocument);
+    procedure ReadENTITIES_CIRCLE(ATokens: TDXFTokens; AData: TvVectorialDocument);
+    procedure ReadENTITIES_ELLIPSE(ATokens: TDXFTokens; AData: TvVectorialDocument);
+    procedure ReadENTITIES_TEXT(ATokens: TDXFTokens; AData: TvVectorialDocument);
     function  GetCoordinateValue(AStr: shortstring): Double;
   public
     { General reading methods }
+    Tokenizer: TDXFTokenizer;
+    constructor Create; override;
+    Destructor Destroy; override;
     procedure ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); override;
   end;
 
@@ -59,23 +92,190 @@ implementation
 {$define FPVECTORIALDEBUG}
 
 const
-  { Coordinate constants }
+  // Group Codes for ENTITIES
+  DXF_ENTITIES_TYPE = 0;
+  DXF_ENTITIES_HANDLE = 5;
+  DXF_ENTITIES_APPLICATION_GROUP = 102;
+  DXF_ENTITIES_AcDbEntity = 100;
+  DXF_ENTITIES_MODEL_OR_PAPER_SPACE = 67; // default=0=model, 1=paper
+  DXF_ENTITIES_VISIBILITY = 60; // default=0 = Visible, 1 = Invisible
 
-  INT_COORDINATE_NONE = 0;
-  INT_COORDINATE_X = 1;
-  INT_COORDINATE_Y = 2;
-  INT_COORDINATE_Z = 3;
+{ TDXFToken }
+
+constructor TDXFToken.Create;
+begin
+  inherited Create;
+
+  Childs := TDXFTokens.Create;
+end;
+
+destructor TDXFToken.Destroy;
+begin
+  Childs.Free;
+
+  inherited Destroy;
+end;
+
+{ TDXFTokenizer }
+
+constructor TDXFTokenizer.Create;
+begin
+  inherited Create;
+
+  Tokens := TDXFTokens.Create;
+end;
+
+destructor TDXFTokenizer.Destroy;
+begin
+  Tokens.Free;
+
+  inherited Destroy;
+end;
+
+procedure TDXFTokenizer.ReadFromStrings(AStrings: TStrings);
+var
+  i: Integer;
+  StrSectionGroupCode, StrSectionName: string;
+  IntSectionGroupCode: Integer;
+  CurTokenBase, NextTokenBase, SectionTokenBase: TDXFTokens;
+  NewToken: TDXFToken;
+  ParserState: Integer;
+begin
+  //  Tokens.ForEachCall(); deletecallback
+  Tokens.Clear;
+
+  CurTokenBase := Tokens;
+  NextTokenBase := Tokens;
+  i := 0;
+  ParserState := 0;
 
-  { GCode constants }
+  while i < AStrings.Count - 1 do
+  begin
+    CurTokenBase := NextTokenBase;
+
+    // Now read and process the section name
+    StrSectionGroupCode := AStrings.Strings[i];
+    IntSectionGroupCode := StrToInt(Trim(StrSectionGroupCode));
+    StrSectionName := AStrings.Strings[i+1];
+
+    NewToken := TDXFToken.Create;
+    NewToken.GroupCode := IntSectionGroupCode;
+    NewToken.StrValue := StrSectionName;
+
+    // Waiting for a section
+    if ParserState = 0 then
+    begin
+      if (StrSectionName = 'SECTION') then
+      begin
+        ParserState := 1;
+        NextTokenBase := NewToken.Childs;
+      end
+      else if (StrSectionName = 'EOF') then
+      begin
+        Exit;
+      end
+      else
+      begin
+        raise Exception.Create(Format(
+          'TDXFTokenizer.ReadFromStrings: Expected SECTION, but got: %s', [StrSectionname]));
+      end;
+    end
+    // Processing the section name
+    else if ParserState = 1 then
+    begin
+      if (StrSectionName = 'HEADER') or
+        (StrSectionName = 'CLASSES') or
+        (StrSectionName = 'TABLES') or
+        (StrSectionName = 'BLOCKS') or
+        (StrSectionName = 'OBJECTS') or
+        (StrSectionName = 'THUMBNAILIMAGE') then
+      begin
+        ParserState := 2;
+        SectionTokenBase := CurTokenBase;
+      end
+      else if (StrSectionName = 'ENTITIES') then
+      begin
+        ParserState := 3;
+        SectionTokenBase := CurTokenBase;
+      end
+      else
+      begin
+        raise Exception.Create(Format(
+          'TDXFTokenizer.ReadFromStrings: Invalid section name: %s', [StrSectionname]));
+      end;
+    end
+    // Reading a generic section
+    else if ParserState = 2 then
+    begin
+      if StrSectionName = 'ENDSEC' then
+      begin
+        ParserState := 0;
+        CurTokenBase := SectionTokenBase;
+        NextTokenBase := Tokens;
+      end;
+    end
+    // Reading the ENTITIES section
+    else if ParserState = 3 then
+    begin
+      if IsENTITIES_Subsection(StrSectionName) then
+      begin
+        CurTokenBase := SectionTokenBase;
+        NextTokenBase := NewToken.Childs;
+      end
+      else if StrSectionName = 'ENDSEC' then
+      begin
+        ParserState := 0;
+        CurTokenBase := SectionTokenBase;
+        NextTokenBase := Tokens;
+      end;
+    end;
+
+    CurTokenBase.Add(NewToken);
+
+    Inc(i, 2);
+  end;
+end;
 
-  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';
+function TDXFTokenizer.IsENTITIES_Subsection(AStr: string): Boolean;
+begin
+  Result :=
+    (AStr = '3DFACE') or
+    (AStr = '3DSOLID') or
+    (AStr = 'ACAD_PROXY_ENTITY') or
+    (AStr = 'ARC') or
+    (AStr = 'ATTDEF') or
+    (AStr = 'ATTRIB') or
+    (AStr = 'BODY') or
+    (AStr = 'CIRCLE') or
+    (AStr = 'DIMENSION') or
+    (AStr = 'ELLIPSE') or
+    (AStr = 'HATCH') or
+    (AStr = 'IMAGE') or
+    (AStr = 'INSERT') or
+    (AStr = 'LEADER') or
+    (AStr = 'LINE') or
+    (AStr = 'LWPOLYLINE') or
+    (AStr = 'MLINE') or
+    (AStr = 'MTEXT') or
+    (AStr = 'OLEFRAME') or
+    (AStr = 'OLE2FRAME') or
+    (AStr = 'POINT') or
+    (AStr = 'POLYLINE') or
+    (AStr = 'RAY') or
+    (AStr = 'REGION') or
+    (AStr = 'SEQEND') or
+    (AStr = 'SHAPE') or
+    (AStr = 'SOLID') or
+    (AStr = 'SPLINE') or
+    (AStr = 'TEXT') or
+    (AStr = 'TOLERANCE') or
+    (AStr = 'TRACE') or
+    (AStr = 'VERTEX') or
+    (AStr = 'VIEWPORT') or
+    (AStr = 'XLINE');
+end;
 
-{ TvAvisoCNCGCodeReader }
+{ TvDXFVectorialReader }
 
 {@@
   Reads a string and separates it in substring
@@ -110,172 +310,219 @@ begin
   end;
 end;
 
-{@@
-  returns   If an end of file marker was found
-}
-function TvDXFVectorialReader.ReadSection(
-  AStrings: TStrings; var AIndex: Integer; AData: TvVectorialDocument): Boolean;
+procedure TvDXFVectorialReader.ReadENTITIES(ATokens: TDXFTokens; AData: TvVectorialDocument);
 var
-  DestX, DestY, DestZ: Double;
-  StrSectionNum, StrSectionName: string;
-  IntSectionNum, i: Integer;
+  i: Integer;
+  CurToken: TDXFToken;
 begin
-  Result := False;
-
-  // Check if there is minimal space for a section
-  if AIndex+5 > AStrings.Count then
+  for i := 0 to ATokens.Count - 1 do
   begin
-    {$ifdef FPVECTORIALDEBUG}
-    WriteLn('Not enough space for a section');
-    {$endif}
-    Exit(True);
-  end;
+    CurToken := TDXFToken(ATokens.Items[i]);
+    if CurToken.StrValue = 'CIRCLE' then
+    begin
+      CircleCenterX := 0.0;
+      CircleCenterY := 0.0;
+      CircleCenterZ := 0.0;
+      CircleRadius := 0.0;
 
-  // 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;
+      ReadENTITIES_CIRCLE(CurToken.Childs, AData);
 
-  // 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
+      AData.AddCircle(CircleCenterX, CircleCenterY,
+        CircleCenterZ, CircleRadius);
+    end
+    else if CurToken.StrValue = 'ELLIPSE' then
     begin
-      if AStrings.Strings[i] = 'ENDSEC' then
-      begin
-        AIndex := i + 1;
-        Exit;
-      end;
+      // ...
+      ReadENTITIES_ELLIPSE(CurToken.Childs, AData);
+    end
+    else if CurToken.StrValue = 'LINE' then
+    begin
+      // Initial values
+      LineStartX := 0;
+      LineStartY := 0;
+      LineStartZ := 0;
+      LineEndX := 0;
+      LineEndY := 0;
+      LineEndZ := 0;
+
+      // Read the data of the line
+      ReadENTITIES_LINE(CurToken.Childs, AData);
+
+      // 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
+    else if CurToken.StrValue = 'TEXT' then
+    begin
+      // ...
     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
+end;
+
+procedure TvDXFVectorialReader.ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvVectorialDocument);
+var
+  CurToken: TDXFToken;
+  i: Integer;
+begin
+  for i := 0 to ATokens.Count - 1 do
   begin
-  end;}
+    // Now read and process the item name
+    CurToken := TDXFToken(ATokens.Items[i]);
+
+    // Avoid an exception by previously checking if the conversion can be made
+    if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or
+      (CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue;
+
+    CurToken.FloatValue :=  StrToFloat(Trim(CurToken.StrValue));
+
+    case CurToken.GroupCode of
+      10: LineStartX := CurToken.FloatValue;
+      20: LineStartY := CurToken.FloatValue;
+      30: LineStartZ := CurToken.FloatValue;
+      11: LineEndX := CurToken.FloatValue;
+      21: LineEndY := CurToken.FloatValue;
+      31: LineEndZ := CurToken.FloatValue;
+    end;
+  end;
 end;
 
-function TvDXFVectorialReader.ReadENTITIES(AStrings: TStrings;
-  var AIndex: Integer; AData: TvVectorialDocument): Boolean;
+{
+Group codes	Description
+100 Subclass marker (AcDbCircle)
+39 Thickness (optional; default = 0)
+10 Center point (in OCS) DXF: X value; APP: 3D point
+20, 30 DXF: Y and Z values of center point (in OCS)
+40 Radius
+210 Extrusion direction (optional; default = 0, 0, 1) DXF: X value; APP: 3D vector
+220, 230 DXF: Y and Z values of extrusion direction  (optional)
+}
+procedure TvDXFVectorialReader.ReadENTITIES_CIRCLE(ATokens: TDXFTokens;
+  AData: TvVectorialDocument);
 var
-  StrSectionNum, StrSectionName: string;
-  IntSectionNum, i: Integer;
+  CurToken: TDXFToken;
+  i: Integer;
 begin
-  Result := False;
+  for i := 0 to ATokens.Count - 1 do
+  begin
+    // Now read and process the item name
+    CurToken := TDXFToken(ATokens.Items[i]);
 
-  // Now read and process the item name
-  StrSectionName := AStrings.Strings[AIndex+1];
+    // Avoid an exception by previously checking if the conversion can be made
+    if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or
+      (CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue;
 
-  {$ifdef FPVECTORIALDEBUG}
-  WriteLn('TvDXFVectorialReader.ReadENTITIES ', StrSectionName);
-  {$endif}
+    CurToken.FloatValue :=  StrToFloat(Trim(CurToken.StrValue));
 
-  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();
+    case CurToken.GroupCode of
+      10: CircleCenterX := CurToken.FloatValue;
+      20: CircleCenterY := CurToken.FloatValue;
+      30: CircleCenterZ := CurToken.FloatValue;
+      40: CircleRadius := CurToken.FloatValue;
+    end;
   end;
 end;
 
-function TvDXFVectorialReader.ReadENTITIES_LINE(AStrings: TStrings;
-  var AIndex: Integer; AData: TvVectorialDocument): Boolean;
+{
+100 Subclass marker (AcDbEllipse)
+10 Center point (in WCS) DXF: X value; APP: 3D point
+20, 30 DXF: Y and Z values of center point (in WCS)
+11 Endpoint of major axis, relative to the center (in WCS) DXF: X value; APP: 3D point
+21, 31 DXF: Y and Z values of endpoint of major axis, relative to the center (in WCS)
+210 Extrusion direction (optional; default = 0, 0, 1) DXF: X value; APP: 3D vector
+220, 230 DXF: Y and Z values of extrusion direction  (optional)
+40 Ratio of minor axis to major axis
+41 Start parameter (this value is 0.0 for a full ellipse)
+42 End parameter (this value is 2pi for a full ellipse)
+}
+procedure TvDXFVectorialReader.ReadENTITIES_ELLIPSE(ATokens: TDXFTokens;
+  AData: TvVectorialDocument);
 var
-  StrSectionNum, StrSectionValue: string;
-  IntSectionNum: Integer;
-  FloatSectionValue: double;
+  CurToken: TDXFToken;
+  i: Integer;
+  CenterX, CenterY, CenterZ: Double;
 begin
-  Result := False;
+  for i := 0 to ATokens.Count - 1 do
+  begin
+    // Now read and process the item name
+    CurToken := TDXFToken(ATokens.Items[i]);
 
-  // Now read and process the item name
-  StrSectionNum := AStrings.Strings[AIndex];
-  StrSectionValue := AStrings.Strings[AIndex+1];
+    // Avoid an exception by previously checking if the conversion can be made
+    if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or
+      (CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue;
 
-  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;
+    CurToken.FloatValue :=  StrToFloat(Trim(CurToken.StrValue));
+
+    case CurToken.GroupCode of
+      10: CenterX := CurToken.FloatValue;
+      20: CenterY := CurToken.FloatValue;
+      30: CenterZ := CurToken.FloatValue;
     end;
   end;
 end;
 
-function TvDXFVectorialReader.GetCoordinate(AStr: shortstring): Integer;
+{
+100 Subclass marker (AcDbText)
+39 Thickness (optional; default = 0)
+10 First alignment point (in OCS) DXF: X value; APP: 3D point
+20, 30 DXF: Y and Z values of first alignment point (in OCS)
+40 Text height
+1 Default value (the string itself)
+50 Text rotation (optional; default = 0)
+41 Relative X scale factor-width (optional; default = 1)
+  This value is also adjusted when fit-type text is used.
+51 Oblique angle (optional; default = 0)
+7 Text style name (optional, default = STANDARD)
+71 Text generation flags (optional, default = 0):
+  2 = Text is backward (mirrored in X).
+  4 = Text is upside down (mirrored in Y).
+72 Horizontal text justification type (optional, default = 0) integer codes (not bit-coded)
+  0 = Left; 1= Center; 2 = Right
+  3 = Aligned (if vertical alignment = 0)
+  4 = Middle (if vertical alignment = 0)
+  5 = Fit (if vertical alignment = 0)
+  See the Group 72 and 73 integer codes table for clarification.
+11 Second alignment point (in OCS) (optional)
+  DXF: X value; APP: 3D point
+  This value is meaningful only if the value of a 72 or 73 group is nonzero (if the justification is anything other than baseline/left).
+21, 31 DXF: Y and Z values of second alignment point (in OCS) (optional)
+210 Extrusion direction (optional; default = 0, 0, 1)
+  DXF: X value; APP: 3D vector
+220, 230 DXF: Y and Z values of extrusion direction (optional)
+73 Vertical text justification type (optional, default = 0): integer codes (not bit- coded):
+  0 = Baseline; 1 = Bottom; 2 = Middle; 3 = Top
+  See the Group 72 and 73 integer codes table for clarification.
+}
+procedure TvDXFVectorialReader.ReadENTITIES_TEXT(ATokens: TDXFTokens;
+  AData: TvVectorialDocument);
 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;
+{  if Length(AStr) <= 1 then Exit;
+
+  Result := StrToFloat(Copy(AStr, 2, Length(AStr) - 1));}
+end;
+
+constructor TvDXFVectorialReader.Create;
+begin
+  inherited Create;
 
-  Result := StrToFloat(Copy(AStr, 2, Length(AStr) - 1));
+  Tokenizer := TDXFTokenizer.Create;
+end;
+
+destructor TvDXFVectorialReader.Destroy;
+begin
+  Tokenizer.Free;
+
+  inherited Destroy;
 end;
 
 {@@
@@ -286,10 +533,18 @@ procedure TvDXFVectorialReader.ReadFromStrings(AStrings: TStrings;
   AData: TvVectorialDocument);
 var
   i: Integer;
+  CurToken, CurTokenFirstChild: TDXFToken;
 begin
-  i := 0;
-  while i < AStrings.Count - 1 do
-    if ReadSection(AStrings, i, AData) then Break;
+  Tokenizer.ReadFromStrings(AStrings);
+
+  for i := 0 to Tokenizer.Tokens.Count - 1 do
+  begin
+    CurToken := TDXFToken(Tokenizer.Tokens.Items[i]);
+    CurTokenFirstChild := TDXFToken(CurToken.Childs.Items[0]);
+
+    if CurTokenFirstChild.StrValue = 'ENTITIES' then
+      ReadENTITIES(CurToken.Childs, AData);
+  end;
 end;
 
 initialization

+ 53 - 0
packages/fpvectorial/src/fpvectorial.pas

@@ -121,6 +121,15 @@ type
     Value: utf8string;
   end;
 
+  TvEntity = class
+  public
+  end;
+
+  TvCircle = class(TvEntity)
+  public
+    X, Y, Z, Radius: Double;
+  end;
+
 type
 
   TvCustomVectorialWriter = class;
@@ -132,6 +141,7 @@ type
   private
     FPaths: TFPList;
     FTexts: TFPList;
+    FEntities: TFPList;
     FTmpPath: TPath;
     FTmpText: TvText;
     procedure RemoveCallback(data, arg: pointer);
@@ -158,6 +168,8 @@ type
     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;
@@ -172,6 +184,7 @@ type
     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(AX, AY, AZ, ARadius: Double);
     { properties }
     property PathCount: Integer read GetPathCount;
     property Paths[Index: Cardinal]: TPath read GetPath;
@@ -186,6 +199,7 @@ type
   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;
@@ -202,6 +216,7 @@ type
   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;
@@ -339,6 +354,7 @@ begin
 
   FPaths := TFPList.Create;
   FTexts := TFPList.Create;
+  FEntities := TFPList.Create;
   FTmpPath := TPath.Create;
 end;
 
@@ -351,6 +367,7 @@ begin
 
   FPaths.Free;
   FTexts.Free;
+  FEntities.Free;
 
   inherited Destroy;
 end;
@@ -520,6 +537,18 @@ begin
   AddText(AX, AY, AZ, '', 10, AStr);
 end;
 
+procedure TvVectorialDocument.AddCircle(AX, AY, AZ, ARadius: Double);
+var
+  lCircle: TvCircle;
+begin
+  lCircle := TvCircle.Create;
+  lCircle.X := AX;
+  lCircle.Y := AY;
+  lCircle.Z := AZ;
+  lCircle.Radius := ARadius;
+  FEntities.Add(lCircle);
+end;
+
 {@@
   Convenience method which creates the correct
   writer object for a given vector graphics document format.
@@ -747,6 +776,20 @@ 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
 }
@@ -758,6 +801,11 @@ end;
 
 { TvCustomVectorialReader }
 
+constructor TvCustomVectorialReader.Create;
+begin
+  inherited Create;
+end;
+
 procedure TvCustomVectorialReader.ReadFromFile(AFileName: string; AData: TvVectorialDocument);
 var
   FileStream: TFileStream;
@@ -806,6 +854,11 @@ end;
 
 { TsCustomSpreadWriter }
 
+constructor TvCustomVectorialWriter.Create;
+begin
+  inherited Create;
+end;
+
 {@@
   Default file writting method.
 

+ 18 - 0
packages/fpvectorial/src/fpvtocanvas.pas

@@ -40,6 +40,9 @@ var
   CurX, CurY: Integer; // Not modified by ADestX, etc
   CurveLength: Integer;
   t: Double;
+  // For entities
+  CurEntity: TvEntity;
+  CurCircle: TvCircle;
 begin
   {$ifdef FPVECTORIALDEBUG}
   WriteLn(':>DrawFPVectorialToCanvas');
@@ -100,6 +103,21 @@ begin
     end;
   end;
 
+  for i := 0 to ASource.GetEntityCount - 1 do
+  begin
+    CurEntity := ASource.GetEntity(i);
+    CurCircle := CurEntity as TvCircle;
+    if CurEntity is TvCircle then
+    begin
+      ADest.Ellipse(
+        Round(ADestX + AmulX * (CurCircle.X - CurCircle.Radius)),
+        Round(ADestY + AMulY * (CurCircle.Y - CurCircle.Radius)),
+        Round(ADestX + AmulX * (CurCircle.X + CurCircle.Radius)),
+        Round(ADestY + AMulY * (CurCircle.Y + CurCircle.Radius))
+        );
+    end;
+  end;
+
   {$ifdef FPVECTORIALDEBUG}
   WriteLn(':<DrawFPVectorialToCanvas');
   {$endif}