Creating columns of text with a variable width fon

2019-04-10 15:43发布

问题:

I'm trying to create coloumns of text for a C# app that will be running on MSN Messenger. I'm having trouple getting everything to line up.

Here's an example of the output that I want:

1)  Pizza Hut                                            123 Fake St.
2)  Domino's Pizza                                       123 Fake St.
3)  The Cheesecake Cafe                                  123 Fake St.
4)  Capital Pizza & Steakhouse                           123 Fake St.
5)  Funky Pickle Pizza                                   123 Fake St.
6)  Boston Pizza                                         123 Fake St.
7)  Rose Bowl Rouge Lounge                               123 Fake St.
8)  Royal Pizza                                          123 Fake St.
9)  A Columbus Pizza & Donair Inc                        123 Fake St.

But because it is a variable width font it is displaying like this:

1)  Pizza Hut                                        123 Fake St.
2)  Domino's Pizza                                   123 Fake St.
3)  The Cheesecake Cafe                                  123 Fake St.
4)  Capital Pizza & Steakhouse                               123 Fake St.
5)  Funky Pickle Pizza                                   123 Fake St.
6)  Boston Pizza                                     123 Fake St.
7)  Rose Bowl Rouge Lounge                               123 Fake St.
8)  Royal Pizza                                          123 Fake St.
9)  A Columbus Pizza & Donair Inc                    123 Fake St.

I have tried using the C# string.PadRight() function as well as creating my own function that add padding using spaces and tabs. Both work fine with fixed width fonts but break with variable width fonts.

Is there any way to determine the width of a string in a given font?

Or does anyone have any other suggestions?

回答1:

Just use the TextRenderer class. The simplest case :

Size size = TextRenderer.MeasureText("Hello world", someFont);

If you don't have access to System.Windows.Fonts Graphics.MeasureString remains, it have some limitations but should do the work :

Bitmap bmp = new Bitmap(1,1);
Graphics graphics = Graphics.FromImage(bmp);
SizeF size = graphics.MeasureString("Hello world", someFont);

But be aware that if the font of your text and the spaces MUST be the same there will be cases where you can't align the text perfectly. I don't know what MSN Messenger is able to do in your case but except if you have access to at least a subset of HTML you won't have a perfect output.

You should also be aware that if you do measurements on a local computer and send to another without the correct font installed your columns won't look like columns anymore so your are limited to the basic subset of fonts presents on all computers.

If multiple operating system support is also a requirement you will have some big problems as the Arial font on Mac and PCs doesn't look (and measure) exactly the same.



回答2:

You could try using the Graphics class to measure the length of the string given a specific font, then use that to determine how many tabs to use.



回答3:

It looks like you are trying to render this all in ASCII in a single text field. Yes? If that's the case, that's pretty tricky. It looks like you have a fixed number of tabs after each one right now, and that would be the issue. You could instead do spaces -- which I suspect you are doing with padright (not very familiar with that specific function).

The key thing, though, is that with pure ASCII like that, shown in a variable width font, you will never get it to line up perfectly in a second column. You can get it close if you are diligent, but that's it -- if you have one row with a lot of capital W's, and another with a lot of lowercase i's, you'll have big width differences no matter what you do. If you're rendering in GDI, the best approach is to make one call to DrawText per column. You can make one big string out of each column if you want, and call MeasureString on the first column to determine how much space you need to move over for the second column.

Or if this is an interface where you can do html, tables or divs would work great. Depends on the specifics of your environment. You could also do something like having two auto-height-set labels in a FlowLayout panel if this was WinForms, etc. There are a lot of options for making this work, but just not pure ascii with a variable width font.

EDIT: Also, I saw you asked about how to get a Graphics class instance in a web service. You can do something like this:

    private static Bitmap bitmap = new Bitmap( 1, 1 );
    private static Graphics graphics = null;

    public static Graphics GetGeneralGraphics()
    {
        if ( graphics == null )
            graphics = Graphics.FromImage( bitmap );
        return graphics;
    }

You probably want to make those local variables (that you properly dispose when finished with) in a web services context.



回答4:

You should probably format everything in HTML, then you could just output TABLE and TR/TD elements for the columns