OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Blame | Last modification | View Log | RSS feed

package org.openconcerto.modules.label;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.SimpleDoc;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.PlainDocument;

import org.openconcerto.modules.label.filter.BatchDocumentFilter;
import org.openconcerto.modules.label.filter.EANDocumentFilter;
import org.openconcerto.modules.label.filter.LimitDocumentFilter;
import org.openconcerto.modules.label.filter.NumberOfProductDocumentFilter;
import org.openconcerto.modules.label.gs1.GS1AIElements;
import org.openconcerto.modules.label.gs1.GS1Util;
import org.openconcerto.ui.JDate;

import uk.org.okapibarcode.backend.Code128;
import uk.org.okapibarcode.backend.DataMatrix;
import uk.org.okapibarcode.backend.DataMatrix.ForceMode;
import uk.org.okapibarcode.backend.Ean;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.output.Java2DRenderer;

public class GS1Frame extends JFrame {

    private final SimpleDateFormat dfDate = new SimpleDateFormat("yyMMdd");
    private final JTextField textSupplier = new JTextField(20);
    private final JTextField textName = new JTextField(20);
    private final JTextField textEAN = new JTextField(14);
    private final JTextField textBatch = new JTextField(20);
    private final JSpinner portZPL = new JSpinner(new SpinnerNumberModel(9100, 24, 10000, 1));
    private final JLabel labelEan = new JLabel("EAN 13/14", SwingConstants.RIGHT);
    private final JTextField textNumberOfProducts = new JTextField(12);
    private final JSpinner labelsSpinner = new JSpinner(new SpinnerNumberModel(5, 0, 9000, 1));
    private final JTextField labelBarCode = new JTextField(64);
    private final JDate dateDLUODLC = new JDate();
    private final JRadioButton b1 = new JRadioButton("DDM/DLUO");
    private final JCheckBox checkIgnoreMargins = new JCheckBox("Ignorer les marges de l'imprimante");
    private final JRadioButton twoPerPage = new JRadioButton("2");
    private final JRadioButton fourPerPage = new JRadioButton("4");
    private final JRadioButton radioZPLUSB = new JRadioButton("imprimante locale");
    private final JTextField textIP = new JTextField(20);
    private final JTabbedPane tabs = new JTabbedPane();
    private LabelPanel labelPanel;
    private BufferedImage barcodeImage = null;
    private BufferedImage barcodeImageBatch = null;
    private BufferedImage barcodeDatamatrix = null;
    private final Properties properties = new Properties();

