How To Automatically Set The Font Size Relative To The Page Size

Overview

Whenever we make a computer program that uses a printer for printing documents, there is a need to know how many characters of a selected font can fit on a selected page width and height. Thanks to this information, we can create a program that can automatically change font size so that a document content can fit on a page.

Article References

Introduction

In this article, it is explained,

  • How to calculate the printer page size, height, and width, expressed in the number of characters that can fit across the page height and width, for a selected font?
  • How to know if a line of text that we want to print will fit into page row?
  • How to automatically change font size so that the whole document text can fit into the page?

To solve these three simple tasks we need to know,

  • Printer characteristics
    • Physical size of page supported by printer
    • Printer hard margins
  • Page margins set by user
  • Page printable area size height and width
  • Typeface proportion

Printer Characteristics

There are many important printer characteristics, one of them is page sizes supported by printer. There are world standards when printer page sizes are in question. Here are some standard page sizes in millimeters:

Physical Size of Page

  • A3 (297×420 mm)
  • A4 (210×297 mm)
  • A5 (148×210 mm)
  • Legal (215,9×355,6 mm)
  • Letter (215,9×279,4 mm), etc.

Important Notice

Measurement units are often different for Font (points or pica), Paper size (points, inches or millimeters), Computer graphics (pixels), etc. Whatever calculation we do, the measurement unit must be the same. In this article and computer program proposed as solution, pixel is used as measurement unit. According to that here are some measurement units ratio:

  • 0,01 inch ≈ 0,96 pixel
  • 0,75 point ≈ 1,00 pixel

What is Page Printable Area?

The area of the page that can be used to print or write document on, is called Page Printable Area. If we take plain A4 paper and use whole page area to draw something on, the whole page area would be printable area and size of that area would be 210×297 mm. Size of this area may vary because of many factors.

Page Margins Set By User

We usually leave some unused empty space on page from page borders toward center of page that separates content of the page from the edge of the page. This space is called Page Margin Set By User. There are four page margins, accordingly to the page edges:

  • Left Margin
  • Right Margin
  • Top Margin
  • Bottom Margin

So when we set all four page margins the page printable area gets smaller than actual physical size of page.

Printer Hard Margins

It is important to know that every printer has its own physical page margins that narrows size of printable area of the selected page, so called Printer Hard Margins. They are different from page margins set by user. Reasons for that are usually in constructive nature of printers. Some printers physically are not capable to draw or print at whole area of the page. Some printers have options to do so called border-less printing but at cost of time of printing. Printer hard margins are set by printer and can not be overridden if printer does not allow border-less printing. If we set page margins to be less than printer hard margins, printer will override those settings and set its own margins.

Typeface Proportion

Typeface proportion is directly correlated to the size of letters. There are two typefaces when proportion is in question, Proportional and Non-Proportional. Letter height for any typeface proportion is the same. Letter width differs. Width of characters is the same for Non-Proportional typeface, font with this typeface is also known as mono-spaced or fixed width font. Width of letters for proportional typeface is variable. So when we select font, with non-proportional typeface, we can calculate exact number of letters that can fit across page printable area height and width. But when we select font, with proportional typeface, we can calculate exact number of letters that can fit across height of page, or number of rows, but there is no exact method for calculating how many letters can fit across page printable area width, except printing letter by letter of text, and counting number of letters that fit in from left to the right margin of page. Most important, if there is need to print aligned text in columns, we need to use font with non-proportional typeface, so that letters get vertically aligned.

Solution

There are many ways to solve this problem, I have chosen to use standard System Printer and Page dialog with assigned one System Drawing Printing Print Document for obtaining all necessary printer data. There is also, System Font Dialog for collecting data about installed fonts. When program starts and Printer and Page dialog gets initialized, all printer necessary data are automatically set to Print Document. To access those data we can use Print Document Default Page Settings. After getting values for page printable area height and width, I have used Graphics Measure String method for finding out how many letters can fit across width and height. If Type Face of selected font is Non Proportional, this method gives accurate value for number of letters that can fit across page width. If selected Font Typeface is Proportional then, it is necessary to know exactly what text is going to be printed in a single line on the page, and then to use Graphics Measure String method to find out if a line of text can fit on page and change font size accordingly. Number of letters that can fit across page height do not depends on Font Type Face Proportion, and is always accurate when we use Graphics Measure String method.

Here is simple Page_Size_In_Letters method that accepts two parameters Print Document and Font Type. Method returns Tuple, Page Size In Chars with three item values, Page Width, Page Height and Type Face Monospaced Proportion. If Selected Font Type Face is Proportional, the value of Page Width is -1, otherwise it is accurately calculated. Notice that measurement unit for Page Size, Font Size, and Graphics used for calculation is set Pixel.

