This week guest blogger Peter Webb continues his series of deployment-related posts with an article describing how printing works in a deployed application.
With this post, I hope to answer some long-standing and frequently asked questions about how a deployed application generates hardcopy and why that process differs from printing in MATLAB. The differences are most evident in applications running under Microsoft Windows; thus the bulk of this post concerns printing on Microsoft Windows. Most of these issues simply aren't issues at all on the UNIX platforms.
MATLAB's print command can send the graphics displayed in a figure window either to a file or a printer. MATLAB executes very different code for when preparing graphics for these two destinations. Printing to a file is the simpler process, as it effectively involves copying the pixels already displayed in the figure into a file on the disk. Printing to a printer requires MATLAB to send instructions to the printer to regenerate the figure's pixels inside the printer.
MATLAB figure windows render graphics using a mixture of C/C++ functions and Java classes and methods. MATLAB's C/C++ functions invoke the native platform's graphical APIs directly; these C/C++ functions comprise native mode Handle Graphics. MATLAB is evolving away from native mode graphics, because Java-based graphics are less platform-dependent, but certain functions have not yet made the transistion. On the Microsoft Windows platform, printing figure windows still requires calls to the native Windows API. UNIX platforms use Java, non-native mode, graphics to print.
Why does all this matter? Because applications generated with the MATLAB Compiler and the deployment tools cannot use native mode Handle Graphics. Without some kind of additional support, MATLAB applications deployed to computers running Microsoft Windows would not be able to print. The MATLAB Compiler hides this complexity behind the deployprint function, presenting a uniform API on both Microsoft Windows and UNIX systems. Deployed applications must use deployprint rather than print to send data to the printer:
if ~isdeployed print else deployprint end
The isdeployed function only returns true when executed by a deployed application.
Note that deployprint cannot be used to print to a file, but only to send output to a printer. For printing to file, even in a deployed application, use print instead.
In MATLAB, printing is a four step process. In each step, you interact with a single function or dialog box:
- Begin printing, specify figure to print: print
- Set MATLAB-specific print options: printdlg
- Set platform-specific print options: the platform-specific print dialog box.
- Send the data to the printer: the MATLAB Cancel Print Job dialog box.
Step 1 creates a print job and initializes it with default data. Steps 2 and 3 optionally modify the defaults. Step 4 reads the settings in the print job and uses it to send image data and instructions to the printer. Together, these four steps make up the printing pipeline. In an interactive MATLAB session, MATLAB manages all four steps of the pipeline, but in a deployed application different programs manage different parts of the pipeline.
When a deployed application prints, the printing pipeline has an additional step:
- Begin printing.
- Set platform-specific print options.
- Generate bitmap file from figure.
- Set platform-specific print options.
- Send data to the printer.
The deployprint function manages the entire pipeline, but instead of using the Microsoft Windows printing API for the last two steps, as print does, deployprint delegates this responsibility to a separate program, PrintImage.exe. Since PrintImage cannot directly access the figure window data, deployprint invokes print to create a Microsoft Windows bitmap from the figure, and passes the name of the bitmap file to PrintImage.
Deployed applications use the MATLAB Common Runtime (MCR) rather than MATLAB to execute MATLAB functions. MATLAB functions executed by the MCR produce exactly the same results as when MATLAB executes those functions. However, PrintImage is not MATLAB, and though we've made every effort to duplicate MATLAB's behavior in PrintImage some differences are inevitable.
In both MATLAB and deployed applications, properties stored on the figure window affect the printed appearence of the figure window. For example, the PaperOrientation property determines if the figure prints in landscape or portrait mode.
In an interactive session, MATLAB processes all of the figure properties relevant to printing. In a deployed application, the MCR processes all of the figure properties relevant to creating a bitmap (such as figure background color) and leaves the properties that position the image on the page for PrintImage to process.
Figure properties processed by PrintImage:
PrintImage endeavours to process these properties just as MATLAB would, but probably doesn't get it exactly right every time.
In addition to specifying the position and orientation of the printed figure, you can use the print preview dialog box to place header information on every printed page. In a deployed application, PrintImage, rather than MATLAB, processes the header data to produce the header text. Page header properties supported by PrintImage:
- Date format
- Font angle
- Font name
- Font size
- Font weight
Like it does with the figure properties, PrintImage attempts to process header text just as MATLAB does.
- Deployed applications print to a printer using deployprint and print to files using print. MATLAB uses a single function, print, for both types of printing.
- Deployed applications print to a bitmap first, then send the bitmap to the printer. If the figure uses a vector renderer, such as Painters, the printed output may differ visually from the graphics displayed in the figure. Deployed application always print using a raster renderer.
- Deployed applications format the figure data and the header text separately. The MCR renders the figure data to a bitmap, and PrintImage superimposes the header text on the bitmap as it sends the bitmap to the printer. This may cause the header text to be placed in a slightly different position on the page in a deployed application.
Can't the Compiler substitute deployprint for print at compile time? No. The MATLAB Compiler, as a matter of policy, does not alter the executable code in any of the M-files that it packages for deployment. In part, we established this policy to ensure that the MATLAB Compiler does not introduce any bugs into your M-files, but we also recognize that this kind of substitution is impossible to implement with 100% accuracy. Calls to print could be hidden in strings passed to eval, or in anonymous function handles or passed in as function parameters at runtime. Rather than make promises we couldn't keep, we opted to require the use of isdeployed and deployprint as noted above.
Why does mlint warn about printdlg? This warning is out-of-date. In a future release it will be changed to indicate that deployed applications do support printdlg, but only the single-argument form of printdlg.
Why can't I print (using deployprint) to PDFs? Since deployprint uses print -dbmp to create the bitmap file, it explicitly ignores any output device specification passed in as an argument. However, you can use print -dpdf to create a PDF file in your deployed applications. For example:
switch KindOfPrinting case 'Interactive' if ~isdeployed print; else deployprint; end case 'ToFile' % Here, device should be a string such as 'pdf', and % outputFileName a name like 'output.pdf'. print(['-d' device], outputFileName); otherwise disp(['Unrecognized printing mode: ' KindOfPrinting]); end
Why do my figures look different when printed? Printed output may differ from on-screen graphics if the figure uses the Painters renderer. You can ensure your on-screen and printed graphics always use a raster renderer by setting the figure to use the Z-buffer renderer:
set(gcf, 'Renderer', 'zbuffer')
Or, you can preview the appearence of your figures in a deployed application by printing them (in MATLAB) to a bitmap, and then either viewing that bitmap on the screen or printing it. This command prints the current figure to a bitmap file named myfig.bmp.
print -dbmp myfig.bmp
How can I print from the menu bar or toolbar of a deployed application? The default figure menu bar in a deployed application contains a single entry File. At the end of this menu is the selection Print.... Selecting Print... invokes deployprint and PrintImage. On the figure toolbar all deployed applications display a button containing a printer icon. Pressing this button sends the associated figure to the printer using deployprint and PrintImage.
How can I print from a shared library? Either use the figure's menu or toolbar to print interactively, or write your own function that calls deployprint and include that function in your compiled application. For example, this myprint function sends the given figure (specified by figure number) to the given printer (specified by name):
function myprint(fig, printer) deployprint(['-f' num2str(fig)], ['-P' printer]); end
If you create this function in myprint.m and add it to your compiled application, you'll be able to call the myprint function from whatever code you link your shared library against.
When MATLAB abandons native mode Handle Graphics entirely, and supports Java-based printing on Microsoft Windows, deployprint and PrintImage will no longer be necessary. deployprint will simply call print (as it does now on Unix systems), and the printing-related mlint warnings will be modified or eliminated. Until then, however, you'll likely want to keep a copy of this posting handy if you deploy applications that target Microsoft Windows.
Future posts will cover troubleshooting a deployed application (overcoming common failures), debugging and managing and organizing your code for deployment; if you've got a topic you'd like to hear more about, please let me know. In the meantime, you can refer to the documentation for the MATLAB Compiler or post follow-up questions here.