001package jmri.jmrit.operations.trains; 002 003import java.awt.*; 004import java.io.*; 005import java.nio.charset.StandardCharsets; 006import java.util.ArrayList; 007import java.util.List; 008 009import javax.print.attribute.standard.Sides; 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.CompatibleHardcopyWriter; 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 sides 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, Sides sides) { 045 046 // obtain a CompatibleHardcopyWriter to do this 047 double margin = .5; 048 Dimension pagesize = null; // HardcopyWritter provides default page 049 // sizes for portrait and landscape 050 051 if (orientation.equals(Setup.HANDHELD) || orientation.equals(Setup.HALFPAGE)) { 052 isPrintHeader = false; 053 // add margins to page size 054 pagesize = new Dimension(getPageSize(orientation).width + PAPER_MARGINS.width, 055 getPageSize(orientation).height + PAPER_MARGINS.height); 056 } 057 try (CompatibleHardcopyWriter writer = new CompatibleHardcopyWriter(new Frame(), name, fontSize, margin, 058 margin, margin, margin, isPreview, printerName, orientation.equals(Setup.LANDSCAPE), isPrintHeader, sides, pagesize); 059 BufferedReader in = new BufferedReader(new InputStreamReader( 060 new FileInputStream(file), StandardCharsets.UTF_8));) { 061 062 // set font 063 if (!fontName.isEmpty()) { 064 writer.setFontName(fontName); 065 } 066 067 if (logoURL != null && !logoURL.equals(Setup.NONE)) { 068 ImageIcon icon = new ImageIcon(logoURL); 069 if (icon.getIconWidth() == -1) { 070 log.error("Logo not found: {}", logoURL); 071 } else { 072 writer.write(icon.getImage(), new JLabel(icon)); 073 } 074 } 075 076 List<String> lines = new ArrayList<>(); 077 String line; 078 while (true) { 079 line = in.readLine(); 080 if (line == null) { 081 if (isPreview) { 082 // need to do this in case the input file was empty to create preview 083 writer.write(" "); 084 } 085 break; 086 } 087 lines.add(line); 088 if (line.isBlank()) { 089 print(writer, lines, false); 090 } 091 } 092 print(writer, lines, true); 093 } catch (FileNotFoundException e) { 094 log.error("Build file doesn't exist", e); 095 } catch (CompatibleHardcopyWriter.PrintCanceledException ex) { 096 log.debug("Print canceled"); 097 } catch (IOException e) { 098 log.warn("Exception printing: {}", e.getLocalizedMessage()); 099 } 100 } 101 102 private static void print(CompatibleHardcopyWriter writer, List<String> lines, boolean lastBlock) 103 throws IOException { 104 int lineSize = getNumberOfLines(lines); 105 if (Setup.isPrintNoPageBreaksEnabled() && 106 writer.getCurrentLineNumber() != 0 && 107 writer.getLinesPerPage() - writer.getCurrentLineNumber() < lineSize) { 108 writer.pageBreak(); 109 } 110 // check for exact page break 111 if (writer.getLinesPerPage() - writer.getCurrentLineNumber() == lineSize) { 112 // eliminate blank line after page break 113 String s = lines.get(lines.size() - 1); 114 if (s.isBlank()) { 115 lines.remove(lines.size() - 1); 116 } 117 } 118 // use line feed for all lines? 119 if (lastBlock && writer.getLinesPerPage() - writer.getCurrentLineNumber() < lineSize) { 120 lastBlock = false; // yes 121 } 122 123 Color color = null; 124 boolean printingColor = false; 125 for (String line : lines) { 126 // determine if there's a line separator 127 if (printHorizontialLineSeparator(writer, line)) { 128 color = null; 129 continue; 130 } 131 // color text? 132 if (line.contains(TEXT_COLOR_START)) { 133 color = getTextColor(line); 134 if (line.contains(TEXT_COLOR_END)) { 135 printingColor = false; 136 } else { 137 // printing multiple lines in color 138 printingColor = true; 139 } 140 // could be a color change when using two column format 141 if (line.contains(Character.toString(VERTICAL_LINE_CHAR))) { 142 String s = line.substring(0, line.indexOf(VERTICAL_LINE_CHAR)); 143 s = getTextColorString(s); 144 writer.write(color, s); // 1st half of line printed 145 // get the new color and text 146 line = line.substring(line.indexOf(VERTICAL_LINE_CHAR)); 147 color = getTextColor(line); 148 // pad out string 149 line = tabString(getTextColorString(line), s.length()); 150 } else { 151 // simple case only one color 152 line = getTextColorString(line); 153 } 154 } else if (line.contains(TEXT_COLOR_END)) { 155 printingColor = false; 156 line = getTextColorString(line); 157 } else if (!printingColor) { 158 color = null; 159 } 160 161 printVerticalLineSeparator(writer, line); 162 line = line.replace(VERTICAL_LINE_CHAR, SPACE); 163 164 if (color != null) { 165 writer.write(color, line + NEW_LINE); 166 continue; 167 } 168 writer.write(line); 169 // no line feed if last line of file, eliminates blank page 170 if (!lastBlock || 171 writer.getCurrentLineNumber() < writer.getLinesPerPage() - 1) { 172 writer.write(NEW_LINE); 173 } 174 } 175 lines.clear(); 176 } 177 178 /* 179 * When determining the number of lines to print, we need to ignore any 180 * horizontal lines. 181 */ 182 private static int getNumberOfLines(List<String> lines) { 183 int numberLines = lines.size(); 184 for (String line : lines) { 185 for (char c : line.toCharArray()) { 186 if (c == HORIZONTAL_LINE_CHAR) { 187 numberLines--; 188 break; 189 } 190 } 191 } 192 return numberLines; 193 } 194 195 /* 196 * Returns true if horizontal line was printed, or line length = 0 197 */ 198 private static boolean printHorizontialLineSeparator(CompatibleHardcopyWriter writer, String line) { 199 boolean horizontialLineSeparatorFound = true; 200 if (line.length() > 0) { 201 for (int i = 0; i < line.length(); i++) { 202 if (line.charAt(i) != HORIZONTAL_LINE_CHAR) { 203 horizontialLineSeparatorFound = false; 204 break; 205 } 206 } 207 if (horizontialLineSeparatorFound) { 208 int endCol = writer.getCharactersPerLine() + 1; 209 writer.write(writer.getCurrentLineNumber(), 0, writer.getCurrentLineNumber(), 210 endCol); 211 } 212 } 213 return horizontialLineSeparatorFound; 214 } 215 216 private static void printVerticalLineSeparator(CompatibleHardcopyWriter writer, String line) { 217 for (int i = 0; i < line.length(); i++) { 218 if (line.charAt(i) == VERTICAL_LINE_CHAR) { 219 // make a frame (two column format) 220 if (Setup.isTabEnabled()) { 221 int endCol = writer.getCharactersPerLine() + 1; 222 writer.write(writer.getCurrentLineNumber(), 0, writer.getCurrentLineNumber() + 1, 0); 223 writer.write(writer.getCurrentLineNumber(), endCol, 224 writer.getCurrentLineNumber() + 1, endCol); 225 } 226 writer.write(writer.getCurrentLineNumber(), i + 1, writer.getCurrentLineNumber() + 1, 227 i + 1); 228 } 229 } 230 } 231 232 private final static Logger log = LoggerFactory.getLogger(TrainPrintManifest.class); 233}