javaswingawtprintersjava-print

Wrong iteration while printing a multiple page(Possibly printer or PDF) document using Java Printing API


I am using Java Printing API and have got the code of printing multiple page document of billing invoice from this oracle page.

Printing a Multiple Page Document

And Example code from this page

PaginationExample

Now as you know most invoice have billing details of items start from center-top of the page which is dynamic values sometimes details get pushed to next page.

I fiddle and modify with above code and below is my working code.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.print.*;

public class PaginationExample implements Printable, ActionListener {

    int[] pageBreaks1, pageBreaks2;  // array of page break line positions.

    /* Synthesize some sample lines of text */
    String[][] textLines;
    private void initTextLines() {
        if (textLines == null) {
            int numLines=300;
            textLines = new String[numLines][3];
            textLines = new String[numLines][4];
            for (int i=0;i<numLines;i++) {
                textLines[i][0]= "1. This is line number " + i;
                textLines[i][1]= "2. This is line number " + i;
                textLines[i][2]= "3. This is line number " + i;
            }
        }
    }

    public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException {

        Font font = new Font("Serif", Font.PLAIN, 10);
        FontMetrics metrics = g.getFontMetrics(font);
        int lineHeight = metrics.getHeight();
        
        int linesPerPage1 = (int)(550/lineHeight);
        int linesPerPage2 = (int)(750/lineHeight);
        
        initTextLines();
        
        if (pageBreaks1 == null) {            
            int numBreaks1 = (textLines.length-1)/linesPerPage1;
            pageBreaks1 = new int[numBreaks1];
            for (int b1=0; b1<numBreaks1; b1++) {
                pageBreaks1[b1] = (b1+1)*linesPerPage1;
            }
        }
        
        if (pageBreaks2 == null) {            
            int numBreaks2 = (textLines.length-1)/linesPerPage2;
            pageBreaks2 = new int[numBreaks2];
            for (int b2=0; b2<numBreaks2; b2++) {
                pageBreaks2[b2] = (b2+1)*linesPerPage2; 
            }
        }

        if (pageIndex > pageBreaks1.length) {
            return NO_SUCH_PAGE;
        }
        if (pageIndex > pageBreaks2.length) {
            return NO_SUCH_PAGE;
        }

        /* User (0,0) is typically outside the imageable area, so we must
         * translate by the X and Y values in the PageFormat to avoid clipping
         * Since we are drawing text we
         */
        
        Graphics2D g2d = (Graphics2D)g;
        g2d.translate(pf.getImageableX(), pf.getImageableY());

        /* Draw each line that is on this page.
         * Increment 'y' position by lineHeight for each line.
         */
        
        int y = (pageIndex == 0) ? 200 : 30;
        
        int start = (pageIndex == 0) ? ((pageIndex == 0) ? 0 : pageBreaks1[pageIndex-1])
                                     : ((pageIndex == 0) ? 0 : pageBreaks2[pageIndex-1]);
        
        int end   = (pageIndex == 0) ? ((pageIndex == pageBreaks1.length) ? textLines.length : pageBreaks1[pageIndex]) 
                                     : ((pageIndex == pageBreaks2.length) ? textLines.length : pageBreaks2[pageIndex]);
        
        for (int line=start; line<end; line++) {
            y += lineHeight;
            g.drawString(textLines[line][0], 10, y);
            g.drawString(textLines[line][1], 100, y);
            g.drawString(textLines[line][2], 200, y);
            g.drawString(textLines[line][3], 300, y);
            
        }
        return PAGE_EXISTS;
    }

    public void actionPerformed(ActionEvent e) {
         PrinterJob job = PrinterJob.getPrinterJob();
         job.setPrintable(this);
         boolean ok = job.printDialog();
         if (ok) {
             try {
                  job.print();
             } catch (PrinterException ex) {
              /* The job did not successfully complete */
             }
         }
    }
    
    public static void main(String args[]) {
        try {
            String cn = UIManager.getSystemLookAndFeelClassName();
            UIManager.setLookAndFeel(cn); // Use the native L&F
        } catch (Exception cnf) {
        }
        JFrame f = new JFrame("Printing Pagination Example");
        
        f.addWindowListener(new WindowAdapter() {
           public void windowClosing(WindowEvent e) {System.exit(0);}
        });
        
        JButton printButton = new JButton("Print Pages");
        printButton.addActionListener(new PaginationExample());
        f.add("Center", printButton);
        f.pack();
        f.setVisible(true);
    }
}


Now the issue I get is this in below picture.

pdf output image

What is happening is code is taking the iteration wrong because of and jumping some elements in the array and this error is only happening in first page.

I do believe my problem in the above posted code lies here.

