Monthly Archives: March 2016

Bad Delphi Code

David Millington of Parnassus had a fun little Bad Delphi Code contest and just announced the results.  My submission got an honorable mention.  (Nothing like getting an honorable mention in bad code writing to give people confidence in my software.  “Hurry and buy today!” 🙂 )

Seriously, it was a lot of fun.  Apparently, results were judged on creativity, deviousness, and backstory, to submit “the worst believable code snippet or small app that you can [write], in the spirit of amusing, entertaining, or horrifying the reader.”

All of the submissions were entertaining, but I especially liked the Break entry.  Apparently, Break and Continue are not reserved words in Delphi, and are pseudo-implemented in the System unit.  It is easy to define your own Break and Continue functions and, because of scoping rules, cause your Break/Continue command to be called instead of the regular Delphi command in subsequent code, breaking everyone’s nice loop structures 🙂

To me, this one exemplifies truly “evil” (not just bad) code, because it betrays user’s expectations.  Bad code is easy to write and, admit it, all of us have done it.  Under the pressure of deadlines or just plain laziness (or sometimes incompetence), we have written long, muddled, and just plain wrong code that is a nightmare to debug and maintain.  However, if it does work, people may never even see it or have to think about it.  But when you betray user’s expectations, that is true evil code.  Component writers must guard most closely against this type of code.  Because not only do we break our code, we break our customers.

For example, one I encountered recently is in the FMX TControl3D code.  In FMX, there is a TControl.Position property.  To have 2 controls in the same position, you can easily assign to the position property:

Label1.Position := Button1.Position;

Everything we have learned with the VCL and Delphi style guides condition us to expect that the button1’s position object is copied to Label1’s position property and everything just works.

In the FMX 3D library, there is also a TControl3D.Position property.  However, it betrays our expectations.  Here is the declaration of the Position property:

 property Position: TPosition3D read FPosition write FPosition;

If we do code like:

Cube1.Position := Sphere1.Position

Instead of copying the TPosition3D object, it just sets the reference to the same TPosition3D (and throws away without freeing the old TPosition3D object).  The code appears to work at first.  However, not only have we leaked memory, we have tied the two objects’ positions together and changing one will move the other.  “Luckily,” this code blows up on desktop (though it wouldn’t with ARC code) when we try to free both objects as they both try to free the same TPosition3D object.

This is truly evil and incompetent code.  We have led our users astray and made their code bad through our own incompetence.

My Bad Delphi Code Submission

But enough about that.  I saw that David’s readers wanted access to the code for every submission.  Here is my submission:

This code is inspired by Embarcadero's FMX TPathData.SetPathString 
method, which parses a path for you.  In this code, they had a loop 
for getting the next command in the path (move, line, etc) where 
they would compare the current token against 18 commands, every 
single time even when there was a match found earlier:
 while TokenBuilder.Length > 0 do
 begin
  Token := TokenBuilder.Chars[0];
  TokenBuilder.Remove(0, 1);
  if Token.IsInArray(['z', 'Z']) then
   ClosePath;
  if Token = 'M' then
  begin
   […]
  end;
  if Token = 'm' then
  begin
   […]
  end;
  if Token = 'L' then
  begin
   […]
  end;
  […]

Finally, in XE8, they fixed it and used a case statement.

I thought I would "improve" on their code by doing the same thing 
but with longer strings and adding in unnecessary code for not 
finding the string.  I made sure that the short circuit for the 
if statements didn't work as well (wrong order).
procedure TForm1.StringCompareUnOpt(aString: String);
var
 Found: Boolean;
begin
  Found := False;
  if aString = 'A' then
  begin
    Log('B');
    Found := True;
  end;
  if (aString = 'One') and (not Found) then
  begin
    Log('Two');
    Found := True;
  end;
  if (aString = 'Foo') and (not Found) then
  begin
    Log('Bar');
    Found := True;
  end;
  if (aString = 'Four Score') and (not Found) then
  begin
    Log('And Seven Years Ago');
    Found := True;
  end;
  if (aString = 'Hello') and (not Found) then
  begin
    Log('World');
    Found := True;
  end;
  if (aString = 'Question') and (not Found) then
  begin
    Log('Answer');
    Found := True;
  end;

  if not Found then
    Log('Unknown');
end;

