1 // SPDX-License-Identifier: LGPL-3.0-linking-exception
2 unit BGRASVGOriginal;
3 
4 {$mode objfpc}{$H+}
5 
6 interface
7 
8 uses
9   BGRAClasses, SysUtils, BGRABitmapTypes, BGRABitmap, BGRASVG, BGRATransform,
10   BGRALayerOriginal, BGRAUnits, BGRALayers;
11 
12 type
13   TBGRALayerSVGOriginal = class;
14 
15   { TBGRASVGOriginalDiff }
16 
17   TBGRASVGOriginalDiff = class(TBGRAOriginalDiff)
18   protected
19     FContentVersionBefore,FContentVersionAfter: integer;
20     FSvgStreamBefore,FSvgStreamAfter: TMemoryStream;
21     FDpiBefore, FDpiAfter: single;
22   public
23     constructor Create(AFromOriginal: TBGRALayerSVGOriginal);
24     procedure ComputeDiff(AToOriginal: TBGRALayerSVGOriginal);
25     procedure Apply(AOriginal: TBGRALayerCustomOriginal); override;
26     procedure Unapply(AOriginal: TBGRALayerCustomOriginal); override;
CanAppendnull27     function CanAppend(ADiff: TBGRAOriginalDiff): boolean; override;
28     procedure Append(ADiff: TBGRAOriginalDiff); override;
IsIdentitynull29     function IsIdentity: boolean; override;
30     destructor Destroy; override;
31   end;
32 
33   { TBGRALayerSVGOriginal }
34 
35   TBGRALayerSVGOriginal = class(TBGRALayerCustomOriginal)
36   private
GetDPInull37     function GetDPI: single;
GetSvgHeightnull38     function GetSvgHeight: single;
GetSvgWidthnull39     function GetSvgWidth: single;
40     procedure SetDPI(AValue: single);
41   protected
42     FSVG: TBGRASVG;
43     FPresentationMatrix: TAffineMatrix;
44     FDiff: TBGRASVGOriginalDiff;
45     FContentVersion: integer;
46     procedure BeginUpdate;
47     procedure EndUpdate;
48     procedure ComputePresentation(AContainerWidth, AContainerHeight: integer; AScaleDPI: single);
49   public
50     constructor Create; override;
51     destructor Destroy; override;
52     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
GetRenderBoundsnull53     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix): TRect; override;
54     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
55     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
56     procedure LoadFromStream(AStream: TStream); override;
57     procedure SaveSVGToStream(AStream: TStream);
58     procedure SetSVG(ASVG: TBGRASVG; AContainerWidth: integer = 640;
59       AContainerHeight: integer = 480; AScaleDPI: single = 1);
60     procedure LoadSVGFromStream(AStream: TStream; AContainerWidth: integer = 640;
61       AContainerHeight: integer = 480; AScaleDPI: single = 1);
GetSVGCopynull62     function GetSVGCopy: TBGRASVG;
StorageClassNamenull63     class function StorageClassName: RawByteString; override;
CanConvertToSVGnull64     class function CanConvertToSVG: boolean; override;
ConvertToSVGnull65     function ConvertToSVG(const AMatrix: TAffineMatrix; out AOffset: TPoint): TBGRASVG; override;
66     property Width: single read GetSvgWidth;
67     property Height: single read GetSvgHeight;
68     property DPI: single read GetDPI write SetDPI;
69     property PresentationMatrix: TAffineMatrix read FPresentationMatrix;
70   end;
71 
72   { TBGRALayeredSVG }
73 
74   TBGRALayeredSVG = class(TBGRALayeredBitmap)
75     protected
GetMimeTypenull76       function GetMimeType: string; override;
77       procedure InternalLoadFromStream(AStream: TStream);
78       procedure InternalSaveToStream(AStream: TStream);
79     public
80       ContainerWidth, ContainerHeight, DefaultSvgDPI, DPI: integer;
81       DefaultLayerName: string;
82       constructor Create; overload; override;
83       constructor Create(AWidth, AHeight: integer); overload; override;
84       procedure LoadFromStream(AStream: TStream); override;
85       procedure LoadFromFile(const filenameUTF8: string); override;
86       procedure SaveToStream(AStream: TStream); override;
87       procedure SaveToFile(const filenameUTF8: string); override;
88   end;
89 
90 implementation
91 
92 uses BGRACanvas2D, BGRAMemDirectory, BGRAUTF8, BGRASVGShapes, math, BGRASVGType,
93   BGRAVectorize;
94 
95 { TBGRASVGOriginalDiff }
96 
97 constructor TBGRASVGOriginalDiff.Create(AFromOriginal: TBGRALayerSVGOriginal);
98 begin
99   if Assigned(AFromOriginal.FSVG) then
100   begin
101     FSvgStreamBefore := TMemoryStream.Create;
102     AFromOriginal.FSVG.SaveToStream(FSvgStreamBefore);
103   end;
104   FContentVersionBefore:= AFromOriginal.FContentVersion;
105   FDpiBefore:= AFromOriginal.DPI;
106 end;
107 
108 procedure TBGRASVGOriginalDiff.ComputeDiff(AToOriginal: TBGRALayerSVGOriginal);
109 begin
110   FreeAndNil(FSvgStreamAfter);
111   if Assigned(AToOriginal.FSVG) then
112   begin
113     FSvgStreamAfter := TMemoryStream.Create;
114     AToOriginal.FSVG.SaveToStream(FSvgStreamAfter);
115   end;
116   FContentVersionAfter:= AToOriginal.FContentVersion;
117   FDpiAfter:= AToOriginal.DPI;
118 end;
119 
120 procedure TBGRASVGOriginalDiff.Apply(AOriginal: TBGRALayerCustomOriginal);
121 var
122   orig: TBGRALayerSVGOriginal;
123 begin
124   orig := AOriginal as TBGRALayerSVGOriginal;
125   if Assigned(FSvgStreamAfter) then
126   begin
127     FSvgStreamAfter.Position:= 0;
128     orig.FSVG.LoadFromStream(FSvgStreamAfter);
129   end else
130     orig.FSVG.Content.Clear;
131   orig.FContentVersion := FContentVersionAfter;
132 end;
133 
134 procedure TBGRASVGOriginalDiff.Unapply(AOriginal: TBGRALayerCustomOriginal);
135 var
136   orig: TBGRALayerSVGOriginal;
137 begin
138   orig := AOriginal as TBGRALayerSVGOriginal;
139   if Assigned(FSvgStreamBefore) then
140   begin
141     FSvgStreamBefore.Position:= 0;
142     orig.FSVG.LoadFromStream(FSvgStreamBefore);
143   end else
144     orig.FSVG.Content.Clear;
145   orig.FContentVersion := FContentVersionBefore;
146 end;
147 
TBGRASVGOriginalDiff.CanAppendnull148 function TBGRASVGOriginalDiff.CanAppend(ADiff: TBGRAOriginalDiff): boolean;
149 begin
150   result := (ADiff is TBGRASVGOriginalDiff) and
151     (TBGRASVGOriginalDiff(ADiff).FContentVersionAfter >= FContentVersionAfter);
152 end;
153 
154 procedure TBGRASVGOriginalDiff.Append(ADiff: TBGRAOriginalDiff);
155 var
156   next: TBGRASVGOriginalDiff;
157 begin
158   next := ADiff as TBGRASVGOriginalDiff;
159   if next.FContentVersionAfter < FContentVersionAfter then
160     raise exception.Create('Cannot append diff made before this one.');
161   FDpiAfter:= next.FDpiAfter;
162   FreeAndNil(FSvgStreamAfter);
163   if Assigned(next.FSvgStreamAfter) then
164   begin
165     FSvgStreamAfter := TMemoryStream.Create;
166     next.FSvgStreamAfter.Position:= 0;
167     FSvgStreamAfter.CopyFrom(next.FSvgStreamAfter, next.FSvgStreamAfter.Size);
168   end;
169   FContentVersionAfter:= next.FContentVersionAfter;
170 end;
171 
IsIdentitynull172 function TBGRASVGOriginalDiff.IsIdentity: boolean;
173 begin
174   result := (FDpiBefore = FDpiAfter) and
175     ( ((FSvgStreamBefore=nil) and (FSvgStreamAfter=nil)) or
176       (Assigned(FSvgStreamBefore) and Assigned(FSvgStreamAfter) and
177        (FSvgStreamBefore.Size = FSvgStreamAfter.Size) and
178        CompareMem(FSvgStreamBefore.Memory,FSvgStreamAfter.Memory,FSvgStreamBefore.Size)) );
179 end;
180 
181 destructor TBGRASVGOriginalDiff.Destroy;
182 begin
183   FSvgStreamBefore.Free;
184   inherited Destroy;
185 end;
186 
187 { TBGRALayerSVGOriginal }
188 
TBGRALayerSVGOriginal.GetDPInull189 function TBGRALayerSVGOriginal.GetDPI: single;
190 begin
191   result := FSVG.DefaultDpi;
192 end;
193 
TBGRALayerSVGOriginal.GetSvgHeightnull194 function TBGRALayerSVGOriginal.GetSvgHeight: single;
195 begin
196   result := FSVG.HeightAsPixel;
197 end;
198 
GetSvgWidthnull199 function TBGRALayerSVGOriginal.GetSvgWidth: single;
200 begin
201   result := FSVG.WidthAsPixel;
202 end;
203 
204 procedure TBGRALayerSVGOriginal.SetDPI(AValue: single);
205 begin
206   BeginUpdate;
207   FSVG.DefaultDpi:= AValue;
208   EndUpdate;
209 end;
210 
211 procedure TBGRALayerSVGOriginal.BeginUpdate;
212 begin
213   if DiffExpected and (FDiff=nil) then
214     FDiff := TBGRASVGOriginalDiff.Create(self);
215 end;
216 
217 procedure TBGRALayerSVGOriginal.EndUpdate;
218 begin
219   if Assigned(FDiff) then FDiff.ComputeDiff(self);
220   NotifyChange(FDiff);
221   FDiff := nil;
222 end;
223 
224 procedure TBGRALayerSVGOriginal.ComputePresentation(AContainerWidth, AContainerHeight: integer;
225   AScaleDPI: single);
226 var
227   compWidth, compHeight: single;
228 begin
229   FSVG.Units.ContainerWidth := FloatWithCSSUnit(AContainerWidth / AScaleDPI, cuPixel);
230   FSVG.Units.ContainerHeight := FloatWithCSSUnit(AContainerHeight / AScaleDPI, cuPixel);
231   compWidth := FSVG.WidthAsPixel;
232   compHeight := FSVG.HeightAsPixel;
233   FSVG.WidthAsPixel := compWidth * AScaleDPI;
234   FSVG.HeightAsPixel := compHeight * AScaleDPI;
235   FPresentationMatrix := FSVG.GetStretchPresentationMatrix(cuPixel);
236 end;
237 
238 constructor TBGRALayerSVGOriginal.Create;
239 begin
240   inherited Create;
241   FSVG := TBGRASVG.Create;
242   FContentVersion := 0;
243 end;
244 
245 destructor TBGRALayerSVGOriginal.Destroy;
246 begin
247   FSVG.Free;
248   FDiff.Free;
249   inherited Destroy;
250 end;
251 
252 procedure TBGRALayerSVGOriginal.Render(ADest: TBGRABitmap;
253   AMatrix: TAffineMatrix; ADraft: boolean);
254 var
255   c2D: TBGRACanvas2D;
256 begin
257   if Assigned(FSVG) then
258   begin
259     c2D := TBGRACanvas2D.Create(ADest);
260     c2D.transform(AMatrix*FPresentationMatrix);
261     c2D.fontRenderer := TBGRAVectorizedFontRenderer.Create;
262     if ADraft then c2D.antialiasing := false;
263     FSVG.Draw(c2D, 0, 0, cuPixel);
264     c2D.Free;
265   end;
266 end;
267 
GetRenderBoundsnull268 function TBGRALayerSVGOriginal.GetRenderBounds(ADestRect: TRect;
269   AMatrix: TAffineMatrix): TRect;
270 var
271   aff: TAffineBox;
272   r: TRectF;
273 begin
274   if Assigned(FSVG) then
275   begin
276     with FSVG.ViewBox do
277       r := RectWithSizeF(min.x, min.y, size.x, size.y);
278     aff := AMatrix * FPresentationMatrix * TAffineBox.AffineBox(r);
279     result := aff.RectBounds;
280   end else
281     result := EmptyRect;
282 end;
283 
284 procedure TBGRALayerSVGOriginal.LoadFromStorage(
285   AStorage: TBGRACustomOriginalStorage);
286 var svgStream: TMemoryStream;
287   valDpi: Single;
288 begin
289   svgStream := TMemoryStream.Create;
290   try
291     if AStorage.ReadFile('content.svg', svgStream) then
292     begin
293       if not Assigned(FSVG) then FSVG := TBGRASVG.Create;
294       svgStream.Position:= 0;
295       FSVG.LoadFromStream(svgStream);
296     end else
297     begin
298       FreeAndNil(FSVG);
299       FSVG := TBGRASVG.Create;
300     end;
301     FPresentationMatrix := FSVG.GetStretchPresentationMatrix(cuPixel);
302     FContentVersion:= AStorage.Int['content-version'];
303   finally
304     svgStream.Free;
305   end;
306   valDpi := AStorage.Float['dpi'];
307   if valDpi <> EmptySingle then
308     FSVG.DefaultDpi:= valDpi;
309 end;
310 
311 procedure TBGRALayerSVGOriginal.SaveToStorage(
312   AStorage: TBGRACustomOriginalStorage);
313 var svgStream: TMemoryStream;
314 begin
315   if Assigned(FSVG) then
316   begin
317     if FContentVersion > AStorage.Int['content-version'] then
318     begin
319       svgStream := TMemoryStream.Create;
320       try
321         FSVG.SaveToStream(svgStream);
322         AStorage.WriteFile('content.svg', svgStream, true, true);
323         svgStream := nil;
324         AStorage.Int['content-version'] := FContentVersion;
325       finally
326         svgStream.Free;
327       end;
328     end;
329     AStorage.Float['dpi'] := FSVG.DefaultDpi;
330   end;
331 end;
332 
333 procedure TBGRALayerSVGOriginal.LoadFromStream(AStream: TStream);
334 begin
335   if TMemDirectory.CheckHeader(AStream) then
336     inherited LoadFromStream(AStream)
337   else
338     LoadSVGFromStream(AStream);
339 end;
340 
341 procedure TBGRALayerSVGOriginal.SaveSVGToStream(AStream: TStream);
342 begin
343   FSVG.SaveToStream(AStream);
344 end;
345 
346 procedure TBGRALayerSVGOriginal.SetSVG(ASVG: TBGRASVG; AContainerWidth: integer;
347       AContainerHeight: integer; AScaleDPI: single);
348 begin
349   BeginUpdate;
350   FSVG.Free;
351   FSVG := ASVG;
352   ComputePresentation(AContainerWidth, AContainerHeight, AScaleDPI);
353   Inc(FContentVersion);
354   EndUpdate;
355 end;
356 
357 procedure TBGRALayerSVGOriginal.LoadSVGFromStream(AStream: TStream; AContainerWidth: integer;
358       AContainerHeight: integer; AScaleDPI: single);
359 begin
360   BeginUpdate;
361   FSVG.LoadFromStream(AStream);
362   ComputePresentation(AContainerWidth, AContainerHeight, AScaleDPI);
363   Inc(FContentVersion);
364   EndUpdate;
365 end;
366 
GetSVGCopynull367 function TBGRALayerSVGOriginal.GetSVGCopy: TBGRASVG;
368 begin
369   result := FSVG.Duplicate;
370 end;
371 
TBGRALayerSVGOriginal.StorageClassNamenull372 class function TBGRALayerSVGOriginal.StorageClassName: RawByteString;
373 begin
374   result := 'svg';
375 end;
376 
TBGRALayerSVGOriginal.CanConvertToSVGnull377 class function TBGRALayerSVGOriginal.CanConvertToSVG: boolean;
378 begin
379   Result:= true;
380 end;
381 
TBGRALayerSVGOriginal.ConvertToSVGnull382 function TBGRALayerSVGOriginal.ConvertToSVG(const AMatrix: TAffineMatrix; out AOffset: TPoint): TBGRASVG;
383 begin
384   if not IsAffineMatrixIdentity(AMatrix) then
385     raise exception.Create('Matrix not valid for SVG original');
386   AOffset := Point(0,0);
387   Result:= GetSVGCopy;
388 end;
389 
390 { TBGRALayeredSVG }
391 
TBGRALayeredSVG.GetMimeTypenull392 function TBGRALayeredSVG.GetMimeType: string;
393 begin
394   Result:= 'image/svg+xml';
395 end;
396 
397 procedure TBGRALayeredSVG.InternalLoadFromStream(AStream: TStream);
398 var
399   svg, svgLayer: TBGRASVG;
400   visualWidth, visualHeight: single;
401   svgOrig: TBGRALayerSVGOriginal;
402   idx, i, j: Integer;
403   layer: TSVGGroup;
404   prefix: String;
405   originalViewBox: TSVGViewBox;
406   m: TAffineMatrix;
407 begin
408   svg := TBGRASVG.Create;
409   try
410     svg.DefaultDpi := DefaultSvgDPI;
411     svg.LoadFromStream(AStream);
412     svg.Units.ContainerWidth := FloatWithCSSUnit(ContainerWidth / DPI * svg.DefaultDpi, cuPixel);
413     svg.Units.ContainerHeight := FloatWithCSSUnit(ContainerHeight / DPI * svg.DefaultDpi, cuPixel);
414     svg.CropToViewBox(DPI / svg.DefaultDpi);
415     visualWidth := svg.WidthAsPixel;
416     visualHeight := svg.HeightAsPixel;
417     Clear;
418     SetSize(floor(visualWidth + 0.95),floor(visualHeight + 0.95));
419     if svg.LayerCount > 0 then
420     begin
421       for i := 0 to svg.LayerCount-1 do
422       begin
423         layer := svg.Layer[i];
424         svgLayer := TBGRASVG.Create;
425         svgLayer.DefaultDpi:= svg.DefaultDpi;
426         svgLayer.WidthAsPixel := visualWidth;
427         svgLayer.HeightAsPixel := visualHeight;
428         for j := 0 to svg.NamespaceCount-1 do
429         begin
430           prefix := svg.NamespacePrefix[j];
431           svgLayer.NamespaceURI[prefix] := svg.NamespaceURI[prefix];
432         end;
433         try
434           svgLayer.ViewBox := svg.ViewBox;
435           if layer.DOMElement.hasAttribute('bgra:originalViewBox') then
436           begin
437             originalViewBox := TSVGViewBox.Parse(layer.DOMElement.GetAttribute('bgra:originalViewBox'));
438             svgLayer.WidthAsPixel := originalViewBox.size.x;
439             svgLayer.HeightAsPixel := originalViewBox.size.y;
440             svgLayer.ViewBox := originalViewBox;
441             m := layer.matrix[cuPixel] * AffineMatrixTranslation(originalViewBox.min.x, originalViewBox.min.y);
442           end else
443             m := layer.matrix[cuPixel];
444           for j := 0 to svg.Content.IndexOfElement(layer)-1 do
445             if svg.Content.ElementObject[j] is TSVGDefine then
446               svgLayer.Content.CopyElement(svg.Content.ElementObject[j]);
447           for j := 0 to layer.Content.ElementCount-1 do
448             svgLayer.Content.CopyElement(layer.Content.ElementObject[j]);
449           svgOrig := TBGRALayerSVGOriginal.Create;
450           svgOrig.SetSVG(svgLayer);
451           svgLayer := nil;
452           idx := AddLayerFromOwnedOriginal(svgOrig);
453           LayerName[idx] := layer.Name;
454           LayerVisible[idx] := layer.Visible;
455           LayerOpacity[idx] := min(255,max(0,round(layer.opacity*255)));
456           BlendOperation[idx] := layer.mixBlendMode;
457           LayerOriginalMatrix[idx] := m;
458           RenderLayerFromOriginal(idx);
459         finally
460           svgLayer.Free;
461         end;
462       end;
463     end else
464     begin
465       svgOrig := TBGRALayerSVGOriginal.Create;
466       svgOrig.SetSVG(svg);
467       svg := nil;
468       idx := AddLayerFromOwnedOriginal(svgOrig);
469       LayerName[idx] := DefaultLayerName+'1';
470       RenderLayerFromOriginal(idx);
471     end;
472   finally
473     svg.Free;
474   end;
475 end;
476 
477 procedure TBGRALayeredSVG.InternalSaveToStream(AStream: TStream);
478 
479   procedure StoreLayerBitmap(ABitmap: TBGRABitmap; AOwned: boolean; const AMatrix: TAffineMatrix;
480         ADestElem: TSVGCustomElement; AContent: TSVGContent);
481   var
482     img: TSVGImage;
483     ab: TAffineBox;
484     vb: TSVGViewBox;
485   begin
486     ab := AMatrix * TAffineBox.AffineBox(PointF(0, 0), PointF(ABitmap.Width, 0), PointF(0, ABitmap.Height));
487 
488     img := AContent.AppendImage(AMatrix[1,3], AMatrix[2, 3], ABitmap.Width, ABitmap.Height, ABitmap, AOwned);
489     img.matrix[cuCustom] := AffineMatrixLinear(AMatrix);
490 
491     if ADestElem is TSVGGroup then
492     with TSVGGroup(ADestElem) do
493     begin
494       with ab.RectBounds do
495       begin
496         vb.min := PointF(Left, Top);
497         vb.size := PointF(Width, Height);
498       end;
499       DOMElement.SetAttribute('xmlns:bgra', 'https://wiki.freepascal.org/LazPaint_SVG_format');
500       DOMElement.SetAttribute('bgra:originalViewBox', vb.ToString);
501     end;
502   end;
503 
504   procedure StoreLayer(ALayerIndex: integer; ASVG: TBGRASVG; ADestElem: TSVGCustomElement;
505         ADest: TSVGContent; out AMatrix: TAffineMatrix);
506   var
507     c: TBGRALayerOriginalAny;
508     bmp, part: TBGRABitmap;
509     layerSvg: TBGRASVG;
510     i: Integer;
511     prefix: String;
512     origViewBox: TSVGViewBox;
513     wantedOfs: TPoint;
514     r: TRect;
515   begin
516     AMatrix := AffineMatrixIdentity;
517     if LayerOriginalKnown[ALayerIndex] then
518       c:= LayerOriginalClass[ALayerIndex]
519       else c := nil;
520 
521     if Assigned(c) and c.CanConvertToSVG then
522     begin
523       if LayerOriginal[ALayerIndex].IsInfiniteSurface then
524       begin
525         layerSvg := LayerOriginal[ALayerIndex].ConvertToSVG(LayerOriginalMatrix[ALayerIndex],
526           wantedOfs) as TBGRASVG;
527         layerSvg.WidthAsPixel:= Self.Width;
528         layerSvg.HeightAsPixel:= Self.Height;
529         AMatrix := AffineMatrixTranslation(wantedOfs.X, wantedOfs.Y);
530       end else
531       begin
532         layerSvg := LayerOriginal[ALayerIndex].ConvertToSVG(AffineMatrixIdentity,
533           wantedOfs) as TBGRASVG;
534         AMatrix:= LayerOriginalMatrix[ALayerIndex]
535           * AffineMatrixTranslation(wantedOfs.X, wantedOfs.Y)
536           * layerSvg.GetStretchPresentationMatrix(cuPixel);
537       end;
538       origViewBox := layerSvg.ViewBox;
539       try
540         layerSvg.ConvertToUnit(cuCustom);
541         if ADestElem is TSVGGroup then
542         with TSVGGroup(ADestElem) do
543         begin
544           DOMElement.SetAttribute('xmlns:bgra', 'https://wiki.freepascal.org/LazPaint_SVG_format');
545           DOMElement.SetAttribute('bgra:originalViewBox', origViewBox.ToString);
546         end;
547         for i := 0 to layerSvg.Content.ElementCount-1 do
548           ADest.CopyElement(layerSvg.Content.ElementObject[i]);
549         for i := 0 to layerSvg.NamespaceCount-1 do
550         begin
551           prefix := layerSvg.NamespacePrefix[i];
552           ASVG.NamespaceURI[prefix] := layerSvg.NamespaceURI[prefix];
553         end;
554       finally
555         layerSvg.Free;
556       end;
557     end else
558     begin
559       r := LayerBitmap[ALayerIndex].GetImageBounds;
560       if (r.Left = 0) and (r.Top = 0) and (r.Width = LayerBitmap[ALayerIndex].Width) and
561         (r.Height = LayerBitmap[ALayerIndex].Height) then
562         StoreLayerBitmap(LayerBitmap[ALayerIndex], false,
563           AffineMatrixTranslation(LayerOffset[ALayerIndex].X, LayerOffset[ALayerIndex].Y), ADestElem, ADest)
564       else
565       begin
566         part := LayerBitmap[ALayerIndex].GetPart(r);
567         StoreLayerBitmap(part, true,
568           AffineMatrixTranslation(LayerOffset[ALayerIndex].X + r.Left,
569           LayerOffset[ALayerIndex].Y + r.Top), ADestElem, ADest)
570       end;
571     end;
572   end;
573 
574 var
575   svg: TBGRASVG;
576   vb : TSVGViewBox;
577   i,j: Integer;
578   g: TSVGGroup;
579   m: TAffineMatrix;
580   identifiers, newIdentifiers: TStringList;
581 begin
582   svg := TBGRASVG.Create;
583   identifiers := nil;
584   newIdentifiers := nil;
585   try
586     svg.WidthAsPixel := Width;
587     svg.HeightAsPixel := Height;
588     vb.min := PointF(0, 0);
589     vb.size := PointF(Width, Height);
590     svg.ViewBox := vb;
591     if (NbLayers = 1) and (LayerOpacity[0] = 255) and LayerVisible[0] and
592        (LayerOriginalGuid[0] = GUID_NULL) then
593     begin
594       StoreLayer(0, svg, svg, svg.Content, m);
595     end else
596     begin
597       svg.NamespaceURI['inkscape'] := 'http://www.inkscape.org/namespaces/inkscape';
598       identifiers := TStringList.Create;
599       newIdentifiers := TStringList.Create;
600       for i := 0 to NbLayers-1 do
601       begin
602         g := svg.Content.AppendGroup;
603         g.IsLayer := true;
604         g.Name:= LayerName[i];
605         g.opacity:= LayerOpacity[i]/255;
606         g.Visible:= LayerVisible[i];
607         g.mixBlendMode:= BlendOperation[i];
608         StoreLayer(i, svg, g, g.Content, m);
609         g.matrix[cuPixel] := m;
610         identifiers.Clear;
611         g.ListIdentifiers(identifiers);
612         newIdentifiers.Clear;
613         for j := 0 to identifiers.Count-1 do
614           newIdentifiers.Add('layer'+inttostr(i+1)+'-'+identifiers[j]);
615         g.RenameIdentifiers(identifiers, newIdentifiers);
616       end;
617     end;
618     svg.SaveToStream(AStream);
619   finally
620     newIdentifiers.Free;
621     identifiers.Free;
622     svg.Free;
623   end;
624 end;
625 
626 constructor TBGRALayeredSVG.Create;
627 begin
628   inherited Create;
629   ContainerWidth:= 640;
630   ContainerHeight:= 480;
631   DefaultLayerName := 'Layer';
632   DPI := 96;
633   DefaultSvgDPI:= 96;
634 end;
635 
636 constructor TBGRALayeredSVG.Create(AWidth, AHeight: integer);
637 begin
638   inherited Create(AWidth, AHeight);
639   ContainerWidth:= 640;
640   ContainerHeight:= 480;
641   DefaultLayerName := 'Layer';
642 end;
643 
644 procedure TBGRALayeredSVG.LoadFromStream(AStream: TStream);
645 begin
646   OnLayeredBitmapLoadFromStreamStart;
647   try
648     InternalLoadFromStream(AStream);
649   finally
650     OnLayeredBitmapLoaded;
651   end;
652 end;
653 
654 procedure TBGRALayeredSVG.LoadFromFile(const filenameUTF8: string);
655 var AStream: TFileStreamUTF8;
656 begin
657   AStream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
658   OnLayeredBitmapLoadStart(filenameUTF8);
659   try
660     LoadFromStream(AStream);
661   finally
662     OnLayeredBitmapLoaded;
663     AStream.Free;
664   end;
665 end;
666 
667 procedure TBGRALayeredSVG.SaveToStream(AStream: TStream);
668 begin
669   OnLayeredBitmapSaveToStreamStart;
670   try
671     InternalSaveToStream(AStream);
672   finally
673     OnLayeredBitmapSaved;
674   end;
675 end;
676 
677 procedure TBGRALayeredSVG.SaveToFile(const filenameUTF8: string);
678 var AStream: TFileStreamUTF8;
679 begin
680   AStream := TFileStreamUTF8.Create(filenameUTF8,fmCreate);
681   OnLayeredBitmapSaveStart(filenameUTF8);
682   try
683     InternalSaveToStream(AStream);
684   finally
685     OnLayeredBitmapSaved;
686     AStream.Free;
687   end;
688 end;
689 
690 procedure RegisterLayeredSvgFormat;
691 begin
692   RegisterLayeredBitmapReader('svg', TBGRALayeredSVG);
693   RegisterLayeredBitmapWriter('svg', TBGRALayeredSVG);
694 end;
695 
696 initialization
697 
698   RegisterLayerOriginal(TBGRALayerSVGOriginal);
699   RegisterLayeredSvgFormat;
700 
701 end.
702 
703