int y = (pageIndex == 0) ? 200 : 30;
        
        int start = (pageIndex == 0) ? ((pageIndex == 0) ? 0 : pageBreaks1[pageIndex-1])
                                     : ((pageIndex == 0) ? 0 : pageBreaks2[pageIndex-1]);
        
        int end   = (pageIndex == 0) ? ((pageIndex == pageBreaks1.length) ? textLines.length : pageBreaks1[pageIndex]) 
                                     : ((pageIndex == pageBreaks2.length) ? textLines.length : pageBreaks2[pageIndex]);
        
        for (int line=start; line<end; line++) {
            y += lineHeight;
            g.drawString(textLines[line][0], 10, y);
            g.drawString(textLines[line][1], 100, y);
            g.drawString(textLines[line][2], 200, y);
            g.drawString(textLines[line][3], 300, y);
            
        }

It tried fixing the iteration but it doesn't work. Please if possible guide me where I am going wrong here.

Thank you :).


Solution

  • I made changes to your print method to greatly simplify the calculation of the start and end text line.

    I made some other changes, mainly to generate the text lines one time and to space out the lines horizontally.

    Here's the revised, contained, executable code. I wish I could post the pdf print output, but you'll have to run the code to see the results.

    import java.awt.BorderLayout;
    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.Rectangle2D;
    import java.awt.print.PageFormat;
    import java.awt.print.Printable;
    import java.awt.print.PrinterException;
    import java.awt.print.PrinterJob;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    
    public class PaginationExample implements Printable, ActionListener {
    
        int[] pageBreaks1, pageBreaks2; // array of page break line positions.
    
        /* Synthesize some sample lines of text */
        String[][] textLines;
        
        public PaginationExample() {
            initTextLines();
    
        }
    
        private void initTextLines() {
            int numLines = 300;
            textLines = new String[numLines][4];
            for (int i = 0; i < numLines; i++) {
                textLines[i][0] = "1. This is line number " + i;
                textLines[i][1] = "2. This is line number " + i;
                textLines[i][2] = "3. This is line number " + i;
                textLines[i][3] = "4. This is line number " + i;
            }
        }
    
        public int print(Graphics g, PageFormat pf, int pageIndex) 
                throws PrinterException {
    
            Font font = new Font("Serif", Font.PLAIN, 10);
            FontMetrics metrics = g.getFontMetrics(font);
            int lineHeight = metrics.getHeight();
    
            int linesPerPage1 = (int) (550 / lineHeight);
            int linesPerPage2 = (int) (750 / lineHeight);
            
            /*
             * User (0,0) is typically outside the imageable area, so we must translate by
             * the X and Y values in the PageFormat to avoid clipping Since we are drawing
             * text we
             */
    
            Graphics2D g2d = (Graphics2D) g;
            g2d.translate(pf.getImageableX(), pf.getImageableY());
    
            /*
             * Draw each line that is on this page. Increment 'y' position by lineHeight for
             * each line.
             */
    
            int y = (pageIndex == 0) ? 200 : 30;
    
            int start = 0;
            int end = linesPerPage1;
            if (pageIndex > 0) {
                start += linesPerPage1 + linesPerPage2 * (pageIndex - 1);
                end = start + linesPerPage2;
            }
            end = Math.min(end, textLines.length);
            
            for (int line = start; line < end; line++) {
                y += lineHeight;
                Rectangle2D r2d = metrics.getStringBounds(textLines[line][0], g2d);
                g.drawString(textLines[line][0], 10, y);
                int width = 50 + (int) r2d.getWidth();
                g.drawString(textLines[line][1], width, y);
                
                r2d = metrics.getStringBounds(textLines[line][1], g2d);
                width += 40 + (int) r2d.getWidth();
                g.drawString(textLines[line][2], width, y);
                
                r2d = metrics.getStringBounds(textLines[line][2], g2d);
                width += 40 + (int) r2d.getWidth();
                g.drawString(textLines[line][3], width, y);
            }
            
            if (start <= textLines.length) {
                return PAGE_EXISTS;
            } else {
                return NO_SUCH_PAGE;
                        
            }
        }
    
        public void actionPerformed(ActionEvent e) {
            PrinterJob job = PrinterJob.getPrinterJob();
            job.setPrintable(this);
            boolean ok = job.printDialog();
            if (ok) {
                try {
                    job.print();
                } catch (PrinterException ex) {
                    ex.printStackTrace();
                }
            }
        }
    
        public static void main(String args[]) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame f = new JFrame("Printing Pagination Example");
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
                    JButton printButton = new JButton("Print Pages");
                    printButton.addActionListener(new PaginationExample());
                    f.add(printButton, BorderLayout.CENTER);
                    
                    f.pack();
                    f.setLocationByPlatform(true);
                    f.setVisible(true);
                }
            });
        }
    }