Печать с атрибутами (управление лотком, дуплекс и т. Д.) С использованием библиотеки javax.print

В течение некоторого времени я пытался определить способ использования стандартной библиотеки Java Print для печати файлов - в частности, документов PDF - с определенными атрибутами - в частности, для определенных лотков или с использованием дуплекса.

Существует множество документов о том, как это сделать, и я действительно исследовал и попробовал эти методы. Типичный способ примерно такой:

public static void main (String [] args) {
    try {

        PrintService[] pservices = PrintServiceLookup.lookupPrintServices(null, null);

        //Acquire Printer
        PrintService printer = null;
        for (PrintService serv: pservices) {
            System.out.println(serv.toString());
            if (serv.getName().equals("PRINTER_NAME_BLAH")) {
                printer = serv;
            }
        }

        if (printer != null) {
            System.out.println("Found!");


            //Open File
            FileInputStream fis = new FileInputStream("FILENAME_BLAH_BLAH.pdf");

            //Create Doc out of file, autosense filetype
            Doc pdfDoc = new SimpleDoc(fis, DocFlavor.INPUT_STREAM.AUTOSENSE, null);

            //Create job for printer
            DocPrintJob printJob = printer.createPrintJob();

            //Create AttributeSet
            PrintRequestAttributeSet pset = new HashPrintRequestAttributeSet();

            //Add MediaTray to AttributeSet
            pset.add(MediaTray.TOP);

            //Add Duplex Option to AttributeSet
            pset.add(Sides.DUPLEX);

            //Print using Doc and Attributes
            printJob.print(pdfDoc, pset);

            //Close File
            fis.close();

        }

    }
    catch (Throwable t) {
        t.printStackTrace();
    }
}

Короче говоря, вы делаете следующее

  1. Найти принтер
  2. Создать PrinterJob
  3. Создать набор атрибутов
  4. Добавьте атрибуты в набор атрибутов, такие как лоток и дуплекс
  5. Вызовите печать на задании принтера с помощью AttributeSet

Проблема здесь в том, что, несмотря на то, что это документированный способ сделать это, а также то, что я нашел в нескольких уроках, этот метод... не работает. Имейте в виду, я знаю, это звучит не очень описательно, но выслушайте меня. Я не говорю это легко...

Официальная документация для PrinterJob фактически упоминает, что AttributeSet игнорируется в реализации по умолчанию. Исходный код, показанный здесь, показывает, что это правда - атрибуты передаются и полностью игнорируются.

Очевидно, вам нужна какая-то расширенная версия класса, которая, возможно, основана на конкретных принтерах и их возможностях? Я попытался написать некоторый тестовый код, который сообщал бы мне о таких возможностях - у нас в офисе установлено большое количество принтеров, больших или маленьких, простых или полных наворотов - не говоря уже о нескольких драйверах на моем компьютере только для псевдо -принтеры, которые просто создают документы и имитируют принтеры, не прибегая к какому-либо оборудованию. Тестовый код выглядит следующим образом:

public static void main (String [] args) {

    PrintService[] pservices = PrintServiceLookup.lookupPrintServices(null, null);

    for (PrintService serv: pservices) {
        System.out.println(serv.toString());

        printFunctionality(serv, "Trays", MediaTray.class);
        printFunctionality(serv, "Copies", Copies.class);
        printFunctionality(serv, "Print Quality", PrintQuality.class);
        printFunctionality(serv, "Color", ColorSupported.class);
        printFunctionality(serv, "Media Size", MediaSize.class);
        printFunctionality(serv, "Accepting Jobs", PrinterIsAcceptingJobs.class);
    }
}

private static void printFunctionality(PrintService serv, String attrName, Class<? extends Attribute> attr) {
    boolean isSupported = serv.isAttributeCategorySupported(attr);
    System.out.println("    " + attrName + ": " + (isSupported ? "Y" : "N"));
}

Результаты, которые я обнаружил, состояли в том, что каждый принтер без исключения возвращал, что "копии" были поддержаны, а все остальные атрибуты - нет. Кроме того, возможности каждого принтера были идентичны, независимо от того, насколько неправдоподобным это могло бы показаться.

Неизбежный вопрос многослойен: как отправить атрибуты таким образом, чтобы они были зарегистрированы? Кроме того, как правильно определить возможности принтера? Действительно, действительно ли класс PrinterJob расширен в удобном для использования виде или атрибуты всегда игнорируются?

Примеры, которые я нашел в Интернете, как мне кажется, подсказывают мне, что ответ на последний вопрос - "Нет, они всегда игнорируются", что мне кажется смешным (но все более правдоподобным, когда я просеиваю сотни страниц). Является ли этот код, который Sun просто установил, но он так и не перешел в завершенное состояние? Если да, есть ли альтернативы?

4 ответа

Решение

Итак, мы неизбежно нашли способ печати на разных лотках и с разными настройками, но не напрямую. Мы обнаружили невозможность отправки атрибутов с помощью метода printJob.print, и это практически не изменилось. Однако мы смогли задать имя задания на печать, а затем перехватить задание на печать с помощью низкоуровневого сценария Perl, проанализировать имя и установить там параметры лотка и дуплекса. Это экстремальный взлом, но это работает. Все еще остается верным, что атрибуты принтера Java не работают, и вам нужно будет найти другой способ, если вы хотите их установить.