/// <summary>
/// Calculates size of page in characters
/// using SizeF Graphics.MeasureString() method
/// </summary>
/// <param name="Print_Document"></param>
/// <param name="Font_Type"></param>
/// <returns>
/// Triple, Page Width, Page Height and
/// Font Type Face Monospaced true or false value.
/// </returns>
Tuple<int, int, bool> Page_Size_In_Letters(PrintDocument Print_Document, Font Font_Type)
{
    // Printer Hard Margins
    float Hard_Margin_X = 0F;
    float Hard_Margin_Y = 0F;

    // Page Margins
    float Top_Margin = 0F;
    float Bottom_Margin = 0F;
    float Left_Margin = 0F;
    float Right_Margin = 0F;

    // Phisycal Page Size
    float Page_Height = 0F;
    float Page_Width = 0F;

    // Set Value to Printer Hard Margins
    Hard_Margin_X = Print_Document.DefaultPageSettings.HardMarginX;
    Hard_Margin_Y = Print_Document.DefaultPageSettings.HardMarginY;

    // Set Value to Page Margins
    Top_Margin = Print_Document.DefaultPageSettings.Margins.Top;
    Bottom_Margin = Print_Document.DefaultPageSettings.Margins.Bottom;
    Left_Margin = Print_Document.DefaultPageSettings.Margins.Left;
    Right_Margin = Print_Document.DefaultPageSettings.Margins.Right;

    // Compare Values Of Page Margins Set By User
    // with Printer Hard Margins
    if (Top_Margin < Hard_Margin_Y)
        Top_Margin = Hard_Margin_Y;
    if (Bottom_Margin < Hard_Margin_Y)
        Bottom_Margin = Hard_Margin_Y;

    if (Left_Margin < Hard_Margin_X)
        Left_Margin = Hard_Margin_X;
    if (Right_Margin < Hard_Margin_X)
        Right_Margin = Hard_Margin_X;

    // Set Values of Page Size accordingly to Page Orientation
    Page_Height = Print_Document.DefaultPageSettings.Bounds.Height;
    Page_Width = Print_Document.DefaultPageSettings.Bounds.Width;

    // Calculate Page Printable Area Size Height and Width
    Page_Height = Page_Height - Top_Margin - Bottom_Margin;
    Page_Width = Page_Width - Left_Margin - Right_Margin;

    // Recalculate Size from Hundreds of an Inch to Pixel
    Page_Height = Page_Height * (DeviceDpi / 100F);
    Page_Width = Page_Width * (DeviceDpi / 100F);

    // Set New Font Type with size in Pixel and
    // with measurement unit Pixel
    Font_Type = new Font(
        Font_Type.FontFamily,
        Font_Type.SizeInPoints / (72F / (float)DeviceDpi), // Points to Pixel
        Font_Type.Style,
        GraphicsUnit.Pixel,
        Font_Type.GdiCharSet);

    // Set new Graphics with measurement unit Pixel
    Graphics G = Graphics.FromImage(new Bitmap(1, 1));
    G.Clear(Color.White);
    G.SmoothingMode = SmoothingMode.HighQuality;
    G.InterpolationMode = InterpolationMode.HighQualityBicubic;
    G.PixelOffsetMode = PixelOffsetMode.HighQuality;

    G.PageUnit = GraphicsUnit.Pixel; // Measurement unit Pixel

    // Check Font Type Face

    // Smallest character
    string Word_1 = ".";
    SizeF Measured_Length_Of_Word_1 = new SizeF();
    Measured_Length_Of_Word_1 = G.MeasureString(Word_1, Font_Type, 0, StringFormat.GenericTypographic);

    // Widest character
    string Word_2 = "W";
    SizeF Measured_Length_Of_Word_2 = new SizeF();
    Measured_Length_Of_Word_2 = G.MeasureString(Word_2, Font_Type, 0, StringFormat.GenericTypographic);

    // If measured Width of characters is equal, font is Monocpaced
    bool Monospaced = (Measured_Length_Of_Word_1.Width == Measured_Length_Of_Word_2.Width);

    // Calculate how many letters can fit across
    // printable page area width for selected font type
    int Number_Of_letters_In_Row = 0;
    string Word = "";
    SizeF Measured_Length_Of_Word = new SizeF();
    while (Measured_Length_Of_Word.Width <= Page_Width)
    {
        Number_Of_letters_In_Row++;
        Word += "W";
        Measured_Length_Of_Word = G.MeasureString(Word, Font_Type, 0, StringFormat.GenericTypographic);
    }

    Number_Of_letters_In_Row--;

    // Calculate how many letters can fit across
    // printable page area height for selected font type
    int Number_Of_Letters_In_Column = 0;
    Word = "";
    Measured_Length_Of_Word = new SizeF();
    while (Measured_Length_Of_Word.Height <= Page_Height)
    {
        Number_Of_Letters_In_Column++;
        Word += Environment.NewLine;
        Measured_Length_Of_Word = G.MeasureString(Word, Font_Type, 0, StringFormat.GenericTypographic);
    }

    Number_Of_Letters_In_Column--;

    // If Type Face is Proportional set Width of page to -1
    if(!Monospaced)
        Number_Of_letters_In_Row = -1;

    Tuple<int, int, bool> Page_Size_In_Chars =
            new Tuple<int, int, bool>(
                        Number_Of_letters_In_Row,
                        Number_Of_Letters_In_Column,
                        Monospaced);

    // Return Page Size in Characters
    return Page_Size_In_Chars;
}

This method can be used in a loop to automatically find out, and set, size of font that allows us to fit multiple lines of text across printing page area.

At the beginning of this article, there is an open source solution created in Visual Studio 2022, .Net Framework 4.8. Download it and thoroughly analyze program code.

All the best,


Similar Articles