Text rotation

Knowledge exchange related to the VPE Report Engine and PDF Library

Moderator: IDEAL Software Support

Text rotation

Postby Brent Rose » Tue Apr 12, 2016 9:49 am

Seeing some hints in this forum regarding text rotation to any angle using EMFs, I have been testing the general principle. Below is a demonstration of how this could work (for Delphi VCL)... but there is a significant issue with font line placement. Feedback or suggestions would be welcome!!

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;
Brent Rose
 
Posts: 50
Joined: Wed Mar 21, 2012 8:13 pm

Re: Text rotation

Postby IDEAL Software Support » Tue Apr 12, 2016 2:29 pm

How is the text placement in the unrotated metafile? Is this ok?
IDEAL Software Support
 
Posts: 1621
Joined: Thu Nov 18, 2004 4:03 pm

Re: Text rotation

Postby Brent Rose » Tue Apr 12, 2016 8:23 pm

Yes, text placement in the EMF exported from the VPE doc is correct and exactly as expected. My testing has involved saving both the source EMF and target (rotated) EMF to file to check and compare the non-VPE rotation part of the process.

Drawing text directly to an EMF canvas and rotating that, and still getting the same result, suggests that the VPE-generated EMF is not the issue as such.

I should add that rotating the text by 90, 180, or 270 degrees by the same method above produces an accurate result, regardless of the fonts I have tested (ie OpenType and TrueType). It is only the "between angles" that cause a problem when using the standard OpenType fonts...
Brent Rose
 
Posts: 50
Joined: Wed Mar 21, 2012 8:13 pm

Re: Text rotation

Postby IDEAL Software Support » Wed Apr 13, 2016 9:57 am

Ok, just wanted to make sure there is no problem in VPE. Unfortunately we have no idea how to solve the displacement problem you describe.
IDEAL Software Support
 
Posts: 1621
Joined: Thu Nov 18, 2004 4:03 pm

Re: Text rotation

Postby Brent Rose » Wed Apr 13, 2016 10:35 am

OK, fair enough. Perhaps someone else can comment on the issue or suggest an alternative.

I may investigate whether the displacement is "predictable" - which may provide a partial workaround.

All that said, the ability to rotate text (at least a single line) to any angle is one for the VPE wish list. I always wondered why VPE.Rotation was limited to multiples of 90 degrees - does it pose practical difficulties like the exercise above?
Brent Rose
 
Posts: 50
Joined: Wed Mar 21, 2012 8:13 pm

Re: Text rotation

Postby IDEAL Software Support » Wed Apr 13, 2016 12:25 pm

This has several reasons. We would need to manage more data (coordinates, angle, and a bounding rect). Placing a second text object as a dependent object connected below a rotated object what cause real trouble - especially in our visual designer dycodoc. But our main reason is, that we do not see this as a requirement for a reporting engine - except for watermarks.
IDEAL Software Support
 
Posts: 1621
Joined: Thu Nov 18, 2004 4:03 pm

Re: Text rotation

Postby Brent Rose » Thu Apr 14, 2016 12:27 am

Thanks for the feedback.

My thoughts in response to your 90-degree-limit reasons (and assuming it is theoretically possible to rotate text to ANY angle):

Currently, VPE.Rotation at 90 degree multiples means that the h and w of the output text rect remain the same, albeit swapped as appropriate. You can read off these values in nRight and nBottom. Conceptually, though, it involves specifying an "input text rect" and reading off a potentially different "output text rect".

Assuming that VPE.Rotation could be ANY non-90-degree angle, then the output rect will CHANGE in size, of course, to fit the angled text rect. In the code sample above with SetWorldTransform, the rotation process involves an x and y axis shift (xDelta, yDelta) so that the rotated rect remains entirely within the new output rect.

Logically, you could again read off nRight and nBottom for this re-sized rect exactly as you do for the 90-degree options. In other words, at this point, it is no different from the existing rotation mechanism in terms of the "user input" required to achieve the result (just set VPE.Rotation), or in terms of the subsequent positioning of other report objects (just read the n-vars etc). Naturally, there may well be a lot more going on internally in VPE ;-)

The issue now becomes, how do you position the new output rect?

