001package jmri.jmrit.operations.trains;
002
003import java.awt.*;
004import java.awt.JobAttributes.SidesType;
005import java.io.*;
006import java.nio.charset.StandardCharsets;
007import java.util.ArrayList;
008import java.util.List;
009
010import javax.swing.ImageIcon;
011import javax.swing.JLabel;
012
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016import jmri.jmrit.operations.setup.Setup;
017import jmri.jmrit.operations.trains.trainbuilder.TrainCommon;
018import jmri.util.davidflanagan.HardcopyWriter;
019
020/**
021 * Used for train Manifests and switch lists.
022 *
023 * @author Daniel Boudreau (C) 2025
024 */
025public class TrainPrintManifest extends TrainCommon {
026
027    static final char SPACE = ' ';
028
029    /**
030     * Print or preview a train Manifest or switch list.
031     *
032     * @param file          File to be printed or previewed
033     * @param name          Title of document
034     * @param isPreview     true if preview
035     * @param fontName      optional font to use when printing document
036     * @param logoURL       optional pathname for logo
037     * @param printerName   optional default printer name
038     * @param orientation   Setup.LANDSCAPE, Setup.PORTRAIT, or Setup.HANDHELD
039     * @param fontSize      font size
040     * @param isPrintHeader when true print page header
041     * @param sidesType     two sides long or short can be null
042     */
043    public static void printReport(File file, String name, boolean isPreview, String fontName, String logoURL,
044            String printerName, String orientation, int fontSize, boolean isPrintHeader, SidesType sidesType) {
045        // obtain a HardcopyWriter to do this
046
047        boolean isLandScape = false;
048        double margin = .5;
049        Dimension pagesize = null; // HardcopyWritter provides default page
050                                   // sizes for portrait and landscape
051        if (orientation.equals(Setup.LANDSCAPE)) {
052            margin = .65;
053            isLandScape = true;
054        }
055        if (orientation.equals(Setup.HANDHELD) || orientation.equals(Setup.HALFPAGE)) {
056            isPrintHeader = false;
057            // add margins to page size
058            pagesize = new Dimension(getPageSize(orientation).width + PAPER_MARGINS.width,
059                    getPageSize(orientation).height + PAPER_MARGINS.height);
060        }
061        try (HardcopyWriter writer = new HardcopyWriter(new Frame(), name, fontSize, margin,
062                margin, .5, .5, isPreview, printerName, isLandScape, isPrintHeader, sidesType, pagesize);
063                BufferedReader in = new BufferedReader(new InputStreamReader(
064                        new FileInputStream(file), StandardCharsets.UTF_8));) {
065
066            // set font
067            if (!fontName.isEmpty()) {
068                writer.setFontName(fontName);
069            }
070
071            if (logoURL != null && !logoURL.equals(Setup.NONE)) {
072                ImageIcon icon = new ImageIcon(logoURL);
073                if (icon.getIconWidth() == -1) {
074                    log.error("Logo not found: {}", logoURL);
075                } else {
076                    writer.write(icon.getImage(), new JLabel(icon));
077                }
078            }
079
080            List<String> lines = new ArrayList<>();
081            String line;
082            while (true) {
083                line = in.readLine();
084                if (line == null) {
085                    if (isPreview) {
086                        // need to do this in case the input file was empty to create preview
087                        writer.write(" ");
088                    }
089                    break;
090                }
091                lines.add(line);
092                if (line.isBlank()) {
093                    print(writer, lines, false);
094                }
095            }
096            print(writer, lines, true);
097        } catch (FileNotFoundException e) {
098            log.error("Build file doesn't exist", e);
099        } catch (HardcopyWriter.PrintCanceledException ex) {
100            log.debug("Print canceled");
101        } catch (IOException e) {
102            log.warn("Exception printing: {}", e.getLocalizedMessage());
103        }
104    }
105
106    private static void print(HardcopyWriter writer, List<String> lines, boolean lastBlock) throws IOException {
107        if (Setup.isPrintNoPageBreaksEnabled() &&
108                writer.getCurrentLineNumber() != 0 &&
109                writer.getLinesPerPage() - writer.getCurrentLineNumber() + 1 < lines.size()) {
110            writer.pageBreak();
111        }
112        // check for exact page break
113        if (writer.getLinesPerPage() - writer.getCurrentLineNumber() + 1 == lines.size()) {
114            // eliminate blank line after page break
115            String s = lines.get(lines.size() -1);
116            if (s.isBlank()) {
117                lines.remove(lines.size() - 1);
118            }
119        }
120
121        Color color = null;
122        boolean printingColor = false;
123        for (String line : lines) {
124            // determine if there's a line separator
125            if (printHorizontialLineSeparator(writer, line)) {
126                color = null;
127                continue;
128            }
129            // color text?
130            if (line.contains(TEXT_COLOR_START)) {
131                color = getTextColor(line);
132                if (line.contains(TEXT_COLOR_END)) {
133                    printingColor = false;
134                } else {
135                    // printing multiple lines in color
136                    printingColor = true;
137                }
138                // could be a color change when using two column format
139                if (line.contains(Character.toString(VERTICAL_LINE_CHAR))) {
140                    String s = line.substring(0, line.indexOf(VERTICAL_LINE_CHAR));
141                    s = getTextColorString(s);
142                    writer.write(color, s); // 1st half of line printed
143                    // get the new color and text
144                    line = line.substring(line.indexOf(VERTICAL_LINE_CHAR));
145                    color = getTextColor(line);
146                    // pad out string
147                    line = tabString(getTextColorString(line), s.length());
148                } else {
149                    // simple case only one color
150                    line = getTextColorString(line);
151                }
152            } else if (line.contains(TEXT_COLOR_END)) {
153                printingColor = false;
154                line = getTextColorString(line);
155            } else if (!printingColor) {
156                color = null;
157            }
158
159            printVerticalLineSeparator(writer, line);
160            line = line.replace(VERTICAL_LINE_CHAR, SPACE);
161
162            if (color != null) {
163                writer.write(color, line + NEW_LINE);
164                continue;
165            }
166            writer.write(line);
167            // no line feed if last line of file, eliminates blank page
168            if (!lastBlock ||
169                    writer.getCurrentLineNumber() < writer.getLinesPerPage() - 1) {
170                writer.write(NEW_LINE);
171            }
172        }
173        lines.clear();
174    }
175
176    /*
177     * Returns true if horizontal line was printed, or line length = 0
178     */
179    private static boolean printHorizontialLineSeparator(HardcopyWriter writer, String line) {
180        boolean horizontialLineSeparatorFound = true;
181        if (line.length() > 0) {
182            for (int i = 0; i < line.length(); i++) {
183                if (line.charAt(i) != HORIZONTAL_LINE_CHAR) {
184                    horizontialLineSeparatorFound = false;
185                    break;
186                }
187            }
188            if (horizontialLineSeparatorFound) {
189                writer.write(writer.getCurrentLineNumber(), 0, writer.getCurrentLineNumber(),
190                        line.length() + 1);
191            }
192        }
193        return horizontialLineSeparatorFound;
194    }
195
196    private static void printVerticalLineSeparator(HardcopyWriter writer, String line) {
197        for (int i = 0; i < line.length(); i++) {
198            if (line.charAt(i) == VERTICAL_LINE_CHAR) {
199                // make a frame (two column format)
200                if (Setup.isTabEnabled()) {
201                    writer.write(writer.getCurrentLineNumber(), 0, writer.getCurrentLineNumber() + 1, 0);
202                    writer.write(writer.getCurrentLineNumber(), line.length() + 1,
203                            writer.getCurrentLineNumber() + 1, line.length() + 1);
204                }
205                writer.write(writer.getCurrentLineNumber(), i + 1, writer.getCurrentLineNumber() + 1,
206                        i + 1);
207            }
208        }
209    }
210
211    private final static Logger log = LoggerFactory.getLogger(TrainPrintManifest.class);
212}