2013-02-08 8 views
5

Z wielu powodów próbuję połączyć wyjście wielu JTables w jedno zadanie drukowania. Po rozerwaniu włosów, próbując zbudować pliki PDF i przeczesując Java API, zdecydowałem się na klasę Book. Mój kod drukowania wygląda obecnie tak.Drukowanie wielu JTables jako jednego zadania - Obiekt książki tylko drukuje pierwszą tabelę

try {  
    PrinterJob printer = PrinterJob.getPrinterJob(); 

    //Set 1/2 " margins and orientation 
    PageFormat pf = printer.defaultPage(); 
    pf.setOrientation(PageFormat.LANDSCAPE); 
    Paper paper = new Paper(); 
    double margin = 36; // half inch 
    paper.setImageableArea(margin, margin, paper.getWidth() - margin * 2, paper.getHeight() - margin * 2); 
    pf.setPaper(paper); 

    Book printJob = new Book(); 

    // Note for next line: getAllTables() returns an ArrayList of JTables 
    for (JTable t : getAllTables()) 
     printJob.append(t.getPrintable(PrintMode.FIT_WIDTH, null, null), pf,2); 

    printer.setPageable(printJob); 

    System.out.println(printJob.getNumberOfPages()); 

    if (printer.printDialog()) 
     printer.print(); 
    } catch (PrinterException e) { 
     e.printStackTrace(); 
} 

Podstawowy problem: Tylko wyjście z "pierwszej" tabeli jest druk. Upewniłem się, że pętla for iteruje poprawnie, a instrukcje debugowania pokazały, że każda tabela jest dodawana do Księgi. Zmiana kolejności tabel nie ma żadnego wpływu. Zmiana trybu drukowania na PrintMode.NORMAL powoduje wyświetlenie pozostałych wydrukowanych tabel. Ja jednak napotkasz mnóstwo poziomych problemów paginacji, jak szerokość tabeli często przekracza szerokość strony (i to nadal nie wyjaśnia, dlaczego PrintMode.FIT_WIDTH nie działa)

Pytanie wtórny: W jaki sposób można wykryć poprawne liczba stron w każdym druku? Większość moich tabel ma dwie strony, więc na razie dodam po prostu 2 strony za każdym razem, gdy dołączam. Przeczytałem "gdzieś", że użycie numeru Book.UNKNOWN_NUMBER_OF_PAGES naprawi ten problem, ale spowoduje to jedynie wyjątek IndexOutOfBounds w kodzie API. Rozważałem wywoływanie druku, dopóki nie otrzymam NO_PAGE_EXISTS, ale potrzebuję obiektu Graphics z odpowiednimi wymiarami strony (i nie mam pojęcia, jak to uzyskać).

Wreszcie: Jeśli podejście Książka jest beznadziejna, jak inaczej mogę połączyć wyjście wielu JTables (np. Wielu drukarek) w jedno zadanie? Zajrzałem do exporting the table as a PDF, ale wbudowana pagina JTable jest tak dobra, że ​​wolałabym nie robić tego sama. Ostatnią rzeczą jest po prostu zrezygnować i użyć wbudowanej funkcji tabeli iText do skonstruowania kopii tabeli.

Edit 3: Na moim komentarzu poniżej, dostałem tej pracy przez generowanie wydruku, ustalania # stron, a następnie wygenerowanie nowego. Zmodyfikowałem także opakowanie Durendala, aby zaoszczędzić nam kłopotów z iteracją po każdej stronie. Kod z opakowania jest

class PrintableWrapper implements Printable 
    { 
     private Printable delegate; 
     private int offset; 

     public PrintableWrapper(Printable delegate, int offset) { 
      this.offset = offset; 
      this.delegate = delegate; 
     } 

     @Override 
     public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { 
      return delegate.print(graphics, pageFormat, pageIndex-offset); 
     } 
    } 

umieścić kod Durendal dla określenia liczby stron w swojej własnej funkcji

