{*******************************************************************} { } { Developer Express Visual Component Library } { ExpressPrinting System COMPONENT SUITE } { } { Copyright (C) 1998-2009 Developer Express Inc. } { ALL RIGHTS RESERVED } { } { The entire contents of this file is protected by U.S. and } { International Copyright Laws. Unauthorized reproduction, } { reverse-engineering, and distribution of all or any portion of } { the code contained in this file is strictly prohibited and may } { result in severe civil and criminal penalties and will be } { prosecuted to the maximum extent possible under the law. } { } { RESTRICTIONS } { } { THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES } { (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE } { SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS } { LICENSED TO DISTRIBUTE THE EXPRESSPRINTINGSYSTEM AND } { ALL ACCOMPANYING VCL CONTROLS AS PART OF AN } { EXECUTABLE PROGRAM ONLY. } { } { THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED } { FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE } { COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE } { AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT } { AND PERMISSION FROM DEVELOPER EXPRESS INC. } { } { CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON } { ADDITIONAL RESTRICTIONS. } { } {*******************************************************************} unit dxPSPDFExport; interface {$I cxVer.inc} uses Windows, Classes, SysUtils, Graphics, cxGraphics, dxPSCore, dxBkGnd, cxClasses, dxPrnPg, dxPSPDFExportCore, dxPSReportRenderCanvas, cxDrawTextUtils, cxGeometry, dxPSFillPatterns, dxPSGlbl, dxPrnDlg; type TdxPSPDFReportRenderCanvas = class; { TdxPSPDFReportExportProvider } TdxPSPDFReportExportProvider = class(TdxPSCustomReportExportProvider) private FOptions: TdxPSPDFReportExportOptions; FPage: TdxPSPDFPage; FPageContent: TdxPSPDFPageContent; FPDFFile: TdxPSPDFFile; procedure SetOptions(AOptions: TdxPSPDFReportExportOptions); procedure SetPage(AValue: TdxPSPDFPage); protected procedure AssignSettings(APDFFile: TdxPSPDFFile); procedure ExportPage(APageIndex: Integer); virtual; function ScaleValue(AValue: Integer): Integer; public constructor Create(AReportLink: TBasedxReportLink); override; destructor Destroy; override; procedure Build; override; procedure SaveToStream(AStream: TStream); override; // property Options: TdxPSPDFReportExportOptions read FOptions write SetOptions; property Page: TdxPSPDFPage read FPage write SetPage; property PageContent: TdxPSPDFPageContent read FPageContent; property PDFFile: TdxPSPDFFile read FPDFFile; end; { TdxPSPDFReportRenderCanvas } TdxPSPDFReportRenderCanvas = class(TdxPSReportRenderScreenCanvas) private FCustomDrawBounds: TRect; FCustomDrawMetaFile: TMetafile; FCustomDrawMetaFileCanvas: TMetafileCanvas; FLogicalToPDFCoordinates: Double; FPageSize: TPoint; FProvider: TdxPSPDFReportExportProvider; FRegion: TcxRegion; FSavedRegions: TcxObjectList; function GetPageContent: TdxPSPDFPageContent; protected function CalculatePDFTextLineWidth(const AText: WideString): Integer; function ConvertPoint(const P: TPoint): TdxPSPDFPoint; function ConvertRect(R: TRect; ACorrectOffset: Boolean = True): TdxPSPDFRect; function ConvertValue(AValue: Integer): Double; virtual; function CorrectRectByViewOrg(const R: TRect): TRect; virtual; procedure DrawExpandButtonBorders(const R: TRect; AMarlettFont: TFont; ABorderColor: TColor; AEdgeStyle: TdxCheckButtonEdgeStyle; ALineThickness: Integer); override; procedure DrawExpandButtonGlyph(R: TRect; ASymbolFont: TFont; ATextColor: TColor; AExpanded: Boolean; ALineThickness: Integer); override; procedure InternalDrawStretchPicture(APicture: TGraphic; const R: TRect); override; procedure InternalDrawText(const ABounds: TRect; const AText: string; AFont: TFont; ATextColor: TColor; AFormat: TcxTextOutFormat; AMaxLineCount: Integer; AVertical: Boolean; ADirection: TcxVerticalTextOutDirection); procedure InternalDrawTilePicture(APicture: TGraphic; const R: TRect; APictureWidth, APictureHeight: Integer); override; procedure InternalFillRect(const R: TRect; ABackColor, AForeColor: TColor; APattern: TdxPSFillPatternClass; APatternBrush: TBrush); virtual; function GetIsPrinterCanvas: Boolean; override; function GetSupportsTransparentImagesDrawing: Boolean; override; procedure SelectFont(AFont: TFont; ATextColor: TColor); // property PageContent: TdxPSPDFPageContent read GetPageContent; property Provider: TdxPSPDFReportExportProvider read FProvider; public constructor Create(AProvider: TdxPSPDFReportExportProvider); destructor Destroy; override; function CalculateLineThickness(AUnitsPerPixel, AUnitsPerPoint: Integer): Integer; override; procedure DeviceToLogicalCoordinates(var R: TRect); override; procedure LogicalToDeviceCoordinates(var R: TRect); override; procedure SetCanvasExts(const APageSize: TPoint; AMappingMode: TdxPSReportRenderCanvasMappingMode; AScaleNumerator: Integer; AScaleDenominator: Integer; const AViewPort: TRect); override; procedure ResetPageRegion; // Custom Draw function BeginCustomDraw(const AClipBounds: TRect; AFont: TFont; AColor: TColor): TCanvas; override; procedure EndCustomDraw(var ACanvas: TCanvas); override; // function ExcludeClipRect(const R: TRect): Integer; override; function IntersectClipRgn(const R: TRect): Integer; override; procedure RestoreClipRgn; override; procedure SaveClipRgn; override; procedure SetClipRect(const R: TRect); override; // Text procedure DrawText(var R: TRect; const AText: string; AFont: TFont; AFormat: DWORD; ATextColor: TColor = clDefault; AMaxLineCount: Integer = 0); override; procedure DrawTextLine(const ATextRect, AClipBounds: TRect; ATextWidth: Integer; const AText: WideString; ATextColor: TColor = clDefault; ARotateAngle: Double = 0; ABreakExtraSpacing: Integer = 0); procedure RotatedTextOut(const ABounds: TRect; const AText: string; AFont: TFont; AAlignHorz: TcxTextAlignX = taCenterX; AAlignVert: TcxTextAlignY = taCenterY; AWordBreak: Boolean = True; ADirection: TcxVerticalTextOutDirection = vtdBottomToTop); override; function TextSize(const AText: string): TSize; override; // procedure DrawEllipseFrame(const R: TRect; AColor: TColor; AThickness: Integer); override; procedure DrawExpandButton(R: TRect; AEdgeStyle: TdxCheckButtonEdgeStyle; AMarlettFont, ASymbolFont: TFont; AExpanded, AShadow, AFillInterior: Boolean; ABorderColor, ABackgroundColor: TColor; ALineThickness: Integer); override; procedure DrawFrame(const R: TRect; ATopLeftColor, ARightBottomColor: TColor; ABorderWidth: Integer = 1; ABorders: TcxBorders = cxBordersAll); override; procedure DrawPicture(APicture: TGraphic; const R: TRect; APictureMode: TdxPicturePaintMode; ANumerator: Integer; ADenominator: Integer; AOffsetX: Integer = 0; AOffsetY: Integer = 0); override; procedure DrawRoundFrame(const R: TRect; AEllipseWidth, AEllipseHeight: Integer; AColor: TColor; AThickness: Integer); override; procedure FillEllipse(const R: TRect; ABackColor, AForeColor: TColor; APattern: TdxPSFillPatternClass; APatternBrush: TBrush); override; procedure FillRect(const R: TRect; AColor: TColor); override; procedure FillRegion(ARegion: HRGN; ABackColor, AForeColor: TColor; APattern: TdxPSFillPatternClass; APatternBrush: TBrush); override; procedure FillRoundRect(R: TRect; AEllipseWidth, AEllipseHeight: Integer; ABackColor, AForeColor: TColor; APattern: TdxPSFillPatternClass; APatternBrush: TBrush); override; function IsRectVisible(const R: TRect): Boolean; override; procedure Pie(const R: TRect; const APoint1, APoint2: TPoint); procedure Polyline(const APoints: array of TPoint; AColor: TColor; ALineWidth: Integer); override; procedure Polygon(const APoints: array of TPoint; AColor, ABackgroundColor: TColor; ALineWidth: Integer); override; // property LogicalToPDFCoordinates: Double read FLogicalToPDFCoordinates; property PageSize: TPoint read FPageSize; property Region: TcxRegion read FRegion; end; function dxPSExportToPDF(AReportLink: TBasedxReportLink): Boolean; function dxPSExportToPDFStream(AStream: TStream; AReportLink: TBasedxReportLink; AShowSettingsDialog: Boolean = True; ADefaultSettings: TdxPSPDFReportExportOptions = nil): Boolean; function dxPSExportToPDFFile(const AFileName: string; AReportLink: TBasedxReportLink; AShowSettingsDialog: Boolean = True; ADefaultSettings: TdxPSPDFReportExportOptions = nil): Boolean; implementation uses Types, Math, Dialogs, ShellApi, dxCore, dxPSPDFExportDialog, dxPSPDFMetaFileParser; const sdxPdfSaveDialogFilter = 'PDF Document (*.pdf)|*.pdf;'; type { TdxPSPDFExportCustomTextHelper } TdxPSPDFExportCustomTextHelper = class(TObject) private FHasEndEllipsis: Boolean; FPDFCanvas: TdxPSPDFReportRenderCanvas; FRows: TcxTextRows; FTextParams: TcxTextParams; function GetLastTextRow: PcxTextRow; function GetTextRow(Index: Integer): PcxTextRow; function GetTextRowCount: Integer; function GetTextRowText(Index: Integer): WideString; public constructor Create(APDFCanvas: TdxPSPDFReportRenderCanvas; ATextFormat: Cardinal); virtual; function CalculateBreakExtractSpacing(const AWidth: Integer; ARow: PcxTextRow): Integer; virtual; function CalculateTextRect(const ABounds: TRect): TRect; virtual; abstract; function CalculateTextRowOutRect(ATextRow: PcxTextRow; var R: TRect; out ABreakExtraSpacing: Integer): TRect; virtual; abstract; function CalculateTextOutParams(const ABounds: TRect; AMaxLineCount: Integer; const AText: string): TRect; virtual; function CalculateTotalTextHeight: Integer; procedure CalculateEndEllipsis(DC: HDC; const R: TRect; ARow: PcxTextRow); // property PDFCanvas: TdxPSPDFReportRenderCanvas read FPDFCanvas; property LastTextRow: PcxTextRow read GetLastTextRow; property TextParams: TcxTextParams read FTextParams; property TextRow[Index: Integer]: PcxTextRow read GetTextRow; property TextRowCount: Integer read GetTextRowCount; property TextRowText[Index: Integer]: WideString read GetTextRowText; end; { TdxPSPDFExportHorizontalTextHelper } TdxPSPDFExportHorizontalTextHelper = class(TdxPSPDFExportCustomTextHelper) public function CalculateTextRect(const ABounds: TRect): TRect; override; function CalculateTextRowOutRect(ATextRow: PcxTextRow; var R: TRect; out ABreakExtraSpacing: Integer): TRect; override; end; { TdxPSPDFExportVerticalTextHelper } TdxPSPDFExportVerticalTextHelper = class(TdxPSPDFExportCustomTextHelper) public function CalculateTextRect(const ABounds: TRect): TRect; override; function CalculateTextRowOutRect(ATextRow: PcxTextRow; var R: TRect; out ABreakExtraSpacing: Integer): TRect; override; function CalculateTextOutParams(const ABounds: TRect; AMaxLineCount: Integer; const AText: string): TRect; override; end; function dxPDFCalcTextRowExtents(AHandle: TCanvasHandle; AText: PWideChar; ATextLength: Integer; AExpandTabs: Boolean; PDFCanvas: TdxPSPDFReportRenderCanvas): TSize; var AWideStr: WideString; begin SetString(AWideStr, AText, ATextLength); Result := cxCalcTextExtents(AHandle, AText, ATextLength, AExpandTabs); Result.cx := PDFCanvas.CalculatePDFTextLineWidth(AWideStr); end; function dxPSExportToPDF(AReportLink: TBasedxReportLink): Boolean; var ADestFileName: string; AOptions: TdxPSPDFReportExportOptions; ASaveDialog: TSaveDialog; begin AOptions := AReportLink.PDFExportOptions; Result := dxPSShowPDFSettingsDialog(AOptions); if Result then begin ASaveDialog := TSaveDialog.Create(nil); try ASaveDialog.Filter := sdxPDFSaveDialogFilter; ASaveDialog.FileName := AOptions.DefaultFileName; ASaveDialog.Options := ASaveDialog.Options + [ofOverwritePrompt]; ASaveDialog.DefaultExt := sdxPDFExt; Result := ASaveDialog.Execute; if Result then begin ADestFileName := ChangeFileExt(ASaveDialog.FileName, sdxPDFExt); Result := dxPSExportToPDFFile(ADestFileName, AReportLink, False, AOptions); if Result and AReportLink.PDFExportOptions.OpenDocumentAfterExport then ShellExecute(0, 'open', PChar(ADestFileName), '', '', SW_SHOW); end; finally ASaveDialog.Free; end; end; end; function dxPSExportToPDFStream(AStream: TStream; AReportLink: TBasedxReportLink; AShowSettingsDialog: Boolean = True; ADefaultSettings: TdxPSPDFReportExportOptions = nil): Boolean; var AProvider: TdxPSPDFReportExportProvider; begin AProvider := TdxPSPDFReportExportProvider.Create(AReportLink); try if Assigned(ADefaultSettings) then AProvider.Options := ADefaultSettings else AProvider.Options := AReportLink.PDFExportOptions; if AShowSettingsDialog then Result := dxPSShowPDFSettingsDialog(AProvider.Options) else Result := True; if Result then begin AProvider.Build; AProvider.SaveToStream(AStream); end; finally AProvider.Free; end; end; function dxPSExportToPDFFile(const AFileName: string; AReportLink: TBasedxReportLink; AShowSettingsDialog: Boolean = True; ADefaultSettings: TdxPSPDFReportExportOptions = nil): Boolean; var AStream: TFileStream; begin Result := False; AStream := TFileStream.Create(AFileName, fmCreate); try Result := dxPSExportToPDFStream(AStream, AReportLink, AShowSettingsDialog, ADefaultSettings); finally FreeAndNil(AStream); if not Result then DeleteFile(AFileName); end; end; { TdxPSPDFExportCustomTextHelper } constructor TdxPSPDFExportCustomTextHelper.Create( APDFCanvas: TdxPSPDFReportRenderCanvas; ATextFormat: Cardinal); begin inherited Create; FPDFCanvas := APDFCanvas; FTextParams := PDFCanvas.CalculateTextParams(ATextFormat or CXTO_CALCROWCOUNT); end; function TdxPSPDFExportCustomTextHelper.CalculateBreakExtractSpacing( const AWidth: Integer; ARow: PcxTextRow): Integer; begin if ARow.BreakCount > 0 then Result := (AWidth - ARow.TextExtents.cx) div ARow.BreakCount else Result := 0; end; function TdxPSPDFExportCustomTextHelper.CalculateTextOutParams( const ABounds: TRect; AMaxLineCount: Integer; const AText: string): TRect; var ATextRowCount: Integer; begin Result := cxPrepareRect(ABounds, TextParams, 0, 0); cxMakeTextRows(PDFCanvas.Canvas.Handle, PChar(AText), Length(AText), Result, TextParams, FRows, ATextRowCount, AMaxLineCount, PDFCanvas, @dxPDFCalcTextRowExtents); if TextParams.EndEllipsis and (TextRowCount > 0) then CalculateEndEllipsis(PDFCanvas.Canvas.Handle, ABounds, LastTextRow); Result := CalculateTextRect(Result); end; procedure TdxPSPDFExportCustomTextHelper.CalculateEndEllipsis( DC: HDC; const R: TRect; ARow: PcxTextRow); var AWidth: Integer; begin AWidth := cxRectWidth(R); FHasEndEllipsis := ARow^.TextExtents.cx > AWidth; if FHasEndEllipsis then begin cxPrepareEndEllipsis(DC, ARow, TextParams, @dxPDFCalcTextRowExtents, AWidth, PDFCanvas); Inc(ARow^.TextExtents.cx, TextParams.EndEllipsisWidth); end; end; function TdxPSPDFExportCustomTextHelper.CalculateTotalTextHeight: Integer; var I: Integer; begin Result := 0; for I := 0 to TextRowCount - 1 do Inc(Result, TextRow[I].TextExtents.cy); end; function TdxPSPDFExportCustomTextHelper.GetLastTextRow: PcxTextRow; begin Result := TextRow[TextRowCount - 1]; end; function TdxPSPDFExportCustomTextHelper.GetTextRow(Index: Integer): PcxTextRow; begin Result := cxGetTextRow(FRows, Index); end; function TdxPSPDFExportCustomTextHelper.GetTextRowCount: Integer; begin Result := FRows.Count; end; function TdxPSPDFExportCustomTextHelper.GetTextRowText(Index: Integer): WideString; var ARow: PcxTextRow; begin ARow := TextRow[Index]; if Assigned(ARow) then begin SetString(Result, ARow^.Text, ARow^.TextLength); if FHasEndEllipsis and (Index = TextRowCount - 1) then Result := Result + '...'; end else Result := ''; end; { TdxPSPDFExportHorizontalTextHelper } function TdxPSPDFExportHorizontalTextHelper.CalculateTextRect(const ABounds: TRect): TRect; var AHeight: Integer; begin Result := ABounds; AHeight := CalculateTotalTextHeight; case TextParams.TextAlignY of taCenterY: Result := cxRectCenter(ABounds, cxRectWidth(ABounds), AHeight); taBottom: Result.Top := Result.Bottom - AHeight; end; if TextParams.PreventTopExceed then Result := cxRectSetTop(Result, Max(Result.Top, ABounds.Top)); end; function TdxPSPDFExportHorizontalTextHelper.CalculateTextRowOutRect( ATextRow: PcxTextRow; var R: TRect; out ABreakExtraSpacing: Integer): TRect; var APoint: TPoint; ASize: TSize; begin APoint := R.TopLeft; ABreakExtraSpacing := 0; ASize := ATextRow.TextExtents; case TextParams.TextAlignX of taCenterX: APoint.X := Max(R.Left, (R.Right + R.Left - ASize.cx) div 2); taRight: APoint.X := R.Right - ASize.cx; taJustifyX, taDistributeX: begin ASize.cx := cxRectWidth(R); ABreakExtraSpacing := CalculateBreakExtractSpacing(ASize.cx, ATextRow); end; end; Inc(R.Top, ASize.cy); if TextParams.PreventLeftExceed then APoint.X := Max(APoint.X, R.Left); Result := Bounds(APoint.X, APoint.Y, ASize.cx, ASize.cy); end; { TdxPSPDFExportVerticalTextHelper } function TdxPSPDFExportVerticalTextHelper.CalculateTextRect(const ABounds: TRect): TRect; var AHeight: Integer; begin Result := ABounds; AHeight := CalculateTotalTextHeight; case TextParams.TextAlignY of taCenterY: Result := cxRectCenter(ABounds, AHeight, cxRectHeight(ABounds)); taBottom: Result.Left := Result.Right - AHeight end; end; function TdxPSPDFExportVerticalTextHelper.CalculateTextRowOutRect( ATextRow: PcxTextRow; var R: TRect; out ABreakExtraSpacing: Integer): TRect; var APoint: TPoint; ASize: TSize; begin ABreakExtraSpacing := 0; APoint := Point(R.Left, R.Bottom); ASize := ATextRow.TextExtents; case TextParams.TextAlignX of taCenterX: APoint.Y := Max(R.Top, (R.Bottom + R.Top + ASize.cx) div 2); taRight: APoint.Y := R.Top + ATextRow.TextExtents.cx; taJustifyX, taDistributeX: begin ASize.cx := cxRectHeight(R); ABreakExtraSpacing := CalculateBreakExtractSpacing(ASize.cx, ATextRow); end; end; Inc(R.Left, ASize.cy); if TextParams.PreventLeftExceed then APoint.Y := Max(APoint.Y, R.Top); Result := Bounds(APoint.X, APoint.Y, ASize.cy, ASize.cx); end; function TdxPSPDFExportVerticalTextHelper.CalculateTextOutParams( const ABounds: TRect; AMaxLineCount: Integer; const AText: string): TRect; begin Result := inherited CalculateTextOutParams(cxRectRotate(ABounds), AMaxLineCount, AText); end; { TdxPSPDFReportExportProvider } constructor TdxPSPDFReportExportProvider.Create(AReportLink: TBasedxReportLink); begin inherited Create(AReportLink); FOptions := TdxPSPDFReportExportOptions.Create; FPDFFile := TdxPSPDFFile.Create; end; destructor TdxPSPDFReportExportProvider.Destroy; begin FreeAndNil(FOptions); FreeAndNil(FPDFFile); inherited Destroy; end; procedure TdxPSPDFReportExportProvider.AssignSettings(APDFFile: TdxPSPDFFile); begin APDFFile.EmbedFonts := Options.EmbedFonts; APDFFile.UseCIDFonts := Options.UseCIDFonts; APDFFile.JPEGQuality := Options.JPEGQuality; APDFFile.CompressStreams := Options.CompressStreams; APDFFile.UseJPEGCompression := Options.UseJPEGCompression; APDFFile.DocumentInfo.Author := Options.Author; APDFFile.DocumentInfo.Creator := Options.Creator; APDFFile.DocumentInfo.Keywords := Options.Keywords; APDFFile.DocumentInfo.Subject := Options.Subject; APDFFile.DocumentInfo.Title := Options.Title; APDFFile.DocumentInfo.Producer := 'ExpressPrinting System'; APDFFile.SecurityOptions.Assign(Options.SecurityOptions); end; procedure TdxPSPDFReportExportProvider.Build; var APageIndex: Integer; begin AssignSettings(PDFFile); ReportLink.IsCurrentLink := True; ReportLink.RebuildReport; case Options.PageRangeInfo.PageRanges of prCurrent: ExportPage(ReportLink.CurrentPage - 1); prAll: for APageIndex := 0 to ReportLink.PageCount - 1 do ExportPage(APageIndex); prRange: for APageIndex := 0 to Options.PageRangeInfo.PageIndexCount - 1 do ExportPage(Options.PageRangeInfo.PageIndex[APageIndex] - 1); end; end; procedure TdxPSPDFReportExportProvider.ExportPage(APageIndex: Integer); var ACanvas: TdxPSPDFReportRenderCanvas; begin if (APageIndex >= 0) and (APageIndex < ReportLink.PageCount) then begin ACanvas := TdxPSPDFReportRenderCanvas.Create(Self); try Page := PDFFile.AddPage; Page.PageWidth := ScaleValue(RenderInfo.PageSize.X); Page.PageHeight := ScaleValue(RenderInfo.PageSize.Y); Renderer.RenderPageEx(ACanvas, Page.PageBounds, APageIndex, 0, 100); finally ACanvas.Free; end; end; end; procedure TdxPSPDFReportExportProvider.SaveToStream(AStream: TStream); begin PDFFile.SaveToStream(AStream); end; function TdxPSPDFReportExportProvider.ScaleValue(AValue: Integer): Integer; begin Result := MulDiv(AValue, RenderInfo.PointsPerInch, RenderInfo.UnitsPerInch); end; procedure TdxPSPDFReportExportProvider.SetOptions(AOptions: TdxPSPDFReportExportOptions); begin FOptions.Assign(AOptions); end; procedure TdxPSPDFReportExportProvider.SetPage(AValue: TdxPSPDFPage); begin if FPage <> AValue then begin FPage := AValue; if FPage = nil then FPageContent := nil else FPageContent := FPage.Content; end; end; { TdxPSPDFReportRenderCanvas } constructor TdxPSPDFReportRenderCanvas.Create(AProvider: TdxPSPDFReportExportProvider); begin inherited Create; FProvider := AProvider; FRegion := TcxRegion.Create(cxNullRect); FSavedRegions := TcxObjectList.Create; FLogicalToPDFCoordinates := 1; end; destructor TdxPSPDFReportRenderCanvas.Destroy; begin FreeAndNil(FSavedRegions); FreeAndNil(FRegion); inherited Destroy; end; function TdxPSPDFReportRenderCanvas.CalculateLineThickness( AUnitsPerPixel, AUnitsPerPoint: Integer): Integer; begin Result := AUnitsPerPixel; end; procedure TdxPSPDFReportRenderCanvas.DeviceToLogicalCoordinates(var R: TRect); begin R.Top := MulDiv(R.Top, PixelsNumerator, PixelsDenominator); R.Left := MulDiv(R.Left, PixelsNumerator, PixelsDenominator); R.Right := MulDiv(R.Right, PixelsNumerator, PixelsDenominator); R.Bottom := MulDiv(R.Bottom, PixelsNumerator, PixelsDenominator); end; procedure TdxPSPDFReportRenderCanvas.LogicalToDeviceCoordinates(var R: TRect); begin R.Top := MulDiv(R.Top, PixelsDenominator, PixelsNumerator); R.Left := MulDiv(R.Left, PixelsDenominator, PixelsNumerator); R.Right := MulDiv(R.Right, PixelsDenominator, PixelsNumerator); R.Bottom := MulDiv(R.Bottom, PixelsDenominator, PixelsNumerator); end; procedure TdxPSPDFReportRenderCanvas.SetCanvasExts( const APageSize: TPoint; AMappingMode: TdxPSReportRenderCanvasMappingMode; AScaleNumerator, AScaleDenominator: Integer; const AViewPort: TRect); var AScaleFactor: Double; begin inherited SetCanvasExts(APageSize, AMappingMode, AScaleNumerator, AScaleDenominator, AViewPort); AScaleFactor := AScaleDenominator / AScaleNumerator; FLogicalToPDFCoordinates := cxRectWidth(AViewPort) / APageSize.X * AScaleFactor; FPageSize := Point(Round(APageSize.X / AScaleFactor), Round(APageSize.Y / AScaleFactor)); ResetPageRegion; end; function TdxPSPDFReportRenderCanvas.CorrectRectByViewOrg(const R: TRect): TRect; begin Result := R; with WindowOrg do OffsetRect(Result, -X, -Y); end; function TdxPSPDFReportRenderCanvas.CalculatePDFTextLineWidth(const AText: WideString): Integer; var AScaleKoeff: Single; begin AScaleKoeff := PixelsNumerator / PixelsDenominator; AScaleKoeff := AScaleKoeff * 100 / Provider.ReportLink.RealScaleFactor; Result := Round(PageContent.TextWidth(AText) * AScaleKoeff); end; function TdxPSPDFReportRenderCanvas.ConvertPoint(const P: TPoint): TdxPSPDFPoint; begin Result := dxConvertToPDFPoint(cxPointOffset(P, WindowOrg, False), LogicalToPDFCoordinates); end; function TdxPSPDFReportRenderCanvas.ConvertRect( R: TRect; ACorrectOffset: Boolean = True): TdxPSPDFRect; begin if ACorrectOffset then R := CorrectRectByViewOrg(R); Result := dxConvertToPDFRect(R, LogicalToPDFCoordinates); end; function TdxPSPDFReportRenderCanvas.ConvertValue(AValue: Integer): Double; begin Result := AValue * LogicalToPDFCoordinates; end; procedure TdxPSPDFReportRenderCanvas.InternalDrawStretchPicture( APicture: TGraphic; const R: TRect); begin if IsRectVisible(R) then begin PageContent.SaveGraphicState; try PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.DrawGraphic(ConvertRect(R), APicture); finally PageContent.RestoreGraphicState; end; end; end; procedure TdxPSPDFReportRenderCanvas.InternalDrawText( const ABounds: TRect; const AText: string; AFont: TFont; ATextColor: TColor; AFormat: TcxTextOutFormat; AMaxLineCount: Integer; AVertical: Boolean; ADirection: TcxVerticalTextOutDirection); function CreateTextExportHelper(AFormat: TcxTextOutFormat): TdxPSPDFExportCustomTextHelper; begin if AVertical then Result := TdxPSPDFExportVerticalTextHelper.Create(Self, AFormat) else Result := TdxPSPDFExportHorizontalTextHelper.Create(Self, AFormat); end; const AngleMap: array[TcxVerticalTextOutDirection] of Double = (270, 90); var ABreakExtraSpacing: Integer; ATextExportHelper: TdxPSPDFExportCustomTextHelper; ATextRow: PcxTextRow; ATextRowRect: TRect; I: Integer; R: TRect; begin SaveState; try SelectFont(AFont, ATextColor); ATextExportHelper := CreateTextExportHelper(AFormat); try R := ATextExportHelper.CalculateTextOutParams(ABounds, AMaxLineCount, AText); for I := 0 to ATextExportHelper.TextRowCount - 1 do begin ATextRow := ATextExportHelper.TextRow[I]; ATextRowRect := ATextExportHelper.CalculateTextRowOutRect(ATextRow, R, ABreakExtraSpacing); if not IsRectEmpty(ATextRowRect) then begin DrawTextLine(ATextRowRect, ABounds, ATextRow.TextExtents.cx, ATextExportHelper.TextRowText[I], ATextColor, IfThen(AVertical, AngleMap[ADirection], 0), ABreakExtraSpacing); end; end; finally ATextExportHelper.Free; end; finally RestoreState; end; end; procedure TdxPSPDFReportRenderCanvas.DrawTextLine(const ATextRect: TRect; const AClipBounds: TRect; ATextWidth: Integer; const AText: WideString; ATextColor: TColor = clDefault; ARotateAngle: Double = 0; ABreakExtraSpacing: Integer = 0); var ACharsSpacing: Double; begin if (Length(AText) > 0) and IsRectVisible(ATextRect) then begin PageContent.SaveGraphicState; try SelectFont(Font, ATextColor); PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); ACharsSpacing := ConvertValue(ATextWidth - CalculatePDFTextLineWidth(AText)) / Length(AText); PageContent.DrawText(ConvertRect(AClipBounds), ConvertRect(ATextRect), AText, ARotateAngle, ACharsSpacing, ConvertValue(ABreakExtraSpacing)); finally PageContent.RestoreGraphicState; end; end; end; procedure TdxPSPDFReportRenderCanvas.InternalDrawTilePicture( APicture: TGraphic; const R: TRect; APictureWidth, APictureHeight: Integer); begin if IsRectVisible(R) then begin PageContent.SaveGraphicState; try PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.FillRectByGraphic(ConvertRect(R), ConvertValue(APictureWidth), ConvertValue(APictureHeight), APicture); finally PageContent.RestoreGraphicState end; end; end; procedure TdxPSPDFReportRenderCanvas.InternalFillRect( const R: TRect; ABackColor, AForeColor: TColor; APattern: TdxPSFillPatternClass; APatternBrush: TBrush); var ABitmap: TcxBitmap; ARect: TRect; begin if (Assigned(APattern) and APattern.Solid) or (APatternBrush.Bitmap = nil) or APatternBrush.Bitmap.Empty then begin if Assigned(APatternBrush) then ABackColor := APatternBrush.Color; PageContent.FillRect(ConvertRect(R), ABackColor); end else begin ABitmap := TcxBitmap.Create; try ABitmap.SetSize(APatternBrush.Bitmap.Width, APatternBrush.Bitmap.Height); ABitmap.Canvas.Font.Assign(Font); ABitmap.Canvas.Font.Color := AForeColor; ABitmap.Canvas.Brush.Assign(APatternBrush); SetBkColor(ABitmap.Canvas.Handle, ColorToRGB(ABackColor)); if Assigned(APattern) and APattern.RequiredBrushOrigin then begin ARect := R; LogicalToDeviceCoordinates(ARect); SetBrushOrgEx(ABitmap.Canvas.Handle, ARect.Left mod ABitmap.Width, ARect.Top mod ABitmap.Height, nil); end; ABitmap.Canvas.FillRect(ABitmap.ClientRect); DrawPicture(ABitmap, R, ppmTile, PixelsNumerator, PixelsDenominator); finally ABitmap.Free; end; end; end; function TdxPSPDFReportRenderCanvas.GetIsPrinterCanvas: Boolean; begin Result := True; end; function TdxPSPDFReportRenderCanvas.GetSupportsTransparentImagesDrawing: Boolean; begin Result := False; end; procedure TdxPSPDFReportRenderCanvas.DrawEllipseFrame( const R: TRect; AColor: TColor; AThickness: Integer); var ARect: TRect; begin if IsRectVisible(R) then begin PageContent.SaveGraphicState; try ARect := R; PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.WriteEllipse(ConvertRect(ARect)); InflateRect(ARect, -AThickness, -AThickness); PageContent.WriteEllipse(ConvertRect(ARect)); PageContent.ApplyClipping(pcmDiff); PageContent.FillRect(ConvertRect(R), AColor); finally PageContent.RestoreGraphicState; end; end; end; procedure TdxPSPDFReportRenderCanvas.DrawExpandButton(R: TRect; AEdgeStyle: TdxCheckButtonEdgeStyle; AMarlettFont, ASymbolFont: TFont; AExpanded, AShadow, AFillInterior: Boolean; ABorderColor, ABackgroundColor: TColor; ALineThickness: Integer); var R1: TRect; begin if IsRectVisible(R) then begin PageContent.SaveGraphicState; try ALineThickness := ALineThickness div 2; if AFillInterior then begin R1 := R; InflateRect(R1, -ALineThickness, -ALineThickness); PageContent.FillRect(ConvertRect(R1), ABackgroundColor); end; R := cxRectInflate(R, ALineThickness, ALineThickness); DrawExpandButtonBorders(R, AMarlettFont, ABorderColor, AEdgeStyle, ALineThickness); DrawExpandButtonGlyph(R, ASymbolFont, ABorderColor, AExpanded, ALineThickness); PageContent.Fill; finally PageContent.RestoreGraphicState;; end; end; end; procedure TdxPSPDFReportRenderCanvas.DrawExpandButtonBorders(const R: TRect; AMarlettFont: TFont; ABorderColor: TColor; AEdgeStyle: TdxCheckButtonEdgeStyle; ALineThickness: Integer); var ATopLeftColor, ARightBottomColor: TColor; R1: TRect; begin if AEdgeStyle <> cbesNone then begin R1 := cxRectInflate(R, -ALineThickness, -ALineThickness); GetBorderColors(AEdgeStyle, ABorderColor, True, ARightBottomColor, ATopLeftColor); PageContent.DrawFrame(ConvertRect(R1), ConvertValue(ALineThickness), ATopLeftColor, ARightBottomColor, cxBordersAll); if AEdgeStyle in [cbes3D, cbesSoft3D, cbesBoldFlat] then begin GetBorderColors(AEdgeStyle, ABorderColor, False, ARightBottomColor, ATopLeftColor); PageContent.DrawFrame(ConvertRect(R), ConvertValue(ALineThickness), ATopLeftColor, ARightBottomColor, cxBordersAll); end; end; end; procedure TdxPSPDFReportRenderCanvas.DrawExpandButtonGlyph(R: TRect; ASymbolFont: TFont; ATextColor: TColor; AExpanded: Boolean; ALineThickness: Integer); var R1: TRect; begin InflateRect(R, -6 * ALineThickness, -6 * ALineThickness); R1 := cxRectCenter(R, cxRectWidth(R), ALineThickness); PageContent.FillRect(ConvertRect(R1), ATextColor); if not AExpanded then begin R1 := cxRectCenter(R, ALineThickness, cxRectHeight(R)); PageContent.FillRect(ConvertRect(R1), ATextColor); end; end; procedure TdxPSPDFReportRenderCanvas.DrawFrame(const R: TRect; ATopLeftColor, ARightBottomColor: TColor; ABorderWidth: Integer = 1; ABorders: TcxBorders = cxBordersAll); begin if IsRectVisible(R) and (ABorders <> []) then begin PageContent.SaveGraphicState; try PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.DrawFrame(ConvertRect(R), ConvertValue(ABorderWidth), ATopLeftColor, ARightBottomColor, ABorders) finally PageContent.RestoreGraphicState; end; end; end; procedure TdxPSPDFReportRenderCanvas.DrawPicture(APicture: TGraphic; const R: TRect; APictureMode: TdxPicturePaintMode; ANumerator: Integer; ADenominator: Integer; AOffsetX: Integer = 0; AOffsetY: Integer = 0); var AMetaFileExport: TdxPSPDFMetaFileExportProvider; begin if not APicture.InheritsFrom(TMetafile) then inherited DrawPicture(APicture, R, APictureMode, ANumerator, ADenominator, AOffsetX, AOffsetY) else begin AMetaFileExport := TdxPSPDFMetaFileExportProvider.Create(Self); try AMetaFileExport.Render(TMetafile(APicture), R, ANumerator, ADenominator); finally AMetaFileExport.Free; end; end; end; procedure TdxPSPDFReportRenderCanvas.DrawRoundFrame(const R: TRect; AEllipseWidth, AEllipseHeight: Integer; AColor: TColor; AThickness: Integer); var ARect: TRect; begin if IsRectVisible(R) then begin PageContent.SaveGraphicState; try ARect := R; PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.WriteRoundRect(ConvertRect(ARect), ConvertValue(AEllipseWidth), ConvertValue(AEllipseHeight)); AEllipseHeight := MulDiv(AEllipseHeight, cxRectHeight(R) - 2 * AThickness, cxRectHeight(R)); AEllipseWidth := MulDiv(AEllipseWidth, cxRectWidth(R) - 2 * AThickness, cxRectWidth(R)); InflateRect(ARect, -AThickness, -AThickness); PageContent.WriteRoundRect(ConvertRect(ARect), ConvertValue(AEllipseWidth), ConvertValue(AEllipseHeight)); PageContent.ApplyClipping(pcmDiff); PageContent.FillRect(ConvertRect(R), AColor); finally PageContent.RestoreGraphicState; end; end; end; procedure TdxPSPDFReportRenderCanvas.DrawText( var R: TRect; const AText: string; AFont: TFont; AFormat: DWORD; ATextColor: TColor = clDefault; AMaxLineCount: Integer = 0); begin InternalDrawText(R, AText, AFont, ATextColor, AFormat, AMaxLineCount, False, vtdTopToBottom); end; procedure TdxPSPDFReportRenderCanvas.FillEllipse(const R: TRect; ABackColor, AForeColor: TColor; APattern: TdxPSFillPatternClass; APatternBrush: TBrush); begin if IsRectVisible(R) then begin PageContent.SaveGraphicState; try PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.WriteEllipse(ConvertRect(R)); PageContent.ApplyClipping; InternalFillRect(R, ABackColor, AForeColor, APattern, APatternBrush); finally PageContent.RestoreGraphicState; end; end; end; procedure TdxPSPDFReportRenderCanvas.FillRect(const R: TRect; AColor: TColor); begin if IsRectVisible(R) and (AColor <> clNone) then begin PageContent.SaveGraphicState; try PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); InternalFillRect(R, AColor, clNone, TdxPSSolidFillPattern, nil); finally PageContent.RestoreGraphicState; end; end; end; procedure TdxPSPDFReportRenderCanvas.FillRegion(ARegion: HRGN; ABackColor, AForeColor: TColor; APattern: TdxPSFillPatternClass; APatternBrush: TBrush); var ARect: TRect; ATempRegion: TcxRegion; begin SaveClipRgn; try ATempRegion := TcxRegion.Create(cxNullRect); CombineRgn(ATempRegion.Handle, ATempRegion.Handle, ARegion, RGN_OR); ATempRegion.Offset(cxPointInvert(WindowOrg)); Region.Combine(ATempRegion, roIntersect); if not Region.IsEmpty then begin PageContent.SaveGraphicState; try ARect := Region.BoundsRect; OffsetRect(ARect, WindowOrg.X, WindowOrg.Y); PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); InternalFillRect(ARect, ABackColor, AForeColor, APattern, APatternBrush); finally PageContent.RestoreGraphicState; end; end; finally RestoreClipRgn; end; end; procedure TdxPSPDFReportRenderCanvas.FillRoundRect(R: TRect; AEllipseWidth, AEllipseHeight: Integer; ABackColor, AForeColor: TColor; APattern: TdxPSFillPatternClass; APatternBrush: TBrush); begin if IsRectVisible(R) then begin PageContent.SaveGraphicState; try PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.WriteRoundRect(ConvertRect(R), ConvertValue(AEllipseWidth), ConvertValue(AEllipseHeight)); PageContent.ApplyClipping; InternalFillRect(R, ABackColor, AForeColor, APattern, APatternBrush); finally PageContent.RestoreGraphicState; end; end; end; function TdxPSPDFReportRenderCanvas.IsRectVisible(const R: TRect): Boolean; begin Result := not IsRectEmpty(R) and RectInRegion(Region.Handle, CorrectRectByViewOrg(R)); end; procedure TdxPSPDFReportRenderCanvas.Pie(const R: TRect; const APoint1, APoint2: TPoint); begin PageContent.SaveGraphicState; try PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.Pie(ConvertRect(R), ConvertPoint(APoint1), ConvertPoint(APoint2), Brush.Color); finally PageContent.RestoreGraphicState; end; end; procedure TdxPSPDFReportRenderCanvas.Polyline( const APoints: array of TPoint; AColor: TColor; ALineWidth: Integer); begin if Length(APoints) > 1 then begin PageContent.SaveGraphicState; try PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.Polyline( dxConvertPointsToPDFPoints(APoints, WindowOrg, LogicalToPDFCoordinates), ConvertValue(ALineWidth), AColor); finally PageContent.RestoreGraphicState; end; end; end; procedure TdxPSPDFReportRenderCanvas.Polygon( const APoints: array of TPoint; AColor, ABackgroundColor: TColor; ALineWidth: Integer); begin if Length(APoints) > 1 then begin PageContent.SaveGraphicState; try PageContent.SelectClipRegion(Region.Handle, LogicalToPDFCoordinates); PageContent.Polygon( dxConvertPointsToPDFPoints(APoints, WindowOrg, LogicalToPDFCoordinates), ConvertValue(ALineWidth), AColor, ABackgroundColor); finally PageContent.RestoreGraphicState; end; end; end; procedure TdxPSPDFReportRenderCanvas.RotatedTextOut( const ABounds: TRect; const AText: string; AFont: TFont; AAlignHorz: TcxTextAlignX = taCenterX; AAlignVert: TcxTextAlignY = taCenterY; AWordBreak: Boolean = True; ADirection: TcxVerticalTextOutDirection = vtdBottomToTop); var AFormat: Cardinal; begin AFormat := cxMakeFormat(AAlignHorz, AAlignVert); if AWordBreak then AFormat := AFormat or CXTO_WORDBREAK; InternalDrawText(ABounds, AText, AFont, clDefault, AFormat, 0, True, ADirection); end; function TdxPSPDFReportRenderCanvas.TextSize(const AText: string): TSize; var ATempStr: WideString; begin SelectFont(Font, clDefault); ATempStr := dxStringToWideString(AText, dxGetCodePageFromCharset(Font.Charset)); Result := dxPDFCalcTextRowExtents(Canvas.Handle, PWideChar(ATempStr), Length(ATempStr), False, Self); end; function TdxPSPDFReportRenderCanvas.GetPageContent: TdxPSPDFPageContent; begin Result := Provider.PageContent; end; function TdxPSPDFReportRenderCanvas.ExcludeClipRect(const R: TRect): Integer; var R1: TRect; begin Region.Combine(TcxRegion.Create(CorrectRectByViewOrg(R)), roSubtract); Result := GetRgnBox(Region.Handle, R1); end; function TdxPSPDFReportRenderCanvas.IntersectClipRgn(const R: TRect): Integer; var R1: TRect; begin Region.Combine(TcxRegion.Create(CorrectRectByViewOrg(R)), roIntersect); Result := GetRgnBox(Region.Handle, R1); end; procedure TdxPSPDFReportRenderCanvas.SetClipRect(const R: TRect); begin Region.Combine(TcxRegion.Create(CorrectRectByViewOrg(R)), roSet); end; function TdxPSPDFReportRenderCanvas.BeginCustomDraw( const AClipBounds: TRect; AFont: TFont; AColor: TColor): TCanvas; begin SaveState; FCustomDrawBounds := AClipBounds; PrepareCanvasForCustomDraw(AFont, AColor); FCustomDrawMetaFile := TMetafile.Create; FCustomDrawMetaFile.Width := cxRectWidth(AClipBounds); FCustomDrawMetaFile.Height := cxRectHeight(AClipBounds); FCustomDrawMetaFileCanvas := TMetafileCanvas.Create(FCustomDrawMetaFile, 0); FCustomDrawMetaFileCanvas.Brush.Assign(Brush); FCustomDrawMetaFileCanvas.Font.Assign(Font); SetWindowOrgEx(FCustomDrawMetaFileCanvas.Handle, -AClipBounds.Left, -AClipBounds.Top, nil); Result := FCustomDrawMetaFileCanvas; end; procedure TdxPSPDFReportRenderCanvas.EndCustomDraw(var ACanvas: TCanvas); begin FreeAndNil(FCustomDrawMetaFileCanvas); DrawPicture(FCustomDrawMetaFile, FCustomDrawBounds, ppmStretch, 1, 1); FreeAndNil(FCustomDrawMetaFile); RestoreState; end; procedure TdxPSPDFReportRenderCanvas.ResetPageRegion; begin Region.Handle := CreateRectRgn(0, 0, PageSize.X, PageSize.Y); end; procedure TdxPSPDFReportRenderCanvas.RestoreClipRgn; var ALastRegion: TcxRegion; begin ALastRegion := TcxRegion(FSavedRegions.Last); FSavedRegions.Remove(ALastRegion); Region.Combine(ALastRegion, roSet) end; procedure TdxPSPDFReportRenderCanvas.SaveClipRgn; var ARegion: TcxRegion; begin ARegion := TcxRegion.Create; ARegion.Combine(Region, roSet, False); FSavedRegions.Add(ARegion); end; procedure TdxPSPDFReportRenderCanvas.SelectFont(AFont: TFont; ATextColor: TColor); begin Font := AFont; if ATextColor = clDefault then ATextColor := Font.Color; if (ATextColor = clDefault) or (ATextColor = clNone) then ATextColor := clWindowText; PageContent.Font := Provider.PDFFile.AddFont(Font); PageContent.FontSize := -ConvertValue(Font.Height); PageContent.FontColor := ATextColor; end; end.