With the 90-degree options, the maths is pretty easy and you can place the text rect without much of a challenge. ie You basically know where the text is in relation to the output rect, so you can left, centre or right justify the rect on the page along the line of rotation accordingly, for example. However, with other angles, the text rect shifts by some amount (xDelta, yDelta) within the output rect and you lose track of where your original "input rect" ends up. While the adjustment can certainly be computed, it is messy... but there is a much easier way to control placement of the rotated output...

Just as you modify the output placement of VPE text functions by specifying VPE.Rotation, you could modify the placement of output by calling "VPE.RealignRotation" which would take parameters for two points, AlignBy(X, Y) and AlignTo(X, Y). The default values (if this method were not called) would be AlignBy(0, 0) or top-left of the input rect, and AlignTo(ALeft, ATop) matching the top left point passed in the VPE text output function.

ie VPE.RealignRotation(AlignByX, AlignByY, AlignToX, AlignToY);

Conceptually, you nominate a point AlignBy(X, Y) on the input text rect about which you want to have it positioned/justified (relative to its Top, Left). This could be any point on the rect border, or any point within the rect - eg the centre point, or, with the VPE font metric functions it could be some point relative to the actual font. You then nominate the target point AlignTo(X, Y) which is where AlignBy is to be placed on the page. Basic rotational trigonometry will allow the point AlignBy to be (internally) transformed to the same point on the rotated object and then the nLeft, nTop, nRight and nBottom co-ordinates of the output rect can be calculated to achieve placement of the transformed AlignBy at the point AlignTo on the page.

***********************************************************************************************************
EXAMPLE: Suppose I draw a right-angle triangle whose hypotenuse is at 45 degrees on the page. I want to place the text label "hypotenuse" centred along the hypotenuse line. So:

Set the rotation, VPE.Rotation := 45;

I can Render the text "hypotenuse" in a rect with width matching the length of the hypotenuse line, then set AlignBy to the bottom, centre of this rect.
ie Bottom(Y) = height of rect, centre(X) = half the width.

Set AlignTo(X, Y) to the geometric mid-point of the hypotenuse line on the page.

Call VPE.RealignRotation(AlignByX, AlignByY, AlignToX, AlignToY) with this point data.

Output the text with eg VPE.Write in the normal way...

This is pretty simple, and pretty powerful in terms of managing rotated text.
***********************************************************************************************************

Following on with output relative to the n-vars set after a rotated and repositioned text rect will be perfectly logical, and really no different from the current 90-degree options.

For the user interface, you have full rotational and positional control with only one new (quite simple) VPE.RealignRotation method added.

>> We would need to manage more data (coordinates, angle, and a bounding rect).

Well, not really in the user interface as such, as pointed out - but perhaps you mean this would impose an undue load internally for VPE? Of course, I cannot claim to know how much extra code and resources would be required to rotate by any angle as opposed to only 90 degree increments, but I would have guessed that once you can do it for a given angle, you can do it for any angle ;-)

***********************************************************************************************************
>> But our main reason is, that we do not see this as a requirement for a reporting engine - except for watermarks.

Hmmm - that's a difficult point to understand. On the same basis, why do you need to be able to rotate text by 90 degree increments? It is quite true that, mostly, report text is not rotated... but rotation is still a very useful feature. So my answer is simply "Yes, it is needed, and it is a reasonable requirement of a reporting engine". (Not that I think VPE is majorly lacking because it cannot do it - although the 90 degree limit has always struck me as "unusual", and I have been putting a lot of effort into finding a way to get around it). You have pointed out watermarks as one very good example, and labelling diagrammes as in the above example is another one.

Anyway, that's my contribution to the general melting pot for now :-)
Brent Rose
 
Posts: 50
Joined: Wed Mar 21, 2012 8:13 pm

Re: Text rotation

Postby IDEAL Software Support » Thu Apr 14, 2016 8:11 am

You do not want a report engine, you want a CorelDraw engine. :)

For the foreseeable future we have no plans to implement anything like this, as it would require to rewrite major parts of the code. The amount of work is not in balance with the benefit.
IDEAL Software Support
 
Posts: 1621
Joined: Thu Nov 18, 2004 4:03 pm

Re: Text rotation

Postby Brent Rose » Fri Apr 15, 2016 11:33 am

Oh, I definitely want a report engine - not CorelDraw!! Anyway, I fully understand the cost/benefit consideration... still good to have discussed the matter and thrown it out there :D

