Index: modules/javafx.graphics/src/shims/java/javafx/print/PageLayoutShim.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/modules/javafx.graphics/src/shims/java/javafx/print/PageLayoutShim.java b/modules/javafx.graphics/src/shims/java/javafx/print/PageLayoutShim.java new file mode 100644 --- /dev/null (date 1665645604632) +++ b/modules/javafx.graphics/src/shims/java/javafx/print/PageLayoutShim.java (date 1665645604632) @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package javafx.print; + +public class PageLayoutShim { + + public static PageLayout createPageLayout(Paper paper, PageOrientation orient){ + return new PageLayout(paper, orient); + } + +} Index: modules/javafx.graphics/src/main/java/com/sun/prism/j2d/print/J2DPrinterJob.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/modules/javafx.graphics/src/main/java/com/sun/prism/j2d/print/J2DPrinterJob.java b/modules/javafx.graphics/src/main/java/com/sun/prism/j2d/print/J2DPrinterJob.java --- a/modules/javafx.graphics/src/main/java/com/sun/prism/j2d/print/J2DPrinterJob.java (revision 9ab4e0198166e06c972d93a918112bf4fd6d1f0c) +++ b/modules/javafx.graphics/src/main/java/com/sun/prism/j2d/print/J2DPrinterJob.java (date 1665666097714) @@ -153,17 +153,25 @@ fxPrinter = fxPrinterJob.getPrinter(); j2dPrinter = getJ2DPrinter(fxPrinter); settings = fxPrinterJob.getJobSettings(); - pJob2D = java.awt.print.PrinterJob.getPrinterJob(); + pJob2D = createPrinterJob(); try { pJob2D.setPrintService(j2dPrinter.getService()); } catch (PrinterException pe) { } printReqAttrSet = new HashPrintRequestAttributeSet(); printReqAttrSet.add(DialogTypeSelection.NATIVE); - j2dPageable = new J2DPageable(); + j2dPageable = createJ2dPageable(); pJob2D.setPageable(j2dPageable); } + protected java.awt.print.PrinterJob createPrinterJob() { + return java.awt.print.PrinterJob.getPrinterJob(); + } + + protected J2DPageable createJ2dPageable() { + return new J2DPageable(); + } + private void setEnabledState(Window owner, boolean state) { if (owner == null) { return; @@ -1035,7 +1043,7 @@ } } - private class J2DPageable implements Pageable, Printable { + protected class J2DPageable implements Pageable, Printable { private volatile boolean pageDone; @@ -1061,7 +1069,7 @@ } } currPageInfo = null; - pageDone = true; + setPageDone(true); synchronized (monitor) { if (newPageInfo == null) { monitor.notify(); // page is printed and no new page to print @@ -1171,6 +1179,10 @@ return this; } + protected PageFormat getCurrentPageFormat() { + return currPageFormat; + } + public PageFormat getPageFormat(int pageIndex) { getPage(pageIndex); return currPageFormat; @@ -1202,7 +1214,7 @@ * So, when we are in here, we know that the app is providing * the info for the next page. */ - pageDone = false; + setPageDone(false); synchronized (monitor) { newPageInfo = new PageInfo(pageLayout, node); monitor.notify(); @@ -1225,6 +1237,10 @@ } } + protected void setPageDone(boolean pageDone) { + this.pageDone = pageDone; + } + } /* END J2DPageable class */ Index: modules/javafx.graphics/src/test/addExports IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/modules/javafx.graphics/src/test/addExports b/modules/javafx.graphics/src/test/addExports --- a/modules/javafx.graphics/src/test/addExports (revision 9ab4e0198166e06c972d93a918112bf4fd6d1f0c) +++ b/modules/javafx.graphics/src/test/addExports (date 1665556186974) @@ -40,11 +40,13 @@ --add-exports javafx.graphics/com.sun.prism.impl.shape=ALL-UNNAMED --add-exports javafx.graphics/com.sun.prism=ALL-UNNAMED --add-exports javafx.graphics/com.sun.prism.paint=ALL-UNNAMED +--add-exports javafx.graphics/com.sun.prism.j2d.print=ALL-UNNAMED --add-exports javafx.graphics/com.sun.scenario.animation=ALL-UNNAMED --add-exports javafx.graphics/com.sun.scenario.animation.shared=ALL-UNNAMED --add-exports javafx.graphics/com.sun.scenario.effect=ALL-UNNAMED --add-exports javafx.graphics/com.sun.scenario.effect.light=ALL-UNNAMED --add-exports javafx.graphics/com.sun.scenario=ALL-UNNAMED +--add-exports javafx.graphics/sun.print=ALL-UNNAMED --add-opens javafx.graphics/javafx.scene=ALL-UNNAMED --add-opens javafx.graphics/javafx.scene.robot=ALL-UNNAMED --add-opens javafx.graphics/javafx.scene.layout=ALL-UNNAMED Index: modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java --- a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java (revision 9ab4e0198166e06c972d93a918112bf4fd6d1f0c) +++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java (date 1665645535939) @@ -98,6 +98,9 @@ private ScreenConfiguration[] screenConfigurations = DEFAULT_SCREEN_CONFIG; + // By default, always on the FX app thread + private boolean fxUserThread = true; + static { try { // ugly hack to initialize "runLater" method in Platform.java @@ -153,8 +156,11 @@ @Override public boolean isFxUserThread() { - // Always on the FX app thread - return true; + return fxUserThread; + } + + public void setFxUserThread(boolean fxUserThread) { + this.fxUserThread = fxUserThread; } @Override Index: modules/javafx.graphics/src/test/java/test/com/sun/prism/j2d/print/J2DPrinterJobTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/prism/j2d/print/J2DPrinterJobTest.java b/modules/javafx.graphics/src/test/java/test/com/sun/prism/j2d/print/J2DPrinterJobTest.java new file mode 100644 --- /dev/null (date 1665665750550) +++ b/modules/javafx.graphics/src/test/java/test/com/sun/prism/j2d/print/J2DPrinterJobTest.java (date 1665665750550) @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package test.com.sun.prism.j2d.print; + +import java.awt.HeadlessException; +import java.awt.print.PageFormat; +import java.awt.print.Pageable; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.io.ByteArrayOutputStream; +import java.util.function.Supplier; + +import javax.print.DocFlavor; +import javax.print.DocPrintJob; +import javax.print.PrintService; +import javax.print.ServiceUIFactory; +import javax.print.StreamPrintService; +import javax.print.attribute.Attribute; +import javax.print.attribute.AttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.PrintServiceAttribute; +import javax.print.attribute.PrintServiceAttributeSet; +import javax.print.event.PrintServiceAttributeListener; + +import com.sun.javafx.tk.Toolkit; +import com.sun.prism.j2d.print.J2DPrinter; +import com.sun.prism.j2d.print.J2DPrinterJob; + +import org.junit.Before; +import org.junit.Test; + +import javafx.print.PageLayoutShim; +import javafx.print.PageOrientation; +import javafx.print.Paper; +import javafx.print.PrinterJob; +import javafx.print.PrinterShim; +import javafx.scene.ParentShim; +import test.com.sun.javafx.pgstub.StubToolkit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class J2DPrinterJobTest { + + private J2DPrinterJob job; + private Supplier pageFormatAccessor; + + @Before + public void setUp() { + // Otherwise, the printer job will try to create a nestedLoop which is not supported by the StubToolkit. + ((StubToolkit) Toolkit.getToolkit()).setFxUserThread(false); + + PrinterJob printerJob = PrinterJob.createPrinterJob(PrinterShim.createPrinter(new J2DPrinter(new PrintServiceMock()))); + + job = new J2DPrinterJob(printerJob) { + + @Override + protected java.awt.print.PrinterJob createPrinterJob() { + return new TestPrinterJobMock(); + } + + @Override + protected J2DPrinterJob.J2DPageable createJ2dPageable() { + J2DPageable pageable = new J2DPageable(); + + // The printer thread initializes the current page format when it processes a page. + // We use it to check if the page has been printed. + pageFormatAccessor = pageable::getCurrentPageFormat; + + return pageable; + } + + class J2DPageable extends J2DPrinterJob.J2DPageable { + @Override + public void setPageDone(boolean pageDone) { + super.setPageDone(pageDone); + + String methodName = Thread.currentThread().getStackTrace()[2].getMethodName(); + if (methodName.equals("waitForNextPage")) { + // Stop the function 'waitForNextPage()' for a while. + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } else if (!methodName.equals("implPrintPage")) { + fail("The test needs to be revised, " + + "because the setPageDone() method is only expected in the 'waitForNextPage()' and 'implPrintPage()' methods, " + + "but it was called from '" + methodName + "()'"); + } + } + + @Override + public PageFormat getCurrentPageFormat() { + return super.getCurrentPageFormat(); + } + } + }; + } + + @Test + public void testJobEnd() throws InterruptedException { + assertTrue(job.print(PageLayoutShim.createPageLayout(Paper.A4, PageOrientation.PORTRAIT), new ParentShim())); + job.endJob(); + Thread.sleep(1000); // wait until the last page is printed + assertNotNull("The submitted page was not printed.", pageFormatAccessor.get()); + } + + @Test + public void testJobCanceled() throws InterruptedException { + assertTrue(job.print(PageLayoutShim.createPageLayout(Paper.A4, PageOrientation.PORTRAIT), new ParentShim())); + job.cancelJob(); + Thread.sleep(1000); // wait until the last page is printed + assertNull("The page was printed even though the job was canceled.", pageFormatAccessor.get()); + } + + private static class PrintServiceMock extends StreamPrintService { + public PrintServiceMock() { + super(new ByteArrayOutputStream()); + } + + @Override + public String getOutputFormat() { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public DocPrintJob createPrintJob() { + return null; + } + + @Override + public void addPrintServiceAttributeListener(PrintServiceAttributeListener listener) { + + } + + @Override + public void removePrintServiceAttributeListener(PrintServiceAttributeListener listener) { + + } + + @Override + public PrintServiceAttributeSet getAttributes() { + return null; + } + + @Override + public T getAttribute(Class category) { + return null; + } + + @Override + public DocFlavor[] getSupportedDocFlavors() { + return new DocFlavor[0]; + } + + @Override + public boolean isDocFlavorSupported(DocFlavor flavor) { + return false; + } + + @Override + public Class[] getSupportedAttributeCategories() { + return new Class[0]; + } + + @Override + public boolean isAttributeCategorySupported(Class category) { + return false; + } + + @Override + public Object getDefaultAttributeValue(Class category) { + return null; + } + + @Override + public Object getSupportedAttributeValues(Class category, DocFlavor flavor, AttributeSet attributes) { + return null; + } + + @Override + public boolean isAttributeValueSupported(Attribute attrval, DocFlavor flavor, AttributeSet attributes) { + return false; + } + + @Override + public AttributeSet getUnsupportedAttributes(DocFlavor flavor, AttributeSet attributes) { + return null; + } + + @Override + public ServiceUIFactory getServiceUIFactory() { + return null; + } + } + + private static class TestPrinterJobMock extends java.awt.print.PrinterJob { + public PrintService service; + public Pageable pageable; + + @Override + public void setPrintService(PrintService service) throws PrinterException { + this.service = service; + } + + public PrintService getPrintService() { + return service; + } + + @Override + public void setPageable(Pageable pageable) throws NullPointerException { + this.pageable = pageable; + } + + @Override + public void print(PrintRequestAttributeSet attributes) throws PrinterException { + pageable.getPageFormat(0); + } + + @Override + public void print() throws PrinterException { + } + + @Override + public void setPrintable(Printable painter) { + + } + + @Override + public void setPrintable(Printable painter, PageFormat format) { + + } + + @Override + public boolean printDialog() throws HeadlessException { + return false; + } + + @Override + public PageFormat pageDialog(PageFormat page) throws HeadlessException { + return null; + } + + @Override + public PageFormat defaultPage(PageFormat page) { + return null; + } + + @Override + public PageFormat validatePage(PageFormat page) { + return null; + } + + @Override + public void setCopies(int copies) { + + } + + @Override + public int getCopies() { + return 0; + } + + @Override + public String getUserName() { + return null; + } + + @Override + public void setJobName(String jobName) { + + } + + @Override + public String getJobName() { + return null; + } + + @Override + public void cancel() { + + } + + @Override + public boolean isCancelled() { + return false; + } + } +} Index: modules/javafx.graphics/src/shims/java/javafx/print/PrinterShim.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/modules/javafx.graphics/src/shims/java/javafx/print/PrinterShim.java b/modules/javafx.graphics/src/shims/java/javafx/print/PrinterShim.java new file mode 100644 --- /dev/null (date 1665645604639) +++ b/modules/javafx.graphics/src/shims/java/javafx/print/PrinterShim.java (date 1665645604639) @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package javafx.print; + +import com.sun.javafx.print.PrinterImpl; + +public class PrinterShim { + public static Printer createPrinter(PrinterImpl impl) { + return new Printer(impl); + } +}