410 lines
15 KiB
Plaintext
410 lines
15 KiB
Plaintext
(* ImageEn Build 7.0.0.06.2637 @ 7-4-17 14:58:42.679 *)
|
|
(*
|
|
Copyright (c) 1998-2017 by Carlotta Calandra. All rights reserved.
|
|
Copyright (c) 2011-2017 by Xequte Software.
|
|
|
|
This software comes without express or implied warranty.
|
|
In no case shall the author be liable for any damage or unwanted behavior of any
|
|
computer hardware and/or software.
|
|
|
|
Author grants you the right to include the component
|
|
in your application, whether COMMERCIAL, SHAREWARE, or FREEWARE.
|
|
|
|
ImageEn, IEvolution and ImageEn ActiveX may not be included in any
|
|
commercial, shareware or freeware libraries or components.
|
|
|
|
www.ImageEn.com
|
|
*)
|
|
|
|
(*
|
|
File: iexSVG.pas
|
|
Description: Exporting to Scalable Vector Graphics format
|
|
File version 1001
|
|
Doc revision 1001
|
|
*)
|
|
|
|
|
|
|
|
unit iexSVG;
|
|
|
|
|
|
{$R-}
|
|
{$Q-}
|
|
|
|
{$I ie.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$ifdef IEHASUITYPES} System.UITypes, {$endif}
|
|
Windows, Messages, Graphics, Controls, Forms, Classes, SysUtils, StdCtrls, ExtCtrls, Contnrs, hyiedefs, iexBitmaps, ieview,
|
|
hyieutils;
|
|
|
|
procedure WriteSVGFileOrStream(const FileName: String; Stream: TStream; IEView: TIEView; Bitmap: TIEBitmap; ImageFormat: TIOFileType; var xProgress: TProgressRec);
|
|
|
|
|
|
|
|
function CreateSVGImageTag(Bitmap: TIEBitmap; SaveFormat: TIOFileType; X, Y, Width, Height: Integer;
|
|
BorderWidth: Integer = 1; BorderColor: TColor = clNone_;
|
|
Rotate: Double = 0; Transparency: Integer = 255): string;
|
|
function CreateSVGShapeTag(Shape: TIEShape; X, Y, Width, Height: Integer; BorderWidth: Integer; BorderColor: TColor; FillColor: TColor;
|
|
Rotate: Double; Transparency: Integer): string;
|
|
function CreateSVGLineTag(Point1, Point2: TPoint; LineWidth: Integer; LineColor: TColor; Transparency: Integer): string;
|
|
function CreateSVGPolylineTag(Points: array of TPoint; PointCount: Integer; Closed: Boolean; BorderWidth: Integer; BorderColor: TColor; FillColor: TColor;
|
|
Rotate: Double; RotateCenter: TPoint; Transparency: Integer): string;
|
|
function CreateSVGTextTag(Text: string; Font: TFont; X, Y, Width, Height: Integer;
|
|
TextAlign: TIEAlignment; TextLayout: TIELayout; TextAngle: Double;
|
|
BorderWidth: Integer; BorderColor: TColor; FillColor: TColor;
|
|
BorderShape : TIEShape; Transparency: Integer): string;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$ifdef DelphiXE5orNewer}System.Types,{$endif}
|
|
imageenview, math, imageenio, iexCanvasUtils, ietextc, iesettings, ievect
|
|
{$IfDef UNICODE}, AnsiStrings {$EndIf};
|
|
|
|
const
|
|
SVG_Opening_Tag = '<svg width="%dpx" height="%dpx" viewBox="%d %d %d %d" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">';
|
|
SVG_Closing_Tag = '</svg>';
|
|
SVG_Image_Tag = '<image x="%dpx" y="%dpx" width="%dpx" height="%dpx" xlink:href="data:image/%s;base64,%s"%s />';
|
|
SVG_Rectangle_Tag = '<rect x="%dpx" y="%dpx" width="%dpx" height="%dpx" stroke="%s" stroke-width="%dpx" fill="%s"%s />';
|
|
SVG_RoundRect_Tag = '<rect x="%dpx" y="%dpx" width="%dpx" height="%dpx" rx="%dpx" ry="%dpx" stroke="%s" stroke-width="%dpx" fill="%s"%s />';
|
|
SVG_Ellipse_Tag = '<ellipse cx="%dpx" cy="%dpx" rx="%dpx" ry="%dpx" stroke="%s" stroke-width="%dpx" fill="%s"%s />';
|
|
SVG_Polygon_Tag = '<polygon points="%s" stroke="%s" stroke-width="%dpx" fill="%s"%s />';
|
|
SVG_Polyline_Tag = '<polyline points="%s" stroke="%s" stroke-width="%dpx" fill="none"%s />';
|
|
SVG_Text_Tag = '<text x="%dpx" y="%dpx" text-anchor="%s" dominant-baseline="%s" font-family="%s" font-size="%dpt" fill="%s"%s>%s</text>';
|
|
SVG_Line_Tag = ' <line x1="%d" y1="%d" x2="%d" y2="%d" stroke="%s" stroke-width="%dpx"%s />';
|
|
XML_Comment_Block = '<!-- %s -->';
|
|
|
|
function ColorToSVG(Color: TColor): string;
|
|
begin
|
|
if Color = clNone_ then
|
|
Result := 'none'
|
|
else
|
|
Result := ColorToHex( Color );
|
|
end;
|
|
|
|
|
|
// Note: Result is prefixed with space
|
|
function GetSVGRotationElement(Angle: Double; RotateCenter: TPoint): string;
|
|
const
|
|
SVG_Trans_Rotation_Element = ' transform="rotate(%d %d %d)"';
|
|
begin
|
|
Result := '';
|
|
if Angle <> 0 then
|
|
Result := format( SVG_Trans_Rotation_Element, [ -Round( Angle ), RotateCenter.X, RotateCenter.Y ]);
|
|
end;
|
|
|
|
// Transparency: 255=opaque, 0=transparent
|
|
// Note: Result is prefixed with space
|
|
function GetSVGTransparencyElement(Transparency: Integer): string;
|
|
const
|
|
SVG_Opacity_Element = ' opacity="%s"';
|
|
begin
|
|
Result := '';
|
|
if Transparency <> 255 then
|
|
Result := format( SVG_Opacity_Element, [ IEFloatToFormatString( Transparency / 255, 2, True )]);
|
|
end;
|
|
|
|
|
|
function CreateSVGLineTag(Point1, Point2: TPoint; LineWidth: Integer; LineColor: TColor; Transparency: Integer): string;
|
|
begin
|
|
Result := format( SVG_Line_Tag, [ Point1.X,
|
|
Point1.Y,
|
|
Point2.X,
|
|
Point2.Y,
|
|
ColorToSVG( LineColor ), LineWidth,
|
|
GetSVGTransparencyElement( Transparency ) ]);
|
|
end;
|
|
|
|
function CreateSVGPolylineTag(Points: array of TPoint; PointCount: Integer; Closed: Boolean; BorderWidth: Integer; BorderColor: TColor; FillColor: TColor;
|
|
Rotate: Double; RotateCenter: TPoint; Transparency: Integer): string;
|
|
var
|
|
i: Integer;
|
|
pointStr: string;
|
|
begin
|
|
Result := '';
|
|
pointStr := '';
|
|
for i := 0 to PointCount - 1 do
|
|
pointStr := pointStr + format( '%d,%d ', [ Points[i].X, Points[i].Y ]);
|
|
|
|
if pointStr = '' then
|
|
exit;
|
|
|
|
SetLength( pointStr, Length( pointStr ) - 2 );
|
|
|
|
if Closed then
|
|
Result := format( SVG_Polygon_Tag, [ pointStr, ColorToSVG( BorderColor ), BorderWidth, ColorToSVG( FillColor ),
|
|
GetSVGRotationElement( Rotate, RotateCenter ) + GetSVGTransparencyElement( Transparency ) ])
|
|
else
|
|
Result := format( SVG_Polyline_Tag, [ pointStr, ColorToSVG( BorderColor ), BorderWidth,
|
|
GetSVGRotationElement( Rotate, RotateCenter ) + GetSVGTransparencyElement( Transparency ) ]);
|
|
end;
|
|
|
|
|
|
function CreateSVGShapeTag(Shape: TIEShape; X, Y, Width, Height: Integer; BorderWidth: Integer; BorderColor: TColor; FillColor: TColor;
|
|
Rotate: Double; Transparency: Integer): string;
|
|
const
|
|
Rect_Rounding = 20;
|
|
var
|
|
arPts : Array[ 0 .. Shape_Array_Max_Points ] of TPoint;
|
|
pts: Integer;
|
|
RotateCenter: TPoint;
|
|
begin
|
|
Result := '';
|
|
if (( BorderWidth = 0 ) or ( BorderColor = clNone_ )) and
|
|
( FillColor = clNone_ ) then
|
|
exit; // Nothing to see
|
|
|
|
RotateCenter := Point( X + Width div 2, Y + Height div 2 );
|
|
|
|
case Shape of
|
|
iesRectangle : Result := format( SVG_Rectangle_Tag, [ X, Y, Width, Height, ColorToSVG( BorderColor ), BorderWidth, ColorToSVG( FillColor ),
|
|
GetSVGRotationElement( Rotate, RotateCenter ) + GetSVGTransparencyElement( Transparency ) ]);
|
|
iesRoundRect : Result := format( SVG_RoundRect_Tag, [ X, Y, Width, Height, Rect_Rounding, Rect_Rounding, ColorToSVG( BorderColor ), BorderWidth, ColorToSVG( FillColor ),
|
|
GetSVGRotationElement( Rotate, RotateCenter ) + GetSVGTransparencyElement( Transparency ) ]);
|
|
iesEllipse : Result := format( SVG_Ellipse_Tag, [ X + Width div 2, Y + Height div 2, Width div 2, Height div 2, ColorToSVG( BorderColor ), BorderWidth, ColorToSVG( FillColor ),
|
|
GetSVGTransparencyElement( Transparency ) ]); // No rotation of ellipses
|
|
else
|
|
try
|
|
IEGenerateShapePoints( arPts, pts, Shape, X, Y, Width, Height );
|
|
Result := CreateSVGPolylineTag( arPts, pts, True, BorderWidth, BorderColor, FillColor, Rotate, RotateCenter, Transparency );
|
|
except
|
|
// UNSUPPORTED SHAPE
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function CreateSVGImageTag(Bitmap: TIEBitmap; SaveFormat: TIOFileType; X, Y, Width, Height: Integer;
|
|
BorderWidth: Integer = 1; BorderColor: TColor = clNone_;
|
|
Rotate: Double = 0; Transparency: Integer = 255): string;
|
|
var
|
|
io: TImageEnIO;
|
|
ms: TMemoryStream;
|
|
base64Img: Ansistring;
|
|
fileType: string;
|
|
RotateCenter: TPoint;
|
|
begin
|
|
// todo: support masking https://www.w3.org/TR/SVG/masking.html#MaskProperty
|
|
Result := '';
|
|
if ( Bitmap.Width < 2 ) or ( Bitmap.Height < 2 ) then
|
|
exit;
|
|
ms := TMemoryStream.Create();
|
|
io := TImageEnIO.Create( nil );
|
|
try
|
|
io.AttachedIEBitmap := Bitmap;
|
|
io.SaveToText( ms, SaveFormat, ietfBase64);
|
|
SetString( base64Img , PAnsiChar( ms.Memory), ms.Size );
|
|
finally
|
|
io.AttachedIEBitmap := nil;
|
|
FreeAndNil( io );
|
|
FreeAndNil( ms );
|
|
end;
|
|
|
|
fileType := IEFileFormatGetInfo( SaveFormat ).SuitableExtension;
|
|
RotateCenter := Point( X + Width div 2, Y + Height div 2 );
|
|
|
|
if ( Length( base64Img ) > 2 ) and ( base64Img[ Length( base64Img ) - 1] = #13 ) and ( base64Img[ Length( base64Img )] = #10 ) then
|
|
SetLength( base64Img, Length( base64Img ) -2 );
|
|
|
|
Result := format( SVG_Image_Tag, [ X, Y, Width, Height, fileType, base64Img,
|
|
GetSVGRotationElement( Rotate, RotateCenter ) + GetSVGTransparencyElement( Transparency )]) +
|
|
CreateSVGShapeTag( iesRectangle, X, Y, Width, Height, BorderWidth, BorderColor, clNone_, Rotate, Transparency );
|
|
end;
|
|
|
|
|
|
function IEEncodeHTMLString(Text: String): String;
|
|
var
|
|
i: integer;
|
|
begin
|
|
result := '';
|
|
for i := 1 to length( Text ) do
|
|
begin
|
|
if Text[i] = '"' then
|
|
result := result + '"'
|
|
else
|
|
if Text[i] = '<' then
|
|
result := result + '<'
|
|
else
|
|
if Text[i] = '>' then
|
|
result := result + '>'
|
|
else
|
|
if Text[i] = '''' then
|
|
result := result + '''
|
|
else
|
|
if Text[i] = '&' then
|
|
result := result + '&'
|
|
else
|
|
result := result + Text[i];
|
|
end;
|
|
end;
|
|
|
|
|
|
function CreateSVGTextTag(Text: string; Font: TFont; X, Y, Width, Height: Integer;
|
|
TextAlign: TIEAlignment; TextLayout: TIELayout; TextAngle: Double;
|
|
BorderWidth: Integer; BorderColor: TColor; FillColor: TColor;
|
|
BorderShape : TIEShape; Transparency: Integer): string;
|
|
const
|
|
Use_Dominant_Baseline = False; // Supported by Chrome and Firefox, but not IE
|
|
Fallback_Fonts = ', Arial, Helvetica'; // Fonts to use if the specified font is not supported
|
|
var
|
|
borderPadding: Integer;
|
|
fontFamily: string;
|
|
fontStyle, fontWeight: string;
|
|
shapeTag, textTag: string;
|
|
textX, textY: Integer;
|
|
textAnchor, alignBaseline: string;
|
|
RotateCenter: TPoint;
|
|
begin
|
|
Result := '';
|
|
borderPadding := 0;
|
|
if BorderColor <> clNone_ then
|
|
borderPadding := BorderWidth + 3;
|
|
|
|
case TextAlign of
|
|
iejLeft : begin
|
|
textX := X + borderPadding;
|
|
textAnchor := 'start';
|
|
end;
|
|
iejRight : begin
|
|
textX := X + Width - borderPadding;
|
|
textAnchor := 'end';
|
|
end;
|
|
else begin
|
|
textX := X + Width div 2;
|
|
textAnchor := 'middle';
|
|
end;
|
|
end;
|
|
|
|
alignBaseline := 'hanging';
|
|
if Use_Dominant_Baseline then
|
|
case TextLayout of
|
|
ielTop : begin
|
|
textY := Y + borderPadding;
|
|
alignBaseline := 'hanging';
|
|
end;
|
|
ielBottom : begin
|
|
textY := Y + Height - borderPadding;
|
|
alignBaseline := 'baseline';
|
|
end;
|
|
else begin
|
|
textY := Y + Height div 2;
|
|
alignBaseline := 'middle';
|
|
end;
|
|
end
|
|
else
|
|
case TextLayout of
|
|
ielTop : textY := Y + borderPadding + abs( Font.Height );
|
|
ielBottom : textY := Y + Height - borderPadding;
|
|
else textY := Y + Height - ( Height - abs( Font.Height )) div 2;
|
|
end;
|
|
|
|
fontStyle := '';
|
|
if fsItalic in Font.Style then
|
|
fontStyle := ' font-style="italic"';
|
|
fontWeight := '';
|
|
if fsBold in Font.Style then
|
|
fontWeight := ' font-weight="bold"';
|
|
|
|
fontFamily := Font.Name;
|
|
if Pos( ' ', fontFamily ) > 0 then
|
|
fontFamily := '''' + fontFamily + '''';
|
|
|
|
RotateCenter := Point( X + Width div 2, Y + Height div 2 );
|
|
|
|
shapeTag := CreateSVGShapeTag( BorderShape, X, Y, Width, Height, BorderWidth, BorderColor, FillColor, 0 { only text rotates }, Transparency);
|
|
textTag := format( SVG_Text_Tag, [ textX, textY, textAnchor, alignBaseline, fontFamily + Fallback_Fonts, Font.Size, ColorToSVG( Font.Color ),
|
|
fontStyle + fontWeight + GetSVGRotationElement( TextAngle, RotateCenter ) + GetSVGTransparencyElement( Transparency ),
|
|
IEEncodeHTMLString( Text )]);
|
|
|
|
Result := shapeTag + textTag;
|
|
end;
|
|
|
|
|
|
|
|
procedure WriteSVGFileOrStream(const FileName: String; Stream: TStream; IEView: TIEView; Bitmap: TIEBitmap; ImageFormat: TIOFileType; var xProgress: TProgressRec);
|
|
var
|
|
commentBlock: string;
|
|
ss: TStringList;
|
|
iev: TImageEnView;
|
|
layersRect: TIERectangle;
|
|
|
|
procedure _GetLayerSVG(IEView: TImageEnView);
|
|
var
|
|
I: Integer;
|
|
layerTag: string;
|
|
begin
|
|
for I := 0 to IEView.LayersCount - 1 do
|
|
begin
|
|
if assigned(xProgress.fOnProgress) then
|
|
xProgress.fOnProgress( xProgress.Sender, Round( i / IEView.LayersCount * 100 ));
|
|
if IEView.Layers[ I ].Visible and not IEView.Layers[ I ].IsMask then
|
|
begin
|
|
layerTag := IEView.Layers[ I ].AsSVG;
|
|
if layerTag <> '' then
|
|
ss.Add( layerTag );
|
|
end;
|
|
end;
|
|
layersRect := IEView.LayersRect;
|
|
end;
|
|
|
|
begin
|
|
ss := TStringList.create;
|
|
try
|
|
if assigned( IEView ) and ( IEView is TImageEnVect ) and ( TImageEnVect( IEView ).ObjectsCount > 0 ) then
|
|
begin
|
|
// We are saving objects of a TImageEnVect. Convert them to TImageEnView layers
|
|
|
|
iev := TImageEnView.create( nil );
|
|
iev.LegacyBitmap := False;
|
|
try
|
|
// Clone the content of a TImageEnVect as layers
|
|
iev.IEBitmap.Assign( TImageEnVect( IEView ).IEBitmap );
|
|
TImageEnVect( IEView ).CopyAllObjectsTo( iev );
|
|
_GetLayerSVG( iev );
|
|
finally
|
|
iev.Free;
|
|
end;
|
|
end
|
|
else
|
|
if assigned( IEView ) and ( IEView is TImageEnView ) then
|
|
begin
|
|
// We are saving Layers of a TImageEnView
|
|
_GetLayerSVG( TImageEnView( IEView ));
|
|
end
|
|
else
|
|
begin
|
|
// Saving only a bitmap
|
|
ss.Text := CreateSVGImageTag( Bitmap, ImageFormat, 0, 0, Bitmap.Width, Bitmap.Height );
|
|
layersRect.X := 0;
|
|
layersRect.Y := 0;
|
|
layersRect.Width := Bitmap.Width;
|
|
layersRect.Height := Bitmap.Height;
|
|
end;
|
|
|
|
if ss.Text = '' then
|
|
xProgress.Aborting^ := true
|
|
else
|
|
begin
|
|
commentBlock := format( 'Created with ImageEn %s - www.ImageEn.com', [ IEMAINVERSION ]);
|
|
ss.Insert( 0, format( SVG_Opening_Tag, [ layersRect.Width, layersRect.Height, layersRect.X, layersRect.Y, layersRect.Width, layersRect.Height ]));
|
|
ss.Insert( 1, format( XML_Comment_Block, [ commentBlock ]));
|
|
|
|
ss.Add( SVG_Closing_Tag );
|
|
|
|
if Stream <> nil then
|
|
ss.SaveToStream( Stream )
|
|
else
|
|
ss.SaveToFile( filename );
|
|
end;
|
|
finally
|
|
ss.Free;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|