public int getNumberOfPages(Printable delegate, PageFormat pageFormat) throws PrinterException 
    { 
     Graphics g = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB).createGraphics(); 
     int numPages = 0; 
     while (true) { 
      int result = delegate.print(g, pageFormat, numPages); 
      if (result == Printable.PAGE_EXISTS) { 
       ++numPages; 
      } else { 
       break; 
      } 
     } 

     return numPages; 
    } 

Po utworzeniu obiektu książce, mój kod druk wygląda to

  int totalPages = 0; 
      for (DragNDropTable t : getAllTables()) 
      { 
       int pages = getNumberOfPages(t.getPrintable(PrintMode.FIT_WIDTH, null, null), pf); 
       Printable p = t.getPrintable(PrintMode.FIT_WIDTH, null, null); 
       printJob.append(new PrintableWrapper(p,totalPages), pf, pages); 
       totalPages += pages; 
      } 
      printer.setPageable(printJob); 

      if (printer.printDialog()) 
       printer.print(); 

I działa jak urok!

Edytuj 2: (możesz to pominąć) Próbowałem odpowiedzi Durendala. Podczas drukowania wystarczającej liczby stron, wielostronicowe wydruki drukują ostatnią stronę wiele razy (raz dla każdej strony w druku). To jest ten sam problem, który omówiłem w mojej pierwszej edycji (poniżej) i nie mam pojęcia, dlaczego tak się dzieje, a moje oświadczenia dotyczące debugowania mówią, że poprawnie drukuje wszystkie strony, ale drukuje się ostatnia strona wydruku wielostronicowego w miejsce każdej strony. Kod jest załączony. Insight jest doceniana (i spłacili z wirtualnymi ciasteczka)

try {  
    PrinterJob printer = PrinterJob.getPrinterJob(); 

    //Set 1/2 " margins and orientation 
    PageFormat pf = printer.defaultPage(); 
    pf.setOrientation(PageFormat.LANDSCAPE); 
    Paper paper = new Paper(); 
    double margin = 36; // half inch 
    paper.setImageableArea(margin, margin, paper.getWidth() - margin * 2, paper.getHeight() - margin * 2); 
    pf.setPaper(paper); 

    Book printJob = new Book(); 

    // Note for next line: getAllTables() returns an ArrayList of JTables 
    for (JTable t : getAllTables()) 
    { 
     Printable p = t.getPrintable(PrintMode.FIT_WIDTH, null, null); 
     int pages = getNumberOfPages(p, pf); 

     for (int i=0; i < pages; i++) 
      printJob.append(new PageWrapper(p,i), pf); 
    } 

    printer.setPageable(printJob); 

    System.out.println(printJob.getNumberOfPages()); 

    if (printer.printDialog()) 
     printer.print(); 
    } catch (PrinterException e) { 
     e.printStackTrace(); 
} 

public int getNumberOfPages(PageFormat pageFormat) throws PrinterException 
{ 
    Graphics g = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB).createGraphics(); 
    int numPages = 0; 
    while (true) { 
     int result = delegate.print(g, pageFormat, numPages); 
     if (result == Printable.PAGE_EXISTS) 
      ++numPages; 
     else 
      break; 
    } 

    return numPages; 
} 

Używam niezmodyfikowanej PageWrapper że Durendal dał poniżej.

Edytuj 1: (Możesz to pominąć) Wychodząc od odpowiedzi Durendala, próbowałem zrobić opakowanie, które oszczędza nam trudu, by samemu przeglądać strony. Niestety nie działa poprawnie w przypadku drukarek wielostronicowych, wielokrotnie drukuje tę samą stronę w dokumencie. Publikuję to, ponieważ ktoś może go uruchomić i jest nieco wygodniejszy w użyciu.

class PrintableWrapper implements Printable 
    { 
     private Printable delegate; 
     private int offset; 

     public PrintableWrapper(Printable delegate, int offset) { 
      this.offset = offset; 
      this.delegate = delegate; 
     } 

     @Override 
     public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { 
      return delegate.print(graphics, pageFormat, pageIndex-offset); 
     } 


     public int getNumberOfPages(PageFormat pageFormat) throws PrinterException 
     { 
      Graphics g = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB).createGraphics(); 
      int numPages = 0; 
      while (true) { 
       int result = delegate.print(g, pageFormat, numPages); 
       if (result == Printable.PAGE_EXISTS) 
        ++numPages; 
       else 
        break; 
      } 

      return numPages; 
     } 
    } 

