All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.prism.j2d.print.J2DPrinterJob Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013, 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 com.sun.prism.j2d.print;

import java.util.ArrayList;
import java.util.Set;

import javafx.scene.*;
import javafx.scene.shape.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.print.Pageable;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;

import javax.print.PrintService;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.ResolutionSyntax;
import javax.print.attribute.Size2DSyntax;
import javax.print.attribute.standard.Chromaticity;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.Destination;
import javax.print.attribute.standard.DialogTypeSelection;
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.print.attribute.standard.MediaTray;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.attribute.standard.PageRanges;
import javax.print.attribute.standard.PrintQuality;
import javax.print.attribute.standard.PrinterResolution;
import javax.print.attribute.standard.SheetCollate;
import javax.print.attribute.standard.Sides;

import javafx.stage.Window;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import com.sun.javafx.scene.NodeHelper;
import com.sun.javafx.tk.Toolkit;
import com.sun.glass.ui.Application;

import java.security.AccessController;
import java.security.PrivilegedAction;

import javafx.print.JobSettings;
import javafx.print.PageLayout;
import javafx.print.PageOrientation;
import javafx.print.Paper;
import javafx.print.Printer;
import javafx.print.Printer.MarginType;
import javafx.print.PrinterAttributes;
import javafx.print.Collation;
import javafx.print.PageOrientation;
import static javafx.print.PageOrientation.*;

import javafx.print.PageRange;
import javafx.print.PaperSource;
import javafx.print.PrintColor;
import javafx.print.PrintResolution;
import javafx.print.PrintSides;

import com.sun.javafx.print.PrintHelper;
import com.sun.javafx.print.PrinterImpl;
import com.sun.javafx.print.PrinterJobImpl;

import javafx.collections.ObservableList;
import com.sun.javafx.sg.BaseNode;
import com.sun.javafx.sg.PGNode;
import com.sun.prism.j2d.PrismPrintGraphics;

public class J2DPrinterJob implements PrinterJobImpl {

    javafx.print.PrinterJob fxPrinterJob;
    java.awt.print.PrinterJob pJob2D;
    javafx.print.Printer fxPrinter;
    J2DPrinter j2dPrinter;

    private JobSettings settings;
    private PrintRequestAttributeSet printReqAttrSet;

    public J2DPrinterJob(javafx.print.PrinterJob fxJob) {

        fxPrinterJob = fxJob;
        fxPrinter = fxPrinterJob.getPrinter();
        j2dPrinter = getJ2DPrinter(fxPrinter);
        settings = fxPrinterJob.getJobSettings();
        pJob2D = java.awt.print.PrinterJob.getPrinterJob();
        printReqAttrSet = new HashPrintRequestAttributeSet();
        // dialog selection is a JDK 1.7 attribute.
        // We expect to run on 1.8 and above so this should be fine.
        printReqAttrSet.add(DialogTypeSelection.NATIVE);
    }

    public boolean showPrintDialog(Window owner) {

        if (jobRunning || jobDone) {
            return false;
        }

        if (GraphicsEnvironment.isHeadless()) {
            return true;
        }

        boolean rv = false;
        syncSettingsToAttributes();
        if (!Toolkit.getToolkit().isFxUserThread()) {
            rv = pJob2D.printDialog(printReqAttrSet);
        } else {
            rv = showPrintDialogWithNestedLoop(owner);
        }
        if (rv) {
            updateSettingsFromDialog();
        }
        return rv;
    }

    private class PrintDialogRunnable implements Runnable {

        volatile boolean inNestedLoop = true;

        public void run() {
            boolean rv = false;
            try {
                rv = pJob2D.printDialog(printReqAttrSet);
            } catch (Exception e) {
            } finally {
                synchronized (this) {
                    inNestedLoop = false;
                    Application.invokeLater(new ExitLoopRunnable(this, rv));
                    this.notify();
                }
            }
        }
    }

    private boolean showPrintDialogWithNestedLoop(Window owner) {
        PrintDialogRunnable dr = new PrintDialogRunnable();
        Thread prtThread = new Thread(dr, "FX Print Dialog Thread");
        prtThread.start();
        // the nested event loop will return after the runnable exits.
        Object rv = null;
        synchronized (dr) {
            if (dr.inNestedLoop) {
                try {
                    dr.wait();
                } catch (InterruptedException e) {
                }
                rv = Toolkit.getToolkit().enterNestedEventLoop(dr);
            }
        }
        boolean rvbool = false;
        try {
            rvbool = ((Boolean)rv).booleanValue();
        } catch (Exception e) {
        }
        return rvbool;
    }

