Typesetting text with .NET - c#

I'm writing a WinForms .NET program that needs to lay some text out on a page (with a few basic geometric/vector graphics). Is there an equivalent of OS X's Core Graphics and/or Core Text? So far, I am just using a PrintDocument and using the Graphics object provided by the PrintPageEventArgs to draw text on the page, but there's very little control over things such as interword spacing, interline spacing etc. and a lot of stuff has to be done manually.
I feel like I'm missing something; is there a better way for typesetting text on a page? I don't mind using 3rd party solutions as long as they are free for personal use.
This will be used for typesetting a small variety of documents, including one-page brochures/fliers (where most text is variable but images are static), award certificates (where most text and images are static but some text is variable), timetables, etc.

WinForms
When you use WinForms it is close to impossible to do proper type setting and yes, almost everything must be done manually. There are a couple of reasons for this:
You don't get detailed glyph information
The text measurement is horrible inaccurate, both the GDI+ and the GDI based version.
Windows Presentation Foundation
If you use WPF this is an entirely different matter as you get detailed geometrical information about each glyph.
However, you can interleave the two, a bit messy, but possible. Although, I do not recommend it as the graphics and bitmaps are not directly interchangeable which can result in slow performance.
If you want to look at the possibility you need to import the System.Windows.Media into your project to access the typeface and glyph capability of WPF.
You will meet a couple of other challenges as well:
The font lists between WinForm and WPF are not identical (more font and font types with WPF).
You cannot just use a WPF glyph and draw it to a WinForm bitmap.
The values from the glyph are, as expected, in em so they need to be converted to pixels based on point size.
However, you can get this (not limited to) information:
All details (too many to list here):
http://msdn.microsoft.com/en-us/library/system.windows.media.glyphtypeface.aspx
Conclusion
But if you stick with GDI+ and WinForms the best approach you can take is probably to use the GDI based TextMetrics class.
You will in any case experience possibly padding issues, you cannot set char spacing and so forth.
You will have to calculate baseline for each typeface and size by using top + ascent:
FontFamily ff = myFont.FontFamily;
float lineSpace = ff.GetLineSpacing(myFont.Style);
float ascent = ff.GetCellAscent(myFont.Style);
float baseline = myFont.GetHeight(ev.Graphics) * ascent / lineSpace;
PointF renderPt = new PointF(pt.X, pt.Y - baseline));
ev.Graphics.DrawString("My baselined text", myFont, textBrush, renderPt);
I do not know of any third-party library that can do this within the WinForm environment, I believe for the reasons mentioned here and the pain it would cause.
In conclusion I can only recommend you to take a look at Windows Presentation Foundation/WPF for a better ground to achieve proper type setting as you will get stuck in a lot of compromises using WinForms for this (I made a font viewer which is where I came across WPF as I wanted to show glyphs and detailed information about it including black-box and so forth - that was painful enough).
UPDATE:
WebBrowser as type-setting engine
A possible work-around of this is to use the WebBrowser control to do the setting. It's not an actual hack, but a bit hack-ish as it establish a initially unnecessary dependency on the IE browser on the user's computer.
However, this can turn out to be flexible when it comes to type setting as you have the same simple controls over text as with any HTML page.
To get a result you attach the HTML with styles and attributes for the text. You can even combine it with images and so forth (obviously).
The control can be for example on another hidden form not visible to the user.
After dropping in the WebControl on the form or creating it manually setting HTML is done in a single step:
WebBrowser1.DocumentText = "<span style='font-size:24px;'>Type-setting</span> <span style='font-family:sans-serif;font-size:18px;font-style:italic;'>using the browser component.</span>";
Next step is to grab what you render as HTML as an image which can be done as this:
using mshtml;
using System.Drawing;
using System.Runtime.InteropServices;
[ComImport, InterfaceType((short)1), Guid("3050F669-98B5-11CF-BB82-00AA00BDCE0B")]
private interface IHTMLElementRenderFixed
{
void DrawToDC(IntPtr hdc);
void SetDocumentPrinter(string bstrPrinterName, IntPtr hdc);
}
public Bitmap GetImage(string id)
{
HtmlElement e = webBrowser1.Document.GetElementById(id);
IHTMLImgElement img = (IHTMLImgElement)e.DomElement;
IHTMLElementRenderFixed render = (IHTMLElementRenderFixed)img;
Bitmap bmp = new Bitmap(e.OffsetRectangle.Width, e.OffsetRectangle.Height);
Graphics g = Graphics.FromImage(bmp);
IntPtr hdc = g.GetHdc();
render.DrawToDC(hdc);
g.ReleaseHdc(hdc);
return bmp;
}
From: Saving images from a WebBrowser Control
Now you can place the bitmap on to your normal page. ..Or just use the control directly on the form with reduced interactive capability (right-click menu could easily become a problem if not).
Not knowing the exact usage that this is for, this may or may not be a suitable solution but it gives some powerful options and can be in place of a third-party solution.
Components
Looking high and low it seem that this area is not so much targeted. I could find one component that comes close but the pattern continues: one will be hitting a nail with a sledgehammer. It ends up in the same category as the WebBrowser work-around.
This component is really a full fledged editor but by disabling most of it it can perhaps be used to simply display formatted text/graphics with type setting sort of at hand:
http://www.textcontrol.com/en_US/products/dotnet/overview/
Another possibility is to use a PDF component to set up the content and use a component to render the PDF as graphics (commercial):
http://www.dynamicpdf.com/Generate-PDF-.NET.aspx
http://www.dynamicpdf.com/Rasterizer-PDF-.NET.aspx
Non-commercial:
http://www.pdfsharp.net/?AspxAutoDetectCookieSupport=1
Using GhostScript to get image:
http://www.codeproject.com/Articles/32274/How-To-Convert-PDF-to-Image-Using-Ghostscript-API
The obvious static alternative
..and corky perhaps, but in any case I'll include it as simplicity sometimes works best -
If it is not a absolute requirement to be able to create these pages dynamically, generate all the text and graphics in Illustrator/Photoshop/inDesign etc. and save the result as images to be displayed.

