| /******************************************************************************* |
| * Copyright (c) 2000, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.swt.printing; |
| |
| |
| import org.eclipse.swt.*; |
| import org.eclipse.swt.graphics.*; |
| import org.eclipse.swt.internal.*; |
| import org.eclipse.swt.internal.cairo.*; |
| import org.eclipse.swt.internal.gtk.*; |
| |
| /** |
| * Instances of this class are used to print to a printer. |
| * Applications create a GC on a printer using <code>new GC(printer)</code> |
| * and then draw on the printer GC using the usual graphics calls. |
| * <p> |
| * A <code>Printer</code> object may be constructed by providing |
| * a <code>PrinterData</code> object which identifies the printer. |
| * A <code>PrintDialog</code> presents a print dialog to the user |
| * and returns an initialized instance of <code>PrinterData</code>. |
| * Alternatively, calling <code>new Printer()</code> will construct a |
| * printer object for the user's default printer. |
| * </p><p> |
| * Application code must explicitly invoke the <code>Printer.dispose()</code> |
| * method to release the operating system resources managed by each instance |
| * when those instances are no longer required. |
| * </p> |
| * |
| * @see PrinterData |
| * @see PrintDialog |
| * @see <a href="http://www.eclipse.org/swt/snippets/#printing">Printing snippets</a> |
| * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> |
| */ |
| public final class Printer extends Device { |
| static PrinterData [] printerList; |
| static long /*int*/ findPrinter; |
| static PrinterData findData; |
| |
| PrinterData data; |
| long /*int*/ printer; |
| long /*int*/ printJob; |
| long /*int*/ settings; |
| long /*int*/ pageSetup; |
| long /*int*/ surface; |
| long /*int*/ cairo; |
| |
| /** |
| * whether or not a GC was created for this printer |
| */ |
| boolean isGCCreated = false; |
| |
| static byte [] settingsData; |
| static int start, end; |
| |
| static final String GTK_LPR_BACKEND = "GtkPrintBackendLpr"; //$NON-NLS-1$ |
| static final String GTK_FILE_BACKEND = "GtkPrintBackendFile"; //$NON-NLS-1$ |
| |
| static boolean disablePrinting = OS.IsWin32 || System.getProperty("org.eclipse.swt.internal.gtk.disablePrinting") != null; //$NON-NLS-1$ |
| |
| static void gtk_init() { |
| if (!GTK.gtk_init_check (new long /*int*/ [] {0}, null)) { |
| SWT.error (SWT.ERROR_NO_HANDLES, null, " [gtk_init_check() failed]"); |
| } |
| } |
| |
| /** |
| * Returns an array of <code>PrinterData</code> objects |
| * representing all available printers. If there are no |
| * printers, the array will be empty. |
| * |
| * @return an array of PrinterData objects representing the available printers |
| */ |
| public static PrinterData[] getPrinterList() { |
| printerList = new PrinterData [0]; |
| if (disablePrinting) { |
| return printerList; |
| } |
| gtk_init(); |
| Callback printerCallback = new Callback(Printer.class, "GtkPrinterFunc_List", 2); //$NON-NLS-1$ |
| long /*int*/ GtkPrinterFunc_List = printerCallback.getAddress(); |
| if (GtkPrinterFunc_List == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); |
| GTK.gtk_enumerate_printers(GtkPrinterFunc_List, 0, 0, true); |
| /* |
| * This call to gdk_threads_leave() is a temporary work around |
| * to avoid deadlocks when gdk_threads_init() is called by native |
| * code outside of SWT (i.e AWT, etc). It ensures that the current |
| * thread leaves the GTK lock acquired by the function above. |
| */ |
| GDK.gdk_threads_leave(); |
| printerCallback.dispose (); |
| return printerList; |
| } |
| |
| static long /*int*/ GtkPrinterFunc_List (long /*int*/ printer, long /*int*/ user_data) { |
| int length = printerList.length; |
| PrinterData [] newList = new PrinterData [length + 1]; |
| System.arraycopy (printerList, 0, newList, 0, length); |
| printerList = newList; |
| printerList [length] = printerDataFromGtkPrinter(printer); |
| /* |
| * Bug in GTK. While performing a gtk_enumerate_printers(), GTK finds all of the |
| * available printers from each backend and can hang. If a backend requires more |
| * time to gather printer info, GTK will start an event loop waiting for a done |
| * signal before continuing. For the Lpr backend, GTK does not send a done signal |
| * which means the event loop never ends. The fix is to check to see if the driver |
| * is of type Lpr, and stop the enumeration, which exits the event loop. |
| */ |
| if (printerList[length].driver.equals (GTK_LPR_BACKEND)) return 1; |
| return 0; |
| } |
| |
| /** |
| * Returns a <code>PrinterData</code> object representing |
| * the default printer or <code>null</code> if there is no |
| * default printer. |
| * |
| * @return the default printer data or null |
| * |
| * @since 2.1 |
| */ |
| public static PrinterData getDefaultPrinterData() { |
| findData = null; |
| if (disablePrinting) { |
| return null; |
| } |
| gtk_init(); |
| Callback printerCallback = new Callback(Printer.class, "GtkPrinterFunc_Default", 2); //$NON-NLS-1$ |
| long /*int*/ GtkPrinterFunc_Default = printerCallback.getAddress(); |
| if (GtkPrinterFunc_Default == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); |
| GTK.gtk_enumerate_printers(GtkPrinterFunc_Default, 0, 0, true); |
| /* |
| * This call to gdk_threads_leave() is a temporary work around |
| * to avoid deadlocks when gdk_threads_init() is called by native |
| * code outside of SWT (i.e AWT, etc). It ensures that the current |
| * thread leaves the GTK lock acquired by the function above. |
| */ |
| GDK.gdk_threads_leave(); |
| printerCallback.dispose (); |
| return findData; |
| } |
| |
| static long /*int*/ GtkPrinterFunc_Default (long /*int*/ printer, long /*int*/ user_data) { |
| if (GTK.gtk_printer_is_default(printer)) { |
| findData = printerDataFromGtkPrinter(printer); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static long /*int*/ gtkPrinterFromPrinterData(PrinterData data) { |
| gtk_init(); |
| Callback printerCallback = new Callback(Printer.class, "GtkPrinterFunc_FindNamedPrinter", 2); //$NON-NLS-1$ |
| long /*int*/ GtkPrinterFunc_FindNamedPrinter = printerCallback.getAddress(); |
| if (GtkPrinterFunc_FindNamedPrinter == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); |
| findPrinter = 0; |
| findData = data; |
| GTK.gtk_enumerate_printers(GtkPrinterFunc_FindNamedPrinter, 0, 0, true); |
| /* |
| * This call to gdk_threads_leave() is a temporary work around |
| * to avoid deadlocks when gdk_threads_init() is called by native |
| * code outside of SWT (i.e AWT, etc). It ensures that the current |
| * thread leaves the GTK lock acquired by the function above. |
| */ |
| GDK.gdk_threads_leave(); |
| printerCallback.dispose (); |
| return findPrinter; |
| } |
| |
| static long /*int*/ GtkPrinterFunc_FindNamedPrinter (long /*int*/ printer, long /*int*/ user_data) { |
| PrinterData pd = printerDataFromGtkPrinter(printer); |
| if ((pd.driver.equals(findData.driver) && pd.name.equals(findData.name)) |
| || (pd.driver.equals(GTK_FILE_BACKEND)) && findData.printToFile && findData.driver == null && findData.name == null) { |
| // TODO: GTK_FILE_BACKEND is not GTK API (see gtk bug 345590) |
| findPrinter = printer; |
| OS.g_object_ref(printer); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static PrinterData printerDataFromGtkPrinter(long /*int*/ printer) { |
| long /*int*/ backend = GTK.gtk_printer_get_backend(printer); |
| long /*int*/ address = OS.G_OBJECT_TYPE_NAME(backend); |
| int length = C.strlen (address); |
| byte [] buffer = new byte [length]; |
| C.memmove (buffer, address, length); |
| String backendType = new String (Converter.mbcsToWcs (buffer)); |
| |
| address = GTK.gtk_printer_get_name (printer); |
| length = C.strlen (address); |
| buffer = new byte [length]; |
| C.memmove (buffer, address, length); |
| String name = new String (Converter.mbcsToWcs (buffer)); |
| |
| return new PrinterData (backendType, name); |
| } |
| |
| /* |
| * Restore printer settings and page_setup data from data. |
| */ |
| static void restore(byte [] data, long /*int*/ settings, long /*int*/ page_setup) { |
| settingsData = data; |
| start = end = 0; |
| while (end < settingsData.length && settingsData[end] != 0) { |
| start = end; |
| while (end < settingsData.length && settingsData[end] != 0) end++; |
| end++; |
| byte [] keyBuffer = new byte [end - start]; |
| System.arraycopy (settingsData, start, keyBuffer, 0, keyBuffer.length); |
| start = end; |
| while (end < settingsData.length && settingsData[end] != 0) end++; |
| end++; |
| byte [] valueBuffer = new byte [end - start]; |
| System.arraycopy (settingsData, start, valueBuffer, 0, valueBuffer.length); |
| GTK.gtk_print_settings_set(settings, keyBuffer, valueBuffer); |
| if (DEBUG) System.out.println(new String (Converter.mbcsToWcs (keyBuffer))+": "+new String (Converter.mbcsToWcs (valueBuffer))); |
| } |
| end++; // skip extra null terminator |
| |
| /* Retrieve stored page_setup data. |
| * Note that page_setup properties must be stored (in PrintDialog) and restored (here) in the same order. |
| */ |
| GTK.gtk_page_setup_set_orientation(page_setup, restoreInt("orientation")); //$NON-NLS-1$ |
| GTK.gtk_page_setup_set_top_margin(page_setup, restoreDouble("top_margin"), GTK.GTK_UNIT_MM); //$NON-NLS-1$ |
| GTK.gtk_page_setup_set_bottom_margin(page_setup, restoreDouble("bottom_margin"), GTK.GTK_UNIT_MM); //$NON-NLS-1$ |
| GTK.gtk_page_setup_set_left_margin(page_setup, restoreDouble("left_margin"), GTK.GTK_UNIT_MM); //$NON-NLS-1$ |
| GTK.gtk_page_setup_set_right_margin(page_setup, restoreDouble("right_margin"), GTK.GTK_UNIT_MM); //$NON-NLS-1$ |
| byte [] name = restoreBytes("paper_size_name", true); //$NON-NLS-1$ |
| byte [] display_name = restoreBytes("paper_size_display_name", true); //$NON-NLS-1$ |
| byte [] ppd_name = restoreBytes("paper_size_ppd_name", true); //$NON-NLS-1$ |
| double width = restoreDouble("paper_size_width"); //$NON-NLS-1$ |
| double height = restoreDouble("paper_size_height"); //$NON-NLS-1$ |
| boolean custom = restoreBoolean("paper_size_is_custom"); //$NON-NLS-1$ |
| long /*int*/ paper_size = 0; |
| if (custom) { |
| if (ppd_name.length > 0) { |
| paper_size = GTK.gtk_paper_size_new_from_ppd(ppd_name, display_name, width, height); |
| } else { |
| paper_size = GTK.gtk_paper_size_new_custom(name, display_name, width, height, GTK.GTK_UNIT_MM); |
| } |
| } else { |
| paper_size = GTK.gtk_paper_size_new(name); |
| } |
| GTK.gtk_page_setup_set_paper_size(page_setup, paper_size); |
| GTK.gtk_paper_size_free(paper_size); |
| } |
| |
| static byte [] uriFromFilename(String filename) { |
| if (filename == null) return null; |
| int length = filename.length(); |
| if (length == 0) return null; |
| char[] chars = new char[length]; |
| filename.getChars(0, length, chars, 0); |
| long /*int*/[] error = new long /*int*/[1]; |
| long /*int*/ utf8Ptr = OS.g_utf16_to_utf8(chars, chars.length, null, null, error); |
| if (error[0] != 0 || utf8Ptr == 0) return null; |
| long /*int*/ localePtr = OS.g_filename_from_utf8(utf8Ptr, -1, null, null, error); |
| OS.g_free(utf8Ptr); |
| if (error[0] != 0 || localePtr == 0) return null; |
| long /*int*/ uriPtr = OS.g_filename_to_uri(localePtr, 0, error); |
| OS.g_free(localePtr); |
| if (error[0] != 0 || uriPtr == 0) return null; |
| length = C.strlen(uriPtr); |
| byte[] uri = new byte[length + 1]; |
| C.memmove (uri, uriPtr, length); |
| OS.g_free(uriPtr); |
| return uri; |
| } |
| |
| static DeviceData checkNull (PrinterData data) { |
| if (data == null) data = new PrinterData(); |
| if (data.driver == null || data.name == null) { |
| PrinterData defaultData = null; |
| if (data.printToFile) { |
| long /*int*/ filePrinter = gtkPrinterFromPrinterData(data); |
| if (filePrinter != 0) { |
| defaultData = printerDataFromGtkPrinter(filePrinter); |
| OS.g_object_unref(filePrinter); |
| } |
| } |
| if (defaultData == null) { |
| defaultData = getDefaultPrinterData(); |
| if (defaultData == null) SWT.error(SWT.ERROR_NO_HANDLES); |
| } |
| data.driver = defaultData.driver; |
| data.name = defaultData.name; |
| } |
| return data; |
| } |
| |
| /** |
| * Constructs a new printer representing the default printer. |
| * <p> |
| * Note: You must dispose the printer when it is no longer required. |
| * </p> |
| * |
| * @exception SWTError <ul> |
| * <li>ERROR_NO_HANDLES - if there is no valid default printer |
| * </ul> |
| * |
| * @see Device#dispose |
| */ |
| public Printer() { |
| this(null); |
| } |
| |
| /** |
| * Constructs a new printer given a <code>PrinterData</code> |
| * object representing the desired printer. If the argument |
| * is null, then the default printer will be used. |
| * <p> |
| * Note: You must dispose the printer when it is no longer required. |
| * </p> |
| * |
| * @param data the printer data for the specified printer, or null to use the default printer |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the specified printer data does not represent a valid printer |
| * </ul> |
| * @exception SWTError <ul> |
| * <li>ERROR_NO_HANDLES - if there are no valid printers |
| * </ul> |
| * |
| * @see Device#dispose |
| */ |
| public Printer(PrinterData data) { |
| super(checkNull(data)); |
| } |
| |
| static int restoreInt(String key) { |
| byte [] value = restoreBytes(key, false); |
| return Integer.parseInt(new String(value)); |
| } |
| |
| static double restoreDouble(String key) { |
| byte [] value = restoreBytes(key, false); |
| return Double.parseDouble(new String(value)); |
| } |
| |
| static boolean restoreBoolean(String key) { |
| byte [] value = restoreBytes(key, false); |
| return Boolean.valueOf(new String(value)).booleanValue(); |
| } |
| |
| static byte [] restoreBytes(String key, boolean nullTerminate) { |
| //get key |
| start = end; |
| while (end < settingsData.length && settingsData[end] != 0) end++; |
| end++; |
| byte [] keyBuffer = new byte [end - start]; |
| System.arraycopy (settingsData, start, keyBuffer, 0, keyBuffer.length); |
| |
| //get value |
| start = end; |
| while (end < settingsData.length && settingsData[end] != 0) end++; |
| int length = end - start; |
| end++; |
| if (nullTerminate) length++; |
| byte [] valueBuffer = new byte [length]; |
| System.arraycopy (settingsData, start, valueBuffer, 0, length); |
| |
| if (DEBUG) System.out.println(new String (Converter.mbcsToWcs (keyBuffer))+": "+new String (Converter.mbcsToWcs (valueBuffer))); |
| |
| return valueBuffer; |
| } |
| |
| /** |
| * Invokes platform specific functionality to allocate a new GC handle. |
| * <p> |
| * <b>IMPORTANT:</b> This method is <em>not</em> part of the public |
| * API for <code>Printer</code>. It is marked public only so that it |
| * can be shared within the packages provided by SWT. It is not |
| * available on all platforms, and should never be called from |
| * application code. |
| * </p> |
| * |
| * @param data the platform specific GC data |
| * @return the platform specific GC handle |
| * |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| @Override |
| public long /*int*/ internal_new_GC(GCData data) { |
| long /*int*/ drawable = 0; |
| long /*int*/ gc = cairo; |
| if (gc == 0) SWT.error (SWT.ERROR_NO_HANDLES); |
| if (data != null) { |
| if (isGCCreated) SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; |
| if ((data.style & mask) == 0) { |
| data.style |= SWT.LEFT_TO_RIGHT; |
| } |
| data.device = this; |
| data.drawable = drawable; |
| data.backgroundRGBA = getSystemColor (SWT.COLOR_WHITE).handle; |
| data.foregroundRGBA = getSystemColor (SWT.COLOR_BLACK).handle; |
| data.font = getSystemFont (); |
| Point dpi = getDPI(), screenDPI = getIndependentDPI(); |
| data.width = (int)(GTK.gtk_page_setup_get_paper_width (pageSetup, GTK.GTK_UNIT_POINTS) * dpi.x / screenDPI.x); |
| data.height = (int)(GTK.gtk_page_setup_get_paper_height (pageSetup, GTK.GTK_UNIT_POINTS) * dpi.y / screenDPI.y); |
| if (cairo == 0) SWT.error(SWT.ERROR_NO_HANDLES); |
| Cairo.cairo_identity_matrix(cairo); |
| double printX = GTK.gtk_page_setup_get_left_margin(pageSetup, GTK.GTK_UNIT_POINTS); |
| double printY = GTK.gtk_page_setup_get_top_margin(pageSetup, GTK.GTK_UNIT_POINTS); |
| Cairo.cairo_translate(cairo, printX, printY); |
| Cairo.cairo_scale(cairo, screenDPI.x / (float)dpi.x, screenDPI.y / (float)dpi.y); |
| double[] matrix = new double[6]; |
| Cairo.cairo_get_matrix(cairo, matrix); |
| data.identity = matrix; |
| data.cairo = cairo; |
| isGCCreated = true; |
| } |
| return gc; |
| } |
| |
| /** |
| * Invokes platform specific functionality to dispose a GC handle. |
| * <p> |
| * <b>IMPORTANT:</b> This method is <em>not</em> part of the public |
| * API for <code>Printer</code>. It is marked public only so that it |
| * can be shared within the packages provided by SWT. It is not |
| * available on all platforms, and should never be called from |
| * application code. |
| * </p> |
| * |
| * @param hDC the platform specific GC handle |
| * @param data the platform specific GC data |
| * |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| @Override |
| public void internal_dispose_GC(long /*int*/ hDC, GCData data) { |
| if (data != null) isGCCreated = false; |
| } |
| |
| /** |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| @Override |
| public boolean isAutoScalable() { |
| return false; |
| } |
| |
| /** |
| * Starts a print job and returns true if the job started successfully |
| * and false otherwise. |
| * <p> |
| * This must be the first method called to initiate a print job, |
| * followed by any number of startPage/endPage calls, followed by |
| * endJob. Calling startPage, endPage, or endJob before startJob |
| * will result in undefined behavior. |
| * </p> |
| * |
| * @param jobName the name of the print job to start |
| * @return true if the job started successfully and false otherwise. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> |
| * </ul> |
| * |
| * @see #startPage |
| * @see #endPage |
| * @see #endJob |
| */ |
| public boolean startJob(String jobName) { |
| checkDevice(); |
| byte [] buffer = Converter.wcsToMbcs (jobName, true); |
| printJob = GTK.gtk_print_job_new (buffer, printer, settings, pageSetup); |
| if (printJob == 0) return false; |
| surface = GTK.gtk_print_job_get_surface(printJob, null); |
| if (surface == 0) { |
| OS.g_object_unref(printJob); |
| printJob = 0; |
| return false; |
| } |
| cairo = Cairo.cairo_create(surface); |
| if (cairo == 0) { |
| OS.g_object_unref(printJob); |
| printJob = 0; |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Destroys the printer handle. |
| * This method is called internally by the dispose |
| * mechanism of the <code>Device</code> class. |
| */ |
| @Override |
| protected void destroy () { |
| if (printer != 0) OS.g_object_unref (printer); |
| if (settings != 0) OS.g_object_unref (settings); |
| if (pageSetup != 0) OS.g_object_unref (pageSetup); |
| if (cairo != 0) Cairo.cairo_destroy (cairo); |
| if (printJob != 0) OS.g_object_unref (printJob); |
| printer = settings = pageSetup = cairo = printJob = 0; |
| } |
| |
| /** |
| * Ends the current print job. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> |
| * </ul> |
| * |
| * @see #startJob |
| * @see #startPage |
| * @see #endPage |
| */ |
| public void endJob() { |
| checkDevice(); |
| if (printJob == 0) return; |
| Cairo.cairo_surface_finish(surface); |
| GTK.gtk_print_job_send(printJob, 0, 0, 0); |
| OS.g_object_unref(printJob); |
| printJob = 0; |
| } |
| |
| /** |
| * Cancels a print job in progress. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> |
| * </ul> |
| */ |
| public void cancelJob() { |
| checkDevice(); |
| if (printJob == 0) return; |
| //TODO: Need to implement (waiting on gtk bug 339323) |
| Cairo.cairo_surface_finish(surface); |
| OS.g_object_unref(printJob); |
| printJob = 0; |
| } |
| |
| /** |
| * Starts a page and returns true if the page started successfully |
| * and false otherwise. |
| * <p> |
| * After calling startJob, this method may be called any number of times |
| * along with a matching endPage. |
| * </p> |
| * |
| * @return true if the page started successfully and false otherwise. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> |
| * </ul> |
| * |
| * @see #endPage |
| * @see #startJob |
| * @see #endJob |
| */ |
| public boolean startPage() { |
| checkDevice(); |
| if (printJob == 0) return false; |
| double width = GTK.gtk_page_setup_get_paper_width (pageSetup, GTK.GTK_UNIT_POINTS); |
| double height = GTK.gtk_page_setup_get_paper_height (pageSetup, GTK.GTK_UNIT_POINTS); |
| int type = Cairo.cairo_surface_get_type (surface); |
| switch (type) { |
| case Cairo.CAIRO_SURFACE_TYPE_PS: |
| Cairo.cairo_ps_surface_set_size (surface, width, height); |
| break; |
| case Cairo.CAIRO_SURFACE_TYPE_PDF: |
| Cairo.cairo_pdf_surface_set_size (surface, width, height); |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Ends the current page. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> |
| * </ul> |
| * |
| * @see #startPage |
| * @see #startJob |
| * @see #endJob |
| */ |
| public void endPage() { |
| checkDevice(); |
| Cairo.cairo_show_page(cairo); |
| } |
| |
| /** |
| * Returns a point whose x coordinate is the horizontal |
| * dots per inch of the printer, and whose y coordinate |
| * is the vertical dots per inch of the printer. |
| * |
| * @return the horizontal and vertical DPI |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> |
| * </ul> |
| */ |
| @Override |
| public Point getDPI() { |
| checkDevice(); |
| int resolution = GTK.gtk_print_settings_get_resolution(settings); |
| if (DEBUG) System.out.println("print_settings.resolution=" + resolution); |
| //TODO: use new api for get x resolution and get y resolution |
| if (resolution == 0) return new Point(72, 72); |
| return new Point(resolution, resolution); |
| } |
| |
| /** |
| * Returns a rectangle describing the receiver's size and location. |
| * <p> |
| * For a printer, this is the size of the physical page, in pixels. |
| * </p> |
| * |
| * @return the bounding rectangle |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> |
| * </ul> |
| * |
| * @see #getClientArea |
| * @see #computeTrim |
| */ |
| @Override |
| public Rectangle getBounds() { |
| checkDevice(); |
| Point dpi = getDPI(), screenDPI = getIndependentDPI(); |
| double width = GTK.gtk_page_setup_get_paper_width (pageSetup, GTK.GTK_UNIT_POINTS) * dpi.x / screenDPI.x; |
| double height = GTK.gtk_page_setup_get_paper_height (pageSetup, GTK.GTK_UNIT_POINTS) * dpi.y / screenDPI.y; |
| return new Rectangle(0, 0, (int) width, (int) height); |
| } |
| |
| /** |
| * Returns a rectangle which describes the area of the |
| * receiver which is capable of displaying data. |
| * <p> |
| * For a printer, this is the size of the printable area |
| * of the page, in pixels. |
| * </p> |
| * |
| * @return the client area |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> |
| * </ul> |
| * |
| * @see #getBounds |
| * @see #computeTrim |
| */ |
| @Override |
| public Rectangle getClientArea() { |
| checkDevice(); |
| Point dpi = getDPI(), screenDPI = getIndependentDPI(); |
| double width = GTK.gtk_page_setup_get_page_width(pageSetup, GTK.GTK_UNIT_POINTS) * dpi.x / screenDPI.x; |
| double height = GTK.gtk_page_setup_get_page_height(pageSetup, GTK.GTK_UNIT_POINTS) * dpi.y / screenDPI.y; |
| return new Rectangle(0, 0, (int) width, (int) height); |
| } |
| |
| Point getIndependentDPI () { |
| return new Point(72, 72); |
| } |
| |
| /** |
| * Given a <em>client area</em> (as described by the arguments), |
| * returns a rectangle, relative to the client area's coordinates, |
| * that is the client area expanded by the printer's trim (or minimum margins). |
| * <p> |
| * Most printers have a minimum margin on each edge of the paper where the |
| * printer device is unable to print. This margin is known as the "trim." |
| * This method can be used to calculate the printer's minimum margins |
| * by passing in a client area of 0, 0, 0, 0 and then using the resulting |
| * x and y coordinates (which will be <= 0) to determine the minimum margins |
| * for the top and left edges of the paper, and the resulting width and height |
| * (offset by the resulting x and y) to determine the minimum margins for the |
| * bottom and right edges of the paper, as follows: |
| * <ul> |
| * <li>The left trim width is -x pixels</li> |
| * <li>The top trim height is -y pixels</li> |
| * <li>The right trim width is (x + width) pixels</li> |
| * <li>The bottom trim height is (y + height) pixels</li> |
| * </ul> |
| * </p> |
| * |
| * @param x the x coordinate of the client area |
| * @param y the y coordinate of the client area |
| * @param width the width of the client area |
| * @param height the height of the client area |
| * @return a rectangle, relative to the client area's coordinates, that is |
| * the client area expanded by the printer's trim (or minimum margins) |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> |
| * </ul> |
| * |
| * @see #getBounds |
| * @see #getClientArea |
| */ |
| public Rectangle computeTrim(int x, int y, int width, int height) { |
| checkDevice(); |
| Point dpi = getDPI(), screenDPI = getIndependentDPI(); |
| double printWidth = GTK.gtk_page_setup_get_page_width(pageSetup, GTK.GTK_UNIT_POINTS) * dpi.x / screenDPI.x; |
| double printHeight = GTK.gtk_page_setup_get_page_height(pageSetup, GTK.GTK_UNIT_POINTS) * dpi.y / screenDPI.y; |
| double paperWidth = GTK.gtk_page_setup_get_paper_width (pageSetup, GTK.GTK_UNIT_POINTS) * dpi.x / screenDPI.x; |
| double paperHeight = GTK.gtk_page_setup_get_paper_height (pageSetup, GTK.GTK_UNIT_POINTS) * dpi.y / screenDPI.y; |
| double printX = -GTK.gtk_page_setup_get_left_margin(pageSetup, GTK.GTK_UNIT_POINTS) * dpi.x / screenDPI.x; |
| double printY = -GTK.gtk_page_setup_get_top_margin(pageSetup, GTK.GTK_UNIT_POINTS) * dpi.y / screenDPI.y; |
| double hTrim = paperWidth - printWidth; |
| double vTrim = paperHeight - printHeight; |
| return new Rectangle(x + (int)printX, y + (int)printY, width + (int)hTrim, height + (int)vTrim); |
| } |
| |
| /** |
| * Creates the printer handle. |
| * This method is called internally by the instance creation |
| * mechanism of the <code>Device</code> class. |
| * @param deviceData the device data |
| */ |
| @Override |
| protected void create(DeviceData deviceData) { |
| this.data = (PrinterData)deviceData; |
| if (disablePrinting) SWT.error(SWT.ERROR_NO_HANDLES); |
| printer = gtkPrinterFromPrinterData(data); |
| if (printer == 0) SWT.error(SWT.ERROR_NO_HANDLES); |
| } |
| |
| /** |
| * Initializes any internal resources needed by the |
| * device. |
| * <p> |
| * This method is called after <code>create</code>. |
| * </p><p> |
| * If subclasses reimplement this method, they must |
| * call the <code>super</code> implementation. |
| * </p> |
| * |
| * @see #create |
| */ |
| @Override |
| protected void init() { |
| settings = GTK.gtk_print_settings_new(); |
| pageSetup = GTK.gtk_page_setup_new(); |
| if (data.otherData != null) { |
| restore(data.otherData, settings, pageSetup); |
| } |
| |
| /* Set values of print_settings and page_setup from PrinterData. */ |
| if (data.printToFile && data.fileName != null) { |
| byte [] uri = uriFromFilename(data.fileName); |
| if (uri != null) { |
| GTK.gtk_print_settings_set(settings, GTK.GTK_PRINT_SETTINGS_OUTPUT_URI, uri); |
| } |
| } |
| GTK.gtk_print_settings_set_n_copies(settings, data.copyCount); |
| GTK.gtk_print_settings_set_collate(settings, data.collate); |
| if (data.duplex != SWT.DEFAULT) { |
| int duplex = data.duplex == PrinterData.DUPLEX_LONG_EDGE ? GTK.GTK_PRINT_DUPLEX_HORIZONTAL |
| : data.duplex == PrinterData.DUPLEX_SHORT_EDGE ? GTK.GTK_PRINT_DUPLEX_VERTICAL |
| : GTK.GTK_PRINT_DUPLEX_SIMPLEX; |
| GTK.gtk_print_settings_set_duplex (settings, duplex); |
| /* |
| * Bug in GTK. The cups backend only looks at the value |
| * of the non-API field cups-Duplex in the print_settings. |
| * The fix is to manually set cups-Duplex to Tumble or NoTumble. |
| */ |
| String cupsDuplexType = null; |
| if (duplex == GTK.GTK_PRINT_DUPLEX_HORIZONTAL) cupsDuplexType = "DuplexNoTumble"; |
| else if (duplex == GTK.GTK_PRINT_DUPLEX_VERTICAL) cupsDuplexType = "DuplexTumble"; |
| if (cupsDuplexType != null) { |
| byte [] keyBuffer = Converter.wcsToMbcs ("cups-Duplex", true); |
| byte [] valueBuffer = Converter.wcsToMbcs (cupsDuplexType, true); |
| GTK.gtk_print_settings_set(settings, keyBuffer, valueBuffer); |
| } |
| } |
| int orientation = data.orientation == PrinterData.LANDSCAPE ? GTK.GTK_PAGE_ORIENTATION_LANDSCAPE : GTK.GTK_PAGE_ORIENTATION_PORTRAIT; |
| GTK.gtk_page_setup_set_orientation(pageSetup, orientation); |
| GTK.gtk_print_settings_set_orientation(settings, orientation); |
| super.init (); |
| } |
| |
| /** |
| * Returns a <code>PrinterData</code> object representing the |
| * target printer for this print job. |
| * |
| * @return a PrinterData object describing the receiver |
| */ |
| public PrinterData getPrinterData() { |
| checkDevice(); |
| return data; |
| } |
| |
| } |