    public boolean showPageDialog(Window owner) {
        if (jobRunning || jobDone) {
            return false;
        }
        if (GraphicsEnvironment.isHeadless()) {
            return true;
        }
        boolean rv = false;
        syncSettingsToAttributes();
        if (!Toolkit.getToolkit().isFxUserThread()) {
            PageFormat pf = pJob2D.pageDialog(printReqAttrSet);
            rv = pf != null;
        } else {
            rv = showPageDialogFromNestedLoop(owner);
        }
        if (rv) {
            updateSettingsFromDialog();
        }
        return rv;
    }

    private class PageDialogRunnable implements Runnable {

        volatile boolean inNestedLoop = true;

        public void run() {
            PageFormat pf = null;
            try {
                pf = pJob2D.pageDialog(printReqAttrSet);
            } catch (Exception e) {
            } finally {
                synchronized (this) {
                    inNestedLoop = false;
                    Boolean rv = Boolean.valueOf(pf != null);
                    Application.invokeLater(new ExitLoopRunnable(this, rv));
                    this.notify();
                }
            }
        }
    }

    private boolean showPageDialogFromNestedLoop(Window owner) {

        PageDialogRunnable dr = new PageDialogRunnable();
        Thread prtThread = new Thread(dr, "FX Page Setup Dialog Thread");
        prtThread.start();
        // the nested event loop will return after the runnable exits.
        Object rv = null;
        synchronized (dr) {
            if (dr.inNestedLoop) {
                try {
                    dr.wait();
                } catch (InterruptedException e) {
                }
                rv = Toolkit.getToolkit().enterNestedEventLoop(dr);
            }
        }
        boolean rvbool = false;
        try {
            rvbool = ((Boolean)rv).booleanValue();
        } catch (Exception e) {
        }
        return rvbool;
    }

    /*
     * The update-Foo methods here are only used to update the
     * FX JobSettings as a result of changes by user interaction
     * with a print dialog. The new values are stored in the
     * PrintRequestAttributeSet and pulled from there in to the
     * equivalent FX public API JobSettings.
     */
    private void updateJobName() {
        String name =  pJob2D.getJobName();
        if (!name.equals(settings.getJobName())) {
            settings.setJobName(name);
        }
    }
    private void updateCopies() {
        int nCopies = pJob2D.getCopies();
        if (settings.getCopies() != nCopies) {
            settings.setCopies(nCopies);
        }
    }