Now, to make it interesting, I decided to "optimize" the code by 
creating a constant array of hashes for each string and then 
comparing the current string's hash code to the constant array values.
procedure TForm1.StringCompareOpt(aString: String);
begin
  // "Optimized" string compare
  // Pros:
  //    Shorter looking code (if you disregard setting up the constants)
  //    "Looks" like it could be faster as case is just a integer comparison instead of string comparison
  // Cons:
  //    Wrong (case insensitive vs original case sensitive compare),
  //    Brittle (what if Strings const changes?  Need to regenerate hashcodes.
  //             what is GetHashCode implementation changes?  Wrong answers)
  //    Slower (GetHashCode can be slow)
  case aString.GetHashCode of
    StringA:         Log('B');
    StringOne:       Log('Two');
    StringFoo:       Log('Bar');
    StringFour:      Log('And Seven Years Ago');
    StringHello:     Log('World');
    StringQuestion:  Log('Answer');
  else
    Log('Unknown');
  end;
end;

What I like about this code is that it *almost* looks brilliant.  The 
code is much shorter and in theory using a case statement is faster.  
However, as mentioned in the code it is wrong sometimes, brittle, 
and slower as well as being a lot more work to set up. FTW! 🙂

Tom

The full code, including test program, can be downloaded here.  Happy CodeSmithing!

World Wide Web of SVGs

Scalable Vector Graphics (SVG) are awesome.  Not only because they are scaleable, but perhaps even more importantly, because they are editable.  When you download a bitmap or png from the web, your options for editing it are limited.  With a lot of work and expertise in Photoshop, you can perhaps edit them, but usually not perfectly and changing them on the fly is just not possible.

Final SVG Gauge with customized elements

Final SVG Gauge with customized elements

SVGs are different.  Each piece of the graphic is editable.  You can change colors, rotations, visibility, etc.  With the RiverSoftAVG SVG Component Library (RSCL), you get access to a whole world wide web of customizable graphics for your applications.

In this blog post, I am going to show you how to download, prepare, and use a SVG to create incredible customizable graphics for your applications.  I will take you through the exact steps in my journey, the easy and the hard, to find and customize an SVG to provide a great new gauge, that can be used in our applications.  For those who don’t own the RSCL, there is an evaluation version available from the RiverSoftAVG Download Page that you can use to follow along.

Finding the SVG

Some say that the first step on a journey is the hardest; in this case, they are definitely right.  The hardest part about this whole process is finding the right SVG.  Finding good SVGs is easy.  Even finding good, free SVGs is easy.  But finding good, free SVGs that can be easily customized (whether on purpose or by accident) is not quite as easy.

The first thing I did was go to www.openclipart.org.  OpenClipArt.org is a great repository for clip art that have been released into the public domain and licensed for unlimited commercial use.

I had no idea what I was going to create for this blog post, but I knew I wanted a gauge, so I typed “gauge” into their search box and browsed the results.  I finally settled on the speedometer https://openclipart.org/detail/99937/speedometer created and uploaded by rg1024 back in 2010.

Speedometer from www.openclipart.org

Speedometer from www.openclipart.org

It had a clean look and it looked like it had a nice discrete object for the needle (at least I hoped so).  My idea was that by rotating the needle I would have a nice, dynamic speedometer gauge.

I downloaded the SVG, which was called velocimetro.svg.  First, I opened the SVG using the SVGPaintBoxVCL demo and it looked great, but I needed to do some pre-processing work to make the gauge useful as a dynamic object.  I started the SVGEditorFMX demo application provided with the RSCL and opened the velocimetro.svg file.

Finding and changing the needle in speedometer SVG using the SVG Editor Demo application included with the RSCL

Finding and changing the needle in speedometer SVG using the SVG Editor Demo application included with the RSCL

Clicking on the needle in the SVG selected the glass shine in front of the needle, but it got me in the right area.  By clicking on elements in the left tree view, I finally found 3 paths that made up the needle: the needle itself, its shadow, and the highlight for the needle.  I changed the names of the elements to Needle, NeedleShadow, and NeedleHighlight to make them easier to access in code and saved the SVG.  I would need to group the three paths to make it easier to rotate, but the SVGEditorFMX does not have a way to add a svg group yet so I loaded the SVG in NotePad++.  I searched for “Needle” and quickly created a svg group around the three paths.