In order to lay down text: there are several possibilities:
A simple label, that lets you simple text manipulation (like font, placement, color, etc.)
A rich text document that you may change the properties of the control to suite your needs, keep in mind that this one may present some simple images, etc.
An image -> that you may create dynamically, with the graphics object, with the onpaint event. this is not recomended, as its a very low level approach, but as you know as low level as you get, the more power you gain.
Combination of many controls, in a custom control you create, that you hold the information you want to draw / write in local members -> then keeping cont to when you change something, then set a flag (_needToBeRepainted = true;), and then on repainting, if(_needToBeRepainted) draw everything.
please let me know if you need further assistance solving this one.

Related

Render DMD in Unity3D

I want to render a custom display from an emulation. Think like a dot matrix display from pinball machines.
How would i effectively go about this? (Think about actually writing to a texture that size will probably run way too slow)
There has to be a good way to get this to render, but i have trouble finding a way that actually performs properly as well.
There are many options to do this but without further details (DMD screen resolution, number of colors, animated or not, etc) it's not easy to help. Here are a bunch of options popped into my mind, hope the one you are looking for is somewhere here :)
1) There was a similar question, you can find it along with the answer here
2) If you want to display text only, there's a wide range of sites offering DMD fonts for free, e.g. here
3) You can also edit/extend the font set you download and display 'special characters' as graphics, or just use the standard ASCII table for the purpose if that's enough for your needs. e.g. ▓ █ ╔ ═ ╗ and similar "drawing characters"
You can find inspiration and ASCII art (including animated ones) e.g. here
4) Might be slow (again, "depends") but you can go for bitmap and .SetPixels with a Texture2D and DrawTexture
5) A bit "hacky", but you can save your anim phases into either bitmap data/array (readonly/constant variables for example, or read from disc in a managed way, or draw with the help of a free asset from the store, like this one here, etc) and do Graphics.DrawTexture
6) If the thing you want to display is 100% static (i.e. it's not actual data like score, but "hardcoded" animations like "TILT" text or such), you can create a Sprite Animation
7) You can mix the above and e.g. go for a font (#2) to display dynamic data on a canvas, and play the static animation around it making it look like the whole thing is dynamic
Hm. That's all right off the top of my head :)
Hope this helps!

How to show an image in PictureBox if the picture can be from 10x10 to 500x500

Now I used next code to download picture from desctop to PictureBox
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "BMP|*.bmp";
if (openFileDialog.ShowDialog() != DialogResult.OK)
return;
pictureBox.Load(openFileDialog.FileName);
pictureBox.SizeMode=PictureBoxSizeMode.StretchImage;
pictureBox.BorderStyle = BorderStyle.Fixed3D;
If I used a normal picture(100x100) it looks nice(unfuzzy).
If I used a smal picture(15x15) it looks fuzzy.
I want to know how to make them unfuzzy( they can look like bricks, but they need to be unfuzzy);
Waiting result for small picture need to look like this
Waiting result for normal picture need to look like this
The fuzziness comes from image interpolation. To have your desired blocky look, you need to use nearest neighbour interpolation.
Thankfully, .NET has this build into. You can set to the interpolation mode to the GDI Graphics object which renders your PictureBox.
To do so I'd recommend exteding the PictureBox user control and adding an InterpolationMode parameter. Then, you can override the OnPaint method, and use your interpolation mode.
public class InterpolatingPictureBox : PictureBox
{
public InterpolationMode InterpolationMode { get; set; }
protected override void OnPaint(PaintEventArgs eventArgs)
{
eventArgs.Graphics.InterpolationMode = InterpolationMode;
base.OnPaint(eventArgs);
}
}
Here is a 16x16 image: , upscaled to 256x256 using
NearestNeighbour (left)
Bilinear (which is default, middle)
HighQualityBilinear (right)
All are rendered on the InterpolatingPictureBox control.
You have a SERIOUS problem. There is no easy solution.
I want to know how to make them unfuzzy
Impossible. Sharpening (which is what you want) of images to larger resolution is possible within reason, but this takes seriously advanced programs that run for quite some time. Not talking as programmer here - but this is a standard step in printing fine art photography. Photos have a relatively low resolution (mine come at up to 43 megapixel) and printers a much higher one.
There are some AA approaches that are easy, but 10x10 sized up t0 100x100 is going to challenge even the most advanced programs - 10x10 is nothing to work with. You talk of 10x starting with no data on the beginning.
You CAN turn off anti aliasing, resize the image in code (easy to do) with no anti aliasing - but this will turn real pictures looking UGLY. .NET has no real high level sharpening functionality and again, 10x10 is not enough to start working with.
The best you can do is not load them into a picture box. Load them into a bitmap, then draw that onto a new bitmap of optimal size (100x100). This at least gives you full control over the resizing.
The code for this you can find at
Scaling a System.Drawing.Bitmap to a given size while maintaining aspect ratio
while
Image resizing in .Net with Antialiasing
tells you how to select anti aliasing.
But again: expect quite little. Those anti aliasing modes are purely technical and not anything that you use for photos. Plus selecting the right one depends on the input image - so you can use a default or give the user a choice. I am probably overcomplicating things, but outside programming I actually DO deal with images that need to be scaled up with as little image loss as possible for large printing.

SetCompatibleTextRenderingDefault(true) and Graphics.DrawString() text rendering

I'm writing a C# (ActiveX) plugin for an application that uses SetCompatibleTextRenderingDefault(true) (forces .net 1.1 text rendering style) This setting mangles some of the text I output with Graphics.DrawString() causing it to look slightly smudged and bolded. Unlike individual controls neither the Graphics class nor the BitMap have UseCompatibleTextRendering properties that can be used to override the individual behavior. Short of fiddling around to try and figure out what's special about the places where I'm drawing text that doesn't get mangled is there anything I can do about this?
The app my plugin is for belongs to a third party so simply changing the SetCompatibleTextRenderingDefault call it inflicts on me is not an option.
Edit: The 'special' thing appears to be the color of the background and how it's affecting the anti-aliasing used; so fiddling to fix it by how I setup the rectangles doesn't seem to be an option.
I'd advise using TextRenderer.DrawText instead of Graphics.DrawString - even with compatible text rendering disabled, it seems to produce crisper, more consistent results.
I found a fix for my problem by changing the TextRenderingHint to SingleBitPerPixelGridFit which is the default when not using compatible text rendering. When it's instead set to true it uses a the ClearType enumeration except that for whatever reason unlike normal cleartype text the results are ugly and extremely hard to read.
textGraphics.TextRenderingHint =
System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;

Smooth Text On Glass

I have seen many other samples out there that draw smooth text on glass. But I can't use them. I need every single label that gets added at runtime to be smooth. I can't just "draw" text onto the screen.
Is this at all possible, and are there and sources around?
Thank you
Take a long at this article http://msdn.microsoft.com/en-us/magazine/cc163435.aspx#S6
It's a bit long but it answers alot of your question and alore more in regards to glass.
but the relevant part for you directly is
One particular gotcha is that
rendering a GDI item in black uses the
bit pattern 0x00000000-which also
happens to be a completely transparent
black if you are using an alpha
channel. This means that if you draw
with a black GDI brush or pen you'll
get a transparent color, not a black
one. The biggest problem this presents
is when you try to use the default
text color in a control of a text
label that sits on the glass area.
Since the default text color is
usually black, the DWM will consider
this to be transparent and the text
will be written in the glass
incorrectly. An example can be seen in
Figure 10. The first line is written
with GDI+, the second is a text label
control using the default color. As
you can see, it's nearly illegible
because it's actually incorrectly
rendered text that shows up as gray,
not black.
Happily, there are a number of ways
around this problem. Using owner-draw
controls is one. Rendering to a bitmap
that has an alpha channel is another.
Fortunately, the easiest way to get
text on controls is to let the .NET
Framework 2.0 use GDI+ for you. This
is easily accomplished by setting the
UseCompatibleTextRendering property on
your controls. By default, this
property is set to false so that
controls written for previous versions
of the .NET Framework will render the
same. But if you set it to true, your
text will come out looking correct.
You can set the property globally with
the
Application.SetUseCompatibleTextRenderingDefault
method.
He also provides example code you can place in your Main()
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
Application.Run(new GlassForm());
}
But I recommend reading the article, It'll clear up alot of what's going on with Aero/Glass
Cheers,
Phyx