    public GS1Frame() {
        super("Code à barres GS1-128");

        final File file = new File("openconcerto-gs1.properties");
        if (file.exists()) {
            try {
                properties.load(new FileInputStream(file));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        final JSplitPane split = new JSplitPane();
        final JPanel left = createLeftContent();
        final JComponent right = createRightContent();
        split.setLeftComponent(left);
        split.setRightComponent(right);
        this.setContentPane(split);
        split.setEnabled(false);
        try {
            updateList();
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                try {
                    saveProperties();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                dispose();
            }
        });
    }

    public void saveProperties() throws FileNotFoundException, IOException {
        properties.setProperty("supplier", this.textSupplier.getText());
        properties.setProperty("product", this.textName.getText());
        properties.setProperty("ean", this.textEAN.getText());
        properties.setProperty("nbOfProdcuts", this.textNumberOfProducts.getText());
        properties.setProperty("batch", this.textBatch.getText());
        final Date value = this.dateDLUODLC.getValue();
        if (value != null) {
            properties.setProperty("sellByDate", String.valueOf(value.getTime()));
        } else {
            properties.setProperty("sellByDate", "");
        }
        properties.setProperty("sellBy", b1.isSelected() ? "true" : "false");
        properties.setProperty("nbLabels", this.labelsSpinner.getValue().toString());
        properties.setProperty("labelsPerPage", fourPerPage.isSelected() ? "4" : "2");
        properties.setProperty("ignoreMargin", checkIgnoreMargins.isSelected() ? "true" : "false");
        properties.setProperty("usbPrinter", this.radioZPLUSB.isSelected() ? "true" : "false");
        properties.setProperty("zplIP", this.textIP.getText());
        properties.setProperty("zplPort", this.portZPL.getValue().toString());
        properties.setProperty("tab", String.valueOf(tabs.getSelectedIndex()));

        final FileOutputStream out = new FileOutputStream("openconcerto-gs1.properties");
        properties.store(out, "");
        out.flush();
        out.close();
    }

    boolean isEANValid() {
        final String ean = this.textEAN.getText().trim();
        if (ean.length() < 13) {
            return false;
        }
        final char cd1 = Ean.calcDigit(ean.substring(0, ean.length() - 1));
        final char cd2 = ean.charAt(ean.length() - 1);
        return (cd1 == cd2);
    }

    private void updateList() throws Exception {
        this.barcodeImage = null;
        this.barcodeImageBatch = null;
        this.barcodeDatamatrix = null;
        this.labelPanel.setList(new ArrayList<>());
        this.labelBarCode.setText("");
        // Check EAN
        String ean = this.textEAN.getText().trim();
        if (!isEANValid()) {
            this.labelEan.setForeground(Color.RED);
        } else {
            this.labelEan.setForeground(Color.BLACK);
        }
        if (ean.length() == 13) {
            this.labelEan.setText("EAN 13");
        } else if (ean.length() == 14) {
            this.labelEan.setText("EAN 14");
        } else {
            this.labelEan.setText("EAN invalide");
            this.labelEan.setForeground(Color.RED);
        }

        if (!isEANValid()) {
            return;
        }

        int n = ((Number) this.labelsSpinner.getValue()).intValue();
        this.labelBarCode.setText(this.getGS1().formatHumanReadable());
        this.labelBarCode.setEditable(false);
        GS1Label l = new GS1Label(this.textSupplier.getText(), this.textName.getText(), this.getGS1());
        List<GS1Label> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add(l);
        }
        this.labelPanel.setList(list);

        // Configure the barcode generator
        // adjust barcode width here
        // 32mm de hatu minimum
        // 0,495mm et 1,016mm pour largeur de barre

        GS1Util util = new GS1Util();

        {
            final GS1AIElements gs128 = getGS128();
            Code128 dataMatrix = new Code128();
            dataMatrix.setDataType(Symbol.DataType.GS1);
            dataMatrix.setBarHeight(70);
            dataMatrix.setContent(util.formatDataMatrix(gs128));
            int magnification = 5;
            int borderSize = 3;
            this.barcodeImage = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize), BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = this.barcodeImage.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setColor(Color.WHITE);
            g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize));
            Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK);
            renderer.render(dataMatrix);
        }
        final GS1AIElements gs128b = getGS128Batch();
        if (!gs128b.isEmpty()) {
            Code128 dataMatrix = new Code128();
            dataMatrix.setDataType(Symbol.DataType.GS1);
            dataMatrix.setBarHeight(90);
            dataMatrix.setContent(util.formatDataMatrix(gs128b));
            int magnification = 4;
            int borderSize = 3;
            this.barcodeImageBatch = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (int) (dataMatrix.getHeight() * magnification) + (2 * borderSize),
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = this.barcodeImageBatch.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setColor(Color.WHITE);
            g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize));
            Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK);
            renderer.render(dataMatrix);
        }

        {
            String s = util.formatDataMatrix(getGS1());
            DataMatrix dataMatrix = new DataMatrix();
            dataMatrix.setDataType(Symbol.DataType.GS1);
            dataMatrix.setForceMode(ForceMode.SQUARE);
            dataMatrix.setContent(s);
            int magnification = 30;
            int borderSize = 3;
            this.barcodeDatamatrix = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize),
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = this.barcodeDatamatrix.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setColor(Color.WHITE);
            g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize));
            Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK);
            renderer.render(dataMatrix);
        }

        // ImageIO.write(barcodeImage, "PNG", new File("barcode.png"));
        // ImageIO.write(barcodeImageBatch, "PNG", new File("barcode2.png"));
        // ImageIO.write(barcodeDatamatrix, "PNG", new File("datamatrix.png"));

    }

    private JPanel createLeftContent() {
        final JPanel p = new JPanel();
        p.setLayout(new GridBagLayout());
        final GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.gridy = 0;
        c.gridx = 0;
        c.gridwidth = 3;
        p.add(createLogo(), c);
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridwidth = 1;
        c.gridy++;
        c.anchor = GridBagConstraints.WEST;
        c.insets = new Insets(2, 3, 2, 2);
        //
        c.weightx = 0;
        c.gridwidth = 1;
        p.add(new JLabel("Fournisseur / fabricant", SwingConstants.RIGHT), c);
        c.gridx++;
        c.weightx = 1;
        c.gridwidth = 2;
        this.textSupplier.setText(properties.getProperty("supplier", ""));
        ((AbstractDocument) this.textSupplier.getDocument()).setDocumentFilter(new LimitDocumentFilter(22));
        p.add(this.textSupplier, c);

        //
        c.gridx = 0;
        c.gridy++;
        c.weightx = 0;
        c.gridwidth = 1;
        p.add(new JLabel("Désignation du produit", SwingConstants.RIGHT), c);
        c.gridx++;
        c.weightx = 1;
        c.gridwidth = 2;
        this.textName.setText(properties.getProperty("product", "Biscuit Extra"));
        ((AbstractDocument) this.textName.getDocument()).setDocumentFilter(new LimitDocumentFilter(22));
        p.add(this.textName, c);

        // EAN 13/14
        c.gridx = 0;
        c.gridy++;
        c.weightx = 0;
        c.gridwidth = 1;
        p.add(this.labelEan, c);
        c.gridx++;
        c.weightx = 1;
        c.gridwidth = 2;
        this.textEAN.setText(properties.getProperty("ean", "7612345678900"));
        ((AbstractDocument) this.textEAN.getDocument()).setDocumentFilter(new EANDocumentFilter());
        p.add(this.textEAN, c);
        // nb de produit
        c.gridx = 0;
        c.gridy++;
        c.weightx = 0;
        c.gridwidth = 1;
        p.add(new JLabel("Nombre par carton", SwingConstants.RIGHT), c);
        c.gridx++;
        c.weightx = 1;
        c.gridwidth = 2;
        c.fill = GridBagConstraints.NONE;
        PlainDocument doc = (PlainDocument) this.textNumberOfProducts.getDocument();
        doc.setDocumentFilter(new NumberOfProductDocumentFilter());
        this.textNumberOfProducts.setText(properties.getProperty("nbOfProdcuts", "10"));
        p.add(this.textNumberOfProducts, c);
        // Numéro de lot
        c.gridx = 0;
        c.gridy++;
        c.weightx = 0;
        c.gridwidth = 1;
        c.fill = GridBagConstraints.HORIZONTAL;
        p.add(new JLabel("Lot", SwingConstants.RIGHT), c);
        c.gridx++;
        c.weightx = 1;
        c.gridwidth = 2;
        ((AbstractDocument) this.textBatch.getDocument()).setDocumentFilter(new BatchDocumentFilter());
        this.textBatch.setText(properties.getProperty("batch", ""));
        p.add(this.textBatch, c);
        // DLC
        c.gridx = 1;
        c.gridy++;
        c.weightx = 0;
        c.gridwidth = 1;

        p.add(this.b1, c);
        c.gridx++;
        c.weightx = 1;
        this.dateDLUODLC.setFormat(new SimpleDateFormat("dd/MM/yyyy"));
        if (!properties.getProperty("sellByDate", "").isEmpty()) {
            this.dateDLUODLC.setDateInMillis(Long.parseLong(properties.getProperty("sellByDate")));
        }
        p.add(this.dateDLUODLC, c);
        // DLUO
        c.gridx = 1;
        c.gridy++;
        c.weightx = 0;
        JRadioButton b2 = new JRadioButton("DLC");
        p.add(b2, c);
        c.gridx++;
        c.weightx = 1;

        this.b1.setSelected(properties.getProperty("sellBy", "true").equals("true"));
        b2.setSelected(!properties.getProperty("sellBy", "true").equals("true"));
        // Barcode
        c.gridx = 0;
        c.gridy++;
        c.weightx = 0;
        c.gridwidth = 3;
        p.add(new JLabel("Code GS1 complet de l'étiquette", SwingConstants.LEFT), c);
        c.gridy++;
        c.weightx = 1;
        this.labelBarCode.setFont(this.textBatch.getFont());
        this.labelBarCode.setEnabled(false);
        p.add(this.labelBarCode, c);

        // Nombre d'étiquettes
        c.gridx = 0;
        c.gridy++;
        c.weightx = 0;
        c.gridwidth = 1;
        p.add(new JLabel("Nombre d'étiquettes", SwingConstants.RIGHT), c);
        c.gridx++;
        c.weightx = 0;
        c.fill = GridBagConstraints.NONE;

        this.labelsSpinner.setValue(Integer.parseInt(properties.getProperty("nbLabels", "4")));

        p.add(this.labelsSpinner, c);

        final JPanel spacer = new JPanel();
        spacer.setOpaque(false);
        c.gridy++;
        c.weighty = 1;
        p.add(spacer, c);

        final ButtonGroup gr = new ButtonGroup();
        gr.add(this.b1);
        gr.add(b2);

        // Listeners

        final ChangeListener changeListener = new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                try {
                    updateList();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        };
        this.labelsSpinner.addChangeListener(changeListener);

        final DocumentListener dListener = new DocumentListener() {

            @Override
            public void removeUpdate(DocumentEvent e) {
                changedUpdate(e);
            }

            @Override
            public void insertUpdate(DocumentEvent e) {
                changedUpdate(e);

            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                try {
                    updateList();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        };
        this.textSupplier.getDocument().addDocumentListener(dListener);
        this.textName.getDocument().addDocumentListener(dListener);
        this.textEAN.getDocument().addDocumentListener(dListener);
        this.textNumberOfProducts.getDocument().addDocumentListener(dListener);
        this.textBatch.getDocument().addDocumentListener(dListener);

        this.dateDLUODLC.addValueListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                try {
                    updateList();
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        });
        this.b1.addChangeListener(changeListener);
        b2.addChangeListener(changeListener);
        return p;
    }

    private Component createLogo() {
        final Image i1 = new ImageIcon(this.getClass().getResource("logo.png")).getImage();
        final Image i2 = new ImageIcon(this.getClass().getResource("oc-qrcode.png")).getImage();
        return new JComponent() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.setColor(Color.WHITE);
                g.fillRect(0, 0, getWidth(), getHeight());
                g.drawImage(i1, 0, 3, null);
                g.drawImage(i2, getWidth() - i2.getWidth(null), 0, null);
                g.setColor(Color.GRAY);
                g.drawLine(0, 58, getWidth(), 58);
            }

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 59);
            }
        };
    }

    private JComponent createRightContent() {

        final JPanel panelA4 = new JPanel();
        panelA4.setOpaque(false);
        panelA4.setLayout(new GridBagLayout());
        final GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.insets = new Insets(2, 3, 2, 2);
        c.gridx = 0;
        c.gridy = 0;
        panelA4.add(createToolBar(), c);
        c.gridy++;
        // panelA4.add(createToolBar2(), c);
        // c.gridy++;
        c.insets = new Insets(0, 0, 0, 0);
        panelA4.add(new JSeparator(JSeparator.HORIZONTAL), c);
        c.gridy++;
        c.weightx = 1;
        c.weighty = 1;
        c.fill = GridBagConstraints.BOTH;
        final int columnCount = fourPerPage.isSelected() ? 2 : 1;
        this.labelPanel = new LabelPanel(new ArrayList<Label>(), 2, columnCount, createRenderer()) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(190 * 3, 280 * 3);
            }
        };
        this.labelPanel.setIgnoreMargins(checkIgnoreMargins.isSelected());
        panelA4.add(this.labelPanel, c);
        tabs.addTab("Feuilles A4", panelA4);
        tabs.addTab("Imprimante ZPL", createZPLPanel());
        tabs.setSelectedIndex(Integer.parseInt(properties.getProperty("tab", "0")));
        return tabs;
    }

    private Component createZPLPanel() {
        final JPanel panelZPL = new JPanel();
        panelZPL.setOpaque(false);
        panelZPL.setLayout(new GridBagLayout());
        final GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.insets = new Insets(2, 3, 2, 2);
        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 0;
        c.gridwidth = 1;
        c.anchor = GridBagConstraints.EAST;

        radioZPLUSB.setOpaque(false);
        panelZPL.add(radioZPLUSB, c);
        c.gridx += 5;
        c.weightx = 1;
        c.fill = GridBagConstraints.NONE;
        c.insets = new Insets(4, 3, 2, 4);
        final JButton buttonPrintZPL = new JButton("Imprimer");
        buttonPrintZPL.setOpaque(false);
        panelZPL.add(buttonPrintZPL, c);
        c.insets = new Insets(2, 3, 2, 2);
        c.gridx = 0;
        c.gridy++;
        c.weightx = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        final JRadioButton radioZPLNetwork = new JRadioButton("imprimante réseau");
        radioZPLNetwork.setOpaque(false);
        panelZPL.add(radioZPLNetwork, c);
        c.gridx++;
        panelZPL.add(new JLabel("Adresse"), c);
        c.gridx++;

        textIP.setText(properties.getProperty("zplIP", "192.168.1.50"));
        panelZPL.add(textIP, c);
        c.gridx++;
        panelZPL.add(new JLabel("Port"), c);
        this.portZPL.setValue(Integer.parseInt(properties.getProperty("zplPort", "9100")));
        c.gridx++;
        panelZPL.add(this.portZPL, c);
        c.gridx = 0;
        c.gridy++;
        c.gridwidth = 6;

        radioZPLUSB.setSelected(properties.getProperty("usbPrinter", "true").equals("true"));
        radioZPLNetwork.setSelected(!properties.getProperty("usbPrinter", "true").equals("true"));

        final ButtonGroup g = new ButtonGroup();
        g.add(radioZPLUSB);
        g.add(radioZPLNetwork);

        c.anchor = GridBagConstraints.NORTHWEST;
        panelZPL.add(new JLabel("L'impression nécessite une imprimante d'étiquettes compatible ZPL (Zebra ou autre)."), c);
        c.gridy++;
        c.weighty = 1;
        panelZPL.add(new JLabel("Vous pouvez paramétrer l'impression par défaut (largeur 6\" à 203 dpi) dans le fichier zpl.txt ."), c);
        buttonPrintZPL.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (!isEANValid()) {
                    JOptionPane.showMessageDialog(GS1Frame.this, "EAN invalide");
                    return;
                }

                final String code = createZPLCode();
                System.out.println("ZPL:");
                System.out.println(code);
                byte[] data = code.getBytes(StandardCharsets.US_ASCII);
                if (radioZPLNetwork.isSelected()) {
                    Socket socket = null;
                    try {
                        socket = new Socket(textIP.getText(), ((Number) GS1Frame.this.portZPL.getValue()).intValue());
                        final DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                        final int nb = ((Number) labelsSpinner.getValue()).intValue();
                        for (int i = 0; i < nb; i++) {
                            out.write(data);
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        JOptionPane.showMessageDialog(buttonPrintZPL, "Erreur d'impression réseau : " + ex.getMessage());
                    } finally {
                        if (socket != null) {
                            try {
                                socket.close();
                            } catch (IOException e1) {
                                e1.printStackTrace();
                            }
                        }

                    }
                } else {
                    try {
                        final PrinterJob pj1 = PrinterJob.getPrinterJob();
                        if (pj1.printDialog()) {
                            final PrintService ps = pj1.getPrintService();
                            final DocPrintJob pj = ps.createPrintJob();
                            final SimpleDoc doc = new SimpleDoc(data, DocFlavor.BYTE_ARRAY.AUTOSENSE, null);
                            final int nb = ((Number) labelsSpinner.getValue()).intValue();
                            for (int i = 0; i < nb; i++) {
                                pj.print(doc, null);
                            }
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        JOptionPane.showMessageDialog(buttonPrintZPL, "Erreur d'impression locale : " + ex.getMessage());
                    }
                }
            }

        });

        return panelZPL;

    }

    private LabelRenderer createRenderer() {
        return new LabelRenderer() {

            @Override
            public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) {
                final Graphics2D g2d = (Graphics2D) g;
                g.setColor(Color.BLACK);
                g2d.setStroke(new BasicStroke(3));
                String str1 = GS1Frame.this.textSupplier.getText().trim();
                String str2 = GS1Frame.this.textName.getText();
                int pos1 = 26;
                if (str1.isEmpty()) {
                    str1 = GS1Frame.this.textName.getText();
                    str2 = "";
                    pos1 += 16;
                }

                final AffineTransform defaultAt = g2d.getTransform();
                final AffineTransform at = ((AffineTransform) (g2d.getTransform().clone()));
                at.rotate(-Math.PI / 2);
                g2d.setTransform(at);
                g.setFont(getFont().deriveFont(15f));
                final int d1 = 60;

                final int d2 = 60;
                g.drawString(str1, -y - gridHeight + d1, x + pos1);
                g.drawString(str2, -y - gridHeight + d2, x + 58);
                if (GS1Frame.this.barcodeDatamatrix != null) {
                    float ratioDatamatrix = 15f;
                    final int wd = (int) (GS1Frame.this.barcodeDatamatrix.getWidth() / ratioDatamatrix);
                    final int hd = (int) (GS1Frame.this.barcodeDatamatrix.getHeight() / ratioDatamatrix);
                    g.drawImage(GS1Frame.this.barcodeDatamatrix, -y - 10 - wd, x + 10, wd, hd, null);
                    final int w1 = GS1Frame.this.barcodeImage.getWidth() / 4;
                    final int h1 = GS1Frame.this.barcodeImage.getHeight() / 4;
                    g.drawImage(GS1Frame.this.barcodeImage, -y + -gridHeight + 60, x + 70, w1, h1, null);

                }
                if (GS1Frame.this.barcodeImageBatch != null) {
                    // GS1-128 numero de lot
                    final int w2 = GS1Frame.this.barcodeImageBatch.getWidth() / 4;
                    final int h2 = GS1Frame.this.barcodeImageBatch.getHeight() / 4;
                    g.drawImage(GS1Frame.this.barcodeImageBatch, -y - gridHeight + 60, x + 180, w2, h2, null);
                }
                g2d.setTransform(defaultAt);

            }
        };

    }

    public JPanel createToolBar() {
        final JPanel toolbar = new JPanel();
        toolbar.setOpaque(false);
        toolbar.setLayout(new GridBagLayout());
        final GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.insets = new Insets(2, 3, 2, 2);
        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 0;
        c.gridwidth = 1;
        toolbar.add(new JLabel("Etiquettes par page"), c);
        c.gridx++;

        twoPerPage.setOpaque(false);
        fourPerPage.setOpaque(false);
        final ButtonGroup g = new ButtonGroup();
        g.add(twoPerPage);
        g.add(fourPerPage);

        if (properties.getProperty("labelsPerPage", "4").equals("4")) {
            fourPerPage.setSelected(true);
        } else {
            twoPerPage.setSelected(true);
        }

        toolbar.add(twoPerPage, c);
        c.gridx++;
        toolbar.add(fourPerPage, c);
        c.gridx++;
        checkIgnoreMargins.setOpaque(false);
        checkIgnoreMargins.setSelected(properties.getProperty("ignoreMargin", "false").equals("true"));
        c.gridx++;
        toolbar.add(checkIgnoreMargins, c);
        c.gridx++;
        c.weightx = 1;
        c.fill = GridBagConstraints.NONE;
        c.anchor = GridBagConstraints.NORTHEAST;
        final JButton printButton = new JButton("Imprimer");
        printButton.setOpaque(false);
        toolbar.add(printButton, c);
        c.gridx++;

        twoPerPage.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (twoPerPage.isSelected()) {
                    GS1Frame.this.labelPanel.setColumnCount(1);
                } else {
                    GS1Frame.this.labelPanel.setColumnCount(2);
                }

            }
        });

        fourPerPage.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (twoPerPage.isSelected()) {
                    GS1Frame.this.labelPanel.setColumnCount(1);
                } else {
                    GS1Frame.this.labelPanel.setColumnCount(2);
                }

            }
        });

        printButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (!isEANValid()) {
                    JOptionPane.showMessageDialog(GS1Frame.this, "EAN invalide");
                    return;
                }

                final PrinterJob job = PrinterJob.getPrinterJob();
                job.setPrintable(GS1Frame.this.labelPanel);
                boolean ok = job.printDialog();
                if (ok) {
                    try {
                        job.print();
                    } catch (PrinterException ex) {
                        JOptionPane.showMessageDialog(GS1Frame.this, "Print error :" + ex.getMessage());
                    }
                }

            }
        });
        checkIgnoreMargins.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                GS1Frame.this.labelPanel.setIgnoreMargins(checkIgnoreMargins.isSelected());

            }
        });
        return toolbar;
    }

    public JPanel createToolBar2() {
        final JPanel toolbar = new JPanel();
        toolbar.setOpaque(false);
        toolbar.setLayout(new GridBagLayout());
        final GridBagConstraints c = new GridBagConstraints();
        c.insets = new Insets(2, 3, 2, 2);
        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 0;
        c.gridwidth = 1;
        c.fill = GridBagConstraints.NONE;
        toolbar.add(new JLabel("Marge en haut (mm)"), c);
        c.gridx++;
        final JSpinner sLines = new JSpinner(new SpinnerNumberModel(0, 0, 200, 1));
        toolbar.add(sLines, c);
        c.gridx++;
        toolbar.add(new JLabel("Marge à gauche (mm)"), c);
        c.gridx++;
        final JSpinner sColums = new JSpinner(new SpinnerNumberModel(0, 0, 200, 1));
        c.weightx = 1;
        toolbar.add(sColums, c);
        c.gridx++;

        sLines.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                final Number n = (Number) sLines.getValue();
                if (n != null) {
                    GS1Frame.this.labelPanel.setTopMargin(n.intValue());
                }
            }
        });
        sColums.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                final Number n = (Number) sColums.getValue();
                if (n != null) {
                    GS1Frame.this.labelPanel.setLeftMargin(n.intValue());
                }
            }
        });

        return toolbar;
    }

    public String readZPLScript() {
        if (new File("zpl.txt").exists()) {
            try (InputStream in = new FileInputStream(new File("zpl.txt"))) {
                return readAscii(in);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            try (InputStream in = GS1Frame.class.getResourceAsStream("default-zpl.txt")) {
                return readAscii(in);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        return "";
    }

    protected String createZPLCode() {
        final String script = readZPLScript();
        final BufferedReader reader = new BufferedReader(new StringReader(script));
        final StringBuilder builder = new StringBuilder();
        final GS1Util u = new GS1Util();
        try {
            String line = reader.readLine();
            while (line != null) {
                if (line.contains("XXXXXXXXXX")) {
                    if (!this.textSupplier.getText().isEmpty()) {
                        line = line.replace("XXXXXXXXXX", this.textSupplier.getText());
                        builder.append(line);
                        builder.append("\r\n");
                    }
                } else if (line.contains("YYYYYYYYYY")) {
                    line = line.replace("YYYYYYYYYY", this.textName.getText());
                    builder.append(line);
                    builder.append("\r\n");
                } else if (line.contains("(02)7612345678900(15)201218(37)9999")) {
                    line = line.replace("(02)7612345678900(15)201218(37)9999", getGS128().formatHumanReadable());
                    builder.append(line);
                    builder.append("\r\n");

                } else if (line.contains("(10)LLLLLLLLL")) {
                    GS1AIElements batch = getGS128Batch();
                    if (!batch.isEmpty()) {
                        line = line.replace("(10)LLLLLLLLL", batch.formatHumanReadable());
                        builder.append(line);
                        builder.append("\r\n");
                    }
                } else if (line.contains("OPENCONCERTO")) {
                    GS1AIElements all = getGS1();
                    line = line.replace("OPENCONCERTO", u.formatZPL(all));
                    builder.append(line);
                    builder.append("\r\n");

                } else {
                    builder.append(line);
                    builder.append("\r\n");
                }

                line = reader.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return builder.toString();
    }

    private GS1AIElements getGS1() {
        final GS1AIElements gs1 = new GS1AIElements();
        final String ean = this.textEAN.getText().trim();
        if (!ean.isEmpty()) {
            if (ean.length() == 13 || (ean.length() == 14 && ean.charAt(0) == '0')) {
                if (ean.length() == 13) {
                    gs1.put("02", '0' + ean);
                } else {
                    gs1.put("02", ean);
                }
            } else {
                gs1.put("01", ean);
            }
        }

        if (this.dateDLUODLC.getValue() != null) {
            String date = this.dfDate.format(this.dateDLUODLC.getValue());
            if (this.b1.isSelected()) {
                gs1.put("15", date);
            } else {
                gs1.put("17", date);
            }
        }

        final String nbProducts = this.textNumberOfProducts.getText();
        if (!nbProducts.trim().isEmpty()) {
            int n = Integer.parseInt(this.textNumberOfProducts.getText());
            gs1.put("37", String.valueOf(n));

        }
        final String batch = this.textBatch.getText().trim();
        if (!batch.isEmpty()) {
            gs1.put("10", batch);
        }

        return gs1;
    }

    private GS1AIElements getGS128Batch() {
        final GS1AIElements gs1 = new GS1AIElements();
        final String batch = this.textBatch.getText().trim();
        if (!batch.isEmpty()) {
            gs1.put("10", batch);
        }
        return gs1;
    }

    private GS1AIElements getGS128() {
        final GS1AIElements gs1 = getGS1();
        gs1.remove("10");
        return gs1;
    }

    public String readAscii(final InputStream in) throws IOException {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        final byte[] buf = new byte[8192];
        int length;
        while ((length = in.read(buf)) > 0) {
            out.write(buf, 0, length);
        }
        out.flush();
        out.close();
        return new String(out.toByteArray(), StandardCharsets.US_ASCII);
    }

    static List<Image> frameIcon;

    public static synchronized List<Image> getFrameIcon() {
        if (frameIcon == null) {
            frameIcon = new ArrayList<>();
            final int[] sizes = { 16, 32, 48, 96 };
            for (int i = 0; i < sizes.length; i++) {
                int v = sizes[i];
                try {
                    frameIcon.add(new ImageIcon(GS1Frame.class.getResource(v + ".png")).getImage());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return frameIcon;
    }

    public static void main(String[] args) {

        if (!new File("zpl.txt").exists()) {
            final InputStream in = GS1Frame.class.getResourceAsStream("default-zpl.txt");
            try (FileOutputStream fOut = new FileOutputStream(new File("zpl.txt"))) {
                byte[] buf = new byte[8192];
                int length;
                while ((length = in.read(buf)) > 0) {
                    fOut.write(buf, 0, length);
                }
                fOut.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e1) {
                    e1.printStackTrace();
                }

                final GS1Frame f = new GS1Frame();
                f.setIconImages(getFrameIcon());
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.pack();
                f.setVisible(true);
                f.setLocationRelativeTo(null);
                f.setResizable(false);

            }
        });

    }

}