fpvectorial: Many fixes in ODG handling, specially of custom-shape, frame and coordenates, but not yet working properly for the test file

git-svn-id: trunk@44118 -
This commit is contained in:
sekelsenmat 2014-02-17 16:13:03 +00:00
parent b699d68760
commit 16b4faacff
3 changed files with 432 additions and 29 deletions

View File

@ -344,12 +344,13 @@ type
public
RX, RY, XRotation: Double; // RX and RY are the X and Y half axis sizes
LeftmostEllipse, ClockwiseArcFlag: Boolean;
CX, CY: Double;
CX, CY: Double; // Ellipse center
CenterSetByUser: Boolean; // defines if we should use LeftmostEllipse to calculate the center, or if CX, CY is set directly
procedure CalculateCenter;
procedure CalculateEllipseBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double);
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
end;
TvFindEntityResult = (vfrNotFound, vfrFound, vfrSubpartFound);
TvRenderInfo = record
@ -490,6 +491,7 @@ type
procedure AppendMoveToSegment(AX, AY: Double);
procedure AppendLineToSegment(AX, AY: Double);
procedure AppendEllipticalArc(ARadX, ARadY, AXAxisRotation, ADestX, ADestY: Double; ALeftmostEllipse, AClockwiseArcFlag: Boolean); // See http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
procedure AppendEllipticalArcWithCenter(ARadX, ARadY, AXAxisRotation, ADestX, ADestY, ACenterX, ACenterY: Double; AClockwiseArcFlag: Boolean); // See http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
procedure Move(ADeltaX, ADeltaY: Double); override;
procedure MoveSubpart(ADeltaX, ADeltaY: Double; ASubpart: Cardinal); override;
function MoveToSubpart(ASubpart: Cardinal): TPathSegment;
@ -1791,6 +1793,8 @@ var
CX1, CY1, CX2, CY2, LeftMostX, LeftMostY, RightMostX, RightMostY: Double;
RotatedCenter: T3DPoint;
begin
if CenterSetByUser then Exit;
// Rotated Ellipse equation:
// (xcosθ+ysinθ)^2 / RX^2 + (ycosθxsinθ)^2 / RY^2 = 1
//
@ -1960,6 +1964,21 @@ begin
end;
end;
function T2DEllipticalArcSegment.GenerateDebugTree(
ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer;
var
lStr: string;
lStrLeftmostEllipse, lStrClockwiseArcFlag: string;
begin
if LeftmostEllipse then lStrLeftmostEllipse := 'true'
else lStrLeftmostEllipse := 'false';
if ClockwiseArcFlag then lStrClockwiseArcFlag := 'true'
else lStrClockwiseArcFlag := 'false';
lStr := Format('[%s] X=%f Y=%f RX=%f RY=%f LeftmostEllipse=%s ClockwiseArcFlag=%s CX=%f CY=%f',
[Self.ClassName, X, Y, RX, RY, lStrLeftmostEllipse, lStrClockwiseArcFlag, CX, CY]);
Result := ADestRoutine(lStr, APageItem);
end;
{ TvVerticalFormulaStack }
function TvVerticalFormulaStack.CalculateHeight(ADest: TFPCustomCanvas): Double;
@ -2611,6 +2630,27 @@ begin
AppendSegment(segment);
end;
procedure TPath.AppendEllipticalArcWithCenter(ARadX, ARadY, AXAxisRotation,
ADestX, ADestY, ACenterX, ACenterY: Double; AClockwiseArcFlag: Boolean);
var
segment: T2DEllipticalArcSegment;
begin
segment := T2DEllipticalArcSegment.Create;
segment.SegmentType := st2DEllipticalArc;
segment.X := ADestX;
segment.Y := ADestY;
segment.RX := ARadX;
segment.RY := ARadY;
segment.CX := ACenterX;
segment.CY := ACenterY;
segment.XRotation := AXAxisRotation;
segment.LeftmostEllipse := False; // which value would it have?
segment.ClockwiseArcFlag := AClockwiseArcFlag;
segment.CenterSetByUser := True;
AppendSegment(segment);
end;
procedure TPath.Move(ADeltaX, ADeltaY: Double);
var
i: Integer;

View File

@ -55,6 +55,8 @@ uses
fpvectorial, fpvutils, lazutf8;
type
TDoubleArray = array of Double;
TSVGTokenType = (
// moves
sttMoveTo, sttRelativeMoveTo,
@ -95,6 +97,7 @@ type
Destructor Destroy; override;
procedure AddToken(AStr: string);
procedure TokenizePathString(AStr: string);
procedure TokenizeFunctions(AStr: string);
end;
TODGStyle = class(TvStyle)
@ -116,7 +119,7 @@ type
end;
TCustomShapeInfo = packed record
Width, Height: Double; // in milimiters
Left, Top, Width, Height: Double; // in milimiters
ViewBox_Left, ViewBox_Top, ViewBox_Width, ViewBox_Height: Double; // unitless
VariableNames: array of string;
VariableValues: array of Double;
@ -133,6 +136,9 @@ type
FMasterPages: TFPList; // of TODGMasterPage;
//FSVGPathTokenizer: TSVGPathTokenizer;
//
function ReadSpaceSeparatedFloats(AInput: string; AOtherSeparators: string): TDoubleArray;
function ReadSpaceSeparatedStrings(AInput: string; AOtherSeparators: string): TStringList;
//
procedure DeleteStyle(data,arg:pointer);
procedure ApplyGraphicAttributeToPenAndBrush(ANodeName, ANodeValue: string; var APen: TvPen; var ABrush: TvBrush);
procedure ApplyGraphicAttributeToEntity(ANodeName, ANodeValue: string; ADest: TvEntityWithPen);
@ -166,11 +172,14 @@ type
procedure ConvertODGDeltaToFPVDelta(
const AData: TvVectorialPage;
const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
procedure ConvertViewBoxCoordinatesToODGCoordinates(
procedure ConvertViewBoxCoordinatesToFPVCoordinates(
const AData: TvVectorialPage; const AInfo: TCustomShapeInfo;
const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
procedure ConvertViewBoxDeltaToFPVDelta(
const AInfo: TCustomShapeInfo;
const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
procedure ConvertViewBoxDeltaToODGDelta(
const AInfo: TCustomShapeInfo;
procedure ConvertMilimiterCoordinatesToFPVCoordinates(
const AData: TvVectorialPage;
const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
public
{ General reading methods }
@ -434,6 +443,106 @@ begin
if (lState = 0) and (lTmpStr <> '') then AddToken(lTmpStr);
end;
procedure TSVGPathTokenizer.TokenizeFunctions(AStr: string);
const
Str_Space: Char = ' ';
Str_Start_Params: Char = '(';
Str_End_Params: Char = ')';
ListOfCommandLetters: set of Char = ['a'..'d', 'f'..'z', 'A'..'D', 'F'..'Z'];
var
i: Integer;
lTmpStr: string = '';
lState: Integer;
lFirstTmpStrChar, lCurChar: Char;
lToken: TSVGToken;
begin
lState := 0;
i := 1;
while i <= Length(AStr) do
begin
case lState of
0: // Adding to the tmp string
begin
lCurChar := AStr[i];
if lCurChar in [Str_Start_Params, Str_End_Params] then
begin
lState := 1;
// Add the token
lToken := TSVGToken.Create;
lToken.StrValue := lTmpStr;
Tokens.Add(lToken);
//
lTmpStr := '';
end
else
begin
lTmpStr := lTmpStr + lCurChar;
end;
Inc(i);
end;
1: // Removing spaces
begin
if AStr[i] <> Str_Space then lState := 0
else Inc(i);
end;
end;
end;
// If there is a token still to be added, add it now
if (lState = 0) and (lTmpStr <> '') then AddToken(lTmpStr);
end;
function TvODGVectorialReader.ReadSpaceSeparatedFloats(AInput: string;
AOtherSeparators: string): TDoubleArray;
var
lStrings: TStringList;
lInputStr: string;
lMatrixElements: array of Double;
i: Integer;
begin
lStrings := TStringList.Create;
try
lStrings.Delimiter := ' ';
// now other separator too
lInputStr := AInput;
for i := 1 to Length(AOtherSeparators) do
begin
lInputStr := StringReplace(lInputStr, AOtherSeparators[i], ' ', [rfReplaceAll]);
end;
//
lStrings.DelimitedText := lInputStr;
SetLength(lMatrixElements, lStrings.Count);
for i := 0 to lStrings.Count-1 do
begin
lMatrixElements[i] := StringWithUnitToFloat(lStrings.Strings[i]);
end;
Result := lMatrixElements;
finally
lStrings.Free;
end;
end;
function TvODGVectorialReader.ReadSpaceSeparatedStrings(AInput: string;
AOtherSeparators: string): TStringList;
var
i: Integer;
lInputStr: String;
begin
Result := TStringList.Create;
Result.Delimiter := ' ';
// now other separator too
lInputStr := AInput;
for i := 1 to Length(AOtherSeparators) do
begin
lInputStr := StringReplace(lInputStr, AOtherSeparators[i], ' ', [rfReplaceAll]);
end;
//
Result.DelimitedText := lInputStr;
end;
procedure TvODGVectorialReader.DeleteStyle(data, arg: pointer);
begin
TObject(data).Free;
@ -869,25 +978,23 @@ begin
sttEllipticArcTo, sttRelativeEllipticArcTo, sttEllipticArcToWithAngle:
begin
CurToken := TSVGToken(lTokenizer.Tokens.Items[j+1]);
x1 := CurToken.Value;// + ADeltaX;
x1 := CurToken.Value;
CurToken := TSVGToken(lTokenizer.Tokens.Items[j+2]);
y1 := CurToken.Value;// + ADeltaY;
y1 := CurToken.Value;
CurToken := TSVGToken(lTokenizer.Tokens.Items[j+3]);
x2 := ResolveEnhancedGeometryFormula(CurToken, AInfo) / 2;
CurToken := TSVGToken(lTokenizer.Tokens.Items[j+4]);
y2 := ResolveEnhancedGeometryFormula(CurToken, AInfo) / 2;
CurToken := TSVGToken(lTokenizer.Tokens.Items[j+5]);
t1 := CurToken.Value;
t1 := t1 / Pi;
t1 := DegToRad(t1);
CurToken := TSVGToken(lTokenizer.Tokens.Items[j+6]);
t2 := CurToken.Value;
t2 := t2 / Pi;
t2 := DegToRad(t2);
ConvertViewBoxCoordinatesToODGCoordinates(AInfo, x1, y1, x1, y1);
ConvertODGCoordinatesToFPVCoordinates(AData, x1, y1, x1, y1);
ConvertViewBoxCoordinatesToFPVCoordinates(AData, AInfo, x1, y1, x1, y1);
ConvertViewBoxDeltaToODGDelta(AInfo, x2, y2, x2, y2);
ConvertODGDeltaToFPVDelta(AData, x2, y2, x2, y2);
ConvertViewBoxDeltaToFPVDelta(AInfo, x2, y2, x2, y2);
// Parametrized Ellipse equation
lSrcX := x2 * Cos(t1) + x1;
@ -897,7 +1004,7 @@ begin
// See http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
ADest.AppendMoveToSegment(lSrcX, lSrcY);
ADest.AppendEllipticalArc(x2, y2, 0, lDestX, lDestY, False, False);
ADest.AppendEllipticalArcWithCenter(x2, y2, 0, lDestX, lDestY, x1, y1, t2 > t1);
Inc(j, 7);
end;
@ -979,6 +1086,12 @@ begin
lWidth := 0.0;
lHeight := 0.0;
lSkewX := 0.0;
lSkewY := 0.0;
lRotate := 0.0;
lTranslateX := 0.0;
lTranslateY := 0.0;
lGroup := TvEntityWithSubEntities.Create(AData);
lPath := TPath.Create(Adata);
lGroup.AddEntity(lPath);
@ -1006,10 +1119,7 @@ begin
// lEllipse.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue)
end;
ConvertODGCoordinatesToFPVCoordinates(
AData, x1, y1, x2, y2);
ConvertODGDeltaToFPVDelta(
AData, lWidth, lHeight, lWidth, lHeight);
ConvertMilimiterCoordinatesToFPVCoordinates(AData, x1, y1, x2, y2);
// Go through sub-nodes
lCurNode := ANode.FirstChild;
@ -1030,6 +1140,8 @@ begin
end;
'draw:enhanced-geometry':
begin
lInfo.Left := x1 + lTranslateX;
lInfo.Top := y1 + lTranslateY;
lInfo.Width := lWidth;
lInfo.Height := lHeight;
ReadEnhancedGeometryNodeToTPath(lCurNode, AData, lPath, x1, y1, lInfo);
@ -1312,6 +1424,15 @@ end;
procedure TvODGVectorialReader.GetDrawTransforms(AInputStr: string; out ASkewX,
ASkewY, ARotate, ATranslateX, ATranslateY: Double);
var
// transform
MA, MB, MC, MD, ME, MF: Double;
lMTranslateX, lMTranslateY, lMScaleX, lMScaleY, lMSkewX, lMSkewY, lMRotate: Double;
lTokenizer: TSVGPathTokenizer;
i: Integer;
lFunctionName, lParamStr: string;
lMatrixElements: array of Double;
lMatrixStrElements: TStringList;
begin
ASkewX := 0.0;
ASkewY := 0.0;
@ -1319,7 +1440,60 @@ begin
ATranslateX := 0.0;
ATranslateY := 0.0;
// Examples:
// transform="matrix(0.860815 0 -0 1.07602 339.302 489.171)"
// transform="scale(0.24) translate(0, 35)"
// transform="rotate(90)"
lTokenizer := TSVGPathTokenizer.Create;
try
lTokenizer.TokenizeFunctions(AInputStr);
i := 0;
while i < lTokenizer.Tokens.Count-1 do
begin
lFunctionName := Trim(lTokenizer.Tokens.Items[i].StrValue);
lParamStr := lTokenizer.Tokens.Items[i+1].StrValue;
//lMatrixElements := ReadSpaceSeparatedFloats(lParamStr, ',');
if lFunctionName = 'matrix' then
begin
{ReadSVGTransformationMatrix(lParamStr, MA, MB, MC, MD, ME, MF);
ConvertTransformationMatrixToOperations(MA, MB, MC, MD, ME, MF,
lMTranslateX, lMTranslateY, lMScaleX, lMScaleY, lMSkewX, lMSkewY, lMRotate);
ConvertSVGDeltaToFPVDelta(nil,
lMTranslateX, lMTranslateY,
lMTranslateX, lMTranslateY);
ADestEntity.Move(lMTranslateX, lMTranslateY);
ADestEntity.Scale(lMScaleX, lMScaleY); }
end
else if lFunctionName = 'scale' then
begin
;
end
else if lFunctionName = 'translate' then
begin
lMatrixStrElements := ReadSpaceSeparatedStrings(lParamStr, ',');
try
ATranslateX := StringWithUnitToFloat(lMatrixStrElements.Strings[0]);
ATranslateY := StringWithUnitToFloat(lMatrixStrElements.Strings[1]);
finally
lMatrixStrElements.Free;
end;
//ADestEntity.Move(lMatrixElements[0], lMatrixElements[1]);
end
else if lFunctionName = 'rotate' then
begin
//ADestEntity.Rotate(lMatrixElements[0], Make3DPoint(0, 0, 0));
end;
Inc(i, 2);
end;
finally
lTokenizer.Free;
end;
end;
function TvODGVectorialReader.ReadSVGColor(AValue: string): TFPColor;
@ -1764,25 +1938,44 @@ end;
We should use these formulas to obtain the X, Y position from something in the drawing:
Xreal = Xenhanced-path * (draw:custom-shape_svg:width / svg:viewBox_Width) + svg:viewBox_Xo;
Xreal = (Xenhanced-path - ViewBox_X0) * (draw:custom-shape_svg:width / svg:viewBox_Width) + svg:viewBox_Xo;
And the same for Y
For sizes just use without Xo
<draw:custom-shape draw:style-name="gr6" draw:text-style-name="P1" draw:layer="layout" svg:width="3.783cm" svg:height="3.602cm" draw:transform="skewX (-0.000872664625997166) rotate (-1.62385433605552) translate (19.087cm 20.266cm)">
<text:p />
<draw:enhanced-geometry svg:viewBox="0 0 21600 21600" draw:glue-points="10800 0 3163 3163 0 10800 3163 18437 10800 21600 18437 18437 21600 10800 18437 3163" draw:text-areas="3163 3163 18437 18437" draw:type="ring" draw:modifiers="647.870425914817"
draw:enhanced-path="U 10800 10800 10800 10800 0 360 Z U 10800 10800 ?f1 ?f1 0 360 N">
<draw:equation draw:name="f0" draw:formula="$0 " />
<draw:equation draw:name="f1" draw:formula="10800-$0 " />
<draw:handle draw:handle-position="$0 10800" draw:handle-range-x-minimum="0" draw:handle-range-x-maximum="10800" />
</draw:enhanced-geometry>
</draw:custom-shape>
}
procedure TvODGVectorialReader.ConvertViewBoxCoordinatesToODGCoordinates(
const AInfo: TCustomShapeInfo; const ASrcX, ASrcY: Double; var ADestX,
ADestY: Double);
procedure TvODGVectorialReader.ConvertViewBoxCoordinatesToFPVCoordinates(
const AData: TvVectorialPage; const AInfo: TCustomShapeInfo;
const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
begin
ADestX := ASrcX * (AInfo.Width / AInfo.ViewBox_Width) + AInfo.ViewBox_Left;
ADestY := ASrcY * (AInfo.Height / AInfo.ViewBox_Height) + AInfo.ViewBox_Top;
ADestX := (ASrcX - AInfo.ViewBox_Left) * (AInfo.Width / AInfo.ViewBox_Width) + AInfo.Left;
ADestY := (ASrcY - AInfo.ViewBox_Top) * (AInfo.Height / AInfo.ViewBox_Height) + AInfo.Top;
ADestY := AData.Height - ADestY;
end;
procedure TvODGVectorialReader.ConvertViewBoxDeltaToODGDelta(
procedure TvODGVectorialReader.ConvertViewBoxDeltaToFPVDelta(
const AInfo: TCustomShapeInfo; const ASrcX, ASrcY: Double; var ADestX,
ADestY: Double);
begin
ADestX := ASrcX * (AInfo.Width / AInfo.ViewBox_Width);
ADestY := ASrcY * (AInfo.Height / AInfo.ViewBox_Height);
ADestX := (ASrcX - AInfo.ViewBox_Left) * (AInfo.Width / AInfo.ViewBox_Width);
ADestY := (ASrcY - AInfo.ViewBox_Top) * (AInfo.Height / AInfo.ViewBox_Height);
end;
procedure TvODGVectorialReader.ConvertMilimiterCoordinatesToFPVCoordinates(
const AData: TvVectorialPage; const ASrcX, ASrcY: Double; var ADestX,
ADestY: Double);
begin
ADestX := ASrcX;
ADestY := AData.Height - ASrcY;
end;
constructor TvODGVectorialReader.Create;

View File

@ -92,6 +92,8 @@ type
function ReadEntityFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
function ReadCircleFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
function ReadEllipseFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
function ReadFrameFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
function ReadFrameTextFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
function ReadImageFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
procedure ReadLayerFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
function ReadLineFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
@ -957,7 +959,7 @@ begin
i := 0;
while i < lTokenizer.Tokens.Count-1 do
begin
lFunctionName := lTokenizer.Tokens.Items[i].StrValue;
lFunctionName := Trim(lTokenizer.Tokens.Items[i].StrValue);
lParamStr := lTokenizer.Tokens.Items[i+1].StrValue;
lMatrixElements := ReadSpaceSeparatedFloats(lParamStr, ',');
@ -1226,6 +1228,7 @@ begin
case lEntityName of
'circle': Result := ReadCircleFromNode(ANode, AData, ADoc);
'ellipse': Result := ReadEllipseFromNode(ANode, AData, ADoc);
'frame': Result := ReadFrameFromNode(ANode, AData, ADoc);
'g': ReadLayerFromNode(ANode, AData, ADoc);
'image': Result := ReadImageFromNode(ANode, AData, ADoc);
'line': Result := ReadLineFromNode(ANode, AData, ADoc);
@ -1342,6 +1345,173 @@ begin
Result := lEllipse;
end;
{
<draw:frame draw:style-name="gr5" draw:layer="layout" svg:width="9.024cm" svg:height="0.963cm"
draw:transform="rotate (-1.58737695468884) translate (2.3cm 1.197cm)">
<draw:text-box>
<text:p>Jump opposite arm and leg up</text:p>
</draw:text-box>
</draw:frame>
<draw:frame draw:style-name="gr5" draw:layer="layout" svg:width="15.07cm" svg:height="1.115cm" svg:x="2.6cm" svg:y="26.9cm">
<draw:text-box>
<text:p>
<text:span text:style-name="T1">Back muscle movement</text:span>
<text:span text:style-name="T2">opposite</text:span>
<text:span text:style-name="T3">arm</text:span>
and
<text:span text:style-name="T3">leg</text:span>
up
</text:p>
</draw:text-box>
</draw:frame>
}
function TvSVGVectorialReader.ReadFrameFromNode(ANode: TDOMNode;
AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
var
lTextStr: string = '';
lx, ly: double;
lText: TvText;
i: Integer;
lNodeName, lNodeValue, lSubNodeName, lSubNodeValue: DOMString;
lCurNode, lCurSubNode: TDOMNode;
begin
lx := 0.0;
ly := 0.0;
Result := nil;
lText := nil;//TvText.Create(nil);
// Apply the layer style
ApplyLayerStyles(lText);
// read the attributes
for i := 0 to ANode.Attributes.Length - 1 do
begin
lNodeName := ANode.Attributes.Item[i].NodeName;
lNodeValue := ANode.Attributes.Item[i].NodeValue;
if lNodeName = 'svg:x' then
lx := lx + StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'svg:y' then
ly := ly + StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'draw:style-name' then
ReadSVGStyle(lNodeValue, lText);
end;
// Get the text contents
lCurNode := Anode.FirstChild;
while lCurNode <> nil do
begin
lNodeName := LowerCase(lCurNode.NodeName);
if lNodeName <> 'draw:text-box' then Continue;
lCurSubNode := lCurNode.FirstChild;
while lCurSubNode <> nil do
begin
lSubNodeName := LowerCase(lCurSubNode.NodeName);
if lSubNodeName <> 'draw:text-box' then Continue;
lText := ReadFrameTextFromNode(lCurNode, AData, ADoc) as TvText;
Break;
lCurSubNode := lCurSubNode.NextSibling;
end;
if lText <> nil then Break;
lCurNode := lCurNode.NextSibling;
end;
if lText = nil then Exit;
// Set the coordinates
ConvertSVGCoordinatesToFPVCoordinates(
AData, lx, ly, lText.X, lText.Y);
// Finalization
Result := lText;
end;
{
<text:p>Jump opposite arm and leg up</text:p>
<text:p>
<text:span text:style-name="T1">Back muscle movement</text:span>
<text:span text:style-name="T2">opposite</text:span>
<text:span text:style-name="T3">arm</text:span>
and
<text:span text:style-name="T3">leg</text:span>
up
</text:p>
}
function TvSVGVectorialReader.ReadFrameTextFromNode(ANode: TDOMNode;
AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
var
lTextStr: string = '';
lx, ly: double;
lText: TvText;
i: Integer;
lNodeName, lNodeValue: DOMString;
lCurNode: TDOMNode;
begin
lx := 0.0;
ly := 0.0;
lText := TvText.Create(nil);
// Apply the layer style
{ApplyLayerStyles(lText);
// read the attributes
for i := 0 to ANode.Attributes.Length - 1 do
begin
lNodeName := ANode.Attributes.Item[i].NodeName;
lNodeValue := ANode.Attributes.Item[i].NodeValue;
if lNodeName = 'x' then
lx := lx + StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'y' then
ly := ly + StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'id' then
lText.Name := lNodeValue
else if lNodeName = 'style' then
ReadSVGStyle(lNodeValue, lText);
end;}
// The text contents are inside as a child text, not as a attribute
// ex: <text x="0" y="15" fill="red" transform="rotate(30 20,40)">I love SVG</text>
if Anode.FirstChild <> nil then
lTextStr := Anode.FirstChild.NodeValue;
// Add the first line
lText.Value.Add(lTextStr);
// Recover the position if there was a transformation matrix
//lx := lx + lText.X;
//ly := ly + lText.Y;
// Set the coordinates
ConvertSVGCoordinatesToFPVCoordinates(
AData, lx, ly, lText.X, lText.Y);
// Now add other lines, which appear as <tspan ...>another line</tspan>
// Example:
// <text x="10" y="20" style="fill:red;">Several lines:
// <tspan x="10" y="45">First line</tspan>
// <tspan x="10" y="70">Second line</tspan>
// </text>
// These other lines can be positioned, so they need to appear as independent TvText elements
{ lCurNode := Anode.FirstChild;
while lCurNode <> nil do
begin
lNodeName := LowerCase(lCurNode.NodeName);
if lNodeName <> 'tspan' then Continue;
ReadTextFromNode(lCurNode, AData, ADoc);
lCurNode := lCurNode.NextSibling;
end;}
// Finalization
Result := lText;
end;
// <image width="92.5" x="0" y="0" height="76.0429"
// xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKMAAACGCAYAA
// ACxDToF......" clip-path="url(#Clip0)" transform="matrix(1 0 0 1 0 0)"/>