mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
1088 lines
32 KiB
ObjectPascal
1088 lines
32 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: SynEditPrint.pas, released 2000-06-01.
|
|
|
|
The Initial Author of the Original Code is Morten J. Skovrup.
|
|
Portions written by Morten J. Skovrup are copyright 2000 Morten J. Skovrup.
|
|
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: SynEditPrint.pas,v 1.34.2.12 2008/09/23 14:02:08 maelh Exp $
|
|
|
|
You may retrieve the latest version of this file at the SynEdit home page,
|
|
located at http://SynEdit.SourceForge.net
|
|
|
|
Known Issues:
|
|
Wrapping across page boundaries is not supported
|
|
-------------------------------------------------------------------------------}
|
|
|
|
{-------------------------------------------------------------------------------
|
|
CONTENTS:
|
|
Print controller component.
|
|
Allows setting margins, headers and footers.
|
|
|
|
Design time properties:
|
|
Header : Class property to set properties for headers -
|
|
see CSynEditHeaderFooter.pas
|
|
Footer : Class property to set properties for footers -
|
|
see CSynEditHeaderFooter.pas
|
|
Margins : Class property to set properties for margins -
|
|
see CSynEditPrintMargins.pas
|
|
Lines : The lines that should be printed (see also SynEdit the
|
|
property below)
|
|
Font : The font the lines should be printed in (see also SynEdit
|
|
the property below)
|
|
Title : A title - can be referenced in headers/footers by using the
|
|
$TITLE$ macro
|
|
Wrap : Wrap text to margins
|
|
Highlight : Highlight text
|
|
Colors : Print in colors
|
|
LineNumbers : Print line numbers
|
|
LineOffset : Value added to linenumbers when printing
|
|
PageOffset : Value added to pagenumbers when printing
|
|
OnPrintLine : Fired when a line is printed
|
|
OnPrintStatus : Fired at Beginning, End and when a new page is started
|
|
Highlighter : The highlighter used for highlighting the text (see also the
|
|
SynEdit property below)
|
|
LineNumbersInMargin : If true line numbers are printed in the left margin,
|
|
else left margin is increased by width of line
|
|
number text.
|
|
SelectedOnly : Print only the selected Area
|
|
Run-time properties:
|
|
DocTitle : Used to display the document name in the print queue monitor
|
|
PrinterInfo : Read only. Returns info on printer (used internally)
|
|
PageCount : Returns the total number of pages;
|
|
SynEdit : By setting SynEdit to a specific TSynEdit component, the
|
|
properties Lines, Font and Highlighter are automatically
|
|
set to the corresponding values of the TSynEdit component
|
|
Run-time methods:
|
|
UpdatePages : Used internally by the TSynEditPrintPreview component
|
|
PrintToCanvas : Used internally by the TSynEditPrintPreview component
|
|
Print : Prints the contents of the Lines property
|
|
PrintRange(StartPage,EndPage) : Prints the specified page-range (both inclusive)
|
|
-------------------------------------------------------------------------------}
|
|
|
|
{$IFNDEF QSYNEDITPRINT}
|
|
unit SynEditPrint;
|
|
{$ENDIF}
|
|
|
|
{$M+}
|
|
{$I SynEdit.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF SYN_COMPILER_17_UP}
|
|
UITypes,
|
|
{$ENDIF}
|
|
Windows,
|
|
Graphics,
|
|
Printers,
|
|
SynEdit,
|
|
SynEditTypes,
|
|
SynEditPrintTypes,
|
|
SynEditPrintHeaderFooter,
|
|
SynEditPrinterInfo,
|
|
SynEditPrintMargins,
|
|
SynEditMiscProcs,
|
|
SynEditHighlighter,
|
|
SynUnicode,
|
|
SysUtils,
|
|
Classes;
|
|
|
|
type
|
|
TPageLine = class
|
|
public
|
|
FirstLine: Integer;
|
|
end;
|
|
//The actual print controller object
|
|
TSynEditPrint = class(TComponent)
|
|
private
|
|
FCopies: Integer;
|
|
FFooter: TFooter;
|
|
FHeader: THeader;
|
|
FLines: TUnicodeStrings;
|
|
FMargins: TSynEditPrintMargins;
|
|
FPageCount: Integer;
|
|
FFont: TFont;
|
|
FTitle: UnicodeString;
|
|
FDocTitle: UnicodeString;
|
|
FPrinterInfo: TSynEditPrinterInfo;
|
|
FPages: TList;
|
|
FCanvas: TCanvas;
|
|
FCharWidth: Integer;
|
|
FMaxLeftChar: Integer;
|
|
FWrap: Boolean;
|
|
FOnPrintLine: TPrintLineEvent;
|
|
FOnPrintStatus: TPrintStatusEvent;
|
|
FYPos: Integer;
|
|
FLineHeight: Integer;
|
|
FHighlight: Boolean;
|
|
FColors: Boolean;
|
|
FHighlighter: TSynCustomHighlighter;
|
|
FOldFont: TFont;
|
|
FSynOK: Boolean;
|
|
FLineNumbers: Boolean;
|
|
FLineNumber: Integer;
|
|
FLineOffset: Integer;
|
|
FAbort: Boolean;
|
|
FPrinting: Boolean;
|
|
FDefaultBG: TColor;
|
|
FPageOffset: Integer;
|
|
FRangesOK: Boolean;
|
|
FMaxWidth: Integer;
|
|
FMaxCol: Integer;
|
|
FPagesCounted: Boolean;
|
|
FLineNumbersInMargin: Boolean;
|
|
FTabWidth: Integer;
|
|
FFontColor: TColor;
|
|
FSelectedOnly: Boolean;
|
|
FSelAvail: Boolean;
|
|
FSelMode: TSynSelectionMode;
|
|
FBlockBegin: TBufferCoord;
|
|
FBlockEnd: TBufferCoord;
|
|
FETODist: PIntArray;
|
|
procedure CalcPages;
|
|
procedure PrintPage(Num: Integer);
|
|
procedure WriteLine(const Text: UnicodeString);
|
|
procedure WriteLineNumber;
|
|
procedure HandleWrap(const Text: UnicodeString; MaxWidth: Integer);
|
|
procedure TextOut(const Text: UnicodeString; AList: TList);
|
|
procedure RestoreCurrentFont;
|
|
procedure SaveCurrentFont;
|
|
procedure SetPixelsPrInch;
|
|
procedure InitPrint;
|
|
procedure InitRanges;
|
|
function GetPageCount: Integer;
|
|
procedure SetSynEdit(const Value: TCustomSynEdit);
|
|
procedure SetFooter(const Value: TFooter);
|
|
procedure SetHeader(const Value: THeader);
|
|
procedure SetMargins(const Value: TSynEditPrintMargins);
|
|
procedure SetHighlighter(const Value: TSynCustomHighlighter);
|
|
procedure SetLines(const Value: TUnicodeStrings);
|
|
procedure SetFont(const Value: TFont);
|
|
procedure SetCharWidth(const Value: Integer);
|
|
procedure SetMaxLeftChar(const Value: Integer);
|
|
function ClipLineToRect(S: UnicodeString; R: TRect): UnicodeString;
|
|
function ExpandAtWideGlyphs(const S: UnicodeString): UnicodeString;
|
|
protected
|
|
procedure DefineProperties(Filer: TFiler); override;
|
|
property MaxLeftChar: Integer read FMaxLeftChar write SetMaxLeftChar;
|
|
property CharWidth: Integer read FCharWidth write SetCharWidth;
|
|
procedure PrintStatus(Status: TSynPrintStatus; PageNumber: Integer;
|
|
var Abort: Boolean); virtual;
|
|
procedure PrintLine(LineNumber, PageNumber: Integer); virtual;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure UpdatePages(ACanvas: TCanvas);
|
|
procedure PrintToCanvas(ACanvas: TCanvas; PageNumber: Integer);
|
|
procedure Print;
|
|
procedure PrintRange(StartPage, EndPage: Integer);
|
|
property PrinterInfo: TSynEditPrinterInfo read FPrinterInfo;
|
|
property PageCount: Integer read GetPageCount;
|
|
property SynEdit: TCustomSynEdit write SetSynEdit;
|
|
|
|
procedure LoadFromStream(AStream: TStream);
|
|
procedure SaveToStream(AStream: TStream);
|
|
published
|
|
property Copies: Integer read FCopies write FCopies;
|
|
property Header: THeader read FHeader write SetHeader;
|
|
property Footer: TFooter read FFooter write SetFooter;
|
|
property Margins: TSynEditPrintMargins read FMargins write SetMargins;
|
|
property Lines: TUnicodeStrings read FLines write SetLines;
|
|
property Font: TFont read FFont write SetFont;
|
|
property Title: UnicodeString read FTitle write FTitle;
|
|
property DocTitle: UnicodeString read FDocTitle write FDocTitle;
|
|
property Wrap: Boolean read FWrap write FWrap default True;
|
|
property Highlight: Boolean read FHighlight write FHighlight default True;
|
|
property SelectedOnly: Boolean read FSelectedOnly write FSelectedOnly
|
|
default False;
|
|
property Colors: Boolean read FColors write FColors default False;
|
|
property LineNumbers: Boolean read FLineNumbers write FLineNumbers
|
|
default False;
|
|
property LineOffset: Integer read FLineOffset write FLineOffset default 0;
|
|
property PageOffset: Integer read FPageOffset write FPageOffset default 0;
|
|
property OnPrintLine: TPrintLineEvent read FOnPrintLine write FOnPrintLine;
|
|
property OnPrintStatus: TPrintStatusEvent read FOnPrintStatus
|
|
write FOnPrintStatus;
|
|
property Highlighter: TSynCustomHighlighter read FHighlighter
|
|
write SetHighlighter;
|
|
property LineNumbersInMargin: Boolean read FLineNumbersInMargin
|
|
write FLineNumbersInMargin default False;
|
|
property TabWidth: Integer read fTabWidth write fTabWidth;
|
|
property Color: TColor read fDefaultBG write fDefaultBG;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Math;
|
|
|
|
{ TSynEditPrint }
|
|
|
|
constructor TSynEditPrint.Create(AOwner: TComponent);
|
|
begin
|
|
inherited;
|
|
FCopies := 1;
|
|
FFooter := TFooter.Create;
|
|
FHeader := THeader.Create;
|
|
FLines := TUnicodeStringList.Create;
|
|
FMargins := TSynEditPrintMargins.Create;
|
|
FPrinterInfo := TSynEditPrinterInfo.Create;
|
|
FFont := TFont.Create;
|
|
FOldFont := TFont.Create;
|
|
MaxLeftChar := 1024;
|
|
FWrap := True;
|
|
FHighlight := True;
|
|
FColors := False;
|
|
FLineNumbers := False;
|
|
FLineOffset := 0;
|
|
FPageOffset := 0;
|
|
FLineNumbersInMargin := False;
|
|
FPages := TList.Create;
|
|
FTabWidth := 8;
|
|
FDefaultBG := clWhite;
|
|
end;
|
|
|
|
destructor TSynEditPrint.Destroy;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
FFooter.Free;
|
|
FHeader.Free;
|
|
FLines.Free;
|
|
FMargins.Free;
|
|
FPrinterInfo.Free;
|
|
FFont.Free;
|
|
FOldFont.Free;
|
|
for i := 0 to FPages.Count - 1 do
|
|
TPageLine(FPages[i]).Free;
|
|
FPages.Free;
|
|
FreeMem(FETODist);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TSynEditPrint.DefineProperties(Filer: TFiler);
|
|
begin
|
|
inherited;
|
|
{$IFNDEF UNICODE}
|
|
UnicodeDefineProperties(Filer, Self);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetLines(const Value: TUnicodeStrings);
|
|
var
|
|
i, j: Integer;
|
|
ConvertTabsProc: TConvertTabsProc;
|
|
TmpString: UnicodeString;
|
|
begin
|
|
ConvertTabsProc := GetBestConvertTabsProc(FTabWidth);
|
|
with FLines do
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
Clear;
|
|
for i := 0 to Value.Count - 1 do
|
|
begin
|
|
TmpString := ConvertTabsProc(Value[i], FTabWidth);
|
|
j := Pos(#9, TmpString);
|
|
while j > 0 do
|
|
begin
|
|
TmpString[j] := ' ';
|
|
j := Pos(#9, TmpString);
|
|
end;
|
|
Add(TmpString);
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
FRangesOK := False;
|
|
FPagesCounted := False;
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetFont(const Value: TFont);
|
|
begin
|
|
FFont.Assign(Value);
|
|
FPagesCounted := False;
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetCharWidth(const Value: Integer);
|
|
begin
|
|
FCharWidth := Value;
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetMaxLeftChar(const Value: Integer);
|
|
begin
|
|
FMaxLeftChar := Value;
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetHighlighter(const Value: TSynCustomHighlighter);
|
|
begin
|
|
FHighlighter := Value;
|
|
FRangesOK := False;
|
|
FPagesCounted := False;
|
|
end;
|
|
|
|
// Inserts filling chars into a string containing chars that display as glyphs
|
|
// wider than an average glyph. (This is often the case with Asian glyphs, which
|
|
// are usually wider than latin glpyhs)
|
|
// This is only to simplify paint-operations and has nothing to do with
|
|
// multi-byte chars.
|
|
function TSynEditPrint.ExpandAtWideGlyphs(const S: UnicodeString): UnicodeString;
|
|
var
|
|
i, j, CountOfAvgGlyphs: Integer;
|
|
begin
|
|
FCanvas.Font := Font;
|
|
|
|
j := 0;
|
|
SetLength(Result, Length(S) * 2); // speed improvement
|
|
for i := 1 to Length(S) do
|
|
begin
|
|
Inc(j);
|
|
CountOfAvgGlyphs := Ceil(TextWidth(FCanvas, S[i]) / fCharWidth);
|
|
|
|
if j + CountOfAvgGlyphs > Length(Result) then
|
|
SetLength(Result, Length(Result) + 128);
|
|
|
|
// insert CountOfAvgGlyphs filling chars
|
|
while CountOfAvgGlyphs > 1 do
|
|
begin
|
|
Result[j] := FillerChar;
|
|
Inc(j);
|
|
Dec(CountOfAvgGlyphs);
|
|
end;
|
|
|
|
Result[j] := S[i];
|
|
end;
|
|
|
|
SetLength(Result, j);
|
|
end;
|
|
|
|
procedure TSynEditPrint.InitPrint;
|
|
{ Initialize Font.PixelsPerInch, Character widths, Margins, Total Page count,
|
|
headers and footers}
|
|
var
|
|
TmpSize: Integer;
|
|
TmpTextMetrics: TTextMetric;
|
|
begin
|
|
// FDefaultBG := FCanvas.Brush.Color;
|
|
FFontColor := FFont.Color;
|
|
FCanvas.Font.Assign(FFont);
|
|
if not FPrinting then
|
|
begin
|
|
SetPixelsPrInch;
|
|
TmpSize := FCanvas.Font.Size;
|
|
FCanvas.Font.PixelsPerInch := FFont.PixelsPerInch;
|
|
FCanvas.Font.Size := TmpSize;
|
|
end;
|
|
// Calculate TextMetrics with the (probably) most wider text styles so text is
|
|
// never clipped (although potentially wasting space)
|
|
FCanvas.Font.Style := [fsBold, fsItalic, fsUnderline, fsStrikeOut];
|
|
GetTextMetrics(FCanvas.Handle, TmpTextMetrics);
|
|
CharWidth := TmpTextMetrics.tmAveCharWidth;
|
|
FLineHeight := TmpTextMetrics.tmHeight + TmpTextMetrics.tmExternalLeading;
|
|
FCanvas.Font.Style := FFont.Style;
|
|
FMargins.InitPage(FCanvas, 1, FPrinterInfo, FLineNumbers, FLineNumbersInMargin,
|
|
FLines.Count - 1 + FLineOffset);
|
|
CalcPages;
|
|
FHeader.InitPrint(FCanvas, FPageCount, FTitle, FMargins);
|
|
FFooter.InitPrint(FCanvas, FPageCount, FTitle, FMargins);
|
|
FSynOK := Highlight and Assigned(FHighlighter) and (FLines.Count > 0);
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetPixelsPrInch;
|
|
var
|
|
TmpSize: Integer;
|
|
begin
|
|
FHeader.SetPixPrInch(FPrinterInfo.YPixPrInch);
|
|
FFooter.SetPixPrInch(FPrinterInfo.YPixPrInch);
|
|
//This should be necessary - else size would be changed...
|
|
TmpSize := FFont.Size;
|
|
FFont.PixelsPerInch := FPrinterInfo.YPixPrInch;
|
|
FFont.Size := TmpSize;
|
|
end;
|
|
|
|
procedure TSynEditPrint.InitRanges;
|
|
//Initialize ranges in Highlighter
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if not FRangesOK and Assigned(FHighlighter) and (Lines.Count > 0) then
|
|
begin
|
|
FHighlighter.ResetRange;
|
|
FLines.Objects[0] := FHighlighter.GetRange;
|
|
i := 1;
|
|
while i < Lines.Count do
|
|
begin
|
|
FHighlighter.SetLine(FLines[i - 1], i - 1);
|
|
FHighlighter.NextToEol;
|
|
FLines.Objects[i] := FHighlighter.GetRange;
|
|
Inc(i);
|
|
end;
|
|
FRangesOK := True;
|
|
end;
|
|
end;
|
|
|
|
// Calculates the total number of pages
|
|
procedure TSynEditPrint.CalcPages;
|
|
var
|
|
AStr, Text: UnicodeString;
|
|
StrWidth: Integer;
|
|
i, j: Integer;
|
|
AList: TList;
|
|
YPos: Integer;
|
|
PageLine: TPageLine;
|
|
|
|
//Counts the number of lines a line is wrapped to
|
|
procedure CountWrapped;
|
|
var
|
|
j: Integer;
|
|
begin
|
|
for j := 0 to AList.Count - 1 do
|
|
YPos := YPos + FLineHeight;
|
|
end;
|
|
|
|
var
|
|
iStartLine, iEndLine: Integer;
|
|
iSelStart, iSelLen: Integer;
|
|
begin
|
|
InitRanges;
|
|
for i := 0 to FPages.Count - 1 do
|
|
TPageLine(FPages[i]).Free;
|
|
FPages.Clear;
|
|
FMaxWidth := FMargins.PRight - FMargins.PLeft;
|
|
AStr := '';
|
|
FMaxCol := 0;
|
|
while TextWidth(FCanvas, AStr) < FMaxWidth do
|
|
begin
|
|
AStr := AStr + 'W';
|
|
FMaxCol := FMaxCol + 1;
|
|
end;
|
|
FMaxCol := FMaxCol - 1;
|
|
{FTestString is used to Calculate MaxWidth when prewiewing and printing -
|
|
else the length is not calculated correctly when prewiewing and the
|
|
zoom is different from 0.25,0.5,1,2,4 (as for example 1.20) - WHY???}
|
|
// fTestString := UnicodeStringOfChar('W', FMaxCol);
|
|
AStr := UnicodeStringOfChar('W', FMaxCol);
|
|
FMaxWidth := TextWidth(FCanvas, AStr);
|
|
FPageCount := 1;
|
|
PageLine := TPageLine.Create;
|
|
PageLine.FirstLine := 0;
|
|
FPages.Add(PageLine);
|
|
YPos := FMargins.PTop;
|
|
if SelectedOnly then
|
|
begin
|
|
iStartLine := FBlockBegin.Line -1;
|
|
iEndLine := FBlockEnd.Line -1;
|
|
end
|
|
else begin
|
|
iStartLine := 0;
|
|
iEndLine := Lines.Count -1;
|
|
end;
|
|
for i := iStartLine to iEndLine do
|
|
begin
|
|
if not FSelectedOnly or (FSelMode = smLine) then
|
|
Text := Lines[i]
|
|
else
|
|
begin
|
|
if (FSelMode = smColumn) or (i = FBlockBegin.Line -1) then
|
|
iSelStart := FBlockBegin.Char
|
|
else
|
|
iSelStart := 1;
|
|
if (FSelMode = smColumn) or (i = FBlockEnd.Line -1) then
|
|
iSelLen := FBlockEnd.Char - iSelStart
|
|
else
|
|
iSelLen := MaxInt;
|
|
Text := Copy( Lines[i], iSelStart, iSelLen );
|
|
end;
|
|
{if new page then increase FPageCount and save the top-line number in
|
|
FPages}
|
|
if YPos + FLineHeight > FMargins.PBottom then
|
|
begin
|
|
YPos := FMargins.PTop;
|
|
FPageCount := FPageCount + 1;
|
|
PageLine := TPageLine.Create;
|
|
PageLine.FirstLine := i;
|
|
FPages.Add(PageLine);
|
|
end;
|
|
StrWidth := TextWidth(FCanvas, Text);
|
|
{Check for wrap}
|
|
if Wrap and (StrWidth > FMaxWidth) then begin
|
|
AList := TList.Create;
|
|
if WrapTextEx(Text, [' ', '-', #9, ','], FMaxCol, AList) then
|
|
CountWrapped
|
|
else
|
|
begin
|
|
{If WrapTextToList didn't succed with the first set of breakchars
|
|
then try this one:}
|
|
if WrapTextEx(Text, [';', ')', '.'], FMaxCol, AList) then
|
|
CountWrapped
|
|
else
|
|
begin
|
|
{If WrapTextToList didn't succed at all, then do it the
|
|
primitive way}
|
|
while Length(Text) > 0 do
|
|
begin
|
|
AStr := Copy(Text, 1, FMaxCol);
|
|
Delete(Text, 1, FMaxCol);
|
|
if Length(Text) > 0 then
|
|
YPos := YPos + FLineHeight;
|
|
end;
|
|
end;
|
|
end;
|
|
for j := 0 to AList.Count - 1 do
|
|
TWrapPos(AList[j]).Free;
|
|
AList.Free;
|
|
end;
|
|
YPos := YPos + FLineHeight;
|
|
end;
|
|
FPagesCounted := True;
|
|
end;
|
|
|
|
{ Writes the line number. FMargins. PLeft is the position of the left margin
|
|
(which is automatically incremented by the length of the linenumber text, if
|
|
the linenumbers should not be placed in the margin) }
|
|
procedure TSynEditPrint.WriteLineNumber;
|
|
var
|
|
AStr: UnicodeString;
|
|
begin
|
|
SaveCurrentFont;
|
|
AStr := IntToStr(FLineNumber + FLineOffset) + ': ';
|
|
FCanvas.Brush.Color := FDefaultBG;
|
|
FCanvas.Font.Style := [];
|
|
FCanvas.Font.Color := clBlack;
|
|
SynUnicode.TextOut(FCanvas, FMargins.PLeft - TextWidth(FCanvas, AStr), FYPos, AStr);
|
|
RestoreCurrentFont;
|
|
end;
|
|
|
|
procedure TSynEditPrint.HandleWrap(const Text: UnicodeString; MaxWidth: Integer);
|
|
var
|
|
AStr: UnicodeString;
|
|
AList: TList;
|
|
j: Integer;
|
|
|
|
procedure WrapPrimitive;
|
|
var
|
|
i: Integer;
|
|
WrapPos: TWrapPos;
|
|
begin
|
|
i := 1;
|
|
while i <= Length(Text) do
|
|
begin
|
|
AStr := '';
|
|
while (Length(AStr) < FMaxCol) and (i <= Length(Text)) do
|
|
begin
|
|
AStr := AStr + Text[i];
|
|
i := i + 1;
|
|
end;
|
|
WrapPos := TWrapPos.Create;
|
|
WrapPos.Index := i - 1;
|
|
AList.Add(WrapPos);
|
|
if (Length(AStr) - i) <= FMaxCol then
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
AStr := '';
|
|
//First try to break the string at the following chars:
|
|
AList := TList.Create;
|
|
if WrapTextEx(Text, [' ', '-', #9, ','], FMaxCol, AList) then
|
|
TextOut(Text, AList)
|
|
else
|
|
begin
|
|
//Then try to break the string at the following chars:
|
|
if WrapTextEx(Text, [';', ')', '.'], FMaxCol, AList) then
|
|
TextOut(Text, AList)
|
|
else
|
|
begin
|
|
WrapPrimitive;
|
|
TextOut(Text, AList)
|
|
end;
|
|
end;
|
|
for j := 0 to AList.Count - 1 do
|
|
TWrapPos(Alist[j]).Free;
|
|
AList.Free;
|
|
end;
|
|
|
|
procedure TSynEditPrint.SaveCurrentFont;
|
|
begin
|
|
FOldFont.Assign(FCanvas.Font);
|
|
end;
|
|
|
|
procedure TSynEditPrint.RestoreCurrentFont;
|
|
begin
|
|
FCanvas.Font.Assign(FOldFont);
|
|
end;
|
|
|
|
function TSynEditPrint.ClipLineToRect(S: UnicodeString; R: TRect): UnicodeString;
|
|
begin
|
|
while TextWidth(FCanvas, S) > FMaxWidth do
|
|
SetLength(S, Length(S) - 1);
|
|
|
|
Result := S;
|
|
end;
|
|
|
|
//Does the actual printing
|
|
procedure TSynEditPrint.TextOut(const Text: UnicodeString; AList: TList);
|
|
var
|
|
Token: UnicodeString;
|
|
TokenPos: Integer;
|
|
Attr: TSynHighlighterAttributes;
|
|
AColor: TColor;
|
|
TokenStart: Integer;
|
|
LCount: Integer;
|
|
Handled: Boolean;
|
|
aStr: UnicodeString;
|
|
i, WrapPos, OldWrapPos: Integer;
|
|
Lines: TUnicodeStringList;
|
|
ClipRect: TRect;
|
|
sLine, sLineExpandedAtWideGlyphs: UnicodeString;
|
|
ExpandedPos: Integer;
|
|
|
|
procedure InitETODist(CharWidth: Integer; const Text: UnicodeString);
|
|
var
|
|
Size: TSize;
|
|
i: Integer;
|
|
begin
|
|
ReallocMem(FETODist, Length(Text) * SizeOf(Integer));
|
|
for i := 0 to Length(Text) - 1 do
|
|
begin
|
|
Size := GetTextSize(FCanvas.Handle, @Text[i + 1], 1);
|
|
FETODist[i] := Ceil(Size.cx / CharWidth) * CharWidth;
|
|
end;
|
|
end;
|
|
|
|
procedure ClippedTextOut(X, Y: Integer; Text: UnicodeString);
|
|
begin
|
|
Text := ClipLineToRect(Text, ClipRect);
|
|
InitETODist(FCharWidth, Text);
|
|
Windows.ExtTextOutW(FCanvas.Handle, X, Y, 0, nil, PWideChar(Text),
|
|
Length(Text), PInteger(FETODist));
|
|
end;
|
|
|
|
procedure SplitToken;
|
|
var
|
|
AStr: UnicodeString;
|
|
Last: Integer;
|
|
FirstPos: Integer;
|
|
TokenEnd: Integer;
|
|
begin
|
|
Last := TokenPos;
|
|
FirstPos := TokenPos;
|
|
TokenEnd := TokenPos + Length(Token);
|
|
while (LCount < AList.Count) and (TokenEnd > TWrapPos(AList[LCount]).Index) do
|
|
begin
|
|
AStr := Copy(Text, Last + 1, TWrapPos(AList[LCount]).Index - Last);
|
|
Last := TWrapPos(AList[LCount]).Index;
|
|
ExpandedPos := FHighlighter.PosToExpandedPos(FirstPos);
|
|
ClippedTextOut(FMargins.PLeft + ExpandedPos * FCharWidth, FYPos, AStr);
|
|
FirstPos := 0;
|
|
LCount := LCount + 1;
|
|
FYPos := FYPos + FLineHeight;
|
|
end;
|
|
AStr := Copy(Text, Last + 1, TokenEnd - Last);
|
|
ExpandedPos := FHighlighter.PosToExpandedPos(FirstPos);
|
|
ClippedTextOut(FMargins.PLeft + ExpandedPos * FCharWidth, FYPos, AStr);
|
|
//Ready for next token:
|
|
TokenStart := TokenPos + Length(Token) - Length(AStr);
|
|
end;
|
|
begin
|
|
with FMargins do
|
|
ClipRect := Rect(PLeft, PTop, PRight, PBottom);
|
|
|
|
if FSynOK then
|
|
begin
|
|
SaveCurrentFont;
|
|
FHighlighter.SetRange(FLines.Objects[FLineNumber - 1]);
|
|
sLine := Text;
|
|
sLineExpandedAtWideGlyphs := ExpandAtWideGlyphs(sLine);
|
|
FHighlighter.SetLineExpandedAtWideGlyphs(sLine, sLineExpandedAtWideGlyphs, FLineNumber);
|
|
|
|
Token := '';
|
|
TokenStart := 0;
|
|
LCount := 0;
|
|
while not FHighlighter.GetEol do
|
|
begin
|
|
Token := FHighlighter.GetToken;
|
|
TokenPos := FHighlighter.GetTokenPos;
|
|
Attr := FHighlighter.GetTokenAttribute;
|
|
if Assigned(Attr) then
|
|
begin
|
|
FCanvas.Font.Style := Attr.Style;
|
|
if FColors then
|
|
begin
|
|
AColor := Attr.Foreground;
|
|
if AColor = clNone then
|
|
AColor := FFont.Color;
|
|
FCanvas.Font.Color := AColor;
|
|
AColor := Attr.Background;
|
|
if AColor = clNone then
|
|
AColor := FDefaultBG;
|
|
FCanvas.Brush.Color := AColor;
|
|
end
|
|
else
|
|
begin
|
|
FCanvas.Font.Color := FFontColor;
|
|
FCanvas.Brush.Color := FDefaultBG;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
FCanvas.Font.Color := FFontColor;
|
|
FCanvas.Brush.Color := FDefaultBG;
|
|
end;
|
|
Handled := False;
|
|
if Assigned(AList) then
|
|
begin
|
|
if (LCount < AList.Count) then
|
|
begin
|
|
//Split between tokens:
|
|
if (TokenPos >= TWrapPos(AList[LCount]).Index) then
|
|
begin
|
|
LCount := LCount + 1;
|
|
TokenStart := TokenPos;
|
|
FYPos := FYPos + FLineHeight;
|
|
end
|
|
else
|
|
begin
|
|
//Split in the middle of a token:
|
|
if (TokenPos + Length(Token) > TWrapPos(AList[LCount]).Index) then begin
|
|
Handled := True;
|
|
SplitToken;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
if not Handled then
|
|
begin
|
|
ExpandedPos := FHighlighter.PosToExpandedPos(TokenPos - TokenStart);
|
|
ClippedTextOut(FMargins.PLeft + ExpandedPos * FCharWidth, FYPos, Token);
|
|
end;
|
|
FHighlighter.Next;
|
|
end;
|
|
RestoreCurrentFont;
|
|
end
|
|
else
|
|
begin
|
|
Lines := TUnicodeStringList.Create;
|
|
try
|
|
OldWrapPos := 0;
|
|
if Assigned(AList) then
|
|
for i := 0 to AList.Count - 1 do
|
|
begin
|
|
WrapPos := TWrapPos(AList[i]).Index;
|
|
if i = 0 then
|
|
AStr := Copy(Text, 1, WrapPos)
|
|
else
|
|
AStr := Copy(Text, OldWrapPos + 1, WrapPos - OldWrapPos);
|
|
Lines.Add(AStr);
|
|
OldWrapPos := WrapPos;
|
|
end;
|
|
if Length(Text) > 0 then
|
|
Lines.Add(Copy(Text, OldWrapPos + 1, MaxInt));
|
|
|
|
for i := 0 to Lines.Count - 1 do
|
|
begin
|
|
ClippedTextOut(FMargins.PLeft, FYPos, Lines[i]);
|
|
if i < Lines.Count - 1 then
|
|
FYPos := FYPos + FLineHeight;
|
|
end;
|
|
finally
|
|
Lines.Free;
|
|
end
|
|
end
|
|
end;
|
|
|
|
procedure TSynEditPrint.WriteLine(const Text: UnicodeString);
|
|
var
|
|
StrWidth: Integer;
|
|
begin
|
|
if FLineNumbers then WriteLineNumber;
|
|
StrWidth := TextWidth(FCanvas, Text);
|
|
{Note that MaxWidth is calculated, using FTestString found in CalcPages -
|
|
else the length is not calculated correctly when prewiewing and the
|
|
zoom is different from 0.25,0.5,1,2,4 (as for example 1.20) - WHY???
|
|
}
|
|
if Wrap and (StrWidth > FMaxWidth) then
|
|
HandleWrap(Text, FMaxWidth)
|
|
else
|
|
TextOut(Text, nil);
|
|
FYPos := FYPos + FLineHeight;
|
|
end;
|
|
|
|
procedure TSynEditPrint.PrintPage(Num: Integer);
|
|
//Prints a page
|
|
var
|
|
i, iEnd: Integer;
|
|
iSelStart, iSelLen: Integer;
|
|
begin
|
|
PrintStatus(psNewPage, Num, FAbort);
|
|
if not FAbort then
|
|
begin
|
|
FCanvas.Brush.Color := Color;
|
|
with FMargins do
|
|
FCanvas.FillRect(Rect(PLeft, PTop, PRight, PBottom));
|
|
FMargins.InitPage(FCanvas, Num, FPrinterInfo, FLineNumbers,
|
|
FLineNumbersInMargin, FLines.Count - 1 + FLineOffset);
|
|
FHeader.Print(FCanvas, Num + FPageOffset);
|
|
if FPages.Count > 0 then
|
|
begin
|
|
FYPos := FMargins.PTop;
|
|
if Num = FPageCount then
|
|
iEnd := FLines.Count - 1
|
|
else
|
|
iEnd := TPageLine(FPages[Num]).FirstLine - 1;
|
|
for i := TPageLine(FPages[Num - 1]).FirstLine to iEnd do
|
|
begin
|
|
FLineNumber := i + 1;
|
|
if (not FSelectedOnly or ((i >= FBlockBegin.Line - 1) and (i <= FBlockEnd.Line - 1))) then begin
|
|
if (not FSelectedOnly or (FSelMode = smLine)) then
|
|
WriteLine(Lines[i])
|
|
else
|
|
begin
|
|
if (FSelMode = smColumn) or (i = FBlockBegin.Line -1) then
|
|
iSelStart := FBlockBegin.Char
|
|
else
|
|
iSelStart := 1;
|
|
if (FSelMode = smColumn) or (i = FBlockEnd.Line -1) then
|
|
iSelLen := FBlockEnd.Char - iSelStart
|
|
else
|
|
iSelLen := MaxInt;
|
|
WriteLine( Copy( Lines[i], iSelStart, iSelLen ) );
|
|
end;
|
|
PrintLine(i + 1, Num);
|
|
end;
|
|
end;
|
|
end;
|
|
FFooter.Print(FCanvas, Num + FPageOffset);
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditPrint.UpdatePages(ACanvas: TCanvas);
|
|
//Update pages (called explicitly by preview component)
|
|
begin
|
|
FCanvas := ACanvas;
|
|
FPrinterInfo.UpdatePrinter;
|
|
InitPrint;
|
|
end;
|
|
|
|
procedure TSynEditPrint.PrintToCanvas(ACanvas: TCanvas; PageNumber: Integer);
|
|
//Print to specified canvas. Used by preview component
|
|
begin
|
|
FAbort := False;
|
|
FPrinting := False;
|
|
FCanvas := ACanvas;
|
|
PrintPage(PageNumber);
|
|
end;
|
|
|
|
procedure TSynEditPrint.Print;
|
|
begin
|
|
PrintRange(1, -1);
|
|
end;
|
|
|
|
procedure TSynEditPrint.PrintRange(StartPage, EndPage: Integer);
|
|
//Prints the pages in the specified range
|
|
var
|
|
i, ii: Integer;
|
|
begin
|
|
if FSelectedOnly and not FSelAvail then
|
|
Exit;
|
|
|
|
FPrinting := True;
|
|
FAbort := False;
|
|
// The next part sets the document title that is used by the printer queue.
|
|
if FDocTitle <> '' then
|
|
Printer.Title := FDocTitle
|
|
else
|
|
Printer.Title := FTitle;
|
|
Printer.BeginDoc;
|
|
PrintStatus(psBegin, StartPage, FAbort);
|
|
UpdatePages(Printer.Canvas);
|
|
|
|
for ii:=1 to Copies do
|
|
begin
|
|
i := StartPage;
|
|
if EndPage < 0 then
|
|
EndPage := FPageCount;
|
|
while (i <= EndPage) and (not FAbort) do begin
|
|
PrintPage(i);
|
|
if ((i < EndPage) or (ii<Copies)) and not FAbort then
|
|
Printer.NewPage;
|
|
i := i + 1;
|
|
end;
|
|
end;
|
|
if not FAbort then
|
|
PrintStatus(psEnd, EndPage, FAbort);
|
|
Printer.EndDoc;
|
|
FPrinting := False;
|
|
end;
|
|
|
|
procedure TSynEditPrint.PrintLine(LineNumber, PageNumber: Integer);
|
|
//Fires the OnPrintLine event
|
|
begin
|
|
if Assigned(FOnPrintLine) then
|
|
FOnPrintLine(Self, LineNumber, PageNumber);
|
|
end;
|
|
|
|
procedure TSynEditPrint.PrintStatus(Status: TSynPrintStatus;
|
|
PageNumber: Integer; var Abort: Boolean);
|
|
//Fires the OnPrintStatus event
|
|
begin
|
|
Abort := False;
|
|
if Assigned(FOnPrintStatus) then
|
|
FOnPrintStatus(Self, Status, PageNumber, Abort);
|
|
if Abort then begin
|
|
if FPrinting then
|
|
Printer.Abort;
|
|
end;
|
|
end;
|
|
|
|
function TSynEditPrint.GetPageCount: Integer;
|
|
{Returns total page count. If pages hasn't been counted before,
|
|
then a UpdatePages is called with a temporary canvas}
|
|
var
|
|
TmpCanvas: TCanvas;
|
|
DC: HDC;
|
|
begin
|
|
Result := 0;
|
|
if FPagesCounted then
|
|
Result := FPageCount
|
|
else begin
|
|
TmpCanvas := TCanvas.Create;
|
|
try
|
|
DC := GetDC(0);
|
|
try
|
|
if DC <> 0 then
|
|
begin
|
|
TmpCanvas.Handle := DC;
|
|
UpdatePages(TmpCanvas);
|
|
TmpCanvas.Handle := 0;
|
|
Result := FPageCount;
|
|
FPagesCounted := True;
|
|
end;
|
|
finally
|
|
ReleaseDC(0, DC);
|
|
end;
|
|
finally
|
|
TmpCanvas.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetSynEdit(const Value: TCustomSynEdit);
|
|
begin
|
|
// Lines := Value.Lines;
|
|
HighLighter := Value.Highlighter;
|
|
Font := Value.Font;
|
|
FTabWidth := Value.TabWidth;
|
|
Lines := Value.Lines;
|
|
FSelAvail := Value.SelAvail;
|
|
FBlockBegin := Value.BlockBegin;
|
|
FBlockEnd := Value.BlockEnd;
|
|
FSelMode := Value.SelectionMode;
|
|
end;
|
|
|
|
procedure TSynEditPrint.LoadFromStream(AStream: TStream);
|
|
var
|
|
Len, BufferSize: Integer;
|
|
Buffer: PWideChar;
|
|
begin
|
|
FHeader.LoadFromStream(AStream);
|
|
FFooter.LoadFromStream(AStream);
|
|
FMargins.LoadFromStream(AStream);
|
|
with AStream do
|
|
begin
|
|
Read(Len, sizeof(Len));
|
|
BufferSize := Len * sizeof(WideChar);
|
|
GetMem(Buffer, BufferSize + sizeof(WideChar));
|
|
try
|
|
Read(Buffer^, BufferSize);
|
|
Buffer[BufferSize div sizeof(WideChar)] := #0;
|
|
FTitle := Buffer;
|
|
finally
|
|
FreeMem(Buffer);
|
|
end;
|
|
Read(Len, sizeof(Len));
|
|
BufferSize := Len * sizeof(WideChar);
|
|
GetMem(Buffer, BufferSize + sizeof(WideChar));
|
|
try
|
|
Read(Buffer^, BufferSize);
|
|
Buffer[BufferSize div sizeof(WideChar)] := #0;
|
|
FDocTitle := Buffer;
|
|
finally
|
|
FreeMem(Buffer);
|
|
end;
|
|
Read(FWrap, SizeOf(FWrap));
|
|
Read(FHighlight, SizeOf(FHighlight));
|
|
Read(FColors, SizeOf(FColors));
|
|
Read(FLineNumbers, SizeOf(FLineNumbers));
|
|
Read(FLineOffset, SizeOf(FLineOffset));
|
|
Read(FPageOffset, SizeOf(FPageOffset));
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditPrint.SaveToStream(AStream: TStream);
|
|
var
|
|
aLen: Integer;
|
|
begin
|
|
FHeader.SaveToStream(AStream);
|
|
FFooter.SaveToStream(AStream);
|
|
FMargins.SaveToStream(AStream);
|
|
with AStream do
|
|
begin
|
|
aLen := Length(FTitle);
|
|
Write(aLen, SizeOf(aLen));
|
|
Write(PWideChar(FTitle)^, aLen * sizeof(WideChar));
|
|
aLen := Length(FDocTitle);
|
|
Write(aLen, SizeOf(aLen));
|
|
Write(PWideChar(FDocTitle)^, aLen * sizeof(WideChar));
|
|
Write(FWrap, SizeOf(FWrap));
|
|
Write(FHighlight, SizeOf(FHighlight));
|
|
Write(FColors, SizeOf(FColors));
|
|
Write(FLineNumbers, SizeOf(FLineNumbers));
|
|
Write(FLineOffset, SizeOf(FLineOffset));
|
|
Write(FPageOffset, SizeOf(FPageOffset));
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetFooter(const Value: TFooter);
|
|
begin
|
|
FFooter.Assign(Value);
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetHeader(const Value: THeader);
|
|
begin
|
|
FHeader.Assign(Value);
|
|
end;
|
|
|
|
procedure TSynEditPrint.SetMargins(const Value: TSynEditPrintMargins);
|
|
begin
|
|
FMargins.Assign(Value);
|
|
end;
|
|
|
|
end.
|
|
|