Merged revision(s) 50768 #0f4cc7840e, 50807-50810 #478a6d00a4-#478a6d00a4, 50818-50819 #5fe2efae86-#5fe2efae86, 50875 #506bdbde54 from trunk:

fpvectorial: Fix text positioning issue when reading svg files.
........
fpvectorial: Fix calculation of bounding rectangle of all entities on a page
........
fpvectorial: Fix memory leak of svg tokenizer. Add ReadDefsFromNode to TvSVGVectorialReader.ReadEntityFromNode. Prepare reading of line styles for svg.
........
fpvectorial: Fix memory leak of TvEntityWithSubEntities due to not releasing items in FElements list.
........
fpvectorial: Fix bounding box of circle. Fix page bounding box in case of several top-level entities.
........
fpvectorial: Add parameter to Render method for calculation of bounding box without drawing. 
Fix svgreader crashing due to incorrect decimal separator in "stroke-opacity".
........
fpvectorial: Fix runtime error with fpc trunk due to duplicate application of ExtractFileExt returning no extension any more.
........
fpvectorial: svg reader detects pen styles & patterns now. Fix rendering of lines with the specified pen styles & patterns.
........

git-svn-id: branches/fixes_1_6@50877 -
This commit is contained in:
maxim 2015-12-17 21:54:46 +00:00
parent 0ea790b8d3
commit 0f4ab685b0
2 changed files with 360 additions and 194 deletions

View File

