Java tutorial
/* * File: QRCodeUtil.java * * Created on Aug 18, 2007 * * * Copyright 2006-2007 Felix Garcia Borrego (borrego@gmail.com) * * 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 org.viafirma.util; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import javax.imageio.ImageIO; import net.sourceforge.barbecue.BarcodeFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.viafirma.excepciones.CodigoError; import org.viafirma.excepciones.ExcepcionErrorInterno; import org.viafirma.plugin.AuthoritySupportPlugin; import org.viafirma.plugin.PluginLocator; import org.viafirma.vo.CertificadoGenerico; import com.lowagie.text.BadElementException; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Image; import com.lowagie.text.Paragraph; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfImportedPage; import com.lowagie.text.pdf.PdfPCell; import com.lowagie.text.pdf.PdfPTable; import com.lowagie.text.pdf.PdfReader; import com.lowagie.text.pdf.PdfWriter; import com.swetake.util.Qrcode; /** * Genera etiquetas basadas en QRCode Y Pdf de justificante. * * @author Felix Garcia Borrego (borrego at gmail.com) * @author Alexis Castilla Armero (pencerval at gmail.com) */ public class QRCodeUtil { private static QRCodeUtil singleton; private static boolean doUrlFriendly = true; private static String urlFriendly; public static QRCodeUtil getInstance() { if (singleton == null) { throw new ExceptionInInitializerError("QRCodeUtil no esta inicializado."); } return singleton; } public static void init(Properties properties) { if (properties.getProperty(Constantes.PRINT_URL_FRIENDLY) != null) doUrlFriendly = Boolean.valueOf(properties.getProperty(Constantes.PRINT_URL_FRIENDLY)); urlFriendly = properties.getProperty(Constantes.PUBLIC_URL_FRIENDLY); singleton = new QRCodeUtil(); } /** * URI a la imagen de cabecera utilizada en los pdfs. */ private final static String IMAGE_CABECERA = "/cabecera-pdf.png"; // ************************************************************ // ** Datos para la generacin de la etiqueta QR /** * Ancho de la etiqueta que contiene el QR */ private final static int ANCHO_ETIQUETA = 1100; /** * Alto de la etiqueta que contiene el QR */ private final static int ALTO_ETIQUETA = 254; /** * Marjen de la caja contenedora de la etiqueta */ private final static int MARGEN_CAJA = 10; /** * Marjen de todos los elementos de la etiqueta */ private final static int MARGEN = 25; /** * Tamao de los caracteres. */ private static final int FONT_SIZE = 18; /** * Tamao de los pixeles del QR. */ private static final int SIZE_QR = 5; /** * Nmero mximo de caracteres por linea. */ private static final int MAX_SIZE_LINEA = 75; /** * Nmero maximo de lineas de texto en la etiqueta. */ private static final int MAX_ROWS = 7; /** * Nmero mximo de caracteres que van a ser codificados dentro del QRCODE */ private static final int MAX_TEXT_SIZE = 130; // ************************************************************ // ** Datos para el formateado del PDF /** * Seperacin entre la tabla de vista previa y el cdigo CR_CODE */ private final static int SPACE_SEPARACION = 10; /** * Tamao del cdigo QR en el pdf. */ private final static float HEIGHT_QR_CODE_PDF = 130; private static Log log = LogFactory.getLog(Qrcode.class); /** * Genera un nuevo pdf con la imagen firmada y el pie de firma. * @param url Url desde la que se puede descargar el documeto */ public void generarImagenPdf(byte[] input, String texto, String url, String textoQR, String codFirma, OutputStream out) throws ExcepcionErrorInterno { try { // Nuevo Documento PDF Document document = new Document(); // Obtenemos el tamao de la pgina Rectangle pageSize = document.getPageSize(); PdfWriter.getInstance(document, out); document.open(); addContent(texto, url, textoQR, codFirma, document, pageSize, true, false); // Aadimos imagen original Image imagen = Image.getInstance(input); imagen.setBorder(10); imagen.scaleToFit(500, 500); imagen.setAbsolutePosition(50, pageSize.getHeight() - 400); document.add(imagen); // Cerramos el documento document.close(); } catch (Exception e) { throw new ExcepcionErrorInterno(CodigoError.ERROR_INTERNO, e); } } /** * Genera un NUEVO pdf con la imagen firmada y el pie de firma. */ public void generarPdf(String texto, String texto2Linea, String textoQR, String codFirma, OutputStream out) throws ExcepcionErrorInterno { try { // Nuevo Documento PDF Document document = new Document(); // Obtenemos el tamao de la pgina Rectangle pageSize = document.getPageSize(); PdfWriter.getInstance(document, out); document.open(); addContent(texto, texto2Linea, textoQR, codFirma, document, pageSize, false, false); // Cerramos el documento document.close(); } catch (Exception e) { throw new ExcepcionErrorInterno(CodigoError.ERROR_INTERNO, e); } } /** * @param texto * Texto a colocar * @param textoQR * Imagen QR a colocar * @param codFirma * Cdigo de Firma a generar. * @param url Url del servicio de verificacin desde donde se puede descargar el documento. * @param document * Documeto destino * @param pageSize * Tamao de la pgina * @return * @throws BadElementException * @throws MalformedURLException * @throws IOException * @throws DocumentException * @throws ExcepcionErrorInterno */ private static float addContent(String texto, String url, String textoQR, String codFirma, Document document, Rectangle pageSize, boolean completo, boolean isSellado) throws BadElementException, MalformedURLException, IOException, DocumentException, ExcepcionErrorInterno { Image imagenCabezera = null; float widthCabecera = 0; float heightCabecera = 0; if (!isSellado) { // Recuperamos la imagen de cabecera imagenCabezera = Image.getInstance(QRCodeUtil.class.getResource(IMAGE_CABECERA)); // Colocamos la imagen en la zona superior de la pgina imagenCabezera.setAbsolutePosition(0, pageSize.getHeight() - imagenCabezera.getHeight()); heightCabecera = imagenCabezera.getHeight(); widthCabecera = imagenCabezera.getWidth(); } // Recuperamos el pie de firma Image imagenQR = Image.getInstance(QRCodeUtil.getInstance().generate(texto, url, textoQR, codFirma)); float rescalado = pageSize.getWidth() / (widthCabecera + document.leftMargin() + document.rightMargin()); float sizeCabecera = 0; if (rescalado < 1f && !isSellado) { // Requiere rescalado, la imagen es mayor que la pgina. imagenCabezera.scalePercent(rescalado * 100); sizeCabecera = rescalado * imagenCabezera.getHeight(); } float sizeCabecerayPie = HEIGHT_QR_CODE_PDF + sizeCabecera + document.bottomMargin() + document.topMargin() + SPACE_SEPARACION; // float escaleQR = HEIGHT_QR_CODE_PDF / (imagenQR.getHeight() + 5); float escaleQR = (pageSize.getWidth() - document.leftMargin() - document.rightMargin()) / (imagenQR.getWidth() + 6); // imagen.setSpacingAfter(120); if (!isSellado) { document.add(imagenCabezera); } // Aadimos una caja de texto si estamos mostrando el contenido del // fichero firmado. if (completo) { // Aadimos el espacio ocupado por la imagen Paragraph p = new Paragraph(""); p.setSpacingAfter(heightCabecera * rescalado); document.add(p); // Aadimos una tabla con borde donde colocar el documento. PdfPTable table = new PdfPTable(1); table.setWidthPercentage(100); table.getDefaultCell().setBorderWidth(10); PdfPCell celda = new PdfPCell(); celda.setFixedHeight(pageSize.getHeight() - sizeCabecerayPie); table.addCell(celda); table.setSpacingAfter(SPACE_SEPARACION); document.add(table); } if (completo) { // La imagen la colocamos justo debajo imagenQR.setAbsolutePosition(document.leftMargin(), escaleQR * HEIGHT_QR_CODE_PDF - SPACE_SEPARACION * 2); } else { imagenQR.setAbsolutePosition(document.leftMargin(), pageSize.getHeight() - sizeCabecerayPie); } imagenQR.scalePercent(escaleQR * 100); document.add(imagenQR); return sizeCabecerayPie; } /** * Genera un justificante de firma de un fichero pdf de entrada. * * @param input * Fichero pdf de entrada * @param texto * @param textoQR * @param codFirma * @param out * @throws ExcepcionErrorInterno */ public void firmarPDF(InputStream input, String texto, String texto2Line, String textoQR, String codFirma, OutputStream out) throws ExcepcionErrorInterno { // leemos el pdf utilizando iText. try { // Recuperamos el documento original PdfReader reader = new PdfReader(input); // Obtenemos el tamao de la pgina Rectangle pageSize = reader.getPageSize(1); // Creamos un nuevo documento del mismo tamao que el original Document document = new Document(pageSize); // creo una instancia para escritura en el documento PdfWriter writer = PdfWriter.getInstance(document, out); document.open(); // insertamos la portada del documento que estamos firmando. float escala = (pageSize.getHeight() - 350) / pageSize.getHeight(); // Aadimos al documento la imagen de cabecera y de pie addContent(texto, texto2Line, textoQR, codFirma, document, pageSize, true, false); PdfContentByte cb = writer.getDirectContent(); PdfImportedPage portada = writer.getImportedPage(reader, 1); cb.setRGBColorStroke(0xCC, 0xCC, 0xCC); cb.transform(AffineTransform.getTranslateInstance(document.leftMargin(), 210)); cb.transform(AffineTransform.getScaleInstance(escala, escala)); cb.addTemplate(portada, 0, 0);// , escala, 0, 0, escala,dx,dy); document.close(); out.close(); } catch (Exception e) { throw new ExcepcionErrorInterno(CodigoError.ERROR_INTERNO, e); } } /** * Genera un documento sellado en fichero pdf. * * @param input * Fichero pdf de entrada * @param texto * @param textoQR * @param codFirma * @param out * @throws ExcepcionErrorInterno */ public void firmarPDFSellado(InputStream input, String texto, String texto2Line, String textoQR, String codFirma, OutputStream out) throws ExcepcionErrorInterno { // leemos el pdf utilizando iText. try { // Recuperamos el documento original PdfReader reader = new PdfReader(input); // Obtenemos el tamao de la pgina Rectangle pageSize = reader.getPageSize(1); // Creamos un nuevo documento del mismo tamao que el original Document document = new Document(pageSize); // creo una instancia para escritura en el documento PdfWriter writer = PdfWriter.getInstance(document, out); document.open(); // Aadimos al documento la imagen de cabecera y de pie float altoCabeceraYPie = addContent(texto, texto2Line, textoQR, codFirma, document, pageSize, true, true); altoCabeceraYPie = altoCabeceraYPie + document.topMargin(); // insertamos la portada del documento que estamos firmando. float escala = (pageSize.getHeight() - altoCabeceraYPie) / pageSize.getHeight(); PdfContentByte cb = writer.getDirectContent(); PdfImportedPage portada = writer.getImportedPage(reader, 1); cb.setRGBColorStroke(0xCC, 0xCC, 0xCC); cb.transform(AffineTransform.getScaleInstance(escala, escala)); cb.transform(AffineTransform.getTranslateInstance(document.leftMargin() * 2.5, ALTO_ETIQUETA + document.topMargin())); cb.addTemplate(portada, 0, 0);// , escala, 0, 0, escala,dx,dy); document.close(); out.close(); } catch (Exception e) { throw new ExcepcionErrorInterno(CodigoError.ERROR_INTERNO, e); } } /** * Genera codigo de firma para impresin formado por el texto, el cdigo qr * del texto y un cdigo de barras con el cdigo de firma. * * @param text * @param codFirma * @return * @throws ExcepcionErrorInterno */ public byte[] generate(String text, String url, String textoQR, String codFirma) throws ExcepcionErrorInterno { // Comprobamos el tamao del texto. No recomendamos textos de mas de 120 // caracteres if (textoQR.length() > MAX_TEXT_SIZE) { log.warn("El tamao del texto '" + text + "' ha excedido el tamao mximo. " + text.length() + ">" + MAX_TEXT_SIZE); textoQR = textoQR.substring(textoQR.lastIndexOf(" ")); log.warn("El texto sera recortado: '" + textoQR + "'"); } // Generamos la sufperficie de dibujo BufferedImage imagenQR = new BufferedImage(ANCHO_ETIQUETA, ALTO_ETIQUETA, BufferedImage.TYPE_INT_RGB); Graphics2D g = imagenQR.createGraphics(); // El fondo es blanco g.setBackground(Color.WHITE); g.clearRect(0, 0, ANCHO_ETIQUETA, ALTO_ETIQUETA); g.setColor(Color.BLACK); g.setStroke(new BasicStroke(2f)); // Pintamos la caja de borde g.draw(new java.awt.Rectangle(MARGEN_CAJA, MARGEN_CAJA, ANCHO_ETIQUETA - MARGEN_CAJA * 2, ALTO_ETIQUETA - MARGEN_CAJA * 2)); g.draw(new java.awt.Rectangle(MARGEN_CAJA + 3, MARGEN_CAJA + 3, ANCHO_ETIQUETA - MARGEN_CAJA * 2 - 6, ALTO_ETIQUETA - MARGEN_CAJA * 2 - 6)); // Generamos el cdigo QR Qrcode x = new Qrcode(); x.setQrcodeErrorCorrect('L'); x.setQrcodeEncodeMode('B'); // Modo Binario byte[] d = textoQR.getBytes(); // Generamos el cdigo QR boolean[][] s = x.calQrcode(d); for (int i = 0; i < s.length; i++) { for (int j = 0; j < s.length; j++) { if (s[j][i]) { g.fillRect(j * SIZE_QR + MARGEN, i * SIZE_QR + MARGEN, SIZE_QR, SIZE_QR); } } } int marjenSeparador = MARGEN + SIZE_QR * s.length + MARGEN_CAJA; int marjenTexto = marjenSeparador + MARGEN_CAJA; // Linea de separacin entre el QR cdigo y el texto g.drawLine(marjenSeparador - 3, MARGEN_CAJA + 3, marjenSeparador - 3, ALTO_ETIQUETA - MARGEN_CAJA - 3); g.drawLine(marjenSeparador, MARGEN_CAJA + 3, marjenSeparador, ALTO_ETIQUETA - MARGEN_CAJA - 3); // Linea de separacin entre texto y cdigo de barras. int marjenCodigoBarras = MARGEN + MAX_ROWS * FONT_SIZE; // Pintamos una pequea linea de separacin g.drawLine(marjenSeparador, marjenCodigoBarras, ANCHO_ETIQUETA - MARGEN_CAJA - 3, marjenCodigoBarras); g.drawLine(marjenSeparador, marjenCodigoBarras - 3, ANCHO_ETIQUETA - MARGEN_CAJA - 3, marjenCodigoBarras - 3); // Escribimos el texto List<String> parrafos = split(text); g.setFont(new Font(g.getFont().getName(), Font.BOLD, FONT_SIZE)); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); for (int i = 0; i < parrafos.size(); i++) { String linea = parrafos.get(i); // Pintamos la nueva linea de texto g.drawString(linea, marjenTexto, MARGEN + 3 + (FONT_SIZE * i + 1)); } g.setFont(new Font(g.getFont().getName(), Font.BOLD, FONT_SIZE - 4)); // Pintamos el texto final de una sola lnea( Generalmente el MD5 ) //if(url.length()) if (!url.equals("")) { g.drawString("Custodia del documento: ", marjenTexto, marjenCodigoBarras - 10 * 2); g.drawString(url, marjenTexto, marjenCodigoBarras - 6); } marjenCodigoBarras += MARGEN_CAJA; // Generamos el cdigo de barras try { // int marjenCodigoBarras=MARGEN+MAX_ROWS*FONT_SIZE; BarcodeFactory.createCode39(codFirma, true).draw(g, marjenTexto, marjenCodigoBarras); } catch (Exception e1) { // TODO e1.printStackTrace(); } // g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // RenderingHints.VALUE_ANTIALIAS_OFF); // Finalizamos la superficie de dibujo g.dispose(); ByteArrayOutputStream out = new ByteArrayOutputStream(); // Generamos la imagen try { ImageIO.write(imagenQR, "png", out); // ImageIO.write(imagenQR, "png", new // FileOutputStream("/tmp/imagen.png")); out.close(); } catch (Exception e) { throw new ExcepcionErrorInterno(CodigoError.ERROR_INTERNO, e); } return out.toByteArray(); } /** * Retorna el texto en parrafos preparados para la etiqueta. Trocea en * lineas y en "," si el tamao de la linea es excesivo. * * @param text * texto a procesar * @return Listado de lineas. */ private static List<String> split(String text) { String[] parrafos = text.split("\\n"); List<String> parrafosProcesados = new ArrayList<String>(); for (int i = 0; i < parrafos.length; i++) { String linea = parrafos[i]; if (linea.length() > MAX_SIZE_LINEA + 1) { // La linea es demasiado grande, troceamos StringBuilder builder = new StringBuilder(); String seccion[] = linea.split(","); for (int j = 0; j < seccion.length; j++) { String temp = seccion[j]; if (builder.length() + temp.length() > MAX_SIZE_LINEA) { builder.append(","); parrafosProcesados.add(builder.toString()); builder = new StringBuilder(); } builder.append(temp); } parrafosProcesados.add(builder.toString()); } else { parrafosProcesados.add(linea); } } return parrafosProcesados; } /* Cdigo de firma: A1HH-EZH9T5E1-1889-3802-8854 DIRECCION GENERAL DE IMPUESTOS INTERNOS RNC: 123 456 789 Email: oficinavirtual@dgii.gov.do Custodia del documento: http://viafirma.viavansi.com/viafirma/v/A1HH-EZH9T5E1-1889-3802-8854 */ /** Crea los textos utilizados en la etiqueta de justificante de firma. * @param certificado Array con el texto y el texto QR * @param sha1 cdigo del documento sha1 * @param url Url del servicio de verificacin. * @return */ public String[] buildTextoEtiqueta(CertificadoGenerico certificado, String codFirma, String url, String sha1) { StringBuilder texto = new StringBuilder(); StringBuilder textoQR = new StringBuilder(); // TODO Recuperamos el plugin asociado a este tipo de certificados. AuthoritySupportPlugin support; try { support = PluginLocator.getAuthoritySupport(certificado); // Indicamos el la etiqueta el cdigo de firma. texto.append("Cdigo de firma: ").append(codFirma).append("\n"); texto.append(sha1).append("\n"); textoQR.append(codFirma).append("\n"); support.appendEtiqueta(texto, certificado, codFirma); support.appendEtiquetaSimple(textoQR, certificado, codFirma); textoQR.append(url); } catch (ExcepcionErrorInterno e) { log.fatal("Tipo de certificado no reconocido", e); } // Autoridad de Certificacin /*if (!StringUtils.isEmpty(certificado.getCa())) { String[] textCertificado = certificado.getCa().split(","); StringBuilder linea = new StringBuilder(); StringBuilder ca = new StringBuilder(); for (int i = 0; i < textCertificado.length; i++) { String cadena = textCertificado[i]; // Nmero mximo de caracteres por lnea if (linea.length() + cadena.length() > 80) { ca.append(linea); ca.append("\n"); linea = new StringBuilder(cadena); } else { linea.append(cadena); if (i < textCertificado.length - 1) { linea.append(","); } } } ca.append(linea); texto.append(ca); } */ return new String[] { texto.toString(), textoQR.toString() }; } /** * Compone la url correcta a mostrar en el resguardo de la firma. * * @param url * @return */ public String buildFriendlyURLIfNeeded(String url) { if (doUrlFriendly) { if (urlFriendly != null) { url = url.substring(url.indexOf("/v/")); url = urlFriendly + url; return url; } else { return url; } } return ""; } }