unit SynRTF2PDF; interface uses Windows, Messages, SysUtils, Classes, Graphics, SynPdf, RVReport, CRVData; const PageSizesMM: array [TPDFPaperSize] of TSize = ((cx:210; cy:297), // psA4 (cx:148; cy:210), // psA5 (cx:297; cy:420), // psA3 (cx:420; cy:594), // psA2 (cx:594; cy:841), // psA1 (cx:841; cy:1189), // psA0, (cx:216; cy:279), // psLetter (cx:216; cy:356), // psLegal (cx:0; cy:0) // psUserDefined ); type TPageInfo = record PageFormat: TPDFPaperSize; PageSize: TSize; PaperSize: TPDFPaperSize; Doc, Header, Footer: TRect; end; procedure InitHelper(h: TRVReportHelper); function ConvertRTF2PDF(Canvas: TCanvas; const RTFFile, PDFFile: string): boolean; implementation uses SynCommons, RVStyle, RVFuncs, Forms, Math, Printers; type TRVReportEvents = class FPageInfo: TPageInfo; FPdfDoc: TPdfDocumentGDI; constructor Create(PageInfo: TPageInfo; PdfDoc: TPdfDocumentGDI); procedure rvhDrawCheckpoint(Sender: TRVReportHelper; RVData: TCustomRVData; ItemNo, X, Y: Integer); procedure rvhDrawHyperlink(Sender: TRVReportHelper; RVData: TCustomRVData; ItemNo: Integer; R: TRect); end; procedure InitHelper(h: TRVReportHelper); begin { SynPDF handles h.MetafileCompatibility = False mode pretty well. However, if False, it calculates character positions itself, and they may be slightly different. If True, all characters are positioned by TRichView } h.MetafileCompatibility := True; h.RichView.Style := TRVStyle.Create(h); h.RichView.RTFReadProperties.TextStyleMode := rvrsAddIfNeeded; h.RichView.RTFReadProperties.ParaStyleMode := rvrsAddIfNeeded; h.RichView.LeftMargin := 0; h.RichView.TopMargin := 0; h.RichView.RightMargin := 0; h.RichView.BottomMargin := 0; h.RichView.RVFOptions := [rvfoIgnoreUnknownPicFmt, rvfoIgnoreUnknownCtrls, rvfoIgnoreUnknownCtrlProperties, rvfoConvUnknownStylesToZero, rvfoConvLargeImageIdxToZero, rvfoLoadBack, rvfoLoadDocProperties]; h.RichView.Clear; h.RichView.RTFReadProperties.ReadDocParameters := True end; constructor TRVReportEvents.Create(PageInfo: TPageInfo; PdfDoc: TPdfDocumentGDI); begin FPageInfo := PageInfo; FPdfDoc := PdfDoc end; { This event occurs when drawing an item associated with the checkpoint. We save it in PDF as a bookmark } procedure TRVReportEvents.rvhDrawCheckpoint(Sender: TRVReportHelper; RVData: TCustomRVData; ItemNo, X, Y: Integer); var CheckpointData: TCheckpointData; CPTag: TRVTag; CPName: String; RE: Boolean; BookmarkY: Single; begin CheckpointData := RVData.GetItemCheckpoint(ItemNo); RVData.GetCheckpointInfo(CheckpointData, CPTag, CPName, RE); BookmarkY := (FPageInfo.PageSize.cy - (Y + FPageInfo.Doc.Top)) * 72 / Screen.PixelsPerInch; FPdfDoc.CreateBookMark(BookmarkY + FPageInfo.Doc.Top, StringToUTF8(CPName)) end; {------------------------------------------------------------------------------} { This event occurs when drawing a hyperlink. We use it to add a link to bookmark. The current version of SynPDF does not support links to URL } procedure TRVReportEvents.rvhDrawHyperlink(Sender: TRVReportHelper; RVData: TCustomRVData; ItemNo: Integer; R: TRect); var Target: String; PdfRect: TPdfRect; dpi: Integer; begin try Target := RVData.GetItemTag(ItemNo); if (Target<>'') and (Target[1] = '#') then begin OffsetRect(R, FPageInfo.Doc.Left, FPageInfo.Doc.Top); dpi := Screen.PixelsPerInch; PdfRect.Left := R.Left * 72 / dpi; PdfRect.Top := (FPageInfo.PageSize.cy - R.Top) * 72 / dpi; PdfRect.Right := R.Right * 72 / dpi; PdfRect.Bottom := (FPageInfo.PageSize.cy - R.Bottom) * 72 / dpi; FPdfDoc.CreateLink(PdfRect, StringToUTF8(Copy(Target, 2, MaxInt))) end except end end; {------------------------------------------------------------------------------} // Convert to unit function GetValue(rvh: TRVReportHelper; Value: TRVLength; NewUnits: TRVUnits = rvuPixels): Integer; begin Result := Round(RV_UnitsToUnits(Value, rvh.RichView.DocParameters.Units, NewUnits)) end; // Converting mm to pixels function MMToPixels(mm, ppi: Integer): Integer; begin Result := Round(mm*5*ppi/127) end; function ConvertRTF2PDF(Canvas: TCanvas; const RTFFile, PDFFile: string): boolean; var rvh: TRVReportHelper; rvhHeader: TRVReportHelper; rvhFooter: TRVReportHelper; PdfDoc: TPdfDocumentGDI; events: TRVReportEvents; PageInfo: TPageInfo; HeaderYMM, FooterYMM: Integer; ppix,ppiy: Integer; i: Integer; dpi: Integer; {....................................................} procedure CalcPageLayout; var w, h, i: Integer; begin ppix := GetDeviceCaps(Canvas.Handle, LOGPIXELSX); ppiy := GetDeviceCaps(Canvas.Handle, LOGPIXELSY); //////////////////////////////////////////// w := GetValue(rvh, rvh.RichView.DocParameters.PageWidth, rvuMillimeters); h := GetValue(rvh, rvh.RichView.DocParameters.PageHeight, rvuMillimeters); PageInfo.PaperSize := psUserDefined; for i := Ord(Low(TPDFPaperSize)) to Ord(High(TPDFPaperSize)) do if ((w = PageSizesMM[TPDFPaperSize(i)].cx) and (h = PageSizesMM[TPDFPaperSize(i)].cy)) or ((h = PageSizesMM[TPDFPaperSize(i)].cx) and (w = PageSizesMM[TPDFPaperSize(i)].cy)) then begin PageInfo.PaperSize := TPDFPaperSize(i); break end; ////////////////////////////////////////// w := GetValue(rvh, rvh.RichView.DocParameters.PageWidth); h := GetValue(rvh, rvh.RichView.DocParameters.PageHeight); if rvh.RichView.DocParameters.Orientation = poPortrait then begin PageInfo.PageSize.cx := Min(w, h); PageInfo.PageSize.cy := Max(w, h) end else begin PageInfo.PageSize.cx := Max(w, h); PageInfo.PageSize.cy := Min(w, h) end; PageInfo.Doc.Top := GetValue(rvh, rvh.RichView.DocParameters.TopMargin); PageInfo.Doc.Bottom := PageInfo.PageSize.cy- GetValue(rvh, rvh.RichView.DocParameters.BottomMargin); PageInfo.Doc.Left := GetValue(rvh, rvh.RichView.DocParameters.LeftMargin); PageInfo.Doc.Right := PageInfo.PageSize.cx- GetValue(rvh, rvh.RichView.DocParameters.RightMargin); //////////////////////////////////// HeaderYMM := rvh.RichView.RTFReadProperties.HeaderYMM; FooterYMM := rvh.RichView.RTFReadProperties.FooterYMM; if rvhHeader.RichView.ItemCount>0 then begin PageInfo.Header.Left := PageInfo.Doc.Left; PageInfo.Header.Right := PageInfo.Doc.Right; rvhHeader.Init(Canvas, PageInfo.Header.Right-PageInfo.Header.Left); rvhHeader.FormatNextPage($FFFFFFF); PageInfo.Header.Top := MMToPixels(HeaderYMM, ppiy); //PageInfo.Header.Top := PageInfo.Doc.Top; PageInfo.Header.Bottom := PageInfo.Header.Top+rvhHeader.GetLastPageHeight; if PageInfo.Header.Bottom>PageInfo.Doc.Top then PageInfo.Doc.Top := PageInfo.Header.Bottom end; if rvhFooter.RichView.ItemCount>0 then begin PageInfo.Footer.Left := PageInfo.Doc.Left; PageInfo.Footer.Right := PageInfo.Doc.Right; rvhFooter.Init(Canvas, PageInfo.Header.Right-PageInfo.Header.Left); rvhFooter.FormatNextPage($FFFFFFF); PageInfo.Footer.Bottom := PageInfo.PageSize.cy-MMToPixels(FooterYMM, ppiy); //PageInfo.Footer.Bottom := PageInfo.Doc.Bottom; PageInfo.Footer.Top := PageInfo.Footer.Bottom-rvhFooter.GetLastPageHeight; if PageInfo.Footer.Top0 then rvhHeader.DrawPageAt(PageInfo.Header.Left, PageInfo.Header.Top, 1, PdfDoc.VCLCanvas, True, PageInfo.Header.Bottom-PageInfo.Header.Top); if rvhFooter.RichView.ItemCount>0 then rvhFooter.DrawPageAt(PageInfo.Footer.Left, PageInfo.Footer.Top, 1, PdfDoc.VCLCanvas, True, PageInfo.Footer.Bottom-PageInfo.Footer.Top) end; {....................................................} begin Result := False; rvh := TRVReportHelper.Create(nil); rvhHeader := TRVReportHelper.Create(nil); rvhFooter := TRVReportHelper.Create(nil); try InitHelper(rvh); InitHelper(rvhHeader); InitHelper(rvhFooter); PdfDoc := TPdfDocumentGDI.Create; try events := TRVReportEvents.Create(PageInfo, PdfDoc); try rvh.OnDrawCheckpoint := events.rvhDrawCheckpoint; rvh.OnDrawHyperlink := events.rvhDrawHyperlink; rvh.RichView.RTFReadProperties.SetHeader(rvhHeader.RichView.RVData); rvh.RichView.RTFReadProperties.SetFooter(rvhFooter.RichView.RVData); if not rvh.RichView.LoadRTF(RTFFile) then Exit; CalcPageLayout; dpi := Screen.PixelsPerInch; PdfDoc.ScreenLogPixels := dpi; if PageInfo.PaperSize = psUserDefined then begin PdfDoc.DefaultPageWidth := Round(PageInfo.PageSize.cx * 72 / dpi); PdfDoc.DefaultPageHeight := Round(PageInfo.PageSize.cy * 72 / dpi) end; PdfDoc.DefaultPaperSize := PageInfo.PaperSize; PdfDoc.DefaultPageLandscape := rvh.RichView.DocParameters.Orientation = poLandscape; for i := 1 to rvh.PagesCount do begin PdfDoc.AddPage; DrawPage end; Result := PdfDoc.SaveToFile(PDFFile) finally events.Free end finally PdfDoc.Free end finally rvhFooter.Free; rvhHeader.Free; rvh.Free end end; end.