changeset 91:b58ec55ce70d

package printing
author Frank Benoit <benoit@tionex.de>
date Thu, 17 Jan 2008 15:44:59 +0100
parents 9ba02d7fb226
children ddb19cb18d2e
files dwt/internal/c/atk.d dwt/internal/c/cairo.d dwt/internal/c/cairo_pdf.d dwt/internal/c/cairo_ps.d dwt/internal/cairo/Cairo.d dwt/internal/gtk/OS.d dwt/printing/PrintDialog.d dwt/printing/Printer.d dwt/printing/PrinterData.d
diffstat 9 files changed, 1368 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/dwt/internal/c/atk.d	Thu Jan 17 09:03:45 2008 +0100
+++ b/dwt/internal/c/atk.d	Thu Jan 17 15:44:59 2008 +0100
@@ -1,6 +1,6 @@
 /******************************************************************************
     Based on the generated files from the BCD tool
-    modified by: Frank Benoit <keinfarbton@googlemail.com>
+    modified by: Frank Benoit <benoit@tionex.de>
 ******************************************************************************/
 module dwt.internal.c.atk;
 
--- a/dwt/internal/c/cairo.d	Thu Jan 17 09:03:45 2008 +0100
+++ b/dwt/internal/c/cairo.d	Thu Jan 17 15:44:59 2008 +0100
@@ -1,6 +1,6 @@
 /******************************************************************************
     Based on the generated files from the BCD tool
-    modified by: Frank Benoit <keinfarbton@googlemail.com>
+    modified by: Frank Benoit <benoit@tionex.de>
 ******************************************************************************/
 module dwt.internal.c.cairo;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/c/cairo_pdf.d	Thu Jan 17 15:44:59 2008 +0100
