How can I pre measure a string before is printed?
Asked Answered
S

3

6

This is a sketch of the paper form Hello, I am learning programming with C# VS 2010 EE and I’m making an application to fill up a preprinted form. This form has several places in different coordinates. Three of the boxes on the paper are multiline 5” W x 2” H boxes.

I already created the windows form with one TextBox for each place on the paper form.

The thing is that when entering the info in these multiline TextBoxes I need to know how many lines are left on the paper to enter more text, and also, when to stop typing because there is no more space available in the PrePrinted box.

I’ve done a lot of search but everything I have found is about measuring on the screen, which does not match the final result on the paper.

In other words, I need to know how to find out what are going to be the string dimensions on the paper while is being typed into the TextBoxes and compare it with the space available on the PrePrinted form so I can stop before it goes over the bottom border of the box on the paper.

The first box on the paper is 5” in width by 2” in height, starting at “new RectangleF(60.0F, 200.0F, 560.0F, 200.0F)”. I understand those numbers are hundredths of an inch.

All of this, taking in to account that I can not limit the TextBoxes by quantity of characters because not all characters occupy the same space, like H != I; M != l; etc.

Thank you in advance for your help. Today Sep 05 2011, based on your comments and suggestions I’ve changed the code to use Graphics.MeasureString .

