@@ -57,6 +57,16 @@ public class PDFDocument implements Drawable {
5757 boolean pageStarted = false ;
5858 String filename ;
5959
60+ /**
61+ * Width of the page in device-independent units
62+ */
63+ double width ;
64+
65+ /**
66+ * Height of the page in device-independent units
67+ */
68+ double height ;
69+
6070 /**
6171 * Width of the page in points (1/72 inch)
6272 */
@@ -69,6 +79,74 @@ public class PDFDocument implements Drawable {
6979
7080 /** The name of the Microsoft Print to PDF printer */
7181 private static final String PDF_PRINTER_NAME = "Microsoft Print to PDF" ;
82+
83+ /** Helper class to represent a paper size with orientation */
84+ private static class PaperSize {
85+ int paperSizeConstant ;
86+ int orientation ;
87+ double widthInInches ;
88+ double heightInInches ;
89+ double wastedArea ;
90+
91+ PaperSize (int paperSize , int orientation , double width , double height , double wasted ) {
92+ this .paperSizeConstant = paperSize ;
93+ this .orientation = orientation ;
94+ this .widthInInches = width ;
95+ this .heightInInches = height ;
96+ this .wastedArea = wasted ;
97+ }
98+ }
99+
100+ /**
101+ * Finds the best matching standard paper size for the given dimensions.
102+ * Tries both portrait and landscape orientations and selects the one that
103+ * minimizes wasted space while ensuring the content fits.
104+ */
105+ private static PaperSize findBestPaperSize (double widthInInches , double heightInInches ) {
106+ // Common paper sizes (width x height in inches, portrait orientation)
107+ int [][] standardSizes = {
108+ {OS .DMPAPER_LETTER , 850 , 1100 }, // 8.5 x 11
109+ {OS .DMPAPER_LEGAL , 850 , 1400 }, // 8.5 x 14
110+ {OS .DMPAPER_A4 , 827 , 1169 }, // 8.27 x 11.69
111+ {OS .DMPAPER_TABLOID , 1100 , 1700 }, // 11 x 17
112+ {OS .DMPAPER_A3 , 1169 , 1654 }, // 11.69 x 16.54
113+ {OS .DMPAPER_EXECUTIVE , 725 , 1050 }, // 7.25 x 10.5
114+ {OS .DMPAPER_A5 , 583 , 827 }, // 5.83 x 8.27
115+ };
116+
117+ PaperSize bestMatch = null ;
118+ double minWaste = Double .MAX_VALUE ;
119+
120+ for (int [] size : standardSizes ) {
121+ double paperWidth = size [1 ] / 100.0 ;
122+ double paperHeight = size [2 ] / 100.0 ;
123+
124+ // Try portrait orientation
125+ if (widthInInches <= paperWidth && heightInInches <= paperHeight ) {
126+ double waste = (paperWidth * paperHeight ) - (widthInInches * heightInInches );
127+ if (waste < minWaste ) {
128+ minWaste = waste ;
129+ bestMatch = new PaperSize (size [0 ], OS .DMORIENT_PORTRAIT , paperWidth , paperHeight , waste );
130+ }
131+ }
132+
133+ // Try landscape orientation (swap width and height)
134+ if (widthInInches <= paperHeight && heightInInches <= paperWidth ) {
135+ double waste = (paperHeight * paperWidth ) - (widthInInches * heightInInches );
136+ if (waste < minWaste ) {
137+ minWaste = waste ;
138+ bestMatch = new PaperSize (size [0 ], OS .DMORIENT_LANDSCAPE , paperHeight , paperWidth , waste );
139+ }
140+ }
141+ }
142+
143+ // Default to Letter if no match found
144+ if (bestMatch == null ) {
145+ bestMatch = new PaperSize (OS .DMPAPER_LETTER , OS .DMORIENT_PORTRAIT , 8.5 , 11.0 , 0 );
146+ }
147+
148+ return bestMatch ;
149+ }
72150
73151 /**
74152 * Constructs a new PDFDocument with the specified filename and page dimensions.
@@ -77,8 +155,8 @@ public class PDFDocument implements Drawable {
77155 * </p>
78156 *
79157 * @param filename the path to the PDF file to create
80- * @param widthInPoints the width of each page in points (1/72 inch)
81- * @param heightInPoints the height of each page in points (1/72 inch)
158+ * @param width the width of each page in device-independent units
159+ * @param height the height of each page in device-independent units
82160 *
83161 * @exception IllegalArgumentException <ul>
84162 * <li>ERROR_NULL_ARGUMENT - if filename is null</li>
@@ -90,8 +168,8 @@ public class PDFDocument implements Drawable {
90168 *
91169 * @see #dispose()
92170 */
93- public PDFDocument (String filename , double widthInPoints , double heightInPoints ) {
94- this (null , filename , widthInPoints , heightInPoints );
171+ public PDFDocument (String filename , double width , double height ) {
172+ this (null , filename , width , height );
95173 }
96174
97175 /**
@@ -103,8 +181,8 @@ public PDFDocument(String filename, double widthInPoints, double heightInPoints)
103181 *
104182 * @param device the device to associate with this PDFDocument
105183 * @param filename the path to the PDF file to create
106- * @param widthInPoints the width of each page in points (1/72 inch)
107- * @param heightInPoints the height of each page in points (1/72 inch)
184+ * @param width the width of each page in device-independent units
185+ * @param height the height of each page in device-independent units
108186 *
109187 * @exception IllegalArgumentException <ul>
110188 * <li>ERROR_NULL_ARGUMENT - if filename is null</li>
@@ -116,13 +194,13 @@ public PDFDocument(String filename, double widthInPoints, double heightInPoints)
116194 *
117195 * @see #dispose()
118196 */
119- public PDFDocument (Device device , String filename , double widthInPoints , double heightInPoints ) {
197+ public PDFDocument (Device device , String filename , double width , double height ) {
120198 if (filename == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
121- if (widthInPoints <= 0 || heightInPoints <= 0 ) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
199+ if (width <= 0 || height <= 0 ) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
122200
123201 this .filename = filename ;
124- this .widthInPoints = widthInPoints ;
125- this .heightInPoints = heightInPoints ;
202+ this .width = width ;
203+ this .height = height ;
126204
127205 // Get device from the current display if not provided
128206 if (device == null ) {
@@ -135,6 +213,23 @@ public PDFDocument(Device device, String filename, double widthInPoints, double
135213 this .device = device ;
136214 }
137215
216+ // Calculate physical size in inches from screen pixels
217+ int screenDpiX = 96 ;
218+ int screenDpiY = 96 ;
219+ if (this .device != null ) {
220+ Point dpi = this .device .getDPI ();
221+ screenDpiX = dpi .x ;
222+ screenDpiY = dpi .y ;
223+ }
224+ double widthInInches = width / screenDpiX ;
225+ double heightInInches = height / screenDpiY ;
226+
227+ // Microsoft Print to PDF doesn't support custom page sizes
228+ // Find the best matching standard paper size
229+ PaperSize bestMatch = findBestPaperSize (widthInInches , heightInInches );
230+ this .widthInPoints = bestMatch .widthInInches * 72.0 ;
231+ this .heightInPoints = bestMatch .heightInInches * 72.0 ;
232+
138233 // Create printer DC for "Microsoft Print to PDF"
139234 TCHAR driver = new TCHAR (0 , "WINSPOOL" , true );
140235 TCHAR deviceName = new TCHAR (0 , PDF_PRINTER_NAME , true );
@@ -149,6 +244,12 @@ public PDFDocument(Device device, String filename, double widthInPoints, double
149244 if (lpInitData != 0 ) {
150245 int rc = OS .DocumentProperties (0 , hPrinter [0 ], deviceName , lpInitData , 0 , OS .DM_OUT_BUFFER );
151246 if (rc == OS .IDOK ) {
247+ DEVMODE devmode = new DEVMODE ();
248+ OS .MoveMemory (devmode , lpInitData , DEVMODE .sizeof );
249+ devmode .dmPaperSize = (short ) bestMatch .paperSizeConstant ;
250+ devmode .dmOrientation = (short ) bestMatch .orientation ;
251+ devmode .dmFields = OS .DM_PAPERSIZE | OS .DM_ORIENTATION ;
252+ OS .MoveMemory (lpInitData , devmode , DEVMODE .sizeof );
152253 handle = OS .CreateDC (driver , deviceName , 0 , lpInitData );
153254 }
154255 OS .HeapFree (hHeap , 0 , lpInitData );
@@ -322,6 +423,43 @@ public long internal_new_GC(GCData data) {
322423 data .font = device .getSystemFont ();
323424 }
324425 }
426+
427+ // Set up coordinate system scaling
428+ // Get screen DPI
429+ int screenDpiX = 96 ;
430+ int screenDpiY = 96 ;
431+ if (device != null ) {
432+ Point dpi = device .getDPI ();
433+ screenDpiX = dpi .x ;
434+ screenDpiY = dpi .y ;
435+ }
436+
437+ // Get PDF printer DPI
438+ int pdfDpiX = OS .GetDeviceCaps (handle , OS .LOGPIXELSX );
439+ int pdfDpiY = OS .GetDeviceCaps (handle , OS .LOGPIXELSY );
440+
441+ // Calculate content size in inches (what user wanted)
442+ double contentWidthInInches = width / screenDpiX ;
443+ double contentHeightInInches = height / screenDpiY ;
444+
445+ // Calculate scale factor to fit content to page
446+ // The page size is the physical paper size we selected
447+ double pageWidthInInches = widthInPoints / 72.0 ;
448+ double pageHeightInInches = heightInPoints / 72.0 ;
449+ double scaleToFitWidth = pageWidthInInches / contentWidthInInches ;
450+ double scaleToFitHeight = pageHeightInInches / contentHeightInInches ;
451+
452+ // Use the smaller scale to ensure both width and height fit
453+ double scaleToFit = Math .min (scaleToFitWidth , scaleToFitHeight );
454+
455+ // Combined scale: fit-to-page * DPI conversion
456+ float scaleX = (float )(scaleToFit * pdfDpiX / screenDpiX );
457+ float scaleY = (float )(scaleToFit * pdfDpiY / screenDpiY );
458+
459+ OS .SetGraphicsMode (handle , OS .GM_ADVANCED );
460+ float [] transform = new float [] {scaleX , 0 , 0 , scaleY , 0 , 0 };
461+ OS .SetWorldTransform (handle , transform );
462+
325463 isGCCreated = true ;
326464 return handle ;
327465 }
0 commit comments