@ -117,6 +117,7 @@ type
Color: TFPColor; Color: TFPColor;
Style: TFPPenStyle; Style: TFPPenStyle;
Width: Integer; Width: Integer;
Pattern: array of LongWord;
end; end;
PvPen = ^TvPen; PvPen = ^TvPen;
@ -642,6 +643,7 @@ type
TvCircle = class(TvEntityWithPenAndBrush) TvCircle = class(TvEntityWithPenAndBrush)
public public
Radius: Double; Radius: Double;
procedure CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); override;
procedure Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; ADestX: Integer = 0; procedure Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; ADestX: Integer = 0;
ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0; ADoDraw: Boolean = True); override; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0; ADoDraw: Boolean = True); override;
end; end;
@ -1373,6 +1375,7 @@ type
MinX, MinY, MinZ, MaxX, MaxY, MaxZ: Double; MinX, MinY, MinZ, MaxX, MaxY, MaxZ: Double;
// Other basic document information // Other basic document information
BackgroundColor: TFPColor; BackgroundColor: TFPColor;
AdjustPenColorToBackground: Boolean;
RenderInfo: TvRenderInfo; // Prepared by the reader with info on how to draw the page RenderInfo: TvRenderInfo; // Prepared by the reader with info on how to draw the page
{ Base methods } { Base methods }
constructor Create(AOwner: TvVectorialDocument); virtual; constructor Create(AOwner: TvVectorialDocument); virtual;
@ -1398,7 +1401,8 @@ type
procedure RenderPageBorder(ADest: TFPCustomCanvas; procedure RenderPageBorder(ADest: TFPCustomCanvas;
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); virtual; abstract; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); virtual; abstract;
procedure Render(ADest: TFPCustomCanvas; procedure Render(ADest: TFPCustomCanvas;
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); virtual; abstract; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0;
ADoDraw: Boolean = true); virtual; abstract;
procedure AutoFit(ADest: TFPCustomCanvas; AWidth, AHeight: Integer; out ADeltaX, ADeltaY: Integer; out AZoom: Double); virtual; abstract; procedure AutoFit(ADest: TFPCustomCanvas; AWidth, AHeight: Integer; out ADeltaX, ADeltaY: Integer; out AZoom: Double); virtual; abstract;
{ Debug methods } { Debug methods }
procedure GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer); virtual; abstract; procedure GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer); virtual; abstract;
@ -1479,8 +1483,8 @@ type
procedure DrawBackground(ADest: TFPCustomCanvas); override; procedure DrawBackground(ADest: TFPCustomCanvas); override;
procedure RenderPageBorder(ADest: TFPCustomCanvas; procedure RenderPageBorder(ADest: TFPCustomCanvas;
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override;
procedure Render(ADest: TFPCustomCanvas; procedure Render(ADest: TFPCustomCanvas; ADestX: Integer = 0; ADestY: Integer = 0;
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override; AMulX: Double = 1.0; AMulY: Double = 1.0; ADoDraw: Boolean = true); override;
procedure AutoFit(ADest: TFPCustomCanvas; AWidth, AHeight: Integer; out ADeltaX, ADeltaY: Integer; out AZoom: Double); override; procedure AutoFit(ADest: TFPCustomCanvas; AWidth, AHeight: Integer; out ADeltaX, ADeltaY: Integer; out AZoom: Double); override;
{ Debug methods } { Debug methods }
procedure GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer); override; procedure GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer); override;
@ -1523,8 +1527,8 @@ type
procedure DrawBackground(ADest: TFPCustomCanvas); override; procedure DrawBackground(ADest: TFPCustomCanvas); override;
procedure RenderPageBorder(ADest: TFPCustomCanvas; procedure RenderPageBorder(ADest: TFPCustomCanvas;
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override;
procedure Render(ADest: TFPCustomCanvas; procedure Render(ADest: TFPCustomCanvas; ADestX: Integer = 0; ADestY: Integer = 0;
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override; AMulX: Double = 1.0; AMulY: Double = 1.0; ADoDraw: Boolean = true); override;
procedure AutoFit(ADest: TFPCustomCanvas; AWidth, AHeight: Integer; out ADeltaX, ADeltaY: Integer; out AZoom: Double); override; procedure AutoFit(ADest: TFPCustomCanvas; AWidth, AHeight: Integer; out ADeltaX, ADeltaY: Integer; out AZoom: Double); override;
{ Debug methods } { Debug methods }
procedure GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer); override; procedure GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer); override;
@ -3168,7 +3172,7 @@ begin
ARenderInfo.EntityCanvasMinXY := Point(INVALID_RENDERINFO_CANVAS_XY, INVALID_RENDERINFO_CANVAS_XY); ARenderInfo.EntityCanvasMinXY := Point(INVALID_RENDERINFO_CANVAS_XY, INVALID_RENDERINFO_CANVAS_XY);
ARenderInfo.EntityCanvasMaxXY := Point(INVALID_RENDERINFO_CANVAS_XY, INVALID_RENDERINFO_CANVAS_XY); ARenderInfo.EntityCanvasMaxXY := Point(INVALID_RENDERINFO_CANVAS_XY, INVALID_RENDERINFO_CANVAS_XY);
//ARenderInfo.BackgroundColor := colBlack; Don't change this because otherwise we lose the value set by the page //ARenderInfo.BackgroundColor := colBlack; Don't change this because otherwise we lose the value set by the page
ARenderInfo.AdjustPenColorToBackground := True; //ARenderInfo.AdjustPenColorToBackground := True; dto.
ARenderInfo.Selected := False; ARenderInfo.Selected := False;
ARenderInfo.ForceRenderBlock := False; ARenderInfo.ForceRenderBlock := False;
end; end;
@ -3340,8 +3344,15 @@ procedure TvEntityWithPen.ApplyPenToCanvas(ADest: TFPCustomCanvas;
ARenderInfo: TvRenderInfo; APen: TvPen); ARenderInfo: TvRenderInfo; APen: TvPen);
begin begin
ADest.Pen.FPColor := AdjustColorToBackground(APen.Color, ARenderInfo); ADest.Pen.FPColor := AdjustColorToBackground(APen.Color, ARenderInfo);
ADest.Pen.Width := 1;//APen.Width; ADest.Pen.Width := Max(1, APen.Width); // wp: why was here "1;//APen.Width;" ???
ADest.Pen.Style := APen.Style; ADest.Pen.Style := APen.Style;
{$ifdef USE_LCL_CANVAS}
if (APen.Style = psPattern) then
begin
TCanvas(ADest).Pen.SetPattern(APen.Pattern);
if APen.Width = 1 then TCanvas(ADest).Pen.Cosmetic := false;
end;
{$endif}
end; end;
procedure TvEntityWithPen.AssignPen(APen: TvPen); procedure TvEntityWithPen.AssignPen(APen: TvPen);
@ -3543,7 +3554,7 @@ procedure TvEntityWithPenBrushAndFont.Render(ADest: TFPCustomCanvas;
AMulY: Double; ADoDraw: Boolean); AMulY: Double; ADoDraw: Boolean);
begin begin
inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
ApplyFontToCanvas(ADest, ARenderInfo, AMulX); ApplyFontToCanvas(ADest, ARenderInfo, AMulX); // wp: why not AMulY ?
end; end;
function TvEntityWithPenBrushAndFont.GenerateDebugTree( function TvEntityWithPenBrushAndFont.GenerateDebugTree(
@ -3939,12 +3950,14 @@ var
ACanvas: TCanvas absolute ADest; ACanvas: TCanvas absolute ADest;
{$endif} {$endif}
begin begin
inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
PosX := 0; PosX := 0;
PosY := 0; PosY := 0;
ADest.Brush.Style := bsClear; ADest.Brush.Style := bsClear;
ADest.MoveTo(ADestX, ADestY); ADest.MoveTo(ADestX, ADestY);
{
// Set the path Pen and Brush options // Set the path Pen and Brush options
ADest.Pen.Style := Pen.Style; ADest.Pen.Style := Pen.Style;
ADest.Pen.Width := Round(Pen.Width * AMulX); ADest.Pen.Width := Round(Pen.Width * AMulX);
@ -3952,8 +3965,12 @@ begin
if (Pen.Width <= 2) and (ADest.Pen.Width > 2) then ADest.Pen.Width := 2; if (Pen.Width <= 2) and (ADest.Pen.Width > 2) then ADest.Pen.Width := 2;
if (Pen.Width <= 5) and (ADest.Pen.Width > 5) then ADest.Pen.Width := 5; if (Pen.Width <= 5) and (ADest.Pen.Width > 5) then ADest.Pen.Width := 5;
ADest.Pen.FPColor := AdjustColorToBackground(Pen.Color, ARenderInfo); ADest.Pen.FPColor := AdjustColorToBackground(Pen.Color, ARenderInfo);
{$ifdef USE_LCL_CANVAS}
if (Pen.Style = psPattern) then
ACanvas.Pen.SetPattern(Pen.Pattern);
{$endif}
ADest.Brush.FPColor := Brush.Color; ADest.Brush.FPColor := Brush.Color;
}
// Prepare the Clipping Region, if any // Prepare the Clipping Region, if any
{$ifdef USE_CANVAS_CLIP_REGION} {$ifdef USE_CANVAS_CLIP_REGION}
if ClipPath <> nil then if ClipPath <> nil then
@ -3972,7 +3989,8 @@ begin
{$endif} {$endif}
// useful in some paths, like stars! // useful in some paths, like stars!
RenderInternalPolygon(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY); if ADoDraw then
RenderInternalPolygon(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY);
// //
// For other paths, draw more carefully // For other paths, draw more carefully
@ -3990,7 +4008,9 @@ begin
begin begin
CoordX := CoordToCanvasX(Cur2DSegment.X); CoordX := CoordToCanvasX(Cur2DSegment.X);
CoordY := CoordToCanvasY(Cur2DSegment.Y); CoordY := CoordToCanvasY(Cur2DSegment.Y);
ADest.MoveTo(CoordX, CoordY); if ADoDraw then
ADest.MoveTo(CoordX, CoordY);
CalcEntityCanvasMinMaxXY(ARenderInfo, CoordX, CoordY);
PosX := Cur2DSegment.X; PosX := Cur2DSegment.X;
PosY := Cur2DSegment.Y; PosY := Cur2DSegment.Y;
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG} {$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
@ -4118,20 +4138,21 @@ begin
ADest.Brush.Style := Brush.Style; ADest.Brush.Style := Brush.Style;
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX4, CoordY4); CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX4, CoordY4);
if ADoDraw then
begin
// Arc draws counterclockwise // Arc draws counterclockwise
if ADoDraw and Cur2DArcSegment.ClockwiseArcFlag then if Cur2DArcSegment.ClockwiseArcFlag then
begin begin
ACanvas.Arc( ACanvas.Arc(
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom, EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom,
CoordX4, CoordY4, CoordX, CoordY); CoordX4, CoordY4, CoordX, CoordY);
end end else
else if ADoDraw then begin
begin ACanvas.Arc(
ACanvas.Arc( EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom,
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom, CoordX, CoordY, CoordX4, CoordY4);
CoordX, CoordY, CoordX4, CoordY4); end;
end; end;
PosX := Cur2DArcSegment.X; PosX := Cur2DArcSegment.X;
PosY := Cur2DArcSegment.Y; PosY := Cur2DArcSegment.Y;
@ -4346,12 +4367,14 @@ procedure TvText.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; A
Result := Round(ADestY + AmulY * ACoord); Result := Round(ADestY + AmulY * ACoord);
end; end;
const
LINE_SPACING = 0.2; // fraction of font height for line spacing
var var
i: Integer; i: Integer;
// //
LowerDim: T3DPoint; LowerDim: T3DPoint;
XAnchorAdjustment: Integer; XAnchorAdjustment: Integer;
lLongestLine, lLineWidth, lFontSizePx: Integer; lLongestLine, lLineWidth, lFontSizePx, lFontDescenderPx: Integer;
lText: string; lText: string;
{$ifdef USE_LCL_CANVAS} {$ifdef USE_LCL_CANVAS}
ACanvas: TCanvas absolute ADest; ACanvas: TCanvas absolute ADest;
@ -4372,7 +4395,7 @@ begin
lLongestLine := 0; lLongestLine := 0;
for i := 0 to Value.Count - 1 do for i := 0 to Value.Count - 1 do
begin begin
lLineWidth := ACanvas.TextWidth(Value.Strings[i]); lLineWidth := ACanvas.TextWidth(Value.Strings[i]); // contains multiplier
if lLineWidth > lLongestLine then if lLineWidth > lLongestLine then
lLongestLine := lLineWidth; lLongestLine := lLineWidth;
end; end;
@ -4386,15 +4409,15 @@ begin
// TvText supports multiple lines // TvText supports multiple lines
for i := 0 to Value.Count - 1 do for i := 0 to Value.Count - 1 do
begin begin
lFontSizePx := Font.Size; lFontSizePx := Font.Size; // is without multiplier!
if lFontSizePx = 0 then lFontSizePx := 10; if lFontSizePx = 0 then lFontSizePx := 10;
// We need to keep the order of lines drawing correct regardless of // We need to keep the order of lines drawing correct regardless of
// the drawing direction // the drawing direction
if AMulY < 0 then if AMulY < 0 then
LowerDim.Y := CoordToCanvasY(Y) + lFontSizePx * 1.2 * (Value.Count - i) lowerDim.Y := CoordToCanvasY(Y) + lFontSizePx * (1 + LINE_SPACING) * (Value.Count - i) * AMulY
else else
LowerDim.Y := CoordToCanvasY(Y) + lFontSizePx * 1.2 * i; LowerDim.Y := CoordToCanvasY(Y) + lFontSizePx * (1 + LINE_SPACING) * i * AMulY;
ADest.Font.FPColor := AdjustColorToBackground(Font.Color, ARenderInfo); ADest.Font.FPColor := AdjustColorToBackground(Font.Color, ARenderInfo);
lText := Value.Strings[i]; lText := Value.Strings[i];
@ -4540,6 +4563,15 @@ end;
{ TvCircle } { TvCircle }
procedure TvCircle.CalculateBoundingBox(ADest: TFPCustomCanvas;
var ALeft, ATop, ARight, ABottom: Double);
begin
ALeft := X - Radius;
ARight := X + Radius;
ATop := Y + Radius;
ABottom := Y - Radius;
end;
procedure TvCircle.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; ADestX: Integer; procedure TvCircle.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; ADestX: Integer;
ADestY: Integer; AMulX: Double; AMulY: Double; ADoDraw: Boolean); ADestY: Integer; AMulX: Double; AMulY: Double; ADoDraw: Boolean);
@ -4553,14 +4585,18 @@ procedure TvCircle.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo;
Result := Round(ADestY + AmulY * ACoord); Result := Round(ADestY + AmulY * ACoord);
end; end;
var
x1, y1, x2, y2: Integer;
begin begin
inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
ADest.Ellipse(
CoordToCanvasX(X - Radius), x1 := CoordToCanvasX(X - Radius);
CoordToCanvasY(Y - Radius), y1 := CoordToCanvasY(Y - Radius);
CoordToCanvasX(X + Radius), x2 := CoordToCanvasX(X + Radius);
CoordToCanvasY(Y + Radius) y2 := CoordToCanvasY(Y + Radius);
); ADest.Ellipse(x1, y1, x2, y2);
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, x1, y1, x2, y2);
end; end;
{ TvCircularArc } { TvCircularArc }
@ -4691,8 +4727,8 @@ begin
// First do the trivial // First do the trivial
ALeft := X - HorzHalfAxis; ALeft := X - HorzHalfAxis;
ARight := X + HorzHalfAxis; ARight := X + HorzHalfAxis;
ATop := Y - VertHalfAxis; ATop := Y - VertHalfAxis; // wp: shouldn't this be "+" ...
ABottom := Y + VertHalfAxis; ABottom := Y + VertHalfAxis; // ... and this "-" ?
{ {
To calculate the bounding rectangle we can do this: To calculate the bounding rectangle we can do this:
@ -4771,12 +4807,13 @@ begin
// Conrollpoint of secondpart endpoint // Conrollpoint of secondpart endpoint
PointList[6] := PointList[0]; // Endpoint of PointList[6] := PointList[0]; // Endpoint of
// Back to the startpoint // Back to the startpoint
ALCLDest.PolyBezier(Pointlist[0]); if ADoDraw then
ALCLDest.PolyBezier(Pointlist[0]);
end end
else else
{$endif} {$endif}
begin begin
ADest.Ellipse(x1, y1, x2, y2); if ADoDraw then ADest.Ellipse(x1, y1, x2, y2);
end; end;
// Apply brush gradient // Apply brush gradient
if x1 > x2 then if x1 > x2 then
@ -4792,6 +4829,8 @@ begin
y2 := dk; y2 := dk;
end; end;
DrawBrushGradient(ADest, ARenderInfo, x1, y1, x2, y2, ADestX, ADestY, AMulX, AMulY); DrawBrushGradient(ADest, ARenderInfo, x1, y1, x2, y2, ADestX, ADestY, AMulX, AMulY);
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, x1, y1, x2, y2);
end; end;
{ TvRectangle } { TvRectangle }
@ -4830,14 +4869,19 @@ begin
y1 := CoordToCanvasY(fy1); y1 := CoordToCanvasY(fy1);
y2 := CoordToCanvasY(fy2); y2 := CoordToCanvasY(fy2);
{$ifdef USE_LCL_CANVAS} if ADoDraw then
if (RX = 0) and (RY = 0) then begin
{$ifdef USE_LCL_CANVAS}
if (RX = 0) and (RY = 0) then
ADest.Rectangle(x1, y1, x2, y2)
else
LCLIntf.RoundRect(TCanvas(ADest).Handle, x1, y1, x2, y2, Round(rx), Round(ry));
{$else}
ADest.Rectangle(x1, y1, x2, y2) ADest.Rectangle(x1, y1, x2, y2)
else {$endif}
LCLIntf.RoundRect(TCanvas(ADest).Handle, x1, y1, x2, y2, Round(rx), Round(ry)); end;
{$else}
ADest.Rectangle(x1, y1, x2, y2) CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, x1, y1, x2, y2);
{$endif}
end; end;
function TvRectangle.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; function TvRectangle.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc;
@ -4896,9 +4940,11 @@ begin
begin begin
lPoints[i].X := CoordToCanvasX(Points[i].X); lPoints[i].X := CoordToCanvasX(Points[i].X);
lPoints[i].Y := CoordToCanvasY(Points[i].Y); lPoints[i].Y := CoordToCanvasY(Points[i].Y);
CalcEntityCanvasMinMaxXY(ARenderInfo, lPoints[i].X, lPoints[i].Y);
end; end;
ADest.Polygon(lPoints); if ADoDraw then
ADest.Polygon(lPoints);
end; end;
{ TvAlignedDimension } { TvAlignedDimension }
@ -4922,6 +4968,7 @@ var
{$ifdef USE_LCL_CANVAS} {$ifdef USE_LCL_CANVAS}
ALCLDest: TCanvas absolute ADest; ALCLDest: TCanvas absolute ADest;
{$endif} {$endif}
txt: String;
begin begin
ADest.Pen.FPColor := AdjustColorToBackground(colBlack, ARenderInfo); ADest.Pen.FPColor := AdjustColorToBackground(colBlack, ARenderInfo);
ADest.Pen.Width := 1; ADest.Pen.Width := 1;
@ -4948,12 +4995,14 @@ begin
Points[0] := Point(CoordToCanvasX(DimensionLeft.X), CoordToCanvasY(DimensionLeft.Y)); Points[0] := Point(CoordToCanvasX(DimensionLeft.X), CoordToCanvasY(DimensionLeft.Y));
Points[1] := Point(Points[0].X + 7, Points[0].Y - 3); Points[1] := Point(Points[0].X + 7, Points[0].Y - 3);
Points[2] := Point(Points[0].X + 7, Points[0].Y + 3); Points[2] := Point(Points[0].X + 7, Points[0].Y + 3);
ADest.Polygon(Points); CalcEntityCanvasMinMaxXY(ARenderInfo, Points[0].X, Points[1].Y);
if ADoDraw then ADest.Polygon(Points);
// Right arrow // Right arrow
Points[0] := Point(CoordToCanvasX(DimensionRight.X), CoordToCanvasY(DimensionRight.Y)); Points[0] := Point(CoordToCanvasX(DimensionRight.X), CoordToCanvasY(DimensionRight.Y));
Points[1] := Point(Points[0].X - 7, Points[0].Y - 3); Points[1] := Point(Points[0].X - 7, Points[0].Y - 3);
Points[2] := Point(Points[0].X - 7, Points[0].Y + 3); Points[2] := Point(Points[0].X - 7, Points[0].Y + 3);
ADest.Polygon(Points); CalcEntityCanvasMinMaxXY(ARenderInfo, Points[0].X, Points[2].Y);
if ADoDraw then ADest.Polygon(Points);
ADest.Brush.Style := bsClear; ADest.Brush.Style := bsClear;
// Dimension text // Dimension text
Points[0].X := CoordToCanvasX((DimensionLeft.X+DimensionRight.X)/2); Points[0].X := CoordToCanvasX((DimensionLeft.X+DimensionRight.X)/2);
@ -4962,7 +5011,12 @@ begin
ADest.Font.Size := 10; ADest.Font.Size := 10;
ADest.Font.Orientation := 0; ADest.Font.Orientation := 0;
ADest.Font.FPColor := AdjustColorToBackground(colBlack, ARenderInfo); ADest.Font.FPColor := AdjustColorToBackground(colBlack, ARenderInfo);
ADest.TextOut(Points[0].X, Points[0].Y-Round(ADest.Font.Size*1.5), Format('%.1f', [LowerDim.X])); txt := Format('%.1f', [LowerDim.X]);
if ADoDraw then
ADest.TextOut(Points[0].X, Points[0].Y-Round(ADest.Font.Size*1.5), txt);
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo,
Points[0].X, Points[0].Y - round(ADest.Font.Size*1.5),
Points[0].X + ADest.TextWidth(txt), Points[0].Y);
end end
else else
begin begin
@ -4983,12 +5037,14 @@ begin
Points[0] := Point(CoordToCanvasX(UpperDim.X), CoordToCanvasY(UpperDim.Y)); Points[0] := Point(CoordToCanvasX(UpperDim.X), CoordToCanvasY(UpperDim.Y));
Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y - Round(AMulY*3)); Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y - Round(AMulY*3));
Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y - Round(AMulY*3)); Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y - Round(AMulY*3));
ADest.Polygon(Points); if ADoDraw then ADest.Polygon(Points);
CalcEntityCanvasMinMaxXY(ARenderInfo, Points[1].X, Points[0].Y);
// Lower arrow // Lower arrow
Points[0] := Point(CoordToCanvasX(LowerDim.X), CoordToCanvasY(LowerDim.Y)); Points[0] := Point(CoordToCanvasX(LowerDim.X), CoordToCanvasY(LowerDim.Y));
Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y + Round(AMulY*3)); Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y + Round(AMulY*3));
Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y + Round(AMulY*3)); Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y + Round(AMulY*3));
ADest.Polygon(Points); if ADoDraw then ADest.Polygon(Points);
CalcEntityCanvasMinMaxXY(ARenderInfo, Points[2].X, Points[0].Y);
ADest.Brush.Style := bsClear; ADest.Brush.Style := bsClear;
// Dimension text // Dimension text
Points[0].X := CoordToCanvasX(DimensionLeft.X); Points[0].X := CoordToCanvasX(DimensionLeft.X);
@ -4998,8 +5054,14 @@ begin
ADest.Font.Size := 10; ADest.Font.Size := 10;
ADest.Font.Orientation := 900; ADest.Font.Orientation := 900;
ADest.Font.FPColor := AdjustColorToBackground(colBlack, ARenderInfo); ADest.Font.FPColor := AdjustColorToBackground(colBlack, ARenderInfo);
ADest.TextOut(Points[0].X-Round(ADest.Font.Size*1.5), Points[0].Y, Format('%.1f', [LowerDim.Y])); txt := Format('%.1f', [LowerDim.Y]);
if ADoDraw then
ADest.TextOut(Points[0].X-Round(ADest.Font.Size*1.5), Points[0].Y, txt);
ADest.Font.Orientation := 0; ADest.Font.Orientation := 0;
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo,
Points[0].X - Round(ADest.Font.Size*1.5), Points[0].Y,
Points[0].X, Points[0].Y + ADest.TextWidth(txt)
);
end; end;
SetLength(Points, 0); SetLength(Points, 0);
@ -5074,51 +5136,54 @@ begin
Points[1] := Rotate2DPoint(Points[1], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle); Points[1] := Rotate2DPoint(Points[1], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle);
Points[2] := Rotate2DPoint(Points[2], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle); Points[2] := Rotate2DPoint(Points[2], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle);
if not IsDiameter then if ADoDraw then
begin begin
// Basic line if not IsDiameter then
ADest.MoveTo(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)); begin
ADest.LineTo(CoordToCanvasX(DimensionLeft.X), CoordToCanvasY(DimensionLeft.Y)); // Basic line
ADest.MoveTo(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y));
ADest.LineTo(CoordToCanvasX(DimensionLeft.X), CoordToCanvasY(DimensionLeft.Y));
// Draw the arrow // Draw the arrow
ADest.Polygon(Points); ADest.Polygon(Points);
ADest.Brush.Style := bsClear; ADest.Brush.Style := bsClear;
// Dimension text // Dimension text
Points[0].X := CoordToCanvasX(Center.X); Points[0].X := CoordToCanvasX(Center.X);
Points[0].Y := CoordToCanvasY(Center.Y); Points[0].Y := CoordToCanvasY(Center.Y);
ADest.Font.Size := 10; ADest.Font.Size := 10;
ADest.Font.FPColor := AdjustColorToBackground(colBlack, ARenderInfo); ADest.Font.FPColor := AdjustColorToBackground(colBlack, ARenderInfo);
ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [lRadius])); ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [lRadius]));
end end
else else
begin begin
// Basic line // Basic line
ADest.MoveTo(CoordToCanvasX(DimensionLeft.X), CoordToCanvasY(DimensionLeft.Y)); ADest.MoveTo(CoordToCanvasX(DimensionLeft.X), CoordToCanvasY(DimensionLeft.Y));
ADest.LineTo(CoordToCanvasX(DimensionRight.X), CoordToCanvasY(DimensionRight.Y)); ADest.LineTo(CoordToCanvasX(DimensionRight.X), CoordToCanvasY(DimensionRight.Y));
// Draw the first arrow // Draw the first arrow
ADest.Polygon(Points); ADest.Polygon(Points);
ADest.Brush.Style := bsClear; ADest.Brush.Style := bsClear;
// And the second // And the second
Points[0] := Point(CoordToCanvasX(Center.X + lRadius), CoordToCanvasY(Center.Y)); Points[0] := Point(CoordToCanvasX(Center.X + lRadius), CoordToCanvasY(Center.Y));
Points[1] := Point(CoordToCanvasX(Center.X + lRadius*0.8), CoordToCanvasY(Center.Y - lRadius*0.1)); Points[1] := Point(CoordToCanvasX(Center.X + lRadius*0.8), CoordToCanvasY(Center.Y - lRadius*0.1));
Points[2] := Point(CoordToCanvasX(Center.X + lRadius*0.8), CoordToCanvasY(Center.Y + lRadius*0.1)); Points[2] := Point(CoordToCanvasX(Center.X + lRadius*0.8), CoordToCanvasY(Center.Y + lRadius*0.1));
// Now rotate it to the actual position // Now rotate it to the actual position
Points[0] := Rotate2DPoint(Points[0], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle + Pi); Points[0] := Rotate2DPoint(Points[0], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle + Pi);
Points[1] := Rotate2DPoint(Points[1], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle + Pi); Points[1] := Rotate2DPoint(Points[1], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle + Pi);
Points[2] := Rotate2DPoint(Points[2], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle + Pi); Points[2] := Rotate2DPoint(Points[2], Point(CoordToCanvasX(Center.X), CoordToCanvasY(Center.Y)), lAngle + Pi);
// //
ADest.Polygon(Points); ADest.Polygon(Points);
ADest.Brush.Style := bsClear; ADest.Brush.Style := bsClear;
// Dimension text // Dimension text
Points[0].X := CoordToCanvasX(Center.X); Points[0].X := CoordToCanvasX(Center.X);
Points[0].Y := CoordToCanvasY(Center.Y); Points[0].Y := CoordToCanvasY(Center.Y);
ADest.Font.Size := 10; ADest.Font.Size := 10;
ADest.Font.FPColor := AdjustColorToBackground(colBlack, ARenderInfo); ADest.Font.FPColor := AdjustColorToBackground(colBlack, ARenderInfo);
ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [lRadius * 2])); ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [lRadius * 2]));
end;
end; end;
SetLength(Points, 0); SetLength(Points, 0);
@ -5163,6 +5228,7 @@ var
{$ifdef USE_LCL_CANVAS} {$ifdef USE_LCL_CANVAS}
ALCLDest: TCanvas absolute ADest; ALCLDest: TCanvas absolute ADest;
{$endif} {$endif}
txt: String;
begin begin
ADest.Pen.FPColor := colYellow;//AdjustColorToBackground(colBlack, ARenderInfo); ADest.Pen.FPColor := colYellow;//AdjustColorToBackground(colBlack, ARenderInfo);
ADest.Pen.Width := 1; ADest.Pen.Width := 1;
@ -5173,11 +5239,12 @@ begin
//ADest.Line(CoordToCanvasX(BaseRight.X), CoordToCanvasY(BaseRight.Y), CoordToCanvasX(DimensionRight.X), CoordToCanvasY(DimensionRight.Y)); //ADest.Line(CoordToCanvasX(BaseRight.X), CoordToCanvasY(BaseRight.Y), CoordToCanvasX(DimensionRight.X), CoordToCanvasY(DimensionRight.Y));
// Now the arc // Now the arc
ALCLDest.Arc( if ADoDraw then
CoordToCanvasX(BaseLeft.X - ArcRadius), CoordToCanvasY(BaseLeft.Y - ArcRadius), ALCLDest.Arc(
CoordToCanvasX(BaseLeft.X + ArcRadius), CoordToCanvasY(BaseLeft.Y + ArcRadius), CoordToCanvasX(BaseLeft.X - ArcRadius), CoordToCanvasY(BaseLeft.Y - ArcRadius),
CoordToCanvasX(DimensionRight.X), CoordToCanvasY(DimensionRight.Y), CoordToCanvasX(BaseLeft.X + ArcRadius), CoordToCanvasY(BaseLeft.Y + ArcRadius),
CoordToCanvasX(DimensionLeft.X), CoordToCanvasY(DimensionLeft.Y)); CoordToCanvasX(DimensionRight.X), CoordToCanvasY(DimensionRight.Y),
CoordToCanvasX(DimensionLeft.X), CoordToCanvasY(DimensionLeft.Y));
// Now the arrows // Now the arrows
SetLength(Points, 3); SetLength(Points, 3);
@ -5193,7 +5260,8 @@ begin
Points[1] := Point(CoordToCanvasX(lTriangleCorner.X), CoordToCanvasY(lTriangleCorner.Y)); Points[1] := Point(CoordToCanvasX(lTriangleCorner.X), CoordToCanvasY(lTriangleCorner.Y));
lTriangleCorner := Rotate3DPointInXY(lTriangleCenter, ArcLeft, - Pi * 10 / 180); lTriangleCorner := Rotate3DPointInXY(lTriangleCenter, ArcLeft, - Pi * 10 / 180);
Points[2] := Point(CoordToCanvasX(lTriangleCorner.X), CoordToCanvasY(lTriangleCorner.Y)); Points[2] := Point(CoordToCanvasX(lTriangleCorner.X), CoordToCanvasY(lTriangleCorner.Y));
ADest.Polygon(Points); if ADoDraw then
ADest.Polygon(Points);
// Right Arrow // Right Arrow
Points[0] := Point(CoordToCanvasX(ArcRight.X), CoordToCanvasY(ArcRight.Y)); Points[0] := Point(CoordToCanvasX(ArcRight.X), CoordToCanvasY(ArcRight.Y));
@ -5204,7 +5272,8 @@ begin
lTriangleCorner := Rotate3DPointInXY(lTriangleCenter, ArcRight, - Pi * 10 / 180); lTriangleCorner := Rotate3DPointInXY(lTriangleCenter, ArcRight, - Pi * 10 / 180);
Points[2] := Point(CoordToCanvasX(lTriangleCorner.X), CoordToCanvasY(lTriangleCorner.Y)); Points[2] := Point(CoordToCanvasX(lTriangleCorner.X), CoordToCanvasY(lTriangleCorner.Y));
ADest.Polygon(Points); ADest.Polygon(Points);
ADest.Brush.Style := bsClear; if ADoDraw then
ADest.Brush.Style := bsClear;
// Dimension text // Dimension text
Points[0].X := CoordToCanvasX(TextPos.X); Points[0].X := CoordToCanvasX(TextPos.X);
@ -5212,7 +5281,9 @@ begin
ADest.Font.Size := 10; ADest.Font.Size := 10;
ADest.Font.Orientation := 0; ADest.Font.Orientation := 0;
ADest.Font.FPColor := colYellow;//AdjustColorToBackground(colBlack, ARenderInfo); ADest.Font.FPColor := colYellow;//AdjustColorToBackground(colBlack, ARenderInfo);
ADest.TextOut(Points[0].X, Points[0].Y-Round(ADest.Font.Size*1.5), Format('%.1fº', [ArcValue])); txt := Format('%.1fº', [ArcValue]);
if ADoDraw then
ADest.TextOut(Points[0].X, Points[0].Y-Round(ADest.Font.Size*1.5), txt);
end; end;
procedure TvArcDimension.CalculateExtraArcInfo; procedure TvArcDimension.CalculateExtraArcInfo;
@ -5412,7 +5483,8 @@ begin
if lFinalW < 0 then lFinalW := lFinalW * -1; if lFinalW < 0 then lFinalW := lFinalW * -1;
lFinalH := Round(Height * AMulY); lFinalH := Round(Height * AMulY);
if lFinalH < 0 then lFinalH := lFinalH * -1; if lFinalH < 0 then lFinalH := lFinalH * -1;
TCanvas(ADest).StretchDraw(Bounds(lFinalX, lFinalY, lFinalW, lFinalH), lBitmap); if ADoDraw then
TCanvas(ADest).StretchDraw(Bounds(lFinalX, lFinalY, lFinalW, lFinalH), lBitmap);
finally finally
lImageWriter.Free; lImageWriter.Free;
lMemoryStream.Free; lMemoryStream.Free;
@ -5530,7 +5602,7 @@ begin
lPoints[1].Y := CoordToCanvasY(lPointE.Y); lPoints[1].Y := CoordToCanvasY(lPointE.Y);
lPoints[2].X := CoordToCanvasX(lPointF.X); lPoints[2].X := CoordToCanvasX(lPointF.X);
lPoints[2].Y := CoordToCanvasY(lPointF.Y); lPoints[2].Y := CoordToCanvasY(lPointF.Y);
ADest.Polygon(lPoints); if ADoDraw then ADest.Polygon(lPoints);
end; end;
{ TvFormulaElement } { TvFormulaElement }
@ -5703,95 +5775,96 @@ begin
LeftC := CoordToCanvasX(Left); LeftC := CoordToCanvasX(Left);
TopC := CoordToCanvasY(Top); TopC := CoordToCanvasY(Top);
case Kind of if ADoDraw then
fekVariable: ADest.TextOut(LeftC, TopC, Text); case Kind of
fekEqual: ADest.TextOut(LeftC, TopC, '='); fekVariable: ADest.TextOut(LeftC, TopC, Text);
fekSubtraction: ADest.TextOut(LeftC, TopC, '-'); fekEqual: ADest.TextOut(LeftC, TopC, '=');
fekMultiplication: fekSubtraction: ADest.TextOut(LeftC, TopC, '-');
begin fekMultiplication:
// Don't draw anything, leave an empty space, it looks better begin
//ADest.TextOut(LeftC, TopC, 'x'); // × -> Unicode times symbol // Don't draw anything, leave an empty space, it looks better
end; //ADest.TextOut(LeftC, TopC, 'x'); // × -> Unicode times symbol
fekSum: ADest.TextOut(LeftC, TopC, '+'); end;
fekPlusMinus:ADest.TextOut(LeftC, TopC, '±'); fekSum: ADest.TextOut(LeftC, TopC, '+');
fekLessThan: ADest.TextOut(LeftC, TopC, '<'); fekPlusMinus:ADest.TextOut(LeftC, TopC, '±');
fekLessOrEqualThan: ADest.TextOut(LeftC, TopC, '≤'); fekLessThan: ADest.TextOut(LeftC, TopC, '<');
fekGreaterThan: ADest.TextOut(LeftC, TopC, '>'); fekLessOrEqualThan: ADest.TextOut(LeftC, TopC, '≤');
fekGreaterOrEqualThan: ADest.TextOut(LeftC, TopC, '≥'); fekGreaterThan: ADest.TextOut(LeftC, TopC, '>');
fekHorizontalLine: ADest.Line(LeftC, TopC, CoordToCanvasX(Left+Width), TopC); fekGreaterOrEqualThan: ADest.TextOut(LeftC, TopC, '≥');
// Complex ones fekHorizontalLine: ADest.Line(LeftC, TopC, CoordToCanvasX(Left+Width), TopC);
fekFraction: // Complex ones
begin fekFraction:
Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); begin
AdjacentFormula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
AdjacentFormula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
// Division line // Division line
lPt[0].X := CoordToCanvasX(Formula.Left); lPt[0].X := CoordToCanvasX(Formula.Left);
lPt[1].X := CoordToCanvasX(Formula.Left + Formula.Width); lPt[1].X := CoordToCanvasX(Formula.Left + Formula.Width);
lPt[0].Y := CoordToCanvasY(Formula.Top - Formula.Height); lPt[0].Y := CoordToCanvasY(Formula.Top - Formula.Height);
lPt[1].Y := CoordToCanvasY(Formula.Top - Formula.Height); lPt[1].Y := CoordToCanvasY(Formula.Top - Formula.Height);
ADest.Line(lPt[0].X, lPt[0].Y, lPt[1].X, lPt[1].Y); ADest.Line(lPt[0].X, lPt[0].Y, lPt[1].X, lPt[1].Y);
end; end;
fekRoot: fekRoot:
begin begin
Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
// Root drawing // Root drawing
lPt[0].X := CoordToCanvasX(Left); lPt[0].X := CoordToCanvasX(Left);
lPt[0].Y := CoordToCanvasY(Top - Formula.Height * 0.7 + 5); lPt[0].Y := CoordToCanvasY(Top - Formula.Height * 0.7 + 5);
// diagonal down // diagonal down
lPt[1].X := CoordToCanvasX(Left + 5); lPt[1].X := CoordToCanvasX(Left + 5);
lPt[1].Y := CoordToCanvasY(Top - Formula.Height * 0.7); lPt[1].Y := CoordToCanvasY(Top - Formula.Height * 0.7);
// up // up
lPt[2].X := CoordToCanvasX(Left + 5); lPt[2].X := CoordToCanvasX(Left + 5);
lPt[2].Y := CoordToCanvasY(Top); lPt[2].Y := CoordToCanvasY(Top);
// straight right // straight right
lPt[3].X := CoordToCanvasX(Left + Formula.Width); lPt[3].X := CoordToCanvasX(Left + Formula.Width);
lPt[3].Y := CoordToCanvasY(Top); lPt[3].Y := CoordToCanvasY(Top);
// //
ADest.Polyline(lPt); ADest.Polyline(lPt);
end; end;
fekPower: fekPower:
begin begin
Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
// The superscripted power // The superscripted power
lOldFontSize := ADest.Font.Size; lOldFontSize := ADest.Font.Size;
if lOldFontSize = 0 then ADest.Font.Size := 5 if lOldFontSize = 0 then ADest.Font.Size := 5
else ADest.Font.Size := lOldFontSize div 2; else ADest.Font.Size := lOldFontSize div 2;
AdjacentFormula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); AdjacentFormula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
ADest.Font.Size := lOldFontSize; ADest.Font.Size := lOldFontSize;
end; end;
fekSubscript: fekSubscript:
begin begin
Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
// The subscripted item // The subscripted item
lOldFontSize := ADest.Font.Size; lOldFontSize := ADest.Font.Size;
if lOldFontSize = 0 then ADest.Font.Size := 5 if lOldFontSize = 0 then ADest.Font.Size := 5
else ADest.Font.Size := lOldFontSize div 2; else ADest.Font.Size := lOldFontSize div 2;
AdjacentFormula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); AdjacentFormula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
ADest.Font.Size := lOldFontSize; ADest.Font.Size := lOldFontSize;
end; end;
fekSummation: fekSummation:
begin begin
// Draw the summation symbol // Draw the summation symbol
lOldFontSize := ADest.Font.Size; lOldFontSize := ADest.Font.Size;
ADest.Font.Size := 15; ADest.Font.Size := 15;
lStr := #$E2#$88#$91; // Unicode Character 'N-ARY SUMMATION' (U+2211) lStr := #$E2#$88#$91; // Unicode Character 'N-ARY SUMMATION' (U+2211)
ADest.TextOut(LeftC, TopC, lStr); ADest.TextOut(LeftC, TopC, lStr);
ADest.Font.Size := lOldFontSize; ADest.Font.Size := lOldFontSize;
// Draw the bottom/main formula // Draw the bottom/main formula
Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
// Draw the top formula // Draw the top formula
AdjacentFormula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); AdjacentFormula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
end;
fekFormula:
begin
// Draw the formula
Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
end;
end; end;
fekFormula:
begin
// Draw the formula
Formula.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
end;
end;
end; end;
procedure TvFormulaElement.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; procedure TvFormulaElement.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc;
@ -6271,7 +6344,11 @@ begin
end; end;
destructor TvEntityWithSubEntities.Destroy; destructor TvEntityWithSubEntities.Destroy;
var
i: Integer;
begin begin
for i:= FElements.Count-1 downto 0 do
TvEntity(FElements[i]).Free;
FElements.Free; FElements.Free;
inherited Destroy; inherited Destroy;
end; end;
@ -6359,8 +6436,12 @@ procedure TvEntityWithSubEntities.Render(ADest: TFPCustomCanvas; var ARenderInfo
ADestX: Integer; ADestY: Integer; AMulX: Double; AMulY: Double; ADoDraw: Boolean); ADestX: Integer; ADestY: Integer; AMulX: Double; AMulY: Double; ADoDraw: Boolean);
var var
lEntity: TvEntity; lEntity: TvEntity;
rinfo: TvRenderInfo;
isFirst: Boolean;
begin begin
rinfo := ARenderInfo;
inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
isFirst := true;
lEntity := GetFirstEntity(); lEntity := GetFirstEntity();
while lEntity <> nil do while lEntity <> nil do
begin begin
@ -6372,8 +6453,22 @@ begin
// Render // Render
lEntity.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMuly, ADoDraw); lEntity.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMuly, ADoDraw);
if isFirst then
begin
rinfo := ARenderInfo;
isFirst := false;
end else
CalcEntityCanvasMinMaxXY_With2Points(rinfo,
ARenderInfo.EntityCanvasMinXY.X,
ARenderInfo.EntityCanvasMinXY.Y,
ARenderInfo.EntityCanvasMaxXY.X,
ARenderInfo.EntityCanvasMaxXY.Y
);
lEntity := GetNextEntity(); lEntity := GetNextEntity();
end; end;
ARenderInfo := rinfo;
end; end;
function TvEntityWithSubEntities.GenerateDebugTree( function TvEntityWithSubEntities.GenerateDebugTree(
@ -6694,7 +6789,7 @@ begin
if Style <> nil then if Style <> nil then
Style.ApplyIntoEntity(lText); Style.ApplyIntoEntity(lText);
lText.Render(ADest, lEntityRenderInfo, CurX, ADestY + lHeight_px, AMulX, AMulY, ADoDraw); lText.Render(ADest, lEntityRenderInfo, CurX, ADestY, AMulX, AMulY, ADoDraw);
lText.CalculateBoundingBox(ADest, lLeft, lTop, lRight, lBottom); lText.CalculateBoundingBox(ADest, lLeft, lTop, lRight, lBottom);
lCurWidth := lCurWidth + Abs(lRight - lLeft); lCurWidth := lCurWidth + Abs(lRight - lLeft);
lFirstText := False; lFirstText := False;
@ -7043,6 +7138,7 @@ constructor TvPage.Create(AOwner: TvVectorialDocument);
begin begin
inherited Create; inherited Create;
FOwner := AOwner; FOwner := AOwner;
AdjustPenColorToBackground := true;
end; end;
destructor TvPage.Destroy; destructor TvPage.Destroy;
@ -7770,12 +7866,17 @@ end;
use this function like this: use this function like this:
ASource.Render(ADest, 0, ASource.Height, 1.0, -1.0); ASource.Render(ADest, 0, ASource.Height, 1.0, -1.0);
Set ADoDraw to falses in order to just get the bounding box of all entities
on the page in RenderInfo.EnitityCanvasMinXY/EntityCanvasMaxXY.
} }
procedure TvVectorialPage.Render(ADest: TFPCustomCanvas; procedure TvVectorialPage.Render(ADest: TFPCustomCanvas;
ADestX: Integer; ADestY: Integer; AMulX: Double; AMulY: Double); ADestX: Integer; ADestY: Integer; AMulX: Double; AMulY: Double;
ADoDraw: Boolean = true);
var var
i: Integer; i: Integer;
CurEntity: TvEntity; CurEntity: TvEntity;
rinfo: TvRenderInfo;
begin begin
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG} {$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
WriteLn(':>DrawFPVectorialToCanvas'); WriteLn(':>DrawFPVectorialToCanvas');
@ -7790,9 +7891,23 @@ begin
CurEntity := GetEntity(i); CurEntity := GetEntity(i);
RenderInfo.BackgroundColor := BackgroundColor; RenderInfo.BackgroundColor := BackgroundColor;
CurEntity.Render(ADest, RenderInfo, ADestX, ADestY, AMulX, AMulY); RenderInfo.AdjustPenColorToBackground := AdjustPenColorToBackground;
CurEntity.Render(ADest, RenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
if i = 0 then
rInfo := RenderInfo
else
begin
rInfo.EntityCanvasMinXY.X := Min(rInfo.EntityCanvasMinXY.X, RenderInfo.EntityCanvasMinXY.X);
rInfo.EntityCanvasMinXY.Y := Min(rInfo.EntityCanvasMinXY.Y, RenderInfo.EntityCanvasMinXY.Y);
rInfo.EntityCanvasMaxXY.X := Max(rInfo.EntityCanvasMaxXY.X, RenderInfo.EntityCanvasMaxXY.X);
rInfo.EntityCanvasMaxXY.Y := Max(rInfo.EntityCanvasMaxXY.Y, RenderInfo.EntityCanvasMaxXY.Y);
end;
end; end;
RenderInfo := rInfo;
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG} {$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
WriteLn(':<DrawFPVectorialToCanvas'); WriteLn(':<DrawFPVectorialToCanvas');
{$endif} {$endif}
@ -7983,7 +8098,7 @@ begin
end; end;
procedure TvTextPageSequence.Render(ADest: TFPCustomCanvas; ADestX: Integer; procedure TvTextPageSequence.Render(ADest: TFPCustomCanvas; ADestX: Integer;
ADestY: Integer; AMulX: Double; AMulY: Double); ADestY: Integer; AMulX: Double; AMulY: Double; ADoDraw: Boolean = true);
function CoordToCanvasX(ACoord: Double): Integer; function CoordToCanvasX(ACoord: Double): Integer;
begin begin
@ -8019,7 +8134,7 @@ begin
CurEntity.Y := 0; CurEntity.Y := 0;
lHeight_px := CurEntity.GetEntityFeatures(ADest).DrawsUpwardHeightAdjustment; lHeight_px := CurEntity.GetEntityFeatures(ADest).DrawsUpwardHeightAdjustment;
RenderInfo.BackgroundColor := BackgroundColor; RenderInfo.BackgroundColor := BackgroundColor;
CurEntity.Render(ADest, RenderInfo, ADestX, CurY_px + lHeight_px, AMulX, AMulY); CurEntity.Render(ADest, RenderInfo, ADestX, CurY_px + lHeight_px, AMulX, AMulY, ADoDraw);
// Store the old position in X/Y but don't use it, we use this to debug out the position // Store the old position in X/Y but don't use it, we use this to debug out the position
CurEntity.X := ADestX; CurEntity.X := ADestX;
CurEntity.Y := CurY_px; CurEntity.Y := CurY_px;
@ -8233,7 +8348,7 @@ procedure TvVectorialDocument.ReadFromFile(AFileName: string);
var var
lFormat: TvVectorialFormat; lFormat: TvVectorialFormat;
begin begin
lFormat := GetFormatFromExtension(ExtractFileExt(AFileName)); lFormat := GetFormatFromExtension(AFileName);
ReadFromFile(AFileName, lFormat); ReadFromFile(AFileName, lFormat);
end; end;

View File

@ -130,9 +130,11 @@ type
function ReadTextFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity; function ReadTextFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
function ReadUseFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity; function ReadUseFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
// //
procedure StringToPenPattern(const AStr: String; var APen: TvPen);
function StringWithUnitToFloat(AStr: string; ACoordKind: TSVGCoordinateKind = sckUnknown; function StringWithUnitToFloat(AStr: string; ACoordKind: TSVGCoordinateKind = sckUnknown;
ADefaultUnit: TSVGUnit = suPX; ATargetUnit: TSVGUnit = suPX): Double; ADefaultUnit: TSVGUnit = suPX; ATargetUnit: TSVGUnit = suPX): Double;
function StringFloatZeroToOneToWord(AStr: string): Word; function StringFloatZeroToOneToWord(AStr: string): Word;
procedure ConvertSVGCoordinatesToFPVCoordinates( procedure ConvertSVGCoordinatesToFPVCoordinates(
const AData: TvVectorialPage; const AData: TvVectorialPage;
const ASrcX, ASrcY: Double; var ADestX, ADestY: Double; const ASrcX, ASrcY: Double; var ADestX, ADestY: Double;
@ -147,6 +149,7 @@ type
ADoViewBoxAdjust: Boolean = True); ADoViewBoxAdjust: Boolean = True);
procedure AutoDetectDocSize(var ALeft, ATop, ARight, ABottom: Double; ABaseNode: TDOMNode); procedure AutoDetectDocSize(var ALeft, ATop, ARight, ABottom: Double; ABaseNode: TDOMNode);
function SVGColorValueStrToWord(AStr: string): Word; function SVGColorValueStrToWord(AStr: string): Word;
public public
{ General reading methods } { General reading methods }
constructor Create; override; constructor Create; override;
@ -205,7 +208,11 @@ begin
end; end;
destructor TSVGPathTokenizer.Destroy; destructor TSVGPathTokenizer.Destroy;
var
i: Integer;
begin begin
for i:=Tokens.Count-1 downto 0 do
Tokens[i].Free;
Tokens.Free; Tokens.Free;
inherited Destroy; inherited Destroy;
@ -928,7 +935,7 @@ begin
end end
else if AKey = 'stroke-opacity' then else if AKey = 'stroke-opacity' then
begin begin
ADestEntity.Pen.Color.Alpha := Round(StrToFloat(AValue)*$FFFF); ADestEntity.Pen.Color.Alpha := Round(StrToFloat(AValue, FPointSeparator)*$FFFF);
end end
else if AKey = 'stroke-linecap' then else if AKey = 'stroke-linecap' then
begin begin
@ -937,7 +944,9 @@ begin
'round': 'round':
'square': ADestEntity.Pen; 'square': ADestEntity.Pen;
end;} end;}
end; end
else if AKey = 'stroke-dasharray' then
StringToPenPattern(AValue, ADestEntity.Pen);
end; end;
function TvSVGVectorialReader.ReadSVGBrushStyleWithKeyAndValue(AKey, function TvSVGVectorialReader.ReadSVGBrushStyleWithKeyAndValue(AKey,
@ -1458,6 +1467,7 @@ begin
lEntityName := LowerCase(ANode.NodeName); lEntityName := LowerCase(ANode.NodeName);
case lEntityName of case lEntityName of
'circle': Result := ReadCircleFromNode(ANode, AData, ADoc); 'circle': Result := ReadCircleFromNode(ANode, AData, ADoc);
'defs': ReadDefsFromNode(ANode, AData, ADoc);
'ellipse': Result := ReadEllipseFromNode(ANode, AData, ADoc); 'ellipse': Result := ReadEllipseFromNode(ANode, AData, ADoc);
'frame': Result := ReadFrameFromNode(ANode, AData, ADoc); 'frame': Result := ReadFrameFromNode(ANode, AData, ADoc);
'g': ReadLayerFromNode(ANode, AData, ADoc); 'g': ReadLayerFromNode(ANode, AData, ADoc);
@ -2841,6 +2851,47 @@ begin
Result := lInsert; Result := lInsert;
end; end;
procedure TvSVGVectorialReader.StringToPenPattern(const AStr: String;
var APen: TvPen);
var
float_patt: TDoubleArray;
patt: array of LongWord;
i: Integer;
begin
float_patt := ReadSpaceSeparatedFloats(AStr, ',');
if Length(float_patt) < 2 then
exit;
SetLength(patt, Length(float_patt));
for i:=0 to High(patt) do
begin
if float_patt[i] < 0 then
raise Exception.CreateFmt('Incorrect value in stroke-dasharray "%s"', [AStr]);
patt[i] := round(float_patt[i]);
end;
case Length(patt) of
2: if patt[1] = 5 then
case patt[0] of
3: begin APen.Style := psDot; exit; end; // stroke-dasharray: 3, 5
9: begin APen.Style := psDash; exit; end; // stroke-dasharray: 9, 5
end;
4: if (patt[0] = 9) and (patt[1] = 5) and (patt[2] = 3) and (patt[3] = 5) then
begin // stroke-dasharray: 9, 5, 3, 5
APen.Style := psDashDot;
exit;
end;
6: if (patt[0] = 9) and (patt[1] = 5) and (patt[2] = 3) and (patt[3] = 5) and
(patt[4] = 3) and (patt[5] = 5)
then begin // stroke-dasharray: 9, 5, 3, 5, 3, 5
APen.Style := psDashDotDot;
exit;
end;
end;
APen.Style := psPattern;
APen.Pattern := patt;
end;
function TvSVGVectorialReader.StringWithUnitToFloat(AStr: string; function TvSVGVectorialReader.StringWithUnitToFloat(AStr: string;
ACoordKind: TSVGCoordinateKind = sckUnknown; ADefaultUnit: TSVGUnit = suPX; ACoordKind: TSVGCoordinateKind = sckUnknown; ADefaultUnit: TSVGUnit = suPX;
ATargetUnit: TSVGUnit = suPX): Double; ATargetUnit: TSVGUnit = suPX): Double;