This is the code I have now with the Graphics.MeasureString and only one richTextBox: Is working perfectly from the printDocument1_PrintPage event, but I have no idea how to make it work from the richTextBox1_TextChanged event.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;//Needed for the PrintDocument()
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Printing
{
  public partial class Form1 : Form
  {
    private Font printFont1;
    string strPrintText;

    public Form1()
    {
      InitializeComponent();
    }

    private void cmdPrint_Click(object sender, EventArgs e)
    {
      try
      {
        PrintDocument pdocument = new PrintDocument();
        pdocument.PrintPage += new PrintPageEventHandler
        (this.printDocument1_PrintPage);
        pdocument.Print();
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }

    public void printDocument1_PrintPage (object sender,
      System.Drawing.Printing.PrintPageEventArgs e)
    {
      strPrintText = richTextBox1.Text.ToString();
      printFont1 = new Font("Times New Roman", 10); //I had to remove this line from the btnPrintAnexo1_Click
      Graphics g = e.Graphics;
      StringFormat format1 = new StringFormat();
      RectangleF rectfText;
      int iCharactersFitted, iLinesFitted;

      rectfText = new RectangleF(60.0F, 200.0F, 560.0F, 200.0F);
      // The following e.Graphics.DrawRectangle is
      // just for debuging with printpreview
      e.Graphics.DrawRectangle(new Pen(Color.Black, 1F),
        rectfText.X, rectfText.Y, rectfText.Width, rectfText.Height);

      format1.Trimming = StringTrimming.Word; //Word wrapping

      //The next line of code "StringFormatFlags.LineLimit" was commented so the condition "iLinesFitted > 12" could be taken into account by the MessageBox
// Use next line of code if you don't want to show last line, which will be clipped, in rectangleF
      //format1.FormatFlags = StringFormatFlags.LineLimit;

      //Don't use this next line of code. Some how it gave me a wrong linesFilled
      //g.MeasureString(strPrintText, font, rectfFull.Size, 
      //StringFormat.GenericTypographic, out iCharactersFitted, out iLinesFilled);

      //Use this one instead:
      //Here is where we get the quantity of characters and lines used 
      g.MeasureString(strPrintText, printFont1, rectfText.Size, format1, out iCharactersFitted, out iLinesFitted);

      if (strPrintText.Length == 0)
      {
        e.Cancel = true;
        return;
      }
      if (iLinesFitted > 12)
      {
        MessageBox.Show("Too many lines in richTextBox1.\nPlease reduce text entered.");
        e.Cancel = true;
        return;
      }

      g.DrawString(strPrintText, printFont1, Brushes.Black, rectfText, format1);
      g.DrawString("iLinesFilled = Lines in the rectangle: " + iLinesFitted.ToString(), printFont1, Brushes.Black,
        rectfText.X, rectfText.Height + rectfText.Y);
      g.DrawString("iCharactersFitted = Characters in the rectangle: " + iCharactersFitted.ToString(), printFont1, Brushes.Black,
        rectfText.X, rectfText.Height + rectfText.Y + printFont1.Height);
    }

    private void richTextBox1_TextChanged(object sender, EventArgs e)
    {
      //***I don’t know what to type here.*** 

        if (iLinesFitted == 13)
        {
            MessageBox.Show("Too many lines in richTextBox1.\nPlease erase some characters.");
        }
    }

      private void cmdPrintPreview_Click(object sender, EventArgs e)
    {
      printPreviewDialog1.ShowDialog();
    }

    private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
    {
//      strPrintText = richTextBox1.Text;
    }
  }
}
Sidwohl answered 14/8, 2011 at 3:57 Comment(8)
Did you try to use Graphics.MeasureString?Valli
This is not going to work well, Winforms doesn't do resolution independent text rendering. TrueType hinting will be your nemesis, making the text on the screen subtly different from the text on the printer. This can produce large differences when you word-wrap. Consider WPF or an Adobe product.Armchair
Based on your comments and suggestions I’ve changed the code to use Graphics.MeasureString. Is working perfectly from the printDocument1_PrintPage event, but I have no idea how to make it work from the richTextBox1_TextChanged event so I can stop the user with a MessageBox and make them erase some text in the richTextBox. @Hans PassantSidwohl
That couldn't have been based on my comment :) Do avoid spamming the user with message boxes. Just make it obvious that text is not going to get printed by simply not showing it on the monitor either.Armchair
Do check this answer for an example of what MeasureString() will do: https://mcmap.net/q/617607/-drawing-text-in-net/…Armchair
Hi @Hans Passant, I’ll follow your advice on the messageBoxes. About the text_Change event issue, the thing is that I need to restrict the user before the data is stored in a database. It could take several days for the user to enter all the info that’ll fill up the preprinted form, when the user decides to print it, the quantity of lines and/or characters will have to be within the rectangleF limits.Sidwohl
You are entirely too much focused on telling the user she did something wrong. Make it right.Armchair
@ Hans Passant Exactly, that’s what I’m trying to find out. All I needed to know is how to access iLinesFitted from the text_change event because that’s what's not working and I do not know how to do it. I'm just trying to learn programming; in other words, I'm trying to learn how to "make it right". Thank you for your time.Sidwohl
L
6

I think that this is what you are after.

MeasureString(...)

Juat make sure that your graphics object is a PrintDocument.

using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;

namespace GraphicsHandler
{
    public partial class Form1 : Form
    {
        public Form1()
        {
           InitializeComponent();
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            // Check the width of the text whenever it is changed.
            if (checkTextWillFit(textBox1.Text) == true)
            {
                 MessageBox.Show("Entered test is too wide, please reduce the number of characters.");
            }
        }

        private bool checkTextWillFit(string enteredText)
        {
            // Create a handle to the graphics property of the PrintPage object
            Graphics g = pd.PrinterSettings.CreateMeasurementGraphics();
            // Set up a font to be used in the measurement
            Font myFont = new Font("Arial", 12, FontStyle.Regular, GraphicsUnit.Millimeter);
            // Measure the size of the string using the selected font
            // Return true if it is too large
            if (g.MeasureString(enteredText, myFont).Width > 100)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        PrintDocument pd = null;

        private void Form1_Load(object sender, EventArgs e)
        {
            // Initialise the print documnet used to render the printed page
            pd = new PrintDocument();
            // Create the event handler for when the page is printed
            pd.PrintPage += new PrintPageEventHandler(pd_PrintPage);
        }

        void pd_PrintPage(object sender, PrintPageEventArgs e)
        {
            // Page printing logic here            
        }
    } 
}
Linctus answered 14/8, 2011 at 5:37 Comment(3)
Hello guys, thank you for comments. I just uploaded an image; it is a sketch of the paper form that I want to fill up with this application. The printing is going to be only one page at a time and also, not all of the boxes are going to be used. I am going to try your advices but I don’t even know how to implement them. If you could give me some more info I’ll appreciate it. Thank you. @Jalal Aldeen Saa'dSidwohl
Based on all comments and suggestions I’ve changed the code to use Graphics.MeasureString. Now Is working perfectly from the printDocument1_PrintPage event, but I need to, and have no idea how to make it work from the richTextBox1_TextChanged event so I can stop the user with a MessageBox and make them erase some text in the richTextBox. I’m very new to programming; perhaps you or someone else could help me with this. Thanks in advance for your time.@tinchouSidwohl
Hello @Jason James, your code helped me “A LOT”. I adapted it to my application and now is working, it’s doing exactly what I wanted to do. I'm going to post the code that I have up until now which is working great. As Hans advise me the other day, the final code will avoid the use of the MessageBoxes, I think I'll add a hidden label with big bold flashy red letters instead :D. If any of you have more suggestions please let me know. Thank you so very much.Sidwohl
S
1

The information in the text box will be stored in a database and printed on a preprinted form some time. Since the space in each box on the paper is limited, I had to make sure that what ever goes to the database is not more than what the each cell on the paper form can handle. This is the code to avoid the user to enter more lines than can fit in the rectangleF:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;//Needed for the PrintDocument()
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Printing
{
    public partial class Form1 : Form
    {
        private Font printFont1;
        string strPrintText;

        public Form1()
        {
            InitializeComponent();
        }
        //PrintDocument printDocument1 = null; In ny case it makes this error: 'Printing.Form1' already contains a definition for 'primtDocument1'
        private void Form1_Load(object sender, EventArgs eP)
        {
            PrintDocument pd = new PrintDocument();
            pd.PrintPage += new PrintPageEventHandler
            (this.printDocument1_PrintPage);
        }
        private void richTextBox1_TextChanged(object sender, EventArgs e)
        {
            // Check the width of the text whenever it is changed.
            if (checkTextWillFit(richTextBox1.Text) == true)
            {
                MessageBox.Show("\nEntered text pruduces too many lines. \n\nPlease reduce the number of characters.", "Too Many Lines");
            }
        }

        private bool checkTextWillFit(string enteredText)
        {
            StringFormat format1 = new StringFormat();
            format1.Trimming = StringTrimming.Word; //Word wrapping
            RectangleF rectfText;
            int iCharactersFitted, iLinesFitted;

            rectfText = new RectangleF(60.0F, 200.0F, 560.0F, 200.0F);
            // Create a handle to the graphics property of the PrintPage object
            Graphics g = printDocument1.PrinterSettings.CreateMeasurementGraphics();

            // Set up a font to be used in the measurement
            Font myFont = new Font("Times New Roman", 10, FontStyle.Regular);

            // Measure the size of the string using the selected font
            // Return true if it is too large
            g.MeasureString(enteredText, myFont, rectfText.Size, format1, out iCharactersFitted, out iLinesFitted);
            if (iLinesFitted > 12)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        private void cmdPrint_Click(object sender, EventArgs e)
        {
            try
            {
                PrintDocument pdocument = new PrintDocument();
                pdocument.PrintPage += new PrintPageEventHandler
                (this.printDocument1_PrintPage);
                pdocument.Print();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        public void printDocument1_PrintPage(object sender,
          System.Drawing.Printing.PrintPageEventArgs e)
        {
            strPrintText = richTextBox1.Text.ToString();
            printFont1 = new Font("Times New Roman", 10); //I had to remove this line fromthe btnPrintAnexo1_Click
            Graphics g = e.Graphics;
            //float cyFont = printFont1.GetHeight(g);
            StringFormat format1 = new StringFormat();
            format1.Trimming = StringTrimming.Word; //Word wrapping
            RectangleF rectfText;
            int iCharactersFitted, iLinesFitted;

            rectfText = new RectangleF(60.0F, 200.0F, 560.0F, 200.0F);
            // The following e.Graphics.DrawRectangle is
            // just for debuging with printpreview
            e.Graphics.DrawRectangle(new Pen(Color.Black, 1F),
              rectfText.X, rectfText.Y, rectfText.Width, rectfText.Height);

            //Here is where we get the quantity of characters and lines used 
            g.MeasureString(strPrintText, printFont1, rectfText.Size, format1, out iCharactersFitted, out iLinesFitted);

            if (strPrintText.Length == 0)
            {
                e.Cancel = true;
                return;
            }
            if (iLinesFitted > 12)
            {
                MessageBox.Show("Too many lines in richTextBox1.\nPlease reduce text entered.");
                e.Cancel = true;
                return;
            }

            g.DrawString(strPrintText, printFont1, Brushes.Black, rectfText, format1);
            g.DrawString("iLinesFilled = Lines in the rectangle: " + iLinesFitted.ToString(), printFont1, Brushes.Black,
              rectfText.X, rectfText.Height + rectfText.Y);
            g.DrawString("iCharactersFitted = Characters in the rectangle: " + iCharactersFitted.ToString(), printFont1, Brushes.Black,
              rectfText.X, rectfText.Height + rectfText.Y + printFont1.Height);
        }



        private void cmdPrintPreview_Click(object sender, EventArgs e)
        {
            printPreviewDialog1.ShowDialog();
        }

        private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
        {
            //      strPrintText = richTextBox1.Text;
        }

    }
}
Sidwohl answered 7/9, 2011 at 19:9 Comment(0)
D
0

If I'm right, you can use a class called FormattedText to format it before printing. Then you can check the FormatedText's width property.

There's a good tutorial on printing here: http://www.switchonthecode.com/tutorials/printing-in-wpf. The second part is focused more on pagination (which is what I think you are dealing with): http://www.switchonthecode.com/tutorials/wpf-printing-part-2-pagination. FormattedText appears here.

Detest answered 14/8, 2011 at 4:46 Comment(3)
Hello @tinchou, thank you for comment. I just uploaded an image; it is a sketch of the paper form that I want to fill up with this application. The printing is going to be only one page at a time and also, not all of the boxes are going to be used. I'll take a look at that link. The thing is that I didn't use WPF, I used Windows Form Application. Do you think that will matter or there is no difference? Thank you.Sidwohl
unfortunately, yes, printing in WPF and WinForms is quite different. almost all WPF's elements are visuals, which are vector based graphics that can be easily printed. I've never done printing in WinForms, but I found a link to msdn with info about WPF's FormatedText and WinForms' DrawText similarities. msdn.microsoft.com/en-us/library/ms752098.aspx#win32_migration. Hope that helpsMisdemeanant
you should also check the DrawText documentation, it might give you an easy way to measure the formatted textMisdemeanant

© 2022 - 2024 — McMap. All rights reserved.