View Issue Details

IDProjectCategoryView StatusLast Update
0014333LazarusLCLpublic2014-10-02 08:33
ReporterAnton Kavalenka Assigned ToFelipe Monteiro de Carvalho  
PrioritynormalSeverityfeatureReproducibilityalways
Status assignedResolutionopen 
Platformx86_64OSDebian GNU/Linux 
Product Version0.9.27 (SVN) 
Summary0014333: Lazarus Metafile: X-platform vector graphic implementation
DescriptionProposed X-platform LCL-based TGraphic descendant (TlmfImage) and corresponding TCanvas descendant (TlmfCanvas) which behaves mostly as metafiles in Delphi.
Basic features:
* True X-platform (currently tested under Win32 and GTK2)
* Usage exact as Delphi TMetafile and TMetafileCanvas (makes Delphi projects migration convenient)
* Stream of recorded graphic commands can be easily enumerated and converted to other vector formats (SVG)
* Scalable (StretchDraw works fine)
* Extensible - metafile records are TComponent descendants - so it is easy to subclass them.

Steps To ReproducePresented sample print-preview application with 3 sample pages.
1st page generated pragmatically
2nd and 3rd are recorded by another application.

Feel free to use for any purpose.
TagsNo tags attached.
Fixed in Revision
LazTarget-
WidgetsetGTK 2, Win32/Win64
Attached Files

Relationships

related to 0009263 acknowledged TMetaFile is missing 
related to 0020276 resolvedJesus Reyes Lazarus does not print Greek characters 

Activities

2009-08-12 18:46

 

lmftest.zip (278,013 bytes)

Anton Kavalenka

2009-08-12 18:47

reporter   ~0029801

1st page in print-preview generated programmatically

Felipe Monteiro de Carvalho

2009-08-17 01:22

developer   ~0029917

Just a hint: There is also fpvectorial in Free Pascal subversion, which already supports some formats and could be extended for more:

http://wiki.lazarus.freepascal.org/fpvectorial

Anton Kavalenka

2009-08-17 11:32

reporter   ~0029920

TvVectorialDocument is the abstraction of vector document format.
It is usable in file converters.
As far I understand - it supports very limited set of graphic primitives:
only paths.

TlmfImage is TGraphic descendant, so it can be Read|Written with stream and drawn to TCanvas descendants. Any component capable of showing TGraphic can draw TlmfImage.

TlmfImage can be encapsulated into TvVectorialDocument descendant to provide vector format conversions.

Felipe Monteiro de Carvalho

2009-08-22 18:03

developer   ~0030080

It supports only paths because thats all that I needed for the initial use, and they can represent anything, even text, but it could surely be expanded to have other drawing primitives. A conversion from other primitives to paths can also be added.

What I am proposing here is a better architecture, just like the LCL raster image classes are based in fpimage, LCL vectorial classes could also be based in fpvectorial to do I/O, in-memory data storage other low level activities.

In more concrete words, this component could be modified to be a link between fpvectorial and TGraphic and then it would automatically have support for all formats added to fpvectorial.

Because fpvectorial is only in svn FPC it can be copyed to Lazarus until it is included in a release.

2010-04-22 18:33

 

lmf.pas (26,620 bytes)   
unit lmf;

{$mode delphi}{$H+}

interface

