how to print a jpg file using without showing the print dialog

Posted on 2015-02-16
Last Modified: 2015-02-18
Here is my code, it always prints empty page. What could be the problem.

        Dim thepath As String = "D:\My Applications\Transactions\filelocation.jpg"
        PrintDocument1.DocumentName = thepath
        PrintDialog1.Document = PrintDocument1
        PrintDialog1.PrinterSettings = PrintDocument1.PrinterSettings

Can some expert help please?
Question by:fmichail
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 6
LVL 40
ID: 40613094
The way you are doing it, you are printing the file, not the image. The PrintDocument is a low level object that is designed so that you control the printing. When you specify only a file name, it takes a shortcut.

The file is then sent to the standard printing interface in Windows, the one that is called by Windows Explorer when you right click on a file in it and select Print. Windows Explorer is not able to print the file (it would need to know about all the types of files that exist in the world), so it sends it to the application that is registered as the default for the extension of the file.

The same thing happens in your application. Although you set the printer, the PrintDocument, just as Windows Explorer, simply sends the path to the file to the default .jpg application. No information about the printer is sent.

You need to control the printing yourself, so you have a little more work to do:

First, react to the PrintPage event or your PrintDocument object. This event is called automatically when you call the Print method.

Private Sub PrintDocument1_PrintPage(sender As Object, e As Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage

In that method, load the file in an Image object:

Dim stream As New FileStream("D:\My Applications\Transactions\filelocation.jpg", FileMode.Open, FileAccess.Read)
Dim image As Image = Image.FromStream(stream)

Print the image through the Graphics object that is passed to the PrintPage event:

e.Graphics.DrawImage(image, 0, 0)

Play with 0,0 if you do not want to have the image in the upper left corner of the page.

Author Comment

ID: 40613146
Wow, Great Jacques. However, there is still one point, If the Jpg file was created by scanning more than one paper, will the print call print them all or just first one?

meanwhile I will use your approach and see the result. Thanks Jacques

Author Comment

ID: 40613168
I tried your approach and it works, however, it only print the first page... any further trick?. Thanks
The Orion Papers

Are you interested in becoming an AWS Certified Solutions Architect?

Discover a new interactive way of training for the exam.

LVL 40
ID: 40613341
I thought you needed only 1 page for the picture on the .jpg.

It's a little more tricky for multiple page printing. As its name imply, PrintPage prints only one page. It has to be called again for each subsequent page. The tricky part is that the way PrintPage has been designed you do not usually call it again with another PrintDocument.Print (although you might do it that way if you want), but from inside the event itself.

If you have many images to print, one on each page, then simply call PrintDocument.Print for each, setting your filename in a class level variable before each call so that PrintPage can now which image to load.

The other way to print multiple page is to control the page feeds inside of the PrintPage event itself. This is used in cases where you need to launch the printing and do everything from one PrintDocument.Print. This is used most often, because you have inside of PrintPage information that can tell you if you need to change page or not, and enables you to call for another page to be printed. Print simply print what it can and stops when the page is finished.

You simply set the e.HasMorePages property to True. This sends a message to the printer to skip to the next page, and trigger a new call to the PrintPage event. Simple to do, but sometimes a little tricky to implement.

This means that you need an internal mechanism in the PrintPage event itself, to know where you stopped on the previous page and thus where to continue on the new page. I usually do this through one of more static variable(s) defined in PrintPage. A static variable is defined with Static instead of Dim. It is not destroyed when you exit the method, so it has kept its value when you come back in.

If you have an array of images for instance, and a static variable that points to the current index in the array, that variable will keep its value between calls, and you can thus know which image needs to be printed next.

If your problem is of another nature, such as an image that is too big to fit on a page, then the trickiness increases. You will have to control the printing bit by bit / pixel by pixel. This one is not in my bag of trick. What you might want to do in such a case is to make the image smaller so that it fits in one page. You might then want to use the GetThumbnailImage method on your Image object to resize the image before you send it to the page with the DrawImage method.
LVL 40
ID: 40613346
Did not see the thing about the scanner before my previous answer. I do not know how it works with other scanners, but with mine, when I scan many images, it simply saves them in separate files with the same name and a number that goes in sequence. The thing would then be to simply print multiple .jpg files with the technique in my previous answer.

Do you need to keep the files after they are printed? With the few scanners I have used in my life, there was always an option to scan direct to the printer. That could save you some code. And although you might like to code as much as I do, it is usually better to make things direct when you can skip the code.

Author Comment

ID: 40613427
Thanks again Jacque, but how to know if there is anymore pages in order to set e.HasMorePages to true / false. OR how to know the number of pages in the file being printed to handle them using the static variable. In other words how to say

If there is more pages then
end if


For i=1 to the number of pages
    print the page number (i)
next i
I appreciate your help Jacque
LVL 40
ID: 40613778
This might vary depending on the job. You won't do it the same way whether you are printing text that is all the same height or text that has some headers. If the text is always the same height, you just test to see how many lines fit in a page and then print that number of lines, set to True to print the same number of line on the following page and so on, until you reach your last line. You then set the property to False to end the printing.

You can do the same with your images. You know how many images you are going to print, so if they are all the same size and you print the same number of pages on each page, you just have to keep a tab of where you are in your sequence and can then know when to trigger a new page and when you are finished.

It's more complicated if you are working with images that are not the same height.

If you are working with images of different height, or with a combination of text and image, or with text that can vary in height because of headers, then the only way to go is to count pixels as you go along.

Maybe the following code can give you an idea. It's real code, so a little more complex than code written for a demo. I unfortunately do not have time to write and test a demo. But I think that it shows the mechanism. It prints only text, but you would do something similar with images. I am sorry, the only print job for which I have code that handles an image always print on a single page, so I cannot use it to answer your question. It is also a lot more complex, because the images needs to be sized and positioned according to its orientation and the space available, all things that changes for every report and need a lot of pixel counting to be done properly.
		Const topLine As Integer = 40							'Position of the first line of the list

		Static x As Integer												'Current position in the inventory
		Dim cursorX As Integer										'Current position in the current line
		Dim cursorY As Integer										'Current position in the page
		Static smallParts As SmallPartCollection	'Report data
		Static pageNumber As Integer							'Page Number

		If e.Cancel Then Stop

		pageNumber += 1

		If smallParts Is Nothing Then	'Starting a new report
			smallParts = New SmallPartCollection(True)
		End If

		MyBase.PrintPage(sender, e)

		Graphics = e.Graphics	'The base class needs that information

		With Graphics

			e.Graphics.DrawString(Me.Name, New Font("Arial", 12, FontStyle.Bold), Brushes.Black, cursorX, cursorY)
			'The Lexicon should be an option defined by the user
			e.Graphics.DrawString(My.Resources.Rpt21Lexicon, DefaultFooterFont, Brushes.Black, 200, cursorY + 4)
			cursorY = topLine

			While x < smallParts.Count

				With smallParts(x)

					e.Graphics.DrawString(.PartName, DefaultFooterFont, Brushes.Black, cursorX, cursorY)
					e.Graphics.DrawString(.Location, DefaultFooterFont, Brushes.Black, cursorX + 150, cursorY)

					x += 1
					cursorY += 20

					If cursorY >= PrintDocument.PrinterSettings.DefaultPageSettings.Bounds.Height - 150 Then	'Move to a new column
						e.HasMorePages = True
					End If

Open in new window

This prints data from a Collection (smallParts), so I simply have to get the Count at the start of the printing how many lines I will have to print.

Note the Static x at the beginning. This is my pointer in the collection. It is incremented by one to fetch the next element after a line is printed. When PrintPage is called on a new page, because it is static, it has kept its value and thus knows where to start for the new page.

The collection is also static. This enables me to create it on the first pass into PrintPage, but keeps the same one on further calls. This is faster because I do not need to recreate the collection for each page. It also prevents the possibility that changes in the database would fool my sequence.

I also have a pageNumber that is static, because I print the page number. I simply need to increment it at the beginning on each page.

The variables cursorX and cursorY are my cursor into the page. They get reinitialized for each page, so they do not need to be static. I increment them as needed as I go along. In your case, cursorY is probably enough. In my case, I need cursorX because the data is printed in 6 columns and I use cursorX to control their positions.

I loop from x up to the number of elements in my collection.

After each line (each image in your case), I increment x by 1 to get the following piece of data, and cursorY by 20, the vertical spacing that I find is OK for that report. You would do it by the eight of your image.

 If I end up at the bottom of the page (as explained in the following paragraph) before I have reached the count, I know I have more pages to print. I can get out of the loop and trigger a change of page. Because x is static, it has the value that points to the next line in the sequence when it starts looping again on each of the following pages. If the loop ends by itself, then we are finished printing. I simply let the Sub finish without setting e.HasMorePages, and this marks the end of the printing.

To know if I am at the end of the page, I check the value of my vertical cursor against the number of pixels available in the page. This is done with the If cursorY >= .... line. Bounds.Height gives me the number of pixels available in the printing portion of the page, according to the settings of the PrintDocument. The - 150 leaves a space for a margin and the page number that I print at the bottom of each page. If cursorY is greater than that position, then I know that I am at the bottom of the page. This is where I put my e.HasMorePages=True. Still in the loop, so there are still lines to be printed. But at the bottom of the page. So time to move to the next page.

Working with multiple images would be similar. If all the images are the same size, determine how many fit in a page and that is enough to know when to skip.

If the images are of different height however, do something similar to what I did, tracing the vertical position in the page as you go along, and comparing it with the amount of pixels available (Bounds.Height). The main difference in your case is that Instead instead of checking if you are at the bottom of the page after drawing an image, you would do it before drawing it, to see if it fits before sending it to the printer. If you see that the current position of the cursor + the height of the image gives you a value that is greater than Bounds.Height, do not print, skip the page. With text, because the increment between lines is constant, I simply took the 20 into account when I set my bottom margin.

I hope its clear enough to give you an idea of how it is controlled.

You would also have to

Author Comment

ID: 40615314
Dear Jacques

I do not know how to express my appreciation for how much you are keen to get things explained (not only resolved), Please be aware of how much I ppreciate that.

The challenge that I am facing (of course utilizing what I learned from you) is mainly, how to define the number of pages in the file, so that I can define the moment to stop. I found some idea in the net, and I would like to share it with you:

        Private currPage As Integer = 0             'Current Page in the current file/Image
        Private pCount As Integer = 0               'PageCount in the current file/Image
        Private currImage As Image                  'My Current Image

        currImage = Image.FromFile("u:\test printing for mediation.jpg")
        pCount = currImage.GetFrameCount(FrameDimension.Page)

All the code can be enclosed and handled in the printPage event as you indicated.

So we can easily now loop (pCount) and keep on using e.HasMorePages=True till we are done. I feel this is cool.

I do not want to close this issue and grant you the point as I really enjoy communicating with you. Please just reply back and I will then grant points and close the question. Thanks so much again Jacques
LVL 40

Accepted Solution

Jacques Bourgeois (James Burger) earned 500 total points
ID: 40615405
I earn my living training professional programmers in Visual Basic, but in French. I see myself here as training my trainer skills. If I am able to explain complex things clearly in English, that is not my main language, then I end up doing my job better in French. By taking the time to read me and reacting to my comments, you help me in my own training because you give me clues about how I should present this topic if it ever come as a question in one of my classes.

I was not aware that you were printing many images from the same file. I have never seen a .jpg doing that so it did not come to my mind. I have seen that only in .ico and .png files. Although I have never had to use the concept through code, I would have oriented the discussion otherwise if I had known.

Are you printing the images one per page or are you trying to fit many on the same page? If it is one per page, simply loop through the FrameCount, setting HasMorePages to True at the end of each loop, and you are finished printing once the the loop is over. Then you simply leave HasMorePages to its default value of False.

If you are printing many images on the same page, then the problem is not really to know when to skip pages, it's the display of the images on the page.

In case it suits you, be aware that printing does no has to be sequential, from top to bottom. What PrintPage does is prepare a bitmap of the page, that it sends to the printer when it hits End Sub. You can move around as you want in the page if you want. This is something that needs to be done in a complex presentation. You can Print an image in the lower right corner and then in the upper left corner if you want.

Author Closing Comment

ID: 40615429
Dear Jacques,
It was great to discuss this topic with you, my name is Fayez, and I live in Canada and create database applications on my own in addition to my main job as the application architect in a big company in the insurance business. Here is my Email and I will be honered if you establish a contact with me.
LVL 40
ID: 40615453
Where are you in Canada? I am in Longueuil, just across Montreal from the Saint Lawrence river.

Thanks for you invitation, but I had close to 4500 programmers in my classes since 1994, and they keep me pretty busy as far as e-mail is concerned. They are my first concern, because they are the people who bring food in my plate and enable me to buy a computer game from time to time. I thus limit my e-mails to them, otherwise I would spend my days at it.

I have fun doing this here when I have some free time. But I would be doing a disservice to my students if I was to also give e-mail time to programmers who did not have training with me.

Author Comment

ID: 40617067
Dear Jacques,
I am in Toronto, and it will be always my pleasure to hear from you, or hopefully meet you in Toronto. There will never be a need for private consultation request from my side, just exchanging Hi. if it happens that you are visiting Toronto, you find here a friend. Just let me know. Thanks again

Featured Post

Salesforce Made Easy to Use

On-screen guidance at the moment of need enables you & your employees to focus on the core, you can now boost your adoption rates swiftly and simply with one easy tool.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Wouldn’t it be nice if you could test whether an element is contained in an array by using a Contains method just like the one available on List objects? Wouldn’t it be good if you could write code like this? (CODE) In .NET 3.5, this is possible…
A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
This is a high-level webinar that covers the history of enterprise open source database use. It addresses both the advantages companies see in using open source database technologies, as well as the fears and reservations they might have. In this…
NetCrunch network monitor is a highly extensive platform for network monitoring and alert generation. In this video you'll see a live demo of NetCrunch with most notable features explained in a walk-through manner. You'll also get to know the philos…

724 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question