mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-25 04:39:24 +02:00
fpvectorial: Improves a lot the SVG reader. Finishes the support for commands without a command token, adds support for reading the style of the <svg> tag. Radad test image now almost perfect.
git-svn-id: trunk@40852 -
This commit is contained in:
parent
47d1848397
commit
810f515e86
@ -65,6 +65,7 @@ type
|
|||||||
FLayerStylesKeys, FLayerStylesValues: TFPList; // of TStringList;
|
FLayerStylesKeys, FLayerStylesValues: TFPList; // of TStringList;
|
||||||
function ReadSVGColor(AValue: string): TFPColor;
|
function ReadSVGColor(AValue: string): TFPColor;
|
||||||
function ReadSVGStyle(AValue: string; ADestEntity: TvEntityWithPen; AUseFillAsPen: Boolean = False): TvSetPenBrushAndFontElements;
|
function ReadSVGStyle(AValue: string; ADestEntity: TvEntityWithPen; AUseFillAsPen: Boolean = False): TvSetPenBrushAndFontElements;
|
||||||
|
function ReadSVGStyleToStyleLists(AValue: string; AStyleKeys, AStyleValues: TStringList): TvSetPenBrushAndFontElements;
|
||||||
function ReadSVGPenStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
|
function ReadSVGPenStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
|
||||||
function ReadSVGBrushStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPenAndBrush): TvSetPenBrushAndFontElements;
|
function ReadSVGBrushStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPenAndBrush): TvSetPenBrushAndFontElements;
|
||||||
function ReadSVGFontStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPenBrushAndFont): TvSetPenBrushAndFontElements;
|
function ReadSVGFontStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPenBrushAndFont): TvSetPenBrushAndFontElements;
|
||||||
@ -78,6 +79,7 @@ type
|
|||||||
procedure ReadLineFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
procedure ReadLineFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
||||||
procedure ReadPathFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
procedure ReadPathFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
||||||
procedure ReadPathFromString(AStr: string; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
procedure ReadPathFromString(AStr: string; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
||||||
|
procedure ReadNextPathCommand(ACurTokenType: TSVGTokenType; var i: Integer; var CurX, CurY: Double; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
||||||
procedure ReadPointsFromString(AStr: string; AData: TvVectorialPage; ADoc: TvVectorialDocument; AClosePath: Boolean);
|
procedure ReadPointsFromString(AStr: string; AData: TvVectorialPage; ADoc: TvVectorialDocument; AClosePath: Boolean);
|
||||||
procedure ReadPolyFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
procedure ReadPolyFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
||||||
procedure ReadRectFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
procedure ReadRectFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
||||||
@ -95,6 +97,7 @@ type
|
|||||||
constructor Create; override;
|
constructor Create; override;
|
||||||
Destructor Destroy; override;
|
Destructor Destroy; override;
|
||||||
procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
|
procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
|
||||||
|
procedure ReadFromXML(Doc: TXMLDocument; AData: TvVectorialDocument);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@ -698,6 +701,36 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// style="fill:none;stroke:black;stroke-width:3"
|
||||||
|
function TvSVGVectorialReader.ReadSVGStyleToStyleLists(AValue: string;
|
||||||
|
AStyleKeys, AStyleValues: TStringList): TvSetPenBrushAndFontElements;
|
||||||
|
var
|
||||||
|
lStr, lStyleKeyStr, lStyleValueStr: String;
|
||||||
|
lStrings: TStringList;
|
||||||
|
i, lPosEqual: Integer;
|
||||||
|
begin
|
||||||
|
Result := [];
|
||||||
|
if AValue = '' then Exit;
|
||||||
|
|
||||||
|
// Now split using ";" separator
|
||||||
|
lStrings := TStringList.Create;
|
||||||
|
try
|
||||||
|
lStrings.Delimiter := ';';
|
||||||
|
lStrings.DelimitedText := LowerCase(AValue);
|
||||||
|
for i := 0 to lStrings.Count-1 do
|
||||||
|
begin
|
||||||
|
lStr := lStrings.Strings[i];
|
||||||
|
lPosEqual := Pos(':', lStr);
|
||||||
|
lStyleKeyStr := Copy(lStr, 0, lPosEqual-1);
|
||||||
|
lStyleValueStr := Copy(lStr, lPosEqual+1, Length(lStr));
|
||||||
|
AStyleKeys.Add(lStyleKeyStr);
|
||||||
|
AStyleValues.Add(lStyleValueStr);
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
lStrings.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function TvSVGVectorialReader.ReadSVGPenStyleWithKeyAndValue(AKey,
|
function TvSVGVectorialReader.ReadSVGPenStyleWithKeyAndValue(AKey,
|
||||||
AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
|
AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
|
||||||
var
|
var
|
||||||
@ -982,7 +1015,9 @@ begin
|
|||||||
lNodeName := ANode.Attributes.Item[i].NodeName;
|
lNodeName := ANode.Attributes.Item[i].NodeName;
|
||||||
if lNodeName = 'style' then
|
if lNodeName = 'style' then
|
||||||
begin
|
begin
|
||||||
{$ifndef SVG_MERGE_LAYER_STYLES}
|
{$ifdef SVG_MERGE_LAYER_STYLES}
|
||||||
|
ReadSVGStyleToStyleLists(ANode.Attributes.Item[i].NodeValue, lLayerStyleKeys, lLayerStyleValues);
|
||||||
|
{$else}
|
||||||
lLayer.SetPenBrushAndFontElements += ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lLayer)
|
lLayer.SetPenBrushAndFontElements += ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lLayer)
|
||||||
{$endif}
|
{$endif}
|
||||||
end
|
end
|
||||||
@ -1116,6 +1151,7 @@ var
|
|||||||
CurX, CurY: Double;
|
CurX, CurY: Double;
|
||||||
lCurTokenType, lLastCommandToken: TSVGTokenType;
|
lCurTokenType, lLastCommandToken: TSVGTokenType;
|
||||||
lDebugStr: String;
|
lDebugStr: String;
|
||||||
|
lTmpTokenType: TSVGTokenType;
|
||||||
begin
|
begin
|
||||||
FSVGPathTokenizer.Tokens.Clear;
|
FSVGPathTokenizer.Tokens.Clear;
|
||||||
FSVGPathTokenizer.TokenizePathString(AStr);
|
FSVGPathTokenizer.TokenizePathString(AStr);
|
||||||
@ -1128,237 +1164,245 @@ begin
|
|||||||
while i < FSVGPathTokenizer.Tokens.Count do
|
while i < FSVGPathTokenizer.Tokens.Count do
|
||||||
begin
|
begin
|
||||||
lCurTokenType := FSVGPathTokenizer.Tokens.Items[i].TokenType;
|
lCurTokenType := FSVGPathTokenizer.Tokens.Items[i].TokenType;
|
||||||
if not (lCurTokenType = sttFloatValue) then lLastCommandToken := lCurTokenType;
|
if not (lCurTokenType = sttFloatValue) then
|
||||||
// --------------
|
|
||||||
// Moves
|
|
||||||
// --------------
|
|
||||||
if lCurTokenType in [sttMoveTo, sttRelativeMoveTo] then
|
|
||||||
begin
|
begin
|
||||||
X := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
lLastCommandToken := lCurTokenType;
|
||||||
Y := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
ReadNextPathCommand(lCurTokenType, i, CurX, CurY, AData, ADoc);
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
|
||||||
|
|
||||||
// take care of relative or absolute
|
|
||||||
if lCurTokenType = sttRelativeMoveTo then
|
|
||||||
begin
|
|
||||||
CurX := CurX + X;
|
|
||||||
CurY := CurY + Y;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
CurX := X;
|
|
||||||
CurY := Y;
|
|
||||||
end;
|
|
||||||
AData.AddMoveToPath(CurX, CurY);
|
|
||||||
|
|
||||||
Inc(i, 3);
|
|
||||||
end
|
|
||||||
// --------------
|
|
||||||
// Lines which appear without a command token and after a MoveTo
|
|
||||||
// --------------
|
|
||||||
else if (lCurTokenType = sttFloatValue) and (lLastCommandToken in [sttMoveTo, sttRelativeMoveTo]) then
|
|
||||||
begin
|
|
||||||
X := FSVGPathTokenizer.Tokens.Items[i].Value;
|
|
||||||
Y := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
|
||||||
|
|
||||||
CurX := X;
|
|
||||||
CurY := Y;
|
|
||||||
AData.AddLineToPath(CurX, CurY);
|
|
||||||
|
|
||||||
Inc(i, 2);
|
|
||||||
end
|
|
||||||
// --------------
|
|
||||||
// Close Path
|
|
||||||
// --------------
|
|
||||||
else if lCurTokenType = sttClosePath then
|
|
||||||
begin
|
|
||||||
// Get the first point
|
|
||||||
AData.GetTmpPathStartPos(X, Y);
|
|
||||||
|
|
||||||
// And repeat it
|
|
||||||
CurX := X;
|
|
||||||
CurY := Y;
|
|
||||||
AData.AddLineToPath(CurX, CurY);
|
|
||||||
|
|
||||||
Inc(i, 3);
|
|
||||||
end
|
|
||||||
// --------------
|
|
||||||
// Lines
|
|
||||||
// --------------
|
|
||||||
else if lCurTokenType in [sttLineTo, sttRelativeLineTo, sttHorzLineTo,
|
|
||||||
sttRelativeHorzLineTo, sttVertLineTo, sttRelativeVertLineTo] then
|
|
||||||
begin
|
|
||||||
X := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
|
||||||
if not (lCurTokenType in [sttHorzLineTo, sttRelativeHorzLineTo, sttVertLineTo, sttRelativeVertLineTo]) then
|
|
||||||
Y := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
|
||||||
|
|
||||||
// "l" LineTo uses relative coordenates in SVG
|
|
||||||
if lCurTokenType in [sttRelativeLineTo, sttRelativeHorzLineTo, sttRelativeVertLineTo] then
|
|
||||||
begin
|
|
||||||
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
|
||||||
CurX := CurX + X;
|
|
||||||
CurY := CurY + Y;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
|
||||||
CurX := X;
|
|
||||||
CurY := Y;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// horizontal and vertical line corrections
|
|
||||||
if lCurTokenType in [sttHorzLineTo, sttRelativeHorzLineTo] then
|
|
||||||
Y := 0
|
|
||||||
else if lCurTokenType in [sttVertLineTo, sttRelativeVertLineTo] then
|
|
||||||
begin
|
|
||||||
Y := X;
|
|
||||||
X := 0;
|
|
||||||
end;
|
|
||||||
|
|
||||||
AData.AddLineToPath(CurX, CurY);
|
|
||||||
|
|
||||||
if not (lCurTokenType in [sttHorzLineTo, sttRelativeHorzLineTo, sttVertLineTo, sttRelativeVertLineTo]) then
|
|
||||||
Inc(i, 3)
|
|
||||||
else Inc(i, 2);
|
|
||||||
end
|
|
||||||
// --------------
|
|
||||||
// Cubic Bezier
|
|
||||||
// --------------
|
|
||||||
else if lCurTokenType in [sttBezierTo, sttRelativeBezierTo,
|
|
||||||
sttSmoothBezierTo, sttRelativeSmoothBezierTo] then
|
|
||||||
begin
|
|
||||||
if lCurTokenType in [sttBezierTo, sttRelativeBezierTo] then
|
|
||||||
begin
|
|
||||||
X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
|
||||||
Y2 := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
|
||||||
X3 := FSVGPathTokenizer.Tokens.Items[i+3].Value;
|
|
||||||
Y3 := FSVGPathTokenizer.Tokens.Items[i+4].Value;
|
|
||||||
X := FSVGPathTokenizer.Tokens.Items[i+5].Value;
|
|
||||||
Y := FSVGPathTokenizer.Tokens.Items[i+6].Value;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
X2 := CurX;
|
|
||||||
Y2 := CurY;
|
|
||||||
X3 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
|
||||||
Y3 := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
|
||||||
X := FSVGPathTokenizer.Tokens.Items[i+3].Value;
|
|
||||||
Y := FSVGPathTokenizer.Tokens.Items[i+4].Value;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates
|
|
||||||
if lCurTokenType in [sttRelativeBezierTo, sttRelativeSmoothBezierTo] then
|
|
||||||
begin
|
|
||||||
ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2);
|
|
||||||
ConvertSVGDeltaToFPVDelta(AData, X3, Y3, X3, Y3);
|
|
||||||
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X2, Y2, X2, Y2);
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X3, Y3, X3, Y3);
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
|
||||||
end;
|
|
||||||
|
|
||||||
if lCurTokenType = sttRelativeBezierTo then
|
|
||||||
begin
|
|
||||||
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X3 + CurX, Y3 + CurY, X + CurX, Y + CurY);
|
|
||||||
CurX := CurX + X;
|
|
||||||
CurY := CurY + Y;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
AData.AddBezierToPath(X2, Y2, X3, Y3, X, Y);
|
|
||||||
CurX := X;
|
|
||||||
CurY := Y;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if lCurTokenType in [sttBezierTo, sttRelativeBezierTo] then
|
|
||||||
Inc(i, 7)
|
|
||||||
else Inc(i, 5);
|
|
||||||
end
|
|
||||||
// --------------
|
|
||||||
// Quadratic Bezier
|
|
||||||
// --------------
|
|
||||||
else if lCurTokenType in [sttQuadraticBezierTo, sttRelativeQuadraticBezierTo] then
|
|
||||||
begin
|
|
||||||
X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
|
||||||
Y2 := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
|
||||||
X := FSVGPathTokenizer.Tokens.Items[i+3].Value;
|
|
||||||
Y := FSVGPathTokenizer.Tokens.Items[i+4].Value;
|
|
||||||
|
|
||||||
// Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates
|
|
||||||
if lCurTokenType in [sttRelativeQuadraticBezierTo] then
|
|
||||||
begin
|
|
||||||
ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2);
|
|
||||||
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X2, Y2, X2, Y2);
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
|
||||||
end;
|
|
||||||
|
|
||||||
if lCurTokenType = sttRelativeQuadraticBezierTo then
|
|
||||||
begin
|
|
||||||
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X2 + CurX, Y2 + CurY, X + CurX, Y + CurY);
|
|
||||||
CurX := CurX + X;
|
|
||||||
CurY := CurY + Y;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
AData.AddBezierToPath(X2, Y2, X2, Y2, X, Y);
|
|
||||||
CurX := X;
|
|
||||||
CurY := Y;
|
|
||||||
end;
|
|
||||||
|
|
||||||
Inc(i, 5);
|
|
||||||
end
|
|
||||||
// --------------
|
|
||||||
// Elliptical arcs
|
|
||||||
// --------------
|
|
||||||
else if lCurTokenType in [sttEllipticArcTo, sttRelativeEllipticArcTo] then
|
|
||||||
begin
|
|
||||||
{X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
|
||||||
Y2 := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
|
||||||
X := FSVGPathTokenizer.Tokens.Items[i+3].Value;
|
|
||||||
Y := FSVGPathTokenizer.Tokens.Items[i+4].Value;
|
|
||||||
|
|
||||||
// Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates
|
|
||||||
if lCurTokenType in [sttRelativeQuadraticBezierTo] then
|
|
||||||
begin
|
|
||||||
ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2);
|
|
||||||
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X2, Y2, X2, Y2);
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
|
||||||
end;
|
|
||||||
|
|
||||||
if lCurTokenType = sttRelativeQuadraticBezierTo then
|
|
||||||
begin
|
|
||||||
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X2 + CurX, Y2 + CurY, X + CurX, Y + CurY);
|
|
||||||
CurX := CurX + X;
|
|
||||||
CurY := CurY + Y;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
AData.AddBezierToPath(X2, Y2, X2, Y2, X, Y);
|
|
||||||
CurX := X;
|
|
||||||
CurY := Y;
|
|
||||||
end; }
|
|
||||||
|
|
||||||
Inc(i, 8);
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
Inc(i);
|
lTmpTokenType := lLastCommandToken;
|
||||||
|
if lLastCommandToken = sttMoveTo then lTmpTokenType := sttLineTo;
|
||||||
|
if lLastCommandToken = sttRelativeMoveTo then lTmpTokenType := sttRelativeLineTo;
|
||||||
|
Dec(i);// because there is command token
|
||||||
|
ReadNextPathCommand(lTmpTokenType, i, CurX, CurY, AData, ADoc);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TvSVGVectorialReader.ReadNextPathCommand(ACurTokenType: TSVGTokenType;
|
||||||
|
var i: Integer; var CurX, CurY: Double; AData: TvVectorialPage;
|
||||||
|
ADoc: TvVectorialDocument);
|
||||||
|
var
|
||||||
|
X, Y, X2, Y2, X3, Y3: Double;
|
||||||
|
lCurTokenType: TSVGTokenType;
|
||||||
|
lDebugStr: String;
|
||||||
|
begin
|
||||||
|
lCurTokenType := ACurTokenType;
|
||||||
|
// --------------
|
||||||
|
// Moves
|
||||||
|
// --------------
|
||||||
|
if lCurTokenType in [sttMoveTo, sttRelativeMoveTo] then
|
||||||
|
begin
|
||||||
|
X := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||||
|
Y := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||||
|
|
||||||
|
// take care of relative or absolute
|
||||||
|
if lCurTokenType = sttRelativeMoveTo then
|
||||||
|
begin
|
||||||
|
CurX := CurX + X;
|
||||||
|
CurY := CurY + Y;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
CurX := X;
|
||||||
|
CurY := Y;
|
||||||
|
end;
|
||||||
|
AData.AddMoveToPath(CurX, CurY);
|
||||||
|
|
||||||
|
Inc(i, 3);
|
||||||
|
end
|
||||||
|
// --------------
|
||||||
|
// Close Path
|
||||||
|
// --------------
|
||||||
|
else if lCurTokenType = sttClosePath then
|
||||||
|
begin
|
||||||
|
// Get the first point
|
||||||
|
AData.GetTmpPathStartPos(X, Y);
|
||||||
|
|
||||||
|
// And repeat it
|
||||||
|
CurX := X;
|
||||||
|
CurY := Y;
|
||||||
|
AData.AddLineToPath(CurX, CurY);
|
||||||
|
|
||||||
|
Inc(i, 3);
|
||||||
|
end
|
||||||
|
// --------------
|
||||||
|
// Lines
|
||||||
|
// --------------
|
||||||
|
else if lCurTokenType in [sttLineTo, sttRelativeLineTo, sttHorzLineTo,
|
||||||
|
sttRelativeHorzLineTo, sttVertLineTo, sttRelativeVertLineTo] then
|
||||||
|
begin
|
||||||
|
X := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||||
|
if not (lCurTokenType in [sttHorzLineTo, sttRelativeHorzLineTo, sttVertLineTo, sttRelativeVertLineTo]) then
|
||||||
|
Y := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
||||||
|
|
||||||
|
// "l" LineTo uses relative coordenates in SVG
|
||||||
|
if lCurTokenType in [sttRelativeLineTo, sttRelativeHorzLineTo, sttRelativeVertLineTo] then
|
||||||
|
begin
|
||||||
|
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
||||||
|
CurX := CurX + X;
|
||||||
|
CurY := CurY + Y;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||||
|
CurX := X;
|
||||||
|
CurY := Y;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// horizontal and vertical line corrections
|
||||||
|
if lCurTokenType in [sttHorzLineTo, sttRelativeHorzLineTo] then
|
||||||
|
Y := 0
|
||||||
|
else if lCurTokenType in [sttVertLineTo, sttRelativeVertLineTo] then
|
||||||
|
begin
|
||||||
|
Y := X;
|
||||||
|
X := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
AData.AddLineToPath(CurX, CurY);
|
||||||
|
|
||||||
|
if not (lCurTokenType in [sttHorzLineTo, sttRelativeHorzLineTo, sttVertLineTo, sttRelativeVertLineTo]) then
|
||||||
|
Inc(i, 3)
|
||||||
|
else Inc(i, 2);
|
||||||
|
end
|
||||||
|
// --------------
|
||||||
|
// Cubic Bezier
|
||||||
|
// --------------
|
||||||
|
else if lCurTokenType in [sttBezierTo, sttRelativeBezierTo,
|
||||||
|
sttSmoothBezierTo, sttRelativeSmoothBezierTo] then
|
||||||
|
begin
|
||||||
|
if lCurTokenType in [sttBezierTo, sttRelativeBezierTo] then
|
||||||
|
begin
|
||||||
|
X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||||
|
Y2 := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
||||||
|
X3 := FSVGPathTokenizer.Tokens.Items[i+3].Value;
|
||||||
|
Y3 := FSVGPathTokenizer.Tokens.Items[i+4].Value;
|
||||||
|
X := FSVGPathTokenizer.Tokens.Items[i+5].Value;
|
||||||
|
Y := FSVGPathTokenizer.Tokens.Items[i+6].Value;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
X2 := CurX;
|
||||||
|
Y2 := CurY;
|
||||||
|
X3 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||||
|
Y3 := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
||||||
|
X := FSVGPathTokenizer.Tokens.Items[i+3].Value;
|
||||||
|
Y := FSVGPathTokenizer.Tokens.Items[i+4].Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates
|
||||||
|
if lCurTokenType in [sttRelativeBezierTo, sttRelativeSmoothBezierTo] then
|
||||||
|
begin
|
||||||
|
ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2);
|
||||||
|
ConvertSVGDeltaToFPVDelta(AData, X3, Y3, X3, Y3);
|
||||||
|
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X2, Y2, X2, Y2);
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X3, Y3, X3, Y3);
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if lCurTokenType = sttRelativeBezierTo then
|
||||||
|
begin
|
||||||
|
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X3 + CurX, Y3 + CurY, X + CurX, Y + CurY);
|
||||||
|
CurX := CurX + X;
|
||||||
|
CurY := CurY + Y;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
AData.AddBezierToPath(X2, Y2, X3, Y3, X, Y);
|
||||||
|
CurX := X;
|
||||||
|
CurY := Y;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if lCurTokenType in [sttBezierTo, sttRelativeBezierTo] then
|
||||||
|
Inc(i, 7)
|
||||||
|
else Inc(i, 5);
|
||||||
|
end
|
||||||
|
// --------------
|
||||||
|
// Quadratic Bezier
|
||||||
|
// --------------
|
||||||
|
else if lCurTokenType in [sttQuadraticBezierTo, sttRelativeQuadraticBezierTo] then
|
||||||
|
begin
|
||||||
|
X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||||
|
Y2 := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
||||||
|
X := FSVGPathTokenizer.Tokens.Items[i+3].Value;
|
||||||
|
Y := FSVGPathTokenizer.Tokens.Items[i+4].Value;
|
||||||
|
|
||||||
|
// Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates
|
||||||
|
if lCurTokenType in [sttRelativeQuadraticBezierTo] then
|
||||||
|
begin
|
||||||
|
ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2);
|
||||||
|
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X2, Y2, X2, Y2);
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if lCurTokenType = sttRelativeQuadraticBezierTo then
|
||||||
|
begin
|
||||||
|
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X2 + CurX, Y2 + CurY, X + CurX, Y + CurY);
|
||||||
|
CurX := CurX + X;
|
||||||
|
CurY := CurY + Y;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
AData.AddBezierToPath(X2, Y2, X2, Y2, X, Y);
|
||||||
|
CurX := X;
|
||||||
|
CurY := Y;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Inc(i, 5);
|
||||||
|
end
|
||||||
|
// --------------
|
||||||
|
// Elliptical arcs
|
||||||
|
// --------------
|
||||||
|
else if lCurTokenType in [sttEllipticArcTo, sttRelativeEllipticArcTo] then
|
||||||
|
begin
|
||||||
|
{X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||||
|
Y2 := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
||||||
|
X := FSVGPathTokenizer.Tokens.Items[i+3].Value;
|
||||||
|
Y := FSVGPathTokenizer.Tokens.Items[i+4].Value;
|
||||||
|
|
||||||
|
// Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates
|
||||||
|
if lCurTokenType in [sttRelativeQuadraticBezierTo] then
|
||||||
|
begin
|
||||||
|
ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2);
|
||||||
|
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X2, Y2, X2, Y2);
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if lCurTokenType = sttRelativeQuadraticBezierTo then
|
||||||
|
begin
|
||||||
|
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X2 + CurX, Y2 + CurY, X + CurX, Y + CurY);
|
||||||
|
CurX := CurX + X;
|
||||||
|
CurY := CurY + Y;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
AData.AddBezierToPath(X2, Y2, X2, Y2, X, Y);
|
||||||
|
CurX := X;
|
||||||
|
CurY := Y;
|
||||||
|
end; }
|
||||||
|
|
||||||
|
Inc(i, 8);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Inc(i);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TvSVGVectorialReader.ReadPointsFromString(AStr: string;
|
procedure TvSVGVectorialReader.ReadPointsFromString(AStr: string;
|
||||||
AData: TvVectorialPage; ADoc: TvVectorialDocument; AClosePath: Boolean);
|
AData: TvVectorialPage; ADoc: TvVectorialDocument; AClosePath: Boolean);
|
||||||
var
|
var
|
||||||
@ -1649,33 +1693,86 @@ procedure TvSVGVectorialReader.ReadFromStream(AStream: TStream;
|
|||||||
AData: TvVectorialDocument);
|
AData: TvVectorialDocument);
|
||||||
var
|
var
|
||||||
Doc: TXMLDocument;
|
Doc: TXMLDocument;
|
||||||
lCurNode: TDOMNode;
|
|
||||||
lPage: TvVectorialPage;
|
|
||||||
begin
|
begin
|
||||||
try
|
try
|
||||||
// Read in xml file from the stream
|
// Read in xml file from the stream
|
||||||
ReadXMLFile(Doc, AStream);
|
ReadXMLFile(Doc, AStream);
|
||||||
|
ReadFromXML(Doc, AData);
|
||||||
// Read the properties of the <svg> tag
|
|
||||||
AData.Width := StringWithUnitToFloat(Doc.DocumentElement.GetAttribute('width'));
|
|
||||||
AData.Height := StringWithUnitToFloat(Doc.DocumentElement.GetAttribute('height'));
|
|
||||||
|
|
||||||
// Now process the elements
|
|
||||||
lCurNode := Doc.DocumentElement.FirstChild;
|
|
||||||
lPage := AData.AddPage();
|
|
||||||
lPage.Width := AData.Width;
|
|
||||||
lPage.Height := AData.Height;
|
|
||||||
while Assigned(lCurNode) do
|
|
||||||
begin
|
|
||||||
ReadEntityFromNode(lCurNode, lPage, AData);
|
|
||||||
lCurNode := lCurNode.NextSibling;
|
|
||||||
end;
|
|
||||||
finally
|
finally
|
||||||
// finally, free the document
|
// finally, free the document
|
||||||
Doc.Free;
|
Doc.Free;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TvSVGVectorialReader.ReadFromXML(Doc: TXMLDocument;
|
||||||
|
AData: TvVectorialDocument);
|
||||||
|
var
|
||||||
|
lCurNode: TDOMNode;
|
||||||
|
lPage: TvVectorialPage;
|
||||||
|
{$ifdef SVG_MERGE_LAYER_STYLES}
|
||||||
|
lLayerStyleKeys, lLayerStyleValues: TStringList;
|
||||||
|
{$endif}
|
||||||
|
lNodeName: DOMString;
|
||||||
|
ANode: TDOMElement;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
// ----------------
|
||||||
|
// Read the properties of the <svg> tag
|
||||||
|
// ----------------
|
||||||
|
AData.Width := StringWithUnitToFloat(Doc.DocumentElement.GetAttribute('width'));
|
||||||
|
AData.Height := StringWithUnitToFloat(Doc.DocumentElement.GetAttribute('height'));
|
||||||
|
{$ifdef SVG_MERGE_LAYER_STYLES}
|
||||||
|
FLayerStylesKeys.Clear;
|
||||||
|
FLayerStylesValues.Clear;
|
||||||
|
lLayerStyleKeys := TStringList.Create;
|
||||||
|
lLayerStyleValues := TStringList.Create;
|
||||||
|
FLayerStylesKeys.Add(lLayerStyleKeys);
|
||||||
|
FLayerStylesValues.Add(lLayerStyleValues);
|
||||||
|
{$endif}
|
||||||
|
ANode := Doc.DocumentElement;
|
||||||
|
for i := 0 to ANode.Attributes.Length - 1 do
|
||||||
|
begin
|
||||||
|
lNodeName := ANode.Attributes.Item[i].NodeName;
|
||||||
|
if lNodeName = 'style' then
|
||||||
|
begin
|
||||||
|
{$ifdef SVG_MERGE_LAYER_STYLES}
|
||||||
|
ReadSVGStyleToStyleLists(ANode.Attributes.Item[i].NodeValue, lLayerStyleKeys, lLayerStyleValues);
|
||||||
|
{$endif}
|
||||||
|
end
|
||||||
|
else if IsAttributeFromStyle(lNodeName) then
|
||||||
|
begin
|
||||||
|
{$ifdef SVG_MERGE_LAYER_STYLES}
|
||||||
|
lLayerStyleKeys.Add(lNodeName);
|
||||||
|
lLayerStyleValues.Add(UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue));
|
||||||
|
{$endif}
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// ----------------
|
||||||
|
// Now process the elements
|
||||||
|
// ----------------
|
||||||
|
lCurNode := Doc.DocumentElement.FirstChild;
|
||||||
|
lPage := AData.AddPage();
|
||||||
|
lPage.Width := AData.Width;
|
||||||
|
lPage.Height := AData.Height;
|
||||||
|
while Assigned(lCurNode) do
|
||||||
|
begin
|
||||||
|
ReadEntityFromNode(lCurNode, lPage, AData);
|
||||||
|
lCurNode := lCurNode.NextSibling;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// ----------------
|
||||||
|
// Remove the memory of the styles
|
||||||
|
// ----------------
|
||||||
|
{$ifdef SVG_MERGE_LAYER_STYLES}
|
||||||
|
// Now remove the style from this layer
|
||||||
|
FLayerStylesKeys.Remove(lLayerStyleKeys);
|
||||||
|
lLayerStyleKeys.Free;
|
||||||
|
FLayerStylesValues.Remove(lLayerStyleValues);
|
||||||
|
lLayerStyleValues.Free;
|
||||||
|
{$endif}
|
||||||
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
|
|
||||||
RegisterVectorialReader(TvSVGVectorialReader, vfSVG);
|
RegisterVectorialReader(TvSVGVectorialReader, vfSVG);
|
||||||
|
Loading…
Reference in New Issue
Block a user