uses
	Types,
  Classes, SysUtils,
	Graphics,FPCanvas, FPImage, syncobjs;

  type
		TlmfList = class;

    TlmfImage=class(TGraphic)
    private
			forgX,forgY,
      fWidth,fHeight:integer;
      kx,ky:double;
      fList:TlmfList;
      fCrs:TCriticalSection;
    protected
			procedure AssignTo(Dest:TPersistent);override;
      function GetWidth:integer;override;
      procedure SetWidth(AVal:integer);override;
      function GetHeight:integer;override;
      procedure SetHeight(AVal:integer);override;
      function GetEmpty:boolean;override;
      function GetTransparent: Boolean; override;
      procedure SetTransparent(Value: Boolean); override;
      //procedure Erase;override;

      function ScaleX(ax:integer):integer;
      function ScaleY(ay:integer):integer;
    public
      constructor Create;override;
      destructor Destroy;override;
      procedure Clear;override;
      procedure SaveToStream(Stream: TStream); override;
			procedure LoadFromStream(Stream: TStream); override;
      procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
			property List:TlmfList read fList;
    end;

		TlmfList=class(TComponent)
    private
    	fWidth,fHeight:integer;
		public
			procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
	    function GetChildOwner: TComponent; override;
    published
    	property Width:integer read fWidth write fWidth;
      property Height:integer read fHeight write fHeight;
		end;

    TlmfObject=class(TComponent)
    public
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);virtual;abstract;
    end;

    TlmfAnchor=class(TlmfObject)
    private
      fPos:TPoint;
    public
      constructor Create(Ax,Ay:integer);virtual;
		published
			property px:integer read fPos.x write fpos.x;
			property py:integer read fPos.y write fpos.y;
    end;

    TlmfMoveTo=class(TlmfAnchor)
    public
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
    end;

    TlmfLineTo=class(TlmfAnchor)
    public
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
    end;

    TlmfLine=class(TlmfAnchor)
    private
      fEndPos:TPoint;
    public
      constructor Create(x1,y1,x2,y2:integer);overload;
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		published
			property px1:integer read fEndPos.x write fEndpos.x;
			property py1:integer read fEndPos.y write fEndpos.y;
    end;

    TlmfText=class(TlmfAnchor)
    private
      fText:string;
    //  fHeight:integer;
    //  fStyle:TTextStyle;
		protected
			procedure DefineProperties(Filer: TFiler); override;
    public
      constructor Create(x,y:integer; const AText:string);overload;
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		published
			property Text:string read fText write fText;
		//	property Height:integer read fHeight write fHeight;
			//property Style:TTextStyle read fTextStyle write fTextStyle;
    end;

    TlmfColor=class(TlmfAnchor)
    private
      fColor:TfpColor;
    public
      constructor Create(x,y:integer; AColor:TfpColor);overload;
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		published
			property r:word read fColor.red write fColor.red;
			property g:word read fColor.green write fColor.green;
			property b:word read fColor.blue write fColor.blue;
			property a:word read fColor.alpha write fColor.alpha;
    end;

    TlmfClip=class(TlmfObject)
    private
      fClip:TRect;
    public
      constructor Create(AClip:TRect);virtual;overload;
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		published
			property Left:integer read fClip.Left write fClip.Left;
			property Top:integer read fClip.Top write fClip.Top;
			property Right:integer read fClip.Right write fClip.Right;
			property Bottom:integer read fClip.Bottom write fClip.Bottom;
    end;

    TlmfRect=class(TlmfClip)
    public
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
    end;

    TlmfFillRect=class(TlmfRect)
		public
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		end;

    TlmfEllipse=class(TlmfClip)
    public
      procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
    end;

		TlmfFont=class(TlmfObject)
		private
			fFont:TFont;
      fHeight,fRotation:integer;
      fName:string;
		public
			constructor Create(AnOwner:TComponent);override;
			destructor Destroy;override;
			procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		published
			property Font:TFont read fFont write fFont;
      property Height:integer read fHeight write fHeight;
      property Rotation:integer read fRotation write fRotation;
		end;

		TlmfBrush=class(TlmfObject)
		private
			fBrush:TBrush;
		public
			constructor Create(AnOwner:TComponent);override;
			destructor Destroy;override;
			procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		published
			property Brush:TBrush read fBrush write fBrush;
		end;

		TlmfPen=class(TlmfObject)
		private
			fPen:TPen;
		public
			constructor Create(AnOwner:TComponent);override;
			destructor Destroy;override;
			procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		published
			property Pen:TPen read fPen write fPen;
		end;

		TlmfGraph=class(TlmfClip)
		private
			fGraph:TPicture;
		public
			constructor Create(AnOwner:TComponent);override;
			destructor Destroy;override;
			procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		published
			property Graph:TPicture read fGraph write fGraph;
		end;

		TlmfPolyline=class(TlmfRect)
		private
			pts:array of TPoint;
    protected
      procedure StorePoints(AStream:TStream);virtual;
      procedure LoadPoints(AStream:TStream);virtual;
    	procedure DefineProperties(Afiler:TFiler);override;
		public
			constructor Create(Points:PPoint;NumPts:integer);overload;
			destructor Destroy;override;
			procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		end;

		TlmfPolygon=class(TlmfPolyline)
		private
			fWinding:boolean;
		public
			constructor Create(Points:PPoint;NumPts:integer;Winding:boolean=false);overload;
			procedure Action(fImage:TlmfImage;ACanvas:TCanvas);override;
		published
    	property Winding:boolean read fWinding write fWinding;
		end;



    TlmfCanvas=class(TCanvas)
    private
    	fClipRect:TRect;
			fState:TCanvasState;
      fImage:TlmfImage;
    protected
			procedure CreateFont;override;
			procedure CreateBrush;override;
			procedure CreatePen;override;
      function DoCreateDefaultFont : TFPCustomFont; override;
      function DoCreateDefaultPen : TFPCustomPen; override;
      function DoCreateDefaultBrush : TFPCustomBrush; override;
			procedure DoGetTextSize (text:string; var w,h:integer);override;
      function  DoAllowBrush (ABrush : TFPCustomBrush) : boolean; override;
      procedure DoMoveTo(x, y: integer); override;
      procedure DoLineTo(x, y: integer); override;
      procedure DoLine(x1, y1, x2, y2: integer); override;
    //  procedure DoEllipseFill (const Bounds:TRect); override;
    	procedure DoEllipse (const Bounds:TRect); override;
			procedure DoRectangleFill (Const Bounds:TRect); override;



			procedure SetPixel(X,Y: Integer; Value: TColor); override;
      procedure SetColor (x,y:integer; const Value:TFPColor); override;
      function  GetColor (x,y:integer) : TFPColor; override;
      procedure SetClipRect(const AValue: TRect); override;
      function GetClipRect:TRect; override;
			procedure RequiredState(ReqState: TCanvasState); override;
      procedure CopyRect(const Dest: TRect; SrcCanvas: TCanvas; const Source: TRect);override;
    public
      constructor Create(Almf:TlmfImage);
			procedure TextOut (x,y:integer;const text:string); override; // already in fpcanvas
      function TextExtent(const Text: string): TSize;override;
      procedure TextRect(ARect: TRect; X, Y: integer; const Text: string;
                       const Style: TTextStyle); override;
      procedure StretchDraw(const DestRect: TRect; SrcGraphic: TGraphic); override;
      procedure Rectangle(X1,Y1,X2,Y2: Integer); override; // already in fpcanvas
			procedure Polyline(Points: PPoint; NumPts: Integer);override;
			procedure Polygon(Points: PPoint; NumPts: Integer;  Winding: boolean = False);override;
      procedure Ellipse (x1,y1,x2,y2:integer); override;
    end;

implementation
uses lcltype,lclintf;

constructor TlmfImage.Create;
begin
  inherited Create;
  fCrs:=syncobjs.TCriticalSection.Create;
  fList:=TlmfList.Create(nil);
end;

destructor TlmfImage.Destroy;
begin
  Clear;
	fList.Free;
  inherited Destroy;
  fCrs.Free;
end;

procedure TlmfImage.AssignTo(Dest:TPersistent);
var
	mf:TMemoryStream;
begin
	if Dest is TlmfImage then
	begin
		mf:=TMemoryStream.Create;
		try
  		mf.WriteComponent(fList);
			mf.Position:=0;
			mf.ReadComponent(TlmfImage(Dest).fList);
			TlmfImage(Dest).fWidth:=fWidth;
			TlmfImage(Dest).fHeight:=fHeight;
		finally
			mf.Free;
		end;
	end
	else
	inherited AssignTo(Dest);
end;

procedure TlmfImage.Clear;
var
  i:integer;
  item:TObject;
begin
	fList.DestroyComponents;
 (* for i:=fList.Count-1 downto 0 do
  begin
    item:=TObject(fList[i]);
    fList.Delete(i);
    item.Free;
  end;*)
end;

function TlmfImage.GetWidth:integer;
begin
  Result:=flist.fWidth;
end;

procedure TlmfImage.SetWidth(AVal:integer);
begin
  if (AVal=fWidth) then exit;
  fWidth:=AVal;
  fList.fWidth:=fWidth;
  Self.Modified:=true;
end;

function TlmfImage.GetHeight:integer;
begin
  Result:=fList.fHeight;
end;

function TlmfImage.ScaleX(ax:integer):integer;
begin
  Result:=fOrgX+trunc(ax*kx);
	//if Result>Width then Result:=width;
end;

function TlmfImage.ScaleY(ay:integer):integer;
begin
  Result:=fOrgY+trunc(ay*ky);
	//if Result>height then Result:=height;
end;

procedure TlmfImage.SetHeight(AVal:integer);
begin
  if (AVal=fHeight) then exit;
  fHeight:=AVal;
  fList.fHeight:=fHeight;
  Modified:=true;
end;

function TlmfImage.GetEmpty:boolean;
begin
  Result:=Assigned(fList) and (fList.ComponentCount>0);
end;

procedure TlmfImage.Draw(ACanvas: TCanvas; const Rect: TRect);
var
  i:integer;
begin
	fCrs.Acquire;
  try
  	fOrgX:=Rect.Left;
  	fOrgY:=Rect.Top;
    kx:=(Rect.Right-Rect.Left)/Width;
    ky:=(Rect.Bottom-Rect.Top)/Height;
    ACanvas.MoveTo(ScaleX(Rect.Left),ScaleY(Rect.Top));
    for i:=0 to fList.ComponentCount-1 do
    begin
      TlmfObject(flist.Components[i]).Action(Self,ACanvas);
    end;
  finally
    kx:=1;
    ky:=1;
    fCrs.Release;
  end;