@@ -0,0 +1,30 @@
+/******************************************************************************
+    Based on the generated files from the BCD tool
+    modified by: Frank Benoit <benoit@tionex.de>
+******************************************************************************/
+module dwt.internal.c.cairo_pdf;
+public import dwt.internal.c.glib_object;
+public import dwt.internal.c.cairo;
+
+align(4):
+
+alias int function(void *, char *, uint) _BCD_func__480;
+alias int function(void *, char *, uint) _BCD_func__479;
+alias void function(void *) _BCD_func__484;
+version(DYNLINK){
+extern (C) void function(void *, double, double)cairo_pdf_surface_set_size;
+extern (C) void * function(_BCD_func__480, void *, double, double)cairo_pdf_surface_create_for_stream;
+extern (C) void * function(char *, double, double)cairo_pdf_surface_create;
+
+
+Symbol[] symbols = [
+    { "cairo_pdf_surface_set_size",  cast(void**)& cairo_pdf_surface_set_size},
+    { "cairo_pdf_surface_create_for_stream",  cast(void**)& cairo_pdf_surface_create_for_stream},
+    { "cairo_pdf_surface_create",  cast(void**)& cairo_pdf_surface_create},
+];
+
+} else { // version(DYNLINK)
+extern (C) void cairo_pdf_surface_set_size(void *, double, double);
+extern (C) void * cairo_pdf_surface_create_for_stream(_BCD_func__480, void *, double, double);
+extern (C) void * cairo_pdf_surface_create(char *, double, double);
+} // version(DYNLINK)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/c/cairo_ps.d	Thu Jan 17 15:44:59 2008 +0100
@@ -0,0 +1,51 @@
+/******************************************************************************
+    Based on the generated files from the BCD tool
+    modified by: Frank Benoit <benoit@tionex.de>
+******************************************************************************/
+module dwt.internal.c.cairo_ps;
+public import dwt.internal.c.glib_object;
+public import dwt.internal.c.cairo;
+
+align(4):
+
+alias int function(void *, char *, uint) _BCD_func__879;
+alias int function(void *) _BCD_func__133;
+alias int function(void *, long *, int) _BCD_func__135;
+alias int function(void *, char *, uint) _BCD_func__137;
+alias int function(void *, char *, uint) _BCD_func__139;
+alias void function(void *) _BCD_func__734;
+alias int function(void * *, char *) _BCD_func__735;
+alias int function(char *, char * * *, uint *) _BCD_func__736;
+alias int function(void *, char *, char *, char *, char *) _BCD_func__737;
+alias int function(__gconv_step *, __gconv_step_data *, void *, char *, char * *, char *, char * *, uint *) _BCD_func__738;
+alias void function(__gconv_step *) _BCD_func__739;
+alias int function(__gconv_step *) _BCD_func__740;
+alias uint function(__gconv_step *, char) _BCD_func__741;
+alias int function(__gconv_step *, __gconv_step_data *, char * *, char *, char * *, uint *, int, int) _BCD_func__742;
+alias int function(void *, char *, uint) _BCD_func__878;
+version(DYNLINK){
+extern (C) void function(void *)cairo_ps_surface_dsc_begin_page_setup;
+extern (C) void function(void *)cairo_ps_surface_dsc_begin_setup;
+extern (C) void function(void *, char *)cairo_ps_surface_dsc_comment;
+extern (C) void function(void *, double, double)cairo_ps_surface_set_size;
+extern (C) void * function(_BCD_func__879, void *, double, double)cairo_ps_surface_create_for_stream;
+extern (C) void * function(char *, double, double)cairo_ps_surface_create;
+
+
+Symbol[] symbols = [
+    { "cairo_ps_surface_dsc_begin_page_setup",  cast(void**)& cairo_ps_surface_dsc_begin_page_setup},
+    { "cairo_ps_surface_dsc_begin_setup",  cast(void**)& cairo_ps_surface_dsc_begin_setup},
+    { "cairo_ps_surface_dsc_comment",  cast(void**)& cairo_ps_surface_dsc_comment},
+    { "cairo_ps_surface_set_size",  cast(void**)& cairo_ps_surface_set_size},
+    { "cairo_ps_surface_create_for_stream",  cast(void**)& cairo_ps_surface_create_for_stream},
+    { "cairo_ps_surface_create",  cast(void**)& cairo_ps_surface_create},
+];
+
+} else { // version(DYNLINK)
+extern (C) void cairo_ps_surface_dsc_begin_page_setup(void *);
+extern (C) void cairo_ps_surface_dsc_begin_setup(void *);
+extern (C) void cairo_ps_surface_dsc_comment(void *, char *);
+extern (C) void cairo_ps_surface_set_size(void *, double, double);
+extern (C) void * cairo_ps_surface_create_for_stream(_BCD_func__879, void *, double, double);
+extern (C) void * cairo_ps_surface_create(char *, double, double);
+} // version(DYNLINK)
--- a/dwt/internal/cairo/Cairo.d	Thu Jan 17 09:03:45 2008 +0100
+++ b/dwt/internal/cairo/Cairo.d	Thu Jan 17 15:44:59 2008 +0100
@@ -1,4 +1,4 @@
-/* ***** BEGIN LICENSE BLOCK *****
+/* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
@@ -22,6 +22,8 @@
 
 import  dwt.internal.c.cairo;
 import  dwt.internal.c.cairo_xlib;
+import  dwt.internal.c.cairo_ps;
+import  dwt.internal.c.cairo_pdf;
 import  dwt.internal.c.Xlib;
 import  dwt.internal.Platform;
 
@@ -221,8 +223,8 @@
     mixin ForwardGtkCairoCFunc!(.cairo_pattern_set_extend);
     mixin ForwardGtkCairoCFunc!(.cairo_pattern_set_filter);
     mixin ForwardGtkCairoCFunc!(.cairo_pattern_set_matrix);
-    //mixin ForwardGtkCairoCFunc!(.cairo_pdf_surface_set_size);
-    //mixin ForwardGtkCairoCFunc!(.cairo_ps_surface_set_size);
+    mixin ForwardGtkCairoCFunc!(.cairo_pdf_surface_set_size);
+    mixin ForwardGtkCairoCFunc!(.cairo_ps_surface_set_size);
     mixin ForwardGtkCairoCFunc!(.cairo_rectangle);
     mixin ForwardGtkCairoCFunc!(.cairo_reference);
     mixin ForwardGtkCairoCFunc!(.cairo_rel_curve_to);
--- a/dwt/internal/gtk/OS.d	Thu Jan 17 09:03:45 2008 +0100
+++ b/dwt/internal/gtk/OS.d	Thu Jan 17 15:44:59 2008 +0100
@@ -493,6 +493,11 @@
 public alias dwt.internal.c.gtk.GtkAllocation GtkAllocation;
 
 
+public alias dwt.internal.c.gtk_unix_print_2_0.GtkPrinter GtkPrinter;
+public alias dwt.internal.c.gtk_unix_print_2_0.GtkPrintUnixDialog GtkPrintUnixDialog;
+public alias dwt.internal.c.gtk_unix_print_2_0.GtkPrintJob GtkPrintJob;
+public alias dwt.internal.c.gtk_unix_print_2_0.GtkPrintSettings GtkPrintSettings;
+public alias dwt.internal.c.gtk_unix_print_2_0.GtkPaperSize GtkPaperSize;
 
 public alias dwt.internal.c.Xlib.XErrorEvent XErrorEvent;
 public alias dwt.internal.c.Xlib.XExposeEvent XExposeEvent;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/printing/PrintDialog.d	Thu Jan 17 15:44:59 2008 +0100
@@ -0,0 +1,381 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.printing.PrintDialog;
+
+
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.internal.gtk.OS;
+import dwt.widgets.Dialog;
+import dwt.widgets.Display;
+import dwt.widgets.Shell;
+import dwt.widgets.Widget;
+import dwt.printing.Printer;
+import dwt.printing.PrinterData;
+
+import tango.util.Convert;
+import Math = tango.math.Math;
+static import tango.stdc.stringz;
+
+/**
+ * Instances of this class allow the user to select
+ * a printer and various print-related parameters
+ * prior to starting a print job.
+ * <p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+public class PrintDialog : Dialog {
+    int scope_ = PrinterData.ALL_PAGES;
+    int startPage = 1, endPage = 1;
+    bool printToFile = false;
+
+    GtkPrintUnixDialog* handle;
+    int index;
+    char [] settingsData;
+
+    static const char[] ADD_IDLE_PROC_KEY = "org.eclipse.swt.internal.gtk2.addIdleProc";
+    static const char[] REMOVE_IDLE_PROC_KEY = "org.eclipse.swt.internal.gtk2.removeIdleProc";
+
+/**
+ * Constructs a new instance of this class given only its parent.
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public this (Shell parent) {
+    this (parent, DWT.PRIMARY_MODAL);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public this (Shell parent, int style) {
+    super (parent, style);
+    checkSubclass ();
+}
+
+protected void checkSubclass() {
+}
+
+/**
+ * Returns the print job scope that the user selected
+ * before pressing OK in the dialog. This will be one
+ * of the following values:
+ * <dl>
+ * <dt><code>ALL_PAGES</code></dt>
+ * <dd>Print all pages in the current document</dd>
+ * <dt><code>PAGE_RANGE</code></dt>
+ * <dd>Print the range of pages specified by startPage and endPage</dd>
+ * <dt><code>SELECTION</code></dt>
+ * <dd>Print the current selection</dd>
+ * </dl>
+ *
+ * @return the scope setting that the user selected
+ */
+public int getScope() {
+    return scope_;
+}
+
+/**
+ * Sets the scope of the print job. The user will see this
+ * setting when the dialog is opened. This can have one of
+ * the following values:
+ * <dl>
+ * <dt><code>ALL_PAGES</code></dt>
+ * <dd>Print all pages in the current document</dd>
+ * <dt><code>PAGE_RANGE</code></dt>
+ * <dd>Print the range of pages specified by startPage and endPage</dd>
+ * <dt><code>SELECTION</code></dt>
+ * <dd>Print the current selection</dd>
+ * </dl>
+ *
+ * @param scope the scope setting when the dialog is opened
+ */
+public void setScope(int scope_) {
+    this.scope_ = scope_;
+}
+
+/**
+ * Returns the start page setting that the user selected
+ * before pressing OK in the dialog.
+ * <p>
+ * This value can be from 1 to the maximum number of pages for the platform.
+ * Note that it is only valid if the scope is <code>PAGE_RANGE</code>.
+ * </p>
+ *
+ * @return the start page setting that the user selected
+ */
+public int getStartPage() {
+    return startPage;
+}
+
+/**
+ * Sets the start page that the user will see when the dialog
+ * is opened.
+ * <p>
+ * This value can be from 1 to the maximum number of pages for the platform.
+ * Note that it is only valid if the scope is <code>PAGE_RANGE</code>.
+ * </p>
+ *
+ * @param startPage the startPage setting when the dialog is opened
+ */
+public void setStartPage(int startPage) {
+    this.startPage = startPage;
+}
+
+/**
+ * Returns the end page setting that the user selected
+ * before pressing OK in the dialog.
+ * <p>
+ * This value can be from 1 to the maximum number of pages for the platform.
+ * Note that it is only valid if the scope is <code>PAGE_RANGE</code>.
+ * </p>
+ *
+ * @return the end page setting that the user selected
+ */
+public int getEndPage() {
+    return endPage;
+}
+
+/**
+ * Sets the end page that the user will see when the dialog
+ * is opened.
+ * <p>
+ * This value can be from 1 to the maximum number of pages for the platform.
+ * Note that it is only valid if the scope is <code>PAGE_RANGE</code>.
+ * </p>
+ *
+ * @param endPage the end page setting when the dialog is opened
+ */
+public void setEndPage(int endPage) {
+    this.endPage = endPage;
+}
+
+/**
+ * Returns the 'Print to file' setting that the user selected
+ * before pressing OK in the dialog.
+ *
+ * @return the 'Print to file' setting that the user selected
+ */
+public bool getPrintToFile() {
+    return printToFile;
+}
+
+/**
+ * Sets the 'Print to file' setting that the user will see
+ * when the dialog is opened.
+ *
+ * @param printToFile the 'Print to file' setting when the dialog is opened
+ */
+public void setPrintToFile(bool printToFile) {
+    this.printToFile = printToFile;
+}
+
+/**
+ * Makes the receiver visible and brings it to the front
+ * of the display.
+ *
+ * @return a printer data object describing the desired print job parameters
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public PrinterData open() {
+    if (OS.GTK_VERSION < OS.buildVERSION (2, 10, 0)) {
+        return Printer.getDefaultPrinterData();
+    } else {
+        char* titleBytes = tango.stdc.stringz.toStringz( getText() );
+        auto topHandle = getParent().handle;
+        while (topHandle !is null && !OS.GTK_IS_WINDOW(topHandle)) {
+            topHandle = OS.gtk_widget_get_parent(topHandle);
+        }
+        handle = cast(GtkPrintUnixDialog*)OS.gtk_print_unix_dialog_new(titleBytes, cast(GtkWindow*)topHandle);
+
+        //TODO: Not currently implemented. May need new API. For now, disable 'Current' in the dialog. (see gtk bug 344519)
+        OS.gtk_print_unix_dialog_set_current_page(handle, -1);
+
+        OS.gtk_print_unix_dialog_set_manual_capabilities(handle,
+            OS.GTK_PRINT_CAPABILITY_COLLATE | OS.GTK_PRINT_CAPABILITY_COPIES | OS.GTK_PRINT_CAPABILITY_PAGE_SET);
+
+        /* Set state into print dialog settings. */
+        auto settings = OS.gtk_print_settings_new();
+        auto page_setup = OS.gtk_page_setup_new();
+        Printer.setScope(settings, scope_, startPage, endPage);
+        if (printToFile) {
+            char* buffer = tango.stdc.stringz.toStringz( "Print to File" ); //$NON-NLS-1$
+            OS.gtk_print_settings_set_printer(settings, buffer);
+        }
+        OS.gtk_print_unix_dialog_set_settings(handle, settings);
+        OS.gtk_print_unix_dialog_set_page_setup(handle, page_setup);
+        OS.g_object_unref(settings);
+        OS.g_object_unref(page_setup);
+
+        PrinterData data = null;
+        //TODO: Handle 'Print Preview' (GTK_RESPONSE_APPLY).
+        Display display = getParent() !is null ? getParent().getDisplay (): Display.getCurrent ();
+        display.setData (ADD_IDLE_PROC_KEY, null);
+        if (OS.gtk_dialog_run (handle) is OS.GTK_RESPONSE_OK) {
+            auto printer = OS.gtk_print_unix_dialog_get_selected_printer(handle);
+            if (printer !is null) {
+                /* Get state from print dialog. */
+                settings = OS.gtk_print_unix_dialog_get_settings(handle); // must unref
+                page_setup = OS.gtk_print_unix_dialog_get_page_setup(handle); // do not unref
+                data = Printer.printerDataFromGtkPrinter(printer);
+                int print_pages = OS.gtk_print_settings_get_print_pages(settings);
+                switch (print_pages) {
+                    case OS.GTK_PRINT_PAGES_ALL:
+                        scope_ = PrinterData.ALL_PAGES;
+                        break;
+                    case OS.GTK_PRINT_PAGES_RANGES:
+                        scope_ = PrinterData.PAGE_RANGE;
+                        int num_ranges;
+                        auto page_ranges = OS.gtk_print_settings_get_page_ranges(settings, &num_ranges);
+                        int length = num_ranges;
+                        int min = int.max, max = 0;
+                        for (int i = 0; i < length; i++) {
+                            min = Math.min(min, page_ranges[i].start + 1);
+                            max = Math.max(max, page_ranges[i].end + 1);
+                        }
+                        OS.g_free(page_ranges);
+                        startPage = min is int.max ? 1 : min;
+                        endPage = max is 0 ? 1 : max;
+                        break;
+                    case OS.GTK_PRINT_PAGES_CURRENT:
+                        //TODO: Disabled in dialog (see above). This code will not run. (see gtk bug 344519)
+                        scope_ = PrinterData.SELECTION;
+                        startPage = endPage = OS.gtk_print_unix_dialog_get_current_page(handle);
+                        break;
+                }
+
+                printToFile = ( data.name ==/*eq*/ "Print to File" ); //$NON-NLS-1$
+                if (printToFile) {
+                    auto address = OS.gtk_print_settings_get(settings, OS.GTK_PRINT_SETTINGS_OUTPUT_URI.ptr);
+                    data.fileName = tango.stdc.stringz.fromUtf8z( address).dup;
+                }
+
+                data.scope_ = scope_;
+                data.startPage = startPage;
+                data.endPage = endPage;
+                data.printToFile = printToFile;
+                data.copyCount = OS.gtk_print_settings_get_n_copies(settings);
+                data.collate = cast(bool)OS.gtk_print_settings_get_collate(settings);
+
+                /* Save other print_settings data as key/value pairs in otherData. */
+                index = 0;
+                settingsData = new char[1024];
+                settingsData[] = '\0';
+                OS.gtk_print_settings_foreach (settings, &GtkPrintSettingsFunc, cast(void*)this );
+                index++; // extra null terminator after print_settings and before page_setup
+
+                /* Save page_setup data as key/value pairs in otherData.
+                 * Note that page_setup properties must be stored and restored in the same order.
+                 */
+                store("orientation", OS.gtk_page_setup_get_orientation(page_setup)); //$NON-NLS-1$
+                store("top_margin", OS.gtk_page_setup_get_top_margin(page_setup, OS.GTK_UNIT_MM)); //$NON-NLS-1$
+                store("bottom_margin", OS.gtk_page_setup_get_bottom_margin(page_setup, OS.GTK_UNIT_MM)); //$NON-NLS-1$
+                store("left_margin", OS.gtk_page_setup_get_left_margin(page_setup, OS.GTK_UNIT_MM)); //$NON-NLS-1$
+                store("right_margin", OS.gtk_page_setup_get_right_margin(page_setup, OS.GTK_UNIT_MM)); //$NON-NLS-1$
+                auto paper_size = OS.gtk_page_setup_get_paper_size(page_setup); //$NON-NLS-1$
+                storeBytes("paper_size_name", OS.gtk_paper_size_get_name(paper_size)); //$NON-NLS-1$
+                storeBytes("paper_size_display_name", OS.gtk_paper_size_get_display_name(paper_size)); //$NON-NLS-1$
+                storeBytes("paper_size_ppd_name", OS.gtk_paper_size_get_ppd_name(paper_size)); //$NON-NLS-1$
+                store("paper_size_width", OS.gtk_paper_size_get_width(paper_size, OS.GTK_UNIT_MM)); //$NON-NLS-1$
+                store("paper_size_height", OS.gtk_paper_size_get_height(paper_size, OS.GTK_UNIT_MM)); //$NON-NLS-1$
+                store("paper_size_is_custom", OS.gtk_paper_size_is_custom(paper_size)); //$NON-NLS-1$
+                data.otherData = settingsData;
+                OS.g_object_unref(settings);
+            }
+        }
+        display.setData (REMOVE_IDLE_PROC_KEY, null);
+        OS.gtk_widget_destroy (handle);
+        return data;
+    }
+}
+
+private static extern(C) void GtkPrintSettingsFunc (char* key, char* value, void* data) {
+    (cast(PrintDialog)data).GtkPrintSettingsMeth(key, value );
+}
+
+void GtkPrintSettingsMeth (char* key, char* value) {
+    store( tango.stdc.stringz.fromUtf8z(key).dup, tango.stdc.stringz.fromUtf8z(value).dup );
+}
+
+void store(char[] key, int value) {
+    store(key, to!(char[])(value));
+}
+
+void store(char[] key, double value) {
+    store(key, to!(char[])(value));
+}
+
+void store(char[] key, bool value) {
+    store(key, to!(char[])(value));
+}
+
+void storeBytes(char[] key, char* value) {
+    store(key, tango.stdc.stringz.fromUtf8z(value).dup );
+}
+
+void store(char [] key, char[] value) {
+    int length = key.length + 1 + value.length + 1;
+    if (index + length + 1 > settingsData.length) {
+        char [] newData = new char[settingsData.length + Math.max(length + 1, 1024)];
+        newData[] = '\0';
+        System.arraycopy (settingsData, 0, newData, 0, settingsData.length);
+        settingsData = newData;
+    }
+    System.arraycopy (key, 0, settingsData, index, key.length);
+    index += key.length + 1; // null terminated
+    System.arraycopy (value, 0, settingsData, index, value.length);
+    index += value.length + 1; // null terminated
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/printing/Printer.d	Thu Jan 17 15:44:59 2008 +0100
@@ -0,0 +1,726 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.printing.Printer;
+
+
+
+import dwt.DWT;
+import dwt.DWTError;
+import dwt.DWTException;
+import dwt.graphics.Device;
+import dwt.graphics.DeviceData;
+import dwt.graphics.Font;
+import dwt.graphics.GCData;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.cairo.Cairo;
+import dwt.internal.gtk.OS;
+import dwt.printing.PrinterData;
+
+static import tango.stdc.stringz;
+static import tango.io.Stdout;
+import tango.util.Convert;
+
+
+/**
+ * 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
+ */
+public final class Printer : Device {
+    static PrinterData [] printerList;
+
+    PrinterData data;
+    GtkPrinter* printer;
+    GtkPrintJob* printJob;
+    GtkPrintSettings* settings;
+    void* pageSetup;
+    cairo_surface_t* surface;
+    cairo_t* cairo;
+
+    /**
+     * whether or not a GC was created for this printer
+     */
+    bool isGCCreated = false;
+    Font systemFont;
+
+    char[] settingsData;
+    int start, end;
+
+    static const char[] GTK_LPR_BACKEND = "GtkPrintBackendLpr"; //$NON-NLS-1$
+
+/**
+ * Returns an array of <code>PrinterData</code> objects
+ * representing all available printers.
+ *
+ * @return the list of available printers
+ */
+public static PrinterData[] getPrinterList() {
+    printerList = new PrinterData [0];
+    if (OS.GTK_VERSION < OS.buildVERSION (2, 10, 0)) {
+        return printerList;
+    }
+    OS.gtk_enumerate_printers(&GtkPrinterFunc_List, null, null, true);
+    return printerList;
+}
+
+private static extern(C) int GtkPrinterFunc_List (GtkPrinter* printer, void* 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 ==/*eq*/ 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
+ * printer available on the System.
+ *
+ * @return the default printer data or null
+ *
+ * @since 2.1
+ */
+public static PrinterData getDefaultPrinterData() {
+    printerList = new PrinterData [1];
+    if (OS.GTK_VERSION < OS.buildVERSION (2, 10, 0)) {
+        return null;
+    }
+    OS.gtk_enumerate_printers(&GtkPrinterFunc_Default, null, null, true);
+    return printerList[0];
+}
+
+private static extern(C) int GtkPrinterFunc_Default (GtkPrinter* printer, void* user_data) {
+    if (OS.gtk_printer_is_default(printer)) {
+        printerList[0] = printerDataFromGtkPrinter(printer);
+        return 1;
+    }
+    return 0;
+}
+
+GtkPrinter* gtkPrinterFromPrinterData() {
+    printer = null;
+    OS.gtk_enumerate_printers(&GtkPrinterFunc_FindNamedPrinterFunc, cast(void*)this, null, true);
+    return printer;
+}
+
+private static extern(C) int GtkPrinterFunc_FindNamedPrinterFunc (GtkPrinter* printer, void* user_data) {
+    return (cast(Printer)user_data).GtkPrinterFunc_FindNamedPrinter( printer, null );
+}
+int GtkPrinterFunc_FindNamedPrinter (GtkPrinter* printer, void* user_data) {
+    PrinterData pd = printerDataFromGtkPrinter(printer);
+    if (pd.driver ==/*eq*/data.driver && pd.name ==/*eq*/ data.name ) {
+        this.printer = printer;
+        OS.g_object_ref(printer);
+        return 1;
+    }
+    return 0;
+}
+
+static PrinterData printerDataFromGtkPrinter(GtkPrinter*  printer) {
+    auto backend = OS.gtk_printer_get_backend(printer);
+    auto address = OS.G_OBJECT_TYPE_NAME(backend);
+    char[] backendType =tango.stdc.stringz.fromUtf8z( address ).dup;
+
+    address = OS.gtk_printer_get_name (printer);
+    char[] name =tango.stdc.stringz.fromUtf8z( address ).dup;
+
+    return new PrinterData (backendType, name);
+}
+
+static void setScope(GtkPrintSettings* settings, int scope_, int startPage, int endPage) {
+    switch (scope_) {
+    case PrinterData.ALL_PAGES:
+        OS.gtk_print_settings_set_print_pages(settings, OS.GTK_PRINT_PAGES_ALL);
+        break;
+    case PrinterData.PAGE_RANGE:
+        OS.gtk_print_settings_set_print_pages(settings, OS.GTK_PRINT_PAGES_RANGES);
+        GtkPageRange pageRange;
+        pageRange.start = startPage - 1;
+        pageRange.end = endPage - 1;
+        OS.gtk_print_settings_set_page_ranges(settings, &pageRange, 1);
+        break;
+    case PrinterData.SELECTION:
+        //TODO: Not correctly implemented. May need new API. For now, set to ALL. (see gtk bug 344519)
+        OS.gtk_print_settings_set_print_pages(settings, OS.GTK_PRINT_PAGES_ALL);
+        break;
+    default:
+    }
+}
+
+static DeviceData checkNull (PrinterData data) {
+    if (data is null) data = new PrinterData();
+    if (data.driver is null || data.name is null) {
+        PrinterData defaultPrinter = getDefaultPrinterData();
+        if (defaultPrinter is null) DWT.error(DWT.ERROR_NO_HANDLES);
+        data.driver = defaultPrinter.driver;
+        data.name = defaultPrinter.name;
+    }
+    return data;
+}
+
+/**
+ * Constructs a new printer representing the default printer.
+ * <p>
+ * You must dispose the printer when it is no longer required.
+ * </p>
+ *
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES - if there are no valid printers
+ * </ul>
+ *
+ * @see Device#dispose
+ */
+public this() {
+    this(null);
+}
+
+/**
+ * Constructs a new printer given a <code>PrinterData</code>
+ * object representing the desired printer.
+ * <p>
+ * You must dispose the printer when it is no longer required.
+ * </p>
+ *
+ * @param data the printer data for the specified printer
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the specified printer data does not represent a valid printer
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES - if there are no valid printers
+ * </ul>
+ *
+ * @see Device#dispose
+ */
+public this(PrinterData data) {
+    super(checkNull(data));
+}
+
+int restoreInt(char[] key) {
+    char [] value = restoreBytes(key, false);
+    return to!(int)( value );
+}
+
+double restoreDouble(char[] key) {
+    char [] value = restoreBytes(key, false);
+    return to!(double)( value );
+}
+
+bool restoreBoolean(char[] key) {
+    char [] value = restoreBytes(key, false);
+    return to!(bool)( value );
+}
+
+char[] restoreBytes(char[] key, bool nullTerminate) {
+    //get key
+    start = end;
+    while (end < settingsData.length && settingsData[end] !is 0) end++;
+    end++;
+    char [] keyBuffer = new char [end - start];
+    System.arraycopy (settingsData, start, keyBuffer, 0, keyBuffer.length);
+
+    //get value
+    start = end;
+    while (end < settingsData.length && settingsData[end] !is 0) end++;
+    int length_ = end - start;
+    end++;
+    if (nullTerminate) length_++;
+    char [] valueBuffer = new char [length_];
+    System.arraycopy (settingsData, start, valueBuffer, 0, length_);
+
+    if (DEBUG) tango.io.Stdout.Stdout.formatln( "{}: {}", keyBuffer, valueBuffer );
+
+    return valueBuffer;
+}
+
+/**
+ * Returns a reasonable font for applications to use.
+ * On some platforms, this will match the "default font"
+ * or "system font" if such can be found.  This font
+ * should not be free'd because it was allocated by the
+ * system, not the application.
+ * <p>
+ * Typically, applications which want the default look
+ * should simply not set the font on the widgets they
+ * create. Widgets are always created with the correct
+ * default font for the class of user-interface component
+ * they represent.
+ * </p>
+ *
+ * @return a font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Font getSystemFont () {
+    checkDevice ();
+    if (systemFont !is null) return systemFont;
+    auto style = OS.gtk_widget_get_default_style();
+    auto defaultFont = OS.pango_font_description_copy (OS.gtk_style_get_font_desc (style));
+    return systemFont = Font.gtk_new (this, defaultFont);
+}
+
+/**
+ * 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 DWT. 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
+ */
+public GdkGC* internal_new_GC(GCData data) {
+    auto drawable = OS.gdk_pixmap_new(OS.GDK_ROOT_PARENT(), 1, 1, 1);
+    auto gdkGC = OS.gdk_gc_new (drawable);
+    if (gdkGC is null) DWT.error (DWT.ERROR_NO_HANDLES);
+    if (data !is null) {
+        if (isGCCreated) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        int mask = DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT;
+        if ((data.style & mask) is 0) {
+            data.style |= DWT.LEFT_TO_RIGHT;
+        }
+        data.device = this;
+        data.drawable = drawable;
+        data.background = getSystemColor (DWT.COLOR_WHITE).handle;
+        data.foreground = getSystemColor (DWT.COLOR_BLACK).handle;
+        data.font = getSystemFont ().handle;
+        if (cairo is null) DWT.error(DWT.ERROR_NO_HANDLES);
+        data.cairo = cairo;
+        isGCCreated = true;
+    }
+    return gdkGC;
+}
+
+/**
+ * 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 DWT. 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
+ */
+public void internal_dispose_GC(GdkGC* gdkGC, GCData data) {
+    if (data !is null) isGCCreated = false;
+    OS.g_object_unref (gdkGC);
+    if (data !is null) {
+        if (data.drawable !is null) OS.g_object_unref (data.drawable);
+        data.drawable = null;
+        data.cairo = null;
+    }
+}
+
+/**
+ * Releases any internal state prior to destroying this printer.
+ * This method is called internally by the dispose
+ * mechanism of the <code>Device</code> class.
+ */
+protected void release () {
+    super.release();
+
+    /* Dispose the default font */
+    if (systemFont !is null) systemFont.dispose ();
+    systemFont = null;
+}
+
+/**
+ * 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 DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #startPage
+ * @see #endPage
+ * @see #endJob
+ */
+public bool startJob(char[] jobName) {
+    checkDevice();
+    char* buffer = tango.stdc.stringz.toStringz(jobName);
+    printJob = OS.gtk_print_job_new (buffer, printer, settings, pageSetup);
+    if (printJob is null) return false;
+    surface = OS.gtk_print_job_get_surface(printJob, null);
+    if (surface is null) {
+        OS.g_object_unref(printJob);
+        printJob = null;
+        return false;
+    }
+    cairo = Cairo.cairo_create(surface);
+    if (cairo is null)  {
+        OS.g_object_unref(printJob);
+        printJob = null;
+        return false;
+    }
+    return true;
+}
+
+/**
+ * Destroys the printer handle.
+ * This method is called internally by the dispose
+ * mechanism of the <code>Device</code> class.
+ */
+protected void destroy () {
+    if (printer !is null) OS.g_object_unref (printer);
+    if (settings !is null) OS.g_object_unref (settings);
+    if (pageSetup !is null) OS.g_object_unref (pageSetup);
+    if (cairo !is null) Cairo.cairo_destroy (cairo);
+    if (printJob !is null) OS.g_object_unref (printJob);
+    printer = null;
+    settings = null;
+    pageSetup = null;
+    cairo = null;
+    printJob = null;
+}
+
+/**
+ * Ends the current print job.
+ *
+ * @exception DWTException <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 is null) return;
+    Cairo.cairo_surface_finish(surface);
+    OS.gtk_print_job_send(printJob, null, null, null );
+}
+
+/**
+ * Cancels a print job in progress.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void cancelJob() {
+    checkDevice();
+    if (printJob is null) return;
+    //TODO: Need to implement (waiting on gtk bug 339323)
+    //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 DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #endPage
+ * @see #startJob
+ * @see #endJob
+ */
+public bool startPage() {
+    checkDevice();
+    if (printJob is null) return false;
+    double width = OS.gtk_page_setup_get_paper_width (pageSetup, OS.GTK_UNIT_POINTS);
+    double height = OS.gtk_page_setup_get_paper_height (pageSetup, OS.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 DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #startPage
+ * @see #startJob
+ * @see #endJob
+ */
+public void endPage() {
+    checkDevice();
+    if (cairo !is null) 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 DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Point getDPI() {
+    checkDevice();
+    int resolution = OS.gtk_print_settings_get_resolution(settings);
+    if (DEBUG) tango.io.Stdout.Stdout.formatln("print_settings.resolution={}", resolution);
+    //TODO: Return 72 (1/72 inch = 1 point) until gtk bug 346245 is fixed
+    //TODO: Fix this: gtk_print_settings_get_resolution returns 0? (see gtk bug 346252)
+    if (true || resolution is 0) return new Point(72, 72);
+    return new Point(resolution, resolution);
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location.
+ * For a printer, this is the size of a physical page, in pixels.
+ *
+ * @return the bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #getClientArea
+ * @see #computeTrim
+ */
+public Rectangle getBounds() {
+    checkDevice();
+    //TODO: We are supposed to return this in pixels, but GTK_UNIT_PIXELS is currently not implemented (gtk bug 346245)
+    double width = OS.gtk_page_setup_get_paper_width (pageSetup, OS.GTK_UNIT_POINTS);
+    double height = OS.gtk_page_setup_get_paper_height (pageSetup, OS.GTK_UNIT_POINTS);
+    return new Rectangle(0, 0, cast(int) width, cast(int) height);
+}
+
+/**
+ * Returns a rectangle which describes the area of the
+ * receiver which is capable of displaying data.
+ * For a printer, this is the size of the printable area
+ * of a page, in pixels.
+ *
+ * @return the client area
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #getBounds
+ * @see #computeTrim
+ */
+public Rectangle getClientArea() {
+    checkDevice();
+    //TODO: We are supposed to return this in pixels, but GTK_UNIT_PIXELS is currently not implemented (gtk bug 346245)
+    double width = OS.gtk_page_setup_get_page_width(pageSetup, OS.GTK_UNIT_POINTS);
+    double height = OS.gtk_page_setup_get_page_height(pageSetup, OS.GTK_UNIT_POINTS);
+    return new Rectangle(0, 0, cast(int) width, cast(int) height);
+}
+
+/**
+ * Given a desired <em>client area</em> for the receiver
+ * (as described by the arguments), returns the bounding
+ * rectangle which would be required to produce that client
+ * area.
+ * <p>
+ * In other words, it returns a rectangle such that, if the
+ * receiver's bounds were set to that rectangle, the area
+ * of the receiver which is capable of displaying data
+ * (that is, not covered by the "trimmings") would be the
+ * rectangle described by the arguments (relative to the
+ * receiver's parent).
+ * </p><p>
+ * Note that there is no setBounds for a printer. This method
+ * is usually used by passing in the client area (the 'printable
+ * area') of the printer. It can also be useful to pass in 0, 0, 0, 0.
+ * </p>
+ *
+ * @param x the desired x coordinate of the client area
+ * @param y the desired y coordinate of the client area
+ * @param width the desired width of the client area
+ * @param height the desired height of the client area
+ * @return the required bounds to produce the given client area
+ *
+ * @exception DWTException <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();
+    //TODO: We are supposed to return this in pixels, but GTK_UNIT_PIXELS is currently not implemented (gtk bug 346245)
+    double printWidth = OS.gtk_page_setup_get_page_width(pageSetup, OS.GTK_UNIT_POINTS);
+    double printHeight = OS.gtk_page_setup_get_page_height(pageSetup, OS.GTK_UNIT_POINTS);
+    double paperWidth = OS.gtk_page_setup_get_paper_width (pageSetup, OS.GTK_UNIT_POINTS);
+    double paperHeight = OS.gtk_page_setup_get_paper_height (pageSetup, OS.GTK_UNIT_POINTS);
+    double printX = -OS.gtk_page_setup_get_left_margin(pageSetup, OS.GTK_UNIT_POINTS);
+    double printY = -OS.gtk_page_setup_get_top_margin(pageSetup, OS.GTK_UNIT_POINTS);
+    double hTrim = paperWidth - printWidth;
+    double vTrim = paperHeight - printHeight;
+    return new Rectangle(x + cast(int)printX, y + cast(int)printY, width + cast(int)hTrim, height + cast(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
+ */
+protected void create(DeviceData deviceData) {
+    this.data = cast(PrinterData)deviceData;
+    if (OS.GTK_VERSION < OS.buildVERSION (2, 10, 0)) DWT.error(DWT.ERROR_NO_HANDLES);
+    printer = gtkPrinterFromPrinterData();
+    if (printer is null) DWT.error(DWT.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
+ */
+protected void init() {
+    super.init ();
+    settings = OS.gtk_print_settings_new();
+    pageSetup = OS.gtk_page_setup_new();
+    if (data.otherData !is null) {
+        /* Retreive stored printer_settings data. */
+        settingsData = data.otherData;
+        start = end = 0;
+        while (end < settingsData.length && settingsData[end] !is 0) {
+            start = end;
+            while (end < settingsData.length && settingsData[end] !is 0) end++;
+            end++;
+            char [] keyBuffer = new char [end - start];
+            System.arraycopy (settingsData, start, keyBuffer, 0, keyBuffer.length);
+            start = end;
+            while (end < settingsData.length && settingsData[end] !is 0) end++;
+            end++;
+            char [] valueBuffer = new char [end - start];
+            System.arraycopy (settingsData, start, valueBuffer, 0, valueBuffer.length);
+            OS.gtk_print_settings_set(settings, keyBuffer.ptr, valueBuffer.ptr);
+            if (DEBUG) tango.io.Stdout.Stdout.formatln("{}: {}", keyBuffer, valueBuffer);
+        }
+        end++; // skip extra null terminator
+
+        /* Retreive stored page_setup data.
+         * Note that page_setup properties must be stored (in PrintDialog) and restored (here) in the same order.
+         */
+        OS.gtk_page_setup_set_orientation(pageSetup, restoreInt("orientation")); //$NON-NLS-1$
+        OS.gtk_page_setup_set_top_margin(pageSetup, restoreDouble("top_margin"), OS.GTK_UNIT_MM); //$NON-NLS-1$
+        OS.gtk_page_setup_set_bottom_margin(pageSetup, restoreDouble("bottom_margin"), OS.GTK_UNIT_MM); //$NON-NLS-1$
+        OS.gtk_page_setup_set_left_margin(pageSetup, restoreDouble("left_margin"), OS.GTK_UNIT_MM); //$NON-NLS-1$
+        OS.gtk_page_setup_set_right_margin(pageSetup, restoreDouble("right_margin"), OS.GTK_UNIT_MM); //$NON-NLS-1$
+        char [] name = restoreBytes("paper_size_name", true); //$NON-NLS-1$
+        char [] display_name = restoreBytes("paper_size_display_name", true); //$NON-NLS-1$
+        char [] 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$
+        bool custom = restoreBoolean("paper_size_is_custom"); //$NON-NLS-1$
+        GtkPaperSize* paper_size;
+        if (custom) {
+            if (ppd_name.length > 0) {
+                paper_size = OS.gtk_paper_size_new_from_ppd(ppd_name.ptr, display_name.ptr, width, height);
+            } else {
+                paper_size = OS.gtk_paper_size_new_custom(name.ptr, display_name.ptr, width, height, OS.GTK_UNIT_MM);
+            }
+        } else {
+            paper_size = OS.gtk_paper_size_new(name.ptr);
+        }
+        OS.gtk_page_setup_set_paper_size(pageSetup, paper_size);
+        OS.g_free(paper_size);
+    }
+
+    /* Set values of settings from PrinterData. */
+    setScope(settings, data.scope_, data.startPage, data.endPage);
+    //TODO: Should we look at printToFile, or driver/name for "Print to File", or both? (see gtk bug 345590)
+    if (data.printToFile) {
+        char* buffer = tango.stdc.stringz.toStringz( data.fileName );
+        OS.gtk_print_settings_set(settings, OS.GTK_PRINT_SETTINGS_OUTPUT_URI.ptr, buffer);
+    }
+    if (data.driver ==/*eq*/ "GtkPrintBackendFile" && data.name ==/*eq*/ "Print to File" ) { //$NON-NLS-1$ //$NON-NLS-2$
+        char* buffer = tango.stdc.stringz.toStringz( data.fileName );
+        OS.gtk_print_settings_set(settings, OS.GTK_PRINT_SETTINGS_OUTPUT_URI.ptr, buffer);
+    }
+    OS.gtk_print_settings_set_n_copies(settings, data.copyCount);
+    OS.gtk_print_settings_set_collate(settings, data.collate);
+}
+
+/**
+ * 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;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/printing/PrinterData.d	Thu Jan 17 15:44:59 2008 +0100
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.printing.PrinterData;
+
+
+import dwt.graphics.DeviceData;
+
+import tango.text.convert.Format;
+
+
+/**
+ * Instances of this class are descriptions of a print job
+ * in terms of the printer, and the scope and type of printing
+ * that is desired. For example, the number of pages and copies
+ * can be specified, as well as whether or not the print job
+ * should go to a file.
+ * <p>
+ * Application code does <em>not</em> need to explicitly release the
+ * resources managed by each instance when those instances are no longer
+ * required, and thus no <code>dispose()</code> method is provided.
+ * </p>
+ *
+ * @see Printer
+ * @see Printer#getPrinterList
+ * @see PrintDialog#open
+ */
+
+public final class PrinterData : DeviceData {
+
+    /**
+     * the printer driver
+     * On Windows systems, this is the name of the driver (often "winspool").
+     * On Mac OSX, this is the destination type ("Printer", "Fax", "File", or "Preview").
+     * On X/Window systems, this is the name of a display connection to the
+     * Xprt server (the default is ":1").
+     * On GTK+, this is the backend type name (eg. GtkPrintBackendCups).
+     */
+    // TODO: note that this api is not finalized for GTK+
+    public char[] driver;
+
+    /**
+     * the name of the printer
+     * On Windows systems, this is the name of the 'device'.
+     * On Mac OSX, X/Window systems, and GTK+, this is the printer's 'name'.
+     */
+    public char[] name;
+
+    /**
+     * the scope of the print job, expressed as one of the following values:
+     * <dl>
+     * <dt><code>ALL_PAGES</code></dt>
+     * <dd>Print all pages in the current document</dd>
+     * <dt><code>PAGE_RANGE</code></dt>
+     * <dd>Print the range of pages specified by startPage and endPage</dd>
+     * <dt><code>SELECTION</code></dt>
+     * <dd>Print the current selection</dd>
+     * </dl>
+     */
+    public int scope_ = ALL_PAGES;
+
+    /**
+     * the start page of a page range, used when scope is PAGE_RANGE.
+     * This value can be from 1 to the maximum number of pages for the platform.
+     */
+    public int startPage = 0;
+
+    /**
+     * the end page of a page range, used when scope is PAGE_RANGE.
+     * This value can be from 1 to the maximum number of pages for the platform.
+     */
+    public int endPage = 0;
+
+    /**
+     * whether or not the print job should go to a file
+     */
+    public bool printToFile = false;
+
+    /**
+     * the name of the file to print to if printToFile is true.
+     * Note that this field is ignored if printToFile is false.
+     */
+    public char[] fileName;
+
+    /**
+     * the number of copies to print.
+     * Note that this field may be controlled by the printer driver
+     * In other words, the printer itself may be capable of printing
+     * multiple copies, and if so, the value of this field will always be 1.
+     */
+    public int copyCount = 1;
+
+    /**
+     * whether or not the printer should collate the printed paper
+     * Note that this field may be controlled by the printer driver.
+     * In other words, the printer itself may be capable of doing the
+     * collation, and if so, the value of this field will always be false.
+     */
+    public bool collate = false;
+
+    /**
+     * <code>scope</code> field value indicating that
+     * all pages should be printed
+     */
+    public static const int ALL_PAGES = 0;
+
+    /**
+     * <code>scope</code> field value indicating that
+     * the range of pages specified by startPage and endPage
+     * should be printed
+     */
+    public static const int PAGE_RANGE = 1;
+
+    /**
+     * <code>scope</code> field value indicating that
+     * the current selection should be printed
+     */
+    public static const int SELECTION = 2;
+
+    /**
+     * private, platform-specific data
+     * On Windows, this contains a copy of the DEVMODE struct
+     * returned from the <code>PrintDialog</code>.
+     * This field is not currently used on the X/Window System.
+     */
+    char [] otherData;
+
+    /**
+     * Constructs an instance of this class that can be
+     * used to print to the default printer.
+     *
+     * @see Printer#getDefaultPrinterData
+     */
+    public this() {
+    }
+
+    /**
+     * Constructs an instance of this class with the given
+     * printer driver and printer name.
+     *
+     * @param driver the printer driver for the printer
+     * @param name the name of the printer
+     *
+     * @see #driver
+     * @see #name
+     */
+    public this(char[] driver, char[] name) {
+        this.driver = driver;
+        this.name = name;
+    }
+
+    /**
+     * Returns a string containing a concise, human-readable
+     * description of the receiver.
+     *
+     * @return a string representation of the receiver
+     */
+    public char[] toString() {
+        return Format( "PrinterData {{driver = {}, name = {}}", driver, name );  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+    }
+}