Merged revision(s) 51232 #d593e000f8, 51257 #804a4b285a, 51262 #094f14ccb8, 51297 #cb29b6e05a from trunk:

fpvectorial: Supported rotated linear gradients. Support entity- and page-relative coordinates for gradient vectors.
........
fpvectorial: Support all linear gradients, translate and rotate for rectangles, circles, ellipses. 
........
fpvectorial: Complete rotation and translation for all 2d segments of TPath
........
fpvectorial: Fix rendering of rotated text
........

git-svn-id: branches/fixes_1_6@51324 -
This commit is contained in:
maxim 2016-01-18 22:55:11 +00:00
parent be08d09833
commit 18ead5d0f9
3 changed files with 1167 additions and 487 deletions

File diff suppressed because it is too large Load Diff

View File

@ -42,6 +42,7 @@ type
function FPColorToRGBHexString(AColor: TFPColor): string;
function RGBToFPColor(AR, AG, AB: byte): TFPColor; inline;
function MixColors(AColor1, AColor2: TFPColor; APos, AMax: Double): TFPColor;
function GradientColor(AColors: TvGradientColors; AValue: Double): TFPColor;
// Coordinate Conversion routines
function CanvasCoordsToFPVectorial(AY: Integer; AHeight: Integer): Integer; inline;
function CanvasTextPosToFPVectorial(AY: Integer; ACanvasHeight, ATextHeight: Integer): Integer;
@ -49,7 +50,9 @@ function CoordToCanvasX(ACoord: Double; ADestX: Integer; AMulX: Double): Integer
function CoordToCanvasY(ACoord: Double; ADestY: Integer; AMulY: Double): Integer; inline;
// Other routines
function SeparateString(AString: string; ASeparator: char): T10Strings;
function Make3DPoint(AX, AY, AZ: Double): T3DPoint;
function Make3DPoint(AX, AY, AZ: Double): T3DPoint; overload; inline;
function Make3DPoint(AX, AY: Double): T3DPoint; overload; inline;
function Point2D(AX, AY: Double): T2DPoint; inline;
// Mathematical routines
function LineEquation_GetPointAndTangentForLength(AStart, AEnd: T3DPoint; ADistance: Double; out AX, AY, ATangentAngle: Double): Boolean;
procedure EllipticalArcToBezier(Xc, Yc, Rx, Ry, startAngle, endAngle: Double; var P1, P2, P3, P4: T3DPoint);
@ -64,12 +67,15 @@ function BezierEquation_GetPointAndTangentForLength(P1, P2, P3, P4: T3DPoint;
function CalcEllipseCenter(x1,y1, x2,y2, rx,ry, phi: Double; fa, fs: Boolean;
out cx,cy, lambda: Double): Boolean;
function CalcEllipsePointAngle(x,y, rx,ry, cx,cy, phi: Double): Double;
procedure CalcEllipsePoint(angle, rx,ry, cx,cy, phi: Double; out x,y: Double);
procedure CalcEllipsePoint(t, rx,ry, cx,cy, phi: Double; out x,y: Double);
procedure ConvertPathToPolygons(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double;
var PolygonPoints: TPointsArray; var PolygonStartIndexes: TIntegerDynArray);
procedure ConvertPathToPoints(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray);
function GetLinePolygonIntersectionPoints(ACoord: Double;
const APoints: T2DPointsArray; ACoordIsX: Boolean): T2DPointsArray;
function Rotate2DPoint(P, RotCenter: TPoint; alpha:double): TPoint;
function Rotate3DPointInXY(P, RotCenter: T3DPoint; alpha:double): T3DPoint;
procedure NormalizeRect(var ARect: TRect);
// Transformation matrix operations
// See http://www.useragentman.com/blog/2011/01/07/css3-matrix-transform-for-the-mathematically-challenged/
procedure ConvertTransformationMatrixToOperations(AA, AB, AC, AD, AE, AF: Double; out ATranslateX, ATranslateY, AScaleX, AScaleY, ASkewX, ASkewY, ARotate: Double);
@ -115,12 +121,48 @@ begin
Result.Alpha := $FFFF;
end;
{@@ Returns AColor1 if APos = 0, AColor2 if APos = AMax, or interpolates between }
function MixColors(AColor1, AColor2: TFPColor; APos, AMax: Double): TFPColor;
var
f1, f2: Double;
begin
Result.Alpha := Round(AColor1.Alpha * APos / AMax + AColor2.Alpha * (AMax - APos) / AMax);
Result.Red := Round(AColor1.Red * APos / AMax + AColor2.Red * (AMax - APos) / AMax);
Result.Green := Round(AColor1.Green * APos / AMax + AColor2.Green * (AMax - APos) / AMax);
Result.Blue := Round(AColor1.Blue * APos / AMax + AColor2.Blue * (AMax - APos) / AMax);
f1 := (AMax - APos) / AMax;
f2 := APos / AMax;
Result.Alpha := Round(AColor1.Alpha * f1 + AColor2.Alpha * f2);
Result.Red := Round(AColor1.Red * f1 + AColor2.Red * f2);
Result.Green := Round(AColor1.Green * f1 + AColor2.Green * f2);
Result.Blue := Round(AColor1.Blue * f1 + AColor2.Blue * f2);
end;
{@@ Assigns a color to the specified value. The color is interpolated between
the colors defined in AColors.
}
function GradientColor(AColors: TvGradientColors; AValue: Double): TFPColor;
var
i: Integer;
c1, c2: TFPColor;
p1, p2: Double;
begin
// Return first color if AValue is below the first color position
if AValue <= AColors[0].Position then
Result := AColors[0].Color
else
// Return last color if AValue is above the last color position
if AValue >= AColors[High(AColors)].Position then
Result := AColors[High(AColors)].Color
else
// Find pair of colors positions which bracket the specified value and
// interpolate color
for i:= High(AColors)-1 downto 0 do
if AValue >= AColors[i].Position then
begin
c1 := AColors[i].Color;
c2 := AColors[i+1].Color;
p1 := AColors[i].Position;
p2 := AColors[i+1].Position;
Result := MixColors(c1, c2, AValue - p1, p2 - p1);
exit;
end;
end;
{@@ Converts the coordinate system from a TCanvas to FPVectorial
@ -193,6 +235,12 @@ begin
end;
end;
function Point2D(AX, AY: Double): T2DPoint;
begin
Result.X := AX;
Result.Y := AY;
end;
function Make3DPoint(AX, AY, AZ: Double): T3DPoint;
begin
Result.X := AX;
@ -200,6 +248,13 @@ begin
Result.Z := AZ;
end;
function Make3DPoint(AX, AY: Double): T3DPoint;
begin
Result.X := AX;
Result.Y := AY;
Result.Z := 0;
end;
{ Considering a counter-clockwise arc, elliptical and alligned to the axises
An elliptical Arc can be converted to
@ -252,7 +307,8 @@ var
lLineAngle: Double; // to X axis
begin
Result := False;
lLineAngle := arctan((AEnd.Y-AStart.Y) / (AEnd.X - AStart.X));
// lLineAngle := arctan((AEnd.Y-AStart.Y) / (AEnd.X - AStart.X));
lLineAngle := arctan2(AEnd.Y - AStart.Y, AEnd.X - AStart.X);
AX := AStart.X + ADistance * Cos(lLineAngle);
AY := AStart.Y + ADistance * Sin(lLineAngle);
end;
@ -471,7 +527,7 @@ begin
P := Rotate3dPointInXY(Make3dPoint(x-cx, y-cy, 0), Make3dPoint(0, 0, 0), phi);
// Correctly speaking, above line should use -phi, instead of phi. But
// Make3DPointInXY seems to define the angle in the opposite way.
Result := arctan2(P.Y, P.X);
Result := arctan2(P.Y/ry, P.X/rx);
if Result < 0 then Result := TWO_PI + Result;
end;
@ -479,18 +535,18 @@ end;
parameters:
- rx, ry: major and minor radius
- phi: rotation angle of the ellipse (angle between major axis and x axis)
- angle: angle from ellipse center between x axis and the point
- t: angle between x axis and line from ellipse center to point
parameterized:
x = Cx + RX*cos(t)*cos(phi) - RY*sin(t)*sin(phi) [1]
y = Cy + RY*sin(t)*cos(phi) + RX*cos(t)*sin(phi) [2] }
procedure CalcEllipsePoint(angle, rx,ry, cx,cy, phi: Double; out x,y: Double);
x = cx + rx*cos(t)*cos(phi) - ry*sin(t)*sin(phi) [1]
y = cy + ry*sin(t)*cos(phi) + rx*cos(t)*sin(phi) [2] }
procedure CalcEllipsePoint(t, rx,ry, cx,cy, phi: Double; out x,y: Double);
var
P: T3dPoint;
cost, sint: Extended;
cosphi, sinphi: Extended;
begin
SinCos(angle, sint, cost);
SinCos(t, sint, cost);
SinCos(phi, sinphi, cosphi);
x := cx + rx*cost*cosphi - ry*sint*sinphi;
y := cy + ry*sint*cosphi + rx*cost*sinphi;
@ -616,10 +672,10 @@ begin
CoordX3 := CoordToCanvasX(Cur2DBSegment.X3, ADestX, AMulX);
CoordY3 := CoordToCanvasY(Cur2DBSegment.Y3, ADestY, AMulY);
AddBezierToPoints(
Make2DPoint(CoordX, CoordY),
Make2DPoint(CoordX2, CoordY2),
Make2DPoint(CoordX3, CoordY3),
Make2DPoint(CoordX4, CoordY4),
Make3DPoint(CoordX, CoordY, 0),
Make3DPoint(CoordX2, CoordY2, 0),
Make3DPoint(CoordX3, CoordY3, 0),
Make3DPoint(CoordX4, CoordY4, 0),
Points);
end;
else
@ -631,6 +687,77 @@ begin
end;
end;
function CompareDbl(P1, P2: Pointer): Integer;
var
val1, val2: ^Double;
begin
val1 := P1;
val2 := P2;
Result := CompareValue(val1^, val2^);
end;
{@@ Calculates the intersection points of a vertical (ACoordIsX = true) or
horizontal (ACoordIsX = false) line with border of the polygon specified
by APoints. Returns the coordinates of the intersection points }
function GetLinePolygonIntersectionPoints(ACoord: Double;
const APoints: T2DPointsArray; ACoordIsX: Boolean): T2DPointsArray;
const
EPS = 1e-9;
var
j: Integer;
dx, dy: Double;
xval, yval: Double;
val: ^Double;
list: TFPList;
begin
list := TFPList.Create;
if ACoordIsX then
begin
for j:=0 to High(APoints) - 1 do
begin
if ((APoints[j].X <= ACoord) and (ACoord < APoints[j+1].X)) or
((APoints[j+1].X <= ACoord) and (ACoord < APoints[j].X)) then
begin
dx := APoints[j+1].X - APoints[j].X; // can't be zero here
dy := APoints[j+1].Y - APoints[j].Y;
New(val);
val^ := APoints[j].Y + (ACoord - APoints[j].X) * dy / dx;
list.Add(val);
end;
end;
end else
begin
for j:=0 to High(APoints) - 1 do
if ((APoints[j].Y <= ACoord) and (ACoord < APoints[j+1].Y)) or
((APoints[j+1].Y <= ACoord) and (ACoord < APoints[j].Y)) then
begin
dy := APoints[j+1].Y - APoints[j].Y; // can't be zero here
dx := APoints[j+1].X - APoints[j].X;
New(val);
val^ := APoints[j].X + (ACoord - APoints[j].Y) * dx / dy;
list.Add(val);
end;
end;
// Sort intersection coordinates in ascending order
list.Sort(@CompareDbl);
SetLength(Result, list.Count);
if ACoordIsX then
for j:=0 to list.Count-1 do
Result[j] := Point2D(ACoord, Double(list[j]^))
else
for j:=0 to list.Count-1 do
Result[j] := Point2D(Double(list[j]^), ACoord);
// Clean-up
for j:=list.Count-1 downto 0 do
begin
val := List[j];
Dispose(val);
end;
list.Free;
end;
// Rotates a point P around RotCenter
function Rotate2DPoint(P, RotCenter: TPoint; alpha:double): TPoint;
var
@ -654,8 +781,27 @@ begin
SinCos(alpha, sinus, cosinus);
P.x := P.x - RotCenter.x;
P.y := P.y - RotCenter.y;
result.x := Round(p.x*cosinus + p.y*sinus) + RotCenter.x;
result.y := Round(-p.x*sinus + p.y*cosinus) + RotCenter.y;
result.x := Round( p.x*cosinus + p.y*sinus) + RotCenter.x;
result.y := Round(-p.x*sinus + p.y*cosinus) + RotCenter.y;
result.z := P.z;
end;
procedure NormalizeRect(var ARect: TRect);
var
tmp: Integer;
begin
if ARect.Left > ARect.Right then
begin
tmp := ARect.Left;
ARect.left := ARect.Right;
ARect.Right := tmp;
end;
if ARect.Top > ARect.Bottom then
begin
tmp := ARect.Top;
ARect.Top := ARect.Bottom;
ARect.Bottom := tmp;
end;
end;
// Current Transformation Matrix

View File

@ -100,15 +100,18 @@ type
// debug symbols
FPathNumber: Integer;
function ReadSVGColor(AValue: string): TFPColor;
function ReadSVGGradientColorStyle(AValue: STring): TFPColor;
function ReadSVGStyle(AValue: string; ADestEntity: TvEntityWithPen; ADestStyle: TvStyle = nil; AUseFillAsPen: Boolean = False): TvSetPenBrushAndFontElements;
function ReadSVGGradientColorStyle(AValue: String): TFPColor;
function ReadSVGStyle(AData: TvVectorialPage; AValue: string;
ADestEntity: TvEntityWithPen; ADestStyle: TvStyle = nil;
AUseFillAsPen: Boolean = False): TvSetPenBrushAndFontElements;
function ReadSVGStyleToStyleLists(AValue: string; AStyleKeys, AStyleValues: TStringList): TvSetPenBrushAndFontElements;
function ReadSVGPenStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
function ReadSVGBrushStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPenAndBrush): TvSetPenBrushAndFontElements;
function ReadSVGFontStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPenBrushAndFont; ADestStyle: TvStyle = nil): TvSetPenBrushAndFontElements;
function ReadSVGGeneralStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntity): TvSetPenBrushAndFontElements;
procedure ReadSVGGeneralStyleWithKeyAndValue(AData: TvVectorialPage;
AKey, AValue: string; ADestEntity: TvEntity);
function IsAttributeFromStyle(AStr: string): Boolean;
procedure ApplyLayerStyles(ADestEntity: TvEntity);
procedure ApplyLayerStyles(AData: TvVectorialPage; ADestEntity: TvEntity);
function ReadSpaceSeparatedFloats(AInput: string; AOtherSeparators: string): TDoubleArray;
procedure ReadSVGTransformationMatrix(AMatrix: string; out AA, AB, AC, AD, AE, AF: Double);
//
@ -137,6 +140,7 @@ type
function StringWithUnitToFloat(AStr: string; ACoordKind: TSVGCoordinateKind = sckUnknown;
ADefaultUnit: TSVGUnit = suPX; ATargetUnit: TSVGUnit = suPX): Double;
function StringFloatZeroToOneToWord(AStr: string): Word;
function StringWithPercentToFloat(AStr: String): Double;
procedure ConvertSVGCoordinatesToFPVCoordinates(
const AData: TvVectorialPage;
@ -173,8 +177,8 @@ const
// 90 inches per pixel = (1 / 90) * 25.4 = 0.2822
// FLOAT_MILIMETERS_PER_PIXEL = 0.3528; // DPI 72 = 1 / 72 inches per pixel
FLOAT_MILIMETERS_PER_PIXEL = 1; //0.2822; // DPI 90 = 1 / 90 inches per pixel => Actually I changed the value! Because otherwise it looks ugly!
FLOAT_PIXELS_PER_MILIMETER = 1 / FLOAT_MILIMETERS_PER_PIXEL; // DPI 90 = 1 / 90 inches per pixel
FLOAT_MILLIMETERS_PER_PIXEL = 1; //0.2822; // DPI 90 = 1 / 90 inches per pixel => Actually I changed the value! Because otherwise it looks ugly!
FLOAT_PIXELS_PER_MILIMETER = 1 / FLOAT_MILLIMETERS_PER_PIXEL; // DPI 90 = 1 / 90 inches per pixel
FLOAT_POINTS_PER_PIXEL = 0.75; // For conversion
FLOAT_PIXEL_PER_POINT = 1 / FLOAT_POINTS_PER_PIXEL; // For conversion
@ -753,7 +757,7 @@ begin
end;
// style="fill:none;stroke:black;stroke-width:3"
function TvSVGVectorialReader.ReadSVGStyle(AValue: string;
function TvSVGVectorialReader.ReadSVGStyle(AData: TvVectorialPage; AValue: string;
ADestEntity: TvEntityWithPen; ADestStyle: TvStyle = nil;
AUseFillAsPen: Boolean = False): TvSetPenBrushAndFontElements;
var
@ -781,7 +785,7 @@ begin
if ADestEntity <> nil then
begin
ReadSVGPenStyleWithKeyAndValue(lStyleKeyStr, lStyleValueStr, ADestEntity);
ReadSVGGeneralStyleWithKeyAndValue(lStyleKeyStr, lStyleValueStr, ADestEntity);
ReadSVGGeneralStyleWithKeyAndValue(AData, lStyleKeyStr, lStyleValueStr, ADestEntity);
if AUseFillAsPen and (lStyleKeyStr = 'fill') then
Result := Result + ReadSVGPenStyleWithKeyAndValue('stroke', lStyleValueStr, ADestEntity)
else if ADestEntity is TvText then
@ -1031,8 +1035,8 @@ begin
ADestStyle.SetElements := ADestStyle.SetElements + Result;
end;
function TvSVGVectorialReader.ReadSVGGeneralStyleWithKeyAndValue(AKey,
AValue: string; ADestEntity: TvEntity): TvSetPenBrushAndFontElements;
procedure TvSVGVectorialReader.ReadSVGGeneralStyleWithKeyAndValue(AData: TvVectorialPage;
AKey, AValue: string; ADestEntity: TvEntity);
var
// transform
MA, MB, MC, MD, ME, MF: Double;
@ -1087,7 +1091,17 @@ begin
end
else if lFunctionName = 'rotate' then
begin
ADestEntity.Rotate(lMatrixElements[0], Make3DPoint(0, 0, 0));
lMRotate := -DegToRad(lMatrixElements[0]);
// "-" because of orientation of svg coordinate system
lMTranslateX := 0;
lMTranslateY := 0;
if Length(lMatrixElements) > 1 then
lMTranslateX := lMatrixElements[1];
if Length(lMatrixElements) > 2 then
lMTranslateY := lMatrixElements[2];
ConvertSVGCoordinatesToFPVCoordinates(AData,
lMTranslateX, lMTranslateY, lMTranslateX, lMTranslateY);
ADestEntity.Rotate(lMRotate, Make3DPoint(lMTranslateX, lMTranslateY));
end;
Inc(i, 2);
@ -1114,7 +1128,8 @@ begin
(AStr = 'font-weight') or (AStr = 'text-anchor');
end;
procedure TvSVGVectorialReader.ApplyLayerStyles(ADestEntity: TvEntity);
procedure TvSVGVectorialReader.ApplyLayerStyles(AData: TvVectorialPage;
ADestEntity: TvEntity);
var
lStringsKeys, lStringsValues: TStringList;
i, j: Integer;
@ -1137,7 +1152,7 @@ begin
if ADestEntity is TvEntityWithPenBrushAndFont then
ReadSVGFontStyleWithKeyAndValue(lCurKey, lCurValue, ADestEntity as TvEntityWithPenBrushAndFont);
// transform
ReadSVGGeneralStyleWithKeyAndValue(lCurKey, lCurValue, ADestEntity);
ReadSVGGeneralStyleWithKeyAndValue(AData, lCurKey, lCurValue, ADestEntity);
end;
end;
end;
@ -1235,7 +1250,7 @@ var
lBrushEntity: TvEntityWithPenAndBrush;
lCurEntity: TvEntity;
lGradientColor: TvGradientColor;
x1, x2, y1, y2: string;
x1, y1, x2, y2: Double;
begin
lCurNode := ANode.FirstChild;
while Assigned(lCurNode) do
@ -1307,24 +1322,76 @@ begin
lBrushEntity := TvEntityWithPenAndBrush.Create(nil);
// <linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
x1 := 0;
x2 := 0;
y1 := 0;
y2 := 0;
for i := 0 to lCurNode.Attributes.Length - 1 do
begin
lAttrName := lCurNode.Attributes.Item[i].NodeName;
lAttrValue := lCurNode.Attributes.Item[i].NodeValue;
lAttrName := lowercase(lCurNode.Attributes.Item[i].NodeName);
lAttrValue := lowercase(lCurNode.Attributes.Item[i].NodeValue);
if lAttrName = 'id' then
lBrushEntity.Name := lAttrValue
else if lAttrName = 'x1' then
x1 := lAttrValue
else if lAttrName = 'x2' then
x2 := lAttrValue
else if lAttrName = 'y1' then
y1 := lAttrValue
else if lAttrName = 'y2' then
y2 := lAttrValue;
else
if lAttrName = 'x1' then
begin
if lAttrValue[Length(lAttrValue)] = '%' then
Include(lBrushEntity.Brush.Gradient_flags, gfRelStartX);
x1 := StringWithPercentToFloat(lAttrValue);
end else
if lAttrName = 'x2' then
begin
if lAttrValue[Length(lAttrValue)] = '%' then
Include(lBrushEntity.Brush.Gradient_flags, gfRelEndX);
x2 := StringWithPercentToFloat(lAttrValue);
end else
if lAttrName = 'y1' then
begin
if lAttrValue[Length(lAttrValue)] = '%' then
Include(lBrushEntity.Brush.Gradient_flags, gfRelStartY);
y1 := StringWithPercentToFloat(lAttrValue);
end else
if lAttrName = 'y2' then
begin
if lAttrValue[Length(lAttrValue)] = '%' then
Include(lBrushEntity.Brush.Gradient_flags, gfRelEndY);
y2 := StringWithPercentToFloat(lAttrValue);
end else
if lAttrName = 'gradientunits' then
begin
if lAttrValue = 'userspaceonuse' then
Include(lBrushEntity.Brush.Gradient_flags, gfRelToUserSpace)
else if lAttrValue = 'objectboundingbox' then
Exclude(lBrushEntity.Brush.Gradient_flags, gfRelToUserSpace);
end;
end;
if x2 = x1 then
lBrushEntity.Brush.Gradient_start.X := x1;
lBrushEntity.Brush.Gradient_end.X := x2;
lBrushEntity.Brush.Gradient_start.Y := y1;
lBrushEntity.Brush.Gradient_end.Y := y2;
ConvertSVGCoordinatesToFPVCoordinates(AData, x1, y1, x1, y1);
ConvertSVGCoordinatesToFPVCoordinates(AData, x2, y2, x2, y2);
if not (gfRelStartX in lBrushEntity.Brush.Gradient_flags) then
lBrushEntity.Brush.Gradient_start.X := x1;
if not (gfRelEndX in lBrushEntity.Brush.Gradient_flags) then
lBrushEntity.Brush.Gradient_end.X := x2;
if not (gfRelStartY in lBrushEntity.Brush.Gradient_flags) then
lBrushEntity.Brush.Gradient_start.Y := y1;
if not (gfRelEndY in lBrushEntity.Brush.Gradient_flags) then
lBrushEntity.Brush.Gradient_end.Y := y2;
if (lBrushEntity.Brush.Gradient_start.X = 0) and
(lBrushEntity.Brush.Gradient_start.Y = 0) and
(lBrushEntity.Brush.Gradient_end.X = 0) and
(lBrushEntity.Brush.Gradient_end.Y = 0) then
begin
lBrushEntity.Brush.Gradient_start.X := 0.0;
lBrushEntity.Brush.Gradient_start.Y := 0.0;
lBrushEntity.Brush.Gradient_end.X := 1.0;
lBrushEntity.Brush.Gradient_end.Y := 1.0;
end;
if lBrushEntity.Brush.Gradient_start.X = lBrushEntity.Brush.Gradient_end.X then
lBrushEntity.Brush.Kind := bkVerticalGradient
else if y2=y1 then
else if lBrushEntity.Brush.Gradient_start.Y = lBrushEntity.Brush.Gradient_end.Y then
lBrushEntity.Brush.Kind := bkHorizontalGradient
else
lBrushEntity.Brush.Kind := bkOtherLinearGradient;
@ -1341,7 +1408,8 @@ begin
lAttrName := lCurSubNode.Attributes.Item[i].NodeName;
lAttrValue := lCurSubNode.Attributes.Item[i].NodeValue;
if lAttrName = 'offset' then
lGradientColor.Position := StringWithUnitToFloat(lAttrValue)
lGradientColor.Position := StringWithPercentToFloat(lAttrValue)
// use as fraction 0..1
else if lAttrName = 'style' then
lGradientColor.Color := ReadSVGGradientColorStyle(lAttrValue)
else if lAttrName = 'stop-color' then
@ -1433,7 +1501,7 @@ end;
function TvSVGVectorialReader.ReadCircleFromNode(ANode: TDOMNode;
AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
var
cx, cy, cr, dtmp: double;
cx, cy, cr, tmp: double;
lCircle: TvCircle;
i: Integer;
lNodeName, lNodeValue: DOMString;
@ -1448,7 +1516,7 @@ begin
lCircle.Brush.Style := bsSolid;
lCircle.Brush.Color := colBlack;
// Apply the layer style
ApplyLayerStyles(lCircle);
ApplyLayerStyles(AData, lCircle);
// read the attributes
for i := 0 to ANode.Attributes.Length - 1 do
@ -1456,27 +1524,36 @@ begin
lNodeName := ANode.Attributes.Item[i].NodeName;
lNodeValue := ANode.Attributes.Item[i].NodeValue;
if lNodeName = 'cx' then
cx := StringWithUnitToFloat(lNodeValue, sckX, suPX, suMM)
cx := StringWithUnitToFloat(lNodeValue) //, sckX, suPX, suMM)
else if lNodeName = 'cy' then
cy := StringWithUnitToFloat(lNodeValue, sckY, suPX, suMM)
cy := StringWithUnitToFloat(lNodeValue) //, sckY, suPX, suMM)
else if lNodeName = 'r' then
cr := StringWithUnitToFloat(lNodeValue, sckXSize, suPX, suMM)
cr := StringWithUnitToFloat(lNodeValue) //, sckXSize, suPX, suMM)
else if lNodeName = 'id' then
lCircle.Name := lNodeValue
else if lNodeName = 'style' then
ReadSVGStyle(lNodeValue, lCircle)
lCircle.Name := lNodeValue;
end;
ConvertSVGCoordinatesToFPVCoordinates(AData, cx, cy, cx, cy);
ConvertSVGSizeToFPVSize(AData, cr, cr, lCircle.Radius, tmp);
lCircle.X := lCircle.X + cx;
lCircle.Y := lCircle.Y + cy;
// Make sure that transformations are read after geometry and position
// of cirlce is known.
for i := 0 to ANode.Attributes.Length - 1 do
begin
lNodeName := ANode.Attributes.Item[i].NodeName;
lNodeValue := ANode.Attributes.Item[i].NodeValue;
if lNodeName = 'style' then
ReadSVGStyle(AData, lNodeValue, lCircle)
else if IsAttributeFromStyle(lNodeName) then
begin
ReadSVGPenStyleWithKeyAndValue(lNodeName, lNodeValue, lCircle);
ReadSVGBrushStyleWithKeyAndValue(lNodeName, lNodeValue, lCircle);
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, lNodeValue, lCircle);
ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName, lNodeValue, lCircle);
end;
end;
lCircle.X := lCircle.X + cx;
lCircle.Y := lCircle.Y + cy;
lCircle.Radius := lCircle.Radius + cr;
Result := lCircle;
end;
@ -1499,7 +1576,7 @@ begin
lEllipse.Brush.Style := bsSolid;
lEllipse.Brush.Color := colBlack;
// Apply the layer style
ApplyLayerStyles(lEllipse);
ApplyLayerStyles(AData, lEllipse);
// read the attributes
for i := 0 to ANode.Attributes.Length - 1 do
@ -1515,22 +1592,28 @@ begin
else if lNodeName = 'ry' then
cry := StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'id' then
lEllipse.Name := ANode.Attributes.Item[i].NodeValue
else if lNodeName = 'style' then
ReadSVGStyle(lNodeValue, lEllipse)
lEllipse.Name := ANode.Attributes.Item[i].NodeValue;
end;
ConvertSVGCoordinatesToFPVCoordinates(AData, cx, cy, lEllipse.X, lEllipse.Y);
ConvertSVGSizeToFPVSize(AData, crx, cry, lEllipse.HorzHalfAxis, lEllipse.VertHalfAxis);
// Make sure that transformations are read after geometry and position
// of ellipse is known.
for i := 0 to ANode.Attributes.Length - 1 do
begin
lNodeName := ANode.Attributes.Item[i].NodeName;
lNodeValue := ANode.Attributes.Item[i].NodeValue;
if lNodeName = 'style' then
ReadSVGStyle(AData, lNodeValue, lEllipse)
else if IsAttributeFromStyle(lNodeName) then
begin
ReadSVGPenStyleWithKeyAndValue(lNodeName, lNodeValue, lEllipse);
ReadSVGBrushStyleWithKeyAndValue(lNodeName, lNodeValue, lEllipse);
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, lNodeValue, lEllipse);
ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName, lNodeValue, lEllipse);
end;
end;
ConvertSVGCoordinatesToFPVCoordinates(
AData, cx, cy, lEllipse.X, lEllipse.Y);
ConvertSVGDeltaToFPVDelta(
AData, crx, cry, lEllipse.HorzHalfAxis, lEllipse.VertHalfAxis);
Result := lEllipse;
end;
@ -1572,7 +1655,7 @@ begin
lText := nil;//TvText.Create(nil);
// Apply the layer style
ApplyLayerStyles(lText);
ApplyLayerStyles(AData, lText);
// read the attributes
for i := 0 to ANode.Attributes.Length - 1 do
@ -1584,7 +1667,7 @@ begin
else if lNodeName = 'svg:y' then
ly := ly + StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'draw:style-name' then
ReadSVGStyle(lNodeValue, lText);
ReadSVGStyle(AData, lNodeValue, lText);
end;
// Get the text contents
@ -1648,7 +1731,7 @@ begin
lText := TvText.Create(nil);
// Apply the layer style
ApplyLayerStyles(lText);
ApplyLayerStyles(AData, lText);
{// read the attributes
for i := 0 to ANode.Attributes.Length - 1 do
@ -1777,7 +1860,7 @@ begin
begin
//ReadSVGPenStyleWithKeyAndValue(lNodeName, lNodeValue, lImage);
//ReadSVGBrushStyleWithKeyAndValue(lNodeName, lNodeValue, lImage);
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, lNodeValue, lImage);
ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName, lNodeValue, lImage);
end;
end;
@ -1802,7 +1885,7 @@ begin
lImage.Y := lImage.Y + lImage.Height / 2;
// Apply the layer style
ApplyLayerStyles(lImage);
ApplyLayerStyles(AData, lImage);
Result := lImage;
end;
@ -1931,18 +2014,20 @@ begin
lPath.Pen.Style := psClear;
// Apply the layer style
ApplyLayerStyles(lPath);
ApplyLayerStyles(AData, lPath);
// Add the entity styles
for i := 0 to ANode.Attributes.Length - 1 do
begin
lNodeName := ANode.Attributes.Item[i].NodeName;
if lNodeName = 'style' then
ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lPath)
ReadSVGStyle(AData, ANode.Attributes.Item[i].NodeValue, lPath)
else if IsAttributeFromStyle(lNodeName) then
begin
ReadSVGPenStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGPenStyleWithKeyAndValue(lNodeName,
ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName,
ANode.Attributes.Item[i].NodeValue, lPath);
end;
end;
//
@ -1975,7 +2060,7 @@ begin
lPath.Brush.Color := colBlack;
lPath.Brush.Style := bsClear;
// Apply the layer style
ApplyLayerStyles(lPath);
ApplyLayerStyles(AData, lPath);
// Add the pen/brush/name
for i := 0 to ANode.Attributes.Length - 1 do
begin
@ -1983,12 +2068,15 @@ begin
if lNodeName = 'id' then
lPath.Name := ANode.Attributes.Item[i].NodeValue
else if lNodeName = 'style' then
ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lPath)
ReadSVGStyle(AData, ANode.Attributes.Item[i].NodeValue, lPath)
else if IsAttributeFromStyle(lNodeName) then
begin
ReadSVGPenStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGBrushStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGPenStyleWithKeyAndValue(lNodeName,
ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGBrushStyleWithKeyAndValue(lNodeName,
ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName,
ANode.Attributes.Item[i].NodeValue, lPath);
end;
end;
end;
@ -2459,7 +2547,7 @@ begin
lPath.Brush.Style := bsClear;
// Apply the layer style
ApplyLayerStyles(lPath);
ApplyLayerStyles(AData, lPath);
// now read the other attributes
for i := 0 to ANode.Attributes.Length - 1 do
@ -2468,12 +2556,15 @@ begin
if lNodeName = 'id' then
lPath.Name := ANode.Attributes.Item[i].NodeValue
else if lNodeName = 'style' then
ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lPath)
ReadSVGStyle(AData, ANode.Attributes.Item[i].NodeValue, lPath)
else if IsAttributeFromStyle(lNodeName) then
begin
ReadSVGPenStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGBrushStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGPenStyleWithKeyAndValue(lNodeName,
ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGBrushStyleWithKeyAndValue(lNodeName,
ANode.Attributes.Item[i].NodeValue, lPath);
ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName,
ANode.Attributes.Item[i].NodeValue, lPath);
end;
end;
end;
@ -2486,6 +2577,7 @@ var
lRect: TvRectangle;
i: Integer;
lNodeName: DOMString;
lNodeValue: String;
begin
lx := 0.0;
ly := 0.0;
@ -2500,45 +2592,53 @@ begin
lRect.Brush.Style := bsSolid;
lRect.Brush.Color := colBlack;
// Apply the layer style
ApplyLayerStyles(lRect);
ApplyLayerStyles(AData, lRect);
// 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 := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
lx := StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'y' then
ly := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
ly := StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'rx' then
lrx := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
lrx := StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'ry' then
lry := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
lry := StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'width' then
cx := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
cx := StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'height' then
cy := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
cy := StringWithUnitToFloat(lNodeValue)
else if lNodeName = 'id' then
lRect.Name := ANode.Attributes.Item[i].NodeValue
else if lNodeName = 'style' then
ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lRect)
else if IsAttributeFromStyle(lNodeName) then
begin
ReadSVGPenStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lRect);
ReadSVGBrushStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lRect);
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lRect);
end;
lRect.Name := lNodeValue;
end;
ConvertSVGCoordinatesToFPVCoordinates(
AData, lx, ly, lRect.X, lRect.Y);
ConvertSVGSizeToFPVSize(
AData, cx, cy, lRect.CX, lRect.CY);
ConvertSVGSizeToFPVSize(
AData, lrx, lry, lRect.RX, lRect.RY);
ConvertSVGCoordinatesToFPVCoordinates(AData, lx, ly, lRect.X, lRect.Y);
ConvertSVGSizeToFPVSize(AData, cx, cy, lRect.CX, lRect.CY);
ConvertSVGSizeToFPVSize(AData, lrx, lry, lRect.RX, lRect.RY);
lRect.RX := Abs(lRect.RX) * 2;
lRect.RY := Abs(lRect.RY) * 2;
// Make sure that transformations are read after geometry and position
// of rectangle is known.
for i := 0 to ANode.Attributes.Length - 1 do
begin
lNodeName := ANode.Attributes.Item[i].NodeName;
if lNodeName = 'style' then
ReadSVGStyle(AData, lNodeValue, lRect)
else if IsAttributeFromStyle(lNodeName) then
begin
ReadSVGPenStyleWithKeyAndValue(lNodeName,
ANode.Attributes.Item[i].NodeValue, lRect);
ReadSVGBrushStyleWithKeyAndValue(lNodeName,
ANode.Attributes.Item[i].NodeValue, lRect);
ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName,
ANode.Attributes.Item[i].NodeValue, lRect);
end;
end;
Result := lRect;
end;
@ -2627,12 +2727,13 @@ var
procedure ReadTextSpans(ACurNode: TDOMNode);
var
j: Integer;
i,j: Integer;
lCurNode: TDOMNode;
lTextStr: string;
lText: TvText;
lCText: TvCurvedText;
lInsertedEntity, lInsertedSubEntity: TvEntity;
s: String;
begin
lCurNode := ACurNode.FirstChild;
while lCurNode <> nil do
@ -2714,25 +2815,23 @@ var
end;
// Apply the layer style
ApplyLayerStyles(lCText);
ApplyLayerStyles(AData, lCText);
// Apply the layer style
ApplyStackStylesToText(lCText);
end
else
if lNodeValue <> '' then
begin
lText := lParagraph.AddText(lNodeValue);
lText.Font.Size := 10;
lText.Name := lName;
// Apply the layer style
ApplyLayerStyles(lText);
ApplyLayerStyles(AData, lText);
// Apply the layer style
ApplyStackStylesToText(lText);
end;
lCurNode := lCurNode.NextSibling;
end;
end;
@ -2766,11 +2865,11 @@ begin
lParagraph.Name := lName;
end
else if lNodeName = 'style' then
ReadSVGStyle(lNodeValue, nil, lCurStyle)
ReadSVGStyle(AData, lNodeValue, nil, lCurStyle)
else if IsAttributeFromStyle(lNodeName) then
begin
ReadSVGFontStyleWithKeyAndValue(lNodeName, lNodeValue, nil, lCurStyle);
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, lNodeValue, lParagraph);
// ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName, lNodeValue, lParagraph);
end;
end;
@ -2792,6 +2891,15 @@ begin
// These other lines can be positioned, so they need to appear as independent TvText elements
ReadTextSpans(ANode);
// 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 IsAttributeFromStyle(lNodeName) then
ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName, lNodeValue, lParagraph);
end;
// Finalization
lCurObject := lTextSpanStack.Pop();
if lCurObject <> nil then lCurObject.Free;
@ -2849,14 +2957,14 @@ begin
lNodeValue := ANode.Attributes.Item[i].NodeValue;
if lNodeName = 'style' then
begin
ReadSVGStyle(lNodeValue, lInsert);
ReadSVGStyle(AData, lNodeValue, lInsert);
end
else if IsAttributeFromStyle(lNodeName) then
begin
lInsert.SetElements += ReadSVGPenStyleWithKeyAndValue(lNodeName, lNodeValue, lInsert);
lInsert.SetElements += ReadSVGBrushStyleWithKeyAndValue(lNodeName, lNodeValue, lInsert);
lInsert.SetElements += ReadSVGFontStyleWithKeyAndValue(lNodeName, lNodeValue, lInsert);
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, lNodeValue, lInsert);
ReadSVGGeneralStyleWithKeyAndValue(AData, lNodeName, lNodeValue, lInsert);
end;
end;
@ -2947,7 +3055,7 @@ var
procedure DoProcessMM_End();
begin
if ATargetUnit = suPX then
Result := Result / FLOAT_MILIMETERS_PER_PIXEL;
Result := Result / FLOAT_MILLIMETERS_PER_PIXEL;
DoViewBoxAdjust();
end;
@ -2955,7 +3063,7 @@ var
begin
Result := StrToFloat(ValueStr, FPointSeparator);
case ATargetUnit of
suMM: Result := Result * FLOAT_MILIMETERS_PER_PIXEL;
suMM: Result := Result * FLOAT_MILLIMETERS_PER_PIXEL;
suPT: Result := Result * FLOAT_POINTS_PER_PIXEL;
end;
DoViewBoxAdjust();
@ -3027,12 +3135,25 @@ begin
Result := Round(StrToFloat(AStr, FPointSeparator) * $FFFF);
end;
{@@ Converts a number string to a floating-point number. If the string has a
% character at its end then it is removed, and the numerical value is
divided by 100. }
function TvSVGVectorialReader.StringWithPercentToFloat(AStr: String): Double;
begin
if AStr[Length(AStr)] = '%' then
begin
Delete(AStr, Length(AStr), 1);
Result := 0.01 * StrToFloat(trim(AStr), FPointSeparator);
end else
Result := StrToFloat(AStr, FPointSeparator);
end;
procedure TvSVGVectorialReader.ConvertSVGCoordinatesToFPVCoordinates(
const AData: TvVectorialPage; const ASrcX, ASrcY: Double;
var ADestX,ADestY: Double; ADoViewBoxAdjust: Boolean = True);
begin
ADestX := ASrcX * FLOAT_MILIMETERS_PER_PIXEL;
ADestY := AData.Height - ASrcY * FLOAT_MILIMETERS_PER_PIXEL;
ADestX := ASrcX * FLOAT_MILLIMETERS_PER_PIXEL;
ADestY := AData.Height - ASrcY * FLOAT_MILLIMETERS_PER_PIXEL;
if ViewBoxAdjustment and ADoViewBoxAdjust then
begin
ADestX := (ASrcX - ViewBox_Left) * Page_Width / ViewBox_Width;
@ -3044,8 +3165,8 @@ procedure TvSVGVectorialReader.ConvertSVGDeltaToFPVDelta(
const AData: TvVectorialPage; const ASrcX, ASrcY: Double; var ADestX,
ADestY: Double; ADoViewBoxAdjust: Boolean = True);
begin
ADestX := ASrcX * FLOAT_MILIMETERS_PER_PIXEL;
ADestY := - ASrcY * FLOAT_MILIMETERS_PER_PIXEL;
ADestX := ASrcX * FLOAT_MILLIMETERS_PER_PIXEL;
ADestY := - ASrcY * FLOAT_MILLIMETERS_PER_PIXEL;
if ViewBoxAdjustment and ADoViewBoxAdjust then
begin
ADestX := ASrcX * Page_Width / ViewBox_Width;
@ -3057,8 +3178,8 @@ procedure TvSVGVectorialReader.ConvertSVGSizeToFPVSize(
const AData: TvVectorialPage; const ASrcX, ASrcY: Double; var ADestX,
ADestY: Double; ADoViewBoxAdjust: Boolean = True);
begin
ADestX := ASrcX * FLOAT_MILIMETERS_PER_PIXEL;
ADestY := ASrcY * FLOAT_MILIMETERS_PER_PIXEL;
ADestX := ASrcX * FLOAT_MILLIMETERS_PER_PIXEL;
ADestY := ASrcY * FLOAT_MILLIMETERS_PER_PIXEL;
if ViewBoxAdjustment and ADoViewBoxAdjust then
begin
ADestX := ASrcX * Page_Width / ViewBox_Width;