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() < 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        // use line feed for all lines?
121        if (lastBlock && writer.getLinesPerPage() - writer.getCurrentLineNumber() < lines.size()) {
122            lastBlock = false; // yes
123        }
124
125        Color color = null;
126        boolean printingColor = false;
127        for (String line : lines) {
128            // determine if there's a line separator
129            if (printHorizontialLineSeparator(writer, line)) {
130                color = null;
131                continue;
132            }
133            // color text?
134            if (line.contains(TEXT_COLOR_START)) {
135                color = getTextColor(line);
136                if (line.contains(TEXT_COLOR_END)) {
137                    printingColor = false;
138                } else {
139                    // printing multiple lines in color
140                    printingColor = true;
141                }
142                // could be a color change when using two column format
143                if (line.contains(Character.toString(VERTICAL_LINE_CHAR))) {
144                    String s = line.substring(0, line.indexOf(VERTICAL_LINE_CHAR));
145                    s = getTextColorString(s);
146                    writer.write(color, s); // 1st half of line printed
147                    // get the new color and text
148                    line = line.substring(line.indexOf(VERTICAL_LINE_CHAR));
149                    color = getTextColor(line);
150                    // pad out string
151                    line = tabString(getTextColorString(line), s.length());
152                } else {
153                    // simple case only one color
154                    line = getTextColorString(line);
155                }
156            } else if (line.contains(TEXT_COLOR_END)) {
157                printingColor = false;
158                line = getTextColorString(line);
159            } else if (!printingColor) {
160                color = null;
161            }
162
163            printVerticalLineSeparator(writer, line);
164            line = line.replace(VERTICAL_LINE_CHAR, SPACE);
165
166            if (color != null) {
167                writer.write(color, line + NEW_LINE);
168                continue;
169            }
170            writer.write(line);
171            // no line feed if last line of file, eliminates blank page
172            if (!lastBlock ||
173                    writer.getCurrentLineNumber() < writer.getLinesPerPage() - 1) {
174                writer.write(NEW_LINE);
175            }
176        }
177        lines.clear();
178    }
179
180    /*
181     * Returns true if horizontal line was printed, or line length = 0
182     */
183    private static boolean printHorizontialLineSeparator(HardcopyWriter writer, String line) {
184        boolean horizontialLineSeparatorFound = true;
185        if (line.length() > 0) {
186            for (int i = 0; i < line.length(); i++) {
187                if (line.charAt(i) != HORIZONTAL_LINE_CHAR) {
188                    horizontialLineSeparatorFound = false;
189                    break;
190                }
191            }
192            if (horizontialLineSeparatorFound) {
193                writer.write(writer.getCurrentLineNumber(), 0, writer.getCurrentLineNumber(),
194                        line.length() + 1);
195            }
196        }
197        return horizontialLineSeparatorFound;
198    }
199
200    private static void printVerticalLineSeparator(HardcopyWriter writer, String line) {
201        for (int i = 0; i < line.length(); i++) {
202            if (line.charAt(i) == VERTICAL_LINE_CHAR) {
203                // make a frame (two column format)
204                if (Setup.isTabEnabled()) {
205                    writer.write(writer.getCurrentLineNumber(), 0, writer.getCurrentLineNumber() + 1, 0);
206                    writer.write(writer.getCurrentLineNumber(), line.length() + 1,
207                            writer.getCurrentLineNumber() + 1, line.length() + 1);
208                }
209                writer.write(writer.getCurrentLineNumber(), i + 1, writer.getCurrentLineNumber() + 1,
210                        i + 1);
211            }
212        }
213    }
214
215    private final static Logger log = LoggerFactory.getLogger(TrainPrintManifest.class);
216}