RTF ÑоÑмаÑÑÑ ÑÑÑеÑÑвÑва доÑÑаÑÑÑно дÑлго, за да Ñе поÑви на меÑÑа, за коиÑо никой не е планиÑал: ÑÑаÑи генеÑаÑоÑи на оÑÑеÑи, конвейеÑи за поÑа по Ñаблон, аÑÑ
иви Ñ Ð¿Ñавни докÑменÑи, ÑÑздадени пÑеди поÑваÑа на ÑÑвÑеменниÑе ÑекÑÑообÑабоÑваÑи пÑогÑами. ÐонвеÑÑиÑанеÑо Ð¼Ñ Ð² PDF в движение е повÑаÑÑÑа Ñе задаÑа, и подÑ
одÑÑ, койÑо Ñеално ÑабоÑи под Windows, не е ÑпеÑиализиÑан RTF паÑÑеÑ, а пÑÑÑÑ Ð·Ð° изобÑазÑване, пÑедоÑÑавен диÑекÑно Ð¾Ñ ÑÐ°Ð¼Ð¸Ñ Windows ÑÑез TRichEdit и EM_FORMATRANGE. DLL изданиеÑо на PDF библиоÑекаÑа на losLab излага виÑÑÑален конÑекÑÑ Ð½Ð° ÑÑÑÑойÑÑво, койÑо Ñе впиÑва диÑекÑно в Ñази веÑига.
ÐÐµÑ Ð°Ð½Ð¸Ð·Ð¼ÑÑ: виÑÑÑален DC и EM_FORMATRANGE
Rich Edit конÑÑолиÑе Ð¼Ð¾Ð³Ð°Ñ Ð´Ð° ÑазпÑеделÑÑ ÑÑдÑÑжаниеÑо Ñи за вÑеки конÑекÑÑ Ð½Ð° ÑÑÑÑойÑÑво (DC), не Ñамо за ÑизиÑеÑки пÑинÑеÑ. СÑобÑениеÑо EM_FORMATRANGE наÑежда на конÑÑолаÑа да наÑедобÑази диапазон Ð¾Ñ Ð·Ð½Ð°Ñи в даден DC и вÑÑÑа позиÑиÑÑа на поÑÐ»ÐµÐ´Ð½Ð¸Ñ Ð·Ð½Ð°Ðº, койÑо е ÑÑпÑла да вмеÑÑи. ÐзвиквайÑе го многокÑаÑно, ÑвелиÑавайки cpMin вÑеки пÑÑ, и полÑÑаваÑе изÑ
од ÑÑÑаниÑа по ÑÑÑаниÑа. ÐеÑодÑÑ GetCanvasDC на PDF библиоÑекаÑа на losLab пÑедоÑÑÐ°Ð²Ñ DC в памеÑÑа, оÑазмеÑен ÑпÑÑмо зададениÑе Ð¾Ñ Ð²Ð°Ñ ÑазмеÑи на ÑÑÑаниÑаÑа. След изобÑазÑванеÑо на ÑÑÑаниÑа в него, LoadFromCanvasDc ÑÐ»Ð°Ð²Ñ ÑезÑлÑаÑа каÑо PDF ÑÑÑаниÑа. Това е ÑÑлаÑа веÑига за обÑабоÑка.
Ðдно неÑо, коеÑо ÑÑÑбва да Ñе напÑави пÑавилно Ð¾Ñ ÑамоÑо наÑало: конÑÑолаÑа TRichEdit ÑÑÑбва да бÑде оÑазмеÑена да ÑÑоÑвеÑÑÑва на ÑелеваÑа ÑÑÑаниÑа. Ðко конÑÑолаÑа е по-малка или по-голÑма Ð¾Ñ ÑазмеÑиÑе на DC, ÑазпÑеделениеÑо на ÑÑÑаниÑиÑе нÑма да ÑÑвпадне Ñ ÐºÑÐ°Ð¹Ð½Ð¸Ñ PDF. Ðа изÑ
од в Ð4 ÑÑандаÑÑниÑÑ Ð¿Ð¾Ð´Ñ
од е да зададеÑе пикÑелниÑе ÑазмеÑи на конÑÑолаÑа Ñака, Ñе да ÑÑоÑвеÑÑÑÐ²Ð°Ñ Ð½Ð° 210 x 297 мм пÑи 96 DPI, пÑеди да заÑедиÑе RTF Ñайла, каÑо използваÑе ÑÑÑиÑе ÑпомагаÑелни ÑÑнкÑии за маÑабиÑане, Ñ ÐºÐ¾Ð¸Ñо Ñе оÑазмеÑиÑе DC.
ÐмплеменÑаÑÐ¸Ñ Ð² Delphi
СледноÑо използва импоÑÑÐ½Ð¸Ñ Ð¼Ð¾Ð´Ñл PDFlibAX_TLB, койÑо обвива DLL изданиеÑо на библиоÑекаÑа. ФоÑмаÑа ÑÑдÑÑжа TRichEdit и бÑÑон; обÑабоÑÑикÑÑ OnCreate на ÑоÑмаÑа оÑазмеÑÑва конÑÑолаÑа и заÑежда RTF Ñайла, а ÑÑакванеÑо вÑÑÑ
Ñ Ð±ÑÑона ÑпÑавлÑва ÑикÑла за конвеÑÑиÑане.
unit MainUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, PDFlibAX_TLB, ActiveX;
type
TForm1 = class(TForm)
RichEdit1: TRichEdit;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
function PrintRtfBox(hDc: HDC; rtfBox: TRichEdit;
FirstChar: Integer): Integer;
end;
var
Form1: TForm1;
PdfDoc: TPDFLibrary;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
PdfDoc := TPDFLibrary.Create(Self);
// Size the control to A4 at screen DPI so pagination matches the DC
RichEdit1.Width := Round(ScaleX(210, mmPixel));
RichEdit1.Height := Round(ScaleY(297, mmPixel));
RichEdit1.Lines.LoadFromFile(
ExtractFilePath(Application.ExeName) + 'document.rtf');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Dc: HDC;
PageNumber, LastChar, PdfDocId: Integer;
begin
PageNumber := 1;
LastChar := 0;
repeat
// Obtain a virtual DC sized to A4
Dc := PdfDoc.GetCanvasDC(
Round(ScaleX(210, mmPixel)),
Round(ScaleY(297, mmPixel)));
// Render the next page of RTF content into the DC
LastChar := PrintRtfBox(Dc, RichEdit1, LastChar);
// Capture the DC contents as a PDF document
PdfDoc.LoadFromCanvasDc(96, 0);
PdfDocId := PdfDoc.SelectedPdfDocument;
PdfDoc.SaveToFile(
ExtractFilePath(Application.ExeName)
+ 'Output' + IntToStr(PageNumber) + '.pdf');
PdfDoc.RemovePdfDocument(PdfDocId);
Inc(PageNumber);
until LastChar = 0;
end;
function TForm1.PrintRtfBox(hDc: HDC; rtfBox: TRichEdit;
FirstChar: Integer): Integer;
var
RcDrawTo, RcPage: TRect;
Fr: TFormatRange;
NextCharPosition: Integer;
begin
RcPage.Left := 0;
RcPage.Top := 0;
RcPage.Right := rtfBox.Left + rtfBox.Width + 100;
RcPage.Bottom := rtfBox.Top + rtfBox.Height + 100;
RcDrawTo.Left := rtfBox.Left;
RcDrawTo.Top := rtfBox.Top;
RcDrawTo.Right := rtfBox.Left + rtfBox.Width;
RcDrawTo.Bottom := rtfBox.Top + rtfBox.Height;
Fr.hdc := hDc;
Fr.hdcTarget := hDc;
Fr.rc := RcDrawTo;
Fr.rcPage := RcPage;
Fr.chrg.cpMin := FirstChar;
Fr.chrg.cpMax := -1;
NextCharPosition :=
SendMessage(rtfBox.Handle, EM_FORMATRANGE, 1, LPARAM(@Fr));
if NextCharPosition < Length(rtfBox.Text) then
Result := NextCharPosition
else
Result := 0; // signals last page
end;
end.
Ðакво пÑави ÑикÑлÑÑ
PrintRtfBox попÑлва ÑÑÑÑкÑÑÑаÑа TFormatRange и Ñ Ð¿Ñедава на Rich Edit конÑÑолаÑа ÑÑез SendMessage. ÐонÑÑолаÑа изобÑазÑва знаÑиÑе, запоÑвайки Ð¾Ñ cpMin, ÑпиÑа, когаÑо DC Ñе запÑлни, и вÑÑÑа позиÑиÑÑа на пÑÑÐ²Ð¸Ñ Ð·Ð½Ð°Ðº, койÑо не е вмеÑÑен. ÐогаÑо вÑÑнаÑаÑа ÑÑойноÑÑ Ðµ Ñавна на или надвиÑава обÑаÑа дÑлжина на ÑекÑÑа, вÑиÑки знаÑи Ñа изобÑазени и ÑÑнкÑиÑÑа вÑÑÑа нÑла, коеÑо пÑекÑаÑÑва ÑикÑла repeat...until.
ÐÑÑка иÑеÑаÑÐ¸Ñ Ð³ÐµÐ½ÐµÑиÑа един PDF Ñайл Ñ Ð¸Ð¼ÐµÐ½Ð° Output1.pdf, Output2.pdf и Ñ.н. Ðко иÑкаÑе вмеÑÑо Ñова един многоÑÑÑаниÑен докÑменÑ, API за добавÑне на ÑÑÑаниÑи на библиоÑекаÑа ви позволÑва да ги ÑглобиÑе Ñлед Ñова, или можеÑе да пÑеÑÑÑÑкÑÑÑиÑаÑе ÑикÑла, за да извиква AddPage в ÑамкиÑе на една ÑеÑÐ¸Ñ Ð½Ð° докÑменÑа. ÐоделÑÑ Ð½Ð° SaveToFile, Ñледван Ð¾Ñ RemovePdfDocument пÑи вÑÑка иÑеÑаÑиÑ, огÑаниÑава пиковаÑа Ð¿Ð°Ð¼ÐµÑ Ð´Ð¾ ÑÑдÑÑжаниеÑо на една ÑÑÑаниÑа, коеÑо е важно пÑи много дÑлги RTF Ñайлове.
ÐеÑайли за оÑазмеÑÑванеÑо, коиÑо обÑÑÐºÐ²Ð°Ñ ÑазÑабоÑÑиÑиÑе
ÐÑгÑменÑÑÑ 96 DPI за LoadFromCanvasDc Ñказва на библиоÑекаÑа пÑи каква екÑанна ÑазделиÑелна ÑпоÑобноÑÑ Ðµ изобÑазен DC, за да може ÑÑ Ð´Ð° изÑиÑли пÑавилноÑо ÑÑоÑвеÑÑÑвие ÑоÑки-пикÑели за PDF ÑÑÑаниÑаÑа. ÐÑеÑна ÑÑойноÑÑ ÑÑк Ñе доведе до непÑавилен ÑÐ°Ð·Ð¼ÐµÑ Ð½Ð° ÑекÑÑа в изÑ
Ð¾Ð´Ð½Ð¸Ñ Ñайл, доÑи ако изобÑажениеÑо изглежда пÑавилно на екÑана.
ÐобавеноÑо +100 кÑм RcPage.Right и RcPage.Bottom е малко поле извÑн Ð²Ð¸Ð´Ð¸Ð¼Ð¸Ñ ÑÑб на конÑÑолаÑа. Rich Edit използва пÑавоÑгÑлника rcPage, за да ÑеÑи кÑде да ÑÐ°Ð·Ð´ÐµÐ»Ñ ÑÑÑаниÑиÑе; без Ñова поле, Ñед, Ð¿Ð¾Ð¿Ð°Ð´Ð°Ñ ÑоÑно на гÑаниÑаÑа, може да Ñе дÑблиÑа в две ÑÑÑаниÑ. Това не е магиÑеÑка конÑÑанÑа: ÑÑойноÑÑÑа ÑÑÑбва да бÑде доÑÑаÑÑÑно голÑма, за да може гÑаниÑаÑа на ÑÑÑаниÑаÑа да попада ÑиÑÑо вÑÑÑе в облаÑÑÑа на оÑоÑмлениеÑо на конÑÑолаÑа, а не на поÑÐ»ÐµÐ´Ð½Ð¸Ñ Ð¿Ð¸ÐºÑел.
ÐÑвен Ñова, конÑÑолаÑа ÑÑÑбва веÑе да е пÑикÑепена кÑм видим пÑозоÑÐµÑ Ð½Ð° ÑоÑма пÑи изпÑлнение на FormCreate, за да бÑде манипÑлаÑоÑÑÑ Ð¹ на пÑозоÑÐµÑ Ð²Ð°Ð»Ð¸Ð´ÐµÐ½ пÑеди пÑÑвоÑо извикване на SendMessage. TRichEdit, ÑÑздаден динамиÑно по вÑеме на изпÑлнение, Ñе нÑждае Ð¾Ñ Ð¸Ð·ÑиÑно извикване на HandleNeeded пÑеди наÑалоÑо на ÑикÑла за изобÑазÑване, ако ÑоÑмаÑа не е показана вÑе оÑе.
РабоÑа Ñ ÑÑиÑÑове и ÑÑнкÑии на RTF
ТÑй каÑо изобÑазÑванеÑо Ñе извÑÑÑва Ð¾Ñ Ð¼ÐµÑ Ð°Ð½Ð¸Ð·Ð¼Ð° Windows Rich Edit, замеÑÑванеÑо на ÑÑиÑÑове Ñледва ÑÑÑиÑе пÑавила, коиÑо Ñой използва за показване и пеÑаÑ. ШÑиÑÑовеÑе, поÑоÑени в RTF Ñайла, инÑÑалиÑани на маÑинаÑа, Ñе Ñе изобÑазÑÑ Ð²ÑÑно; липÑваÑиÑе ÑÑиÑÑове Ñе бÑÐ´Ð°Ñ Ð·Ð°Ð¼ÐµÐ½ÐµÐ½Ð¸ безÑÑмно, коеÑо може да пÑомени дÑлжиниÑе на ÑедовеÑе и ÑазпÑеделениеÑо на ÑÑÑаниÑиÑе. Ðа пÑоизводÑÑвено пакеÑно конвеÑÑиÑане Ñова Ñи ÑÑÑÑва да Ñе ÑеÑÑва изÑиÑно: заÑедеÑе докÑÐ¼ÐµÐ½Ñ Ñ Ð²Ñеки ÑÑиÑÑ, използван Ð¾Ñ Ð²Ð°ÑиÑе RTF изÑоÑниÑи, и поÑвÑÑдеÑе, Ñе бÑоÑÑ Ð½Ð° ÑÑÑаниÑиÑе в Ð¸Ð·Ñ Ð¾Ð´Ð½Ð¸Ñ Ñайл ÑÑоÑвеÑÑÑва на оÑакваноÑо пÑи ÑÑÑен пÑеглед пÑеди пеÑаÑ.
ТаблиÑиÑе, вгÑадениÑе изобÑÐ°Ð¶ÐµÐ½Ð¸Ñ Ð¸ повеÑеÑо ÑÑнкÑии за ÑоÑмаÑиÑане на Rich Text ÑабоÑÑÑ Ð±ÐµÐ· допÑлниÑелна обÑабоÑка, ÑÑй каÑо Rich Edit ги изобÑазÑва наÑивно. ÐблаÑÑÑа, коÑÑо може да бÑде изненадваÑа, е ÑекÑÑÑÑ Ñ Ð¿ÐµÑÑонализиÑано ÑазÑÑоÑние Ð¼ÐµÐ¶Ð´Ñ Ð°Ð±Ð·Ð°ÑиÑе или оÑÑÑÑпи на пÑÑÐ²Ð¸Ñ Ñед, изÑазени в ÑÑипове: вÑÑÑеÑнаÑа кооÑдинаÑна ÑиÑÑема на Rich Edit е в ÑÑипове (1/1440 инÑа), докаÑо DC кооÑдинаÑиÑе, зададени в TFormatRange, Ñа в пикÑели пÑи ÑекÑÑоÑо DPI. ÐонÑÑолаÑа конвеÑÑиÑа вÑÑÑеÑно, но ако изгÑаждаÑе RTF пÑогÑамно, ÑÑÑбва да пÑовеÑиÑе дали ÑÑойноÑÑиÑе на ваÑиÑе полеÑа Ñа в пÑавилнаÑа единиÑа.
DPI ÑÑзнание и диÑплеи Ñ Ð²Ð¸Ñок DPI
Ðа диÑплей, ÑабоÑÐµÑ Ð¿Ñи маÑабиÑане 150% (144 DPI), ScaleX(210, mmPixel) Ñе вÑÑне по-голÑм бÑой пикÑели, оÑколкоÑо на 100% диÑплей. PDF библиоÑекаÑа запиÑва пикÑелниÑе ÑазмеÑи, пÑедадени на GetCanvasDC, и използва DPI аÑгÑменÑа в LoadFromCanvasDc, за да изÑиÑли обÑаÑно ÑизиÑеÑÐºÐ¸Ñ ÑÐ°Ð·Ð¼ÐµÑ Ð½Ð° ÑÑÑаниÑаÑа в PDF. ÐокаÑо DPI ÑÑойноÑÑÑа, коÑÑо пÑедаваÑе, ÑÑоÑвеÑÑÑва на DPI, пÑи коеÑо ÑабоÑи ваÑеÑо пÑиложение, ÑазмеÑÑÑ Ð½Ð° изÑ
однаÑа ÑÑÑаниÑа Ñе бÑде пÑавилен незавиÑимо Ð¾Ñ Ð¼Ð°ÑабиÑанеÑо на диÑплеÑ.
Ðко ваÑеÑо пÑиложение не е DPI-ÑÑзнаÑелно (ÑÑаÑоÑо поведение по подÑазбиÑане), Windows маÑабиÑа екÑÐ°Ð½Ð½Ð¸Ñ DC и пикÑелниÑе ви изÑиÑÐ»ÐµÐ½Ð¸Ñ Ñе бÑÐ´Ð°Ñ Ð³ÑеÑни на маÑини Ñ Ð²Ð¸Ñок DPI. Ðай-пÑоÑÑоÑо ÑеÑение е да деклаÑиÑаÑе DPI ÑÑзнание в маниÑеÑÑа на пÑиложениеÑо; Ñогава пÑиложениеÑо полÑÑава иÑÑинÑки пикÑели на ÑÑÑÑойÑÑвоÑо и ÑиÑлоÑо 96, пÑедадено на LoadFromCanvasDc, ÑÑÑбва да Ñе замени Ñ Ð´ÐµÐ¹ÑÑвиÑÐµÐ»Ð½Ð¸Ñ DPI на диÑплеÑ, полÑÑен Ð¾Ñ GetDeviceCaps(GetDC(0), LOGPIXELSX). ÐÑимеÑниÑÑ ÐºÐ¾Ð´ по-гоÑе кодиÑа ÑвÑÑдо 96, ÑÑй каÑо е подÑ
одÑÑ Ð·Ð° ÑÑеда Ñ Ð¼Ð°ÑабиÑане 100% и пÑави пÑимеÑа по-кÑаÑÑк.
СÑÑÑкÑÑÑа на Ð¸Ð·Ñ Ð¾Ð´Ð½Ð¸Ñ Ñайл: по един Ñайл на ÑÑÑаниÑа или обединен докÑменÑ
ЦикÑлÑÑ Ð¿Ð¾-гоÑе запиÑва вÑÑка ÑÑÑаниÑа в оÑделен PDF Ñайл. Ðали Ñова е желаноÑо поведение завиÑи Ð¾Ñ Ð¿Ð¾ÑледваÑоÑо използване. СиÑÑемиÑе за генеÑиÑане на оÑÑеÑи ÑеÑÑо Ñе нÑждаÑÑ Ð¾Ñ Ð¾Ñделни ÑÑÑаниÑи, ÑÑй каÑо по-кÑÑно ÑглобÑÐ²Ð°Ñ ÐºÑÐ°Ð¹Ð½Ð¸Ñ Ð´Ð¾ÐºÑÐ¼ÐµÐ½Ñ ÑÑез обединÑване или пÑенаÑеждане на ÑÑÑаниÑиÑе. Ðко Ð¾Ñ ÑамоÑо наÑало иÑкаÑе един PDF, библиоÑекаÑа ви позволÑва да ÑÑздадеÑе докÑÐ¼ÐµÐ½Ñ Ñ Ð¼Ð½Ð¾Ð¶ÐµÑÑво ÑÑÑаниÑи в една ÑеÑиÑ: ÑÑздайÑе докÑменÑа веднÑж извÑн ÑикÑла, извиквайÑе меÑода за добавÑне на ÑÑÑаниÑа вмеÑÑо SaveToFile вÑÑÑе в ÑикÑла и запазеÑе пÑÐ»Ð½Ð¸Ñ Ð´Ð¾ÐºÑÐ¼ÐµÐ½Ñ Ñлед кÑÐ°Ñ Ð½Ð° ÑикÑла. Това избÑгва междинниÑе Ñайлове и е пÑавилнаÑа ÑÑÑÑкÑÑÑа за повеÑеÑо ÑÑенаÑии за конвеÑÑиÑане на единиÑен докÑменÑ.
Ðа големи RTF Ñайлове Ñи ÑÑÑÑва да добавиÑе извеÑÑна обÑаÑна вÑÑзка за напÑедÑка в ÑикÑла, ÑÑй каÑо ÑкоÑоÑÑÑа на конвеÑÑиÑане е пÑиблизиÑелно пÑопоÑÑионална на бÑÐ¾Ñ Ð½Ð° ÑÑÑаниÑиÑе и докÑÐ¼ÐµÐ½Ñ Ð¾Ñ 200 ÑÑÑаниÑи може да оÑнеме нÑколко ÑекÑнди. СÑÑÑкÑÑÑаÑа repeat...until е леÑна за ÑазÑиÑÑване: пÑоÑледÑвайÑе оÑмеÑÑванеÑо на знаÑиÑе в акÑÑализаÑÐ¸Ñ Ð½Ð° ленÑа за напÑедÑк Ñлед вÑÑка иÑеÑаÑиÑ, използвайки LastChar, Ñазделен на обÑÐ¸Ñ Ð±Ñой знаÑи Ð¾Ñ RichEdit1.GetTextLen.
ÐеÑодиÑе GetCanvasDC и LoadFromCanvasDc, показани ÑÑк, Ñа ÑаÑÑ Ð¾Ñ PDF библиоÑекаÑа на losLab за Delphi и C++Builder.