end;

function TlmfImage.GetTransparent: Boolean;
begin
  Result:=true; // assume it is always
end;

procedure TlmfImage.SetTransparent(Value: Boolean);
begin
  // nothing to do
end;

procedure TlmfImage.SaveToStream(Stream: TStream);
begin
	Stream.WriteComponent(fList);
end;

procedure TlmfImage.LoadFromStream(Stream: TStream);
begin
	Stream.ReadComponent(fList);
	//Stream.
end;



//  TlmfCanvas
constructor TlmfCanvas.Create(Almf:TlmfImage);
begin
  fImage:=Almf;
  inherited Create;
end;

procedure TlmfCanvas.RequiredState(ReqState: TCanvasState);
var
  Needed: TCanvasState;
begin
  Needed := ReqState - fState;
  if Needed <> [] then
  begin
    if csHandleValid in Needed then
    begin
      RealizeAntialiasing;
      Include(FState, csHandleValid);
    end;
    if csFontValid in Needed then
			CreateFont;
    if csPenValid in Needed then
    begin
      CreatePen;
      if Pen.Style in [psDash, psDot, psDashDot, psDashDotDot]
      then Include(Needed, csBrushValid);
    end;
    if csBrushValid in Needed then
			CreateBrush;
  end;
end;

// workaround

function TlmfCanvas.DoCreateDefaultFont : TFPCustomFont;
begin
  Result:=TFont.Create;
  Result.Name:='Sans';
  Result.Size:=10;
  TFont(Result).Orientation:=0;
end;

function TlmfCanvas.DoCreateDefaultPen : TFPCustomPen;
begin
  Result:=TPen.Create;
  TPen(Result).Color:=clBlack;
  Tpen(Result).Style:=psSolid;
end;

function TlmfCanvas.DoCreateDefaultBrush : TFPCustomBrush;
begin
  Result:=TBrush.Create;
  Result.Style:=bsClear;
  Tbrush(Result).Color:=clNone;
end;


procedure TlmfCanvas.DoMoveTo(x, y: integer);
var
  item:TlmfMoveTo;
begin
  item:=TlmfMoveTo.Create(x,y);
  fImage.fList.InsertComponent(item);
end;

procedure TlmfCanvas.DoLineTo(x, y: integer);
var
  item:TlmfAnchor;
begin
	RequiredState([csPenValid]);
  item:=TlmfLineTo.Create(x,y);
	fImage.fList.InsertComponent(item);
end;


procedure TlmfCanvas.DoLine(x1, y1, x2, y2: integer);
var
  item:TlmfAnchor;
begin
	RequiredState([csPenValid]);
  item:=TlmfLine.Create(x1,y1,x2,y2);
  fImage.fList.InsertComponent(item);
end;

procedure TlmfCanvas.DoEllipse (const Bounds:TRect);
var
  item:TlmfEllipse;
begin
	RequiredState([csPenValid,csBrushValid]);
  item:=TlmfEllipse.Create(Bounds);
  fImage.fList.InsertComponent(item);
end;


procedure TlmfCanvas.TextOut (x,y:integer;const text:string);
var
  item:TlmfText;
begin
	RequiredState([csFontValid,csBrushValid]);
  item:=TlmfText.Create(x,y,text);
  fImage.fList.InsertComponent(item);
//  item.fHeight:=Font.Height;
//	fillchar(item.fStyle,sizeof(item.fStyle),0);
end;

procedure TlmfCanvas.TextRect(ARect: TRect; X, Y: integer; const Text: string;
                       const Style: TTextStyle);
var
  item:TlmfText;
begin
	RequiredState([csFontValid,csBrushValid]);
  item:=TlmfText.Create(x,y,text);
  fImage.fList.InsertComponent(item);
//  item.fHeight:=Font.Height;
//	item.fStyle:=Style;
end;

procedure TlmfCanvas.StretchDraw(const DestRect: TRect; SrcGraphic: TGraphic);
var
  item:TlmfGraph;
begin
	//RequiredState([csFontValid,csBrushValid]);
  item:=TlmfGraph.Create(nil);
  fImage.fList.InsertComponent(item);
	item.fGraph.Assign(SrcGraphic);
	item.fClip:=DestRect;
end;

procedure TlmfCanvas.CopyRect(const Dest: TRect; SrcCanvas: TCanvas;
  const Source: TRect);
var
  SH, SW, DH, DW: Integer;
  item:TlmfGraph;
  bmp:TBitmap;
Begin
  if SrcCanvas= nil then exit;

  SH := Source.Bottom - Source.Top;
  SW := Source.Right - Source.Left;
  if (SH=0) or (SW=0) then exit;
  DH := Dest.Bottom - Dest.Top;
  DW := Dest.Right - Dest.Left;
  if (Dh=0) or (DW=0) then exit;

  TlmfCanvas(SrcCanvas).RequiredState([csHandleValid]);
  Changing;
  RequiredState([csHandleValid]);

  bmp:=TBitmap.Create;
  try
    bmp.SetSize(SW,SH);
    bmp.Canvas.CopyRect(Rect(0,0,SW,SH),SrcCanvas,Source);
    Self.StretchDraw(Dest,bmp); // this stores graphic
  finally
    bmp.Free;
  end;

  Changed;
end;

procedure TlmfCanvas.SetColor (x,y:integer; const Value:TFPColor);
var
  item:TlmfAnchor;
begin
  item:=TlmfColor.Create(x,y,Value);
  fImage.fList.InsertComponent(item);
end;

procedure TlmfCanvas.SetPixel(X,Y: Integer; Value: TColor);
begin
	SetColor(x,y,TColorToFPColor(Value));
end;

function TlmfCanvas.GetColor (x,y:integer) : TFPColor;
begin
  Result.alpha:=0;
  Result.red:=0;
  Result.green:=0;
  Result.blue:=0;
end;

procedure TlmfCanvas.SetClipRect(const AValue: TRect);
var
  item:TlmfObject;
begin
  inherited SetClipRect(AValue);
  fClipRect:=AValue;
  item:=TlmfClip.Create(AValue);
  fImage.fList.InsertComponent(item);
end;

function TlmfCanvas.GetClipRect:TRect;
begin
	Result:=fClipRect;
end;


procedure TlmfCanvas.Rectangle(X1,Y1,X2,Y2: Integer);
var
  item:TlmfObject;
begin
	RequiredState([csPenValid,csBrushValid]); // this adds TlmfPen
  item:=TlmfRect.Create(Rect(x1,y1,x2,y2));
  fImage.fList.InsertComponent(item);
end;

procedure TlmfCanvas.DoRectangleFill (Const Bounds:TRect);
var
  item:TlmfFillRect;
