View Issue Details

IDProjectCategoryView StatusLast Update
0015011LazarusPatchpublic2009-12-15 07:31
ReporterTsvetoslav Assigned ToJesus Reyes  
PrioritynormalSeverityminorReproducibilityN/A
Status closedResolutionfixed 
Platformi386OSwindows 
Product Version0.9.28.3 (SVN) 
Target Version0.9.30Fixed in Version0.9.29 (SVN) 
Summary0015011: LazReport Html export patch
DescriptionThe attached patch improves LazReport Html export by using CSS.
For backward compatibility, there is a global var HTMExportUsesCSS, which is false by default and only if it is set to true before the export the created html will use CSS.
TagsNo tags attached.
Fixed in Revision23089
LazTarget0.9.30
WidgetsetGTK 2, Win32/Win64
Attached Files

Activities

2009-11-06 15:48

 

lazreport_source.diff (18,701 bytes)   
Index: components/lazreport/source/lr_e_htm.pas
===================================================================
--- components/lazreport/source/lr_e_htm.pas	(revision 22463)
+++ components/lazreport/source/lr_e_htm.pas	(working copy)
@@ -1,11 +1,11 @@
 
 {*****************************************}
-{                                         }
+
 {             FastReport v2.3             }
 {            HTM export filter            }
-{                                         }
+
 {  Copyright (c) 1998-99 by Tzyganenko A. }
-{                                         }
+
 {*****************************************}
 
 unit LR_E_HTM;
@@ -13,59 +13,105 @@
 interface
 
 {$I lr_vers.inc}
+{$COPERATORS on}
 
 uses
   Classes, SysUtils, LResources,
-  Graphics,GraphType, Controls, Forms, Dialogs, LR_E_TXT,
-  LCLType,LCLIntf,LR_Class;
+  Graphics, GraphType, Controls, Forms, Dialogs, LR_E_TXT,
+  LCLType, LCLIntf, LR_Class;
 
 type
 
+  { TStyleDesc }
+  TStyleDesc = record
+    styleID: AnsiString;
+    styleInfo: AnsiString;
+  end;
+
   { TfrHTMExport }
 
   TfrHTMExport = class(TComponent)
   public
-    Constructor Create(aOwner : TComponent); override;
+    constructor Create(aOwner: TComponent); override;
   end;
 
+  { TfrHTMExportFilter }
+
   TfrHTMExportFilter = class(TfrTextExportFilter)
+  private
+    cssStyles: array of TStyleDesc;
+    styleStartLine: integer;
+    outputLines: TStringList;
+    function AddStyle(p: PfrTextRec): Integer;
+    function ColorToHex(c: TColor): AnsiString;
+    function StyleIndex(p: PfrTextRec; AddIfNotFound: boolean = true): Integer;
+    function TextStyleID(p: PfrTextRec): AnsiString;
+  protected
+    procedure AppendLine(const s: UTF8String);
+    procedure InsertLine(const s: UTF8String; position: Integer);
   public
     constructor Create(AStream: TStream); override;
     destructor Destroy; override;
     procedure OnEndPage; override;
+    procedure OnEndDoc; override;
   end;
 
+var
+  HTMExportUsesCSS: boolean = false;
 
 implementation
 
 uses LR_Const;
 
-
 constructor TfrHTMExportFilter.Create(AStream: TStream);
 var
-  s: String;
+  s: UTF8String;
 begin
   inherited Create(AStream);
-  s := '<HTML>'#13#10'<Body bgColor="#FFFFFF">'#13#10'<Table>'#13#10;
-  Stream.Write(s[1], Length(s));
+
+  outputLines:= TStringList.Create;
+  SetLength(cssStyles, 0);
+
+  s:= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'#10;
+  AppendLine(s);
+  s:= '<html>'#10 +
+      '<head>'#10 +
+      '<meta http-equiv="content-type" content="text/html; charset=UTF-8" />'#10 +
+      '<title>' + 'Report html export' + '</title>'#10;
+  AppendLine(s);
+  s:= '<!-- CSS section start -->'#10 +
+      '<style type="text/css">'#10;
+  AppendLine(s);
+  styleStartLine:= outputLines.Count;
+  s:= '</style>'#10 +
+      '<!-- CSS section end -->'#10'</head>'#10#10;
+  AppendLine(s);
+  s:= '<body bgColor="#FFFFFF">'#10;
+  AppendLine(s);
 end;
 
+
 destructor TfrHTMExportFilter.Destroy;
-var
-  s: String;
 begin
-  s := '</Table>'#13#10'</Body>'#13#10'</HTML>'#13#10;
-  Stream.Write(s[1], Length(s));
+  SetLength(cssStyles, 0);
+  outputLines.Free;
   inherited Destroy;
 end;
 
+
+{%REGION 'procedure TfrHTMExportFilter.OnEndPage' }
 procedure TfrHTMExportFilter.OnEndPage;
+const
+  xMult = 1.0;
 var
-  i, n: Integer;
+  i, j, n, cw, xp, xp2: integer;
   p: PfrTextRec;
-  s, s1, s2: String;
+  s, s1, s2, sp, sAlign, sStyle, sEmpCells, sColSpan: AnsiString;
+  section: TfrBandType;
+  hasInnerTable: boolean;
+  xPos: TStringList;
 
-  function GetHTMLFontSize(Size: Integer): String;
+  function GetHTMLFontSize(Size: integer): string;
   begin
     case Size of
       6, 7: Result := '1';
@@ -73,64 +119,289 @@
       14..17: Result := '4';
       18..23: Result := '5';
       24..35: Result := '6'
-    else
-      Result := '7';
+      else
+        Result := '7';
     end;
   end;
 
-  function GetHTMLFontStyle(Style: Integer): String;
+  function GetHTMLFontStyle(Style: integer): string;
   begin
     Result := '';
-    if (Style and $1) <> 0 then Result := '<i>';
-    if (Style and $2) <> 0 then Result := Result + '<b>';
-    if (Style and $4) <> 0 then Result := Result + '<u>';
+    if (Style and $1) <> 0 then
+      Result := '<i>';
+    if (Style and $2) <> 0 then
+      Result := Result + '<b>';
+    if (Style and $4) <> 0 then
+      Result := Result + '<u>';
   end;
 
+  function FormatCellText(const sIn: AnsiString): AnsiString;
+  var
+    c, m: Integer;
+  begin
+    Result:= '';
+    c:=1;
+    while (c<=Length(sIn)) and (sIn[c]=' ') do
+      inc(c);
+    dec(c);
+    for m:=1 to c do
+      Result:= Result + '&nbsp;';
+     Result:= Result + Copy(sIn, c+1, Length(sIn)-c);
+  end;
+
 begin
+  section := btNone;
+  hasInnerTable := False;
+
   n := Lines.Count - 1;
   while n >= 0 do
   begin
-    if Lines[n] <> nil then break;
+    if Lines[n] <> nil then
+      break;
     Dec(n);
   end;
 
+  xPos:= TStringList.Create;
+  xPos.Sorted:= true;
   for i := 0 to n do
   begin
     p := PfrTextRec(Lines[i]);
+    while p <> nil do
+    begin
+      s:= Format('%.5d', [Round(p^.X * xMult)]);
+      if xPos.IndexOf(s) < 0 then
+        xPos.Add(s);
+      s:= Format('%.5d', [Round((p^.X + p^.W) * xMult)]);
+      if xPos.IndexOf(s) < 0 then
+        xPos.Add(s);
+      p:= p^.Next;
+    end;
+  end;
+
+  for i := 0 to n do
+  begin
+    p := PfrTextRec(Lines[i]);
     s := '<tr>';
+    cw:= 0;
     while p <> nil do
     begin
-      s1 := ''; s2 := '';
+      if (p^.BandType <> section) then
+      begin
+        section := p^.BandType;
+        if hasInnerTable then
+        begin
+          s := '</table>'#10;
+          AppendLine(s);
+        end;
+        s := '<table align="center" width="90%" margin="1">'#10'<tr height="0">';
+        for j:=1 to xPos.Count do
+          s += '<td></td>';
+        s+= '</tr>'#10'<tr>';
+        cw:= 0;
+        hasInnerTable := True;
+      end;
+
+      s1:= '';
+      s2:= '';
+      sEmpCells:= '';
+      sColSpan:= '';
+      sAlign:= '';
+      sStyle:= '';
+
       if (p^.FontColor = clWhite) or (p^.FontColor = clNone) then
         p^.FontColor := clBlack;
-      if p^.FontColor <> clBlack then
+
+      if HTMExportUsesCSS then
       begin
-        s1 := IntToHex(p^.FontColor, 6);
-        s1 := 'Color="#' + Copy(s1, 5, 2) + Copy(s1, 3, 2) +
-          Copy(s1, 1, 2) + '"';
+        sStyle:= Format(' class="fs%d"', [StyleIndex(p, true)]);
+      end
+      else
+      begin
+        if p^.FontColor <> clBlack then
+          s1:= ' Color="' + ColorToHex(p^.FontColor) + '"';
+        // most reports is done with font size = 10..13 - treat it as default font
+        if not (p^.FontSize in [10..13]) then
+          s1 := s1 + ' Size=' + GetHTMLFontSize(p^.FontSize);
+        if p^.FontStyle <> 0 then
+          s2 := GetHTMLFontStyle(p^.FontStyle);
+        if s1 <> '' then
+          s1 := '<Font' + s1 + '>';
       end;