Of note:

1) I have experimented with a variety of fonts exported from VPE to an emf and then rotated... The unrotated emf exported from VPE is always accurate. The rotated emf results range from pretty accurate to significantly displaced... quite inconsistent.

2) Drawing text directly into an emf (not involving VPE at all) produces a similar range of displacements on rotation.

3) However (thanks to a suggestion from Peter Below, Embarcadero TeamB) by applying a text alignment of "TA_BASELINE" the displacement is pretty much tidied up for all fonts when drawing directly to an emf. This offers some reasonable hope that I can achieve the result I need. It will be a matter of computing the text metrics for the emf canvas to manage text positioning... Assuming I work it all out, I'll post the updated procedure here.
Brent Rose
 
Posts: 50
Joined: Wed Mar 21, 2012 8:13 pm

Re: Text rotation

Postby Brent Rose » Tue Apr 26, 2016 6:37 am

Here is a working example of justified text rotation to any angle.

1. Due to the Windows GDI problem with rotating text in an emf described above (and resolved by positioning text with baseline alignment), a source emf created by VPE cannot be used for rotation. This is not an issue with VPE as such. As an alternative, this example rotates a single line of text "manually" drawn directly to the emf canvas.

2. As a consequence, the AlignBy/AlignTo concept described above cannot be applied in the same manner. Rather, the AlignBy point, which would otherwise be derived by the user from VPE font metrics, must be derived from the emf canvas font metrics instead... which means that you need to pass a horizontal justification (left, centre, right) for the rotated text and a vertical justification (fonttop, fontbottom, linetop, linebottom etc) so that the AlignBy point can be determined "internally".

3. Theoretically, this example could be extended to handle multi-line text as well, or other drawn objects, but the alignment options would be more complex to specify. This aspect would not be a problem if a VPE exported emf could be used because you would have total flexibility in setting an "AlignBy" point. However, as it has been my principal goal to be able to rotate single lines of text (as "labels"), this is not really a problem. Naturally, VPE can still handle multi-line text rotation at 90 degree steps without problem!

4. NB The example report and rotation procedure have been lumped into a single button click method for the purposes of this demonstration...

Code: Select all
procedure TForm3.Button1Click(Sender: TObject);
type
   TJustifyText = (
      jtLeft,
      jtRight,
    jtCentre);
   TTextLineMetric = (
      tlmFontTop,
      tlmFontMiddle,
      tlmFontBaseline,
      tlmFontBottom,
      tlmLineTop,
      tlmLineMiddle,
      tlmLineBottom);
const
   GlyphTransformMatrix: TMat2 = (
      eM11:(fract: 0; value:1);
      eM12:(fract: 0; value:0);
      eM21:(fract: 0; value:0);
      eM22:(fract: 0; value:1));
var
   RotateText: string;
   AngleDegrees: Double;
   AJustify: TJustifyText;
   ALineMetric: TTextLineMetric;
   AlignToX, AlignToY: Double;
   ALineShape: Pointer;
   SrcMetafile, TrgMetafile: TMetafile;
   MFCanvas: TMetafileCanvas;
   AngleRadians, AngleSin, AngleCos: Double;
   TrgDeltaXPosMM, TrgDeltaYPosMM: Double;
   wSrcMM, hSrcMM, wTrgMM, hTrgMM: Double;
   wSrcLU, hSrcLU, wTrgLU, hTrgLU: Integer;
   FontTopLU, FontMiddleLU, FontBaselineLU, FontHeightLU: Integer;
   xDeltaLU, yDeltaLU: Integer;
   AOutlineTextMetric: TOutlineTextmetric;
   AGlyphMetric: TGlyphMetrics;
   TransformMatrix: XFORM;
   TrgMemStream: TMemoryStream;
   TrgVPEStream: TVPEStream;
   BytesToRead, ACount: Integer;
   ABuffer: array[0..VPE_BUFFER_SIZE] of Byte;

   //NB alignment vars are Integer LU's, but keep as doubles for better accuracy
   SrcAlignByXPosLU, SrcAlignByYPosLU: Double;
   TrgAlignByXPosLU, TrgAlignByYPosLU: Double;
   TrgAlignByXPosMM, TrgAlignByYPosMM: Double;
