diff --git a/packages/fpvectorial/examples/fpvwritetest.lpi b/packages/fpvectorial/examples/fpvwritetest.lpi
index 495db7bccf..0333775fac 100644
--- a/packages/fpvectorial/examples/fpvwritetest.lpi
+++ b/packages/fpvectorial/examples/fpvwritetest.lpi
@@ -13,7 +13,6 @@
-
@@ -21,6 +20,9 @@
+
+
+
@@ -29,6 +31,7 @@
+
@@ -40,13 +43,13 @@
-
+
-
+
diff --git a/packages/fpvectorial/examples/fpvwritetest.pas b/packages/fpvectorial/examples/fpvwritetest.pas
index 0244cde71a..393fde3694 100644
--- a/packages/fpvectorial/examples/fpvwritetest.pas
+++ b/packages/fpvectorial/examples/fpvwritetest.pas
@@ -22,7 +22,7 @@ program fpvwritetest;
{$mode objfpc}{$H+}
uses
- fpvectorial, svgvectorialwriter;
+ fpvectorial, svgvectorialwriter, fpvutils;
const
cFormat = vfSVG;
@@ -134,6 +134,51 @@ begin
Vec.AddText(20, 20, 0, '20, 20 Mówić, cześć, Włosku, Parabéns.');
Vec.AddText(30, 30, 0, '30, 30 森林,是一个高密');
Vec.WriteToFile('multi_test_1' + cExtension, cFormat);
+
+ // pen_test_1 Tests the properties of the Pen
+ Vec.Clear;
+ Vec.StartPath(0, 20);
+ Vec.AddLineToPath(30, 30);
+ Vec.SetPenWidth(10);
+ Vec.EndPath();
+ Vec.StartPath(0, 0);
+ Vec.AddLineToPath(100, 0);
+ Vec.AddLineToPath(100, 100);
+ Vec.AddLineToPath(0, 100);
+ Vec.AddLineToPath(0, 0);
+ Vec.SetPenWidth(10);
+ Vec.EndPath();
+ Vec.StartPath(0, 0);
+ Vec.AddLineToPath(10, 10);
+ Vec.AddBezierToPath(10, 20, 20, 20, 20, 10);
+ Vec.AddLineToPath(30, 0);
+ Vec.SetPenWidth(10);
+ Vec.EndPath();
+ Vec.WriteToFile('pen_test_1' + cExtension, cFormat);
+
+ // pen_test_2 Tests the properties of the Pen
+ Vec.Clear;
+ Vec.StartPath(0, 20);
+ Vec.AddLineToPath(30, 30);
+ Vec.SetPenWidth(10);
+ Vec.SetPenColor(RGBToVColor(255, 0, 0));
+ Vec.EndPath();
+ Vec.StartPath(0, 0);
+ Vec.AddLineToPath(100, 0);
+ Vec.AddLineToPath(100, 100);
+ Vec.AddLineToPath(0, 100);
+ Vec.AddLineToPath(0, 0);
+ Vec.SetPenWidth(10);
+ Vec.SetPenColor(RGBToVColor(0, 255, 0));
+ Vec.EndPath();
+ Vec.StartPath(0, 0);
+ Vec.AddLineToPath(10, 10);
+ Vec.AddBezierToPath(10, 20, 20, 20, 20, 10);
+ Vec.AddLineToPath(30, 0);
+ Vec.SetPenWidth(10);
+ Vec.SetPenColor(RGBToVColor(0, 0, 255));
+ Vec.EndPath();
+ Vec.WriteToFile('pen_test_2' + cExtension, cFormat);
finally
Vec.Free;
end;
diff --git a/packages/fpvectorial/src/fpvectorial.pas b/packages/fpvectorial/src/fpvectorial.pas
index a505c9bab3..d61b28e727 100644
--- a/packages/fpvectorial/src/fpvectorial.pas
+++ b/packages/fpvectorial/src/fpvectorial.pas
@@ -45,6 +45,17 @@ type
Red, Green, Blue, Alpha: Byte;
end;
+ TvPen = record
+ Color: TvColor;
+ Style: TFPPenStyle;
+ Width: Integer;
+ end;
+
+ TvBrush = record
+ Color: TvColor;
+ Style: TFPBrushStyle;
+ end;
+
const
FPValphaTransparent = $00;
FPValphaOpaque = $FF;
@@ -59,7 +70,7 @@ type
P3DPoint = ^T3DPoint;
TSegmentType = (
- st2DLine, st2DBezier,
+ st2DLine, st2DLineWithPen, st2DBezier,
st3DLine, st3DBezier, stMoveTo);
{@@
@@ -75,10 +86,6 @@ type
// Fields for linking the list
Previous: TPathSegment;
Next: TPathSegment;
- // Data fields
- PenColor: TvColor;
- PenStyle: TFPPenStyle;
- PenWidth: Integer;
end;
{@@
@@ -93,6 +100,11 @@ type
X, Y: Double;
end;
+ T2DSegmentWithPen = class(T2DSegment)
+ public
+ Pen: TvPen;
+ 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
@@ -124,6 +136,13 @@ type
Points: TPathSegment; // Beginning of the double-linked list
PointsEnd: TPathSegment; // End of the double-linked list
CurPoint: TPathSegment; // Used in PrepareForSequentialReading and Next
+ {@@ The global Pen for the entire path. This Pen might be overriden by
+ individual elements of the polyline. }
+ Pen: TvPen;
+ {@@ Sets a Brush to paint the inner area inside the path.
+ There is no inner area if Brush.Style = bsClear, which is the default. }
+ Brush: TvBrush;
+ constructor Create();
procedure Assign(APath: TPath);
function Count(): TPathSegment;
procedure PrepareForSequentialReading;
@@ -139,23 +158,18 @@ type
TvText = class
public
X, Y, Z: Double; // Z is ignored in 2D formats
+ Value: utf8string;
+ FontColor: TvColor;
FontSize: integer;
FontName: utf8string;
- Value: utf8string;
- Color: TvColor;
end;
{@@
}
TvEntity = class
public
- // Pen
- PenColor: TvColor;
- PenStyle: TFPPenStyle;
- PenWidth: Integer;
- // Brush
- BrushStyle: TFPBrushStyle;
- BrushColor: TvColor;
+ Pen: TvPen;
+ Brush: TvBrush;
end;
{@@
@@ -253,6 +267,11 @@ type
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 SetBrushColor(AColor: TvColor);
+ procedure SetBrushStyle(AStyle: TFPBrushStyle);
+ procedure SetPenColor(AColor: TvColor);
+ procedure SetPenStyle(AStyle: TFPPenStyle);
+ procedure SetPenWidth(AWidth: Integer);
procedure EndPath();
procedure AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string); overload;
procedure AddText(AX, AY, AZ: Double; AStr: utf8string); overload;
@@ -550,20 +569,19 @@ begin
segment.SegmentType := st2DLine;
segment.X := AX;
segment.Y := AY;
- segment.PenColor := clvBlack;
AppendSegmentToTmpPath(segment);
end;
procedure TvVectorialDocument.AddLineToPath(AX, AY: Double; AColor: TvColor);
var
- segment: T2DSegment;
+ segment: T2DSegmentWithPen;
begin
- segment := T2DSegment.Create;
- segment.SegmentType := st2DLine;
+ segment := T2DSegmentWithPen.Create;
+ segment.SegmentType := st2DLineWithPen;
segment.X := AX;
segment.Y := AY;
- segment.PenColor := AColor;
+ segment.Pen.Color := AColor;
AppendSegmentToTmpPath(segment);
end;
@@ -623,6 +641,31 @@ begin
AppendSegmentToTmpPath(segment);
end;
+procedure TvVectorialDocument.SetBrushColor(AColor: TvColor);
+begin
+ FTmPPath.Brush.Color := AColor;
+end;
+
+procedure TvVectorialDocument.SetBrushStyle(AStyle: TFPBrushStyle);
+begin
+ FTmPPath.Brush.Style := AStyle;
+end;
+
+procedure TvVectorialDocument.SetPenColor(AColor: TvColor);
+begin
+ FTmPPath.Pen.Color := AColor;
+end;
+
+procedure TvVectorialDocument.SetPenStyle(AStyle: TFPPenStyle);
+begin
+ FTmPPath.Pen.Style := AStyle;
+end;
+
+procedure TvVectorialDocument.SetPenWidth(AWidth: Integer);
+begin
+ FTmPPath.Pen.Width := AWidth;
+end;
+
{@@
Finishes writing a Path, which was created in multiple
steps using StartPath and AddPointToPath,
@@ -683,7 +726,7 @@ begin
lCircularArc.Radius := ARadius;
lCircularArc.StartAngle := AStartAngle;
lCircularArc.EndAngle := AEndAngle;
- lCircularArc.PenColor := AColor;
+ lCircularArc.Pen.Color := AColor;
FEntities.Add(lCircularArc);
end;
@@ -728,12 +771,13 @@ begin
for i := 0 to Length(GvVectorialFormats) - 1 do
if GvVectorialFormats[i].Format = AFormat then
begin
- Result := GvVectorialFormats[i].WriterClass.Create;
+ if GvVectorialFormats[i].WriterClass <> nil then
+ Result := GvVectorialFormats[i].WriterClass.Create;
Break;
end;
- if Result = nil then raise Exception.Create('Unsuported vector graphics format.');
+ if Result = nil then raise Exception.Create('Unsupported vector graphics format.');
end;
{@@
@@ -749,12 +793,13 @@ begin
for i := 0 to Length(GvVectorialFormats) - 1 do
if GvVectorialFormats[i].Format = AFormat then
begin
- Result := GvVectorialFormats[i].ReaderClass.Create;
+ if GvVectorialFormats[i].ReaderClass <> nil then
+ Result := GvVectorialFormats[i].ReaderClass.Create;
Break;
end;
- if Result = nil then raise Exception.Create('Unsuported vector graphics format.');
+ if Result = nil then raise Exception.Create('Unsupported vector graphics format.');
end;
procedure TvVectorialDocument.ClearTmpPath();
@@ -1073,12 +1118,20 @@ end;
{ TPath }
+constructor TPath.Create();
+begin
+ Brush.Style := bsClear;
+ inherited Create();
+end;
+
procedure TPath.Assign(APath: TPath);
begin
Len := APath.Len;
Points := APath.Points;
PointsEnd := APath.PointsEnd;
CurPoint := APath.CurPoint;
+ Pen := APath.Pen;
+ Brush := APath.Brush;
end;
function TPath.Count(): TPathSegment;
diff --git a/packages/fpvectorial/src/fpvtocanvas.pas b/packages/fpvectorial/src/fpvtocanvas.pas
index 6c21fb7524..16c3a5126b 100644
--- a/packages/fpvectorial/src/fpvtocanvas.pas
+++ b/packages/fpvectorial/src/fpvtocanvas.pas
@@ -4,7 +4,7 @@ unit fpvtocanvas;
interface
-{.$define USE_LCL_CANVAS}
+{$define USE_LCL_CANVAS}
uses
Classes, SysUtils, Math,
@@ -115,7 +115,7 @@ end;
DrawFPVectorialToCanvas(ASource, ADest, 0, ASource.Height, 1.0, -1.0);
}
-{$define FPVECTORIAL_TOCANVAS_DEBUG}
+{.$define FPVECTORIAL_TOCANVAS_DEBUG}
procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument;
{$ifdef USE_LCL_CANVAS}ADest: TCanvas;{$else}ADest: TFPCustomCanvas;{$endif}
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0);
@@ -187,15 +187,22 @@ begin
Write(Format(' M%d,%d', [CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y)]));
{$endif}
end;
- st2DLine, st3DLine:
+ st2DLineWithPen:
begin
- {$ifdef USE_LCL_CANVAS}ADest.Pen.Color := VColorToTColor(Cur2DSegment.PenColor);{$endif}
+ {$ifdef USE_LCL_CANVAS}ADest.Pen.Color := VColorToTColor(T2DSegmentWithPen(Cur2DSegment).Pen.Color);{$endif}
ADest.LineTo(CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y));
{$ifdef USE_LCL_CANVAS}ADest.Pen.Color := clBlack;{$endif}
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
Write(Format(' L%d,%d', [CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y)]));
{$endif}
end;
+ st2DLine, st3DLine:
+ begin
+ ADest.LineTo(CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y));
+ {$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
+ Write(Format(' L%d,%d', [CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y)]));
+ {$endif}
+ end;
{ To draw a bezier we need to divide the interval in parts and make
lines between this parts }
st2DBezier, st3DBezier:
@@ -321,7 +328,7 @@ begin
WriteLn(Format('Drawing Arc Center=%f,%f Radius=%f StartAngle=%f AngleLength=%f',
[CurArc.CenterX, CurArc.CenterY, CurArc.Radius, IntStartAngle/16, IntAngleLength/16]));
{$endif}
- ADest.Pen.Color := {$ifdef USE_LCL_CANVAS}VColorToTColor(CurArc.PenColor);{$else}VColorToFPColor(CurArc.PenColor);{$endif}
+ ADest.Pen.Color := {$ifdef USE_LCL_CANVAS}VColorToTColor(CurArc.Pen.Color);{$else}VColorToFPColor(CurArc.Pen.Color);{$endif}
ADest.Arc(
BoundsLeft, BoundsTop, BoundsRight, BoundsBottom,
IntStartAngle, IntAngleLength
diff --git a/packages/fpvectorial/src/svgvectorialwriter.pas b/packages/fpvectorial/src/svgvectorialwriter.pas
index a1ae21495d..65a42f5f8d 100644
--- a/packages/fpvectorial/src/svgvectorialwriter.pas
+++ b/packages/fpvectorial/src/svgvectorialwriter.pas
@@ -13,7 +13,7 @@ unit svgvectorialwriter;
interface
uses
- Classes, SysUtils, math, fpvectorial;
+ Classes, SysUtils, math, fpvectorial, fpvutils;
type
{ TvSVGVectorialWriter }
@@ -24,6 +24,7 @@ type
procedure WriteDocumentSize(AStrings: TStrings; AData: TvVectorialDocument);
procedure WriteDocumentName(AStrings: TStrings; AData: TvVectorialDocument);
procedure WritePaths(AStrings: TStrings; AData: TvVectorialDocument);
+ procedure WritePath(AIndex: Integer; APath: TPath; AStrings: TStrings; AData: TvVectorialDocument);
procedure WriteTexts(AStrings: TStrings; AData: TvVectorialDocument);
procedure ConvertFPVCoordinatesToSVGCoordinates(
const AData: TvVectorialDocument;
@@ -60,6 +61,19 @@ begin
AStrings.Add(' sodipodi:docname="New document 1">');
end;
+procedure TvSVGVectorialWriter.WritePaths(AStrings: TStrings; AData: TvVectorialDocument);
+var
+ i: Integer;
+ lPath: TPath;
+begin
+ for i := 0 to AData.GetPathCount() - 1 do
+ begin
+ lPath := AData.GetPath(i);
+ lPath.PrepareForSequentialReading;
+ WritePath(i ,lPath, AStrings, AData);
+ end;
+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.
@@ -74,90 +88,98 @@ end;
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);
+procedure TvSVGVectorialWriter.WritePath(AIndex: Integer; APath: TPath; AStrings: TStrings;
+ AData: TvVectorialDocument);
var
- i, j: Integer;
+ 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;
+ // Pen properties
+ lPenWidth: Integer;
+ lPenColor: string;
begin
- for i := 0 to AData.GetPathCount() - 1 do
+ OldPtX := 0;
+ OldPtY := 0;
+ PathStr := '';
+
+ APath.PrepareForSequentialReading();
+
+ for j := 0 to APath.Len - 1 do
begin
- OldPtX := 0;
- OldPtY := 0;
+ segment := TPathSegment(APath.Next());
- PathStr := '';
- lPath := AData.GetPath(i);
- lPath.PrepareForSequentialReading;
+ if (segment.SegmentType <> st2DLine)
+ and (segment.SegmentType <> stMoveTo)
+ and (segment.SegmentType <> st2DBezier)
+ then Break; // unsupported line type
- for j := 0 to lPath.Len - 1 do
+ // 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
- 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
+ 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, l2DSegment.X, l2DSegment.Y, PtX, PtY);
- PtX := PtX - OldPtX;
- PtY := PtY - OldPtY;
+ AData, l2DBSegment.X2, l2DBSegment.Y2, BezierCP1X, BezierCP1Y);
+ ConvertFPVCoordinatesToSVGCoordinates(
+ AData, l2DBSegment.X3, l2DBSegment.Y3, BezierCP2X, BezierCP2Y);
- 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;
- // 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
- // 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;
+ // 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;
- AStrings.Add(' ');
+ // Store the current position for future points
+ OldPtX := OldPtX + PtX;
+ OldPtY := OldPtY + PtY;
end;
+
+ // Get the Pen Width
+ if APath.Pen.Width >= 1 then lPenWidth := APath.Pen.Width
+ else lPenWidth := 1;
+
+ // Get the Pen Color
+ lPenColor := VColorToRGBHexString(APath.Pen.Color);
+
+ AStrings.Add(' ');
end;
procedure TvSVGVectorialWriter.ConvertFPVCoordinatesToSVGCoordinates(