839 lines · 30.2 KB · Java
RawDownload
     * This method is to be called on the EDT only. Layout should be done before     * calling this method.     */    private int printOnEDT(final Graphics graphics,            final PageFormat pf,            final int pageIndex) throws PrinterException {        assert SwingUtilities.isEventDispatchThread();        Border border = BorderFactory.createEmptyBorder();        //handle header and footer        if (headerFormat != null || footerFormat != null) {            //Printable page enumeration is 0 base. We need 1 based.            Object[] formatArg = new Object[]{Integer.valueOf(pageIndex + 1)};            if (headerFormat != null) {                border = new TitledBorder(border,                    headerFormat.format(formatArg),                    TitledBorder.CENTER, TitledBorder.ABOVE_TOP,                    headerFont, printShell.getForeground());            }            if (footerFormat != null) {                border = new TitledBorder(border,                    footerFormat.format(formatArg),                    TitledBorder.CENTER, TitledBorder.BELOW_BOTTOM,                    footerFont, printShell.getForeground());            }        }        Insets borderInsets = border.getBorderInsets(printShell);        updatePagesMetrics(pageIndex,            (int)Math.floor(pf.getImageableHeight()) - borderInsets.top                           - borderInsets.bottom);         if (pagesMetrics.size() <= pageIndex) {            return NO_SUCH_PAGE;        }         Graphics2D g2d = (Graphics2D)graphics.create();         g2d.translate(pf.getImageableX(), pf.getImageableY());        border.paintBorder(printShell, g2d, 0, 0,            (int)Math.floor(pf.getImageableWidth()),            (int)Math.floor(pf.getImageableHeight()));         g2d.translate(0, borderInsets.top);        //want to clip only vertically        Rectangle clip = new Rectangle(0, 0,            (int) pf.getWidth(),            pagesMetrics.get(pageIndex).end                - pagesMetrics.get(pageIndex).start + 1);         g2d.clip(clip);        int xStart = 0;        if (ComponentOrientation.RIGHT_TO_LEFT ==                printShell.getComponentOrientation()) {            xStart = (int) pf.getImageableWidth() - printShell.getWidth();        }        g2d.translate(xStart, - pagesMetrics.get(pageIndex).start);        printShell.print(g2d);         g2d.dispose();         return Printable.PAGE_EXISTS;    }      private boolean needReadLock = false;     /**     * Tries to release document's readlock     *     * Note: Not to be called on the EDT.     */    private void releaseReadLock() {        assert ! SwingUtilities.isEventDispatchThread();        Document document = textComponentToPrint.getDocument();        if (document instanceof AbstractDocument) {            try {                ((AbstractDocument) document).readUnlock();                needReadLock = true;            } catch (Error ignore) {                // readUnlock() might throw StateInvariantError            }        }    }      /**     * Tries to acquire document's readLock if it was released     * in releaseReadLock() method.     *     * Note: Not to be called on the EDT.     */    private void acquireReadLock() {        assert ! SwingUtilities.isEventDispatchThread();        if (needReadLock) {            try {                /*                 * wait until all the EDT events are processed                 * some of the document changes are asynchronous                 * we need to make sure we get the lock after those changes                 */                SwingUtilities.invokeAndWait(                    new Runnable() {                        public void run() {                        }                    });            } catch (InterruptedException ignore) {            } catch (java.lang.reflect.InvocationTargetException ignore) {            }            Document document = textComponentToPrint.getDocument();            ((AbstractDocument) document).readLock();            needReadLock = false;        }    }     /**     * Prepares {@code printShell} for printing.     *     * Sets properties from the component to print.     * Sets width and FontRenderContext.     *     * Triggers Views creation for the printShell.     *     * There are two parts in the implementation.     * First part (layout) is to be called on the printing thread.     * Second part (layoutOnEDT) is to be called on the EDT only.     *     * {@code layout} triggers {@code layoutOnEDT}.     *     * @param width width to layout the text for     */    private void layout(final int width) {        if (!SwingUtilities.isEventDispatchThread()) {            Callable<Object> doLayoutOnEDT = new Callable<Object>() {                public Object call() throws Exception {                    layoutOnEDT(width);                    return null;                }            };            FutureTask<Object> futureLayoutOnEDT = new FutureTask<Object>(                doLayoutOnEDT);             /*             * We need to release document's readlock while printShell is             * initializing             */            releaseReadLock();            SwingUtilities.invokeLater(futureLayoutOnEDT);            try {                futureLayoutOnEDT.get();            } catch (InterruptedException e) {                throw new RuntimeException(e);            } catch (ExecutionException e) {                Throwable cause = e.getCause();                if (cause instanceof RuntimeException) {                    throw (RuntimeException) cause;                } else if (cause instanceof Error) {                    throw (Error) cause;                } else {                    throw new RuntimeException(cause);                }            } finally {                acquireReadLock();            }        } else {            layoutOnEDT(width);        }         isLayouted = true;    }     /**     * The EDT part of layout method.     *     * This method is to be called on the EDT only.     */    private void layoutOnEDT(final int width) {        assert SwingUtilities.isEventDispatchThread();        //need to have big value but smaller than MAX_VALUE otherwise        //printing goes south due to overflow somewhere        final int HUGE_INTEGER = Integer.MAX_VALUE - 1000;         CellRendererPane rendererPane = new CellRendererPane();         //need to use JViewport since text is layouted to the viewPort width        //otherwise it will be layouted to the maximum text width        JViewport viewport = new JViewport();        viewport.setBorder(null);        Dimension size = new Dimension(width, HUGE_INTEGER);         //JTextField is a special case        //it layouts text in the middle by Y        if (printShell instanceof JTextField) {            size =                new Dimension(size.width, printShell.getPreferredSize().height);        }        printShell.setSize(size);        viewport.setComponentOrientation(printShell.getComponentOrientation());        viewport.setSize(size);        viewport.add(printShell);        rendererPane.add(viewport);    }     /**     * Calculates pageMetrics for the pages up to the {@code pageIndex} using     * {@code rowsMetrics}.     * Metrics are stored in the {@code pagesMetrics}.     *     * @param pageIndex the page to update the metrics for     * @param pageHeight the page height     */    private void updatePagesMetrics(final int pageIndex, final int pageHeight) {        while (pageIndex >= pagesMetrics.size() && !rowsMetrics.isEmpty()) {            // add one page to the pageMetrics            int lastPage = pagesMetrics.size() - 1;            int pageStart = (lastPage >= 0)               ? pagesMetrics.get(lastPage).end + 1               : 0;            int rowIndex;            for (rowIndex = 0;                   rowIndex < rowsMetrics.size()                   && (rowsMetrics.get(rowIndex).end - pageStart + 1)                     <= pageHeight;                   rowIndex++) {            }            if (rowIndex == 0) {                // can not fit a single row                // need to split                pagesMetrics.add(                    new IntegerSegment(pageStart, pageStart + pageHeight - 1));            } else {                rowIndex--;                pagesMetrics.add(new IntegerSegment(pageStart,                    rowsMetrics.get(rowIndex).end));                for (int i = 0; i <= rowIndex; i++) {                    rowsMetrics.remove(0);                }            }        }    }     /**     * Calculates rowsMetrics for the document. The result is stored     * in the {@code rowsMetrics}.     *     * Two steps process.     * First step is to find yStart and yEnd for the every document position.     * Second step is to merge all intersected segments ( [yStart, yEnd] ).     */    private void calculateRowsMetrics() {        final int documentLength = printShell.getDocument().getLength();        List<IntegerSegment> documentMetrics = new ArrayList<IntegerSegment>(LIST_SIZE);        Rectangle rect;        for (int i = 0, previousY = -1, previousHeight = -1; i < documentLength;                 i++) {            try {                rect = printShell.modelToView(i);                if (rect != null) {                    int y = (int) rect.getY();                    int height = (int) rect.getHeight();                    if (height != 0                            && (y != previousY || height != previousHeight)) {                        /*                         * we do not store the same value as previous. in our                         * documents it is often for consequent positons to have                         * the same modelToView y and height.                         */                        previousY = y;                        previousHeight = height;                        documentMetrics.add(new IntegerSegment(y, y + height - 1));                    }                }            } catch (BadLocationException e) {                assert false;            }        }        /*         * Merge all intersected segments.         */        Collections.sort(documentMetrics);        int yStart = Integer.MIN_VALUE;        int yEnd = Integer.MIN_VALUE;        for (IntegerSegment segment : documentMetrics) {            if (yEnd < segment.start) {                if (yEnd != Integer.MIN_VALUE) {                    rowsMetrics.add(new IntegerSegment(yStart, yEnd));                }                yStart = segment.start;                yEnd = segment.end;            } else {                yEnd = segment.end;            }        }        if (yEnd != Integer.MIN_VALUE) {            rowsMetrics.add(new IntegerSegment(yStart, yEnd));        }    }     /**     *  Class to represent segment of integers.     *  we do not call it Segment to avoid confusion with     *  javax.swing.text.Segment     */    private static class IntegerSegment implements Comparable<IntegerSegment> {        final int start;        final int end;         IntegerSegment(int start, int end) {            this.start = start;            this.end = end;        }         public int compareTo(IntegerSegment object) {            int startsDelta = start - object.start;            return (startsDelta != 0) ? startsDelta : end - object.end;        }         @Override        public boolean equals(Object obj) {            if (obj instanceof IntegerSegment) {                return compareTo((IntegerSegment) obj) == 0;            } else {                return false;            }        }         @Override        public int hashCode() {            // from the "Effective Java: Programming Language Guide"            int result = 17;            result = 37 * result + start;            result = 37 * result + end;            return result;        }         @Override        public String toString() {            return "IntegerSegment [" + start + ", " + end + "]";        }    }}

Lines 501–839 of 839. Previous lines