April 19, 2024, 07:10:30 AM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


Calculating how many lines / characters on a page

Started by Andy, June 05, 2017, 06:11:18 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Andy

Okay,

So I'm getting very confused between pixels, dots, twipps etc.

How do I calculate the following:

Page size A4.

Dots per inch for X and Y are both 600.
Page width 203.20 mm
Page height 279.40 mm

Using a font size of say 12 - how many lines can I print?
How many characters across can I go?

Help please!!  :P
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

Egil

Hi Andy,

Just downloaded and  tried your printing job. You have done a great job!

Then to your question...

First, it is not quite clear to me what units you use for the x and y values in your PrintLine function, but for now  I assume you use pixels.

So for printing out text, you first have to know the paper or page width and height in pixels.
Then you have to decide the size of the left and right margins and subtract both the left and right margins from that number. Now you know what width you have available for your text.

Then you have to find how many characters (including spaces) you have room for in that width for the font size you have choosen. (USE GETTEXTSIZE)

Then do the same for the top and bottom margins.
(By checking the print function in the IWB Editor, you see what I mean).

Before you can print, you have to make a wordwrap routine to avoid splitting up words.


Sorry I can't be more specific, and hope I don't confuse you too much... In my my opinion you have already done the hard parts of you printing library.


Good Luck!

Egil.
Support Amateur Radio  -  Have a ham  for dinner!

Andy

Egil,

Thanks for the encouragement! gladly received!

GETTEXTSIZE does indeed return the number of pixels, but it's meant for a window or dialog.

So do I have to send a line to a window first and then get the size?

If so, won't that be different from the size of print?

Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

Egil

When using console Mode, you can still call many windows functions. Just use ZERO instead of the windows handle.
So guess this can be done here as well.

