mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-16 23:29:16 +02:00
fpvectorial-svg: Implements support for reading PNG raster images inside SVG
git-svn-id: trunk@42309 -
This commit is contained in:
parent
fe0a06d109
commit
9230dae80f
@ -26,6 +26,7 @@ uses
|
||||
{$ifdef USE_LCL_CANVAS}
|
||||
Graphics, LCLIntf, LCLType,
|
||||
{$endif}
|
||||
base64,
|
||||
fpvectorial, fpimage, zstream;
|
||||
|
||||
type
|
||||
@ -65,8 +66,11 @@ function SolveNumericallyAngle(ANumericalEquation: TNumericalEquation;
|
||||
// Compression/Decompression
|
||||
procedure DeflateBytes(var ASource, ADest: TFPVUByteArray);
|
||||
procedure DeflateStream(ASource, ADest: TStream);
|
||||
// ASCII85
|
||||
// Binary to Text encodings
|
||||
procedure DecodeASCII85(ASource: string; var ADest: TFPVUByteArray);
|
||||
procedure DecodeBase64(ASource: string; ADest: TStream);
|
||||
// Byte array to stream conversion
|
||||
procedure ByteArrayToStream(ASource: TFPVUByteArray; ADest: TStream);
|
||||
// Debug
|
||||
procedure FPVUDebug(AStr: string);
|
||||
procedure FPVUDebugLn(AStr: string);
|
||||
@ -577,6 +581,29 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure DecodeBase64(ASource: string; ADest: TStream);
|
||||
var
|
||||
lSourceStream: TStringStream;
|
||||
lDecoder: TBase64DecodingStream;
|
||||
begin
|
||||
lSourceStream := TStringStream.Create(ASource);
|
||||
lDecoder := TBase64DecodingStream.Create(lSourceStream);
|
||||
try
|
||||
ADest.CopyFrom(lDecoder, lDecoder.Size);
|
||||
finally
|
||||
lDecoder.Free;
|
||||
lSourceStream.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure ByteArrayToStream(ASource: TFPVUByteArray; ADest: TStream);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := 0 to Length(ASource)-1 do
|
||||
ADest.WriteByte(ASource[i]);
|
||||
end;
|
||||
|
||||
procedure FPVUDebug(AStr: string);
|
||||
begin
|
||||
FPVDebugBuffer := FPVDebugBuffer + AStr;
|
||||
|
@ -16,6 +16,8 @@ interface
|
||||
uses
|
||||
Classes, SysUtils, math,
|
||||
fpimage, fpcanvas, laz2_xmlread, laz2_dom, fgl,
|
||||
// image data formats
|
||||
fpreadpng,
|
||||
fpvectorial, fpvutils, lazutf8, TypInfo;
|
||||
|
||||
type
|
||||
@ -78,10 +80,11 @@ type
|
||||
function ReadSVGPenStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
|
||||
function ReadSVGBrushStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPenAndBrush): TvSetPenBrushAndFontElements;
|
||||
function ReadSVGFontStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPenBrushAndFont): TvSetPenBrushAndFontElements;
|
||||
function ReadSVGGeneralStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
|
||||
function ReadSVGGeneralStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntity): TvSetPenBrushAndFontElements;
|
||||
function IsAttributeFromStyle(AStr: string): Boolean;
|
||||
procedure ApplyLayerStyles(ADestEntity: TvEntity);
|
||||
function ReadSpaceSeparatedFloats(AInput: string): TDoubleArray;
|
||||
function ReadSpaceSeparatedFloats(AInput: string; AOtherSeparators: string): TDoubleArray;
|
||||
function ReadSpaceSeparatedStrings(AInput: string; AOtherSeparators: string): TStringList;
|
||||
procedure ReadSVGTransformationMatrix(AMatrix: string; out AA, AB, AC, AD, AE, AF: Double);
|
||||
//
|
||||
procedure ReadDefsFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
||||
@ -89,6 +92,7 @@ type
|
||||
function ReadEntityFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
|
||||
function ReadCircleFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
|
||||
function ReadEllipseFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
|
||||
function ReadImageFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
|
||||
procedure ReadLayerFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
||||
function ReadLineFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
|
||||
function ReadPathFromNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
|
||||
@ -923,7 +927,7 @@ begin
|
||||
end;
|
||||
|
||||
function TvSVGVectorialReader.ReadSVGGeneralStyleWithKeyAndValue(AKey,
|
||||
AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
|
||||
AValue: string; ADestEntity: TvEntity): TvSetPenBrushAndFontElements;
|
||||
var
|
||||
// transform
|
||||
MA, MB, MC, MD, ME, MF: Double;
|
||||
@ -949,7 +953,7 @@ begin
|
||||
begin
|
||||
lFunctionName := lTokenizer.Tokens.Items[i].StrValue;
|
||||
lParamStr := lTokenizer.Tokens.Items[i+1].StrValue;
|
||||
lMatrixElements := ReadSpaceSeparatedFloats(lParamStr);
|
||||
lMatrixElements := ReadSpaceSeparatedFloats(lParamStr, ',');
|
||||
|
||||
if lFunctionName = 'matrix' then
|
||||
begin
|
||||
@ -1024,17 +1028,25 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TvSVGVectorialReader.ReadSpaceSeparatedFloats(AInput: string
|
||||
): TDoubleArray;
|
||||
function TvSVGVectorialReader.ReadSpaceSeparatedFloats(AInput: string;
|
||||
AOtherSeparators: string): TDoubleArray;
|
||||
var
|
||||
lStrings: TStringList;
|
||||
lInputStr: string;
|
||||
lMatrixElements: array of Double;
|
||||
i: Integer;
|
||||
begin
|
||||
lStrings := TStringList.Create;
|
||||
try
|
||||
lStrings.Delimiter := ' ';
|
||||
lStrings.DelimitedText := AInput;
|
||||
// now other separator too
|
||||
lInputStr := AInput;
|
||||
for i := 1 to Length(AOtherSeparators) do
|
||||
begin
|
||||
lInputStr := StringReplace(lInputStr, AOtherSeparators[i], ' ', [rfReplaceAll]);
|
||||
end;
|
||||
//
|
||||
lStrings.DelimitedText := lInputStr;
|
||||
SetLength(lMatrixElements, lStrings.Count);
|
||||
for i := 0 to lStrings.Count-1 do
|
||||
begin
|
||||
@ -1047,6 +1059,24 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TvSVGVectorialReader.ReadSpaceSeparatedStrings(AInput: string;
|
||||
AOtherSeparators: string): TStringList;
|
||||
var
|
||||
i: Integer;
|
||||
lInputStr: String;
|
||||
begin
|
||||
Result := TStringList.Create;
|
||||
Result.Delimiter := ' ';
|
||||
// now other separator too
|
||||
lInputStr := AInput;
|
||||
for i := 1 to Length(AOtherSeparators) do
|
||||
begin
|
||||
lInputStr := StringReplace(lInputStr, AOtherSeparators[i], ' ', [rfReplaceAll]);
|
||||
end;
|
||||
//
|
||||
Result.DelimitedText := lInputStr;
|
||||
end;
|
||||
|
||||
// transform="matrix(0.860815 0 -0 1.07602 354.095 482.177)"=>matrix(a, b, c, d, e, f)
|
||||
// See http://apike.ca/prog_svg_transform.html
|
||||
procedure TvSVGVectorialReader.ReadSVGTransformationMatrix(
|
||||
@ -1054,7 +1084,7 @@ procedure TvSVGVectorialReader.ReadSVGTransformationMatrix(
|
||||
var
|
||||
lMatrixElements: array of Double;
|
||||
begin
|
||||
lMatrixElements := ReadSpaceSeparatedFloats(AMatrix);
|
||||
lMatrixElements := ReadSpaceSeparatedFloats(AMatrix, '');
|
||||
|
||||
AA := lMatrixElements[0];
|
||||
AB := lMatrixElements[1];
|
||||
@ -1177,6 +1207,7 @@ begin
|
||||
'circle': Result := ReadCircleFromNode(ANode, AData, ADoc);
|
||||
'ellipse': Result := ReadEllipseFromNode(ANode, AData, ADoc);
|
||||
'g': ReadLayerFromNode(ANode, AData, ADoc);
|
||||
'image': Result := ReadImageFromNode(ANode, AData, ADoc);
|
||||
'line': Result := ReadLineFromNode(ANode, AData, ADoc);
|
||||
'path': Result := ReadPathFromNode(ANode, AData, ADoc);
|
||||
'polygon', 'polyline': Result := ReadPolyFromNode(ANode, AData, ADoc);
|
||||
@ -1192,7 +1223,7 @@ var
|
||||
cx, cy, cr, dtmp: double;
|
||||
lCircle: TvCircle;
|
||||
i: Integer;
|
||||
lNodeName: DOMString;
|
||||
lNodeName, lNodeValue: DOMString;
|
||||
begin
|
||||
cx := 0.0;
|
||||
cy := 0.0;
|
||||
@ -1210,21 +1241,22 @@ begin
|
||||
for i := 0 to ANode.Attributes.Length - 1 do
|
||||
begin
|
||||
lNodeName := ANode.Attributes.Item[i].NodeName;
|
||||
lNodeValue := ANode.Attributes.Item[i].NodeName;
|
||||
if lNodeName = 'cx' then
|
||||
cx := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
|
||||
cx := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'cy' then
|
||||
cy := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
|
||||
cy := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'r' then
|
||||
cr := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
|
||||
cr := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'id' then
|
||||
lCircle.Name := ANode.Attributes.Item[i].NodeValue
|
||||
lCircle.Name := lNodeValue
|
||||
else if lNodeName = 'style' then
|
||||
ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lCircle)
|
||||
ReadSVGStyle(lNodeValue, lCircle)
|
||||
else if IsAttributeFromStyle(lNodeName) then
|
||||
begin
|
||||
ReadSVGPenStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lCircle);
|
||||
ReadSVGBrushStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lCircle);
|
||||
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lCircle);
|
||||
ReadSVGPenStyleWithKeyAndValue(lNodeName, lNodeValue, lCircle);
|
||||
ReadSVGBrushStyleWithKeyAndValue(lNodeName, lNodeValue, lCircle);
|
||||
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, lNodeValue, lCircle);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -1242,7 +1274,7 @@ var
|
||||
cx, cy, crx, cry: double;
|
||||
lEllipse: TvEllipse;
|
||||
i: Integer;
|
||||
lNodeName: DOMString;
|
||||
lNodeName, lNodeValue: DOMString;
|
||||
begin
|
||||
cx := 0.0;
|
||||
cy := 0.0;
|
||||
@ -1261,23 +1293,24 @@ begin
|
||||
for i := 0 to ANode.Attributes.Length - 1 do
|
||||
begin
|
||||
lNodeName := ANode.Attributes.Item[i].NodeName;
|
||||
lNodeValue := ANode.Attributes.Item[i].NodeName;
|
||||
if lNodeName = 'cx' then
|
||||
cx := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
|
||||
cx := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'cy' then
|
||||
cy := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
|
||||
cy := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'rx' then
|
||||
crx := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
|
||||
crx := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'ry' then
|
||||
cry := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
|
||||
cry := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'id' then
|
||||
lEllipse.Name := ANode.Attributes.Item[i].NodeValue
|
||||
else if lNodeName = 'style' then
|
||||
ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lEllipse)
|
||||
ReadSVGStyle(lNodeValue, lEllipse)
|
||||
else if IsAttributeFromStyle(lNodeName) then
|
||||
begin
|
||||
ReadSVGPenStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lEllipse);
|
||||
ReadSVGBrushStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lEllipse);
|
||||
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lEllipse);
|
||||
ReadSVGPenStyleWithKeyAndValue(lNodeName, lNodeValue, lEllipse);
|
||||
ReadSVGBrushStyleWithKeyAndValue(lNodeName, lNodeValue, lEllipse);
|
||||
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, lNodeValue, lEllipse);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -1289,6 +1322,95 @@ begin
|
||||
Result := lEllipse;
|
||||
end;
|
||||
|
||||
// <image width="92.5" x="0" y="0" height="76.0429"
|
||||
// xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKMAAACGCAYAA
|
||||
// ACxDToF......" clip-path="url(#Clip0)" transform="matrix(1 0 0 1 0 0)"/>
|
||||
function TvSVGVectorialReader.ReadImageFromNode(ANode: TDOMNode;
|
||||
AData: TvVectorialPage; ADoc: TvVectorialDocument): TvEntity;
|
||||
var
|
||||
lImage: TvRasterImage;
|
||||
lx, ly, lw, lh: Double;
|
||||
i: Integer;
|
||||
lNodeName, lNodeValue: DOMString;
|
||||
lImageDataParts: TStringList;
|
||||
lImageDataBase64: string;
|
||||
lImageData: array of Byte;
|
||||
lImageDataStream: TMemoryStream;
|
||||
lImageReader: TFPCustomImageReader;
|
||||
begin
|
||||
lImage := TvRasterImage.Create;
|
||||
lx := 0;
|
||||
ly := 0;
|
||||
lw := 0;
|
||||
lh := 0;
|
||||
|
||||
// Apply the layer style
|
||||
//ApplyLayerStyles(lEllipse);
|
||||
|
||||
// read the attributes
|
||||
for i := 0 to ANode.Attributes.Length - 1 do
|
||||
begin
|
||||
lNodeName := ANode.Attributes.Item[i].NodeName;
|
||||
lNodeValue := ANode.Attributes.Item[i].NodeValue;
|
||||
if lNodeName = 'x' then
|
||||
lx := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'y' then
|
||||
ly := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'width' then
|
||||
lw := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'height' then
|
||||
lh := StringWithUnitToFloat(lNodeValue)
|
||||
else if lNodeName = 'xlink:href' then
|
||||
begin
|
||||
lImageDataParts := ReadSpaceSeparatedStrings(lNodeValue, ':;,');
|
||||
try
|
||||
if (lImageDataParts.Strings[0] = 'data') and
|
||||
(lImageDataParts.Strings[1] = 'image/png') and
|
||||
(lImageDataParts.Strings[2] = 'base64') then
|
||||
begin
|
||||
lImageReader := TFPReaderPNG.Create;
|
||||
lImageDataStream := TMemoryStream.Create;
|
||||
try
|
||||
lImageDataBase64 := lImageDataParts.Strings[3];
|
||||
DecodeBase64(lImageDataBase64, lImageDataStream);
|
||||
lImageDataStream.Position := 0;
|
||||
lImage.CreateRGB888Image(10, 10);
|
||||
lImage.RasterImage.LoadFromStream(lImageDataStream, lImageReader);
|
||||
finally
|
||||
lImageDataStream.Free;
|
||||
lImageReader.Free;
|
||||
end;
|
||||
end
|
||||
else
|
||||
raise Exception.Create('[TvSVGVectorialReader.ReadImageFromNode] Unimplemented image format');
|
||||
finally
|
||||
lImageDataParts.Free;
|
||||
end;
|
||||
end
|
||||
else if lNodeName = 'id' then
|
||||
lImage.Name := lNodeValue
|
||||
{ else if lNodeName = 'style' then
|
||||
ReadSVGStyle(lNodeValue, lImage)}
|
||||
else if IsAttributeFromStyle(lNodeName) then
|
||||
begin
|
||||
//ReadSVGPenStyleWithKeyAndValue(lNodeName, lNodeValue, lImage);
|
||||
//ReadSVGBrushStyleWithKeyAndValue(lNodeName, lNodeValue, lImage);
|
||||
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, lNodeValue, lImage);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Record translate data
|
||||
lx := lx + lImage.X;
|
||||
ly := ly + lImage.Y;
|
||||
|
||||
ConvertSVGCoordinatesToFPVCoordinates(
|
||||
AData, lx, ly, lImage.X, lImage.Y);
|
||||
ConvertSVGDeltaToFPVDelta(
|
||||
AData, lw, lh, lImage.Width, lImage.Height);
|
||||
|
||||
Result := lImage;
|
||||
end;
|
||||
|
||||
procedure TvSVGVectorialReader.ReadLayerFromNode(ANode: TDOMNode;
|
||||
AData: TvVectorialPage; ADoc: TvVectorialDocument);
|
||||
var
|
||||
@ -2037,7 +2159,7 @@ var
|
||||
lXLink: DOMString = '';
|
||||
lInsertedEntity: TvEntity;
|
||||
i: Integer;
|
||||
lNodeName: DOMString;
|
||||
lNodeName, lNodeValue: DOMString;
|
||||
begin
|
||||
Result := nil;
|
||||
|
||||
@ -2061,6 +2183,24 @@ begin
|
||||
|
||||
lInsert := TvInsert.Create;
|
||||
lInsert.InsertEntity := lInsertedEntity;
|
||||
|
||||
// Apply the styles
|
||||
// read the attributes
|
||||
for i := 0 to ANode.Attributes.Length - 1 do
|
||||
begin
|
||||
lNodeName := ANode.Attributes.Item[i].NodeName;
|
||||
lNodeValue := ANode.Attributes.Item[i].NodeValue;
|
||||
{if lNodeName = 'style' then
|
||||
ReadSVGStyle(lNodeValue, lText)
|
||||
else} if IsAttributeFromStyle(lNodeName) then
|
||||
begin
|
||||
{ReadSVGPenStyleWithKeyAndValue(lNodeName, lNodeValue, lText);
|
||||
ReadSVGBrushStyleWithKeyAndValue(lNodeName, lNodeValue, lText);
|
||||
ReadSVGFontStyleWithKeyAndValue(lNodeName, lNodeValue, lText);}
|
||||
ReadSVGGeneralStyleWithKeyAndValue(lNodeName, lNodeValue, lInsert);
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := lInsert;
|
||||
end;
|
||||
|
||||
@ -2175,7 +2315,7 @@ var
|
||||
{$ifdef SVG_MERGE_LAYER_STYLES}
|
||||
lLayerStyleKeys, lLayerStyleValues: TStringList;
|
||||
{$endif}
|
||||
lNodeName: DOMString;
|
||||
lNodeName, lNodeValue: DOMString;
|
||||
ANode: TDOMElement;
|
||||
i: Integer;
|
||||
lCurEntity: TvEntity;
|
||||
@ -2200,23 +2340,24 @@ begin
|
||||
for i := 0 to ANode.Attributes.Length - 1 do
|
||||
begin
|
||||
lNodeName := ANode.Attributes.Item[i].NodeName;
|
||||
lNodeValue := ANode.Attributes.Item[i].NodeValue;
|
||||
if lNodeName = 'viewBox' then
|
||||
begin
|
||||
lViewBox := ReadSpaceSeparatedFloats(ANode.Attributes.Item[i].NodeValue);
|
||||
lViewBox := ReadSpaceSeparatedFloats(lNodeValue, '');
|
||||
AData.Width := lViewBox[2] - lViewBox[0];
|
||||
AData.Height := lViewBox[3] - lViewBox[1];
|
||||
end
|
||||
else if lNodeName = 'style' then
|
||||
begin
|
||||
{$ifdef SVG_MERGE_LAYER_STYLES}
|
||||
ReadSVGStyleToStyleLists(ANode.Attributes.Item[i].NodeValue, lLayerStyleKeys, lLayerStyleValues);
|
||||
ReadSVGStyleToStyleLists(lNodeValue, lLayerStyleKeys, lLayerStyleValues);
|
||||
{$endif}
|
||||
end
|
||||
else if IsAttributeFromStyle(lNodeName) then
|
||||
begin
|
||||
{$ifdef SVG_MERGE_LAYER_STYLES}
|
||||
lLayerStyleKeys.Add(lNodeName);
|
||||
lLayerStyleValues.Add(ANode.Attributes.Item[i].NodeValue);
|
||||
lLayerStyleValues.Add(lNodeValue);
|
||||
{$endif}
|
||||
end;
|
||||
end;
|
||||
|
Loading…
Reference in New Issue
Block a user