Проблема в том, что API печати Java - это мост между мирами. Производители принтеров не выпускают драйверы для JVM. Они выпускают драйверы для Windows, Macintosh и, возможно, у кого-то есть драйвер для данного принтера, который работает на одной или нескольких *nix-платформах.

Вместе с некоторым Java-кодом, работающим внутри JVM на некоторой хост-системе. Когда вы начинаете запрашивать функции принтера, вы не разговариваете с принтерами - вы говорите с классом моста в java.awt.print, который подключается к JVM, которая подключается к операционной системе хоста, которая подключается к какому-либо конкретному драйвер был установлен для данного принтера. Таким образом, есть несколько мест, где это может развалиться... Конкретная JVM, на которой вы работаете, может или не может полностью реализовать API для запроса функций принтера, не говоря уже о передаче этих параметров для данной работы.

Несколько предложений:

  1. посмотрите на классы javax.print как альтернативу java.awt.print - мне больше повезло, печатая оттуда.
  2. попробуйте использовать альтернативные драйверы печати для своих принтеров - вы можете определить несколько именованных подключений к данному принтеру, каждое из которых имеет свой драйвер. Если у вас есть драйвер, предоставленный производителем, попробуйте более универсальный драйвер, если у вас есть универсальный драйвер, попробуйте установить более конкретный драйвер.
  3. запустить ваш код в альтернативных реализациях JVM для вашей платформы

Я обнаружил, что трюк с лотками для принтера состоит в том, чтобы перебирать Media.class с помощью getSupportedAttributeValues(...), сопоставьте удобочитаемое имя и выберите это конкретное значение. Протестировано в Windows, MacOS с несколькими конфигурациями лотка.

String tray = "1";

// Handle human-readable names, see PRINTER_TRAY_ALIASES usage below for context.  Adjust as needed.
List<String> PRINTER_TRAY_ALIASES = Arrays.asList("", "Tray ", "Paper Cassette ");

// Get default printer
PrintService printService = PrintServiceLookup.lookupDefaultPrintService();

// Attributes to be provided at print time
PrintRequestAttributeSet pset = new HashPrintRequestAttributeSet();

Media[] supported = printService.getSupportedAttributeValues(Media.class, null, null);
for(Media m : supported) {           
    for(String pta : PRINTER_TRAY_ALIASES) {
        // Matches "1", "Tray 1", or "Paper Cassette 1"
        if (m.toString().trim().equalsIgnoreCase(pta + tray)) {
            attributes.add(m);
            break;
        }
    }
}

// Print, etc
// printJob.print(pdfDoc, pset);

Вот как это выглядит в javafx. Лотки могут отличаться, и он также распечатает все доступные лотки, просто измените имя лотка.

private void printImage(Node node) {
    PrinterJob job = PrinterJob.createPrinterJob();
    if (job != null) {
        JobSettings js = job.getJobSettings();
        PaperSource papersource = js.getPaperSource();
        System.out.println("PaperSource=" + papersource);
        PrinterAttributes pa = printer.getPrinterAttributes();
        Set<PaperSource> s = pa.getSupportedPaperSources();
        System.out.println("# of papersources=" + s.size());
        if (s != null) {
            for (PaperSource newPaperSource : s) {
                System.out.println("newpapersource= " + newPaperSource);
                //Here is where you would put the tray name that is appropriate
                //in the contains section
                if(newPaperSource.toString().contains("Tray 2"))
                    js.setPaperSource(newPaperSource);
            }
        }
        job.getJobSettings().setJobName("Whatever");
        ObjectProperty<PaperSource> sources = job.getJobSettings().paperSourceProperty();
        System.out.println(sources.toString());
        boolean success = job.printPage(node);
        if (success) {
            System.out.println("PRINTING FINISHED");
            job.endJob();
            //Stage mainStage = (Stage) root.getScene().getWindow();
            //mainStage.close();
        }
    }
}

Вот мой вывод:

PaperSource=Paper source : Automatic
# of papersources=6
newpapersource= Paper source :
newpapersource= Paper source :  Manual Feed in Tray 1
newpapersource= Paper source :  Printer auto select
newpapersource= Paper source :  Tray 1
newpapersource= Paper source :  Tray 2
newpapersource= Paper source : Form-Source
ObjectProperty [bean:  Collation = UNCOLLATED
 Copies = 1
 Sides = ONE_SIDED
 JobName = Whatever
 Page ranges = null
 Print color = COLOR
 Print quality = NORMAL
 Print resolution = Feed res=600dpi. Cross Feed res=600dpi.
 Paper source = Paper source :  Tray 2
 Page layout = Paper=Paper: Letter size=8.5x11.0 INCH Orient=PORTRAIT leftMargin=54.0 rightMargin=54.0 topMargin=54.0 bottomMargin=54.0, name: paperSource, value: Paper source :  Tray 2]
PRINTING FINISHED

У нас было аналогичное требование для печати PDF-файлов, и мы хотели отправить несколько страниц в Specific tray, а также хотели, чтобы документ был сшит. Мы использовали комбинацию Java code + ghost script. Сначала преобразуйте PDF в ghost script, а затем добавьте команды PJL (Print job language) в файл ghost script, чтобы выбрать лотки и сшить документы. Затем отправьте этот отредактированный файл сценария-призрака на принтер.

Вот полный пример, написанный на Java

http://reddymails.blogspot.com/2014/07/how-to-print-documents-using-java-how.html

-Баран

Другие вопросы по тегам