begin
	RequiredState([csBrushValid,csPenvalid]); // this adds TlmfBrush, TlmfPen
  item:=TlmfFillRect.Create(nil);
  fImage.fList.InsertComponent(item);
end;

function  TlmfCanvas.DoAllowBrush (ABrush : TFPCustomBrush) : boolean;
begin
  Result:=true;
end;

procedure TlmfCanvas.CreateFont;
var
  item:TlmfFont;
begin
  item:=TlmfFont.Create(nil);
  item.fRotation:=TFont(Font).Orientation;
	item.Font.Assign(Font);
  item.fHeight:=Font.Height;
  item.fName:=Font.Name;
  item.fRotation:=TFont(item.Font).Orientation;


  //writems('Created font "%s" size=%d rot=%d',[item.Font.Name,item.Font.Size,TrotFont(item.Font).Rotation]);
  fImage.fList.InsertComponent(item);
end;

procedure TlmfCanvas.CreateBrush;
var
  item:TlmfBrush;
begin
  item:=TlmfBrush.Create(nil);
	item.Brush.Assign(Brush);
  fImage.fList.InsertComponent(item);
end;

procedure TlmfCanvas.CreatePen;
var
  item:TlmfPen;
begin
  item:=TlmfPen.Create(nil);
	item.Pen.Assign(Pen);
  fImage.fList.InsertComponent(item);
end;


type
	TSafeObject=class(TObject)
    Font:TFont;
    ext:TSize;
    str:string;
    procedure MeasureExtent;
  end;

  procedure TSafeObject.MeasureExtent;
  var
  	dc:HDC;
 		ofh:HFONT;
  begin
  	dc:=CreateCompatibleDC(0);
  	try
      try
    		ofh:=SelectObject(dc,Font.Handle);
    	  GetTextExtentPoint(dc, PChar(str), Length(str), ext);
     // writeln('Result.cx=',Result.cx,' Result.cy=',result.cy);
    		SelectObject(dc,ofh);
    //
    	finally
    	  DeleteDC(dc);
      end
	  except
  		writeln('wrong string:',str);
	  end;
  end;

function TlmfCanvas.TextExtent(const Text: string): TSize;
var
	so:TSafeObject;
begin
  Result.cX := 0;
  Result.cY := 0;
  if Text='' then exit;
  RequiredState([csHandleValid,csFontValid]);
  so:=TSafeObject.Create;
  try
  	so.Font:=Self.Font;
    so.Str:=Text;
    TThread.Synchronize(nil,so.MeasureExtent);
    Result:=so.ext;
  finally
  	so.Free;
  end;

(*Result.cx:=round(Result.cx*1200/96);
	Result.cy:=round(Result.cy*1200/96);*)
end;

procedure TlmfCanvas.DoGetTextSize (text:string; var w,h:integer);
var
	sz:TSize;
begin
	sz:=TextExtent(Text);
  w:=sz.cx;
  h:=sz.cy;
end;

procedure TlmfCanvas.Polyline(Points: PPoint; NumPts: Integer);
var
  item:TlmfPolyLine;
begin
  Changing;
  RequiredState([csHandleValid, csPenValid]);
  item:=TlmfPolyline.Create(Points,NumPts);
  item.fClip:=Self.ClipRect;
	fImage.fList.InsertComponent(item);
  Changed;
end;

procedure TlmfCanvas.Polygon(Points: PPoint; NumPts: Integer;
  Winding: boolean = False);
var
  item:TlmfPolygon;
begin
  if NumPts<=0 then exit;
  Changing;
  RequiredState([csHandleValid, csBrushValid, csPenValid]);
  item:=TlmfPolygon.Create(Points,NumPts);
  item.fClip:=Self.ClipRect;
	fImage.fList.InsertComponent(item);
  Changed;
end;

procedure TlmfCanvas.Ellipse (x1,y1,x2,y2:integer);
begin
	DoEllipse(Rect(x1,y1,x2,y2));
end;

// LMF list

procedure TlmfList.GetChildren(Proc: TGetChildProc; Root: TComponent);
var
	i:integer;
begin
	for i:=0 to ComponentCount-1 do
  begin
		Proc(Components[i]);
	end;
end;

function TlmfList.GetChildOwner: TComponent;
begin
	Result:=self;
end;



/// LMF object
constructor TlmfAnchor.Create(Ax,Ay:integer);
begin
  inherited Create(nil);
  fPos.X:=Ax;
  fPos.Y:=Ay;
end;

procedure TlmfMoveTo.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
  ACanvas.MoveTo(fImage.ScaleX(fPos.X),fImage.ScaleY(fPos.Y));
end;

procedure TlmfLineTo.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
  ACanvas.LineTo(fImage.ScaleX(fPos.X),fImage.ScaleY(fPos.Y));
end;

constructor TlmfLine.Create(x1,y1,x2,y2:integer);
begin
  inherited Create(x1,y1);
  fEndPos.X:=x2;
  fEndPos.Y:=y2;
end;


procedure TlmfLine.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
  ACanvas.Line(
    fImage.ScaleX(fPos.X),
    fImage.ScaleY(fPos.Y),
    fImage.ScaleX(fEndPos.X),
    fImage.ScaleY(fEndPos.Y));
end;


constructor TlmfText.Create(x,y:integer; const AText:string);
begin
  inherited Create(x,y);
  fText:=AText;
end;

procedure TlmfText.Action(fImage:TlmfImage;ACanvas:TCanvas);
var
	fnt:TFont;
  ofh:Hfont;
begin
(*	if (fRotation<>0) then
  begin
  	fnt:=CreateOrtFont(round(fImage.ky*fHeight),fRotation div 10,ACanvas.Font.PixelsPerInch);
    Acanvas.Font.Assign(fnt);
    Acanvas.Font.Name:='Arial';
	  {$message 'This is font-selection workaround'}
  	ofh:=SelectObject(ACanvas.Handle,fnt.Handle);
    ACanvas.TextOut(fImage.ScaleX(fPos.X),fImage.ScaleY(fPos.Y),fText);
    ofh:=SelectObject(ACanvas.Handle,ofh);
    fnt.Free;
  end
  else
  begin
	  ACanvas.Font.Height:=round(fImage.ky*fHeight);
  	ACanvas.TextOut(fImage.ScaleX(fPos.X),fImage.ScaleY(fPos.Y),fText);
  end;*)
  ACanvas.TextOut(fImage.ScaleX(fPos.X),fImage.ScaleY(fPos.Y),fText);
end;

procedure TlmfText.DefineProperties(Filer: TFiler);
begin
	inherited DefineProperties(Filer);
end;

// pixel mode
constructor TlmfColor.Create(x,y:integer; AColor:TfpColor);
begin
  inherited Create(x,y);
  fColor:=AColor;
end;

procedure TlmfColor.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
  ACanvas.Colors[
    fImage.ScaleX(fpos.x),
    fImage.ScaleY(fpos.y)]:=fColor;