begin
   with VPEngine1 do
   begin
      OpenDoc;
      //License('', '');
      GridMode := InBackground;
      GridVisible := True;


      //DRAW A RIGHT ANGLE TRIANGLE SHAPE TO LABEL WITH ROTATED TEXT
      //triangle vertices at (2, 2), (2, 4), (6, 4)
      //this gives a vertical side of 2, and a horizontal side of 4
      ALineShape := PolyLine(4);
      AddPolyPoint(ALineShape, 2, 2);
      AddPolyPoint(ALineShape, 2, 4);
      AddPolyPoint(ALineShape, 6, 4);
      AddPolyPoint(ALineShape, 2, 2);

      //hypotenuse to be labelled with rotated text = "This is the Hypotenuse"
      //centre the bottom of the text line
      RotateText := 'This is the Hypotenuse';
      AJustify := jtCentre;
      ALineMetric := tlmLineBottom;

      //calculate angle of clockwise rotation to meet the hypotenuse at (2, 2) vertex
      AngleDegrees := (90 - RadToDeg(ArcTan(4 / 2)));

      //calculate the midpoint of hypotenuse as the point about which to justify the label
      AlignToX := ((2 + 6) / 2);
      AlignToY := ((2 + 4) / 2);


      //CREATE SOURCE EMF
      SrcMetafile := TMetafile.Create;
      try
         //must size metafile outside canvas access, so get text metrics first
         MFCanvas := TMetafileCanvas.Create(SrcMetafile, 0);
         try
            MFCanvas.Font.Name := FontName;
            MFCanvas.Font.Size := FontSize;

            //get text metrics
            if (GetOutlineTextMetrics(
               MFCanvas.Handle,
               SizeOf(AOutlineTextMetric),
               @AOutlineTextMetric) = 0) then
               RaiseLastOSError;

            //get font character metrics to establish FontTop and FontBaseline
            if (GetGlyphOutline(MFCanvas.Handle, Ord('X'), GGO_METRICS, AGlyphMetric, 0, nil, GlyphTransformMatrix) = 0) then
               RaiseLastOSError;

            //get extent of RotateText to size the metafile
            //account for a LineGap in the height of the metafile
            with MFCanvas.TextExtent(RotateText) do
            begin
               wSrcLU := cx;
               hSrcLU := (cy + Abs(AOutlineTextMetric.otmLineGap));
            end;
         finally
            MFCanvas.Free;
         end;

         //convert source size from LU to MM
         wSrcMM := (wSrcLU * MM_PER_INCH / Screen.PixelsPerInch);
         hSrcMM := (hSrcLU * MM_PER_INCH / Screen.PixelsPerInch);

         //mark some vertical (Y) font positions
         FontHeightLU := AGlyphMetric.gmBlackBoxY;
         FontBaselineLU := AOutlineTextMetric.otmTextMetrics.tmAscent;
         FontTopLU := (FontBaselineLU - FontHeightLU);
         FontMiddleLU := Trunc(FontBaselineLU - (FontHeightLU / 2));

         //apply required size to source metafile
         SrcMetafile.SetSize(wSrcLU, hSrcLU);

         //draw text onto the canvas
         MFCanvas := TMetafileCanvas.Create(SrcMetafile, 0);
         try
            //set font with alignment by baseline (this rotates much more accurately!)
            SetTextAlign(MFCanvas.Handle, TA_BASELINE);
            MFCanvas.Font.Name := FontName;
            MFCanvas.Font.Size := FontSize;

            //set background mode to transparent - property Transparent has no effect
            SetBkMode(MFCanvas.Handle, TRANSPARENT);

            //output text to baseline
            MFCanvas.TextOut(0, FontBaselineLU, RotateText);
         finally
            MFCanvas.Free;
         end;

         //set angle trig
         AngleRadians := DegToRad(AngleDegrees);
         SinCos(AngleRadians, AngleSin, AngleCos);

         //mark horizontal (X) reference point for justification (wrt source metafile)
         case AJustify of
            jtRight: SrcAlignByXPosLU := wSrcLU;
            jtCentre: SrcAlignByXPosLU := (wSrcLU / 2);
         else
            //default = jLeft
            SrcAlignByXPosLU := 0;
         end;

         //mark vertical (Y) reference point for justification (wrt source metafile)
         case ALineMetric of
            tlmFontTop: SrcAlignByYPosLU := FontTopLU;
            tlmFontMiddle: SrcAlignByYPosLU := FontMiddleLU;
            tlmFontBaseline: SrcAlignByYPosLU := FontBaselineLU;
            tlmLineMiddle: SrcAlignByYPosLU := (hSrcLU / 2);
            tlmFontBottom, tlmLineBottom: SrcAlignByYPosLU := hSrcLU;
         else
            //default = tlmLineTop
            SrcAlignByYPosLU := 0;
         end;

         //CREATE TARGET EMF
         TrgMetafile := TMetafile.Create;
         try
            //adjust size to suit rotation
            wTrgLU := Round((wSrcLU * Abs(AngleCos)) + (hSrcLU * Abs(AngleSin)));
            hTrgLU := Round((wSrcLU * Abs(AngleSin)) + (hSrcLU * Abs(AngleCos)));
            xDeltaLU := Round((wTrgLU - (wSrcLU * AngleCos) + (hSrcLU * AngleSin)) / 2);
            yDeltaLU := Round((hTrgLU - (wSrcLU * AngleSin) - (hSrcLU * AngleCos)) / 2);

            //relocate the AlignBy reference point
            TrgAlignByXPosLU := ((SrcAlignByXPosLU * AngleCos) - (SrcAlignByYPosLU * AngleSin) + xDeltaLU);
            TrgAlignByYPosLU := ((SrcAlignByXPosLU * AngleSin) + (SrcAlignByYPosLU * AngleCos) + yDeltaLU);

            //convert offset lu to mm
            TrgAlignByXPosMM := (TrgAlignByXPosLU * MM_PER_INCH / Screen.PixelsPerInch);
            TrgAlignByYPosMM := (TrgAlignByYPosLU * MM_PER_INCH / Screen.PixelsPerInch);

            //set target emf size
            TrgMetafile.SetSize(wTrgLU, hTrgLU);

            //transform source MF to target MF
            MFCanvas := TMetafileCanvas.Create(TrgMetafile, 0);
            try
               //rotate
               SetGraphicsMode(MFCanvas.Handle, GM_ADVANCED);
               TransformMatrix.eM11 := AngleCos;
               TransformMatrix.eM12 := AngleSin;
               TransformMatrix.eM21 := -AngleSin;
               TransformMatrix.eM22 := AngleCos;
               TransformMatrix.eDx := xDeltaLU;
               TransformMatrix.eDy := yDeltaLU;
               SetWorldTransform(MFCanvas.Handle, TransformMatrix);

               //play source emf into target emf canvas
               PlayEnhMetaFile(MFCanvas.Handle, SrcMetafile.Handle, Rect(0, 0, wSrcLU, hSrcLU));
            finally
               MFCanvas.Free;
            end;

            //save target emf to MemoryStream and draw
            TrgMemStream := TMemoryStream.Create;
            try
               TrgMetafile.SaveToStream(TrgMemStream);

               //convert target size from LU to MM
               wTrgMM := (wTrgLU * MM_PER_INCH / Screen.PixelsPerInch);
               hTrgMM := (hTrgLU * MM_PER_INCH / Screen.PixelsPerInch);

               //adjust AlignTo point so AlignBy(TrgAlignByXPosMM, TrgAlignByYPosMM)
               //is placed at the input AlignTo
               AlignToX := (AlignToX - (TrgAlignByXPosMM / 10));
               AlignToY := (AlignToY - (TrgAlignByYPosMM / 10));

               //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
               PenSize := 0;
               PictureStream(TrgVPEStream,
                  AlignToX, AlignToY,
                  (AlignToX + (wTrgMM / 10)), (AlignToY + (hTrgMM / 10)), '');
            finally
               TrgMemStream.Free;
            end;
         finally
            TrgMetafile.Free;
         end;
      finally
         SrcMetafile.Free;
      end;

      Preview;
   end;
end;


AFTER NOTE: I have since added font style, background shading and text box options for the rotated text... fairly straight forward, and works well. (This is all coded in the latest version of the VPE+ add-on for Delphi VCL available at onlycode.nz)
Brent Rose
 
Posts: 50
Joined: Wed Mar 21, 2012 8:13 pm


Return to VPE Open Forum

Who is online

Users browsing this forum: No registered users and 5 guests