<g id="NeedleGroup">
  <path id="Needle" style="fill:white" d="M391.31,343.02 L392.646,343.378 L363.659,465.898 L358.572,474.673 L355.17,473.761 L355.152,463.618 L391.31,343.018 Z "/>
  <path id="NeedleShadow" style="fill:black" d="M394.22,344.12 L359.114,475.14 L360.922,475.625 L366.004,466.866 L394.995,344.326 L394.219,344.118 Z "/>
  <path id="NeedleHighlight" style="fill:#babdb6" d="M392.22,342.62 L357.114,473.64 L358.922,474.125 L364.004,465.366 L392.995,342.826 L392.219,342.618 Z "/>
</g>

Saving my work, my speedometer was almost ready for its first use…

Rotating the Speedometer Needle at Run-Time

I quickly created a test project to display and hopefully edit my SVG.  I performed the following steps:

  • Created a New VCL Project
  • Dropped a TRSSVGDocument component on the form
  • Set the RSSVGDocument1.Filename property to the velocimetro.svg file
  • Dropped a TRSSVGImage component on the form
  • Set the RSSVGImage1.SVGDocument to RSSVGDocument1

I now had my SVG displaying in a project.  Now, I needed to edit the needle. For testing, I decided to use a TTrackBar to change the speed value.  From looking at the SVG output, the author seemed to want the speed to go from -5 (?!) to 245.  I needed to convert that value into a rotation angle for the needle group.  Dropping a TTrackBar onto the form, I set its min and max, and created a OnChange event handler to find the needle group by its name and then apply a rotation matrix to it.

However, there were two problems.  Firstly, rotation usually occurs around the origin which is most definitely not what we wanted here.  I needed the pivot point or base of the needle.  When I had examined the needle, I realized the bottom of the needle is in the bottom left.  Thankfully, that is really easy to get.  Every graphical element in the RSCL has a BoundsRect property.  I would use the BoundsRect.BottomLeft as the pivot point. Secondly, by default the needle was not placed pointing left or right or top or bottom.  It was at a slight angle, which I had to account for in the rotation angle. Also, I needed to constrain the angle so that the needle stopped at the bounds of the SVG.  By experimentation, I figured out that the needle needed to be rotated counter-clockwise at the most to -109.  Clockwise, the angle could not be greater than 78.

Finding the needle itself is very easy with the RSCL since I changed its ID.  I just called the TSVGDocument.AllItems property with the name of the needle.  Here is the final event handler:

procedure TfrmSpeedometer.TrackBar1Change(Sender: TObject);
const
 LowGauge = -109;
 HiGauge = 187-LowGauge;
var
 Needle: TSVGGraphicElement;
 NeedleAngle: Single;
 aRect: TSVGRect;
begin
 Label1.Caption := TTrackBar(Sender).Position.ToString;
 // Get Needle Group to manipulate
 Needle := RSSVGDocument1.SVG.AllItems['NeedleGroup'] as TSVGGraphicElement;
 aRect := Needle.BoundsRect;
 // scale trackbar value into between Min and Max
 NeedleAngle := TTrackBar(Sender).Position;
 NeedleAngle := ((NeedleAngle-TTrackBar(Sender).Min)/(TTrackBar(Sender).Max-TTrackBar(Sender).Min))*(HiGauge-LowGauge)+LowGauge;
 // rotate around bottom left
 Needle.Matrix := CreateRotationRSMatrix(RSPoint(aRect.Left, aRect.Bottom), DegToRad(NeedleAngle));
end;

Running the application, it worked but there was horrible flicker.  I set the form’s DoubleBuffered property to True and that fixed that issue.

However, there was one issue left.  Annoyingly, it turned out that the SVG was not created with the needle properly centered.  When the needle was a low number like 40, it would overlap the markers.  However, when the needle was a high number like 220, the needle wouldn’t reach the markers.  To fix this issue, I decided to create another group around our NeedleGroup, this group would translate (move in X and Y) the needle group over so everything would line up just so.

 <g id="OuterNeedleGroup" transform="translate(5 -10)">
   <g id="NeedleGroup">
    <path id="Needle" style="fill:white" d="M391.31,343.02 L392.646,343.378 L363.659,465.898 L358.572,474.673 L355.17,473.761 L355.152,463.618 L391.31,343.018 Z "/>
    <path id="NeedleShadow" style="fill:black" d="M394.22,344.12 L359.114,475.14 L360.922,475.625 L366.004,466.866 L394.995,344.326 L394.219,344.118 Z "/>
    <path id="NeedleHighlight" style="fill:#babdb6" d="M392.22,342.62 L357.114,473.64 L358.922,474.125 L364.004,465.366 L392.995,342.826 L392.219,342.618 Z "/>
   </g>
 </g>

