File: CUPSPrinterImageableAreaTest.java

package info (click to toggle)
openjdk-25 25.0.1%2B8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 825,408 kB
  • sloc: java: 5,585,680; cpp: 1,333,948; xml: 1,321,242; ansic: 488,034; asm: 404,003; objc: 21,088; sh: 15,106; javascript: 13,265; python: 8,319; makefile: 2,518; perl: 357; awk: 351; pascal: 103; exp: 83; sed: 72; jsp: 24
file content (338 lines) | stat: -rw-r--r-- 14,186 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/*
 * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2025, BELLSOFT. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.util.ArrayList;
import java.util.List;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.Size2DSyntax;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.MediaSizeName;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.plaf.basic.BasicComboBoxRenderer;

/*
 * @test
 * @bug 8344119
 * @key printer
 * @requires (os.family == "linux" | os.family == "mac")
 * @library /java/awt/regtesthelpers
 * @build PassFailJFrame
 * @summary CUPSPrinter imageable area
 * @run main/manual CUPSPrinterImageableAreaTest
 */

public class CUPSPrinterImageableAreaTest {

    private static final List<MediaSizeName> ALLOWED_MEDIA_LIST = List.of(MediaSizeName.ISO_A4, MediaSizeName.NA_LETTER);
    private static final double DPI = 72.0;
    private static final double MM_PER_INCH = 2.54;
    private static final String INSTRUCTIONS = """
            <html>
                <div>
                The test checks that the media margins fetched from the printer's PPD file are correct.<br>
                Press the '<b>Print sample</b>' button to print a test page.<br>
                Required paper size and expected margins will be shown on the print dialog.
                A passing test will print the page with a black rectangle along the printable area.
                Ensure that all sides of the rectangle are printed.<br>
                Click '<b>Pass</b>' button, or click '<b>Fail</b>' button if the test failed.
                </div>
            <html>
            """;
    private static final String PAPER_INSTRUCTIONS_FORMAT = """
            <html><body style='margin: 0; text-align:left;'>
            Required paper size: <ul><li>%s</li></ul>
            Expected margins: <ul><li>left: %.1f</li>
            <li>bottom: %.1f</li>
            <li>right: %.1f</li>
            <li>top: %.1f</li>
            </ul></body></html>
            """;

    public static void main(String[] args) throws Exception {
        PassFailJFrame.builder()
                .instructions(INSTRUCTIONS)
                .testUI(createTestUI())
                .columns(55)
                .build()
                .awaitAndCheck();
    }

