{------------------------------------------------------------------------------- The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: SynExportTeX.pas, released 2002-09-12. The Original Code is partly based on the mwHTMLExport.pas file from the mwEdit component suite by Martin Waldenburg and other developers, the Initial Author of this file is Ascher Stefan. Portions created by Ascher Stefan are Copyright 2002 Ascher Stefan. Unicode translation by Maël Hörz. All Rights Reserved. Contributors to the SynEdit project are listed in the Contributors.txt file. Alternatively, the contents of this file may be used under the terms of the GNU General Public License Version 2 or later (the "GPL"), in which case the provisions of the GPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the GPL and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the GPL. $Id: SynExportTeX.pas,v 1.8.2.5 2008/09/14 16:24:59 maelh Exp $ You may retrieve the latest version of this file at the SynEdit home page, located at http://SynEdit.SourceForge.net Known Issues: - LaTeX 2e doesn't support Unicode, so this exporter doesn't either. (There are solutions like the package utc.sty but still they don't allow mixing of different languages like Arabic and Chinese. We'll have to wait for LaTeX 3.) -------------------------------------------------------------------------------} {$IFNDEF QSYNEXPORTTEX} unit SynExportTeX; {$ENDIF} {$I SynEdit.inc} interface uses Windows, Graphics, SynEditExport, SynEditHighlighter, SynUnicode, Classes; type TSynExporterTeX = class(TSynCustomExporter) private FMargin: Integer; FLastAttri: TSynHighlighterAttributes; function AttriToCommand(Attri: TSynHighlighterAttributes; UniqueAttriName: string): string; function AttriToCommandCallback(Highlighter: TSynCustomHighlighter; Attri: TSynHighlighterAttributes; UniqueAttriName: string; Params: array of Pointer): Boolean; function CommandNameCallback(Highlighter: TSynCustomHighlighter; Attri: TSynHighlighterAttributes; UniqueAttriName: string; Params: array of Pointer): Boolean; function GetCommandName(Highlighter: TSynCustomHighlighter; Attri: TSynHighlighterAttributes): string; function GetNewCommands: string; function MakeValidName(Name: string): string; protected FCreateTeXFragment: Boolean; FTabWidth: Integer; FPageStyleEmpty: Boolean; // overriding these abstract methods (though they are never called for this // specific highlighter) to prevent abstract instance warnings procedure FormatAfterLastAttribute; override; procedure FormatAttributeDone(BackgroundChanged: Boolean; ForegroundChanged: Boolean; FontStylesChanged: TFontStyles); override; procedure FormatAttributeInit(BackgroundChanged: Boolean; ForegroundChanged: Boolean; FontStylesChanged: TFontStyles); override; procedure FormatBeforeFirstAttribute(BackgroundChanged: Boolean; ForegroundChanged: Boolean; FontStylesChanged: TFontStyles); override; procedure FormatNewLine; override; procedure FormatToken(Token: UnicodeString); override; function GetFooter: UnicodeString; override; function GetFormatName: string; override; function GetHeader: UnicodeString; override; function ReplaceReservedChar(AChar: WideChar): UnicodeString; override; procedure SetTokenAttribute(Attri: TSynHighlighterAttributes); override; function UseBom: Boolean; override; public constructor Create(AOwner: TComponent); override; function SupportedEncodings: TSynEncodings; override; published property Margin: Integer read FMargin write FMargin default 2; property TabWidth: Integer read FTabWidth write FTabWidth default 2; property Color; property CreateTeXFragment: Boolean read FCreateTeXFragment write FCreateTeXFragment default false; property PageStyleEmpty: Boolean read FPageStyleEmpty write FPageStyleEmpty default false; property DefaultFilter; property Encoding; property Font; property Highlighter; property Title; property UseBackground; end; implementation uses SynEditMiscProcs, SynEditStrConst, SysUtils; // DotDecSepFormat always formats with a dot as decimal separator. // This is necessary because LaTeX expects a dot, but VCL's Format is // language-dependent, i.e. with another locale set, the separator can be // different (for example a comma). function DotDecSepFormat(const Format: string; const Args: array of const): string; var {$IFDEF UNICODE} OldDecimalSeparator: WideChar; {$ELSE} OldDecimalSeparator: AnsiChar; {$ENDIF} begin OldDecimalSeparator := {$IFDEF SYN_COMPILER_15_UP}FormatSettings.{$ENDIF}DecimalSeparator; {$IFDEF SYN_COMPILER_15_UP}FormatSettings.{$ENDIF}DecimalSeparator := '.'; Result := SysUtils.Format(Format, Args); {$IFDEF SYN_COMPILER_15_UP}FormatSettings.{$ENDIF}DecimalSeparator := OldDecimalSeparator; end; function ColorToTeX(AColor: TColor): string; const f = '%1.2g'; f2 = '%s,%s,%s'; var RGBColor: LongWord; RValue, GValue, BValue: string; begin RGBColor := ColorToRGB(AColor); RValue := DotDecSepFormat(f, [GetRValue(RGBColor) / 255]); GValue := DotDecSepFormat(f, [GetGValue(RGBColor) / 255]); BValue := DotDecSepFormat(f, [GetBValue(RGBColor) / 255]); Result := Format(f2, [RValue, GValue, BValue]); end; { TSynExporterTeX } constructor TSynExporterTeX.Create(AOwner: TComponent); begin inherited Create(AOwner); FMargin := 2; FTabWidth := 2; FPageStyleEmpty := False; FDefaultFilter := SYNS_FilterTeX; FEncoding := seAnsi; end; function TSynExporterTeX.AttriToCommandCallback( Highlighter: TSynCustomHighlighter; Attri: TSynHighlighterAttributes; UniqueAttriName: string; Params: array of Pointer): Boolean; var Commands: ^string; begin Commands := Params[0]; Commands^ := Commands^ + AttriToCommand(Attri, UniqueAttriName) + SLineBreak; Result := True; // we want all attributes => tell EnumHighlighterAttris to continue end; function TSynExporterTeX.AttriToCommand(Attri: TSynHighlighterAttributes; UniqueAttriName: string): string; const NewCommand = '\newcommand{\%s}[1]{%s#1%s}'; SBold = '\textbf{'; SItalic = '\textit{'; SUnderline = '\uln{'; SColor = '\textcolor[rgb]{%s}{'; SBackColor = '\colorbox[rgb]{%s}{'; var Formatting: string; BracketCount: Integer; begin BracketCount := 0; with Attri do begin if fsBold in Style then begin Formatting := Formatting + SBold; Inc(BracketCount); end; if fsItalic in Style then begin Formatting := Formatting + SItalic; Inc(BracketCount); end; if fsUnderline in Style then begin Formatting := Formatting + SUnderline; Inc(BracketCount); end; if (Foreground <> clBlack) and (Foreground <> clNone) then begin Formatting := Formatting + Format(SColor, [ColorToTeX(Foreground)]); Inc(BracketCount); end; if fUseBackground and (Background <> clNone) then begin Formatting := Formatting + Format(SBackColor, [ColorToTeX(Background)]); Inc(BracketCount); end; Result := Format(NewCommand, [MakeValidName(UniqueAttriName), Formatting, StringOfChar('}', BracketCount)]) end; end; function TSynExporterTeX.CommandNameCallback( Highlighter: TSynCustomHighlighter; Attri: TSynHighlighterAttributes; UniqueAttriName: string; Params: array of Pointer): Boolean; var AttriToFind: TSynHighlighterAttributes; CommandName: ^string; begin AttriToFind := Params[0]; CommandName := Params[1]; if Attri = AttriToFind then begin CommandName^ := MakeValidName(UniqueAttriName); Result := False; // found => inform EnumHighlighterAttris to stop searching end else Result := True; end; procedure TSynExporterTeX.FormatToken(Token: UnicodeString); var CommandName: string; begin CommandName := GetCommandName(Highlighter, FLastAttri); AddData('\' + CommandName + '{' + Token + '}'); end; procedure TSynExporterTeX.FormatNewLine; begin AddData('\\' + SLineBreak); end; // do nothing with these procedure TSynExporterTeX.FormatAfterLastAttribute; begin end; procedure TSynExporterTeX.FormatAttributeDone; begin end; procedure TSynExporterTeX.FormatAttributeInit; begin end; procedure TSynExporterTeX.FormatBeforeFirstAttribute; begin end; function TSynExporterTeX.GetCommandName(Highlighter: TSynCustomHighlighter; Attri: TSynHighlighterAttributes): string; begin EnumHighlighterAttris(Highlighter, False, CommandNameCallback, [Attri, @Result]); end; function TSynExporterTeX.GetFooter: UnicodeString; begin if not FCreateTeXFragment then Result := SLineBreak + '\end{ttfamily}' + SLineBreak + '\end{document}' else Result := SLineBreak + '\end{ttfamily}'; end; function TSynExporterTeX.GetFormatName: string; begin Result := SYNS_ExporterFormatTeX; end; function TSynExporterTeX.GetHeader: UnicodeString; const TeXHeader = '\documentclass[a4paper, %dpt]{article}' + SLineBreak + '\usepackage[a4paper, margin=%dcm]{geometry}' + SLineBreak + '\usepackage[T1]{fontenc}' + SLineBreak + '\usepackage{color}' + SLineBreak + '\usepackage{alltt}' + SLineBreak + '\usepackage{times}' + SLineBreak + '\usepackage{ulem}' + SLineBreak + {$IFDEF WIN32} // It is recommennded to use AnsiNew on Windows '\usepackage[ansinew]{inputenc}' + SLineBreak + {$ELSE} // and Latin1 on UNIX Systems, see also DE FAQ 8.5.3 '\usepackage[latin1]{inputenc}' + SLineBreak + {$ENDIF} '%s' + SLineBreak; // New Commands TeXHeader2 = '%% Generated by SynEdit TeX exporter' + SLineBreak + SLineBreak + '\begin{document}'; EmptyPage = '\pagestyle{empty}'; TeXDocument = '\begin{ttfamily}' + SLineBreak + '\noindent' + SLineBreak; var PageStyle: string; begin if not FCreateTeXFragment then begin if FPageStyleEmpty then PageStyle := SLineBreak + EmptyPage else PageStyle := ''; Result := Format(TeXHeader + SLineBreak + SLineBreak, [Font.Size, FMargin, GetNewCommands]); Result := Result + '\title{' + Title + '}' + SLineBreak + TeXHeader2 + SLineBreak + PageStyle; end; Result := Result + TeXDocument; end; function TSynExporterTeX.GetNewCommands: string; const FixedCommands = '%% Special Characters' + SLineBreak + '\newcommand\SPC{\hspace*{0.6em}}' + SLineBreak + '\newcommand\TAB{\hspace*{%sem}}' + SLineBreak + '\newcommand\BS{\mbox{\char 92}}' + SLineBreak + // Backslash '\newcommand\TLD{\mbox{\char 126}}' + SLineBreak + // ~ '\newcommand\CIR{\mbox{\char 94}}' + SLineBreak + // ^ '\newcommand\HYP{\mbox{\char 45}}' + SLineBreak + // a simple - '\newcommand\QOT{\mbox{\char 34}}' + SLineBreak + // " '\newcommand{\uln}[1]{\bgroup \markoverwith{\hbox{\_}}\ULon{{#1}}}' + SLineBreak + '%% Highlighter Attributes' + SLineBreak; f = '%1.1g'; var tw: string; Commands: string; begin tw := DotDecSepFormat(f, [FTabWidth * 0.6]); Result := Format(FixedCommands, [tw]); EnumHighlighterAttris(Highlighter, True, AttriToCommandCallback, [@Commands]); Result := Result + Commands; end; function TSynExporterTeX.MakeValidName(Name: string): string; var i: Integer; begin Result := Name; for i := Length(Result) downto 1 do if CharInSet(Result[i], ['1'..'9']) then Result[i] := Char(Ord('A') + Ord(Result[i]) - Ord('1')) else if Result[i] = '0' then Result[i] := 'Z' else if not CharInSet(Result[i], ['a'..'z', 'A'..'Z']) then Delete(Result, i, 1); end; function TSynExporterTeX.ReplaceReservedChar(AChar: WideChar): UnicodeString; begin case AChar of '{': Result := '\{'; '}': Result := '\}'; '\': Result := '\BS '; '~': Result := '\TLD '; '^': Result := '\CIR '; ' ': Result := '\SPC '; #9: Result := '\TAB '; '-': Result := '\HYP '; '"': Result := '\QOT '; '@': Result := '$@$'; '$': Result := '\$'; '&': Result := '\&'; '<': Result := '$<$'; '>': Result := '$>$'; '_': Result := '\_'; '#': Result := '\#'; '%': Result := '\%'; else Result := ''; end; end; procedure TSynExporterTeX.SetTokenAttribute(Attri: TSynHighlighterAttributes); begin FLastAttri := Attri; end; function TSynExporterTeX.SupportedEncodings: TSynEncodings; begin Result := [seAnsi]; end; function TSynExporterTeX.UseBom: Boolean; begin Result := False; end; end.