lazarus/components/fpvectorial/svgvectorialreader_rsvg.pas

187 lines
5.1 KiB
ObjectPascal

{
Reads an SVG Document
License: The same modified LGPL as the Free Pascal RTL
See the file COPYING.modifiedLGPL for more details
AUTHORS: Felipe Monteiro de Carvalho
SVG rendering system using an external dependency: librsvg which uses cairo
This provides a better rendering, but has the downside of the external dependency
}
unit svgvectorialreader_rsvg;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, math, contnrs, process, utf8Process,
laz2_xmlread, laz2_xmlwrite, laz2_dom,
// image data formats
fpreadpng,
fpvectorial, fpvutils, lazutf8, TypInfo,
fileutil, lazfileutils;
type
{ TvSVGVectorialReader_RSVG }
TvSVGVectorialReader_RSVG = class(TvCustomVectorialReader)
private
FRSVG_CMD, FTmpFile_Path: string;
function ExecuteRSVG(AUsePipes: Boolean = False): TvRasterImage;
procedure AddRasterImageToDoc(AData: TvVectorialDocument; AImage: TvRasterImage);
public
{ General reading methods }
constructor Create; override;
Destructor Destroy; override;
procedure ReadFromFile(AFileName: string; AData: TvVectorialDocument); override;
procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
procedure ReadFromXML(Doc: TXMLDocument; AData: TvVectorialDocument); override;
end;
implementation
{ TvSVGVectorialReader_RSVG }
function TvSVGVectorialReader_RSVG.ExecuteRSVG(AUsePipes: Boolean): TvRasterImage;
const
BUF_SIZE = 1024;
var
AProcess : TProcessUTF8;
OutputStream: TStream = nil;
lImageReader: TFPReaderPNG = nil;
BytesRead : longint;
Buffer : array[1..BUF_SIZE] of byte;
lOutputTmpFileName, gSVGVecReader_RSVG_Path: String;
begin
Result := TvRasterImage.Create(nil);
AProcess := TProcessUTF8.Create(nil);
try
gSVGVecReader_RSVG_Path := Settings.HelperToolPath;
if gSVGVecReader_RSVG_Path <> '' then FRSVG_CMD := gSVGVecReader_RSVG_Path;
if not FileExistsUTF8(FRSVG_CMD) then
raise Exception.Create('TvSVGVectorialReader_RSVG: rsvg-convert.exe not found: ' + FRSVG_CMD);
AProcess.Executable := FRSVG_CMD;
//AProcess.Parameters.Add('-f png');
if not AUsePipes then
begin
lOutputTmpFileName := FTmpFile_Path+'.o';
// Needs to be .Add twice because otherwise TProcess.Execute
// will think it is a path that needs quoting
AProcess.Parameters.Add('-o');
AProcess.Parameters.Add(lOutputTmpFileName);
AProcess.Options := [poNoConsole, powaitonexit];
end
else
begin
AProcess.Options := [poUsePipes, poNoConsole];
end;
AProcess.Parameters.Add(FTmpFile_Path);
AProcess.Execute;
if AUsePipes then
begin
OutputStream := TMemoryStream.Create;
// All generated output from AProcess is read in a loop until no more data is available
repeat
BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);
OutputStream.Write(Buffer, BytesRead)
until BytesRead = 0; // Stop if no more data is available
end
else
begin
OutputStream := TFileStream.Create(lOutputTmpFileName, fmOpenRead);
end;
// Now that all data has been read it can be used; for example to save it to a file on disk
OutputStream.Position := 0;
lImageReader := TFPReaderPNG.Create;
Result.CreateImageFromStream(OutputStream, lImageReader);
Result.Width := Result.RasterImage.Width;
Result.Height := Result.RasterImage.Height;
finally
// Clean up
AProcess.Free;
if OutputStream <> nil then OutputStream.Free;
if lImageReader <> nil then lImageReader.Free;
end;
end;
procedure TvSVGVectorialReader_RSVG.AddRasterImageToDoc(
AData: TvVectorialDocument; AImage: TvRasterImage);
var
lPage: TvVectorialPage;
begin
lPage := AData.AddPage(True);
lPage.Width := AImage.Width;
lPage.Height := AImage.Height;
lPage.AddEntity(AImage);
end;
constructor TvSVGVectorialReader_RSVG.Create;
begin
inherited Create;
// rsvg-convert.exe doesnt work without .svg extension
FTmpFile_Path := SysUtils.GetTempFileName('', 'fpvectorial_')+'.svg';
{$ifdef Windows}
FRSVG_CMD := ExtractFilePath(paramstr(0))+'rsvg-convert.exe';
{$endif}
{$if defined(Unix) and not defined(darwin)}
FRSVG_CMD := '/usr/bin/rsvg-convert';
{$endif}
end;
destructor TvSVGVectorialReader_RSVG.Destroy;
begin
inherited Destroy;
end;
procedure TvSVGVectorialReader_RSVG.ReadFromFile(AFileName: string;
AData: TvVectorialDocument);
var
lRasterImage: TvRasterImage;
begin
CopyFile(AFileName, FTmpFile_Path);
lRasterImage := ExecuteRSVG();
AddRasterImageToDoc(AData, lRasterImage);
end;
procedure TvSVGVectorialReader_RSVG.ReadFromStream(AStream: TStream;
AData: TvVectorialDocument);
var
Doc: TXMLDocument = nil;
begin
try
// Read in xml file from the stream
ReadXMLFile(Doc, AStream);
ReadFromXML(Doc, AData);
finally
// finally, free the document
Doc.Free;
end;
end;
procedure TvSVGVectorialReader_RSVG.ReadFromXML(Doc: TXMLDocument;
AData: TvVectorialDocument);
var
lRasterImage: TvRasterImage;
begin
WriteXMLFile(Doc, FTmpFile_Path);
lRasterImage := ExecuteRSVG();
AddRasterImageToDoc(AData, lRasterImage);
end;
initialization
RegisterVectorialReader(TvSVGVectorialReader_RSVG, vfSVG);
end.