Loading drawings¶
Drawings reach an application as files next to it, as resources in the executable, as blobs in a database or as text from a server. This page shows how each gets into the library, and what can go wrong on the way.
Into a component¶
A component takes its drawing from one of three properties:
SVG2Image1.FileName := 'C:\Plant\Drawings\area-3.svg'; // a file
SVG2Image1.SVG.Text := DrawingText; // SVG text
SVG2Image1.SVGDoc := SVG2Doc1; // a shared TSVG2Doc
An assignment does not parse the drawing straight away. The component parses
it when it is next painted, or when you call ParseSVG, and then raises
OnAfterParse. Until then, SVGRoot still holds the previous drawing, so
look up elements in OnAfterParse, or call ParseSVG first. SVG wins over
FileName when both are set. Choosing a component
explains which one to use when.
To load from a stream, such as a resource or a database blob, read it into
SVG:
var
Stream: TResourceStream;
begin
Stream := TResourceStream.Create(HInstance, 'AREA3', RT_RCDATA);
try
SVG2Image1.SVG.LoadFromStream(Stream, TEncoding.UTF8);
finally
Stream.Free;
end;
end;
Pass the encoding. See Encodings below for why.
Without a component¶
TSVGSaxParser reads a file, a stream or a TStrings into a document of
your own, as in Your first drawing:
Root := TSVGRootVcl.Create;
Parser := TSVGSaxParser.Create(nil);
try
Parser.Parse(Stream, Root); // or a file name, or a TStrings
finally
Parser.Free;
end;
Loading from a stream this way needs no encoding: the parser reads UTF-8 without a byte order mark correctly, as tool exports need.
Compressed files¶
Files compressed with gzip, usually named .svgz, load like any other through
FileName or the parser. The library recognises the compression from the
content, not from the file name, so a compressed drawing in a stream or a
database blob loads through Parser.Parse(Stream, Root) as well. Do not read
one into the SVG property: that holds text, and compressed data is not
text.
Encodings¶
Most drawing tools save UTF-8 without a byte order mark. The parser and the
FileName property handle that correctly. The trap is loading the text
yourself first:
// Wrong: without a byte order mark this reads the file in the Windows code
// page, so "Druckbehälter" becomes "Druckbehälter"
SVG2Image1.SVG.LoadFromFile(FileName);
// Right
SVG2Image1.SVG.LoadFromFile(FileName, TEncoding.UTF8);
The same applies to LoadFromStream and to text read from a database or a
web service: decode it as UTF-8, or use the encoding the XML declaration names.
When a drawing does not load¶
A file that is missing, or that is not well-formed XML, raises an exception
while it is parsed (EDOMParseError on Delphi). The message says what is wrong
and where:
What the application sees depends on where the drawing is parsed:
- When a component parses it while painting, the exception is caught and
the message is shown in the component instead of the drawing. The component
raises
OnParseErrorwith the message, andParseErrorMessageholds it until a drawing is parsed. (This is the default. UndefineSvgCtrlExceptionsOffinCompilerSettings.incto have the exception raised from the paint instead.) - When you call
ParseSVG, orTSVGSaxParser.Parse, the exception is raised to your code.
OnParseError is the simple way to log bad drawings or tell the user:
procedure TForm1.SVG2Image1ParseError(Sender: TObject; const aMessage: string);
begin
StatusBar1.SimpleText := 'The drawing could not be loaded: ' + aMessage;
end;
To decide yourself what the user sees, parse inside try ... except instead,
before the component paints:
try
SVG2Image1.FileName := aFileName;
SVG2Image1.ParseSVG;
except
on E: Exception do
begin
SVG2Image1.FileName := '';
ShowMessage(Format('%s could not be loaded:'#13'%s', [aFileName, E.Message]));
end;
end;
Everything short of broken XML loads. An attribute value the library cannot read, such as a colour in a syntax it does not know, is skipped for that one element, and the element is drawn as if the attribute were not there. An element it does not know is kept in the document but not drawn.
Log messages¶
Some problems do not stop loading but are worth knowing about. The library
reports them through the component's OnLog event, or ISVGRoot.OnLogMsg
without a component. The most common one in tool exports is a repeated id:
Duplicate element id "shape", lookup by id is ambiguous.
Use FindElements to get every element with it.
Log these while drawings are being made, so the designer hears about a repeated id before the application depends on it:
procedure TForm1.SVG2Image1Log(Sender: TObject; aRoot: ISVGRoot;
aDocument: ISVGXMLDocument; const aMsg: string);
begin
MemoLog.Lines.Add(aMsg);
end;
Linked images and style sheets¶
A drawing can refer to other files: bitmaps and other SVG files in <image>,
and style sheets through @import or an XHTML <link>. How the library finds
them depends on how the drawing was loaded:
| Loaded from | A relative link such as img/pump.png is looked for |
|---|---|
FileName, or the parser with a file name |
Next to the drawing: in img below the drawing's folder |
SVG, a stream or a TStrings |
Relative to the current directory of the process |
The current directory is rarely what you want, so for drawings that do not come
from a file, either ask the designer to embed the bitmaps (most tools have an
Embed option; the image is then part of the file) or supply the files
yourself in OnLoadExternalResource. The library calls it for every linked
file before it looks on disk:
procedure TForm1.SVG2Image1LoadExternalResource(Sender: TObject;
aReferer: ISVGReferer; aIri: TSVGIri; aStream: TStream;
var aMimeType: TSVGMimeType; var aLoaded: Boolean);
var
FileName: string;
FileStream: TFileStream;
begin
// aIri.Path is the link as written in the drawing, for example 'img/pump.png'
FileName := TPath.Combine(FDrawingFolder, string(aIri.Path).Replace('/', '\'));
if FileExists(FileName) then
begin
FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
aStream.CopyFrom(FileStream, 0);
finally
FileStream.Free;
end;
aMimeType := FileMimeType(FileName); // BVE.SVG2Types
aLoaded := True;
end;
end;
Leave aLoaded at False to let the library look for the file itself. The
same event can serve images from a database or a resource.
Links to http and https addresses are fetched: SVGInternetAccess is on
by default in CompilerSettings.inc. On FPC, https needs the OpenSSL
libraries at run time; see Shipping your product.
Showing part of a drawing¶
One file can hold several views: a whole plant, and each area as a nested
svg element. RootID renders only the svg element with that id:
The whole document is still loaded, so ids elsewhere in it are still found.