    private static JFrame createTestUI() {
        final TestServiceData[] testServiceList = getTestServiceList();
        if (testServiceList.length == 0) {
            throw new RuntimeException("Print services support borderless print only");
        }

        final JFrame frame = new JFrame("CUPS Printer imageable area test");
        JPanel pnlRoot = new JPanel();
        JLabel lblPrintServices = new JLabel("Select a print service for the test");
        JComboBox<String> cbPrintServices = new JComboBox<>();
        JPanel pnlInstruction = new JPanel();
        JLabel lblInstruction = new JLabel();
        JButton btnPrint = new JButton("Print sample");

        lblPrintServices.setLabelFor(cbPrintServices);
        lblPrintServices.setAlignmentX(SwingConstants.LEFT);

        lblInstruction.setPreferredSize(new Dimension(250, 150));
        pnlInstruction.setBackground(Color.white);
        pnlInstruction.add(lblInstruction);

        cbPrintServices.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int selectedIndex = cbPrintServices.getSelectedIndex();
                if (selectedIndex < 0) {
                    lblInstruction.setText("");
                    btnPrint.setEnabled(false);
                    return;
                }

                TestServiceData testServiceData = testServiceList[selectedIndex];
                PageFormat pageFormat = testServiceData.pageFormat;
                // margins: left, bottom, right, top
                double[] margins = new double[]{
                        pageFormat.getImageableX(),
                        pageFormat.getHeight() - pageFormat.getImageableHeight() - pageFormat.getImageableY(),
                        pageFormat.getWidth() - pageFormat.getImageableWidth() - pageFormat.getImageableX(),
                        pageFormat.getImageableY()
                };
                String printServiceInstructions = PAPER_INSTRUCTIONS_FORMAT.formatted(
                        testServiceData.mediaSizeName.toString(), inchesToMM(margins[0]),
                        inchesToMM(margins[1]), inchesToMM(margins[2]), inchesToMM(margins[3]));
                lblInstruction.setText(printServiceInstructions);
                btnPrint.setEnabled(true);
            }
        });

        DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
        for(TestServiceData tsd : testServiceList) {
            model.addElement(tsd.printService.getName());
        }
        cbPrintServices.setModel(model);
        cbPrintServices.setSelectedIndex(-1);
        PrintService defaultPrintService = PrintServiceLookup.lookupDefaultPrintService();
        if (defaultPrintService != null && model.getIndexOf(defaultPrintService.getName()) >= 0) {
            cbPrintServices.setSelectedItem(defaultPrintService.getName());
        } else {
            cbPrintServices.setSelectedIndex(0);
        }

        btnPrint.setPreferredSize(new Dimension(200, 80));
        btnPrint.addActionListener((e) -> {
            int selectedIndex = cbPrintServices.getSelectedIndex();
            if (selectedIndex < 0) {
                return;
            }
            btnPrint.setEnabled(false);
            cbPrintServices.setEnabled(false);
            TestServiceData testServiceData = testServiceList[selectedIndex];
            PrinterJob job = PrinterJob.getPrinterJob();
            try {
                job.setPrintService(testServiceData.printService);
                job.setPrintable(new RectPrintable(), testServiceData.pageFormat);
                job.print();
            } catch (PrinterException ex) {
                throw new RuntimeException(ex);
            }
        });

        LayoutManager layout = new GridBagLayout();
        pnlRoot.setLayout(layout);

        addGridBagComponent(pnlRoot, lblPrintServices, 0);
        addGridBagComponent(pnlRoot, cbPrintServices, 1);
        addGridBagComponent(pnlRoot, pnlInstruction, 2);
        addGridBagComponent(pnlRoot, btnPrint, 3);

        frame.add(pnlRoot);
        frame.pack();
        frame.setResizable(false);
        return frame;
    }

    private static TestServiceData[] getTestServiceList() {
        PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
        if (printServices == null || printServices.length == 0) {
            throw new RuntimeException("Print services not found");
        }

        List<TestServiceData> testServiceList = new ArrayList<>();
        for (PrintService ps : printServices) {
            try {
                MediaSizeName msn = getTestMediaSizeName(ps);
                PageFormat pf = createTestPageFormat(msn, ps);
                testServiceList.add(new TestServiceData(ps, msn, pf));
            } catch (Exception ignore) { //in case if can't create required PageFormat
            }
        }
        return testServiceList.toArray(TestServiceData[]::new);
    }

    private static MediaSizeName getTestMediaSizeName(PrintService printService) {
        //Use printer's default media or one of the alloed medias
        Media testMedia = (Media) printService.getDefaultAttributeValue(Media.class);
        if (testMedia == null) {
            Media[] medias = (Media[]) printService
                    .getSupportedAttributeValues(Media.class, null, null);
            if (medias == null || medias.length == 0) {
                throw new RuntimeException("Medias not found");
            }
            for (Media media : medias) {
                if (ALLOWED_MEDIA_LIST.contains(media)) {
                    testMedia = media;
                    break;
                }
            }
        }
        if (!(testMedia instanceof MediaSizeName)) {
            throw new RuntimeException("Test media not found");
        }
        return (MediaSizeName) testMedia;
    }

    private static PageFormat createTestPageFormat(MediaSizeName testMedia, PrintService printService) {
        MediaSize ms = MediaSize.getMediaSizeForName(testMedia);
        if (ms == null) {
            throw new RuntimeException("Media size not defined");
        }

        MediaPrintableArea mpa = getMaximumMediaPrintableArea(testMedia, ms, printService);
        if (mpa == null) {
            throw new RuntimeException("Media printable area not defined");
        }

        PageFormat pageFormat = new PageFormat();
        pageFormat.setOrientation(PageFormat.PORTRAIT);
        Paper paper = new Paper();
        paper.setSize(ms.getX(MediaSize.INCH) * DPI, ms.getY(MediaSize.INCH) * DPI);
        paper.setImageableArea(mpa.getX(MediaPrintableArea.INCH) * DPI,
                mpa.getY(MediaPrintableArea.INCH) * DPI,
                mpa.getWidth(MediaPrintableArea.INCH) * DPI,
                mpa.getHeight(MediaPrintableArea.INCH) * DPI);
        pageFormat.setPaper(paper);
        return pageFormat;
    }

    private static MediaPrintableArea getMaximumMediaPrintableArea(MediaSizeName msn, MediaSize ms,
                                                                   PrintService printService) {
        final float paperSizeX = ms.getX(Size2DSyntax.MM);
        final float paperSizeY = ms.getY(Size2DSyntax.MM);
        final float sizeDev = 0.2f;

        PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
        attrs.add(msn);
        MediaPrintableArea[] mpas = (MediaPrintableArea[]) printService
                .getSupportedAttributeValues(MediaPrintableArea.class, null, attrs);
        if (mpas == null || mpas.length == 0) {
            throw new RuntimeException("Printable area not found");
        }

        MediaPrintableArea mpa = null;
        for (MediaPrintableArea area : mpas) {
            float mpaSize = area.getWidth(MediaPrintableArea.MM) * area.getHeight(MediaPrintableArea.MM);
            //do not use borderless printable area
            if (sizeDev >= Math.abs(paperSizeX - area.getWidth(MediaPrintableArea.MM)) &&
                    sizeDev >= Math.abs(paperSizeY - area.getHeight(MediaPrintableArea.MM))) {
                continue;
            }
            if (mpa == null) {
                mpa = area;
            } else if (mpaSize > (area.getWidth(MediaPrintableArea.MM) * area.getHeight(MediaPrintableArea.MM))) {
                mpa = area;
            }
        }
        return mpa;
    }

    private static double inchesToMM(double inches) {
        return inches / MM_PER_INCH;
    }

    private static void addGridBagComponent(JPanel p, Component c, int y) {
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.insets = new Insets(4, 4, 4, 4);
        constraints.gridx = 0;
        constraints.gridy = y;
        p.add(c, constraints);
    }

    private static class RectPrintable implements Printable {

        @Override
        public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
            if (pageIndex == 0) {
                Graphics2D g = (Graphics2D) graphics;
                g.setStroke(new BasicStroke(3));
                g.drawRect((int) pageFormat.getImageableX(), (int) pageFormat.getImageableY(),
                        (int) pageFormat.getImageableWidth(), (int) pageFormat.getImageableHeight());
                return PAGE_EXISTS;
            }
            return NO_SUCH_PAGE;
        }
    }

    private static class TestServiceData {

        final PrintService printService;
        final MediaSizeName mediaSizeName;
        final PageFormat pageFormat;

        private TestServiceData(PrintService printService, MediaSizeName mediaSizeName, PageFormat pageFormat) {
            this.printService = printService;
            this.mediaSizeName = mediaSizeName;
            this.pageFormat = pageFormat;
        }
    }
}