In theory, text (or other) elements can be drawn directly into an EMF, rotated, and then the rotated EMF drawn into the VPE doc. Alternatively, the required elements could be written into a VPE doc directly, exported out to an EMF to be rotated, then the resulting rotated EMF drawn into the VPE doc. This example adopts the second method which potentially allows you to export and rotate any elements you can generate with VPE, exactly as they are rendered by VPE. In practice, a second VPE doc could be used to generate the source for rotation, or you could temporarily InsertPage for this purpose and then RemovePage once the EMF had been exported. Below, I have simply retained the "source text" on the same page as the rotated EMF is drawn to better demonstrate the results.
The sample draws the text "XXOXX" in a box, exports that to an EMF, rotates it to a new EMF file (at 45 degrees), then outputs that to the doc.
The main problem I have encountered is that text in the rotated EMF file is displaced in the vertical font axis: rather than being vertically centred within the box, the text is a lot closer to the bottom of the box. This makes tracking font metrics and accurately positioning the rotated text difficult (whereas, if the text was positioned correctly, you can relatively easily justify and position the rotated text along its line of rotation).
From my experimenting, using typical Windows OpenType fonts seem to produce the displacement, whereas using a TrueType font is very close to accurate. Too bad standard Windows fonts are all OpenType!
Maybe there is a way to get better accuracy with OpenType fonts?? I have not been able to resolve this as yet - I think it will take someone with a much stronger grasp of Windows graphics than me.
Maybe there is a different approach altogether for rotating text with VPE?
- Code: Select all
procedure TForm3.Button2Click(Sender: TObject);
var
wText, hText: Double;
ALeft, ATop, ARight, ABottom: Double;
SrcMetafile, TrgMetafile: TMetafile;
SrcVPEStream, TrgVPEStream: TVPEStream;
SrcMemStream, TrgMemStream: TMemoryStream;
BytesToRead, ACount: Integer;
ABuffer: array[0..VPE_BUFFER_SIZE] of Byte;
MetafileCanvas: TMetafileCanvas;
TransformMatrix: XFORM;
AngleDegrees, AngleRadians, AngleSin, AngleCos: Double;
wSrcMF, hSrcMF, wTrgMF, hTrgMF: Integer;
wTrgText, hTrgText: Double;
xDelta, yDelta: Integer;
begin
//set an angle of rotation
AngleDegrees := 45;
with VPEngine1 do
begin
OpenDoc;
//License('', '');
//print some boxed text and mark output rect
SetFont('Arial', 48);
ALeft := 2;
ATop := 2;
WriteBox(ALeft, ATop, VFREE, VFREE, 'XXOXX');
ARight := (ALeft + nWidth);
ABottom := (ATop + nHeight);
SrcMetafile := TMetafile.Create();
try
//create source emf MemoryStream via VPEMemoryStream
SrcVPEStream := CreateMemoryStream(VPE_BUFFER_SIZE);
try
//save output to VPE stream
PictureType := PIC_TYPE_EMF;
PictureExportStream(SrcVPEStream, CurrentPage,
ALeft,
ATop,
ARight,
ABottom);
//stream VPEStream to a MemoryStream
SrcMemStream := TMemoryStream.Create;
try
BytesToRead := SrcVPEStream.Size;
SrcVPEStream.Seek(0);
while (BytesToRead > 0) do
begin
if (BytesToRead > VPE_BUFFER_SIZE) then
ACount := VPE_BUFFER_SIZE
else
ACount := BytesToRead;
SrcVPEStream.Read(ABuffer, ACount);
SrcMemStream.WriteBuffer(ABuffer, ACount);
Dec(BytesToRead, ACount);
end;
SrcMemStream.Position := 0;
//load MemoryStream into emf stream
SrcMetafile.LoadFromStream(SrcMemStream);
finally
SrcMemStream.Free;
end;
finally
SrcVPEStream.Close;
end;
//ROTATE INTO TARGET EMF STREAM
//mark size of source MF
wSrcMF := SrcMetafile.MMWidth;
hSrcMF := SrcMetafile.MMHeight;
//set angle trig
AngleRadians := DegToRad(AngleDegrees);
SinCos(AngleRadians, AngleSin, AngleCos);
//create target emf
TrgMetafile := TMetafile.Create;
try
//adjust size to suit rotation
wTrgText := (((ARight - ALeft) * Abs(AngleCos)) + ((ABottom - ATop) * Abs(AngleSin)));
hTrgText := (((ARight - ALeft) * Abs(AngleSin)) + ((ABottom - ATop) * Abs(AngleCos)));
wTrgMF := Round((wSrcMF * Abs(AngleCos)) + (hSrcMF * Abs(AngleSin)));
hTrgMF := Round((wSrcMF * Abs(AngleSin)) + (hSrcMF * Abs(AngleCos)));
xDelta := Round((wTrgMF - (wSrcMF * AngleCos) + (hSrcMF * AngleSin)) / 2);
yDelta := Round((hTrgMF - (wSrcMF * AngleSin) - (hSrcMF * AngleCos)) / 2);
//set target emf size
TrgMetafile.SetSize(wTrgMF, hTrgMF);
//transform source MF to target MF
MetafileCanvas := TMetafileCanvas.Create(TrgMetafile, 0);
try
SetGraphicsMode(MetafileCanvas.Handle, GM_ADVANCED);
//set rotation transform
TransformMatrix.eM11 := AngleCos;
TransformMatrix.eM12 := AngleSin;
TransformMatrix.eM21 := -AngleSin;
TransformMatrix.eM22 := AngleCos;
TransformMatrix.eDx := xDelta;
TransformMatrix.eDy := yDelta;
SetWorldTransform(MetafileCanvas.Handle, TransformMatrix);
//play source emf into destination emf canvas
PlayEnhMetaFile(MetafileCanvas.Handle, SrcMetafile.Handle, Rect(0, 0, wSrcMF, hSrcMF));
finally
MetafileCanvas.Free;
end;
//stream target emf into a MemoryStream
TrgMemStream := TMemoryStream.Create;
try
TrgMetafile.SaveToStream(TrgMemStream);
//stream into a VPEStream
TrgVPEStream := CreateMemoryStream(VPE_BUFFER_SIZE);
BytesToRead := TrgMemStream.Size;
TrgMemStream.Position := 0;
while (BytesToRead > 0) do
begin
if (BytesToRead > VPE_BUFFER_SIZE) then
ACount := VPE_BUFFER_SIZE
else
ACount := BytesToRead;
TrgMemStream.ReadBuffer(ABuffer, ACount);
TrgVPEStream.Write(ABuffer, ACount);
Dec(BytesToRead, ACount);
end;
TrgVPEStream.Seek(0);
//draw target emf (below "source text")
ARight := (ALeft + wTrgText);
ATop := ABottom;
ABottom := (ATop + hTrgText);
PenSize := 0;
PictureStream(TrgVPEStream, ALeft, ATop, ARight, ABottom, '');
finally
TrgMemStream.Free;
end;
finally
TrgMetafile.Free;
end;
finally
SrcMetafile.Free;
end;
Preview;
end;
end;