diff --git a/components/fpvectorial/fpvectorial.pas b/components/fpvectorial/fpvectorial.pas index ac554ea720..b73d3073b8 100644 --- a/components/fpvectorial/fpvectorial.pas +++ b/components/fpvectorial/fpvectorial.pas @@ -408,6 +408,7 @@ type procedure CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); override; procedure Render(ADest: TFPCustomCanvas; ARenderInfo: TvRenderInfo; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override; + function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override; end; { TvPolygon } @@ -2049,6 +2050,22 @@ begin {$endif} end; +function TvRectangle.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; + APageItem: Pointer): Pointer; +var + lStr: string; + lCurPathSeg: TPathSegment; +begin + Result := inherited GenerateDebugTree(ADestRoutine, APageItem); + // Add the font debug info in a sub-item + lStr := Format('[TvRectangle] Text=%s CX=%f CY=%f CZ=%f RX=%f RY=%f', + [Text, + CX, CY, CZ, + RX, RY + ]); + ADestRoutine(lStr, Result); +end; + { TvPolygon } procedure TvPolygon.Render(ADest: TFPCustomCanvas; ARenderInfo: TvRenderInfo; diff --git a/components/fpvectorial/odgvectorialreader.pas b/components/fpvectorial/odgvectorialreader.pas index 2070e3e4ea..fdf856aff6 100644 --- a/components/fpvectorial/odgvectorialreader.pas +++ b/components/fpvectorial/odgvectorialreader.pas @@ -55,7 +55,7 @@ uses fpvectorial, fpvutils, lazutf8; type -{ TSVGTokenType = ( + TSVGTokenType = ( // moves sttMoveTo, sttRelativeMoveTo, // Close Path @@ -69,6 +69,8 @@ type sttQuadraticBezierTo, sttRelativeQuadraticBezierTo, // Elliptic curves sttEllipticArcTo, sttRelativeEllipticArcTo, + // ToDo: Find out what these are + sttUnknown, // numbers sttFloatValue); @@ -89,7 +91,7 @@ type Destructor Destroy; override; procedure AddToken(AStr: string); procedure TokenizePathString(AStr: string); - end; } + end; TODGMasterPage = class public @@ -128,8 +130,11 @@ type // procedure ReadStyleNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument); procedure ReadStyleStyleNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument); + procedure ReadEnhancedGeometryNodeToTPath(ANode: TDOMNode; AData: TvVectorialPage; ADest: TPath; ADeltaX, ADeltaY: Double); + procedure ConvertPathStringToTPath(AStr: string; AData: TvVectorialPage; ADest: TPath; ADeltaX, ADeltaY: Double); // procedure ReadElement(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument); + procedure ReadCustomShapeNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument); procedure ReadEllipseNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument); procedure ReadFrameNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument); procedure ReadLineNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument); @@ -214,7 +219,7 @@ const { TSVGPathTokenizer } -{constructor TSVGPathTokenizer.Create; +constructor TSVGPathTokenizer.Create; begin inherited Create; @@ -232,6 +237,22 @@ begin inherited Destroy; end; +{ + + + + + + +} procedure TSVGPathTokenizer.AddToken(AStr: string); var lToken: TSVGToken; @@ -243,29 +264,43 @@ begin if lStr = '' then Exit; // Moves - if lStr[1] = 'M' then lToken.TokenType := sttMoveTo - else if lStr[1] = 'm' then lToken.TokenType := sttRelativeMoveTo + case lStr[1] of + 'M': lToken.TokenType := sttMoveTo; + 'm': lToken.TokenType := sttRelativeMoveTo; // Close Path - else if lStr[1] = 'Z' then lToken.TokenType := sttClosePath - else if lStr[1] = 'z' then lToken.TokenType := sttClosePath + 'Z': lToken.TokenType := sttClosePath; + 'z': lToken.TokenType := sttClosePath; // Lines - else if lStr[1] = 'L' then lToken.TokenType := sttLineTo - else if lStr[1] = 'l' then lToken.TokenType := sttRelativeLineTo - else if lStr[1] = 'H' then lToken.TokenType := sttHorzLineTo - else if lStr[1] = 'h' then lToken.TokenType := sttRelativeHorzLineTo - else if lStr[1] = 'V' then lToken.TokenType := sttVertLineTo - else if lStr[1] = 'v' then lToken.TokenType := sttRelativeVertLineTo + 'L': lToken.TokenType := sttLineTo; + 'l': lToken.TokenType := sttRelativeLineTo; + 'H': lToken.TokenType := sttHorzLineTo; + 'h': lToken.TokenType := sttRelativeHorzLineTo; + 'V': lToken.TokenType := sttVertLineTo; + 'v': lToken.TokenType := sttRelativeVertLineTo; // cubic Bézier curve commands - else if lStr[1] = 'C' then lToken.TokenType := sttBezierTo - else if lStr[1] = 'c' then lToken.TokenType := sttRelativeBezierTo + 'C': lToken.TokenType := sttBezierTo; + 'c': lToken.TokenType := sttRelativeBezierTo; // quadratic beziers - else if lStr[1] = 'Q' then lToken.TokenType := sttQuadraticBezierTo - else if lStr[1] = 'q' then lToken.TokenType := sttRelativeQuadraticBezierTo + 'Q': lToken.TokenType := sttQuadraticBezierTo; + 'q': lToken.TokenType := sttRelativeQuadraticBezierTo; // Elliptic curves - else if lStr[1] = 'A' then lToken.TokenType := sttEllipticArcTo - else if lStr[1] = 'a' then lToken.TokenType := sttRelativeEllipticArcTo - else + 'A': lToken.TokenType := sttEllipticArcTo; + 'a': lToken.TokenType := sttRelativeEllipticArcTo; + // Types that I don't know what they do + 'U', 'N', 'e': lToken.TokenType := sttUnknown; + // Constants + '$': begin + lToken.TokenType := sttFloatValue; + lToken.Value := 0; // ToDo + end; + // Named Equations + '?': + begin + lToken.TokenType := sttFloatValue; + lToken.Value := 0; // ToDo + end; + else lToken.TokenType := sttFloatValue; lToken.Value := StrToFloat(AStr, FPointSeparator); end; @@ -312,6 +347,12 @@ begin AddToken(lTmpStr); lTmpStr := ''; end + else if lCurChar in ['?', '$'] then + begin + if lTmpStr <> '' then AddToken(lTmpStr); + lState := 2; + lTmpStr := lCurChar; + end else begin // Check for a break, from letter to number @@ -337,12 +378,26 @@ begin if AStr[i] <> Str_Space then lState := 0 else Inc(i); end; + 2: // Adding a special group, such as "$2" or "?f3", which stop only at a space + begin + lCurChar := AStr[i]; + if lCurChar = Str_Space then + begin + lState := 1; + AddToken(lTmpStr); + lTmpStr := ''; + end + else + lTmpStr := lTmpStr + lCurChar; + + 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;} +end; procedure TvODGVectorialReader.DeleteStyle(data, arg: pointer); begin @@ -593,6 +648,110 @@ begin FStyles.Add(lStyle); end; +{ + + Rectangle + + +} +procedure TvODGVectorialReader.ReadEnhancedGeometryNodeToTPath(ANode: TDOMNode; + AData: TvVectorialPage; ADest: TPath; ADeltaX, ADeltaY: Double); +var + i: Integer; + lNodeName, lNodeValue: string; +begin + // 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 = 'draw:enhanced-path' then + begin + ConvertPathStringToTPath(lNodeValue, AData, ADest, ADeltaX, ADeltaY); + end; + end; +end; + +procedure TvODGVectorialReader.ConvertPathStringToTPath(AStr: string; + AData: TvVectorialPage; ADest: TPath; ADeltaX, ADeltaY: Double); +var + x1, y1, x2, y2, lCurX, lCurY: double; + j: Integer; + lNodeName, lNodeValue: string; + lTokenizer: TSVGPathTokenizer; + CurToken: TSVGToken; +begin + x1 := 0.0; + y1 := 0.0; + x2 := 0.0; + y2 := 0.0; + lCurX := 0.0; + lCurY := 0.0; + + lTokenizer := TSVGPathTokenizer.Create; + try + lTokenizer.TokenizePathString(AStr); + + j := 0; + while j < lTokenizer.Tokens.Count do + begin + CurToken := TSVGToken(lTokenizer.Tokens.Items[j]); + + case CurToken.TokenType of + // moves + sttMoveTo, sttRelativeMoveTo: + begin + CurToken := TSVGToken(lTokenizer.Tokens.Items[j+1]); + x1 := CurToken.Value + ADeltaX; + CurToken := TSVGToken(lTokenizer.Tokens.Items[j+2]); + y1 := CurToken.Value + ADeltaY; + ConvertODGCoordinatesToFPVCoordinates( + AData, x1, y1, x1, y1); + ADest.AppendMoveToSegment(x1, y1); + Inc(j, 3); + end; + // Close Path + sttClosePath: Inc(j); + // lines + sttLineTo, sttRelativeLineTo, + sttHorzLineTo, sttRelativeHorzLineTo, sttVertLineTo, sttRelativeVertLineTo: + begin + Inc(j); + while TSVGToken(lTokenizer.Tokens.Items[j]).TokenType = sttFloatValue do + begin + CurToken := TSVGToken(lTokenizer.Tokens.Items[j+1]); + x1 := CurToken.Value + ADeltaX; + CurToken := TSVGToken(lTokenizer.Tokens.Items[j+2]); + y1 := CurToken.Value + ADeltaY; + ConvertODGCoordinatesToFPVCoordinates( + AData, x1, y1, x1, y1); + ADest.AppendLineToSegment(x1, y1); + + Inc(j, 2); + + if j >= lTokenizer.Tokens.Count then Break; + end; + end; +{ // cubic beziers + sttBezierTo, sttRelativeBezierTo, + // quadratic beziers + sttQuadraticBezierTo, sttRelativeQuadraticBezierTo, + // Elliptic curves + sttEllipticArcTo, sttRelativeEllipticArcTo, + // numbers + sttFloatValue);} + else + Inc(j); + //raise Exception.Create('[TvODGVectorialReader.ConvertPathStringToTPath] Unexpected token type!'); + end; + end; + finally + lTokenizer.Free; + end; +end; + procedure TvODGVectorialReader.ReadElement(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument); var @@ -600,6 +759,7 @@ var begin Str := LowerCase(ANode.NodeName); case Str of + 'draw:custom-shape': ReadCustomShapeNode(ANode, AData, ADoc); 'draw:ellipse': ReadEllipseNode(ANode, AData, ADoc); 'draw:frame': ReadFrameNode(ANode, AData, ADoc); 'draw:line': ReadLineNode(ANode, AData, ADoc); @@ -607,6 +767,95 @@ begin end; end; +{ + + Rectangle + + +} +procedure TvODGVectorialReader.ReadCustomShapeNode(ANode: TDOMNode; + AData: TvVectorialPage; ADoc: TvVectorialDocument); +var + x1, y1, x2, y2, lWidth, lHeight: double; + i: Integer; + lNodeName, lNodeValue: string; + lCurNode, lTextNode, lEnhancedGeometryNode, lDrawTypeAttrib: TDOMNode; + lSkewX, lSkewY, lRotate, lTranslateX, lTranslateY: Double; + // various possible custom shape types + lGroup: TvEntityWithSubEntities; + lPath: TPath; + lText: TvText; +begin + x1 := 0.0; + y1 := 0.0; + x2 := 0.0; + y2 := 0.0; + lWidth := 0.0; + lHeight := 0.0; + + lGroup := TvEntityWithSubEntities.Create; + lPath := TPath.Create; + lGroup.AddEntity(lPath); + + // 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:width' then + lWidth := StringWithUnitToFloat(lNodeValue) + else if lNodeName = 'svg:height' then + lHeight := StringWithUnitToFloat(lNodeValue) + else if lNodeName = 'draw:transform' then + GetDrawTransforms(ANode.Attributes.Item[i].NodeValue, lSkewX, lSkewY, lRotate, lTranslateX, lTranslateY) + else if lNodeName = 'svg:x' then + x1 := StringWithUnitToFloat(lNodeValue) + else if lNodeName = 'svg:y' then + y1 := StringWithUnitToFloat(lNodeValue) + else if lNodeName = 'draw:style-name' then + ApplyStyleByNameToEntity(lNodeValue, lPath) + else if lNodeName = 'draw:text-style-name' then + ApplyTextStyleByNameToEntity(lNodeValue, lPath); +// else if lNodeName = 'id' then +// lEllipse.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue) + end; + + ConvertODGCoordinatesToFPVCoordinates( + AData, x1, y1, x2, y2); + ConvertODGDeltaToFPVDelta( + AData, lWidth, lHeight, lWidth, lHeight); + + // Go through sub-nodes + lCurNode := ANode.FirstChild; + while lCurNode <> nil do + begin + lNodeName := lCurNode.NodeName; + + case lNodeName of + 'text:p': + begin + if lCurNode.FirstChild <> nil then + begin + lText := TvText.Create; + lNodeValue := lCurNode.FirstChild.NodeValue; + lText.Value.Add(lNodeValue); + lGroup.AddEntity(lText); + end; + end; + 'draw:enhanced-geometry': + begin + ReadEnhancedGeometryNodeToTPath(lCurNode, AData, lPath, x1, y1); + end; + end; + + lCurNode := lCurNode.NextSibling; + end; + + AData.AddEntity(lGroup); +end; + {