    private void updatePageRanges() {
        PageRanges ranges = (PageRanges)printReqAttrSet.get(PageRanges.class);
        // JDK sets default to 1,Integer.MAX_VALUE
        // So in this case I think we can just check for non-null and
        // only set if its non-null.
        if (ranges != null) {
            int[][] members = ranges.getMembers();
            if (members.length == 1) {
                PageRange range = new PageRange(members[0][0], members[0][1]);
                settings.setPageRanges(range);
            } else if (members.length > 0) {
                try {
                    ArrayList prList = new ArrayList();
                    int last = 0;
                    for (int i=0; i printerSet = Printer.getAllPrinters();
        for (Printer p : printerSet) {
            J2DPrinter p2d = (J2DPrinter)PrintHelper.getPrinterImpl(p);
            PrintService s = p2d.getService();
            if (s.equals(service)) {
                return p;
            }
        }
        return fxPrinter; // current printer.
    }

    public void setPrinterImpl(PrinterImpl impl) {
        j2dPrinter = (J2DPrinter)impl;
        fxPrinter = j2dPrinter.getPrinter();
    }

    public PrinterImpl getPrinterImpl() {
        return j2dPrinter;
    }

    private J2DPrinter getJ2DPrinter(Printer printer) {
        return (J2DPrinter)PrintHelper.getPrinterImpl(printer);
    }

    public Printer getPrinter() {
        return fxPrinter;
    }

    public void setPrinter(Printer printer) {
        fxPrinter = printer;
        j2dPrinter = getJ2DPrinter(printer);
    }

    private void updatePrinter() {
        PrintService currService = j2dPrinter.getService();
        PrintService jobService = pJob2D.getPrintService();
        if (currService.equals(jobService)) {
            return; // no change
        }
        Printer newFXPrinter = getFXPrinterForService(jobService);
        // The public setPrinter call also updates the job to be valid for
        // the new printer. Any old values not supported will be updated
        // to supported values. If we do that, then apply the new user
        // settings, any listener will see both sets of changes.
        // Its best to just see the single transition.
        fxPrinterJob.setPrinter(newFXPrinter);
    }

    private void updateSettingsFromDialog() {
        updatePrinter();
        updateJobName();
        updateCopies();
        updatePageRanges();
        updateSides();
        updateCollation();
        updatePageLayout();
        updatePaperSource();
        updateColor();
        updatePrintQuality();
        updatePrintResolution();
    }

    private void syncSettingsToAttributes() {
        syncJobName();
        syncCopies();
        syncPageRanges();
        syncSides();
        syncCollation();
        syncPageLayout();
        syncPaperSource();
        syncColor();
        syncPrintQuality();
        syncPrintResolution();
    }

    private void syncJobName() {
        pJob2D.setJobName(settings.getJobName());
    }

    private void syncCopies() {
        pJob2D.setCopies(settings.getCopies());
        printReqAttrSet.add(new Copies(settings.getCopies()));
    }

    private void syncPageRanges() {
        printReqAttrSet.remove(PageRanges.class);
        PageRange[] prArr = settings.getPageRanges();
        if (prArr != null && prArr.length>0) {
            int len = prArr.length;
            int[][] ranges = new int[len][2];
            for (int i=0;i currPageIndex) {
                nextPage = waitForNextPage(pageIndex);
            } else if (pageIndex < currPageIndex) {
                System.err.println("PAGE INDEX DECREASED");
            }
            return nextPage;
        }

        public int print(Graphics g, PageFormat pf, int pageIndex) {
            if (jobError || jobDone || !getPage(pageIndex)) {
                return Printable.NO_SUCH_PAGE;
            }
            int x = (int)pf.getImageableX();
            int y = (int)pf.getImageableY();
            int w = (int)pf.getImageableWidth();
            int h = (int)pf.getImageableHeight();
            Node appNode = currPageInfo.getNode();
            g.translate(x, y);
            printNode(appNode, g, w, h);
            return Printable.PAGE_EXISTS;
        }


        private void printNode(Node node, Graphics g, int w, int h) {

            PrismPrintGraphics ppg =
                new PrismPrintGraphics((Graphics2D)g, w, h);
            PGNode pgNode = node.impl_getPGNode();
            ((BaseNode)pgNode).render(ppg);
        }

        public Printable getPrintable(int pageIndex) {
            getPage(pageIndex);
            return this;
        }

        public PageFormat getPageFormat(int pageIndex) {
            getPage(pageIndex);
            return currPageFormat;
        }

        /*
         * Since we return unknown number of pages, then
         * the behaviour must be that we can only signal
         * end of the job by returning NO_SUCH_PAGE from
         * the print(..) method.
         */
        public int getNumberOfPages() {
            return Pageable.UNKNOWN_NUMBER_OF_PAGES;
        }


        private volatile Object elo = null;

        /*
         * Executed on the application's thread.
         * Messages over to the printing thread.
         */
        private void implPrintPage(PageLayout pageLayout, Node node) {

            /* The public API printPage() is synchronized, so we know
             * that the app can't call it from 2 threads at the same
             * time, not that this is encouraged either.
             * Therefore when we are in this code, we know that any
             * previous page rendering has completed.
             * We also know that this means the app can't have 'queued up'
             * pages.
             * So, when we are in here, we know that the app is providing
             * the info for the next page.
             */
            pageDone = false;
            synchronized (monitor) {
                newPageInfo = new PageInfo(pageLayout, node);
                monitor.notify();
            }
            if (Toolkit.getToolkit().isFxUserThread()) {
                elo = new Object();
                Toolkit.getToolkit().enterNestedEventLoop(elo);
                elo = null;
            } else {
                while (!pageDone) {
                    synchronized (monitor) {
                        try {
                            monitor.wait(1000);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }
        }

    } /* END J2DPageable class */


    public boolean endJob() {
        if (jobRunning && !jobDone &&!jobError) {
            jobDone = true;
            try {
                synchronized (monitor) {
                    monitor.notify();
                    return jobDone;
                }
            } catch (IllegalStateException e) {
                System.err.println("Internal Error " + e);
            }
        } else {
            return false;
        }
        return jobDone;
    }

    public void cancelJob() {
        if (!pJob2D.isCancelled()) {
            pJob2D.cancel();
        }
        jobDone = true;
        if (jobRunning) {
            jobRunning = false;
            try {
                synchronized (monitor) {
                    monitor.notify();
                }
            } catch (IllegalStateException e) {
                System.err.println("Internal Error " + e);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy