| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- unit Img32.Fmt.SVG;
- (*******************************************************************************
- * Author : Angus Johnson *
- * Version : 4.7 *
- * Date : 6 January 2025 *
- * Website : http://www.angusj.com *
- * Copyright : Angus Johnson 2019-2025 *
- * Purpose : SVG file format extension for TImage32 *
- * License : http://www.boost.org/LICENSE_1_0.txt *
- *******************************************************************************)
- interface
- {$I Img32.inc}
- uses
- {$IFDEF MSWINDOWS} Windows, {$ENDIF}
- {$IF NOT DEFINED(NEWPOSFUNC) OR DEFINED(FPC)} StrUtils, {$IFEND}
- {$IFDEF UNICODE} AnsiStrings, {$ENDIF}
- SysUtils, Classes, Math,
- {$IFDEF XPLAT_GENERICS} Generics.Collections, Generics.Defaults, {$ENDIF}
- Img32, Img32.Vector, Img32.SVG.Core, Img32.SVG.Reader
- {$IF DEFINED(USING_LCL)}, Types{$IFEND}
- ;
- type
- TImageFormat_SVG = class(TImageFormat)
- public
- class function IsValidImageStream(stream: TStream): Boolean; override;
- function LoadFromStream(stream: TStream;
- img32: TImage32; imgIndex: integer = 0): Boolean; override;
- // SaveToStream: not implemented for SVG streams
- procedure SaveToStream(stream: TStream;
- img32: TImage32; quality: integer = 0); override;
- class function CanCopyToClipboard: Boolean; override;
- class function CopyToClipboard(img32: TImage32): Boolean; override;
- class function CanPasteFromClipboard: Boolean; override;
- class function PasteFromClipboard(img32: TImage32): Boolean; override;
- end;
- TSvgListObject = class
- xml : string;
- name : string;
- end;
- TSvgImageList32 = class(TInterfacedObj, INotifySender)
- private
- fReader : TSvgReader;
- {$IFDEF XPLAT_GENERICS}
- fList : TList<TSvgListObject>;
- {$ELSE}
- fList : TList;
- {$ENDIF}
- fDefWidth : integer;
- fDefHeight : integer;
- fRecipientList : TRecipients;
- fUpdateCnt : integer;
- {$IFDEF MSWINDOWS}
- fResName : string;
- procedure SetResName(const resName: string);
- {$ENDIF}
- procedure SetDefWidth(value: integer);
- procedure SetDefHeight(value: integer);
- protected
- procedure Changed; virtual;
- procedure BeginUpdate;
- procedure EndUpdate;
- procedure NotifyRecipients(notifyFlag: TImg32Notification);
- public
- constructor Create;
- destructor Destroy; override;
- procedure Clear;
- function Count: integer;
- function Find(const aName: string): integer;
- procedure AddRecipient(recipient: INotifyRecipient);
- procedure DeleteRecipient(recipient: INotifyRecipient);
- function CreateImage(index: integer): TImage32;
- procedure GetImage(index: integer; image: TImage32); overload;
- procedure GetImage(index: integer; image: TImage32; out aName: string); overload;
- procedure Add(const aName, xml: string);
- procedure AddFromFile(const aName, filename: string);
- procedure AddFromResource(const aName, resName: string; resType: PChar);
- procedure Insert(index: integer; const name, xml: string);
- procedure Move(currentIndex, newIndex: integer);
- procedure Delete(index: integer);
- property DefaultWidth: integer read fDefWidth write SetDefWidth;
- property DefaultHeight: integer read fDefHeight write SetDefHeight;
- {$IFDEF MSWINDOWS}
- property ResourceName: string read fResName write SetResName;
- {$ENDIF}
- end;
- implementation
- //------------------------------------------------------------------------------
- // Three routines used to enumerate a resource type
- //------------------------------------------------------------------------------
- function Is_IntResource(lpszType: PChar): Boolean;
- begin
- Result := NativeUInt(lpszType) shr 16 = 0;
- end;
- //------------------------------------------------------------------------------
- function ResourceNameToString(lpszName: PChar): string;
- begin
- if Is_IntResource(lpszName) then
- Result := '#' + IntToStr(NativeUInt(lpszName)) else
- Result := lpszName;
- end;
- //------------------------------------------------------------------------------
- function EnumResNameProc(hModule: HMODULE; lpszType, lpszName: PChar;
- lParam: NativeInt): Boolean; stdcall;
- var
- n: string;
- begin
- n:= ResourceNameToString(lpszName);
- TSvgImageList32(lParam).AddFromResource(n, n, lpszType);
- Result := true;
- end;
- //------------------------------------------------------------------------------
- // TSvgImageList32
- //------------------------------------------------------------------------------
- constructor TSvgImageList32.Create;
- begin
- fReader := TSvgReader.Create;
- {$IFDEF XPLAT_GENERICS}
- fList := TList<TSvgListObject>.Create;
- {$ELSE}
- fList := TList.Create;
- {$ENDIF}
- end;
- //------------------------------------------------------------------------------
- destructor TSvgImageList32.Destroy;
- begin
- NotifyRecipients(inDestroy);
- Clear;
- fList.Free;
- fReader.Free;
- inherited;
- end;
- //------------------------------------------------------------------------------
- {$IFDEF MSWINDOWS}
- procedure TSvgImageList32.SetResName(const resName: string);
- begin
- if fResName = resName then Exit;
- fResName := resName;
- BeginUpdate;
- try
- Clear;
- EnumResourceNames(HInstance, PChar(resName), @EnumResNameProc, lParam(self));
- finally
- EndUpdate;
- end;
- end;
- //------------------------------------------------------------------------------
- {$ENDIF}
- function TSvgImageList32.Count: integer;
- begin
- result := fList.Count;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.Clear;
- var
- i: integer;
- begin
- for i := 0 to fList.Count -1 do
- TSvgListObject(fList[i]).Free;
- fList.Clear;
- Changed;
- end;
- //------------------------------------------------------------------------------
- function TSvgImageList32.Find(const aName: string): integer;
- var
- i: integer;
- begin
- for i := 0 to fList.Count -1 do
- with TSvgListObject(fList[i]) do
- if SameText(name, aName) then
- begin
- Result := i;
- Exit;
- end;
- Result := -1;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.GetImage(index: integer; image: TImage32; out aName: string);
- begin
- if not Assigned(image) or (index < 0) or (index >= count) then Exit;
- if image.IsEmpty then
- image.SetSize(fDefWidth, fDefHeight);
- with TSvgListObject(fList[index]) do
- begin
- fReader.LoadFromString(xml);
- aName := name;
- end;
- fReader.DrawImage(image, true);
- end;
- //------------------------------------------------------------------------------
- function TSvgImageList32.CreateImage(index: integer): TImage32;
- begin
- Result := TImage32.Create(DefaultWidth, DefaultHeight);
- GetImage(index, Result);
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.GetImage(index: integer; image: TImage32);
- var
- dummy: string;
- begin
- GetImage(index, image, dummy);
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.Add(const aName, xml: string);
- begin
- Insert(count, aName, xml);
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.AddFromFile(const aName, filename: string);
- begin
- if not FileExists(filename) then Exit;
- with TStringList.Create do
- try
- LoadFromFile(filename);
- Self.Insert(Self.Count, aName, Text);
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.AddFromResource(const aName, resName: string; resType: PChar);
- var
- rs: TResourceStream;
- ansi: AnsiString;
- begin
- rs := TResourceStream.Create(hInstance, resName, resType);
- try
- SetLength(ansi, rs.Size);
- rs.Read(ansi[1], rs.Size);
- Self.Insert(Self.Count, aName, string(ansi));
- finally
- rs.Free;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.Insert(index: integer; const name, xml: string);
- var
- lo: TSvgListObject;
- begin
- if index < 0 then index := 0
- else if index > Count then index := Count;
- lo := TSvgListObject.Create;
- lo.name := name;
- lo.xml := xml;
- fList.Insert(index, lo);
- Changed;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.Move(currentIndex, newIndex: integer);
- begin
- fList.Move(currentIndex, newIndex);
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.Delete(index: integer);
- begin
- TSvgListObject(fList[index]).Free;
- fList.Delete(index);
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.BeginUpdate;
- begin
- inc(fUpdateCnt);
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.EndUpdate;
- begin
- dec(fUpdateCnt);
- if fUpdateCnt = 0 then Changed;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.Changed;
- begin
- if (fUpdateCnt = 0) then
- NotifyRecipients(inStateChange);
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.SetDefWidth(value: integer);
- begin
- if fDefWidth = value then Exit;
- fDefWidth := value;
- Changed;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.SetDefHeight(value: integer);
- begin
- if fDefHeight = value then Exit;
- fDefHeight := value;
- Changed;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.AddRecipient(recipient: INotifyRecipient);
- var
- len: integer;
- begin
- len := Length(fRecipientList);
- SetLength(fRecipientList, len+1);
- fRecipientList[len] := Recipient;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.DeleteRecipient(recipient: INotifyRecipient);
- var
- i, highI: integer;
- begin
- highI := High(fRecipientList);
- i := highI;
- while (i >= 0) and (fRecipientList[i] <> Recipient) do dec(i);
- if i < 0 then Exit;
- if i < highI then
- System.Move(fRecipientList[i+1], fRecipientList[i],
- (highI - i) * SizeOf(INotifyRecipient));
- SetLength(fRecipientList, highI);
- end;
- //------------------------------------------------------------------------------
- procedure TSvgImageList32.NotifyRecipients(notifyFlag: TImg32Notification);
- var
- i: integer;
- begin
- if fUpdateCnt > 0 then Exit;
- for i := High(fRecipientList) downto 0 do
- try
- //when destroying in a finalization section
- //it's possible for recipients to have been destroyed
- //without their destructors being called.
- fRecipientList[i].ReceiveNotification(self, notifyFlag);
- except
- end;
- end;
- //------------------------------------------------------------------------------
- // Loading (reading) SVG images from file ...
- //------------------------------------------------------------------------------
- function TImageFormat_SVG.LoadFromStream(stream: TStream;
- img32: TImage32; imgIndex: integer = 0): Boolean;
- var
- r: TRectWH;
- sx: double;
- begin
- with TSvgReader.Create do
- try
- Result := LoadFromStream(stream);
- if not Result then Exit;
- r := RootElement.viewboxWH;
- img32.BeginUpdate;
- try
- if img32.IsEmpty then
- begin
- with RootElement do
- if Width.IsValid and Height.IsValid then
- img32.SetSize(
- Round(Width.GetValue(defaultSvgWidth, 0)),
- Round(Height.GetValue(defaultSvgHeight, 0)))
- else if not r.IsEmpty then
- img32.SetSize(Round(r.Width), Round(r.Height))
- else
- img32.SetSize(defaultSvgWidth, defaultSvgHeight);
- end
- else if not r.IsEmpty then
- begin
- // scale the SVG to best fit the image dimensions
- sx := GetScaleForBestFit(r.Width, r.Height, img32.Width, img32.Height);
- img32.SetSize(Round(r.Width * sx), Round(r.Height * sx));
- end;
- //draw the SVG image to fit inside the canvas
- DrawImage(img32, True);
- finally
- img32.EndUpdate;
- end;
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- // Saving (writing) SVG images to file (not currently implemented) ...
- //------------------------------------------------------------------------------
- class function TImageFormat_SVG.IsValidImageStream(stream: TStream): Boolean;
- var
- i, savedPos, len: integer;
- buff: array [1..1024] of AnsiChar;
- begin
- Result := false;
- savedPos := stream.Position;
- len := Min(1024, stream.Size - savedPos);
- stream.Read(buff[1], len);
- stream.Position := savedPos;
- for i := 1 to len -4 do
- begin
- if buff[i] < #9 then Exit
- else if (buff[i] = '<') and
- (buff[i +1] = 's') and
- (buff[i +2] = 'v') and
- (buff[i +3] = 'g') then
- begin
- Result := true;
- break;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TImageFormat_SVG.SaveToStream(stream: TStream;
- img32: TImage32; quality: integer);
- begin
- //not enabled
- end;
- //------------------------------------------------------------------------------
- class function TImageFormat_SVG.CanCopyToClipboard: Boolean;
- begin
- Result := false;
- end;
- //------------------------------------------------------------------------------
- class function TImageFormat_SVG.CopyToClipboard(img32: TImage32): Boolean;
- begin
- Result := false;
- end;
- //------------------------------------------------------------------------------
- class function TImageFormat_SVG.CanPasteFromClipboard: Boolean;
- begin
- Result := false;
- end;
- //------------------------------------------------------------------------------
- class function TImageFormat_SVG.PasteFromClipboard(img32: TImage32): Boolean;
- begin
- Result := false;
- end;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- initialization
- TImage32.RegisterImageFormatClass('SVG', TImageFormat_SVG, cpLow);
- end.
|