end;

// cliprect
constructor TlmfClip.Create(AClip:TRect);
begin
  inherited Create(nil);
  fClip:=AClip;
end;

procedure TlmfClip.Action(fImage:TlmfImage;ACanvas:TCanvas);
var
	newClip:TRect;
begin
	// reset the clipping
  if (fClip.Left=0) and (fClip.Top=0) and (fClip.Right=MaxInt) and (fClip.Bottom=MaxInt) then
  begin
  	// this clip rect have not to scale
    ACanvas.Clipping:=false;
  	ACanvas.ClipRect:=fClip; // actually does clipping through virtualization
  	SelectClipRgn(ACanvas.Handle,0)
  end
  else
  begin
    newClip:=Rect(fImage.ScaleX(fClip.Left),fImage.ScaleY(fClip.Top),
      fImage.Scalex(fClip.Right),fImage.ScaleY(fClip.Bottom));

  	ACanvas.ClipRect:=newClip; // actually does nothing

    // this is real clipping
    lclintf.IntersectClipRect(ACanvas.Handle,
    	newClip.Left,newClip.Top,newClip.Right,newClip.Bottom);
  end;
end;

// rectangle
procedure TlmfRect.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
 // ACanvas.Brush.Style:=bsClear;
  ACanvas.Rectangle(fImage.ScaleX(fClip.Left),fImage.ScaleY(fClip.Top),
    fImage.Scalex(fClip.Right),fImage.ScaleY(fClip.Bottom));
end;

procedure TlmfFillRect.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
	ACanvas.FillRect(
		fImage.ScaleX(fClip.Left),fImage.ScaleY(fClip.Top),
  	fImage.Scalex(fClip.Right),fImage.ScaleY(fClip.Bottom));
end;

procedure TlmfEllipse.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
  ACanvas.Ellipse(
  	fImage.ScaleX(fClip.Left),
    fImage.ScaleY(fClip.Top),
    fImage.Scalex(fClip.Right),
    fImage.ScaleY(fClip.Bottom));
end;


constructor TlmfFont.Create(AnOwner:TComponent);
begin
	inherited Create(AnOwner);
	fFont:=TFont.Create;
end;

destructor TlmfFont.Destroy;
begin
	fFont.Free;
	inherited Destroy;
end;

procedure TlmfFont.Action(fImage:TlmfImage;ACanvas:TCanvas);
var
	AFont:TFont;
	rot,ht:integer;
  ofh:Hfont;
begin
	rot:=fRotation;//TRotFont(fFont).Rotation;

  Acanvas.Font.Assign(fFont);
	ht:=abs(round(fImage.ky*fHeight));
  if ht<=0 then ht:=1;
  Acanvas.Font.Height:=-ht;
  ACanvas.Font.Orientation:=rot;
end;

constructor TlmfBrush.Create(AnOwner:TComponent);
begin
	inherited Create(AnOwner);
	fBrush:=TBrush.Create;
end;

destructor TlmfBrush.Destroy;
begin
	fBrush.Free;
	inherited Destroy;
end;

procedure TlmfBrush.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
	ACanvas.Brush.Assign(fBrush);
end;


constructor TlmfPen.Create(AnOwner:TComponent);
begin
	inherited Create(AnOwner);
	fPen:=TPen.Create;
end;

destructor TlmfPen.Destroy;
begin
	fPen.Free;
	inherited Destroy;
end;

procedure TlmfPen.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
	ACanvas.Pen.Assign(fPen);
  ACanvas.Pen.Width:=round(fImage.ky*fPen.Width);
end;

constructor TlmfGraph.Create(AnOwner:TComponent);
begin
	inherited Create(AnOwner);
	fGraph:=TPicture.Create;
end;

destructor TlmfGraph.Destroy;
begin
	fGraph.Free;
	inherited Destroy;
end;

procedure TlmfGraph.Action(fImage:TlmfImage;ACanvas:TCanvas);
begin
	ACanvas.StretchDraw(
		(*Rect(
		fClip.Left,
		fClip.Top,
		fClip.Right,
		fClip.Bottom)*)
		Rect(
		fImage.ScaleX(fClip.Left),
		fImage.ScaleY(fClip.Top),
		fImage.ScaleX(fClip.Right),
		fImage.ScaleY(fClip.Bottom)),fGraph.Graphic);
end;

constructor TlmfPolyLine.Create(Points:PPoint;NumPts:integer);
begin
	inherited Create(nil);
	setlength(pts,numPts);
	system.Move(Points^,pts[0],NumPts*sizeof(pts[0]));
end;

destructor TlmfPolyLine.Destroy;
begin
	setlength(pts,0);
	inherited Destroy;
end;


procedure TlmfPolyLine.StorePoints(AStream:TStream);
var
	len:longint;
begin
	len:=length(pts);
  AStream.Write(len,sizeof(len));
  if len>0 then
  AStream.Write(pts[0],len*sizeof(pts[0]));

end;

procedure TlmfPolyLine.LoadPoints(AStream:TStream);
var
	len:longint;
begin
	setlength(pts,0);
  if AStream.Read(len,sizeof(len))=sizeof(len) then
  if len>0 then
  begin
  	setlength(pts,len);
	  AStream.Read(pts[0],len*sizeof(pts[0]));
  end;

end;

procedure TlmfPolyLine.DefineProperties(Afiler:TFiler);
begin
	inherited DefineProperties(AFiler);
  AFiler.DefineBinaryProperty('Points',LoadPoints,StorePoints,length(pts)>0);
end;


procedure TlmfPolyLine.Action(fImage:TlmfImage;ACanvas:TCanvas);
var
	i:longint;
	npts:array of TPoint;
begin
	setlength(npts,length(pts));
	for i:=0 to high(pts) do
	begin
		npts[i].x:=fImage.ScaleX(pts[i].x);
		npts[i].y:=fImage.ScaleY(pts[i].y);
	end;
	Acanvas.Polyline(npts);
end;


constructor TlmfPolygon.Create(Points:PPoint;NumPts:integer;Winding:boolean=false);
begin
	inherited Create(Points,NumPts);
	fWinding:=Winding;
end;

procedure TlmfPolygon.Action(fImage:TlmfImage;ACanvas:TCanvas);
var
	i:longint;
	npts:array of TPoint;
begin
	setlength(npts,length(pts));
	for i:=0 to high(pts) do
	begin
		npts[i].x:=fImage.ScaleX(pts[i].x);
		npts[i].y:=fImage.ScaleY(pts[i].y);
	end;
	ACanvas.Polygon(npts,fWinding,0,length(npts));
end;


initialization
	RegisterClasses([TlmfList,TlmfAnchor,TlmfMoveTo,TlmfLineTo,
		TlmfLine,TlmfText,TlmfColor,TlmfClip,TlmfRect,TlmfFont,TlmfBrush,TlmfPen,TlmfGraph,
    TlmfPolyLine,TlmfPolygon,
    TlmfEllipse]);