And a pixel ( if that's the units you actually use) is always a pixel, windows or not.


Egil
Support Amateur Radio  -  Have a ham  for dinner!

Egil

Andy,

Apparently GETTEXTSIZE does not work whithout an open window with IWB... sorry.
Perhaps you can open a dummy window/dialog with coordinates well outside the screen for this?


Egil
Support Amateur Radio  -  Have a ham  for dinner!

Andy

Thanks Egil,

Still puzzled though.....

According to my calculations, I should be able to fit 767.99999 pixels across a line.

The GETTEXTSIZE is returning a value of 842 - and yet the line still has a little room to spare on the line.

I'm setting the text in both cases to font size 12.

window win1



DEF tWidth, tHeight as INT

OpenWindow win1,0,0,500,300,@SIZE,0,"Window Printing Example",&WHnd
setfont win1,"Arial",12,400
GETTEXTSIZE win1, "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890", tWidth, tHeight
closewindow win1

'tWidth 164 pixels

'               mm          Pixles
'pixels to mm 203.20 * 3.7795275590551 = pixels per line = 767.9999999999


openconsole
print
print tWidth," ",tHeight
print
print
do:until inkey$ <> ""
closeconsole

mydc1 = OpenThePrinter("MyDocument") 'Returns the uint of printer DC

   'Create some fonts and sizes.

largefont=MakeFont("Times New Roman,ui",30)
mediumfont=MakeFont("Arial",12)
smallfont=MakeFont("Verdana",14)


   'Set a font for printing.
   SetThefont(mydc1,mediumfont)
PrintLine(xcm(10),800,"1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890","")



Confused.
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

Egil

That is something I've experienced when printing to screen as well. Apparently some fonts use equal width on all characters, while others don't. Think Courier or Courier New use equal width on all characters.

PS:

Almost forgot... here is a link to a better explanation: https://en.wikipedia.org/wiki/Monospaced_font
Support Amateur Radio  -  Have a ham  for dinner!

Egil

Andy,

Have to leave soon, as I must go to the airport.
But here is how I set things up when using a console window for printing out intermediate values. In this way the  console ends when you close the window.


Egil
autodefine "Off"

window win1

DEF tWidth, tHeight, run as INT
Openconsole
OpenWindow win1,0,0,500,300,@SIZE,0,"Window Printing Example",&WHnd
setfont win1,"Arial",12,400
GETTEXTSIZE win1, "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890", tWidth, tHeight

print tWidth," ",tHeight
run =1

waituntil run=0
closewindow win1
closeconsole
END


SUB WHnd(),int

SELECT @class

CASE @idcreate 
centerwindow win1 

CASE @idclosewindow 
run = 0 

ENDSELECT 

RETURN  0
EndSub

'tWidth 164 pixels

'               mm          Pixles
'pixels to mm 203.20 * 3.7795275590551 = pixels per line = 767.9999999999

/*
openconsole
print
print tWidth," ",tHeight
print
print
do:until inkey$ <> ""
closeconsole

mydc1 = OpenThePrinter("MyDocument") 'Returns the uint of printer DC

   'Create some fonts and sizes.

largefont=MakeFont("Times New Roman,ui",30)
mediumfont=MakeFont("Arial",12)
smallfont=MakeFont("Verdana",14)


   'Set a font for printing.
   SetThefont(mydc1,mediumfont)
PrintLine(xcm(10),800,"1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890","")

*/


Support Amateur Radio  -  Have a ham  for dinner!

Andy

Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

ckoehn

Andy,

One more measurement to confuse you, logical units.

Look here: https://msdn.microsoft.com/en-us/library/windows/desktop/dd144938(v=vs.85).aspx
and here: https://support.microsoft.com/en-us/help/74299/info-calculating-the-logical-height-and-point-size-of-a-font

It's like this:
SIZE txtsize

GetTextExtentPoint32(dc, Text, LEN(Text), &txtsize)


SIZE structure returns a cx and a cy which is the width and height of the Text you send to the function.

Later,
Clint

LarryMc

@Andy
While I was typing this Clint made his post
I'd like to just make a few observations/comments:
You're working on a "printer library".
You're at a point of trying to figure out how many lines you can print on a page in a given font.
Egil suggested GETTEXTSIZE but that requires an open window, as has been mentioned.

But if you solve that then what next?
How do you handle printing color to the printer?
How do you handle multiple fonts on the same line?
What about printing pictures on the same page with text?

My point being that you aren't going to just have it print a simple line and be through with it Andy.  ;D

So, think of it this a way.
Underneath IWBasic everything is actually displayed via the screens DEVICE CONTEXT.
You got the information about the default print via its DEVICE CONTEXT.
You print to the printer via its DEVICE CONTEXT.

So, you got the DC of the print already, right.
Well, let' s look at the source code for the GETTEXTSIZE command.
SUB GETTEXTSIZE(win as WINDOW,text as STRING,vWidth as INT BYREF,vHeight as INT BYREF)
vWidth = 0
vHeight = 0
def hdc as UINT
def pt as POINT
IF win.hwnd
hdc = GetHDC(win)
GetTextExtentPoint32A(hdc,text,LEN(text),pt)
ReleaseHDC(win,hdc)
vWidth = pt.x
vHeight = pt.y
ENDIF
RETURN
ENDSUB

If the printer font is set and the DC (hdc) is open then you should be able to get the width and height of a string of text.
This is because you may want to print at a different font size(s) than a window size.
QuoteGetTextExtentPoint32 function
The GetTextExtentPoint32 function computes the width and height of the specified string of text.
Syntax

C++

BOOL GetTextExtentPoint32(
  _In_  HDC     hdc,
  _In_  LPCTSTR lpString,
  _In_  int     c,
  _Out_ LPSIZE  lpSize
);

Parameters

hdc [in]
A handle to the device context.
lpString [in]
A pointer to a buffer that specifies the text string. The string does not need to be null-terminated, because the c parameter specifies the length of the string.
c [in]
The length of the string pointed to by lpString.
lpSize [out]
A pointer to a SIZE structure that receives the dimensions of the string, in logical units.
Return value

If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
Remarks

The GetTextExtentPoint32 function uses the currently selected font to compute the dimensions of the string. The width and height, in logical units, are computed without considering any clipping.
Because some devices kern characters, the sum of the extents of the characters in a string may not be equal to the extent of the string.
The calculated string width takes into account the intercharacter spacing set by the SetTextCharacterExtra function and the justification set by SetTextJustification. This is true for both displaying on a screen and for printing. However, if lpDx is set in ExtTextOut, GetTextExtentPoint32 does not take into account either intercharacter spacing or justification. In addition, for EMF, the print result always takes both intercharacter spacing and justification into account.
When dealing with text displayed on a screen, the calculated string width takes into account the intercharacter spacing set by the SetTextCharacterExtra function and the justification set by SetTextJustification. However, if lpDx is set in ExtTextOut, GetTextExtentPoint32 does not take into account either intercharacter spacing or justification. However, when printing with EMF:
The print result ignores intercharacter spacing, although GetTextExtentPoint32 takes it into account.
The print result takes justification into account, although GetTextExtentPoint32 ignores it.
When this function returns the text extent, it assumes that the text is horizontal, that is, that the escapement is always 0. This is true for both the horizontal and vertical measurements of the text. Even if you use a font that specifies a nonzero escapement, this function doesn't use the angle while it computes the text extent. The app must convert it explicitly. However, when the graphics mode is set to GM_ADVANCED and the character orientation is 90 degrees from the print orientation, the values that this function return do not follow this rule. When the character orientation and the print orientation match for a given string, this function returns the dimensions of the string in the SIZE structure as { cx : 116, cy : 18 }. When the character orientation and the print orientation are 90 degrees apart for the same string, this function returns the dimensions of the string in the SIZE structure as { cx : 18, cy : 116 }.
GetTextExtentPoint32 doesn't consider "\n" (new line) or "\r\n" (carriage return and new line) characters when it computes the height of a text string.
Examples

For an example, see Drawing Text from Different Fonts on the Same Line.
QuoteDrawing Text from Different Fonts on the Same Line
Different type styles within a font family can have different widths. For example, bold and italic styles of a family are always wider than the roman style for a specified point size. When you display or print several type styles on a single line, you must keep track of the width of the line to avoid having characters displayed or printed on top of one another.
You can use two functions to retrieve the width (or extent) of text in the current font. The GetTabbedTextExtent function computes the width and height of a character string. If the string contains one or more tab characters, the width of the string is based upon a specified array of tab-stop positions. The GetTextExtentPoint32 function computes the width and height of a line of text.
When necessary, the system synthesizes a font by changing the character bitmaps. To synthesize a character in a bold font, the system draws the character twice: at the starting point, and again one pixel to the right of the starting point. To synthesize a character in an italic font, the system draws two rows of pixels at the bottom of the character cell, moves the starting point one pixel to the right, draws the next two rows, and continues until the character has been drawn. By shifting pixels, each character appears to be sheared to the right. The amount of shear is a function of the height of the character.
One way to write a line of text that contains multiple fonts is to use the GetTextExtentPoint32 function after each call to TextOut and add the length to a current position. The following example writes the line "This is a sample string." using bold characters for "This is a", switches to italic characters for "sample", then returns to bold characters for "string." After printing all the strings, it restores the system default characters.
C++


int XIncrement;
int YStart;
TEXTMETRIC tm;
HFONT hfntDefault, hfntItalic, hfntBold;
SIZE sz;
LPSTR lpszString1 = "This is a ";
LPSTR lpszString2 = "sample ";
LPSTR lpszString3 = "string.";
HRESULT hr;
size_t * pcch;

// Create a bold and an italic logical font. 

hfntItalic = MyCreateFont();
hfntBold = MyCreateFont();


// Select the bold font and draw the first string 
// beginning at the specified point (XIncrement, YStart). 

XIncrement = 10;
YStart = 50;
hfntDefault = SelectObject(hdc, hfntBold);
hr = StringCchLength(lpszString1, 11, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler
        }
TextOut(hdc, XIncrement, YStart, lpszString1, *pcch);

// Compute the length of the first string and add 
// this value to the x-increment that is used for the 
// text-output operation. 

hr = StringCchLength(lpszString1, 11, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler
        }
GetTextExtentPoint32(hdc, lpszString1, *pcch, &sz);
XIncrement += sz.cx;

// Retrieve the overhang value from the TEXTMETRIC 
// structure and subtract it from the x-increment. 
// (This is only necessary for non-TrueType raster 
// fonts.) 

GetTextMetrics(hdc, &tm);
XIncrement -= tm.tmOverhang;

// Select an italic font and draw the second string 
// beginning at the point (XIncrement, YStart). 

hfntBold = SelectObject(hdc, hfntItalic);
GetTextMetrics(hdc, &tm);
XIncrement -= tm.tmOverhang;
hr = StringCchLength(lpszString2, 8, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler
        }
TextOut(hdc, XIncrement, YStart, lpszString2, *pcch);

// Compute the length of the second string and add 
// this value to the x-increment that is used for the 
// text-output operation. 

hr = StringCchLength(lpszString2, 8, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler
        } 
GetTextExtentPoint32(hdc, lpszString2, *pcch, &sz);
XIncrement += sz.cx;

// Reselect the bold font and draw the third string 
// beginning at the point (XIncrement, YStart). 

SelectObject(hdc, hfntBold);
hr = StringCchLength(lpszString3, 8, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler
        } 
TextOut(hdc, XIncrement - tm.tmOverhang, YStart, lpszString3,
            *pcch);

// Reselect the original font. 

SelectObject(hdc, hfntDefault);

// Delete the bold and italic fonts. 

DeleteObject(hfntItalic);
DeleteObject(hfntBold);


In this example, the GetTextExtentPoint32 function initializes the members of a SIZE structure with the length and height of the specified string. The GetTextMetrics function retrieves the overhang for the current font. Because the overhang is zero if the font is a TrueType font, the overhang value does not change the string placement. For raster fonts, however, it is important to use the overhang value.
The overhang is subtracted from the bold string once, to bring subsequent characters closer to the end of the string if the font is a raster font. Because overhang affects both the beginning and end of the italic string in a raster font, the glyphs start at the right of the specified location and end at the left of the endpoint of the last character cell. (The GetTextExtentPoint32 function retrieves the extent of the character cells, not the extent of the glyphs.) To account for the overhang in the raster italic string, the example subtracts the overhang before placing the string and subtracts it again before placing subsequent characters.
The SetTextJustification function adds extra space to the break characters in a line of text. You can use the GetTextExtentPoint function to determine the extent of a string, then subtract that extent from the total amount of space the line should occupy, and use the SetTextJustification function to distribute the extra space among the break characters in the string. The SetTextCharacterExtra function adds extra space to every character cell in the selected font, including the break character. (You can use the GetTextCharacterExtra function to determine the current amount of extra space being added to the character cells; the default setting is zero.)
You can place characters with greater precision by using the GetCharWidth32 or GetCharABCWidths function to retrieve the widths of individual characters in a font. The GetCharABCWidths function is more accurate than the GetCharWidth32 function, but it can only be used with TrueType fonts.
ABC spacing also allows an application to perform very accurate text alignment. For example, when the application right aligns a raster roman font without using ABC spacing, the advance width is calculated as the character width. This means the white space to the right of the glyph in the bitmap is aligned, not the glyph itself. By using ABC widths, applications have more flexibility in the placement and removal of white space when aligning text, because they have information that allows them to finely control intercharacter spacing.

My point in all of this.
The answer to all the questions you will run into will have answers associated with the DEVICE CONTEXT.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Now I'll mind my own business ::) :o ;D




LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

Andy

June 12, 2017, 07:57:02 AM #11 Last Edit: June 12, 2017, 08:08:01 AM by Andy
Had a breakthrough today.

I came across a website that totally dismisses DPI when printing, there rational was that DPI is simply the dot resolution of the print.

I also came across a formula which gave X characters per line, based on the paper size where you denote how many X characters you are trying for.

Anyway, I have tested for 100 characters per line.  Now it matters what font size you're using obviously, during testing I set the font to 10, and got 100 characters - next I set the font to 20 and got 50.

As you change the font size, the number of characters per line changes accordingly.

This now leads the way for me to wrap text onto a second line if the first line is too long as I can calculate it.

I can now also get a line to change colour (@Larry  ;D) but only applies to the whole line.

This is always going to just be a line printing library, not even thought about anything else yet.

Sample program that calls my updated library file (which I will release soon):

uint mydc1
float LeftMargin  = 0.5 'In inches
float RightMargin = 0.5 'In inches

mydc1 = OpenThePrinter("MyDocument") 'Returns the uint of printer DC

   'Set left and right margins.
   SetMarginLR(LeftMargin , RightMargin)

   'Create a fonts and it's size.
mediumfont=MakeFont("Arial",20)

   'Set which font you want to use.
   SetThefont(mydc1,mediumfont)

   'Print a line.
PrintLine(Across(LeftMargin),800,"XXXXXXXXX XXXXXXXXX XXXXXXXXX XXXXXXXXX XXXXXXXXXX XXXXXXXXX XXXXXXXXX XXXXXXXXX XXXXXXXXX XXXXXXXXX","")


   'Delete the fonts we made.
DeleteFont(mediumfont)

CloseThePrinter()
end


Andy.
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

Brian


LarryMc

Just a thought...
What are ya'lls thoughts about if I took the "Print Preview" feature of the IWBasic IDE and created a standalone app that allowed you to:
1. Select any text file to print.
2. Select the Font , Bold, Italic, Text Color, Word Wrap , for the entire file.
3. Optionally add a page header for (1st page/all pages) with or without graphic
4. Select the Font , Bold, Italic, Text Color for #3
5. Optionally add a page footer for (1st page/all pages)  with or without graphic
6. Select the Font , Bold, Italic, Text Color for #5
7. Optionally add  a watermark to all pages
8. Select one, all, or span of pages.
9. Set margins
10. Save a given setup for reuse.
11. Preview before print

Think the benefit would be worth the effort considering other things that are out there?
Look at the what's in the IDE; I would be modifying the look of it some.

Let me know what ya'll think.
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

Brian

That sounds rsther good, Larry. I suppose we will be able to call it with the SYSTEM command from our own program? And will the text file have to exist as a file, or can the text be taken direct from the screen?
Brian

Andy

June 13, 2017, 03:29:58 AM #15 Last Edit: June 13, 2017, 03:32:00 AM by Andy
Larry,

Yes that sounds a good idea.

Brian, I have sent you what I have so far regarding printing, please let me know how you get on.

Update:

The lib file should now detect if a line fits across the page.
It also detect how many line you can print.
If you try to print more lines than fits on a page, it opens a new page, and will keep on doing until
you finish printing.

Hopefully it will be a reasonable replacement for the WRITEPRINTER function which doesn't work for GDI only printers.
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

LarryMc

Quote from: Brian Pugh on June 13, 2017, 01:49:54 AM
That sounds rsther good, Larry. I suppose we will be able to call it with the SYSTEM command from our own program? And will the text file have to exist as a file, or can the text be taken direct from the screen?
Brian
So, you want to be able to use my Print Preview in your own program.  The should should it be free or should I sell it?

As for a file or taking directly off the screen.  How do I know what would be grabbed off the screen.  If you grab something that is an area of the screen that has text and graphics how do I figure out what is text?
So the answer is, if I do it, it will be for text files.
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

Brian

Sell it - I'd buy it. I've always wanted a Print Preview capability

I see what you mean by the text file conundrum, though

Brian

Andy

Got a lot closer today with the word wrap problem.

You can send a string to be printed up to 255 characters.

Yes, you may debate about the space on the right hand side of the page, but I think this is a decent replacement for the WRITEPRINTER command.

I will upload it tomorrow so you can all have a test.

Andy.
:)
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