I ran the project, and moved the Track bar left and right.  The needle smoothly updated from -5 to 245 and nicely centered.  Fantastic!  The hard part was done.

Customizing the Speedometer

Besides the problems with the needle, this SVG turned out to be a very good SVG for customization.  Everything was nicely ordered if not identified well.  By spelunking around with the SVG Editor, I identified and labeled the other parts of the speedometer such as the frame, the backface and the markers.  About the only limitation with this SVG is that the author didn’t use text elements for the speed labels (40, 80, 120, etc).  Instead, the author used paths so I cannot change those labels easily.  I also ended up changing the IDs for the gradients (e.g., linearGradientFrame) to more easily identify and find them later.

After I finished identifying all the parts of the speedometer, I wanted to test it out.  I dropped a TColorBox on my form and set up an event handler to change the color of the needle.  The only trick is that I needed to change the needle’s color and the needle’s highlight:

procedure TfrmSpeedometer.ColorBox2Change(Sender: TObject);
var
 Needle: TSVGGraphicElement;
 NewColor: TSVGColorRec;
begin
 NewColor := (Sender as TColorBox).Selected;
 RSSVGDocument1.SVG.BeginUpdate;
 try
 // get needle
 Needle := RSSVGDocument1.SVG.AllItems['Needle'] as TSVGGraphicElement;
 // change its color
 Needle.Brush.Color := NewColor;
 // dim colors
 NewColor.R := trunc(NewColor.R * 0.5);
 NewColor.G := trunc(NewColor.G * 0.5);
 NewColor.B := trunc(NewColor.B * 0.5);
 // get highlight or shaded portion
 Needle := RSSVGDocument1.SVG.AllItems['NeedleHighlight'] as TSVGGraphicElement;
 // change its color
 Needle.Brush.Color := NewColor;
 finally
 RSSVGDocument1.SVG.EndUpdate;
 end;
end;

One important note about the above code.  By default, SVG elements inherit their color from their parent element.  Usually, when you write Needle.Brush, what you are getting is a customized copy of the parent’s Brush.  Setting the brush’s color will not actually change anything as the value is still inherited.  In that case, you need to turn off the inheritance of the fill value before setting the fill value, like so: Needle.Inherits.Fill := False;  However, in this case, the needle’s color is not inherited and we can just set the Needle.Brush without worry.

Running the project again, I could now change the needle’s color.

