/* * Copyright 2014 Jocki Hendry * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package simple.escp.swing; import simple.escp.util.EscpUtil; import javax.swing.JPanel; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontFormatException; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.font.TextAttribute; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This panel will display string as GUI preview. */ public class OutputPane extends JPanel { private static final Font FONT; private static final int DEFAULT_FONT_SIZE = 20; private static final String[] CP437_TO_UNICODE; private static final int CP437_ASCII_START = 128; public static final int CR = 13; public static final int FF = 12; static { Font defaultFont; try { defaultFont = Font.createFont(Font.TRUETYPE_FONT, Thread.currentThread().getContextClassLoader(). getResourceAsStream("DejaVuSansMono.ttf")).deriveFont(Font.PLAIN, DEFAULT_FONT_SIZE); } catch (FontFormatException | IOException e) { defaultFont = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE); } FONT = defaultFont; CP437_TO_UNICODE = new String[] { "\u00C7", "\u00FC", "\u00E9", "\u00E2", "\u00E4", "\u00E0", "\u00E5", "\u00E7", "\u00EA", "\u00EB", "\u00E8", "\u00EF", "\u00EE", "\u00EC", "\u00C4", "\u00C5", "\u00C9", "\u00E6", "\u00C6", "\u00F4", "\u00F6", "\u00F2", "\u00FB", "\u00F9", "\u00FF", "\u00D6", "\u00DC", "\u00A2", "\u00A3", "\u00A5", "\u20A7", "\u0192", "\u00E1", "\u00ED", "\u00F3", "\u00FA", "\u00F1", "\u00D1", "\u00AA", "\u00BA", "\u00BF", "\u2310", "\u00AC", "\u00BD", "\u00BC", "\u00A1", "\u00AB", "\u00BB", "\u2591", "\u2592", "\u2593", "\u2502", "\u2524", "\u2561", "\u2562", "\u2556", "\u2555", "\u2563", "\u2551", "\u2557", "\u255D", "\u255C", "\u255B", "\u2510", "\u2514", "\u2534", "\u252C", "\u251C", "\u2500", "\u253C", "\u255E", "\u255F", "\u255A", "\u2554", "\u2569", "\u2566", "\u2560", "\u2550", "\u256C", "\u2567", "\u2568", "\u2564", "\u2565", "\u2569", "\u2558", "\u2552", "\u2553", "\u256B", "\u256A", "\u2518", "\u250C", "\u258B", "\u2584", "\u258C", "\u2590", "\u2580", "\u03B1", "\u00DF", "\u0393", "\u03C0", "\u03A3", "\u03C3", "\u00B5", "\u03C4", "\u03A6", "\u039B", "\u03A9", "\u03B4", "\u221E", "\u03C6", "\u03B5", "\u2229", "\u2261", "\u00B1", "\u2265", "\u2264", "\u2320", "\u2321", "\u00F7", "\u2248", "\u00B0", "\u2219", "\u00B7", "\u221A", "\u207F", "\u00B2", "\u25A0", "\u00A0", }; } private static final BasicStroke DASH_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, new float[] {1.0f}, 0.0f); private static final float MARGIN_LEFT = 10.0f; private static final float MARGIN_RIGHT = 50.0f; private static final int CHAR_WIDTH = 12; private static final int CHAR_HEIGHT = 25; private static final int CIRCLE_SIZE = 15; private static final int CIRCLE_SPACING = 25; private static final int CIRCLE_LINE_MARGIN = 5; private static final float X_START = 10.0f, Y_START = 10.0f; private static final int DEFAULT_WIDTH = 100; private static final int DEFAULT_HEIGHT = 100; private String[] lines = new String[0]; private int pageLength; private int pageWidth; private int pages; private List<Shape> backgrounds = new ArrayList<>(); private Dimension prefferedSize; private Map<TextAttribute, Object> underlineAttribute; /** * Create new instance of <code>OutputPane</code>. */ public OutputPane() { } /** * Construct a new instance of <code>OutputPane</code>. * * @param text the string that should be displayed. It may contains ESC/P commands. * @param pageLength number of lines per page. * @param pageWidth number of characters per line. */ public OutputPane(String text, int pageLength, int pageWidth) { display(text, pageLength, pageWidth); } /** * Set the data that will be displayed by this <code>OutputPane</code> and display it. * * @param text the string that should be displayed. It may contains ESC/P commands. * @param pageLength number of lines per page. * @param pageWidth number of characters per line. */ public void display(String text, int pageLength, int pageWidth) { this.pageLength = pageLength; this.pageWidth = pageWidth; this.underlineAttribute = new HashMap<>(); this.underlineAttribute.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); // Replace CP347 characters with Unicode StringBuilder result = new StringBuilder(); for (int textIndex = 0; textIndex < text.length(); textIndex++) { int i = CP437_ASCII_START; boolean found = false; for (String ch : CP437_TO_UNICODE) { if (text.charAt(textIndex) == (char)i) { result.append(ch); found = true; break; } i++; } if (!found) { result.append(text.charAt(textIndex)); } } lines = result.toString().split("(" + EscpUtil.CRLF + ")"); pages = lines.length / pageLength; for (int i = 0; i < lines.length; i++) { // Ignore ESC Sequences that didn't have effect in preview lines[i] = lines[i].replaceAll(EscpUtil.esc(EscpUtil.COMMAND_INITIALIZE), ""); lines[i] = lines[i].replaceAll(EscpUtil.esc(EscpUtil.COMMAND_ONE_PER_EIGHT_LINE_SPACING), ""); lines[i] = lines[i].replaceAll(EscpUtil.esc(EscpUtil.COMMAND_ONE_PER_SIX_INCH_LINE_SPACING), ""); lines[i] = lines[i].replaceAll(EscpUtil.esc(EscpUtil.COMMAND_MASTER_SELECT) + "(.|\\r|\\n)", ""); lines[i] = lines[i].replaceAll(EscpUtil.esc(EscpUtil.COMMAND_SELECT_TYPEFACE) + "(.|\\r|\\n)", ""); lines[i] = lines[i].replaceAll(EscpUtil.esc(EscpUtil.COMMAND_LEFT_MARGIN) + "(.|\\r|\\n)", ""); lines[i] = lines[i].replaceAll(EscpUtil.esc(EscpUtil.COMMAND_RIGHT_MARGIN) + "(.|\\r|\\n)", ""); lines[i] = lines[i].replaceAll(EscpUtil.esc(EscpUtil.COMMAND_BOTTOM_MARGIN) + "(.|\\r|\\n)", ""); lines[i] = lines[i].replaceAll(EscpUtil.esc(EscpUtil.COMMAND_PAGE_LENGTH) + "(.|\\r|\\n)", ""); } // Create background shapes float x = X_START, y = Y_START; float xWidth = MARGIN_LEFT + MARGIN_RIGHT + (pageWidth * CHAR_WIDTH); for (int page = 0; page <= pages; page++) { for (int line = 0; line < pageLength; line++) { Shape circleStart = new Ellipse2D.Float(x, y, CIRCLE_SIZE, CIRCLE_SIZE); Shape circleEnd = new Ellipse2D.Float(x + xWidth, y, CIRCLE_SIZE, CIRCLE_SIZE); backgrounds.add(circleStart); backgrounds.add(circleEnd); y += CIRCLE_SPACING; } Shape lineBreak = new Line2D.Float(X_START, y, X_START + xWidth + CIRCLE_SIZE, y); backgrounds.add(lineBreak); } Shape horLineLeft1 = new Line2D.Float(X_START + CIRCLE_SIZE + CIRCLE_LINE_MARGIN, Y_START, X_START + CIRCLE_SIZE + CIRCLE_LINE_MARGIN, y); Shape horLineLeft2 = new Line2D.Float(X_START - CIRCLE_LINE_MARGIN, Y_START, X_START - CIRCLE_LINE_MARGIN, y); Shape horLineRight1 = new Line2D.Float(X_START + xWidth - CIRCLE_LINE_MARGIN, Y_START, X_START + xWidth - CIRCLE_LINE_MARGIN, y); Shape horLineRight2 = new Line2D.Float(X_START + xWidth + CIRCLE_SIZE + CIRCLE_LINE_MARGIN , Y_START, X_START + xWidth + CIRCLE_SIZE + CIRCLE_LINE_MARGIN, y); backgrounds.add(horLineLeft1); backgrounds.add(horLineLeft2); backgrounds.add(horLineRight1); backgrounds.add(horLineRight2); prefferedSize = new Dimension((int)(X_START + xWidth + CIRCLE_SIZE + CIRCLE_SPACING), (int)(y + CIRCLE_SIZE + CIRCLE_SPACING)); revalidate(); repaint(); } /** * Draw background. * * @param g2 a graphic context for drawing. */ private void paintBackground(Graphics2D g2) { g2.setColor(Color.BLACK); g2.setBackground(Color.WHITE); g2.clearRect(0, 0, getWidth(), getHeight()); g2.setColor(Color.GRAY); g2.setStroke(DASH_STROKE); for (Shape shape : backgrounds) { g2.draw(shape); } } /** * Draw text. * * @param g2 a graphic context for drawing. */ private void paintText(Graphics2D g2) { g2.setFont(FONT); g2.setColor(Color.BLACK); int charWidth = g2.getFontMetrics().charWidth('X'); int lineNumber = 0; float y = Y_START + CIRCLE_SIZE; for (String line: lines) { float x = X_START + CIRCLE_SIZE + MARGIN_LEFT; int i = 0; while (i < line.length()) { char c = line.charAt(i++); if (c == EscpUtil.ESC) { c = line.charAt(i++); switch (c) { case EscpUtil.COMMAND_SELECT_BOLD: g2.setFont(FONT.deriveFont(FONT.BOLD)); break; case EscpUtil.COMMAND_SELECT_ITALIC: g2.setFont(FONT.deriveFont(FONT.ITALIC)); break; case EscpUtil.COMMAND_SELECT_UNDERLINE: c = line.charAt(i++); if (c == 1) { g2.setFont(FONT.deriveFont(underlineAttribute)); } else { g2.setFont(FONT); } break; case EscpUtil.COMMAND_CANCEL_BOLD: case EscpUtil.COMMAND_CANCEL_ITALIC: default: g2.setFont(FONT); break; } } else if (c == CR) { c = line.charAt(i++); if (c == FF) { while (lineNumber < pageLength) { y += CHAR_HEIGHT; lineNumber++; } } } else { g2.drawString(Character.toString(c), x, y); x += charWidth; } } y += CHAR_HEIGHT; if (++lineNumber > pageLength) { lineNumber = 1; } } } @Override public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; paintBackground(g2); paintText(g2); } @Override public Dimension getPreferredSize() { return prefferedSize == null ? new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT) : prefferedSize; } }