Mój kod druk teraz wygląda następująco (po tym, jak ustawić format strony)

Book printJob = new Book(); 
int totalPages = 0; 

for (DragNDropTable t : getAllTables()) 
{ 
    Printable p = t.getPrintable(PrintMode.FIT_WIDTH, null, null); 
    PrintableWrapper pw = new PrintableWrapper(p, totalPages); 
    totalPages += pw.getNumberOfPages(pf); 
    printJob.append(pw, pf,pw.getNumberOfPages(pf)); 
} 

printer.setPageable(printJob); 

if (printer.printDialog()) 
{ 
    printer.print(); 

} 
+0

Doskonała prezentacja pytania i odpowiedzi. Miałem ten sam problem i mogłem odtworzyć to rozwiązanie krok po kroku. Bardzo dobre poprawki autorstwa @kleopatra. – downeyt

Odpowiedz

3

miałem ten sam problem bardzo niedawno. Klasa Book sama w sobie to bezużyteczna, ponieważ jeśli dodasz do niej materiały do ​​druku, po wydrukowaniu książki przejdzie ona przez pageIndex z książki do druku na stronie pagendex.

To w większości przypadków nie jest to, czego potrzebujesz.

Tworzenie prostego druku opakowaniu, które można zapamiętać PageIndex być wykorzystywane do delegata do druku i dodać te do rezerwacji:

class PageWrapper implements Printable { 
    private Printable delegate; 
    private int localPageIndex; 

    public PageWrapper(Printable delegate, int pageIndex) { 
     this.localPageIndex = pageIndex; 
     this.delegate = delegate; 
    } 

    @Override 
    public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { 
     return delegate.print(graphics, pageFormat, localPageIndex); 
    } 
} 

Teraz trzeba iteracyjne koryta każdą stronę każdej druku/stronicowalnej i dodać instancja opakowania (która zna stronęIndeks w swoim delegacie) do księgi. To rozwiązuje problem polegający na tym, że Book przekazuje niewłaściwy pageIndex do dodanych do niego druków (jest łatwiejszy w użyciu niż w przypadku drukarek).

można wykryć liczbę stron do wydrukowania, drukując je;) Tak, mówię poważnie:

Graphics g = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB).createGraphics(); 
int numPages = 0; 
while (true) { 
    int result = printable.print(g, pageFormat, numPages); 
    if (result == Printable.PAGE_EXISTS) { 
     ++numPages; 
    } else { 
     break; 
    } 
} 

Jest ważne, aby uzyskać instancję PageFormat od rzeczywistego PrinterJob chcesz drukować, ponieważ liczba stron zależy od formatu strony (rozmiaru papieru).

+0

Masz na myśli to, że jeśli dodaję dwa 2-stronicowe wydruki do książki i wydrukuję, kiedy książka przejdzie na strony 3 i 4, prosi Drucką Drukarnię o wydrukowanie nieistniejących stron? Yikes! Jak to nie zostało oznaczone jako błąd? Nie bardzo rozumiem twoje opakowanie. Przesłonięta funkcja drukowania zawsze używa localPageIndex do drukowania. Czy działa na wielu stronach do wydrukowania? –

+0

Tak, dokładnie to, co się dzieje, żąda nieistniejących stron z drugiego dokumentu. Tworzenie instancji opakowania następuje po przejściu przez opcję Pageable/Printable i przekazaniu jej poprawnej wartości pageIndex dla tego dokumentu. Kiedy owijki są później używane przez instancję książki, wrapper dba o przetłumaczenie stronyIndeks na strony, które rzeczywiście istnieją w dokumencie. – Durandal

+0

I tak, zgadzam się, powinni przynajmniej udokumentować tę pułapkę w Javadoc Book ... zabrał mi jeden dzień, aby się dowiedzieć. – Durandal