Changing the backface or the frame turned out to be slightly more challenging.  For the backface, the fill color is a gradient (e.g., <path id=”backface” style=”fill:url(#linearGradientBackface)”…).  You have to change the color of the gradient (more specifically, the gradient stops) in order to change the color of the backface.  After doing that, you need to force the backface to update its color.  Calling the Reset method will mark every resource (i.e., brush, pen, and font as dirty and make the SVG element regenerate the resources when needed.

Dropping another TColorBox on the form, here is the backface OnChange event handler:

var
 Backface: TSVGLinearGradient;
 NewColor: TSVGColorRec;
 i: Integer;
begin
 NewColor := (Sender as TColorBox).Selected;
 RSSVGDocument1.SVG.BeginUpdate;
 try
  // get Backface gradient highlight
  Backface := RSSVGDocument1.SVG.AllItems['linearGradientBackfaceHighlight'] as TSVGLinearGradient;
  for i := 0 to Backface.Items.Count - 1 do
   (Backface.Items[i] as TSVGGradientStop).StopBrush.Color := NewColor;
  // get Backface gradient
  Backface := RSSVGDocument1.SVG.AllItems['linearGradientBackface'] as TSVGLinearGradient;
  (Backface.Items[0] as TSVGGradientStop).StopBrush.Color := NewColor;
  // dim colors
  NewColor.R := trunc(NewColor.R * 0.5);
  NewColor.G := trunc(NewColor.G * 0.5);
  NewColor.B := trunc(NewColor.B * 0.5);
  (Backface.Items[1] as TSVGGradientStop).StopBrush.Color := NewColor;
  // rebuild all brushes so that they ask for new gradients
  RSSVGDocument1.SVG.Reset;
 finally
  RSSVGDocument1.SVG.EndUpdate;
 end;

I did the same thing for the frame, the markers and text, and the glassy shine.

Finally, to show how easy it is to change other properties, I added checkboxes to turn off the visibility of the different elements.  The check box handlers look like this:

procedure TfrmSpeedometer.CheckBox5Click(Sender: TObject);
begin
 RSSVGDocument1.SVG.BeginUpdate;
 try
  (RSSVGDocument1.SVG.AllItems['backface'] as TSVGGraphicElement).Visible := (Sender as TCheckBox).Checked;
  (RSSVGDocument1.SVG.AllItems['backfaceHighlight'] as TSVGGraphicElement).Visible := (Sender as TCheckBox).Checked;
 finally
  RSSVGDocument1.SVG.EndUpdate;
 end;
end;
Customized Speedometer

Customized Speedometer

The source for the project is here.  The project includes a compiled version to play around with.  If you clone the TSVGDocument, you can easily create multiple speedometers, each with their own color scheme changeable at run-time.  And with a little more work to replace the path elements with true text elements (or a little more luck in finding a more customizable SVG), you could change the scale of the speedometer so it could be any analog gauge.

Well, that is it for today.  I hope this blog post helps you more easily see the awesome possibilities of SVGs.  There is a whole world wide web of free, customizable, scalable, and high-quality content out there.  With the RiverSoftAVG SVG Component Library and a little prep work, you can easily use this content to really make your applications stand out.

Until next time, Happy CodeSmithing!

RSCL v2 Release Deep Dive

The RiverSoftAVG SVG Component Library version 2.0 was finally released yesterday (Yea! 🙂 )  Now that v2.0 is “in the can,” I wanted to investigate and explain exactly what has changed.  The RSCL v2.0 has some great new features, a lot of improvements and refactorizations, and over 60 bug fixes.  However, the History.txt for the RSCL v2.0 is over 1100 lines long for v2.0 beta releases alone, and includes “fixes” of new features, refactorizations, and regressions (all of which should not be considered bug fixes as far as final counts between v1.x and v2.0).  It can be daunting to understand what has really changed, and what really matters, between v1.x and v2.0.

In this blog post, I attempt to separate out the changes and group them logically into

  • New
  • Improved (including refactorizations)
  • Bug Fixes

The New

This is what most people are interested in, the headliner features.

  • Save SVGs to XML – RSCL v2 can now load an SVG, change its properties in code, and then save out a compliant SVG file.
  • Load and Save SVGs to Delphi Binary – a new file format based on Delphi streaming was added.
    textPath example

    textPath example

    This format provides faster loading and saving of SVGs at the expense of being able to use them with other programs (and usually larger file sizes).

  • New Text Support
    • Text on Path

      Baseline Shift Example

      Baseline Shift Example

    • Baseline Shift (i.e., superscript, subscript, shift)
    • Font Variant Support (i.e., Small Caps)
    • Letter Spacing
    • Word Spacing
    • Kerning
    • Direction (i.e., left to right, or right to left.  no bi-directional support though except with simple text elements)
    • Writing Mode (i.e., left to right top to bottom, right to left top to bottom, top to bottom left to right)
    • Glyph Orientation Horizontal (i.e., rotation of characters when going left to right or right to left)

      Writing Mode, Glyph Orientation Example

      Writing Mode, Glyph Orientation Example

    • Glyph Orientation Vertical (i.e., rotation of characters when going top to bottom)
    • X, Y, DX, DY, and Rotation of individual characters
  • New TRSSVGImageList Components (VCL and FMX XE8+) – manage a list of SVGs in your application using TRSSVGImageList.  It embeds every SVG within the image list (simplifying deployment) and re-renders the SVGs as needed.  Like a traditional TImageList, multiple controls can use the same TImageList, either different items or the same item, and it is all managed automatically for you.
  • Detect and respond to mouse events on SVG elements – call TSVGGraphicElement.ElementAtPos to find the element at a screen point or respond to TRSSVGImage.OnClickElement events

    Detect Mouse Events

    Detect Mouse Events

  • New VCL TRSSVGScreen control provides flicker-reduced TRSSVGImage-like control (note that it is not transparent though so you cannot stack TRSSVGScreen controls on top of each other without obscuring SVGs underneath
  • New TSVGDocument DrawBeforeElement, DrawElement, and DrawAfterElement methods – draw specific portions of the SVG in place so that you can buffer to bitmaps unchanging parts (such as background or foreground) to speed up redraws
  • Created RSCL Evaluation version

The Improved

There were a lot of improvements and refactorizations in v2.0.  The following list highlights the most important:

  • Speed Optimizations
    • Refactored TSVGStyle class to store Style=Value pairs in a
      hash table (reduced time to load SVGs)
    • Improved TRSSVGImage (VCL) painting efficiency by deferring bitmap update until Paint call
    • Optimized TSVGElement.Assign method by wrapping assignment in
      BeginUpdate/EndUpdate

      Improved Linear and Radial Gradients

      Improved Linear and Radial Gradients

    • Slightly optimized TRSPathData.GetNum method (used in parsing paths)
  • Linear Gradients
    • Rotate & Scale – Linear Gradients transform based on referencing element
  • Radial Gradients
    • Added Focal point as well as Center Point
  • Text Support
    • Pen Stroke

      Gradients on Text Fill and Stroke

      Gradients on Text Fill and Stroke

    • Path-Based Text Drawing
    • Text Support of GPU Canvas in FMX – the GPU Canvas has real problems with converting text to path and layouts.  The RSCL cleanly handles any difficiencies in GPU Canvas.
  • Font Support
    •  Implemented choosing generic fonts (serif, sans-serif, and monospace)
  • Path Support
    • Added TRSPathData.FormatStr property to allow formatting the floating point values, generally to make the GetPathString return a smaller string to reduce the size of saved SVG files
  • Refactoring
    • TSVGLength (and descendants) – manages a length in SVG, which includes a raw value and the unit it is enumerated in (e.g., millimeters).  To refactor the SVG from a static SVG viewer to a static SVG editor required some major changes to the TSVGxxxElement classes and their properties, generally to be able to save properties that were read (before the elements would convert the svg values and then discard any extraneous information. For example, to save a length value such as ‘1in’ requires saving the raw value (1) and the unit (in) instead of the converted value (96 pixels).
    • TSVGViewBox – Refactored ViewBox property from TSVGRect (record) to TSVGViewBox *object*
    • The behavior of TSVGDocument.OnChange event has changed. The OnChange event usually does not get called when the structure of the SVG has changed anymore; please use the  SVGDocument.OnAddElement and  SVGDocument.OnRemoveElement property instead. The Sender
      parameter is now set to the element where the change occurs.
      If the Sender is nil, this means that the change event occurred
      because of BeginUpdate/EndUpdate method calls; in this case it
      can mean that the structure of the SVG document has changed.
    • Moved basic SVG shape elements (rect, ellipse, etc) to
      RSSVG.BasicShapes.pas and FMX.RS.SVG.BasicShapes.pas
    • Moved SVG text elements to RSSVG.Text.pas and FMX.RS.SVGText.pas
  • TSVGDocument Improvements
    • Gradients property to track list of Gradients in document
    • Markers property to track list of Markers in document
    • Patterns property to track list of Patterns in document
  • TSVGElement (base class for every element in SVG) Improvements
    • BoundsRect property is writable as well
      as readable
    • Added Enabled property (Disabled elements are drawn
      grayed out and ignored in ElementAtPos method calls by default)
    • ToString method returns XML string of element and its children
    • Added OnAddElement event
    • Added OnRemoveElement event
    • Modified the behavior of OnChange event to set the Sender to the element that changed. If Sender = nil then it means the structure of the SVG has changed
    • Modified OnDrawing event signature to include a DoDraw
      parameter. If this variable is set to false, the current Element
      will NOT be drawn. However, its children may still be.
    • Added mechanism to TSVGElement to track and notify listeners if the
      current element is changed. For example, this allows TSVGUse to be notified when the elements that it uses changes and then to ask to be redrawn
  • TSVGGraphicElement (base class for every visible SVG element) Improvements
    • Modified behavior of Style Properties (e.g.,
      ClipRule, Overflow, ShapeRendering, etc) to automatically
      remove themselves from Inherits property when they are changed
  • TSVGBrush Improvements
    • Added ParentColor property
    • Added ParentOpacity property
    • Added OnIsInherited event
  • TSVGFont Improvements
    • Added IsDecorated method to return if font is decorated (underlined, strike-out or overline)
    • Added ParentFamily property
    • Added ParentFontVariant property
    • Added ParentSize property
    • Added ParentStyle property
    • Added ParentDecorated property
    • Added ParentWeight property
    • Added OnIsInherited event
  • TSVGPen Improvements
    • Added ParentColor property
    • Added ParentOpacity property
    • Added ParentDasharray property
    • Added ParentDashOffset property
    • Added ParentLinecap property
    • Added ParentLineJoin property
    • Added ParentMiterLimit property
    • Added ParentThickness property
    • Added OnIsInherited event
  • New Demo Projects
    • Printer (VCL)
    • Editor (FMX)
    • Anatomy (VCL)
    • Clock World (FMX)
    • Stream (FMX)
    • ImageList (VCL)
    • ImageList (FMX)

The Fixed

The number of bugs fixed is a great case of that there are lies, damn lies, and statistics.  🙂  There are at least 66 bug fixes in RSCL v2.0 versus v1.9.  The number is open to interpretation as a lot of ParseXXX functions were fixed with a simple copy and paste of correct function parameters.  Should these be included?  Also, fixing bugs in FMX and VCL can technically be separate bug fixes as both have to be tested.  This became a judgement call and is reflected below.

Without further ado, here is the lists of bugs fixed in RSCL v2.0:

  • Fixed edge case memory leak in TSVGDocument.Clear by freeing styles
    (Styles.Clear does not call OnValueNotify event so styles
    were not being freed)
  • Fixed edge case memory leak in TSVGDocument.Destroy by freeing styles (Styles.Clear does not call OnValueNotify event so styles
    were not being freed)
  • Fixed graphical corruption in pattern by clearing canvas before
    generating pattern in TSVGPattern.GetPattern method
  • Fixed TSVGGraphicElement.GetBoundsRect(TSVGMatrix) method to
    correctly transform the bounds rectangle (including rotation) and return the boundsrect for the transformed boundsrect
  • Fixed font-family style property support (on Windows and Mac)
    to include list of font families and supporting generic (serif,
    sans-serif, and monospace) fonts
    Note: The first font found on the system is used. The RSCL
    does not support detecting a character is not present in the
    current font and selecting the next font in the list for that
    character
  • Fixed TSVGLinearGradient.GetGradient method to account for
    transformations of the gradient
  • Fixed TSVGGraphicElement.GetClipRect method to slightly inflate
    clipping rectangle to be bigger than BoundsRect
  • Fixed value of SVGAlignNames[saNone] to equal ‘none’
  • Fixed bug in TSVGElement.Assign method where Items were SHARED
    instead of copied/cloned
  • Fixed TRSSVGDocument/TRSFmxSVGDocument.LoadSVGFromStrings method to correctly read unicode TStrings
  • Fixed floating point error in TSVGMarker.DrawMidMarker method
  • Fixed floating point error in TSVGMarker.MidMarkerAtPos method
  • Fixed SVGs to be properly clipped when Overflow attribute is hidden
  • Fixed TSVGCustomViewBox.Draw method to correctly modify the matrix
    for the ViewBox (if specified) or BoundsRect INSTEAD of modifying
    the drawing rectangle
  • Fixed markers to be properly clipped when Overflow attribute is hidden
  • Fixed correctly setting FillMode for TRSSVGPath control (VCL)
    in TSVGPolyline.AssignTo method
  • Fixed CleanText function to strip off control characters before
    seeing if any spaces should be appended to left or right
  • Fixed bug in TRSGPBrush.Assign method where TBrush.Color was
    incorrectly converted to alpha color (VCL Only)
  • Fixed bug in TRSGPPen.Assign method where TBrush.Color was
    incorrectly converted to alpha color (VCL Only)
  • Fixed bug in ToMatrix function where result matrix was not
    zeroed out before retrieving the matrix elements (VCL Only)
  • Fixed TSVGCustomViewBox.GetBoundsRect method to return children’s
    combined boundsrect if the Width and Height are not set for the
    viewbox
  • Fixed bug in TSVGGraphicElement.AssignProperties method where
    ClipRect was not being copied correctly
  • Fixed bug in TSVGElement.Assign method where incorrect Owner
    was being assigned if Self was TSVGDocument
  • Fixed TSVGElement.HasAncestor method
  • Fixed bug in TSVGDocument.ReadStyles method where descendant
    selectors were parsed as if they were grouped together (e.g.,
    ‘#alpha * {fill:green}’ would be separated into 2 styles, ‘#alpha’ and ‘*’
  • Fixed TSVGImage.DoInternalDraw method to preserve aspect ratio
    when drawing image
  • Fixed bug in TSVGCustomText where BoundsRect would be reported
    incorrectly when a font property would change (overrode
    TSVGCustomText.PropagateObjectPropChange method to force
    recalculation of TextWidth and TextHeight)
  • Fixed ParseRect function to return empty rect if Value does not
    contain 4 numbers
  • Fixed access violation when accessing TRSGPImage Codecs (VCL Only)
  • Fixed bug in installer which would not set the library path correctly
    for iOS64 in XE8+
  • Fixed TSVGGraphicElement.SetCursor method to set inherits for
    cursor to false when changed
  • Fixed TSVGElement.Destroy method so that you can just call
    Element.Free and it will correctly remove itself from its parent
  • Fixed subtle bug where setting TSVGBrush.Kind property would be overwritten if the URI contained a gradient or pattern URI (which would reset the Kind to bkGradient/bkBitmap
  • Fixed subtle bug where setting TSVGPen.Kind property would be overwritten if the URI contained a gradient or pattern URI (which would reset the Kind to bkGradient/bkBitmap
  • Fixed bug in ParseOverflow function where ‘auto’ was not correctly
    interpreted as visible
  • Fixed finding Design-time ‘Help…’ topics by searching for Unit_ClassName, not Unit.ClassName
  • Fixed TSVGRadialGradient.GetGradient method to correctly calculate
    focal point when it is a percentage
  • Fixed TSVGRadialGradient.GetGradient method to correctly calculate
    rotation center when units are csuObjectBoundingBox
  • Fixed FMX to use Shape Caching (accidentally turned off)
  • Fixed bug in TSVGUse.ShouldDraw method where it would display
    a referenced element when the TSVGUse.Visible=False and the
    Reference.Visible=inherit
  • Fixed bug in TSVGImage.Draw method where image would be drawn even if not visible
  • Fixed incorrect calculation for picas in TSVGDocument.CalcUnitFactors
    method
  • Fixed bug in TSVGGraphicElement.AssignTo method where FloatToStr
    was not using USFormatSettings (as required by SVG spec)
  • Fixed TSVGGraphicElement.FillRule default property value to frNonZero
  • Fixed bug in TSVGDocument.SupportsLanguage method to return FALSE
    if input string is empty
  • Fixed TSVGElement.HasExtensions to return FALSE when an empty string
    is supplied
  • Fixed TSVGElement.HasFeatures to return FALSE when an empty string
    is supplied
  • Fixed TSVGElement.HasLanguages to return FALSE when an empty string
    is supplied
  • Fixed bug in TSVGGraphicElement.BeginDraw method where Canvas.Font.Brush was not set in VCL
  • Improved csuObjectBoundingBox handling in TSVGPattern.GetPattern method (still incorrect but better)
  • Fixed bug in TRSFmxSVGDocument destructor where viewers are not
    notified the document is going away
  • Fixed bug in TSVGText.DoLoadFromXML method where multiple
    spaces (and other control codes) between words were not removed
    for the Text property
  • Fixed TSVGText.GetBounds to return an accurate Bounding box
    after the first time the text is drawn (DoInternalDraw caches
    TextWidth and TextHeight for later use)
  • Improved appearance of overline extended style for TSVGText
    (now a rectangle instead of thin line)
  • Fixed bug in TSVGPolyline.GetShape method where 2 ClosePaths
    could be added in a row, causing FMX to black out the entire canvas
    (However, this bug was hidden until fixed bug where FMX was not
    using Shape Caching)
  • Fixed ParseFontFamily function to return a list of font names
  • Fixed GetSVGUnit function to return suPercent if input string
    ends with ‘%’ (before input string had to be exactly equal)
  • Fixed bug in ParsePoints where if number of values was not even,
    NO points would be returned. Now, all the points except the
    missing last one are returned (follows SVG specification)
  • Fixed ParseNumbers function to also use line feeds and carriage returns
    as delimiters
  • Fixed ParseXXX function to also use line feeds and carriage returns
    as delimiters
  • Fixed bug in TRSGPPath.AddText(Pt: TPointF;…) method where
    aRect was not initialized for measuring the text properly
  • Fixed invalid parameter for GDI+ function bug in TRSGPPath.AddPolygon
    method by ensuring at least 3 points are specified
  • Fixed TRSGPFont.GetHeight method to return a value even when
    Owner is nil
  • Fixed access violation in TSVGCustomGradient.ElementNotification method where the method tried to use the Item after it had been freed
    by inherited method
  • Fixed bug in TRSPathData.GetPathString method to use USFormatSettings for FloatToStr calls
  • Fixed bug in TRSPathData.AddArcSvg method where arcs that begin
    and end at same point would not get added (now adds ellipse)