unit fptexexport;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, DB, fpdbexport;

Type
  TTeXExportOption = (teHeaderRow,teTableEnvironment,teHeaderLine,teTopLine,teBottomLine,teUseWidths,teCreateDocument);
  TTeXExportOptions = Set of TTeXExportOption;
  TTexTabularEnvironment = (ttTabular,ttTabularX,ttLongtable,ttSuperTabular);
  TTexUnits = (tuEm,tuMM);
  
  { TTeXExportFormatSettings }

  TTeXExportFormatSettings = Class(TExportFormatSettings)
  Private
    FOptions : TTeXExportOptions;
    FUnits : TTexUnits;
    FTabular : TTexTabularEnvironment;
  Public
    Constructor Create(DoInitSettings : Boolean); override;
    Procedure Assign(Source : TPersistent); override;
  Published
    // Properties
    Property Options : TTeXExportOptions Read FOptions Write FOptions;
    Property Units : TTexUnits Read FUnits Write FUnits;
    Property Tabular : TTexTabularEnvironment Read FTabular Write FTabular;
  end;

  { TTeXExportFieldItem }
  TTeXExportFieldItem = Class(TExportFieldItem)
  private
    FLineAfter: Boolean;
    FLineBefore: Boolean;
    FWidth: Integer;
    FAlign: TAlignment;
  Public
    Procedure Assign(Source : TPersistent); override;
  Published
    Property Width : Integer Read FWidth Write FWidth;
    Property Align: TAlignment Read FAlign write FAlign;
    Property LineBefore : Boolean Read FLineBefore Write FLineBefore;
    Property LineAfter : Boolean Read FLineAfter Write FLineAfter;
  end;

  { TCustomTeXExporter }

  TCustomTeXExporter = Class(TCustomFileExporter)
  Private
    FCurrentRow : String;
    FEO : TTeXExportOptions;
    FTD : String; // Tabular(X) Table definition string
    FTH : String; // Table header row
    FTN : String; // Tabular environment name (for closing)
    function GetTeXFormatsettings: TTexExportFormatSettings;
    procedure SetTeXFormatSettings(const AValue: TTexExportFormatSettings);
  Protected
    function EscapeLaTeX(S: String): String;
    procedure OutputRow(const ARow: String); virtual;
    procedure OutputTableEnd; virtual;
    procedure OutputTableStart; virtual;
    procedure CloseDocument;  virtual;
    procedure OpenDocument; virtual;
    Function CreateFormatSettings : TCustomExportFormatSettings; override;
    Procedure BuildDefaultFieldMap(AMap : TExportFields); override;
    Function  CreateExportFields : TExportFields; override;
    Procedure DoDataHeader; override;
    Procedure DoDataFooter; override;
    Procedure DoBeforeExecute; override;
    Procedure DoAfterExecute; override;
    Procedure DoDataRowStart; override;
    Procedure ExportField(EF : TExportFieldItem); override;
    Procedure DoDataRowEnd; override;
  Public
    Property FormatSettings : TTexExportFormatSettings Read GetTeXFormatsettings Write SetTeXFormatSettings;
  end;

  TTeXExporter = Class(TCustomTeXExporter)
  Published
    Property FileName;
    Property Dataset;
    Property ExportFields;
    Property FromCurrent;
    Property RestorePosition;
    Property FormatSettings;
    Property OnExportRow;
  end;

Procedure RegisterTexExportFormat;
Procedure UnRegisterTexExportFormat;

Const
  STeXExport    = 'TeX export';
  STeXExportExt = '.tex';
  
  TabularPackageNames  : Array[TTexTabularEnvironment] of string
                       = ('array','tabularx','longtable','supertabular');
  TabularNames : Array[TTexTabularEnvironment] of string
               = ('tabular','tabularx','longtable','supertabular');
  TeXUnitNames : array[TTexUnits] of string = ('em','cm');

Resourcestring
  STeXExportDescr = 'Export to LaTeX table';

implementation

procedure RegisterTexExportFormat;
begin
  ExportFormats.RegisterExportFormat(STeXExport,STeXExportDescr,STexExportExt,TTexExporter);
