Programming Gambas from Zip/Printing
When practising printing, printing “to a file” will save paper. You can open the resulting PDF (Portable Document Format) file in your favourite PDF reader, such as Okular, and see on screen what you would get on paper.
This is about the simplest demonstration of printing. In your program you need a “printer”. We have used objects like buttons and tableviews. You can see them. A printer, though, is invisible. There is a printer class, just as there is a button class. You drag a printer onto your form just the same as you would drag a button or any other object. On the form it looks like a printer, but when the program runs it cannot be seen. It is really just a lump of code that is built into Gambas and does the things that printers are supposed to do, namely print and look after page sizes and orientation and so on. Printer is a clever little object.
First you tell your Printer object to configure, then you tell it to print. (“Printer, print!”, or as we write it in Gambas, prn1.print ). When you tell it to print it will issue the Draw event. In the draw event you put things on the page that you want printed. You do this with all the abilities that another class has, the Paint class. The Paint class can put things onto the page for printing, but it has other uses too, such as painting into DrawingAreas or ScrollAreas on the form. Right: here we go!
Printing Some Plain Text
This small form has a Printer object and a button called bPlainPrint.
Public SimpleText As String Public Sub Form_Open() SimpleText = "Countries of the World<br><br>Papua New Guinea<br><br>Papua New Guinea is a country that is north of Australia. It has much green rainforest. It has beautiful blue seas. Its capital, located along its southeastern coast, is Port Moresby.<br><br>This is plain text in Linux Libertine 12 point.<br><br>John Smith, editor" End Public Sub pr1_Draw() Paint.Font = Font["Linux Libertine,12"] Paint.DrawRichText(SimpleText, 960, 960, Paint.Width - 2 * 960) End Public Sub bPrintPlain_Click() If pr1.Configure() Then Return 'if user clicks Cancel, don't continue pr1.Print End
When the form opens, some text is put in a variable called SimpleText for printing.
When the button is clicked the printer pr1 is told to configure itself. If the user clicks the Cancel button this returns the value of True, so we should do nothing more. Otherwise, dear friendly printer object, please print.
The printer object pr1 sends us the Draw event. It is saying, “I want to draw something! Please tell me what to paint on the page!”. We oblige by saying
Paint.Font = Font["Linux Libertine,12"] Paint.DrawRichText(SimpleText, 960, 960, Paint.Width - 2 * 960)
Paint.Font is a property describing the font. It is a property with parts to it. We assemble those parts using Font[something]. The something is a string. For example, Font["Bitstream Vera Serif,Bold,24"] means “assemble a font that is Bitstream Vera Serif, bold, 24 point”. That is put in the Font property of the Paint thing. Actually the Paint thing is just a collection of skills. It is nothing you can see. It is another invisible class. Be careful not to put spaces in that string unless part of the font name. Gambas Help warns you of this. No spaces either side of the commas!
Paint.DrawRichText(something) is one of paint’s skills. It is a method it knows how to do. It needs at least three things in brackets. It can take a few more. Here we have four “arguments”, or “things in brackets”. First item: what to print. Second item: how far across to begin printing. Third item: how far down to begin printing. The 960 will give an inch margin. 96 dots per inch is a typical default printer resolution. The number is “tenths of a dot”. (I hope I have that right.) Fourth item: how wide is my printing going to be? Answer: the full width that Paint will allow less an inch on the left and an inch on the right. Each inch is 960. Take away two of them.
<br> means “break”, which goes to the next line. <br><br> means go to a new line, then go to a new line again. It gives us a blank line.
Rich Text understands <br>. It also understands quite a few other symbols planted in the text. There are symbols to make it print using “Heading 1” style, and “Heading 2” and so on. You cannot change what these styles look like, though. They are built in and that is that. You can also change fonts and print size and colour anywhere in your text. These codes make the print come out a certain way. In fact, it is a language in itself: HyperText Markup Language, or HTML. For example, to switch on Bold, put in this tag: <b>. When you want to switch Bold off, put in this one: </b>.
Instead of PlainText, get this to print:
FancyText = "<h3>Countries of the World</h3><Small>Papua New Guinea</Small><hr><br>Papua New Guinea is a country that is north of <font color = #780373><b>Australia</b>.</font><font color = black> It has much</font><font color = green> green rainforest</font><font color = black>. It has beautiful <font color = blue>blue seas</font><font color = black>. Its capital, located along its southeastern coast, is <Font Face=Times New Roman, Color=#FF0000>Port Moresby</font>.<br><br>This is written in <font face = Arial>HTML.<br></font><p align = right><font face = Times New Roman, Size=+1><b>John Smith</b></font>, <i>editor</i></p>"
Incidentally, that text, if saved in a text file with the extension .html, will open and display in a web browser, such as FireFox. You can try it.
The result will be:
I used Heading 3 ( <h3> … </h3> ) because Heading 1 was gross.
There are many tags in the text to make it look like that. Gambas allows these tags. It is only a small selection from the full HTML. Save a document in HTML in your word processor, open it in a text editor like Kate, and be amazed.
Tags that can be used in Rich Text
|<h1>, <h2>, <h3>, <h4>, <h5>, <h6> → Headlines||<sup> → Superscript|
|<b> → Bold font||<small> → Small|
|<i> → Italic||<p> → Paragraph|
|<s> → Crossed out||<br> → Line break|
|<u> → Underlined||<a> → Link|
|<sub> → Subscript||<font> → Font|
Examples to change text colour and align a paragraph:
|<Font Color=#B22222> ... </Font>||<p align=right> ... </p>|
Print an Image on a Page
Drag a picture file onto the Data folder. Set the Picture property of the PictureBox to it.
The Printer is named pr1.
Public Pic As Picture Public Sub pr1_Draw() Paint.DrawPicture(Pic, 960, 960, 3000, 3000) End Public Sub bPrint_Click() If pr1.Configure() Then Return 'if user clicks Cancel, don't continue Pic = PictureBox1.Picture pr1.Print End
The picture is scaled to be 3000 x 3000. When I print to a file, the resolution is 96 dots per inch (96 dpi). The picture is printed 1 inch from the top and left margins and is scaled to fit into about 3 inches x 3 inches (3000x3000).
Print a Class List
In this program, 40 names are invented and put in an array called Z. If you were serious, the list of names could be typed in by the user or loaded from a names file or obtained from a database.
The names are printed down the page. There needs to be a side margin, and here it is set to an inch (960 dots when printing to PDF). It is stored in the variable (private property of the form) SideMargin. It is the same on the left and the right. The top margin is TopMargin.
When you print a name, how far down do you go before printing the next? LineSpacing is set to 280. That works out at about 0.3 of an inch. (960 is an inch).
The plan is: Print a name. However long that name is, move along a bit. That is the starting point for a horizontal line. Line as far as the page width less the right side margin. Draw the line. Go down a linespacing. Print the next name. Draw its line. Go down. Print a name. Draw its line, and so on.
Then draw the vertical lines to make the boxes. Start a little to the right of the width of the longest name. Step 330 dots, draw a vertical line, step another 330 dots, draw the next line, and so on. Don’t go past the end point of the horizontal lines. Finally, to make the right hand edge neat, draw a final vertical line. The Printer is called Prn. The button is bPrint.
Private z As New String Private LineSpacing As Integer = 280 Private TopMargin As Integer = 960 Private SideMargin As Integer = 960 Public Sub Prn_Draw() Dim s As String Dim i As Integer Dim NameWidth, HowFarDown, MaxNameWidth, MaxDistanceDown As Float Dim MaxWidth As Float = Paint.Width - 2 * SideMargin Paint.Font = Font["Linux Libertine,12"] Paint.Color(Color.Black) Paint.MoveTo(SideMargin, TopMargin) 'start here Paint.LineTo(Paint.Width - SideMargin, TopMargin) 'draw to here Paint.Stroke 'paint the top line For i = 0 To z.Max s = z[i] NameWidth = Paint.TextExtents(s).Width + 180 'gap at the end about 1/5 inch MaxNameWidth = Max(MaxNameWidth, NameWidth) 'remember the width of the longest name HowFarDown = TopMargin + (LineSpacing * (i + 1)) Paint.DrawText(s, SideMargin, HowFarDown) Paint.MoveTo(SideMargin + NameWidth, HowFarDown) 'starting position Paint.LineTo(Paint.Width - SideMargin, HowFarDown) 'finishing position Paint.Stroke 'draw the line Next MaxDistanceDown = TopMargin + z.Count * LineSpacing 'vertical lines go down to here For i = SideMargin + MaxNameWidth + 100 To Paint.Width - SideMargin Step 330 'step across the page every 1/3 inch Paint.MoveTo(i, TopMargin) Paint.LineTo(i, MaxDistanceDown) Paint.Stroke Next Paint.MoveTo(Paint.Width - SideMargin, TopMargin) Paint.LineTo(Paint.Width - SideMargin, MaxDistanceDown) Paint.Stroke 'final line on right End Public Sub GetNames() Dim FN As String = ["Oliver", "Jack", "Harry", "Jacob", "Charlie", "Thomas", "George", "Oscar", "James", "William", "Amelia", "Olivia", "Isla", "Emily", "Poppy", "Ava", "Isabella", "Jessica", "Lily", "Sophie"] Dim SN As String = ["Smith", "Jones", "Williams", "Brown", "Wilson", "Taylor", "Moreton", "White", "Martin", "Anderson", "Johnson", "Walsh", "Miller", "Davis", "Burns", "Murphy", "Lee", "Roberts", "Singh", "Evans"] FN.Shuffle SN.Shuffle Dim i, n As Integer For i = 1 To 40 z.Add(FN[n] & " " & SN[n]) n += 1 If n > FN.Max Then FN.Shuffle SN.Shuffle n = 0 Endif Next End Public Sub bPrint_Click() Prn.OutputFile = User.Home &/ "Names.pdf" 'I'm printing to a pdf file If Prn.Configure() Then Return GetNames Prn.Print() End
Print a Calendar
The form contains PictureBox1, a printer called Prn, and two buttons called bPicture and bPrint.
This program prints a calendar for the current month. When you look at the page you want to print you will see the “things” that have to be printed in various places. There are three things that call for repetition: the boxes, the numbers in the top left corner of each, and the names of the days of the week.
I gave PictureBox1 a default picture (that shows as soon as the program is run). First I dragged a photo onto the Data folder. Then I set the Picture property of the picturebox to it.
If you do not have a picture to begin with, the user needs to click the Choose Picture... button before clicking Print. The picture is stored in a property called Pic. If it is null printing does not proceed.
Public Pic As Picture Public Sub LoadPicture() Dim Path As String Dialog.Title = "Please Select a picture" Dialog.Filter = ["*.jpg", "*.png", "Image Files", "*", "All files"] Dialog.Path = User.Home If Dialog.OpenFile() Then Return Pic = Picture.Load(Dialog.Path) PictureBox1.Picture = Pic End Public Sub bPicture_Click() LoadPicture FMain.SetFocus End Public Sub bPrint_Click() Pic = PictureBox1.Picture 'This line can be deleted if you don't give your PictureBox a default picture. If IsNull(Pic) Then Message("Please select a photo first.") Else Prn.OutputFile = User.Home &/ "Calendar.pdf" If Prn.Configure() Then Return Prn.Print Endif End Public Sub Prn_Draw() Dim LeftMargin As Float = 480 'half inch Dim TopMargin As Float = 1200 'inch and a bit Dim row, col, DayNum, CellNum As Integer Dim s As String Dim ThisMonth As Integer = Month(Date(Now)) 'the month number of the date part of the NOW function; 1 to 12 Dim ThisYear As Integer = Year(Date(Now)) 'current year Dim FirstOfMonth As Date = Date(ThisYear, ThisMonth, 1) Dim StartDay As Integer = WeekDay(Date(FirstOfMonth)) 'the weekday of the first of the month Dim TextHeight, TextWidth, GridTop As Float GridTop = 7.2 * 960 'Big Photo Paint.DrawPicture(Pic, LeftMargin, TopMargin / 2, Paint.Width - 2 * LeftMargin, 5 * 960) '5 inch height 'Month and Year title Paint.Font = Font["Copperplate33bc,32"] TextHeight = Paint.TextExtents("S").Height 'the height of a character s = Choose(ThisMonth, "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December") & " " & ThisYear Paint.DrawText(s, 0, GridTop - 1000, Paint.Width, TextHeight, Align.Center) 'inch above grid top 'Grid Dim Side As Float = (Paint.Width - 2 * LeftMargin) / 7 'one-seventh of the width between margins For row = 0 To 4 For col = 0 To 6 Paint.DrawRect(LeftMargin + Side * Col, GridTop + Side * Row, Side, Side, Color.Black) 'each square Next Next 'Days of the Week headings Paint.Font = Font["Apple Chancery,12"] TextHeight = Paint.TextExtents("S").Height 'the height of a character For col = 0 To 7 s = Choose(col + 1, "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") Paint.DrawText(s, LeftMargin + Side * Col, GridTop - TextHeight - 96, Side, TextHeight, Align.Center) Next 'Dates Dim DaysInMonth As Integer If ThisYear Mod 4 = 0 And ThisMonth = 2 Then DaysInMonth = 29 Else DaysInMonth = Choose(ThisMonth, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) Paint.Font = Font["Linux Libertine,20"] TextHeight = Paint.TextExtents("1").Height 'the height of a digit For row = 0 To 4 For col = 0 To 6 CellNum = 7 * row + col If CellNum >= StartDay Then DayNum += 1 If DayNum > DaysInMonth Then Return 'Don't go to 35 days in the month! s = If(Col = 0, "<font color=#DD0000>" & DayNum & "</Font>", "<font color=#000000>" & DayNum & "</Font>") Paint.DrawRichText(s, LeftMargin + Side * Col + 96, GridTop + Side * Row + TextHeight + 96) Endif Next Next Row = 0 Col = 0 While DayNum < DaysInMonth 'Put extra dates up in the top left of the grid. DayNum += 1 s = If(Col = 0, "<font color=#DD0000>" & DayNum & "</Font>", "<font color=#000000>" & DayNum & "</Font>") Paint.DrawRichText(s, LeftMargin + Side * Col + 96, GridTop + Side * Row + TextHeight + 96) Col += 1 'next column Wend End
That’s all, folks. May I finish where I began, with a word of thanks to Benoît Minisini. This programming environment is a delight to use. With the gratitude of all of us users we sing, glass in hand, “For he’s a jolly good fellow, and so say all of us”.
13 August 2019