end.

lmf.pas (26,620 bytes)   

Anton Kavalenka

2010-04-22 18:36

reporter   ~0036921

Last edited: 2010-04-23 10:26

The new-one version with improved clipping support and thread-safe drawing,
which allow printing in non-main thread context.

Thread-synchronization magic around bitmap drawing is made because in LCL-gtk2 is not possible to assign bitmap without breaking current X-message flow.

Felipe Monteiro de Carvalho

2010-10-13 16:10

developer   ~0041778

Using the correct architecture is not optional. It is mandatory.

If you just implement the feature with a wrong architecture it will just be a head-ache when someone needs to implement it properly.

Please provide a new patch based in fpvectorial.

Anton Kavalenka

2010-10-14 15:28

reporter   ~0041809

Correct architecture is good, but there are few statements:

1. Due to fixed enumeration of fpvectorial records types - it is not extensible.

I can NEITHER add own type of records NOR extend existing ones.
Just as simple example consider a polyline with 10000 nodes (it is quite common for me).

3. Record class-dependent behavior (e.g.) to perform specific action for specific record class - i have to write big case (see fpvtocanvas.pas):

case rec.SegmentType of
st2DLine: ....
st2DBuzier: ...
....
100 branches
....
end;

It is not object-oriented approach.

Just for information the EMF (Enhanced windows metafile can contain about 100 types of records)
http://msdn.microsoft.com/en-us/library/dd145054(v=VS.85).aspx
They are different in size and in parameters.