end;

procedure UnRegisterTexExportFormat;
begin
  ExportFormats.UnRegisterExportFormat(STeXExport);
end;

{ TCustomTeXExporter }
function TCustomTexExporter.EscapeLaTeX(S: String): String;

Var
  I,J,L : Integer;
  P : Pchar;

begin
  I:=1;
  J:=1;
  Result:='';
  L:=Length(S);
  P:=PChar(S);
  While I<=L do
    begin
    if (P^ in ['&','{','}','#','_','$','%']) then
      begin
      Result:=Result+Copy(S,J,I-J)+'\'+P^;
      J:=I+1;
      end
    else if (P^ in ['~','^']) then
      begin
      Result:=Result+Copy(S,J,I-J)+'\'+P^+' ';
      J:=I+1;
      end
    else if (P^='\') then
      begin
      Result:=Result+Copy(S,J,I-J)+'$\backslash$';
      J:=I+1;
      end;
    Inc(I);
    Inc(P);
    end;
  Result:=Result+Copy(S,J,I-1);
end;

function TCustomTeXExporter.GetTeXFormatsettings: TTexExportFormatSettings;
begin
  Result:=TTexExportFormatSettings(Inherited FormatSettings)
end;

procedure TCustomTeXExporter.OutputRow(const ARow: String);
begin
  Writeln(TextFile,ARow);
end;

procedure TCustomTeXExporter.BuildDefaultFieldMap(AMap: TExportFields);

Const
  FieldWidths : Array[TFieldType] of integer
              = (-1,0,3,10,5,
                  1,20,20,20,10,8,20,
                  0,0,10,0,0,0,0,
                  0,0,0,0,0,
                  0,0,0,0,0,
                  0,0,0,0,0,
                  0,0,0,0,0,0);

Var
  I  : Integer;
  FL : TTexExportFieldItem;
  F : TField;
  W : Integer;
  
begin
  inherited BuildDefaultFieldMap(AMap);
  For I:=0 to AMap.Count-1 do
    begin
    FL:=TTexExportFieldItem(AMAP[i]);
    F:=Dataset.Fields[i];
    W:= FieldWidths[F.DataType];
    If (W>0) then
      FL.Width:=W
    else if (W=0) then
      begin
      if (F.DataType in StringFieldTypes) then
        FL.Width:=F.Size;
      end;
    If (F.DataType in IntFieldTypes) then
      Fl.Align:=taRightJustify;
    end;

end;

function TCustomTeXExporter.CreateExportFields: TExportFields;
begin
  Result:=TExportFields.Create(TTexExportFieldItem);
end;

procedure TCustomTeXExporter.DoDataHeader;

Const
  AlChars : Array[TAlignment] of char = 'lcr';

Var
  I,TW : Integer;
  B1,B2 : Boolean;
  EF : TTeXExportFieldItem;
  UN,S,FTW : String;
  
begin
  B1:=teUseWidths in FEO;
  B2:=teHeaderRow in FEO;
  UN:=TexUnitnames[FormatSettings.Units];
  S:='';
  TW:=0;
  For I:=0 to ExportFields.Count-1 do
    begin
    EF:=TTexExportFieldItem(ExportFields[i]);
    If EF.Enabled then
      begin
      If EF.LineBefore then
        S:=S+'|';
      if B1 then
        begin
        TW:=TW+EF.Width;
        S:=S+'p{'+IntToStr(EF.Width)+'}'+UN;
        end
      else
        S:=S+ALChars[EF.Align];
      If EF.LineAfter then
        S:=S+'|';
      If B2 THEN
        begin
        If (FTH<>'') then
          FTH:=FTH+' & ';
        FTH:=FTH+EscapeLaTeX(EF.ExportedName);
        end;
      end;
    end;
 If FormatSettings.Tabular=ttTabularx then
   if Not B1 then
     FTW:='{\textwidth}'
   else
     FTW:=Format('{\%d%s}',[TW,UN]);
  FTD:=Format('\begin{%s}%s{%s}',[FTN,FTW,S]);
  If B2 then
    FTH:=FTH+'\\';
  OutPutTableStart;
  inherited DoDataHeader;
end;

procedure TCustomTeXExporter.DoDataFooter;

begin
  OutPutTableEnd;
  Inherited DoDataFooter;
end;

procedure TCustomTeXExporter.OutputTableEnd;

begin
  If teBottomLine in FEO then
    OutputRow('\hline');
  OutputRow(Format('\end{%s}',[FTN]));
  if (teTableEnvironment in FEO) then
    OutputRow('\end{table}');
end;

procedure TCustomTeXExporter.OutputTableStart;

Var
  S : String;
  I : Integer;

begin
  S:='';
  if (teTableEnvironment in FEO) then
    OutputRow('\begin{table}');
  OutputRow(FTD);
  If teHeaderRow in FEO then
    begin
    if (TeHeaderLine in FEO) then
      OutputRow('\hline');
    OutputRow(FTH);
    end;
  if (TeTopLine in FEO) then
    OutputRow('\hline');
end;

procedure TCustomTeXExporter.SetTeXFormatSettings(
  const AValue: TTexExportFormatSettings);
begin
  Inherited FormatSettings:=AValue
end;

function TCustomTeXExporter.CreateFormatSettings: TCustomExportFormatSettings;
begin
  Result:=TTexExportFormatSettings.Create(False);
end;

procedure TCustomTeXExporter.DoBeforeExecute;
begin
  inherited DoBeforeExecute;
  OpenTextFile;
  FEO:=FormatSettings.Options;
  FTD:='';
  FTH:='';
  FTN:=TabularNames[FormatSettings.Tabular];
  If teCreateDocument in FEO then
    OpenDocument;
end;

procedure TCustomTeXExporter.OpenDocument;

Var
  S : string;
  
begin
  OutputRow(Format('\documentclass%s{%s}',['','article']));
  S:=TabularPackageNames[FormatSettings.Tabular];
  If (S<>'') then
    OutputRow(Format('\usepackage{%s}',[s]));
  OutputRow('\begin{document}');
end;

procedure TCustomTeXExporter.CloseDocument;

begin
  OutputRow('\end{document}');
end;

procedure TCustomTeXExporter.DoAfterExecute;
begin
  If teCreateDocument in FEO then
    CloseDocument;
  CloseTextFile;
  inherited DoAfterExecute;
end;

procedure TCustomTeXExporter.DoDataRowStart;
begin
  FCurrentRow:='';
  inherited DoDataRowStart;
end;

procedure TCustomTeXExporter.ExportField(EF: TExportFieldItem);

Var
  S : String;
  
begin
  S:=FormatField(EF.Field);
  If (FCurrentRow<>'') then
    FCurrentRow:=FCurrentRow+' & ';
  FCurrentRow:=FCurrentRow+EscapeLaTex(S);
end;

procedure TCustomTeXExporter.DoDataRowEnd;
begin
  FCurrentRow:=FCurrentRow+' \\';
  OutputRow(FCurrentRow);
end;

{ TTeXExportFormatSettings }

constructor TTeXExportFormatSettings.Create(DoInitSettings: Boolean);
begin
  inherited Create(DoInitSettings);
  FOptions:=[teHeaderRow,teTableEnvironment,teTopLine,teBottomLine]
end;

procedure TTeXExportFormatSettings.Assign(Source: TPersistent);

Var
  FS : TTeXExportFormatSettings;

begin
  If (Source is TTeXExportFormatSettings) then
    begin
    FS:=Source as TTeXExportFormatSettings;
    Options:=FS.OPtions;
    Units:=FS.Units;
    Tabular:=FS.Tabular;
    end;
  inherited Assign(Source);
end;

{ TTeXExportFieldItem }

procedure TTeXExportFieldItem.Assign(Source: TPersistent);

Var
  Fi : TTeXExportFieldItem;

begin
  If (Source is TTeXExportFieldItem) then
    begin
    FI:=Source as TTeXExportFieldItem;
    Width:=FI.Width;
    Align:=FI.Align;
    LineBefore:=FI.LineBefore;
    LineAfter:=FI.LineAfter;
    end;
  inherited Assign(Source);
end;

end.