Winforms: SuspendLayout/ResumeLayout is not enough?

I have a library of a few "custom controls". Essentially we have our own buttons, rounder corner panels, and a few groupboxes with some custom paint. Despite the "math" in the OnPaint methods, the controls are pretty standard. Most of the time, all we do is draw the rounded corners and add gradient to the background. We use GDI+ for all that.
These controls are ok (and very nice looking according to our customers), however and despite the DoubleBuffer, you can see some redrawing, especially when there are 20++ buttons (for example) on the same form. On form load you see the buttons drawing… which is annoying.
I'm pretty sure that our buttons are not the fastest thing on earth but my question is: if double buffer is "on", shouldn't all that redraw happen in background and the Windows subsystem should show the results "instantly" ?
On the other hand, if there's "complex" foreach loop that will create labels, add them to a panel (double buffered) and change their properties, if we suspendlayout of the panel before the loop and resume layout of the panel when the loop is over, shouldn't all these controls (labels and buttons) appear "almost instantly"? This doesn't happen like that, you can see the panel being filled.
Any idea why this is not happening? I know it's hard to evaluate without sample code but that's hard to replicate too. I could make a video with a camera, but trust me on this one, it's not fast :)
We've seen this problem too.
One way we've seen to "fix" it is to completely suspend drawing of the control until we're ready to go. To accomplish this, we send the WM_SETREDRAW message to the control:
// Note that WM_SetRedraw = 0XB
// Suspend drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
...
// Resume drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, new IntPtr(1), IntPtr.Zero);
One of the things you should look at is whether you have set BackColor=Transparent on any of the child controls of your panels. The BackColor=Transparent will significantly degrade rendering performance especially if parent panels are using gradients.
Windows Forms does not use real transparency, rather it is uses "fake" one. Each child control paint call generates paint call on parent so parent can paint its background over which the child control paints its content so it appears transparent.
So if you have 50 child controls that will generate additional 50 paint calls on parent control for background painting. And since gradients are generally slower you will see performance degradation.
Hope this helps.
I'll approach your problem from a performance angle.
foreach loop that will create labels,
add them to a panel (double buffered)
and change their properties
If that's the order things are done, there's room for improvement. First create all your labels, change their properties, and when they are all ready, add them to the panel: Panel.Controls.AddRange(Control[])
Most of the time, all we do is draw
the rounded corners and add gradient
to the background
Are you doing the same thing over and over again? How are your gradients generated? Writing an image can't be that slow. I once had to create a 1680x1050 gradient in-memory, and it was really fast, like, too fast for Stopwatch, so drawing a gradient can't be so hard.
My advice would be to try and cache some stuff. Open Paint, draw your corners and save to disk, or generate an image in-memory just once. Then load (and resize) as needed. Same for the gradient.
Even if different buttons have different colors, but the same motif, you can create a bitmap with Paint or whatever and at runtime load it and multiply the Color values by another Color.
EDIT:
if we suspendlayout of the panel before the
loop and resume layout of the panel when the loop is over
That's not what SuspendLayout and ResumeLayout are for. They suspend the layout logic, that is, the automatic positioning of the controls. Most relevant with FlowLayoutPanel and TableLayoutPanel.
As for doublebuffering, I'm not sure it applies to custom draw code (haven't tried). I guess you should implement your own.
Doublebuffering in a nutshell:
It's very simple, a couple lines of code. On the paint event, render to a bitmap instead of rendering to the Graphics object, and then draw that bitmap to the Graphics object.
In addition to the DoubleBuffered property, also try adding this to your control's constructor:
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint, true);
And if that ends up not being enough (which I'm gonna go out on a limb and say it isn't), consider having a look at my answer to this question and suspend/resume the redraw of the panel or Form. This would let your layout operations complete, then do all of the drawing once that's done.
You may want to look at the answer to my question, How do I suspend painting for a control and its children? for a better Suspend/Resume.
It sounds like what you are looking for is a "composited" display, where the entire application is drawn all at once, almost like one big bitmap. This is what happens with WPF applications, except the "chrome" surrounding the application (things like the title bar, resize handles and scrollbars).
Note that normally, unless you've messed with some of the window styles, each Windows Form control is responsible for painting itself. That is, every control gets a crack at the WM_ PAINT, WM_ NCPAINT, WM_ERASEBKGND, etc painting related messages and handles these message independently. What this means for you is that double buffering only applies to the single control you are dealing with. To get somewhat close to a clean, composited effect, you need to concern yourself not just with your custom controls that you are drawing, but also the container controls on which they are placed. For example, if you have a Form that contains a GroupBox which in turn contains a number of custom drawn buttons, each of these controls should have there DoubleBuffered property set to True. Note that this property is protected, so this means you either end up inheriting for the various controls (just to set the double buffering property) or you use reflection to set the protected property. Also, not all Windows Form controls respect the DoubleBuffered property, as internally some of them are just wrappers around the native "common" controls.
There is a way to set a composited flag if you are targeting Windows XP (and presumably later). There is the WS_ EX_ COMPOSITED window style. I have used it before to mix results. It doesn't work well with WPF/WinForm hybrid applications and also does not play well with the DataGridView control. If you go this route, be sure you do lots of testing on different machines because I've seen strange results. In the end, I abandoned used of this approach.
Maybe first draw on a control-only 'visible' (private) buffer and then render it:
In your control
BufferedGraphicsContext gfxManager;
BufferedGraphics gfxBuffer;
Graphics gfx;
A function to install graphics
private void InstallGFX(bool forceInstall)
{
if (forceInstall || gfxManager == null)
{
gfxManager = BufferedGraphicsManager.Current;
gfxBuffer = gfxManager.Allocate(this.CreateGraphics(), new Rectangle(0, 0, Width, Height));
gfx = gfxBuffer.Graphics;
}
}
In its paint method
protected override void OnPaint(PaintEventArgs e)
{
InstallGFX(false);
// .. use GFX to draw
gfxBuffer.Render(e.Graphics);
}
In its resize method
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
InstallGFX(true); // To reallocate drawing space of new size
}
The code above has been somewhat tested.
I had the same problem with a tablelayoutpanel when switching usercontrols that I wanted displayed.
I completely got rid of the flicker by creating a class that inherited the table, then enabled doublebuffering.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
namespace myNameSpace.Forms.UserControls
{
public class TableLayoutPanelNoFlicker : TableLayoutPanel
{
public TableLayoutPanelNoFlicker()
{
this.DoubleBuffered = true;
}
}
}
I've had a lot of similar issues in the past, and the way I resolved it was to use a third-party UI suite (that is, DevExpress) rather than the standard Microsoft controls.
I started out using the Microsoft standard controls, but I found that I was constantly debugging issues which were caused by their controls. The problem is made worse by the fact that Microsoft generally does not fix any of the issues which are identified and they do very little to provide suitable workarounds.
I switched to DevExpress, and I have nothing but good things to say. The product is solid, they provide great support and documentation and yes they actually listen to their customers. Any time I had a question or an issue, I got a friendly response within 24 hours. In a couple of cases, I did find a bug and in both instances, they implemented a fix for the next service release.
I have seen bad winforms flicker on forms where the controls referred to a missing font.
This is probably not common, but it's worth looking into if you've tried everything else.

Categories

Resources