Monday, May 24, 2010

How to place text vertically (rotated) on your report

With a standard Ax installation and with the standard available report control properties, it's not possible to place text vertically (rotated 90 degrees) on a report. (or am I wrong? Let me know in the comments! I'll rename this post to 'An alternative methode to place text vertically on a report' ...)

Now you can have an add-on for Ax that does this for you. Or you may use a separate report generator all together. Or you can use the following solution, using tools available in standard Ax, but with some help of Microsoft technology.

What we gonna do is create a graphics object in memory, write some text, rotate it and then use it as a source for an image control in Ax. Sounds complicated? Maybe. But it's really not that difficult.

Let's assume you have a report in Ax, based on table InventTable. Your report is showing some item information, and we want the itemid as barcode on it as well.

I've put all the code together in one display method. Not best practice, but easier for me now.
Here goes:

Display container ShowBarcode()
{
System.Drawing.Bitmap   BarcodeBitmap;
System.Drawing.Graphics BarcodeGraphics;

int     dx=200;
int     dy=200;

str     barcodetxt;
Image   BarcodeImage;

System.Drawing.Pen                TxtPen;
System.Drawing.Brush              TxtBrush;

System.Drawing.Brush              DrawBrush;

System.Drawing.StringFormat       StringFormat;
System.Drawing.StringAlignment    StringAlignment;

System.Drawing.Font BarcodeFont = new System.Drawing.Font('BC C128 Narrow',36,System.Drawing.FontStyle::Regular);

Int64             BarcodeBitmapPtr;
BarcodeCode128    MyBarcode = BarcodeCode128::construct();
;

BarcodeImage = new Image();

BarcodeBitmap = new System.Drawing.Bitmap(dx,dy);
BarcodeGraphics = System.Drawing.Graphics::FromImage(BarcodeBitmap);

barcodetxt=InventTable.ItemId;
MyBarCode.string(true,barcodetxt);
MyBarCode.encode();
barcodetxt=MyBarCode.barcodeStr();

// clear canvas
DrawBrush = System.Drawing.Brushes::get_White();
BarcodeGraphics.FillRectangle(DrawBrush,0,0,any2int(dx),any2int(dy));

// set barcode text
TxtBrush = System.Drawing.Brushes::get_Black();
TxtPen = new System.Drawing.Pen(TxtBrush);

// set text alignment
StringFormat = new System.Drawing.StringFormat();
StringAlignment = System.Drawing.StringAlignment::Center;
StringFormat.set_Alignment(StringAlignment);
StringFormat.set_LineAlignment(StringAlignment);

// init rotation
BarcodeGraphics.TranslateTransform(any2int(dx),0);
BarcodeGraphics.RotateTransform(90);

// draw text
BarcodeGraphics.DrawString(barcodetxt, BarcodeFont,TxtBrush, any2int(dx/2) , any2int(dy/2),StringFormat);

// transfer image to Ax
BarcodeBitmapPtr=BarcodeBitmap.GetHbitmap();
BarcodeImage.importBitmap(BarcodeBitmapPtr);

return BarcodeImage.getData();
}

Now make sure you have a report control, type bitmap. Set the newly created display method in the properties.


Tip: Watch out with the ResizeBitmap property. When an incorrect image ratio is used, they will reduce the readibility by scanners.
If we run our report, the result will look like this (based on demo data company):

If we would put the text in plain font, we would get something like this:
(leaving out the barcode encoding and using font Arial)

Like I said: If you know a better/easier way to rotate some text in reports, let me know in the comments!

16 comments:

  1. Good article.

    Do you have the same in AX 4.0 ?

    Best Regards,

    ReplyDelete
  2. Hi,

    Shouldn't be a problem to use this in Ax 4.0, though I haven't tested it.

    For Ax V3.0 it's possible as well, but not by using CLR Interop. But you can use the WinGDI class as an alternative for the drawing (and even create wrappers for missing functions if you wish).

    Regards,

    Willy

    ReplyDelete
  3. Hi,

    In AX 4.0, the System.Drawing and System.Drawing.Font is unknown.

    I will appreciate if you could have a code exemple in ax4.0 or ax 3.0.

    Best Regards

    ReplyDelete
  4. To Anonymous: just have to add the System.Drawing to the AOT\References, in AX 2009 it's already added by default.

    ReplyDelete
  5. Hi Patrick,

    Thanx for the input.

    ReplyDelete
  6. Hi,

    I have added the System.Drawing to the AOT\References and I still have de problem.

    The system knows the System.Drawing object but not the System.Drawing.Font object.

    Do you have an idea ?

    Best Regards,

    ReplyDelete
  7. Hi,

    Are you sure you reference it OK?
    Can you see any other classes from System.Drawing, like fe Image or Brush?
    Can you instantiate these classes?

    Regards,

    Willy

    ReplyDelete
  8. Hi,

    It seems that the reference is OK because I can see and instantiate the Bitmap, Graphics, Brush classes.

    I have the problem on the Font classe and the System.Drawing.StringAlignment value.

    Best Regards,

    ReplyDelete
  9. Clever! Thanks for sharing.

    ReplyDelete
  10. Hi. There is one thing you should consider.
    You may set more DPI for the .NET Bitmap. Otherwise the Barcode may not be readable by scanners and the "Image" Texte looks pretty boring on the printed sheet.

    ReplyDelete
  11. HI, any body got asolution for setting StringAlignment in AX4
    and how to increase the DPI because I face this problem the barcode image not clear and not readable

    thanks

    ReplyDelete
  12. Hi, just a observation;
    This code below does not works when run on server, you must create a client static method to generate the bar code.

    BarcodeBitmapPtr=BarcodeBitmap.GetHbitmap();
    BarcodeImage.importBitmap(BarcodeBitmapPtr);

    look at : https://gist.github.com/davidalencar/6214310

    ReplyDelete
  13. hi can you please be more specific with the statement 'put the text in plain font'?
    what do i have to change?
    i only need to show some texts, not the barcode.
    thank you for sharing!

    ReplyDelete
  14. I want to just print a text vertically not a image, How can i do that..
    Thanks and Regards
    Sachin chauhan

    ReplyDelete
  15. Hi Willy.
    I just want to know where I can add that function in the AOT?

    ReplyDelete