-// most reports is done with font size = 10..13 - treat it as default font
-      if not (p^.FontSize in [10..13]) then
-        s1 := s1 + ' Size=' + GetHTMLFontSize(p^.FontSize);
-      if p^.FontStyle <> 0 then
-        s2 := GetHTMLFontStyle(p^.FontStyle);
-      if s1 <> '' then s1 := '<Font ' + s1 + '>';
-      s := s + '<td>' + s1 + s2 + p^.Text + '</td>';
+
+      case p^.Alignment of
+        taRightJustify: sAlign:= ' align="right"';
+        taCenter:       sAlign:= ' align="center"';
+      end;
+
+      sp:= Format('%.5d', [Round(p^.X * xMult)]);
+      xp:= xPos.IndexOf(sp);
+      sp:= Format('%.5d', [Round((p^.X + p^.W) * xMult)]);
+      xPos.Find(sp, xp2);
+      if Assigned(p^.Next) then
+      begin
+        sp:= Format('%.5d', [Round(p^.Next^.X * xMult)]);
+        if xPos.IndexOf(sp)<xp2 then
+          xp2:= xPos.IndexOf(sp);
+      end;
+      if xp>cw then
+        if (xp-cw)>1 then
+          sEmpCells:= Format('<td colspan=%d></td>', [xp - cw])
+        else
+          sEmpCells:= '<td></td>';
+
+      if (xp2-xp)>1 then
+        sColSpan:= Format(' colspan=%d', [xp2 - xp]);
+      cw:= xp2;
+
+      s := Format('%s%s<td%s%s%s>%s%s%s</td>', [s, sEmpCells, sAlign, sStyle, sColSpan, s1, s2, FormatCellText(p^.Text)]);
       p := p^.Next;
     end;
-    s := s + '</tr>'#13#10;
-    Stream.Write(s[1], Length(s));
+    if hasInnerTable then
+      s:= s + '</tr>'#10
+    else
+      s:= '<br>'#10;
+    AppendLine(s);
   end;
+
+  xPos.Free;
+
+  if hasInnerTable then
+  begin
+    s := '</table>'#10;
+    AppendLine(s);
+  end;
 end;