Egil

June 15, 2017, 02:44:25 AM #19 Last Edit: June 15, 2017, 03:08:28 AM by Egil
Andy,

I don't like to say this as I know that you have put a lot of work into your print routines. But have you tried printing out text from a RE Control using @RTPRINT?
I used my young friends CB program Mockup ( http://www.ionicwind.com/forums/index.php?topic=6004.msg44362#msg44362 ) to do just that, and can hardly see any difference between "our" printout and yours.
RE Controls are working exactly the same way in IWB.


Egil
Support Amateur Radio  -  Have a ham  for dinner!

ckoehn

I'd say keep on Andy.  With you returning a DC you can print graphics on your printout.  I don't think you can do that with a RE control.

Later,
Clint

Andy

Thanks Clint! - I will keep going, actually just about to post an updated lib file.

Egil, yes I see what you mean, but I'm trying to print directly to the printer which is a GDI only printer and WRITEPRINTER does not work for me.

Thanks anyway.

:)
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

Andy

Here is the new lib file.

It's by no means finished yet, (plenty of things still to work out) but works reasonably well on my PC.

You can see the PrintLine commands (in Part 2 of the iwb code).

I would suggest you keep to font size 12 as this is the best.

As always copy the lib file to the IWBDev\Libs folder.

Try printing to PDF to save ink.

Thanks,
Andy.
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

Egil

@Clint:
I only thought of printing text, sorry.


@Andy:
I woke up my 20 years old HP4-L Laserjet, which I always beleived was GDI only, and got the same result as with the three other printers I have tested on. But when checking the manual it turns out it is a PCL Device....

But are you sure that @RTPRINT calls the WRITEPRINTER function?


Egil
Support Amateur Radio  -  Have a ham  for dinner!

Andy

Not sure at all, and I can't remember saying that it did as I never user rich edit controls.

Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.