Skip to content

Commit 03106eb

Browse files
GennadiyKrivosheinAlexander Scherbatiy
authored andcommitted
8344119: CUPSPrinter does not respect PostScript printer definition specification in case of reading ImageableArea values from PPD files
Reviewed-by: prr, psadhukhan
1 parent ad01dfb commit 03106eb

File tree

2 files changed

+342
-4
lines changed

2 files changed

+342
-4
lines changed

src/java.desktop/unix/native/common/awt/CUPSfuncs.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -574,10 +574,10 @@ Java_sun_print_CUPSPrinter_getPageSizes(JNIEnv *env,
574574
// paper width and height
575575
dims[i*6] = size->width;
576576
dims[(i*6)+1] = size->length;
577-
// paper printable area
577+
// paper printable area. x and y coordinates of the lower left corner, width and height
578578
dims[(i*6)+2] = size->left;
579-
dims[(i*6)+3] = size->top;
580-
dims[(i*6)+4] = size->right;
579+
dims[(i*6)+3] = size->top - size->bottom;
580+
dims[(i*6)+4] = size->right - size->left;
581581
dims[(i*6)+5] = size->bottom;
582582
}
583583
}
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2025, BELLSOFT. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* This code is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* version 2 for more details (a copy is included in the LICENSE file that
14+
* accompanied this code).
15+
*
16+
* You should have received a copy of the GNU General Public License version
17+
* 2 along with this work; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*
20+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21+
* or visit www.oracle.com if you need additional information or have any
22+
* questions.
23+
*/
24+
25+
import java.awt.BasicStroke;
26+
import java.awt.Color;
27+
import java.awt.Component;
28+
import java.awt.Dimension;
29+
import java.awt.Graphics2D;
30+
import java.awt.Graphics;
31+
import java.awt.GridBagConstraints;
32+
import java.awt.GridBagLayout;
33+
import java.awt.Insets;
34+
import java.awt.LayoutManager;
35+
import java.awt.event.ActionEvent;
36+
import java.awt.event.ActionListener;
37+
import java.awt.print.PageFormat;
38+
import java.awt.print.Paper;
39+
import java.awt.print.Printable;
40+
import java.awt.print.PrinterException;
41+
import java.awt.print.PrinterJob;
42+
import java.util.ArrayList;
43+
import java.util.List;
44+
import javax.print.PrintService;
45+
import javax.print.PrintServiceLookup;
46+
import javax.print.attribute.HashPrintRequestAttributeSet;
47+
import javax.print.attribute.PrintRequestAttributeSet;
48+
import javax.print.attribute.Size2DSyntax;
49+
import javax.print.attribute.standard.Media;
50+
import javax.print.attribute.standard.MediaPrintableArea;
51+
import javax.print.attribute.standard.MediaSize;
52+
import javax.print.attribute.standard.MediaSizeName;
53+
import javax.swing.DefaultComboBoxModel;
54+
import javax.swing.JButton;
55+
import javax.swing.JComboBox;
56+
import javax.swing.JFrame;
57+
import javax.swing.JLabel;
58+
import javax.swing.JList;
59+
import javax.swing.JPanel;
60+
import javax.swing.SwingConstants;
61+
import javax.swing.plaf.basic.BasicComboBoxRenderer;
62+
63+
/*
64+
* @test
65+
* @bug 8344119
66+
* @key printer
67+
* @requires (os.family == "linux" | os.family == "mac")
68+
* @library /java/awt/regtesthelpers
69+
* @build PassFailJFrame
70+
* @summary CUPSPrinter imageable area
71+
* @run main/manual CUPSPrinterImageableAreaTest
72+
*/
73+
74+
public class CUPSPrinterImageableAreaTest {
75+
76+
private static final List<MediaSizeName> ALLOWED_MEDIA_LIST = List.of(MediaSizeName.ISO_A4, MediaSizeName.NA_LETTER);
77+
private static final double DPI = 72.0;
78+
private static final double MM_PER_INCH = 2.54;
79+
private static final String INSTRUCTIONS = """
80+
<html>
81+
<div>
82+
The test checks that the media margins fetched from the printer's PPD file are correct.<br>
83+
Press the '<b>Print sample</b>' button to print a test page.<br>
84+
Required paper size and expected margins will be shown on the print dialog.
85+
A passing test will print the page with a black rectangle along the printable area.
86+
Ensure that all sides of the rectangle are printed.<br>
87+
Click '<b>Pass</b>' button, or click '<b>Fail</b>' button if the test failed.
88+
</div>
89+
<html>
90+
""";
91+
private static final String PAPER_INSTRUCTIONS_FORMAT = """
92+
<html><body style='margin: 0; text-align:left;'>
93+
Required paper size: <ul><li>%s</li></ul>
94+
Expected margins: <ul><li>left: %.1f</li>
95+
<li>bottom: %.1f</li>
96+
<li>right: %.1f</li>
97+
<li>top: %.1f</li>
98+
</ul></body></html>
99+
""";
100+
101+
public static void main(String[] args) throws Exception {
102+
PassFailJFrame.builder()
103+
.instructions(INSTRUCTIONS)
104+
.testUI(createTestUI())
105+
.columns(55)
106+
.build()
107+
.awaitAndCheck();
108+
}
109+
110+
private static JFrame createTestUI() {
111+
final TestServiceData[] testServiceList = getTestServiceList();
112+
if (testServiceList.length == 0) {
113+
throw new RuntimeException("Print services support borderless print only");
114+
}
115+
116+
final JFrame frame = new JFrame("CUPS Printer imageable area test");
117+
JPanel pnlRoot = new JPanel();
118+
JLabel lblPrintServices = new JLabel("Select a print service for the test");
119+
JComboBox<String> cbPrintServices = new JComboBox<>();
120+
JPanel pnlInstruction = new JPanel();
121+
JLabel lblInstruction = new JLabel();
122+
JButton btnPrint = new JButton("Print sample");
123+
124+
lblPrintServices.setLabelFor(cbPrintServices);
125+
lblPrintServices.setAlignmentX(SwingConstants.LEFT);
126+
127+
lblInstruction.setPreferredSize(new Dimension(250, 150));
128+
pnlInstruction.setBackground(Color.white);
129+
pnlInstruction.add(lblInstruction);
130+
131+
cbPrintServices.addActionListener(new ActionListener() {
132+
@Override
133+
public void actionPerformed(ActionEvent e) {
134+
int selectedIndex = cbPrintServices.getSelectedIndex();
135+
if (selectedIndex < 0) {
136+
lblInstruction.setText("");
137+
btnPrint.setEnabled(false);
138+
return;
139+
}
140+
141+
TestServiceData testServiceData = testServiceList[selectedIndex];
142+
PageFormat pageFormat = testServiceData.pageFormat;
143+
// margins: left, bottom, right, top
144+
double[] margins = new double[]{
145+
pageFormat.getImageableX(),
146+
pageFormat.getHeight() - pageFormat.getImageableHeight() - pageFormat.getImageableY(),
147+
pageFormat.getWidth() - pageFormat.getImageableWidth() - pageFormat.getImageableX(),
148+
pageFormat.getImageableY()
149+
};
150+
String printServiceInstructions = PAPER_INSTRUCTIONS_FORMAT.formatted(
151+
testServiceData.mediaSizeName.toString(), inchesToMM(margins[0]),
152+
inchesToMM(margins[1]), inchesToMM(margins[2]), inchesToMM(margins[3]));
153+
lblInstruction.setText(printServiceInstructions);
154+
btnPrint.setEnabled(true);
155+
}
156+
});
157+
158+
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
159+
for(TestServiceData tsd : testServiceList) {
160+
model.addElement(tsd.printService.getName());
161+
}
162+
cbPrintServices.setModel(model);
163+
cbPrintServices.setSelectedIndex(-1);
164+
PrintService defaultPrintService = PrintServiceLookup.lookupDefaultPrintService();
165+
if (defaultPrintService != null && model.getIndexOf(defaultPrintService.getName()) >= 0) {
166+
cbPrintServices.setSelectedItem(defaultPrintService.getName());
167+
} else {
168+
cbPrintServices.setSelectedIndex(0);
169+
}
170+
171+
btnPrint.setPreferredSize(new Dimension(200, 80));
172+
btnPrint.addActionListener((e) -> {
173+
int selectedIndex = cbPrintServices.getSelectedIndex();
174+
if (selectedIndex < 0) {
175+
return;
176+
}
177+
btnPrint.setEnabled(false);
178+
cbPrintServices.setEnabled(false);
179+
TestServiceData testServiceData = testServiceList[selectedIndex];
180+
PrinterJob job = PrinterJob.getPrinterJob();
181+
try {
182+
job.setPrintService(testServiceData.printService);
183+
job.setPrintable(new RectPrintable(), testServiceData.pageFormat);
184+
job.print();
185+
} catch (PrinterException ex) {
186+
throw new RuntimeException(ex);
187+
}
188+
});
189+
190+
LayoutManager layout = new GridBagLayout();
191+
pnlRoot.setLayout(layout);
192+
193+
addGridBagComponent(pnlRoot, lblPrintServices, 0);
194+
addGridBagComponent(pnlRoot, cbPrintServices, 1);
195+
addGridBagComponent(pnlRoot, pnlInstruction, 2);
196+
addGridBagComponent(pnlRoot, btnPrint, 3);
197+
198+
frame.add(pnlRoot);
199+
frame.pack();
200+
frame.setResizable(false);
201+
return frame;
202+
}
203+
204+
private static TestServiceData[] getTestServiceList() {
205+
PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
206+
if (printServices == null || printServices.length == 0) {
207+
throw new RuntimeException("Print services not found");
208+
}
209+
210+
List<TestServiceData> testServiceList = new ArrayList<>();
211+
for (PrintService ps : printServices) {
212+
try {
213+
MediaSizeName msn = getTestMediaSizeName(ps);
214+
PageFormat pf = createTestPageFormat(msn, ps);
215+
testServiceList.add(new TestServiceData(ps, msn, pf));
216+
} catch (Exception ignore) { //in case if can't create required PageFormat
217+
}
218+
}
219+
return testServiceList.toArray(TestServiceData[]::new);
220+
}
221+
222+
private static MediaSizeName getTestMediaSizeName(PrintService printService) {
223+
//Use printer's default media or one of the alloed medias
224+
Media testMedia = (Media) printService.getDefaultAttributeValue(Media.class);
225+
if (testMedia == null) {
226+
Media[] medias = (Media[]) printService
227+
.getSupportedAttributeValues(Media.class, null, null);
228+
if (medias == null || medias.length == 0) {
229+
throw new RuntimeException("Medias not found");
230+
}
231+
for (Media media : medias) {
232+
if (ALLOWED_MEDIA_LIST.contains(media)) {
233+
testMedia = media;
234+
break;
235+
}
236+
}
237+
}
238+
if (!(testMedia instanceof MediaSizeName)) {
239+
throw new RuntimeException("Test media not found");
240+
}
241+
return (MediaSizeName) testMedia;
242+
}
243+
244+
private static PageFormat createTestPageFormat(MediaSizeName testMedia, PrintService printService) {
245+
MediaSize ms = MediaSize.getMediaSizeForName(testMedia);
246+
if (ms == null) {
247+
throw new RuntimeException("Media size not defined");
248+
}
249+
250+
MediaPrintableArea mpa = getMaximumMediaPrintableArea(testMedia, ms, printService);
251+
if (mpa == null) {
252+
throw new RuntimeException("Media printable area not defined");
253+
}
254+
255+
PageFormat pageFormat = new PageFormat();
256+
pageFormat.setOrientation(PageFormat.PORTRAIT);
257+
Paper paper = new Paper();
258+
paper.setSize(ms.getX(MediaSize.INCH) * DPI, ms.getY(MediaSize.INCH) * DPI);
259+
paper.setImageableArea(mpa.getX(MediaPrintableArea.INCH) * DPI,
260+
mpa.getY(MediaPrintableArea.INCH) * DPI,
261+
mpa.getWidth(MediaPrintableArea.INCH) * DPI,
262+
mpa.getHeight(MediaPrintableArea.INCH) * DPI);
263+
pageFormat.setPaper(paper);
264+
return pageFormat;
265+
}
266+
267+
private static MediaPrintableArea getMaximumMediaPrintableArea(MediaSizeName msn, MediaSize ms,
268+
PrintService printService) {
269+
final float paperSizeX = ms.getX(Size2DSyntax.MM);
270+
final float paperSizeY = ms.getY(Size2DSyntax.MM);
271+
final float sizeDev = 0.2f;
272+
273+
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
274+
attrs.add(msn);
275+
MediaPrintableArea[] mpas = (MediaPrintableArea[]) printService
276+
.getSupportedAttributeValues(MediaPrintableArea.class, null, attrs);
277+
if (mpas == null || mpas.length == 0) {
278+
throw new RuntimeException("Printable area not found");
279+
}
280+
281+
MediaPrintableArea mpa = null;
282+
for (MediaPrintableArea area : mpas) {
283+
float mpaSize = area.getWidth(MediaPrintableArea.MM) * area.getHeight(MediaPrintableArea.MM);
284+
//do not use borderless printable area
285+
if (sizeDev >= Math.abs(paperSizeX - area.getWidth(MediaPrintableArea.MM)) &&
286+
sizeDev >= Math.abs(paperSizeY - area.getHeight(MediaPrintableArea.MM))) {
287+
continue;
288+
}
289+
if (mpa == null) {
290+
mpa = area;
291+
} else if (mpaSize > (area.getWidth(MediaPrintableArea.MM) * area.getHeight(MediaPrintableArea.MM))) {
292+
mpa = area;
293+
}
294+
}
295+
return mpa;
296+
}
297+
298+
private static double inchesToMM(double inches) {
299+
return inches / MM_PER_INCH;
300+
}
301+
302+
private static void addGridBagComponent(JPanel p, Component c, int y) {
303+
GridBagConstraints constraints = new GridBagConstraints();
304+
constraints.fill = GridBagConstraints.HORIZONTAL;
305+
constraints.insets = new Insets(4, 4, 4, 4);
306+
constraints.gridx = 0;
307+
constraints.gridy = y;
308+
p.add(c, constraints);
309+
}
310+
311+
private static class RectPrintable implements Printable {
312+
313+
@Override
314+
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
315+
if (pageIndex == 0) {
316+
Graphics2D g = (Graphics2D) graphics;
317+
g.setStroke(new BasicStroke(3));
318+
g.drawRect((int) pageFormat.getImageableX(), (int) pageFormat.getImageableY(),
319+
(int) pageFormat.getImageableWidth(), (int) pageFormat.getImageableHeight());
320+
return PAGE_EXISTS;
321+
}
322+
return NO_SUCH_PAGE;
323+
}
324+
}
325+
326+
private static class TestServiceData {
327+
328+
final PrintService printService;
329+
final MediaSizeName mediaSizeName;
330+
final PageFormat pageFormat;
331+
332+
private TestServiceData(PrintService printService, MediaSizeName mediaSizeName, PageFormat pageFormat) {
333+
this.printService = printService;
334+
this.mediaSizeName = mediaSizeName;
335+
this.pageFormat = pageFormat;
336+
}
337+
}
338+
}

0 commit comments

Comments
 (0)