diff --git a/bundles/org.eclipse.swt/Eclipse SWT Printing/cocoa/org/eclipse/swt/printing/PDFDocument.java b/bundles/org.eclipse.swt/Eclipse SWT Printing/cocoa/org/eclipse/swt/printing/PDFDocument.java index ce7d51ae1d1..6a0b6d1c473 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Printing/cocoa/org/eclipse/swt/printing/PDFDocument.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Printing/cocoa/org/eclipse/swt/printing/PDFDocument.java @@ -43,14 +43,15 @@ * * @see GC * @since 3.133 + * + * @noreference This class is provisional API and subject to change. It is being made available to gather early feedback. The API or behavior may change in future releases as the implementation evolves based on user feedback. */ -public class PDFDocument implements Drawable { - Device device; +public final class PDFDocument extends Device { long pdfContext; NSGraphicsContext graphicsContext; boolean isGCCreated = false; - boolean disposed = false; boolean pageStarted = false; + String filename; /** * Width of the page in points (1/72 inch) @@ -62,6 +63,16 @@ public class PDFDocument implements Drawable { */ double heightInPoints; + /** + * Internal data class to pass PDF document parameters through + * the Device constructor. + */ + static class PDFDocumentData extends DeviceData { + String filename; + double widthInPoints; + double heightInPoints; + } + /** * Constructs a new PDFDocument with the specified filename and page dimensions. *

@@ -83,52 +94,38 @@ public class PDFDocument implements Drawable { * @see #dispose() */ public PDFDocument(String filename, double widthInPoints, double heightInPoints) { - this(null, filename, widthInPoints, heightInPoints); + super(checkData(filename, widthInPoints, heightInPoints)); } /** - * Constructs a new PDFDocument with the specified filename and page dimensions, - * associated with the given device. - *

- * You must dispose the PDFDocument when it is no longer required. - *

- * - * @param device the device to associate with this PDFDocument - * @param filename the path to the PDF file to create - * @param widthInPoints the width of each page in points (1/72 inch) - * @param heightInPoints the height of each page in points (1/72 inch) - * - * @exception IllegalArgumentException - * @exception SWTError - * - * @see #dispose() + * Validates and prepares the data for construction. */ - public PDFDocument(Device device, String filename, double widthInPoints, double heightInPoints) { + static PDFDocumentData checkData(String filename, double widthInPoints, double heightInPoints) { if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); if (widthInPoints <= 0 || heightInPoints <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + PDFDocumentData data = new PDFDocumentData(); + data.filename = filename; + data.widthInPoints = widthInPoints; + data.heightInPoints = heightInPoints; + return data; + } + + /** + * Creates the PDF device in the operating system. + * This method is called before init. + * + * @param data the DeviceData which describes the receiver + */ + @Override + protected void create(DeviceData data) { + PDFDocumentData pdfData = (PDFDocumentData) data; + this.filename = pdfData.filename; + this.widthInPoints = pdfData.widthInPoints; + this.heightInPoints = pdfData.heightInPoints; NSAutoreleasePool pool = null; if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); try { - this.widthInPoints = widthInPoints; - this.heightInPoints = heightInPoints; - - // Get device from the current display if not provided - if (device == null) { - try { - this.device = org.eclipse.swt.widgets.Display.getDefault(); - } catch (Exception e) { - this.device = null; - } - } else { - this.device = device; - } - // Create CFURL from the filename NSString path = NSString.stringWith(filename); NSURL fileURL = NSURL.fileURLWithPath(path); @@ -184,11 +181,11 @@ private void ensurePageStarted() { *

* * @exception SWTException */ public void newPage() { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); NSAutoreleasePool pool = null; if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); try { @@ -216,11 +213,11 @@ public void newPage() { *
  • ERROR_INVALID_ARGUMENT - if width or height is not positive
  • * * @exception SWTException */ public void newPage(double widthInPoints, double heightInPoints) { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); if (widthInPoints <= 0 || heightInPoints <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); this.widthInPoints = widthInPoints; @@ -234,11 +231,11 @@ public void newPage(double widthInPoints, double heightInPoints) { * @return the width in points (1/72 inch) * * @exception SWTException */ public double getWidth() { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); return widthInPoints; } @@ -248,14 +245,47 @@ public double getWidth() { * @return the height in points (1/72 inch) * * @exception SWTException */ public double getHeight() { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); return heightInPoints; } + /** + * Returns the DPI (dots per inch) of the PDF document. + * PDF documents work in points where 1 point = 1/72 inch, + * so the DPI is always 72. + * + * @return a point whose x coordinate is the horizontal DPI and whose y coordinate is the vertical DPI + * + * @exception SWTException + */ + @Override + public Point getDPI() { + checkDevice(); + return new Point(72, 72); + } + + /** + * Returns a rectangle describing the receiver's size and location. + * The rectangle dimensions are in points (1/72 inch). + * + * @return the bounding rectangle + * + * @exception SWTException + */ + @Override + public Rectangle getBounds() { + checkDevice(); + return new Rectangle(0, 0, (int) widthInPoints, (int) heightInPoints); + } + /** * Invokes platform specific functionality to allocate a new GC handle. *

    @@ -273,7 +303,7 @@ public double getHeight() { */ @Override public long internal_new_GC(GCData data) { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); if (isGCCreated) SWT.error(SWT.ERROR_INVALID_ARGUMENT); NSAutoreleasePool pool = null; @@ -290,18 +320,16 @@ public long internal_new_GC(GCData data) { if ((data.style & mask) == 0) { data.style |= SWT.LEFT_TO_RIGHT; } - data.device = device; + data.device = this; data.flippedContext = graphicsContext; data.restoreContext = true; NSSize size = new NSSize(); size.width = widthInPoints; size.height = heightInPoints; data.size = size; - if (device != null) { - data.background = device.getSystemColor(SWT.COLOR_WHITE).handle; - data.foreground = device.getSystemColor(SWT.COLOR_BLACK).handle; - data.font = device.getSystemFont(); - } + data.background = getSystemColor(SWT.COLOR_WHITE).handle; + data.foreground = getSystemColor(SWT.COLOR_BLACK).handle; + data.font = getSystemFont(); } isGCCreated = true; return graphicsContext.id; @@ -350,27 +378,12 @@ public boolean isAutoScalable() { } /** - * Returns true if the PDFDocument has been disposed, - * and false otherwise. - * - * @return true when the PDFDocument is disposed and false otherwise + * Destroys the PDF document handle. + * This method is called internally by the dispose + * mechanism of the Device class. */ - public boolean isDisposed() { - return disposed; - } - - /** - * Disposes of the operating system resources associated with - * the PDFDocument. Applications must dispose of all PDFDocuments - * that they allocate. - *

    - * This method finalizes the PDF file and writes it to disk. - *

    - */ - public void dispose() { - if (disposed) return; - disposed = true; - + @Override + protected void destroy() { NSAutoreleasePool pool = null; if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); try { diff --git a/bundles/org.eclipse.swt/Eclipse SWT Printing/gtk/org/eclipse/swt/printing/PDFDocument.java b/bundles/org.eclipse.swt/Eclipse SWT Printing/gtk/org/eclipse/swt/printing/PDFDocument.java index 9c76c25187b..a219df6c420 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Printing/gtk/org/eclipse/swt/printing/PDFDocument.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Printing/gtk/org/eclipse/swt/printing/PDFDocument.java @@ -44,13 +44,14 @@ * * @see GC * @since 3.133 + * + * @noreference This class is provisional API and subject to change. It is being made available to gather early feedback. The API or behavior may change in future releases as the implementation evolves based on user feedback. */ -public class PDFDocument implements Drawable { - Device device; +public final class PDFDocument extends Device { long surface; long cairo; boolean isGCCreated = false; - boolean disposed = false; + String filename; /** * Width of the page in points (1/72 inch) @@ -62,6 +63,16 @@ public class PDFDocument implements Drawable { */ double heightInPoints; + /** + * Internal data class to pass PDF document parameters through + * the Device constructor. + */ + static class PDFDocumentData extends DeviceData { + String filename; + double widthInPoints; + double heightInPoints; + } + /** * Constructs a new PDFDocument with the specified filename and page dimensions. *

    @@ -83,60 +94,34 @@ public class PDFDocument implements Drawable { * @see #dispose() */ public PDFDocument(String filename, double widthInPoints, double heightInPoints) { - if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); - if (widthInPoints <= 0 || heightInPoints <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); - - this.widthInPoints = widthInPoints; - this.heightInPoints = heightInPoints; - - byte[] filenameBytes = Converter.wcsToMbcs(filename, true); - surface = Cairo.cairo_pdf_surface_create(filenameBytes, widthInPoints, heightInPoints); - if (surface == 0) SWT.error(SWT.ERROR_NO_HANDLES); - - cairo = Cairo.cairo_create(surface); - if (cairo == 0) { - Cairo.cairo_surface_destroy(surface); - surface = 0; - SWT.error(SWT.ERROR_NO_HANDLES); - } - - // Get device from the current display or create a temporary one - try { - device = org.eclipse.swt.widgets.Display.getDefault(); - } catch (Exception e) { - device = null; - } + super(checkData(filename, widthInPoints, heightInPoints)); } /** - * Constructs a new PDFDocument with the specified filename and page dimensions, - * associated with the given device. - *

    - * You must dispose the PDFDocument when it is no longer required. - *

    - * - * @param device the device to associate with this PDFDocument - * @param filename the path to the PDF file to create - * @param widthInPoints the width of each page in points (1/72 inch) - * @param heightInPoints the height of each page in points (1/72 inch) - * - * @exception IllegalArgumentException - * @exception SWTError - * - * @see #dispose() + * Validates and prepares the data for construction. */ - public PDFDocument(Device device, String filename, double widthInPoints, double heightInPoints) { + static PDFDocumentData checkData(String filename, double widthInPoints, double heightInPoints) { if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); if (widthInPoints <= 0 || heightInPoints <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + PDFDocumentData data = new PDFDocumentData(); + data.filename = filename; + data.widthInPoints = widthInPoints; + data.heightInPoints = heightInPoints; + return data; + } - this.device = device; - this.widthInPoints = widthInPoints; - this.heightInPoints = heightInPoints; + /** + * Creates the PDF device in the operating system. + * This method is called before init. + * + * @param data the DeviceData which describes the receiver + */ + @Override + protected void create(DeviceData data) { + PDFDocumentData pdfData = (PDFDocumentData) data; + this.filename = pdfData.filename; + this.widthInPoints = pdfData.widthInPoints; + this.heightInPoints = pdfData.heightInPoints; byte[] filenameBytes = Converter.wcsToMbcs(filename, true); surface = Cairo.cairo_pdf_surface_create(filenameBytes, widthInPoints, heightInPoints); @@ -159,11 +144,11 @@ public PDFDocument(Device device, String filename, double widthInPoints, double *

    * * @exception SWTException */ public void newPage() { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); Cairo.cairo_show_page(cairo); } @@ -181,11 +166,11 @@ public void newPage() { *
  • ERROR_INVALID_ARGUMENT - if width or height is not positive
  • * * @exception SWTException */ public void newPage(double widthInPoints, double heightInPoints) { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); if (widthInPoints <= 0 || heightInPoints <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); Cairo.cairo_show_page(cairo); @@ -200,11 +185,11 @@ public void newPage(double widthInPoints, double heightInPoints) { * @return the width in points (1/72 inch) * * @exception SWTException */ public double getWidth() { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); return widthInPoints; } @@ -214,14 +199,47 @@ public double getWidth() { * @return the height in points (1/72 inch) * * @exception SWTException */ public double getHeight() { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); return heightInPoints; } + /** + * Returns the DPI (dots per inch) of the PDF document. + * PDF documents work in points where 1 point = 1/72 inch, + * so the DPI is always 72. + * + * @return a point whose x coordinate is the horizontal DPI and whose y coordinate is the vertical DPI + * + * @exception SWTException + */ + @Override + public Point getDPI() { + checkDevice(); + return new Point(72, 72); + } + + /** + * Returns a rectangle describing the receiver's size and location. + * The rectangle dimensions are in points (1/72 inch). + * + * @return the bounding rectangle + * + * @exception SWTException + */ + @Override + public Rectangle getBounds() { + checkDevice(); + return new Rectangle(0, 0, (int) widthInPoints, (int) heightInPoints); + } + /** * Invokes platform specific functionality to allocate a new GC handle. *

    @@ -239,7 +257,7 @@ public double getHeight() { */ @Override public long internal_new_GC(GCData data) { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); if (isGCCreated) SWT.error(SWT.ERROR_INVALID_ARGUMENT); if (data != null) { @@ -247,27 +265,13 @@ public long internal_new_GC(GCData data) { if ((data.style & mask) == 0) { data.style |= SWT.LEFT_TO_RIGHT; } - data.device = device; + data.device = this; data.cairo = cairo; data.width = (int) widthInPoints; data.height = (int) heightInPoints; - if (device != null) { - data.foregroundRGBA = device.getSystemColor(SWT.COLOR_BLACK).handle; - data.backgroundRGBA = device.getSystemColor(SWT.COLOR_WHITE).handle; - data.font = device.getSystemFont(); - } else { - // Fallback: create default colors manually using GdkRGBA values - data.foregroundRGBA = new org.eclipse.swt.internal.gtk.GdkRGBA(); - data.foregroundRGBA.red = 0; - data.foregroundRGBA.green = 0; - data.foregroundRGBA.blue = 0; - data.foregroundRGBA.alpha = 1; - data.backgroundRGBA = new org.eclipse.swt.internal.gtk.GdkRGBA(); - data.backgroundRGBA.red = 1; - data.backgroundRGBA.green = 1; - data.backgroundRGBA.blue = 1; - data.backgroundRGBA.alpha = 1; - } + data.foregroundRGBA = getSystemColor(SWT.COLOR_BLACK).handle; + data.backgroundRGBA = getSystemColor(SWT.COLOR_WHITE).handle; + data.font = getSystemFont(); } isGCCreated = true; return cairo; @@ -302,27 +306,12 @@ public boolean isAutoScalable() { } /** - * Returns true if the PDFDocument has been disposed, - * and false otherwise. - * - * @return true when the PDFDocument is disposed and false otherwise + * Destroys the PDF document handle. + * This method is called internally by the dispose + * mechanism of the Device class. */ - public boolean isDisposed() { - return disposed; - } - - /** - * Disposes of the operating system resources associated with - * the PDFDocument. Applications must dispose of all PDFDocuments - * that they allocate. - *

    - * This method finalizes the PDF file and writes it to disk. - *

    - */ - public void dispose() { - if (disposed) return; - disposed = true; - + @Override + protected void destroy() { if (cairo != 0) { Cairo.cairo_destroy(cairo); cairo = 0; diff --git a/bundles/org.eclipse.swt/Eclipse SWT Printing/win32/org/eclipse/swt/printing/PDFDocument.java b/bundles/org.eclipse.swt/Eclipse SWT Printing/win32/org/eclipse/swt/printing/PDFDocument.java index db15aa658f9..1766f3923c7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Printing/win32/org/eclipse/swt/printing/PDFDocument.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Printing/win32/org/eclipse/swt/printing/PDFDocument.java @@ -47,114 +47,135 @@ * * @see GC * @since 3.133 + * + * @noreference This class is provisional API and subject to change. It is being made available to gather early feedback. The API or behavior may change in future releases as the implementation evolves based on user feedback. */ -public class PDFDocument implements Drawable { - Device device; +public final class PDFDocument extends Device { long handle; boolean isGCCreated = false; - boolean disposed = false; boolean jobStarted = false; boolean pageStarted = false; String filename; /** - * Width of the page in device-independent units + * Preferred width of the page in points (1/72 inch) */ - double width; + double preferredWidthInPoints; /** - * Height of the page in device-independent units + * Preferred height of the page in points (1/72 inch) */ - double height; + double preferredHeightInPoints; /** - * Width of the page in points (1/72 inch) + * Actual width of the page in points (1/72 inch) */ - double widthInPoints; + double actualWidthInPoints; /** - * Height of the page in points (1/72 inch) + * Actual height of the page in points (1/72 inch) */ - double heightInPoints; + double actualHeightInPoints; /** The name of the Microsoft Print to PDF printer */ private static final String PDF_PRINTER_NAME = "Microsoft Print to PDF"; - + + /** Points per inch - the standard PDF coordinate system uses 72 points per inch */ + private static final double POINTS_PER_INCH = 72.0; + + /** + * Internal data class to pass PDF document parameters through + * the Device constructor. + */ + static class PDFDocumentData extends DeviceData { + String filename; + double widthInPoints; + double heightInPoints; + } + /** Helper class to represent a paper size with orientation */ private static class PaperSize { int paperSizeConstant; int orientation; - double widthInInches; - double heightInInches; - + double widthInPoints; + double heightInPoints; + PaperSize(int paperSize, int orientation, double width, double height) { this.paperSizeConstant = paperSize; this.orientation = orientation; - this.widthInInches = width; - this.heightInInches = height; + this.widthInPoints = width; + this.heightInPoints = height; } } /** - * Finds the best matching standard paper size for the given dimensions. + * Finds the best matching standard paper size for the given dimensions in points. * Tries both portrait and landscape orientations and selects the one that * minimizes wasted space while ensuring the content fits. + * The returned paper size will always be >= the requested size. */ - private static PaperSize findBestPaperSize(double widthInInches, double heightInInches) { - // Common paper sizes (width x height in inches, portrait orientation) + private static PaperSize findBestPaperSize(double widthInPoints, double heightInPoints) { + // Common paper sizes (width x height in points, portrait orientation) + // 1 inch = 72 points int[][] standardSizes = { - {OS.DMPAPER_LETTER, 850, 1100}, // 8.5 x 11 - {OS.DMPAPER_LEGAL, 850, 1400}, // 8.5 x 14 - {OS.DMPAPER_A4, 827, 1169}, // 8.27 x 11.69 - {OS.DMPAPER_TABLOID, 1100, 1700}, // 11 x 17 - {OS.DMPAPER_A3, 1169, 1654}, // 11.69 x 16.54 - {OS.DMPAPER_EXECUTIVE, 725, 1050}, // 7.25 x 10.5 - {OS.DMPAPER_A5, 583, 827}, // 5.83 x 8.27 + {OS.DMPAPER_LETTER, 612, 792}, // 8.5 x 11 inches + {OS.DMPAPER_LEGAL, 612, 1008}, // 8.5 x 14 inches + {OS.DMPAPER_A4, 595, 842}, // 8.27 x 11.69 inches (210 x 297 mm) + {OS.DMPAPER_TABLOID, 792, 1224}, // 11 x 17 inches + {OS.DMPAPER_A3, 842, 1191}, // 11.69 x 16.54 inches (297 x 420 mm) + {OS.DMPAPER_EXECUTIVE, 522, 756}, // 7.25 x 10.5 inches + {OS.DMPAPER_A5, 420, 595}, // 5.83 x 8.27 inches (148 x 210 mm) }; - + PaperSize bestMatch = null; double minWaste = Double.MAX_VALUE; - + for (int[] size : standardSizes) { - double paperWidth = size[1] / 100.0; - double paperHeight = size[2] / 100.0; - + double paperWidth = size[1]; + double paperHeight = size[2]; + // Try portrait orientation - if (widthInInches <= paperWidth && heightInInches <= paperHeight) { - double waste = (paperWidth * paperHeight) - (widthInInches * heightInInches); + if (widthInPoints <= paperWidth && heightInPoints <= paperHeight) { + double waste = (paperWidth * paperHeight) - (widthInPoints * heightInPoints); if (waste < minWaste) { minWaste = waste; bestMatch = new PaperSize(size[0], OS.DMORIENT_PORTRAIT, paperWidth, paperHeight); } } - + // Try landscape orientation (swap width and height) - if (widthInInches <= paperHeight && heightInInches <= paperWidth) { - double waste = (paperHeight * paperWidth) - (widthInInches * heightInInches); + if (widthInPoints <= paperHeight && heightInPoints <= paperWidth) { + double waste = (paperHeight * paperWidth) - (widthInPoints * heightInPoints); if (waste < minWaste) { minWaste = waste; bestMatch = new PaperSize(size[0], OS.DMORIENT_LANDSCAPE, paperHeight, paperWidth); } } } - - // Default to Letter if no match found + + // Error if requested size exceeds the largest available standard paper size if (bestMatch == null) { - bestMatch = new PaperSize(OS.DMPAPER_LETTER, OS.DMORIENT_PORTRAIT, 8.5, 11.0); + SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, " [Requested page size exceeds maximum supported size]"); } - + return bestMatch; } /** - * Constructs a new PDFDocument with the specified filename and page dimensions. + * Constructs a new PDFDocument with the specified filename and preferred page dimensions. + *

    + * The dimensions specify the preferred page size in points (1/72 inch). On Windows, + * the Microsoft Print to PDF driver only supports standard paper sizes, so the actual + * page size may be larger than requested. Use {@link #getBounds()} to query the actual + * page dimensions after construction. + *

    *

    * You must dispose the PDFDocument when it is no longer required. *

    * * @param filename the path to the PDF file to create - * @param width the width of each page in device-independent units - * @param height the height of each page in device-independent units + * @param widthInPoints the preferred width of each page in points (1/72 inch) + * @param heightInPoints the preferred height of each page in points (1/72 inch) * * @exception IllegalArgumentException * * @see #dispose() + * @see #getBounds() */ - public PDFDocument(String filename, double width, double height) { - this(null, filename, width, height); + public PDFDocument(String filename, double widthInPoints, double heightInPoints) { + super(checkData(filename, widthInPoints, heightInPoints)); } /** - * Constructs a new PDFDocument with the specified filename and page dimensions, - * associated with the given device. - *

    - * You must dispose the PDFDocument when it is no longer required. - *

    - * - * @param device the device to associate with this PDFDocument - * @param filename the path to the PDF file to create - * @param width the width of each page in device-independent units - * @param height the height of each page in device-independent units - * - * @exception IllegalArgumentException - * @exception SWTError - * - * @see #dispose() + * Validates and prepares the data for construction. */ - public PDFDocument(Device device, String filename, double width, double height) { + static PDFDocumentData checkData(String filename, double widthInPoints, double heightInPoints) { if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); - if (width <= 0 || height <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); - - this.filename = filename; - this.width = width; - this.height = height; - - // Get device from the current display if not provided - if (device == null) { - try { - this.device = org.eclipse.swt.widgets.Display.getDefault(); - } catch (SWTException e) { - this.device = null; - } - } else { - this.device = device; - } + if (widthInPoints <= 0 || heightInPoints <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + PDFDocumentData data = new PDFDocumentData(); + data.filename = filename; + data.widthInPoints = widthInPoints; + data.heightInPoints = heightInPoints; + return data; + } - // Calculate physical size in inches from screen pixels - int screenDpiX = 96; - int screenDpiY = 96; - if (this.device != null) { - Point dpi = this.device.getDPI(); - screenDpiX = dpi.x; - screenDpiY = dpi.y; - } - double widthInInches = width / screenDpiX; - double heightInInches = height / screenDpiY; - - // Microsoft Print to PDF doesn't support custom page sizes - // Find the best matching standard paper size - PaperSize bestMatch = findBestPaperSize(widthInInches, heightInInches); - this.widthInPoints = bestMatch.widthInInches * 72.0; - this.heightInPoints = bestMatch.heightInInches * 72.0; + /** + * Creates the PDF device in the operating system. + * This method is called before init. + * + * @param data the DeviceData which describes the receiver + */ + @Override + protected void create(DeviceData data) { + PDFDocumentData pdfData = (PDFDocumentData) data; + this.filename = pdfData.filename; + this.preferredWidthInPoints = pdfData.widthInPoints; + this.preferredHeightInPoints = pdfData.heightInPoints; + + // Find the best matching standard paper size for the requested dimensions + PaperSize bestMatch = findBestPaperSize(preferredWidthInPoints, preferredHeightInPoints); + this.actualWidthInPoints = bestMatch.widthInPoints; + this.actualHeightInPoints = bestMatch.heightInPoints; // Create printer DC for "Microsoft Print to PDF" TCHAR driver = new TCHAR(0, "WINSPOOL", true); @@ -257,7 +252,7 @@ public PDFDocument(Device device, String filename, double width, double height) } if (handle == 0) { - SWT.error(SWT.ERROR_NO_HANDLES); + SWT.error(SWT.ERROR_NO_HANDLES, null, " [Failed to create device context for '" + PDF_PRINTER_NAME + "'. Ensure the printer is installed and enabled.]"); } } @@ -290,7 +285,8 @@ private void ensureJobStarted() { OS.HeapFree(hHeap, 0, lpszDocName); if (rc <= 0) { - SWT.error(SWT.ERROR_NO_HANDLES); + int lastError = OS.GetLastError(); + SWT.error(SWT.ERROR_NO_HANDLES, null, " [StartDoc failed for '" + PDF_PRINTER_NAME + "' (rc=" + rc + ", lastError=" + lastError + ")]"); } jobStarted = true; } @@ -302,7 +298,11 @@ private void ensureJobStarted() { private void ensurePageStarted() { ensureJobStarted(); if (!pageStarted) { - OS.StartPage(handle); + int rc = OS.StartPage(handle); + if (rc <= 0) { + int lastError = OS.GetLastError(); + SWT.error(SWT.ERROR_NO_HANDLES, null, " [StartPage failed (rc=" + rc + ", lastError=" + lastError + ")]"); + } pageStarted = true; } } @@ -316,11 +316,11 @@ private void ensurePageStarted() { *

    * * @exception SWTException */ public void newPage() { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); if (pageStarted) { OS.EndPage(handle); pageStarted = false; @@ -339,51 +339,138 @@ public void newPage() { * has been started may not be fully supported by all printer drivers. *

    * - * @param widthInPoints the width of the new page in points (1/72 inch) - * @param heightInPoints the height of the new page in points (1/72 inch) + * @param widthInPoints the preferred width of the new page in points (1/72 inch) + * @param heightInPoints the preferred height of the new page in points (1/72 inch) * * @exception IllegalArgumentException * @exception SWTException */ public void newPage(double widthInPoints, double heightInPoints) { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + checkDevice(); if (widthInPoints <= 0 || heightInPoints <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); - this.widthInPoints = widthInPoints; - this.heightInPoints = heightInPoints; + this.preferredWidthInPoints = widthInPoints; + this.preferredHeightInPoints = heightInPoints; + // Note: actual page size may be larger due to standard paper size constraints + PaperSize bestMatch = findBestPaperSize(widthInPoints, heightInPoints); + this.actualWidthInPoints = bestMatch.widthInPoints; + this.actualHeightInPoints = bestMatch.heightInPoints; newPage(); } /** - * Returns the width of the current page in points. + * Returns the actual width of the current page in points. + *

    + * On Windows, this may be larger than the preferred width specified + * in the constructor due to standard paper size constraints. + *

    * - * @return the width in points (1/72 inch) + * @return the actual width in points (1/72 inch) * * @exception SWTException */ public double getWidth() { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); - return widthInPoints; + checkDevice(); + return actualWidthInPoints; } /** - * Returns the height of the current page in points. + * Returns the actual height of the current page in points. + *

    + * On Windows, this may be larger than the preferred height specified + * in the constructor due to standard paper size constraints. + *

    * - * @return the height in points (1/72 inch) + * @return the actual height in points (1/72 inch) * * @exception SWTException */ public double getHeight() { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); - return heightInPoints; + checkDevice(); + return actualHeightInPoints; + } + + /** + * Returns the DPI (dots per inch) of the PDF document. + * Since the coordinate system is scaled to work in points (1/72 inch), + * this always returns 72 DPI, consistent with GTK and Cocoa implementations. + * + * @return a point whose x coordinate is the horizontal DPI and whose y coordinate is the vertical DPI + * + * @exception SWTException + */ + @Override + public Point getDPI() { + checkDevice(); + return new Point(72, 72); + } + + /** + * Returns a rectangle describing the receiver's size and location. + * The rectangle dimensions are in points (1/72 inch). + *

    + * On Windows, this returns the actual page size which may be larger + * than the preferred size specified in the constructor. + *

    + * + * @return the bounding rectangle + * + * @exception SWTException + */ + @Override + public Rectangle getBounds() { + checkDevice(); + return new Rectangle(0, 0, (int) actualWidthInPoints, (int) actualHeightInPoints); + } + + /** + * Returns a rectangle which describes the area of the + * receiver which is capable of displaying data. + *

    + * On Windows, the printable area may be smaller than the page bounds + * due to printer margins. + *

    + * + * @return the client area + * + * @exception SWTException + */ + @Override + public Rectangle getClientArea() { + checkDevice(); + // Get the printable area from the device capabilities + // Need actual printer DPI for conversion (not the user-facing 72 DPI) + int printerDpiX = OS.GetDeviceCaps(handle, OS.LOGPIXELSX); + int printerDpiY = OS.GetDeviceCaps(handle, OS.LOGPIXELSY); + int printableWidth = OS.GetDeviceCaps(handle, OS.HORZRES); + int printableHeight = OS.GetDeviceCaps(handle, OS.VERTRES); + int offsetX = OS.GetDeviceCaps(handle, OS.PHYSICALOFFSETX); + int offsetY = OS.GetDeviceCaps(handle, OS.PHYSICALOFFSETY); + + // Convert from device units to points + double scaleX = POINTS_PER_INCH / printerDpiX; + double scaleY = POINTS_PER_INCH / printerDpiY; + + int x = (int) (offsetX * scaleX); + int y = (int) (offsetY * scaleY); + int width = (int) (printableWidth * scaleX); + int height = (int) (printableHeight * scaleY); + + return new Rectangle(x, y, width, height); } /** @@ -403,62 +490,38 @@ public double getHeight() { */ @Override public long internal_new_GC(GCData data) { - if (disposed) SWT.error(SWT.ERROR_WIDGET_DISPOSED); - if (isGCCreated) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + checkDevice(); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES, null, " [PDF document handle is not valid]"); + if (data != null) { + if (isGCCreated) SWT.error(SWT.ERROR_INVALID_ARGUMENT); - ensurePageStarted(); + ensurePageStarted(); - if (data != null) { int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; if ((data.style & mask) != 0) { data.layout = (data.style & SWT.RIGHT_TO_LEFT) != 0 ? OS.LAYOUT_RTL : 0; } else { data.style |= SWT.LEFT_TO_RIGHT; } - data.device = device; + data.device = this; data.nativeZoom = 100; - if (device != null) { - data.font = device.getSystemFont(); - } - } - - // Set up coordinate system scaling - // Get screen DPI - int screenDpiX = 96; - int screenDpiY = 96; - if (device != null) { - Point dpi = device.getDPI(); - screenDpiX = dpi.x; - screenDpiY = dpi.y; + data.font = getSystemFont(); + + // Set up coordinate system scaling to work in points + // The printer has its own DPI, so we scale to make 1 user unit = 1 point + int printerDpiX = OS.GetDeviceCaps(handle, OS.LOGPIXELSX); + int printerDpiY = OS.GetDeviceCaps(handle, OS.LOGPIXELSY); + + // Scale factor: printer_dpi / POINTS_PER_INCH (since we want 1 unit = 1 point = 1/72 inch) + float scaleX = (float)(printerDpiX / POINTS_PER_INCH); + float scaleY = (float)(printerDpiY / POINTS_PER_INCH); + + OS.SetGraphicsMode(handle, OS.GM_ADVANCED); + float[] transform = new float[] {scaleX, 0, 0, scaleY, 0, 0}; + OS.SetWorldTransform(handle, transform); + + isGCCreated = true; } - - // Get PDF printer DPI - int pdfDpiX = OS.GetDeviceCaps(handle, OS.LOGPIXELSX); - int pdfDpiY = OS.GetDeviceCaps(handle, OS.LOGPIXELSY); - - // Calculate content size in inches (what user wanted) - double contentWidthInInches = width / screenDpiX; - double contentHeightInInches = height / screenDpiY; - - // Calculate scale factor to fit content to page - // The page size is the physical paper size we selected - double pageWidthInInches = widthInPoints / 72.0; - double pageHeightInInches = heightInPoints / 72.0; - double scaleToFitWidth = pageWidthInInches / contentWidthInInches; - double scaleToFitHeight = pageHeightInInches / contentHeightInInches; - - // Use the smaller scale to ensure both width and height fit - double scaleToFit = Math.min(scaleToFitWidth, scaleToFitHeight); - - // Combined scale: fit-to-page * DPI conversion - float scaleX = (float)(scaleToFit * pdfDpiX / screenDpiX); - float scaleY = (float)(scaleToFit * pdfDpiY / screenDpiY); - - OS.SetGraphicsMode(handle, OS.GM_ADVANCED); - float[] transform = new float[] {scaleX, 0, 0, scaleY, 0, 0}; - OS.SetWorldTransform(handle, transform); - - isGCCreated = true; return handle; } @@ -491,27 +554,12 @@ public boolean isAutoScalable() { } /** - * Returns true if the PDFDocument has been disposed, - * and false otherwise. - * - * @return true when the PDFDocument is disposed and false otherwise - */ - public boolean isDisposed() { - return disposed; - } - - /** - * Disposes of the operating system resources associated with - * the PDFDocument. Applications must dispose of all PDFDocuments - * that they allocate. - *

    - * This method finalizes the PDF file and writes it to disk. - *

    + * Destroys the PDF document handle. + * This method is called internally by the dispose + * mechanism of the Device class. */ - public void dispose() { - if (disposed) return; - disposed = true; - + @Override + protected void destroy() { if (handle != 0) { if (pageStarted) { OS.EndPage(handle);