+{%ENDREGION }
 
 
+function TfrHTMExportFilter.TextStyleID(p: PfrTextRec): AnsiString;
+var
+  x: Integer;
+begin
+  Result:= '(none)';
+  if p=nil then
+    exit;
+  Result := p^.FontName;
+  Result += LowerCase(IntToHex(p^.FontSize, 2) + IntToHex(p^.FontStyle, 2) + IntToHex(p^.FontColor, 8) +
+                      IntToHex(p^.FillColor, 8) + IntToHex(Integer(p^.Borders), 2));
+  for x:=1 to Length(Result) do
+    if not (Result[x] in ['$', '%', '&', '0'..'9', '@'..'z']) then
+      Result[x]:= '_';
+end;
+
+
+function TfrHTMExportFilter.StyleIndex(p: PfrTextRec; AddIfNotFound: boolean): integer;
+var
+  s: string;
+  x: integer;
+begin
+  Result:= -1;
+  s:= TextStyleID(p);
+  for x:=0 to High(cssStyles) do
+    if cssStyles[x].styleID = s then
+    begin
+      Result:= x;
+      break;
+    end;
+  if (Result<0) and AddIfNotFound then
+    Result:= AddStyle(p);
+end;
+
+
+function TfrHTMExportFilter.AddStyle(p: PfrTextRec): Integer;
+var
+  s: string;
+begin
+  Result:= Length(cssStyles);
+  SetLength(cssStyles, Result+1);
+  cssStyles[Result].styleID:= TextStyleID(p);
+  s:= '';
+  if Assigned(p) then
+  begin
+    // s += Format(' /* Cell Style "%s" */'#10, [cssStyles[Result].styleID]);
+    s += Format(' td.fs%d {'#10, [Result]);
+    s += Format('  font-family: "%s";'#10, [p^.FontName]);
+    s += Format('  font-size: %dpt;'#10, [p^.FontSize]);
+    if (p^.FontStyle and $1) <> 0 then
+      s += '  font-style: italic;'#10;
+    if (p^.FontStyle and $2) <> 0 then
+      s += '  font-weight: bold;'#10;
+    if (p^.FontStyle and $4) <> 0 then
+      s += '  text-decoration: underline;'#10;
+    if (p^.FontColor <> clNone) and (p^.FontColor <> clDefault) and (p^.FontColor <> clBlack) then
+      s += Format('  color: %s;'#10, [ColorToHex(p^.FontColor)]);
+    if (p^.FillColor <> clNone) and (p^.FillColor <> clDefault) and (p^.FillColor <> clWhite) then
+      s += Format('  background-color: %s;'#10, [ColorToHex(p^.FillColor)]);
+    if (p^.Borders <> []) then
+    begin
+      case p^.BorderStyle of
+        frsSolid:      s += '  border-style: solid;'#10;
+        frsDash:       s += '  border-style: dashed;'#10;
+        frsDot,
+        frsDashDot,
+        frsDashDotDot: s += '  border-style: dotted;'#10;
+        frsDouble:     s += '  border-style: double;'#10;
+      end;
+      if not (frbLeft in p^.Borders) then
+        s += '  border-left-style: none;'#10;
+      if not (frbTop in p^.Borders) then
+        s += '  border-top-style: none;'#10;
+      if not (frbRight in p^.Borders) then
+        s += '  border-right-style: none;'#10;
+      if not (frbBottom in p^.Borders) then
+        s += '  border-bottom-style: none;'#10;
+      s += Format('  border-width: %dpx;'#10, [p^.BorderWidth]);
+      s += Format('  border-color: %s;'#10, [ColorToHex(p^.BorderColor)]);
+    end;
+    s += Format(' } '#10#10, []);
+  end;
+  cssStyles[Result].styleInfo:= s;
+end;
+
+
+function TfrHTMExportFilter.ColorToHex(c: TColor): AnsiString;
+var
+  s: AnsiString;
+begin
+  s:= IntToHex(c, 8);
+  Result:= '#' + Copy(s, 7, 2) + Copy(s, 5, 2) + Copy(s, 3, 2);
+end;
+
+
+procedure TfrHTMExportFilter.AppendLine(const s: UTF8String);
+begin
+  outputLines.Add(s);
+end;
+
+
+procedure TfrHTMExportFilter.InsertLine(const s: UTF8String; position: Integer);
+begin
+  outputLines.Insert(position, s);
+end;
+
+
+procedure TfrHTMExportFilter.OnEndDoc;
+var
+  s: string;
+  x: Integer;
+begin
+  s := '</Body>'#10'</HTML>'#10;
+  AppendLine(s);
+  for x:=0 to High(cssStyles) do
+    InsertLine(cssStyles[x].StyleInfo, styleStartLine + x);
+  for x:= 0 to Pred(outputLines.Count) do
+    if Length(outputLines[x])>0 then
+      Stream.Write(outputLines[x][1], Length(outputLines[x]));
+end;
+
+
 { TfrHTMExport }
 
 constructor TfrHTMExport.Create(aOwner: TComponent);
 begin
   inherited Create(aOwner);
-  
   frRegisterExportFilter(TfrHTMExportFilter, sHTMFile + ' (*.htm)', '*.htm');
 end;
 
 end.
+
Index: components/lazreport/source/lr_class.pas
===================================================================
--- components/lazreport/source/lr_class.pas	(revision 22463)
+++ components/lazreport/source/lr_class.pas	(working copy)
@@ -1086,9 +1086,20 @@
   TfrTextRec = record
     Next: PfrTextRec;
     X: Integer;
-    Text: String[255];
+    W: Integer;
+    Text: String;
     FontName: String[32];
-    FontSize, FontStyle, FontColor, FontCharset, FillColor: Integer;
+    FontSize,
+    FontStyle,
+    FontColor,
+    FontCharset,
+    FillColor: Integer;
+    Borders: TfrFrameBorders;
+    BorderColor: TColor;
+    BorderStyle: TfrFrameStyle;
+    BorderWidth: Integer;
+    Alignment: TAlignment;
+    BandType: TfrBandType;
   end;
 
   TfrAddInObjectInfo = record
@@ -6880,10 +6891,11 @@
     while Stream.Position < Stream.Size do
     begin
       Stream.Read(b, 1);
+      s := '';
       if b = gtAddIn then
-        s := ReadString(Stream) else
-        s := '';
+        s := ReadString(Stream);
       t := frCreateObject(b, s);
+      t.Parent:= nil;
       t.StreamMode := smPrinting;
       t.LoadFromStream(Stream);
       t.ExportData;
Index: components/lazreport/source/lr_e_txt.pas
===================================================================
--- components/lazreport/source/lr_e_txt.pas	(revision 22463)
+++ components/lazreport/source/lr_e_txt.pas	(working copy)
@@ -1,11 +1,11 @@
 
 {*****************************************}
-{                                         }
-{             FastReport v2.3             }
-{           Text export filter            }
-{                                         }
+
+ {             FastReport v2.3             }
+ {           Text export filter            }
+
 {  Copyright (c) 1998-99 by Tzyganenko A. }
-{                                         }
+
 {*****************************************}
 
 unit LR_E_TXT;
@@ -15,9 +15,8 @@
 {$I lr_vers.inc}
 
 uses
-  Classes, SysUtils, LResources,
-  Graphics,GraphType, Controls, Forms, Dialogs,
-  LCLType,LCLIntf,LR_Class;
+  Classes, SysUtils, LResources, Graphics, GraphType, Controls, Forms, Dialogs,
+  LCLType, LCLIntf, LR_Class;
 
 type
 
@@ -25,7 +24,7 @@
 
   TfrTextExport = class(TComponent)
   public
-    Constructor Create(aOwner : TComponent); override;
+    constructor Create(aOwner: TComponent); override;
   end;
 
   TfrTextExportFilter = class(TfrExportFilter)
@@ -33,7 +32,7 @@
     constructor Create(AStream: TStream); override;
     procedure OnEndPage; override;
     procedure OnBeginPage; override;
-    procedure OnText(X, Y: Integer; const Text: String; View: TfrView); override;
+    procedure OnText(X, Y: integer; const Text: string; View: TfrView); override;
   end;
 
 
@@ -41,14 +40,13 @@
 
 uses LR_Utils, LR_Const;
 
-
 var
-  UsedFont: Integer = 16;
+  UsedFont: integer = 16;
 
 constructor TfrTextExportFilter.Create(AStream: TStream);
 var
-  s: String;
-  n: Integer;
+  s: string;
+  n: integer;
 begin
   inherited;
   s := InputBox(sFilter, sFilterParam, '16');
@@ -57,12 +55,13 @@
 
 procedure TfrTextExportFilter.OnEndPage;
 var
-  i, n, x, tc1: Integer;
+  i, n, x, tc1: integer;
   p: PfrTextRec;
-  s: String;
-  function Dup(Count: Integer): String;
+  s: string;
+
+  function Dup(Count: integer): string;
   var
-    i: Integer;
+    i: integer;
   begin
     Result := '';
     for i := 1 to Count do
@@ -73,21 +72,22 @@
   n := Lines.Count - 1;
   while n >= 0 do
   begin
-    if Lines[n] <> nil then break;
+    if Lines[n] <> nil then
+      break;
     Dec(n);
   end;
 
   for i := 0 to n do
   begin
-    s := '';
+    s   := '';
     tc1 := 0;
-    p := PfrTextRec(Lines[i]);
+    p   := PfrTextRec(Lines[i]);
     while p <> nil do
     begin
-      x := Round(p^.X / 6.5);
-      s := s + Dup(x - tc1) + p^.Text;
+      x   := Round(p^.X / 6.5);
+      s   := s + Dup(x - tc1) + p^.Text;
       tc1 := x + Length(p^.Text);
-      p := p^.Next;
+      p   := p^.Next;
     end;
     s := s + #13#10;
     Stream.Write(s[1], Length(s));
@@ -98,35 +98,45 @@
 
 procedure TfrTextExportFilter.OnBeginPage;
 var
-  i: Integer;
+  i: integer;
 begin
   ClearLines;
-  for i := 0 to 200 do Lines.Add(nil);
+  for i := 0 to 200 do
+    Lines.Add(nil);
 end;
 
-procedure TfrTextExportFilter.OnText(X, Y: Integer; const Text: String;
-  View: TfrView);
+procedure TfrTextExportFilter.OnText(X, Y: integer; const Text: string; View: TfrView);
 var
   p, p1, p2: PfrTextRec;
 begin
-  if View = nil then Exit;
+  if View = nil then
+    Exit;
   Y := Round(Y / UsedFont);
-  p1 := PfrTextRec(Lines[Y]);
+  p1:= PfrTextRec(Lines[Y]);
   GetMem(p, SizeOf(TfrTextRec));
   FillChar(p^, SizeOf(TfrTextRec), 0);
   p^.Next := nil;
-  p^.X := X;
+  p^.X    := Round(2 * View.X / UsedFont);
+  p^.W    := Round(2 * View.Width / UsedFont);
   p^.Text := Text;
+  p^.FillColor  := View.FillColor;
+  p^.Borders    := View.Frames;
+  p^.BorderColor:= View.FrameColor;
+  p^.BorderStyle:= View.FrameStyle;
+  p^.BorderWidth:= Round(View.FrameWidth);
+  if Assigned(View.Parent) then
+    p^.BandType:= View.Parent.Typ;
   if View is TfrMemoView then
     with View as TfrMemoView do
     begin
-      p^.FontName := Font.Name;
-      p^.FontSize := Font.Size;
-      p^.FontStyle := frGetFontStyle(Font.Style);
-      p^.FontColor := Font.Color;
+      p^.FontName    := Font.Name;
+      p^.FontSize    := Font.Size;
+      p^.FontStyle   := frGetFontStyle(Font.Style);
+      p^.FontColor   := Font.Color;
       p^.FontCharset := Font.Charset;
+      p^.Alignment   := Alignment;
     end;
-  p^.FillColor := View.FillColor;
+
   if p1 = nil then
     Lines[Y] := TObject(p)
   else
@@ -140,12 +150,12 @@
     if p2 <> p1 then
     begin
       p2^.Next := p;
-      p^.Next := p1;
+      p^.Next  := p1;
     end
     else
     begin
       Lines[Y] := TObject(p);
-      p^.Next := p1;
+      p^.Next  := p1;
     end;
   end;
 end;
@@ -156,8 +166,9 @@
 constructor TfrTextExport.Create(aOwner: TComponent);
 begin
   inherited Create(aOwner);
-  
+
   frRegisterExportFilter(TfrTextExportFilter, sTextFile + ' (*.txt)', '*.txt');
 end;
 
 end.
+
lazreport_source.diff (18,701 bytes)   

2009-11-26 20:43

 

lazreport_src.diff (21,768 bytes)   
Index: lr_e_htm.pas
===================================================================
--- lr_e_htm.pas	(revision 22796)
+++ lr_e_htm.pas	(working copy)
@@ -1,11 +1,11 @@
 
 {*****************************************}
-{                                         }
+
 {             FastReport v2.3             }
 {            HTM export filter            }
-{                                         }
+
 {  Copyright (c) 1998-99 by Tzyganenko A. }
-{                                         }
+
 {*****************************************}
 
 unit LR_E_HTM;
@@ -13,67 +13,104 @@
 interface
 
 {$I lr_vers.inc}
+{$COPERATORS on}
 
 uses
   Classes, SysUtils, LResources,
-  Graphics,GraphType, Controls, Forms, Dialogs, LR_E_TXT,
-  LCLType,LCLIntf,LR_Class;
+  Graphics, GraphType, Controls, Forms, Dialogs, LR_E_TXT,
+  LCLType, LCLIntf, LR_Class;
 
 type
 
+  { TStyleDesc }
+  TStyleDesc = record
+    styleID: AnsiString;
+    styleInfo: AnsiString;
+  end;
+
   { TfrHTMExport }
 
   TfrHTMExport = class(TComponent)
   public
-    Constructor Create(aOwner : TComponent); override;
+    constructor Create(aOwner: TComponent); override;
   end;
 
+  { TfrHTMExportFilter }
+
   TfrHTMExportFilter = class(TfrTextExportFilter)
+  private
+    cssStyles: array of TStyleDesc;
+    styleStartLine: integer;
+    outputLines: TStringList;
+    function AddStyle(p: PfrTextRec): Integer;
+    function ColorToHex(c: TColor): AnsiString;
+    function StyleIndex(p: PfrTextRec; AddIfNotFound: boolean = true): Integer;
+    function TextStyleID(p: PfrTextRec): AnsiString;
+  protected
+    procedure AppendLine(const s: UTF8String);
+    procedure InsertLine(const s: UTF8String; position: Integer);
   public
     constructor Create(AStream: TStream); override;
     destructor Destroy; override;
     procedure OnEndPage; override;
+    procedure OnEndDoc; override;
   end;
 
+var
+  HTMExportUsesCSS: boolean = false;
 
 implementation
 
 uses LR_Const;
 
-
 constructor TfrHTMExportFilter.Create(AStream: TStream);
 var
-  s: String;
+  s: UTF8String;
 begin
   inherited Create(AStream);
+  outputLines:= TStringList.Create;
+  SetLength(cssStyles, 0);
 
-  s :=  '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"' + LineEnding +
-        '    "http://www.w3.org/TR/html4/loose.dtd">' + LineEnding +
-        '<html><head>' + LineEnding +
-        '<meta name="generator" content="LazReport html exporter">' + LineEnding +
-        '<title>LazReport Exported Report</title>' + LineEnding +  // TODO: improve
-        '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' + LineEnding +
-        '</head><body><table>' + LineEnding;
-
-  Stream.Write(s[1], Length(s));
+  s:= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'#10;
+  AppendLine(s);
+  s:= '<html>'#10 +
+      '<head>'#10 +
+      '<meta http-equiv="content-type" content="text/html; charset=UTF-8" />'#10 +
+      '<title>' + 'Report html export' + '</title>'#10;
+  AppendLine(s);
+  s:= '<!-- CSS section start -->'#10 +
+      '<style type="text/css">'#10;
+  AppendLine(s);
+  styleStartLine:= outputLines.Count;
+  s:= '</style>'#10 +
+      '<!-- CSS section end -->'#10'</head>'#10#10;
+  AppendLine(s);
+  s:= '<body bgColor="#FFFFFF">'#10;
+  AppendLine(s);
 end;
 
+
 destructor TfrHTMExportFilter.Destroy;
-var
-  s: String;
 begin
-  s := '</table></body></html>' + LineEnding;
-  Stream.Write(s[1], Length(s));
+  SetLength(cssStyles, 0);
+  outputLines.Free;
   inherited Destroy;
 end;
 
+
+{%REGION 'procedure TfrHTMExportFilter.OnEndPage' }
 procedure TfrHTMExportFilter.OnEndPage;
+const
+  xMult = 1.0;
 var
-  i, n: Integer;
+  i, j, n, cw, xp, xp2: integer;
   p: PfrTextRec;
-  s, s1, s2, s3: String;
+  s, s1, s2, sp, sAlign, sStyle, sEmpCells, sColSpan: AnsiString;
+  section: TfrBandType;
+  hasInnerTable: boolean;
+  xPos: TStringList;
 
-  function GetHTMLFontSize(Size: Integer): String;
+  function GetHTMLFontSize(Size: integer): string;
   begin
     case Size of
       6, 7: Result := '1';
@@ -81,79 +118,289 @@
       14..17: Result := '4';
       18..23: Result := '5';
       24..35: Result := '6'
-    else
-      Result := '7';
+      else
+        Result := '7';
     end;
   end;
 
-  function GetHTMLFontStyle(Style: Integer): String;
+  function GetHTMLFontStyle(Style: integer): string;
   begin
     Result := '';
-    if (Style and $1) <> 0 then Result := '<i>';
-    if (Style and $2) <> 0 then Result := Result + '<b>';
-    if (Style and $4) <> 0 then Result := Result + '<u>';
+    if (Style and $1) <> 0 then
+      Result := '<i>';
+    if (Style and $2) <> 0 then
+      Result := Result + '<b>';
+    if (Style and $4) <> 0 then
+      Result := Result + '<u>';
   end;
 
-  function GetEndHTMLFontStyle(Style: Integer): String;
+  function FormatCellText(const sIn: AnsiString): AnsiString;
+  var
+    c, m: Integer;
   begin
-    Result := '';
-    if (Style and $4) <> 0 then Result := '</u>';
-    if (Style and $2) <> 0 then Result := Result + '</b>';
-    if (Style and $1) <> 0 then Result := Result + '</i>';
+    Result:= '';
+    c:=1;
+    while (c<=Length(sIn)) and (sIn[c]=' ') do
+      inc(c);
+    dec(c);
+    for m:=1 to c do
+      Result:= Result + '&nbsp;';
+     Result:= Result + Copy(sIn, c+1, Length(sIn)-c);
   end;
 
 begin
+  section := btNone;
+  hasInnerTable := False;
+
   n := Lines.Count - 1;
   while n >= 0 do
   begin
-    if Lines[n] <> nil then break;
+    if Lines[n] <> nil then
+      break;
     Dec(n);
   end;
 
+  xPos:= TStringList.Create;
+  xPos.Sorted:= true;
   for i := 0 to n do
   begin
     p := PfrTextRec(Lines[i]);
-    s := '';
     while p <> nil do
     begin
-      s1 := ''; s2 := ''; s3 := '';
+      s:= Format('%.5d', [Round(p^.X * xMult)]);
+      if xPos.IndexOf(s) < 0 then
+        xPos.Add(s);
+      s:= Format('%.5d', [Round((p^.X + p^.W) * xMult)]);
+      if xPos.IndexOf(s) < 0 then
+        xPos.Add(s);
+      p:= p^.Next;
+    end;
+  end;
+
+  for i := 0 to n do
+  begin
+    p := PfrTextRec(Lines[i]);
+    s := '<tr>';
+    cw:= 0;
+    while p <> nil do
+    begin
+      if (p^.BandType <> section) then
+      begin
+        section := p^.BandType;
+        if hasInnerTable then
+        begin
+          s := '</table>'#10;
+          AppendLine(s);
+        end;
+        s := '<table align="center" width="90%" margin="1">'#10'<tr height="0">';
+        for j:=1 to xPos.Count do
+          s += '<td></td>';
+        s+= '</tr>'#10'<tr>';
+        cw:= 0;
+        hasInnerTable := True;
+      end;
+
+      s1:= '';
+      s2:= '';
+      sEmpCells:= '';
+      sColSpan:= '';
+      sAlign:= '';
+      sStyle:= '';
+
       if (p^.FontColor = clWhite) or (p^.FontColor = clNone) then
         p^.FontColor := clBlack;
-      if p^.FontColor <> clBlack then
+
+      if HTMExportUsesCSS then
       begin
-        s1 := IntToHex(p^.FontColor, 6);
-        s1 := 'Color="#' + Copy(s1, 5, 2) + Copy(s1, 3, 2) +
-          Copy(s1, 1, 2) + '"';
+        sStyle:= Format(' class="fs%d"', [StyleIndex(p, true)]);
+      end
+      else
+      begin
+        if p^.FontColor <> clBlack then
+          s1:= ' Color="' + ColorToHex(p^.FontColor) + '"';
+        // most reports is done with font size = 10..13 - treat it as default font
+        if not (p^.FontSize in [10..13]) then
+          s1 := s1 + ' Size=' + GetHTMLFontSize(p^.FontSize);
+        if p^.FontStyle <> 0 then
+          s2 := GetHTMLFontStyle(p^.FontStyle);
+        if s1 <> '' then
+          s1 := '<Font' + s1 + '>';
       end;
-// most reports is done with font size = 10..13 - treat it as default font
-      if not (p^.FontSize in [10..13]) then
-        s1 := s1 + ' Size=' + GetHTMLFontSize(p^.FontSize);
-      if p^.FontStyle <> 0 then
+
+      case p^.Alignment of
+        taRightJustify: sAlign:= ' align="right"';
+        taCenter:       sAlign:= ' align="center"';
+      end;
+
+      sp:= Format('%.5d', [Round(p^.X * xMult)]);
+      xp:= xPos.IndexOf(sp);
+      sp:= Format('%.5d', [Round((p^.X + p^.W) * xMult)]);
+      xPos.Find(sp, xp2);
+      if Assigned(p^.Next) then
       begin
-        s2 := GetHTMLFontStyle(p^.FontStyle);
-        s3 := GetEndHTMLFontStyle(p^.FontStyle);
+        sp:= Format('%.5d', [Round(p^.Next^.X * xMult)]);
+        if xPos.IndexOf(sp)<xp2 then
+          xp2:= xPos.IndexOf(sp);
       end;
-      if s1 <> '' then s1 := '<Font ' + s1 + '>';
-      s := s + '<td>' + s1 + s2 + p^.Text + s3;
-      if s1 <> '' then s := s + '</Font>';
-      s := s + '</td>';
+      if xp>cw then
+        if (xp-cw)>1 then
+          sEmpCells:= Format('<td colspan=%d></td>', [xp - cw])
+        else
+          sEmpCells:= '<td></td>';
+
+      if (xp2-xp)>1 then
+        sColSpan:= Format(' colspan=%d', [xp2 - xp]);
+      cw:= xp2;
+
+      s := Format('%s%s<td%s%s%s>%s%s%s</td>', [s, sEmpCells, sAlign, sStyle, sColSpan, s1, s2, FormatCellText(p^.Text)]);
       p := p^.Next;
     end;
-    if s='' then
-      s := '<td></td>';
-    s := '<tr>' + s + '</tr>' + LineEnding;
-    Stream.Write(s[1], Length(s));
+    if hasInnerTable then
+      s:= s + '</tr>'#10
+    else
+      s:= '<br>'#10;
+    AppendLine(s);
   end;
+
+  xPos.Free;
+
+  if hasInnerTable then
+  begin
+    s := '</table>'#10;
+    AppendLine(s);
+  end;
 end;
+{%ENDREGION }
 
 
+function TfrHTMExportFilter.TextStyleID(p: PfrTextRec): AnsiString;
+var
+  x: Integer;
+begin
+  Result:= '(none)';
+  if p=nil then
+    exit;
+  Result := p^.FontName;
+  Result += LowerCase(IntToHex(p^.FontSize, 2) + IntToHex(p^.FontStyle, 2) + IntToHex(p^.FontColor, 8) +
+                      IntToHex(p^.FillColor, 8) + IntToHex(Integer(p^.Borders), 2));
+  for x:=1 to Length(Result) do
+    if not (Result[x] in ['$', '%', '&', '0'..'9', '@'..'z']) then
+      Result[x]:= '_';
+end;
+
+
+function TfrHTMExportFilter.StyleIndex(p: PfrTextRec; AddIfNotFound: boolean): integer;
+var
+  s: string;
+  x: integer;
+begin
+  Result:= -1;
+  s:= TextStyleID(p);
+  for x:=0 to High(cssStyles) do
+    if cssStyles[x].styleID = s then
+    begin
+      Result:= x;
+      break;
+    end;
+  if (Result<0) and AddIfNotFound then
+    Result:= AddStyle(p);
+end;
+
+
+function TfrHTMExportFilter.AddStyle(p: PfrTextRec): Integer;
+var
+  s: string;
+begin
+  Result:= Length(cssStyles);
+  SetLength(cssStyles, Result+1);
+  cssStyles[Result].styleID:= TextStyleID(p);
+  s:= '';
+  if Assigned(p) then
+  begin
+    // s += Format(' /* Cell Style "%s" */'#10, [cssStyles[Result].styleID]);
+    s += Format(' td.fs%d {'#10, [Result]);
+    s += Format('  font-family: "%s";'#10, [p^.FontName]);
+    s += Format('  font-size: %dpt;'#10, [p^.FontSize]);
+    if (p^.FontStyle and $1) <> 0 then
+      s += '  font-style: italic;'#10;
+    if (p^.FontStyle and $2) <> 0 then
+      s += '  font-weight: bold;'#10;
+    if (p^.FontStyle and $4) <> 0 then
+      s += '  text-decoration: underline;'#10;
+    if (p^.FontColor <> clNone) and (p^.FontColor <> clDefault) and (p^.FontColor <> clBlack) then
+      s += Format('  color: %s;'#10, [ColorToHex(p^.FontColor)]);
+    if (p^.FillColor <> clNone) and (p^.FillColor <> clDefault) and (p^.FillColor <> clWhite) then
+      s += Format('  background-color: %s;'#10, [ColorToHex(p^.FillColor)]);
+    if (p^.Borders <> []) then
+    begin
+      case p^.BorderStyle of
+        frsSolid:      s += '  border-style: solid;'#10;
+        frsDash:       s += '  border-style: dashed;'#10;
+        frsDot,
+        frsDashDot,
+        frsDashDotDot: s += '  border-style: dotted;'#10;
+        frsDouble:     s += '  border-style: double;'#10;
+      end;
+      if not (frbLeft in p^.Borders) then
+        s += '  border-left-style: none;'#10;
+      if not (frbTop in p^.Borders) then
+        s += '  border-top-style: none;'#10;
+      if not (frbRight in p^.Borders) then
+        s += '  border-right-style: none;'#10;
+      if not (frbBottom in p^.Borders) then
+        s += '  border-bottom-style: none;'#10;
+      s += Format('  border-width: %dpx;'#10, [p^.BorderWidth]);
+      s += Format('  border-color: %s;'#10, [ColorToHex(p^.BorderColor)]);
+    end;
+    s += Format(' } '#10#10, []);
+  end;
+  cssStyles[Result].styleInfo:= s;
+end;
+
+
+function TfrHTMExportFilter.ColorToHex(c: TColor): AnsiString;
+var
+  s: AnsiString;
+begin
+  s:= IntToHex(c, 8);
+  Result:= '#' + Copy(s, 7, 2) + Copy(s, 5, 2) + Copy(s, 3, 2);
+end;
+
+
+procedure TfrHTMExportFilter.AppendLine(const s: UTF8String);
+begin
+  outputLines.Add(s);
+end;
+
+
+procedure TfrHTMExportFilter.InsertLine(const s: UTF8String; position: Integer);
+begin
+  outputLines.Insert(position, s);
+end;
+
+
+procedure TfrHTMExportFilter.OnEndDoc;
+var
+  s: string;
+  x: Integer;
+begin
+  s := '</Body>'#10'</HTML>'#10;
+  AppendLine(s);
+  for x:=0 to High(cssStyles) do
+    InsertLine(cssStyles[x].StyleInfo, styleStartLine + x);
+  for x:= 0 to Pred(outputLines.Count) do
+    if Length(outputLines[x])>0 then
+      Stream.Write(outputLines[x][1], Length(outputLines[x]));
+end;
+
+
 { TfrHTMExport }
 
 constructor TfrHTMExport.Create(aOwner: TComponent);
 begin
   inherited Create(aOwner);
-  
   frRegisterExportFilter(TfrHTMExportFilter, sHTMFile + ' (*.htm)', '*.htm');
 end;
 
 end.
+
Index: lr_class.pas
===================================================================
--- lr_class.pas	(revision 22796)
+++ lr_class.pas	(working copy)
@@ -1086,9 +1086,20 @@
   TfrTextRec = record
     Next: PfrTextRec;
     X: Integer;
-    Text: String[255];
+    W: Integer;
+    Text: String;
     FontName: String[32];
-    FontSize, FontStyle, FontColor, FontCharset, FillColor: Integer;
+    FontSize,
+    FontStyle,
+    FontColor,
+    FontCharset,
+    FillColor: Integer;
+    Borders: TfrFrameBorders;
+    BorderColor: TColor;
+    BorderStyle: TfrFrameStyle;
+    BorderWidth: Integer;
+    Alignment: TAlignment;
+    BandType: TfrBandType;
   end;
 
   TfrAddInObjectInfo = record
@@ -6880,10 +6891,11 @@
     while Stream.Position < Stream.Size do
     begin
       Stream.Read(b, 1);
+      s := '';
       if b = gtAddIn then
-        s := ReadString(Stream) else
-        s := '';
+        s := ReadString(Stream);
       t := frCreateObject(b, s);
+      t.Parent:= nil;
       t.StreamMode := smPrinting;
       t.LoadFromStream(Stream);
       t.ExportData;
@@ -8260,18 +8272,22 @@
   CurReport := Self;
   MasterReport := Self;
   SavedAllPages := EMFPages.Count;
-  with frProgressForm do
-  begin
-    s := sReportPreparing;
-    if Title = '' then
-      Caption := s
-    else
-      Caption := s + ' - ' + Title;
-    FirstCaption := sPagePreparing;
-    Label1.Caption := FirstCaption + '  1';
-    OnBeforeModal := @ExportBeforeModal;
-    Show_Modal(Self);
-  end;
+  if FShowProgress then begin
+    with frProgressForm do
+    begin
+      s := sReportPreparing;
+      if Title = '' then
+        Caption := s
+      else
+        Caption := s + ' - ' + Title;
+      FirstCaption := sPagePreparing;
+      Label1.Caption := FirstCaption + '  1';
+      OnBeforeModal := @ExportBeforeModal;
+      Show_Modal(Self);
+    end;
+  end else begin		//KGB
+    ExportBeforeModal(nil);	//KGB
+  end;				//KGB
 
   FreeAndNil(FCurrentFilter);
   ExportStream.Free;
@@ -8432,6 +8448,7 @@
   else
   begin
     p := TfrPreviewForm.Create(nil);
+    p.BorderIcons:=p.BorderIcons - [biMinimize]; // KGB
     {$IFDEF DebugLR}
     DebugLn('1 TfrPreviewForm.visible=',BooLToStr(p.Visible));
     {$ENDIF}
@@ -9555,10 +9572,12 @@
 var
   i: Integer;
 begin
+  {
   SBmp.Free;
   TempBmp.Free;
+  }
   SMemo.Free;
-  frProgressForm.Free;
+  // frProgressForm.Free;
   for i := 0 to frFunctionsCount - 1 do
     frFunctions[i].FunctionLibrary.Free;
   frParser.Free;
Index: lr_e_txt.pas
===================================================================
--- lr_e_txt.pas	(revision 22796)
+++ lr_e_txt.pas	(working copy)
@@ -1,11 +1,11 @@
 
 {*****************************************}
-{                                         }
-{             FastReport v2.3             }
-{           Text export filter            }
-{                                         }
+
+ {             FastReport v2.3             }
+ {           Text export filter            }
+
 {  Copyright (c) 1998-99 by Tzyganenko A. }
-{                                         }
+
 {*****************************************}
 
 unit LR_E_TXT;
@@ -15,9 +15,8 @@
 {$I lr_vers.inc}
 
 uses
-  Classes, SysUtils, LResources,
-  Graphics,GraphType, Controls, Forms, Dialogs,
-  LCLType,LCLIntf,LR_Class;
+  Classes, SysUtils, LResources, Graphics, GraphType, Controls, Forms, Dialogs,
+  LCLType, LCLIntf, LR_Class;
 
 type
 
@@ -25,7 +24,7 @@
 
   TfrTextExport = class(TComponent)
   public
-    Constructor Create(aOwner : TComponent); override;
+    constructor Create(aOwner: TComponent); override;
   end;
 
   TfrTextExportFilter = class(TfrExportFilter)
@@ -33,7 +32,7 @@
     constructor Create(AStream: TStream); override;
     procedure OnEndPage; override;
     procedure OnBeginPage; override;
-    procedure OnText(X, Y: Integer; const Text: String; View: TfrView); override;
+    procedure OnText(X, Y: integer; const Text: string; View: TfrView); override;
   end;
 
 
@@ -41,28 +40,30 @@
 
 uses LR_Utils, LR_Const;
 
-
 var
-  UsedFont: Integer = 16;
+  UsedFont: integer = 10;
 
 constructor TfrTextExportFilter.Create(AStream: TStream);
 var
-  s: String;
-  n: Integer;
+  s: string;
+  n: integer;
 begin
   inherited;
-  s := InputBox(sFilter, sFilterParam, '16');
-  Val(s, UsedFont, n);
+  {..$IFNDEF LCLnogui}
+  // s := InputBox(sFilter, sFilterParam, '16');
+  // Val(s, UsedFont, n);
+  {..$ENDIF}
 end;
 
 procedure TfrTextExportFilter.OnEndPage;
 var
-  i, n, x, tc1: Integer;
+  i, n, x, tc1: integer;
   p: PfrTextRec;
-  s: String;
-  function Dup(Count: Integer): String;
+  s: string;
+
+  function Dup(Count: integer): string;
   var
-    i: Integer;
+    i: integer;
   begin
     Result := '';
     for i := 1 to Count do
@@ -73,21 +74,22 @@
   n := Lines.Count - 1;
   while n >= 0 do
   begin
-    if Lines[n] <> nil then break;
+    if Lines[n] <> nil then
+      break;
     Dec(n);
   end;
 
   for i := 0 to n do
   begin
-    s := '';
+    s   := '';
     tc1 := 0;
-    p := PfrTextRec(Lines[i]);
+    p   := PfrTextRec(Lines[i]);
     while p <> nil do
     begin
-      x := Round(p^.X / 6.5);
-      s := s + Dup(x - tc1) + p^.Text;
+      x   := Round(p^.X / 6.5);
+      s   := s + Dup(x - tc1) + p^.Text;
       tc1 := x + Length(p^.Text);
-      p := p^.Next;
+      p   := p^.Next;
     end;
     s := s + LineEnding;
     Stream.Write(s[1], Length(s));
@@ -98,41 +100,51 @@
 
 procedure TfrTextExportFilter.OnBeginPage;
 var
-  i: Integer;
+  i: integer;
 begin
   ClearLines;
-  for i := 0 to 200 do Lines.Add(nil);
+  for i := 0 to 200 do
+    Lines.Add(nil);
 end;
 
-procedure TfrTextExportFilter.OnText(X, Y: Integer; const Text: String;
-  View: TfrView);
+procedure TfrTextExportFilter.OnText(X, Y: integer; const Text: string; View: TfrView);
 var
   p, p1, p2: PfrTextRec;
 begin
-  if View = nil then Exit;
+  if View = nil then
+    Exit;
   Y := Round(Y / UsedFont);
-  p1 := PfrTextRec(Lines[Y]);
+  p1:= PfrTextRec(Lines[Y]);
   GetMem(p, SizeOf(TfrTextRec));
   FillChar(p^, SizeOf(TfrTextRec), 0);
   p^.Next := nil;
-  p^.X := X;
+  p^.X    := Round(View.X / UsedFont);
+  p^.W    := Round(View.Width / UsedFont);
   p^.Text := Text;
+  p^.FillColor  := View.FillColor;
+  p^.Borders    := View.Frames;
+  p^.BorderColor:= View.FrameColor;
+  p^.BorderStyle:= View.FrameStyle;
+  p^.BorderWidth:= Round(View.FrameWidth);
+  if Assigned(View.Parent) then
+    p^.BandType:= View.Parent.Typ;
   if View is TfrMemoView then
     with View as TfrMemoView do
     begin
-      p^.FontName := Font.Name;
-      p^.FontSize := Font.Size;
-      p^.FontStyle := frGetFontStyle(Font.Style);
-      p^.FontColor := Font.Color;
+      p^.FontName    := Font.Name;
+      p^.FontSize    := Font.Size;
+      p^.FontStyle   := frGetFontStyle(Font.Style);
+      p^.FontColor   := Font.Color;
       p^.FontCharset := Font.Charset;
+      p^.Alignment   := Alignment;
     end;
-  p^.FillColor := View.FillColor;
+
   if p1 = nil then
     Lines[Y] := TObject(p)
   else
   begin
     p2 := p1;
-    while (p1 <> nil) and (p1^.X < p^.X) do
+    while (p1 <> nil) and (p1^.X <= p^.X) do
     begin
       p2 := p1;
       p1 := p1^.Next;
@@ -140,12 +152,12 @@
     if p2 <> p1 then
     begin
       p2^.Next := p;
-      p^.Next := p1;
+      p^.Next  := p1;
     end
     else
     begin
       Lines[Y] := TObject(p);
-      p^.Next := p1;
+      p^.Next  := p1;
     end;
   end;
 end;
@@ -156,8 +168,9 @@
 constructor TfrTextExport.Create(aOwner: TComponent);
 begin
   inherited Create(aOwner);
-  
+
   frRegisterExportFilter(TfrTextExportFilter, sTextFile + ' (*.txt)', '*.txt');
 end;
 
 end.
+
lazreport_src.diff (21,768 bytes)   

Tsvetoslav

2009-11-26 20:44

reporter   ~0032508

Updated patch as of November 26th

Jesus Reyes

2009-12-11 08:25

developer   ~0032931

Last edited: 2009-12-11 08:28

I have been working in LazReport export facilities lately, it was really inflexible. The text exporter which is the ancestor for CSV and HTML exporters, is based in the concept of "UsedFont" which I deduce was designed to make a close text representation of a graphical report.

The problem is that the value of UsedFont has no clear meaning. I think you have arrived at the same conclusion because in your patch you have fixed the value to 10 and avoided to show the user a dialog asking the UsedFont value. Indeed, in my tests a 10-12 value produced the best results. This schema really does a very bad job for CSV exports which have a very defined structure.

Now I'm looking at the HTML exporter, I have the following problems/questions which stop me to apply you patch as is.

1) I think a WYSIWYG html output is certainly possible but I prefer to have an accurate report that is close enough to graphical report even when it's not WYSIWYG. What kind of report do your patch tries to do? if needed I think we can have even both kinds of reports, controlled by some exporter parameters.

2) Before my changes on lazreport exporters, I tried your patch and It didn't look very similar to graphical report, I don't know if it was that the "UsedFont" factor which played a role in my report or some other thing (like probably that some objects I used had been word break or word wrapped). Maybe it was doing ok for you, could you attach a screen shot of graphical report and a generated html sample? (you can send them to me directly if you wish)

3) After my patch of r22755, on November 25, the html exporter produced 'Valid HTML 4.01 Transitional' output, after trying your second patch, the generated output was supposed to be 'XHTML 1.0 Transitional' but it didn't pass the validator. Was this really tested? if yes then probably I had some mixed code over there. If possible I don't want to loose that validation feature, either HTML 4.01 transitional, XHTML 1.0 Transitional or whatever.

2009-12-11 15:24

 

report_design.png (40,757 bytes)   
report_design.png (40,757 bytes)   

2009-12-11 15:24

 

report_preview.png (69,752 bytes)   
report_preview.png (69,752 bytes)   

2009-12-11 15:24

 

report_html_export_no_css.png (77,212 bytes)   
report_html_export_no_css.png (77,212 bytes)   

2009-12-11 15:24

 

report_html_export_with_css.png (74,000 bytes)   
report_html_export_with_css.png (74,000 bytes)   

2009-12-11 15:26

 

lazreport_20091211.diff (18,908 bytes)   
Index: lr_e_htm.pas
===================================================================
--- lr_e_htm.pas	(revision 23082)
+++ lr_e_htm.pas	(working copy)
@@ -1,11 +1,11 @@
 
 {*****************************************}
-{                                         }
+
 {             FastReport v2.3             }
 {            HTM export filter            }
-{                                         }
+
 {  Copyright (c) 1998-99 by Tzyganenko A. }
-{                                         }
+
 {*****************************************}
 
 unit LR_E_HTM;
@@ -13,67 +13,107 @@
 interface
 
 {$I lr_vers.inc}
+{$COPERATORS on}
 
 uses
   Classes, SysUtils, LResources,
-  Graphics,GraphType, Controls, Forms, Dialogs, LR_E_TXT,
-  LCLType,LCLIntf,LR_Class;
+  Graphics, GraphType, Controls, Forms, Dialogs, LR_E_TXT,
+  LCLType, LCLIntf, LR_Class;
 
 type
 
+  { TStyleDesc }
+  TStyleDesc = record
+    styleID: AnsiString;
+    styleInfo: AnsiString;
+  end;
+
   { TfrHTMExport }
 
   TfrHTMExport = class(TComponent)
   public
-    Constructor Create(aOwner : TComponent); override;
+    constructor Create(aOwner: TComponent); override;
   end;
 
+  { TfrHTMExportFilter }
+
   TfrHTMExportFilter = class(TfrTextExportFilter)
+  private
+    cssStyles: array of TStyleDesc;
+    styleStartLine: integer;
+    outputLines: TStringList;
+    function AddStyle(p: PfrTextRec): Integer;
+    function ColorToHex(c: TColor): AnsiString;
+    function StyleIndex(p: PfrTextRec; AddIfNotFound: boolean = true): Integer;
+    function TextStyleID(p: PfrTextRec): AnsiString;
+  protected
+    procedure AppendLine(const s: UTF8String);
+    procedure InsertLine(const s: UTF8String; position: Integer);
   public
     constructor Create(AStream: TStream); override;
     destructor Destroy; override;
     procedure OnEndPage; override;
+    procedure OnEndDoc; override;
   end;
 
+var
+  HTMExportUsesCSS: boolean = false;
 
 implementation
 
 uses LR_Const;
 
-
 constructor TfrHTMExportFilter.Create(AStream: TStream);
 var
-  s: String;
+  s: UTF8String;
 begin
   inherited Create(AStream);
+  outputLines:= TStringList.Create;
+  SetLength(cssStyles, 0);
 
-  s :=  '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"' + LineEnding +
-        '    "http://www.w3.org/TR/html4/loose.dtd">' + LineEnding +
-        '<html><head>' + LineEnding +
-        '<meta name="generator" content="LazReport html exporter">' + LineEnding +
-        '<title>LazReport Exported Report</title>' + LineEnding +  // TODO: improve
-        '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' + LineEnding +
-        '</head><body><table>' + LineEnding;
+  s:= '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">' + LineEnding;
+  AppendLine(s);
+  s:= '<html>' + LineEnding +
+      '<head>' + LineEnding +
+      '<meta name="generator" content="LazReport html exporter">' + LineEnding +
+      '<title>LazReport Exported Report</title>' + LineEnding +  // TODO: improve
+      '<meta http-equiv="content-type" content="text/html; charset=UTF-8" />' + LineEnding +
+      '<title>' + 'Report html export' + '</title>' + LineEnding;
+  AppendLine(s);
+  s:= '<!-- CSS section start -->' + LineEnding +
+      '<style type="text/css">' + LineEnding;
+  AppendLine(s);
+  styleStartLine:= outputLines.Count;
 
-  Stream.Write(s[1], Length(s));
+  s:= '</style>' + LineEnding +
+      '<!-- CSS section end -->' + LineEnding + '</head>' + LineEnding + LineEnding;
+  AppendLine(s);
+  s:= '<body bgColor="#FFFFFF">' + LineEnding;
+  AppendLine(s);
 end;
 
+
 destructor TfrHTMExportFilter.Destroy;
-var
-  s: String;
 begin
-  s := '</table></body></html>' + LineEnding;
-  Stream.Write(s[1], Length(s));
+  SetLength(cssStyles, 0);
+  outputLines.Free;
   inherited Destroy;
 end;
 
+
+{%REGION 'procedure TfrHTMExportFilter.OnEndPage' }
 procedure TfrHTMExportFilter.OnEndPage;
+const
+  xMult = 1.0;
 var
-  i, n: Integer;
+  i, j, n, cw, xp, xp2: integer;
   p: PfrTextRec;
-  s, s1, s2, s3: String;
+  s, s1, s2, sp, sAlign, sStyle, sEmpCells, sColSpan: AnsiString;
+  section: TfrBandType;
+  hasInnerTable: boolean;
+  xPos: TStringList;
 
-  function GetHTMLFontSize(Size: Integer): String;
+  function GetHTMLFontSize(Size: integer): string;
   begin
     case Size of
       6, 7: Result := '1';
@@ -81,79 +121,289 @@
       14..17: Result := '4';
       18..23: Result := '5';
       24..35: Result := '6'
-    else
-      Result := '7';
+      else
+        Result := '7';
     end;
   end;
 
-  function GetHTMLFontStyle(Style: Integer): String;
+  function GetHTMLFontStyle(Style: integer): string;
   begin
     Result := '';
-    if (Style and $1) <> 0 then Result := '<i>';
-    if (Style and $2) <> 0 then Result := Result + '<b>';
-    if (Style and $4) <> 0 then Result := Result + '<u>';
+    if (Style and $1) <> 0 then
+      Result := '<i>';
+    if (Style and $2) <> 0 then
+      Result := Result + '<b>';
+    if (Style and $4) <> 0 then
+      Result := Result + '<u>';
   end;
 
-  function GetEndHTMLFontStyle(Style: Integer): String;
+  function FormatCellText(const sIn: AnsiString): AnsiString;
+  var
+    c, m: Integer;
   begin
-    Result := '';
-    if (Style and $4) <> 0 then Result := '</u>';
-    if (Style and $2) <> 0 then Result := Result + '</b>';
-    if (Style and $1) <> 0 then Result := Result + '</i>';
+    Result:= '';
+    c:=1;
+    while (c<=Length(sIn)) and (sIn[c]=' ') do
+      inc(c);
+    dec(c);
+    for m:=1 to c do
+      Result:= Result + '&nbsp;';
+     Result:= Result + Copy(sIn, c+1, Length(sIn)-c);
   end;
 
 begin
+  section := btNone;
+  hasInnerTable := False;
+
   n := Lines.Count - 1;
   while n >= 0 do
   begin
-    if Lines[n] <> nil then break;
+    if Lines[n] <> nil then
+      break;
     Dec(n);
   end;
 
+  xPos:= TStringList.Create;
+  xPos.Sorted:= true;
   for i := 0 to n do
   begin
     p := PfrTextRec(Lines[i]);
-    s := '';
     while p <> nil do
     begin
-      s1 := ''; s2 := ''; s3 := '';
+      s:= Format('%.5d', [Round(p^.X * xMult)]);
+      if xPos.IndexOf(s) < 0 then
+        xPos.Add(s);
+      s:= Format('%.5d', [Round((p^.X + p^.W) * xMult)]);
+      if xPos.IndexOf(s) < 0 then
+        xPos.Add(s);
+      p:= p^.Next;
+    end;
+  end;
+
+  for i := 0 to n do
+  begin
+    p := PfrTextRec(Lines[i]);
+    s := '<tr>';
+    cw:= 0;
+    while p <> nil do
+    begin
+      if (p^.BandType <> section) then
+      begin
+        section := p^.BandType;
+        if hasInnerTable then
+        begin
+          s := '</table>'#10;
+          AppendLine(s);
+        end;
+        s := '<table align="center" width="90%" margin="1">'#10'<tr height="0">';
+        for j:=1 to xPos.Count do
+          s += '<td></td>';
+        s+= '</tr>'#10'<tr>';
+        cw:= 0;
+        hasInnerTable := True;
+      end;
+
+      s1:= '';
+      s2:= '';
+      sEmpCells:= '';
+      sColSpan:= '';
+      sAlign:= '';
+      sStyle:= '';
+
       if (p^.FontColor = clWhite) or (p^.FontColor = clNone) then
         p^.FontColor := clBlack;
-      if p^.FontColor <> clBlack then
+
+      if HTMExportUsesCSS then
       begin
-        s1 := IntToHex(p^.FontColor, 6);
-        s1 := 'Color="#' + Copy(s1, 5, 2) + Copy(s1, 3, 2) +
-          Copy(s1, 1, 2) + '"';
+        sStyle:= Format(' class="fs%d"', [StyleIndex(p, true)]);
+      end
+      else
+      begin
+        if p^.FontColor <> clBlack then
+          s1:= ' Color="' + ColorToHex(p^.FontColor) + '"';
+        // most reports is done with font size = 10..13 - treat it as default font
+        if not (p^.FontSize in [10..13]) then
+          s1 := s1 + ' Size=' + GetHTMLFontSize(p^.FontSize);
+        if p^.FontStyle <> 0 then
+          s2 := GetHTMLFontStyle(p^.FontStyle);
+        if s1 <> '' then
+          s1 := '<Font' + s1 + '>';
       end;
-// most reports is done with font size = 10..13 - treat it as default font
-      if not (p^.FontSize in [10..13]) then
-        s1 := s1 + ' Size=' + GetHTMLFontSize(p^.FontSize);
-      if p^.FontStyle <> 0 then
+
+      case p^.Alignment of
+        taRightJustify: sAlign:= ' align="right"';
+        taCenter:       sAlign:= ' align="center"';
+      end;
+
+      sp:= Format('%.5d', [Round(p^.X * xMult)]);
+      xp:= xPos.IndexOf(sp);
+      sp:= Format('%.5d', [Round((p^.X + p^.W) * xMult)]);
+      xPos.Find(sp, xp2);
+      if Assigned(p^.Next) then
       begin
-        s2 := GetHTMLFontStyle(p^.FontStyle);
-        s3 := GetEndHTMLFontStyle(p^.FontStyle);
+        sp:= Format('%.5d', [Round(p^.Next^.X * xMult)]);
+        if xPos.IndexOf(sp)<xp2 then
+          xp2:= xPos.IndexOf(sp);
       end;
-      if s1 <> '' then s1 := '<Font ' + s1 + '>';
-      s := s + '<td>' + s1 + s2 + p^.Text + s3;
-      if s1 <> '' then s := s + '</Font>';
-      s := s + '</td>';
+      if xp>cw then
+        if (xp-cw)>1 then
+          sEmpCells:= Format('<td colspan=%d></td>', [xp - cw])
+        else
+          sEmpCells:= '<td></td>';
+
+      if (xp2-xp)>1 then
+        sColSpan:= Format(' colspan=%d', [xp2 - xp]);
+      cw:= xp2;
+
+      s := Format('%s%s<td%s%s%s>%s%s%s</td>', [s, sEmpCells, sAlign, sStyle, sColSpan, s1, s2, FormatCellText(p^.Text)]);
       p := p^.Next;
     end;
-    if s='' then
-      s := '<td></td>';
-    s := '<tr>' + s + '</tr>' + LineEnding;
-    Stream.Write(s[1], Length(s));
+    if hasInnerTable then
+      s:= s + '</tr>'#10
+    else
+      s:= '<br>'#10;
+    AppendLine(s);
   end;
+
+  xPos.Free;
+
+  if hasInnerTable then
+  begin
+    s := '</table>'#10;
+    AppendLine(s);
+  end;
 end;
+{%ENDREGION }
 
 
+function TfrHTMExportFilter.TextStyleID(p: PfrTextRec): AnsiString;
+var
+  x: Integer;
+begin
+  Result:= '(none)';
+  if p=nil then
+    exit;
+  Result := p^.FontName;
+  Result += LowerCase(IntToHex(p^.FontSize, 2) + IntToHex(p^.FontStyle, 2) + IntToHex(p^.FontColor, 8) +
+                      IntToHex(p^.FillColor, 8) + IntToHex(Integer(p^.Borders), 2));
+  for x:=1 to Length(Result) do
+    if not (Result[x] in ['$', '%', '&', '0'..'9', '@'..'z']) then
+      Result[x]:= '_';
+end;
+
+
+function TfrHTMExportFilter.StyleIndex(p: PfrTextRec; AddIfNotFound: boolean): integer;
+var
+  s: string;
+  x: integer;
+begin
+  Result:= -1;
+  s:= TextStyleID(p);
+  for x:=0 to High(cssStyles) do
+    if cssStyles[x].styleID = s then
+    begin
+      Result:= x;
+      break;
+    end;
+  if (Result<0) and AddIfNotFound then
+    Result:= AddStyle(p);
+end;
+
+
+function TfrHTMExportFilter.AddStyle(p: PfrTextRec): Integer;
+var
+  s: string;
+begin
+  Result:= Length(cssStyles);
+  SetLength(cssStyles, Result+1);
+  cssStyles[Result].styleID:= TextStyleID(p);
+  s:= '';
+  if Assigned(p) then
+  begin
+    // s += Format(' /* Cell Style "%s" */'#10, [cssStyles[Result].styleID]);
+    s += Format(' td.fs%d {'#10, [Result]);
+    s += Format('  font-family: "%s";'#10, [p^.FontName]);
+    s += Format('  font-size: %dpt;'#10, [p^.FontSize]);
+    if (p^.FontStyle and $1) <> 0 then
+      s += '  font-style: italic;'#10;
+    if (p^.FontStyle and $2) <> 0 then
+      s += '  font-weight: bold;'#10;
+    if (p^.FontStyle and $4) <> 0 then
+      s += '  text-decoration: underline;'#10;
+    if (p^.FontColor <> clNone) and (p^.FontColor <> clDefault) and (p^.FontColor <> clBlack) then
+      s += Format('  color: %s;'#10, [ColorToHex(p^.FontColor)]);
+    if (p^.FillColor <> clNone) and (p^.FillColor <> clDefault) and (p^.FillColor <> clWhite) then
+      s += Format('  background-color: %s;'#10, [ColorToHex(p^.FillColor)]);
+    if (p^.Borders <> []) then
+    begin
+      case p^.BorderStyle of
+        frsSolid:      s += '  border-style: solid;'#10;
+        frsDash:       s += '  border-style: dashed;'#10;
+        frsDot,
+        frsDashDot,
+        frsDashDotDot: s += '  border-style: dotted;'#10;
+        frsDouble:     s += '  border-style: double;'#10;
+      end;
+      if not (frbLeft in p^.Borders) then
+        s += '  border-left-style: none;'#10;
+      if not (frbTop in p^.Borders) then
+        s += '  border-top-style: none;'#10;
+      if not (frbRight in p^.Borders) then
+        s += '  border-right-style: none;'#10;
+      if not (frbBottom in p^.Borders) then
+        s += '  border-bottom-style: none;'#10;
+      s += Format('  border-width: %dpx;'#10, [p^.BorderWidth]);
+      s += Format('  border-color: %s;'#10, [ColorToHex(p^.BorderColor)]);
+    end;
+    s += Format(' } '#10#10, []);
+  end;
+  cssStyles[Result].styleInfo:= s;
+end;
+
+
+function TfrHTMExportFilter.ColorToHex(c: TColor): AnsiString;
+var
+  s: AnsiString;
+begin
+  s:= IntToHex(c, 8);
+  Result:= '#' + Copy(s, 7, 2) + Copy(s, 5, 2) + Copy(s, 3, 2);
+end;
+
+
+procedure TfrHTMExportFilter.AppendLine(const s: UTF8String);
+begin
+  outputLines.Add(s);
+end;
+
+
+procedure TfrHTMExportFilter.InsertLine(const s: UTF8String; position: Integer);
+begin
+  outputLines.Insert(position, s);
+end;
+
+
+procedure TfrHTMExportFilter.OnEndDoc;
+var
+  s: string;
+  x: Integer;
+begin
+  s := '</body>'#10'</html>'#10;
+  AppendLine(s);
+  for x:=0 to High(cssStyles) do
+    InsertLine(cssStyles[x].StyleInfo, styleStartLine + x);
+  for x:= 0 to Pred(outputLines.Count) do
+    if Length(outputLines[x])>0 then
+      Stream.Write(outputLines[x][1], Length(outputLines[x]));
+end;
+
+
 { TfrHTMExport }
 
 constructor TfrHTMExport.Create(aOwner: TComponent);
 begin
   inherited Create(aOwner);
-  
   frRegisterExportFilter(TfrHTMExportFilter, sHTMFile + ' (*.htm)', '*.htm');
 end;
 
 end.
+
Index: lr_e_txt.pas
===================================================================
--- lr_e_txt.pas	(revision 23082)
+++ lr_e_txt.pas	(working copy)
@@ -1,11 +1,11 @@
 
 {*****************************************}
-{                                         }
-{             FastReport v2.3             }
-{           Text export filter            }
-{                                         }
+
+ {             FastReport v2.3             }
+ {           Text export filter            }
+
 {  Copyright (c) 1998-99 by Tzyganenko A. }
-{                                         }
+
 {*****************************************}
 
 unit LR_E_TXT;
@@ -15,9 +15,8 @@
 {$I lr_vers.inc}
 
 uses
-  Classes, SysUtils, LResources,
-  Graphics,GraphType, Controls, Forms, Dialogs,
-  LCLType,LCLIntf,LR_Class;
+  Classes, SysUtils, LResources, Graphics, GraphType, Controls, Forms, Dialogs,
+  LCLType, LCLIntf, LR_Class;
 
 type
 
@@ -25,7 +24,7 @@
 
   TfrTextExport = class(TComponent)
   public
-    Constructor Create(aOwner : TComponent); override;
+    constructor Create(aOwner: TComponent); override;
   end;
 
   { TfrTextExportFilter }
@@ -106,21 +105,22 @@
   n := Lines.Count - 1;
   while n >= 0 do
   begin
-    if Lines[n] <> nil then break;
+    if Lines[n] <> nil then
+      break;
     Dec(n);
   end;
 
   for i := 0 to n do
   begin
-    s := '';
-    tc1 := 0;
-    p := PfrTextRec(Lines[i]);
+    s  := '';
+    tc1:= 0;
+    p  := PfrTextRec(Lines[i]);
     while p <> nil do
     begin
-      x := Round(p^.X / 6.5);
-      s := s + Dup(x - tc1) + p^.Text;
-      tc1 := x + Length(p^.Text);
-      p := p^.Next;
+      x  := Round(p^.X / 6.5);
+      s  := s + Dup(x - tc1) + p^.Text;
+      tc1:= x + Length(p^.Text);
+      p  := p^.Next;
     end;
     s := s + LineEnding;
     Stream.Write(s[1], Length(s));
@@ -131,41 +131,51 @@
 
 procedure TfrTextExportFilter.OnBeginPage;
 var
-  i: Integer;
+  i: integer;
 begin
   ClearLines;
-  for i := 0 to 200 do Lines.Add(nil);
+  for i := 0 to 200 do
+    Lines.Add(nil);
 end;
 
-procedure TfrTextExportFilter.OnText(X, Y: Integer; const Text: String;
-  View: TfrView);
+procedure TfrTextExportFilter.OnText(X, Y: integer; const Text: string; View: TfrView);
 var
   p, p1, p2: PfrTextRec;
 begin
-  if View = nil then Exit;
+  if View = nil then
+    Exit;
   Y := Round(Y / UsedFont);
-  p1 := PfrTextRec(Lines[Y]);
+  p1:= PfrTextRec(Lines[Y]);
   GetMem(p, SizeOf(TfrTextRec));
   FillChar(p^, SizeOf(TfrTextRec), 0);
   p^.Next := nil;
-  p^.X := X;
+  p^.X    := Round(View.X / UsedFont);
+  p^.W    := Round(View.Width / UsedFont);
   p^.Text := Text;
+  p^.FillColor  := View.FillColor;
+  p^.Borders    := View.Frames;
+  p^.BorderColor:= View.FrameColor;
+  p^.BorderStyle:= View.FrameStyle;
+  p^.BorderWidth:= Round(View.FrameWidth);
+  if Assigned(View.Parent) then
+    p^.BandType:= View.Parent.Typ;
   if View is TfrMemoView then
     with View as TfrMemoView do
     begin
-      p^.FontName := Font.Name;
-      p^.FontSize := Font.Size;
-      p^.FontStyle := frGetFontStyle(Font.Style);
-      p^.FontColor := Font.Color;
+      p^.FontName    := Font.Name;
+      p^.FontSize    := Font.Size;
+      p^.FontStyle   := frGetFontStyle(Font.Style);
+      p^.FontColor   := Font.Color;
       p^.FontCharset := Font.Charset;
+      p^.Alignment   := Alignment;
     end;
-  p^.FillColor := View.FillColor;
+
   if p1 = nil then
     Lines[Y] := TObject(p)
   else
   begin
     p2 := p1;
-    while (p1 <> nil) and (p1^.X < p^.X) do
+    while (p1 <> nil) and (p1^.X <= p^.X) do
     begin
       p2 := p1;
       p1 := p1^.Next;
@@ -173,12 +183,12 @@
     if p2 <> p1 then
     begin
       p2^.Next := p;
-      p^.Next := p1;
+      p^.Next  := p1;
     end
     else
     begin
       Lines[Y] := TObject(p);
-      p^.Next := p1;
+      p^.Next  := p1;
     end;
   end;
 end;
@@ -189,8 +199,9 @@
 constructor TfrTextExport.Create(aOwner: TComponent);
 begin
   inherited Create(aOwner);
-  
+
   frRegisterExportFilter(TfrTextExportFilter, sTextFile + ' (*.txt)', '*.txt');
 end;
 
 end.
+
Index: lr_class.pas
===================================================================
--- lr_class.pas	(revision 23082)
+++ lr_class.pas	(working copy)
@@ -1108,9 +1108,16 @@
   TfrTextRec = record
     Next: PfrTextRec;
     X: Integer;
+    W: Integer;
     Text: String[255];
     FontName: String[32];
     FontSize, FontStyle, FontColor, FontCharset, FillColor: Integer;
+    Alignment: TAlignment;
+    Borders: TfrFrameBorders;
+    BorderColor: TColor;
+    BorderStyle: TfrFrameStyle;
+    BorderWidth: Integer;
+    BandType: TfrBandType;
     Typ: Byte;
   end;
 
@@ -6929,10 +6936,11 @@
     while Stream.Position < Stream.Size do
     begin
       Stream.Read(b, 1);
+      s := '';
       if b = gtAddIn then
-        s := ReadString(Stream) else
-        s := '';
+        s := ReadString(Stream);
       t := frCreateObject(b, s);
+      t.Parent:= nil;
       t.StreamMode := smPrinting;
       t.LoadFromStream(Stream);
       t.ExportData;
@@ -8489,6 +8497,7 @@
   else
   begin
     p := TfrPreviewForm.Create(nil);
+    p.BorderIcons:=p.BorderIcons - [biMinimize];
     {$IFDEF DebugLR}
     DebugLn('1 TfrPreviewForm.visible=',BooLToStr(p.Visible));
     {$ENDIF}
lazreport_20091211.diff (18,908 bytes)   

Tsvetoslav

2009-12-11 15:27

reporter   ~0032968

Hi Jesus,
I know the patch is far from perfect, I just wanted to make the html export a little bit better by using CSS. :)

First, there is a global var "HTMExportUsesCSS" in lr_e_htm unit which tells if the export process will create a html file with CSS or not. If HTMExportUsesCSS = false, then the old style export is used (I have made only small modifications in it), so in this case you should obtain almost the same result as before. If HTMExportUsesCSS = true, then CSS will be used, and so the created html should closer represents what you get in "report preview".
It seems there is some problem with wrapped texts, but I haven't find the exact reason/solution, yet.

I mainly tested with lazreport sample application (calleditorwithpkg in lazreport\samples\editor) and it works well, but may be it is not suitable for all kinds of reports. I have attached a few screenshots - report designer, report preview, report html export without CSS and with CSS, so you may see the results.

And no, I haven't tested if the output is 'XHTML 1.0 Transitional' compliant, so I have reverted it to 'HTML 4.01 Transitional'. I have attached a new patch which takes into account your latest changes.

Jesus Reyes

2009-12-12 09:14

developer   ~0032991

Applied with changes and fixes.

I will fix the problem of word wrapped text shortly.

Thanks.

Tsvetoslav

2009-12-14 10:57

reporter   ~0033070

Thank you very much, it would be great if word wrapped text problem is also fixed.

I see you have replaced the global variable HTMExportUsesCSS with a property UseCSS. Generally it is OK, but please note that the class TfrHTMExportFilter is internally created by the export process and practically the new property is unusable. So, we should either use a global variable as before, or if possible make the new property "static", so that the developer can change it in his/her code like this:
TfrHTMExportFilter.UseCSS:= true;

Jesus Reyes

2009-12-15 01:06

developer   ~0033096

Last edited: 2009-12-15 01:07

The word wrap problem should be already fixed.

About the UseCSS property, it is designed to be used in OnExportFilterSetup report event with an instance of TfrHTMExportFilter class.

All is now explained in the LazReport wiki page ( http://wiki.lazarus.freepascal.org/LazReport_Documentation ) and the lazreport/samples/editor sample now includes an actual implementation.

If you agree, please close this report.

Tsvetoslav

2009-12-15 07:31

reporter   ~0033103

Thanks again, everything seems fine now and this issue can be closed.

Issue History

Date Modified Username Field Change
2009-11-06 15:48 Tsvetoslav New Issue
2009-11-06 15:48 Tsvetoslav File Added: lazreport_source.diff
2009-11-06 15:48 Tsvetoslav Widgetset => GTK 2, Win32/Win64
2009-11-06 16:20 Vincent Snijders LazTarget => 0.9.30
2009-11-06 16:20 Vincent Snijders Assigned To => Jesus Reyes
2009-11-06 16:20 Vincent Snijders Status new => assigned
2009-11-06 16:20 Vincent Snijders Target Version => 0.9.30
2009-11-26 20:43 Tsvetoslav File Added: lazreport_src.diff
2009-11-26 20:44 Tsvetoslav Note Added: 0032508
2009-12-11 08:25 Jesus Reyes Note Added: 0032931
2009-12-11 08:25 Jesus Reyes Status assigned => feedback
2009-12-11 08:28 Jesus Reyes Note Edited: 0032931
2009-12-11 15:24 Tsvetoslav File Added: report_design.png
2009-12-11 15:24 Tsvetoslav File Added: report_preview.png
2009-12-11 15:24 Tsvetoslav File Added: report_html_export_no_css.png
2009-12-11 15:24 Tsvetoslav File Added: report_html_export_with_css.png
2009-12-11 15:26 Tsvetoslav File Added: lazreport_20091211.diff
2009-12-11 15:27 Tsvetoslav Note Added: 0032968
2009-12-12 09:14 Jesus Reyes Fixed in Revision => 23089
2009-12-12 09:14 Jesus Reyes Status feedback => resolved
2009-12-12 09:14 Jesus Reyes Fixed in Version => 0.9.29 (SVN)
2009-12-12 09:14 Jesus Reyes Resolution open => fixed
2009-12-12 09:14 Jesus Reyes Note Added: 0032991
2009-12-14 10:57 Tsvetoslav Note Added: 0033070
2009-12-15 01:06 Jesus Reyes Note Added: 0033096
2009-12-15 01:07 Jesus Reyes Note Edited: 0033096
2009-12-15 07:31 Tsvetoslav Status resolved => closed
2009-12-15 07:31 Tsvetoslav Note Added: 0033103