1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
{
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.
|