Files
HeidiSQL/components/synedit/Source/SynExportTeX.pas

416 lines
13 KiB
ObjectPascal

{-------------------------------------------------------------------------------
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.