SVG counts at least 6 basic shapes (http://apike.ca/prog_svg_shapes.html)

I understand that every complex shape can be converted to the set of vertexes or buziers. But shapes are common for all graphic subsystems, and sometimes it would be costly to guess during painting - if we have to paint these 4 points with rectangle, polyline, ellipse, roundrect. Or during metafile recording i have to break roundrect into 8 vertexes and 4 buziers?

Maybe good architecture can automatically break complex shape into primitives (like Shape.Decompile) to make possibility of drawing/writing on marginal platforms.

4. TPathSegment record store excessive 3D information even for very simple segments. So for 10000 segments we would get (6+1)*sizeof(double)*10000 = 560000 bytes (half of megabyte of useless zeros).

5. There is no world-transformation records either for vectors or text.
6. There is no color information records.
7. There is no pen, brush, rectangle, ellipse, pie type records.

There is nothing that makes fpvectorial usable in real life instead of learning vector graphics format.

The only positive solution - merge together TlmfImage and TvVectorialDocument.
E.g. replace TpathSegment record with TpathSegment object (not class), which have to be used as replacement of TlmfObject.
Objects can inherit properties. Objects can initialize itself. Objects can vary behavior.
So, please - make the package extensible - and I will use it.

Felipe Monteiro de Carvalho

2010-10-21 16:19

developer   ~0041974

Ok, so I suppose that I can make it class-based and store the data in a flexible storage.

What do you propose to hold the vertexes in a dynamic way? A double linked list? This will use an extra two pointers per vertex.

Anton Kavalenka

2010-10-21 18:10

reporter   ~0041979

I think dynamic array is preferable.
IMO there should be 2 different base vertex classes for 2D (x,y) and for 3d (x,y,z).
So the record object have to be parametrized in some way - to hold 2d or 3d points.

Felipe Monteiro de Carvalho

2010-10-21 19:44

developer   ~0041985

A dynamic array will be extremely slow at deleting and inserting segments.

Anton Kavalenka

2010-10-22 11:46

reporter   ~0041997

Why should I need delete vertex elements?

type
  TPointArray=array of Point2D;

constructor TRectanglePath.Create(const src:TPointArray);
begin
  topleft:=src[0];
  bottomright:=src[1];
end;

procedure TRectanglePath.Decompile(var result:TPointArray);
begin
  // rectangle has 4 vertexes
  // so convert topleft, bottomright vertex to
  // 4 real points
  setlength(result,4);
  // convert the points
end;

// or in other way
function TRectanglePath.Decompile:TSimplePath;
begin
 Result:=TSimplePath.Create([
  Point2D(topleft.x,topleft.y),
  Point2D(bottomright.x,topleft.y),
  Point2d(bottomright.x,bottomright.y),
  Point2d(topleft.x,bottomright.y)])
end;

destructor TRectanglePath.Free;
begin
  setlength(InternalStorage,0);
  inherited;
end;

IMO decompiling complex paths is just adding new vertexes at the end of list.

Felipe Monteiro de Carvalho

2010-10-22 15:09

developer   ~0042001

> Why should I need delete vertex elements?

For example, in a graphical vectorial image editor like Inkscape, one would use fpvectorial to hold the data and the user might add / remove vertexes.

Fpvectorial is a generic library for doing I/O of vectorial formats, but also for manipulating them, in all kinds of operations.

Anton Kavalenka

2010-10-22 16:23

reporter   ~0042002

Ok, implement it as you want, but please:
* do it efficient (in memory,CPU point of view) because number of objects is HUGE
* allow others encapsulate entities or subclass them

Felipe Monteiro de Carvalho

2010-10-23 10:44

developer   ~0042017

Ok, in rev16202 I rewrote the coded to use classes and I also made the list of segments a double linked list of objects.

As with every rewrite I'm sure it might have introduced some regressions and will need some perfection, but it should be enough that you can start working on it =) Patches are welcome.

Felipe Monteiro de Carvalho

2011-01-27 07:36

developer   ~0045504

Hello, do you still plan on working on this?

Now I am adding entities to fpvectorial, so it should be ready for you to start coding. I added so far circle, circulararc, ellipse.

Anton Kavalenka

2011-01-27 10:05

reporter   ~0045505

Yes, i have seen the improvements yesterday. I can not point the date exactly, but LMF rework is in my near plans.

Anton Kavalenka

2011-06-10 17:03

reporter   ~0049014

Last edited: 2011-06-10 18:00

@Felipe

It would be nice to have native fpvectorial document writer to Assign one document to another or just ta save in file to examine content.

Btw having enum as predefined format types makes impossible to add new formats. Maybe something like RegisterFormat('Format name',Writer,Reader) better?

1. There is no way to access Entities list, so cannot add mine subclassed entities.

2. There is no way to mark path as clipping path like PDF and other vector format do. E.g.
StartPath() (Add nodes) SetClipPath() // close the path an select it as clipping region.
Also ResetClipPath should exist.

Felipe Monteiro de Carvalho

2011-06-16 08:33

developer   ~0049146

> It would be nice to have native fpvectorial document writer to Assign one
> document to another or just ta save in file to examine content.

Could you explain more? I don't understand what you mean. You want an Assign function? A dump of the internal data for debugging purposes?

> Btw having enum as predefined format types makes impossible to add new
> formats. Maybe something like RegisterFormat('Format name',Writer,Reader)
> better?

Impossible? fpvectorial is free software and versioned in svn ... adding a new format requires what? 10 seconds + svn commit

> 1. There is no way to access Entities list, so cannot add mine subclassed
> entities.

If one could add random subclasses from outside then the readers/writers will not know how to handle them.

It seams as if you understand fpvectorial as a monolithic block which should not be modified. It isn't, it is free software. You can send patches to improve it instead of only trying to use it from the outside.

> 2. There is no way to mark path as clipping path like PDF and other vector
> format do. E.g.
> StartPath() (Add nodes) SetClipPath() // close the path an select it as
> clipping region.
> Also ResetClipPath should exist.

Ummmm, not all formats will support this, but it should be fine to add it ... I'll have to research more about this. Please open a new separate report for this. Also, how important is that for you? I don't see this being used very often. At the moment I was thinking of adding multi-page support, which might be more useful.

Anton Kavalenka

2011-06-16 19:33

reporter   ~0049157

Last edited: 2011-06-16 19:35

@Felipe

>Could you explain more? I don't understand what you mean. You want an Assign >function? A dump of the internal data for debugging purposes?

Yes I want an assign function to get a clone of the document.

>Impossible? fpvectorial is free software and versioned in svn ... adding a new >format requires what? 10 seconds + svn commit

I meant write a new format in another module and register it with global fpvectorial class list with its own name and extension. Without rewriting fpvectorial.

>If one could add random subclasses from outside then the readers/writers will >not know how to handle them.

I know how to handle entities which are subclassed from basic.
fpvectorial knows how to store entities,
it also knows how to use properties from base classes.
It is my problem how to handle extended properties.
That's the virtualization. That is why i've used object streaming.
Btw. reading/writing TObject instance is easy.

The path is also entity.
Separate list for paths, entities and texts makes nonsence from clipping, text-to path and other generic graphic effects.
Simple example 1: Which clip region entity should be applied for Text[0]?
Simple example 2: Why font selection is bound to text output (TvText)?
I want one font selection for next 1000 text lines.

>At the moment I was thinking of adding multi-page support, which might be more >useful.

Page in PDF is just brackets (start page/end page) surrounding document which can be represented via fpvectorial.
That is just another reason to implement Assign() method, and make an fpvectorial entity which is fpvectorial document itself.

Document containing inside several documents - is a multi-page document.

About clipping:
in PostScript setting cliprect is (from postscriptcanvas.pas):

  Self.Write('clipsave'); // save previous clipping state
  psDrawRect(FLazClipRect); // draw the path - for instance rectangle
  Write(FBuffer);
  Self.Write('clip'); // use the path as clip region

Restoring the clipping - Self.Write('cliprestore');
Initially clipping equal to page size.

I think other graphic formats behave in the same way.

Felipe Monteiro de Carvalho

2011-06-17 10:01

developer   ~0049172

> Yes I want an assign function to get a clone of the document.

Implemented:

    procedure Assign(ASource: TvVectorialDocument);
    procedure AssignTo(ADest: TvVectorialDocument);

> Separate list for paths, entities and texts makes
> nonsence from clipping, text-to path and other generic graphic effects.

I just made a large rework and in the new code all 3 types of entities were joined together. But my objective with that was not clipping, but rather Z-Order. With all elements in the same list the order in the list can be used as the Z-Order.

> I know how to handle entities which are subclassed from basic.
> fpvectorial knows how to store entities,
> it also knows how to use properties from base classes.
> It is my problem how to handle extended properties.

It was already possible to read the internal lists with these functions:

    function GetEntity(ANum: Cardinal): TvEntity;
    function GetEntityCount: Integer;

Now I added a function which allows you to add a generic entity:

    procedure AddEntity(AEntity: TvEntity);

Felipe Monteiro de Carvalho

2011-06-17 10:31

developer   ~0049173

> Page in PDF is just brackets (start page/end page) surrounding document
> which can be represented via fpvectorial.
> That is just another reason to implement Assign() method, and make
> an fpvectorial entity which is fpvectorial document itself.

I think that's not really a good idea, because the hierarchy is well defined. A document contains only pages. Pages contain only layers. Layers contain only entities. You can't put an entity directly in the document, for example, it is inside a layer which is inside a page.

The best solution would be to have two main classes one for a multi-page document and another for each page, but I don't want to make this huge backwards compatibility break, so I will simply add in the future some functions to set the current page and set the current layer.

About the clip rect, I'd prefer to receive a patch from you for that =)

Felipe Monteiro de Carvalho

2011-06-17 11:26

developer   ~0049175

And also, it would be good to receive something from you, a initial patch about the metafile implementation, so that we can advance on this area. thanks

Anton Kavalenka

2011-06-17 17:56

reporter   ~0049176

I started using fpvectorial document as storage for metafile records.
My metafile is functionally similar to windows metafile.
The difference that WMF use variable-sized records, LMF use TComponent-derived classes.
The canvas just records graphic primitives.

Now i stuck at the problems - I can not place graphic (TGraphic descendant) into storage, neither I can clip something.

Maybe I'm not understanding the concept,
but how the text can be separated from paths painted BEFORE it and AFTER it?

Text is also path and should be in the common list.

Corel clearly shows it converting text into a set of Buziers.
IMO it replaces single TText entity with set of TPath entities finished by clipping path.

Felipe Monteiro de Carvalho

2011-06-20 11:36

developer   ~0049224

> Now i stuck at the problems - I can not place graphic (TGraphic descendant)
> into storage,

The problem is that TGraphic is introduced in the LCL and FPVectorial cannot depend on the LCL ...

One solution is adding an entity which contains a TFPCustomImage. TLazIntfImage descends from that. Then you can convert the raster image into TLazIntfImage and store that. Graphics which contain multiple images like icons will be able to store only 1 image.

> neither I can clip something.

What is the use for that? Does SVG support it?

> but how the text can be separated from paths painted BEFORE it and AFTER it?

? I don't understand your question.

> Text is also path and should be in the common list.

Text is not a path, but it is in the common list since my latest update, why do you think it isn't?

> Corel clearly shows it converting text into a set of Buziers.
> IMO it replaces single TText entity with set of TPath entities finished by
> clipping path.

FPVectorial does not convert text into beziers, if anyone did the conversion, it was Corel itself. Which output format did you use?

Anton Kavalenka

2011-06-21 09:40

reporter   ~0049251

Thank you, the recent changes looks promising.

> The problem is that TGraphic is introduced in the LCL and FPVectorial cannot
> depend on the LCL ...

I do not want LCL dependence.
I want custom entities in common list with retained order.
First I make LMF work with custom entities,
then I'll try to decompose them into basic.

>> neither I can clip something.

>What is the use for that? Does SVG support it?
IMO all graphic formats support this.
At least the page bound.
PS and WMF fully supports clipping to paths.
CDR has PowerClip effect (what is decomposed into I dont know).

>? I don't understand your question.
Your recent changes solved the problem.
Text painted ABOVE the red rectangle.
Or Text painted BELOW the red rectangle.
Both cases depends of the Z-order.

>Text is not a path
Text can be easily converted into set of curves in place.
But I do not need it.
I mentioned this to explain why the order is important.

Marco van de Voort

2011-06-22 09:32

manager   ~0049288

I yesterday enabled building of FPvectorial in 2.5.1, this means it will be installed by default in 2.6.0

Felipe Monteiro de Carvalho

2011-06-22 12:39

developer   ~0049301

I added a new entity which can contain a raster image, but it isn't yet used by any input/output module:

  TvRasterImage = class(TvEntity)
  public
    RasterImage: TFPCustomImage;
    Top, Left, Width, Height: Double;
  end;

>>What is the use for that? Does SVG support it?
>IMO all graphic formats support this.

I searched a bit and svg supports this: http://www.w3.org/TR/SVG/masking.html#OverflowAndClipProperties

But I would prefer to receive a patch from you implementing this, rather then implementing myself. It should include at least svg write support and an example program to be added in the examples dir.

stocki

2013-05-03 18:27

reporter   ~0067400

Can someone please add the solution from Anton
http://bugs.freepascal.org/view.php?id=14333 [^]
to the regular FPC/Lazarus distribution?

Juha Manninen

2013-05-04 06:48

developer   ~0067407

Anton Kavalenka and Felipe, how did things advance?
Is there a new version based on fpvectorial that could be committed?
Your latest notes on this issue are almost 2 years ago.

Anton Kavalenka

2013-05-04 08:31

reporter   ~0067410

still not completeted

fpvectorial is feature-rich but when document is painted it requires constructs like

if AnEntity is TLineEntity then
   TlineEntity(AnEntity).RenderTo(....)
else
if AnEntity is TRectangleEntity then
   TRectangleEntity(AnEntity).RenderToInAnotherWay(....)

i.e. polymorphism is somewhat broken. I tried to build a parallel class hierarchy but get tired.

Will try again.

Bart Broersma

2014-02-15 13:26

developer   ~0073065

Not a showstopper, postponing.

Issue History

Date Modified Username Field Change
2009-08-12 18:46 Anton Kavalenka New Issue
2009-08-12 18:46 Anton Kavalenka File Added: lmftest.zip
2009-08-12 18:46 Anton Kavalenka Widgetset => GTK 2, Win32/Win64
2009-08-12 18:47 Anton Kavalenka Note Added: 0029801
2009-08-17 01:22 Felipe Monteiro de Carvalho Note Added: 0029917
2009-08-17 11:32 Anton Kavalenka Note Added: 0029920
2009-08-17 15:08 Vincent Snijders LazTarget => 1.0
2009-08-17 15:08 Vincent Snijders Assigned To => Marc Weustink
2009-08-17 15:08 Vincent Snijders Status new => assigned
2009-08-17 15:08 Vincent Snijders Target Version => 1.0.0
2009-08-22 18:03 Felipe Monteiro de Carvalho Note Added: 0030080
2009-11-13 16:58 Zeljan Rikalo Relationship added related to 0009263
2010-04-22 18:33 Anton Kavalenka File Added: lmf.pas
2010-04-22 18:36 Anton Kavalenka Note Added: 0036921
2010-04-22 18:43 Anton Kavalenka Note Edited: 0036921
2010-04-23 10:26 Anton Kavalenka Note Edited: 0036921
2010-10-13 16:09 Felipe Monteiro de Carvalho Assigned To Marc Weustink => Felipe Monteiro de Carvalho
2010-10-13 16:10 Felipe Monteiro de Carvalho Note Added: 0041778
2010-10-13 16:10 Felipe Monteiro de Carvalho Status assigned => feedback
2010-10-14 15:28 Anton Kavalenka Note Added: 0041809
2010-10-21 16:19 Felipe Monteiro de Carvalho Note Added: 0041974
2010-10-21 18:10 Anton Kavalenka Note Added: 0041979
2010-10-21 19:44 Felipe Monteiro de Carvalho Note Added: 0041985
2010-10-22 11:46 Anton Kavalenka Note Added: 0041997
2010-10-22 15:09 Felipe Monteiro de Carvalho Note Added: 0042001
2010-10-22 16:23 Anton Kavalenka Note Added: 0042002
2010-10-23 10:44 Felipe Monteiro de Carvalho Note Added: 0042017
2011-01-27 07:36 Felipe Monteiro de Carvalho Note Added: 0045504
2011-01-27 10:05 Anton Kavalenka Note Added: 0045505
2011-04-04 03:35 Paul Ishenin LazTarget 1.0 => 0.99.0
2011-04-04 03:38 Paul Ishenin Target Version 1.0.0 => 0.99.0
2011-06-10 17:03 Anton Kavalenka Note Added: 0049014
2011-06-10 17:07 Anton Kavalenka Note Edited: 0049014
2011-06-10 18:00 Anton Kavalenka Note Edited: 0049014
2011-06-16 08:33 Felipe Monteiro de Carvalho Note Added: 0049146
2011-06-16 19:33 Anton Kavalenka Note Added: 0049157
2011-06-16 19:35 Anton Kavalenka Note Edited: 0049157
2011-06-17 10:01 Felipe Monteiro de Carvalho Note Added: 0049172
2011-06-17 10:31 Felipe Monteiro de Carvalho Note Added: 0049173
2011-06-17 11:26 Felipe Monteiro de Carvalho Note Added: 0049175
2011-06-17 17:56 Anton Kavalenka Note Added: 0049176
2011-06-20 11:36 Felipe Monteiro de Carvalho Note Added: 0049224
2011-06-21 09:40 Anton Kavalenka Note Added: 0049251
2011-06-22 09:32 Marco van de Voort Note Added: 0049288
2011-06-22 12:39 Felipe Monteiro de Carvalho Note Added: 0049301
2011-09-28 13:30 Felipe Monteiro de Carvalho LazTarget 0.99.0 => 1.2
2012-03-06 06:11 Paul Ishenin Target Version 0.99.0 => 1.2.0
2013-05-03 18:27 stocki Note Added: 0067400
2013-05-04 06:48 Juha Manninen Note Added: 0067407
2013-05-04 08:31 Anton Kavalenka Note Added: 0067410
2013-05-04 08:31 Anton Kavalenka Status feedback => assigned
2014-02-15 13:26 Bart Broersma LazTarget 1.2 => 1.4
2014-02-15 13:26 Bart Broersma Note Added: 0073065
2014-02-15 13:26 Bart Broersma Target Version 1.2.0 => 1.4
2014-09-15 13:06 Felipe Monteiro de Carvalho LazTarget 1.4 => -
2014-09-15 13:06 Felipe Monteiro de Carvalho Target Version 1.4 =>
2014-10-02 08:33 Vincent Snijders Relationship added related to 0020276