FastReport_2022_VCL/LibD28/frxEMFtoPDFExport.pas
2024-01-01 16:13:08 +01:00

2187 lines
56 KiB
ObjectPascal

{******************************************}
{ }
{ FastReport VCL }
{ EMF to PDF Export }
{ }
{ Copyright (c) 1998-2021 }
{ by Fast Reports Inc. }
{ }
{******************************************}
unit frxEMFtoPDFExport;
interface
{$I frx.inc}
uses Windows, Graphics, Classes, frxExportHelpers, frxEMFAbstractExport,
frxEMFFormat, frxExportPDFHelpers, frxClass, frxAnaliticGeometry, frxUtils;
type
TBezierResult = record
P0, P1, P2, P3: TfrxPoint;
end;
TPDFDeviceContext = class (TDeviceContext);
TEMFtoPDFExport = class (TEMFAbstractExport)
private
FForceMitterLineJoin: Boolean;
FForceButtLineCap: Boolean;
FForceNullBrush: Boolean;
FTransparency: Boolean;
FForceAnsi: Boolean;
FClipped: Boolean;
FPictureDPI: Integer;
FPrecision: Integer;
procedure Put(const S: AnsiString);
procedure PutCRLF(const S: AnsiString); {$IFDEF Delphi12} overload;
procedure PutCRLF(const S: String); overload; {$ENDIF}
procedure PutLF(const S: AnsiString); {$IFDEF Delphi12} overload;
procedure PutLF(const S: String); overload; {$ENDIF}
function pdfSize(emfSize: Extended): Extended;
function pdfFrxPoint(emfP: TPoint): TfrxPoint; overload;
function pdfFrxPoint(emfSP: TSmallPoint): TfrxPoint; overload;
function pdfFrxPoint(emfDP: TfrxPoint): TfrxPoint; overload;
function pdfFrxRect(emfR: TRect): TfrxRect;
// function emfSize2Str(emfSize: Extended): String;
function emfPoint2Str(emfP: TPoint): String; overload;
function emfPoint2Str(emfSP: TSmallPoint): String; overload;
function emfPoint2Str(emfFP: TfrxPoint): String; overload;
function emfRect2Str(emfR: TRect): String;
function EvenOdd: String;
function IsNullBrush: Boolean;
function IsNullPen: Boolean;
function BezierCurve(Center, Radius: TfrxPoint; startAngle, arcAngle: Double): TBezierResult;
procedure cmd_AngleArc(Center, Radius: TfrxPoint; StartAngle, SweepAngle: Single);
procedure cmd_RoundRect(l, t, r, b, rx, ry: Extended);
procedure cmdPathPainting(Options: integer);
procedure cmdPathParams(Options: integer);
procedure cmdSetClippingPath;
procedure cmdCloseSubpath;
procedure cmdAppendAngleArcToPath(AngleArc: TEMRAngleArc);
procedure cmdAppendArcToPath(Arc: TEMRArc);
procedure cmdAppendPieToPath(Pie: TEMRPie);
procedure cmdAppendEllipsToPath(emfRect: TRect);
procedure cmdAppendRectangleToPath(emfRect: TRect; IsCompartible: Boolean = False);
procedure cmdAppendEMFRectangleToPath(emfRect: TRect);
procedure cmdAppendRoundRectToPath(emfRect: TRect; emfCorners: TSize);
procedure cmdMoveTo(X, Y: extended); overload;
procedure cmdMoveTo(emfP: TPoint); overload;
procedure cmdMoveTo(emfSP: TSmallPoint); overload;
procedure cmdMoveTo(emfFP: TfrxPoint); overload;
procedure cmdLineTo(X, Y: extended); overload;
procedure cmdLineTo(emfP: TPoint); overload;
procedure cmdLineTo(emfSP: TSmallPoint); overload;
procedure cmdLineTo(emfDP: TfrxPoint); overload;
procedure cmdSetLineDashPattern(PenStyle: LongWord; Width: Extended);
procedure cmdSetStrokeColor(Color: TColor);
procedure cmdSetFillColor(Color: TColor);
procedure cmdSetLineWidth(Width: Extended); overload;
procedure cmdSetLineWidth(PDFWidth: String); overload;
procedure cmdSetMiterLimit(MiterLimit: Extended);
procedure cmdSetLineCap(PenEndCap: Integer);
procedure cmdSetLineJoin(PenLineJoin: Integer);
procedure cmdAppendCurvedSegment2final(emfSP1, emfSP3: TSmallPoint); overload;
procedure cmdAppendCurvedSegment2final(emfP1, emfP3: TPoint); overload;
procedure cmdAppendCurvedSegment3(emfSP1, emfSP2, emfSP3: TSmallPoint); overload;
procedure cmdAppendCurvedSegment3(emfP1, emfP2, emfP3: TPoint); overload;
procedure cmdAppendCurvedSegment3(emfDP1, emfDP2, emfDP3: TfrxPoint); overload;
procedure cmdPolyBezier(Options: integer = 0);
procedure cmdPolyBezier16(Options: integer = 0);
procedure cmdPolyLine(Options: integer = 0);
procedure cmdPolyLine16(Options: integer = 0);
procedure cmdPolyPolyLine(Options: integer = 0);
procedure cmdPolyPolyLine16(Options: integer = 0);
procedure cmdCreateExtSelectClipRgn;
procedure cmdSaveGraphicsState;
procedure cmdRestoreGraphicsState;
procedure cmdBitmap(emfRect: TRect; dwRop: LongWord; EMRBitmap: TEMRBitmap);
procedure cmdTranslationAndScaling(Sx, Sy, Tx, Ty: Extended);
protected
FPDFRect: TfrxRect;
FLastClipRect: TRect;
FEMFtoPDFFactor: TfrxPoint;
FPOH: TPDFObjectsHelper;
FRotation2D: TRotation2D;
FRealizationList: TStringList;
FqQBalance: Integer;
procedure Comment(CommentString: String = ''); override;
procedure CommentAboutRealization;
procedure CommentTextRect(rtl: TRect; Color: TColor = clRed);
procedure RealizationListFill(RealizedCommands: array of String);
function NormalizeRect(const Rect: TRect): TRect;
procedure DCCreate; override;
function FontCreate: TEMFFont; override;
function PDFDeviceContext: TPDFDeviceContext;
function IsSameCharacterWidth(FontName: string): Boolean;
procedure DrawFontLines(FontSize: Double; TextPosition: TfrxPoint; TextWidth: Extended);
procedure DrawFigureStart;
procedure DrawFigureFinish(Options: integer);
function FillStrokeOptions(Options: integer): integer;
procedure DoEMR_AngleArc; override;
procedure DoEMR_Arc; override;
procedure DoEMR_AlphaBlend; override;
procedure DoEMR_BitBlt; override;
procedure DoEMR_CloseFigure; override;
procedure DoEMR_Ellipse; override;
procedure DoEMR_EoF; override;
procedure DoEMR_ExtSelectClipRgn; override;
procedure DoEMR_ExtTextOutW; override;
procedure DoEMR_FillPath; override;
procedure DoEMR_FillRgn; override;
procedure DoEMR_Header; override;
procedure DoEMR_IntersectClipRect; override;
procedure DoEMR_LineTo; override;
procedure DoEMR_MaskBlt; override;
procedure DoEMR_MoveToEx; override;
procedure DoEMR_Pie; override;
procedure DoEMR_PolyBezier; override;
procedure DoEMR_PolyBezier16; override;
procedure DoEMR_PolyBezierTo; override;
procedure DoEMR_PolyBezierTo16; override;
procedure DoEMR_PolyDraw; override;
procedure DoEMR_PolyDraw16; override;
procedure DoEMR_Polygon; override;
procedure DoEMR_Polygon16; override;
procedure DoEMR_Polyline; override;
procedure DoEMR_Polyline16; override;
procedure DoEMR_PolylineTo; override;
procedure DoEMR_PolylineTo16; override;
procedure DoEMR_PolyPolygon; override;
procedure DoEMR_PolyPolygon16; override;
procedure DoEMR_PolyPolyline; override;
procedure DoEMR_PolyPolyline16; override;
procedure DoEMR_Rectangle; override;
procedure DoEMR_RestoreDC; override;
procedure DoEMR_RoundRect; override;
procedure DoEMR_SaveDC; override;
procedure DoEMR_SelectClipPath; override;
procedure DoEMR_StretchBlt; override;
procedure DoEMR_StretchDIBits; override;
procedure DoEMR_StrokeAndFillPath; override;
procedure DoEMR_StrokePath; override;
procedure DoEMR_TransparentBlt; override;
procedure DoStart; override;
procedure DoFinish; override;
public
constructor Create(InStream, OutStream: TStream; APDFRect: TfrxRect; APOH: TPDFObjectsHelper);
destructor Destroy; override;
property ForceMitterLineJoin: Boolean read FForceMitterLineJoin write FForceMitterLineJoin;
property ForceButtLineCap: Boolean write FForceButtLineCap;
property ForceNullBrush: Boolean write FForceNullBrush;
property Transparency: Boolean write FTransparency;
property ForceAnsi: Boolean write FForceAnsi;
property Clipped: Boolean write FClipped;
property PictureDPI: Integer write FPictureDPI;
property Precision: Integer write FPrecision;
end;
implementation
uses
Contnrs, SysUtils, Types, Math, frxPictureGraphics
{$IFDEF DELPHI16}
, UITypes
{$ENDIF} {It is necessary to prevent H2443}
;
type
TEMFPDFSizeConverter = class
private
FDev: Double; // EMR_ExtTextOutW.rclBounds
FLog: Double; // EMR_ExtTextOutW.emrtext.ptlReference // WordTransform etc...
FPDF: Double;
FChar: Double;
procedure SetDev(const Value: Double);
procedure SetLog(const Value: Double);
procedure SetPDF(const Value: Double);
procedure SetChar(const Value: Double);
protected
FExport: TEMFtoPDFExport;
FEMFPDFFactor: Double;
FFont: TEMFFont;
public
constructor Create(AExport: TEMFtoPDFExport);
destructor Destroy; override;
function LogToDev(Value: Double): Double;
property Dev: Double read FDev write SetDev;
property Log: Double read FLog write SetLog;
property PDF: Double read FPDF write SetPDF;
property Char: Double read FChar write SetChar;
end;
const
// Path-Painting Operators
ppEnd = $0;
ppClose = $1;
ppStroke = $2;
ppFill = $4;
ppWithTo = $8; // To resets the current position
ppBkFill = $10; // Ignored when ppFill not setted
ppAsIsFill = $20; // Ignored when ppFill not setted
ppFontStroke = $40; // Need for Undeline and StrokeOut, dont use DC.Pen
{ Utility routines }
procedure Swap(var E1, E2: Extended);
var
Temp: Extended;
begin
Temp := E1;
E1 := E2;
E2 := Temp;
end;
function PenStyle2Str(PenStyle: LongWord; Width: Extended): String;
var
Dash, Dot: string;
begin
Dash := Float2Str(6 * Width) + ' ';
Dot := Float2Str(2 * Width) + ' ';
case PenStyle of
PS_SOLID:
Result := '';
PS_DASH:
Result := Dash;
PS_DOT:
Result := Dot + Dash;
PS_DASHDOT:
Result := Dash + Dash + Dot + Dash;
PS_DASHDOTDOT:
Result := Dash + Dash + Dot + Dash + Dot + Dash;
PS_NULL:
Result := '';
PS_INSIDEFRAME:
Result := '';
PS_ALTERNATE:
Result := Dot + Dot;
else // PS_USERSTYLE:
Result := Dash + Dot;
end;
if Result <> '' then
Delete(Result, Length(Result), 1);
end;
{ TEMFtoPDFExport }
function TEMFtoPDFExport.BezierCurve(Center, Radius: TfrxPoint; StartAngle, ArcAngle: Double): TBezierResult;
function Rad(Degree: Double): Extended;
begin
Result := Degree * Pi / 180;
end;
var
Cos1, Sin1, Cos2, Sin2, Aux, Alpha: Extended;
begin
SinCos(Rad(StartAngle), Sin1, Cos1);
SinCos(Rad(StartAngle + ArcAngle), Sin2, Cos2);
//point p1. Start point
Result.P0 := frxPoint(Center.X + Radius.X * Cos1, Center.Y - Radius.Y * Sin1);
//point p2. End point
Result.P3 := frxPoint(Center.X + Radius.X * Cos2, Center.Y - Radius.Y * Sin2);
//Alpha constant
Aux := Tan(Rad(ArcAngle / 2));
Alpha := Sin(Rad(ArcAngle)) * (Sqrt(4 + 3 * Aux * Aux) - 1.0) / 3.0;
//point q1. First control point
Result.P1 := frxPoint(Result.P0.X - Alpha * Radius.X * Sin1,
Result.P0.Y - Alpha * Radius.Y * Cos1);
//point q2. Second control point.
Result.P2 := frxPoint(Result.P3.X + Alpha * Radius.X * Sin2,
Result.P3.Y + Alpha * Radius.Y * Cos2);
end;
procedure TEMFtoPDFExport.cmdAppendAngleArcToPath(AngleArc: TEMRAngleArc);
begin
with AngleArc do
cmd_AngleArc(ToFrxPoint(ptlCenter), frxPoint(nRadius, nRadius), eStartAngle, eSweepAngle);
end;
procedure TEMFtoPDFExport.cmdAppendArcToPath(Arc: TEMRArc);
var
Center, Radius, AspectRatio: TfrxPoint;
StartAngle, EndAngle, SweepAngle: Extended;
begin
AspectRatio := frxPoint(1.0, 1.0);
with NormalizeRect(Arc.rclBox) do
begin
Center := frxPoint((Right + Left) / 2, (Bottom + Top) / 2);
Radius := frxPoint((Right - Left) / 2, (Bottom - Top) / 2);
if (Right - Left) > (Bottom - Top) then
AspectRatio.X := (Bottom - Top) / (Right - Left)
else if (Bottom - Top) > (Right - Left) then
AspectRatio.Y := (Right - Left) / (Bottom - Top);
end;
with Arc do
begin
StartAngle := ArcTan2((ptlStart.Y - Center.Y) * AspectRatio.Y,
(ptlStart.X - Center.X) * AspectRatio.X) / Pi * 180;
EndAngle := ArcTan2((ptlEnd.Y - Center.Y) * AspectRatio.Y,
(ptlEnd.X - Center.X) * AspectRatio.X) / Pi * 180;
end;
if FDC.iArcDirection = AD_CLOCKWISE then
Swap(StartAngle, EndAngle);
SweepAngle := StartAngle - EndAngle;
if SweepAngle = 0.0 then
SweepAngle := 360 - 1e-4
else if SweepAngle < 0 then
SweepAngle := SweepAngle + 360;
cmd_AngleArc(Center, Radius, -StartAngle, SweepAngle);
end;
procedure TEMFtoPDFExport.cmdAppendCurvedSegment2final(emfSP1, emfSP3: TSmallPoint);
begin
PutLF(emfPoint2Str(emfSP1) + ' ' + emfPoint2Str(emfSP3) + ' v');
end;
procedure TEMFtoPDFExport.cmdAppendCurvedSegment2final(emfP1, emfP3: TPoint);
begin
PutLF(emfPoint2Str(emfP1) + ' ' + emfPoint2Str(emfP3) + ' v');
end;
procedure TEMFtoPDFExport.cmdAppendCurvedSegment3(emfDP1, emfDP2, emfDP3: TfrxPoint);
begin
PutLF(emfPoint2Str(emfDP1) + ' ' + emfPoint2Str(emfDP2) + ' ' + emfPoint2Str(emfDP3) + ' c');
end;
procedure TEMFtoPDFExport.cmdAppendCurvedSegment3(emfP1, emfP2, emfP3: TPoint);
begin
PutLF(emfPoint2Str(emfP1) + ' ' + emfPoint2Str(emfP2) + ' ' + emfPoint2Str(emfP3) + ' c');
end;
procedure TEMFtoPDFExport.cmdAppendCurvedSegment3(emfSP1, emfSP2, emfSP3: TSmallPoint);
begin
PutLF(emfPoint2Str(emfSP1) + ' ' + emfPoint2Str(emfSP2) + ' ' + emfPoint2Str(emfSP3) + ' c');
end;
procedure TEMFtoPDFExport.cmdAppendEllipsToPath(emfRect: TRect);
begin
with pdfFrxRect(emfRect) do
cmd_RoundRect(Left, Top, Right, Bottom, (Right - Left) / 2, (Top - Bottom) / 2);
end;
procedure TEMFtoPDFExport.cmdAppendEMFRectangleToPath(emfRect: TRect);
begin
EnableTransform := False;
try
cmdAppendRectangleToPath(emfRect);
finally
EnableTransform := True;
end;
end;
procedure TEMFtoPDFExport.cmdAppendPieToPath(Pie: TEMRPie);
var
Center: TfrxPoint;
begin
cmdAppendArcToPath(Pie);
with NormalizeRect(Pie.rclBox) do
Center := frxPoint((Right + Left) / 2, (Bottom + Top) / 2);
cmdLineTo(Center);
end;
procedure TEMFtoPDFExport.cmdAppendRectangleToPath(emfRect: TRect; IsCompartible: Boolean = False);
begin
FScalingOnly := IsCompartible;
try
PutLF(emfRect2Str(emfRect) + ' re'); // Begin new subpath
finally
FScalingOnly := False;
end;
end;
procedure TEMFtoPDFExport.cmdAppendRoundRectToPath(emfRect: TRect; emfCorners: TSize);
begin
with pdfFrxRect(emfRect), emfCorners do
cmd_RoundRect(Left, Top, Right, Bottom, pdfSize(cx) / 2, pdfSize(cy) / 2);
end;
procedure TEMFtoPDFExport.cmdBitmap(emfRect: TRect; dwRop: LongWord; EMRBitmap: TEMRBitmap);
var
pdfRect: TfrxRect;
TempBitmap: TBitMap;
XObjectStream: TMemoryStream;
XObjectHash: TfrxPDFXObjectHash;
PicIndex: Integer;
Graphic: TGraphic;
Size: TSize;
procedure ReferenceToXObject;
begin
FPOH.PageXObjects.Add(PicIndex);
cmdSaveGraphicsState;
cmdTranslationAndScaling(pdfRect.Right - pdfRect.Left,
pdfRect.Top - pdfRect.Bottom, pdfRect.Left, pdfRect.Bottom);
PutCRLF('/Im' + IntToStr(PicIndex) + ' Do');
cmdRestoreGraphicsState;
end;
procedure OutOpaque;
function GetBitmapWithDPI: TBitmap;
var
Factor: Double;
function CalcFactor(Bitmap: TBitmap): Double;
var
SC: TEMFPDFSizeConverter;
ImageDeviceRect: TDoubleRect;
ImageInchSize, ImageActualDPI: TDoublePoint;
begin
SC := TEMFPDFSizeConverter.Create(Self);
try
ImageDeviceRect := DoubleRect(
SC.LogToDev(emfRect.Left), SC.LogToDev(emfRect.Top),
SC.LogToDev(emfRect.Right), SC.LogToDev(emfRect.Bottom));
finally
SC.Free;
end;
ImageInchSize := DoublePoint(
Abs(ImageDeviceRect.Right - ImageDeviceRect.Left) / FDC.DeviceDPI.X,
Abs(ImageDeviceRect.Bottom - ImageDeviceRect.Top) / FDC.DeviceDPI.X);
ImageActualDPI := DoublePoint(
Bitmap.Width / ImageInchSize.X, Bitmap.Width / ImageInchSize.Y);
Factor := FPictureDPI / Sqrt(ImageActualDPI.X * ImageActualDPI.Y);
Result := Factor;
end;
var
BM: TBitmap;
begin
Result := EMRBitmap.GetBitmap;
if (FPictureDPI <= 0) or (CalcFactor(Result) >= 1) then
Exit;
BM := TBitmap.Create;
BM.PixelFormat := Result.PixelFormat;
BM.Width := Round(Result.Width * Factor);
BM.Height := Round(Result.Height * Factor);
BM.Canvas.StretchDraw(BM.Canvas.ClipRect, Result);
Result.Free;
Result := BM;
end;
var
Image: TGraphic;
begin
TempBitmap := GetBitmapWithDPI; // EMRBitmap.GetBitmap;
try
Image := GetGraphicFormats.Convert(TempBitmap, 'JPG', pf24bit, FPOH.Quality);
try
XObjectStream := TMemoryStream.Create;
try
Image.SaveToStream(XObjectStream);
XObjectStream.Position := 0;
GetStreamHash(XObjectHash, XObjectStream);
PicIndex := FPOH.FindXObject(XObjectHash);
if PicIndex = -1 then
PicIndex := FPOH.OutXObjectImage(XObjectHash, Image, XObjectStream);
finally
XObjectStream.Free;
end;
finally
Image.Free;
end;
finally
TempBitmap.Free;
end;
ReferenceToXObject;
end;
begin
pdfRect := pdfFrxRect(emfRect);
case dwRop of // https://msdn.microsoft.com/en-us/library/cc250408.aspx
PATCOPY {P}:
begin
DrawFigureStart;
cmdAppendRectangleToPath(emfRect);
DrawFigureFinish(ppFill);
end;
$1FF0000: // 32 bit here
if FTransparency then
begin
Graphic := TEMRAlphaBlendObj(EMRBitmap).GetARGBGraphic;
try
Size.cx := Round(pdfRect.Right - pdfRect.Left);
Size.cy := Round(pdfRect.Top - pdfRect.Bottom);
PicIndex := FPOH.OutTransparentGraphic(Graphic, Size);
finally
Graphic.Free;
end;
ReferenceToXObject;
end
else
OutOpaque;
SRCCOPY {S}, SRCPAINT {DSo}, SRCAND {DSa}, SRCINVERT {DSx}:
OutOpaque;
$AA0029: {D}
begin
// Do nothing
end;
else
Comment(' Unsupported dwRop: ' + IntToStr(dwRop));
end;
end;
procedure TEMFtoPDFExport.cmdCloseSubpath;
begin
PutLF('h');
end;
procedure TEMFtoPDFExport.cmdCreateExtSelectClipRgn;
var
PRegionData: PRgnData;
Size, i: Integer;
R: TRect;
begin
if FDC.ClipRgn <> HRGN(nil) then
begin
Size := GetRegionData(FDC.ClipRgn, 0, nil);
if Size > 0 then
begin
GetMem(PRegionData, Size);
try
GetRegionData(FDC.ClipRgn, Size, PRegionData);
for i := 0 to PRegionData^.rdh.nCount - 1 do
begin
Move(PRegionData^.Buffer[i * SizeOf(TRect)], R, SizeOf(TRect));
cmdAppendEMFRectangleToPath(R);
FLastClipRect := R;
end;
cmdSetClippingPath;
cmdPathPainting(ppEnd);
finally
FreeMem(PRegionData, Size);
end;
end;
end;
end;
procedure TEMFtoPDFExport.cmdLineTo(emfDP: TfrxPoint);
begin
PutLF(emfPoint2Str(emfDP) + ' l'); // Append straight line segment to path
end;
procedure TEMFtoPDFExport.cmdLineTo(emfP: TPoint);
begin
PutLF(emfPoint2Str(emfP) + ' l'); // Append straight line segment to path
end;
procedure TEMFtoPDFExport.cmdLineTo(emfSP: TSmallPoint);
begin
PutLF(emfPoint2Str(emfSP) + ' l'); // Append straight line segment to path
end;
procedure TEMFtoPDFExport.cmdLineTo(X, Y: extended);
begin
PutLF(frxPoint2Str(X, Y) + ' l'); // Append straight line segment to path
end;
procedure TEMFtoPDFExport.cmdMoveTo(emfFP: TfrxPoint);
begin
PutLF(emfPoint2Str(emfFP) + ' m'); // Begin new subpath
end;
procedure TEMFtoPDFExport.cmdMoveTo(emfP: TPoint);
begin
PutLF(emfPoint2Str(emfP) + ' m'); // Begin new subpath
end;
procedure TEMFtoPDFExport.cmdMoveTo(emfSP: TSmallPoint);
begin
PutLF(emfPoint2Str(emfSP) + ' m'); // Begin new subpath
end;
procedure TEMFtoPDFExport.cmdMoveTo(X, Y: extended);
begin
PutLF(frxPoint2Str(X, Y, FPrecision) + ' m'); // Begin new subpath
end;
procedure TEMFtoPDFExport.cmdPathPainting(Options: integer);
begin
cmdPathParams(Options);
case Options and (ppEnd or ppClose or ppStroke or ppFontStroke or ppFill) of
ppEnd: PutLF('n');
ppStroke, ppFontStroke: PutLF('S');
ppStroke + ppClose: PutLF('s');
ppFill, ppFill + ppClose: PutLF('f' + EvenOdd);
ppFill + ppStroke: PutLF('B' + EvenOdd);
ppFill + ppStroke + ppClose: PutLF('b' + EvenOdd);
else
raise Exception.Create('Invalid Patch Painting');
end;
end;
procedure TEMFtoPDFExport.cmdPathParams(Options: integer);
begin
if IsInclude(Options, ppFill) then
if IsInclude(Options, ppBkFill) then
cmdSetFillColor(FDC.BkColor)
else if not IsInclude(Options, ppAsIsFill) then
cmdSetFillColor(FDC.BrushColor);
if IsInclude(Options, ppStroke) then
begin
cmdSetLineDashPattern(FDC.PenStyle, pdfSize(FDC.PenWidth));
cmdSetStrokeColor(FDC.PenColor);
cmdSetLineWidth(pdfSize(FDC.PenWidth));
cmdSetMiterLimit(FDC.MiterLimit);
cmdSetLineCap(FDC.PenEndCap);
cmdSetLineJoin(FDC.PenLineJoin);
end;
end;
procedure TEMFtoPDFExport.cmdPolyBezier(Options: integer = 0);
var
Point: integer;
begin
with PLast^ do
begin
if IsInclude(Options, ppWithTo) then
Point := 0
else
begin
cmdMoveTo(Polyline.aptl[0]);
Point := 1;
end;
while True do
case Integer(Polyline.cptl) - Point of
0, 1:
Break;
2, 4:
begin
cmdAppendCurvedSegment2final(Polyline.aptl[Point],
Polyline.aptl[Point + 1]);
Inc(Point, 2);
end;
else
cmdAppendCurvedSegment3(Polyline.aptl[Point],
Polyline.aptl[Point + 1], Polyline.aptl[Point + 2]);
Inc(Point, 3);
end;
end;
end;
procedure TEMFtoPDFExport.cmdPolyBezier16(Options: integer = 0);
var
Point: integer;
begin
with PLast^ do
begin
if IsInclude(Options, ppWithTo) then
Point := 0
else
begin
cmdMoveTo(Polyline16.apts[0]);
Point := 1;
end;
while True do
case Integer(Polyline16.cpts) - Point of
0, 1:
Break;
2, 4:
begin
cmdAppendCurvedSegment2final(Polyline16.apts[Point],
Polyline16.apts[Point + 1]);
Inc(Point, 2);
end;
else
cmdAppendCurvedSegment3(Polyline16.apts[Point],
Polyline16.apts[Point + 1], Polyline16.apts[Point + 2]);
Inc(Point, 3);
end;
end;
end;
procedure TEMFtoPDFExport.cmdPolyLine(Options: integer = 0);
var
Point: integer;
begin
with PLast^ do
begin
if IsInclude(Options, ppWithTo) then
cmdLineTo(Polyline.aptl[0])
else
cmdMoveTo(Polyline.aptl[0]);
for Point := 1 to Polyline.cptl - 1 do
cmdLineTo(Polyline.aptl[Point])
end;
if IsInclude(Options, ppClose) then
cmdCloseSubpath;
end;
procedure TEMFtoPDFExport.cmdPolyLine16(Options: integer = 0);
var
Point: integer;
begin
with PLast^ do
begin
if IsInclude(Options, ppWithTo) then
cmdLineTo(Polyline16.apts[0])
else
cmdMoveTo(Polyline16.apts[0]);
for Point := 1 to Polyline16.cpts - 1 do
cmdLineTo(Polyline16.apts[Point])
end;
if IsInclude(Options, ppClose) then
cmdCloseSubpath;
end;
procedure TEMFtoPDFExport.cmdPolyPolyLine(Options: integer = 0);
var
Poly, Point: integer;
begin
with FEMRList.Last as TEMRPolyPolygonObj do
begin
for Poly := 0 to P^.PolyPolygon.nPolys - 1 do
begin
if IsInclude(Options, ppWithTo) then
cmdLineTo(PolyPoint[Poly, 0])
else
cmdMoveTo(PolyPoint[Poly, 0]);
for Point := 1 to P^.PolyPolygon.aPolyCounts[Poly] - 1 do
cmdLineTo(PolyPoint[Poly, Point]);
end;
end;
if IsInclude(Options, ppClose) then
cmdCloseSubpath;
end;
procedure TEMFtoPDFExport.cmdPolyPolyLine16(Options: integer = 0);
var
Poly, Point: integer;
begin
with FEMRList.Last as TEMRPolyPolygon16Obj do
begin
for Poly := 0 to P^.PolyPolygon16.nPolys - 1 do
begin
if IsInclude(Options, ppWithTo) then
cmdLineTo(PolyPoint[Poly, 0])
else
cmdMoveTo(PolyPoint[Poly, 0]);
for Point := 1 to P^.PolyPolygon16.aPolyCounts[Poly] - 1 do
cmdLineTo(PolyPoint[Poly, Point]);
end;
end;
if IsInclude(Options, ppClose) then
cmdCloseSubpath;
end;
procedure TEMFtoPDFExport.cmdRestoreGraphicsState;
begin
PutLF('Q');
FqQBalance := FqQBalance - 1;
end;
procedure TEMFtoPDFExport.cmdSaveGraphicsState;
begin
PutLF('q');
FqQBalance := FqQBalance + 1;
end;
procedure TEMFtoPDFExport.cmdSetClippingPath;
begin
PutLF('W' + EvenOdd);
end;
procedure TEMFtoPDFExport.cmdSetFillColor(Color: TColor);
begin
PutLF(Color2Str(Color) + ' rg'); // Set RGB color for nonstroking operations
end;
procedure TEMFtoPDFExport.cmdSetLineCap(PenEndCap: Integer);
begin
if FForceButtLineCap then
PutLF('2 J')
else
case FDC.PenEndCap of
PS_ENDCAP_ROUND:
PutLF('1 J');
PS_ENDCAP_SQUARE:
PutLF('2 J');
else // PS_ENDCAP_FLAT
PutLF('0 J');
end;
end;
procedure TEMFtoPDFExport.cmdSetLineDashPattern(PenStyle: LongWord; Width: Extended);
begin
PutLF('[' + PenStyle2Str(PenStyle, Width) + '] 0 d');
end;
procedure TEMFtoPDFExport.cmdSetLineJoin(PenLineJoin: Integer);
begin
if FForceMitterLineJoin then
PutLF('0 j')
else
case FDC.PenLineJoin of
PS_JOIN_ROUND:
PutLF('1 j');
PS_JOIN_BEVEL:
PutLF('2 j');
else // PS_JOIN_MITER
PutLF('0 j');
end;
end;
procedure TEMFtoPDFExport.cmdSetLineWidth(PDFWidth: String);
begin
PutLF(PDFWidth + ' w');
end;
procedure TEMFtoPDFExport.cmdSetLineWidth(Width: Extended);
begin
cmdSetLineWidth(Float2Str(Width, 2));
end;
procedure TEMFtoPDFExport.cmdSetMiterLimit(MiterLimit: Extended);
begin
PutLF(Float2Str(MiterLimit) + ' M');
end;
procedure TEMFtoPDFExport.cmdSetStrokeColor(Color: TColor);
begin
PutLF(Color2Str(Color) + ' RG'); // Set RGB color for stroking operations
end;
procedure TEMFtoPDFExport.cmdTranslationAndScaling(Sx, Sy, Tx, Ty: Extended);
begin
PutLF(Float2Str(Sx) + ' 0 0 ' + Float2Str(Sy) + ' ' +
Float2Str(Tx, FPrecision) + ' ' + Float2Str(Ty, FPrecision) + ' cm');
end;
procedure TEMFtoPDFExport.cmd_AngleArc(Center, Radius: TfrxPoint; StartAngle, SweepAngle: Single);
const
MaxAnglePerCurve = 45;
var
n, i: Integer;
ActualArcAngle: Double;
Bezier: TBezierResult;
begin
n := Ceil(Abs(SweepAngle / MaxAnglePerCurve));
ActualArcAngle := SweepAngle / n;
for i := 0 to n - 1 do
begin
Bezier := BezierCurve(Center, Radius, StartAngle + i * ActualArcAngle, ActualArcAngle);
if i = 0 then
cmdMoveTo(Bezier.P0);
cmdAppendCurvedSegment3(Bezier.P1, Bezier.P2, Bezier.P3);
end;
end;
procedure TEMFtoPDFExport.cmd_RoundRect(l, t, r, b, rx, ry: Extended);
procedure Corner(x1, y1, x2, y2, x3, y3: Extended);
begin
PutLF(Float2Str(x1, FPrecision) + ' ' + Float2Str(y1, FPrecision) + ' ' +
Float2Str(x2, FPrecision) + ' ' + Float2Str(y2, FPrecision) + ' ' +
Float2Str(x3, FPrecision) + ' ' + Float2Str(y3, FPrecision) + ' c');
end;
begin
CmdMoveTo(l + rx, b);
CmdLineTo(r - rx, b); // bottom
Corner(r - rx / 2, b, r, b + ry / 2, r, b + ry); // right-bottom
CmdLineTo(r, t - ry); // right
Corner(r, t - ry / 2, r - rx / 2, t, r - rx, t); // right-top
CmdLineTo(l + rx, t); // top
Corner(l + rx / 2, t, l, t - ry / 2, l, t - ry); // left-top
CmdLineTo(l, b + ry); // left
Corner(l, b + ry / 2, l + rx / 2, b, l + rx, b); // left-bottom
end;
procedure TEMFtoPDFExport.Comment(CommentString: String);
begin
if CommentString <> '' then
PutCRLF('%--'+ CommentString)
else if ShowComments then
begin
CommentAboutRealization;
PutCRLF('%--' + Parsing);
end;
end;
procedure TEMFtoPDFExport.CommentAboutRealization;
var
CommandName, Value: String;
i: integer;
begin
CommandName := Copy(Parsing, 1, Pos(' ', Parsing + ' ') - 1);
if FRealizationList.IndexOf(CommandName) <> -1 then // OK
Exit;
i := FRealizationList.IndexOfName(CommandName);
Value := IfStr(i <> -1, FRealizationList.ValueFromIndex[i], '0');
PutCRLF('% Realization: ' + Value);
end;
procedure TEMFtoPDFExport.CommentTextRect(rtl: TRect; Color: TColor = clRed);
var
XFactor, YFactor: Extended;
P1, P2: TfrxPoint;
begin
XFactor := 1 / FDC.XFormScale.X;
YFactor := 1 / FDC.XFormScale.Y;
P1 := pdfFrxPoint(frxPoint(rtl.Left * XFactor, rtl.Top * YFactor));
P2 := pdfFrxPoint(frxPoint(rtl.Right * XFactor, rtl.Bottom * YFactor));
Comment('Comment ExtTextOutW.rclBounds >>>>>>');
Comment(IntToStr(rtl.Left) + ', ' + IntToStr(rtl.Right) +
' (' + IntToStr(rtl.Right - rtl.Left) + ') -=> ' +
frFloat2Str(P1.X, 2) + ', ' + frFloat2Str(P2.X, 2) +
' (' + frFloat2Str(P2.X - P1.X, 2) + ')');
cmdSaveGraphicsState;
PutLF('[] 0 d');
cmdSetLineWidth(0.25);
cmdMoveTo(P1.X, P1.Y);
cmdLineTo(P1.X, P2.Y);
cmdLineTo(P2.X, P1.Y);
cmdLineTo(P2.X, P2.Y);
cmdSetStrokeColor(Color);
PutLF('S');
cmdRestoreGraphicsState;
Comment('Comment ExtTextOutW.rclBounds <<<<<<');
end;
constructor TEMFtoPDFExport.Create(InStream, OutStream: TStream; APDFRect: TfrxRect; APOH: TPDFObjectsHelper);
begin
inherited Create(InStream, OutStream);
FPDFRect := APDFRect;
FPOH := APOH;
FLastClipRect := Rect(0, 0, 0, 0);
FForceMitterLineJoin := False;
FForceButtLineCap := False;
FForceNullBrush := False;
FTransparency := False;
FForceAnsi := False;
FClipped := True;
FPictureDPI := 0;
FPrecision := 2;
FRotation2D := TRotation2D.Create;
FRealizationList := TStringList.Create;
FRealizationList.NameValueSeparator := '=';
RealizationListFill([
'EMR_AbortPath=?',
'EMR_AngleArc',
'EMR_Arc',
'EMR_AlphaBlend',
'EMR_BeginPath',
'EMR_BitBlt',
'EMR_BrushOrgEx',
'EMR_CloseFigure',
'EMR_CreateBrushIndirect',
'EMR_CreateMonoBrush',
'EMR_CreatePen',
'EMR_DeleteObject',
'EMR_PolyDraw',
'EMR_PolyDraw16',
'EMR_Ellipse',
'EMR_EndPath',
'EMR_EoF',
'EMR_ExtCreateFontIndirectW',
'EMR_ExtCreatePen',
'EMR_ExtSelectClipRgn',
'EMR_ExtTextOutW',
'EMR_FillPath',
'EMR_FillRgn',
'EMR_GDIComment',
'EMR_Header',
'EMR_IntersectClipRect',
'EMR_LineTo',
'EMR_MaskBlt',
'EMR_ModifyWorldTransform',
'EMR_MoveToEx',
'EMR_PolyBezier',
'EMR_PolyBezier16',
'EMR_PolyBezierTo',
'EMR_PolyBezierTo16',
'EMR_Polygon',
'EMR_Polygon16',
'EMR_Polyline',
'EMR_Polyline16',
'EMR_PolylineTo',
'EMR_PolylineTo16',
'EMR_PolyPolygon',
'EMR_PolyPolygon16',
'EMR_PolyPolyline',
'EMR_PolyPolyline16',
'EMR_Rectangle',
'EMR_RestoreDC',
'EMR_RoundRect',
'EMR_SaveDC',
'EMR_SelectClipPath',
'EMR_SelectObject',
'EMR_SetArcDirection',
'EMR_SetBkColor',
'EMR_SetBkMode',
'EMR_SetICMMode',
'EMR_SetLayout',
'EMR_SetMetaRgn',
'EMR_SetMiterLimit',
'EMR_SetPolyFillMode',
'EMR_SetRop2',
'EMR_SetTextAlign',
'EMR_SetTextColor',
'EMR_SetStretchBltMode',
'EMR_SetWorldTransform',
'EMR_StretchDIBits',
'EMR_StretchBlt',
'EMR_StrokeAndFillPath',
'EMR_StrokePath',
'EMR_TransparentBlt'
]);
end;
procedure TEMFtoPDFExport.DCCreate;
begin
FDC := TPDFDeviceContext.Create;
end;
destructor TEMFtoPDFExport.Destroy;
begin
FRotation2D.Free;
FRealizationList.Free;
inherited;
end;
procedure TEMFtoPDFExport.DoEMR_AlphaBlend;
begin
inherited;
with PLast^.AlphaBlend do
cmdBitMap(Bounds(xDest, yDest, cxDest, cyDest), dwRop,
FEMRList.Last as TEMRAlphaBlendObj);
end;
procedure TEMFtoPDFExport.DoEMR_AngleArc;
begin
inherited;
DrawFigureStart;
with PLast^ do
cmdAppendAngleArcToPath(AngleArc);
DrawFigureFinish(ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_Arc;
begin
inherited;
DrawFigureStart;
with PLast^ do
cmdAppendArcToPath(Arc);
DrawFigureFinish(ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_BitBlt;
begin
inherited;
with PLast^.BitBlt do
cmdBitMap(Bounds(xDest, yDest, cxDest, cyDest), dwRop, FEMRList.Last as TEMRBitBltObj);
end;
procedure TEMFtoPDFExport.DoEMR_CloseFigure;
begin
inherited;
cmdCloseSubpath;
end;
procedure TEMFtoPDFExport.DoEMR_Ellipse;
begin
inherited;
DrawFigureStart;
with PLast^ do
cmdAppendEllipsToPath(Ellipse.rclBox);
DrawFigureFinish(ppFill + ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_EoF;
begin
inherited;
while FqQBalance > 0 do
cmdRestoreGraphicsState;
end;
procedure TEMFtoPDFExport.DoEMR_ExtSelectClipRgn;
begin
inherited DoEMR_ExtSelectClipRgn;
case PLast^.ExtSelectClipRgn.iMode of
RGN_AND:
cmdCreateExtSelectClipRgn;
RGN_OR,
RGN_XOR,
RGN_DIFF:
Comment('Ignored ExtSelectClipRgn.iMode'); // Implement when data with this iMode is found
RGN_COPY:
begin
cmdRestoreGraphicsState;
cmdSaveGraphicsState;
end;
end;
end;
procedure TEMFtoPDFExport.DoEMR_ExtTextOutW;
function IsCompatible: Boolean;
begin
Result := PLast^.ExtTextOutW.iGraphicsMode = GM_COMPATIBLE;
end;
function IsAdvanced: Boolean;
begin
Result := PLast^.ExtTextOutW.iGraphicsMode = GM_ADVANCED;
end;
procedure DrawRotation(var pdfTextPosition: TfrxPoint);
var
LineOrientation, SumOrientation: LongInt; // specifies the angle, in tenths of degrees
SumRadian: Single;
begin
if IsAdvanced then
LineOrientation := FDC.LineOrientation
else // GM_COMPATIBLE
LineOrientation := 0;
SumOrientation := FDC.FontOrientation + LineOrientation;
if SumOrientation <> 0 then
begin
SumRadian := SumOrientation / 10.0 * Pi / 180.0;
FRotation2D.Init(SumRadian, frxPoint(0.0, 0.0), FPrecision);
PutLF(FRotation2D.Matrix + ' cm');
pdfTextPosition := FRotation2D.Turn(pdfTextPosition);
end;
end;
var
EMRExtTextOutWObj: TEMRExtTextOutWObj;
Font: TEMFFont;
RS: TRemapedString;
function pdfDX: TDoubleArray;
var
i: Integer;
OutputDx: TLongWordDinArray;
SC: TEMFPDFSizeConverter;
begin
SC := TEMFPDFSizeConverter.Create(Self);
try
OutputDx := EMRExtTextOutWObj.OutputDx;
SetLength(Result, Length(OutputDx));
for i := 0 to High(OutputDx) do
begin
SC.Log := LongInt(OutputDx[i]);
Result[i] := SC.Char;
end;
finally
SC.Free;
end;
end;
var
FontIndex: Integer;
MovedTextPosition, ShiftSign, pdfTextPosition: TfrxPoint;
Simulation: String;
SimulateBold: Boolean;
AlCorr: TfrxPoint; // Alignment correction
IsRTL, IsRTLOptions, IsGlyphOut: Boolean;
LogRect: TRect;
XFactor, YFactor: Extended;
R: TfrxRect;
RoundFontSize: Integer;
AverageDx: Boolean;
const
YCorr: array[TfrxVAlign] of Extended = (0.92, -0.23, 0.0);
XCorr: array[TfrxHalign] of Extended = (0.0, -1.0, -0.5, 0.0);
procedure DoOutputString(const AOutputString: WideString);
var
pdfFont: TfrxPDFFont;
begin
if AOutputString = '' then Exit;
Font := FontCreate;
try
with PLast^.ExtTextOutW do
if IsInclude(emrtext.fOptions, ETO_CLIPPED) then
begin
cmdAppendRectangleToPath(emrtext.rcl, IsCompatible);
cmdSetClippingPath;
cmdPathPainting(ppEnd);
end;
AlCorr.X := Sin(FDC.FontRadian) * YCorr[FDC.VAlign] * FDC.FontSize
+ Cos(FDC.FontRadian) * XCorr[FDC.HAlign] * EMRExtTextOutWObj.TextLength;
AlCorr.Y := Cos(FDC.FontRadian) * YCorr[FDC.VAlign] * FDC.FontSize +
- Sin(FDC.FontRadian) * XCorr[FDC.HAlign] * EMRExtTextOutWObj.TextLength;
ShiftSign := frxPoint(1.0, 1.0);
if IsCompatible then // Need testing for GM_ADVANCED
ShiftSign := frxPoint(Sign(FDC.XForm.eM11), Sign(FDC.XForm.eM22));
with PLast^.ExtTextOutW.emrtext.ptlReference do
MovedTextPosition := frxPoint(
X + ShiftSign.X * (AlCorr.X),
Y + ShiftSign.Y * (AlCorr.Y - FDC.FontSize * (1.0 - Font.DownSizeFactor)));
pdfTextPosition := pdfFrxPoint(MovedTextPosition);
{ cut all bottom outbound text }
if FClipped and (LogRect.Bottom = LogRect.Top) and (FLastClipRect.Bottom <> 0) and
(FLastClipRect.Bottom < MovedTextPosition.Y) then
Exit;
{ TODO : Needs rework }
if (FDC.FontFamily = 'Cambria Math') then
begin
R := pdfFrxRect(LogRect);
pdfTextPosition.Y := pdfTextPosition.Y
- (R.Top - R.Bottom - Font.Size) / 2.25;
end
else if (FDC.FontFamily = 'Segoe UI Symbol') then
begin
R := pdfFrxRect(LogRect);
pdfTextPosition.Y := pdfTextPosition.Y
- (R.Top - R.Bottom - Font.Size) / 2.5;
end;
DrawRotation({var} pdfTextPosition);
if (FDC.FontOrientation > -1800) and (FDC.FontOrientation <= -1) and
(FDC.XForm.eM22 < 0) and IsCompatible then
PutLF('-1 0 0 -1 ' +
Float2Str(2 * pdfTextPosition.X, FPrecision) + ' ' +
Float2Str(2 * (pdfTextPosition.Y + Font.PreciseSize * (1 - tpPt)), FPrecision) +
' cm');
if FPOH.IsBBox then
begin
PutLF('/Tx BMC');
cmdSaveGraphicsState;
end;
PutLF('BT'); // Begin text object
FontIndex := FPOH.GetObjFontNumber(Font);
if IsAdvanced then
PutLF(FPOH.Fonts[FontIndex].Name +
AnsiString(' ' + frFloat2Str(Font.PreciseSize * (1 - tpPt)) + ' Tf'))
else
PutLF(FPOH.Fonts[FontIndex].Name +
AnsiString(' ' + frFloat2Str(EMFPDFFontSize(Font), 3) + ' Tf'));
PutLF('[] 0 d');
cmdSetFillColor(Font.Color);
PutLF(frxPoint2Str(pdfTextPosition, FPrecision) + ' Td'); // Move text position
pdfFont := FPOH.Fonts[FontIndex];
pdfFont.ForceAnsi := FForceAnsi;
try
pdfFont.SameCharacterWidth := IsSameCharacterWidth(FDC.FontFamily);
try
RS := pdfFont.SoftRemapString(AOutputString, IsRTL, IsGlyphOut);
finally
pdfFont.SameCharacterWidth := False;
end;
RoundFontSize := Round(pdfSize(FDC.FontSize));
AverageDx := RoundFontSize <= 20;
finally
pdfFont.ForceAnsi := False;
end;
if IsNeedsItalicSimulation(Font, Simulation) then
PutLF(Simulation + ' ' + frxPoint2Str(pdfTextPosition) + ' Tm');
SimulateBold := IsNeedsBoldSimulation(Font, Simulation);
if SimulateBold then
PutLF(Simulation);
if IsRTLOptions or not RS.IsValidCharWidth or RS.IsHasLigatures then
PutLF('<' + StrToHex(RS.Data) + '> Tj') // Show text
else
PutLF('[<' + StrToHexDx(RS, pdfDX, AverageDx) + '>] TJ');
PutLF('ET'); // End text object
if FPOH.IsBBox then
begin
PutLF('EMC');
cmdRestoreGraphicsState;
end;
if SimulateBold then
PutLF('0 Tr');
if FDC.FontUnderline or FDC.FontStrikeOut then
if PLast^.ExtTextOutW.iGraphicsMode = GM_ADVANCED then
DrawFontLines(Font.PreciseSize, pdfTextPosition, pdfSize(EMRExtTextOutWObj.TextLength))
else
DrawFontLines(Font.Size, pdfTextPosition, pdfSize(EMRExtTextOutWObj.TextLength));
finally
Font.Free;
end;
end;
begin
inherited DoEMR_ExtTextOutW;
cmdSaveGraphicsState;
with PLast^.ExtTextOutW do
if IsInclude(emrtext.fOptions, ETO_OPAQUE) then
begin
cmdAppendRectangleToPath(emrtext.rcl, IsCompatible);
cmdPathPainting(ppFill + ppBkFill); // Use BkColor
end;
XFactor := 1 / FDC.XFormScale.X;
YFactor := 1 / FDC.XFormScale.Y;
with PLast^.ExtTextOutW.rclBounds do
LogRect := Rect(Floor((Left - FDC.DeviceTopLeft.X) * XFactor),
Floor((Top - FDC.DeviceTopLeft.Y) * YFactor),
Ceil((Right - FDC.DeviceTopLeft.X)* XFactor),
Ceil((Bottom - FDC.DeviceTopLeft.Y) * YFactor));
if FDC.BkMode = OPAQUE then
begin
cmdAppendRectangleToPath(LogRect);
cmdPathPainting(ppFill);
end;
IsRTLOptions := IsInclude(PLast^.ExtTextOutW.emrtext.fOptions, ETO_RTLREADING)
or IsInclude(FDC.TextAlignmentMode, TA_RTLREADING)
or IsInclude(FDC.Layout, EMR_LAYOUT_RTL);
IsRTL := IsRTLOptions and
(FDC.FontCharSet in [ARABIC_CHARSET, HEBREW_CHARSET]);
IsGlyphOut := not IsRTL and
IsInclude(PLast^.ExtTextOutW.emrtext.fOptions, ETO_GLYPH_INDEX);
{ disable back conversion in OutputString }
if IsGlyphOut then
PLast^.ExtTextOutW.emrtext.fOptions := PLast^.ExtTextOutW.emrtext.fOptions and not ETO_GLYPH_INDEX;
EMRExtTextOutWObj := FEMRList.Last as TEMRExtTextOutWObj;
DoOutputString(EMRExtTextOutWObj.OutputString(FDC.FontFamily, IsRTL));
cmdRestoreGraphicsState;
end;
procedure TEMFtoPDFExport.DoEMR_FillPath;
begin
inherited DoEMR_FillPath;
cmdPathPainting(ppFill);
end;
procedure TEMFtoPDFExport.DoEMR_FillRgn;
var
PRD: PRgnData;
RectCount, i: Integer;
R: TRect;
begin
inherited DoEMR_FillRgn;
cmdSaveGraphicsState;
PRD := @PLast^.FillRgn.RgnData;
RectCount := PRD^.rdh.nCount;
cmdSetFillColor(BrushAverageColor(PLast^.FillRgn.ihBrush));
for i := 0 to RectCount - 1 do
begin
Move(PRD^.Buffer[i * SizeOf(TRect)], R, SizeOf(TRect));
cmdAppendRectangleToPath(R);
end;
cmdPathPainting(ppFill + ppAsIsFill);
cmdRestoreGraphicsState;
end;
procedure TEMFtoPDFExport.DoEMR_Header;
var
rWidth, rHeight: double;
begin
inherited DoEMR_Header;
with PLast^.Header do
begin
rWidth := szlDevice.cx / szlMillimeters.cx * (rclFrame.Right - rclFrame.Left) / 100;
rHeight := szlDevice.cy / szlMillimeters.cy * (rclFrame.Bottom - rclFrame.Top) / 100;
end;
FEMFtoPDFFactor := frxPoint(
(FPDFRect.Right - FPDFRect.Left) / rWidth,
(FPDFRect.Top - FPDFRect.Bottom) / rHeight);
FqQBalance := 0;
end;
procedure TEMFtoPDFExport.DoEMR_IntersectClipRect;
begin
inherited DoEMR_IntersectClipRect;
with PLast^.IntersectClipRect.rclClip do
if (Right = Left) or (Bottom = Top) then
Exit;
FLastClipRect := PLast^.IntersectClipRect.rclClip;
cmdAppendRectangleToPath(PLast^.IntersectClipRect.rclClip);
cmdSetClippingPath;
cmdPathPainting(ppEnd);
end;
procedure TEMFtoPDFExport.DoEMR_LineTo;
begin
inherited;
// Specifies a line from the current position up to the specified point.
DrawFigureStart;
cmdLineTo(FDC.PositionNext);
DrawFigureFinish(ppStroke + ppWithTo);
end;
procedure TEMFtoPDFExport.DoEMR_MaskBlt;
begin
inherited;
with PLast^.MaskBlt do
cmdBitMap(Bounds(xDest, yDest, cxDest, cyDest), dwRop, FEMRList.Last as TEMRBitBltObj);
end;
procedure TEMFtoPDFExport.DoEMR_MoveToEx;
begin
inherited;
cmdMoveTo(FDC.PositionNext);
end;
procedure TEMFtoPDFExport.DoEMR_Pie;
begin
inherited;
DrawFigureStart;
with PLast^ do
cmdAppendPieToPath(Pie);
DrawFigureFinish(ppClose + ppFill + ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_PolyBezier;
begin
inherited;
DrawFigureStart;
cmdPolyBezier;
DrawFigureFinish(ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_PolyBezier16;
begin
inherited;
DrawFigureStart;
cmdPolyBezier16;
DrawFigureFinish(ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_PolyBezierTo;
begin
inherited;
DrawFigureStart;
cmdPolyBezier(ppWithTo);
DrawFigureFinish(ppStroke + ppWithTo);
end;
procedure TEMFtoPDFExport.DoEMR_PolyBezierTo16;
begin
inherited;
// Specifies one or more Bezier curves based on the current position.
DrawFigureStart;
cmdPolyBezier16(ppWithTo);
DrawFigureFinish(ppStroke + ppWithTo);
end;
procedure TEMFtoPDFExport.DoEMR_PolyDraw;
var
Point, T, Count: integer;
begin
inherited;
DrawFigureStart;
Point := 0;
Count := PLast^.PolyDraw.cptl;
with FEMRList.Last as TEMRPolyDrawObj do
while Point <= Count - 1 do
begin
T := Types[Point];
if IsInclude(T, PT_MOVETO) {PT_MOVETO - MUST be first test} then
cmdMoveTo(P.PolyDraw.aptl[Point])
else if IsInclude(T, PT_LINETO) then
cmdLineTo(P.PolyDraw.aptl[Point])
else if IsInclude(T, PT_BEZIERTO) then
if Point + 2 <= Count - 1 then
begin
cmdAppendCurvedSegment3(P.PolyDraw.aptl[Point],
P.PolyDraw.aptl[Point + 1],
P.PolyDraw.aptl[Point + 2]);
Point := Point + 2;
end
else if Point + 1 <= Count - 1 then
begin
cmdAppendCurvedSegment2final(P.PolyDraw.aptl[Point],
P.PolyDraw.aptl[Point + 1]);
Point := Point + 1;
end;
if IsInclude(T, PT_CLOSEFIGURE) then
cmdCloseSubpath;
Point := Point + 1;
end;
DrawFigureFinish(ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_PolyDraw16;
var
Point, T, Count: integer;
begin
inherited;
DrawFigureStart;
Point := 0;
Count := PLast^.PolyDraw16.cpts;
with FEMRList.Last as TEMRPolyDraw16Obj do
while Point <= Count - 1 do
begin
T := Types[Point];
if IsInclude(T, PT_MOVETO) {PT_MOVETO - MUST be first test} then
cmdMoveTo(P.PolyDraw16.apts[Point])
else if IsInclude(T, PT_LINETO) then
cmdLineTo(P.PolyDraw16.apts[Point])
else if IsInclude(T, PT_BEZIERTO) then
if Point + 2 <= Count - 1 then
begin
cmdAppendCurvedSegment3(P.PolyDraw16.apts[Point],
P.PolyDraw16.apts[Point + 1],
P.PolyDraw16.apts[Point + 2]);
Point := Point + 2;
end
else if Point + 1 <= Count - 1 then
begin
cmdAppendCurvedSegment2final(P.PolyDraw16.apts[Point],
P.PolyDraw16.apts[Point + 1]);
Point := Point + 1;
end;
if IsInclude(T, PT_CLOSEFIGURE) then
cmdCloseSubpath;
Point := Point + 1;
end;
DrawFigureFinish(ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_Polygon;
begin
inherited;
if PLast^.Polyline.cptl > 1 then
begin
DrawFigureStart;
cmdPolyLine(ppClose);
DrawFigureFinish(ppFill + ppStroke);
end;
end;
procedure TEMFtoPDFExport.DoEMR_Polygon16;
begin
inherited;
// The polygon SHOULD be outlined using the current pen and filled using
// the current brush and polygon fill mode. The polygon SHOULD be closed
// automatically by drawing a line from the last vertex to the first.
if PLast^.Polyline16.cpts > 1 then
begin
DrawFigureStart;
cmdPolyLine16(ppClose);
DrawFigureFinish(ppFill + ppStroke);
end;
end;
procedure TEMFtoPDFExport.DoEMR_Polyline;
begin
inherited;
if PLast^.Polyline.cptl > 1 then
begin
DrawFigureStart;
cmdPolyLine;
DrawFigureFinish(ppStroke);
end;
end;
procedure TEMFtoPDFExport.DoEMR_Polyline16;
begin
inherited;
if PLast^.Polyline16.cpts > 1 then
begin
DrawFigureStart;
cmdPolyLine16;
DrawFigureFinish(ppStroke);
end;
end;
procedure TEMFtoPDFExport.DoEMR_PolylineTo;
begin
inherited;
if PLast^.Polyline.cptl > 1 then
begin
DrawFigureStart;
cmdPolyLine(ppWithTo);
DrawFigureFinish(ppStroke + ppWithTo);
end;
end;
procedure TEMFtoPDFExport.DoEMR_PolylineTo16;
begin
inherited;
if PLast^.Polyline16.cpts > 1 then
begin
DrawFigureStart;
cmdPolyLine16(ppWithTo);
DrawFigureFinish(ppStroke + ppWithTo);
end;
end;
procedure TEMFtoPDFExport.DoEMR_PolyPolygon;
begin
inherited;
if PLast^.PolyPolyline.nPolys > 0 then
begin
DrawFigureStart;
cmdPolyPolyLine(ppClose);
DrawFigureFinish(ppFill + ppStroke);
end;
end;
procedure TEMFtoPDFExport.DoEMR_PolyPolygon16;
begin
inherited;
if PLast^.PolyPolyline16.nPolys > 0 then
begin
DrawFigureStart;
cmdPolyPolyLine16(ppClose);
DrawFigureFinish(ppFill + ppStroke);
end;
end;
procedure TEMFtoPDFExport.DoEMR_PolyPolyline;
begin
inherited;
if PLast^.PolyPolyline.nPolys > 0 then
begin
DrawFigureStart;
cmdPolyPolyLine(ppEnd);
DrawFigureFinish(ppStroke);
end;
end;
procedure TEMFtoPDFExport.DoEMR_PolyPolyline16;
begin
inherited;
if PLast^.PolyPolyline16.nPolys > 0 then
begin
DrawFigureStart;
cmdPolyPolyLine16(ppEnd);
DrawFigureFinish(ppStroke);
end;
end;
procedure TEMFtoPDFExport.DoEMR_Rectangle;
begin
inherited DoEMR_Rectangle;
DrawFigureStart;
cmdAppendRectangleToPath(PLast^.Rectangle.rclBox);
DrawFigureFinish(ppFill + ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_RestoreDC;
var
i: integer;
begin
inherited DoEMR_RestoreDC;
for i := PLast^.RestoreDC.iRelative to -1 do
cmdRestoreGraphicsState;
end;
procedure TEMFtoPDFExport.DoEMR_RoundRect;
begin
inherited;
DrawFigureStart;
with PLast^ do
cmdAppendRoundRectToPath(RoundRect.rclBox, RoundRect.szlCorner);
DrawFigureFinish(ppFill + ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_SaveDC;
begin
inherited DoEMR_SaveDC;
cmdSaveGraphicsState;
end;
procedure TEMFtoPDFExport.DoEMR_SelectClipPath;
begin
inherited;
cmdSetClippingPath;
cmdPathPainting(ppEnd);
end;
procedure TEMFtoPDFExport.DoEMR_StretchBlt;
begin
inherited;
with PLast^.StretchBlt do
cmdBitMap(Bounds(xDest, yDest, cxDest, cyDest), dwRop, FEMRList.Last as TEMRStretchBltObj);
end;
procedure TEMFtoPDFExport.DoEMR_StretchDIBits;
begin
inherited;
with PLast^.StretchDIBits do
cmdBitMap(Bounds(xDest, yDest, cxDest, cyDest), dwRop, FEMRList.Last as TEMRStretchDIBitsObj);
end;
procedure TEMFtoPDFExport.DoEMR_StrokeAndFillPath;
begin
inherited;
cmdPathPainting(ppFill + ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_StrokePath;
begin
inherited;
cmdPathPainting(ppStroke);
end;
procedure TEMFtoPDFExport.DoEMR_TransparentBlt;
begin
inherited;
with PLast^.TransparentBlt do
cmdBitMap(Bounds(xDest, yDest, cxDest, cyDest), dwRop, FEMRList.Last as TEMRTransparentBltObj);
end;
procedure TEMFtoPDFExport.DoFinish;
begin
inherited;
cmdRestoreGraphicsState;
end;
procedure TEMFtoPDFExport.DoStart;
begin // Before EMR_Header
inherited;
cmdSaveGraphicsState;
if FClipped then
begin
PutLF(frxRect2Str(FPDFRect, FPrecision) + ' re');
cmdSetClippingPath;
cmdPathPainting(ppEnd);
end;
end;
procedure TEMFtoPDFExport.DrawFigureFinish(Options: integer);
begin
if not FDC.IsPathBracketOpened then
begin
cmdPathPainting(FillStrokeOptions(Options));
cmdRestoreGraphicsState;
if IsInclude(Options, ppWithTo) then
cmdMoveTo(FDC.PositionNext);
end;
end;
procedure TEMFtoPDFExport.DrawFigureStart;
begin
if not FDC.IsPathBracketOpened then
cmdSaveGraphicsState;
end;
procedure TEMFtoPDFExport.DrawFontLines(FontSize: Double; TextPosition: TfrxPoint; TextWidth: Extended);
procedure DrawLine(Shift, Width: Extended);
var
Y: Extended;
begin
Y := TextPosition.Y + FontSize * Shift;
cmdMoveTo(TextPosition.X, Y);
cmdLineTo(TextPosition.X + TextWidth, Y);
cmdSetLineWidth(Width);
cmdPathPainting(ppFontStroke);
end;
begin
cmdSetLineDashPattern(PS_SOLID, 0);
cmdSetStrokeColor(FDC.TextColor);
if FDC.FontUnderline then
DrawLine(UnderlineShift, FontSize * UnderlineWidth);
if FDC.FontStrikeOut then
DrawLine(StrikeOutShift, FontSize * StrikeOutWidth);
end;
function TEMFtoPDFExport.emfPoint2Str(emfSP: TSmallPoint): String;
begin
Result := frxPoint2Str(pdfFrxPoint(emfSP), FPrecision);
end;
function TEMFtoPDFExport.emfPoint2Str(emfP: TPoint): String;
begin
Result := frxPoint2Str(pdfFrxPoint(emfP), FPrecision);
end;
function TEMFtoPDFExport.emfPoint2Str(emfFP: TfrxPoint): String;
begin
Result := frxPoint2Str(pdfFrxPoint(emfFP), FPrecision);
end;
function TEMFtoPDFExport.emfRect2Str(emfR: TRect): String;
begin
Result := frxRect2Str(pdfFrxRect(emfR), FPrecision);
end;
//function TEMFtoPDFExport.emfSize2Str(emfSize: Extended): String;
//begin
// Result := Float2Str(pdfSize(emfSize));
//end;
function TEMFtoPDFExport.EvenOdd: String;
begin
Result := IfStr(FDC.PolyFillMode = ALTERNATE, '*');
end;
function TEMFtoPDFExport.FillStrokeOptions(Options: integer): integer;
begin
Result := IfInt(IsInclude(Options, ppStroke) and not IsNullPen, ppStroke) +
IfInt(IsInclude(Options, ppFill) and not IsNullBrush, ppFill);
end;
function TEMFtoPDFExport.FontCreate: TEMFFont;
var
FontIndex: Integer;
begin
Result := inherited FontCreate;
if FDC.IsFontHeight then
begin
FontIndex := FPOH.GetObjFontNumber(Result);
Result.DownSizeFactor := FPOH.Fonts[FontIndex].FontHeightToPointSizeFactor;
end
else
Result.DownSizeFactor := 1.0;
Result.PreciseSize := pdfSize(FDC.FontSize * Result.DownSizeFactor);
Result.Size := Round(Result.PreciseSize);
end;
function TEMFtoPDFExport.IsNullBrush: Boolean;
begin
Result := FForceNullBrush or
(FDC.BrushStyle in [BS_NULL, BS_PATTERN8X8, BS_DIBPATTERN8X8, BS_MONOPATTERN]);
end;
function TEMFtoPDFExport.IsNullPen: Boolean;
begin
Result := FDC.PenStyle in [PS_NULL];
end;
function TEMFtoPDFExport.IsSameCharacterWidth(FontName: string): Boolean;
begin
Result := (Pos('Arial', FontName) = 1)
or (Pos('Calibri', FontName) = 1)
or (Pos('Cambria', FontName) = 1)
// or (Pos('Garamond', FontName) = 1)
// or (Pos('Georgia', FontName) = 1)
or (Pos('Gotham', FontName) = 1)
or (Pos('Meiryo', FontName) = 1)
// or (Pos('Tahoma', FontName) = 1)
or (Pos('Times New Roman', FontName) = 1)
// or (Pos('Trebuchet MS', FontName) = 1)
// or (Pos('Verdana', FontName) = 1)
;
end;
function TEMFtoPDFExport.NormalizeRect(const Rect: TRect): TRect;
begin
Result := Rect;
if Result.Left > Result.Right then
begin
Result.Left := Rect.Right;
Result.Right := Rect.Left;
end;
if Result.Top > Result.Bottom then
begin
Result.Top := Rect.Bottom;
Result.Bottom := Rect.Top;
end;
end;
function TEMFtoPDFExport.PDFDeviceContext: TPDFDeviceContext;
begin
Result := FDC as TPDFDeviceContext;
end;
function TEMFtoPDFExport.pdfFrxPoint(emfP: TPoint): TfrxPoint;
begin
Result := LogToDevPoint(emfP);
Result.X := FPDFRect.Left + Result.X * FEMFtoPDFFactor.X;
Result.Y := FPDFRect.Top - Result.Y * FEMFtoPDFFactor.Y;
end;
function TEMFtoPDFExport.pdfFrxPoint(emfSP: TSmallPoint): TfrxPoint;
begin
Result := LogToDevPoint(emfSP);
Result.X := FPDFRect.Left + Result.X * FEMFtoPDFFactor.X;
Result.Y := FPDFRect.Top - Result.Y * FEMFtoPDFFactor.Y;
end;
function TEMFtoPDFExport.pdfFrxPoint(emfDP: TfrxPoint): TfrxPoint;
begin
Result := LogToDevPoint(emfDP);
Result.X := FPDFRect.Left + Result.X * FEMFtoPDFFactor.X;
Result.Y := FPDFRect.Top - Result.Y * FEMFtoPDFFactor.Y;
end;
function TEMFtoPDFExport.pdfFrxRect(emfR: TRect): TfrxRect;
var
TopLeft, BottomRight: TfrxPoint;
begin
TopLeft := pdfFrxPoint(emfR.TopLeft);
BottomRight := pdfFrxPoint(emfR.BottomRight);
Result.Left := Min(TopLeft.X, BottomRight.X);
Result.Right := Max(TopLeft.X, BottomRight.X);
Result.Top := Max(TopLeft.Y, BottomRight.Y); // Max !
Result.Bottom := Min(TopLeft.Y, BottomRight.Y); // Min !
end;
function TEMFtoPDFExport.pdfSize(emfSize: Extended): Extended;
begin
Result := LogToDevSize(emfSize) * (FEMFtoPDFFactor.X + FEMFtoPDFFactor.Y) / 2;
end;
procedure TEMFtoPDFExport.Put(const S: AnsiString);
begin
FOutStream.Write(S[1], Length(S));
end;
procedure TEMFtoPDFExport.PutCRLF(const S: AnsiString);
begin
Put(S + AnsiString(#13#10));
end;
procedure TEMFtoPDFExport.PutLF(const S: AnsiString);
begin
Put(S + AnsiString(#10));
end;
{$IFDEF Delphi12}
procedure TEMFtoPDFExport.PutCRLF(const S: String);
begin
PutCRLF(AnsiString(S));
end;
procedure TEMFtoPDFExport.PutLF(const S: String);
begin
PutLF(AnsiString(S));
end;
{$ENDIF}
procedure TEMFtoPDFExport.RealizationListFill(RealizedCommands: array of String);
var
i: integer;
begin
for i := Low(RealizedCommands) to High(RealizedCommands) do
FRealizationList.Add(RealizedCommands[i]);
end;
{ TEMFPDFSizeConverter }
constructor TEMFPDFSizeConverter.Create(AExport: TEMFtoPDFExport);
begin
FExport := AExport;
FEMFPDFFactor := FExport.FEMFtoPDFFactor.X;
FFont := FExport.FontCreate;
end;
destructor TEMFPDFSizeConverter.Destroy;
begin
FFont.Free;
inherited;
end;
function TEMFPDFSizeConverter.LogToDev(Value: Double): Double;
begin
Log := Value;
Result := Dev;
end;
procedure TEMFPDFSizeConverter.SetChar(const Value: Double);
begin
FChar := Value;
FDev := FChar * FFont.Size / 1000 / FEMFPDFFactor;
FPDF := FDev * FEMFPDFFactor;
FLog := FDev * FEXport.FDC.XFormAverageScale;
end;
procedure TEMFPDFSizeConverter.SetDev(const Value: Double);
begin
FDev := Value;
FPDF := FDev * FEMFPDFFactor;
FChar := FDev * 1000 / Max(1, FFont.Size) * FEMFPDFFactor;
FLog := FDev * FEXport.FDC.XFormAverageScale;
end;
procedure TEMFPDFSizeConverter.SetLog(const Value: Double);
begin
FLog := Value;
FDev := FExport.LogToDevSizeX(FLog);
FPDF := FDev * FEMFPDFFactor;
FChar := FDev * 1000 / Max(1, FFont.Size) * FEMFPDFFactor;
end;
procedure TEMFPDFSizeConverter.SetPDF(const Value: Double);
begin
FPDF := Value;
FDev := FPDF / FEMFPDFFactor;
FChar := FDev * 1000 / Max(1, FFont.Size) * FEMFPDFFactor;
FLog := FDev * FEXport.FDC.XFormAverageScale;
end;
end.